


























@@ -560,6 +560,42 @@ describe("convertToOllamaMessages", () => {
560560]);
561561});
562562563+it("normalizes provider-prefixed tool-call names before Ollama replay", () => {
564+const messages = [
565+{
566+role: "assistant",
567+content: [
568+{ type: "toolCall", id: "call_1", name: "functions.exec", arguments: { command: "pwd" } },
569+{ type: "tool_use", id: "call_2", name: "tools/read", input: { path: "README.md" } },
570+],
571+},
572+];
573+const result = convertToOllamaMessages(messages);
574+expect(result[0].tool_calls).toEqual([
575+{ function: { name: "exec", arguments: { command: "pwd" } } },
576+{ function: { name: "read", arguments: { path: "README.md" } } },
577+]);
578+});
579+580+it("keeps non-prefixed Ollama replay tool names intact", () => {
581+const messages = [
582+{
583+role: "assistant",
584+content: [
585+{ type: "toolCall", id: "call_1", name: "functionshell", arguments: {} },
586+{ type: "toolCall", id: "call_2", name: "tooling", arguments: {} },
587+{ type: "toolCall", id: "call_3", name: "tools", arguments: {} },
588+],
589+},
590+];
591+const result = convertToOllamaMessages(messages);
592+expect(result[0].tool_calls).toEqual([
593+{ function: { name: "functionshell", arguments: {} } },
594+{ function: { name: "tooling", arguments: {} } },
595+{ function: { name: "tools", arguments: {} } },
596+]);
597+});
598+563599it("deserializes string arguments back to objects for Ollama (round-trip fix)", () => {
564600// When tool calls round-trip through OpenAI-format storage, arguments
565601// are serialized as a JSON string. Ollama expects an object.
@@ -764,6 +800,54 @@ describe("buildAssistantMessage", () => {
764800expect(toolCall.id).toMatch(/^ollama_call_[0-9a-f-]{36}$/);
765801});
766802803+it("normalizes provider-prefixed tool-call names in Ollama responses", () => {
804+const response = {
805+model: "qwen3:32b",
806+created_at: "2026-01-01T00:00:00Z",
807+message: {
808+role: "assistant" as const,
809+content: "",
810+tool_calls: [
811+{ function: { name: "functions.exec", arguments: { command: "pwd" } } },
812+{ function: { name: "tools/read", arguments: { path: "README.md" } } },
813+],
814+},
815+done: true,
816+};
817+const result = buildAssistantMessage(response, modelInfo);
818+expect(result.content).toEqual([
819+expect.objectContaining({ type: "toolCall", name: "exec", arguments: { command: "pwd" } }),
820+expect.objectContaining({
821+type: "toolCall",
822+name: "read",
823+arguments: { path: "README.md" },
824+}),
825+]);
826+});
827+828+it("keeps non-prefixed Ollama response tool names intact", () => {
829+const response = {
830+model: "qwen3:32b",
831+created_at: "2026-01-01T00:00:00Z",
832+message: {
833+role: "assistant" as const,
834+content: "",
835+tool_calls: [
836+{ function: { name: "functionshell", arguments: {} } },
837+{ function: { name: "tooling", arguments: {} } },
838+{ function: { name: "tools", arguments: {} } },
839+],
840+},
841+done: true,
842+};
843+const result = buildAssistantMessage(response, modelInfo);
844+expect(result.content).toEqual([
845+expect.objectContaining({ type: "toolCall", name: "functionshell", arguments: {} }),
846+expect.objectContaining({ type: "toolCall", name: "tooling", arguments: {} }),
847+expect.objectContaining({ type: "toolCall", name: "tools", arguments: {} }),
848+]);
849+});
850+767851it("parses stringified tool call arguments from Ollama responses", () => {
768852const response = {
769853model: "qwen3:32b",
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。