@combycode/llm-sdk 1.1.0 → 1.3.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 +50 -0
- package/README.md +24 -0
- package/dist/agent/guardrail-types.d.ts +25 -0
- package/dist/agent/loop-config.d.ts +5 -1
- package/dist/agent/loop.d.ts +11 -1
- package/dist/agent/types.d.ts +8 -0
- package/dist/helpers/one-shot.d.ts +8 -1
- package/dist/index.browser.js +341 -23
- package/dist/index.d.ts +2 -1
- package/dist/index.js +341 -23
- 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/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 +3 -0
- package/dist/llm/types/provider.d.ts +9 -1
- package/dist/llm/types/response.d.ts +7 -0
- package/dist/llm/types/stream.d.ts +9 -1
- package/dist/network/types.d.ts +4 -2
- 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;
|
|
@@ -22427,6 +22427,160 @@ async function* wrapParallel(raw, interval, moderate2) {
|
|
|
22427
22427
|
}
|
|
22428
22428
|
}
|
|
22429
22429
|
|
|
22430
|
+
// src/llm/files/retrieve.ts
|
|
22431
|
+
var DEFAULT_BASE = {
|
|
22432
|
+
anthropic: "https://api.anthropic.com",
|
|
22433
|
+
openai: "https://api.openai.com",
|
|
22434
|
+
xai: "https://api.x.ai",
|
|
22435
|
+
google: "https://generativelanguage.googleapis.com",
|
|
22436
|
+
openrouter: "https://openrouter.ai/api"
|
|
22437
|
+
};
|
|
22438
|
+
var OCTET_STREAM = "application/octet-stream";
|
|
22439
|
+
function contentRequest(ctx, file) {
|
|
22440
|
+
const base = ctx.baseURL ?? DEFAULT_BASE[ctx.provider];
|
|
22441
|
+
const id = file.id;
|
|
22442
|
+
if (ctx.provider === "anthropic") {
|
|
22443
|
+
return {
|
|
22444
|
+
url: `${base}/v1/files/${id}/content?beta=true`,
|
|
22445
|
+
headers: {
|
|
22446
|
+
"x-api-key": ctx.apiKey,
|
|
22447
|
+
"anthropic-version": "2023-06-01",
|
|
22448
|
+
"anthropic-beta": "files-api-2025-04-14",
|
|
22449
|
+
accept: "application/binary"
|
|
22450
|
+
}
|
|
22451
|
+
};
|
|
22452
|
+
}
|
|
22453
|
+
if (ctx.provider === "openai" || ctx.provider === "xai" || ctx.provider === "openrouter") {
|
|
22454
|
+
const containerId = file.ref?.containerId;
|
|
22455
|
+
const path = containerId ? `/v1/containers/${containerId}/files/${id}/content` : `/v1/files/${id}/content`;
|
|
22456
|
+
return { url: `${base}${path}`, headers: { authorization: `Bearer ${ctx.apiKey}` } };
|
|
22457
|
+
}
|
|
22458
|
+
if (ctx.provider === "google") {
|
|
22459
|
+
return {
|
|
22460
|
+
url: `${base}/v1beta/files/${id}:download?alt=media`,
|
|
22461
|
+
headers: { "x-goog-api-key": ctx.apiKey }
|
|
22462
|
+
};
|
|
22463
|
+
}
|
|
22464
|
+
throw new Error(`retrieveFile: no file-content endpoint for provider "${ctx.provider}"`);
|
|
22465
|
+
}
|
|
22466
|
+
function providerAuth(ctx, url) {
|
|
22467
|
+
const base = ctx.baseURL ?? DEFAULT_BASE[ctx.provider];
|
|
22468
|
+
if (!url.startsWith(base)) return {};
|
|
22469
|
+
if (ctx.provider === "anthropic") {
|
|
22470
|
+
return { "x-api-key": ctx.apiKey, "anthropic-version": "2023-06-01" };
|
|
22471
|
+
}
|
|
22472
|
+
if (ctx.provider === "google") return { "x-goog-api-key": ctx.apiKey };
|
|
22473
|
+
return { authorization: `Bearer ${ctx.apiKey}` };
|
|
22474
|
+
}
|
|
22475
|
+
async function fetchFileResponse(ctx, file, responseType) {
|
|
22476
|
+
if (file.url) {
|
|
22477
|
+
return ctx.fetch({
|
|
22478
|
+
url: file.url,
|
|
22479
|
+
method: "GET",
|
|
22480
|
+
headers: providerAuth(ctx, file.url),
|
|
22481
|
+
body: void 0,
|
|
22482
|
+
provider: ctx.provider,
|
|
22483
|
+
model: "files",
|
|
22484
|
+
responseType
|
|
22485
|
+
});
|
|
22486
|
+
}
|
|
22487
|
+
if (file.id) {
|
|
22488
|
+
const { url, headers } = contentRequest(ctx, file);
|
|
22489
|
+
return ctx.fetch({
|
|
22490
|
+
url,
|
|
22491
|
+
method: "GET",
|
|
22492
|
+
headers,
|
|
22493
|
+
body: void 0,
|
|
22494
|
+
provider: ctx.provider,
|
|
22495
|
+
model: "files",
|
|
22496
|
+
responseType
|
|
22497
|
+
});
|
|
22498
|
+
}
|
|
22499
|
+
throw new Error("retrieveFile: FileOutput has neither `data`, `url`, nor `id`");
|
|
22500
|
+
}
|
|
22501
|
+
function bytesToBlob(bytes, type) {
|
|
22502
|
+
const ab = new ArrayBuffer(bytes.byteLength);
|
|
22503
|
+
new Uint8Array(ab).set(bytes);
|
|
22504
|
+
return new Blob([ab], { type });
|
|
22505
|
+
}
|
|
22506
|
+
function singleChunkStream(bytes) {
|
|
22507
|
+
return new ReadableStream({
|
|
22508
|
+
start(controller) {
|
|
22509
|
+
controller.enqueue(bytes);
|
|
22510
|
+
controller.close();
|
|
22511
|
+
}
|
|
22512
|
+
});
|
|
22513
|
+
}
|
|
22514
|
+
var EXT_MIME = {
|
|
22515
|
+
png: "image/png",
|
|
22516
|
+
jpg: "image/jpeg",
|
|
22517
|
+
jpeg: "image/jpeg",
|
|
22518
|
+
gif: "image/gif",
|
|
22519
|
+
webp: "image/webp",
|
|
22520
|
+
svg: "image/svg+xml",
|
|
22521
|
+
bmp: "image/bmp",
|
|
22522
|
+
csv: "text/csv",
|
|
22523
|
+
tsv: "text/tab-separated-values",
|
|
22524
|
+
txt: "text/plain",
|
|
22525
|
+
json: "application/json",
|
|
22526
|
+
xml: "application/xml",
|
|
22527
|
+
html: "text/html",
|
|
22528
|
+
md: "text/markdown",
|
|
22529
|
+
pdf: "application/pdf",
|
|
22530
|
+
zip: "application/zip",
|
|
22531
|
+
parquet: "application/vnd.apache.parquet",
|
|
22532
|
+
xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
|
|
22533
|
+
};
|
|
22534
|
+
function mimeFromName(name) {
|
|
22535
|
+
const ext = name?.includes(".") ? name.split(".").pop()?.toLowerCase() : void 0;
|
|
22536
|
+
return ext ? EXT_MIME[ext] : void 0;
|
|
22537
|
+
}
|
|
22538
|
+
function header(headers, name) {
|
|
22539
|
+
const lower = name.toLowerCase();
|
|
22540
|
+
for (const k in headers) if (k.toLowerCase() === lower) return headers[k];
|
|
22541
|
+
return void 0;
|
|
22542
|
+
}
|
|
22543
|
+
function filenameFromDisposition(cd) {
|
|
22544
|
+
if (!cd) return void 0;
|
|
22545
|
+
const ext = cd.match(/filename\*=[^']*''([^;]+)/i);
|
|
22546
|
+
if (ext) {
|
|
22547
|
+
try {
|
|
22548
|
+
return decodeURIComponent(ext[1].trim());
|
|
22549
|
+
} catch {
|
|
22550
|
+
}
|
|
22551
|
+
}
|
|
22552
|
+
const plain = cd.match(/filename="?([^";]+)"?/i);
|
|
22553
|
+
return plain ? plain[1].trim() : void 0;
|
|
22554
|
+
}
|
|
22555
|
+
async function retrieveFile(file, ctx) {
|
|
22556
|
+
if (file.data) {
|
|
22557
|
+
const blob2 = bytesToBlob(base64ToBytes(file.data), file.mimeType ?? OCTET_STREAM);
|
|
22558
|
+
return { blob: blob2, name: file.name, mimeType: blob2.type, size: blob2.size };
|
|
22559
|
+
}
|
|
22560
|
+
const res = await fetchFileResponse(ctx, file, "arraybuffer");
|
|
22561
|
+
const headers = res.headers ?? {};
|
|
22562
|
+
const name = filenameFromDisposition(header(headers, "content-disposition")) ?? file.name;
|
|
22563
|
+
const mimeType = header(headers, "content-type")?.split(";")[0].trim() || file.mimeType || mimeFromName(name) || OCTET_STREAM;
|
|
22564
|
+
const blob = new Blob([res.body], { type: mimeType });
|
|
22565
|
+
return { blob, name, mimeType, size: blob.size };
|
|
22566
|
+
}
|
|
22567
|
+
async function streamFile(file, ctx) {
|
|
22568
|
+
if (file.data) {
|
|
22569
|
+
const bytes = base64ToBytes(file.data);
|
|
22570
|
+
return { stream: singleChunkStream(bytes), name: file.name, mimeType: file.mimeType, size: bytes.byteLength };
|
|
22571
|
+
}
|
|
22572
|
+
const res = await fetchFileResponse(ctx, file, "stream");
|
|
22573
|
+
const headers = res.headers ?? {};
|
|
22574
|
+
const len = header(headers, "content-length");
|
|
22575
|
+
const name = filenameFromDisposition(header(headers, "content-disposition")) ?? file.name;
|
|
22576
|
+
return {
|
|
22577
|
+
stream: res.body,
|
|
22578
|
+
name,
|
|
22579
|
+
mimeType: header(headers, "content-type")?.split(";")[0].trim() || file.mimeType || mimeFromName(name),
|
|
22580
|
+
size: len ? Number(len) : void 0
|
|
22581
|
+
};
|
|
22582
|
+
}
|
|
22583
|
+
|
|
22430
22584
|
// src/util/duration.ts
|
|
22431
22585
|
var UNIT_MS = {
|
|
22432
22586
|
s: 1e3,
|
|
@@ -22669,6 +22823,28 @@ var LLMClient = class {
|
|
|
22669
22823
|
else next.output = result;
|
|
22670
22824
|
return next;
|
|
22671
22825
|
}
|
|
22826
|
+
// ─── File retrieval (hosted-tool output files) ─────────────────────────
|
|
22827
|
+
retrieveContext() {
|
|
22828
|
+
return {
|
|
22829
|
+
provider: this.provider,
|
|
22830
|
+
apiKey: this.apiKey,
|
|
22831
|
+
fetch: this.fetchFn,
|
|
22832
|
+
baseURL: this.adapter.baseURL()
|
|
22833
|
+
};
|
|
22834
|
+
}
|
|
22835
|
+
/** Fetch a hosted-tool output file (e.g. a code-execution file from
|
|
22836
|
+
* `response.files`): its bytes as a `Blob` plus `name` / `mimeType` / `size`.
|
|
22837
|
+
* Resolves inline `data`, a `url`, or a provider file `id` — all through this
|
|
22838
|
+
* client's provider + auth + engine. */
|
|
22839
|
+
retrieveFile(file) {
|
|
22840
|
+
return retrieveFile(file, this.retrieveContext());
|
|
22841
|
+
}
|
|
22842
|
+
/** Stream a hosted-tool output file: a `ReadableStream<Uint8Array>` plus
|
|
22843
|
+
* best-effort `name` / `mimeType` / `size` (from the response headers). For large
|
|
22844
|
+
* files piped straight to a file / GridFS / HTTP response without buffering. */
|
|
22845
|
+
streamFile(file) {
|
|
22846
|
+
return streamFile(file, this.retrieveContext());
|
|
22847
|
+
}
|
|
22672
22848
|
/** Submit a request. Returns the parsed CompletionResponse. */
|
|
22673
22849
|
async complete(input, options = {}) {
|
|
22674
22850
|
const rawMessages = normalizeInput(input);
|
|
@@ -22880,17 +23056,18 @@ var LLMClient = class {
|
|
|
22880
23056
|
let usage = emptyUsage();
|
|
22881
23057
|
let finishReason = "stop";
|
|
22882
23058
|
let moderationReport;
|
|
23059
|
+
const files = [];
|
|
22883
23060
|
const fetchStream = this.fetchStreamFn;
|
|
22884
|
-
const adapter = this.adapter;
|
|
22885
23061
|
const queueName = this.queueName;
|
|
22886
23062
|
const priority = this.priority;
|
|
23063
|
+
const parseStream = this.adapter.createStreamParser();
|
|
22887
23064
|
async function* rawEvents() {
|
|
22888
23065
|
for await (const sseEvent of fetchStream(httpReq, {
|
|
22889
23066
|
queueName,
|
|
22890
23067
|
priority,
|
|
22891
23068
|
ctx
|
|
22892
23069
|
})) {
|
|
22893
|
-
for (const ev of
|
|
23070
|
+
for (const ev of parseStream(sseEvent)) yield ev;
|
|
22894
23071
|
}
|
|
22895
23072
|
}
|
|
22896
23073
|
const mod = options.moderation;
|
|
@@ -22928,6 +23105,9 @@ var LLMClient = class {
|
|
|
22928
23105
|
case "done":
|
|
22929
23106
|
finishReason = event.finishReason;
|
|
22930
23107
|
break;
|
|
23108
|
+
case "file":
|
|
23109
|
+
files.push(event.file);
|
|
23110
|
+
break;
|
|
22931
23111
|
case "moderation":
|
|
22932
23112
|
moderationReport = this.mergeModeration(
|
|
22933
23113
|
moderationReport,
|
|
@@ -22950,6 +23130,7 @@ var LLMClient = class {
|
|
|
22950
23130
|
toolCalls: [],
|
|
22951
23131
|
thinking: thinking || null,
|
|
22952
23132
|
media: [],
|
|
23133
|
+
...files.length ? { files } : {},
|
|
22953
23134
|
...moderationReport ? { moderation: moderationReport } : {},
|
|
22954
23135
|
latencyMs: performance.now() - start,
|
|
22955
23136
|
raw: null
|
|
@@ -23210,6 +23391,22 @@ function anthropicRequestTier(t) {
|
|
|
23210
23391
|
function anthropicBilledTier(raw) {
|
|
23211
23392
|
return typeof raw === "string" && raw ? { serviceTier: raw, pricingTier: raw } : {};
|
|
23212
23393
|
}
|
|
23394
|
+
function filesFromCodeExecBlock(block) {
|
|
23395
|
+
if (block.type !== "bash_code_execution_tool_result" && block.type !== "code_execution_tool_result") {
|
|
23396
|
+
return [];
|
|
23397
|
+
}
|
|
23398
|
+
const result = block.content;
|
|
23399
|
+
if (!result || result.type !== "bash_code_execution_result" && result.type !== "code_execution_result" || !Array.isArray(result.content)) {
|
|
23400
|
+
return [];
|
|
23401
|
+
}
|
|
23402
|
+
const files = [];
|
|
23403
|
+
for (const out of result.content) {
|
|
23404
|
+
if ((out.type === "bash_code_execution_output" || out.type === "code_execution_output") && typeof out.file_id === "string") {
|
|
23405
|
+
files.push({ id: out.file_id, source: "code_execution" });
|
|
23406
|
+
}
|
|
23407
|
+
}
|
|
23408
|
+
return files;
|
|
23409
|
+
}
|
|
23213
23410
|
var AnthropicAdapter = class {
|
|
23214
23411
|
name = "anthropic";
|
|
23215
23412
|
apiKey;
|
|
@@ -23305,7 +23502,10 @@ var AnthropicAdapter = class {
|
|
|
23305
23502
|
});
|
|
23306
23503
|
const headers = {};
|
|
23307
23504
|
if (hasFileRef) headers["anthropic-beta"] = "files-api-2025-04-14";
|
|
23308
|
-
|
|
23505
|
+
const usesCodeExec = req.tools?.some(
|
|
23506
|
+
(t) => !isFunctionTool(t) && t.type === "code_interpreter"
|
|
23507
|
+
);
|
|
23508
|
+
return { body, headers, ...usesCodeExec ? { path: "/v1/messages?beta=true" } : {} };
|
|
23309
23509
|
}
|
|
23310
23510
|
enableStreaming(providerReq, _req) {
|
|
23311
23511
|
providerReq.body.stream = true;
|
|
@@ -23387,15 +23587,8 @@ var AnthropicAdapter = class {
|
|
|
23387
23587
|
};
|
|
23388
23588
|
content.push(tc);
|
|
23389
23589
|
toolCalls.push(tc);
|
|
23390
|
-
} else
|
|
23391
|
-
|
|
23392
|
-
if (result?.type === "code_execution_result" && Array.isArray(result.content)) {
|
|
23393
|
-
for (const out of result.content) {
|
|
23394
|
-
if (out.type === "code_execution_output" && typeof out.file_id === "string") {
|
|
23395
|
-
files.push({ id: out.file_id, source: "code_execution" });
|
|
23396
|
-
}
|
|
23397
|
-
}
|
|
23398
|
-
}
|
|
23590
|
+
} else {
|
|
23591
|
+
files.push(...filesFromCodeExecBlock(block));
|
|
23399
23592
|
}
|
|
23400
23593
|
}
|
|
23401
23594
|
const finishReason = extractFinishReason(toolCalls.length > 0, r.stop_reason, {
|
|
@@ -23434,6 +23627,8 @@ var AnthropicAdapter = class {
|
|
|
23434
23627
|
if (block.type === "tool_use") {
|
|
23435
23628
|
return [{ type: "tool_call_start", id: block.id, name: block.name }];
|
|
23436
23629
|
}
|
|
23630
|
+
const files = filesFromCodeExecBlock(block);
|
|
23631
|
+
if (files.length) return files.map((file) => ({ type: "file", file }));
|
|
23437
23632
|
}
|
|
23438
23633
|
if (type === "content_block_stop") {
|
|
23439
23634
|
}
|
|
@@ -23457,6 +23652,11 @@ var AnthropicAdapter = class {
|
|
|
23457
23652
|
}
|
|
23458
23653
|
return [];
|
|
23459
23654
|
}
|
|
23655
|
+
/** Stateless — every event is self-contained (code-execution result blocks
|
|
23656
|
+
* arrive complete in a single content_block_start). */
|
|
23657
|
+
createStreamParser() {
|
|
23658
|
+
return (event) => this.parseStreamEvent(event);
|
|
23659
|
+
}
|
|
23460
23660
|
parseUsage(u) {
|
|
23461
23661
|
if (!u) return emptyUsage();
|
|
23462
23662
|
const inputTokens = u.input_tokens ?? 0;
|
|
@@ -23899,7 +24099,9 @@ var GoogleAdapter = class {
|
|
|
23899
24099
|
const content = [];
|
|
23900
24100
|
const toolCalls = [];
|
|
23901
24101
|
const media = [];
|
|
24102
|
+
const files = [];
|
|
23902
24103
|
let thinking = null;
|
|
24104
|
+
const hasCodeExec = parts.some((p) => p.executableCode || p.codeExecutionResult);
|
|
23903
24105
|
for (const part of parts) {
|
|
23904
24106
|
if (part.text !== void 0 && !part.thought) {
|
|
23905
24107
|
content.push({ type: "text", text: part.text });
|
|
@@ -23910,7 +24112,9 @@ var GoogleAdapter = class {
|
|
|
23910
24112
|
if (part.inlineData) {
|
|
23911
24113
|
const inline = part.inlineData;
|
|
23912
24114
|
const mime = inline.mimeType;
|
|
23913
|
-
if (
|
|
24115
|
+
if (hasCodeExec) {
|
|
24116
|
+
files.push({ data: inline.data, mimeType: mime, source: "code_execution" });
|
|
24117
|
+
} else if (mime.startsWith("image/")) {
|
|
23914
24118
|
const p = {
|
|
23915
24119
|
type: "image_output",
|
|
23916
24120
|
mediaId: "",
|
|
@@ -23971,11 +24175,24 @@ var GoogleAdapter = class {
|
|
|
23971
24175
|
toolCalls,
|
|
23972
24176
|
thinking,
|
|
23973
24177
|
media,
|
|
24178
|
+
...files.length ? { files } : {},
|
|
23974
24179
|
latencyMs,
|
|
23975
24180
|
raw
|
|
23976
24181
|
};
|
|
23977
24182
|
}
|
|
23978
24183
|
parseStreamEvent(event) {
|
|
24184
|
+
return this.streamEvents(event, { codeExec: false });
|
|
24185
|
+
}
|
|
24186
|
+
/** Stateful — Google splits the code-execution marker (`executableCode` /
|
|
24187
|
+
* `codeExecutionResult`) and the produced file (`inlineData`) across parts and
|
|
24188
|
+
* often across SSE events. The closure remembers "code execution began in this
|
|
24189
|
+
* stream" so a later `inlineData` blob is routed to `files` (a code-exec
|
|
24190
|
+
* artifact) rather than `media` (conversational output). */
|
|
24191
|
+
createStreamParser() {
|
|
24192
|
+
const state = { codeExec: false };
|
|
24193
|
+
return (event) => this.streamEvents(event, state);
|
|
24194
|
+
}
|
|
24195
|
+
streamEvents(event, state) {
|
|
23979
24196
|
const data = JSON.parse(event.data);
|
|
23980
24197
|
const candidates = data.candidates ?? [];
|
|
23981
24198
|
const candidate = candidates[0];
|
|
@@ -23989,6 +24206,9 @@ var GoogleAdapter = class {
|
|
|
23989
24206
|
const rawContent = candidate.content ?? {};
|
|
23990
24207
|
const parts = rawContent.parts ?? [];
|
|
23991
24208
|
const events = [];
|
|
24209
|
+
for (const part of parts) {
|
|
24210
|
+
if (part.executableCode || part.codeExecutionResult) state.codeExec = true;
|
|
24211
|
+
}
|
|
23992
24212
|
for (const part of parts) {
|
|
23993
24213
|
if (part.text !== void 0 && !part.thought)
|
|
23994
24214
|
events.push({ type: "text", text: part.text });
|
|
@@ -23996,10 +24216,17 @@ var GoogleAdapter = class {
|
|
|
23996
24216
|
if (part.inlineData) {
|
|
23997
24217
|
const inline = part.inlineData;
|
|
23998
24218
|
const mime = inline.mimeType;
|
|
23999
|
-
|
|
24000
|
-
|
|
24001
|
-
|
|
24002
|
-
|
|
24219
|
+
if (state.codeExec) {
|
|
24220
|
+
events.push({
|
|
24221
|
+
type: "file",
|
|
24222
|
+
file: { data: inline.data, mimeType: mime, source: "code_execution" }
|
|
24223
|
+
});
|
|
24224
|
+
} else {
|
|
24225
|
+
const mediaType = mime.startsWith("image/") ? "image" : mime.startsWith("audio/") ? "audio" : "video";
|
|
24226
|
+
events.push({ type: "media_start", mediaType, mimeType: mime });
|
|
24227
|
+
events.push({ type: "media_chunk", data: inline.data });
|
|
24228
|
+
events.push({ type: "media_end" });
|
|
24229
|
+
}
|
|
24003
24230
|
}
|
|
24004
24231
|
if (part.functionCall) {
|
|
24005
24232
|
const fc = part.functionCall;
|
|
@@ -24294,6 +24521,10 @@ var GoogleInteractionsAdapter = class {
|
|
|
24294
24521
|
}
|
|
24295
24522
|
return events;
|
|
24296
24523
|
}
|
|
24524
|
+
/** Stateless — the Interactions adapter surfaces no code-execution file outputs. */
|
|
24525
|
+
createStreamParser() {
|
|
24526
|
+
return (event) => this.parseStreamEvent(event);
|
|
24527
|
+
}
|
|
24297
24528
|
parseUsage(u) {
|
|
24298
24529
|
if (!u) return emptyUsage();
|
|
24299
24530
|
const input = u.total_input_tokens ?? u.prompt_tokens ?? 0;
|
|
@@ -25259,6 +25490,10 @@ var OpenAIAdapter = class {
|
|
|
25259
25490
|
}
|
|
25260
25491
|
return events;
|
|
25261
25492
|
}
|
|
25493
|
+
/** Stateless — Chat Completions has no hosted code-execution file outputs. */
|
|
25494
|
+
createStreamParser() {
|
|
25495
|
+
return (event) => this.parseStreamEvent(event);
|
|
25496
|
+
}
|
|
25262
25497
|
parseUsage(u) {
|
|
25263
25498
|
if (!u) return emptyUsage();
|
|
25264
25499
|
const input = u.prompt_tokens ?? u.input_tokens ?? 0;
|
|
@@ -25774,6 +26009,33 @@ function filenameForMime(mimeType) {
|
|
|
25774
26009
|
if (mimeType.startsWith("image/")) return `file.${mimeType.slice("image/".length)}`;
|
|
25775
26010
|
return "file.bin";
|
|
25776
26011
|
}
|
|
26012
|
+
function filesFromResponsesOutputItem(item) {
|
|
26013
|
+
const files = [];
|
|
26014
|
+
const type = item.type;
|
|
26015
|
+
if (type === "message") {
|
|
26016
|
+
for (const c of item.content ?? []) {
|
|
26017
|
+
if (c.type !== "output_text") continue;
|
|
26018
|
+
for (const a of c.annotations ?? []) {
|
|
26019
|
+
if (a.type === "container_file_citation" && typeof a.file_id === "string") {
|
|
26020
|
+
files.push({
|
|
26021
|
+
id: a.file_id,
|
|
26022
|
+
...typeof a.filename === "string" ? { name: a.filename } : {},
|
|
26023
|
+
...typeof a.container_id === "string" ? { ref: { containerId: a.container_id } } : {},
|
|
26024
|
+
source: "code_execution"
|
|
26025
|
+
});
|
|
26026
|
+
}
|
|
26027
|
+
}
|
|
26028
|
+
}
|
|
26029
|
+
}
|
|
26030
|
+
if (type === "code_interpreter_call") {
|
|
26031
|
+
for (const out of item.outputs ?? []) {
|
|
26032
|
+
if (out.type === "image" && typeof out.url === "string") {
|
|
26033
|
+
files.push({ url: out.url, source: "code_execution" });
|
|
26034
|
+
}
|
|
26035
|
+
}
|
|
26036
|
+
}
|
|
26037
|
+
return files;
|
|
26038
|
+
}
|
|
25777
26039
|
var OpenAIResponsesAdapter = class {
|
|
25778
26040
|
name = "openai";
|
|
25779
26041
|
apiKey;
|
|
@@ -25949,10 +26211,12 @@ var OpenAIResponsesAdapter = class {
|
|
|
25949
26211
|
const content = [];
|
|
25950
26212
|
const toolCalls = [];
|
|
25951
26213
|
const media = [];
|
|
26214
|
+
const files = [];
|
|
25952
26215
|
let thinking = null;
|
|
25953
26216
|
let text = "";
|
|
25954
26217
|
for (const item of output) {
|
|
25955
26218
|
const type = item.type;
|
|
26219
|
+
files.push(...filesFromResponsesOutputItem(item));
|
|
25956
26220
|
if (type === "message") {
|
|
25957
26221
|
const itemContent = item.content ?? [];
|
|
25958
26222
|
for (const c of itemContent) {
|
|
@@ -26012,6 +26276,7 @@ var OpenAIResponsesAdapter = class {
|
|
|
26012
26276
|
toolCalls,
|
|
26013
26277
|
thinking,
|
|
26014
26278
|
media,
|
|
26279
|
+
...files.length ? { files } : {},
|
|
26015
26280
|
...moderation ? { moderation } : {},
|
|
26016
26281
|
latencyMs,
|
|
26017
26282
|
raw
|
|
@@ -26053,6 +26318,7 @@ var OpenAIResponsesAdapter = class {
|
|
|
26053
26318
|
}
|
|
26054
26319
|
if (type === "response.output_item.done") {
|
|
26055
26320
|
const item = data.item;
|
|
26321
|
+
for (const file of filesFromResponsesOutputItem(item)) events.push({ type: "file", file });
|
|
26056
26322
|
if (item?.type === "function_call") {
|
|
26057
26323
|
events.push({ type: "tool_call_end", id: item.call_id ?? "" });
|
|
26058
26324
|
}
|
|
@@ -26083,6 +26349,11 @@ var OpenAIResponsesAdapter = class {
|
|
|
26083
26349
|
}
|
|
26084
26350
|
return events;
|
|
26085
26351
|
}
|
|
26352
|
+
/** Stateless — each output item finalizes with all its file annotations in a
|
|
26353
|
+
* single response.output_item.done event. */
|
|
26354
|
+
createStreamParser() {
|
|
26355
|
+
return (event) => this.parseStreamEvent(event);
|
|
26356
|
+
}
|
|
26086
26357
|
parseUsage(u) {
|
|
26087
26358
|
if (!u) return emptyUsage();
|
|
26088
26359
|
const input = u.input_tokens ?? 0;
|
|
@@ -27738,6 +28009,7 @@ var AgentLoop = class _AgentLoop {
|
|
|
27738
28009
|
_toolTimeout;
|
|
27739
28010
|
_maxSteps;
|
|
27740
28011
|
_guardrails;
|
|
28012
|
+
_toolInputGuardrails;
|
|
27741
28013
|
_policy;
|
|
27742
28014
|
_approve;
|
|
27743
28015
|
_checkpoint;
|
|
@@ -27765,6 +28037,7 @@ var AgentLoop = class _AgentLoop {
|
|
|
27765
28037
|
this._toolTimeout = config.toolTimeout ?? DEFAULT_TOOL_TIMEOUT_MS;
|
|
27766
28038
|
this._maxSteps = config.maxSteps !== void 0 && config.maxSteps > 0 ? config.maxSteps : DEFAULT_MAX_STEPS;
|
|
27767
28039
|
this._guardrails = config.guardrails ?? [];
|
|
28040
|
+
this._toolInputGuardrails = config.toolInputGuardrails ?? [];
|
|
27768
28041
|
this._policy = config.policy ?? null;
|
|
27769
28042
|
this._approve = config.approve ?? null;
|
|
27770
28043
|
this._checkpoint = config.checkpoint ?? null;
|
|
@@ -27802,6 +28075,19 @@ var AgentLoop = class _AgentLoop {
|
|
|
27802
28075
|
get model() {
|
|
27803
28076
|
return this.client.model;
|
|
27804
28077
|
}
|
|
28078
|
+
// ─── File retrieval (hosted-tool output files) ──────────────────────────
|
|
28079
|
+
/** Fetch a hosted-tool output file from the run's `response.files` — its bytes
|
|
28080
|
+
* as a `Blob` plus `name` / `mimeType` / `size`. Delegates to the underlying
|
|
28081
|
+
* client, so it uses this agent's provider + key (same as `retrieveFile` on a
|
|
28082
|
+
* `complete()` result). */
|
|
28083
|
+
retrieveFile(file) {
|
|
28084
|
+
return this.client.retrieveFile(file);
|
|
28085
|
+
}
|
|
28086
|
+
/** Stream a hosted-tool output file (large files) — a `ReadableStream` plus
|
|
28087
|
+
* best-effort `name` / `mimeType` / `size`. Delegates to the underlying client. */
|
|
28088
|
+
streamFile(file) {
|
|
28089
|
+
return this.client.streamFile(file);
|
|
28090
|
+
}
|
|
27805
28091
|
get system() {
|
|
27806
28092
|
return this._system;
|
|
27807
28093
|
}
|
|
@@ -27983,6 +28269,10 @@ var AgentLoop = class _AgentLoop {
|
|
|
27983
28269
|
toolCalls: lastResponse?.toolCalls ?? [],
|
|
27984
28270
|
thinking: lastResponse?.thinking ?? null,
|
|
27985
28271
|
media: lastResponse?.media ?? [],
|
|
28272
|
+
// Propagate hosted-tool file outputs + inline-moderation result from the final
|
|
28273
|
+
// LLM response (e.g. code-execution files produced during the run).
|
|
28274
|
+
...lastResponse?.files ? { files: lastResponse.files } : {},
|
|
28275
|
+
...lastResponse?.moderation ? { moderation: lastResponse.moderation } : {},
|
|
27986
28276
|
latencyMs: performance.now() - startPerf,
|
|
27987
28277
|
raw: lastResponse?.raw ?? null
|
|
27988
28278
|
};
|
|
@@ -28177,6 +28467,8 @@ var AgentLoop = class _AgentLoop {
|
|
|
28177
28467
|
toolCalls: lastResponse?.toolCalls ?? [],
|
|
28178
28468
|
thinking: lastResponse?.thinking ?? null,
|
|
28179
28469
|
media: [],
|
|
28470
|
+
...lastResponse?.files ? { files: lastResponse.files } : {},
|
|
28471
|
+
...lastResponse?.moderation ? { moderation: lastResponse.moderation } : {},
|
|
28180
28472
|
latencyMs: performance.now() - startPerf,
|
|
28181
28473
|
raw: null
|
|
28182
28474
|
};
|
|
@@ -28263,6 +28555,18 @@ var AgentLoop = class _AgentLoop {
|
|
|
28263
28555
|
runTrace
|
|
28264
28556
|
);
|
|
28265
28557
|
if (!lookup.found) return lookup.errorResult;
|
|
28558
|
+
for (const g of this._toolInputGuardrails) {
|
|
28559
|
+
const decision = await g.check({
|
|
28560
|
+
toolName: tc.name,
|
|
28561
|
+
arguments: tc.arguments,
|
|
28562
|
+
callId: tc.id,
|
|
28563
|
+
step,
|
|
28564
|
+
trace: { sessionId: runTrace.sessionId, requestId: runTrace.requestId, callId: tc.id }
|
|
28565
|
+
});
|
|
28566
|
+
if (!decision.pass) {
|
|
28567
|
+
return this.buildDeniedResult(tc, decision.reason, reports);
|
|
28568
|
+
}
|
|
28569
|
+
}
|
|
28266
28570
|
if (this._policy !== null) {
|
|
28267
28571
|
const target = {
|
|
28268
28572
|
kind: "tool",
|
|
@@ -28279,7 +28583,7 @@ var AgentLoop = class _AgentLoop {
|
|
|
28279
28583
|
try {
|
|
28280
28584
|
const baseCtx = { step, callId: tc.id, metrics, trace: { sessionId: runTrace.sessionId, requestId: runTrace.requestId, callId: tc.id } };
|
|
28281
28585
|
const result = await executeWithTimeout(lookup.tool, tc, baseCtx, this._toolTimeout);
|
|
28282
|
-
return await this.buildSuccessResult(tc, result, runId, step, metrics, reports, toolStart, runTrace);
|
|
28586
|
+
return await this.buildSuccessResult(tc, result, runId, step, metrics, reports, toolStart, runTrace, lookup.tool, baseCtx);
|
|
28283
28587
|
} catch (e) {
|
|
28284
28588
|
return handleToolError(e, tc, this.hooks, runId, this.id, step, metrics, reports, toolStart, runTrace);
|
|
28285
28589
|
}
|
|
@@ -28370,7 +28674,7 @@ var AgentLoop = class _AgentLoop {
|
|
|
28370
28674
|
try {
|
|
28371
28675
|
const baseCtx = { step, callId: tc.id, metrics, trace: { sessionId: runTrace.sessionId, requestId: runTrace.requestId, callId: tc.id } };
|
|
28372
28676
|
const result = await executeWithTimeout(tool, tc, baseCtx, this._toolTimeout);
|
|
28373
|
-
return await this.buildSuccessResult(tc, result, runId, step, metrics, reports, toolStart, runTrace);
|
|
28677
|
+
return await this.buildSuccessResult(tc, result, runId, step, metrics, reports, toolStart, runTrace, tool, baseCtx);
|
|
28374
28678
|
} catch (e) {
|
|
28375
28679
|
return handleToolError(e, tc, this.hooks, runId, this.id, step, metrics, reports, toolStart, runTrace);
|
|
28376
28680
|
}
|
|
@@ -28382,9 +28686,16 @@ var AgentLoop = class _AgentLoop {
|
|
|
28382
28686
|
return this.buildDeniedResult(tc, denyMsg, reports);
|
|
28383
28687
|
}
|
|
28384
28688
|
/** Emit onToolCallComplete, push report, and return success content part. */
|
|
28385
|
-
async buildSuccessResult(tc, result, runId, step, metrics, reports, toolStart, runTrace) {
|
|
28689
|
+
async buildSuccessResult(tc, result, runId, step, metrics, reports, toolStart, runTrace, tool, context) {
|
|
28386
28690
|
const latencyMs = performance.now() - toolStart;
|
|
28387
28691
|
const resultStr = typeof result === "string" ? result : JSON.stringify(result);
|
|
28692
|
+
let customData;
|
|
28693
|
+
if (tool?.customDataExtractor && context) {
|
|
28694
|
+
try {
|
|
28695
|
+
customData = await tool.customDataExtractor(result, tc.arguments, context);
|
|
28696
|
+
} catch {
|
|
28697
|
+
}
|
|
28698
|
+
}
|
|
28388
28699
|
await this.hooks.emit("onToolCallComplete", {
|
|
28389
28700
|
runId,
|
|
28390
28701
|
agentId: this.id,
|
|
@@ -28406,7 +28717,8 @@ var AgentLoop = class _AgentLoop {
|
|
|
28406
28717
|
latencyMs,
|
|
28407
28718
|
skipped: false,
|
|
28408
28719
|
error: null,
|
|
28409
|
-
metrics: Object.fromEntries(metrics)
|
|
28720
|
+
metrics: Object.fromEntries(metrics),
|
|
28721
|
+
...customData !== void 0 ? { customData } : {}
|
|
28410
28722
|
});
|
|
28411
28723
|
return { type: "tool_result", id: tc.id, content: resultStr };
|
|
28412
28724
|
}
|
|
@@ -34410,7 +34722,13 @@ async function complete(opts) {
|
|
|
34410
34722
|
serviceTier
|
|
34411
34723
|
});
|
|
34412
34724
|
}
|
|
34413
|
-
const result = {
|
|
34725
|
+
const result = {
|
|
34726
|
+
text: res.text,
|
|
34727
|
+
response: res,
|
|
34728
|
+
// Bound to this call's client (same provider/model/key/engine).
|
|
34729
|
+
retrieveFile: (file) => llm.retrieveFile(file),
|
|
34730
|
+
streamFile: (file) => llm.streamFile(file)
|
|
34731
|
+
};
|
|
34414
34732
|
if (opts.structured?.schema) {
|
|
34415
34733
|
result.parsed = parseStructured(res.text);
|
|
34416
34734
|
}
|
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';
|
|
@@ -98,7 +99,7 @@ export type { ConversationHistoryConfig, HistoryEntry, HistorySnapshot } from '.
|
|
|
98
99
|
export { AgentLoop } from './agent/loop';
|
|
99
100
|
export type { AgentLoopConfig } from './agent/loop-config';
|
|
100
101
|
export type { AgentLoopSnapshot, AgentRunReport, AgentStreamEvent, AgentTool, ContentClass, LearnInput, StepReport, TokenCountContext, TokenCounter, ToolCallReport, ToolExecutionContext } from './agent/types';
|
|
101
|
-
export type { Guardrail, GuardrailDecision, GuardrailPass, GuardrailTrip, GuardrailCheckContext, InputGuardrailContext, OutputGuardrailContext, GuardrailTriggeredContext } from './agent/guardrail-types';
|
|
102
|
+
export type { Guardrail, GuardrailDecision, GuardrailPass, GuardrailTrip, GuardrailCheckContext, InputGuardrailContext, OutputGuardrailContext, GuardrailTriggeredContext, ToolInputGuardrail, ToolInputGuardrailContext, ToolInputGuardrailDecision } from './agent/guardrail-types';
|
|
102
103
|
export { BearerKeyAuth } from './server/auth';
|
|
103
104
|
export type { AuthPlugin, AuthVerifyResult } from './server/auth';
|
|
104
105
|
export { dispatch } from './server/dispatch';
|