TypeScript 97.4%
SQL 1%
JavaScript 0.9%
CSS 0.6%
1// (d) streamed function-call round trip in thinking and non-thinking mode; reasoning_content replay rules; tool_choice variants.2import { raw, rawSSE, MODELS, save } from "./lib.ts";34const tools = [{ type: "function", function: { name: "get_weather", description: "Get the current weather for a city", parameters: { type: "object", properties: { city: { type: "string", description: "City name" } }, required: ["city"] } } }];5const out: Record<string, unknown> = {};67async function roundTrip(model: string, thinking: "enabled" | "disabled", replayReasoning: boolean) {8 const key = `${model}|thinking=${thinking}|replay=${replayReasoning}`;9 const messages: any[] = [{ role: "user", content: "What's the weather in Montreal right now? Use the tool." }];10 const s = await rawSSE("/chat/completions", { model, messages, tools, stream: true, stream_options: { include_usage: true }, max_tokens: 200, thinking: { type: thinking } });11 if (s.status !== 200) { out[key] = { step1: s }; console.log(key, "step1 ERR", s.status, JSON.stringify(s.error)); return; }12 // accumulate13 let reasoning = "", content = "";14 const calls: Record<number, { id?: string; name?: string; args: string; type?: string }> = {};15 const toolChunks: any[] = [];16 let finish: string | null = null; let usage: any = null;17 for (const ev of s.events) {18 if (ev === "[DONE]") continue;19 if (ev.usage) usage = ev.usage;20 const c = ev.choices?.[0]; if (!c) continue;21 if (c.finish_reason) finish = c.finish_reason;22 const d = c.delta ?? {};23 if (d.reasoning_content) reasoning += d.reasoning_content;24 if (d.content) content += d.content;25 if (d.tool_calls) { toolChunks.push(d.tool_calls); for (const tc of d.tool_calls) { const slot = (calls[tc.index] ??= { args: "" }); if (tc.id) slot.id = tc.id; if (tc.type) slot.type = tc.type; if (tc.function?.name) slot.name = tc.function.name; if (tc.function?.arguments) slot.args += tc.function.arguments; } }26 }27 const tcs = Object.values(calls).map((c) => ({ id: c.id, type: c.type ?? "function", function: { name: c.name, arguments: c.args } }));28 const assistant: any = { role: "assistant", content: content || null, tool_calls: tcs };29 if (replayReasoning && reasoning) assistant.reasoning_content = reasoning;30 messages.push(assistant);31 for (const tc of tcs) messages.push({ role: "tool", tool_call_id: tc.id, content: JSON.stringify({ city: "Montreal", temp_c: 21, sky: "sunny" }) });32 const r2 = await raw("/chat/completions", { method: "POST", body: JSON.stringify({ model, messages, tools, max_tokens: 200, thinking: { type: thinking } }) });33 const b2: any = r2.body;34 out[key] = { step1: { status: s.status, finish, usage, reasoning_len: reasoning.length, content, toolCalls: tcs, toolChunkCount: toolChunks.length, toolChunksFirst3: toolChunks.slice(0, 3), toolChunksLast: toolChunks.slice(-1), comments: s.comments }, step2: { status: r2.status, body: r2.status === 200 ? { finish: b2.choices?.[0]?.finish_reason, content: b2.choices?.[0]?.message?.content, reasoning_len: b2.choices?.[0]?.message?.reasoning_content?.length ?? null, usage: b2.usage } : b2 } };35 console.log(key, "step1 finish", finish, "calls", JSON.stringify(tcs), "chunks", toolChunks.length, "reasoning", reasoning.length, "| step2", r2.status, r2.status === 200 ? `"${(b2.choices?.[0]?.message?.content ?? "").slice(0, 80)}"` : JSON.stringify(b2).slice(0, 300));36}3738for (const model of MODELS) {39 await roundTrip(model, "enabled", true);40 await roundTrip(model, "enabled", false); // docs: should 40041 await roundTrip(model, "disabled", false);42}4344// tool_choice variants on flash (non-thinking to keep it cheap)45const m = "deepseek-v4-flash";46for (const [name, tool_choice] of Object.entries({ required: "required", none: "none", specific: { type: "function", function: { name: "get_weather" } }, auto: "auto" })) {47 const r = await raw("/chat/completions", { method: "POST", body: JSON.stringify({ model: m, messages: [{ role: "user", content: "Hi there, how are you?" }], tools, tool_choice, max_tokens: 100, thinking: { type: "disabled" } }) });48 const b: any = r.body;49 out[`tool_choice=${name}`] = { status: r.status, finish: b.choices?.[0]?.finish_reason, tool_calls: b.choices?.[0]?.message?.tool_calls, content: (b.choices?.[0]?.message?.content ?? "").slice(0, 80), error: r.status !== 200 ? b : undefined };50 console.log("tool_choice", name, r.status, b.choices?.[0]?.finish_reason, JSON.stringify(b.choices?.[0]?.message?.tool_calls ?? b).slice(0, 200));51}52// parallel tool calls: two cities in one prompt53const rp = await raw("/chat/completions", { method: "POST", body: JSON.stringify({ model: m, messages: [{ role: "user", content: "Weather in Montreal and in Quebec City? Use the tool for both." }], tools, max_tokens: 200, thinking: { type: "disabled" } }) });54out.parallel = { status: rp.status, tool_calls: (rp.body as any).choices?.[0]?.message?.tool_calls, finish: (rp.body as any).choices?.[0]?.finish_reason };55console.log("parallel", rp.status, JSON.stringify(out.parallel).slice(0, 400));56// strict beta57const rs = await raw("/chat/completions", { method: "POST", base: "https://api.deepseek.com/beta", body: JSON.stringify({ model: m, messages: [{ role: "user", content: "Weather in Montreal? Use the tool." }], tools: [{ type: "function", function: { ...tools[0].function, strict: true, parameters: { ...tools[0].function.parameters, additionalProperties: false } } }], max_tokens: 100, thinking: { type: "disabled" } }) });58out.strict_beta = { status: rs.status, tool_calls: (rs.body as any).choices?.[0]?.message?.tool_calls, error: rs.status !== 200 ? rs.body : undefined };59console.log("strict beta", rs.status, JSON.stringify(out.strict_beta).slice(0, 300));60save("03-tools-stream.json", out);61