@combycode/llm-sdk 1.2.0 → 1.4.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/CHANGELOG.md +41 -0
- package/README.md +24 -0
- package/dist/agent/loop.d.ts +10 -1
- package/dist/helpers/one-shot.d.ts +8 -1
- package/dist/index.browser.js +378 -36
- package/dist/index.d.ts +1 -0
- package/dist/index.js +378 -36
- package/dist/llm/client.d.ts +12 -1
- package/dist/llm/files/retrieve.d.ts +44 -0
- package/dist/llm/providers/anthropic/messages.d.ts +3 -0
- package/dist/llm/providers/builtin-tools.d.ts +15 -0
- package/dist/llm/providers/google/generate.d.ts +7 -0
- package/dist/llm/providers/google/interactions.d.ts +2 -0
- package/dist/llm/providers/openai/completions.d.ts +2 -0
- package/dist/llm/providers/openai/responses.d.ts +8 -1
- package/dist/llm/providers/xai/responses.d.ts +4 -0
- package/dist/llm/types/provider.d.ts +9 -1
- package/dist/llm/types/response.d.ts +4 -0
- package/dist/llm/types/stream.d.ts +9 -1
- package/dist/network/types.d.ts +4 -2
- package/dist/plugins/model-catalog/catalog.d.ts +5 -0
- package/package.json +1 -1
package/dist/index.browser.js
CHANGED
|
@@ -2524,7 +2524,7 @@ var QueueState = class {
|
|
|
2524
2524
|
});
|
|
2525
2525
|
this.processed++;
|
|
2526
2526
|
if (response.ok) {
|
|
2527
|
-
const body = await parseResponseBody(response, entry.request.responseType ?? "json");
|
|
2527
|
+
const body = entry.request.responseType === "stream" ? response.body : await parseResponseBody(response, entry.request.responseType ?? "json");
|
|
2528
2528
|
this.semaphore.release();
|
|
2529
2529
|
entry.resolve({ status: response.status, headers: resHeaders, body });
|
|
2530
2530
|
return;
|
|
@@ -22105,6 +22105,15 @@ var catalog_default5 = {
|
|
|
22105
22105
|
}
|
|
22106
22106
|
};
|
|
22107
22107
|
|
|
22108
|
+
// src/llm/providers/builtin-tools.ts
|
|
22109
|
+
var PROVIDER_BUILTIN_TOOLS = {
|
|
22110
|
+
anthropic: ["web_search", "code_interpreter"],
|
|
22111
|
+
openai: ["web_search", "code_interpreter"],
|
|
22112
|
+
google: ["web_search", "code_interpreter"],
|
|
22113
|
+
xai: ["web_search", "code_interpreter"],
|
|
22114
|
+
openrouter: ["web_search"]
|
|
22115
|
+
};
|
|
22116
|
+
|
|
22108
22117
|
// src/plugins/model-catalog/catalog.ts
|
|
22109
22118
|
var PROVIDER_DEFAULT_CATALOGS = [
|
|
22110
22119
|
catalog_default,
|
|
@@ -22142,6 +22151,13 @@ var DEFAULT_REASONING = {
|
|
|
22142
22151
|
encryptedContent: false,
|
|
22143
22152
|
summaryAvailable: false
|
|
22144
22153
|
};
|
|
22154
|
+
function withBuiltinTools(provider, caps) {
|
|
22155
|
+
if (caps.builtinTools !== void 0) return caps;
|
|
22156
|
+
if (!caps.toolUse) return caps;
|
|
22157
|
+
const tools = PROVIDER_BUILTIN_TOOLS[provider];
|
|
22158
|
+
if (!tools || tools.length === 0) return caps;
|
|
22159
|
+
return { ...caps, builtinTools: [...tools] };
|
|
22160
|
+
}
|
|
22145
22161
|
var ModelCatalog = class {
|
|
22146
22162
|
models = /* @__PURE__ */ new Map();
|
|
22147
22163
|
/** `provider/alias` → `provider/canonical-slug`. Lets get()/resolveModelId
|
|
@@ -22160,7 +22176,7 @@ var ModelCatalog = class {
|
|
|
22160
22176
|
supportedApis: info.supportedApis ?? [info.preferredApi ?? "completions"],
|
|
22161
22177
|
contextWindow: info.contextWindow,
|
|
22162
22178
|
maxOutput: info.maxOutput,
|
|
22163
|
-
capabilities: { ...DEFAULT_CAPABILITIES, ...info.capabilities },
|
|
22179
|
+
capabilities: withBuiltinTools(provider, { ...DEFAULT_CAPABILITIES, ...info.capabilities }),
|
|
22164
22180
|
reasoning: { ...DEFAULT_REASONING, ...info.reasoning },
|
|
22165
22181
|
mediaOnly: info.mediaOnly,
|
|
22166
22182
|
tokenizer: info.tokenizer,
|
|
@@ -22214,6 +22230,15 @@ var ModelCatalog = class {
|
|
|
22214
22230
|
supportsTools(provider, model) {
|
|
22215
22231
|
return this.get(provider, model)?.capabilities.toolUse ?? false;
|
|
22216
22232
|
}
|
|
22233
|
+
/** Hosted server-side builtin tools this model supports (e.g. `['web_search',
|
|
22234
|
+
* 'code_interpreter']`). Empty when unknown or the model isn't tool-capable. */
|
|
22235
|
+
builtinToolsFor(provider, model) {
|
|
22236
|
+
return [...this.get(provider, model)?.capabilities.builtinTools ?? []];
|
|
22237
|
+
}
|
|
22238
|
+
/** Whether this model supports a specific hosted builtin tool. */
|
|
22239
|
+
supportsBuiltinTool(provider, model, tool) {
|
|
22240
|
+
return this.get(provider, model)?.capabilities.builtinTools?.includes(tool) ?? false;
|
|
22241
|
+
}
|
|
22217
22242
|
supportsPreviousResponseId(provider, model) {
|
|
22218
22243
|
const info = this.get(provider, model);
|
|
22219
22244
|
if (info && !info.supportedApis.some((a) => a === "responses" || a === "interactions")) {
|
|
@@ -22427,6 +22452,160 @@ async function* wrapParallel(raw, interval, moderate2) {
|
|
|
22427
22452
|
}
|
|
22428
22453
|
}
|
|
22429
22454
|
|
|
22455
|
+
// src/llm/files/retrieve.ts
|
|
22456
|
+
var DEFAULT_BASE = {
|
|
22457
|
+
anthropic: "https://api.anthropic.com",
|
|
22458
|
+
openai: "https://api.openai.com",
|
|
22459
|
+
xai: "https://api.x.ai",
|
|
22460
|
+
google: "https://generativelanguage.googleapis.com",
|
|
22461
|
+
openrouter: "https://openrouter.ai/api"
|
|
22462
|
+
};
|
|
22463
|
+
var OCTET_STREAM = "application/octet-stream";
|
|
22464
|
+
function contentRequest(ctx, file) {
|
|
22465
|
+
const base = ctx.baseURL ?? DEFAULT_BASE[ctx.provider];
|
|
22466
|
+
const id = file.id;
|
|
22467
|
+
if (ctx.provider === "anthropic") {
|
|
22468
|
+
return {
|
|
22469
|
+
url: `${base}/v1/files/${id}/content?beta=true`,
|
|
22470
|
+
headers: {
|
|
22471
|
+
"x-api-key": ctx.apiKey,
|
|
22472
|
+
"anthropic-version": "2023-06-01",
|
|
22473
|
+
"anthropic-beta": "files-api-2025-04-14",
|
|
22474
|
+
accept: "application/binary"
|
|
22475
|
+
}
|
|
22476
|
+
};
|
|
22477
|
+
}
|
|
22478
|
+
if (ctx.provider === "openai" || ctx.provider === "xai" || ctx.provider === "openrouter") {
|
|
22479
|
+
const containerId = file.ref?.containerId;
|
|
22480
|
+
const path = containerId ? `/v1/containers/${containerId}/files/${id}/content` : `/v1/files/${id}/content`;
|
|
22481
|
+
return { url: `${base}${path}`, headers: { authorization: `Bearer ${ctx.apiKey}` } };
|
|
22482
|
+
}
|
|
22483
|
+
if (ctx.provider === "google") {
|
|
22484
|
+
return {
|
|
22485
|
+
url: `${base}/v1beta/files/${id}:download?alt=media`,
|
|
22486
|
+
headers: { "x-goog-api-key": ctx.apiKey }
|
|
22487
|
+
};
|
|
22488
|
+
}
|
|
22489
|
+
throw new Error(`retrieveFile: no file-content endpoint for provider "${ctx.provider}"`);
|
|
22490
|
+
}
|
|
22491
|
+
function providerAuth(ctx, url) {
|
|
22492
|
+
const base = ctx.baseURL ?? DEFAULT_BASE[ctx.provider];
|
|
22493
|
+
if (!url.startsWith(base)) return {};
|
|
22494
|
+
if (ctx.provider === "anthropic") {
|
|
22495
|
+
return { "x-api-key": ctx.apiKey, "anthropic-version": "2023-06-01" };
|
|
22496
|
+
}
|
|
22497
|
+
if (ctx.provider === "google") return { "x-goog-api-key": ctx.apiKey };
|
|
22498
|
+
return { authorization: `Bearer ${ctx.apiKey}` };
|
|
22499
|
+
}
|
|
22500
|
+
async function fetchFileResponse(ctx, file, responseType) {
|
|
22501
|
+
if (file.url) {
|
|
22502
|
+
return ctx.fetch({
|
|
22503
|
+
url: file.url,
|
|
22504
|
+
method: "GET",
|
|
22505
|
+
headers: providerAuth(ctx, file.url),
|
|
22506
|
+
body: void 0,
|
|
22507
|
+
provider: ctx.provider,
|
|
22508
|
+
model: "files",
|
|
22509
|
+
responseType
|
|
22510
|
+
});
|
|
22511
|
+
}
|
|
22512
|
+
if (file.id) {
|
|
22513
|
+
const { url, headers } = contentRequest(ctx, file);
|
|
22514
|
+
return ctx.fetch({
|
|
22515
|
+
url,
|
|
22516
|
+
method: "GET",
|
|
22517
|
+
headers,
|
|
22518
|
+
body: void 0,
|
|
22519
|
+
provider: ctx.provider,
|
|
22520
|
+
model: "files",
|
|
22521
|
+
responseType
|
|
22522
|
+
});
|
|
22523
|
+
}
|
|
22524
|
+
throw new Error("retrieveFile: FileOutput has neither `data`, `url`, nor `id`");
|
|
22525
|
+
}
|
|
22526
|
+
function bytesToBlob(bytes, type) {
|
|
22527
|
+
const ab = new ArrayBuffer(bytes.byteLength);
|
|
22528
|
+
new Uint8Array(ab).set(bytes);
|
|
22529
|
+
return new Blob([ab], { type });
|
|
22530
|
+
}
|
|
22531
|
+
function singleChunkStream(bytes) {
|
|
22532
|
+
return new ReadableStream({
|
|
22533
|
+
start(controller) {
|
|
22534
|
+
controller.enqueue(bytes);
|
|
22535
|
+
controller.close();
|
|
22536
|
+
}
|
|
22537
|
+
});
|
|
22538
|
+
}
|
|
22539
|
+
var EXT_MIME = {
|
|
22540
|
+
png: "image/png",
|
|
22541
|
+
jpg: "image/jpeg",
|
|
22542
|
+
jpeg: "image/jpeg",
|
|
22543
|
+
gif: "image/gif",
|
|
22544
|
+
webp: "image/webp",
|
|
22545
|
+
svg: "image/svg+xml",
|
|
22546
|
+
bmp: "image/bmp",
|
|
22547
|
+
csv: "text/csv",
|
|
22548
|
+
tsv: "text/tab-separated-values",
|
|
22549
|
+
txt: "text/plain",
|
|
22550
|
+
json: "application/json",
|
|
22551
|
+
xml: "application/xml",
|
|
22552
|
+
html: "text/html",
|
|
22553
|
+
md: "text/markdown",
|
|
22554
|
+
pdf: "application/pdf",
|
|
22555
|
+
zip: "application/zip",
|
|
22556
|
+
parquet: "application/vnd.apache.parquet",
|
|
22557
|
+
xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
|
|
22558
|
+
};
|
|
22559
|
+
function mimeFromName(name) {
|
|
22560
|
+
const ext = name?.includes(".") ? name.split(".").pop()?.toLowerCase() : void 0;
|
|
22561
|
+
return ext ? EXT_MIME[ext] : void 0;
|
|
22562
|
+
}
|
|
22563
|
+
function header(headers, name) {
|
|
22564
|
+
const lower = name.toLowerCase();
|
|
22565
|
+
for (const k in headers) if (k.toLowerCase() === lower) return headers[k];
|
|
22566
|
+
return void 0;
|
|
22567
|
+
}
|
|
22568
|
+
function filenameFromDisposition(cd) {
|
|
22569
|
+
if (!cd) return void 0;
|
|
22570
|
+
const ext = cd.match(/filename\*=[^']*''([^;]+)/i);
|
|
22571
|
+
if (ext) {
|
|
22572
|
+
try {
|
|
22573
|
+
return decodeURIComponent(ext[1].trim());
|
|
22574
|
+
} catch {
|
|
22575
|
+
}
|
|
22576
|
+
}
|
|
22577
|
+
const plain = cd.match(/filename="?([^";]+)"?/i);
|
|
22578
|
+
return plain ? plain[1].trim() : void 0;
|
|
22579
|
+
}
|
|
22580
|
+
async function retrieveFile(file, ctx) {
|
|
22581
|
+
if (file.data) {
|
|
22582
|
+
const blob2 = bytesToBlob(base64ToBytes(file.data), file.mimeType ?? OCTET_STREAM);
|
|
22583
|
+
return { blob: blob2, name: file.name, mimeType: blob2.type, size: blob2.size };
|
|
22584
|
+
}
|
|
22585
|
+
const res = await fetchFileResponse(ctx, file, "arraybuffer");
|
|
22586
|
+
const headers = res.headers ?? {};
|
|
22587
|
+
const name = filenameFromDisposition(header(headers, "content-disposition")) ?? file.name;
|
|
22588
|
+
const mimeType = header(headers, "content-type")?.split(";")[0].trim() || file.mimeType || mimeFromName(name) || OCTET_STREAM;
|
|
22589
|
+
const blob = new Blob([res.body], { type: mimeType });
|
|
22590
|
+
return { blob, name, mimeType, size: blob.size };
|
|
22591
|
+
}
|
|
22592
|
+
async function streamFile(file, ctx) {
|
|
22593
|
+
if (file.data) {
|
|
22594
|
+
const bytes = base64ToBytes(file.data);
|
|
22595
|
+
return { stream: singleChunkStream(bytes), name: file.name, mimeType: file.mimeType, size: bytes.byteLength };
|
|
22596
|
+
}
|
|
22597
|
+
const res = await fetchFileResponse(ctx, file, "stream");
|
|
22598
|
+
const headers = res.headers ?? {};
|
|
22599
|
+
const len = header(headers, "content-length");
|
|
22600
|
+
const name = filenameFromDisposition(header(headers, "content-disposition")) ?? file.name;
|
|
22601
|
+
return {
|
|
22602
|
+
stream: res.body,
|
|
22603
|
+
name,
|
|
22604
|
+
mimeType: header(headers, "content-type")?.split(";")[0].trim() || file.mimeType || mimeFromName(name),
|
|
22605
|
+
size: len ? Number(len) : void 0
|
|
22606
|
+
};
|
|
22607
|
+
}
|
|
22608
|
+
|
|
22430
22609
|
// src/util/duration.ts
|
|
22431
22610
|
var UNIT_MS = {
|
|
22432
22611
|
s: 1e3,
|
|
@@ -22669,6 +22848,28 @@ var LLMClient = class {
|
|
|
22669
22848
|
else next.output = result;
|
|
22670
22849
|
return next;
|
|
22671
22850
|
}
|
|
22851
|
+
// ─── File retrieval (hosted-tool output files) ─────────────────────────
|
|
22852
|
+
retrieveContext() {
|
|
22853
|
+
return {
|
|
22854
|
+
provider: this.provider,
|
|
22855
|
+
apiKey: this.apiKey,
|
|
22856
|
+
fetch: this.fetchFn,
|
|
22857
|
+
baseURL: this.adapter.baseURL()
|
|
22858
|
+
};
|
|
22859
|
+
}
|
|
22860
|
+
/** Fetch a hosted-tool output file (e.g. a code-execution file from
|
|
22861
|
+
* `response.files`): its bytes as a `Blob` plus `name` / `mimeType` / `size`.
|
|
22862
|
+
* Resolves inline `data`, a `url`, or a provider file `id` — all through this
|
|
22863
|
+
* client's provider + auth + engine. */
|
|
22864
|
+
retrieveFile(file) {
|
|
22865
|
+
return retrieveFile(file, this.retrieveContext());
|
|
22866
|
+
}
|
|
22867
|
+
/** Stream a hosted-tool output file: a `ReadableStream<Uint8Array>` plus
|
|
22868
|
+
* best-effort `name` / `mimeType` / `size` (from the response headers). For large
|
|
22869
|
+
* files piped straight to a file / GridFS / HTTP response without buffering. */
|
|
22870
|
+
streamFile(file) {
|
|
22871
|
+
return streamFile(file, this.retrieveContext());
|
|
22872
|
+
}
|
|
22672
22873
|
/** Submit a request. Returns the parsed CompletionResponse. */
|
|
22673
22874
|
async complete(input, options = {}) {
|
|
22674
22875
|
const rawMessages = normalizeInput(input);
|
|
@@ -22880,17 +23081,18 @@ var LLMClient = class {
|
|
|
22880
23081
|
let usage = emptyUsage();
|
|
22881
23082
|
let finishReason = "stop";
|
|
22882
23083
|
let moderationReport;
|
|
23084
|
+
const files = [];
|
|
22883
23085
|
const fetchStream = this.fetchStreamFn;
|
|
22884
|
-
const adapter = this.adapter;
|
|
22885
23086
|
const queueName = this.queueName;
|
|
22886
23087
|
const priority = this.priority;
|
|
23088
|
+
const parseStream = this.adapter.createStreamParser();
|
|
22887
23089
|
async function* rawEvents() {
|
|
22888
23090
|
for await (const sseEvent of fetchStream(httpReq, {
|
|
22889
23091
|
queueName,
|
|
22890
23092
|
priority,
|
|
22891
23093
|
ctx
|
|
22892
23094
|
})) {
|
|
22893
|
-
for (const ev of
|
|
23095
|
+
for (const ev of parseStream(sseEvent)) yield ev;
|
|
22894
23096
|
}
|
|
22895
23097
|
}
|
|
22896
23098
|
const mod = options.moderation;
|
|
@@ -22928,6 +23130,9 @@ var LLMClient = class {
|
|
|
22928
23130
|
case "done":
|
|
22929
23131
|
finishReason = event.finishReason;
|
|
22930
23132
|
break;
|
|
23133
|
+
case "file":
|
|
23134
|
+
files.push(event.file);
|
|
23135
|
+
break;
|
|
22931
23136
|
case "moderation":
|
|
22932
23137
|
moderationReport = this.mergeModeration(
|
|
22933
23138
|
moderationReport,
|
|
@@ -22950,6 +23155,7 @@ var LLMClient = class {
|
|
|
22950
23155
|
toolCalls: [],
|
|
22951
23156
|
thinking: thinking || null,
|
|
22952
23157
|
media: [],
|
|
23158
|
+
...files.length ? { files } : {},
|
|
22953
23159
|
...moderationReport ? { moderation: moderationReport } : {},
|
|
22954
23160
|
latencyMs: performance.now() - start,
|
|
22955
23161
|
raw: null
|
|
@@ -23210,6 +23416,22 @@ function anthropicRequestTier(t) {
|
|
|
23210
23416
|
function anthropicBilledTier(raw) {
|
|
23211
23417
|
return typeof raw === "string" && raw ? { serviceTier: raw, pricingTier: raw } : {};
|
|
23212
23418
|
}
|
|
23419
|
+
function filesFromCodeExecBlock(block) {
|
|
23420
|
+
if (block.type !== "bash_code_execution_tool_result" && block.type !== "code_execution_tool_result") {
|
|
23421
|
+
return [];
|
|
23422
|
+
}
|
|
23423
|
+
const result = block.content;
|
|
23424
|
+
if (!result || result.type !== "bash_code_execution_result" && result.type !== "code_execution_result" || !Array.isArray(result.content)) {
|
|
23425
|
+
return [];
|
|
23426
|
+
}
|
|
23427
|
+
const files = [];
|
|
23428
|
+
for (const out of result.content) {
|
|
23429
|
+
if ((out.type === "bash_code_execution_output" || out.type === "code_execution_output") && typeof out.file_id === "string") {
|
|
23430
|
+
files.push({ id: out.file_id, source: "code_execution" });
|
|
23431
|
+
}
|
|
23432
|
+
}
|
|
23433
|
+
return files;
|
|
23434
|
+
}
|
|
23213
23435
|
var AnthropicAdapter = class {
|
|
23214
23436
|
name = "anthropic";
|
|
23215
23437
|
apiKey;
|
|
@@ -23390,15 +23612,8 @@ var AnthropicAdapter = class {
|
|
|
23390
23612
|
};
|
|
23391
23613
|
content.push(tc);
|
|
23392
23614
|
toolCalls.push(tc);
|
|
23393
|
-
} else
|
|
23394
|
-
|
|
23395
|
-
if (result && (result.type === "bash_code_execution_result" || result.type === "code_execution_result") && Array.isArray(result.content)) {
|
|
23396
|
-
for (const out of result.content) {
|
|
23397
|
-
if ((out.type === "bash_code_execution_output" || out.type === "code_execution_output") && typeof out.file_id === "string") {
|
|
23398
|
-
files.push({ id: out.file_id, source: "code_execution" });
|
|
23399
|
-
}
|
|
23400
|
-
}
|
|
23401
|
-
}
|
|
23615
|
+
} else {
|
|
23616
|
+
files.push(...filesFromCodeExecBlock(block));
|
|
23402
23617
|
}
|
|
23403
23618
|
}
|
|
23404
23619
|
const finishReason = extractFinishReason(toolCalls.length > 0, r.stop_reason, {
|
|
@@ -23437,6 +23652,8 @@ var AnthropicAdapter = class {
|
|
|
23437
23652
|
if (block.type === "tool_use") {
|
|
23438
23653
|
return [{ type: "tool_call_start", id: block.id, name: block.name }];
|
|
23439
23654
|
}
|
|
23655
|
+
const files = filesFromCodeExecBlock(block);
|
|
23656
|
+
if (files.length) return files.map((file) => ({ type: "file", file }));
|
|
23440
23657
|
}
|
|
23441
23658
|
if (type === "content_block_stop") {
|
|
23442
23659
|
}
|
|
@@ -23460,6 +23677,11 @@ var AnthropicAdapter = class {
|
|
|
23460
23677
|
}
|
|
23461
23678
|
return [];
|
|
23462
23679
|
}
|
|
23680
|
+
/** Stateless — every event is self-contained (code-execution result blocks
|
|
23681
|
+
* arrive complete in a single content_block_start). */
|
|
23682
|
+
createStreamParser() {
|
|
23683
|
+
return (event) => this.parseStreamEvent(event);
|
|
23684
|
+
}
|
|
23463
23685
|
parseUsage(u) {
|
|
23464
23686
|
if (!u) return emptyUsage();
|
|
23465
23687
|
const inputTokens = u.input_tokens ?? 0;
|
|
@@ -23984,6 +24206,18 @@ var GoogleAdapter = class {
|
|
|
23984
24206
|
};
|
|
23985
24207
|
}
|
|
23986
24208
|
parseStreamEvent(event) {
|
|
24209
|
+
return this.streamEvents(event, { codeExec: false });
|
|
24210
|
+
}
|
|
24211
|
+
/** Stateful — Google splits the code-execution marker (`executableCode` /
|
|
24212
|
+
* `codeExecutionResult`) and the produced file (`inlineData`) across parts and
|
|
24213
|
+
* often across SSE events. The closure remembers "code execution began in this
|
|
24214
|
+
* stream" so a later `inlineData` blob is routed to `files` (a code-exec
|
|
24215
|
+
* artifact) rather than `media` (conversational output). */
|
|
24216
|
+
createStreamParser() {
|
|
24217
|
+
const state = { codeExec: false };
|
|
24218
|
+
return (event) => this.streamEvents(event, state);
|
|
24219
|
+
}
|
|
24220
|
+
streamEvents(event, state) {
|
|
23987
24221
|
const data = JSON.parse(event.data);
|
|
23988
24222
|
const candidates = data.candidates ?? [];
|
|
23989
24223
|
const candidate = candidates[0];
|
|
@@ -23997,6 +24231,9 @@ var GoogleAdapter = class {
|
|
|
23997
24231
|
const rawContent = candidate.content ?? {};
|
|
23998
24232
|
const parts = rawContent.parts ?? [];
|
|
23999
24233
|
const events = [];
|
|
24234
|
+
for (const part of parts) {
|
|
24235
|
+
if (part.executableCode || part.codeExecutionResult) state.codeExec = true;
|
|
24236
|
+
}
|
|
24000
24237
|
for (const part of parts) {
|
|
24001
24238
|
if (part.text !== void 0 && !part.thought)
|
|
24002
24239
|
events.push({ type: "text", text: part.text });
|
|
@@ -24004,10 +24241,17 @@ var GoogleAdapter = class {
|
|
|
24004
24241
|
if (part.inlineData) {
|
|
24005
24242
|
const inline = part.inlineData;
|
|
24006
24243
|
const mime = inline.mimeType;
|
|
24007
|
-
|
|
24008
|
-
|
|
24009
|
-
|
|
24010
|
-
|
|
24244
|
+
if (state.codeExec) {
|
|
24245
|
+
events.push({
|
|
24246
|
+
type: "file",
|
|
24247
|
+
file: { data: inline.data, mimeType: mime, source: "code_execution" }
|
|
24248
|
+
});
|
|
24249
|
+
} else {
|
|
24250
|
+
const mediaType = mime.startsWith("image/") ? "image" : mime.startsWith("audio/") ? "audio" : "video";
|
|
24251
|
+
events.push({ type: "media_start", mediaType, mimeType: mime });
|
|
24252
|
+
events.push({ type: "media_chunk", data: inline.data });
|
|
24253
|
+
events.push({ type: "media_end" });
|
|
24254
|
+
}
|
|
24011
24255
|
}
|
|
24012
24256
|
if (part.functionCall) {
|
|
24013
24257
|
const fc = part.functionCall;
|
|
@@ -24302,6 +24546,10 @@ var GoogleInteractionsAdapter = class {
|
|
|
24302
24546
|
}
|
|
24303
24547
|
return events;
|
|
24304
24548
|
}
|
|
24549
|
+
/** Stateless — the Interactions adapter surfaces no code-execution file outputs. */
|
|
24550
|
+
createStreamParser() {
|
|
24551
|
+
return (event) => this.parseStreamEvent(event);
|
|
24552
|
+
}
|
|
24305
24553
|
parseUsage(u) {
|
|
24306
24554
|
if (!u) return emptyUsage();
|
|
24307
24555
|
const input = u.total_input_tokens ?? u.prompt_tokens ?? 0;
|
|
@@ -25267,6 +25515,10 @@ var OpenAIAdapter = class {
|
|
|
25267
25515
|
}
|
|
25268
25516
|
return events;
|
|
25269
25517
|
}
|
|
25518
|
+
/** Stateless — Chat Completions has no hosted code-execution file outputs. */
|
|
25519
|
+
createStreamParser() {
|
|
25520
|
+
return (event) => this.parseStreamEvent(event);
|
|
25521
|
+
}
|
|
25270
25522
|
parseUsage(u) {
|
|
25271
25523
|
if (!u) return emptyUsage();
|
|
25272
25524
|
const input = u.prompt_tokens ?? u.input_tokens ?? 0;
|
|
@@ -25782,6 +26034,33 @@ function filenameForMime(mimeType) {
|
|
|
25782
26034
|
if (mimeType.startsWith("image/")) return `file.${mimeType.slice("image/".length)}`;
|
|
25783
26035
|
return "file.bin";
|
|
25784
26036
|
}
|
|
26037
|
+
function filesFromResponsesOutputItem(item) {
|
|
26038
|
+
const files = [];
|
|
26039
|
+
const type = item.type;
|
|
26040
|
+
if (type === "message") {
|
|
26041
|
+
for (const c of item.content ?? []) {
|
|
26042
|
+
if (c.type !== "output_text") continue;
|
|
26043
|
+
for (const a of c.annotations ?? []) {
|
|
26044
|
+
if (a.type === "container_file_citation" && typeof a.file_id === "string") {
|
|
26045
|
+
files.push({
|
|
26046
|
+
id: a.file_id,
|
|
26047
|
+
...typeof a.filename === "string" ? { name: a.filename } : {},
|
|
26048
|
+
...typeof a.container_id === "string" ? { ref: { containerId: a.container_id } } : {},
|
|
26049
|
+
source: "code_execution"
|
|
26050
|
+
});
|
|
26051
|
+
}
|
|
26052
|
+
}
|
|
26053
|
+
}
|
|
26054
|
+
}
|
|
26055
|
+
if (type === "code_interpreter_call") {
|
|
26056
|
+
for (const out of item.outputs ?? []) {
|
|
26057
|
+
if (out.type === "image" && typeof out.url === "string") {
|
|
26058
|
+
files.push({ url: out.url, source: "code_execution" });
|
|
26059
|
+
}
|
|
26060
|
+
}
|
|
26061
|
+
}
|
|
26062
|
+
return files;
|
|
26063
|
+
}
|
|
25785
26064
|
var OpenAIResponsesAdapter = class {
|
|
25786
26065
|
name = "openai";
|
|
25787
26066
|
apiKey;
|
|
@@ -25962,6 +26241,7 @@ var OpenAIResponsesAdapter = class {
|
|
|
25962
26241
|
let text = "";
|
|
25963
26242
|
for (const item of output) {
|
|
25964
26243
|
const type = item.type;
|
|
26244
|
+
files.push(...this.filesFromOutputItem(item));
|
|
25965
26245
|
if (type === "message") {
|
|
25966
26246
|
const itemContent = item.content ?? [];
|
|
25967
26247
|
for (const c of itemContent) {
|
|
@@ -25969,22 +26249,6 @@ var OpenAIResponsesAdapter = class {
|
|
|
25969
26249
|
const t = c.text;
|
|
25970
26250
|
text += t;
|
|
25971
26251
|
content.push({ type: "text", text: t });
|
|
25972
|
-
for (const a of c.annotations ?? []) {
|
|
25973
|
-
if (a.type === "container_file_citation" && typeof a.file_id === "string") {
|
|
25974
|
-
files.push({
|
|
25975
|
-
id: a.file_id,
|
|
25976
|
-
...typeof a.filename === "string" ? { name: a.filename } : {},
|
|
25977
|
-
source: "code_execution"
|
|
25978
|
-
});
|
|
25979
|
-
}
|
|
25980
|
-
}
|
|
25981
|
-
}
|
|
25982
|
-
}
|
|
25983
|
-
}
|
|
25984
|
-
if (type === "code_interpreter_call") {
|
|
25985
|
-
for (const out of item.outputs ?? []) {
|
|
25986
|
-
if (out.type === "image" && typeof out.url === "string") {
|
|
25987
|
-
files.push({ url: out.url, source: "code_execution" });
|
|
25988
26252
|
}
|
|
25989
26253
|
}
|
|
25990
26254
|
}
|
|
@@ -26079,6 +26343,7 @@ var OpenAIResponsesAdapter = class {
|
|
|
26079
26343
|
}
|
|
26080
26344
|
if (type === "response.output_item.done") {
|
|
26081
26345
|
const item = data.item;
|
|
26346
|
+
for (const file of this.filesFromOutputItem(item)) events.push({ type: "file", file });
|
|
26082
26347
|
if (item?.type === "function_call") {
|
|
26083
26348
|
events.push({ type: "tool_call_end", id: item.call_id ?? "" });
|
|
26084
26349
|
}
|
|
@@ -26109,6 +26374,17 @@ var OpenAIResponsesAdapter = class {
|
|
|
26109
26374
|
}
|
|
26110
26375
|
return events;
|
|
26111
26376
|
}
|
|
26377
|
+
/** Stateless — each output item finalizes with all its file annotations in a
|
|
26378
|
+
* single response.output_item.done event. */
|
|
26379
|
+
createStreamParser() {
|
|
26380
|
+
return (event) => this.parseStreamEvent(event);
|
|
26381
|
+
}
|
|
26382
|
+
/** Hosted code-execution output files from one output item. Overridable so
|
|
26383
|
+
* Responses-compatible providers with a different file shape (e.g. xAI, which
|
|
26384
|
+
* embeds files in the code-interpreter `logs` payload) can extend it. */
|
|
26385
|
+
filesFromOutputItem(item) {
|
|
26386
|
+
return filesFromResponsesOutputItem(item);
|
|
26387
|
+
}
|
|
26112
26388
|
parseUsage(u) {
|
|
26113
26389
|
if (!u) return emptyUsage();
|
|
26114
26390
|
const input = u.input_tokens ?? 0;
|
|
@@ -26673,6 +26949,29 @@ var XAIMediaAdapter = class {
|
|
|
26673
26949
|
};
|
|
26674
26950
|
|
|
26675
26951
|
// src/llm/providers/xai/responses.ts
|
|
26952
|
+
function xaiCodeExecFiles(item) {
|
|
26953
|
+
if (item.type !== "code_interpreter_call") return [];
|
|
26954
|
+
const files = [];
|
|
26955
|
+
for (const out of item.outputs ?? []) {
|
|
26956
|
+
if (out.type !== "logs" || typeof out.logs !== "string") continue;
|
|
26957
|
+
let parsed;
|
|
26958
|
+
try {
|
|
26959
|
+
parsed = JSON.parse(out.logs);
|
|
26960
|
+
} catch {
|
|
26961
|
+
continue;
|
|
26962
|
+
}
|
|
26963
|
+
for (const f of parsed.output_files ?? []) {
|
|
26964
|
+
if (!Array.isArray(f.data)) continue;
|
|
26965
|
+
files.push({
|
|
26966
|
+
data: bytesToBase64(Uint8Array.from(f.data)),
|
|
26967
|
+
...typeof f.file_name === "string" ? { name: f.file_name } : {},
|
|
26968
|
+
...typeof f.mime_type === "string" ? { mimeType: f.mime_type } : {},
|
|
26969
|
+
source: "code_execution"
|
|
26970
|
+
});
|
|
26971
|
+
}
|
|
26972
|
+
}
|
|
26973
|
+
return files;
|
|
26974
|
+
}
|
|
26676
26975
|
var XAIResponsesAdapter = class extends OpenAIResponsesAdapter {
|
|
26677
26976
|
name = "xai";
|
|
26678
26977
|
constructor(config) {
|
|
@@ -26692,8 +26991,20 @@ var XAIResponsesAdapter = class extends OpenAIResponsesAdapter {
|
|
|
26692
26991
|
if (!req.model.includes("multi-agent")) {
|
|
26693
26992
|
delete body.reasoning;
|
|
26694
26993
|
}
|
|
26994
|
+
const usesCodeInterpreter = req.tools?.some(
|
|
26995
|
+
(t) => !isFunctionTool(t) && t.type === "code_interpreter"
|
|
26996
|
+
);
|
|
26997
|
+
if (usesCodeInterpreter) {
|
|
26998
|
+
const include = /* @__PURE__ */ new Set([...body.include ?? [], "code_interpreter_call.outputs"]);
|
|
26999
|
+
body.include = [...include];
|
|
27000
|
+
}
|
|
26695
27001
|
return result;
|
|
26696
27002
|
}
|
|
27003
|
+
/** xAI embeds code-execution files inline in the `logs` payload — extend the base
|
|
27004
|
+
* extraction (which handles OpenAI-style annotations / image URLs) with the xAI shape. */
|
|
27005
|
+
filesFromOutputItem(item) {
|
|
27006
|
+
return [...super.filesFromOutputItem(item), ...xaiCodeExecFiles(item)];
|
|
27007
|
+
}
|
|
26697
27008
|
};
|
|
26698
27009
|
|
|
26699
27010
|
// src/util/json-schema.ts
|
|
@@ -27830,6 +28141,19 @@ var AgentLoop = class _AgentLoop {
|
|
|
27830
28141
|
get model() {
|
|
27831
28142
|
return this.client.model;
|
|
27832
28143
|
}
|
|
28144
|
+
// ─── File retrieval (hosted-tool output files) ──────────────────────────
|
|
28145
|
+
/** Fetch a hosted-tool output file from the run's `response.files` — its bytes
|
|
28146
|
+
* as a `Blob` plus `name` / `mimeType` / `size`. Delegates to the underlying
|
|
28147
|
+
* client, so it uses this agent's provider + key (same as `retrieveFile` on a
|
|
28148
|
+
* `complete()` result). */
|
|
28149
|
+
retrieveFile(file) {
|
|
28150
|
+
return this.client.retrieveFile(file);
|
|
28151
|
+
}
|
|
28152
|
+
/** Stream a hosted-tool output file (large files) — a `ReadableStream` plus
|
|
28153
|
+
* best-effort `name` / `mimeType` / `size`. Delegates to the underlying client. */
|
|
28154
|
+
streamFile(file) {
|
|
28155
|
+
return this.client.streamFile(file);
|
|
28156
|
+
}
|
|
27833
28157
|
get system() {
|
|
27834
28158
|
return this._system;
|
|
27835
28159
|
}
|
|
@@ -34464,7 +34788,13 @@ async function complete(opts) {
|
|
|
34464
34788
|
serviceTier
|
|
34465
34789
|
});
|
|
34466
34790
|
}
|
|
34467
|
-
const result = {
|
|
34791
|
+
const result = {
|
|
34792
|
+
text: res.text,
|
|
34793
|
+
response: res,
|
|
34794
|
+
// Bound to this call's client (same provider/model/key/engine).
|
|
34795
|
+
retrieveFile: (file) => llm.retrieveFile(file),
|
|
34796
|
+
streamFile: (file) => llm.streamFile(file)
|
|
34797
|
+
};
|
|
34468
34798
|
if (opts.structured?.schema) {
|
|
34469
34799
|
result.parsed = parseStructured(res.text);
|
|
34470
34800
|
}
|
|
@@ -36599,12 +36929,16 @@ var DEFAULT_TAGS = {
|
|
|
36599
36929
|
huge: "context:large"
|
|
36600
36930
|
};
|
|
36601
36931
|
var CAP_KEYS = {
|
|
36602
|
-
search: "webSearch",
|
|
36603
36932
|
vision: "vision",
|
|
36604
36933
|
tools: "toolUse",
|
|
36605
36934
|
audio: "audio",
|
|
36606
36935
|
structured: "structuredOutput"
|
|
36607
36936
|
};
|
|
36937
|
+
var BUILTIN_TOOL_KEYS = {
|
|
36938
|
+
search: "web_search",
|
|
36939
|
+
web_search: "web_search",
|
|
36940
|
+
code_interpreter: "code_interpreter"
|
|
36941
|
+
};
|
|
36608
36942
|
var KNOWN_KEYS = /* @__PURE__ */ new Set([
|
|
36609
36943
|
"price",
|
|
36610
36944
|
"context",
|
|
@@ -36614,7 +36948,8 @@ var KNOWN_KEYS = /* @__PURE__ */ new Set([
|
|
|
36614
36948
|
"status",
|
|
36615
36949
|
"provider",
|
|
36616
36950
|
"active",
|
|
36617
|
-
...Object.keys(CAP_KEYS)
|
|
36951
|
+
...Object.keys(CAP_KEYS),
|
|
36952
|
+
...Object.keys(BUILTIN_TOOL_KEYS)
|
|
36618
36953
|
]);
|
|
36619
36954
|
function parseNum(v) {
|
|
36620
36955
|
const m = /^([\d.]+)\s*([kKmM]?)$/.exec(v.trim());
|
|
@@ -36683,6 +37018,13 @@ function matches(m, c, th, tier) {
|
|
|
36683
37018
|
case "active":
|
|
36684
37019
|
return isNo(c.value) ? m.active === false : m.active !== false;
|
|
36685
37020
|
default: {
|
|
37021
|
+
const tool = BUILTIN_TOOL_KEYS[c.key];
|
|
37022
|
+
if (tool) {
|
|
37023
|
+
const inList = m.capabilities.builtinTools?.includes(tool) ?? false;
|
|
37024
|
+
const legacy = tool === "web_search" && !!m.capabilities.webSearch;
|
|
37025
|
+
const has2 = inList || legacy;
|
|
37026
|
+
return isNo(c.value) ? !has2 : has2;
|
|
37027
|
+
}
|
|
36686
37028
|
const cap = CAP_KEYS[c.key];
|
|
36687
37029
|
const has = !!m.capabilities[cap];
|
|
36688
37030
|
return isNo(c.value) ? !has : has;
|
package/dist/index.d.ts
CHANGED
|
@@ -49,6 +49,7 @@ export { isFunctionTool, isBuiltinTool } from './llm/types/tools';
|
|
|
49
49
|
export type { FunctionTool, BuiltinTool, McpToolParams, Tool, ToolChoice, JsonSchema } from './llm/types/tools';
|
|
50
50
|
export { emptyUsage } from './llm/types/response';
|
|
51
51
|
export type { CompletionResponse, FileOutput, FinishReason, Usage } from './llm/types/response';
|
|
52
|
+
export type { FileStream, RetrievedFile } from './llm/files/retrieve';
|
|
52
53
|
export type { NormalizedRequest, ReasoningContext, ThinkingConfig, CacheConfig } from './llm/types/request';
|
|
53
54
|
export type { MediaStreamType, StreamEvent } from './llm/types/stream';
|
|
54
55
|
export type { ExecuteOptions } from './llm/types/options';
|