@yourgpt/llm-sdk 2.6.0-beta.3 → 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 +46 -10
  3. package/dist/adapters/index.d.mts +11 -13
  4. package/dist/adapters/index.d.ts +11 -13
  5. package/dist/adapters/index.js +327 -100
  6. package/dist/adapters/index.mjs +327 -100
  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 +14 -8
  18. package/dist/index.d.ts +14 -8
  19. package/dist/index.js +599 -195
  20. package/dist/index.mjs +596 -196
  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 +383 -102
  24. package/dist/providers/anthropic/index.mjs +383 -102
  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 +108 -59
  34. package/dist/providers/fireworks/index.mjs +108 -59
  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 +108 -59
  38. package/dist/providers/google/index.mjs +108 -59
  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 +1378 -84
  44. package/dist/providers/openai/index.mjs +1376 -85
  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 +967 -130
  48. package/dist/providers/openrouter/index.mjs +967 -131
  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 +108 -59
  52. package/dist/providers/togetherai/index.mjs +108 -59
  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 +108 -59
  60. package/dist/providers/xai/index.mjs +108 -59
  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
@@ -152,7 +152,22 @@ function zodToJsonSchema(schema) {
152
152
  case "ZodString":
153
153
  return { type: "string", description };
154
154
  case "ZodNumber":
155
- 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
+ };
156
171
  case "ZodBoolean":
157
172
  return { type: "boolean", description };
158
173
  case "ZodEnum": {
@@ -173,10 +188,13 @@ function zodToJsonSchema(schema) {
173
188
  const properties = {};
174
189
  const required = [];
175
190
  for (const [key, value] of Object.entries(shapeObj)) {
176
- properties[key] = zodToJsonSchema(value);
177
- const valueDef = value._def;
178
- const isOptional = valueDef.typeName === "ZodOptional" || valueDef.typeName === "ZodNullable" || valueDef.typeName === "ZodDefault";
179
- 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)) {
180
198
  required.push(key);
181
199
  }
182
200
  }
@@ -187,8 +205,13 @@ function zodToJsonSchema(schema) {
187
205
  description
188
206
  };
189
207
  }
190
- case "ZodOptional":
191
208
  case "ZodNullable": {
209
+ return {
210
+ anyOf: [zodToJsonSchema(def.innerType), { type: "null" }],
211
+ description
212
+ };
213
+ }
214
+ case "ZodOptional": {
192
215
  const innerType = def.innerType;
193
216
  return zodToJsonSchema(innerType);
194
217
  }
@@ -198,6 +221,7 @@ function zodToJsonSchema(schema) {
198
221
  }
199
222
  case "ZodLiteral": {
200
223
  const value = def.value;
224
+ if (value === null) return { type: "null", description };
201
225
  if (typeof value === "string") {
202
226
  return { type: "string", enum: [value], description };
203
227
  }
@@ -221,12 +245,21 @@ function zodToJsonSchema(schema) {
221
245
  if (stringLiterals.length === options.length) {
222
246
  return { type: "string", enum: stringLiterals, description };
223
247
  }
224
- return zodToJsonSchema(options[0]);
248
+ return { anyOf: options.map(zodToJsonSchema), description };
225
249
  }
226
250
  default:
227
251
  return { type: "string", description };
228
252
  }
229
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
+ }
230
263
  function formatToolsForOpenAI(tools) {
231
264
  return Object.entries(tools).map(([name, toolDef]) => ({
232
265
  type: "function",
@@ -257,6 +290,27 @@ function formatToolsForGoogle(tools) {
257
290
  }
258
291
 
259
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
+ }
260
314
  function throwIfAborted(signal) {
261
315
  if (!signal?.aborted) return;
262
316
  const error = new Error("The operation was aborted");
@@ -264,9 +318,42 @@ function throwIfAborted(signal) {
264
318
  throw error;
265
319
  }
266
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
+
267
353
  // src/core/generate-text.ts
268
354
  async function generateText(params) {
269
355
  const { model, tools, maxSteps = 1, signal } = params;
356
+ validateMaxSteps(maxSteps);
270
357
  if (params.responseFormat && model.capabilities.supportsJsonMode === false) {
271
358
  console.warn(
272
359
  `[llm-sdk] ${model.provider}/${model.modelId} does not support structured output (responseFormat); the request will be sent but the provider may ignore it.`
@@ -291,12 +378,13 @@ async function generateText(params) {
291
378
  });
292
379
  throwIfAborted(signal);
293
380
  const toolCalls = result.toolCalls ?? [];
381
+ validateToolCalls(toolCalls);
382
+ allToolCalls.push(...toolCalls);
294
383
  const stepToolResults = [];
295
384
  if (toolCalls.length > 0 && tools) {
296
- allToolCalls.push(...toolCalls);
297
385
  for (const call of toolCalls) {
298
386
  throwIfAborted(signal);
299
- const toolDef = tools[call.name];
387
+ const toolDef = Object.prototype.hasOwnProperty.call(tools, call.name) ? tools[call.name] : void 0;
300
388
  if (!toolDef) {
301
389
  const errorResult = {
302
390
  toolCallId: call.id,
@@ -339,6 +427,7 @@ async function generateText(params) {
339
427
  }
340
428
  steps.push({
341
429
  text: result.text,
430
+ ...result.reasoningText !== void 0 ? { reasoningText: result.reasoningText } : {},
342
431
  toolCalls,
343
432
  toolResults: stepToolResults,
344
433
  finishReason: result.finishReason,
@@ -352,12 +441,12 @@ async function generateText(params) {
352
441
  ...result.providerMetadata ? { providerMetadata: responseReplayMetadata(result.providerMetadata) } : {}
353
442
  };
354
443
  messages = [...messages, assistantMessage];
355
- if (toolCalls.length === 0) break;
444
+ if (toolCalls.length === 0 || !tools) break;
356
445
  for (const tr of stepToolResults) {
357
446
  const toolMessage = {
358
447
  role: "tool",
359
448
  toolCallId: tr.toolCallId,
360
- content: tr.modelOutput ? "[Tool returned content]" : JSON.stringify(tr.result),
449
+ content: tr.modelOutput ? "[Tool returned content]" : JSON.stringify(tr.result ?? null),
361
450
  ...tr.modelOutput ? { modelOutput: tr.modelOutput } : {}
362
451
  };
363
452
  messages = [...messages, toolMessage];
@@ -366,6 +455,7 @@ async function generateText(params) {
366
455
  const lastStep = steps[steps.length - 1];
367
456
  return {
368
457
  text: lastStep?.text ?? "",
458
+ ...lastStep?.reasoningText !== void 0 ? { reasoningText: lastStep.reasoningText } : {},
369
459
  providerMetadata: lastStep?.providerMetadata,
370
460
  usage: sumUsage(steps),
371
461
  finishReason: lastStep?.finishReason ?? "stop",
@@ -412,9 +502,250 @@ function sumUsage(steps) {
412
502
  );
413
503
  }
414
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
+
415
735
  // src/core/stream-text.ts
416
736
  async function streamText(params) {
417
- 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
+ };
418
749
  if (params.responseFormat && model.capabilities.supportsJsonMode === false) {
419
750
  console.warn(
420
751
  `[llm-sdk] ${model.provider}/${model.modelId} does not support structured output (responseFormat); the request will be sent but the provider may ignore it.`
@@ -441,6 +772,7 @@ async function streamText(params) {
441
772
  let stepProviderMetadata;
442
773
  const toolCalls = [];
443
774
  let stepFinishReason = "stop";
775
+ let receivedFinish = false;
444
776
  try {
445
777
  for await (const chunk of model.doStream({
446
778
  messages,
@@ -454,6 +786,9 @@ async function streamText(params) {
454
786
  })) {
455
787
  throwIfAborted(signal);
456
788
  switch (chunk.type) {
789
+ case "reasoning-delta":
790
+ yield chunk;
791
+ break;
457
792
  case "text-delta":
458
793
  stepText += chunk.text;
459
794
  fullText += chunk.text;
@@ -467,6 +802,11 @@ async function streamText(params) {
467
802
  };
468
803
  break;
469
804
  case "finish":
805
+ if (receivedFinish)
806
+ throw invalidProviderResponse(
807
+ "Model stream returned multiple finish events"
808
+ );
809
+ receivedFinish = true;
470
810
  stepProviderMetadata = chunk.providerMetadata;
471
811
  finalProviderMetadata = chunk.providerMetadata;
472
812
  stepFinishReason = chunk.finishReason;
@@ -484,6 +824,12 @@ async function streamText(params) {
484
824
  return;
485
825
  }
486
826
  }
827
+ throwIfAborted(signal);
828
+ if (!receivedFinish)
829
+ throw invalidProviderResponse(
830
+ "Model stream ended without a finish event"
831
+ );
832
+ validateToolCalls(toolCalls);
487
833
  } catch (error) {
488
834
  yield {
489
835
  type: "error",
@@ -509,7 +855,7 @@ async function streamText(params) {
509
855
  if (toolCalls.length === 0 || !tools) break;
510
856
  for (const call of toolCalls) {
511
857
  throwIfAborted(signal);
512
- const toolDef = tools[call.name];
858
+ const toolDef = Object.prototype.hasOwnProperty.call(tools, call.name) ? tools[call.name] : void 0;
513
859
  if (!toolDef) {
514
860
  const errorResult = { error: `Tool not found: ${call.name}` };
515
861
  yield {
@@ -550,7 +896,7 @@ async function streamText(params) {
550
896
  {
551
897
  role: "tool",
552
898
  toolCallId: call.id,
553
- content: modelOutput ? "[Tool returned content]" : JSON.stringify(result),
899
+ content: modelOutput ? "[Tool returned content]" : JSON.stringify(result ?? null),
554
900
  ...modelOutput ? { modelOutput } : {}
555
901
  }
556
902
  ];
@@ -592,11 +938,17 @@ async function streamText(params) {
592
938
  pending = (async () => {
593
939
  try {
594
940
  const next = await source.next();
595
- if (next.done) finished = true;
596
- 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
+ }
597
948
  } catch (error) {
598
949
  failure = error;
599
950
  finished = true;
951
+ cleanup();
600
952
  }
601
953
  })();
602
954
  }
@@ -647,7 +999,7 @@ async function streamText(params) {
647
999
  return consumeStream().then(() => finalFinishReason);
648
1000
  },
649
1001
  toTextStreamResponse(options) {
650
- const stream = createTextStreamReadable(createTextStream());
1002
+ const stream = createTextStreamReadable(createTextStream(), cancel);
651
1003
  return new Response(stream, {
652
1004
  status: options?.status ?? 200,
653
1005
  headers: {
@@ -659,7 +1011,7 @@ async function streamText(params) {
659
1011
  });
660
1012
  },
661
1013
  toDataStreamResponse(options) {
662
- const stream = createDataStreamReadable(sharedStream());
1014
+ const stream = createDataStreamReadable(sharedStream(), cancel);
663
1015
  return new Response(stream, {
664
1016
  status: options?.status ?? 200,
665
1017
  headers: {
@@ -696,35 +1048,51 @@ function formatToolsForProvider2(tools, provider) {
696
1048
  return formatToolsForOpenAI(tools);
697
1049
  }
698
1050
  }
699
- function createTextStreamReadable(textStream) {
1051
+ function createTextStreamReadable(textStream, abort) {
700
1052
  const encoder = new TextEncoder();
1053
+ const iterator = textStream[Symbol.asyncIterator]();
1054
+ let cancelled = false;
701
1055
  return new ReadableStream({
702
- async start(controller) {
1056
+ async pull(controller) {
703
1057
  try {
704
- for await (const text of textStream) {
705
- controller.enqueue(encoder.encode(text));
706
- }
707
- 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));
708
1062
  } catch (error) {
709
- controller.error(error);
1063
+ if (!cancelled) controller.error(error);
710
1064
  }
1065
+ },
1066
+ cancel() {
1067
+ cancelled = true;
1068
+ abort();
1069
+ void iterator.return?.().catch(() => {
1070
+ });
711
1071
  }
712
1072
  });
713
1073
  }
714
- function createDataStreamReadable(fullStream) {
1074
+ function createDataStreamReadable(fullStream, abort) {
715
1075
  const encoder = new TextEncoder();
1076
+ const iterator = fullStream[Symbol.asyncIterator]();
1077
+ let cancelled = false;
716
1078
  return new ReadableStream({
717
- async start(controller) {
1079
+ async pull(controller) {
718
1080
  try {
719
- for await (const part of fullStream) {
720
- const data = JSON.stringify(part);
721
- controller.enqueue(encoder.encode(`data: ${data}
722
-
723
- `));
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;
724
1087
  }
725
- controller.enqueue(encoder.encode("data: [DONE]\n\n"));
726
- 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
+ );
727
1094
  } catch (error) {
1095
+ if (cancelled) return;
728
1096
  const errorData = JSON.stringify({
729
1097
  type: "error",
730
1098
  error: error instanceof Error ? error.message : String(error)
@@ -733,7 +1101,15 @@ function createDataStreamReadable(fullStream) {
733
1101
 
734
1102
  `));
735
1103
  controller.close();
1104
+ void iterator.return?.().catch(() => {
1105
+ });
736
1106
  }
1107
+ },
1108
+ cancel() {
1109
+ cancelled = true;
1110
+ abort();
1111
+ void iterator.return?.().catch(() => {
1112
+ });
737
1113
  }
738
1114
  });
739
1115
  }
@@ -1269,6 +1645,9 @@ var StreamResult = class {
1269
1645
  }
1270
1646
  break;
1271
1647
  }
1648
+ case "error":
1649
+ collected.error = { message: event.message, code: event.code };
1650
+ break;
1272
1651
  case "done":
1273
1652
  if (event.messages) {
1274
1653
  collected.messages.push(...event.messages);
@@ -1735,7 +2114,7 @@ function buildToolResultForAI(tool2, result, args) {
1735
2114
  return aiContext ?? `[Tool ${tool2?.name ?? "unknown"} executed successfully]`;
1736
2115
  case "full":
1737
2116
  default:
1738
- const fullData = JSON.stringify(result);
2117
+ const fullData = JSON.stringify(result ?? null);
1739
2118
  return aiContext ? `${aiContext}
1740
2119
 
1741
2120
  Full data: ${fullData}` : fullData;
@@ -2415,7 +2794,8 @@ var Runtime = class {
2415
2794
  const systemPrompt = request.systemPrompt || this.config.systemPrompt || "";
2416
2795
  let accumulatedText = "";
2417
2796
  const toolCalls = [];
2418
- let currentToolCall = null;
2797
+ const pendingToolCalls = /* @__PURE__ */ new Map();
2798
+ const endedToolIds = /* @__PURE__ */ new Set();
2419
2799
  const serverToolResults = [];
2420
2800
  const toolContextData = "toolContext" in this.config ? this.config.toolContext : void 0;
2421
2801
  let adapterProviderMetadata;
@@ -2440,147 +2820,151 @@ var Runtime = class {
2440
2820
  debug
2441
2821
  };
2442
2822
  const stream = this.adapter.stream(completionRequest);
2443
- for await (const event of stream) {
2444
- switch (event.type) {
2445
- case "message:start":
2446
- yield event;
2447
- break;
2448
- case "message:end":
2449
- yield {
2450
- ...event,
2451
- providerMetadata: responseReplayMetadata(event.providerMetadata)
2452
- };
2453
- break;
2454
- case "message:delta":
2455
- accumulatedText += event.content;
2456
- yield event;
2457
- break;
2458
- case "action:start":
2459
- currentToolCall = {
2460
- id: event.id,
2461
- name: event.name,
2462
- args: "",
2463
- ...event.extra_content ? { extra_content: event.extra_content } : {}
2464
- };
2465
- if (debug) {
2466
- 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;
2467
2868
  }
2468
- yield event;
2469
- break;
2470
- case "action:args":
2471
- if (currentToolCall) {
2472
- try {
2473
- 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) {
2474
2880
  if (debug) {
2475
2881
  console.log(
2476
- `[Copilot SDK] Tool args for ${currentToolCall.name}:`,
2477
- parsedArgs
2882
+ `[Copilot SDK] Executing server-side tool: ${toolName}`
2478
2883
  );
2479
2884
  }
2480
- toolCalls.push({
2481
- id: currentToolCall.id,
2482
- name: currentToolCall.name,
2483
- args: parsedArgs,
2484
- ...currentToolCall.extra_content ? { extra_content: currentToolCall.extra_content } : {}
2485
- });
2486
- } catch (e) {
2487
- console.error(
2488
- "[Copilot SDK] Failed to parse tool args:",
2489
- event.args,
2490
- e
2885
+ const args = tc.args;
2886
+ const toolContext = buildToolContext(
2887
+ event.id,
2888
+ signal,
2889
+ request.threadId,
2890
+ _httpRequest,
2891
+ toolContextData
2491
2892
  );
2492
- toolCalls.push({
2493
- id: currentToolCall.id,
2494
- name: currentToolCall.name,
2495
- args: {},
2496
- ...currentToolCall.extra_content ? { extra_content: currentToolCall.extra_content } : {}
2497
- });
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;
2498
2938
  }
2499
- currentToolCall = null;
2939
+ break;
2500
2940
  }
2501
- yield event;
2502
- break;
2503
- case "action:end": {
2504
- const toolName = event.name;
2505
- const tool2 = toolName ? selectedToolMap.get(toolName) : void 0;
2506
- if (tool2?.location === "server" && tool2.handler) {
2507
- if (debug) {
2508
- console.log(
2509
- `[Copilot SDK] Executing server-side tool: ${toolName}`
2510
- );
2511
- }
2512
- const tc = toolCalls.find((t) => t.id === event.id);
2513
- const args = tc?.args ?? {};
2514
- const toolContext = buildToolContext(
2515
- event.id,
2516
- signal,
2517
- request.threadId,
2518
- _httpRequest,
2519
- 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
2520
2951
  );
2521
- try {
2522
- const result = await tool2.handler(args, toolContext);
2523
- const modelOutput = resolveToolOutput(
2524
- result,
2525
- tool2.toModelOutput?.(result)
2526
- );
2527
- serverToolResults.push({
2528
- id: event.id,
2529
- name: toolName,
2530
- args,
2531
- result,
2532
- modelOutput,
2533
- tool: tool2
2534
- });
2535
- yield {
2536
- type: "action:end",
2537
- id: event.id,
2538
- name: toolName,
2539
- result,
2540
- modelOutput
2541
- };
2542
- } catch (error) {
2543
- const errorResult = {
2544
- success: false,
2545
- error: error instanceof Error ? error.message : "Tool execution failed"
2546
- };
2547
- serverToolResults.push({
2548
- id: event.id,
2549
- name: toolName,
2550
- args,
2551
- result: errorResult,
2552
- tool: tool2
2553
- });
2554
- yield {
2555
- type: "action:end",
2556
- id: event.id,
2557
- name: toolName,
2558
- error: error instanceof Error ? error.message : "Tool execution failed"
2559
- };
2952
+ if (event.usage) {
2953
+ adapterUsage = event.usage;
2560
2954
  }
2561
- } else {
2955
+ break;
2956
+ default:
2562
2957
  yield event;
2563
- }
2564
- break;
2565
2958
  }
2566
- case "citation":
2567
- yield event;
2568
- break;
2569
- case "error":
2570
- yield event;
2571
- return;
2572
- // Exit on error
2573
- case "done":
2574
- adapterProviderMetadata = responseReplayMetadata(
2575
- event.providerMetadata
2576
- );
2577
- if (event.usage) {
2578
- adapterUsage = event.usage;
2579
- }
2580
- break;
2581
- default:
2582
- yield event;
2583
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;
2584
2968
  }
2585
2969
  if (toolCalls.length > 0) {
2586
2970
  if (debug) {
@@ -2689,7 +3073,18 @@ var Runtime = class {
2689
3073
  nextToolSearchState,
2690
3074
  _iteration + 1
2691
3075
  )) {
2692
- 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;
2693
3088
  }
2694
3089
  return;
2695
3090
  }
@@ -2825,6 +3220,8 @@ var Runtime = class {
2825
3220
  };
2826
3221
  try {
2827
3222
  const result = await this.adapter.complete(completionRequest);
3223
+ throwIfAborted(signal);
3224
+ validateToolCalls(result.toolCalls);
2828
3225
  if (result.usage) {
2829
3226
  accumulatedUsage.prompt_tokens += result.usage.promptTokens;
2830
3227
  accumulatedUsage.completion_tokens += result.usage.completionTokens;
@@ -2878,6 +3275,7 @@ var Runtime = class {
2878
3275
  const serverToolResults = [];
2879
3276
  const toolContextData = "toolContext" in this.config ? this.config.toolContext : void 0;
2880
3277
  for (const tc of serverToolCalls) {
3278
+ throwIfAborted(signal);
2881
3279
  const tool2 = selectedToolMap.get(tc.name);
2882
3280
  if (tool2?.handler) {
2883
3281
  if (debug) {
@@ -2892,6 +3290,7 @@ var Runtime = class {
2892
3290
  );
2893
3291
  try {
2894
3292
  const toolResult = await tool2.handler(tc.args, toolContext);
3293
+ throwIfAborted(signal);
2895
3294
  const modelOutput = resolveToolOutput(
2896
3295
  toolResult,
2897
3296
  tool2.toModelOutput?.(toolResult)
@@ -2912,6 +3311,7 @@ var Runtime = class {
2912
3311
  modelOutput
2913
3312
  };
2914
3313
  } catch (error) {
3314
+ throwIfAborted(signal);
2915
3315
  const errorResult = {
2916
3316
  success: false,
2917
3317
  error: error instanceof Error ? error.message : "Tool execution failed"
@@ -3574,20 +3974,11 @@ function parseToolCalls(response) {
3574
3974
  const message = choices?.[0]?.message;
3575
3975
  const toolCalls = message?.tool_calls || [];
3576
3976
  return toolCalls.map((tc) => {
3577
- let input = {};
3578
- try {
3579
- input = JSON.parse(tc.function.arguments);
3580
- } catch (e) {
3581
- console.error(
3582
- "Failed to parse tool arguments:",
3583
- tc.function.arguments,
3584
- e
3585
- );
3586
- }
3977
+ const input2 = parseToolArguments(tc.function.arguments);
3587
3978
  return {
3588
3979
  id: tc.id,
3589
3980
  name: tc.function.name,
3590
- input
3981
+ input: input2
3591
3982
  };
3592
3983
  });
3593
3984
  }
@@ -3668,7 +4059,7 @@ function parseToolCalls2(response) {
3668
4059
  return content.filter((block) => block?.type === "tool_use").map((block) => ({
3669
4060
  id: block.id,
3670
4061
  name: block.name,
3671
- input: block.input || {}
4062
+ input: parseToolArguments(block.input)
3672
4063
  }));
3673
4064
  }
3674
4065
  function extractTextContent2(response) {
@@ -4002,7 +4393,11 @@ async function* runAgentLoop(options) {
4002
4393
  providerTools,
4003
4394
  providerToolOptions
4004
4395
  );
4396
+ throwIfAborted(signal);
4005
4397
  const toolCalls = formatter.parseToolCalls(response);
4398
+ validateToolCalls(
4399
+ toolCalls.map((call) => ({ ...call, args: call.input }))
4400
+ );
4006
4401
  const textContent = formatter.extractTextContent(response);
4007
4402
  if (textContent) {
4008
4403
  const messageId = generateMessageId();
@@ -4017,15 +4412,13 @@ async function* runAgentLoop(options) {
4017
4412
  toolCalls.map((tc) => tc.name)
4018
4413
  );
4019
4414
  }
4020
- const results = await executeToolCalls(
4415
+ const results = yield* executeToolCalls(
4021
4416
  toolCalls,
4022
4417
  effectiveSelectedTools,
4023
4418
  executeServerTool,
4024
4419
  waitForClientToolResult,
4025
- function* (event) {
4026
- yield event;
4027
- },
4028
- debug
4420
+ debug,
4421
+ signal
4029
4422
  );
4030
4423
  for (const result of results) {
4031
4424
  const toolCall = toolCalls.find((tc) => tc.id === result.toolCallId);
@@ -4114,9 +4507,10 @@ function buildConversation(messages, systemPrompt) {
4114
4507
  }
4115
4508
  return conversation;
4116
4509
  }
4117
- async function executeToolCalls(toolCalls, tools, executeServerTool, waitForClientToolResult, emitEvent, debug) {
4510
+ async function* executeToolCalls(toolCalls, tools, executeServerTool, waitForClientToolResult, debug, signal) {
4118
4511
  const results = [];
4119
4512
  for (const toolCall of toolCalls) {
4513
+ throwIfAborted(signal);
4120
4514
  const tool2 = tools.find((t) => t.name === toolCall.name);
4121
4515
  if (!tool2) {
4122
4516
  if (debug) {
@@ -4133,22 +4527,26 @@ async function executeToolCalls(toolCalls, tools, executeServerTool, waitForClie
4133
4527
  });
4134
4528
  continue;
4135
4529
  }
4136
- emitEvent?.({
4530
+ yield {
4137
4531
  type: "action:start",
4138
4532
  id: toolCall.id,
4139
4533
  name: toolCall.name,
4140
4534
  hidden: tool2.hidden ?? false
4141
- });
4142
- emitEvent?.({
4535
+ };
4536
+ yield {
4143
4537
  type: "action:args",
4144
4538
  id: toolCall.id,
4145
4539
  args: JSON.stringify(toolCall.input)
4146
- });
4540
+ };
4147
4541
  try {
4148
4542
  let response;
4543
+ throwIfAborted(signal);
4149
4544
  if (tool2.location === "server") {
4150
4545
  if (tool2.handler) {
4151
- response = await tool2.handler(toolCall.input);
4546
+ response = await tool2.handler(toolCall.input, {
4547
+ signal,
4548
+ toolCallId: toolCall.id
4549
+ });
4152
4550
  } else if (executeServerTool) {
4153
4551
  response = await executeServerTool(toolCall.name, toolCall.input);
4154
4552
  } else {
@@ -4171,12 +4569,13 @@ async function executeToolCalls(toolCalls, tools, executeServerTool, waitForClie
4171
4569
  };
4172
4570
  }
4173
4571
  }
4174
- emitEvent?.({
4572
+ throwIfAborted(signal);
4573
+ yield {
4175
4574
  type: "action:end",
4176
4575
  id: toolCall.id,
4177
4576
  name: toolCall.name,
4178
4577
  result: response
4179
- });
4578
+ };
4180
4579
  results.push({
4181
4580
  toolCallId: toolCall.id,
4182
4581
  content: JSON.stringify(response),
@@ -4184,13 +4583,14 @@ async function executeToolCalls(toolCalls, tools, executeServerTool, waitForClie
4184
4583
  error: response.error
4185
4584
  });
4186
4585
  } catch (error) {
4586
+ throwIfAborted(signal);
4187
4587
  const errorMessage = error instanceof Error ? error.message : "Tool execution failed";
4188
- emitEvent?.({
4588
+ yield {
4189
4589
  type: "action:end",
4190
4590
  id: toolCall.id,
4191
4591
  name: toolCall.name,
4192
4592
  error: errorMessage
4193
- });
4593
+ };
4194
4594
  results.push({
4195
4595
  toolCallId: toolCall.id,
4196
4596
  content: JSON.stringify({
@@ -4207,17 +4607,17 @@ async function executeToolCalls(toolCalls, tools, executeServerTool, waitForClie
4207
4607
 
4208
4608
  // src/core/audio.ts
4209
4609
  var audioErrorMarker = /* @__PURE__ */ Symbol.for("@yourgpt/llm-sdk/AudioAPIError");
4210
- var _a;
4610
+ var _a3;
4211
4611
  var AudioAPIError = class extends Error {
4212
4612
  constructor(message, code, status, requestId) {
4213
4613
  super(message);
4214
4614
  this.code = code;
4215
4615
  this.status = status;
4216
4616
  this.requestId = requestId;
4217
- this[_a] = true;
4617
+ this[_a3] = true;
4218
4618
  this.name = "AudioAPIError";
4219
4619
  }
4220
- static [(_a = audioErrorMarker, Symbol.hasInstance)](value) {
4620
+ static [(_a3 = audioErrorMarker, Symbol.hasInstance)](value) {
4221
4621
  return !!value && typeof value === "object" && value[audioErrorMarker] === true;
4222
4622
  }
4223
4623
  };
@@ -4255,7 +4655,9 @@ async function transcribe({
4255
4655
  exports.AudioAPIError = AudioAPIError;
4256
4656
  exports.DEFAULT_CAPABILITIES = DEFAULT_CAPABILITIES;
4257
4657
  exports.DEFAULT_MAX_ITERATIONS = DEFAULT_MAX_ITERATIONS;
4658
+ exports.DecisionAPIError = DecisionAPIError;
4258
4659
  exports.GenerateResult = GenerateResult;
4660
+ exports.MediaAPIError = MediaAPIError;
4259
4661
  exports.Runtime = Runtime;
4260
4662
  exports.StreamResult = StreamResult;
4261
4663
  exports.buildProviderToolOptions = buildProviderToolOptions;
@@ -4271,11 +4673,13 @@ exports.createSSEResponse = createSSEResponse;
4271
4673
  exports.createStreamResult = createStreamResult;
4272
4674
  exports.createTextStreamHeaders = createTextStreamHeaders;
4273
4675
  exports.createTextStreamResponse = createTextStreamResponse;
4676
+ exports.experimental_decide = experimental_decide;
4274
4677
  exports.extractInputMessages = extractInputMessages;
4275
4678
  exports.formatSSEData = formatSSEData;
4276
4679
  exports.formatToolsForAnthropic = formatToolsForAnthropic;
4277
4680
  exports.formatToolsForGoogle = formatToolsForGoogle;
4278
4681
  exports.formatToolsForOpenAI = formatToolsForOpenAI;
4682
+ exports.generateImage = generateImage;
4279
4683
  exports.generateMessageId = generateMessageId;
4280
4684
  exports.generateSpeech = generateSpeech;
4281
4685
  exports.generateText = generateText;