@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.js
CHANGED
|
@@ -2451,7 +2451,7 @@ var QueueState = class {
|
|
|
2451
2451
|
});
|
|
2452
2452
|
this.processed++;
|
|
2453
2453
|
if (response.ok) {
|
|
2454
|
-
const body = await parseResponseBody(response, entry.request.responseType ?? "json");
|
|
2454
|
+
const body = entry.request.responseType === "stream" ? response.body : await parseResponseBody(response, entry.request.responseType ?? "json");
|
|
2455
2455
|
this.semaphore.release();
|
|
2456
2456
|
entry.resolve({ status: response.status, headers: resHeaders, body });
|
|
2457
2457
|
return;
|
|
@@ -22032,6 +22032,15 @@ var catalog_default5 = {
|
|
|
22032
22032
|
}
|
|
22033
22033
|
};
|
|
22034
22034
|
|
|
22035
|
+
// src/llm/providers/builtin-tools.ts
|
|
22036
|
+
var PROVIDER_BUILTIN_TOOLS = {
|
|
22037
|
+
anthropic: ["web_search", "code_interpreter"],
|
|
22038
|
+
openai: ["web_search", "code_interpreter"],
|
|
22039
|
+
google: ["web_search", "code_interpreter"],
|
|
22040
|
+
xai: ["web_search", "code_interpreter"],
|
|
22041
|
+
openrouter: ["web_search"]
|
|
22042
|
+
};
|
|
22043
|
+
|
|
22035
22044
|
// src/plugins/model-catalog/catalog.ts
|
|
22036
22045
|
var PROVIDER_DEFAULT_CATALOGS = [
|
|
22037
22046
|
catalog_default,
|
|
@@ -22069,6 +22078,13 @@ var DEFAULT_REASONING = {
|
|
|
22069
22078
|
encryptedContent: false,
|
|
22070
22079
|
summaryAvailable: false
|
|
22071
22080
|
};
|
|
22081
|
+
function withBuiltinTools(provider, caps) {
|
|
22082
|
+
if (caps.builtinTools !== void 0) return caps;
|
|
22083
|
+
if (!caps.toolUse) return caps;
|
|
22084
|
+
const tools = PROVIDER_BUILTIN_TOOLS[provider];
|
|
22085
|
+
if (!tools || tools.length === 0) return caps;
|
|
22086
|
+
return { ...caps, builtinTools: [...tools] };
|
|
22087
|
+
}
|
|
22072
22088
|
var ModelCatalog = class {
|
|
22073
22089
|
models = /* @__PURE__ */ new Map();
|
|
22074
22090
|
/** `provider/alias` → `provider/canonical-slug`. Lets get()/resolveModelId
|
|
@@ -22087,7 +22103,7 @@ var ModelCatalog = class {
|
|
|
22087
22103
|
supportedApis: info.supportedApis ?? [info.preferredApi ?? "completions"],
|
|
22088
22104
|
contextWindow: info.contextWindow,
|
|
22089
22105
|
maxOutput: info.maxOutput,
|
|
22090
|
-
capabilities: { ...DEFAULT_CAPABILITIES, ...info.capabilities },
|
|
22106
|
+
capabilities: withBuiltinTools(provider, { ...DEFAULT_CAPABILITIES, ...info.capabilities }),
|
|
22091
22107
|
reasoning: { ...DEFAULT_REASONING, ...info.reasoning },
|
|
22092
22108
|
mediaOnly: info.mediaOnly,
|
|
22093
22109
|
tokenizer: info.tokenizer,
|
|
@@ -22141,6 +22157,15 @@ var ModelCatalog = class {
|
|
|
22141
22157
|
supportsTools(provider, model) {
|
|
22142
22158
|
return this.get(provider, model)?.capabilities.toolUse ?? false;
|
|
22143
22159
|
}
|
|
22160
|
+
/** Hosted server-side builtin tools this model supports (e.g. `['web_search',
|
|
22161
|
+
* 'code_interpreter']`). Empty when unknown or the model isn't tool-capable. */
|
|
22162
|
+
builtinToolsFor(provider, model) {
|
|
22163
|
+
return [...this.get(provider, model)?.capabilities.builtinTools ?? []];
|
|
22164
|
+
}
|
|
22165
|
+
/** Whether this model supports a specific hosted builtin tool. */
|
|
22166
|
+
supportsBuiltinTool(provider, model, tool) {
|
|
22167
|
+
return this.get(provider, model)?.capabilities.builtinTools?.includes(tool) ?? false;
|
|
22168
|
+
}
|
|
22144
22169
|
supportsPreviousResponseId(provider, model) {
|
|
22145
22170
|
const info = this.get(provider, model);
|
|
22146
22171
|
if (info && !info.supportedApis.some((a) => a === "responses" || a === "interactions")) {
|
|
@@ -22354,6 +22379,160 @@ async function* wrapParallel(raw, interval, moderate2) {
|
|
|
22354
22379
|
}
|
|
22355
22380
|
}
|
|
22356
22381
|
|
|
22382
|
+
// src/llm/files/retrieve.ts
|
|
22383
|
+
var DEFAULT_BASE = {
|
|
22384
|
+
anthropic: "https://api.anthropic.com",
|
|
22385
|
+
openai: "https://api.openai.com",
|
|
22386
|
+
xai: "https://api.x.ai",
|
|
22387
|
+
google: "https://generativelanguage.googleapis.com",
|
|
22388
|
+
openrouter: "https://openrouter.ai/api"
|
|
22389
|
+
};
|
|
22390
|
+
var OCTET_STREAM = "application/octet-stream";
|
|
22391
|
+
function contentRequest(ctx, file) {
|
|
22392
|
+
const base = ctx.baseURL ?? DEFAULT_BASE[ctx.provider];
|
|
22393
|
+
const id = file.id;
|
|
22394
|
+
if (ctx.provider === "anthropic") {
|
|
22395
|
+
return {
|
|
22396
|
+
url: `${base}/v1/files/${id}/content?beta=true`,
|
|
22397
|
+
headers: {
|
|
22398
|
+
"x-api-key": ctx.apiKey,
|
|
22399
|
+
"anthropic-version": "2023-06-01",
|
|
22400
|
+
"anthropic-beta": "files-api-2025-04-14",
|
|
22401
|
+
accept: "application/binary"
|
|
22402
|
+
}
|
|
22403
|
+
};
|
|
22404
|
+
}
|
|
22405
|
+
if (ctx.provider === "openai" || ctx.provider === "xai" || ctx.provider === "openrouter") {
|
|
22406
|
+
const containerId = file.ref?.containerId;
|
|
22407
|
+
const path = containerId ? `/v1/containers/${containerId}/files/${id}/content` : `/v1/files/${id}/content`;
|
|
22408
|
+
return { url: `${base}${path}`, headers: { authorization: `Bearer ${ctx.apiKey}` } };
|
|
22409
|
+
}
|
|
22410
|
+
if (ctx.provider === "google") {
|
|
22411
|
+
return {
|
|
22412
|
+
url: `${base}/v1beta/files/${id}:download?alt=media`,
|
|
22413
|
+
headers: { "x-goog-api-key": ctx.apiKey }
|
|
22414
|
+
};
|
|
22415
|
+
}
|
|
22416
|
+
throw new Error(`retrieveFile: no file-content endpoint for provider "${ctx.provider}"`);
|
|
22417
|
+
}
|
|
22418
|
+
function providerAuth(ctx, url) {
|
|
22419
|
+
const base = ctx.baseURL ?? DEFAULT_BASE[ctx.provider];
|
|
22420
|
+
if (!url.startsWith(base)) return {};
|
|
22421
|
+
if (ctx.provider === "anthropic") {
|
|
22422
|
+
return { "x-api-key": ctx.apiKey, "anthropic-version": "2023-06-01" };
|
|
22423
|
+
}
|
|
22424
|
+
if (ctx.provider === "google") return { "x-goog-api-key": ctx.apiKey };
|
|
22425
|
+
return { authorization: `Bearer ${ctx.apiKey}` };
|
|
22426
|
+
}
|
|
22427
|
+
async function fetchFileResponse(ctx, file, responseType) {
|
|
22428
|
+
if (file.url) {
|
|
22429
|
+
return ctx.fetch({
|
|
22430
|
+
url: file.url,
|
|
22431
|
+
method: "GET",
|
|
22432
|
+
headers: providerAuth(ctx, file.url),
|
|
22433
|
+
body: void 0,
|
|
22434
|
+
provider: ctx.provider,
|
|
22435
|
+
model: "files",
|
|
22436
|
+
responseType
|
|
22437
|
+
});
|
|
22438
|
+
}
|
|
22439
|
+
if (file.id) {
|
|
22440
|
+
const { url, headers } = contentRequest(ctx, file);
|
|
22441
|
+
return ctx.fetch({
|
|
22442
|
+
url,
|
|
22443
|
+
method: "GET",
|
|
22444
|
+
headers,
|
|
22445
|
+
body: void 0,
|
|
22446
|
+
provider: ctx.provider,
|
|
22447
|
+
model: "files",
|
|
22448
|
+
responseType
|
|
22449
|
+
});
|
|
22450
|
+
}
|
|
22451
|
+
throw new Error("retrieveFile: FileOutput has neither `data`, `url`, nor `id`");
|
|
22452
|
+
}
|
|
22453
|
+
function bytesToBlob(bytes, type) {
|
|
22454
|
+
const ab = new ArrayBuffer(bytes.byteLength);
|
|
22455
|
+
new Uint8Array(ab).set(bytes);
|
|
22456
|
+
return new Blob([ab], { type });
|
|
22457
|
+
}
|
|
22458
|
+
function singleChunkStream(bytes) {
|
|
22459
|
+
return new ReadableStream({
|
|
22460
|
+
start(controller) {
|
|
22461
|
+
controller.enqueue(bytes);
|
|
22462
|
+
controller.close();
|
|
22463
|
+
}
|
|
22464
|
+
});
|
|
22465
|
+
}
|
|
22466
|
+
var EXT_MIME = {
|
|
22467
|
+
png: "image/png",
|
|
22468
|
+
jpg: "image/jpeg",
|
|
22469
|
+
jpeg: "image/jpeg",
|
|
22470
|
+
gif: "image/gif",
|
|
22471
|
+
webp: "image/webp",
|
|
22472
|
+
svg: "image/svg+xml",
|
|
22473
|
+
bmp: "image/bmp",
|
|
22474
|
+
csv: "text/csv",
|
|
22475
|
+
tsv: "text/tab-separated-values",
|
|
22476
|
+
txt: "text/plain",
|
|
22477
|
+
json: "application/json",
|
|
22478
|
+
xml: "application/xml",
|
|
22479
|
+
html: "text/html",
|
|
22480
|
+
md: "text/markdown",
|
|
22481
|
+
pdf: "application/pdf",
|
|
22482
|
+
zip: "application/zip",
|
|
22483
|
+
parquet: "application/vnd.apache.parquet",
|
|
22484
|
+
xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
|
|
22485
|
+
};
|
|
22486
|
+
function mimeFromName(name) {
|
|
22487
|
+
const ext = name?.includes(".") ? name.split(".").pop()?.toLowerCase() : void 0;
|
|
22488
|
+
return ext ? EXT_MIME[ext] : void 0;
|
|
22489
|
+
}
|
|
22490
|
+
function header(headers, name) {
|
|
22491
|
+
const lower = name.toLowerCase();
|
|
22492
|
+
for (const k in headers) if (k.toLowerCase() === lower) return headers[k];
|
|
22493
|
+
return void 0;
|
|
22494
|
+
}
|
|
22495
|
+
function filenameFromDisposition(cd) {
|
|
22496
|
+
if (!cd) return void 0;
|
|
22497
|
+
const ext = cd.match(/filename\*=[^']*''([^;]+)/i);
|
|
22498
|
+
if (ext) {
|
|
22499
|
+
try {
|
|
22500
|
+
return decodeURIComponent(ext[1].trim());
|
|
22501
|
+
} catch {
|
|
22502
|
+
}
|
|
22503
|
+
}
|
|
22504
|
+
const plain = cd.match(/filename="?([^";]+)"?/i);
|
|
22505
|
+
return plain ? plain[1].trim() : void 0;
|
|
22506
|
+
}
|
|
22507
|
+
async function retrieveFile(file, ctx) {
|
|
22508
|
+
if (file.data) {
|
|
22509
|
+
const blob2 = bytesToBlob(base64ToBytes(file.data), file.mimeType ?? OCTET_STREAM);
|
|
22510
|
+
return { blob: blob2, name: file.name, mimeType: blob2.type, size: blob2.size };
|
|
22511
|
+
}
|
|
22512
|
+
const res = await fetchFileResponse(ctx, file, "arraybuffer");
|
|
22513
|
+
const headers = res.headers ?? {};
|
|
22514
|
+
const name = filenameFromDisposition(header(headers, "content-disposition")) ?? file.name;
|
|
22515
|
+
const mimeType = header(headers, "content-type")?.split(";")[0].trim() || file.mimeType || mimeFromName(name) || OCTET_STREAM;
|
|
22516
|
+
const blob = new Blob([res.body], { type: mimeType });
|
|
22517
|
+
return { blob, name, mimeType, size: blob.size };
|
|
22518
|
+
}
|
|
22519
|
+
async function streamFile(file, ctx) {
|
|
22520
|
+
if (file.data) {
|
|
22521
|
+
const bytes = base64ToBytes(file.data);
|
|
22522
|
+
return { stream: singleChunkStream(bytes), name: file.name, mimeType: file.mimeType, size: bytes.byteLength };
|
|
22523
|
+
}
|
|
22524
|
+
const res = await fetchFileResponse(ctx, file, "stream");
|
|
22525
|
+
const headers = res.headers ?? {};
|
|
22526
|
+
const len = header(headers, "content-length");
|
|
22527
|
+
const name = filenameFromDisposition(header(headers, "content-disposition")) ?? file.name;
|
|
22528
|
+
return {
|
|
22529
|
+
stream: res.body,
|
|
22530
|
+
name,
|
|
22531
|
+
mimeType: header(headers, "content-type")?.split(";")[0].trim() || file.mimeType || mimeFromName(name),
|
|
22532
|
+
size: len ? Number(len) : void 0
|
|
22533
|
+
};
|
|
22534
|
+
}
|
|
22535
|
+
|
|
22357
22536
|
// src/util/duration.ts
|
|
22358
22537
|
var UNIT_MS = {
|
|
22359
22538
|
s: 1e3,
|
|
@@ -22596,6 +22775,28 @@ var LLMClient = class {
|
|
|
22596
22775
|
else next.output = result;
|
|
22597
22776
|
return next;
|
|
22598
22777
|
}
|
|
22778
|
+
// ─── File retrieval (hosted-tool output files) ─────────────────────────
|
|
22779
|
+
retrieveContext() {
|
|
22780
|
+
return {
|
|
22781
|
+
provider: this.provider,
|
|
22782
|
+
apiKey: this.apiKey,
|
|
22783
|
+
fetch: this.fetchFn,
|
|
22784
|
+
baseURL: this.adapter.baseURL()
|
|
22785
|
+
};
|
|
22786
|
+
}
|
|
22787
|
+
/** Fetch a hosted-tool output file (e.g. a code-execution file from
|
|
22788
|
+
* `response.files`): its bytes as a `Blob` plus `name` / `mimeType` / `size`.
|
|
22789
|
+
* Resolves inline `data`, a `url`, or a provider file `id` — all through this
|
|
22790
|
+
* client's provider + auth + engine. */
|
|
22791
|
+
retrieveFile(file) {
|
|
22792
|
+
return retrieveFile(file, this.retrieveContext());
|
|
22793
|
+
}
|
|
22794
|
+
/** Stream a hosted-tool output file: a `ReadableStream<Uint8Array>` plus
|
|
22795
|
+
* best-effort `name` / `mimeType` / `size` (from the response headers). For large
|
|
22796
|
+
* files piped straight to a file / GridFS / HTTP response without buffering. */
|
|
22797
|
+
streamFile(file) {
|
|
22798
|
+
return streamFile(file, this.retrieveContext());
|
|
22799
|
+
}
|
|
22599
22800
|
/** Submit a request. Returns the parsed CompletionResponse. */
|
|
22600
22801
|
async complete(input, options = {}) {
|
|
22601
22802
|
const rawMessages = normalizeInput(input);
|
|
@@ -22807,17 +23008,18 @@ var LLMClient = class {
|
|
|
22807
23008
|
let usage = emptyUsage();
|
|
22808
23009
|
let finishReason = "stop";
|
|
22809
23010
|
let moderationReport;
|
|
23011
|
+
const files = [];
|
|
22810
23012
|
const fetchStream = this.fetchStreamFn;
|
|
22811
|
-
const adapter = this.adapter;
|
|
22812
23013
|
const queueName = this.queueName;
|
|
22813
23014
|
const priority = this.priority;
|
|
23015
|
+
const parseStream = this.adapter.createStreamParser();
|
|
22814
23016
|
async function* rawEvents() {
|
|
22815
23017
|
for await (const sseEvent of fetchStream(httpReq, {
|
|
22816
23018
|
queueName,
|
|
22817
23019
|
priority,
|
|
22818
23020
|
ctx
|
|
22819
23021
|
})) {
|
|
22820
|
-
for (const ev of
|
|
23022
|
+
for (const ev of parseStream(sseEvent)) yield ev;
|
|
22821
23023
|
}
|
|
22822
23024
|
}
|
|
22823
23025
|
const mod = options.moderation;
|
|
@@ -22855,6 +23057,9 @@ var LLMClient = class {
|
|
|
22855
23057
|
case "done":
|
|
22856
23058
|
finishReason = event.finishReason;
|
|
22857
23059
|
break;
|
|
23060
|
+
case "file":
|
|
23061
|
+
files.push(event.file);
|
|
23062
|
+
break;
|
|
22858
23063
|
case "moderation":
|
|
22859
23064
|
moderationReport = this.mergeModeration(
|
|
22860
23065
|
moderationReport,
|
|
@@ -22877,6 +23082,7 @@ var LLMClient = class {
|
|
|
22877
23082
|
toolCalls: [],
|
|
22878
23083
|
thinking: thinking || null,
|
|
22879
23084
|
media: [],
|
|
23085
|
+
...files.length ? { files } : {},
|
|
22880
23086
|
...moderationReport ? { moderation: moderationReport } : {},
|
|
22881
23087
|
latencyMs: performance.now() - start,
|
|
22882
23088
|
raw: null
|
|
@@ -23137,6 +23343,22 @@ function anthropicRequestTier(t) {
|
|
|
23137
23343
|
function anthropicBilledTier(raw) {
|
|
23138
23344
|
return typeof raw === "string" && raw ? { serviceTier: raw, pricingTier: raw } : {};
|
|
23139
23345
|
}
|
|
23346
|
+
function filesFromCodeExecBlock(block) {
|
|
23347
|
+
if (block.type !== "bash_code_execution_tool_result" && block.type !== "code_execution_tool_result") {
|
|
23348
|
+
return [];
|
|
23349
|
+
}
|
|
23350
|
+
const result = block.content;
|
|
23351
|
+
if (!result || result.type !== "bash_code_execution_result" && result.type !== "code_execution_result" || !Array.isArray(result.content)) {
|
|
23352
|
+
return [];
|
|
23353
|
+
}
|
|
23354
|
+
const files = [];
|
|
23355
|
+
for (const out of result.content) {
|
|
23356
|
+
if ((out.type === "bash_code_execution_output" || out.type === "code_execution_output") && typeof out.file_id === "string") {
|
|
23357
|
+
files.push({ id: out.file_id, source: "code_execution" });
|
|
23358
|
+
}
|
|
23359
|
+
}
|
|
23360
|
+
return files;
|
|
23361
|
+
}
|
|
23140
23362
|
var AnthropicAdapter = class {
|
|
23141
23363
|
name = "anthropic";
|
|
23142
23364
|
apiKey;
|
|
@@ -23317,15 +23539,8 @@ var AnthropicAdapter = class {
|
|
|
23317
23539
|
};
|
|
23318
23540
|
content.push(tc);
|
|
23319
23541
|
toolCalls.push(tc);
|
|
23320
|
-
} else
|
|
23321
|
-
|
|
23322
|
-
if (result && (result.type === "bash_code_execution_result" || result.type === "code_execution_result") && Array.isArray(result.content)) {
|
|
23323
|
-
for (const out of result.content) {
|
|
23324
|
-
if ((out.type === "bash_code_execution_output" || out.type === "code_execution_output") && typeof out.file_id === "string") {
|
|
23325
|
-
files.push({ id: out.file_id, source: "code_execution" });
|
|
23326
|
-
}
|
|
23327
|
-
}
|
|
23328
|
-
}
|
|
23542
|
+
} else {
|
|
23543
|
+
files.push(...filesFromCodeExecBlock(block));
|
|
23329
23544
|
}
|
|
23330
23545
|
}
|
|
23331
23546
|
const finishReason = extractFinishReason(toolCalls.length > 0, r.stop_reason, {
|
|
@@ -23364,6 +23579,8 @@ var AnthropicAdapter = class {
|
|
|
23364
23579
|
if (block.type === "tool_use") {
|
|
23365
23580
|
return [{ type: "tool_call_start", id: block.id, name: block.name }];
|
|
23366
23581
|
}
|
|
23582
|
+
const files = filesFromCodeExecBlock(block);
|
|
23583
|
+
if (files.length) return files.map((file) => ({ type: "file", file }));
|
|
23367
23584
|
}
|
|
23368
23585
|
if (type === "content_block_stop") {
|
|
23369
23586
|
}
|
|
@@ -23387,6 +23604,11 @@ var AnthropicAdapter = class {
|
|
|
23387
23604
|
}
|
|
23388
23605
|
return [];
|
|
23389
23606
|
}
|
|
23607
|
+
/** Stateless — every event is self-contained (code-execution result blocks
|
|
23608
|
+
* arrive complete in a single content_block_start). */
|
|
23609
|
+
createStreamParser() {
|
|
23610
|
+
return (event) => this.parseStreamEvent(event);
|
|
23611
|
+
}
|
|
23390
23612
|
parseUsage(u) {
|
|
23391
23613
|
if (!u) return emptyUsage();
|
|
23392
23614
|
const inputTokens = u.input_tokens ?? 0;
|
|
@@ -23911,6 +24133,18 @@ var GoogleAdapter = class {
|
|
|
23911
24133
|
};
|
|
23912
24134
|
}
|
|
23913
24135
|
parseStreamEvent(event) {
|
|
24136
|
+
return this.streamEvents(event, { codeExec: false });
|
|
24137
|
+
}
|
|
24138
|
+
/** Stateful — Google splits the code-execution marker (`executableCode` /
|
|
24139
|
+
* `codeExecutionResult`) and the produced file (`inlineData`) across parts and
|
|
24140
|
+
* often across SSE events. The closure remembers "code execution began in this
|
|
24141
|
+
* stream" so a later `inlineData` blob is routed to `files` (a code-exec
|
|
24142
|
+
* artifact) rather than `media` (conversational output). */
|
|
24143
|
+
createStreamParser() {
|
|
24144
|
+
const state = { codeExec: false };
|
|
24145
|
+
return (event) => this.streamEvents(event, state);
|
|
24146
|
+
}
|
|
24147
|
+
streamEvents(event, state) {
|
|
23914
24148
|
const data = JSON.parse(event.data);
|
|
23915
24149
|
const candidates = data.candidates ?? [];
|
|
23916
24150
|
const candidate = candidates[0];
|
|
@@ -23924,6 +24158,9 @@ var GoogleAdapter = class {
|
|
|
23924
24158
|
const rawContent = candidate.content ?? {};
|
|
23925
24159
|
const parts = rawContent.parts ?? [];
|
|
23926
24160
|
const events = [];
|
|
24161
|
+
for (const part of parts) {
|
|
24162
|
+
if (part.executableCode || part.codeExecutionResult) state.codeExec = true;
|
|
24163
|
+
}
|
|
23927
24164
|
for (const part of parts) {
|
|
23928
24165
|
if (part.text !== void 0 && !part.thought)
|
|
23929
24166
|
events.push({ type: "text", text: part.text });
|
|
@@ -23931,10 +24168,17 @@ var GoogleAdapter = class {
|
|
|
23931
24168
|
if (part.inlineData) {
|
|
23932
24169
|
const inline = part.inlineData;
|
|
23933
24170
|
const mime = inline.mimeType;
|
|
23934
|
-
|
|
23935
|
-
|
|
23936
|
-
|
|
23937
|
-
|
|
24171
|
+
if (state.codeExec) {
|
|
24172
|
+
events.push({
|
|
24173
|
+
type: "file",
|
|
24174
|
+
file: { data: inline.data, mimeType: mime, source: "code_execution" }
|
|
24175
|
+
});
|
|
24176
|
+
} else {
|
|
24177
|
+
const mediaType = mime.startsWith("image/") ? "image" : mime.startsWith("audio/") ? "audio" : "video";
|
|
24178
|
+
events.push({ type: "media_start", mediaType, mimeType: mime });
|
|
24179
|
+
events.push({ type: "media_chunk", data: inline.data });
|
|
24180
|
+
events.push({ type: "media_end" });
|
|
24181
|
+
}
|
|
23938
24182
|
}
|
|
23939
24183
|
if (part.functionCall) {
|
|
23940
24184
|
const fc = part.functionCall;
|
|
@@ -24229,6 +24473,10 @@ var GoogleInteractionsAdapter = class {
|
|
|
24229
24473
|
}
|
|
24230
24474
|
return events;
|
|
24231
24475
|
}
|
|
24476
|
+
/** Stateless — the Interactions adapter surfaces no code-execution file outputs. */
|
|
24477
|
+
createStreamParser() {
|
|
24478
|
+
return (event) => this.parseStreamEvent(event);
|
|
24479
|
+
}
|
|
24232
24480
|
parseUsage(u) {
|
|
24233
24481
|
if (!u) return emptyUsage();
|
|
24234
24482
|
const input = u.total_input_tokens ?? u.prompt_tokens ?? 0;
|
|
@@ -25194,6 +25442,10 @@ var OpenAIAdapter = class {
|
|
|
25194
25442
|
}
|
|
25195
25443
|
return events;
|
|
25196
25444
|
}
|
|
25445
|
+
/** Stateless — Chat Completions has no hosted code-execution file outputs. */
|
|
25446
|
+
createStreamParser() {
|
|
25447
|
+
return (event) => this.parseStreamEvent(event);
|
|
25448
|
+
}
|
|
25197
25449
|
parseUsage(u) {
|
|
25198
25450
|
if (!u) return emptyUsage();
|
|
25199
25451
|
const input = u.prompt_tokens ?? u.input_tokens ?? 0;
|
|
@@ -25709,6 +25961,33 @@ function filenameForMime(mimeType) {
|
|
|
25709
25961
|
if (mimeType.startsWith("image/")) return `file.${mimeType.slice("image/".length)}`;
|
|
25710
25962
|
return "file.bin";
|
|
25711
25963
|
}
|
|
25964
|
+
function filesFromResponsesOutputItem(item) {
|
|
25965
|
+
const files = [];
|
|
25966
|
+
const type = item.type;
|
|
25967
|
+
if (type === "message") {
|
|
25968
|
+
for (const c of item.content ?? []) {
|
|
25969
|
+
if (c.type !== "output_text") continue;
|
|
25970
|
+
for (const a of c.annotations ?? []) {
|
|
25971
|
+
if (a.type === "container_file_citation" && typeof a.file_id === "string") {
|
|
25972
|
+
files.push({
|
|
25973
|
+
id: a.file_id,
|
|
25974
|
+
...typeof a.filename === "string" ? { name: a.filename } : {},
|
|
25975
|
+
...typeof a.container_id === "string" ? { ref: { containerId: a.container_id } } : {},
|
|
25976
|
+
source: "code_execution"
|
|
25977
|
+
});
|
|
25978
|
+
}
|
|
25979
|
+
}
|
|
25980
|
+
}
|
|
25981
|
+
}
|
|
25982
|
+
if (type === "code_interpreter_call") {
|
|
25983
|
+
for (const out of item.outputs ?? []) {
|
|
25984
|
+
if (out.type === "image" && typeof out.url === "string") {
|
|
25985
|
+
files.push({ url: out.url, source: "code_execution" });
|
|
25986
|
+
}
|
|
25987
|
+
}
|
|
25988
|
+
}
|
|
25989
|
+
return files;
|
|
25990
|
+
}
|
|
25712
25991
|
var OpenAIResponsesAdapter = class {
|
|
25713
25992
|
name = "openai";
|
|
25714
25993
|
apiKey;
|
|
@@ -25889,6 +26168,7 @@ var OpenAIResponsesAdapter = class {
|
|
|
25889
26168
|
let text = "";
|
|
25890
26169
|
for (const item of output) {
|
|
25891
26170
|
const type = item.type;
|
|
26171
|
+
files.push(...this.filesFromOutputItem(item));
|
|
25892
26172
|
if (type === "message") {
|
|
25893
26173
|
const itemContent = item.content ?? [];
|
|
25894
26174
|
for (const c of itemContent) {
|
|
@@ -25896,22 +26176,6 @@ var OpenAIResponsesAdapter = class {
|
|
|
25896
26176
|
const t = c.text;
|
|
25897
26177
|
text += t;
|
|
25898
26178
|
content.push({ type: "text", text: t });
|
|
25899
|
-
for (const a of c.annotations ?? []) {
|
|
25900
|
-
if (a.type === "container_file_citation" && typeof a.file_id === "string") {
|
|
25901
|
-
files.push({
|
|
25902
|
-
id: a.file_id,
|
|
25903
|
-
...typeof a.filename === "string" ? { name: a.filename } : {},
|
|
25904
|
-
source: "code_execution"
|
|
25905
|
-
});
|
|
25906
|
-
}
|
|
25907
|
-
}
|
|
25908
|
-
}
|
|
25909
|
-
}
|
|
25910
|
-
}
|
|
25911
|
-
if (type === "code_interpreter_call") {
|
|
25912
|
-
for (const out of item.outputs ?? []) {
|
|
25913
|
-
if (out.type === "image" && typeof out.url === "string") {
|
|
25914
|
-
files.push({ url: out.url, source: "code_execution" });
|
|
25915
26179
|
}
|
|
25916
26180
|
}
|
|
25917
26181
|
}
|
|
@@ -26006,6 +26270,7 @@ var OpenAIResponsesAdapter = class {
|
|
|
26006
26270
|
}
|
|
26007
26271
|
if (type === "response.output_item.done") {
|
|
26008
26272
|
const item = data.item;
|
|
26273
|
+
for (const file of this.filesFromOutputItem(item)) events.push({ type: "file", file });
|
|
26009
26274
|
if (item?.type === "function_call") {
|
|
26010
26275
|
events.push({ type: "tool_call_end", id: item.call_id ?? "" });
|
|
26011
26276
|
}
|
|
@@ -26036,6 +26301,17 @@ var OpenAIResponsesAdapter = class {
|
|
|
26036
26301
|
}
|
|
26037
26302
|
return events;
|
|
26038
26303
|
}
|
|
26304
|
+
/** Stateless — each output item finalizes with all its file annotations in a
|
|
26305
|
+
* single response.output_item.done event. */
|
|
26306
|
+
createStreamParser() {
|
|
26307
|
+
return (event) => this.parseStreamEvent(event);
|
|
26308
|
+
}
|
|
26309
|
+
/** Hosted code-execution output files from one output item. Overridable so
|
|
26310
|
+
* Responses-compatible providers with a different file shape (e.g. xAI, which
|
|
26311
|
+
* embeds files in the code-interpreter `logs` payload) can extend it. */
|
|
26312
|
+
filesFromOutputItem(item) {
|
|
26313
|
+
return filesFromResponsesOutputItem(item);
|
|
26314
|
+
}
|
|
26039
26315
|
parseUsage(u) {
|
|
26040
26316
|
if (!u) return emptyUsage();
|
|
26041
26317
|
const input = u.input_tokens ?? 0;
|
|
@@ -26600,6 +26876,29 @@ var XAIMediaAdapter = class {
|
|
|
26600
26876
|
};
|
|
26601
26877
|
|
|
26602
26878
|
// src/llm/providers/xai/responses.ts
|
|
26879
|
+
function xaiCodeExecFiles(item) {
|
|
26880
|
+
if (item.type !== "code_interpreter_call") return [];
|
|
26881
|
+
const files = [];
|
|
26882
|
+
for (const out of item.outputs ?? []) {
|
|
26883
|
+
if (out.type !== "logs" || typeof out.logs !== "string") continue;
|
|
26884
|
+
let parsed;
|
|
26885
|
+
try {
|
|
26886
|
+
parsed = JSON.parse(out.logs);
|
|
26887
|
+
} catch {
|
|
26888
|
+
continue;
|
|
26889
|
+
}
|
|
26890
|
+
for (const f of parsed.output_files ?? []) {
|
|
26891
|
+
if (!Array.isArray(f.data)) continue;
|
|
26892
|
+
files.push({
|
|
26893
|
+
data: bytesToBase64(Uint8Array.from(f.data)),
|
|
26894
|
+
...typeof f.file_name === "string" ? { name: f.file_name } : {},
|
|
26895
|
+
...typeof f.mime_type === "string" ? { mimeType: f.mime_type } : {},
|
|
26896
|
+
source: "code_execution"
|
|
26897
|
+
});
|
|
26898
|
+
}
|
|
26899
|
+
}
|
|
26900
|
+
return files;
|
|
26901
|
+
}
|
|
26603
26902
|
var XAIResponsesAdapter = class extends OpenAIResponsesAdapter {
|
|
26604
26903
|
name = "xai";
|
|
26605
26904
|
constructor(config) {
|
|
@@ -26619,8 +26918,20 @@ var XAIResponsesAdapter = class extends OpenAIResponsesAdapter {
|
|
|
26619
26918
|
if (!req.model.includes("multi-agent")) {
|
|
26620
26919
|
delete body.reasoning;
|
|
26621
26920
|
}
|
|
26921
|
+
const usesCodeInterpreter = req.tools?.some(
|
|
26922
|
+
(t) => !isFunctionTool(t) && t.type === "code_interpreter"
|
|
26923
|
+
);
|
|
26924
|
+
if (usesCodeInterpreter) {
|
|
26925
|
+
const include = /* @__PURE__ */ new Set([...body.include ?? [], "code_interpreter_call.outputs"]);
|
|
26926
|
+
body.include = [...include];
|
|
26927
|
+
}
|
|
26622
26928
|
return result;
|
|
26623
26929
|
}
|
|
26930
|
+
/** xAI embeds code-execution files inline in the `logs` payload — extend the base
|
|
26931
|
+
* extraction (which handles OpenAI-style annotations / image URLs) with the xAI shape. */
|
|
26932
|
+
filesFromOutputItem(item) {
|
|
26933
|
+
return [...super.filesFromOutputItem(item), ...xaiCodeExecFiles(item)];
|
|
26934
|
+
}
|
|
26624
26935
|
};
|
|
26625
26936
|
|
|
26626
26937
|
// src/util/json-schema.ts
|
|
@@ -27757,6 +28068,19 @@ var AgentLoop = class _AgentLoop {
|
|
|
27757
28068
|
get model() {
|
|
27758
28069
|
return this.client.model;
|
|
27759
28070
|
}
|
|
28071
|
+
// ─── File retrieval (hosted-tool output files) ──────────────────────────
|
|
28072
|
+
/** Fetch a hosted-tool output file from the run's `response.files` — its bytes
|
|
28073
|
+
* as a `Blob` plus `name` / `mimeType` / `size`. Delegates to the underlying
|
|
28074
|
+
* client, so it uses this agent's provider + key (same as `retrieveFile` on a
|
|
28075
|
+
* `complete()` result). */
|
|
28076
|
+
retrieveFile(file) {
|
|
28077
|
+
return this.client.retrieveFile(file);
|
|
28078
|
+
}
|
|
28079
|
+
/** Stream a hosted-tool output file (large files) — a `ReadableStream` plus
|
|
28080
|
+
* best-effort `name` / `mimeType` / `size`. Delegates to the underlying client. */
|
|
28081
|
+
streamFile(file) {
|
|
28082
|
+
return this.client.streamFile(file);
|
|
28083
|
+
}
|
|
27760
28084
|
get system() {
|
|
27761
28085
|
return this._system;
|
|
27762
28086
|
}
|
|
@@ -34391,7 +34715,13 @@ async function complete(opts) {
|
|
|
34391
34715
|
serviceTier
|
|
34392
34716
|
});
|
|
34393
34717
|
}
|
|
34394
|
-
const result = {
|
|
34718
|
+
const result = {
|
|
34719
|
+
text: res.text,
|
|
34720
|
+
response: res,
|
|
34721
|
+
// Bound to this call's client (same provider/model/key/engine).
|
|
34722
|
+
retrieveFile: (file) => llm.retrieveFile(file),
|
|
34723
|
+
streamFile: (file) => llm.streamFile(file)
|
|
34724
|
+
};
|
|
34395
34725
|
if (opts.structured?.schema) {
|
|
34396
34726
|
result.parsed = parseStructured(res.text);
|
|
34397
34727
|
}
|
|
@@ -36526,12 +36856,16 @@ var DEFAULT_TAGS = {
|
|
|
36526
36856
|
huge: "context:large"
|
|
36527
36857
|
};
|
|
36528
36858
|
var CAP_KEYS = {
|
|
36529
|
-
search: "webSearch",
|
|
36530
36859
|
vision: "vision",
|
|
36531
36860
|
tools: "toolUse",
|
|
36532
36861
|
audio: "audio",
|
|
36533
36862
|
structured: "structuredOutput"
|
|
36534
36863
|
};
|
|
36864
|
+
var BUILTIN_TOOL_KEYS = {
|
|
36865
|
+
search: "web_search",
|
|
36866
|
+
web_search: "web_search",
|
|
36867
|
+
code_interpreter: "code_interpreter"
|
|
36868
|
+
};
|
|
36535
36869
|
var KNOWN_KEYS = /* @__PURE__ */ new Set([
|
|
36536
36870
|
"price",
|
|
36537
36871
|
"context",
|
|
@@ -36541,7 +36875,8 @@ var KNOWN_KEYS = /* @__PURE__ */ new Set([
|
|
|
36541
36875
|
"status",
|
|
36542
36876
|
"provider",
|
|
36543
36877
|
"active",
|
|
36544
|
-
...Object.keys(CAP_KEYS)
|
|
36878
|
+
...Object.keys(CAP_KEYS),
|
|
36879
|
+
...Object.keys(BUILTIN_TOOL_KEYS)
|
|
36545
36880
|
]);
|
|
36546
36881
|
function parseNum(v) {
|
|
36547
36882
|
const m = /^([\d.]+)\s*([kKmM]?)$/.exec(v.trim());
|
|
@@ -36610,6 +36945,13 @@ function matches(m, c, th, tier) {
|
|
|
36610
36945
|
case "active":
|
|
36611
36946
|
return isNo(c.value) ? m.active === false : m.active !== false;
|
|
36612
36947
|
default: {
|
|
36948
|
+
const tool = BUILTIN_TOOL_KEYS[c.key];
|
|
36949
|
+
if (tool) {
|
|
36950
|
+
const inList = m.capabilities.builtinTools?.includes(tool) ?? false;
|
|
36951
|
+
const legacy = tool === "web_search" && !!m.capabilities.webSearch;
|
|
36952
|
+
const has2 = inList || legacy;
|
|
36953
|
+
return isNo(c.value) ? !has2 : has2;
|
|
36954
|
+
}
|
|
36613
36955
|
const cap = CAP_KEYS[c.key];
|
|
36614
36956
|
const has = !!m.capabilities[cap];
|
|
36615
36957
|
return isNo(c.value) ? !has : has;
|
package/dist/llm/client.d.ts
CHANGED
|
@@ -18,10 +18,11 @@
|
|
|
18
18
|
* Hooks emitted: onClientCreate (in ctor), onMessageResolve, onBeforeSubmit,
|
|
19
19
|
* onCompletion, onClientDestroy. */
|
|
20
20
|
import type { HookBus } from '../bus/hook-bus';
|
|
21
|
+
import type { FileStream, RetrievedFile } from './files/retrieve';
|
|
21
22
|
import type { ContentPart, Message } from './types/messages';
|
|
22
23
|
import type { ExecuteOptions } from './types/options';
|
|
23
24
|
import type { ApiType, ProviderName } from './types/provider';
|
|
24
|
-
import type { CompletionResponse } from './types/response';
|
|
25
|
+
import type { CompletionResponse, FileOutput } from './types/response';
|
|
25
26
|
import type { StreamEvent } from './types/stream';
|
|
26
27
|
import type { LLMClientConfig } from './client-config';
|
|
27
28
|
export declare class LLMClient {
|
|
@@ -67,6 +68,16 @@ export declare class LLMClient {
|
|
|
67
68
|
private moderationConfig;
|
|
68
69
|
/** Fold a moderation stream event into the accumulating report. */
|
|
69
70
|
private mergeModeration;
|
|
71
|
+
private retrieveContext;
|
|
72
|
+
/** Fetch a hosted-tool output file (e.g. a code-execution file from
|
|
73
|
+
* `response.files`): its bytes as a `Blob` plus `name` / `mimeType` / `size`.
|
|
74
|
+
* Resolves inline `data`, a `url`, or a provider file `id` — all through this
|
|
75
|
+
* client's provider + auth + engine. */
|
|
76
|
+
retrieveFile(file: FileOutput): Promise<RetrievedFile>;
|
|
77
|
+
/** Stream a hosted-tool output file: a `ReadableStream<Uint8Array>` plus
|
|
78
|
+
* best-effort `name` / `mimeType` / `size` (from the response headers). For large
|
|
79
|
+
* files piped straight to a file / GridFS / HTTP response without buffering. */
|
|
80
|
+
streamFile(file: FileOutput): Promise<FileStream>;
|
|
70
81
|
/** Submit a request. Returns the parsed CompletionResponse. */
|
|
71
82
|
complete(input: string | ContentPart[] | Message[], options?: ExecuteOptions): Promise<CompletionResponse>;
|
|
72
83
|
/** Run `complete` with a JSON Schema enforced via `structured`. Strips any
|