




















@@ -7,6 +7,7 @@ const FALLBACK_TEXT = "[assistant turn failed before producing content]";
77function bedrockAssistant(
88content: unknown,
99stopReason: "error" | "stop" | "toolUse" | "length" = "error",
10+usageOverrides: Record<string, number> = {},
1011): AgentMessage {
1112return {
1213role: "assistant",
@@ -21,6 +22,7 @@ function bedrockAssistant(
2122cacheWrite: 0,
2223totalTokens: 0,
2324cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
25+ ...usageOverrides,
2426},
2527 stopReason,
2628timestamp: 0,
@@ -60,19 +62,28 @@ describe("normalizeAssistantReplayContent", () => {
6062expect(repaired.content).toEqual([{ type: "text", text: FALLBACK_TEXT }]);
6163});
626463-it("preserves silent-reply turns (stopReason=stop, content=[]) untouched", () => {
65+it("preserves nonzero-usage silent-reply turns (stopReason=stop, content=[]) untouched", () => {
6466// run.empty-error-retry.test.ts treats `stopReason:"stop"` + `content:[]`
6567// as a legitimate NO_REPLY / silent-reply, NOT a crash. Substituting the
6668// failure sentinel here would inject a fabricated "[assistant turn failed
6769// before producing content]" into the next provider request and change
6870// model behavior even though no failure occurred.
69-const silentStop = bedrockAssistant([], "stop");
71+const silentStop = bedrockAssistant([], "stop", { input: 100, totalTokens: 100 });
7072const messages = [userMessage("hello"), silentStop];
7173const out = normalizeAssistantReplayContent(messages);
7274expect(out).toBe(messages);
7375expect(out[1]).toBe(silentStop);
7476});
757778+it("converts zero-usage empty stop turns to a replay sentinel", () => {
79+const falseSuccessStop = bedrockAssistant([], "stop");
80+const messages = [userMessage("hello"), falseSuccessStop];
81+const out = normalizeAssistantReplayContent(messages);
82+expect(out).not.toBe(messages);
83+const repaired = out[1] as AgentMessage & { content: { type: string; text: string }[] };
84+expect(repaired.content).toEqual([{ type: "text", text: FALLBACK_TEXT }]);
85+});
86+7687it("preserves empty content with non-error stopReasons (toolUse, length) untouched", () => {
7788// Boundary lock: only `stopReason:"error"` should trip the sentinel
7889// substitution. `toolUse` and `length` are reachable in practice when a
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。