@ljoukov/llm 4.0.12 → 4.1.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/README.md +127 -4
- package/dist/index.cjs +2026 -547
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +85 -13
- package/dist/index.d.ts +85 -13
- package/dist/index.js +1986 -510
- package/dist/index.js.map +1 -1
- package/package.json +4 -3
package/dist/index.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
// src/llm.ts
|
|
2
|
-
import { Buffer as
|
|
3
|
-
import { AsyncLocalStorage as
|
|
2
|
+
import { Buffer as Buffer5 } from "buffer";
|
|
3
|
+
import { AsyncLocalStorage as AsyncLocalStorage3 } from "async_hooks";
|
|
4
4
|
import { randomBytes } from "crypto";
|
|
5
|
+
import path5 from "path";
|
|
5
6
|
import {
|
|
6
7
|
FinishReason,
|
|
7
8
|
FunctionCallingConfigMode,
|
|
@@ -2248,6 +2249,14 @@ function normaliseConfigValue(value) {
|
|
|
2248
2249
|
const trimmed = value.trim();
|
|
2249
2250
|
return trimmed.length > 0 ? trimmed : void 0;
|
|
2250
2251
|
}
|
|
2252
|
+
function resolveGeminiApiKey() {
|
|
2253
|
+
loadLocalEnv();
|
|
2254
|
+
const raw = process.env.GEMINI_API_KEY ?? process.env.GOOGLE_API_KEY;
|
|
2255
|
+
return normaliseConfigValue(raw);
|
|
2256
|
+
}
|
|
2257
|
+
function getGeminiBackend() {
|
|
2258
|
+
return resolveGeminiApiKey() ? "api" : "vertex";
|
|
2259
|
+
}
|
|
2251
2260
|
function configureGemini(options = {}) {
|
|
2252
2261
|
const nextProjectId = normaliseConfigValue(options.projectId);
|
|
2253
2262
|
const nextLocation = normaliseConfigValue(options.location);
|
|
@@ -2275,6 +2284,10 @@ function resolveLocation() {
|
|
|
2275
2284
|
async function getGeminiClient() {
|
|
2276
2285
|
if (!geminiClientState.clientPromise) {
|
|
2277
2286
|
geminiClientState.clientPromise = Promise.resolve().then(() => {
|
|
2287
|
+
const apiKey = resolveGeminiApiKey();
|
|
2288
|
+
if (apiKey) {
|
|
2289
|
+
return new GoogleGenAI({ apiKey });
|
|
2290
|
+
}
|
|
2278
2291
|
const projectId = resolveProjectId();
|
|
2279
2292
|
const location = resolveLocation();
|
|
2280
2293
|
const googleAuthOptions = getGoogleAuthOptions(CLOUD_PLATFORM_SCOPE);
|
|
@@ -3241,14 +3254,708 @@ function getCurrentAgentLoggingSession() {
|
|
|
3241
3254
|
return loggingSessionStorage.getStore();
|
|
3242
3255
|
}
|
|
3243
3256
|
|
|
3257
|
+
// src/files.ts
|
|
3258
|
+
import { AsyncLocalStorage as AsyncLocalStorage2 } from "async_hooks";
|
|
3259
|
+
import { Buffer as Buffer4, File as NodeFile } from "buffer";
|
|
3260
|
+
import { createHash } from "crypto";
|
|
3261
|
+
import { createReadStream, createWriteStream, openAsBlob } from "fs";
|
|
3262
|
+
import { mkdir as mkdir2, mkdtemp, stat, unlink, writeFile as writeFile2 } from "fs/promises";
|
|
3263
|
+
import os3 from "os";
|
|
3264
|
+
import path4 from "path";
|
|
3265
|
+
import { Readable } from "stream";
|
|
3266
|
+
import { pipeline } from "stream/promises";
|
|
3267
|
+
import { Storage } from "@google-cloud/storage";
|
|
3268
|
+
import mime from "mime";
|
|
3269
|
+
var DEFAULT_FILE_TTL_SECONDS = 48 * 60 * 60;
|
|
3270
|
+
var OPENAI_FILE_CREATE_MAX_BYTES = 512 * 1024 * 1024;
|
|
3271
|
+
var OPENAI_UPLOAD_PART_MAX_BYTES = 64 * 1024 * 1024;
|
|
3272
|
+
var GEMINI_FILE_POLL_INTERVAL_MS = 1e3;
|
|
3273
|
+
var GEMINI_FILE_POLL_TIMEOUT_MS = 6e4;
|
|
3274
|
+
var FILES_TEMP_ROOT = path4.join(os3.tmpdir(), "ljoukov-llm-files");
|
|
3275
|
+
var filesState = getRuntimeSingleton(/* @__PURE__ */ Symbol.for("@ljoukov/llm.filesState"), () => ({
|
|
3276
|
+
metadataById: /* @__PURE__ */ new Map(),
|
|
3277
|
+
openAiUploadCacheByKey: /* @__PURE__ */ new Map(),
|
|
3278
|
+
materializedById: /* @__PURE__ */ new Map(),
|
|
3279
|
+
geminiMirrorById: /* @__PURE__ */ new Map(),
|
|
3280
|
+
vertexMirrorById: /* @__PURE__ */ new Map(),
|
|
3281
|
+
storageClient: void 0,
|
|
3282
|
+
geminiClientPromise: void 0
|
|
3283
|
+
}));
|
|
3284
|
+
var fileUploadScopeStorage = getRuntimeSingleton(
|
|
3285
|
+
/* @__PURE__ */ Symbol.for("@ljoukov/llm.fileUploadScopeStorage"),
|
|
3286
|
+
() => new AsyncLocalStorage2()
|
|
3287
|
+
);
|
|
3288
|
+
function summarizeUploadEvents(events) {
|
|
3289
|
+
let totalBytes = 0;
|
|
3290
|
+
let totalLatencyMs = 0;
|
|
3291
|
+
for (const event of events) {
|
|
3292
|
+
totalBytes += Math.max(0, event.bytes);
|
|
3293
|
+
totalLatencyMs += Math.max(0, event.durationMs);
|
|
3294
|
+
}
|
|
3295
|
+
return {
|
|
3296
|
+
count: events.length,
|
|
3297
|
+
totalBytes,
|
|
3298
|
+
totalLatencyMs,
|
|
3299
|
+
events: Array.from(events)
|
|
3300
|
+
};
|
|
3301
|
+
}
|
|
3302
|
+
function emptyFileUploadMetrics() {
|
|
3303
|
+
return summarizeUploadEvents([]);
|
|
3304
|
+
}
|
|
3305
|
+
function getCurrentFileUploadMetrics() {
|
|
3306
|
+
const collector = fileUploadScopeStorage.getStore()?.collectors.at(-1);
|
|
3307
|
+
return summarizeUploadEvents(collector?.events ?? []);
|
|
3308
|
+
}
|
|
3309
|
+
async function collectFileUploadMetrics(fn) {
|
|
3310
|
+
const parent = fileUploadScopeStorage.getStore();
|
|
3311
|
+
const collector = { events: [] };
|
|
3312
|
+
const scope = {
|
|
3313
|
+
collectors: [...parent?.collectors ?? [], collector],
|
|
3314
|
+
source: parent?.source
|
|
3315
|
+
};
|
|
3316
|
+
return await fileUploadScopeStorage.run(scope, async () => {
|
|
3317
|
+
const result = await fn();
|
|
3318
|
+
return {
|
|
3319
|
+
result,
|
|
3320
|
+
uploads: summarizeUploadEvents(collector.events)
|
|
3321
|
+
};
|
|
3322
|
+
});
|
|
3323
|
+
}
|
|
3324
|
+
async function runWithFileUploadSource(source, fn) {
|
|
3325
|
+
const parent = fileUploadScopeStorage.getStore();
|
|
3326
|
+
const scope = {
|
|
3327
|
+
collectors: parent?.collectors ?? [],
|
|
3328
|
+
source
|
|
3329
|
+
};
|
|
3330
|
+
return await fileUploadScopeStorage.run(scope, fn);
|
|
3331
|
+
}
|
|
3332
|
+
function formatUploadLogLine(event) {
|
|
3333
|
+
const parts = [
|
|
3334
|
+
"[upload]",
|
|
3335
|
+
`source=${event.source}`,
|
|
3336
|
+
`backend=${event.backend}`,
|
|
3337
|
+
`mode=${event.mode}`,
|
|
3338
|
+
`filename=${JSON.stringify(event.filename)}`,
|
|
3339
|
+
`bytes=${event.bytes.toString()}`,
|
|
3340
|
+
`durationMs=${event.durationMs.toString()}`
|
|
3341
|
+
];
|
|
3342
|
+
if (event.mimeType) {
|
|
3343
|
+
parts.push(`mimeType=${event.mimeType}`);
|
|
3344
|
+
}
|
|
3345
|
+
if (event.fileId) {
|
|
3346
|
+
parts.push(`fileId=${event.fileId}`);
|
|
3347
|
+
}
|
|
3348
|
+
if (event.mirrorId) {
|
|
3349
|
+
parts.push(`mirrorId=${event.mirrorId}`);
|
|
3350
|
+
}
|
|
3351
|
+
if (event.fileUri) {
|
|
3352
|
+
parts.push(`fileUri=${JSON.stringify(event.fileUri)}`);
|
|
3353
|
+
}
|
|
3354
|
+
return parts.join(" ");
|
|
3355
|
+
}
|
|
3356
|
+
function recordUploadEvent(event) {
|
|
3357
|
+
const scope = fileUploadScopeStorage.getStore();
|
|
3358
|
+
const resolvedSource = event.source ?? scope?.source ?? (event.backend === "openai" ? "files_api" : "provider_mirror");
|
|
3359
|
+
const timestampedEvent = {
|
|
3360
|
+
...event,
|
|
3361
|
+
source: resolvedSource,
|
|
3362
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
3363
|
+
};
|
|
3364
|
+
for (const collector of scope?.collectors ?? []) {
|
|
3365
|
+
collector.events.push(timestampedEvent);
|
|
3366
|
+
}
|
|
3367
|
+
getCurrentAgentLoggingSession()?.logLine(formatUploadLogLine(timestampedEvent));
|
|
3368
|
+
}
|
|
3369
|
+
function normaliseFilename(filename, fallback = "attachment.bin") {
|
|
3370
|
+
const trimmed = filename?.trim();
|
|
3371
|
+
if (!trimmed) {
|
|
3372
|
+
return fallback;
|
|
3373
|
+
}
|
|
3374
|
+
const basename = path4.basename(trimmed);
|
|
3375
|
+
return basename.length > 0 ? basename : fallback;
|
|
3376
|
+
}
|
|
3377
|
+
function resolveMimeType(filename, explicitMimeType, fallback = "application/octet-stream") {
|
|
3378
|
+
const trimmed = explicitMimeType?.trim();
|
|
3379
|
+
if (trimmed) {
|
|
3380
|
+
return trimmed;
|
|
3381
|
+
}
|
|
3382
|
+
const inferred = mime.getType(filename);
|
|
3383
|
+
return typeof inferred === "string" && inferred.length > 0 ? inferred : fallback;
|
|
3384
|
+
}
|
|
3385
|
+
function toBuffer(data) {
|
|
3386
|
+
if (typeof data === "string") {
|
|
3387
|
+
return Buffer4.from(data, "utf8");
|
|
3388
|
+
}
|
|
3389
|
+
if (ArrayBuffer.isView(data)) {
|
|
3390
|
+
return Buffer4.from(data.buffer, data.byteOffset, data.byteLength);
|
|
3391
|
+
}
|
|
3392
|
+
return Buffer4.from(data);
|
|
3393
|
+
}
|
|
3394
|
+
function computeSha256Hex(buffer) {
|
|
3395
|
+
return createHash("sha256").update(buffer).digest("hex");
|
|
3396
|
+
}
|
|
3397
|
+
async function computeFileSha256Hex(filePath) {
|
|
3398
|
+
const hash = createHash("sha256");
|
|
3399
|
+
const stream = createReadStream(filePath);
|
|
3400
|
+
for await (const chunk of stream) {
|
|
3401
|
+
hash.update(chunk);
|
|
3402
|
+
}
|
|
3403
|
+
return hash.digest("hex");
|
|
3404
|
+
}
|
|
3405
|
+
function toStoredFile(file) {
|
|
3406
|
+
return {
|
|
3407
|
+
id: file.id,
|
|
3408
|
+
bytes: file.bytes,
|
|
3409
|
+
created_at: file.created_at,
|
|
3410
|
+
filename: file.filename,
|
|
3411
|
+
object: "file",
|
|
3412
|
+
purpose: file.purpose,
|
|
3413
|
+
status: file.status,
|
|
3414
|
+
expires_at: file.expires_at
|
|
3415
|
+
};
|
|
3416
|
+
}
|
|
3417
|
+
function buildCacheKey(filename, mimeType, sha256Hex) {
|
|
3418
|
+
return `${sha256Hex}\0${filename}\0${mimeType}`;
|
|
3419
|
+
}
|
|
3420
|
+
function isFresh(file) {
|
|
3421
|
+
if (!file.expires_at) {
|
|
3422
|
+
return true;
|
|
3423
|
+
}
|
|
3424
|
+
return file.expires_at * 1e3 > Date.now() + 3e4;
|
|
3425
|
+
}
|
|
3426
|
+
function recordMetadata(metadata) {
|
|
3427
|
+
filesState.metadataById.set(metadata.file.id, metadata);
|
|
3428
|
+
if (metadata.sha256Hex) {
|
|
3429
|
+
filesState.openAiUploadCacheByKey.set(
|
|
3430
|
+
buildCacheKey(
|
|
3431
|
+
metadata.filename,
|
|
3432
|
+
metadata.mimeType ?? "application/octet-stream",
|
|
3433
|
+
metadata.sha256Hex
|
|
3434
|
+
),
|
|
3435
|
+
metadata
|
|
3436
|
+
);
|
|
3437
|
+
}
|
|
3438
|
+
return metadata;
|
|
3439
|
+
}
|
|
3440
|
+
async function uploadOpenAiFileFromBytes(params) {
|
|
3441
|
+
const cacheKey = buildCacheKey(params.filename, params.mimeType, params.sha256Hex);
|
|
3442
|
+
const cached = filesState.openAiUploadCacheByKey.get(cacheKey);
|
|
3443
|
+
if (cached && isFresh(cached.file)) {
|
|
3444
|
+
return cached;
|
|
3445
|
+
}
|
|
3446
|
+
const client = getOpenAiClient();
|
|
3447
|
+
const startedAtMs = Date.now();
|
|
3448
|
+
let uploaded;
|
|
3449
|
+
let mode;
|
|
3450
|
+
if (params.bytes.byteLength <= OPENAI_FILE_CREATE_MAX_BYTES) {
|
|
3451
|
+
mode = "files.create";
|
|
3452
|
+
uploaded = await client.files.create({
|
|
3453
|
+
file: new NodeFile([new Uint8Array(params.bytes)], params.filename, {
|
|
3454
|
+
type: params.mimeType
|
|
3455
|
+
}),
|
|
3456
|
+
purpose: params.purpose,
|
|
3457
|
+
expires_after: {
|
|
3458
|
+
anchor: "created_at",
|
|
3459
|
+
seconds: params.expiresAfterSeconds
|
|
3460
|
+
}
|
|
3461
|
+
});
|
|
3462
|
+
} else {
|
|
3463
|
+
mode = "uploads";
|
|
3464
|
+
const upload = await client.uploads.create({
|
|
3465
|
+
bytes: params.bytes.byteLength,
|
|
3466
|
+
filename: params.filename,
|
|
3467
|
+
mime_type: params.mimeType,
|
|
3468
|
+
purpose: params.purpose
|
|
3469
|
+
});
|
|
3470
|
+
const partIds = [];
|
|
3471
|
+
for (let offset = 0; offset < params.bytes.byteLength; offset += OPENAI_UPLOAD_PART_MAX_BYTES) {
|
|
3472
|
+
const chunk = params.bytes.subarray(
|
|
3473
|
+
offset,
|
|
3474
|
+
Math.min(offset + OPENAI_UPLOAD_PART_MAX_BYTES, params.bytes.byteLength)
|
|
3475
|
+
);
|
|
3476
|
+
const uploadPart = await client.uploads.parts.create(upload.id, {
|
|
3477
|
+
data: new NodeFile([new Uint8Array(chunk)], `${params.sha256Hex}.part`, {
|
|
3478
|
+
type: params.mimeType
|
|
3479
|
+
})
|
|
3480
|
+
});
|
|
3481
|
+
partIds.push(uploadPart.id);
|
|
3482
|
+
}
|
|
3483
|
+
const completed = await client.uploads.complete(upload.id, { part_ids: partIds });
|
|
3484
|
+
const fileId = completed.file?.id;
|
|
3485
|
+
if (!fileId) {
|
|
3486
|
+
throw new Error("OpenAI upload completed without a file id.");
|
|
3487
|
+
}
|
|
3488
|
+
uploaded = await client.files.retrieve(fileId);
|
|
3489
|
+
}
|
|
3490
|
+
const file = toStoredFile(uploaded);
|
|
3491
|
+
const metadata = recordMetadata({
|
|
3492
|
+
file,
|
|
3493
|
+
filename: file.filename,
|
|
3494
|
+
bytes: file.bytes,
|
|
3495
|
+
mimeType: params.mimeType,
|
|
3496
|
+
sha256Hex: params.sha256Hex
|
|
3497
|
+
});
|
|
3498
|
+
recordUploadEvent({
|
|
3499
|
+
backend: "openai",
|
|
3500
|
+
mode,
|
|
3501
|
+
filename: metadata.filename,
|
|
3502
|
+
bytes: metadata.bytes,
|
|
3503
|
+
durationMs: Math.max(0, Date.now() - startedAtMs),
|
|
3504
|
+
mimeType: params.mimeType,
|
|
3505
|
+
fileId: metadata.file.id
|
|
3506
|
+
});
|
|
3507
|
+
return metadata;
|
|
3508
|
+
}
|
|
3509
|
+
async function uploadOpenAiFileFromPath(params) {
|
|
3510
|
+
const cacheKey = buildCacheKey(params.filename, params.mimeType, params.sha256Hex);
|
|
3511
|
+
const cached = filesState.openAiUploadCacheByKey.get(cacheKey);
|
|
3512
|
+
if (cached && isFresh(cached.file)) {
|
|
3513
|
+
return cached;
|
|
3514
|
+
}
|
|
3515
|
+
const client = getOpenAiClient();
|
|
3516
|
+
const startedAtMs = Date.now();
|
|
3517
|
+
let uploaded;
|
|
3518
|
+
let mode;
|
|
3519
|
+
if (params.bytes <= OPENAI_FILE_CREATE_MAX_BYTES) {
|
|
3520
|
+
mode = "files.create";
|
|
3521
|
+
const blob = await openAsBlob(params.filePath, { type: params.mimeType });
|
|
3522
|
+
uploaded = await client.files.create({
|
|
3523
|
+
file: new NodeFile([blob], params.filename, { type: params.mimeType }),
|
|
3524
|
+
purpose: params.purpose,
|
|
3525
|
+
expires_after: {
|
|
3526
|
+
anchor: "created_at",
|
|
3527
|
+
seconds: params.expiresAfterSeconds
|
|
3528
|
+
}
|
|
3529
|
+
});
|
|
3530
|
+
} else {
|
|
3531
|
+
mode = "uploads";
|
|
3532
|
+
const upload = await client.uploads.create({
|
|
3533
|
+
bytes: params.bytes,
|
|
3534
|
+
filename: params.filename,
|
|
3535
|
+
mime_type: params.mimeType,
|
|
3536
|
+
purpose: params.purpose
|
|
3537
|
+
});
|
|
3538
|
+
const partIds = [];
|
|
3539
|
+
const stream = createReadStream(params.filePath, {
|
|
3540
|
+
highWaterMark: OPENAI_UPLOAD_PART_MAX_BYTES
|
|
3541
|
+
});
|
|
3542
|
+
let partIndex = 0;
|
|
3543
|
+
for await (const chunk of stream) {
|
|
3544
|
+
const buffer = Buffer4.isBuffer(chunk) ? chunk : Buffer4.from(chunk);
|
|
3545
|
+
const uploadPart = await client.uploads.parts.create(upload.id, {
|
|
3546
|
+
data: new NodeFile(
|
|
3547
|
+
[new Uint8Array(buffer)],
|
|
3548
|
+
`${params.sha256Hex}.${partIndex.toString()}.part`,
|
|
3549
|
+
{
|
|
3550
|
+
type: params.mimeType
|
|
3551
|
+
}
|
|
3552
|
+
)
|
|
3553
|
+
});
|
|
3554
|
+
partIds.push(uploadPart.id);
|
|
3555
|
+
partIndex += 1;
|
|
3556
|
+
}
|
|
3557
|
+
const completed = await client.uploads.complete(upload.id, { part_ids: partIds });
|
|
3558
|
+
const fileId = completed.file?.id;
|
|
3559
|
+
if (!fileId) {
|
|
3560
|
+
throw new Error("OpenAI upload completed without a file id.");
|
|
3561
|
+
}
|
|
3562
|
+
uploaded = await client.files.retrieve(fileId);
|
|
3563
|
+
}
|
|
3564
|
+
const file = toStoredFile(uploaded);
|
|
3565
|
+
const metadata = recordMetadata({
|
|
3566
|
+
file,
|
|
3567
|
+
filename: file.filename,
|
|
3568
|
+
bytes: file.bytes,
|
|
3569
|
+
mimeType: params.mimeType,
|
|
3570
|
+
sha256Hex: params.sha256Hex
|
|
3571
|
+
});
|
|
3572
|
+
recordUploadEvent({
|
|
3573
|
+
backend: "openai",
|
|
3574
|
+
mode,
|
|
3575
|
+
filename: metadata.filename,
|
|
3576
|
+
bytes: metadata.bytes,
|
|
3577
|
+
durationMs: Math.max(0, Date.now() - startedAtMs),
|
|
3578
|
+
mimeType: params.mimeType,
|
|
3579
|
+
fileId: metadata.file.id
|
|
3580
|
+
});
|
|
3581
|
+
return metadata;
|
|
3582
|
+
}
|
|
3583
|
+
async function retrieveOpenAiFile(fileId) {
|
|
3584
|
+
const cached = filesState.metadataById.get(fileId);
|
|
3585
|
+
if (cached && isFresh(cached.file)) {
|
|
3586
|
+
return cached;
|
|
3587
|
+
}
|
|
3588
|
+
const client = getOpenAiClient();
|
|
3589
|
+
const retrieved = await client.files.retrieve(fileId);
|
|
3590
|
+
const file = toStoredFile(retrieved);
|
|
3591
|
+
return recordMetadata({
|
|
3592
|
+
file,
|
|
3593
|
+
filename: file.filename,
|
|
3594
|
+
bytes: file.bytes,
|
|
3595
|
+
mimeType: cached?.mimeType ?? resolveMimeType(file.filename, void 0),
|
|
3596
|
+
sha256Hex: cached?.sha256Hex,
|
|
3597
|
+
localPath: cached?.localPath
|
|
3598
|
+
});
|
|
3599
|
+
}
|
|
3600
|
+
function buildGeminiMirrorName(sha256Hex) {
|
|
3601
|
+
return `files/${sha256Hex.slice(0, 40)}`;
|
|
3602
|
+
}
|
|
3603
|
+
async function waitForGeminiFileActive(client, name) {
|
|
3604
|
+
const startedAt = Date.now();
|
|
3605
|
+
while (true) {
|
|
3606
|
+
const file = await client.files.get({ name });
|
|
3607
|
+
if (!file.state || file.state === "ACTIVE") {
|
|
3608
|
+
return;
|
|
3609
|
+
}
|
|
3610
|
+
if (file.state === "FAILED") {
|
|
3611
|
+
throw new Error(file.error?.message ?? `Gemini file ${name} failed processing.`);
|
|
3612
|
+
}
|
|
3613
|
+
if (Date.now() - startedAt >= GEMINI_FILE_POLL_TIMEOUT_MS) {
|
|
3614
|
+
throw new Error(`Timed out waiting for Gemini file ${name} to become active.`);
|
|
3615
|
+
}
|
|
3616
|
+
await new Promise((resolve) => setTimeout(resolve, GEMINI_FILE_POLL_INTERVAL_MS));
|
|
3617
|
+
}
|
|
3618
|
+
}
|
|
3619
|
+
function resolveVertexMirrorBucket() {
|
|
3620
|
+
const raw = process.env.VERTEX_GCS_BUCKET ?? process.env.LLM_VERTEX_GCS_BUCKET;
|
|
3621
|
+
const trimmed = raw?.trim();
|
|
3622
|
+
if (!trimmed) {
|
|
3623
|
+
throw new Error(
|
|
3624
|
+
"VERTEX_GCS_BUCKET must be set to use OpenAI-backed file ids with Vertex Gemini models."
|
|
3625
|
+
);
|
|
3626
|
+
}
|
|
3627
|
+
return trimmed.replace(/^gs:\/\//u, "").replace(/\/+$/u, "");
|
|
3628
|
+
}
|
|
3629
|
+
function resolveVertexMirrorPrefix() {
|
|
3630
|
+
const raw = process.env.VERTEX_GCS_PREFIX ?? process.env.LLM_VERTEX_GCS_PREFIX;
|
|
3631
|
+
const trimmed = raw?.trim().replace(/^\/+/u, "").replace(/\/+$/u, "");
|
|
3632
|
+
return trimmed ? `${trimmed}/` : "";
|
|
3633
|
+
}
|
|
3634
|
+
function getStorageClient() {
|
|
3635
|
+
if (filesState.storageClient) {
|
|
3636
|
+
return filesState.storageClient;
|
|
3637
|
+
}
|
|
3638
|
+
const serviceAccount = getGoogleServiceAccount();
|
|
3639
|
+
filesState.storageClient = new Storage({
|
|
3640
|
+
projectId: serviceAccount.projectId,
|
|
3641
|
+
credentials: {
|
|
3642
|
+
client_email: serviceAccount.clientEmail,
|
|
3643
|
+
private_key: serviceAccount.privateKey
|
|
3644
|
+
}
|
|
3645
|
+
});
|
|
3646
|
+
return filesState.storageClient;
|
|
3647
|
+
}
|
|
3648
|
+
function getGeminiMirrorClient() {
|
|
3649
|
+
if (!filesState.geminiClientPromise) {
|
|
3650
|
+
filesState.geminiClientPromise = getGeminiClient();
|
|
3651
|
+
}
|
|
3652
|
+
return filesState.geminiClientPromise;
|
|
3653
|
+
}
|
|
3654
|
+
async function materializeOpenAiFile(fileId) {
|
|
3655
|
+
const cachedPromise = filesState.materializedById.get(fileId);
|
|
3656
|
+
if (cachedPromise) {
|
|
3657
|
+
return await cachedPromise;
|
|
3658
|
+
}
|
|
3659
|
+
const promise = (async () => {
|
|
3660
|
+
const metadata = await retrieveOpenAiFile(fileId);
|
|
3661
|
+
if (metadata.localPath && metadata.sha256Hex && metadata.mimeType) {
|
|
3662
|
+
return {
|
|
3663
|
+
file: metadata.file,
|
|
3664
|
+
filename: metadata.filename,
|
|
3665
|
+
bytes: metadata.bytes,
|
|
3666
|
+
mimeType: metadata.mimeType,
|
|
3667
|
+
sha256Hex: metadata.sha256Hex,
|
|
3668
|
+
localPath: metadata.localPath
|
|
3669
|
+
};
|
|
3670
|
+
}
|
|
3671
|
+
await mkdir2(FILES_TEMP_ROOT, { recursive: true });
|
|
3672
|
+
const tempDir = await mkdtemp(
|
|
3673
|
+
path4.join(FILES_TEMP_ROOT, `${fileId.replace(/[^a-z0-9_-]/giu, "")}-`)
|
|
3674
|
+
);
|
|
3675
|
+
const localPath = path4.join(tempDir, normaliseFilename(metadata.filename, `${fileId}.bin`));
|
|
3676
|
+
const response = await getOpenAiClient().files.content(fileId);
|
|
3677
|
+
if (!response.ok) {
|
|
3678
|
+
throw new Error(
|
|
3679
|
+
`Failed to download OpenAI file ${fileId}: ${response.status} ${response.statusText}`
|
|
3680
|
+
);
|
|
3681
|
+
}
|
|
3682
|
+
const responseMimeType = response.headers.get("content-type")?.trim() || void 0;
|
|
3683
|
+
const mimeType = resolveMimeType(metadata.filename, responseMimeType);
|
|
3684
|
+
const hash = createHash("sha256");
|
|
3685
|
+
let bytes = 0;
|
|
3686
|
+
if (response.body) {
|
|
3687
|
+
const source = Readable.fromWeb(response.body);
|
|
3688
|
+
const writable = createWriteStream(localPath, { flags: "wx" });
|
|
3689
|
+
source.on("data", (chunk) => {
|
|
3690
|
+
const buffer = Buffer4.isBuffer(chunk) ? chunk : Buffer4.from(chunk);
|
|
3691
|
+
hash.update(buffer);
|
|
3692
|
+
bytes += buffer.byteLength;
|
|
3693
|
+
});
|
|
3694
|
+
await pipeline(source, writable);
|
|
3695
|
+
} else {
|
|
3696
|
+
const buffer = Buffer4.from(await response.arrayBuffer());
|
|
3697
|
+
hash.update(buffer);
|
|
3698
|
+
bytes = buffer.byteLength;
|
|
3699
|
+
await writeFile2(localPath, buffer);
|
|
3700
|
+
}
|
|
3701
|
+
const sha256Hex = hash.digest("hex");
|
|
3702
|
+
const updated = recordMetadata({
|
|
3703
|
+
file: metadata.file,
|
|
3704
|
+
filename: metadata.filename,
|
|
3705
|
+
bytes: bytes || metadata.bytes,
|
|
3706
|
+
mimeType,
|
|
3707
|
+
sha256Hex,
|
|
3708
|
+
localPath
|
|
3709
|
+
});
|
|
3710
|
+
return {
|
|
3711
|
+
file: updated.file,
|
|
3712
|
+
filename: updated.filename,
|
|
3713
|
+
bytes: updated.bytes,
|
|
3714
|
+
mimeType: updated.mimeType ?? mimeType,
|
|
3715
|
+
sha256Hex,
|
|
3716
|
+
localPath
|
|
3717
|
+
};
|
|
3718
|
+
})();
|
|
3719
|
+
filesState.materializedById.set(fileId, promise);
|
|
3720
|
+
try {
|
|
3721
|
+
return await promise;
|
|
3722
|
+
} catch (error) {
|
|
3723
|
+
filesState.materializedById.delete(fileId);
|
|
3724
|
+
throw error;
|
|
3725
|
+
}
|
|
3726
|
+
}
|
|
3727
|
+
async function ensureGeminiFileMirror(fileId) {
|
|
3728
|
+
const cached = filesState.geminiMirrorById.get(fileId);
|
|
3729
|
+
if (cached) {
|
|
3730
|
+
return cached;
|
|
3731
|
+
}
|
|
3732
|
+
const materialized = await materializeOpenAiFile(fileId);
|
|
3733
|
+
const client = await getGeminiMirrorClient();
|
|
3734
|
+
const name = buildGeminiMirrorName(materialized.sha256Hex);
|
|
3735
|
+
try {
|
|
3736
|
+
const existing = await client.files.get({ name });
|
|
3737
|
+
if (existing.name && existing.uri && existing.mimeType) {
|
|
3738
|
+
const mirror2 = {
|
|
3739
|
+
openAiFileId: fileId,
|
|
3740
|
+
name: existing.name,
|
|
3741
|
+
uri: existing.uri,
|
|
3742
|
+
mimeType: existing.mimeType,
|
|
3743
|
+
displayName: existing.displayName ?? materialized.filename
|
|
3744
|
+
};
|
|
3745
|
+
filesState.geminiMirrorById.set(fileId, mirror2);
|
|
3746
|
+
return mirror2;
|
|
3747
|
+
}
|
|
3748
|
+
} catch {
|
|
3749
|
+
}
|
|
3750
|
+
const startedAtMs = Date.now();
|
|
3751
|
+
const uploaded = await client.files.upload({
|
|
3752
|
+
file: materialized.localPath,
|
|
3753
|
+
config: {
|
|
3754
|
+
name,
|
|
3755
|
+
mimeType: materialized.mimeType,
|
|
3756
|
+
displayName: materialized.filename
|
|
3757
|
+
}
|
|
3758
|
+
});
|
|
3759
|
+
if (uploaded.name && uploaded.state && uploaded.state !== "ACTIVE") {
|
|
3760
|
+
await waitForGeminiFileActive(client, uploaded.name);
|
|
3761
|
+
}
|
|
3762
|
+
const resolved = await client.files.get({ name: uploaded.name ?? name });
|
|
3763
|
+
if (!resolved.name || !resolved.uri || !resolved.mimeType) {
|
|
3764
|
+
throw new Error("Gemini file upload completed without a usable URI.");
|
|
3765
|
+
}
|
|
3766
|
+
const mirror = {
|
|
3767
|
+
openAiFileId: fileId,
|
|
3768
|
+
name: resolved.name,
|
|
3769
|
+
uri: resolved.uri,
|
|
3770
|
+
mimeType: resolved.mimeType,
|
|
3771
|
+
displayName: resolved.displayName ?? materialized.filename
|
|
3772
|
+
};
|
|
3773
|
+
filesState.geminiMirrorById.set(fileId, mirror);
|
|
3774
|
+
recordUploadEvent({
|
|
3775
|
+
backend: "gemini",
|
|
3776
|
+
mode: "mirror",
|
|
3777
|
+
filename: materialized.filename,
|
|
3778
|
+
bytes: materialized.bytes,
|
|
3779
|
+
durationMs: Math.max(0, Date.now() - startedAtMs),
|
|
3780
|
+
mimeType: materialized.mimeType,
|
|
3781
|
+
fileId,
|
|
3782
|
+
mirrorId: mirror.name,
|
|
3783
|
+
fileUri: mirror.uri
|
|
3784
|
+
});
|
|
3785
|
+
return mirror;
|
|
3786
|
+
}
|
|
3787
|
+
async function ensureVertexFileMirror(fileId) {
|
|
3788
|
+
const cached = filesState.vertexMirrorById.get(fileId);
|
|
3789
|
+
if (cached) {
|
|
3790
|
+
return cached;
|
|
3791
|
+
}
|
|
3792
|
+
const materialized = await materializeOpenAiFile(fileId);
|
|
3793
|
+
const bucketName = resolveVertexMirrorBucket();
|
|
3794
|
+
const prefix = resolveVertexMirrorPrefix();
|
|
3795
|
+
const extension = mime.getExtension(materialized.mimeType) ?? path4.extname(materialized.filename).replace(/^\./u, "") ?? "bin";
|
|
3796
|
+
const objectName = `${prefix}${materialized.sha256Hex}.${extension}`;
|
|
3797
|
+
const file = getStorageClient().bucket(bucketName).file(objectName);
|
|
3798
|
+
let uploaded = false;
|
|
3799
|
+
const startedAtMs = Date.now();
|
|
3800
|
+
try {
|
|
3801
|
+
await file.getMetadata();
|
|
3802
|
+
} catch (error) {
|
|
3803
|
+
const code = error.code;
|
|
3804
|
+
if (code !== 404 && code !== "404") {
|
|
3805
|
+
throw error;
|
|
3806
|
+
}
|
|
3807
|
+
try {
|
|
3808
|
+
await pipeline(
|
|
3809
|
+
createReadStream(materialized.localPath),
|
|
3810
|
+
file.createWriteStream({
|
|
3811
|
+
resumable: materialized.bytes >= 10 * 1024 * 1024,
|
|
3812
|
+
preconditionOpts: { ifGenerationMatch: 0 },
|
|
3813
|
+
metadata: {
|
|
3814
|
+
contentType: materialized.mimeType,
|
|
3815
|
+
customTime: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3816
|
+
metadata: {
|
|
3817
|
+
filename: materialized.filename,
|
|
3818
|
+
sha256: materialized.sha256Hex,
|
|
3819
|
+
expiresAt: new Date(Date.now() + DEFAULT_FILE_TTL_SECONDS * 1e3).toISOString()
|
|
3820
|
+
}
|
|
3821
|
+
}
|
|
3822
|
+
})
|
|
3823
|
+
);
|
|
3824
|
+
uploaded = true;
|
|
3825
|
+
} catch (uploadError) {
|
|
3826
|
+
const uploadCode = uploadError.code;
|
|
3827
|
+
if (uploadCode !== 412 && uploadCode !== "412") {
|
|
3828
|
+
throw uploadError;
|
|
3829
|
+
}
|
|
3830
|
+
}
|
|
3831
|
+
}
|
|
3832
|
+
const mirror = {
|
|
3833
|
+
openAiFileId: fileId,
|
|
3834
|
+
bucket: bucketName,
|
|
3835
|
+
objectName,
|
|
3836
|
+
fileUri: `gs://${bucketName}/${objectName}`,
|
|
3837
|
+
mimeType: materialized.mimeType
|
|
3838
|
+
};
|
|
3839
|
+
filesState.vertexMirrorById.set(fileId, mirror);
|
|
3840
|
+
if (uploaded) {
|
|
3841
|
+
recordUploadEvent({
|
|
3842
|
+
backend: "vertex",
|
|
3843
|
+
mode: "mirror",
|
|
3844
|
+
filename: materialized.filename,
|
|
3845
|
+
bytes: materialized.bytes,
|
|
3846
|
+
durationMs: Math.max(0, Date.now() - startedAtMs),
|
|
3847
|
+
mimeType: materialized.mimeType,
|
|
3848
|
+
fileId,
|
|
3849
|
+
mirrorId: mirror.objectName,
|
|
3850
|
+
fileUri: mirror.fileUri
|
|
3851
|
+
});
|
|
3852
|
+
}
|
|
3853
|
+
return mirror;
|
|
3854
|
+
}
|
|
3855
|
+
async function filesCreate(params) {
|
|
3856
|
+
const purpose = params.purpose ?? "user_data";
|
|
3857
|
+
const expiresAfterSeconds = params.expiresAfterSeconds ?? DEFAULT_FILE_TTL_SECONDS;
|
|
3858
|
+
if ("path" in params) {
|
|
3859
|
+
const filePath = path4.resolve(params.path);
|
|
3860
|
+
const info = await stat(filePath);
|
|
3861
|
+
const filename2 = normaliseFilename(params.filename, path4.basename(filePath));
|
|
3862
|
+
const mimeType2 = resolveMimeType(filename2, params.mimeType);
|
|
3863
|
+
const sha256Hex2 = await computeFileSha256Hex(filePath);
|
|
3864
|
+
const uploaded2 = await uploadOpenAiFileFromPath({
|
|
3865
|
+
filePath,
|
|
3866
|
+
filename: filename2,
|
|
3867
|
+
mimeType: mimeType2,
|
|
3868
|
+
purpose,
|
|
3869
|
+
expiresAfterSeconds,
|
|
3870
|
+
sha256Hex: sha256Hex2,
|
|
3871
|
+
bytes: info.size
|
|
3872
|
+
});
|
|
3873
|
+
return uploaded2.file;
|
|
3874
|
+
}
|
|
3875
|
+
const filename = normaliseFilename(params.filename);
|
|
3876
|
+
const bytes = toBuffer(params.data);
|
|
3877
|
+
const mimeType = resolveMimeType(filename, params.mimeType, "text/plain");
|
|
3878
|
+
const sha256Hex = computeSha256Hex(bytes);
|
|
3879
|
+
const uploaded = await uploadOpenAiFileFromBytes({
|
|
3880
|
+
bytes,
|
|
3881
|
+
filename,
|
|
3882
|
+
mimeType,
|
|
3883
|
+
purpose,
|
|
3884
|
+
expiresAfterSeconds,
|
|
3885
|
+
sha256Hex
|
|
3886
|
+
});
|
|
3887
|
+
return uploaded.file;
|
|
3888
|
+
}
|
|
3889
|
+
async function filesRetrieve(fileId) {
|
|
3890
|
+
return (await retrieveOpenAiFile(fileId)).file;
|
|
3891
|
+
}
|
|
3892
|
+
async function filesDelete(fileId) {
|
|
3893
|
+
const cachedGemini = filesState.geminiMirrorById.get(fileId);
|
|
3894
|
+
if (cachedGemini) {
|
|
3895
|
+
try {
|
|
3896
|
+
const client = await getGeminiMirrorClient();
|
|
3897
|
+
await client.files.delete({ name: cachedGemini.name });
|
|
3898
|
+
} catch {
|
|
3899
|
+
}
|
|
3900
|
+
filesState.geminiMirrorById.delete(fileId);
|
|
3901
|
+
}
|
|
3902
|
+
const cachedVertex = filesState.vertexMirrorById.get(fileId);
|
|
3903
|
+
if (cachedVertex) {
|
|
3904
|
+
try {
|
|
3905
|
+
await getStorageClient().bucket(cachedVertex.bucket).file(cachedVertex.objectName).delete({ ignoreNotFound: true });
|
|
3906
|
+
} catch {
|
|
3907
|
+
}
|
|
3908
|
+
filesState.vertexMirrorById.delete(fileId);
|
|
3909
|
+
}
|
|
3910
|
+
const cachedMaterialized = filesState.metadataById.get(fileId)?.localPath;
|
|
3911
|
+
if (cachedMaterialized) {
|
|
3912
|
+
try {
|
|
3913
|
+
await unlink(cachedMaterialized);
|
|
3914
|
+
} catch {
|
|
3915
|
+
}
|
|
3916
|
+
}
|
|
3917
|
+
const response = await getOpenAiClient().files.delete(fileId);
|
|
3918
|
+
filesState.metadataById.delete(fileId);
|
|
3919
|
+
filesState.materializedById.delete(fileId);
|
|
3920
|
+
return {
|
|
3921
|
+
id: response.id,
|
|
3922
|
+
deleted: response.deleted,
|
|
3923
|
+
object: "file"
|
|
3924
|
+
};
|
|
3925
|
+
}
|
|
3926
|
+
async function filesContent(fileId) {
|
|
3927
|
+
return await getOpenAiClient().files.content(fileId);
|
|
3928
|
+
}
|
|
3929
|
+
async function getCanonicalFileMetadata(fileId) {
|
|
3930
|
+
const metadata = await retrieveOpenAiFile(fileId);
|
|
3931
|
+
const mimeType = metadata.mimeType ?? resolveMimeType(metadata.filename, void 0);
|
|
3932
|
+
const updated = metadata.mimeType === mimeType ? metadata : recordMetadata({
|
|
3933
|
+
...metadata,
|
|
3934
|
+
mimeType
|
|
3935
|
+
});
|
|
3936
|
+
return {
|
|
3937
|
+
...updated,
|
|
3938
|
+
mimeType
|
|
3939
|
+
};
|
|
3940
|
+
}
|
|
3941
|
+
var files = {
|
|
3942
|
+
create: filesCreate,
|
|
3943
|
+
retrieve: filesRetrieve,
|
|
3944
|
+
delete: filesDelete,
|
|
3945
|
+
content: filesContent
|
|
3946
|
+
};
|
|
3947
|
+
|
|
3244
3948
|
// src/llm.ts
|
|
3245
3949
|
var toolCallContextStorage = getRuntimeSingleton(
|
|
3246
3950
|
/* @__PURE__ */ Symbol.for("@ljoukov/llm.toolCallContextStorage"),
|
|
3247
|
-
() => new
|
|
3951
|
+
() => new AsyncLocalStorage3()
|
|
3248
3952
|
);
|
|
3249
3953
|
function getCurrentToolCallContext() {
|
|
3250
3954
|
return toolCallContextStorage.getStore() ?? null;
|
|
3251
3955
|
}
|
|
3956
|
+
var INLINE_ATTACHMENT_FILENAME_SYMBOL = /* @__PURE__ */ Symbol.for("@ljoukov/llm.inlineAttachmentFilename");
|
|
3957
|
+
var INLINE_ATTACHMENT_PROMPT_THRESHOLD_BYTES = 20 * 1024 * 1024;
|
|
3958
|
+
var TOOL_OUTPUT_SPILL_THRESHOLD_BYTES = 1 * 1024 * 1024;
|
|
3252
3959
|
var LLM_TEXT_MODEL_IDS = [
|
|
3253
3960
|
...OPENAI_MODEL_IDS,
|
|
3254
3961
|
...CHATGPT_MODEL_IDS,
|
|
@@ -3526,6 +4233,52 @@ function isJsonSchemaObject(schema) {
|
|
|
3526
4233
|
}
|
|
3527
4234
|
return false;
|
|
3528
4235
|
}
|
|
4236
|
+
var CANONICAL_GEMINI_FILE_URI_PREFIX = "openai://file/";
|
|
4237
|
+
function buildCanonicalGeminiFileUri(fileId) {
|
|
4238
|
+
return `${CANONICAL_GEMINI_FILE_URI_PREFIX}${fileId}`;
|
|
4239
|
+
}
|
|
4240
|
+
function parseCanonicalGeminiFileId(fileUri) {
|
|
4241
|
+
if (!fileUri?.startsWith(CANONICAL_GEMINI_FILE_URI_PREFIX)) {
|
|
4242
|
+
return void 0;
|
|
4243
|
+
}
|
|
4244
|
+
const fileId = fileUri.slice(CANONICAL_GEMINI_FILE_URI_PREFIX.length).trim();
|
|
4245
|
+
return fileId.length > 0 ? fileId : void 0;
|
|
4246
|
+
}
|
|
4247
|
+
function cloneContentPart(part) {
|
|
4248
|
+
switch (part.type) {
|
|
4249
|
+
case "text":
|
|
4250
|
+
return {
|
|
4251
|
+
type: "text",
|
|
4252
|
+
text: part.text,
|
|
4253
|
+
thought: part.thought === true ? true : void 0
|
|
4254
|
+
};
|
|
4255
|
+
case "inlineData":
|
|
4256
|
+
return {
|
|
4257
|
+
type: "inlineData",
|
|
4258
|
+
data: part.data,
|
|
4259
|
+
mimeType: part.mimeType,
|
|
4260
|
+
filename: part.filename
|
|
4261
|
+
};
|
|
4262
|
+
case "input_image":
|
|
4263
|
+
return {
|
|
4264
|
+
type: "input_image",
|
|
4265
|
+
image_url: part.image_url ?? void 0,
|
|
4266
|
+
file_id: part.file_id ?? void 0,
|
|
4267
|
+
detail: part.detail,
|
|
4268
|
+
filename: part.filename ?? void 0
|
|
4269
|
+
};
|
|
4270
|
+
case "input_file":
|
|
4271
|
+
return {
|
|
4272
|
+
type: "input_file",
|
|
4273
|
+
file_data: part.file_data ?? void 0,
|
|
4274
|
+
file_id: part.file_id ?? void 0,
|
|
4275
|
+
file_url: part.file_url ?? void 0,
|
|
4276
|
+
filename: part.filename ?? void 0
|
|
4277
|
+
};
|
|
4278
|
+
default:
|
|
4279
|
+
return part;
|
|
4280
|
+
}
|
|
4281
|
+
}
|
|
3529
4282
|
function sanitisePartForLogging(part) {
|
|
3530
4283
|
switch (part.type) {
|
|
3531
4284
|
case "text":
|
|
@@ -3537,16 +4290,33 @@ function sanitisePartForLogging(part) {
|
|
|
3537
4290
|
case "inlineData": {
|
|
3538
4291
|
let omittedBytes;
|
|
3539
4292
|
try {
|
|
3540
|
-
omittedBytes =
|
|
4293
|
+
omittedBytes = Buffer5.from(part.data, "base64").byteLength;
|
|
3541
4294
|
} catch {
|
|
3542
|
-
omittedBytes =
|
|
4295
|
+
omittedBytes = Buffer5.byteLength(part.data, "utf8");
|
|
3543
4296
|
}
|
|
3544
4297
|
return {
|
|
3545
4298
|
type: "inlineData",
|
|
3546
4299
|
mimeType: part.mimeType,
|
|
4300
|
+
filename: part.filename,
|
|
3547
4301
|
data: `[omitted:${omittedBytes}b]`
|
|
3548
4302
|
};
|
|
3549
4303
|
}
|
|
4304
|
+
case "input_image":
|
|
4305
|
+
return {
|
|
4306
|
+
type: "input_image",
|
|
4307
|
+
file_id: part.file_id ?? void 0,
|
|
4308
|
+
filename: part.filename ?? void 0,
|
|
4309
|
+
detail: part.detail ?? void 0,
|
|
4310
|
+
image_url: typeof part.image_url === "string" ? part.image_url.startsWith("data:") ? "[omitted:data-url]" : part.image_url : void 0
|
|
4311
|
+
};
|
|
4312
|
+
case "input_file":
|
|
4313
|
+
return {
|
|
4314
|
+
type: "input_file",
|
|
4315
|
+
file_id: part.file_id ?? void 0,
|
|
4316
|
+
filename: part.filename ?? void 0,
|
|
4317
|
+
file_url: typeof part.file_url === "string" ? part.file_url.startsWith("data:") ? "[omitted:data-url]" : part.file_url : void 0,
|
|
4318
|
+
file_data: typeof part.file_data === "string" ? `[omitted:${Buffer5.byteLength(part.file_data, "utf8")}b]` : void 0
|
|
4319
|
+
};
|
|
3550
4320
|
default:
|
|
3551
4321
|
return "[unknown part]";
|
|
3552
4322
|
}
|
|
@@ -3567,12 +4337,17 @@ function convertGooglePartsToLlmParts(parts) {
|
|
|
3567
4337
|
result.push({
|
|
3568
4338
|
type: "inlineData",
|
|
3569
4339
|
data: inline.data,
|
|
3570
|
-
mimeType: inline.mimeType
|
|
4340
|
+
mimeType: inline.mimeType,
|
|
4341
|
+
filename: inline.displayName
|
|
3571
4342
|
});
|
|
3572
4343
|
continue;
|
|
3573
4344
|
}
|
|
3574
4345
|
if (part.fileData?.fileUri) {
|
|
3575
|
-
|
|
4346
|
+
result.push({
|
|
4347
|
+
type: "input_file",
|
|
4348
|
+
file_url: part.fileData.fileUri,
|
|
4349
|
+
filename: part.fileData.displayName
|
|
4350
|
+
});
|
|
3576
4351
|
}
|
|
3577
4352
|
}
|
|
3578
4353
|
return result;
|
|
@@ -3604,13 +4379,86 @@ function toGeminiPart(part) {
|
|
|
3604
4379
|
text: part.text,
|
|
3605
4380
|
thought: part.thought === true ? true : void 0
|
|
3606
4381
|
};
|
|
3607
|
-
case "inlineData":
|
|
4382
|
+
case "inlineData": {
|
|
4383
|
+
const inlineData = {
|
|
4384
|
+
data: part.data,
|
|
4385
|
+
mimeType: part.mimeType
|
|
4386
|
+
};
|
|
4387
|
+
setInlineAttachmentFilename(inlineData, part.filename);
|
|
3608
4388
|
return {
|
|
3609
4389
|
inlineData: {
|
|
3610
|
-
|
|
3611
|
-
|
|
4390
|
+
...inlineData
|
|
4391
|
+
}
|
|
4392
|
+
};
|
|
4393
|
+
}
|
|
4394
|
+
case "input_image": {
|
|
4395
|
+
if (part.file_id) {
|
|
4396
|
+
return {
|
|
4397
|
+
fileData: {
|
|
4398
|
+
fileUri: buildCanonicalGeminiFileUri(part.file_id),
|
|
4399
|
+
mimeType: inferToolOutputMimeTypeFromFilename(part.filename) ?? "application/octet-stream",
|
|
4400
|
+
displayName: part.filename ?? void 0
|
|
4401
|
+
}
|
|
4402
|
+
};
|
|
4403
|
+
}
|
|
4404
|
+
if (typeof part.image_url !== "string" || part.image_url.trim().length === 0) {
|
|
4405
|
+
throw new Error("input_image requires image_url or file_id.");
|
|
4406
|
+
}
|
|
4407
|
+
const parsed = parseDataUrlPayload(part.image_url);
|
|
4408
|
+
if (parsed) {
|
|
4409
|
+
const geminiPart = createPartFromBase64(parsed.dataBase64, parsed.mimeType);
|
|
4410
|
+
if (part.filename && geminiPart.inlineData) {
|
|
4411
|
+
geminiPart.inlineData.displayName = part.filename;
|
|
4412
|
+
}
|
|
4413
|
+
return geminiPart;
|
|
4414
|
+
}
|
|
4415
|
+
return {
|
|
4416
|
+
fileData: {
|
|
4417
|
+
fileUri: part.image_url,
|
|
4418
|
+
mimeType: inferToolOutputMimeTypeFromFilename(part.filename) ?? "application/octet-stream",
|
|
4419
|
+
displayName: part.filename ?? void 0
|
|
3612
4420
|
}
|
|
3613
4421
|
};
|
|
4422
|
+
}
|
|
4423
|
+
case "input_file": {
|
|
4424
|
+
if (part.file_id) {
|
|
4425
|
+
return {
|
|
4426
|
+
fileData: {
|
|
4427
|
+
fileUri: buildCanonicalGeminiFileUri(part.file_id),
|
|
4428
|
+
mimeType: inferToolOutputMimeTypeFromFilename(part.filename) ?? "application/octet-stream",
|
|
4429
|
+
displayName: part.filename ?? void 0
|
|
4430
|
+
}
|
|
4431
|
+
};
|
|
4432
|
+
}
|
|
4433
|
+
if (typeof part.file_data === "string" && part.file_data.trim().length > 0) {
|
|
4434
|
+
const geminiPart = createPartFromBase64(
|
|
4435
|
+
part.file_data,
|
|
4436
|
+
inferToolOutputMimeTypeFromFilename(part.filename) ?? "application/octet-stream"
|
|
4437
|
+
);
|
|
4438
|
+
if (part.filename && geminiPart.inlineData) {
|
|
4439
|
+
geminiPart.inlineData.displayName = part.filename;
|
|
4440
|
+
}
|
|
4441
|
+
return geminiPart;
|
|
4442
|
+
}
|
|
4443
|
+
if (typeof part.file_url === "string" && part.file_url.trim().length > 0) {
|
|
4444
|
+
const parsed = parseDataUrlPayload(part.file_url);
|
|
4445
|
+
if (parsed) {
|
|
4446
|
+
const geminiPart = createPartFromBase64(parsed.dataBase64, parsed.mimeType);
|
|
4447
|
+
if (part.filename && geminiPart.inlineData) {
|
|
4448
|
+
geminiPart.inlineData.displayName = part.filename;
|
|
4449
|
+
}
|
|
4450
|
+
return geminiPart;
|
|
4451
|
+
}
|
|
4452
|
+
return {
|
|
4453
|
+
fileData: {
|
|
4454
|
+
fileUri: part.file_url,
|
|
4455
|
+
mimeType: inferToolOutputMimeTypeFromFilename(part.filename) ?? "application/octet-stream",
|
|
4456
|
+
displayName: part.filename ?? void 0
|
|
4457
|
+
}
|
|
4458
|
+
};
|
|
4459
|
+
}
|
|
4460
|
+
throw new Error("input_file requires file_id, file_data, or file_url.");
|
|
4461
|
+
}
|
|
3614
4462
|
default:
|
|
3615
4463
|
throw new Error("Unsupported LLM content part");
|
|
3616
4464
|
}
|
|
@@ -3707,6 +4555,14 @@ function isInlineImageMime(mimeType) {
|
|
|
3707
4555
|
}
|
|
3708
4556
|
function guessInlineDataFilename(mimeType) {
|
|
3709
4557
|
switch (mimeType) {
|
|
4558
|
+
case "image/jpeg":
|
|
4559
|
+
return "image.jpg";
|
|
4560
|
+
case "image/png":
|
|
4561
|
+
return "image.png";
|
|
4562
|
+
case "image/webp":
|
|
4563
|
+
return "image.webp";
|
|
4564
|
+
case "image/gif":
|
|
4565
|
+
return "image.gif";
|
|
3710
4566
|
case "application/pdf":
|
|
3711
4567
|
return "document.pdf";
|
|
3712
4568
|
case "application/json":
|
|
@@ -3719,14 +4575,269 @@ function guessInlineDataFilename(mimeType) {
|
|
|
3719
4575
|
return "attachment.bin";
|
|
3720
4576
|
}
|
|
3721
4577
|
}
|
|
3722
|
-
function
|
|
4578
|
+
function normaliseAttachmentFilename(value, fallback) {
|
|
4579
|
+
const trimmed = value?.trim();
|
|
4580
|
+
if (!trimmed) {
|
|
4581
|
+
return fallback;
|
|
4582
|
+
}
|
|
4583
|
+
const basename = path5.basename(trimmed).replace(/[^\w.-]+/g, "-");
|
|
4584
|
+
return basename.length > 0 ? basename : fallback;
|
|
4585
|
+
}
|
|
4586
|
+
function setInlineAttachmentFilename(target, filename) {
|
|
4587
|
+
const normalized = filename?.trim();
|
|
4588
|
+
if (!normalized) {
|
|
4589
|
+
return;
|
|
4590
|
+
}
|
|
4591
|
+
target[INLINE_ATTACHMENT_FILENAME_SYMBOL] = normalized;
|
|
4592
|
+
}
|
|
4593
|
+
function getInlineAttachmentFilename(target) {
|
|
4594
|
+
if (!target || typeof target !== "object") {
|
|
4595
|
+
return void 0;
|
|
4596
|
+
}
|
|
4597
|
+
const value = target[INLINE_ATTACHMENT_FILENAME_SYMBOL];
|
|
4598
|
+
return typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
|
|
4599
|
+
}
|
|
4600
|
+
function estimateInlinePayloadBytes(value) {
|
|
4601
|
+
return Buffer5.byteLength(value, "utf8");
|
|
4602
|
+
}
|
|
4603
|
+
function isOpenAiNativeContentItem(value) {
|
|
4604
|
+
return !!value && typeof value === "object" && typeof value.type === "string";
|
|
4605
|
+
}
|
|
4606
|
+
function estimateOpenAiInlinePromptBytes(input) {
|
|
4607
|
+
let total = 0;
|
|
4608
|
+
const visitItems = (items) => {
|
|
4609
|
+
for (const item of items) {
|
|
4610
|
+
if (!item || typeof item !== "object") {
|
|
4611
|
+
continue;
|
|
4612
|
+
}
|
|
4613
|
+
if (Array.isArray(item.content)) {
|
|
4614
|
+
visitItems(item.content);
|
|
4615
|
+
}
|
|
4616
|
+
if (Array.isArray(item.output)) {
|
|
4617
|
+
visitItems(item.output);
|
|
4618
|
+
}
|
|
4619
|
+
if (!isOpenAiNativeContentItem(item)) {
|
|
4620
|
+
continue;
|
|
4621
|
+
}
|
|
4622
|
+
if (item.type === "input_image" && typeof item.image_url === "string" && item.image_url.trim().toLowerCase().startsWith("data:")) {
|
|
4623
|
+
total += estimateInlinePayloadBytes(item.image_url);
|
|
4624
|
+
}
|
|
4625
|
+
if (item.type === "input_file" && typeof item.file_data === "string" && item.file_data.trim().length > 0) {
|
|
4626
|
+
total += estimateInlinePayloadBytes(item.file_data);
|
|
4627
|
+
}
|
|
4628
|
+
if (item.type === "input_file" && typeof item.file_url === "string" && item.file_url.trim().toLowerCase().startsWith("data:")) {
|
|
4629
|
+
total += estimateInlinePayloadBytes(item.file_url);
|
|
4630
|
+
}
|
|
4631
|
+
}
|
|
4632
|
+
};
|
|
4633
|
+
visitItems(input);
|
|
4634
|
+
return total;
|
|
4635
|
+
}
|
|
4636
|
+
async function storeCanonicalPromptFile(options) {
|
|
4637
|
+
const file = await runWithFileUploadSource("prompt_inline_offload", async () => {
|
|
4638
|
+
return await filesCreate({
|
|
4639
|
+
data: options.bytes,
|
|
4640
|
+
filename: options.filename,
|
|
4641
|
+
mimeType: options.mimeType,
|
|
4642
|
+
expiresAfterSeconds: DEFAULT_FILE_TTL_SECONDS
|
|
4643
|
+
});
|
|
4644
|
+
});
|
|
4645
|
+
return {
|
|
4646
|
+
fileId: file.id,
|
|
4647
|
+
filename: file.filename,
|
|
4648
|
+
mimeType: options.mimeType
|
|
4649
|
+
};
|
|
4650
|
+
}
|
|
4651
|
+
async function prepareOpenAiPromptContentItem(item) {
|
|
4652
|
+
if (!isOpenAiNativeContentItem(item)) {
|
|
4653
|
+
return item;
|
|
4654
|
+
}
|
|
4655
|
+
if (item.type === "input_image" && typeof item.image_url === "string" && item.image_url.trim().toLowerCase().startsWith("data:")) {
|
|
4656
|
+
const parsed = parseDataUrlPayload(item.image_url);
|
|
4657
|
+
if (!parsed) {
|
|
4658
|
+
return item;
|
|
4659
|
+
}
|
|
4660
|
+
const uploaded = await storeCanonicalPromptFile({
|
|
4661
|
+
bytes: parsed.bytes,
|
|
4662
|
+
mimeType: parsed.mimeType ?? "application/octet-stream",
|
|
4663
|
+
filename: normaliseAttachmentFilename(
|
|
4664
|
+
getInlineAttachmentFilename(item),
|
|
4665
|
+
guessInlineDataFilename(parsed.mimeType)
|
|
4666
|
+
)
|
|
4667
|
+
});
|
|
4668
|
+
return {
|
|
4669
|
+
type: "input_image",
|
|
4670
|
+
detail: item.detail === "high" || item.detail === "low" ? item.detail : "auto",
|
|
4671
|
+
file_id: uploaded.fileId
|
|
4672
|
+
};
|
|
4673
|
+
}
|
|
4674
|
+
if (item.type !== "input_file" || item.file_id) {
|
|
4675
|
+
return item;
|
|
4676
|
+
}
|
|
4677
|
+
if (typeof item.file_data === "string" && item.file_data.trim().length > 0) {
|
|
4678
|
+
const filename = normaliseAttachmentFilename(
|
|
4679
|
+
typeof item.filename === "string" ? item.filename : void 0,
|
|
4680
|
+
guessInlineDataFilename(void 0)
|
|
4681
|
+
);
|
|
4682
|
+
const mimeType = inferToolOutputMimeTypeFromFilename(filename) ?? "application/octet-stream";
|
|
4683
|
+
const uploaded = await storeCanonicalPromptFile({
|
|
4684
|
+
bytes: decodeInlineDataBuffer(item.file_data),
|
|
4685
|
+
mimeType,
|
|
4686
|
+
filename
|
|
4687
|
+
});
|
|
4688
|
+
return { type: "input_file", file_id: uploaded.fileId, filename: uploaded.filename };
|
|
4689
|
+
}
|
|
4690
|
+
if (typeof item.file_url === "string" && item.file_url.trim().toLowerCase().startsWith("data:")) {
|
|
4691
|
+
const parsed = parseDataUrlPayload(item.file_url);
|
|
4692
|
+
if (!parsed) {
|
|
4693
|
+
return item;
|
|
4694
|
+
}
|
|
4695
|
+
const uploaded = await storeCanonicalPromptFile({
|
|
4696
|
+
bytes: parsed.bytes,
|
|
4697
|
+
mimeType: parsed.mimeType ?? "application/octet-stream",
|
|
4698
|
+
filename: normaliseAttachmentFilename(
|
|
4699
|
+
typeof item.filename === "string" ? item.filename : void 0,
|
|
4700
|
+
guessInlineDataFilename(parsed.mimeType)
|
|
4701
|
+
)
|
|
4702
|
+
});
|
|
4703
|
+
return { type: "input_file", file_id: uploaded.fileId, filename: uploaded.filename };
|
|
4704
|
+
}
|
|
4705
|
+
return item;
|
|
4706
|
+
}
|
|
4707
|
+
async function prepareOpenAiPromptInput(input) {
|
|
4708
|
+
const prepareItem = async (item) => {
|
|
4709
|
+
if (!item || typeof item !== "object") {
|
|
4710
|
+
return item;
|
|
4711
|
+
}
|
|
4712
|
+
const record = item;
|
|
4713
|
+
if (Array.isArray(record.content)) {
|
|
4714
|
+
return {
|
|
4715
|
+
...record,
|
|
4716
|
+
content: await Promise.all(
|
|
4717
|
+
record.content.map((part) => prepareOpenAiPromptContentItem(part))
|
|
4718
|
+
)
|
|
4719
|
+
};
|
|
4720
|
+
}
|
|
4721
|
+
if (Array.isArray(record.output)) {
|
|
4722
|
+
return {
|
|
4723
|
+
...record,
|
|
4724
|
+
output: await Promise.all(
|
|
4725
|
+
record.output.map((part) => prepareOpenAiPromptContentItem(part))
|
|
4726
|
+
)
|
|
4727
|
+
};
|
|
4728
|
+
}
|
|
4729
|
+
return await prepareOpenAiPromptContentItem(item);
|
|
4730
|
+
};
|
|
4731
|
+
return await Promise.all(input.map((item) => prepareItem(item)));
|
|
4732
|
+
}
|
|
4733
|
+
async function maybePrepareOpenAiPromptInput(input) {
|
|
4734
|
+
if (estimateOpenAiInlinePromptBytes(input) <= INLINE_ATTACHMENT_PROMPT_THRESHOLD_BYTES) {
|
|
4735
|
+
return Array.from(input);
|
|
4736
|
+
}
|
|
4737
|
+
return await prepareOpenAiPromptInput(input);
|
|
4738
|
+
}
|
|
4739
|
+
function estimateGeminiInlinePromptBytes(contents) {
|
|
4740
|
+
let total = 0;
|
|
4741
|
+
for (const content of contents) {
|
|
4742
|
+
for (const part of content.parts ?? []) {
|
|
4743
|
+
if (part.inlineData?.data) {
|
|
4744
|
+
total += estimateInlinePayloadBytes(part.inlineData.data);
|
|
4745
|
+
}
|
|
4746
|
+
}
|
|
4747
|
+
}
|
|
4748
|
+
return total;
|
|
4749
|
+
}
|
|
4750
|
+
function hasCanonicalGeminiFileReferences(contents) {
|
|
4751
|
+
for (const content of contents) {
|
|
4752
|
+
for (const part of content.parts ?? []) {
|
|
4753
|
+
if (parseCanonicalGeminiFileId(part.fileData?.fileUri)) {
|
|
4754
|
+
return true;
|
|
4755
|
+
}
|
|
4756
|
+
}
|
|
4757
|
+
}
|
|
4758
|
+
return false;
|
|
4759
|
+
}
|
|
4760
|
+
async function prepareGeminiPromptContents(contents) {
|
|
4761
|
+
const backend = getGeminiBackend();
|
|
4762
|
+
const preparedContents = [];
|
|
4763
|
+
for (const content of contents) {
|
|
4764
|
+
const parts = [];
|
|
4765
|
+
for (const part of content.parts ?? []) {
|
|
4766
|
+
const canonicalFileId = parseCanonicalGeminiFileId(part.fileData?.fileUri);
|
|
4767
|
+
if (canonicalFileId) {
|
|
4768
|
+
const metadata = await getCanonicalFileMetadata(canonicalFileId);
|
|
4769
|
+
if (backend === "api") {
|
|
4770
|
+
const mirrored = await ensureGeminiFileMirror(canonicalFileId);
|
|
4771
|
+
const mirroredPart = createPartFromUri(mirrored.uri, mirrored.mimeType);
|
|
4772
|
+
if (metadata.filename && mirroredPart.fileData) {
|
|
4773
|
+
mirroredPart.fileData.displayName = metadata.filename;
|
|
4774
|
+
}
|
|
4775
|
+
parts.push(mirroredPart);
|
|
4776
|
+
} else {
|
|
4777
|
+
const mirrored = await ensureVertexFileMirror(canonicalFileId);
|
|
4778
|
+
parts.push({
|
|
4779
|
+
fileData: {
|
|
4780
|
+
fileUri: mirrored.fileUri,
|
|
4781
|
+
mimeType: mirrored.mimeType,
|
|
4782
|
+
displayName: metadata.filename
|
|
4783
|
+
}
|
|
4784
|
+
});
|
|
4785
|
+
}
|
|
4786
|
+
continue;
|
|
4787
|
+
}
|
|
4788
|
+
if (part.inlineData?.data) {
|
|
4789
|
+
const mimeType = part.inlineData.mimeType ?? "application/octet-stream";
|
|
4790
|
+
const filename = normaliseAttachmentFilename(
|
|
4791
|
+
getInlineAttachmentFilename(part.inlineData) ?? part.inlineData.displayName ?? guessInlineDataFilename(mimeType),
|
|
4792
|
+
guessInlineDataFilename(mimeType)
|
|
4793
|
+
);
|
|
4794
|
+
const stored = await storeCanonicalPromptFile({
|
|
4795
|
+
bytes: decodeInlineDataBuffer(part.inlineData.data),
|
|
4796
|
+
mimeType,
|
|
4797
|
+
filename
|
|
4798
|
+
});
|
|
4799
|
+
if (backend === "api") {
|
|
4800
|
+
const mirrored = await ensureGeminiFileMirror(stored.fileId);
|
|
4801
|
+
const mirroredPart = createPartFromUri(mirrored.uri, mirrored.mimeType);
|
|
4802
|
+
if (filename && mirroredPart.fileData) {
|
|
4803
|
+
mirroredPart.fileData.displayName = filename;
|
|
4804
|
+
}
|
|
4805
|
+
parts.push(mirroredPart);
|
|
4806
|
+
} else {
|
|
4807
|
+
const mirrored = await ensureVertexFileMirror(stored.fileId);
|
|
4808
|
+
parts.push({
|
|
4809
|
+
fileData: {
|
|
4810
|
+
fileUri: mirrored.fileUri,
|
|
4811
|
+
mimeType: mirrored.mimeType,
|
|
4812
|
+
displayName: filename
|
|
4813
|
+
}
|
|
4814
|
+
});
|
|
4815
|
+
}
|
|
4816
|
+
continue;
|
|
4817
|
+
}
|
|
4818
|
+
parts.push(part);
|
|
4819
|
+
}
|
|
4820
|
+
preparedContents.push({
|
|
4821
|
+
...content,
|
|
4822
|
+
parts
|
|
4823
|
+
});
|
|
4824
|
+
}
|
|
4825
|
+
return preparedContents;
|
|
4826
|
+
}
|
|
4827
|
+
async function maybePrepareGeminiPromptContents(contents) {
|
|
4828
|
+
if (!hasCanonicalGeminiFileReferences(contents) && estimateGeminiInlinePromptBytes(contents) <= INLINE_ATTACHMENT_PROMPT_THRESHOLD_BYTES) {
|
|
4829
|
+
return Array.from(contents);
|
|
4830
|
+
}
|
|
4831
|
+
return await prepareGeminiPromptContents(contents);
|
|
4832
|
+
}
|
|
4833
|
+
function mergeConsecutiveTextParts(parts) {
|
|
3723
4834
|
if (parts.length === 0) {
|
|
3724
4835
|
return [];
|
|
3725
4836
|
}
|
|
3726
4837
|
const merged = [];
|
|
3727
4838
|
for (const part of parts) {
|
|
3728
4839
|
if (part.type !== "text") {
|
|
3729
|
-
merged.push(
|
|
4840
|
+
merged.push(cloneContentPart(part));
|
|
3730
4841
|
continue;
|
|
3731
4842
|
}
|
|
3732
4843
|
const isThought = part.thought === true;
|
|
@@ -4157,13 +5268,7 @@ function resolveTextContents(input) {
|
|
|
4157
5268
|
const parts = typeof message.content === "string" ? [{ type: "text", text: message.content }] : message.content;
|
|
4158
5269
|
contents.push({
|
|
4159
5270
|
role: message.role,
|
|
4160
|
-
parts: parts.map(
|
|
4161
|
-
(part) => part.type === "text" ? {
|
|
4162
|
-
type: "text",
|
|
4163
|
-
text: part.text,
|
|
4164
|
-
thought: "thought" in part && part.thought === true ? true : void 0
|
|
4165
|
-
} : { type: "inlineData", data: part.data, mimeType: part.mimeType }
|
|
4166
|
-
)
|
|
5271
|
+
parts: parts.map((part) => cloneContentPart(part))
|
|
4167
5272
|
});
|
|
4168
5273
|
}
|
|
4169
5274
|
return contents;
|
|
@@ -4179,22 +5284,58 @@ function toOpenAiInput(contents) {
|
|
|
4179
5284
|
return contents.map((content) => {
|
|
4180
5285
|
const parts = [];
|
|
4181
5286
|
for (const part of content.parts) {
|
|
4182
|
-
|
|
4183
|
-
|
|
4184
|
-
|
|
4185
|
-
|
|
4186
|
-
|
|
4187
|
-
|
|
4188
|
-
|
|
4189
|
-
|
|
4190
|
-
|
|
5287
|
+
switch (part.type) {
|
|
5288
|
+
case "text":
|
|
5289
|
+
parts.push({ type: "input_text", text: part.text });
|
|
5290
|
+
break;
|
|
5291
|
+
case "inlineData": {
|
|
5292
|
+
const mimeType = part.mimeType;
|
|
5293
|
+
if (isInlineImageMime(mimeType)) {
|
|
5294
|
+
const dataUrl = `data:${mimeType};base64,${part.data}`;
|
|
5295
|
+
const imagePart = {
|
|
5296
|
+
type: "input_image",
|
|
5297
|
+
image_url: dataUrl,
|
|
5298
|
+
detail: "auto"
|
|
5299
|
+
};
|
|
5300
|
+
setInlineAttachmentFilename(
|
|
5301
|
+
imagePart,
|
|
5302
|
+
normaliseAttachmentFilename(part.filename, guessInlineDataFilename(mimeType))
|
|
5303
|
+
);
|
|
5304
|
+
parts.push(imagePart);
|
|
5305
|
+
break;
|
|
5306
|
+
}
|
|
5307
|
+
parts.push({
|
|
5308
|
+
type: "input_file",
|
|
5309
|
+
filename: normaliseAttachmentFilename(part.filename, guessInlineDataFilename(mimeType)),
|
|
5310
|
+
file_data: part.data
|
|
5311
|
+
});
|
|
5312
|
+
break;
|
|
5313
|
+
}
|
|
5314
|
+
case "input_image": {
|
|
5315
|
+
const imagePart = {
|
|
5316
|
+
type: "input_image",
|
|
5317
|
+
...part.file_id ? { file_id: part.file_id } : {},
|
|
5318
|
+
...part.image_url ? { image_url: part.image_url } : {},
|
|
5319
|
+
detail: part.detail === "high" || part.detail === "low" ? part.detail : "auto"
|
|
5320
|
+
};
|
|
5321
|
+
if (part.filename) {
|
|
5322
|
+
setInlineAttachmentFilename(imagePart, part.filename);
|
|
5323
|
+
}
|
|
5324
|
+
parts.push(imagePart);
|
|
5325
|
+
break;
|
|
5326
|
+
}
|
|
5327
|
+
case "input_file":
|
|
5328
|
+
parts.push({
|
|
5329
|
+
type: "input_file",
|
|
5330
|
+
...part.file_id ? { file_id: part.file_id } : {},
|
|
5331
|
+
...part.file_data ? { file_data: part.file_data } : {},
|
|
5332
|
+
...part.file_url ? { file_url: part.file_url } : {},
|
|
5333
|
+
...part.filename ? { filename: part.filename } : {}
|
|
5334
|
+
});
|
|
5335
|
+
break;
|
|
5336
|
+
default:
|
|
5337
|
+
throw new Error("Unsupported LLM content part");
|
|
4191
5338
|
}
|
|
4192
|
-
const fileData = decodeInlineDataBuffer(part.data).toString("base64");
|
|
4193
|
-
parts.push({
|
|
4194
|
-
type: "input_file",
|
|
4195
|
-
filename: guessInlineDataFilename(mimeType),
|
|
4196
|
-
file_data: fileData
|
|
4197
|
-
});
|
|
4198
5339
|
}
|
|
4199
5340
|
if (parts.length === 1 && parts[0]?.type === "input_text" && typeof parts[0].text === "string") {
|
|
4200
5341
|
return {
|
|
@@ -4231,28 +5372,54 @@ function toChatGptInput(contents) {
|
|
|
4231
5372
|
continue;
|
|
4232
5373
|
}
|
|
4233
5374
|
if (isAssistant) {
|
|
4234
|
-
const mimeType = part.mimeType ?? "application/octet-stream";
|
|
5375
|
+
const mimeType = part.type === "inlineData" ? part.mimeType ?? "application/octet-stream" : inferToolOutputMimeTypeFromFilename(part.filename) ?? "application/octet-stream";
|
|
4235
5376
|
parts.push({
|
|
4236
5377
|
type: "output_text",
|
|
4237
|
-
text: isInlineImageMime(part.mimeType) ? `[image:${mimeType}]` : `[file:${mimeType}]`
|
|
5378
|
+
text: part.type === "input_image" || isInlineImageMime(part.mimeType) ? `[image:${mimeType}]` : `[file:${mimeType}]`
|
|
4238
5379
|
});
|
|
4239
|
-
|
|
4240
|
-
|
|
4241
|
-
|
|
4242
|
-
|
|
5380
|
+
continue;
|
|
5381
|
+
}
|
|
5382
|
+
switch (part.type) {
|
|
5383
|
+
case "inlineData": {
|
|
5384
|
+
if (isInlineImageMime(part.mimeType)) {
|
|
5385
|
+
const mimeType = part.mimeType ?? "application/octet-stream";
|
|
5386
|
+
const dataUrl = `data:${mimeType};base64,${part.data}`;
|
|
5387
|
+
parts.push({
|
|
5388
|
+
type: "input_image",
|
|
5389
|
+
image_url: dataUrl,
|
|
5390
|
+
detail: "auto"
|
|
5391
|
+
});
|
|
5392
|
+
} else {
|
|
5393
|
+
parts.push({
|
|
5394
|
+
type: "input_file",
|
|
5395
|
+
filename: normaliseAttachmentFilename(
|
|
5396
|
+
part.filename,
|
|
5397
|
+
guessInlineDataFilename(part.mimeType)
|
|
5398
|
+
),
|
|
5399
|
+
file_data: part.data
|
|
5400
|
+
});
|
|
5401
|
+
}
|
|
5402
|
+
break;
|
|
5403
|
+
}
|
|
5404
|
+
case "input_image":
|
|
4243
5405
|
parts.push({
|
|
4244
5406
|
type: "input_image",
|
|
4245
|
-
|
|
4246
|
-
|
|
5407
|
+
...part.file_id ? { file_id: part.file_id } : {},
|
|
5408
|
+
...part.image_url ? { image_url: part.image_url } : {},
|
|
5409
|
+
detail: part.detail === "high" || part.detail === "low" ? part.detail : "auto"
|
|
4247
5410
|
});
|
|
4248
|
-
|
|
4249
|
-
|
|
5411
|
+
break;
|
|
5412
|
+
case "input_file":
|
|
4250
5413
|
parts.push({
|
|
4251
5414
|
type: "input_file",
|
|
4252
|
-
|
|
4253
|
-
file_data:
|
|
5415
|
+
...part.file_id ? { file_id: part.file_id } : {},
|
|
5416
|
+
...part.file_data ? { file_data: part.file_data } : {},
|
|
5417
|
+
...part.file_url ? { file_url: part.file_url } : {},
|
|
5418
|
+
...part.filename ? { filename: part.filename } : {}
|
|
4254
5419
|
});
|
|
4255
|
-
|
|
5420
|
+
break;
|
|
5421
|
+
default:
|
|
5422
|
+
throw new Error("Unsupported LLM content part");
|
|
4256
5423
|
}
|
|
4257
5424
|
}
|
|
4258
5425
|
if (parts.length === 0) {
|
|
@@ -4296,8 +5463,8 @@ ${JSON.stringify(options.responseJsonSchema)}`);
|
|
|
4296
5463
|
if (part.type === "text") {
|
|
4297
5464
|
return part.text;
|
|
4298
5465
|
}
|
|
4299
|
-
const mimeType = part.mimeType ?? "application/octet-stream";
|
|
4300
|
-
if (isInlineImageMime(mimeType)) {
|
|
5466
|
+
const mimeType = part.type === "inlineData" ? part.mimeType ?? "application/octet-stream" : inferToolOutputMimeTypeFromFilename(part.filename) ?? "application/octet-stream";
|
|
5467
|
+
if (part.type === "input_image" || isInlineImageMime(mimeType)) {
|
|
4301
5468
|
return `[image:${mimeType}]`;
|
|
4302
5469
|
}
|
|
4303
5470
|
return `[file:${mimeType}]`;
|
|
@@ -4568,7 +5735,14 @@ function isLlmToolOutputContentItem(value) {
|
|
|
4568
5735
|
return typeof value.text === "string";
|
|
4569
5736
|
}
|
|
4570
5737
|
if (itemType === "input_image") {
|
|
4571
|
-
|
|
5738
|
+
const keys = ["image_url", "file_id", "filename"];
|
|
5739
|
+
for (const key of keys) {
|
|
5740
|
+
const part = value[key];
|
|
5741
|
+
if (part !== void 0 && part !== null && typeof part !== "string") {
|
|
5742
|
+
return false;
|
|
5743
|
+
}
|
|
5744
|
+
}
|
|
5745
|
+
return value.image_url !== void 0 || value.file_id !== void 0;
|
|
4572
5746
|
}
|
|
4573
5747
|
if (itemType === "input_file") {
|
|
4574
5748
|
const keys = ["file_data", "file_id", "file_url", "filename"];
|
|
@@ -4637,15 +5811,252 @@ function inferToolOutputMimeTypeFromFilename(filename) {
|
|
|
4637
5811
|
}
|
|
4638
5812
|
return void 0;
|
|
4639
5813
|
}
|
|
4640
|
-
function
|
|
5814
|
+
function estimateToolOutputItemBytes(item) {
|
|
5815
|
+
if (item.type === "input_text") {
|
|
5816
|
+
return Buffer5.byteLength(item.text, "utf8");
|
|
5817
|
+
}
|
|
5818
|
+
if (item.type === "input_image") {
|
|
5819
|
+
return typeof item.image_url === "string" ? estimateInlinePayloadBytes(item.image_url) : 0;
|
|
5820
|
+
}
|
|
5821
|
+
if (typeof item.file_data === "string" && item.file_data.trim().length > 0) {
|
|
5822
|
+
return estimateInlinePayloadBytes(item.file_data);
|
|
5823
|
+
}
|
|
5824
|
+
if (typeof item.file_url === "string" && item.file_url.trim().length > 0) {
|
|
5825
|
+
return estimateInlinePayloadBytes(item.file_url);
|
|
5826
|
+
}
|
|
5827
|
+
return 0;
|
|
5828
|
+
}
|
|
5829
|
+
async function spillTextToolOutputToFile(options) {
|
|
5830
|
+
const stored = await runWithFileUploadSource("tool_output_spill", async () => {
|
|
5831
|
+
return await filesCreate({
|
|
5832
|
+
data: options.text,
|
|
5833
|
+
filename: options.filename,
|
|
5834
|
+
mimeType: options.mimeType,
|
|
5835
|
+
expiresAfterSeconds: DEFAULT_FILE_TTL_SECONDS
|
|
5836
|
+
});
|
|
5837
|
+
});
|
|
5838
|
+
return [
|
|
5839
|
+
{
|
|
5840
|
+
type: "input_text",
|
|
5841
|
+
text: `Tool output was attached as ${stored.filename} (${stored.id}) because it exceeded the inline payload threshold.`
|
|
5842
|
+
},
|
|
5843
|
+
{
|
|
5844
|
+
type: "input_file",
|
|
5845
|
+
file_id: stored.id,
|
|
5846
|
+
filename: stored.filename
|
|
5847
|
+
}
|
|
5848
|
+
];
|
|
5849
|
+
}
|
|
5850
|
+
async function maybeSpillToolOutputItem(item, toolName, options) {
|
|
5851
|
+
if (options?.force !== true && estimateToolOutputItemBytes(item) <= TOOL_OUTPUT_SPILL_THRESHOLD_BYTES) {
|
|
5852
|
+
return item;
|
|
5853
|
+
}
|
|
5854
|
+
if (item.type === "input_text") {
|
|
5855
|
+
return await spillTextToolOutputToFile({
|
|
5856
|
+
text: item.text,
|
|
5857
|
+
filename: normaliseAttachmentFilename(`${toolName}.txt`, "tool-output.txt"),
|
|
5858
|
+
mimeType: "text/plain"
|
|
5859
|
+
});
|
|
5860
|
+
}
|
|
4641
5861
|
if (item.type === "input_image") {
|
|
5862
|
+
if (item.file_id || !item.image_url) {
|
|
5863
|
+
return item;
|
|
5864
|
+
}
|
|
4642
5865
|
const parsed = parseDataUrlPayload(item.image_url);
|
|
4643
5866
|
if (!parsed) {
|
|
5867
|
+
return item;
|
|
5868
|
+
}
|
|
5869
|
+
const stored = await runWithFileUploadSource("tool_output_spill", async () => {
|
|
5870
|
+
return await filesCreate({
|
|
5871
|
+
data: parsed.bytes,
|
|
5872
|
+
filename: normaliseAttachmentFilename(
|
|
5873
|
+
item.filename ?? guessInlineDataFilename(parsed.mimeType),
|
|
5874
|
+
guessInlineDataFilename(parsed.mimeType)
|
|
5875
|
+
),
|
|
5876
|
+
mimeType: parsed.mimeType,
|
|
5877
|
+
expiresAfterSeconds: DEFAULT_FILE_TTL_SECONDS
|
|
5878
|
+
});
|
|
5879
|
+
});
|
|
5880
|
+
return {
|
|
5881
|
+
type: "input_image",
|
|
5882
|
+
file_id: stored.id,
|
|
5883
|
+
detail: item.detail ?? "auto",
|
|
5884
|
+
filename: stored.filename
|
|
5885
|
+
};
|
|
5886
|
+
}
|
|
5887
|
+
if (item.file_id) {
|
|
5888
|
+
return item;
|
|
5889
|
+
}
|
|
5890
|
+
if (typeof item.file_data === "string" && item.file_data.trim().length > 0) {
|
|
5891
|
+
const fileData = item.file_data;
|
|
5892
|
+
const filename = normaliseAttachmentFilename(
|
|
5893
|
+
item.filename ?? `${toolName}.bin`,
|
|
5894
|
+
`${toolName}.bin`
|
|
5895
|
+
);
|
|
5896
|
+
const stored = await runWithFileUploadSource("tool_output_spill", async () => {
|
|
5897
|
+
return await filesCreate({
|
|
5898
|
+
data: decodeInlineDataBuffer(fileData),
|
|
5899
|
+
filename,
|
|
5900
|
+
mimeType: inferToolOutputMimeTypeFromFilename(filename) ?? "application/octet-stream",
|
|
5901
|
+
expiresAfterSeconds: DEFAULT_FILE_TTL_SECONDS
|
|
5902
|
+
});
|
|
5903
|
+
});
|
|
5904
|
+
return {
|
|
5905
|
+
type: "input_file",
|
|
5906
|
+
file_id: stored.id,
|
|
5907
|
+
filename: stored.filename
|
|
5908
|
+
};
|
|
5909
|
+
}
|
|
5910
|
+
if (typeof item.file_url === "string" && item.file_url.trim().length > 0) {
|
|
5911
|
+
const parsed = parseDataUrlPayload(item.file_url);
|
|
5912
|
+
if (!parsed) {
|
|
5913
|
+
return item;
|
|
5914
|
+
}
|
|
5915
|
+
const stored = await runWithFileUploadSource("tool_output_spill", async () => {
|
|
5916
|
+
return await filesCreate({
|
|
5917
|
+
data: parsed.bytes,
|
|
5918
|
+
filename: normaliseAttachmentFilename(
|
|
5919
|
+
item.filename ?? guessInlineDataFilename(parsed.mimeType),
|
|
5920
|
+
guessInlineDataFilename(parsed.mimeType)
|
|
5921
|
+
),
|
|
5922
|
+
mimeType: parsed.mimeType,
|
|
5923
|
+
expiresAfterSeconds: DEFAULT_FILE_TTL_SECONDS
|
|
5924
|
+
});
|
|
5925
|
+
});
|
|
5926
|
+
return {
|
|
5927
|
+
type: "input_file",
|
|
5928
|
+
file_id: stored.id,
|
|
5929
|
+
filename: stored.filename
|
|
5930
|
+
};
|
|
5931
|
+
}
|
|
5932
|
+
return item;
|
|
5933
|
+
}
|
|
5934
|
+
async function maybeSpillToolOutput(value, toolName, options) {
|
|
5935
|
+
if (typeof value === "string") {
|
|
5936
|
+
if (options?.force !== true && Buffer5.byteLength(value, "utf8") <= TOOL_OUTPUT_SPILL_THRESHOLD_BYTES) {
|
|
5937
|
+
return value;
|
|
5938
|
+
}
|
|
5939
|
+
return await spillTextToolOutputToFile({
|
|
5940
|
+
text: value,
|
|
5941
|
+
filename: normaliseAttachmentFilename(`${toolName}.txt`, "tool-output.txt"),
|
|
5942
|
+
mimeType: "text/plain"
|
|
5943
|
+
});
|
|
5944
|
+
}
|
|
5945
|
+
if (isLlmToolOutputContentItem(value)) {
|
|
5946
|
+
return await maybeSpillToolOutputItem(value, toolName, options);
|
|
5947
|
+
}
|
|
5948
|
+
if (Array.isArray(value) && value.every((item) => isLlmToolOutputContentItem(item))) {
|
|
5949
|
+
const spilledItems = [];
|
|
5950
|
+
for (const item of value) {
|
|
5951
|
+
const maybeSpilled = await maybeSpillToolOutputItem(item, toolName, options);
|
|
5952
|
+
if (Array.isArray(maybeSpilled)) {
|
|
5953
|
+
spilledItems.push(...maybeSpilled);
|
|
5954
|
+
} else {
|
|
5955
|
+
spilledItems.push(maybeSpilled);
|
|
5956
|
+
}
|
|
5957
|
+
}
|
|
5958
|
+
return spilledItems;
|
|
5959
|
+
}
|
|
5960
|
+
try {
|
|
5961
|
+
const serialized = JSON.stringify(value, null, 2);
|
|
5962
|
+
if (options?.force !== true && Buffer5.byteLength(serialized, "utf8") <= TOOL_OUTPUT_SPILL_THRESHOLD_BYTES) {
|
|
5963
|
+
return value;
|
|
5964
|
+
}
|
|
5965
|
+
return await spillTextToolOutputToFile({
|
|
5966
|
+
text: serialized,
|
|
5967
|
+
filename: normaliseAttachmentFilename(`${toolName}.json`, "tool-output.json"),
|
|
5968
|
+
mimeType: "application/json"
|
|
5969
|
+
});
|
|
5970
|
+
} catch {
|
|
5971
|
+
return value;
|
|
5972
|
+
}
|
|
5973
|
+
}
|
|
5974
|
+
function estimateToolOutputPayloadBytes(value) {
|
|
5975
|
+
if (typeof value === "string") {
|
|
5976
|
+
return Buffer5.byteLength(value, "utf8");
|
|
5977
|
+
}
|
|
5978
|
+
if (isLlmToolOutputContentItem(value)) {
|
|
5979
|
+
return value.type === "input_text" ? 0 : estimateToolOutputItemBytes(value);
|
|
5980
|
+
}
|
|
5981
|
+
if (Array.isArray(value) && value.every((item) => isLlmToolOutputContentItem(item))) {
|
|
5982
|
+
return value.reduce((total, item) => {
|
|
5983
|
+
return total + (item.type === "input_text" ? 0 : estimateToolOutputItemBytes(item));
|
|
5984
|
+
}, 0);
|
|
5985
|
+
}
|
|
5986
|
+
return 0;
|
|
5987
|
+
}
|
|
5988
|
+
async function maybeSpillCombinedToolCallOutputs(callResults) {
|
|
5989
|
+
const totalBytes = callResults.reduce(
|
|
5990
|
+
(sum, callResult) => sum + estimateToolOutputPayloadBytes(callResult.outputPayload),
|
|
5991
|
+
0
|
|
5992
|
+
);
|
|
5993
|
+
if (totalBytes <= INLINE_ATTACHMENT_PROMPT_THRESHOLD_BYTES) {
|
|
5994
|
+
return Array.from(callResults);
|
|
5995
|
+
}
|
|
5996
|
+
return await Promise.all(
|
|
5997
|
+
callResults.map(async (callResult) => {
|
|
5998
|
+
if (estimateToolOutputPayloadBytes(callResult.outputPayload) === 0) {
|
|
5999
|
+
return callResult;
|
|
6000
|
+
}
|
|
6001
|
+
const outputPayload = await maybeSpillToolOutput(
|
|
6002
|
+
callResult.outputPayload,
|
|
6003
|
+
callResult.entry.toolName,
|
|
6004
|
+
{
|
|
6005
|
+
force: true
|
|
6006
|
+
}
|
|
6007
|
+
);
|
|
6008
|
+
return {
|
|
6009
|
+
...callResult,
|
|
6010
|
+
outputPayload,
|
|
6011
|
+
result: {
|
|
6012
|
+
...callResult.result,
|
|
6013
|
+
output: outputPayload
|
|
6014
|
+
}
|
|
6015
|
+
};
|
|
6016
|
+
})
|
|
6017
|
+
);
|
|
6018
|
+
}
|
|
6019
|
+
function buildGeminiToolOutputMediaPart(item) {
|
|
6020
|
+
if (item.type === "input_image") {
|
|
6021
|
+
if (typeof item.file_id === "string" && item.file_id.trim().length > 0) {
|
|
6022
|
+
return {
|
|
6023
|
+
fileData: {
|
|
6024
|
+
fileUri: buildCanonicalGeminiFileUri(item.file_id),
|
|
6025
|
+
mimeType: inferToolOutputMimeTypeFromFilename(item.filename) ?? "application/octet-stream",
|
|
6026
|
+
displayName: item.filename ?? void 0
|
|
6027
|
+
}
|
|
6028
|
+
};
|
|
6029
|
+
}
|
|
6030
|
+
if (typeof item.image_url !== "string" || item.image_url.trim().length === 0) {
|
|
4644
6031
|
return null;
|
|
4645
6032
|
}
|
|
4646
|
-
|
|
6033
|
+
const parsed = parseDataUrlPayload(item.image_url);
|
|
6034
|
+
if (parsed) {
|
|
6035
|
+
const part = createPartFromBase64(parsed.dataBase64, parsed.mimeType);
|
|
6036
|
+
const displayName = item.filename?.trim();
|
|
6037
|
+
if (displayName && part.inlineData) {
|
|
6038
|
+
part.inlineData.displayName = displayName;
|
|
6039
|
+
}
|
|
6040
|
+
return part;
|
|
6041
|
+
}
|
|
6042
|
+
return {
|
|
6043
|
+
fileData: {
|
|
6044
|
+
fileUri: item.image_url,
|
|
6045
|
+
mimeType: inferToolOutputMimeTypeFromFilename(item.filename) ?? "application/octet-stream",
|
|
6046
|
+
displayName: item.filename ?? void 0
|
|
6047
|
+
}
|
|
6048
|
+
};
|
|
4647
6049
|
}
|
|
4648
6050
|
if (item.type === "input_file") {
|
|
6051
|
+
if (typeof item.file_id === "string" && item.file_id.trim().length > 0) {
|
|
6052
|
+
return {
|
|
6053
|
+
fileData: {
|
|
6054
|
+
fileUri: buildCanonicalGeminiFileUri(item.file_id),
|
|
6055
|
+
mimeType: inferToolOutputMimeTypeFromFilename(item.filename) ?? "application/octet-stream",
|
|
6056
|
+
displayName: item.filename ?? void 0
|
|
6057
|
+
}
|
|
6058
|
+
};
|
|
6059
|
+
}
|
|
4649
6060
|
const dataUrl = typeof item.file_url === "string" ? parseDataUrlPayload(item.file_url) : null;
|
|
4650
6061
|
if (dataUrl) {
|
|
4651
6062
|
const part = createPartFromBase64(dataUrl.dataBase64, dataUrl.mimeType);
|
|
@@ -4683,11 +6094,12 @@ function toGeminiToolOutputPlaceholder(item) {
|
|
|
4683
6094
|
};
|
|
4684
6095
|
}
|
|
4685
6096
|
if (item.type === "input_image") {
|
|
4686
|
-
const parsed = parseDataUrlPayload(item.image_url);
|
|
6097
|
+
const parsed = typeof item.image_url === "string" ? parseDataUrlPayload(item.image_url) : null;
|
|
4687
6098
|
return {
|
|
4688
6099
|
type: item.type,
|
|
6100
|
+
fileId: item.file_id ?? void 0,
|
|
4689
6101
|
mimeType: parsed?.mimeType ?? void 0,
|
|
4690
|
-
media: "attached-inline-data"
|
|
6102
|
+
media: item.file_id ? "attached-file-id" : parsed ? "attached-inline-data" : item.image_url ? "attached-file-data" : void 0
|
|
4691
6103
|
};
|
|
4692
6104
|
}
|
|
4693
6105
|
const dataUrl = typeof item.file_url === "string" ? parseDataUrlPayload(item.file_url) : null;
|
|
@@ -4696,7 +6108,7 @@ function toGeminiToolOutputPlaceholder(item) {
|
|
|
4696
6108
|
filename: item.filename ?? void 0,
|
|
4697
6109
|
fileId: item.file_id ?? void 0,
|
|
4698
6110
|
mimeType: dataUrl?.mimeType ?? inferToolOutputMimeTypeFromFilename(item.filename) ?? void 0,
|
|
4699
|
-
media: dataUrl || typeof item.file_data === "string" && item.file_data.trim().length > 0 ? "attached-inline-data" : typeof item.file_url === "string" && item.file_url.trim().length > 0 ? "attached-file-data" : void 0
|
|
6111
|
+
media: item.file_id ? "attached-file-id" : dataUrl || typeof item.file_data === "string" && item.file_data.trim().length > 0 ? "attached-inline-data" : typeof item.file_url === "string" && item.file_url.trim().length > 0 ? "attached-file-data" : void 0
|
|
4700
6112
|
};
|
|
4701
6113
|
}
|
|
4702
6114
|
function buildGeminiFunctionResponseParts(options) {
|
|
@@ -4744,8 +6156,8 @@ function parseOpenAiToolArguments(raw) {
|
|
|
4744
6156
|
function formatZodIssues(issues) {
|
|
4745
6157
|
const messages = [];
|
|
4746
6158
|
for (const issue of issues) {
|
|
4747
|
-
const
|
|
4748
|
-
messages.push(`${
|
|
6159
|
+
const path10 = issue.path.length > 0 ? issue.path.map(String).join(".") : "input";
|
|
6160
|
+
messages.push(`${path10}: ${issue.message}`);
|
|
4749
6161
|
}
|
|
4750
6162
|
return messages.join("; ");
|
|
4751
6163
|
}
|
|
@@ -4863,8 +6275,9 @@ async function executeToolCall(params) {
|
|
|
4863
6275
|
const input = typeof rawInput === "string" ? rawInput : String(rawInput ?? "");
|
|
4864
6276
|
try {
|
|
4865
6277
|
const output = await tool2.execute(input);
|
|
4866
|
-
const
|
|
4867
|
-
|
|
6278
|
+
const outputPayload = await maybeSpillToolOutput(output, toolName);
|
|
6279
|
+
const metrics = toolName === "spawn_agent" ? extractSpawnStartupMetrics(outputPayload) : void 0;
|
|
6280
|
+
return finalize({ toolName, input, output: outputPayload }, outputPayload, metrics);
|
|
4868
6281
|
} catch (error) {
|
|
4869
6282
|
const message = error instanceof Error ? error.message : String(error);
|
|
4870
6283
|
const outputPayload = buildToolErrorOutput(`Tool ${toolName} failed: ${message}`);
|
|
@@ -4898,8 +6311,13 @@ async function executeToolCall(params) {
|
|
|
4898
6311
|
}
|
|
4899
6312
|
try {
|
|
4900
6313
|
const output = await tool2.execute(parsed.data);
|
|
4901
|
-
const
|
|
4902
|
-
|
|
6314
|
+
const outputPayload = await maybeSpillToolOutput(output, toolName);
|
|
6315
|
+
const metrics = toolName === "spawn_agent" ? extractSpawnStartupMetrics(outputPayload) : void 0;
|
|
6316
|
+
return finalize(
|
|
6317
|
+
{ toolName, input: parsed.data, output: outputPayload },
|
|
6318
|
+
outputPayload,
|
|
6319
|
+
metrics
|
|
6320
|
+
);
|
|
4903
6321
|
} catch (error) {
|
|
4904
6322
|
const message = error instanceof Error ? error.message : String(error);
|
|
4905
6323
|
const outputPayload = buildToolErrorOutput(`Tool ${toolName} failed: ${message}`);
|
|
@@ -5100,7 +6518,7 @@ function toGemini25ProThinkingBudget(thinkingLevel) {
|
|
|
5100
6518
|
}
|
|
5101
6519
|
}
|
|
5102
6520
|
function resolveGeminiThinkingConfig(modelId, thinkingLevel) {
|
|
5103
|
-
if (isGeminiImageModelId(modelId)) {
|
|
6521
|
+
if (isGeminiImageModelId(modelId) || modelId === "gemini-flash-lite-latest") {
|
|
5104
6522
|
return void 0;
|
|
5105
6523
|
}
|
|
5106
6524
|
if (thinkingLevel) {
|
|
@@ -5131,9 +6549,9 @@ function resolveGeminiThinkingConfig(modelId, thinkingLevel) {
|
|
|
5131
6549
|
}
|
|
5132
6550
|
function decodeInlineDataBuffer(base64) {
|
|
5133
6551
|
try {
|
|
5134
|
-
return
|
|
6552
|
+
return Buffer5.from(base64, "base64");
|
|
5135
6553
|
} catch {
|
|
5136
|
-
return
|
|
6554
|
+
return Buffer5.from(base64, "base64url");
|
|
5137
6555
|
}
|
|
5138
6556
|
}
|
|
5139
6557
|
function extractImages(content) {
|
|
@@ -5203,7 +6621,7 @@ function parseDataUrlPayload(value) {
|
|
|
5203
6621
|
const isBase64 = /;base64(?:;|$)/iu.test(header);
|
|
5204
6622
|
const mimeType = (header.split(";")[0] ?? "application/octet-stream").trim().toLowerCase();
|
|
5205
6623
|
try {
|
|
5206
|
-
const bytes = isBase64 ?
|
|
6624
|
+
const bytes = isBase64 ? Buffer5.from(payload, "base64") : Buffer5.from(decodeURIComponent(payload), "utf8");
|
|
5207
6625
|
return {
|
|
5208
6626
|
mimeType,
|
|
5209
6627
|
dataBase64: bytes.toString("base64"),
|
|
@@ -5306,7 +6724,10 @@ function collectLoggedAttachmentsFromLlmParts(parts, prefix) {
|
|
|
5306
6724
|
continue;
|
|
5307
6725
|
}
|
|
5308
6726
|
attachments.push({
|
|
5309
|
-
filename:
|
|
6727
|
+
filename: normaliseAttachmentFilename(
|
|
6728
|
+
part.filename,
|
|
6729
|
+
buildLoggedAttachmentFilename(prefix, index, part.mimeType)
|
|
6730
|
+
),
|
|
5310
6731
|
bytes: decodeInlineDataBuffer(part.data)
|
|
5311
6732
|
});
|
|
5312
6733
|
index += 1;
|
|
@@ -5435,15 +6856,19 @@ function startLlmCallLoggerFromContents(options) {
|
|
|
5435
6856
|
sections.push(part.text);
|
|
5436
6857
|
continue;
|
|
5437
6858
|
}
|
|
5438
|
-
|
|
5439
|
-
|
|
5440
|
-
|
|
5441
|
-
|
|
5442
|
-
|
|
5443
|
-
|
|
5444
|
-
|
|
5445
|
-
|
|
5446
|
-
|
|
6859
|
+
if (part.type === "inlineData") {
|
|
6860
|
+
const filename = buildLoggedAttachmentFilename("input", attachmentIndex, part.mimeType);
|
|
6861
|
+
attachments.push({
|
|
6862
|
+
filename,
|
|
6863
|
+
bytes: decodeInlineDataBuffer(part.data)
|
|
6864
|
+
});
|
|
6865
|
+
attachmentIndex += 1;
|
|
6866
|
+
sections.push(
|
|
6867
|
+
`[inlineData] file=${filename} mime=${part.mimeType ?? "application/octet-stream"} bytes=${attachments[attachments.length - 1]?.bytes.byteLength ?? 0}`
|
|
6868
|
+
);
|
|
6869
|
+
continue;
|
|
6870
|
+
}
|
|
6871
|
+
sections.push(`[${part.type}] file_id=${"file_id" in part ? part.file_id ?? "" : ""}`);
|
|
5447
6872
|
}
|
|
5448
6873
|
sections.push("");
|
|
5449
6874
|
}
|
|
@@ -5568,313 +6993,323 @@ async function runTextCall(params) {
|
|
|
5568
6993
|
request.signal.addEventListener(
|
|
5569
6994
|
"abort",
|
|
5570
6995
|
() => abortController.abort(request.signal?.reason),
|
|
5571
|
-
{ once: true }
|
|
5572
|
-
);
|
|
5573
|
-
}
|
|
5574
|
-
return abortController.signal;
|
|
5575
|
-
};
|
|
5576
|
-
const signal = resolveAbortSignal();
|
|
5577
|
-
|
|
5578
|
-
|
|
5579
|
-
|
|
5580
|
-
|
|
5581
|
-
|
|
5582
|
-
|
|
5583
|
-
|
|
5584
|
-
|
|
5585
|
-
};
|
|
5586
|
-
const reasoning = {
|
|
5587
|
-
effort: toOpenAiReasoningEffort(reasoningEffort),
|
|
5588
|
-
summary: "detailed"
|
|
5589
|
-
};
|
|
5590
|
-
await runOpenAiCall(async (client) => {
|
|
5591
|
-
const stream = client.responses.stream(
|
|
5592
|
-
{
|
|
5593
|
-
model: modelForProvider,
|
|
5594
|
-
input: openAiInput,
|
|
5595
|
-
reasoning,
|
|
5596
|
-
text: openAiTextConfig,
|
|
5597
|
-
...openAiTools ? { tools: openAiTools } : {},
|
|
5598
|
-
include: ["code_interpreter_call.outputs", "reasoning.encrypted_content"]
|
|
5599
|
-
},
|
|
5600
|
-
{ signal }
|
|
6996
|
+
{ once: true }
|
|
6997
|
+
);
|
|
6998
|
+
}
|
|
6999
|
+
return abortController.signal;
|
|
7000
|
+
};
|
|
7001
|
+
const signal = resolveAbortSignal();
|
|
7002
|
+
const { result } = await collectFileUploadMetrics(async () => {
|
|
7003
|
+
try {
|
|
7004
|
+
if (provider === "openai") {
|
|
7005
|
+
const openAiInput = await maybePrepareOpenAiPromptInput(toOpenAiInput(contents));
|
|
7006
|
+
const openAiTools = toOpenAiTools(request.tools);
|
|
7007
|
+
const reasoningEffort = resolveOpenAiReasoningEffort(
|
|
7008
|
+
modelForProvider,
|
|
7009
|
+
request.thinkingLevel
|
|
5601
7010
|
);
|
|
5602
|
-
|
|
5603
|
-
|
|
5604
|
-
|
|
5605
|
-
|
|
5606
|
-
|
|
5607
|
-
|
|
5608
|
-
|
|
5609
|
-
|
|
5610
|
-
|
|
5611
|
-
|
|
5612
|
-
|
|
5613
|
-
|
|
5614
|
-
|
|
5615
|
-
|
|
5616
|
-
|
|
5617
|
-
|
|
5618
|
-
|
|
5619
|
-
|
|
5620
|
-
|
|
5621
|
-
}
|
|
5622
|
-
}
|
|
5623
|
-
const finalResponse = await stream.finalResponse();
|
|
5624
|
-
modelVersion = typeof finalResponse.model === "string" ? finalResponse.model : request.model;
|
|
5625
|
-
queue.push({ type: "model", modelVersion });
|
|
5626
|
-
if (finalResponse.error) {
|
|
5627
|
-
const message = typeof finalResponse.error.message === "string" ? finalResponse.error.message : "OpenAI response failed";
|
|
5628
|
-
throw new Error(message);
|
|
5629
|
-
}
|
|
5630
|
-
if (finalResponse.status && finalResponse.status !== "completed" && finalResponse.status !== "in_progress") {
|
|
5631
|
-
const detail = finalResponse.incomplete_details?.reason;
|
|
5632
|
-
throw new Error(
|
|
5633
|
-
`OpenAI response status ${finalResponse.status}${detail ? ` (${detail})` : ""}`
|
|
7011
|
+
const openAiTextConfig = {
|
|
7012
|
+
format: request.openAiTextFormat ?? { type: "text" },
|
|
7013
|
+
verbosity: resolveOpenAiVerbosity(modelForProvider)
|
|
7014
|
+
};
|
|
7015
|
+
const reasoning = {
|
|
7016
|
+
effort: toOpenAiReasoningEffort(reasoningEffort),
|
|
7017
|
+
summary: "detailed"
|
|
7018
|
+
};
|
|
7019
|
+
await runOpenAiCall(async (client) => {
|
|
7020
|
+
const stream = client.responses.stream(
|
|
7021
|
+
{
|
|
7022
|
+
model: modelForProvider,
|
|
7023
|
+
input: openAiInput,
|
|
7024
|
+
reasoning,
|
|
7025
|
+
text: openAiTextConfig,
|
|
7026
|
+
...openAiTools ? { tools: openAiTools } : {},
|
|
7027
|
+
include: ["code_interpreter_call.outputs", "reasoning.encrypted_content"]
|
|
7028
|
+
},
|
|
7029
|
+
{ signal }
|
|
5634
7030
|
);
|
|
5635
|
-
|
|
5636
|
-
|
|
5637
|
-
|
|
5638
|
-
|
|
5639
|
-
|
|
5640
|
-
|
|
5641
|
-
|
|
5642
|
-
|
|
5643
|
-
|
|
5644
|
-
|
|
7031
|
+
for await (const event of stream) {
|
|
7032
|
+
switch (event.type) {
|
|
7033
|
+
case "response.output_text.delta": {
|
|
7034
|
+
const delta = event.delta ?? "";
|
|
7035
|
+
pushDelta("response", typeof delta === "string" ? delta : "");
|
|
7036
|
+
break;
|
|
7037
|
+
}
|
|
7038
|
+
case "response.reasoning_summary_text.delta": {
|
|
7039
|
+
const delta = event.delta ?? "";
|
|
7040
|
+
pushDelta("thought", typeof delta === "string" ? delta : "");
|
|
7041
|
+
break;
|
|
7042
|
+
}
|
|
7043
|
+
case "response.refusal.delta": {
|
|
7044
|
+
blocked = true;
|
|
7045
|
+
queue.push({ type: "blocked" });
|
|
7046
|
+
break;
|
|
7047
|
+
}
|
|
7048
|
+
default:
|
|
7049
|
+
break;
|
|
5645
7050
|
}
|
|
5646
7051
|
}
|
|
5647
|
-
|
|
5648
|
-
|
|
5649
|
-
|
|
5650
|
-
|
|
5651
|
-
|
|
5652
|
-
|
|
5653
|
-
const requestPayload = {
|
|
5654
|
-
model: modelForProvider,
|
|
5655
|
-
store: false,
|
|
5656
|
-
stream: true,
|
|
5657
|
-
...providerInfo.serviceTier ? { service_tier: providerInfo.serviceTier } : {},
|
|
5658
|
-
instructions: chatGptInput.instructions ?? "You are a helpful assistant.",
|
|
5659
|
-
input: chatGptInput.input,
|
|
5660
|
-
include: ["reasoning.encrypted_content"],
|
|
5661
|
-
reasoning: {
|
|
5662
|
-
effort: toOpenAiReasoningEffort(reasoningEffort),
|
|
5663
|
-
summary: "detailed"
|
|
5664
|
-
},
|
|
5665
|
-
text: {
|
|
5666
|
-
format: request.openAiTextFormat ?? { type: "text" },
|
|
5667
|
-
verbosity: resolveOpenAiVerbosity(request.model)
|
|
5668
|
-
},
|
|
5669
|
-
...openAiTools ? { tools: openAiTools } : {}
|
|
5670
|
-
};
|
|
5671
|
-
let sawResponseDelta = false;
|
|
5672
|
-
let sawThoughtDelta = false;
|
|
5673
|
-
const result = await collectChatGptCodexResponseWithRetry({
|
|
5674
|
-
request: requestPayload,
|
|
5675
|
-
signal,
|
|
5676
|
-
onDelta: (delta) => {
|
|
5677
|
-
if (delta.thoughtDelta) {
|
|
5678
|
-
sawThoughtDelta = true;
|
|
5679
|
-
pushDelta("thought", delta.thoughtDelta);
|
|
7052
|
+
const finalResponse = await stream.finalResponse();
|
|
7053
|
+
modelVersion = typeof finalResponse.model === "string" ? finalResponse.model : request.model;
|
|
7054
|
+
queue.push({ type: "model", modelVersion });
|
|
7055
|
+
if (finalResponse.error) {
|
|
7056
|
+
const message = typeof finalResponse.error.message === "string" ? finalResponse.error.message : "OpenAI response failed";
|
|
7057
|
+
throw new Error(message);
|
|
5680
7058
|
}
|
|
5681
|
-
if (
|
|
5682
|
-
|
|
5683
|
-
|
|
7059
|
+
if (finalResponse.status && finalResponse.status !== "completed" && finalResponse.status !== "in_progress") {
|
|
7060
|
+
const detail = finalResponse.incomplete_details?.reason;
|
|
7061
|
+
throw new Error(
|
|
7062
|
+
`OpenAI response status ${finalResponse.status}${detail ? ` (${detail})` : ""}`
|
|
7063
|
+
);
|
|
5684
7064
|
}
|
|
5685
|
-
|
|
5686
|
-
|
|
5687
|
-
|
|
5688
|
-
|
|
5689
|
-
|
|
5690
|
-
|
|
5691
|
-
|
|
5692
|
-
|
|
5693
|
-
|
|
5694
|
-
|
|
5695
|
-
|
|
5696
|
-
const fallbackText = typeof result.text === "string" ? result.text : "";
|
|
5697
|
-
const fallbackThoughts = typeof result.reasoningSummaryText === "string" && result.reasoningSummaryText.length > 0 ? result.reasoningSummaryText : typeof result.reasoningText === "string" ? result.reasoningText : "";
|
|
5698
|
-
if (!sawThoughtDelta && fallbackThoughts.length > 0) {
|
|
5699
|
-
pushDelta("thought", fallbackThoughts);
|
|
5700
|
-
}
|
|
5701
|
-
if (!sawResponseDelta && fallbackText.length > 0) {
|
|
5702
|
-
pushDelta("response", fallbackText);
|
|
5703
|
-
}
|
|
5704
|
-
} else if (provider === "fireworks") {
|
|
5705
|
-
if (request.tools && request.tools.length > 0) {
|
|
5706
|
-
throw new Error(
|
|
5707
|
-
"Fireworks provider does not support provider-native tools in generateText; use runToolLoop for function tools."
|
|
5708
|
-
);
|
|
5709
|
-
}
|
|
5710
|
-
const fireworksMessages = toFireworksMessages(contents, {
|
|
5711
|
-
responseMimeType: request.responseMimeType,
|
|
5712
|
-
responseJsonSchema: request.responseJsonSchema
|
|
5713
|
-
});
|
|
5714
|
-
await runFireworksCall(async (client) => {
|
|
5715
|
-
const responseFormat = request.responseJsonSchema ? {
|
|
5716
|
-
type: "json_schema",
|
|
5717
|
-
json_schema: {
|
|
5718
|
-
name: "llm-response",
|
|
5719
|
-
schema: request.responseJsonSchema
|
|
7065
|
+
latestUsage = extractOpenAiUsageTokens(finalResponse.usage);
|
|
7066
|
+
if (responseParts.length === 0) {
|
|
7067
|
+
const fallback = extractOpenAiResponseParts(finalResponse);
|
|
7068
|
+
blocked = blocked || fallback.blocked;
|
|
7069
|
+
for (const part of fallback.parts) {
|
|
7070
|
+
if (part.type === "text") {
|
|
7071
|
+
pushDelta(part.thought === true ? "thought" : "response", part.text);
|
|
7072
|
+
} else if (part.type === "inlineData") {
|
|
7073
|
+
pushInline(part.data, part.mimeType);
|
|
7074
|
+
}
|
|
7075
|
+
}
|
|
5720
7076
|
}
|
|
5721
|
-
}
|
|
5722
|
-
|
|
5723
|
-
|
|
5724
|
-
|
|
5725
|
-
|
|
5726
|
-
|
|
7077
|
+
}, modelForProvider);
|
|
7078
|
+
} else if (provider === "chatgpt") {
|
|
7079
|
+
const chatGptInput = toChatGptInput(contents);
|
|
7080
|
+
const reasoningEffort = resolveOpenAiReasoningEffort(request.model, request.thinkingLevel);
|
|
7081
|
+
const openAiTools = toOpenAiTools(request.tools);
|
|
7082
|
+
const requestPayload = {
|
|
7083
|
+
model: modelForProvider,
|
|
7084
|
+
store: false,
|
|
7085
|
+
stream: true,
|
|
7086
|
+
...providerInfo.serviceTier ? { service_tier: providerInfo.serviceTier } : {},
|
|
7087
|
+
instructions: chatGptInput.instructions ?? "You are a helpful assistant.",
|
|
7088
|
+
input: chatGptInput.input,
|
|
7089
|
+
include: ["reasoning.encrypted_content"],
|
|
7090
|
+
reasoning: {
|
|
7091
|
+
effort: toOpenAiReasoningEffort(reasoningEffort),
|
|
7092
|
+
summary: "detailed"
|
|
5727
7093
|
},
|
|
5728
|
-
{
|
|
5729
|
-
|
|
5730
|
-
|
|
5731
|
-
|
|
5732
|
-
|
|
5733
|
-
|
|
5734
|
-
|
|
7094
|
+
text: {
|
|
7095
|
+
format: request.openAiTextFormat ?? { type: "text" },
|
|
7096
|
+
verbosity: resolveOpenAiVerbosity(request.model)
|
|
7097
|
+
},
|
|
7098
|
+
...openAiTools ? { tools: openAiTools } : {}
|
|
7099
|
+
};
|
|
7100
|
+
let sawResponseDelta = false;
|
|
7101
|
+
let sawThoughtDelta = false;
|
|
7102
|
+
const result2 = await collectChatGptCodexResponseWithRetry({
|
|
7103
|
+
request: requestPayload,
|
|
7104
|
+
signal,
|
|
7105
|
+
onDelta: (delta) => {
|
|
7106
|
+
if (delta.thoughtDelta) {
|
|
7107
|
+
sawThoughtDelta = true;
|
|
7108
|
+
pushDelta("thought", delta.thoughtDelta);
|
|
7109
|
+
}
|
|
7110
|
+
if (delta.textDelta) {
|
|
7111
|
+
sawResponseDelta = true;
|
|
7112
|
+
pushDelta("response", delta.textDelta);
|
|
7113
|
+
}
|
|
7114
|
+
}
|
|
7115
|
+
});
|
|
7116
|
+
blocked = blocked || result2.blocked;
|
|
7117
|
+
if (blocked) {
|
|
5735
7118
|
queue.push({ type: "blocked" });
|
|
5736
7119
|
}
|
|
5737
|
-
|
|
5738
|
-
|
|
5739
|
-
|
|
5740
|
-
if (textOutput.length > 0) {
|
|
5741
|
-
pushDelta("response", textOutput);
|
|
7120
|
+
if (result2.model) {
|
|
7121
|
+
modelVersion = providerInfo.serviceTier ? request.model : `chatgpt-${result2.model}`;
|
|
7122
|
+
queue.push({ type: "model", modelVersion });
|
|
5742
7123
|
}
|
|
5743
|
-
latestUsage =
|
|
5744
|
-
|
|
5745
|
-
|
|
5746
|
-
|
|
5747
|
-
|
|
5748
|
-
|
|
5749
|
-
|
|
5750
|
-
|
|
5751
|
-
|
|
5752
|
-
|
|
5753
|
-
|
|
5754
|
-
|
|
5755
|
-
|
|
5756
|
-
|
|
5757
|
-
|
|
5758
|
-
|
|
5759
|
-
|
|
5760
|
-
|
|
5761
|
-
const geminiTools = toGeminiTools(request.tools);
|
|
5762
|
-
if (geminiTools) {
|
|
5763
|
-
config.tools = geminiTools;
|
|
5764
|
-
}
|
|
5765
|
-
await runGeminiCall(async (client) => {
|
|
5766
|
-
const stream = await client.models.generateContentStream({
|
|
5767
|
-
model: modelForProvider,
|
|
5768
|
-
contents: geminiContents,
|
|
5769
|
-
config
|
|
7124
|
+
latestUsage = extractChatGptUsageTokens(result2.usage);
|
|
7125
|
+
const fallbackText = typeof result2.text === "string" ? result2.text : "";
|
|
7126
|
+
const fallbackThoughts = typeof result2.reasoningSummaryText === "string" && result2.reasoningSummaryText.length > 0 ? result2.reasoningSummaryText : typeof result2.reasoningText === "string" ? result2.reasoningText : "";
|
|
7127
|
+
if (!sawThoughtDelta && fallbackThoughts.length > 0) {
|
|
7128
|
+
pushDelta("thought", fallbackThoughts);
|
|
7129
|
+
}
|
|
7130
|
+
if (!sawResponseDelta && fallbackText.length > 0) {
|
|
7131
|
+
pushDelta("response", fallbackText);
|
|
7132
|
+
}
|
|
7133
|
+
} else if (provider === "fireworks") {
|
|
7134
|
+
if (request.tools && request.tools.length > 0) {
|
|
7135
|
+
throw new Error(
|
|
7136
|
+
"Fireworks provider does not support provider-native tools in generateText; use runToolLoop for function tools."
|
|
7137
|
+
);
|
|
7138
|
+
}
|
|
7139
|
+
const fireworksMessages = toFireworksMessages(contents, {
|
|
7140
|
+
responseMimeType: request.responseMimeType,
|
|
7141
|
+
responseJsonSchema: request.responseJsonSchema
|
|
5770
7142
|
});
|
|
5771
|
-
|
|
5772
|
-
|
|
5773
|
-
|
|
5774
|
-
|
|
5775
|
-
|
|
5776
|
-
|
|
5777
|
-
|
|
7143
|
+
await runFireworksCall(async (client) => {
|
|
7144
|
+
const responseFormat = request.responseJsonSchema ? {
|
|
7145
|
+
type: "json_schema",
|
|
7146
|
+
json_schema: {
|
|
7147
|
+
name: "llm-response",
|
|
7148
|
+
schema: request.responseJsonSchema
|
|
7149
|
+
}
|
|
7150
|
+
} : request.responseMimeType === "application/json" ? { type: "json_object" } : void 0;
|
|
7151
|
+
const response = await client.chat.completions.create(
|
|
7152
|
+
{
|
|
7153
|
+
model: modelForProvider,
|
|
7154
|
+
messages: fireworksMessages,
|
|
7155
|
+
...responseFormat ? { response_format: responseFormat } : {}
|
|
7156
|
+
},
|
|
7157
|
+
{ signal }
|
|
7158
|
+
);
|
|
7159
|
+
modelVersion = typeof response.model === "string" ? response.model : request.model;
|
|
7160
|
+
queue.push({ type: "model", modelVersion });
|
|
7161
|
+
const choice = Array.isArray(response.choices) ? response.choices[0] : void 0;
|
|
7162
|
+
if (choice?.finish_reason === "content_filter") {
|
|
5778
7163
|
blocked = true;
|
|
5779
7164
|
queue.push({ type: "blocked" });
|
|
5780
7165
|
}
|
|
5781
|
-
|
|
5782
|
-
|
|
5783
|
-
extractGeminiUsageTokens(chunk.usageMetadata)
|
|
7166
|
+
const textOutput = extractFireworksMessageText(
|
|
7167
|
+
choice?.message
|
|
5784
7168
|
);
|
|
5785
|
-
|
|
5786
|
-
|
|
5787
|
-
continue;
|
|
7169
|
+
if (textOutput.length > 0) {
|
|
7170
|
+
pushDelta("response", textOutput);
|
|
5788
7171
|
}
|
|
5789
|
-
|
|
5790
|
-
|
|
5791
|
-
|
|
5792
|
-
|
|
5793
|
-
|
|
5794
|
-
|
|
5795
|
-
|
|
5796
|
-
|
|
5797
|
-
|
|
7172
|
+
latestUsage = extractFireworksUsageTokens(response.usage);
|
|
7173
|
+
}, modelForProvider);
|
|
7174
|
+
} else {
|
|
7175
|
+
const geminiContents = await maybePrepareGeminiPromptContents(
|
|
7176
|
+
contents.map(convertLlmContentToGeminiContent)
|
|
7177
|
+
);
|
|
7178
|
+
const thinkingConfig = resolveGeminiThinkingConfig(modelForProvider, request.thinkingLevel);
|
|
7179
|
+
const config = {
|
|
7180
|
+
maxOutputTokens: 32e3,
|
|
7181
|
+
...thinkingConfig ? { thinkingConfig } : {},
|
|
7182
|
+
...request.responseMimeType ? { responseMimeType: request.responseMimeType } : {},
|
|
7183
|
+
...request.responseJsonSchema ? { responseJsonSchema: request.responseJsonSchema } : {},
|
|
7184
|
+
...request.responseModalities ? { responseModalities: Array.from(request.responseModalities) } : {},
|
|
7185
|
+
...request.imageAspectRatio || request.imageSize ? {
|
|
7186
|
+
imageConfig: {
|
|
7187
|
+
...request.imageAspectRatio ? { aspectRatio: request.imageAspectRatio } : {},
|
|
7188
|
+
...request.imageSize ? { imageSize: request.imageSize } : {}
|
|
7189
|
+
}
|
|
7190
|
+
} : {}
|
|
7191
|
+
};
|
|
7192
|
+
const geminiTools = toGeminiTools(request.tools);
|
|
7193
|
+
if (geminiTools) {
|
|
7194
|
+
config.tools = geminiTools;
|
|
7195
|
+
}
|
|
7196
|
+
await runGeminiCall(async (client) => {
|
|
7197
|
+
const stream = await client.models.generateContentStream({
|
|
7198
|
+
model: modelForProvider,
|
|
7199
|
+
contents: geminiContents,
|
|
7200
|
+
config
|
|
7201
|
+
});
|
|
7202
|
+
let latestGrounding;
|
|
7203
|
+
for await (const chunk of stream) {
|
|
7204
|
+
if (chunk.modelVersion) {
|
|
7205
|
+
modelVersion = chunk.modelVersion;
|
|
7206
|
+
queue.push({ type: "model", modelVersion });
|
|
7207
|
+
}
|
|
7208
|
+
if (chunk.promptFeedback?.blockReason) {
|
|
7209
|
+
blocked = true;
|
|
7210
|
+
queue.push({ type: "blocked" });
|
|
5798
7211
|
}
|
|
5799
|
-
|
|
5800
|
-
|
|
7212
|
+
latestUsage = mergeTokenUpdates(
|
|
7213
|
+
latestUsage,
|
|
7214
|
+
extractGeminiUsageTokens(chunk.usageMetadata)
|
|
7215
|
+
);
|
|
7216
|
+
const candidates = chunk.candidates;
|
|
7217
|
+
if (!candidates || candidates.length === 0) {
|
|
7218
|
+
continue;
|
|
5801
7219
|
}
|
|
5802
|
-
const
|
|
5803
|
-
if (
|
|
5804
|
-
|
|
7220
|
+
const primary = candidates[0];
|
|
7221
|
+
if (primary && isModerationFinish(primary.finishReason)) {
|
|
7222
|
+
blocked = true;
|
|
7223
|
+
queue.push({ type: "blocked" });
|
|
5805
7224
|
}
|
|
5806
|
-
for (const
|
|
5807
|
-
|
|
5808
|
-
|
|
5809
|
-
|
|
5810
|
-
|
|
7225
|
+
for (const candidate of candidates) {
|
|
7226
|
+
const candidateContent = candidate.content;
|
|
7227
|
+
if (!candidateContent) {
|
|
7228
|
+
continue;
|
|
7229
|
+
}
|
|
7230
|
+
if (candidate.groundingMetadata) {
|
|
7231
|
+
latestGrounding = candidate.groundingMetadata;
|
|
7232
|
+
}
|
|
7233
|
+
const content2 = convertGeminiContentToLlmContent(candidateContent);
|
|
7234
|
+
if (!responseRole) {
|
|
7235
|
+
responseRole = content2.role;
|
|
7236
|
+
}
|
|
7237
|
+
for (const part of content2.parts) {
|
|
7238
|
+
if (part.type === "text") {
|
|
7239
|
+
pushDelta(part.thought === true ? "thought" : "response", part.text);
|
|
7240
|
+
} else if (part.type === "inlineData") {
|
|
7241
|
+
pushInline(part.data, part.mimeType);
|
|
7242
|
+
}
|
|
5811
7243
|
}
|
|
5812
7244
|
}
|
|
5813
7245
|
}
|
|
7246
|
+
grounding = latestGrounding;
|
|
7247
|
+
}, modelForProvider);
|
|
7248
|
+
}
|
|
7249
|
+
const mergedParts = mergeConsecutiveTextParts(responseParts);
|
|
7250
|
+
const content = mergedParts.length > 0 ? { role: responseRole ?? "assistant", parts: mergedParts } : void 0;
|
|
7251
|
+
const { text, thoughts } = extractTextByChannel(content);
|
|
7252
|
+
const outputAttachments = collectLoggedAttachmentsFromLlmParts(mergedParts, "output");
|
|
7253
|
+
const costUsd = estimateCallCostUsd({
|
|
7254
|
+
modelId: modelVersion,
|
|
7255
|
+
tokens: latestUsage,
|
|
7256
|
+
responseImages,
|
|
7257
|
+
imageSize: request.imageSize
|
|
7258
|
+
});
|
|
7259
|
+
if (latestUsage) {
|
|
7260
|
+
queue.push({ type: "usage", usage: latestUsage, costUsd, modelVersion });
|
|
7261
|
+
}
|
|
7262
|
+
callLogger?.complete({
|
|
7263
|
+
responseText: text,
|
|
7264
|
+
attachments: outputAttachments,
|
|
7265
|
+
metadata: {
|
|
7266
|
+
provider,
|
|
7267
|
+
model: request.model,
|
|
7268
|
+
modelVersion,
|
|
7269
|
+
blocked,
|
|
7270
|
+
costUsd,
|
|
7271
|
+
usage: latestUsage,
|
|
7272
|
+
grounding: grounding ? sanitiseLogValue(grounding) : void 0,
|
|
7273
|
+
responseChars: text.length,
|
|
7274
|
+
thoughtChars: thoughts.length,
|
|
7275
|
+
responseImages,
|
|
7276
|
+
uploads: getCurrentFileUploadMetrics()
|
|
5814
7277
|
}
|
|
5815
|
-
|
|
5816
|
-
|
|
5817
|
-
}
|
|
5818
|
-
const mergedParts = mergeConsecutiveTextParts(responseParts);
|
|
5819
|
-
const content = mergedParts.length > 0 ? { role: responseRole ?? "assistant", parts: mergedParts } : void 0;
|
|
5820
|
-
const { text, thoughts } = extractTextByChannel(content);
|
|
5821
|
-
const outputAttachments = collectLoggedAttachmentsFromLlmParts(mergedParts, "output");
|
|
5822
|
-
const costUsd = estimateCallCostUsd({
|
|
5823
|
-
modelId: modelVersion,
|
|
5824
|
-
tokens: latestUsage,
|
|
5825
|
-
responseImages,
|
|
5826
|
-
imageSize: request.imageSize
|
|
5827
|
-
});
|
|
5828
|
-
if (latestUsage) {
|
|
5829
|
-
queue.push({ type: "usage", usage: latestUsage, costUsd, modelVersion });
|
|
5830
|
-
}
|
|
5831
|
-
callLogger?.complete({
|
|
5832
|
-
responseText: text,
|
|
5833
|
-
attachments: outputAttachments,
|
|
5834
|
-
metadata: {
|
|
5835
|
-
provider,
|
|
5836
|
-
model: request.model,
|
|
5837
|
-
modelVersion,
|
|
5838
|
-
blocked,
|
|
5839
|
-
costUsd,
|
|
5840
|
-
usage: latestUsage,
|
|
5841
|
-
grounding: grounding ? sanitiseLogValue(grounding) : void 0,
|
|
5842
|
-
responseChars: text.length,
|
|
5843
|
-
thoughtChars: thoughts.length,
|
|
5844
|
-
responseImages
|
|
5845
|
-
}
|
|
5846
|
-
});
|
|
5847
|
-
return {
|
|
5848
|
-
provider,
|
|
5849
|
-
model: request.model,
|
|
5850
|
-
modelVersion,
|
|
5851
|
-
content,
|
|
5852
|
-
text,
|
|
5853
|
-
thoughts,
|
|
5854
|
-
blocked,
|
|
5855
|
-
usage: latestUsage,
|
|
5856
|
-
costUsd,
|
|
5857
|
-
grounding
|
|
5858
|
-
};
|
|
5859
|
-
} catch (error) {
|
|
5860
|
-
const partialParts = mergeConsecutiveTextParts(responseParts);
|
|
5861
|
-
const partialContent = partialParts.length > 0 ? { role: responseRole ?? "assistant", parts: partialParts } : void 0;
|
|
5862
|
-
const { text: partialText } = extractTextByChannel(partialContent);
|
|
5863
|
-
callLogger?.fail(error, {
|
|
5864
|
-
responseText: partialText,
|
|
5865
|
-
attachments: collectLoggedAttachmentsFromLlmParts(partialParts, "output"),
|
|
5866
|
-
metadata: {
|
|
7278
|
+
});
|
|
7279
|
+
return {
|
|
5867
7280
|
provider,
|
|
5868
7281
|
model: request.model,
|
|
5869
7282
|
modelVersion,
|
|
7283
|
+
content,
|
|
7284
|
+
text,
|
|
7285
|
+
thoughts,
|
|
5870
7286
|
blocked,
|
|
5871
7287
|
usage: latestUsage,
|
|
5872
|
-
|
|
5873
|
-
|
|
5874
|
-
}
|
|
5875
|
-
})
|
|
5876
|
-
|
|
5877
|
-
|
|
7288
|
+
costUsd,
|
|
7289
|
+
grounding
|
|
7290
|
+
};
|
|
7291
|
+
} catch (error) {
|
|
7292
|
+
const partialParts = mergeConsecutiveTextParts(responseParts);
|
|
7293
|
+
const partialContent = partialParts.length > 0 ? { role: responseRole ?? "assistant", parts: partialParts } : void 0;
|
|
7294
|
+
const { text: partialText } = extractTextByChannel(partialContent);
|
|
7295
|
+
callLogger?.fail(error, {
|
|
7296
|
+
responseText: partialText,
|
|
7297
|
+
attachments: collectLoggedAttachmentsFromLlmParts(partialParts, "output"),
|
|
7298
|
+
metadata: {
|
|
7299
|
+
provider,
|
|
7300
|
+
model: request.model,
|
|
7301
|
+
modelVersion,
|
|
7302
|
+
blocked,
|
|
7303
|
+
usage: latestUsage,
|
|
7304
|
+
partialResponseParts: responseParts.length,
|
|
7305
|
+
responseImages,
|
|
7306
|
+
uploads: getCurrentFileUploadMetrics()
|
|
7307
|
+
}
|
|
7308
|
+
});
|
|
7309
|
+
throw error;
|
|
7310
|
+
}
|
|
7311
|
+
});
|
|
7312
|
+
return result;
|
|
5878
7313
|
}
|
|
5879
7314
|
function streamText(request) {
|
|
5880
7315
|
const queue = createAsyncQueue();
|
|
@@ -6157,7 +7592,12 @@ function normalizeToolLoopSteeringInput(input) {
|
|
|
6157
7592
|
if (part.type === "text") {
|
|
6158
7593
|
parts.push({ type: "text", text: part.text });
|
|
6159
7594
|
} else {
|
|
6160
|
-
parts.push({
|
|
7595
|
+
parts.push({
|
|
7596
|
+
type: "inlineData",
|
|
7597
|
+
data: part.data,
|
|
7598
|
+
mimeType: part.mimeType,
|
|
7599
|
+
filename: part.filename
|
|
7600
|
+
});
|
|
6161
7601
|
}
|
|
6162
7602
|
}
|
|
6163
7603
|
if (parts.length > 0) {
|
|
@@ -6345,9 +7785,10 @@ async function runToolLoop(request) {
|
|
|
6345
7785
|
let reasoningSummary = "";
|
|
6346
7786
|
let stepToolCallText;
|
|
6347
7787
|
let stepToolCallPayload;
|
|
7788
|
+
const preparedInput = await maybePrepareOpenAiPromptInput(input);
|
|
6348
7789
|
const stepRequestPayload = {
|
|
6349
7790
|
model: providerInfo.model,
|
|
6350
|
-
input,
|
|
7791
|
+
input: preparedInput,
|
|
6351
7792
|
...previousResponseId ? { previous_response_id: previousResponseId } : {},
|
|
6352
7793
|
...openAiTools.length > 0 ? { tools: openAiTools } : {},
|
|
6353
7794
|
...openAiTools.length > 0 ? { parallel_tool_calls: true } : {},
|
|
@@ -6375,7 +7816,7 @@ async function runToolLoop(request) {
|
|
|
6375
7816
|
const stream = client.responses.stream(
|
|
6376
7817
|
{
|
|
6377
7818
|
model: providerInfo.model,
|
|
6378
|
-
input,
|
|
7819
|
+
input: preparedInput,
|
|
6379
7820
|
...previousResponseId ? { previous_response_id: previousResponseId } : {},
|
|
6380
7821
|
...openAiTools.length > 0 ? { tools: openAiTools } : {},
|
|
6381
7822
|
...openAiTools.length > 0 ? { parallel_tool_calls: true } : {},
|
|
@@ -6552,27 +7993,29 @@ async function runToolLoop(request) {
|
|
|
6552
7993
|
input: entry.value
|
|
6553
7994
|
});
|
|
6554
7995
|
}
|
|
6555
|
-
const callResults = await
|
|
6556
|
-
|
|
6557
|
-
|
|
6558
|
-
|
|
6559
|
-
|
|
6560
|
-
toolId: entry.toolId,
|
|
6561
|
-
turn: entry.turn,
|
|
6562
|
-
toolIndex: entry.toolIndex
|
|
6563
|
-
},
|
|
6564
|
-
async () => {
|
|
6565
|
-
const { result, outputPayload } = await executeToolCall({
|
|
6566
|
-
callKind: entry.call.kind,
|
|
7996
|
+
const callResults = await maybeSpillCombinedToolCallOutputs(
|
|
7997
|
+
await Promise.all(
|
|
7998
|
+
callInputs.map(async (entry) => {
|
|
7999
|
+
return await toolCallContextStorage.run(
|
|
8000
|
+
{
|
|
6567
8001
|
toolName: entry.toolName,
|
|
6568
|
-
|
|
6569
|
-
|
|
6570
|
-
|
|
6571
|
-
}
|
|
6572
|
-
|
|
6573
|
-
|
|
6574
|
-
|
|
6575
|
-
|
|
8002
|
+
toolId: entry.toolId,
|
|
8003
|
+
turn: entry.turn,
|
|
8004
|
+
toolIndex: entry.toolIndex
|
|
8005
|
+
},
|
|
8006
|
+
async () => {
|
|
8007
|
+
const { result, outputPayload } = await executeToolCall({
|
|
8008
|
+
callKind: entry.call.kind,
|
|
8009
|
+
toolName: entry.toolName,
|
|
8010
|
+
tool: request.tools[entry.toolName],
|
|
8011
|
+
rawInput: entry.value,
|
|
8012
|
+
parseError: entry.parseError
|
|
8013
|
+
});
|
|
8014
|
+
return { entry, result, outputPayload };
|
|
8015
|
+
}
|
|
8016
|
+
);
|
|
8017
|
+
})
|
|
8018
|
+
)
|
|
6576
8019
|
);
|
|
6577
8020
|
const toolOutputs = [];
|
|
6578
8021
|
let toolExecutionMs = 0;
|
|
@@ -6853,27 +8296,29 @@ async function runToolLoop(request) {
|
|
|
6853
8296
|
input: entry.value
|
|
6854
8297
|
});
|
|
6855
8298
|
}
|
|
6856
|
-
const callResults = await
|
|
6857
|
-
|
|
6858
|
-
|
|
6859
|
-
|
|
6860
|
-
|
|
6861
|
-
toolId: entry.toolId,
|
|
6862
|
-
turn: entry.turn,
|
|
6863
|
-
toolIndex: entry.toolIndex
|
|
6864
|
-
},
|
|
6865
|
-
async () => {
|
|
6866
|
-
const { result, outputPayload } = await executeToolCall({
|
|
6867
|
-
callKind: entry.call.kind,
|
|
8299
|
+
const callResults = await maybeSpillCombinedToolCallOutputs(
|
|
8300
|
+
await Promise.all(
|
|
8301
|
+
callInputs.map(async (entry) => {
|
|
8302
|
+
return await toolCallContextStorage.run(
|
|
8303
|
+
{
|
|
6868
8304
|
toolName: entry.toolName,
|
|
6869
|
-
|
|
6870
|
-
|
|
6871
|
-
|
|
6872
|
-
}
|
|
6873
|
-
|
|
6874
|
-
|
|
6875
|
-
|
|
6876
|
-
|
|
8305
|
+
toolId: entry.toolId,
|
|
8306
|
+
turn: entry.turn,
|
|
8307
|
+
toolIndex: entry.toolIndex
|
|
8308
|
+
},
|
|
8309
|
+
async () => {
|
|
8310
|
+
const { result, outputPayload } = await executeToolCall({
|
|
8311
|
+
callKind: entry.call.kind,
|
|
8312
|
+
toolName: entry.toolName,
|
|
8313
|
+
tool: request.tools[entry.toolName],
|
|
8314
|
+
rawInput: entry.value,
|
|
8315
|
+
parseError: entry.parseError
|
|
8316
|
+
});
|
|
8317
|
+
return { entry, result, outputPayload };
|
|
8318
|
+
}
|
|
8319
|
+
);
|
|
8320
|
+
})
|
|
8321
|
+
)
|
|
6877
8322
|
);
|
|
6878
8323
|
let toolExecutionMs = 0;
|
|
6879
8324
|
let waitToolMs = 0;
|
|
@@ -7145,27 +8590,29 @@ async function runToolLoop(request) {
|
|
|
7145
8590
|
input: entry.value
|
|
7146
8591
|
});
|
|
7147
8592
|
}
|
|
7148
|
-
const callResults = await
|
|
7149
|
-
|
|
7150
|
-
|
|
7151
|
-
|
|
7152
|
-
|
|
7153
|
-
toolId: entry.toolId,
|
|
7154
|
-
turn: entry.turn,
|
|
7155
|
-
toolIndex: entry.toolIndex
|
|
7156
|
-
},
|
|
7157
|
-
async () => {
|
|
7158
|
-
const { result, outputPayload } = await executeToolCall({
|
|
7159
|
-
callKind: "function",
|
|
8593
|
+
const callResults = await maybeSpillCombinedToolCallOutputs(
|
|
8594
|
+
await Promise.all(
|
|
8595
|
+
callInputs.map(async (entry) => {
|
|
8596
|
+
return await toolCallContextStorage.run(
|
|
8597
|
+
{
|
|
7160
8598
|
toolName: entry.toolName,
|
|
7161
|
-
|
|
7162
|
-
|
|
7163
|
-
|
|
7164
|
-
}
|
|
7165
|
-
|
|
7166
|
-
|
|
7167
|
-
|
|
7168
|
-
|
|
8599
|
+
toolId: entry.toolId,
|
|
8600
|
+
turn: entry.turn,
|
|
8601
|
+
toolIndex: entry.toolIndex
|
|
8602
|
+
},
|
|
8603
|
+
async () => {
|
|
8604
|
+
const { result, outputPayload } = await executeToolCall({
|
|
8605
|
+
callKind: "function",
|
|
8606
|
+
toolName: entry.toolName,
|
|
8607
|
+
tool: request.tools[entry.toolName],
|
|
8608
|
+
rawInput: entry.value,
|
|
8609
|
+
parseError: entry.parseError
|
|
8610
|
+
});
|
|
8611
|
+
return { entry, result, outputPayload };
|
|
8612
|
+
}
|
|
8613
|
+
);
|
|
8614
|
+
})
|
|
8615
|
+
)
|
|
7169
8616
|
);
|
|
7170
8617
|
const assistantToolCalls = [];
|
|
7171
8618
|
const toolMessages = [];
|
|
@@ -7304,9 +8751,10 @@ async function runToolLoop(request) {
|
|
|
7304
8751
|
...thinkingConfig ? { thinkingConfig } : {}
|
|
7305
8752
|
};
|
|
7306
8753
|
const onEvent = request.onEvent;
|
|
8754
|
+
const preparedGeminiContents = await maybePrepareGeminiPromptContents(geminiContents);
|
|
7307
8755
|
const stepRequestPayload = {
|
|
7308
8756
|
model: request.model,
|
|
7309
|
-
contents:
|
|
8757
|
+
contents: preparedGeminiContents,
|
|
7310
8758
|
config
|
|
7311
8759
|
};
|
|
7312
8760
|
const stepCallLogger = startLlmCallLoggerFromPayload({
|
|
@@ -7320,7 +8768,7 @@ async function runToolLoop(request) {
|
|
|
7320
8768
|
async (client) => {
|
|
7321
8769
|
const stream = await client.models.generateContentStream({
|
|
7322
8770
|
model: request.model,
|
|
7323
|
-
contents:
|
|
8771
|
+
contents: preparedGeminiContents,
|
|
7324
8772
|
config
|
|
7325
8773
|
});
|
|
7326
8774
|
let responseText2 = "";
|
|
@@ -7496,26 +8944,28 @@ async function runToolLoop(request) {
|
|
|
7496
8944
|
input: entry.rawInput
|
|
7497
8945
|
});
|
|
7498
8946
|
}
|
|
7499
|
-
const callResults = await
|
|
7500
|
-
|
|
7501
|
-
|
|
7502
|
-
|
|
7503
|
-
|
|
7504
|
-
toolId: entry.toolId,
|
|
7505
|
-
turn: entry.turn,
|
|
7506
|
-
toolIndex: entry.toolIndex
|
|
7507
|
-
},
|
|
7508
|
-
async () => {
|
|
7509
|
-
const { result, outputPayload } = await executeToolCall({
|
|
7510
|
-
callKind: "function",
|
|
8947
|
+
const callResults = await maybeSpillCombinedToolCallOutputs(
|
|
8948
|
+
await Promise.all(
|
|
8949
|
+
callInputs.map(async (entry) => {
|
|
8950
|
+
return await toolCallContextStorage.run(
|
|
8951
|
+
{
|
|
7511
8952
|
toolName: entry.toolName,
|
|
7512
|
-
|
|
7513
|
-
|
|
7514
|
-
|
|
7515
|
-
|
|
7516
|
-
|
|
7517
|
-
|
|
7518
|
-
|
|
8953
|
+
toolId: entry.toolId,
|
|
8954
|
+
turn: entry.turn,
|
|
8955
|
+
toolIndex: entry.toolIndex
|
|
8956
|
+
},
|
|
8957
|
+
async () => {
|
|
8958
|
+
const { result, outputPayload } = await executeToolCall({
|
|
8959
|
+
callKind: "function",
|
|
8960
|
+
toolName: entry.toolName,
|
|
8961
|
+
tool: request.tools[entry.toolName],
|
|
8962
|
+
rawInput: entry.rawInput
|
|
8963
|
+
});
|
|
8964
|
+
return { entry, result, outputPayload };
|
|
8965
|
+
}
|
|
8966
|
+
);
|
|
8967
|
+
})
|
|
8968
|
+
)
|
|
7519
8969
|
);
|
|
7520
8970
|
let toolExecutionMs = 0;
|
|
7521
8971
|
let waitToolMs = 0;
|
|
@@ -7920,7 +9370,7 @@ ${lines}`;
|
|
|
7920
9370
|
|
|
7921
9371
|
// src/agent.ts
|
|
7922
9372
|
import { randomBytes as randomBytes3 } from "crypto";
|
|
7923
|
-
import
|
|
9373
|
+
import path9 from "path";
|
|
7924
9374
|
|
|
7925
9375
|
// src/agent/subagents.ts
|
|
7926
9376
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
@@ -8395,26 +9845,26 @@ function resolveInputItemsText(items) {
|
|
|
8395
9845
|
}
|
|
8396
9846
|
const itemType = typeof item.type === "string" ? item.type.trim() : "";
|
|
8397
9847
|
const name = typeof item.name === "string" ? item.name.trim() : "";
|
|
8398
|
-
const
|
|
9848
|
+
const path10 = typeof item.path === "string" ? item.path.trim() : "";
|
|
8399
9849
|
const imageUrl = typeof item.image_url === "string" ? item.image_url.trim() : "";
|
|
8400
9850
|
if (itemType === "image") {
|
|
8401
9851
|
lines.push("[image]");
|
|
8402
9852
|
continue;
|
|
8403
9853
|
}
|
|
8404
|
-
if (itemType === "local_image" &&
|
|
8405
|
-
lines.push(`[local_image:${
|
|
9854
|
+
if (itemType === "local_image" && path10) {
|
|
9855
|
+
lines.push(`[local_image:${path10}]`);
|
|
8406
9856
|
continue;
|
|
8407
9857
|
}
|
|
8408
|
-
if (itemType === "skill" && name &&
|
|
8409
|
-
lines.push(`[skill:$${name}](${
|
|
9858
|
+
if (itemType === "skill" && name && path10) {
|
|
9859
|
+
lines.push(`[skill:$${name}](${path10})`);
|
|
8410
9860
|
continue;
|
|
8411
9861
|
}
|
|
8412
|
-
if (itemType === "mention" && name &&
|
|
8413
|
-
lines.push(`[mention:$${name}](${
|
|
9862
|
+
if (itemType === "mention" && name && path10) {
|
|
9863
|
+
lines.push(`[mention:$${name}](${path10})`);
|
|
8414
9864
|
continue;
|
|
8415
9865
|
}
|
|
8416
|
-
if (
|
|
8417
|
-
lines.push(`[${itemType || "input"}:${
|
|
9866
|
+
if (path10 || imageUrl) {
|
|
9867
|
+
lines.push(`[${itemType || "input"}:${path10 || imageUrl}]`);
|
|
8418
9868
|
continue;
|
|
8419
9869
|
}
|
|
8420
9870
|
if (name) {
|
|
@@ -8732,27 +10182,27 @@ function sleep2(ms) {
|
|
|
8732
10182
|
}
|
|
8733
10183
|
|
|
8734
10184
|
// src/tools/filesystemTools.ts
|
|
8735
|
-
import
|
|
8736
|
-
import { Buffer as
|
|
10185
|
+
import path8 from "path";
|
|
10186
|
+
import { Buffer as Buffer6 } from "buffer";
|
|
8737
10187
|
import { z as z6 } from "zod";
|
|
8738
10188
|
|
|
8739
10189
|
// src/tools/applyPatch.ts
|
|
8740
|
-
import
|
|
10190
|
+
import path7 from "path";
|
|
8741
10191
|
import { z as z5 } from "zod";
|
|
8742
10192
|
|
|
8743
10193
|
// src/tools/filesystem.ts
|
|
8744
10194
|
import { promises as fs3 } from "fs";
|
|
8745
|
-
import
|
|
10195
|
+
import path6 from "path";
|
|
8746
10196
|
var InMemoryAgentFilesystem = class {
|
|
8747
10197
|
#files = /* @__PURE__ */ new Map();
|
|
8748
10198
|
#dirs = /* @__PURE__ */ new Map();
|
|
8749
10199
|
#clock = 0;
|
|
8750
10200
|
constructor(initialFiles = {}) {
|
|
8751
|
-
const root =
|
|
10201
|
+
const root = path6.resolve("/");
|
|
8752
10202
|
this.#dirs.set(root, { mtimeMs: this.#nextMtime() });
|
|
8753
10203
|
for (const [filePath, content] of Object.entries(initialFiles)) {
|
|
8754
|
-
const absolutePath =
|
|
8755
|
-
this.#ensureDirSync(
|
|
10204
|
+
const absolutePath = path6.resolve(filePath);
|
|
10205
|
+
this.#ensureDirSync(path6.dirname(absolutePath));
|
|
8756
10206
|
this.#files.set(absolutePath, {
|
|
8757
10207
|
content,
|
|
8758
10208
|
mtimeMs: this.#nextMtime()
|
|
@@ -8760,7 +10210,7 @@ var InMemoryAgentFilesystem = class {
|
|
|
8760
10210
|
}
|
|
8761
10211
|
}
|
|
8762
10212
|
async readTextFile(filePath) {
|
|
8763
|
-
const absolutePath =
|
|
10213
|
+
const absolutePath = path6.resolve(filePath);
|
|
8764
10214
|
const file = this.#files.get(absolutePath);
|
|
8765
10215
|
if (!file) {
|
|
8766
10216
|
throw createNoSuchFileError("open", absolutePath);
|
|
@@ -8772,24 +10222,24 @@ var InMemoryAgentFilesystem = class {
|
|
|
8772
10222
|
return Buffer.from(content, "utf8");
|
|
8773
10223
|
}
|
|
8774
10224
|
async writeTextFile(filePath, content) {
|
|
8775
|
-
const absolutePath =
|
|
8776
|
-
const parentPath =
|
|
10225
|
+
const absolutePath = path6.resolve(filePath);
|
|
10226
|
+
const parentPath = path6.dirname(absolutePath);
|
|
8777
10227
|
if (!this.#dirs.has(parentPath)) {
|
|
8778
10228
|
throw createNoSuchFileError("open", parentPath);
|
|
8779
10229
|
}
|
|
8780
10230
|
this.#files.set(absolutePath, { content, mtimeMs: this.#nextMtime() });
|
|
8781
10231
|
}
|
|
8782
10232
|
async deleteFile(filePath) {
|
|
8783
|
-
const absolutePath =
|
|
10233
|
+
const absolutePath = path6.resolve(filePath);
|
|
8784
10234
|
if (!this.#files.delete(absolutePath)) {
|
|
8785
10235
|
throw createNoSuchFileError("unlink", absolutePath);
|
|
8786
10236
|
}
|
|
8787
10237
|
}
|
|
8788
10238
|
async ensureDir(directoryPath) {
|
|
8789
|
-
this.#ensureDirSync(
|
|
10239
|
+
this.#ensureDirSync(path6.resolve(directoryPath));
|
|
8790
10240
|
}
|
|
8791
10241
|
async readDir(directoryPath) {
|
|
8792
|
-
const absolutePath =
|
|
10242
|
+
const absolutePath = path6.resolve(directoryPath);
|
|
8793
10243
|
const directory = this.#dirs.get(absolutePath);
|
|
8794
10244
|
if (!directory) {
|
|
8795
10245
|
throw createNoSuchFileError("scandir", absolutePath);
|
|
@@ -8800,10 +10250,10 @@ var InMemoryAgentFilesystem = class {
|
|
|
8800
10250
|
if (dirPath === absolutePath) {
|
|
8801
10251
|
continue;
|
|
8802
10252
|
}
|
|
8803
|
-
if (
|
|
10253
|
+
if (path6.dirname(dirPath) !== absolutePath) {
|
|
8804
10254
|
continue;
|
|
8805
10255
|
}
|
|
8806
|
-
const name =
|
|
10256
|
+
const name = path6.basename(dirPath);
|
|
8807
10257
|
if (seenNames.has(name)) {
|
|
8808
10258
|
continue;
|
|
8809
10259
|
}
|
|
@@ -8816,10 +10266,10 @@ var InMemoryAgentFilesystem = class {
|
|
|
8816
10266
|
});
|
|
8817
10267
|
}
|
|
8818
10268
|
for (const [filePath, fileRecord] of this.#files.entries()) {
|
|
8819
|
-
if (
|
|
10269
|
+
if (path6.dirname(filePath) !== absolutePath) {
|
|
8820
10270
|
continue;
|
|
8821
10271
|
}
|
|
8822
|
-
const name =
|
|
10272
|
+
const name = path6.basename(filePath);
|
|
8823
10273
|
if (seenNames.has(name)) {
|
|
8824
10274
|
continue;
|
|
8825
10275
|
}
|
|
@@ -8835,7 +10285,7 @@ var InMemoryAgentFilesystem = class {
|
|
|
8835
10285
|
return entries;
|
|
8836
10286
|
}
|
|
8837
10287
|
async stat(entryPath) {
|
|
8838
|
-
const absolutePath =
|
|
10288
|
+
const absolutePath = path6.resolve(entryPath);
|
|
8839
10289
|
const file = this.#files.get(absolutePath);
|
|
8840
10290
|
if (file) {
|
|
8841
10291
|
return { kind: "file", mtimeMs: file.mtimeMs };
|
|
@@ -8851,7 +10301,7 @@ var InMemoryAgentFilesystem = class {
|
|
|
8851
10301
|
return Object.fromEntries(entries.map(([filePath, record]) => [filePath, record.content]));
|
|
8852
10302
|
}
|
|
8853
10303
|
#ensureDirSync(directoryPath) {
|
|
8854
|
-
const absolutePath =
|
|
10304
|
+
const absolutePath = path6.resolve(directoryPath);
|
|
8855
10305
|
const parts = [];
|
|
8856
10306
|
let cursor = absolutePath;
|
|
8857
10307
|
for (; ; ) {
|
|
@@ -8859,7 +10309,7 @@ var InMemoryAgentFilesystem = class {
|
|
|
8859
10309
|
break;
|
|
8860
10310
|
}
|
|
8861
10311
|
parts.push(cursor);
|
|
8862
|
-
const parent =
|
|
10312
|
+
const parent = path6.dirname(cursor);
|
|
8863
10313
|
if (parent === cursor) {
|
|
8864
10314
|
break;
|
|
8865
10315
|
}
|
|
@@ -8893,7 +10343,7 @@ function createNodeAgentFilesystem() {
|
|
|
8893
10343
|
const entries = await fs3.readdir(directoryPath, { withFileTypes: true });
|
|
8894
10344
|
const result = [];
|
|
8895
10345
|
for (const entry of entries) {
|
|
8896
|
-
const entryPath =
|
|
10346
|
+
const entryPath = path6.resolve(directoryPath, entry.name);
|
|
8897
10347
|
const stats = await fs3.lstat(entryPath);
|
|
8898
10348
|
result.push({
|
|
8899
10349
|
name: entry.name,
|
|
@@ -9057,7 +10507,7 @@ function createApplyPatchTool(options = {}) {
|
|
|
9057
10507
|
});
|
|
9058
10508
|
}
|
|
9059
10509
|
async function applyPatch(request) {
|
|
9060
|
-
const cwd =
|
|
10510
|
+
const cwd = path7.resolve(request.cwd ?? process.cwd());
|
|
9061
10511
|
const adapter = request.fs ?? createNodeAgentFilesystem();
|
|
9062
10512
|
const allowOutsideCwd = request.allowOutsideCwd === true;
|
|
9063
10513
|
const patchBytes = Buffer.byteLength(request.patch, "utf8");
|
|
@@ -9079,7 +10529,7 @@ async function applyPatch(request) {
|
|
|
9079
10529
|
kind: "add",
|
|
9080
10530
|
path: absolutePath2
|
|
9081
10531
|
});
|
|
9082
|
-
await adapter.ensureDir(
|
|
10532
|
+
await adapter.ensureDir(path7.dirname(absolutePath2));
|
|
9083
10533
|
await adapter.writeTextFile(absolutePath2, operation.content);
|
|
9084
10534
|
added.push(toDisplayPath(absolutePath2, cwd));
|
|
9085
10535
|
continue;
|
|
@@ -9113,7 +10563,7 @@ async function applyPatch(request) {
|
|
|
9113
10563
|
fromPath: absolutePath,
|
|
9114
10564
|
toPath: destinationPath
|
|
9115
10565
|
});
|
|
9116
|
-
await adapter.ensureDir(
|
|
10566
|
+
await adapter.ensureDir(path7.dirname(destinationPath));
|
|
9117
10567
|
await adapter.writeTextFile(destinationPath, next);
|
|
9118
10568
|
await adapter.deleteFile(absolutePath);
|
|
9119
10569
|
modified.push(toDisplayPath(destinationPath, cwd));
|
|
@@ -9144,22 +10594,22 @@ function resolvePatchPath(rawPath, cwd, allowOutsideCwd) {
|
|
|
9144
10594
|
if (trimmed.length === 0) {
|
|
9145
10595
|
throw new Error("apply_patch failed: empty file path");
|
|
9146
10596
|
}
|
|
9147
|
-
const absolutePath =
|
|
10597
|
+
const absolutePath = path7.isAbsolute(trimmed) ? path7.resolve(trimmed) : path7.resolve(cwd, trimmed);
|
|
9148
10598
|
if (!allowOutsideCwd && !isPathInsideCwd(absolutePath, cwd)) {
|
|
9149
10599
|
throw new Error(`apply_patch failed: path "${trimmed}" resolves outside cwd "${cwd}"`);
|
|
9150
10600
|
}
|
|
9151
10601
|
return absolutePath;
|
|
9152
10602
|
}
|
|
9153
10603
|
function isPathInsideCwd(candidatePath, cwd) {
|
|
9154
|
-
const relative =
|
|
9155
|
-
return relative === "" || !relative.startsWith("..") && !
|
|
10604
|
+
const relative = path7.relative(cwd, candidatePath);
|
|
10605
|
+
return relative === "" || !relative.startsWith("..") && !path7.isAbsolute(relative);
|
|
9156
10606
|
}
|
|
9157
10607
|
function toDisplayPath(absolutePath, cwd) {
|
|
9158
|
-
const relative =
|
|
10608
|
+
const relative = path7.relative(cwd, absolutePath);
|
|
9159
10609
|
if (relative === "") {
|
|
9160
10610
|
return ".";
|
|
9161
10611
|
}
|
|
9162
|
-
if (!relative.startsWith("..") && !
|
|
10612
|
+
if (!relative.startsWith("..") && !path7.isAbsolute(relative)) {
|
|
9163
10613
|
return relative;
|
|
9164
10614
|
}
|
|
9165
10615
|
return absolutePath;
|
|
@@ -9926,7 +11376,7 @@ async function readBinaryFile(filesystem, filePath) {
|
|
|
9926
11376
|
return await filesystem.readBinaryFile(filePath);
|
|
9927
11377
|
}
|
|
9928
11378
|
const text = await filesystem.readTextFile(filePath);
|
|
9929
|
-
return
|
|
11379
|
+
return Buffer6.from(text, "utf8");
|
|
9930
11380
|
}
|
|
9931
11381
|
function detectImageMimeType(buffer, filePath) {
|
|
9932
11382
|
if (buffer.length >= 8 && buffer[0] === 137 && buffer[1] === 80 && buffer[2] === 78 && buffer[3] === 71 && buffer[4] === 13 && buffer[5] === 10 && buffer[6] === 26 && buffer[7] === 10) {
|
|
@@ -9944,7 +11394,7 @@ function detectImageMimeType(buffer, filePath) {
|
|
|
9944
11394
|
if (buffer.length >= 12 && buffer.subarray(0, 4).toString("ascii") === "RIFF" && buffer.subarray(8, 12).toString("ascii") === "WEBP") {
|
|
9945
11395
|
return "image/webp";
|
|
9946
11396
|
}
|
|
9947
|
-
const fromExtension = IMAGE_MIME_BY_EXTENSION[
|
|
11397
|
+
const fromExtension = IMAGE_MIME_BY_EXTENSION[path8.extname(filePath).toLowerCase()];
|
|
9948
11398
|
if (fromExtension && SUPPORTED_IMAGE_MIME_TYPES.has(fromExtension)) {
|
|
9949
11399
|
return fromExtension;
|
|
9950
11400
|
}
|
|
@@ -9954,13 +11404,13 @@ function isPdfFile(buffer, filePath) {
|
|
|
9954
11404
|
if (buffer.length >= 5 && buffer.subarray(0, 5).toString("ascii") === "%PDF-") {
|
|
9955
11405
|
return true;
|
|
9956
11406
|
}
|
|
9957
|
-
return
|
|
11407
|
+
return path8.extname(filePath).toLowerCase() === ".pdf";
|
|
9958
11408
|
}
|
|
9959
11409
|
function isValidUtf8(buffer) {
|
|
9960
11410
|
if (buffer.length === 0) {
|
|
9961
11411
|
return true;
|
|
9962
11412
|
}
|
|
9963
|
-
return
|
|
11413
|
+
return Buffer6.from(buffer.toString("utf8"), "utf8").equals(buffer);
|
|
9964
11414
|
}
|
|
9965
11415
|
async function readFileGemini(input, options) {
|
|
9966
11416
|
const runtime = resolveRuntime(options);
|
|
@@ -9992,7 +11442,7 @@ async function writeFileGemini(input, options) {
|
|
|
9992
11442
|
action: "write",
|
|
9993
11443
|
path: filePath
|
|
9994
11444
|
});
|
|
9995
|
-
await runtime.filesystem.ensureDir(
|
|
11445
|
+
await runtime.filesystem.ensureDir(path8.dirname(filePath));
|
|
9996
11446
|
await runtime.filesystem.writeTextFile(filePath, input.content);
|
|
9997
11447
|
return `Successfully wrote file: ${toDisplayPath2(filePath, runtime.cwd)}`;
|
|
9998
11448
|
}
|
|
@@ -10013,7 +11463,7 @@ async function replaceFileContentGemini(input, options) {
|
|
|
10013
11463
|
originalContent = await runtime.filesystem.readTextFile(filePath);
|
|
10014
11464
|
} catch (error) {
|
|
10015
11465
|
if (isNoEntError(error) && oldValue.length === 0) {
|
|
10016
|
-
await runtime.filesystem.ensureDir(
|
|
11466
|
+
await runtime.filesystem.ensureDir(path8.dirname(filePath));
|
|
10017
11467
|
await runtime.filesystem.writeTextFile(filePath, newValue);
|
|
10018
11468
|
return `Successfully wrote new file: ${toDisplayPath2(filePath, runtime.cwd)}`;
|
|
10019
11469
|
}
|
|
@@ -10183,15 +11633,15 @@ async function globFilesGemini(input, options) {
|
|
|
10183
11633
|
throw new Error(`Path is not a directory: ${dirPath}`);
|
|
10184
11634
|
}
|
|
10185
11635
|
const matcher = createGlobMatcher(input.pattern, input.case_sensitive === true);
|
|
10186
|
-
const
|
|
11636
|
+
const files2 = await collectSearchFiles({
|
|
10187
11637
|
filesystem: runtime.filesystem,
|
|
10188
11638
|
searchPath: dirPath,
|
|
10189
11639
|
rootKind: "directory",
|
|
10190
11640
|
maxScannedFiles: runtime.grepMaxScannedFiles
|
|
10191
11641
|
});
|
|
10192
11642
|
const matched = [];
|
|
10193
|
-
for (const filePath of
|
|
10194
|
-
const relativePath = normalizeSlashes(
|
|
11643
|
+
for (const filePath of files2) {
|
|
11644
|
+
const relativePath = normalizeSlashes(path8.relative(dirPath, filePath));
|
|
10195
11645
|
if (!matcher(relativePath)) {
|
|
10196
11646
|
continue;
|
|
10197
11647
|
}
|
|
@@ -10209,7 +11659,7 @@ async function globFilesGemini(input, options) {
|
|
|
10209
11659
|
}
|
|
10210
11660
|
function resolveRuntime(options) {
|
|
10211
11661
|
return {
|
|
10212
|
-
cwd:
|
|
11662
|
+
cwd: path8.resolve(options.cwd ?? process.cwd()),
|
|
10213
11663
|
filesystem: options.fs ?? createNodeAgentFilesystem(),
|
|
10214
11664
|
allowOutsideCwd: options.allowOutsideCwd === true,
|
|
10215
11665
|
checkAccess: options.checkAccess,
|
|
@@ -10240,13 +11690,13 @@ function mapApplyPatchAction(action) {
|
|
|
10240
11690
|
return "move";
|
|
10241
11691
|
}
|
|
10242
11692
|
function resolvePathWithPolicy(inputPath, cwd, allowOutsideCwd) {
|
|
10243
|
-
const absolutePath =
|
|
11693
|
+
const absolutePath = path8.isAbsolute(inputPath) ? path8.resolve(inputPath) : path8.resolve(cwd, inputPath);
|
|
10244
11694
|
if (allowOutsideCwd || isPathInsideCwd2(absolutePath, cwd)) {
|
|
10245
11695
|
return absolutePath;
|
|
10246
11696
|
}
|
|
10247
|
-
if (
|
|
11697
|
+
if (path8.isAbsolute(inputPath)) {
|
|
10248
11698
|
const sandboxRelativePath = inputPath.replace(/^[/\\]+/, "");
|
|
10249
|
-
const sandboxRootedPath =
|
|
11699
|
+
const sandboxRootedPath = path8.resolve(cwd, sandboxRelativePath);
|
|
10250
11700
|
if (isPathInsideCwd2(sandboxRootedPath, cwd)) {
|
|
10251
11701
|
return sandboxRootedPath;
|
|
10252
11702
|
}
|
|
@@ -10254,25 +11704,25 @@ function resolvePathWithPolicy(inputPath, cwd, allowOutsideCwd) {
|
|
|
10254
11704
|
throw new Error(`path "${inputPath}" resolves outside cwd "${cwd}"`);
|
|
10255
11705
|
}
|
|
10256
11706
|
function isPathInsideCwd2(candidatePath, cwd) {
|
|
10257
|
-
const relative =
|
|
10258
|
-
return relative === "" || !relative.startsWith("..") && !
|
|
11707
|
+
const relative = path8.relative(cwd, candidatePath);
|
|
11708
|
+
return relative === "" || !relative.startsWith("..") && !path8.isAbsolute(relative);
|
|
10259
11709
|
}
|
|
10260
11710
|
function toDisplayPath2(absolutePath, cwd) {
|
|
10261
|
-
const relative =
|
|
11711
|
+
const relative = path8.relative(cwd, absolutePath);
|
|
10262
11712
|
if (relative === "") {
|
|
10263
11713
|
return ".";
|
|
10264
11714
|
}
|
|
10265
|
-
if (!relative.startsWith("..") && !
|
|
11715
|
+
if (!relative.startsWith("..") && !path8.isAbsolute(relative)) {
|
|
10266
11716
|
return relative;
|
|
10267
11717
|
}
|
|
10268
11718
|
return absolutePath;
|
|
10269
11719
|
}
|
|
10270
11720
|
function toSandboxDisplayPath(absolutePath, cwd) {
|
|
10271
|
-
const relative =
|
|
11721
|
+
const relative = path8.relative(cwd, absolutePath);
|
|
10272
11722
|
if (relative === "") {
|
|
10273
11723
|
return "/";
|
|
10274
11724
|
}
|
|
10275
|
-
if (!relative.startsWith("..") && !
|
|
11725
|
+
if (!relative.startsWith("..") && !path8.isAbsolute(relative)) {
|
|
10276
11726
|
return `/${normalizeSlashes(relative)}`;
|
|
10277
11727
|
}
|
|
10278
11728
|
return normalizeSlashes(absolutePath);
|
|
@@ -10348,7 +11798,7 @@ async function collectSearchFiles(params) {
|
|
|
10348
11798
|
return [searchPath];
|
|
10349
11799
|
}
|
|
10350
11800
|
const queue = [searchPath];
|
|
10351
|
-
const
|
|
11801
|
+
const files2 = [];
|
|
10352
11802
|
while (queue.length > 0) {
|
|
10353
11803
|
const current = queue.shift();
|
|
10354
11804
|
if (!current) {
|
|
@@ -10363,13 +11813,13 @@ async function collectSearchFiles(params) {
|
|
|
10363
11813
|
if (entry.kind !== "file") {
|
|
10364
11814
|
continue;
|
|
10365
11815
|
}
|
|
10366
|
-
|
|
10367
|
-
if (
|
|
10368
|
-
return
|
|
11816
|
+
files2.push(entry.path);
|
|
11817
|
+
if (files2.length >= maxScannedFiles) {
|
|
11818
|
+
return files2;
|
|
10369
11819
|
}
|
|
10370
11820
|
}
|
|
10371
11821
|
}
|
|
10372
|
-
return
|
|
11822
|
+
return files2;
|
|
10373
11823
|
}
|
|
10374
11824
|
function compileRegex(pattern, flags = "m") {
|
|
10375
11825
|
try {
|
|
@@ -10388,7 +11838,7 @@ function createGlobMatcher(pattern, caseSensitive = false) {
|
|
|
10388
11838
|
}));
|
|
10389
11839
|
return (candidatePath) => {
|
|
10390
11840
|
const normalizedPath = normalizeSlashes(candidatePath);
|
|
10391
|
-
const basename =
|
|
11841
|
+
const basename = path8.posix.basename(normalizedPath);
|
|
10392
11842
|
return compiled.some(
|
|
10393
11843
|
(entry) => entry.regex.test(entry.applyToBasename ? basename : normalizedPath)
|
|
10394
11844
|
);
|
|
@@ -10665,13 +12115,24 @@ async function runAgentLoopInternal(request, context) {
|
|
|
10665
12115
|
}
|
|
10666
12116
|
streamEventLogger?.appendEvent(event);
|
|
10667
12117
|
} : void 0;
|
|
12118
|
+
let uploadMetrics = emptyFileUploadMetrics();
|
|
10668
12119
|
try {
|
|
10669
|
-
|
|
10670
|
-
|
|
10671
|
-
|
|
10672
|
-
|
|
10673
|
-
|
|
12120
|
+
let result;
|
|
12121
|
+
await collectFileUploadMetrics(async () => {
|
|
12122
|
+
try {
|
|
12123
|
+
result = await runToolLoop({
|
|
12124
|
+
...toolLoopRequestWithSteering,
|
|
12125
|
+
...instructions ? { instructions } : {},
|
|
12126
|
+
...wrappedOnEvent ? { onEvent: wrappedOnEvent } : {},
|
|
12127
|
+
tools: mergedTools
|
|
12128
|
+
});
|
|
12129
|
+
} finally {
|
|
12130
|
+
uploadMetrics = getCurrentFileUploadMetrics();
|
|
12131
|
+
}
|
|
10674
12132
|
});
|
|
12133
|
+
if (!result) {
|
|
12134
|
+
throw new Error("runToolLoop returned no result.");
|
|
12135
|
+
}
|
|
10675
12136
|
streamEventLogger?.flush();
|
|
10676
12137
|
emitTelemetry({
|
|
10677
12138
|
type: "agent.run.completed",
|
|
@@ -10680,7 +12141,10 @@ async function runAgentLoopInternal(request, context) {
|
|
|
10680
12141
|
stepCount: result.steps.length,
|
|
10681
12142
|
toolCallCount: countToolCalls(result),
|
|
10682
12143
|
totalCostUsd: result.totalCostUsd,
|
|
10683
|
-
usage: summarizeResultUsage(result)
|
|
12144
|
+
usage: summarizeResultUsage(result),
|
|
12145
|
+
uploadCount: uploadMetrics.count,
|
|
12146
|
+
uploadBytes: uploadMetrics.totalBytes,
|
|
12147
|
+
uploadLatencyMs: uploadMetrics.totalLatencyMs
|
|
10684
12148
|
});
|
|
10685
12149
|
loggingSession?.logLine(
|
|
10686
12150
|
[
|
|
@@ -10689,7 +12153,10 @@ async function runAgentLoopInternal(request, context) {
|
|
|
10689
12153
|
`durationMs=${Math.max(0, Date.now() - startedAtMs).toString()}`,
|
|
10690
12154
|
`steps=${result.steps.length.toString()}`,
|
|
10691
12155
|
`toolCalls=${countToolCalls(result).toString()}`,
|
|
10692
|
-
`totalCostUsd=${(result.totalCostUsd ?? 0).toFixed(6)}
|
|
12156
|
+
`totalCostUsd=${(result.totalCostUsd ?? 0).toFixed(6)}`,
|
|
12157
|
+
`uploadCount=${uploadMetrics.count.toString()}`,
|
|
12158
|
+
`uploadBytes=${uploadMetrics.totalBytes.toString()}`,
|
|
12159
|
+
`uploadLatencyMs=${uploadMetrics.totalLatencyMs.toString()}`
|
|
10693
12160
|
].join(" ")
|
|
10694
12161
|
);
|
|
10695
12162
|
for (const step of result.steps) {
|
|
@@ -10710,6 +12177,9 @@ async function runAgentLoopInternal(request, context) {
|
|
|
10710
12177
|
type: "agent.run.completed",
|
|
10711
12178
|
success: false,
|
|
10712
12179
|
durationMs: Math.max(0, Date.now() - startedAtMs),
|
|
12180
|
+
uploadCount: uploadMetrics.count,
|
|
12181
|
+
uploadBytes: uploadMetrics.totalBytes,
|
|
12182
|
+
uploadLatencyMs: uploadMetrics.totalLatencyMs,
|
|
10713
12183
|
error: toErrorMessage3(error)
|
|
10714
12184
|
});
|
|
10715
12185
|
loggingSession?.logLine(
|
|
@@ -10717,6 +12187,9 @@ async function runAgentLoopInternal(request, context) {
|
|
|
10717
12187
|
`[agent:${runId}] run_completed`,
|
|
10718
12188
|
`status=error`,
|
|
10719
12189
|
`durationMs=${Math.max(0, Date.now() - startedAtMs).toString()}`,
|
|
12190
|
+
`uploadCount=${uploadMetrics.count.toString()}`,
|
|
12191
|
+
`uploadBytes=${uploadMetrics.totalBytes.toString()}`,
|
|
12192
|
+
`uploadLatencyMs=${uploadMetrics.totalLatencyMs.toString()}`,
|
|
10720
12193
|
`error=${toErrorMessage3(error)}`
|
|
10721
12194
|
].join(" ")
|
|
10722
12195
|
);
|
|
@@ -10923,7 +12396,7 @@ function resolveWorkspaceDirForLogging(request) {
|
|
|
10923
12396
|
if (explicitSelection && typeof explicitSelection === "object" && !Array.isArray(explicitSelection)) {
|
|
10924
12397
|
const cwd = explicitSelection.options?.cwd;
|
|
10925
12398
|
if (typeof cwd === "string" && cwd.trim().length > 0) {
|
|
10926
|
-
return
|
|
12399
|
+
return path9.resolve(cwd);
|
|
10927
12400
|
}
|
|
10928
12401
|
}
|
|
10929
12402
|
return process.cwd();
|
|
@@ -10933,7 +12406,7 @@ function createRootAgentLoggingSession(request) {
|
|
|
10933
12406
|
if (!selected) {
|
|
10934
12407
|
return void 0;
|
|
10935
12408
|
}
|
|
10936
|
-
const workspaceDir = typeof selected.workspaceDir === "string" && selected.workspaceDir.trim().length > 0 ?
|
|
12409
|
+
const workspaceDir = typeof selected.workspaceDir === "string" && selected.workspaceDir.trim().length > 0 ? path9.resolve(selected.workspaceDir) : resolveWorkspaceDirForLogging(request);
|
|
10937
12410
|
return createAgentLoggingSession({
|
|
10938
12411
|
...selected,
|
|
10939
12412
|
workspaceDir,
|
|
@@ -11667,6 +13140,7 @@ export {
|
|
|
11667
13140
|
CODEX_APPLY_PATCH_FREEFORM_TOOL_DESCRIPTION,
|
|
11668
13141
|
CODEX_APPLY_PATCH_JSON_TOOL_DESCRIPTION,
|
|
11669
13142
|
CODEX_APPLY_PATCH_LARK_GRAMMAR,
|
|
13143
|
+
DEFAULT_FILE_TTL_SECONDS,
|
|
11670
13144
|
FIREWORKS_DEFAULT_GLM_MODEL,
|
|
11671
13145
|
FIREWORKS_DEFAULT_GPT_OSS_120B_MODEL,
|
|
11672
13146
|
FIREWORKS_DEFAULT_KIMI_MODEL,
|
|
@@ -11707,10 +13181,12 @@ export {
|
|
|
11707
13181
|
createViewImageTool,
|
|
11708
13182
|
createWriteFileTool,
|
|
11709
13183
|
customTool,
|
|
13184
|
+
emptyFileUploadMetrics,
|
|
11710
13185
|
encodeChatGptAuthJson,
|
|
11711
13186
|
encodeChatGptAuthJsonB64,
|
|
11712
13187
|
estimateCallCostUsd,
|
|
11713
13188
|
exchangeChatGptOauthCode,
|
|
13189
|
+
files,
|
|
11714
13190
|
generateImageInBatches,
|
|
11715
13191
|
generateImages,
|
|
11716
13192
|
generateJson,
|