Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 9 additions & 10 deletions src/main/claude/agent-runner-message-end.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import type {
ToolCall,
} from '@mariozechner/pi-ai';
import { splitThinkTagBlocks } from './think-tag-parser';
import { mt } from '../i18n';

type MessageEndContentBlock = TextContent | ThinkingContent | ToolCall;

Expand Down Expand Up @@ -43,31 +44,31 @@ export type AbortDisposition = 'timeout' | 'loop_guard' | 'stream_error' | 'user
export function toUserFacingErrorText(errorText: string): string {
const lower = errorText.toLowerCase();
if (lower.includes('first_response_timeout')) {
return '模型响应超时:长时间未收到上游返回,请稍后重试或检查当前模型/网关负载。';
return mt('errModelTimeout');
}
if (lower.includes('empty_success_result')) {
return '模型返回了一个空的成功结果,当前模型或网关兼容性可能有问题,请重试或切换协议后再试。';
return mt('errEmptySuccess');
}
if (
/\b400\b/.test(errorText) ||
lower.includes('bad request') ||
lower.includes('invalid request')
) {
return `请求被上游拒绝(400),可能是模型/协议配置不兼容。请检查模型名称、协议设置和 API 端点。\n原始错误: ${errorText}`;
return mt('errBadRequest', { error: errorText });
}
if (
/\b(401|403)\b/.test(errorText) ||
lower.includes('unauthorized') ||
lower.includes('forbidden')
) {
return `认证失败,请检查 API Key 是否正确、是否已过期或无权访问当前模型。\n原始错误: ${errorText}`;
return mt('errAuthFailed', { error: errorText });
}
if (
/\b429\b/.test(errorText) ||
lower.includes('rate limit') ||
lower.includes('too many requests')
) {
return `请求被限流(429),当前模型或 API 端点的调用频率已达上限,请稍后重试。\n原始错误: ${errorText}`;
return mt('errRateLimited', { error: errorText });
}
if (
/\b(5\d{2})\b/.test(errorText) ||
Expand All @@ -76,7 +77,7 @@ export function toUserFacingErrorText(errorText: string): string {
lower.includes('service unavailable') ||
lower.includes('overloaded')
) {
return `上游服务异常,可能是模型服务过载或临时故障,SDK 将自动重试。\n原始错误: ${errorText}`;
return mt('errUpstreamError', { error: errorText });
}
if (
lower.includes('terminated') ||
Expand All @@ -90,7 +91,7 @@ export function toUserFacingErrorText(errorText: string): string {
lower.includes('upstream connect') ||
lower.includes('retry delay')
) {
return `网络连接中断(${errorText}),可能是代理/网关不稳定,SDK 将自动重试。`;
return mt('errNetworkInterrupted', { error: errorText });
}
return errorText;
}
Expand All @@ -104,9 +105,7 @@ export function resolveAssistantStreamErrorText(

export function buildTerminalErrorMessage(errorText: string, partialText = ''): string {
const normalizedPartial = partialText.trimEnd();
const hint = FOUR_XX_ERROR_RE.test(errorText)
? '_请检查配置后重试。_'
: '_Agent 正在自动重试,请稍候..._';
const hint = FOUR_XX_ERROR_RE.test(errorText) ? mt('errCheckConfigHint') : mt('errRetryingHint');
const errorBlock = `**Error**: ${errorText}\n\n${hint}`;
return normalizedPartial ? `${normalizedPartial}\n\n${errorBlock}` : errorBlock;
}
Expand Down
20 changes: 18 additions & 2 deletions src/main/config/config-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import {
shouldUseAnthropicAuthToken,
} from './auth-utils';
import { API_PROVIDER_PRESETS, PI_AI_CURATED_PRESETS } from '../../shared/api-model-presets';
import { mt, setBackendLanguage, DEFAULT_BACKEND_LANGUAGE } from '../i18n';

/**
* Application configuration schema
Expand Down Expand Up @@ -112,6 +113,10 @@ export interface AppConfig {
// UI theme preference
theme: AppTheme;

// UI language. Mirrors the renderer's active react-i18next language and drives
// backend (main-process) strings via setBackendLanguage(). Defaults to Chinese.
uiLanguage?: string;

// Sandbox mode (WSL/Lima isolation)
sandboxEnabled: boolean;

Expand Down Expand Up @@ -218,7 +223,7 @@ const defaultProfiles: Record<ProviderProfileKey, ProviderProfile> = {

const defaultConfigSet: ApiConfigSet = {
id: DEFAULT_CONFIG_SET_ID,
name: '默认方案',
name: mt('configDefaultSetName'),
isSystem: true,
provider: 'openrouter',
customProtocol: 'anthropic',
Expand All @@ -243,6 +248,7 @@ const defaultConfig: AppConfig = {
globalSkillsPath: '',
enableDevLogs: false,
theme: 'light',
uiLanguage: DEFAULT_BACKEND_LANGUAGE,
sandboxEnabled: false,
memoryEnabled: true,
memoryRuntime: {
Expand Down Expand Up @@ -539,6 +545,9 @@ export class ConfigStore {
warn: logWarn,
}) as unknown as Store<AppConfig>;
this.ensureNormalized();
// Seed the backend translator from the persisted UI language so main-process
// strings (errors, dialogs) match the user's last choice from first launch.
setBackendLanguage((this.store.store as AppConfig).uiLanguage);
}

private ensureNormalized(): void {
Expand Down Expand Up @@ -858,7 +867,7 @@ export class ConfigStore {

const normalizedSet = this.normalizeConfigSet(rawSet, {
id: nextId,
name: toNonEmptyString(rawSet.name) || `方案 ${index + 1}`,
name: toNonEmptyString(rawSet.name) || mt('configFallbackSetName', { index: index + 1 }),
provider: legacy.provider,
customProtocol: legacy.customProtocol,
activeProfileKey: legacy.activeProfileKey,
Expand Down Expand Up @@ -974,6 +983,10 @@ export class ConfigStore {
: defaultConfig.globalSkillsPath,
enableDevLogs: toBoolean(raw.enableDevLogs, defaultConfig.enableDevLogs),
theme: isAppTheme(raw.theme) ? raw.theme : defaultConfig.theme,
uiLanguage:
typeof raw.uiLanguage === 'string' && raw.uiLanguage.trim()
? raw.uiLanguage
: defaultConfig.uiLanguage,
sandboxEnabled: toBoolean(raw.sandboxEnabled, defaultConfig.sandboxEnabled),
memoryEnabled: toBoolean(raw.memoryEnabled, defaultConfig.memoryEnabled),
memoryRuntime: normalizeMemoryRuntimeConfig(raw.memoryRuntime),
Expand All @@ -995,6 +1008,8 @@ export class ConfigStore {
private saveConfig(config: AppConfig): void {
const normalized = this.normalizeConfig(config);
this.store.set(normalized);
// Keep the backend translator in sync when the UI language changes.
setBackendLanguage(normalized.uiLanguage);
}

private composeProjectedConfig(
Expand Down Expand Up @@ -1388,6 +1403,7 @@ export class ConfigStore {
enableDevLogs:
updates.enableDevLogs !== undefined ? updates.enableDevLogs : current.enableDevLogs,
theme: updates.theme !== undefined ? updates.theme : current.theme,
uiLanguage: updates.uiLanguage !== undefined ? updates.uiLanguage : current.uiLanguage,
sandboxEnabled:
updates.sandboxEnabled !== undefined ? updates.sandboxEnabled : current.sandboxEnabled,
memoryEnabled:
Expand Down
Loading
Loading