@yourgpt/llm-sdk 2.6.0-beta.2 → 2.6.0

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.
Files changed (74) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +87 -17
  3. package/dist/adapters/index.d.mts +11 -13
  4. package/dist/adapters/index.d.ts +11 -13
  5. package/dist/adapters/index.js +406 -114
  6. package/dist/adapters/index.mjs +406 -114
  7. package/dist/audio-K-Z3-Bce.d.mts +191 -0
  8. package/dist/audio-K-Z3-Bce.d.ts +191 -0
  9. package/dist/{base-BICaQaRS.d.mts → base-6LzczuCg.d.ts} +8 -1
  10. package/dist/{base-CpRPB8u8.d.ts → base-DySbTP8C.d.mts} +8 -1
  11. package/dist/decisions-CJzOt87t.d.mts +137 -0
  12. package/dist/decisions-CJzOt87t.d.ts +137 -0
  13. package/dist/fallback/index.d.mts +4 -4
  14. package/dist/fallback/index.d.ts +4 -4
  15. package/dist/http-BQxUT5A9.d.mts +9 -0
  16. package/dist/http-BQxUT5A9.d.ts +9 -0
  17. package/dist/index.d.mts +17 -9
  18. package/dist/index.d.ts +17 -9
  19. package/dist/index.js +698 -205
  20. package/dist/index.mjs +695 -206
  21. package/dist/providers/anthropic/index.d.mts +7 -4
  22. package/dist/providers/anthropic/index.d.ts +7 -4
  23. package/dist/providers/anthropic/index.js +389 -115
  24. package/dist/providers/anthropic/index.mjs +389 -115
  25. package/dist/providers/azure/index.d.mts +3 -3
  26. package/dist/providers/azure/index.d.ts +3 -3
  27. package/dist/providers/cloudflare/index.d.mts +11 -0
  28. package/dist/providers/cloudflare/index.d.ts +11 -0
  29. package/dist/providers/cloudflare/index.js +743 -0
  30. package/dist/providers/cloudflare/index.mjs +741 -0
  31. package/dist/providers/fireworks/index.d.mts +3 -3
  32. package/dist/providers/fireworks/index.d.ts +3 -3
  33. package/dist/providers/fireworks/index.js +181 -60
  34. package/dist/providers/fireworks/index.mjs +181 -60
  35. package/dist/providers/google/index.d.mts +3 -3
  36. package/dist/providers/google/index.d.ts +3 -3
  37. package/dist/providers/google/index.js +181 -60
  38. package/dist/providers/google/index.mjs +181 -60
  39. package/dist/providers/ollama/index.d.mts +4 -4
  40. package/dist/providers/ollama/index.d.ts +4 -4
  41. package/dist/providers/openai/index.d.mts +38 -6
  42. package/dist/providers/openai/index.d.ts +38 -6
  43. package/dist/providers/openai/index.js +1451 -85
  44. package/dist/providers/openai/index.mjs +1449 -86
  45. package/dist/providers/openrouter/index.d.mts +21 -4
  46. package/dist/providers/openrouter/index.d.ts +21 -4
  47. package/dist/providers/openrouter/index.js +1040 -131
  48. package/dist/providers/openrouter/index.mjs +1040 -132
  49. package/dist/providers/togetherai/index.d.mts +3 -3
  50. package/dist/providers/togetherai/index.d.ts +3 -3
  51. package/dist/providers/togetherai/index.js +181 -60
  52. package/dist/providers/togetherai/index.mjs +181 -60
  53. package/dist/providers/typesafe/index.d.mts +8 -0
  54. package/dist/providers/typesafe/index.d.ts +8 -0
  55. package/dist/providers/typesafe/index.js +735 -0
  56. package/dist/providers/typesafe/index.mjs +733 -0
  57. package/dist/providers/xai/index.d.mts +3 -3
  58. package/dist/providers/xai/index.d.ts +3 -3
  59. package/dist/providers/xai/index.js +181 -60
  60. package/dist/providers/xai/index.mjs +181 -60
  61. package/dist/{types-DCFqkJAy.d.ts → types-B0y6VZSQ.d.ts} +1 -1
  62. package/dist/{types-DHQEMFHA.d.mts → types-BXQMtaCX.d.ts} +17 -3
  63. package/dist/{types-C3RgRbEC.d.mts → types-BY6BVhFg.d.mts} +1 -1
  64. package/dist/{types-Dikj3En4.d.ts → types-C7_qVSSd.d.mts} +17 -3
  65. package/dist/{types-Dg1Bcg1k.d.mts → types-D7lzMMLu.d.mts} +13 -1
  66. package/dist/{types-Dg1Bcg1k.d.ts → types-D7lzMMLu.d.ts} +13 -1
  67. package/dist/yourgpt/index.d.mts +1 -1
  68. package/dist/yourgpt/index.d.ts +1 -1
  69. package/docs/decisions.md +174 -0
  70. package/docs/media.md +201 -0
  71. package/docs/multimodal-and-audio.md +9 -2
  72. package/package.json +34 -12
  73. package/dist/audio-BBSwnjo8.d.mts +0 -92
  74. package/dist/audio-BBSwnjo8.d.ts +0 -92
package/dist/index.mjs CHANGED
@@ -1,6 +1,56 @@
1
1
  import { Hono } from 'hono';
2
2
  import { cors } from 'hono/cors';
3
3
 
4
+ // src/core/openai-responses.ts
5
+ function responseReplayMetadata(metadata) {
6
+ const response = metadata?.openai?.response;
7
+ if (!response) return metadata;
8
+ const output = response.output.map((item) => {
9
+ const fields = ["type", "id", "status"];
10
+ switch (item.type) {
11
+ case "reasoning":
12
+ fields.push("encrypted_content");
13
+ break;
14
+ case "message":
15
+ fields.push("role", "phase");
16
+ break;
17
+ case "function_call":
18
+ fields.push("call_id", "name", "namespace", "arguments");
19
+ break;
20
+ case "tool_search_call":
21
+ fields.push("call_id", "execution", "arguments");
22
+ break;
23
+ case "tool_search_output":
24
+ fields.push("call_id", "tools");
25
+ break;
26
+ case "web_search_call":
27
+ fields.push("action");
28
+ break;
29
+ default:
30
+ return stripItemDiagnostics(item);
31
+ }
32
+ const replay = Object.fromEntries(
33
+ fields.filter((key) => item[key] !== void 0).map((key) => [key, item[key]])
34
+ );
35
+ if (item.type === "reasoning") replay.summary = [];
36
+ if (item.type === "message")
37
+ replay.content = Array.isArray(item.content) ? item.content.flatMap(
38
+ (part) => part?.type === "output_text" && typeof part.text === "string" ? [{ type: "output_text", text: part.text, annotations: [] }] : part?.type === "refusal" && typeof part.refusal === "string" ? [{ type: "refusal", refusal: part.refusal }] : []
39
+ ) : [];
40
+ return stripItemDiagnostics(replay);
41
+ });
42
+ return { openai: { response: { output, refusals: [] } } };
43
+ }
44
+ function stripItemDiagnostics(item) {
45
+ const {
46
+ usage: _usage,
47
+ logprobs: _logprobs,
48
+ metadata: _metadata,
49
+ ...state
50
+ } = item;
51
+ return state;
52
+ }
53
+
4
54
  // src/core/tool-output.ts
5
55
  var imageTypes = /* @__PURE__ */ new Set([
6
56
  "image/png",
@@ -100,7 +150,22 @@ function zodToJsonSchema(schema) {
100
150
  case "ZodString":
101
151
  return { type: "string", description };
102
152
  case "ZodNumber":
103
- return { type: "number", description };
153
+ return {
154
+ type: def.checks?.some(
155
+ (check) => check.kind === "int"
156
+ ) ? "integer" : "number",
157
+ description
158
+ };
159
+ case "ZodNull":
160
+ return { type: "null", description };
161
+ case "ZodAny":
162
+ case "ZodUnknown":
163
+ return { description };
164
+ case "ZodEffects":
165
+ return {
166
+ ...zodToJsonSchema(def.schema),
167
+ ...description ? { description } : {}
168
+ };
104
169
  case "ZodBoolean":
105
170
  return { type: "boolean", description };
106
171
  case "ZodEnum": {
@@ -121,10 +186,13 @@ function zodToJsonSchema(schema) {
121
186
  const properties = {};
122
187
  const required = [];
123
188
  for (const [key, value] of Object.entries(shapeObj)) {
124
- properties[key] = zodToJsonSchema(value);
125
- const valueDef = value._def;
126
- const isOptional = valueDef.typeName === "ZodOptional" || valueDef.typeName === "ZodNullable" || valueDef.typeName === "ZodDefault";
127
- if (!isOptional) {
189
+ Object.defineProperty(properties, key, {
190
+ value: zodToJsonSchema(value),
191
+ enumerable: true,
192
+ configurable: true,
193
+ writable: true
194
+ });
195
+ if (!acceptsMissingInput(value)) {
128
196
  required.push(key);
129
197
  }
130
198
  }
@@ -135,8 +203,13 @@ function zodToJsonSchema(schema) {
135
203
  description
136
204
  };
137
205
  }
138
- case "ZodOptional":
139
206
  case "ZodNullable": {
207
+ return {
208
+ anyOf: [zodToJsonSchema(def.innerType), { type: "null" }],
209
+ description
210
+ };
211
+ }
212
+ case "ZodOptional": {
140
213
  const innerType = def.innerType;
141
214
  return zodToJsonSchema(innerType);
142
215
  }
@@ -146,6 +219,7 @@ function zodToJsonSchema(schema) {
146
219
  }
147
220
  case "ZodLiteral": {
148
221
  const value = def.value;
222
+ if (value === null) return { type: "null", description };
149
223
  if (typeof value === "string") {
150
224
  return { type: "string", enum: [value], description };
151
225
  }
@@ -169,12 +243,21 @@ function zodToJsonSchema(schema) {
169
243
  if (stringLiterals.length === options.length) {
170
244
  return { type: "string", enum: stringLiterals, description };
171
245
  }
172
- return zodToJsonSchema(options[0]);
246
+ return { anyOf: options.map(zodToJsonSchema), description };
173
247
  }
174
248
  default:
175
249
  return { type: "string", description };
176
250
  }
177
251
  }
252
+ function acceptsMissingInput(schema) {
253
+ const def = schema._def;
254
+ if (["ZodOptional", "ZodDefault", "ZodAny", "ZodUnknown"].includes(def.typeName))
255
+ return true;
256
+ if (def.typeName === "ZodNullable") return acceptsMissingInput(def.innerType);
257
+ if (def.typeName === "ZodEffects") return acceptsMissingInput(def.schema);
258
+ if (def.typeName === "ZodUnion") return def.options.some(acceptsMissingInput);
259
+ return false;
260
+ }
178
261
  function formatToolsForOpenAI(tools) {
179
262
  return Object.entries(tools).map(([name, toolDef]) => ({
180
263
  type: "function",
@@ -205,6 +288,27 @@ function formatToolsForGoogle(tools) {
205
288
  }
206
289
 
207
290
  // src/core/provider-error.ts
291
+ function invalidProviderResponse(message) {
292
+ return Object.assign(new Error(message), {
293
+ name: "InvalidProviderResponseError",
294
+ status: 502,
295
+ code: "INVALID_PROVIDER_RESPONSE"
296
+ });
297
+ }
298
+ function providerErrorEvent(error, code) {
299
+ const details = error ?? {};
300
+ const event = {
301
+ type: "error",
302
+ message: error instanceof Error ? error.message : String(error),
303
+ code,
304
+ ...typeof details.status === "number" ? { status: details.status } : {},
305
+ ...typeof details.code === "string" ? { providerCode: details.code } : {},
306
+ ...typeof details.param === "string" ? { param: details.param } : {},
307
+ ...typeof details.request_id === "string" ? { requestId: details.request_id } : {}
308
+ };
309
+ Object.defineProperty(event, "cause", { value: error, enumerable: false });
310
+ return event;
311
+ }
208
312
  function throwIfAborted(signal) {
209
313
  if (!signal?.aborted) return;
210
314
  const error = new Error("The operation was aborted");
@@ -212,9 +316,42 @@ function throwIfAborted(signal) {
212
316
  throw error;
213
317
  }
214
318
 
319
+ // src/core/tool-validation.ts
320
+ function parseToolArguments(value) {
321
+ let args = value;
322
+ if (typeof value === "string") {
323
+ try {
324
+ args = JSON.parse(value || "{}");
325
+ } catch {
326
+ throw invalidProviderResponse("Tool arguments are not valid JSON");
327
+ }
328
+ }
329
+ if (args === null || typeof args !== "object" || Array.isArray(args))
330
+ throw invalidProviderResponse("Tool arguments must be a JSON object");
331
+ return args;
332
+ }
333
+ function validateToolCalls(calls) {
334
+ const ids = /* @__PURE__ */ new Set();
335
+ for (const call of calls) {
336
+ if (!call || typeof call.id !== "string" || !call.id || typeof call.name !== "string" || !call.name || ids.has(call.id))
337
+ throw invalidProviderResponse(
338
+ "Tool calls have missing or duplicate identities"
339
+ );
340
+ ids.add(call.id);
341
+ if (typeof call.args === "string")
342
+ throw invalidProviderResponse("Parsed tool arguments must be an object");
343
+ parseToolArguments(call.args);
344
+ }
345
+ }
346
+ function validateMaxSteps(value) {
347
+ if (!Number.isSafeInteger(value) || value < 1)
348
+ throw new Error("maxSteps must be a positive safe integer");
349
+ }
350
+
215
351
  // src/core/generate-text.ts
216
352
  async function generateText(params) {
217
353
  const { model, tools, maxSteps = 1, signal } = params;
354
+ validateMaxSteps(maxSteps);
218
355
  if (params.responseFormat && model.capabilities.supportsJsonMode === false) {
219
356
  console.warn(
220
357
  `[llm-sdk] ${model.provider}/${model.modelId} does not support structured output (responseFormat); the request will be sent but the provider may ignore it.`
@@ -238,12 +375,14 @@ async function generateText(params) {
238
375
  signal
239
376
  });
240
377
  throwIfAborted(signal);
378
+ const toolCalls = result.toolCalls ?? [];
379
+ validateToolCalls(toolCalls);
380
+ allToolCalls.push(...toolCalls);
241
381
  const stepToolResults = [];
242
- if (result.toolCalls && result.toolCalls.length > 0 && tools) {
243
- allToolCalls.push(...result.toolCalls);
244
- for (const call of result.toolCalls) {
382
+ if (toolCalls.length > 0 && tools) {
383
+ for (const call of toolCalls) {
245
384
  throwIfAborted(signal);
246
- const toolDef = tools[call.name];
385
+ const toolDef = Object.prototype.hasOwnProperty.call(tools, call.name) ? tools[call.name] : void 0;
247
386
  if (!toolDef) {
248
387
  const errorResult = {
249
388
  toolCallId: call.id,
@@ -286,7 +425,8 @@ async function generateText(params) {
286
425
  }
287
426
  steps.push({
288
427
  text: result.text,
289
- toolCalls: result.toolCalls,
428
+ ...result.reasoningText !== void 0 ? { reasoningText: result.reasoningText } : {},
429
+ toolCalls,
290
430
  toolResults: stepToolResults,
291
431
  finishReason: result.finishReason,
292
432
  usage: result.usage,
@@ -295,16 +435,16 @@ async function generateText(params) {
295
435
  const assistantMessage = {
296
436
  role: "assistant",
297
437
  content: result.text || null,
298
- ...result.toolCalls.length ? { toolCalls: result.toolCalls } : {},
299
- ...result.providerMetadata ? { providerMetadata: result.providerMetadata } : {}
438
+ ...toolCalls.length ? { toolCalls } : {},
439
+ ...result.providerMetadata ? { providerMetadata: responseReplayMetadata(result.providerMetadata) } : {}
300
440
  };
301
441
  messages = [...messages, assistantMessage];
302
- if (!result.toolCalls || result.toolCalls.length === 0) break;
442
+ if (toolCalls.length === 0 || !tools) break;
303
443
  for (const tr of stepToolResults) {
304
444
  const toolMessage = {
305
445
  role: "tool",
306
446
  toolCallId: tr.toolCallId,
307
- content: tr.modelOutput ? "[Tool returned content]" : JSON.stringify(tr.result),
447
+ content: tr.modelOutput ? "[Tool returned content]" : JSON.stringify(tr.result ?? null),
308
448
  ...tr.modelOutput ? { modelOutput: tr.modelOutput } : {}
309
449
  };
310
450
  messages = [...messages, toolMessage];
@@ -313,6 +453,7 @@ async function generateText(params) {
313
453
  const lastStep = steps[steps.length - 1];
314
454
  return {
315
455
  text: lastStep?.text ?? "",
456
+ ...lastStep?.reasoningText !== void 0 ? { reasoningText: lastStep.reasoningText } : {},
316
457
  providerMetadata: lastStep?.providerMetadata,
317
458
  usage: sumUsage(steps),
318
459
  finishReason: lastStep?.finishReason ?? "stop",
@@ -359,9 +500,250 @@ function sumUsage(steps) {
359
500
  );
360
501
  }
361
502
 
503
+ // src/core/image.ts
504
+ async function generateImage({
505
+ model,
506
+ ...options
507
+ }) {
508
+ options.signal?.throwIfAborted();
509
+ if (typeof options.prompt !== "string" || !options.prompt.trim())
510
+ throw new MediaAPIError("Image prompt must not be empty", "invalid_input");
511
+ const result = await model.doGenerate(options);
512
+ options.signal?.throwIfAborted();
513
+ if (!result.images?.length || result.images.some(
514
+ (image) => !(image.uint8Array instanceof Uint8Array) || !image.uint8Array.length
515
+ ))
516
+ throw new MediaAPIError(
517
+ "Image provider returned empty images",
518
+ "invalid_response"
519
+ );
520
+ return result;
521
+ }
522
+ var marker = /* @__PURE__ */ Symbol.for("@yourgpt/llm-sdk/MediaAPIError");
523
+ var _a;
524
+ var MediaAPIError = class extends Error {
525
+ constructor(message, code, status, requestId) {
526
+ super(message);
527
+ this.code = code;
528
+ this.status = status;
529
+ this.requestId = requestId;
530
+ this[_a] = true;
531
+ this.name = "MediaAPIError";
532
+ }
533
+ static [(_a = marker, Symbol.hasInstance)](value) {
534
+ return !!value && typeof value === "object" && value[marker] === true;
535
+ }
536
+ };
537
+
538
+ // src/core/decision-validation.ts
539
+ function decisionInputError(message) {
540
+ throw new DecisionAPIError(message, "invalid_input");
541
+ }
542
+ function decisionResponseError(message) {
543
+ throw new DecisionAPIError(message, "invalid_response");
544
+ }
545
+ function isDecisionRecord(value) {
546
+ if (value === null || typeof value !== "object" || Array.isArray(value))
547
+ return false;
548
+ const proto = Object.getPrototypeOf(value);
549
+ return proto === Object.prototype || proto === null;
550
+ }
551
+ function json(value, seen = /* @__PURE__ */ new Set(), depth = 0) {
552
+ if (depth > 64) return false;
553
+ if (value === null || typeof value === "string" || typeof value === "boolean")
554
+ return true;
555
+ if (typeof value === "number") return Number.isFinite(value);
556
+ if (typeof value !== "object" || !Array.isArray(value) && !isDecisionRecord(value) || seen.has(value))
557
+ return false;
558
+ seen.add(value);
559
+ const valid = Object.getOwnPropertySymbols(value).length === 0 && (Array.isArray(value) ? Array.from(value) : Object.values(value)).every(
560
+ (item) => json(item, seen, depth + 1)
561
+ );
562
+ seen.delete(value);
563
+ return valid;
564
+ }
565
+ function input(value) {
566
+ return (typeof value === "string" || Array.isArray(value) || isDecisionRecord(value)) && json(value);
567
+ }
568
+ function assertDecisionKeys(value, allowed, label) {
569
+ if (Object.keys(value).some((key) => !allowed.includes(key)))
570
+ decisionInputError(`${label} contains an unsupported field`);
571
+ }
572
+ function validateDecisionInput(options) {
573
+ if (!isDecisionRecord(options))
574
+ decisionInputError("Decisions options must be an object");
575
+ assertDecisionKeys(
576
+ options,
577
+ ["state", "questions", "images", "signal", "timeoutMs", "providerOptions"],
578
+ "Decisions options"
579
+ );
580
+ if (!input(options.state))
581
+ decisionInputError(
582
+ "state must be finite, acyclic JSON text, object, or array (maximum depth 64)"
583
+ );
584
+ if (!isDecisionRecord(options.questions) || Object.keys(options.questions).length === 0)
585
+ decisionInputError("questions must be a nonempty map");
586
+ for (const [id, question] of Object.entries(options.questions)) {
587
+ if (!id.length || !isDecisionRecord(question) || !input(question.instructions))
588
+ decisionInputError(
589
+ "Every question needs a nonempty ID and JSON-compatible instructions"
590
+ );
591
+ assertDecisionKeys(
592
+ question,
593
+ ["type", "instructions", "criteria"],
594
+ "Question"
595
+ );
596
+ if (question.type === "boolean") {
597
+ if (question.criteria !== void 0 && (!isDecisionRecord(question.criteria) || Object.entries(question.criteria).some(
598
+ ([key, value]) => !["true", "false"].includes(key) || !input(value)
599
+ )))
600
+ decisionInputError("Boolean criteria may describe only true and false");
601
+ } else if (question.type === "choice") {
602
+ if (!isDecisionRecord(question.criteria) || Object.keys(question.criteria).length < 2 || Object.keys(question.criteria).length > 255 || Object.entries(question.criteria).some(
603
+ ([key, value]) => !key.length || value !== null && !input(value)
604
+ ))
605
+ decisionInputError(
606
+ "Choice criteria require 2\u2013255 named options with JSON descriptions or null"
607
+ );
608
+ } else if (question.type === "score") {
609
+ if (!Array.isArray(question.criteria) || question.criteria.length < 2 || !Array.from(question.criteria).every(input))
610
+ decisionInputError(
611
+ "Score criteria require at least two ordered JSON descriptions"
612
+ );
613
+ } else
614
+ decisionInputError("Question type must be boolean, choice, or score");
615
+ }
616
+ if (options.timeoutMs !== void 0 && (!Number.isInteger(options.timeoutMs) || options.timeoutMs < 1 || options.timeoutMs > 2147483647))
617
+ decisionInputError("timeoutMs must be a positive 32-bit integer");
618
+ if (options.providerOptions !== void 0 && (!isDecisionRecord(options.providerOptions) || !json(options.providerOptions)))
619
+ decisionInputError("providerOptions must be a JSON object");
620
+ if (options.images !== void 0 && (!Array.isArray(options.images) || !Array.from(options.images).every(
621
+ (image) => isDecisionRecord(image) && image.data instanceof Uint8Array
622
+ )))
623
+ decisionInputError(
624
+ "Decision images must contain encoded bytes; hosted image URLs are unsupported"
625
+ );
626
+ }
627
+ function probability(value) {
628
+ return typeof value === "number" && Number.isFinite(value) && value >= 0 && value <= 1;
629
+ }
630
+ function exactKeys(value, keys) {
631
+ return Object.keys(value).length === keys.length && keys.every((key) => Object.prototype.hasOwnProperty.call(value, key));
632
+ }
633
+ function validateDecisionResult(questions, result) {
634
+ if (!isDecisionRecord(result) || !isDecisionRecord(result.answers) || !exactKeys(result.answers, Object.keys(questions)))
635
+ decisionResponseError(
636
+ "Decisions must return exactly one answer per question"
637
+ );
638
+ const rounding = result.rounding;
639
+ let pError = 0, sError = 0;
640
+ if (rounding !== void 0) {
641
+ if (!isDecisionRecord(rounding) || ![rounding.probabilityDecimals, rounding.scoreDecimals].every(
642
+ (v) => typeof v === "number" && Number.isInteger(v) && v >= 0 && v <= 15
643
+ ))
644
+ decisionResponseError("Invalid decision rounding precision");
645
+ pError = 0.5 * 10 ** -rounding.probabilityDecimals;
646
+ sError = 0.5 * 10 ** -rounding.scoreDecimals;
647
+ }
648
+ const epsilon = 1e-6;
649
+ for (const [id, question] of Object.entries(questions)) {
650
+ const answer = result.answers[id];
651
+ if (!isDecisionRecord(answer))
652
+ decisionResponseError("Invalid decision answer");
653
+ if (answer.type === "refusal") continue;
654
+ if (answer.type !== question.type)
655
+ decisionResponseError("Decision answer type does not match its question");
656
+ if (answer.confidence !== void 0 && !probability(answer.confidence))
657
+ decisionResponseError(
658
+ "Confidence must be finite and between zero and one"
659
+ );
660
+ if (question.type === "boolean") {
661
+ if (!probability(answer.probability))
662
+ decisionResponseError(
663
+ "Boolean probability must be between zero and one"
664
+ );
665
+ continue;
666
+ }
667
+ const keys = question.type === "choice" ? Object.keys(question.criteria) : question.criteria.map((_, index) => String(index));
668
+ if (question.type === "choice" && (typeof answer.choice !== "string" || !keys.includes(answer.choice)))
669
+ decisionResponseError("Decision selected an unknown choice");
670
+ if (question.type === "score" && (typeof answer.score !== "number" || !Number.isFinite(answer.score) || answer.score < 0 || answer.score > keys.length - 1))
671
+ decisionResponseError("Decision score is outside its ordered levels");
672
+ if (answer.probabilities === void 0) continue;
673
+ const distribution = answer.probabilities;
674
+ if (!isDecisionRecord(distribution) || !exactKeys(distribution, keys) || !Object.values(distribution).every(probability))
675
+ decisionResponseError(
676
+ "Decision distribution must contain every option exactly once with finite probabilities"
677
+ );
678
+ const values = Object.values(distribution);
679
+ if (Math.abs(values.reduce((a, b) => a + b, 0) - 1) > epsilon + keys.length * pError)
680
+ decisionResponseError("Decision probabilities do not sum to one");
681
+ if (question.type === "choice") {
682
+ if (values.some(
683
+ (v) => v > distribution[answer.choice] + epsilon + 2 * pError
684
+ ))
685
+ decisionResponseError(
686
+ "Decision choice is not a highest-probability option"
687
+ );
688
+ } else {
689
+ const mean = keys.reduce(
690
+ (sum, key) => sum + Number(key) * distribution[key],
691
+ 0
692
+ );
693
+ const tolerance = epsilon + sError + pError * keys.reduce((sum, key) => sum + Number(key), 0);
694
+ if (Math.abs(answer.score - mean) > tolerance)
695
+ decisionResponseError(
696
+ "Decision score does not match its probability-weighted levels"
697
+ );
698
+ }
699
+ }
700
+ }
701
+
702
+ // src/core/decisions.ts
703
+ var decisionErrorMarker = /* @__PURE__ */ Symbol.for("yourgpt.llm-sdk.DecisionAPIError");
704
+ var _a2, _b;
705
+ var DecisionAPIError = class extends (_b = Error, _a2 = decisionErrorMarker, _b) {
706
+ constructor(message, code, status, requestId) {
707
+ super(message);
708
+ this.code = code;
709
+ this.status = status;
710
+ this.requestId = requestId;
711
+ this.name = "DecisionAPIError";
712
+ this[_a2] = true;
713
+ }
714
+ /** Works across CJS/ESM and separately bundled provider entry points. */
715
+ static isInstance(error) {
716
+ return typeof error === "object" && error !== null && /* @__PURE__ */ Symbol.for("yourgpt.llm-sdk.DecisionAPIError") in error;
717
+ }
718
+ /** Advisory only: this SDK does not retry Decisions automatically. */
719
+ get retryable() {
720
+ return this.code === "network_error" || this.code === "http_error" && (this.status === 408 || this.status === 429 || this.status != null && this.status >= 500);
721
+ }
722
+ };
723
+ async function experimental_decide(options) {
724
+ const { model, ...request } = options;
725
+ validateDecisionInput(request);
726
+ request.signal?.throwIfAborted();
727
+ const result = await model.doDecide(request);
728
+ request.signal?.throwIfAborted();
729
+ validateDecisionResult(request.questions, result);
730
+ return result;
731
+ }
732
+
362
733
  // src/core/stream-text.ts
363
734
  async function streamText(params) {
364
- const { model, tools, maxSteps = 1, signal } = params;
735
+ const { model, tools, maxSteps = 1, signal: callerSignal } = params;
736
+ validateMaxSteps(maxSteps);
737
+ const controller = new AbortController();
738
+ const signal = controller.signal;
739
+ const relayAbort = () => controller.abort(callerSignal?.reason);
740
+ if (callerSignal?.aborted) relayAbort();
741
+ else callerSignal?.addEventListener("abort", relayAbort, { once: true });
742
+ const cleanup = () => callerSignal?.removeEventListener("abort", relayAbort);
743
+ const cancel = () => {
744
+ controller.abort();
745
+ cleanup();
746
+ };
365
747
  if (params.responseFormat && model.capabilities.supportsJsonMode === false) {
366
748
  console.warn(
367
749
  `[llm-sdk] ${model.provider}/${model.modelId} does not support structured output (responseFormat); the request will be sent but the provider may ignore it.`
@@ -388,6 +770,7 @@ async function streamText(params) {
388
770
  let stepProviderMetadata;
389
771
  const toolCalls = [];
390
772
  let stepFinishReason = "stop";
773
+ let receivedFinish = false;
391
774
  try {
392
775
  for await (const chunk of model.doStream({
393
776
  messages,
@@ -401,6 +784,9 @@ async function streamText(params) {
401
784
  })) {
402
785
  throwIfAborted(signal);
403
786
  switch (chunk.type) {
787
+ case "reasoning-delta":
788
+ yield chunk;
789
+ break;
404
790
  case "text-delta":
405
791
  stepText += chunk.text;
406
792
  fullText += chunk.text;
@@ -414,6 +800,11 @@ async function streamText(params) {
414
800
  };
415
801
  break;
416
802
  case "finish":
803
+ if (receivedFinish)
804
+ throw invalidProviderResponse(
805
+ "Model stream returned multiple finish events"
806
+ );
807
+ receivedFinish = true;
417
808
  stepProviderMetadata = chunk.providerMetadata;
418
809
  finalProviderMetadata = chunk.providerMetadata;
419
810
  stepFinishReason = chunk.finishReason;
@@ -431,6 +822,12 @@ async function streamText(params) {
431
822
  return;
432
823
  }
433
824
  }
825
+ throwIfAborted(signal);
826
+ if (!receivedFinish)
827
+ throw invalidProviderResponse(
828
+ "Model stream ended without a finish event"
829
+ );
830
+ validateToolCalls(toolCalls);
434
831
  } catch (error) {
435
832
  yield {
436
833
  type: "error",
@@ -450,13 +847,13 @@ async function streamText(params) {
450
847
  role: "assistant",
451
848
  content: stepText || null,
452
849
  ...toolCalls.length ? { toolCalls } : {},
453
- ...stepProviderMetadata ? { providerMetadata: stepProviderMetadata } : {}
850
+ ...stepProviderMetadata ? { providerMetadata: responseReplayMetadata(stepProviderMetadata) } : {}
454
851
  }
455
852
  ];
456
853
  if (toolCalls.length === 0 || !tools) break;
457
854
  for (const call of toolCalls) {
458
855
  throwIfAborted(signal);
459
- const toolDef = tools[call.name];
856
+ const toolDef = Object.prototype.hasOwnProperty.call(tools, call.name) ? tools[call.name] : void 0;
460
857
  if (!toolDef) {
461
858
  const errorResult = { error: `Tool not found: ${call.name}` };
462
859
  yield {
@@ -497,7 +894,7 @@ async function streamText(params) {
497
894
  {
498
895
  role: "tool",
499
896
  toolCallId: call.id,
500
- content: modelOutput ? "[Tool returned content]" : JSON.stringify(result),
897
+ content: modelOutput ? "[Tool returned content]" : JSON.stringify(result ?? null),
501
898
  ...modelOutput ? { modelOutput } : {}
502
899
  }
503
900
  ];
@@ -539,11 +936,17 @@ async function streamText(params) {
539
936
  pending = (async () => {
540
937
  try {
541
938
  const next = await source.next();
542
- if (next.done) finished = true;
543
- else parts.push(next.value);
939
+ if (next.done) {
940
+ finished = true;
941
+ cleanup();
942
+ } else {
943
+ parts.push(next.value);
944
+ if (next.value.type === "error") cleanup();
945
+ }
544
946
  } catch (error) {
545
947
  failure = error;
546
948
  finished = true;
949
+ cleanup();
547
950
  }
548
951
  })();
549
952
  }
@@ -594,7 +997,7 @@ async function streamText(params) {
594
997
  return consumeStream().then(() => finalFinishReason);
595
998
  },
596
999
  toTextStreamResponse(options) {
597
- const stream = createTextStreamReadable(createTextStream());
1000
+ const stream = createTextStreamReadable(createTextStream(), cancel);
598
1001
  return new Response(stream, {
599
1002
  status: options?.status ?? 200,
600
1003
  headers: {
@@ -606,7 +1009,7 @@ async function streamText(params) {
606
1009
  });
607
1010
  },
608
1011
  toDataStreamResponse(options) {
609
- const stream = createDataStreamReadable(sharedStream());
1012
+ const stream = createDataStreamReadable(sharedStream(), cancel);
610
1013
  return new Response(stream, {
611
1014
  status: options?.status ?? 200,
612
1015
  headers: {
@@ -643,35 +1046,51 @@ function formatToolsForProvider2(tools, provider) {
643
1046
  return formatToolsForOpenAI(tools);
644
1047
  }
645
1048
  }
646
- function createTextStreamReadable(textStream) {
1049
+ function createTextStreamReadable(textStream, abort) {
647
1050
  const encoder = new TextEncoder();
1051
+ const iterator = textStream[Symbol.asyncIterator]();
1052
+ let cancelled = false;
648
1053
  return new ReadableStream({
649
- async start(controller) {
1054
+ async pull(controller) {
650
1055
  try {
651
- for await (const text of textStream) {
652
- controller.enqueue(encoder.encode(text));
653
- }
654
- controller.close();
1056
+ const next = await iterator.next();
1057
+ if (cancelled) return;
1058
+ if (next.done) controller.close();
1059
+ else controller.enqueue(encoder.encode(next.value));
655
1060
  } catch (error) {
656
- controller.error(error);
1061
+ if (!cancelled) controller.error(error);
657
1062
  }
1063
+ },
1064
+ cancel() {
1065
+ cancelled = true;
1066
+ abort();
1067
+ void iterator.return?.().catch(() => {
1068
+ });
658
1069
  }
659
1070
  });
660
1071
  }
661
- function createDataStreamReadable(fullStream) {
1072
+ function createDataStreamReadable(fullStream, abort) {
662
1073
  const encoder = new TextEncoder();
1074
+ const iterator = fullStream[Symbol.asyncIterator]();
1075
+ let cancelled = false;
663
1076
  return new ReadableStream({
664
- async start(controller) {
1077
+ async pull(controller) {
665
1078
  try {
666
- for await (const part of fullStream) {
667
- const data = JSON.stringify(part);
668
- controller.enqueue(encoder.encode(`data: ${data}
669
-
670
- `));
1079
+ const next = await iterator.next();
1080
+ if (cancelled) return;
1081
+ if (next.done) {
1082
+ controller.enqueue(encoder.encode("data: [DONE]\n\n"));
1083
+ controller.close();
1084
+ return;
671
1085
  }
672
- controller.enqueue(encoder.encode("data: [DONE]\n\n"));
673
- controller.close();
1086
+ if (next.value.type === "error") throw next.value.error;
1087
+ controller.enqueue(
1088
+ encoder.encode(`data: ${JSON.stringify(next.value)}
1089
+
1090
+ `)
1091
+ );
674
1092
  } catch (error) {
1093
+ if (cancelled) return;
675
1094
  const errorData = JSON.stringify({
676
1095
  type: "error",
677
1096
  error: error instanceof Error ? error.message : String(error)
@@ -680,7 +1099,15 @@ function createDataStreamReadable(fullStream) {
680
1099
 
681
1100
  `));
682
1101
  controller.close();
1102
+ void iterator.return?.().catch(() => {
1103
+ });
683
1104
  }
1105
+ },
1106
+ cancel() {
1107
+ cancelled = true;
1108
+ abort();
1109
+ void iterator.return?.().catch(() => {
1110
+ });
684
1111
  }
685
1112
  });
686
1113
  }
@@ -782,17 +1209,32 @@ function createTextStreamHeaders() {
782
1209
  };
783
1210
  }
784
1211
  function formatSSEData(event) {
785
- return `data: ${JSON.stringify(event)}
1212
+ const safeEvent = { ...event };
1213
+ if ("providerMetadata" in safeEvent)
1214
+ safeEvent.providerMetadata = responseReplayMetadata(
1215
+ safeEvent.providerMetadata
1216
+ );
1217
+ if (safeEvent.type === "done" && safeEvent.messages)
1218
+ safeEvent.messages = safeEvent.messages.map((message) => ({
1219
+ ...message,
1220
+ ...message.providerMetadata ? { providerMetadata: responseReplayMetadata(message.providerMetadata) } : {}
1221
+ }));
1222
+ return `data: ${JSON.stringify(safeEvent)}
786
1223
 
787
1224
  `;
788
1225
  }
789
- function createEventStream(generator) {
1226
+ function createEventStream(generator, options) {
790
1227
  const encoder = new TextEncoder();
791
1228
  return new ReadableStream({
792
1229
  async start(controller) {
793
1230
  try {
794
1231
  for await (const event of generator) {
795
- const data = formatSSEData(event);
1232
+ let clientEvent = event;
1233
+ if (event.type === "done" && !options?.includeUsage) {
1234
+ const { usage: _usage, ...withoutUsage } = event;
1235
+ clientEvent = withoutUsage;
1236
+ }
1237
+ const data = formatSSEData(clientEvent);
796
1238
  controller.enqueue(encoder.encode(data));
797
1239
  }
798
1240
  } catch (error) {
@@ -933,7 +1375,9 @@ var StreamResult = class {
933
1375
  ...createSSEHeaders(),
934
1376
  ...options?.headers
935
1377
  };
936
- return new Response(createEventStream(this.generator), { headers });
1378
+ return new Response(createEventStream(this.generator, options), {
1379
+ headers
1380
+ });
937
1381
  }
938
1382
  /**
939
1383
  * Alias for toResponse() - returns SSE Response
@@ -1199,6 +1643,9 @@ var StreamResult = class {
1199
1643
  }
1200
1644
  break;
1201
1645
  }
1646
+ case "error":
1647
+ collected.error = { message: event.message, code: event.code };
1648
+ break;
1202
1649
  case "done":
1203
1650
  if (event.messages) {
1204
1651
  collected.messages.push(...event.messages);
@@ -1665,7 +2112,7 @@ function buildToolResultForAI(tool2, result, args) {
1665
2112
  return aiContext ?? `[Tool ${tool2?.name ?? "unknown"} executed successfully]`;
1666
2113
  case "full":
1667
2114
  default:
1668
- const fullData = JSON.stringify(result);
2115
+ const fullData = JSON.stringify(result ?? null);
1669
2116
  return aiContext ? `${aiContext}
1670
2117
 
1671
2118
  Full data: ${fullData}` : fullData;
@@ -2345,7 +2792,8 @@ var Runtime = class {
2345
2792
  const systemPrompt = request.systemPrompt || this.config.systemPrompt || "";
2346
2793
  let accumulatedText = "";
2347
2794
  const toolCalls = [];
2348
- let currentToolCall = null;
2795
+ const pendingToolCalls = /* @__PURE__ */ new Map();
2796
+ const endedToolIds = /* @__PURE__ */ new Set();
2349
2797
  const serverToolResults = [];
2350
2798
  const toolContextData = "toolContext" in this.config ? this.config.toolContext : void 0;
2351
2799
  let adapterProviderMetadata;
@@ -2370,142 +2818,151 @@ var Runtime = class {
2370
2818
  debug
2371
2819
  };
2372
2820
  const stream = this.adapter.stream(completionRequest);
2373
- for await (const event of stream) {
2374
- switch (event.type) {
2375
- case "message:start":
2376
- yield event;
2377
- break;
2378
- case "message:end":
2379
- yield event;
2380
- break;
2381
- case "message:delta":
2382
- accumulatedText += event.content;
2383
- yield event;
2384
- break;
2385
- case "action:start":
2386
- currentToolCall = {
2387
- id: event.id,
2388
- name: event.name,
2389
- args: "",
2390
- ...event.extra_content ? { extra_content: event.extra_content } : {}
2391
- };
2392
- if (debug) {
2393
- console.log(`[Copilot SDK] Tool call started: ${event.name}`);
2821
+ try {
2822
+ for await (const event of stream) {
2823
+ throwIfAborted(signal);
2824
+ switch (event.type) {
2825
+ case "message:start":
2826
+ yield event;
2827
+ break;
2828
+ case "message:end":
2829
+ yield {
2830
+ ...event,
2831
+ providerMetadata: responseReplayMetadata(event.providerMetadata)
2832
+ };
2833
+ break;
2834
+ case "message:delta":
2835
+ accumulatedText += event.content;
2836
+ yield event;
2837
+ break;
2838
+ case "action:start":
2839
+ if (!event.id || !event.name || pendingToolCalls.has(event.id) || toolCalls.some((call) => call.id === event.id))
2840
+ throw invalidProviderResponse(
2841
+ "Tool calls have missing or duplicate identities"
2842
+ );
2843
+ pendingToolCalls.set(event.id, {
2844
+ id: event.id,
2845
+ name: event.name,
2846
+ ...event.extra_content ? { extra_content: event.extra_content } : {}
2847
+ });
2848
+ if (debug) {
2849
+ console.log(`[Copilot SDK] Tool call started: ${event.name}`);
2850
+ }
2851
+ yield event;
2852
+ break;
2853
+ case "action:args": {
2854
+ const current = pendingToolCalls.get(event.id);
2855
+ if (!current)
2856
+ throw invalidProviderResponse(
2857
+ "Tool arguments have no matching call"
2858
+ );
2859
+ toolCalls.push({
2860
+ ...current,
2861
+ args: parseToolArguments(event.args)
2862
+ });
2863
+ pendingToolCalls.delete(event.id);
2864
+ yield event;
2865
+ break;
2394
2866
  }
2395
- yield event;
2396
- break;
2397
- case "action:args":
2398
- if (currentToolCall) {
2399
- try {
2400
- const parsedArgs = JSON.parse(event.args || "{}");
2867
+ case "action:end": {
2868
+ const tc = toolCalls.find((t) => t.id === event.id);
2869
+ const eventName = event.name;
2870
+ if (!tc || eventName && eventName !== tc.name || endedToolIds.has(event.id))
2871
+ throw invalidProviderResponse(
2872
+ "Tool completion has no matching unexecuted call"
2873
+ );
2874
+ endedToolIds.add(event.id);
2875
+ const toolName = tc.name;
2876
+ const tool2 = toolName ? selectedToolMap.get(toolName) : void 0;
2877
+ if (tool2?.location === "server" && tool2.handler) {
2401
2878
  if (debug) {
2402
2879
  console.log(
2403
- `[Copilot SDK] Tool args for ${currentToolCall.name}:`,
2404
- parsedArgs
2880
+ `[Copilot SDK] Executing server-side tool: ${toolName}`
2405
2881
  );
2406
2882
  }
2407
- toolCalls.push({
2408
- id: currentToolCall.id,
2409
- name: currentToolCall.name,
2410
- args: parsedArgs,
2411
- ...currentToolCall.extra_content ? { extra_content: currentToolCall.extra_content } : {}
2412
- });
2413
- } catch (e) {
2414
- console.error(
2415
- "[Copilot SDK] Failed to parse tool args:",
2416
- event.args,
2417
- e
2883
+ const args = tc.args;
2884
+ const toolContext = buildToolContext(
2885
+ event.id,
2886
+ signal,
2887
+ request.threadId,
2888
+ _httpRequest,
2889
+ toolContextData
2418
2890
  );
2419
- toolCalls.push({
2420
- id: currentToolCall.id,
2421
- name: currentToolCall.name,
2422
- args: {},
2423
- ...currentToolCall.extra_content ? { extra_content: currentToolCall.extra_content } : {}
2424
- });
2891
+ try {
2892
+ throwIfAborted(signal);
2893
+ const result = await tool2.handler(args, toolContext);
2894
+ throwIfAborted(signal);
2895
+ const modelOutput = resolveToolOutput(
2896
+ result,
2897
+ tool2.toModelOutput?.(result)
2898
+ );
2899
+ serverToolResults.push({
2900
+ id: event.id,
2901
+ name: toolName,
2902
+ args,
2903
+ result,
2904
+ modelOutput,
2905
+ tool: tool2
2906
+ });
2907
+ yield {
2908
+ type: "action:end",
2909
+ id: event.id,
2910
+ name: toolName,
2911
+ result,
2912
+ modelOutput
2913
+ };
2914
+ } catch (error) {
2915
+ throwIfAborted(signal);
2916
+ const errorResult = {
2917
+ success: false,
2918
+ error: error instanceof Error ? error.message : "Tool execution failed"
2919
+ };
2920
+ serverToolResults.push({
2921
+ id: event.id,
2922
+ name: toolName,
2923
+ args,
2924
+ result: errorResult,
2925
+ tool: tool2
2926
+ });
2927
+ yield {
2928
+ type: "action:end",
2929
+ id: event.id,
2930
+ name: toolName,
2931
+ error: error instanceof Error ? error.message : "Tool execution failed"
2932
+ };
2933
+ }
2934
+ } else {
2935
+ yield event;
2425
2936
  }
2426
- currentToolCall = null;
2937
+ break;
2427
2938
  }
2428
- yield event;
2429
- break;
2430
- case "action:end": {
2431
- const toolName = event.name;
2432
- const tool2 = toolName ? selectedToolMap.get(toolName) : void 0;
2433
- if (tool2?.location === "server" && tool2.handler) {
2434
- if (debug) {
2435
- console.log(
2436
- `[Copilot SDK] Executing server-side tool: ${toolName}`
2437
- );
2438
- }
2439
- const tc = toolCalls.find((t) => t.id === event.id);
2440
- const args = tc?.args ?? {};
2441
- const toolContext = buildToolContext(
2442
- event.id,
2443
- signal,
2444
- request.threadId,
2445
- _httpRequest,
2446
- toolContextData
2939
+ case "citation":
2940
+ yield event;
2941
+ break;
2942
+ case "error":
2943
+ yield event;
2944
+ return;
2945
+ // Exit on error
2946
+ case "done":
2947
+ adapterProviderMetadata = responseReplayMetadata(
2948
+ event.providerMetadata
2447
2949
  );
2448
- try {
2449
- const result = await tool2.handler(args, toolContext);
2450
- const modelOutput = resolveToolOutput(
2451
- result,
2452
- tool2.toModelOutput?.(result)
2453
- );
2454
- serverToolResults.push({
2455
- id: event.id,
2456
- name: toolName,
2457
- args,
2458
- result,
2459
- modelOutput,
2460
- tool: tool2
2461
- });
2462
- yield {
2463
- type: "action:end",
2464
- id: event.id,
2465
- name: toolName,
2466
- result,
2467
- modelOutput
2468
- };
2469
- } catch (error) {
2470
- const errorResult = {
2471
- success: false,
2472
- error: error instanceof Error ? error.message : "Tool execution failed"
2473
- };
2474
- serverToolResults.push({
2475
- id: event.id,
2476
- name: toolName,
2477
- args,
2478
- result: errorResult,
2479
- tool: tool2
2480
- });
2481
- yield {
2482
- type: "action:end",
2483
- id: event.id,
2484
- name: toolName,
2485
- error: error instanceof Error ? error.message : "Tool execution failed"
2486
- };
2950
+ if (event.usage) {
2951
+ adapterUsage = event.usage;
2487
2952
  }
2488
- } else {
2953
+ break;
2954
+ default:
2489
2955
  yield event;
2490
- }
2491
- break;
2492
2956
  }
2493
- case "citation":
2494
- yield event;
2495
- break;
2496
- case "error":
2497
- yield event;
2498
- return;
2499
- // Exit on error
2500
- case "done":
2501
- adapterProviderMetadata = event.providerMetadata;
2502
- if (event.usage) {
2503
- adapterUsage = event.usage;
2504
- }
2505
- break;
2506
- default:
2507
- yield event;
2508
2957
  }
2958
+ throwIfAborted(signal);
2959
+ if (pendingToolCalls.size || toolCalls.some((call) => !endedToolIds.has(call.id)))
2960
+ throw invalidProviderResponse(
2961
+ "Stream ended with incomplete tool calls"
2962
+ );
2963
+ } catch (error) {
2964
+ yield providerErrorEvent(error, "RUNTIME_STREAM_ERROR");
2965
+ return;
2509
2966
  }
2510
2967
  if (toolCalls.length > 0) {
2511
2968
  if (debug) {
@@ -2614,7 +3071,18 @@ var Runtime = class {
2614
3071
  nextToolSearchState,
2615
3072
  _iteration + 1
2616
3073
  )) {
2617
- yield event;
3074
+ if (event.type === "done" && adapterUsage) {
3075
+ const promptTokens = adapterUsage.prompt_tokens + (event.usage?.prompt_tokens ?? 0);
3076
+ const completionTokens = adapterUsage.completion_tokens + (event.usage?.completion_tokens ?? 0);
3077
+ yield {
3078
+ ...event,
3079
+ usage: {
3080
+ prompt_tokens: promptTokens,
3081
+ completion_tokens: completionTokens,
3082
+ total_tokens: (adapterUsage.total_tokens ?? adapterUsage.prompt_tokens + adapterUsage.completion_tokens) + (event.usage?.total_tokens ?? (event.usage?.prompt_tokens ?? 0) + (event.usage?.completion_tokens ?? 0))
3083
+ }
3084
+ };
3085
+ } else yield event;
2618
3086
  }
2619
3087
  return;
2620
3088
  }
@@ -2750,6 +3218,8 @@ var Runtime = class {
2750
3218
  };
2751
3219
  try {
2752
3220
  const result = await this.adapter.complete(completionRequest);
3221
+ throwIfAborted(signal);
3222
+ validateToolCalls(result.toolCalls);
2753
3223
  if (result.usage) {
2754
3224
  accumulatedUsage.prompt_tokens += result.usage.promptTokens;
2755
3225
  accumulatedUsage.completion_tokens += result.usage.completionTokens;
@@ -2769,7 +3239,7 @@ var Runtime = class {
2769
3239
  }
2770
3240
  yield {
2771
3241
  type: "message:end",
2772
- providerMetadata: result.providerMetadata
3242
+ providerMetadata: responseReplayMetadata(result.providerMetadata)
2773
3243
  };
2774
3244
  if (result.toolCalls.length > 0) {
2775
3245
  const serverToolCalls = [];
@@ -2803,6 +3273,7 @@ var Runtime = class {
2803
3273
  const serverToolResults = [];
2804
3274
  const toolContextData = "toolContext" in this.config ? this.config.toolContext : void 0;
2805
3275
  for (const tc of serverToolCalls) {
3276
+ throwIfAborted(signal);
2806
3277
  const tool2 = selectedToolMap.get(tc.name);
2807
3278
  if (tool2?.handler) {
2808
3279
  if (debug) {
@@ -2817,6 +3288,7 @@ var Runtime = class {
2817
3288
  );
2818
3289
  try {
2819
3290
  const toolResult = await tool2.handler(tc.args, toolContext);
3291
+ throwIfAborted(signal);
2820
3292
  const modelOutput = resolveToolOutput(
2821
3293
  toolResult,
2822
3294
  tool2.toModelOutput?.(toolResult)
@@ -2837,6 +3309,7 @@ var Runtime = class {
2837
3309
  modelOutput
2838
3310
  };
2839
3311
  } catch (error) {
3312
+ throwIfAborted(signal);
2840
3313
  const errorResult = {
2841
3314
  success: false,
2842
3315
  error: error instanceof Error ? error.message : "Tool execution failed"
@@ -2870,7 +3343,11 @@ var Runtime = class {
2870
3343
  const assistantMessage = {
2871
3344
  role: "assistant",
2872
3345
  content: result.content || null,
2873
- ...result.providerMetadata ? { providerMetadata: result.providerMetadata } : {},
3346
+ ...result.providerMetadata ? {
3347
+ providerMetadata: responseReplayMetadata(
3348
+ result.providerMetadata
3349
+ )
3350
+ } : {},
2874
3351
  tool_calls: result.toolCalls.map((tc) => ({
2875
3352
  id: tc.id,
2876
3353
  type: "function",
@@ -2900,7 +3377,11 @@ var Runtime = class {
2900
3377
  const assistantWithToolCalls = {
2901
3378
  role: "assistant",
2902
3379
  content: result.content || null,
2903
- ...result.providerMetadata ? { providerMetadata: result.providerMetadata } : {},
3380
+ ...result.providerMetadata ? {
3381
+ providerMetadata: responseReplayMetadata(
3382
+ result.providerMetadata
3383
+ )
3384
+ } : {},
2904
3385
  tool_calls: result.toolCalls.map((tc) => ({
2905
3386
  id: tc.id,
2906
3387
  type: "function",
@@ -2932,7 +3413,11 @@ var Runtime = class {
2932
3413
  const assistantMessage = {
2933
3414
  role: "assistant",
2934
3415
  content: result.content || null,
2935
- ...result.providerMetadata ? { providerMetadata: result.providerMetadata } : {},
3416
+ ...result.providerMetadata ? {
3417
+ providerMetadata: responseReplayMetadata(
3418
+ result.providerMetadata
3419
+ )
3420
+ } : {},
2936
3421
  tool_calls: clientToolCalls.map((tc) => ({
2937
3422
  id: tc.id,
2938
3423
  type: "function",
@@ -2962,7 +3447,11 @@ var Runtime = class {
2962
3447
  newMessages.push({
2963
3448
  role: "assistant",
2964
3449
  content: result.content,
2965
- ...result.providerMetadata ? { providerMetadata: result.providerMetadata } : {}
3450
+ ...result.providerMetadata ? {
3451
+ providerMetadata: responseReplayMetadata(
3452
+ result.providerMetadata
3453
+ )
3454
+ } : {}
2966
3455
  });
2967
3456
  }
2968
3457
  if (debug) {
@@ -3483,20 +3972,11 @@ function parseToolCalls(response) {
3483
3972
  const message = choices?.[0]?.message;
3484
3973
  const toolCalls = message?.tool_calls || [];
3485
3974
  return toolCalls.map((tc) => {
3486
- let input = {};
3487
- try {
3488
- input = JSON.parse(tc.function.arguments);
3489
- } catch (e) {
3490
- console.error(
3491
- "Failed to parse tool arguments:",
3492
- tc.function.arguments,
3493
- e
3494
- );
3495
- }
3975
+ const input2 = parseToolArguments(tc.function.arguments);
3496
3976
  return {
3497
3977
  id: tc.id,
3498
3978
  name: tc.function.name,
3499
- input
3979
+ input: input2
3500
3980
  };
3501
3981
  });
3502
3982
  }
@@ -3577,7 +4057,7 @@ function parseToolCalls2(response) {
3577
4057
  return content.filter((block) => block?.type === "tool_use").map((block) => ({
3578
4058
  id: block.id,
3579
4059
  name: block.name,
3580
- input: block.input || {}
4060
+ input: parseToolArguments(block.input)
3581
4061
  }));
3582
4062
  }
3583
4063
  function extractTextContent2(response) {
@@ -3911,7 +4391,11 @@ async function* runAgentLoop(options) {
3911
4391
  providerTools,
3912
4392
  providerToolOptions
3913
4393
  );
4394
+ throwIfAborted(signal);
3914
4395
  const toolCalls = formatter.parseToolCalls(response);
4396
+ validateToolCalls(
4397
+ toolCalls.map((call) => ({ ...call, args: call.input }))
4398
+ );
3915
4399
  const textContent = formatter.extractTextContent(response);
3916
4400
  if (textContent) {
3917
4401
  const messageId = generateMessageId();
@@ -3926,15 +4410,13 @@ async function* runAgentLoop(options) {
3926
4410
  toolCalls.map((tc) => tc.name)
3927
4411
  );
3928
4412
  }
3929
- const results = await executeToolCalls(
4413
+ const results = yield* executeToolCalls(
3930
4414
  toolCalls,
3931
4415
  effectiveSelectedTools,
3932
4416
  executeServerTool,
3933
4417
  waitForClientToolResult,
3934
- function* (event) {
3935
- yield event;
3936
- },
3937
- debug
4418
+ debug,
4419
+ signal
3938
4420
  );
3939
4421
  for (const result of results) {
3940
4422
  const toolCall = toolCalls.find((tc) => tc.id === result.toolCallId);
@@ -4023,9 +4505,10 @@ function buildConversation(messages, systemPrompt) {
4023
4505
  }
4024
4506
  return conversation;
4025
4507
  }
4026
- async function executeToolCalls(toolCalls, tools, executeServerTool, waitForClientToolResult, emitEvent, debug) {
4508
+ async function* executeToolCalls(toolCalls, tools, executeServerTool, waitForClientToolResult, debug, signal) {
4027
4509
  const results = [];
4028
4510
  for (const toolCall of toolCalls) {
4511
+ throwIfAborted(signal);
4029
4512
  const tool2 = tools.find((t) => t.name === toolCall.name);
4030
4513
  if (!tool2) {
4031
4514
  if (debug) {
@@ -4042,22 +4525,26 @@ async function executeToolCalls(toolCalls, tools, executeServerTool, waitForClie
4042
4525
  });
4043
4526
  continue;
4044
4527
  }
4045
- emitEvent?.({
4528
+ yield {
4046
4529
  type: "action:start",
4047
4530
  id: toolCall.id,
4048
4531
  name: toolCall.name,
4049
4532
  hidden: tool2.hidden ?? false
4050
- });
4051
- emitEvent?.({
4533
+ };
4534
+ yield {
4052
4535
  type: "action:args",
4053
4536
  id: toolCall.id,
4054
4537
  args: JSON.stringify(toolCall.input)
4055
- });
4538
+ };
4056
4539
  try {
4057
4540
  let response;
4541
+ throwIfAborted(signal);
4058
4542
  if (tool2.location === "server") {
4059
4543
  if (tool2.handler) {
4060
- response = await tool2.handler(toolCall.input);
4544
+ response = await tool2.handler(toolCall.input, {
4545
+ signal,
4546
+ toolCallId: toolCall.id
4547
+ });
4061
4548
  } else if (executeServerTool) {
4062
4549
  response = await executeServerTool(toolCall.name, toolCall.input);
4063
4550
  } else {
@@ -4080,12 +4567,13 @@ async function executeToolCalls(toolCalls, tools, executeServerTool, waitForClie
4080
4567
  };
4081
4568
  }
4082
4569
  }
4083
- emitEvent?.({
4570
+ throwIfAborted(signal);
4571
+ yield {
4084
4572
  type: "action:end",
4085
4573
  id: toolCall.id,
4086
4574
  name: toolCall.name,
4087
4575
  result: response
4088
- });
4576
+ };
4089
4577
  results.push({
4090
4578
  toolCallId: toolCall.id,
4091
4579
  content: JSON.stringify(response),
@@ -4093,13 +4581,14 @@ async function executeToolCalls(toolCalls, tools, executeServerTool, waitForClie
4093
4581
  error: response.error
4094
4582
  });
4095
4583
  } catch (error) {
4584
+ throwIfAborted(signal);
4096
4585
  const errorMessage = error instanceof Error ? error.message : "Tool execution failed";
4097
- emitEvent?.({
4586
+ yield {
4098
4587
  type: "action:end",
4099
4588
  id: toolCall.id,
4100
4589
  name: toolCall.name,
4101
4590
  error: errorMessage
4102
- });
4591
+ };
4103
4592
  results.push({
4104
4593
  toolCallId: toolCall.id,
4105
4594
  content: JSON.stringify({
@@ -4116,17 +4605,17 @@ async function executeToolCalls(toolCalls, tools, executeServerTool, waitForClie
4116
4605
 
4117
4606
  // src/core/audio.ts
4118
4607
  var audioErrorMarker = /* @__PURE__ */ Symbol.for("@yourgpt/llm-sdk/AudioAPIError");
4119
- var _a;
4608
+ var _a3;
4120
4609
  var AudioAPIError = class extends Error {
4121
4610
  constructor(message, code, status, requestId) {
4122
4611
  super(message);
4123
4612
  this.code = code;
4124
4613
  this.status = status;
4125
4614
  this.requestId = requestId;
4126
- this[_a] = true;
4615
+ this[_a3] = true;
4127
4616
  this.name = "AudioAPIError";
4128
4617
  }
4129
- static [(_a = audioErrorMarker, Symbol.hasInstance)](value) {
4618
+ static [(_a3 = audioErrorMarker, Symbol.hasInstance)](value) {
4130
4619
  return !!value && typeof value === "object" && value[audioErrorMarker] === true;
4131
4620
  }
4132
4621
  };
@@ -4161,6 +4650,6 @@ async function transcribe({
4161
4650
  return result;
4162
4651
  }
4163
4652
 
4164
- export { AudioAPIError, DEFAULT_CAPABILITIES, DEFAULT_MAX_ITERATIONS, GenerateResult, Runtime, StreamResult, buildProviderToolOptions, createEventStream, createExpressHandler, createExpressMiddleware, createHonoApp, createNextHandler, createNodeHandler, createRuntime, createSSEHeaders, createSSEResponse, createStreamResult, createTextStreamHeaders, createTextStreamResponse, extractInputMessages, formatSSEData, formatToolsForAnthropic, formatToolsForGoogle, formatToolsForOpenAI, generateMessageId, generateSpeech, generateText, generateThreadId, generateToolCallId, mapOutputMessages, pipeSSEToResponse, pipeTextToResponse, runAgentLoop, searchTools, selectTools, shouldExposeToolSearch, streamText, tool, transcribe };
4653
+ export { AudioAPIError, DEFAULT_CAPABILITIES, DEFAULT_MAX_ITERATIONS, DecisionAPIError, GenerateResult, MediaAPIError, Runtime, StreamResult, buildProviderToolOptions, createEventStream, createExpressHandler, createExpressMiddleware, createHonoApp, createNextHandler, createNodeHandler, createRuntime, createSSEHeaders, createSSEResponse, createStreamResult, createTextStreamHeaders, createTextStreamResponse, experimental_decide, extractInputMessages, formatSSEData, formatToolsForAnthropic, formatToolsForGoogle, formatToolsForOpenAI, generateImage, generateMessageId, generateSpeech, generateText, generateThreadId, generateToolCallId, mapOutputMessages, pipeSSEToResponse, pipeTextToResponse, runAgentLoop, searchTools, selectTools, shouldExposeToolSearch, streamText, tool, transcribe };
4165
4654
  //# sourceMappingURL=index.mjs.map
4166
4655
  //# sourceMappingURL=index.mjs.map