@wrongstack/providers 0.1.4 → 0.1.8

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 ADDED
@@ -0,0 +1,101 @@
1
+ # @wrongstack/providers
2
+
3
+ LLM provider adapters for WrongStack: Anthropic, OpenAI, Google, OpenAI-compatible (Mistral, Groq, DeepSeek, Together, Fireworks, OpenRouter, …).
4
+
5
+ Most providers ride a single declarative `WireFormatConfig` adapter; only the three majors (Anthropic / OpenAI / Google) have hand-written classes. Adding a new provider is usually a 20-line preset, not a new file.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ pnpm add @wrongstack/providers @wrongstack/core
11
+ ```
12
+
13
+ `@wrongstack/core` is a peer of every provider — providers depend on the core `Provider` interface, message types, and tool format.
14
+
15
+ ## What's in here
16
+
17
+ ```
18
+ src/
19
+ anthropic.ts native Anthropic Messages API
20
+ openai.ts native OpenAI Chat Completions API
21
+ google.ts native Google Gemini generateContent API
22
+ openai-compatible.ts drop-in for any /v1/chat/completions endpoint
23
+ wire-adapter.ts declarative adapter — pass a WireFormatConfig
24
+ presets/ wire configs for anthropic / openai / google / mistral
25
+ sse.ts SSE parser with 256 KB buffer cap
26
+ aggregate.ts tool_use stream-event aggregator
27
+ tool-format/ tools ↔ Anthropic / OpenAI converters
28
+ stop-reason.ts normalize provider stop_reason → canonical
29
+ error-parse.ts parse provider HTTP error envelopes
30
+ capabilities.ts map models.dev capability strings → bool flags
31
+ ```
32
+
33
+ ## Quick example
34
+
35
+ ```ts
36
+ import { AnthropicProvider } from '@wrongstack/providers';
37
+
38
+ const provider = new AnthropicProvider({
39
+ apiKey: process.env.ANTHROPIC_API_KEY!,
40
+ modelId: 'claude-sonnet-4-6',
41
+ });
42
+
43
+ const stream = provider.stream({
44
+ messages: [{ role: 'user', content: 'hello' }],
45
+ tools: [],
46
+ });
47
+
48
+ for await (const event of stream) {
49
+ if (event.type === 'text_delta') process.stdout.write(event.text);
50
+ }
51
+ ```
52
+
53
+ ## Using a preset (OpenAI-compatible service)
54
+
55
+ ```ts
56
+ import { OpenAICompatibleProvider } from '@wrongstack/providers';
57
+
58
+ const groq = new OpenAICompatibleProvider({
59
+ id: 'groq',
60
+ apiKey: process.env.GROQ_API_KEY!,
61
+ baseURL: 'https://api.groq.com/openai/v1',
62
+ modelId: 'llama-3.3-70b-versatile',
63
+ capabilities: { tools: true, vision: false, maxContext: 128_000 },
64
+ });
65
+ ```
66
+
67
+ ## Wire-format adapter (declarative)
68
+
69
+ For a new provider that doesn't fit one of the existing presets, write a `WireFormatConfig` and plug it into `WireAdapter`. See [docs/provider-author-guide.md](../../docs/provider-author-guide.md) for the full spec.
70
+
71
+ ```ts
72
+ import { WireAdapter } from '@wrongstack/providers';
73
+ import type { WireFormatConfig } from '@wrongstack/core';
74
+
75
+ const myWire: WireFormatConfig = {
76
+ family: 'openai',
77
+ endpoint: 'https://api.myprovider.com/v1/chat/completions',
78
+ authHeader: (key) => ({ 'Authorization': `Bearer ${key}` }),
79
+ // tool format, message shape, stream parsing — see WireFormatConfig type
80
+ };
81
+
82
+ const provider = new WireAdapter({
83
+ id: 'myprovider',
84
+ apiKey: '…',
85
+ modelId: 'my-model-1',
86
+ wire: myWire,
87
+ capabilities: { tools: true, maxContext: 32_000 },
88
+ });
89
+ ```
90
+
91
+ ## Tool input parsing (`parseToolInput`)
92
+
93
+ All four stream parsers (anthropic / openai / aggregate + the three OpenAI-compatible presets) 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
+
95
+ ## Capabilities
96
+
97
+ The capability flags on each provider come from [models.dev](https://models.dev) catalog data, mapped by `capabilitiesFor()`. The agent uses them to pick adaptive context-management thresholds, gate vision/reasoning features, and refuse tools on models that don't support tool calls.
98
+
99
+ ## License
100
+
101
+ MIT
package/dist/index.d.ts CHANGED
@@ -360,6 +360,9 @@ declare const googleWireFormat: WireFormatConfig<GoogleStreamState>;
360
360
  */
361
361
  declare function capabilitiesFor(registry: ModelsRegistry, providerId: string, modelId: string): Promise<Capabilities>;
362
362
 
363
+ declare const CAPABILITIES_BY_FAMILY: Record<WireFamily, Capabilities>;
364
+ declare function capabilitiesForFamily(family: WireFamily, overrides?: Partial<Capabilities>): Capabilities;
365
+
363
366
  /**
364
367
  * Provider HTTP error bodies come in three or four shapes depending on
365
368
  * vendor. Rather than dump the raw JSON into the error message (which is
@@ -459,4 +462,4 @@ declare function buildProviderFactoriesFromRegistry(opts: BuildFactoriesOptions)
459
462
  */
460
463
  declare function makeProviderFromConfig(id: string, cfg: ProviderConfig): Provider;
461
464
 
462
- export { AnthropicProvider, type AnthropicProviderOptions, type BuildFactoriesOptions, type CompatibilityQuirks, type ConvertOptions, GoogleProvider, type GoogleProviderOptions, type OpenAIChoice, type OpenAICompatibleOptions, OpenAICompatibleProvider, type OpenAIMessage, OpenAIProvider, type OpenAIProviderOptions, type OpenAIToolCall, WireAdapter, type WireFactoryOptions, type WireFormatConfig, WireFormatProvider, anthropicWireFormat, buildProviderFactoriesFromRegistry, capabilitiesFor, contentFromAnthropic, contentFromOpenAI, createWireFormatFactory, defineWireFormat, googleWireFormat, makeProviderFromConfig, messagesToOpenAI, mistralWireFormat, normalizeAnthropic, normalizeOpenAI, openaiWireFormat, parseProviderHttpError, toolsToAnthropic, toolsToOpenAI };
465
+ export { AnthropicProvider, type AnthropicProviderOptions, type BuildFactoriesOptions, CAPABILITIES_BY_FAMILY, type CompatibilityQuirks, type ConvertOptions, GoogleProvider, type GoogleProviderOptions, type OpenAIChoice, type OpenAICompatibleOptions, OpenAICompatibleProvider, type OpenAIMessage, OpenAIProvider, type OpenAIProviderOptions, type OpenAIToolCall, WireAdapter, type WireFactoryOptions, type WireFormatConfig, WireFormatProvider, anthropicWireFormat, buildProviderFactoriesFromRegistry, capabilitiesFor, capabilitiesForFamily, contentFromAnthropic, contentFromOpenAI, createWireFormatFactory, defineWireFormat, googleWireFormat, makeProviderFromConfig, messagesToOpenAI, mistralWireFormat, normalizeAnthropic, normalizeOpenAI, openaiWireFormat, parseProviderHttpError, toolsToAnthropic, toolsToOpenAI };
package/dist/index.js CHANGED
@@ -1,4 +1,5 @@
1
- import { safeParse, ProviderError, sanitizeJsonString } from '@wrongstack/core';
1
+ import { ProviderError, safeParse, sanitizeJsonString } from '@wrongstack/core';
2
+ import { randomUUID } from 'crypto';
2
3
 
3
4
  var __defProp = Object.defineProperty;
4
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
@@ -9,6 +10,20 @@ var __export = (target, all) => {
9
10
  for (var name in all)
10
11
  __defProp(target, name, { get: all[name], enumerable: true });
11
12
  };
13
+ function parseToolInput(raw) {
14
+ if (!raw) return {};
15
+ const parsed = safeParse(raw);
16
+ if (!parsed.ok) return { __raw: raw };
17
+ const v = parsed.value;
18
+ if (v && typeof v === "object" && !Array.isArray(v)) {
19
+ return v;
20
+ }
21
+ return { __raw: v ?? raw };
22
+ }
23
+ var init_tool_input = __esm({
24
+ "src/_tool-input.ts"() {
25
+ }
26
+ });
12
27
 
13
28
  // src/aggregate.ts
14
29
  var aggregate_exports = {};
@@ -50,13 +65,14 @@ async function aggregateStream(stream, onEvent) {
50
65
  case "tool_use_stop": {
51
66
  const b = toolBuffers.get(ev.id);
52
67
  if (b) {
53
- if (ev.input !== void 0) {
68
+ if (ev.input === void 0) {
69
+ b.input = parseToolInput(b.partial);
70
+ } else if (typeof ev.input === "string") {
71
+ b.input = parseToolInput(ev.input);
72
+ } else if (ev.input && typeof ev.input === "object" && !Array.isArray(ev.input)) {
54
73
  b.input = ev.input;
55
- } else if (b.partial) {
56
- const parsed = safeParse(b.partial);
57
- b.input = parsed.ok ? parsed.value : { _raw: b.partial };
58
74
  } else {
59
- b.input = {};
75
+ b.input = { __raw: ev.input };
60
76
  }
61
77
  }
62
78
  currentTextIndex = -1;
@@ -80,7 +96,7 @@ async function aggregateStream(stream, onEvent) {
80
96
  type: "tool_use",
81
97
  id: b.id,
82
98
  name: tb.name,
83
- input: tb.input ?? {}
99
+ input: tb.input && typeof tb.input === "object" && !Array.isArray(tb.input) ? tb.input : {}
84
100
  });
85
101
  }
86
102
  }
@@ -90,6 +106,7 @@ async function aggregateStream(stream, onEvent) {
90
106
  }
91
107
  var init_aggregate = __esm({
92
108
  "src/aggregate.ts"() {
109
+ init_tool_input();
93
110
  }
94
111
  });
95
112
  function parseProviderHttpError(providerId, status, rawText) {
@@ -98,9 +115,10 @@ function parseProviderHttpError(providerId, status, rawText) {
98
115
  const message = `${providerId} HTTP ${status}`;
99
116
  return new ProviderError(message, status, retryable, providerId, { body });
100
117
  }
118
+ var RAW_TRUNCATE_AT = 2e3;
101
119
  function parseBody(rawText) {
102
- const raw = rawText.slice(0, 2e3);
103
- const body = { raw };
120
+ const raw = rawText.slice(0, RAW_TRUNCATE_AT);
121
+ const body = rawText.length > RAW_TRUNCATE_AT ? { raw, truncated: true, rawLength: rawText.length } : { raw };
104
122
  if (!rawText.trim()) return body;
105
123
  let parsed;
106
124
  try {
@@ -144,6 +162,9 @@ function stringOf(v) {
144
162
  return typeof v === "string" && v.length > 0 ? v : void 0;
145
163
  }
146
164
 
165
+ // src/anthropic.ts
166
+ init_tool_input();
167
+
147
168
  // src/tool-format/to-anthropic.ts
148
169
  function toolsToAnthropic(tools) {
149
170
  return tools.map((t) => ({
@@ -209,6 +230,7 @@ function normalizeGemini(stop) {
209
230
  }
210
231
 
211
232
  // src/sse.ts
233
+ var MAX_BUFFER_BYTES = 256 * 1024;
212
234
  async function* parseSSE(body) {
213
235
  if (!body) return;
214
236
  const decoder = new TextDecoder("utf-8");
@@ -241,12 +263,23 @@ async function* parseSSE(body) {
241
263
  else if (field === "data") dataLines.push(value);
242
264
  return void 0;
243
265
  };
266
+ const appendChunk = (chunkStr) => {
267
+ if (chunkStr.length === 0) return;
268
+ buffer += chunkStr;
269
+ if (buffer.length > MAX_BUFFER_BYTES) {
270
+ throw new Error(
271
+ `SSE: pending line exceeds ${MAX_BUFFER_BYTES} bytes \u2014 upstream is not framing events`
272
+ );
273
+ }
274
+ };
244
275
  if (isNodeReadable(body)) {
245
276
  for await (const chunk of body) {
246
- buffer += typeof chunk === "string" ? chunk : decoder.decode(chunk, { stream: true });
247
- const lines = splitBuffer(buffer);
248
- buffer = lines.tail;
249
- for (const line of lines.lines) {
277
+ appendChunk(
278
+ typeof chunk === "string" ? chunk : decoder.decode(chunk, { stream: true })
279
+ );
280
+ const split = splitBuffer(buffer);
281
+ buffer = split.tail;
282
+ for (const line of split.lines) {
250
283
  const msg = processLine(line);
251
284
  if (msg) yield msg;
252
285
  }
@@ -257,10 +290,10 @@ async function* parseSSE(body) {
257
290
  for (; ; ) {
258
291
  const { done, value } = await reader.read();
259
292
  if (done) break;
260
- buffer += decoder.decode(value, { stream: true });
261
- const lines = splitBuffer(buffer);
262
- buffer = lines.tail;
263
- for (const line of lines.lines) {
293
+ appendChunk(decoder.decode(value, { stream: true }));
294
+ const split = splitBuffer(buffer);
295
+ buffer = split.tail;
296
+ for (const line of split.lines) {
264
297
  const msg = processLine(line);
265
298
  if (msg) yield msg;
266
299
  }
@@ -270,17 +303,17 @@ async function* parseSSE(body) {
270
303
  }
271
304
  }
272
305
  if (buffer.length > 0) {
273
- const msg = processLine(buffer);
306
+ const msg = processLine(buffer.replace(/\r$/, ""));
274
307
  if (msg) yield msg;
275
308
  }
276
309
  const final = flush();
277
310
  if (final) yield final;
278
311
  }
279
312
  function splitBuffer(buf) {
280
- const norm = buf.replace(/\r\n/g, "\n");
281
- const parts = norm.split("\n");
313
+ const parts = buf.split("\n");
282
314
  const tail = parts.pop() ?? "";
283
- return { lines: parts, tail };
315
+ const lines = parts.map((p) => p.endsWith("\r") ? p.slice(0, -1) : p);
316
+ return { lines, tail };
284
317
  }
285
318
  function isNodeReadable(b) {
286
319
  return !!b && typeof b === "object" && typeof b.pipe === "function" && typeof b.on === "function";
@@ -347,12 +380,9 @@ var WireAdapter = class {
347
380
  }
348
381
  };
349
382
 
350
- // src/anthropic.ts
351
- var DEFAULT_BASE = "https://api.anthropic.com";
352
- var DEFAULT_VERSION = "2023-06-01";
353
- var AnthropicProvider = class extends WireAdapter {
354
- id = "anthropic";
355
- capabilities = {
383
+ // src/family-capabilities.ts
384
+ var CAPABILITIES_BY_FAMILY = {
385
+ anthropic: {
356
386
  tools: true,
357
387
  parallelTools: true,
358
388
  vision: true,
@@ -362,7 +392,65 @@ var AnthropicProvider = class extends WireAdapter {
362
392
  jsonMode: false,
363
393
  maxContext: 2e5,
364
394
  cacheControl: "native"
395
+ },
396
+ openai: {
397
+ tools: true,
398
+ parallelTools: true,
399
+ vision: true,
400
+ streaming: true,
401
+ promptCache: false,
402
+ systemPrompt: true,
403
+ jsonMode: true,
404
+ maxContext: 128e3,
405
+ cacheControl: "auto"
406
+ },
407
+ "openai-compatible": {
408
+ tools: true,
409
+ parallelTools: true,
410
+ vision: false,
411
+ streaming: true,
412
+ promptCache: false,
413
+ systemPrompt: true,
414
+ jsonMode: false,
415
+ maxContext: 32e3,
416
+ cacheControl: "none"
417
+ },
418
+ google: {
419
+ tools: true,
420
+ parallelTools: true,
421
+ vision: true,
422
+ streaming: true,
423
+ promptCache: false,
424
+ systemPrompt: true,
425
+ jsonMode: true,
426
+ maxContext: 1e6,
427
+ cacheControl: "none"
428
+ },
429
+ unsupported: {
430
+ tools: false,
431
+ parallelTools: false,
432
+ vision: false,
433
+ streaming: false,
434
+ promptCache: false,
435
+ systemPrompt: false,
436
+ jsonMode: false,
437
+ maxContext: 0,
438
+ cacheControl: "none"
439
+ }
440
+ };
441
+ function capabilitiesForFamily(family, overrides = {}) {
442
+ return {
443
+ ...CAPABILITIES_BY_FAMILY[family] ?? CAPABILITIES_BY_FAMILY.unsupported,
444
+ ...overrides
365
445
  };
446
+ }
447
+
448
+ // src/anthropic.ts
449
+ var DEFAULT_BASE = "https://api.anthropic.com";
450
+ var DEFAULT_VERSION = "2023-06-01";
451
+ var AnthropicProvider = class extends WireAdapter {
452
+ id = "anthropic";
453
+ capabilities = capabilitiesForFamily("anthropic");
366
454
  opts;
367
455
  constructor(opts) {
368
456
  super(opts.apiKey, opts.baseUrl ?? DEFAULT_BASE, opts.fetchImpl);
@@ -475,7 +563,7 @@ async function* parseAnthropicStream(body, fallbackModel) {
475
563
  const index = Number(ev["index"] ?? 0);
476
564
  const block = blocks.get(index);
477
565
  if (block?.kind === "tool_use" && block.id) {
478
- const input = block.partial ? safeParse(block.partial).value ?? {} : {};
566
+ const input = parseToolInput(block.partial);
479
567
  yield { type: "tool_use_stop", id: block.id, input };
480
568
  }
481
569
  break;
@@ -508,6 +596,7 @@ async function* parseAnthropicStream(body, fallbackModel) {
508
596
  yield { type: "message_stop", stopReason, usage };
509
597
  }
510
598
  }
599
+ init_tool_input();
511
600
 
512
601
  // src/tool-format/to-openai.ts
513
602
  function toolsToOpenAI(tools) {
@@ -613,18 +702,11 @@ var OpenAIProvider = class extends WireAdapter {
613
702
  super(opts.apiKey, opts.baseUrl ?? DEFAULT_BASE2, opts.fetchImpl);
614
703
  this.opts = opts;
615
704
  this.id = opts.id ?? "openai";
616
- this.capabilities = {
617
- tools: true,
705
+ this.capabilities = capabilitiesForFamily("openai", {
618
706
  parallelTools: !opts.quirks?.parallelToolsDisabled,
619
- vision: true,
620
- streaming: true,
621
- promptCache: false,
622
707
  systemPrompt: !opts.quirks?.systemAsMessage,
623
- jsonMode: true,
624
- maxContext: 128e3,
625
- cacheControl: "auto",
626
708
  ...opts.capabilities
627
- };
709
+ });
628
710
  }
629
711
  buildUrl(_req) {
630
712
  const base = this.baseUrl.replace(/\/+$/, "");
@@ -737,7 +819,7 @@ async function* parseOpenAIStream(body, fallbackModel) {
737
819
  }
738
820
  }
739
821
  for (const entry of toolByIndex.values()) {
740
- const input = entry.argBuf ? safeParse(entry.argBuf).value ?? { _raw: entry.argBuf } : {};
822
+ const input = parseToolInput(entry.argBuf);
741
823
  yield { type: "tool_use_stop", id: entry.id, input };
742
824
  }
743
825
  if (started) {
@@ -755,12 +837,12 @@ var OpenAICompatibleProvider = class extends OpenAIProvider {
755
837
  baseUrl: opts.baseUrl,
756
838
  fetchImpl: opts.fetchImpl,
757
839
  id: opts.id,
758
- capabilities: opts.capabilities,
759
- quirks: {
760
- ...opts.quirks,
761
- parallelToolsDisabled: opts.quirks?.parallelToolsDisabled,
762
- jsonArgumentsBuggy: opts.quirks?.jsonArgumentsBuggy
763
- }
840
+ capabilities: capabilitiesForFamily("openai-compatible", {
841
+ parallelTools: !opts.quirks?.parallelToolsDisabled,
842
+ systemPrompt: !opts.quirks?.systemAsMessage,
843
+ ...opts.capabilities
844
+ }),
845
+ quirks: opts.quirks
764
846
  });
765
847
  this.extraHeaders = opts.headers;
766
848
  this.urlOverride = opts.urlOverride;
@@ -787,18 +869,9 @@ var GoogleProvider = class extends WireAdapter {
787
869
  super(opts.apiKey, opts.baseUrl ?? DEFAULT_BASE3, opts.fetchImpl);
788
870
  this.opts = opts;
789
871
  this.id = opts.id ?? "google";
790
- this.capabilities = {
791
- tools: true,
792
- parallelTools: true,
793
- vision: true,
794
- streaming: true,
795
- promptCache: false,
796
- systemPrompt: true,
797
- jsonMode: true,
798
- maxContext: 1e6,
799
- cacheControl: "none",
872
+ this.capabilities = capabilitiesForFamily("google", {
800
873
  ...opts.capabilities
801
- };
874
+ });
802
875
  }
803
876
  buildUrl(req) {
804
877
  return `${this.baseUrl}/models/${encodeURIComponent(req.model)}:streamGenerateContent?alt=sse`;
@@ -945,8 +1018,9 @@ function messagesToGemini(messages) {
945
1018
  });
946
1019
  }
947
1020
  }
948
- if (textParts.length > 0) out.push({ role: "user", parts: textParts });
949
- if (functionParts.length > 0) out.push({ role: "function", parts: functionParts });
1021
+ const userParts = [...textParts];
1022
+ if (functionParts.length > 0) userParts.push(...functionParts);
1023
+ if (userParts.length > 0) out.push({ role: "user", parts: userParts });
950
1024
  }
951
1025
  return out;
952
1026
  }
@@ -972,7 +1046,7 @@ async function* parseGoogleStream(body, fallbackModel) {
972
1046
  yield { type: "text_delta", text: part.text };
973
1047
  } else if (part.functionCall) {
974
1048
  sawFunctionCall = true;
975
- const id = `${part.functionCall.name}_${Math.random().toString(36).slice(2, 10)}`;
1049
+ const id = randomUUID();
976
1050
  yield { type: "tool_use_start", id, name: part.functionCall.name };
977
1051
  const providerMeta = typeof part.thoughtSignature === "string" ? { "google.thoughtSignature": part.thoughtSignature } : void 0;
978
1052
  yield {
@@ -1064,20 +1138,16 @@ function createWireFormatFactory(cfg, opts = {}) {
1064
1138
  }
1065
1139
  };
1066
1140
  }
1141
+
1142
+ // src/presets/mistral.ts
1143
+ init_tool_input();
1067
1144
  var mistralWireFormat = defineWireFormat({
1068
1145
  id: "mistral",
1069
1146
  family: "openai-compatible",
1070
- capabilities: {
1071
- tools: true,
1072
- parallelTools: true,
1073
- vision: false,
1074
- streaming: true,
1075
- promptCache: false,
1076
- systemPrompt: true,
1147
+ capabilities: capabilitiesForFamily("openai-compatible", {
1077
1148
  jsonMode: true,
1078
- maxContext: 128e3,
1079
- cacheControl: "none"
1080
- },
1149
+ maxContext: 128e3
1150
+ }),
1081
1151
  defaultBaseUrl: "https://api.mistral.ai/v1",
1082
1152
  buildUrl: (base) => `${base.replace(/\/+$/, "")}/chat/completions`,
1083
1153
  buildHeaders: (apiKey) => ({ authorization: `Bearer ${apiKey}` }),
@@ -1133,11 +1203,10 @@ var mistralWireFormat = defineWireFormat({
1133
1203
  if (choice?.finish_reason) {
1134
1204
  for (const block of state.toolCalls.values()) {
1135
1205
  if (block.id) {
1136
- const parsedInput = safeParse(block.partial || "{}");
1137
1206
  out.push({
1138
1207
  type: "tool_use_stop",
1139
1208
  id: block.id,
1140
- input: parsedInput.ok ? parsedInput.value : {}
1209
+ input: parseToolInput(block.partial)
1141
1210
  });
1142
1211
  }
1143
1212
  }
@@ -1165,21 +1234,14 @@ function mapStopReason(reason) {
1165
1234
  return "end_turn";
1166
1235
  }
1167
1236
  }
1237
+
1238
+ // src/presets/anthropic.ts
1239
+ init_tool_input();
1168
1240
  var DEFAULT_VERSION2 = "2023-06-01";
1169
1241
  var anthropicWireFormat = defineWireFormat({
1170
1242
  id: "anthropic",
1171
1243
  family: "anthropic",
1172
- capabilities: {
1173
- tools: true,
1174
- parallelTools: true,
1175
- vision: true,
1176
- streaming: true,
1177
- promptCache: true,
1178
- systemPrompt: true,
1179
- jsonMode: false,
1180
- maxContext: 2e5,
1181
- cacheControl: "native"
1182
- },
1244
+ capabilities: capabilitiesForFamily("anthropic"),
1183
1245
  defaultBaseUrl: "https://api.anthropic.com",
1184
1246
  buildUrl: (base) => {
1185
1247
  const b = base.replace(/\/+$/, "");
@@ -1273,7 +1335,7 @@ var anthropicWireFormat = defineWireFormat({
1273
1335
  const index = Number(ev["index"] ?? 0);
1274
1336
  const block = state.blocks.get(index);
1275
1337
  if (block?.kind === "tool_use" && block.id) {
1276
- const input = block.partial ? safeParse(block.partial).value ?? {} : {};
1338
+ const input = parseToolInput(block.partial);
1277
1339
  out.push({ type: "tool_use_stop", id: block.id, input });
1278
1340
  }
1279
1341
  break;
@@ -1312,20 +1374,13 @@ var anthropicWireFormat = defineWireFormat({
1312
1374
  return [];
1313
1375
  }
1314
1376
  });
1377
+
1378
+ // src/presets/openai.ts
1379
+ init_tool_input();
1315
1380
  var openaiWireFormat = defineWireFormat({
1316
1381
  id: "openai",
1317
1382
  family: "openai",
1318
- capabilities: {
1319
- tools: true,
1320
- parallelTools: true,
1321
- vision: true,
1322
- streaming: true,
1323
- promptCache: false,
1324
- systemPrompt: true,
1325
- jsonMode: true,
1326
- maxContext: 128e3,
1327
- cacheControl: "auto"
1328
- },
1383
+ capabilities: capabilitiesForFamily("openai"),
1329
1384
  defaultBaseUrl: "https://api.openai.com/v1",
1330
1385
  buildUrl: (base) => {
1331
1386
  const b = base.replace(/\/+$/, "");
@@ -1424,7 +1479,7 @@ var openaiWireFormat = defineWireFormat({
1424
1479
  state.finalEmitted = true;
1425
1480
  const out = [];
1426
1481
  for (const entry of state.toolByIndex.values()) {
1427
- const input = entry.argBuf ? safeParse(entry.argBuf).value ?? { _raw: entry.argBuf } : {};
1482
+ const input = parseToolInput(entry.argBuf);
1428
1483
  out.push({ type: "tool_use_stop", id: entry.id, input });
1429
1484
  }
1430
1485
  if (state.started) {
@@ -1443,17 +1498,7 @@ function stripCacheControl(system) {
1443
1498
  var googleWireFormat = defineWireFormat({
1444
1499
  id: "google",
1445
1500
  family: "google",
1446
- capabilities: {
1447
- tools: true,
1448
- parallelTools: true,
1449
- vision: true,
1450
- streaming: true,
1451
- promptCache: false,
1452
- systemPrompt: true,
1453
- jsonMode: true,
1454
- maxContext: 1e6,
1455
- cacheControl: "none"
1456
- },
1501
+ capabilities: capabilitiesForFamily("google"),
1457
1502
  defaultBaseUrl: "https://generativelanguage.googleapis.com/v1beta",
1458
1503
  buildUrl: (base, req) => `${base}/models/${encodeURIComponent(req.model)}:streamGenerateContent?alt=sse`,
1459
1504
  buildHeaders: (apiKey) => ({ "x-goog-api-key": apiKey }),
@@ -1650,74 +1695,15 @@ function messagesToGemini2(messages) {
1650
1695
  }
1651
1696
 
1652
1697
  // src/capabilities.ts
1653
- var BASE_BY_FAMILY = {
1654
- anthropic: {
1655
- tools: true,
1656
- parallelTools: true,
1657
- vision: true,
1658
- streaming: true,
1659
- promptCache: true,
1660
- systemPrompt: true,
1661
- jsonMode: false,
1662
- maxContext: 2e5,
1663
- cacheControl: "native"
1664
- },
1665
- openai: {
1666
- tools: true,
1667
- parallelTools: true,
1668
- vision: true,
1669
- streaming: true,
1670
- promptCache: false,
1671
- systemPrompt: true,
1672
- jsonMode: true,
1673
- maxContext: 128e3,
1674
- cacheControl: "auto"
1675
- },
1676
- "openai-compatible": {
1677
- tools: true,
1678
- parallelTools: true,
1679
- vision: false,
1680
- streaming: true,
1681
- promptCache: false,
1682
- systemPrompt: true,
1683
- jsonMode: false,
1684
- maxContext: 32e3,
1685
- cacheControl: "none"
1686
- },
1687
- google: {
1688
- tools: true,
1689
- parallelTools: true,
1690
- vision: true,
1691
- streaming: true,
1692
- promptCache: false,
1693
- systemPrompt: true,
1694
- jsonMode: true,
1695
- maxContext: 1e6,
1696
- cacheControl: "none"
1697
- },
1698
- unsupported: {
1699
- tools: false,
1700
- parallelTools: false,
1701
- vision: false,
1702
- streaming: false,
1703
- promptCache: false,
1704
- systemPrompt: false,
1705
- jsonMode: false,
1706
- maxContext: 0,
1707
- cacheControl: "none"
1708
- }
1709
- };
1710
- function baseFor(family) {
1711
- return BASE_BY_FAMILY[family] ?? BASE_BY_FAMILY.unsupported;
1712
- }
1713
1698
  async function capabilitiesFor(registry, providerId, modelId) {
1714
1699
  const provider = await registry.getProvider(providerId);
1715
- const base = baseFor(provider?.family ?? "unsupported");
1700
+ const base = capabilitiesForFamily(provider?.family ?? "unsupported");
1716
1701
  const model = await registry.getModel(providerId, modelId);
1717
1702
  if (!model) return { ...base };
1718
1703
  return {
1719
1704
  ...base,
1720
1705
  tools: model.capabilities.tools && base.tools,
1706
+ parallelTools: model.capabilities.tools && base.parallelTools,
1721
1707
  vision: model.capabilities.vision && base.vision,
1722
1708
  maxContext: model.capabilities.maxContext || base.maxContext
1723
1709
  };
@@ -1800,12 +1786,15 @@ function parseToolArguments(raw, toolName, toolCallId, opts) {
1800
1786
  opts.onParseFailure?.({ toolName, toolCallId, raw });
1801
1787
  return { __raw_arguments: raw };
1802
1788
  } catch {
1803
- try {
1804
- const sanitized = JSON.parse(sanitizeJsonString(raw));
1805
- if (sanitized && typeof sanitized === "object" && !Array.isArray(sanitized)) {
1806
- return sanitized;
1789
+ const sanitized = sanitizeJsonString(raw);
1790
+ if (sanitized !== null) {
1791
+ try {
1792
+ const parsed = JSON.parse(sanitized);
1793
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
1794
+ return parsed;
1795
+ }
1796
+ } catch {
1807
1797
  }
1808
- } catch {
1809
1798
  }
1810
1799
  opts.onParseFailure?.({ toolName, toolCallId, raw });
1811
1800
  return { __raw_arguments: raw };
@@ -1916,6 +1905,6 @@ function requireKey(cfg) {
1916
1905
  throw new Error("Provider config requires apiKey (or set the corresponding env var).");
1917
1906
  }
1918
1907
 
1919
- export { AnthropicProvider, GoogleProvider, OpenAICompatibleProvider, OpenAIProvider, WireAdapter, WireFormatProvider, anthropicWireFormat, buildProviderFactoriesFromRegistry, capabilitiesFor, contentFromAnthropic, contentFromOpenAI, createWireFormatFactory, defineWireFormat, googleWireFormat, makeProviderFromConfig, messagesToOpenAI, mistralWireFormat, normalizeAnthropic, normalizeOpenAI, openaiWireFormat, parseProviderHttpError, toolsToAnthropic, toolsToOpenAI };
1908
+ export { AnthropicProvider, CAPABILITIES_BY_FAMILY, GoogleProvider, OpenAICompatibleProvider, OpenAIProvider, WireAdapter, WireFormatProvider, anthropicWireFormat, buildProviderFactoriesFromRegistry, capabilitiesFor, capabilitiesForFamily, contentFromAnthropic, contentFromOpenAI, createWireFormatFactory, defineWireFormat, googleWireFormat, makeProviderFromConfig, messagesToOpenAI, mistralWireFormat, normalizeAnthropic, normalizeOpenAI, openaiWireFormat, parseProviderHttpError, toolsToAnthropic, toolsToOpenAI };
1920
1909
  //# sourceMappingURL=index.js.map
1921
1910
  //# sourceMappingURL=index.js.map