


























@@ -0,0 +1,82 @@
1+import type { Api, Context, Model } from "@mariozechner/pi-ai";
2+import { describe, expect, it } from "vitest";
3+import { transformTransportMessages } from "./transport-message-transform.js";
4+5+function makeModel(api: Api, provider: string, id: string): Model<Api> {
6+return { api, provider, id, input: [], output: [] } as unknown as Model<Api>;
7+}
8+9+function assistantToolCall(
10+id: string,
11+name = "read",
12+): Extract<Context["messages"][number], { role: "assistant" }> {
13+return {
14+role: "assistant",
15+provider: "openai",
16+api: "openai-responses",
17+model: "gpt-5.4",
18+stopReason: "toolUse",
19+timestamp: Date.now(),
20+content: [{ type: "toolCall", id, name, arguments: {} }],
21+} as Extract<Context["messages"][number], { role: "assistant" }>;
22+}
23+24+describe("transformTransportMessages synthetic tool-result policy", () => {
25+it("does not synthesize missing tool results for OpenAI-compatible transports", () => {
26+const messages: Context["messages"] = [
27+assistantToolCall("call_openai_1"),
28+{ role: "user", content: "continue", timestamp: Date.now() },
29+];
30+31+const result = transformTransportMessages(
32+messages,
33+makeModel("openai-responses", "openai", "gpt-5.4"),
34+);
35+36+expect(result.map((msg) => msg.role)).toEqual(["assistant", "user"]);
37+});
38+39+it("still synthesizes missing tool results for Anthropic transports", () => {
40+const messages: Context["messages"] = [
41+assistantToolCall("call_anthropic_1"),
42+{ role: "user", content: "continue", timestamp: Date.now() },
43+];
44+45+const result = transformTransportMessages(
46+messages,
47+makeModel("anthropic-messages", "anthropic", "claude-opus-4-6"),
48+);
49+50+expect(result.map((msg) => msg.role)).toEqual(["assistant", "toolResult", "user"]);
51+expect(result[1]).toMatchObject({
52+role: "toolResult",
53+toolCallId: "call_anthropic_1",
54+isError: true,
55+});
56+});
57+58+it("still synthesizes missing tool results for transport alias apis that own replay repair", () => {
59+const messages: Context["messages"] = [
60+assistantToolCall("call_transport_1"),
61+{ role: "user", content: "continue", timestamp: Date.now() },
62+];
63+64+const anthropicAlias = transformTransportMessages(
65+messages,
66+makeModel("openclaw-anthropic-messages-transport" as Api, "anthropic", "claude-opus-4-6"),
67+);
68+expect(anthropicAlias.map((msg) => msg.role)).toEqual(["assistant", "toolResult", "user"]);
69+70+const googleAlias = transformTransportMessages(
71+messages,
72+makeModel("openclaw-google-generative-ai-transport" as Api, "google", "gemini-2.5-pro"),
73+);
74+expect(googleAlias.map((msg) => msg.role)).toEqual(["assistant", "toolResult", "user"]);
75+76+const bedrockCanonical = transformTransportMessages(
77+messages,
78+makeModel("bedrock-converse-stream" as Api, "bedrock", "anthropic.claude-opus-4-6"),
79+);
80+expect(bedrockCanonical.map((msg) => msg.role)).toEqual(["assistant", "toolResult", "user"]);
81+});
82+});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。