Skip to content
Merged
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
4 changes: 2 additions & 2 deletions frontend/src/adk/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1727,9 +1727,9 @@ export function runSseIncompleteResponseError(): string {
return formatRunSseError(adkT("client.noDisplayableSseReply"));
}

const RUN_SSE_FIRST_EVENT_TIMEOUT_MS = 30_000;
const RUN_SSE_FIRST_EVENT_TIMEOUT_MS = 60_000;
export function runSseFirstEventTimeoutError(): string {
return formatRunSseError(adkT("client.firstSseEventTimeout"));
return adkT("client.firstSseEventTimeout");
}

interface RunSseFirstEventDeadline {
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/i18n/resources/en-US/adk.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
"unsupportedRouteHint": "Tip: This Runtime does not provide the session run API and may be incompatible with the current Studio version.",
"toolArgumentHint": "Tip: The model generated incomplete tool arguments. Send the request again.",
"resourceCollectionExpiredHint": "Tip: This resource collection has expired. Send the task again so the system can collect the resources before creating the Agent.",
"networkConfigurationHint": "Tip: Check network settings such as the shared public egress, then try again.",
"networkConfigurationHint": "Tip: Try again later, and review the Runtime, model, or gateway logs.",
"modelQuotaHint": "Tip: The model has reached its TPM/RPM quota. Try again later or increase the model quota.",
"rawResponseLabel": "Raw response: "
},
Expand Down Expand Up @@ -405,7 +405,7 @@
"agentSearchFailed": "Agent search failed",
"emptySseBody": "HTTP 200 with an empty SSE response body.",
"noDisplayableSseReply": "HTTP 200, but the SSE response did not contain a displayable model reply.",
"firstSseEventTimeout": "No SSE event was received within 30 seconds.",
"firstSseEventTimeout": "No SSE event was received within 60 seconds. Try again later, and review the Runtime, model, or gateway logs.",
"runSessionFailed": "Session run failed",
"runSseFailedWithDetail": "run_sse failed ({{status}}): {{detail}}",
"checkRuntimeNameFailed": "Failed to check the Runtime name",
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/i18n/resources/zh-CN/adk.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
"unsupportedRouteHint": "提示:该 Runtime 未提供会话能力运行接口,可能是 Runtime 版本与当前 Studio 不兼容。",
"toolArgumentHint": "提示:模型生成的工具参数格式不完整,请重新发送一次。",
"resourceCollectionExpiredHint": "提示:本次资源清单已失效,请重新发送任务;系统会重新收集资源后再创建 Agent。",
"networkConfigurationHint": "提示:请检查共享公网出口等网络配置,然后重试。",
"networkConfigurationHint": "提示:请稍后重试,查看 Runtime、模型或网关日志。",
"modelQuotaHint": "提示:模型当前触发了 TPM/RPM 配额限制,请稍后重试或提高模型配额。",
"rawResponseLabel": "原始响应:"
},
Expand Down Expand Up @@ -405,7 +405,7 @@
"agentSearchFailed": "Agent 检索失败",
"emptySseBody": "HTTP 200,SSE 响应体为空。",
"noDisplayableSseReply": "HTTP 200,SSE 响应中没有可展示的模型回复。",
"firstSseEventTimeout": "30 秒内未收到首个 SSE 事件。",
"firstSseEventTimeout": "60 秒内未收到首个 SSE 事件。请稍后重试,或查看 Runtime、模型、网关日志定位原因。",
"runSessionFailed": "运行会话失败",
"runSseFailedWithDetail": "运行会话失败:{{status}}:{{detail}}",
"checkRuntimeNameFailed": "检查 Runtime 名称失败",
Expand Down
10 changes: 8 additions & 2 deletions frontend/tests/adkI18n.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@ test("ADK client messages and headers follow the active locale", async (t) => {
assert.equal(sandboxStatusLabel("ready"), "就绪");
assert.equal(cloudRegionOptions("volcengine")[0].label, "华北 2(北京)");
assert.equal(withLocaleHeaders().get("Accept-Language"), "zh-CN");
assert.match(runSseFirstEventTimeoutError(), /30 秒内未收到首个 SSE 事件/);
assert.equal(
runSseFirstEventTimeoutError(),
"60 秒内未收到首个 SSE 事件。请稍后重试,或查看 Runtime、模型、网关日志定位原因。",
);
assert.equal(
localizeDeployStageMessage({
phase: "build",
Expand All @@ -38,7 +41,10 @@ test("ADK client messages and headers follow the active locale", async (t) => {
assert.equal(sandboxStatusLabel("ready"), "Ready");
assert.equal(cloudRegionOptions("volcengine")[0].label, "China North 2 (Beijing)");
assert.equal(withLocaleHeaders().get("Accept-Language"), "en-US");
assert.match(runSseFirstEventTimeoutError(), /No SSE event was received within 30 seconds/);
assert.equal(
runSseFirstEventTimeoutError(),
"No SSE event was received within 60 seconds. Try again later, and review the Runtime, model, or gateway logs.",
);
assert.equal(
localizeDeployStageMessage({
phase: "build",
Expand Down
18 changes: 9 additions & 9 deletions frontend/tests/runSseAbort.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -135,13 +135,13 @@ test("runSSE aborts when no first event arrives before the deadline", async (t)
});

const next = events.next();
assert.equal(timeoutMs, 30_000);
assert.equal(timeoutMs, 60_000);
timeoutCallback();

await assert.rejects(next, (error) => {
assert.equal(error.message, runSseFirstEventTimeoutError());
assert.match(error.message, /No SSE event was received within 30 seconds/);
assert.match(error.message, /Check network settings such as the shared public egress, then try again/);
assert.match(error.message, /No SSE event was received within 60 seconds/);
assert.match(error.message, /review the Runtime, model, or gateway logs/);
return true;
});
});
Expand All @@ -160,7 +160,7 @@ test("runSSE clears the first-event deadline after yielding the first event", as
let timeoutCallback;
let clearedTimer;
globalThis.setTimeout = (callback, ms, ...args) => {
assert.equal(ms, 30_000);
assert.equal(ms, 60_000);
timeoutCallback = () => callback(...args);
return 7;
};
Expand Down Expand Up @@ -214,7 +214,7 @@ test("runSSE formats a fetch rejection before any response arrives", async (t) =

await assert.rejects(events.next(), (error) => {
assert.match(error.message, /^Raw response: TypeError: fetch failed: upstream unavailable/);
assert.match(error.message, /Check network settings such as the shared public egress, then try again/);
assert.match(error.message, /review the Runtime, model, or gateway logs/);
return true;
});
});
Expand All @@ -240,7 +240,7 @@ test("runSSE preserves an AbortError rejected by fetch", async (t) => {
await assert.rejects(events.next(), (error) => {
assert.equal(error, abortError);
assert.equal(error.name, "AbortError");
assert.doesNotMatch(error.message, /Raw response|shared public egress/);
assert.doesNotMatch(error.message, /Raw response|Runtime, model, or gateway logs/);
return true;
});
});
Expand All @@ -265,7 +265,7 @@ test("runSSE rejects an HTTP 200 response that contains no valid events", async

await assert.rejects(
events.next(),
/Raw response: HTTP 200 with an empty SSE response body\.[\s\S]*Check network settings such as the shared public egress, then try again/,
/Raw response: HTTP 200 with an empty SSE response body\.[\s\S]*review the Runtime, model, or gateway logs/,
);
});

Expand All @@ -290,7 +290,7 @@ test("runSSE formats malformed SSE JSON without losing the original data", async
await assert.rejects(events.next(), (error) => {
assert.match(error.message, /^Raw response: Error: Failed to parse the SSE event JSON/);
assert.match(error.message, /Raw data: malformed-json/);
assert.match(error.message, /Check network settings such as the shared public egress, then try again/);
assert.match(error.message, /review the Runtime, model, or gateway logs/);
return true;
});
});
Expand Down Expand Up @@ -335,7 +335,7 @@ test("runSSE preserves a partial event and reports an unexpected stream failure"
assert.equal(first.value.content.parts[0].text, "part");
await assert.rejects(events.next(), (error) => {
assert.match(error.message, /^Raw response: TypeError: terminated/);
assert.match(error.message, /Check network settings such as the shared public egress, then try again/);
assert.match(error.message, /review the Runtime, model, or gateway logs/);
assert.doesNotMatch(error.message, /Runtime may|Unable to access the model service/);
return true;
});
Expand Down
17 changes: 9 additions & 8 deletions frontend/tests/runSseError.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ const result = await build({
const moduleUrl = `data:text/javascript;base64,${Buffer.from(result.outputFiles[0].contents).toString("base64")}`;
const { formatRunSseError } = await import(moduleUrl);

const NETWORK_HINT = "提示:请检查共享公网出口等网络配置,然后重试。";
const GENERIC_RECOVERY_HINT =
"提示:请稍后重试,查看 Runtime、模型或网关日志。";
const MODEL_QUOTA_HINT =
"提示:模型当前触发了 TPM/RPM 配额限制,请稍后重试或提高模型配额。";

Expand All @@ -29,7 +30,7 @@ test("adds memory guidance only when the response says the session is missing",
assert.match(formatted, /in-memory/);
assert.match(formatted, /进程重启/);
assert.match(formatted, /基于数据库的持久化短期记忆/);
assert.ok(formatted.endsWith(NETWORK_HINT));
assert.ok(formatted.endsWith(GENERIC_RECOVERY_HINT));
});

test("identifies an unsupported harness route without blaming memory", () => {
Expand All @@ -43,15 +44,15 @@ test("does not guess at the cause of an unexplained 404", () => {
const error = "run_sse failed: 404:upstream error";
const formatted = formatRunSseError(error);
assert.ok(formatted.startsWith(`原始响应:${error}`));
assert.ok(formatted.endsWith(NETWORK_HINT));
assert.ok(formatted.endsWith(GENERIC_RECOVERY_HINT));
assert.doesNotMatch(formatted, /会话已不存在|Runtime 未提供/);
});

test("preserves unrelated errors before appending network guidance", () => {
test("preserves unrelated errors before appending generic recovery guidance", () => {
for (const error of ["run_sse failed: 500", "create_session failed: 404"]) {
const formatted = formatRunSseError(error);
assert.ok(formatted.startsWith(`原始响应:${error}`));
assert.ok(formatted.endsWith(NETWORK_HINT));
assert.ok(formatted.endsWith(GENERIC_RECOVERY_HINT));
}
});

Expand All @@ -67,7 +68,7 @@ test("preserves malformed tool argument details and adds an actionable message",
const formatted = formatRunSseError(error);
assert.ok(formatted.startsWith(`原始响应:${error}`));
assert.match(formatted, /模型生成的工具参数格式不完整/);
assert.ok(formatted.endsWith(NETWORK_HINT));
assert.ok(formatted.endsWith(GENERIC_RECOVERY_HINT));
});

test("explains an expired resource collection without blaming public egress", () => {
Expand All @@ -84,7 +85,7 @@ test("does not claim that a model error was caused by public egress", () => {
const error = "ModelInvocationError: upstream model returned 503";
const formatted = formatRunSseError(error);
assert.ok(formatted.startsWith(`原始响应:${error}`));
assert.ok(formatted.endsWith(NETWORK_HINT));
assert.ok(formatted.endsWith(GENERIC_RECOVERY_HINT));
assert.doesNotMatch(formatted, /Runtime 可能|无法访问模型服务|导致/);
});

Expand All @@ -107,5 +108,5 @@ test("does not duplicate an original-response label provided by HTTP handling",
const formatted = formatRunSseError(error);
assert.equal(formatted.match(/原始响应:/g)?.length, 1);
assert.ok(formatted.startsWith("run_sse failed: 502"));
assert.ok(formatted.endsWith(NETWORK_HINT));
assert.ok(formatted.endsWith(GENERIC_RECOVERY_HINT));
});

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading