feat(chat): add localized error message for model API call and improve error handling in chat history (#947)

This commit is contained in:
Haze
2026-04-30 18:46:55 +08:00
committed by GitHub
parent e545fa2194
commit e6c18022a5
7 changed files with 47 additions and 1 deletions

View File

@@ -1,4 +1,7 @@
{
"runError": {
"title": "Model API call error"
},
"gatewayNotRunning": "Gateway Not Running",
"gatewayRequired": "The OpenClaw Gateway needs to be running to use chat. It will start automatically, or you can start it from Settings.",
"welcome": {

View File

@@ -1,4 +1,7 @@
{
"runError": {
"title": "モデルAPI呼び出しエラー"
},
"gatewayNotRunning": "ゲートウェイが停止中",
"gatewayRequired": "チャットを利用するには OpenClaw ゲートウェイが実行されている必要があります。自動的に起動するか、設定から起動できます。",
"welcome": {

View File

@@ -1,4 +1,7 @@
{
"runError": {
"title": "Ошибка вызова API модели"
},
"gatewayNotRunning": "Шлюз не запущен",
"gatewayRequired": "Для использования чата требуется запущенный шлюз OpenClaw. Он запустится автоматически, или вы можете запустить его в Настройках.",
"welcome": {

View File

@@ -1,4 +1,7 @@
{
"runError": {
"title": "模型API调用错误"
},
"gatewayNotRunning": "网关未运行",
"gatewayRequired": "OpenClaw 网关需要运行才能使用聊天。它将自动启动,或者您可以从设置中启动。",
"welcome": {

View File

@@ -805,7 +805,7 @@ export function Chat() {
{/* Run error callout */}
{runError && (
<div className="px-4 pt-2">
<div className="px-4 pt-2" data-testid="chat-run-error">
<div className="max-w-4xl mx-auto rounded-xl border border-destructive/20 bg-destructive/10 px-4 py-3">
<p className="text-sm font-medium text-destructive flex items-center gap-2">
<AlertCircle className="h-4 w-4" />

View File

@@ -416,6 +416,9 @@ test.describe('ClawX chat execution graph', () => {
await expect(page.getByTestId('main-layout')).toBeVisible();
await expect(page.getByText('404 Resource not found')).toBeVisible({ timeout: 30_000 });
const runErrorCallout = page.getByTestId('chat-run-error');
await expect(runErrorCallout).toBeVisible({ timeout: 30_000 });
await expect(runErrorCallout).toContainText('404 Resource not found');
await expect(page.getByTestId('chat-execution-graph')).toHaveCount(0);
await expect(page.getByTestId('chat-execution-step-thinking-trailing')).toHaveCount(0);
await expect(page.getByText('404 Resource not found')).toHaveCount(1);

View File

@@ -308,6 +308,37 @@ describe('chat history actions', () => {
]);
});
it('does not set runError from an older assistant failure when a later turn succeeded', async () => {
const { createHistoryActions } = await import('@/stores/chat/history-actions');
const h = makeHarness({
currentSessionKey: 'agent:main:main',
runError: 'stale',
});
const actions = createHistoryActions(h.set as never, h.get as never);
invokeIpcMock.mockResolvedValueOnce({
success: true,
result: {
messages: [
{ role: 'user', content: 'first', timestamp: 1773281730 },
{
role: 'assistant',
content: 'fail',
timestamp: 1773281731,
stopReason: 'error',
errorMessage: '400 bad model',
},
{ role: 'user', content: 'retry', timestamp: 1773281732 },
{ role: 'assistant', content: 'ok now', timestamp: 1773281733 },
],
},
});
await actions.loadHistory(true);
expect(h.read().runError).toBeNull();
});
it('retries the first foreground startup history load after a timeout and then succeeds', async () => {
vi.useFakeTimers();
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});