
































@@ -4,6 +4,7 @@ import { createAssistantMessageEventStream } from "@mariozechner/pi-ai";
44import { describe, expect, it } from "vitest";
55import {
66createOpenAIAttributionHeadersWrapper,
7+createOpenAICompletionsToolsCompatWrapper,
78createOpenAIThinkingLevelWrapper,
89} from "./openai-stream-wrappers.js";
910@@ -33,6 +34,67 @@ const openaiModel = {
3334id: "gpt-5.2",
3435} as Model<"openai-responses">;
353637+describe("createOpenAICompletionsToolsCompatWrapper", () => {
38+it("strips tools fields when OpenAI-compatible models disable tool support", () => {
39+const payloads: Array<Record<string, unknown>> = [];
40+const baseStreamFn: StreamFn = (model, _context, options) => {
41+const payload: Record<string, unknown> = {
42+model: model.id,
43+tools: [{ type: "function", function: { name: "noop" } }],
44+tool_choice: "auto",
45+parallel_tool_calls: true,
46+};
47+options?.onPayload?.(payload, model);
48+payloads.push(structuredClone(payload));
49+return createAssistantMessageEventStream();
50+};
51+52+const wrapped = createOpenAICompletionsToolsCompatWrapper(baseStreamFn);
53+void wrapped(
54+{
55+api: "openai-completions",
56+provider: "venice",
57+id: "chat-only-model",
58+baseUrl: "https://example.invalid/v1",
59+compat: { supportsTools: false },
60+} as Model<"openai-completions">,
61+{ messages: [] },
62+{},
63+);
64+65+expect(payloads[0]).not.toHaveProperty("tools");
66+expect(payloads[0]).not.toHaveProperty("tool_choice");
67+expect(payloads[0]).not.toHaveProperty("parallel_tool_calls");
68+});
69+70+it("keeps tools fields for OpenAI-compatible models without an explicit opt-out", () => {
71+const payloads: Array<Record<string, unknown>> = [];
72+const baseStreamFn: StreamFn = (model, _context, options) => {
73+const payload: Record<string, unknown> = {
74+model: model.id,
75+tools: [{ type: "function", function: { name: "noop" } }],
76+};
77+options?.onPayload?.(payload, model);
78+payloads.push(structuredClone(payload));
79+return createAssistantMessageEventStream();
80+};
81+82+const wrapped = createOpenAICompletionsToolsCompatWrapper(baseStreamFn);
83+void wrapped(
84+{
85+api: "openai-completions",
86+provider: "venice",
87+id: "tool-capable-model",
88+baseUrl: "https://example.invalid/v1",
89+} as Model<"openai-completions">,
90+{ messages: [] },
91+{},
92+);
93+94+expect(payloads[0]).toHaveProperty("tools");
95+});
96+});
97+3698describe("createOpenAIThinkingLevelWrapper", () => {
3799it("overrides effort on reasoning-capable model when thinkingLevel is medium", () => {
38100const { baseStreamFn, payloads } = createPayloadCapture({
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。