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