@broberg/ai-sdk 0.2.0 → 0.3.1

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/dist/index.js CHANGED
@@ -81,6 +81,51 @@ async function subprocessTransport(req) {
81
81
  return parseClaudeCliJson(stdout);
82
82
  }
83
83
 
84
+ // src/transport/stream.ts
85
+ var StreamHttpError = class extends Error {
86
+ status;
87
+ constructor(message, status) {
88
+ super(message);
89
+ this.name = "StreamHttpError";
90
+ this.status = status;
91
+ }
92
+ };
93
+ async function* streamTransport(req) {
94
+ if (!req.http) throw new Error("streamTransport: req.http is required for http transport");
95
+ const { url, method = "POST", headers, body } = req.http;
96
+ const fetchImpl = req.fetch ?? fetch;
97
+ const res = await fetchImpl(url, {
98
+ method,
99
+ headers,
100
+ body: body === void 0 ? void 0 : typeof body === "string" ? body : JSON.stringify(body)
101
+ });
102
+ if (!res.ok || !res.body) {
103
+ const text = await res.text().catch(() => "");
104
+ throw new StreamHttpError(`stream ${res.status}: ${text.slice(0, 300)}`, res.status);
105
+ }
106
+ const reader = res.body.getReader();
107
+ const decoder = new TextDecoder();
108
+ let buffer = "";
109
+ try {
110
+ for (; ; ) {
111
+ const { done, value } = await reader.read();
112
+ if (done) break;
113
+ buffer += decoder.decode(value, { stream: true });
114
+ let nl;
115
+ while ((nl = buffer.indexOf("\n")) >= 0) {
116
+ const line = buffer.slice(0, nl).replace(/\r$/, "");
117
+ buffer = buffer.slice(nl + 1);
118
+ if (!line.startsWith("data:")) continue;
119
+ const data = line.slice(5).trim();
120
+ if (data === "[DONE]") return;
121
+ if (data) yield data;
122
+ }
123
+ }
124
+ } finally {
125
+ reader.releaseLock();
126
+ }
127
+ }
128
+
84
129
  // src/providers/tools.ts
85
130
  function family(provider) {
86
131
  if (provider === "gemini" || provider === "google") return "gemini";
@@ -274,17 +319,41 @@ function flattenForSubprocess(messages) {
274
319
  function anthropicAdapter(config = {}) {
275
320
  const baseUrl = config.baseUrl ?? "https://api.anthropic.com";
276
321
  const version = config.anthropicVersion ?? "2023-06-01";
277
- async function chatHttp(req) {
278
- const apiKey = config.apiKey ?? process.env.ANTHROPIC_API_KEY;
279
- if (!apiKey) throw new Error("anthropic adapter: API key not set (env ANTHROPIC_API_KEY)");
322
+ function buildBody(req) {
280
323
  const system = [];
281
324
  const messages = [];
282
325
  for (const m of req.messages) {
283
326
  if (m.role === "system") {
284
327
  system.push(typeof m.content === "string" ? m.content : "");
285
- } else {
286
- messages.push({ role: m.role === "assistant" ? "assistant" : "user", content: contentBlocks(m.content) });
328
+ continue;
287
329
  }
330
+ if (m.role === "tool") {
331
+ messages.push({
332
+ role: "user",
333
+ content: [
334
+ {
335
+ type: "tool_result",
336
+ tool_use_id: m.toolCallId ?? "",
337
+ content: typeof m.content === "string" ? m.content : ""
338
+ }
339
+ ]
340
+ });
341
+ continue;
342
+ }
343
+ if (m.role === "assistant" && m.toolCalls && m.toolCalls.length > 0) {
344
+ const blocks = [];
345
+ if (typeof m.content === "string") {
346
+ if (m.content.length > 0) blocks.push({ type: "text", text: m.content });
347
+ } else {
348
+ blocks.push(...contentBlocks(m.content));
349
+ }
350
+ for (const tc of m.toolCalls) {
351
+ blocks.push({ type: "tool_use", id: tc.id, name: tc.name, input: tc.arguments });
352
+ }
353
+ messages.push({ role: "assistant", content: blocks });
354
+ continue;
355
+ }
356
+ messages.push({ role: m.role === "assistant" ? "assistant" : "user", content: contentBlocks(m.content) });
288
357
  }
289
358
  const body = {
290
359
  model: req.spec.model,
@@ -295,6 +364,16 @@ function anthropicAdapter(config = {}) {
295
364
  if (system.length > 0) body.system = system.join("\n");
296
365
  if (req.tools) body.tools = toProviderTools(req.tools, "anthropic");
297
366
  if (req.temperature !== void 0) body.temperature = req.temperature;
367
+ return body;
368
+ }
369
+ function apiKeyOrThrow() {
370
+ const apiKey = config.apiKey ?? process.env.ANTHROPIC_API_KEY;
371
+ if (!apiKey) throw new Error("anthropic adapter: API key not set (env ANTHROPIC_API_KEY)");
372
+ return apiKey;
373
+ }
374
+ async function chatHttp(req) {
375
+ const apiKey = apiKeyOrThrow();
376
+ const body = buildBody(req);
298
377
  const res = await httpTransport({
299
378
  spec: req.spec,
300
379
  http: {
@@ -345,7 +424,110 @@ function anthropicAdapter(config = {}) {
345
424
  async function chat(req) {
346
425
  return req.spec.transport === "subprocess" ? chatSubprocess(req) : chatHttp(req);
347
426
  }
348
- return { name: "anthropic", chat, vision: chat };
427
+ async function* chatStream(req) {
428
+ if (req.spec.transport === "subprocess") {
429
+ throw new Error("anthropic adapter: streaming is not supported over the subprocess transport");
430
+ }
431
+ const apiKey = apiKeyOrThrow();
432
+ const body = { ...buildBody(req), stream: true };
433
+ const stream = streamTransport({
434
+ spec: req.spec,
435
+ fetch: config.fetch,
436
+ http: {
437
+ url: `${baseUrl}/v1/messages`,
438
+ headers: {
439
+ "content-type": "application/json",
440
+ "x-api-key": apiKey,
441
+ "anthropic-version": version
442
+ },
443
+ body
444
+ }
445
+ });
446
+ let inputTokens = 0;
447
+ let outputTokens = 0;
448
+ let cacheReadTokens = 0;
449
+ let cacheCreationTokens = 0;
450
+ let stopReason = null;
451
+ const toolBlocks = /* @__PURE__ */ new Map();
452
+ for await (const data of stream) {
453
+ let ev;
454
+ try {
455
+ ev = JSON.parse(data);
456
+ } catch {
457
+ continue;
458
+ }
459
+ switch (ev.type) {
460
+ case "message_start": {
461
+ const u = ev.message?.usage;
462
+ inputTokens = u?.input_tokens ?? 0;
463
+ cacheReadTokens = u?.cache_read_input_tokens ?? 0;
464
+ cacheCreationTokens = u?.cache_creation_input_tokens ?? 0;
465
+ break;
466
+ }
467
+ case "content_block_start": {
468
+ if (ev.content_block?.type === "tool_use" && ev.index !== void 0) {
469
+ toolBlocks.set(ev.index, {
470
+ id: ev.content_block.id ?? "",
471
+ name: ev.content_block.name ?? "",
472
+ json: ""
473
+ });
474
+ }
475
+ break;
476
+ }
477
+ case "content_block_delta": {
478
+ const d = ev.delta;
479
+ if (d?.type === "text_delta" && d.text) {
480
+ yield { type: "text", delta: d.text };
481
+ } else if (d?.type === "input_json_delta" && d.partial_json && ev.index !== void 0) {
482
+ const b = toolBlocks.get(ev.index);
483
+ if (b) b.json += d.partial_json;
484
+ }
485
+ break;
486
+ }
487
+ case "message_delta": {
488
+ if (ev.delta?.stop_reason) stopReason = ev.delta.stop_reason;
489
+ if (ev.usage?.output_tokens !== void 0) outputTokens = ev.usage.output_tokens;
490
+ break;
491
+ }
492
+ default:
493
+ break;
494
+ }
495
+ }
496
+ for (const [, b] of [...toolBlocks.entries()].sort((a, c) => a[0] - c[0])) {
497
+ let args = {};
498
+ try {
499
+ args = b.json ? JSON.parse(b.json) : {};
500
+ } catch {
501
+ args = {};
502
+ }
503
+ yield { type: "tool_call", id: b.id, name: b.name, args };
504
+ }
505
+ const usage = freshUsage({
506
+ provider: "anthropic",
507
+ model: req.spec.model,
508
+ transport: "http",
509
+ capability: "chat",
510
+ inputTokens,
511
+ outputTokens,
512
+ cacheReadTokens,
513
+ cacheCreationTokens
514
+ });
515
+ yield { type: "usage", costUsd: usage.costUsd, model: usage.model, usage };
516
+ yield { type: "finish", reason: mapAnthropicStop(stopReason) };
517
+ }
518
+ return { name: "anthropic", chat, chatStream, vision: chat };
519
+ }
520
+ function mapAnthropicStop(reason) {
521
+ switch (reason) {
522
+ case "tool_use":
523
+ return "tool_calls";
524
+ case "max_tokens":
525
+ return "length";
526
+ case "stop_sequence":
527
+ return "stop";
528
+ default:
529
+ return "end_turn";
530
+ }
349
531
  }
350
532
 
351
533
  // src/providers/openai-compatible.ts
@@ -353,6 +535,13 @@ function toOpenAIMessage(m) {
353
535
  if (typeof m.content === "string") {
354
536
  const base = { role: m.role, content: m.content };
355
537
  if (m.toolCallId) base.tool_call_id = m.toolCallId;
538
+ if (m.toolCalls && m.toolCalls.length > 0) {
539
+ base.tool_calls = m.toolCalls.map((tc) => ({
540
+ id: tc.id,
541
+ type: "function",
542
+ function: { name: tc.name, arguments: JSON.stringify(tc.arguments) }
543
+ }));
544
+ }
356
545
  return base;
357
546
  }
358
547
  const content = m.content.map((p) => {
@@ -375,6 +564,8 @@ function makeOpenAICompatibleAdapter(config) {
375
564
  if (req.tools) body.tools = toProviderTools(req.tools, "openai");
376
565
  if (req.maxTokens !== void 0) body.max_tokens = req.maxTokens;
377
566
  if (req.temperature !== void 0) body.temperature = req.temperature;
567
+ if (req.responseFormat === "json") body.response_format = { type: "json_object" };
568
+ if (config.costFromResponseField) body.usage = { include: true };
378
569
  const res = await httpTransport({
379
570
  spec: req.spec,
380
571
  http: {
@@ -404,17 +595,113 @@ function makeOpenAICompatibleAdapter(config) {
404
595
  inputTokens: data.usage?.prompt_tokens ?? 0,
405
596
  outputTokens: data.usage?.completion_tokens ?? 0
406
597
  });
598
+ if (config.costFromResponseField && typeof data.usage?.cost === "number") {
599
+ usage.costUsd = data.usage.cost;
600
+ }
407
601
  const result = { text, usage };
408
602
  if (toolCalls && toolCalls.length > 0) result.toolCalls = toolCalls;
409
603
  return result;
410
604
  }
605
+ async function* chatStream(req) {
606
+ const apiKey = config.apiKey ?? process.env[`${config.name.toUpperCase()}_API_KEY`];
607
+ if (!apiKey) {
608
+ throw new Error(`${config.name} adapter: API key not set (env ${config.name.toUpperCase()}_API_KEY)`);
609
+ }
610
+ const body = {
611
+ model: req.spec.model,
612
+ messages: req.messages.map(toOpenAIMessage),
613
+ stream: true,
614
+ stream_options: { include_usage: true }
615
+ };
616
+ if (req.tools) body.tools = toProviderTools(req.tools, "openai");
617
+ if (req.maxTokens !== void 0) body.max_tokens = req.maxTokens;
618
+ if (req.temperature !== void 0) body.temperature = req.temperature;
619
+ if (req.responseFormat === "json") body.response_format = { type: "json_object" };
620
+ if (config.costFromResponseField) body.usage = { include: true };
621
+ const stream = streamTransport({
622
+ spec: req.spec,
623
+ fetch: config.fetch,
624
+ http: {
625
+ url: `${config.baseUrl}/chat/completions`,
626
+ headers: {
627
+ "content-type": "application/json",
628
+ Authorization: `Bearer ${apiKey}`,
629
+ ...config.extraHeaders
630
+ },
631
+ body
632
+ }
633
+ });
634
+ const toolAcc = /* @__PURE__ */ new Map();
635
+ let finishReason = null;
636
+ for await (const data of stream) {
637
+ let chunk;
638
+ try {
639
+ chunk = JSON.parse(data);
640
+ } catch {
641
+ continue;
642
+ }
643
+ const choice = chunk.choices?.[0];
644
+ if (choice) {
645
+ const delta = choice.delta ?? {};
646
+ if (typeof delta.content === "string" && delta.content.length > 0) {
647
+ yield { type: "text", delta: delta.content };
648
+ }
649
+ for (const tc of delta.tool_calls ?? []) {
650
+ const idx = tc.index ?? 0;
651
+ const cur = toolAcc.get(idx) ?? { id: "", name: "", args: "" };
652
+ if (tc.id) cur.id = tc.id;
653
+ if (tc.function?.name) cur.name = tc.function.name;
654
+ if (tc.function?.arguments) cur.args += tc.function.arguments;
655
+ toolAcc.set(idx, cur);
656
+ }
657
+ if (choice.finish_reason) finishReason = choice.finish_reason;
658
+ }
659
+ if (chunk.usage) {
660
+ const usage = freshUsage({
661
+ provider: config.name,
662
+ model: req.spec.model,
663
+ transport: "http",
664
+ capability: "chat",
665
+ inputTokens: chunk.usage.prompt_tokens ?? 0,
666
+ outputTokens: chunk.usage.completion_tokens ?? 0
667
+ });
668
+ if (config.costFromResponseField && typeof chunk.usage.cost === "number") {
669
+ usage.costUsd = chunk.usage.cost;
670
+ }
671
+ yield { type: "usage", costUsd: usage.costUsd, model: usage.model, usage };
672
+ }
673
+ }
674
+ for (const [, t] of [...toolAcc.entries()].sort((a, b) => a[0] - b[0])) {
675
+ let args = {};
676
+ try {
677
+ args = t.args ? JSON.parse(t.args) : {};
678
+ } catch {
679
+ args = {};
680
+ }
681
+ yield { type: "tool_call", id: t.id, name: t.name, args };
682
+ }
683
+ yield { type: "finish", reason: mapFinishReason(finishReason) };
684
+ }
411
685
  return {
412
686
  name: config.name,
413
687
  chat,
688
+ chatStream,
414
689
  // gpt-4o-class models are multimodal — vision shares the chat path.
415
690
  vision: chat
416
691
  };
417
692
  }
693
+ function mapFinishReason(reason) {
694
+ switch (reason) {
695
+ case "tool_calls":
696
+ return "tool_calls";
697
+ case "length":
698
+ return "length";
699
+ case "stop":
700
+ return "stop";
701
+ default:
702
+ return "end_turn";
703
+ }
704
+ }
418
705
 
419
706
  // src/providers/openai.ts
420
707
  var WHISPER_PRICE_PER_MIN = {
@@ -495,11 +782,12 @@ function partsFrom(content) {
495
782
  }
496
783
  function geminiAdapter(config = {}) {
497
784
  const baseUrl = config.baseUrl ?? "https://generativelanguage.googleapis.com/v1beta";
498
- async function chat(req) {
785
+ function resolveKey() {
499
786
  const apiKey = config.apiKey ?? process.env.GOOGLE_API_KEY ?? process.env.GEMINI_API_KEY;
500
- if (!apiKey) {
501
- throw new Error("gemini adapter: API key not set (env GOOGLE_API_KEY)");
502
- }
787
+ if (!apiKey) throw new Error("gemini adapter: API key not set (env GOOGLE_API_KEY)");
788
+ return apiKey;
789
+ }
790
+ function buildBody(req) {
503
791
  const systemParts = [];
504
792
  const contents = [];
505
793
  for (const m of req.messages) {
@@ -519,6 +807,11 @@ function geminiAdapter(config = {}) {
519
807
  if (req.maxTokens !== void 0) genConfig.maxOutputTokens = req.maxTokens;
520
808
  if (req.temperature !== void 0) genConfig.temperature = req.temperature;
521
809
  if (Object.keys(genConfig).length > 0) body.generationConfig = genConfig;
810
+ return body;
811
+ }
812
+ async function chat(req) {
813
+ const apiKey = resolveKey();
814
+ const body = buildBody(req);
522
815
  const res = await httpTransport({
523
816
  spec: req.spec,
524
817
  http: {
@@ -546,7 +839,71 @@ function geminiAdapter(config = {}) {
546
839
  if (toolCalls.length > 0) result.toolCalls = toolCalls;
547
840
  return result;
548
841
  }
549
- return { name: "gemini", chat, vision: chat };
842
+ async function* chatStream(req) {
843
+ const apiKey = resolveKey();
844
+ const body = buildBody(req);
845
+ const stream = streamTransport({
846
+ spec: req.spec,
847
+ fetch: config.fetch,
848
+ http: {
849
+ url: `${baseUrl}/models/${req.spec.model}:streamGenerateContent?alt=sse&key=${encodeURIComponent(apiKey)}`,
850
+ headers: { "content-type": "application/json" },
851
+ body
852
+ }
853
+ });
854
+ const toolCalls = [];
855
+ let inputTokens = 0;
856
+ let outputTokens = 0;
857
+ let finishReason = null;
858
+ for await (const data of stream) {
859
+ let chunk;
860
+ try {
861
+ chunk = JSON.parse(data);
862
+ } catch {
863
+ continue;
864
+ }
865
+ const candidate = chunk.candidates?.[0];
866
+ for (const p of candidate?.content?.parts ?? []) {
867
+ if (typeof p.text === "string" && p.text.length > 0) {
868
+ yield { type: "text", delta: p.text };
869
+ } else if (p.functionCall) {
870
+ toolCalls.push(fromProviderToolCall({ functionCall: p.functionCall }, "gemini"));
871
+ }
872
+ }
873
+ if (candidate?.finishReason) finishReason = candidate.finishReason;
874
+ if (chunk.usageMetadata) {
875
+ inputTokens = chunk.usageMetadata.promptTokenCount ?? inputTokens;
876
+ outputTokens = chunk.usageMetadata.candidatesTokenCount ?? outputTokens;
877
+ }
878
+ }
879
+ for (const tc of toolCalls) {
880
+ yield { type: "tool_call", id: tc.id, name: tc.name, args: tc.arguments };
881
+ }
882
+ const usage = freshUsage({
883
+ provider: "gemini",
884
+ model: req.spec.model,
885
+ transport: "http",
886
+ capability: "chat",
887
+ inputTokens,
888
+ outputTokens
889
+ });
890
+ yield { type: "usage", costUsd: usage.costUsd, model: usage.model, usage };
891
+ yield {
892
+ type: "finish",
893
+ reason: toolCalls.length > 0 ? "tool_calls" : mapGeminiFinish(finishReason)
894
+ };
895
+ }
896
+ return { name: "gemini", chat, chatStream, vision: chat };
897
+ }
898
+ function mapGeminiFinish(reason) {
899
+ switch (reason) {
900
+ case "MAX_TOKENS":
901
+ return "length";
902
+ case "STOP":
903
+ return "end_turn";
904
+ default:
905
+ return reason ? "stop" : "end_turn";
906
+ }
550
907
  }
551
908
 
552
909
  // src/providers/deepinfra.ts
@@ -567,7 +924,10 @@ function openrouterAdapter(config = {}) {
567
924
  extraHeaders: {
568
925
  "HTTP-Referer": config.referer ?? "https://broberg.ai",
569
926
  "X-Title": config.title ?? "@broberg/ai-sdk"
570
- }
927
+ },
928
+ // OpenRouter returns ground-truth usage.cost (USD) when usage:{include:true}
929
+ // is set — use it over the local pricing-table estimate (F010).
930
+ costFromResponseField: true
571
931
  });
572
932
  }
573
933
 
@@ -911,6 +1271,8 @@ var chatInputSchema = z.object({
911
1271
  tools: z.array(toolSchema).optional(),
912
1272
  maxTokens: z.number().int().positive().optional(),
913
1273
  temperature: z.number().min(0).max(2).optional(),
1274
+ /** "json" requests JSON-object output (OpenAI-compatible response_format). */
1275
+ responseFormat: z.enum(["json", "text"]).optional(),
914
1276
  ...callOptions
915
1277
  });
916
1278
  var visionInputSchema = z.object({
@@ -1029,6 +1391,73 @@ function createAI(config = {}) {
1029
1391
  }
1030
1392
  throw lastErr;
1031
1393
  }
1394
+ function eligibleForFallback(e) {
1395
+ const status = e?.status;
1396
+ if (status === void 0) return true;
1397
+ return status === 429 || status >= 500;
1398
+ }
1399
+ function errorEvent(e) {
1400
+ const ev = {
1401
+ type: "error",
1402
+ message: e instanceof Error ? e.message : String(e)
1403
+ };
1404
+ const status = e?.status;
1405
+ if (status !== void 0) ev.status = status;
1406
+ return ev;
1407
+ }
1408
+ async function* chatStreamImpl(input) {
1409
+ input = chatInputSchema.parse(input);
1410
+ const tier = input.tier ?? "smart";
1411
+ const messages = toMessages(input);
1412
+ const estIn = messages.reduce(
1413
+ (n, m) => n + estTokens(typeof m.content === "string" ? m.content : JSON.stringify(m.content)),
1414
+ 0
1415
+ );
1416
+ const estOut = input.maxTokens ?? 512;
1417
+ const routes = [
1418
+ resolveTier(tier, input.override, cfg.defaults),
1419
+ ...(input.fallback ?? []).map(
1420
+ (f) => typeof f === "string" ? resolveTier(f, void 0, cfg.defaults) : f
1421
+ )
1422
+ ];
1423
+ let lastErr;
1424
+ for (let i = 0; i < routes.length; i++) {
1425
+ const spec = routes[i];
1426
+ await preflight(spec, estIn, estOut);
1427
+ const adapter = pickProvider(spec.provider);
1428
+ if (!adapter.chatStream) {
1429
+ throw new Error(`createAI: provider "${spec.provider}" does not support streaming`);
1430
+ }
1431
+ const t0 = performance.now();
1432
+ let emitted = false;
1433
+ try {
1434
+ for await (const ev of adapter.chatStream({
1435
+ messages,
1436
+ spec,
1437
+ tools: input.tools,
1438
+ maxTokens: input.maxTokens,
1439
+ temperature: input.temperature,
1440
+ responseFormat: input.responseFormat
1441
+ })) {
1442
+ if (ev.type === "text" || ev.type === "tool_call") emitted = true;
1443
+ if (ev.type === "usage") {
1444
+ enrich(ev.usage, "chat", i === 0 ? tier : void 0, input.purpose, performance.now() - t0);
1445
+ await settle(ev.usage);
1446
+ await report(ev.usage);
1447
+ }
1448
+ yield ev;
1449
+ }
1450
+ return;
1451
+ } catch (e) {
1452
+ lastErr = e;
1453
+ if (emitted || !eligibleForFallback(e)) {
1454
+ yield errorEvent(e);
1455
+ return;
1456
+ }
1457
+ }
1458
+ }
1459
+ yield errorEvent(lastErr);
1460
+ }
1032
1461
  const client = {
1033
1462
  async chat(input) {
1034
1463
  input = chatInputSchema.parse(input);
@@ -1049,10 +1478,11 @@ function createAI(config = {}) {
1049
1478
  invoke: async (spec) => {
1050
1479
  const adapter = pickProvider(spec.provider);
1051
1480
  if (!adapter.chat) throw new Error(`createAI: provider "${spec.provider}" does not support chat`);
1052
- return adapter.chat({ messages, spec, tools: input.tools, maxTokens: input.maxTokens, temperature: input.temperature });
1481
+ return adapter.chat({ messages, spec, tools: input.tools, maxTokens: input.maxTokens, temperature: input.temperature, responseFormat: input.responseFormat });
1053
1482
  }
1054
1483
  });
1055
1484
  },
1485
+ chatStream: chatStreamImpl,
1056
1486
  async vision(input) {
1057
1487
  input = visionInputSchema.parse(input);
1058
1488
  const tier = input.tier ?? VISION_DEFAULT_TIER;
@@ -1235,8 +1665,8 @@ var stubProviders = {
1235
1665
  };
1236
1666
 
1237
1667
  // src/version.ts
1238
- var VERSION = "0.2.0";
1239
- var SDK_TAG = "@broberg/ai-sdk@0.2.0";
1668
+ var VERSION = "0.3.1";
1669
+ var SDK_TAG = "@broberg/ai-sdk@0.3.1";
1240
1670
 
1241
1671
  // src/cost/budget-store.ts
1242
1672
  function sqliteBudgetStore(config) {
@@ -1465,6 +1895,7 @@ export {
1465
1895
  BudgetGuard,
1466
1896
  DEFAULT_TIER_MAP,
1467
1897
  SDK_TAG,
1898
+ StreamHttpError,
1468
1899
  VERSION,
1469
1900
  aiConfigSchema,
1470
1901
  anthropicAdapter,
@@ -1499,6 +1930,7 @@ export {
1499
1930
  resolveTier,
1500
1931
  sqliteBudgetStore,
1501
1932
  sqliteSink,
1933
+ streamTransport,
1502
1934
  stubProviders,
1503
1935
  subprocessTransport,
1504
1936
  tierSpecSchema,