@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.
- package/LICENSE +21 -0
- package/README.md +46 -10
- package/dist/adapters/index.d.mts +11 -13
- package/dist/adapters/index.d.ts +11 -13
- package/dist/adapters/index.js +327 -100
- package/dist/adapters/index.mjs +327 -100
- package/dist/audio-K-Z3-Bce.d.mts +191 -0
- package/dist/audio-K-Z3-Bce.d.ts +191 -0
- package/dist/{base-BICaQaRS.d.mts → base-6LzczuCg.d.ts} +8 -1
- package/dist/{base-CpRPB8u8.d.ts → base-DySbTP8C.d.mts} +8 -1
- package/dist/decisions-CJzOt87t.d.mts +137 -0
- package/dist/decisions-CJzOt87t.d.ts +137 -0
- package/dist/fallback/index.d.mts +4 -4
- package/dist/fallback/index.d.ts +4 -4
- package/dist/http-BQxUT5A9.d.mts +9 -0
- package/dist/http-BQxUT5A9.d.ts +9 -0
- package/dist/index.d.mts +14 -8
- package/dist/index.d.ts +14 -8
- package/dist/index.js +599 -195
- package/dist/index.mjs +596 -196
- package/dist/providers/anthropic/index.d.mts +7 -4
- package/dist/providers/anthropic/index.d.ts +7 -4
- package/dist/providers/anthropic/index.js +383 -102
- package/dist/providers/anthropic/index.mjs +383 -102
- package/dist/providers/azure/index.d.mts +3 -3
- package/dist/providers/azure/index.d.ts +3 -3
- package/dist/providers/cloudflare/index.d.mts +11 -0
- package/dist/providers/cloudflare/index.d.ts +11 -0
- package/dist/providers/cloudflare/index.js +743 -0
- package/dist/providers/cloudflare/index.mjs +741 -0
- package/dist/providers/fireworks/index.d.mts +3 -3
- package/dist/providers/fireworks/index.d.ts +3 -3
- package/dist/providers/fireworks/index.js +108 -59
- package/dist/providers/fireworks/index.mjs +108 -59
- package/dist/providers/google/index.d.mts +3 -3
- package/dist/providers/google/index.d.ts +3 -3
- package/dist/providers/google/index.js +108 -59
- package/dist/providers/google/index.mjs +108 -59
- package/dist/providers/ollama/index.d.mts +4 -4
- package/dist/providers/ollama/index.d.ts +4 -4
- package/dist/providers/openai/index.d.mts +38 -6
- package/dist/providers/openai/index.d.ts +38 -6
- package/dist/providers/openai/index.js +1378 -84
- package/dist/providers/openai/index.mjs +1376 -85
- package/dist/providers/openrouter/index.d.mts +21 -4
- package/dist/providers/openrouter/index.d.ts +21 -4
- package/dist/providers/openrouter/index.js +967 -130
- package/dist/providers/openrouter/index.mjs +967 -131
- package/dist/providers/togetherai/index.d.mts +3 -3
- package/dist/providers/togetherai/index.d.ts +3 -3
- package/dist/providers/togetherai/index.js +108 -59
- package/dist/providers/togetherai/index.mjs +108 -59
- package/dist/providers/typesafe/index.d.mts +8 -0
- package/dist/providers/typesafe/index.d.ts +8 -0
- package/dist/providers/typesafe/index.js +735 -0
- package/dist/providers/typesafe/index.mjs +733 -0
- package/dist/providers/xai/index.d.mts +3 -3
- package/dist/providers/xai/index.d.ts +3 -3
- package/dist/providers/xai/index.js +108 -59
- package/dist/providers/xai/index.mjs +108 -59
- package/dist/{types-DCFqkJAy.d.ts → types-B0y6VZSQ.d.ts} +1 -1
- package/dist/{types-DHQEMFHA.d.mts → types-BXQMtaCX.d.ts} +17 -3
- package/dist/{types-C3RgRbEC.d.mts → types-BY6BVhFg.d.mts} +1 -1
- package/dist/{types-Dikj3En4.d.ts → types-C7_qVSSd.d.mts} +17 -3
- package/dist/{types-Dg1Bcg1k.d.mts → types-D7lzMMLu.d.mts} +13 -1
- package/dist/{types-Dg1Bcg1k.d.ts → types-D7lzMMLu.d.ts} +13 -1
- package/dist/yourgpt/index.d.mts +1 -1
- package/dist/yourgpt/index.d.ts +1 -1
- package/docs/decisions.md +174 -0
- package/docs/media.md +201 -0
- package/docs/multimodal-and-audio.md +9 -2
- package/package.json +34 -12
- package/dist/audio-BBSwnjo8.d.mts +0 -92
- 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 {
|
|
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
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
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
|
|
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)
|
|
596
|
-
|
|
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
|
|
1056
|
+
async pull(controller) {
|
|
703
1057
|
try {
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
controller.
|
|
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
|
|
1079
|
+
async pull(controller) {
|
|
718
1080
|
try {
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
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
|
-
|
|
726
|
-
controller.
|
|
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
|
-
|
|
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
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
name
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
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
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
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]
|
|
2477
|
-
parsedArgs
|
|
2882
|
+
`[Copilot SDK] Executing server-side tool: ${toolName}`
|
|
2478
2883
|
);
|
|
2479
2884
|
}
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
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
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
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
|
-
|
|
2939
|
+
break;
|
|
2500
2940
|
}
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
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
|
-
|
|
2522
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
4415
|
+
const results = yield* executeToolCalls(
|
|
4021
4416
|
toolCalls,
|
|
4022
4417
|
effectiveSelectedTools,
|
|
4023
4418
|
executeServerTool,
|
|
4024
4419
|
waitForClientToolResult,
|
|
4025
|
-
|
|
4026
|
-
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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[
|
|
4617
|
+
this[_a3] = true;
|
|
4218
4618
|
this.name = "AudioAPIError";
|
|
4219
4619
|
}
|
|
4220
|
-
static [(
|
|
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;
|