This commit is contained in:
吴杨帆 2026-05-31 01:23:28 -04:00 committed by GitHub
commit 593f79ca2c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 65 additions and 9 deletions

View file

@ -12,6 +12,7 @@ import type {
import { Icon } from './Icon';
import { navigate } from '../router';
import { useT } from '../i18n';
import { localizeRunFailureReason } from '../i18n/runErrors';
import type { Dict } from '../i18n/types';
import { useAnalytics } from '../analytics/provider';
import { trackAutomationsClick } from '../analytics/events';
@ -174,14 +175,16 @@ function formatRunTimestamp(ts: number): string {
});
}
function runFailureReason(run: {
function runFailureReason(
run: {
status: RoutineRun['status'];
error?: string | null;
summary?: string | null;
} | null | undefined): string | null {
} | null | undefined,
t: TranslateFn,
): string | null {
if (!run || run.status !== 'failed') return null;
const reason = (run.error || run.summary || '').trim();
return reason || null;
return localizeRunFailureReason(run.error || run.summary || '', t);
}
type FormState = {
@ -414,7 +417,7 @@ function RunHistory({
return (
<ul className="routines-history">
{runs.map((r) => {
const failureReason = runFailureReason(r);
const failureReason = runFailureReason(r, t);
return (
<li key={r.id} className="routines-history-row">
<StatusPill status={r.status} t={t} />
@ -763,7 +766,7 @@ export function RoutinesSection({ onClose }: RoutinesSectionProps) {
: t('routines.targetCreate');
const isBusy = busyId === r.id;
const isExpanded = expandedId === r.id;
const failureReason = runFailureReason(r.lastRun);
const failureReason = runFailureReason(r.lastRun, t);
return (
<li key={r.id} className={`routines-card routines-item${r.enabled ? '' : ' is-disabled'}`}>
<div className="routines-item-head">

View file

@ -967,6 +967,8 @@ export const en: Dict = {
'Delete this automation? Past runs and their projects are kept.',
'routines.errorPickProject':
'Pick a project to reuse, or switch to “Create new each run”',
'routines.errorAgentEmptyOutput':
'Agent completed without producing any output. The model or provider may have returned an empty response — check the agent logs for upstream errors.',
'entry.helpAria': 'Help',
'entry.helpMenuAria': 'Help menu',
'entry.helpGetHelp': 'Get help on GitHub',

View file

@ -964,6 +964,8 @@ export const zhCN: Dict = {
'routines.status.canceled': '已取消',
'routines.confirmDelete': '删除此自动化?过往运行记录及其项目将予以保留。',
'routines.errorPickProject': '请选择要复用的项目,或切换为“每次运行新建项目”。',
'routines.errorAgentEmptyOutput':
'代理已完成运行但未产生任何输出。模型或提供商可能返回了空响应,请检查代理日志中的上游错误。',
'entry.helpAria': '帮助',
'entry.helpMenuAria': '帮助菜单',
'entry.helpGetHelp': '在 GitHub 上获取帮助',

View file

@ -698,6 +698,8 @@ export const zhTW: Dict = {
'routines.status.canceled': '已取消',
'routines.confirmDelete': '刪除此自動化?過往執行記錄及其專案將予以保留。',
'routines.errorPickProject': '請選擇要重複使用的專案,或切換為「每次執行建立新專案」。',
'routines.errorAgentEmptyOutput':
'代理已完成執行但未產生任何輸出。模型或提供商可能返回了空回應,請檢查代理日誌中的上游錯誤。',
'newproj.tabPrototype': '原型',
'newproj.tabLiveArtifact': '即時成品',

View file

@ -0,0 +1,20 @@
import type { Dict } from './types';
type TranslateFn = (key: keyof Dict, vars?: Record<string, string | number>) => string;
const AGENT_EMPTY_OUTPUT_EN =
'Agent completed without producing any output. The model or provider may have returned an empty response — check the agent logs for upstream errors.';
const KNOWN_RUN_FAILURE_KEYS: Record<string, keyof Dict> = {
[AGENT_EMPTY_OUTPUT_EN]: 'routines.errorAgentEmptyOutput',
};
export function localizeRunFailureReason(
reason: string | null | undefined,
t: TranslateFn,
): string | null {
if (!reason?.trim()) return null;
const trimmed = reason.trim();
const key = KNOWN_RUN_FAILURE_KEYS[trimmed];
return key ? t(key) : trimmed;
}

View file

@ -1279,6 +1279,7 @@ export interface Dict {
'routines.status.canceled': string;
'routines.confirmDelete': string;
'routines.errorPickProject': string;
'routines.errorAgentEmptyOutput': string;
// Bottom-of-rail help menu
'entry.helpAria': string;
'entry.helpMenuAria': string;

View file

@ -0,0 +1,26 @@
import { describe, expect, it } from 'vitest';
import { localizeRunFailureReason } from '../../src/i18n/runErrors';
import type { Dict } from '../../src/i18n/types';
const t = ((key: keyof Dict) => {
if (key === 'routines.errorAgentEmptyOutput') {
return '代理已完成运行但未产生任何输出。';
}
return key;
}) as (key: keyof Dict) => string;
describe('localizeRunFailureReason', () => {
it('maps known daemon empty-output errors to i18n', () => {
const reason =
'Agent completed without producing any output. The model or provider may have returned an empty response — check the agent logs for upstream errors.';
expect(localizeRunFailureReason(reason, t)).toBe(
'代理已完成运行但未产生任何输出。',
);
});
it('passes through unknown errors unchanged', () => {
expect(localizeRunFailureReason('Network timeout', t)).toBe('Network timeout');
});
});