fix(ai): improve API retry handling

This commit is contained in:
Abeautifulsnow authored and GitHub committed 2026-07-29 16:16:39 +08:00
1 parent a4352d6560
commit 52d5c99a92
29 files changed
+3962 -1097

No files matched your search

File diff suppressed because it is too large. Load diff
+6
View File
@@ -1776,6 +1776,12 @@ export default {
contextWindow: "Context Window",
contextWindowAuto: "Auto (detect from model name)",
contextWindowHint: "Tokens. Leave empty to auto-detect. Set manually for local/custom models.",
maxRetries: "Max Retries",
maxRetriesHint: "Retry on rate limits, timeouts, and temporary network errors. 0 = never, max 10.",
maxRetriesGlobal: "Max Retries",
maxRetriesGlobalDescription: "Number of automatic retries on transient API errors (rate limits, timeouts, network blips). Applies to all API-backed AI providers. CLI providers (claude-code, codex, pi) are unaffected.",
maxRetriesRange: "{min}–{max}, default {default}",
maxRetriesSaved: "Max retries saved",
codexCliPath: "Codex CLI Path",
codexCliPathHint: "Leave empty to use codex from PATH. Sign in separately with codex login.",
codexCliPathEnvError: "Codex CLI Path should contain only the executable path. Add environment variables below.",
+6
View File
@@ -1815,6 +1815,10 @@ export default withEnglishFallback({
contextWindow: "Ventana de contexto",
contextWindowAuto: "Auto (detectar desde el nombre del modelo)",
contextWindowHint: "Tokens. Déjalo vacío para detección automática. Configúralo manualmente para modelos locales/personalizados.",
maxRetriesGlobal: "Reintentos máximos",
maxRetriesGlobalDescription: "Número de reintentos automáticos en errores transitorios de API (límite de tasa, timeout, fallos de red). Se aplica a todos los proveedores de IA por API. Los proveedores CLI (claude-code, codex, pi) no se ven afectados.",
maxRetriesRange: "{min}–{max}, predeterminado {default}",
maxRetriesSaved: "Reintentos máximos guardados",
codexCliPath: "Ruta de Codex CLI",
codexCliPathHint: "Déjalo vacío para usar codex desde PATH. Inicia sesión por separado con codex login.",
actions: {
@@ -1846,6 +1850,8 @@ export default withEnglishFallback({
editResend: "Reenviar",
editCancel: "Cancelar",
cannotDeleteActiveConfig: "No se puede eliminar la configuración activa actual. Cambie a otra configuración primero.",
maxRetries: "Número máximo de reintentos",
maxRetriesHint: "Reintentar automáticamente en caso de limitación de tasa, tiempo de espera o error de red temporal. 0 = no reintentar, máximo 10.",
},
contextMenu: {
openConnection: "Abrir conexión",
+6
View File
@@ -1755,6 +1755,10 @@ export default withEnglishFallback({
contextWindow: "Finestra di Contesto",
contextWindowAuto: "Auto (rileva dal nome del modello)",
contextWindowHint: "Token. Lascia vuoto per il rilevamento automatico. Imposta manualmente per modelli locali/personalizzati.",
maxRetriesGlobal: "Tentativi massimi",
maxRetriesGlobalDescription: "Numero di tentativi automatici su errori API transitori (limite di velocità, timeout, problemi di rete). Si applica a tutti i provider IA via API. I provider CLI (claude-code, codex, pi) non sono interessati.",
maxRetriesRange: "{min}–{max}, predefinito {default}",
maxRetriesSaved: "Tentativi massimi salvati",
codexCliPath: "Percorso Codex CLI",
codexCliPathHint: "Lascia vuoto per usare codex dal PATH. Esegui login separatamente con codex login.",
codexCliPathEnvError: "Codex CLI Path deve contenere solo il percorso dell'eseguibile. Aggiungi le variabili d'ambiente sotto.",
@@ -1844,6 +1848,8 @@ export default withEnglishFallback({
editResend: "Invia di nuovo",
editCancel: "Annulla",
cannotDeleteActiveConfig: "Non è possibile eliminare la configurazione attualmente attiva. Passa a un'altra configurazione prima.",
maxRetries: "Numero massimo di tentativi",
maxRetriesHint: "Riprova automaticamente in caso di limitazione, timeout o errore di rete temporaneo. 0 = nessun tentativo, massimo 10.",
},
contextMenu: {
openConnection: "Apri Connessione",
+6
View File
@@ -1649,6 +1649,10 @@ export default withEnglishFallback({
contextWindow: "コンテキストウィンドウ",
contextWindowAuto: "自動(モデル名から検出)",
contextWindowHint: "トークン数。空欄で自動検出。ローカル/カスタムモデルは手動設定してください。",
maxRetriesGlobal: "最大再試行回数",
maxRetriesGlobalDescription: "一時的なAPIエラー(レート制限、タイムアウト、ネットワーク障害)発生時の自動再試行回数。すべてのAPI対応AIプロバイダーに適用されます。CLIプロバイダー(claude-code、codex、pi)は影響を受けません。",
maxRetriesRange: "{min}–{max}、デフォルト {default}",
maxRetriesSaved: "最大再試行回数を保存しました",
codexCliPath: "Codex CLI パス",
codexCliPathHint: "空欄の場合はPATHからcodexを使用します。codex loginで別途サインインしてください。",
reasoningLevel: "推論レベル",
@@ -1845,6 +1849,8 @@ export default withEnglishFallback({
editResend: "再送信",
editCancel: "キャンセル",
cannotDeleteActiveConfig: "現在アクティブな設定は削除できません。先に他の設定に切り替えてください。",
maxRetries: "最大リトライ回数",
maxRetriesHint: "レート制限、タイムアウト、または一時的なネットワークエラーが発生した場合に自動的にリトライします。0 = リトライなし、最大10。",
},
contextMenu: {
openConnection: "接続を開く",
+6
View File
@@ -1813,6 +1813,10 @@ export default withEnglishFallback({
contextWindow: "Janela de Contexto",
contextWindowAuto: "Automático (detectar pelo nome do modelo)",
contextWindowHint: "Tokens. Deixe vazio para detecção automática. Defina manualmente para modelos locais/personalizados.",
maxRetriesGlobal: "Máximo de Tentativas",
maxRetriesGlobalDescription: "Número de tentativas automáticas em erros transitórios de API (limite de taxa, timeout, falhas de rede). Aplica-se a todos os provedores de IA via API. Provedores CLI (claude-code, codex, pi) não são afetados.",
maxRetriesRange: "{min}–{max}, padrão {default}",
maxRetriesSaved: "Máximo de tentativas salvo",
enableThinking: "Raciocínio",
enableThinkingOn: "Ativado",
enableThinkingOff: "Desativado",
@@ -1846,6 +1850,8 @@ export default withEnglishFallback({
editResend: "Reenviar",
editCancel: "Cancelar",
cannotDeleteActiveConfig: "Não é possível excluir a configuração ativa atual. Mude para outra configuração primeiro.",
maxRetries: "Número máximo de tentativas",
maxRetriesHint: "Tente novamente automaticamente ao encontrar limitação de taxa, tempo limite ou erro de rede temporário. 0 = não tentar novamente, máximo 10.",
},
contextMenu: {
openConnection: "Abrir Conexão",
+6
View File
@@ -1777,6 +1777,12 @@ export default withEnglishFallback({
contextWindow: "上下文窗口",
contextWindowAuto: "自动(根据模型名称推断)",
contextWindowHint: "单位:token。留空自动推断,本地/自定义模型建议手动设置。",
maxRetries: "最大重试次数",
maxRetriesHint: "遇到限流、超时或临时网络错误时自动重试。0 = 不重试,最大 10。",
maxRetriesGlobal: "最大重试次数",
maxRetriesGlobalDescription: "遇到临时 API 错误(限流、超时、网络波动)时的自动重试次数。适用于所有 API 模式 AI 供应商。CLI 供应商(claude-code、codex、pi)不受影响。",
maxRetriesRange: "{min}–{max},默认 {default}",
maxRetriesSaved: "最大重试次数已保存",
codexCliPath: "Codex CLI 路径",
codexCliPathHint: "留空则使用 PATH 中的 codex。请先在终端执行 codex login 登录。",
codexCliPathEnvError: "Codex CLI 路径只能填写可执行文件路径。请在下方添加环境变量。",
+6
View File
@@ -1756,6 +1756,12 @@ export default withEnglishFallback({
contextWindow: "Context Window",
contextWindowAuto: "自動(從模型名稱偵測)",
contextWindowHint: "Token 數。留空以自動偵測。本機/自訂模型請手動設定。",
maxRetries: "最大重試次數",
maxRetriesHint: "遇到限流、超時或暫時性網路錯誤時自動重試。0 = 不重試,最大 10。",
maxRetriesGlobal: "最大重試次數",
maxRetriesGlobalDescription: "遇到暫時性 API 錯誤(限流、超時、網路波動)時的自動重試次數。適用於所有 API 模式 AI 供應商。CLI 供應商(claude-code、codex、pi)不受影響。",
maxRetriesRange: "{min}–{max},預設 {default}",
maxRetriesSaved: "最大重試次數已儲存",
codexCliPath: "Codex CLI 路徑",
codexCliPathHint: "留空以使用 PATH 中的 codex。請另外以 codex login 登入。",
codexMcpRequiredTitle: "需要 DBX MCP Server",
+2
View File
@@ -272,6 +272,8 @@ export const loadMcpGlobalPolicy = forward("loadMcpGlobalPolicy");
export const saveMcpGlobalPolicy = forward("saveMcpGlobalPolicy");
export const loadMaxAgentTurns = forward("loadMaxAgentTurns");
export const saveMaxAgentTurns = forward("saveMaxAgentTurns");
export const loadMaxRetries = forward("loadMaxRetries");
export const saveMaxRetries = forward("saveMaxRetries");
export const completeAppClose = forward("completeAppClose");
export const requestAppClose = forward("requestAppClose");
export const setDriverStoreDir = forward("setDriverStoreDir");
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+77 -15
View File
@@ -393,7 +393,14 @@ export interface CustomTheme {
ddlColors: CustomThemeDdlColors;
}
export const DEFAULT_CUSTOM_THEMES: CustomTheme[] = [{ id: "default", name: "Custom", colors: { ...DEFAULT_CUSTOM_THEME_COLORS }, ddlColors: { ...DEFAULT_CUSTOM_THEME_DDL_COLORS } }];
export const DEFAULT_CUSTOM_THEMES: CustomTheme[] = [
{
id: "default",
name: "Custom",
colors: { ...DEFAULT_CUSTOM_THEME_COLORS },
ddlColors: { ...DEFAULT_CUSTOM_THEME_DDL_COLORS },
},
];
export type SidebarObjectInfoMode = "comment-inline" | "comment-aligned" | "comment-right" | "size" | "hidden";
@@ -538,7 +545,11 @@ export function enforceRightSidebarPanelExclusivity(current: RightSidebarPanelSt
return transitionRightSidebarPanels(current, panelToKeep, true, true);
}
export const EDITOR_THEMES: { value: EditorTheme; label: string; dark: boolean }[] = [
export const EDITOR_THEMES: {
value: EditorTheme;
label: string;
dark: boolean;
}[] = [
{ value: "app", label: "Follow app theme", dark: false },
{ value: "one-dark", label: "One Dark", dark: true },
{ value: "vscode-dark", label: "VS Dark+", dark: true },
@@ -790,7 +801,13 @@ function normalizeSqlSnippets(value: unknown, existing?: SqlSnippet[]): SqlSnipp
if (seenPrefixes.has(item.prefix)) continue;
seenPrefixes.add(item.prefix);
// Older settings do not have this field; only an explicit false disables a snippet.
valid.push({ id: item.id, label: item.label, prefix: item.prefix, body: item.body, enabled: item.enabled !== false });
valid.push({
id: item.id,
label: item.label,
prefix: item.prefix,
body: item.body,
enabled: item.enabled !== false,
});
}
if (valid.length === 0) return existing ?? DEFAULT_SQL_SNIPPETS;
return valid;
@@ -845,7 +862,10 @@ export function normalizeEditorSettings(settings: Partial<EditorSettings>, exist
return {
...renamed,
colors: { ...DEFAULT_CUSTOM_THEME_COLORS, ...renamed.colors },
ddlColors: { ...DEFAULT_CUSTOM_THEME_DDL_COLORS, ...(renamed as any).ddlColors },
ddlColors: {
...DEFAULT_CUSTOM_THEME_DDL_COLORS,
...(renamed as any).ddlColors,
},
};
});
}
@@ -854,7 +874,10 @@ export function normalizeEditorSettings(settings: Partial<EditorSettings>, exist
{
id: "migrated",
name: "Migrated",
colors: { ...DEFAULT_CUSTOM_THEME_COLORS, ...settings.customThemeColors },
colors: {
...DEFAULT_CUSTOM_THEME_COLORS,
...settings.customThemeColors,
},
ddlColors: { ...DEFAULT_CUSTOM_THEME_DDL_COLORS },
},
]
@@ -915,17 +938,43 @@ export function normalizeEditorSettings(settings: Partial<EditorSettings>, exist
sidebarObjectDisplay: settings.sidebarObjectDisplay === "simple" || settings.sidebarObjectDisplay === "grouped" ? settings.sidebarObjectDisplay : DEFAULT_EDITOR_SETTINGS.sidebarObjectDisplay,
sidebarTableSearchEnabled: typeof settings.sidebarTableSearchEnabled === "boolean" ? settings.sidebarTableSearchEnabled : DEFAULT_EDITOR_SETTINGS.sidebarTableSearchEnabled,
autoSelectActiveSidebarNode: settings.autoSelectActiveSidebarNode ?? DEFAULT_EDITOR_SETTINGS.autoSelectActiveSidebarNode,
openTabsRestoreMode: normalizeOpenTabsRestoreMode((settings as Partial<EditorSettings>).openTabsRestoreMode, (settings as Partial<EditorSettings> & { restoreOpenTabsOnLaunch?: boolean }).restoreOpenTabsOnLaunch),
disconnectTabHandlingMode: normalizeDisconnectTabHandlingMode((settings as Partial<EditorSettings>).disconnectTabHandlingMode, (settings as Partial<EditorSettings> & { closeQueryTabsOnDisconnect?: boolean }).closeQueryTabsOnDisconnect),
openTabsRestoreMode: normalizeOpenTabsRestoreMode(
(settings as Partial<EditorSettings>).openTabsRestoreMode,
(
settings as Partial<EditorSettings> & {
restoreOpenTabsOnLaunch?: boolean;
}
).restoreOpenTabsOnLaunch,
),
disconnectTabHandlingMode: normalizeDisconnectTabHandlingMode(
(settings as Partial<EditorSettings>).disconnectTabHandlingMode,
(
settings as Partial<EditorSettings> & {
closeQueryTabsOnDisconnect?: boolean;
}
).closeQueryTabsOnDisconnect,
),
reuseDataTab: settings.reuseDataTab ?? DEFAULT_EDITOR_SETTINGS.reuseDataTab,
prefillNewQueryWithSelect: typeof settings.prefillNewQueryWithSelect === "boolean" ? settings.prefillNewQueryWithSelect : DEFAULT_EDITOR_SETTINGS.prefillNewQueryWithSelect,
updateNotificationsEnabled: settings.updateNotificationsEnabled ?? DEFAULT_EDITOR_SETTINGS.updateNotificationsEnabled,
sidebarHiddenTablePrefixes: normalizeSidebarHiddenTablePrefixes(settings.sidebarHiddenTablePrefixes),
sidebarObjectInfoMode: normalizeSidebarObjectInfoMode(
settings.sidebarObjectInfoMode,
(settings as Partial<EditorSettings> & { sidebarTableCommentLayout?: string }).sidebarTableCommentLayout,
(settings as Partial<EditorSettings> & { sidebarHideTableComments?: boolean }).sidebarHideTableComments,
(settings as Partial<EditorSettings> & { sidebarShowDatabaseSizes?: boolean }).sidebarShowDatabaseSizes,
(
settings as Partial<EditorSettings> & {
sidebarTableCommentLayout?: string;
}
).sidebarTableCommentLayout,
(
settings as Partial<EditorSettings> & {
sidebarHideTableComments?: boolean;
}
).sidebarHideTableComments,
(
settings as Partial<EditorSettings> & {
sidebarShowDatabaseSizes?: boolean;
}
).sidebarShowDatabaseSizes,
),
sidebarAllowHorizontalScroll: settings.sidebarAllowHorizontalScroll ?? DEFAULT_EDITOR_SETTINGS.sidebarAllowHorizontalScroll,
columnFormatters: normalizeColumnFormatters(settings.columnFormatters),
@@ -996,7 +1045,9 @@ export const useSettingsStore = defineStore("settings", () => {
const isAiConfigLoaded = ref(false);
const aiConfigs = ref<AiConfigItem[]>([]);
const desktopSettings = ref<DesktopSettings>({ ...DEFAULT_DESKTOP_SETTINGS });
const mcpGlobalPolicy = ref<McpGlobalPolicy>({ ...DEFAULT_MCP_GLOBAL_POLICY });
const mcpGlobalPolicy = ref<McpGlobalPolicy>({
...DEFAULT_MCP_GLOBAL_POLICY,
});
const isDesktopSettingsLoaded = ref(false);
const isMcpGlobalPolicyLoaded = ref(false);
const isEditorSettingsLoaded = ref(false);
@@ -1119,7 +1170,10 @@ export const useSettingsStore = defineStore("settings", () => {
const savedActive = savedSelection?.active;
const savedConfig = savedActive ? aiConfigs.value.find((config) => config.id === savedActive.configId) : undefined;
if (savedConfig && savedActive?.modelId.trim()) {
activeModel.value = { configId: savedConfig.id, modelId: savedActive.modelId.trim() };
activeModel.value = {
configId: savedConfig.id,
modelId: savedActive.modelId.trim(),
};
} else {
const fallback = aiConfigs.value.find((config) => config.isDefault) || aiConfigs.value[0];
activeModel.value = fallback?.model.trim() ? { configId: fallback.id, modelId: fallback.model.trim() } : null;
@@ -1175,7 +1229,10 @@ export const useSettingsStore = defineStore("settings", () => {
await api.saveAiConfigItem(normalized);
aiConfigs.value.push(normalized);
if (aiConfigs.value.length === 1 && normalized.model.trim()) {
activeModel.value = { configId: normalized.id, modelId: normalized.model };
activeModel.value = {
configId: normalized.id,
modelId: normalized.model,
};
persistAiChatSelection();
}
}
@@ -1214,7 +1271,10 @@ export const useSettingsStore = defineStore("settings", () => {
}
function updateActiveModel(model: { configId: string; modelId: string }) {
activeModel.value = { configId: model.configId, modelId: model.modelId.trim() };
activeModel.value = {
configId: model.configId,
modelId: model.modelId.trim(),
};
persistAiChatSelection();
}
@@ -1412,7 +1472,9 @@ export const useSettingsStore = defineStore("settings", () => {
}
function deleteCustomColumnFormatter(id: string) {
const customColumnFormatters = { ...editorSettings.value.customColumnFormatters };
const customColumnFormatters = {
...editorSettings.value.customColumnFormatters,
};
delete customColumnFormatters[id];
const columnFormatters = Object.fromEntries(
Object.entries(editorSettings.value.columnFormatters).filter(([, formatter]) => {