
























@@ -6984,7 +6984,134 @@ describe("openai transport stream", () => {
69846984});
69856985});
698669866987-it("strips tool call blocks when provider signals finish_reason stop", async () => {
6987+it("promotes silent tool calls when provider signals finish_reason stop", async () => {
6988+const model = {
6989+id: "qwen3.6-27b",
6990+name: "Qwen 3.6 27B",
6991+api: "openai-completions",
6992+provider: "vllm",
6993+baseUrl: "http://localhost:8000/v1",
6994+reasoning: false,
6995+input: ["text"],
6996+cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
6997+contextWindow: 131072,
6998+maxTokens: 8192,
6999+} satisfies Model<"openai-completions">;
7000+7001+const output = createAssistantOutput(model);
7002+const stream = { push: () => {} };
7003+7004+const mockChunks = [
7005+{
7006+id: "chatcmpl-test",
7007+object: "chat.completion.chunk" as const,
7008+created: 1775425651,
7009+model: "qwen3.6-27b",
7010+choices: [
7011+{
7012+index: 0,
7013+delta: { role: "assistant" as const, content: "" },
7014+logprobs: null,
7015+finish_reason: null,
7016+},
7017+],
7018+},
7019+{
7020+id: "chatcmpl-test",
7021+object: "chat.completion.chunk" as const,
7022+created: 1775425651,
7023+model: "qwen3.6-27b",
7024+choices: [
7025+{
7026+index: 0,
7027+delta: {
7028+tool_calls: [
7029+{
7030+index: 0,
7031+id: "call_legit",
7032+function: { name: "bash", arguments: '{"cmd":"echo hi"}' },
7033+},
7034+],
7035+},
7036+logprobs: null,
7037+finish_reason: "stop",
7038+},
7039+],
7040+},
7041+] as const;
7042+7043+async function* mockStream() {
7044+for (const chunk of mockChunks) {
7045+yield chunk as never;
7046+}
7047+}
7048+7049+await testing.processOpenAICompletionsStream(mockStream(), output, model, stream);
7050+7051+expect(output.stopReason).toBe("toolUse");
7052+const toolCalls = output.content.filter(
7053+(block) => (block as { type?: string }).type === "toolCall",
7054+);
7055+expect(toolCalls).toHaveLength(1);
7056+});
7057+7058+it("does not promote tool calls when provider omits final finish_reason", async () => {
7059+const model = {
7060+id: "qwen3.6-27b",
7061+name: "Qwen 3.6 27B",
7062+api: "openai-completions",
7063+provider: "vllm",
7064+baseUrl: "http://localhost:8000/v1",
7065+reasoning: false,
7066+input: ["text"],
7067+cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
7068+contextWindow: 131072,
7069+maxTokens: 8192,
7070+} satisfies Model<"openai-completions">;
7071+7072+const output = createAssistantOutput(model);
7073+const stream = { push: () => {} };
7074+7075+const mockChunks = [
7076+{
7077+id: "chatcmpl-test",
7078+object: "chat.completion.chunk" as const,
7079+created: 1775425651,
7080+model: "qwen3.6-27b",
7081+choices: [
7082+{
7083+index: 0,
7084+delta: {
7085+tool_calls: [
7086+{
7087+index: 0,
7088+id: "call_unfinished",
7089+function: { name: "bash", arguments: '{"cmd":"echo hi"}' },
7090+},
7091+],
7092+},
7093+logprobs: null,
7094+finish_reason: null,
7095+},
7096+],
7097+},
7098+] as const;
7099+7100+async function* mockStream() {
7101+for (const chunk of mockChunks) {
7102+yield chunk as never;
7103+}
7104+}
7105+7106+await testing.processOpenAICompletionsStream(mockStream(), output, model, stream);
7107+7108+expect(output.stopReason).toBe("stop");
7109+expect(
7110+output.content.filter((block) => (block as { type?: string }).type === "toolCall"),
7111+).toStrictEqual([]);
7112+});
7113+7114+it("strips tool call blocks when provider signals finish_reason stop after visible text", async () => {
69887115const model = {
69897116id: "llama-3.3-70b",
69907117name: "Llama 3.3 70B",
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。