@wrongstack/providers 0.3.3 → 0.3.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +33 -21
- package/dist/index.d.ts +7 -3
- package/dist/index.js +143 -38
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@ Most providers ride a single declarative `WireFormatConfig` adapter; only the th
|
|
|
10
10
|
pnpm add @wrongstack/providers @wrongstack/core
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
`@wrongstack/core`
|
|
13
|
+
`@wrongstack/core` provides the shared `Provider` interface, message types, and tool format.
|
|
14
14
|
|
|
15
15
|
## What's in here
|
|
16
16
|
|
|
@@ -37,13 +37,16 @@ import { AnthropicProvider } from '@wrongstack/providers';
|
|
|
37
37
|
|
|
38
38
|
const provider = new AnthropicProvider({
|
|
39
39
|
apiKey: process.env.ANTHROPIC_API_KEY!,
|
|
40
|
-
modelId: 'claude-sonnet-4-6',
|
|
41
40
|
});
|
|
42
41
|
|
|
43
|
-
const stream = provider.stream(
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
}
|
|
42
|
+
const stream = provider.stream(
|
|
43
|
+
{
|
|
44
|
+
model: 'claude-sonnet-4-6',
|
|
45
|
+
messages: [{ role: 'user', content: 'hello' }],
|
|
46
|
+
maxTokens: 512,
|
|
47
|
+
},
|
|
48
|
+
{ signal: new AbortController().signal },
|
|
49
|
+
);
|
|
47
50
|
|
|
48
51
|
for await (const event of stream) {
|
|
49
52
|
if (event.type === 'text_delta') process.stdout.write(event.text);
|
|
@@ -58,39 +61,48 @@ import { OpenAICompatibleProvider } from '@wrongstack/providers';
|
|
|
58
61
|
const groq = new OpenAICompatibleProvider({
|
|
59
62
|
id: 'groq',
|
|
60
63
|
apiKey: process.env.GROQ_API_KEY!,
|
|
61
|
-
|
|
62
|
-
modelId: 'llama-3.3-70b-versatile',
|
|
64
|
+
baseUrl: 'https://api.groq.com/openai/v1',
|
|
63
65
|
capabilities: { tools: true, vision: false, maxContext: 128_000 },
|
|
64
66
|
});
|
|
67
|
+
|
|
68
|
+
const result = await groq.complete(
|
|
69
|
+
{
|
|
70
|
+
model: 'llama-3.3-70b-versatile',
|
|
71
|
+
messages: [{ role: 'user', content: 'hello' }],
|
|
72
|
+
maxTokens: 512,
|
|
73
|
+
},
|
|
74
|
+
{ signal: new AbortController().signal },
|
|
75
|
+
);
|
|
65
76
|
```
|
|
66
77
|
|
|
67
78
|
## Wire-format adapter (declarative)
|
|
68
79
|
|
|
69
|
-
For a new provider that doesn't fit one of the existing presets, write a `WireFormatConfig` and plug it into `
|
|
80
|
+
For a new provider that doesn't fit one of the existing presets, write a `WireFormatConfig` and plug it into `WireFormatProvider`. See [docs/provider-author-guide.md](../../docs/provider-author-guide.md) for the full spec.
|
|
70
81
|
|
|
71
82
|
```ts
|
|
72
|
-
import {
|
|
73
|
-
import type { WireFormatConfig } from '@wrongstack/core';
|
|
83
|
+
import { WireFormatProvider, type WireFormatConfig } from '@wrongstack/providers';
|
|
74
84
|
|
|
75
85
|
const myWire: WireFormatConfig = {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
86
|
+
id: 'myprovider',
|
|
87
|
+
family: 'openai-compatible',
|
|
88
|
+
capabilities: { tools: true, parallelTools: true, vision: false, streaming: true, promptCache: false, systemPrompt: true, jsonMode: false, maxContext: 32_000, cacheControl: 'none' },
|
|
89
|
+
defaultBaseUrl: 'https://api.myprovider.com/v1',
|
|
90
|
+
buildUrl: (baseUrl) => `${baseUrl.replace(/\/+$/, '')}/chat/completions`,
|
|
91
|
+
buildHeaders: (apiKey) => ({ authorization: `Bearer ${apiKey}` }),
|
|
92
|
+
buildBody: (req) => ({ model: req.model, messages: req.messages, max_tokens: req.maxTokens, stream: true }),
|
|
93
|
+
createStreamState: (fallbackModel) => ({ model: fallbackModel, started: false }),
|
|
94
|
+
parseStreamEvent: () => [],
|
|
95
|
+
finalizeStream: () => [{ type: 'message_stop', stopReason: 'end_turn', usage: { input: 0, output: 0 } }],
|
|
80
96
|
};
|
|
81
97
|
|
|
82
|
-
const provider = new
|
|
83
|
-
id: 'myprovider',
|
|
98
|
+
const provider = new WireFormatProvider(myWire, {
|
|
84
99
|
apiKey: '…',
|
|
85
|
-
modelId: 'my-model-1',
|
|
86
|
-
wire: myWire,
|
|
87
|
-
capabilities: { tools: true, maxContext: 32_000 },
|
|
88
100
|
});
|
|
89
101
|
```
|
|
90
102
|
|
|
91
103
|
## Tool input parsing (`parseToolInput`)
|
|
92
104
|
|
|
93
|
-
|
|
105
|
+
Anthropic/OpenAI-style stream parsers and the aggregate path run tool-call JSON through one canonical helper: [`_tool-input.ts`](src/_tool-input.ts). It guarantees the agent always receives a `Record<string, unknown>` for `tool_use.input`, never a parse-error or `null`. Invalid or non-object inputs are wrapped under `{ __raw: ... }` instead of crashing the provider runner.
|
|
94
106
|
|
|
95
107
|
## Capabilities
|
|
96
108
|
|
package/dist/index.d.ts
CHANGED
|
@@ -320,16 +320,18 @@ interface MistralStreamState {
|
|
|
320
320
|
name?: string;
|
|
321
321
|
partial: string;
|
|
322
322
|
emittedStart: boolean;
|
|
323
|
+
emittedArgLength: number;
|
|
323
324
|
}>;
|
|
324
325
|
}
|
|
325
326
|
declare const mistralWireFormat: WireFormatConfig<MistralStreamState>;
|
|
326
327
|
|
|
327
|
-
type BlockKind = 'text' | 'tool_use' | 'unknown';
|
|
328
|
+
type BlockKind = 'text' | 'tool_use' | 'thinking' | 'unknown';
|
|
328
329
|
interface AnthropicStreamState {
|
|
329
330
|
model: string;
|
|
330
331
|
usage: Usage;
|
|
331
332
|
stopReason: StopReason;
|
|
332
333
|
started: boolean;
|
|
334
|
+
stopped: boolean;
|
|
333
335
|
blocks: Map<number, {
|
|
334
336
|
kind: BlockKind;
|
|
335
337
|
id?: string;
|
|
@@ -347,9 +349,11 @@ interface OpenAIStreamState {
|
|
|
347
349
|
textOpen: boolean;
|
|
348
350
|
thinkingOpen: boolean;
|
|
349
351
|
toolByIndex: Map<number, {
|
|
350
|
-
id
|
|
351
|
-
name
|
|
352
|
+
id?: string;
|
|
353
|
+
name?: string;
|
|
352
354
|
argBuf: string;
|
|
355
|
+
emittedStart: boolean;
|
|
356
|
+
emittedArgLength: number;
|
|
353
357
|
}>;
|
|
354
358
|
finalEmitted: boolean;
|
|
355
359
|
}
|
package/dist/index.js
CHANGED
|
@@ -433,6 +433,18 @@ function toolsToAnthropic(tools) {
|
|
|
433
433
|
}
|
|
434
434
|
}));
|
|
435
435
|
}
|
|
436
|
+
function validateResponse(res) {
|
|
437
|
+
const r = res;
|
|
438
|
+
if (r === void 0 || typeof r.ok !== "boolean" || typeof r.status !== "number") {
|
|
439
|
+
throw new Error("fetchImpl returned invalid response shape \u2014 expected { ok, status, text, body }");
|
|
440
|
+
}
|
|
441
|
+
if (!("body" in r) || r.body === void 0) {
|
|
442
|
+
const proto = Object.getPrototypeOf(r);
|
|
443
|
+
if (proto === Object.prototype || proto === null) {
|
|
444
|
+
r.body = null;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
}
|
|
436
448
|
async function safeText(res) {
|
|
437
449
|
try {
|
|
438
450
|
return await res.text();
|
|
@@ -460,12 +472,14 @@ var WireAdapter = class {
|
|
|
460
472
|
const body = this.buildBody(req);
|
|
461
473
|
let httpRes;
|
|
462
474
|
try {
|
|
463
|
-
|
|
475
|
+
const raw = await this.fetchImpl(url, {
|
|
464
476
|
method: "POST",
|
|
465
477
|
headers,
|
|
466
478
|
body: JSON.stringify(body),
|
|
467
479
|
signal: opts.signal
|
|
468
480
|
});
|
|
481
|
+
validateResponse(raw);
|
|
482
|
+
httpRes = raw;
|
|
469
483
|
} catch (err) {
|
|
470
484
|
if (opts.signal.aborted) throw err;
|
|
471
485
|
throw new ProviderError(err instanceof Error ? err.message : String(err), 0, true, this.id, {
|
|
@@ -554,6 +568,7 @@ async function* parseAnthropicStream(body, fallbackModel) {
|
|
|
554
568
|
let usage = { input: 0, output: 0 };
|
|
555
569
|
let stopReason = "end_turn";
|
|
556
570
|
let started = false;
|
|
571
|
+
let stopped = false;
|
|
557
572
|
for await (const msg of parseSSE(body)) {
|
|
558
573
|
if (!msg.data || msg.data === "[DONE]") continue;
|
|
559
574
|
const parsed = safeParse(msg.data);
|
|
@@ -634,6 +649,7 @@ async function* parseAnthropicStream(body, fallbackModel) {
|
|
|
634
649
|
break;
|
|
635
650
|
}
|
|
636
651
|
case "message_stop":
|
|
652
|
+
stopped = true;
|
|
637
653
|
yield { type: "message_stop", stopReason, usage };
|
|
638
654
|
break;
|
|
639
655
|
case "error": {
|
|
@@ -644,7 +660,7 @@ async function* parseAnthropicStream(body, fallbackModel) {
|
|
|
644
660
|
}
|
|
645
661
|
}
|
|
646
662
|
}
|
|
647
|
-
if (started) {
|
|
663
|
+
if (started && !stopped) {
|
|
648
664
|
yield { type: "message_stop", stopReason, usage };
|
|
649
665
|
}
|
|
650
666
|
}
|
|
@@ -1086,17 +1102,33 @@ async function* parseOpenAIStream(body, fallbackModel) {
|
|
|
1086
1102
|
for (const tc of choice.delta.tool_calls) {
|
|
1087
1103
|
const idx = tc.index ?? 0;
|
|
1088
1104
|
let entry = toolByIndex.get(idx);
|
|
1089
|
-
if (!entry
|
|
1090
|
-
entry = {
|
|
1105
|
+
if (!entry) {
|
|
1106
|
+
entry = {
|
|
1107
|
+
id: tc.id,
|
|
1108
|
+
name: tc.function?.name,
|
|
1109
|
+
argBuf: "",
|
|
1110
|
+
emittedStart: false,
|
|
1111
|
+
emittedArgLength: 0
|
|
1112
|
+
};
|
|
1091
1113
|
toolByIndex.set(idx, entry);
|
|
1092
|
-
|
|
1114
|
+
} else {
|
|
1115
|
+
if (tc.id && !entry.id) entry.id = tc.id;
|
|
1116
|
+
if (tc.function?.name && !entry.name) entry.name = tc.function.name;
|
|
1093
1117
|
}
|
|
1094
|
-
if (
|
|
1118
|
+
if (tc.function?.arguments) {
|
|
1095
1119
|
entry.argBuf += tc.function.arguments;
|
|
1120
|
+
}
|
|
1121
|
+
if (!entry.emittedStart && entry.id && entry.name) {
|
|
1122
|
+
entry.emittedStart = true;
|
|
1123
|
+
yield { type: "tool_use_start", id: entry.id, name: entry.name };
|
|
1124
|
+
}
|
|
1125
|
+
if (entry.emittedStart && entry.id && entry.emittedArgLength < entry.argBuf.length) {
|
|
1126
|
+
const partial = entry.argBuf.slice(entry.emittedArgLength);
|
|
1127
|
+
entry.emittedArgLength = entry.argBuf.length;
|
|
1096
1128
|
yield {
|
|
1097
1129
|
type: "tool_use_input_delta",
|
|
1098
1130
|
id: entry.id,
|
|
1099
|
-
partial
|
|
1131
|
+
partial
|
|
1100
1132
|
};
|
|
1101
1133
|
}
|
|
1102
1134
|
}
|
|
@@ -1106,10 +1138,11 @@ async function* parseOpenAIStream(body, fallbackModel) {
|
|
|
1106
1138
|
}
|
|
1107
1139
|
const u = obj["usage"];
|
|
1108
1140
|
if (u) {
|
|
1109
|
-
const
|
|
1110
|
-
const
|
|
1141
|
+
const hasDeepSeekCacheFields = u.prompt_cache_hit_tokens !== void 0 || u.prompt_cache_miss_tokens !== void 0;
|
|
1142
|
+
const cached = u.prompt_tokens_details?.cached_tokens ?? u.prompt_cache_hit_tokens ?? 0;
|
|
1143
|
+
const promptTotal = u.prompt_tokens ?? (hasDeepSeekCacheFields ? (u.prompt_cache_hit_tokens ?? 0) + (u.prompt_cache_miss_tokens ?? 0) : usage.input + cached);
|
|
1111
1144
|
usage = {
|
|
1112
|
-
input: Math.max(0, promptTotal - cached),
|
|
1145
|
+
input: u.prompt_cache_miss_tokens ?? Math.max(0, promptTotal - cached),
|
|
1113
1146
|
output: u.completion_tokens ?? usage.output,
|
|
1114
1147
|
cacheRead: cached || usage.cacheRead
|
|
1115
1148
|
};
|
|
@@ -1119,6 +1152,10 @@ async function* parseOpenAIStream(body, fallbackModel) {
|
|
|
1119
1152
|
yield { type: "thinking_stop" };
|
|
1120
1153
|
}
|
|
1121
1154
|
for (const entry of toolByIndex.values()) {
|
|
1155
|
+
if (!entry.id || !entry.name) continue;
|
|
1156
|
+
if (!entry.emittedStart) {
|
|
1157
|
+
yield { type: "tool_use_start", id: entry.id, name: entry.name };
|
|
1158
|
+
}
|
|
1122
1159
|
const input = parseToolInput(entry.argBuf);
|
|
1123
1160
|
yield { type: "tool_use_stop", id: entry.id, input };
|
|
1124
1161
|
}
|
|
@@ -1237,16 +1274,31 @@ var mistralWireFormat = defineWireFormat({
|
|
|
1237
1274
|
defaultBaseUrl: "https://api.mistral.ai/v1",
|
|
1238
1275
|
buildUrl: (base) => `${base.replace(/\/+$/, "")}/chat/completions`,
|
|
1239
1276
|
buildHeaders: (apiKey) => ({ authorization: `Bearer ${apiKey}` }),
|
|
1240
|
-
buildBody: (req) =>
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1277
|
+
buildBody: (req) => {
|
|
1278
|
+
const body = {
|
|
1279
|
+
model: req.model,
|
|
1280
|
+
messages: messagesToOpenAI(stripCacheControl(req.system), req.messages, {}),
|
|
1281
|
+
max_tokens: req.maxTokens,
|
|
1282
|
+
stream: true
|
|
1283
|
+
};
|
|
1284
|
+
if (req.tools && req.tools.length > 0) {
|
|
1285
|
+
body["tools"] = toolsToOpenAI(req.tools);
|
|
1286
|
+
if (req.toolChoice) {
|
|
1287
|
+
if (typeof req.toolChoice === "string") {
|
|
1288
|
+
body["tool_choice"] = req.toolChoice === "required" ? "required" : req.toolChoice;
|
|
1289
|
+
} else {
|
|
1290
|
+
body["tool_choice"] = {
|
|
1291
|
+
type: "function",
|
|
1292
|
+
function: { name: req.toolChoice.name }
|
|
1293
|
+
};
|
|
1294
|
+
}
|
|
1295
|
+
}
|
|
1296
|
+
}
|
|
1297
|
+
if (req.temperature !== void 0) body["temperature"] = req.temperature;
|
|
1298
|
+
if (req.topP !== void 0) body["top_p"] = req.topP;
|
|
1299
|
+
if (req.stopSequences) body["stop"] = req.stopSequences;
|
|
1300
|
+
return body;
|
|
1301
|
+
},
|
|
1250
1302
|
createStreamState: (fallbackModel) => ({
|
|
1251
1303
|
model: fallbackModel,
|
|
1252
1304
|
started: false,
|
|
@@ -1270,25 +1322,38 @@ var mistralWireFormat = defineWireFormat({
|
|
|
1270
1322
|
for (const tc of choice?.delta?.tool_calls ?? []) {
|
|
1271
1323
|
let block = state.toolCalls.get(tc.index);
|
|
1272
1324
|
if (!block) {
|
|
1273
|
-
block = {
|
|
1325
|
+
block = {
|
|
1326
|
+
id: tc.id,
|
|
1327
|
+
name: tc.function?.name,
|
|
1328
|
+
partial: "",
|
|
1329
|
+
emittedStart: false,
|
|
1330
|
+
emittedArgLength: 0
|
|
1331
|
+
};
|
|
1274
1332
|
state.toolCalls.set(tc.index, block);
|
|
1275
1333
|
} else {
|
|
1276
1334
|
if (tc.id && !block.id) block.id = tc.id;
|
|
1277
1335
|
if (tc.function?.name && !block.name) block.name = tc.function.name;
|
|
1278
1336
|
}
|
|
1337
|
+
const arg = tc.function?.arguments;
|
|
1338
|
+
if (arg) {
|
|
1339
|
+
block.partial += arg;
|
|
1340
|
+
}
|
|
1279
1341
|
if (!block.emittedStart && block.id && block.name) {
|
|
1280
1342
|
block.emittedStart = true;
|
|
1281
1343
|
out.push({ type: "tool_use_start", id: block.id, name: block.name });
|
|
1282
1344
|
}
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
block.
|
|
1286
|
-
out.push({ type: "tool_use_input_delta", id: block.id, partial
|
|
1345
|
+
if (block.emittedStart && block.id && block.emittedArgLength < block.partial.length) {
|
|
1346
|
+
const partial = block.partial.slice(block.emittedArgLength);
|
|
1347
|
+
block.emittedArgLength = block.partial.length;
|
|
1348
|
+
out.push({ type: "tool_use_input_delta", id: block.id, partial });
|
|
1287
1349
|
}
|
|
1288
1350
|
}
|
|
1289
1351
|
if (choice?.finish_reason) {
|
|
1290
1352
|
for (const block of state.toolCalls.values()) {
|
|
1291
|
-
if (block.id) {
|
|
1353
|
+
if (block.id && block.name) {
|
|
1354
|
+
if (!block.emittedStart) {
|
|
1355
|
+
out.push({ type: "tool_use_start", id: block.id, name: block.name });
|
|
1356
|
+
}
|
|
1292
1357
|
out.push({
|
|
1293
1358
|
type: "tool_use_stop",
|
|
1294
1359
|
id: block.id,
|
|
@@ -1313,13 +1378,21 @@ function mapStopReason(reason) {
|
|
|
1313
1378
|
case "tool_calls":
|
|
1314
1379
|
return "tool_use";
|
|
1315
1380
|
case "length":
|
|
1381
|
+
case "model_length":
|
|
1316
1382
|
return "max_tokens";
|
|
1317
1383
|
case "stop":
|
|
1318
|
-
return "
|
|
1384
|
+
return "end_turn";
|
|
1319
1385
|
default:
|
|
1320
1386
|
return "end_turn";
|
|
1321
1387
|
}
|
|
1322
1388
|
}
|
|
1389
|
+
function stripCacheControl(system) {
|
|
1390
|
+
if (!system) return void 0;
|
|
1391
|
+
return system.map((b) => {
|
|
1392
|
+
const { cache_control: _cc, ...rest } = b;
|
|
1393
|
+
return rest;
|
|
1394
|
+
});
|
|
1395
|
+
}
|
|
1323
1396
|
|
|
1324
1397
|
// src/presets/anthropic.ts
|
|
1325
1398
|
init_tool_input();
|
|
@@ -1362,6 +1435,7 @@ var anthropicWireFormat = defineWireFormat({
|
|
|
1362
1435
|
usage: { input: 0, output: 0 },
|
|
1363
1436
|
stopReason: "end_turn",
|
|
1364
1437
|
started: false,
|
|
1438
|
+
stopped: false,
|
|
1365
1439
|
blocks: /* @__PURE__ */ new Map()
|
|
1366
1440
|
}),
|
|
1367
1441
|
parseStreamEvent: (msg, state) => {
|
|
@@ -1397,6 +1471,9 @@ var anthropicWireFormat = defineWireFormat({
|
|
|
1397
1471
|
}
|
|
1398
1472
|
} else if (cb?.type === "text") {
|
|
1399
1473
|
state.blocks.set(index, { kind: "text", partial: "" });
|
|
1474
|
+
} else if (cb?.type === "thinking" || cb?.type === "redacted_thinking") {
|
|
1475
|
+
state.blocks.set(index, { kind: "thinking", partial: "" });
|
|
1476
|
+
out.push({ type: "thinking_start" });
|
|
1400
1477
|
} else {
|
|
1401
1478
|
state.blocks.set(index, { kind: "unknown", partial: "" });
|
|
1402
1479
|
}
|
|
@@ -1414,6 +1491,10 @@ var anthropicWireFormat = defineWireFormat({
|
|
|
1414
1491
|
block.partial += delta.partial_json;
|
|
1415
1492
|
out.push({ type: "tool_use_input_delta", id: block.id, partial: delta.partial_json });
|
|
1416
1493
|
}
|
|
1494
|
+
} else if (delta.type === "thinking_delta" && typeof delta.thinking === "string") {
|
|
1495
|
+
out.push({ type: "thinking_delta", text: delta.thinking });
|
|
1496
|
+
} else if (delta.type === "signature_delta" && typeof delta.signature === "string") {
|
|
1497
|
+
out.push({ type: "thinking_signature", signature: delta.signature });
|
|
1417
1498
|
}
|
|
1418
1499
|
break;
|
|
1419
1500
|
}
|
|
@@ -1423,6 +1504,8 @@ var anthropicWireFormat = defineWireFormat({
|
|
|
1423
1504
|
if (block?.kind === "tool_use" && block.id) {
|
|
1424
1505
|
const input = parseToolInput(block.partial);
|
|
1425
1506
|
out.push({ type: "tool_use_stop", id: block.id, input });
|
|
1507
|
+
} else if (block?.kind === "thinking") {
|
|
1508
|
+
out.push({ type: "thinking_stop" });
|
|
1426
1509
|
}
|
|
1427
1510
|
break;
|
|
1428
1511
|
}
|
|
@@ -1438,6 +1521,7 @@ var anthropicWireFormat = defineWireFormat({
|
|
|
1438
1521
|
break;
|
|
1439
1522
|
}
|
|
1440
1523
|
case "message_stop":
|
|
1524
|
+
state.stopped = true;
|
|
1441
1525
|
out.push({ type: "message_stop", stopReason: state.stopReason, usage: state.usage });
|
|
1442
1526
|
break;
|
|
1443
1527
|
case "error": {
|
|
@@ -1450,7 +1534,7 @@ var anthropicWireFormat = defineWireFormat({
|
|
|
1450
1534
|
return out;
|
|
1451
1535
|
},
|
|
1452
1536
|
finalizeStream: (state) => {
|
|
1453
|
-
if (state.started) {
|
|
1537
|
+
if (state.started && !state.stopped) {
|
|
1454
1538
|
return [{ type: "message_stop", stopReason: state.stopReason, usage: state.usage }];
|
|
1455
1539
|
}
|
|
1456
1540
|
return [];
|
|
@@ -1474,7 +1558,7 @@ var openaiWireFormat = defineWireFormat({
|
|
|
1474
1558
|
buildBody: (req) => {
|
|
1475
1559
|
const body = {
|
|
1476
1560
|
model: req.model,
|
|
1477
|
-
messages: messagesToOpenAI(
|
|
1561
|
+
messages: messagesToOpenAI(stripCacheControl2(req.system), req.messages, {}),
|
|
1478
1562
|
max_tokens: req.maxTokens,
|
|
1479
1563
|
stream: true,
|
|
1480
1564
|
stream_options: { include_usage: true }
|
|
@@ -1544,18 +1628,34 @@ var openaiWireFormat = defineWireFormat({
|
|
|
1544
1628
|
for (const tc of choice.delta.tool_calls) {
|
|
1545
1629
|
const idx = tc.index ?? 0;
|
|
1546
1630
|
let entry = state.toolByIndex.get(idx);
|
|
1547
|
-
if (!entry
|
|
1548
|
-
entry = {
|
|
1631
|
+
if (!entry) {
|
|
1632
|
+
entry = {
|
|
1633
|
+
id: tc.id,
|
|
1634
|
+
name: tc.function?.name,
|
|
1635
|
+
argBuf: "",
|
|
1636
|
+
emittedStart: false,
|
|
1637
|
+
emittedArgLength: 0
|
|
1638
|
+
};
|
|
1549
1639
|
state.toolByIndex.set(idx, entry);
|
|
1640
|
+
} else {
|
|
1641
|
+
if (tc.id && !entry.id) entry.id = tc.id;
|
|
1642
|
+
if (tc.function?.name && !entry.name) entry.name = tc.function.name;
|
|
1643
|
+
}
|
|
1644
|
+
if (tc.function?.arguments) {
|
|
1645
|
+
entry.argBuf += tc.function.arguments;
|
|
1646
|
+
}
|
|
1647
|
+
if (!entry.emittedStart && entry.id && entry.name) {
|
|
1648
|
+
entry.emittedStart = true;
|
|
1550
1649
|
state.textOpen = false;
|
|
1551
1650
|
out.push({ type: "tool_use_start", id: entry.id, name: entry.name });
|
|
1552
1651
|
}
|
|
1553
|
-
if (entry &&
|
|
1554
|
-
entry.argBuf
|
|
1652
|
+
if (entry.emittedStart && entry.id && entry.emittedArgLength < entry.argBuf.length) {
|
|
1653
|
+
const partial = entry.argBuf.slice(entry.emittedArgLength);
|
|
1654
|
+
entry.emittedArgLength = entry.argBuf.length;
|
|
1555
1655
|
out.push({
|
|
1556
1656
|
type: "tool_use_input_delta",
|
|
1557
1657
|
id: entry.id,
|
|
1558
|
-
partial
|
|
1658
|
+
partial
|
|
1559
1659
|
});
|
|
1560
1660
|
}
|
|
1561
1661
|
}
|
|
@@ -1565,10 +1665,11 @@ var openaiWireFormat = defineWireFormat({
|
|
|
1565
1665
|
}
|
|
1566
1666
|
const u = obj["usage"];
|
|
1567
1667
|
if (u) {
|
|
1568
|
-
const
|
|
1569
|
-
const
|
|
1668
|
+
const hasDeepSeekCacheFields = u.prompt_cache_hit_tokens !== void 0 || u.prompt_cache_miss_tokens !== void 0;
|
|
1669
|
+
const cached = u.prompt_tokens_details?.cached_tokens ?? u.prompt_cache_hit_tokens ?? 0;
|
|
1670
|
+
const promptTotal = u.prompt_tokens ?? (hasDeepSeekCacheFields ? (u.prompt_cache_hit_tokens ?? 0) + (u.prompt_cache_miss_tokens ?? 0) : state.usage.input + cached);
|
|
1570
1671
|
state.usage = {
|
|
1571
|
-
input: Math.max(0, promptTotal - cached),
|
|
1672
|
+
input: u.prompt_cache_miss_tokens ?? Math.max(0, promptTotal - cached),
|
|
1572
1673
|
output: u.completion_tokens ?? state.usage.output,
|
|
1573
1674
|
cacheRead: cached || state.usage.cacheRead
|
|
1574
1675
|
};
|
|
@@ -1584,6 +1685,10 @@ var openaiWireFormat = defineWireFormat({
|
|
|
1584
1685
|
out.push({ type: "thinking_stop" });
|
|
1585
1686
|
}
|
|
1586
1687
|
for (const entry of state.toolByIndex.values()) {
|
|
1688
|
+
if (!entry.id || !entry.name) continue;
|
|
1689
|
+
if (!entry.emittedStart) {
|
|
1690
|
+
out.push({ type: "tool_use_start", id: entry.id, name: entry.name });
|
|
1691
|
+
}
|
|
1587
1692
|
const input = parseToolInput(entry.argBuf);
|
|
1588
1693
|
out.push({ type: "tool_use_stop", id: entry.id, input });
|
|
1589
1694
|
}
|
|
@@ -1593,7 +1698,7 @@ var openaiWireFormat = defineWireFormat({
|
|
|
1593
1698
|
return out;
|
|
1594
1699
|
}
|
|
1595
1700
|
});
|
|
1596
|
-
function
|
|
1701
|
+
function stripCacheControl2(system) {
|
|
1597
1702
|
if (!system) return void 0;
|
|
1598
1703
|
return system.map((b) => {
|
|
1599
1704
|
const { cache_control: _cc, ...rest } = b;
|