@ljoukov/llm 4.1.0 → 5.0.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 +53 -32
- package/dist/index.cjs +632 -305
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +90 -44
- package/dist/index.d.ts +90 -44
- package/dist/index.js +632 -307
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -2533,7 +2533,7 @@ async function runGeminiCall(fn, modelId, runOptions) {
|
|
|
2533
2533
|
|
|
2534
2534
|
// src/openai/client.ts
|
|
2535
2535
|
import OpenAI2 from "openai";
|
|
2536
|
-
import { Agent as Agent2
|
|
2536
|
+
import { Agent as Agent2 } from "undici";
|
|
2537
2537
|
var openAiClientState = getRuntimeSingleton(/* @__PURE__ */ Symbol.for("@ljoukov/llm.openAiClientState"), () => ({
|
|
2538
2538
|
cachedApiKey: null,
|
|
2539
2539
|
cachedClient: null,
|
|
@@ -2562,7 +2562,7 @@ function getOpenAiFetch() {
|
|
|
2562
2562
|
headersTimeout: timeoutMs
|
|
2563
2563
|
});
|
|
2564
2564
|
openAiClientState.cachedFetch = ((input, init) => {
|
|
2565
|
-
return
|
|
2565
|
+
return fetch(input, {
|
|
2566
2566
|
...init ?? {},
|
|
2567
2567
|
dispatcher
|
|
2568
2568
|
});
|
|
@@ -3259,7 +3259,7 @@ import { AsyncLocalStorage as AsyncLocalStorage2 } from "async_hooks";
|
|
|
3259
3259
|
import { Buffer as Buffer4, File as NodeFile } from "buffer";
|
|
3260
3260
|
import { createHash } from "crypto";
|
|
3261
3261
|
import { createReadStream, createWriteStream, openAsBlob } from "fs";
|
|
3262
|
-
import { mkdir as mkdir2, mkdtemp, stat, unlink, writeFile as writeFile2 } from "fs/promises";
|
|
3262
|
+
import { copyFile, mkdir as mkdir2, mkdtemp, readFile, stat, unlink, writeFile as writeFile2 } from "fs/promises";
|
|
3263
3263
|
import os3 from "os";
|
|
3264
3264
|
import path4 from "path";
|
|
3265
3265
|
import { Readable } from "stream";
|
|
@@ -3272,6 +3272,9 @@ var OPENAI_UPLOAD_PART_MAX_BYTES = 64 * 1024 * 1024;
|
|
|
3272
3272
|
var GEMINI_FILE_POLL_INTERVAL_MS = 1e3;
|
|
3273
3273
|
var GEMINI_FILE_POLL_TIMEOUT_MS = 6e4;
|
|
3274
3274
|
var FILES_TEMP_ROOT = path4.join(os3.tmpdir(), "ljoukov-llm-files");
|
|
3275
|
+
var FILES_CACHE_ROOT = path4.join(FILES_TEMP_ROOT, "cache");
|
|
3276
|
+
var FILES_CACHE_CONTENT_ROOT = path4.join(FILES_CACHE_ROOT, "content");
|
|
3277
|
+
var FILES_CACHE_METADATA_ROOT = path4.join(FILES_CACHE_ROOT, "metadata");
|
|
3275
3278
|
var filesState = getRuntimeSingleton(/* @__PURE__ */ Symbol.for("@ljoukov/llm.filesState"), () => ({
|
|
3276
3279
|
metadataById: /* @__PURE__ */ new Map(),
|
|
3277
3280
|
openAiUploadCacheByKey: /* @__PURE__ */ new Map(),
|
|
@@ -3417,6 +3420,12 @@ function toStoredFile(file) {
|
|
|
3417
3420
|
function buildCacheKey(filename, mimeType, sha256Hex) {
|
|
3418
3421
|
return `${sha256Hex}\0${filename}\0${mimeType}`;
|
|
3419
3422
|
}
|
|
3423
|
+
function buildCachedContentPath(sha256Hex) {
|
|
3424
|
+
return path4.join(FILES_CACHE_CONTENT_ROOT, sha256Hex);
|
|
3425
|
+
}
|
|
3426
|
+
function buildCachedMetadataPath(fileId) {
|
|
3427
|
+
return path4.join(FILES_CACHE_METADATA_ROOT, `${fileId}.json`);
|
|
3428
|
+
}
|
|
3420
3429
|
function isFresh(file) {
|
|
3421
3430
|
if (!file.expires_at) {
|
|
3422
3431
|
return true;
|
|
@@ -3437,6 +3446,82 @@ function recordMetadata(metadata) {
|
|
|
3437
3446
|
}
|
|
3438
3447
|
return metadata;
|
|
3439
3448
|
}
|
|
3449
|
+
async function ensureFilesCacheReady() {
|
|
3450
|
+
await mkdir2(FILES_CACHE_CONTENT_ROOT, { recursive: true });
|
|
3451
|
+
await mkdir2(FILES_CACHE_METADATA_ROOT, { recursive: true });
|
|
3452
|
+
}
|
|
3453
|
+
async function cacheBufferLocally(bytes, sha256Hex) {
|
|
3454
|
+
await ensureFilesCacheReady();
|
|
3455
|
+
const localPath = buildCachedContentPath(sha256Hex);
|
|
3456
|
+
try {
|
|
3457
|
+
await writeFile2(localPath, bytes, { flag: "wx" });
|
|
3458
|
+
} catch (error) {
|
|
3459
|
+
const code = error.code;
|
|
3460
|
+
if (code !== "EEXIST") {
|
|
3461
|
+
throw error;
|
|
3462
|
+
}
|
|
3463
|
+
}
|
|
3464
|
+
return localPath;
|
|
3465
|
+
}
|
|
3466
|
+
async function cacheFileLocally(filePath, sha256Hex) {
|
|
3467
|
+
await ensureFilesCacheReady();
|
|
3468
|
+
const localPath = buildCachedContentPath(sha256Hex);
|
|
3469
|
+
try {
|
|
3470
|
+
await copyFile(filePath, localPath);
|
|
3471
|
+
} catch (error) {
|
|
3472
|
+
const code = error.code;
|
|
3473
|
+
if (code !== "EEXIST") {
|
|
3474
|
+
throw error;
|
|
3475
|
+
}
|
|
3476
|
+
}
|
|
3477
|
+
return localPath;
|
|
3478
|
+
}
|
|
3479
|
+
async function persistMetadataToDisk(metadata) {
|
|
3480
|
+
await ensureFilesCacheReady();
|
|
3481
|
+
const payload = {
|
|
3482
|
+
file: metadata.file,
|
|
3483
|
+
filename: metadata.filename,
|
|
3484
|
+
bytes: metadata.bytes,
|
|
3485
|
+
mimeType: metadata.mimeType,
|
|
3486
|
+
sha256Hex: metadata.sha256Hex,
|
|
3487
|
+
localPath: metadata.localPath
|
|
3488
|
+
};
|
|
3489
|
+
await writeFile2(
|
|
3490
|
+
buildCachedMetadataPath(metadata.file.id),
|
|
3491
|
+
`${JSON.stringify(payload, null, 2)}
|
|
3492
|
+
`
|
|
3493
|
+
);
|
|
3494
|
+
}
|
|
3495
|
+
async function loadPersistedMetadata(fileId) {
|
|
3496
|
+
try {
|
|
3497
|
+
const payload = JSON.parse(
|
|
3498
|
+
await readFile(buildCachedMetadataPath(fileId), "utf8")
|
|
3499
|
+
);
|
|
3500
|
+
if (!payload || typeof payload !== "object" || !payload.file) {
|
|
3501
|
+
return void 0;
|
|
3502
|
+
}
|
|
3503
|
+
if (payload.localPath) {
|
|
3504
|
+
try {
|
|
3505
|
+
const localStats = await stat(payload.localPath);
|
|
3506
|
+
if (!localStats.isFile()) {
|
|
3507
|
+
return void 0;
|
|
3508
|
+
}
|
|
3509
|
+
} catch {
|
|
3510
|
+
return void 0;
|
|
3511
|
+
}
|
|
3512
|
+
}
|
|
3513
|
+
return recordMetadata({
|
|
3514
|
+
file: payload.file,
|
|
3515
|
+
filename: payload.filename,
|
|
3516
|
+
bytes: payload.bytes,
|
|
3517
|
+
mimeType: payload.mimeType,
|
|
3518
|
+
sha256Hex: payload.sha256Hex,
|
|
3519
|
+
localPath: payload.localPath
|
|
3520
|
+
});
|
|
3521
|
+
} catch {
|
|
3522
|
+
return void 0;
|
|
3523
|
+
}
|
|
3524
|
+
}
|
|
3440
3525
|
async function uploadOpenAiFileFromBytes(params) {
|
|
3441
3526
|
const cacheKey = buildCacheKey(params.filename, params.mimeType, params.sha256Hex);
|
|
3442
3527
|
const cached = filesState.openAiUploadCacheByKey.get(cacheKey);
|
|
@@ -3585,17 +3670,23 @@ async function retrieveOpenAiFile(fileId) {
|
|
|
3585
3670
|
if (cached && isFresh(cached.file)) {
|
|
3586
3671
|
return cached;
|
|
3587
3672
|
}
|
|
3673
|
+
const persisted = await loadPersistedMetadata(fileId);
|
|
3674
|
+
if (persisted && isFresh(persisted.file)) {
|
|
3675
|
+
return persisted;
|
|
3676
|
+
}
|
|
3588
3677
|
const client = getOpenAiClient();
|
|
3589
3678
|
const retrieved = await client.files.retrieve(fileId);
|
|
3590
3679
|
const file = toStoredFile(retrieved);
|
|
3591
|
-
|
|
3680
|
+
const metadata = recordMetadata({
|
|
3592
3681
|
file,
|
|
3593
3682
|
filename: file.filename,
|
|
3594
3683
|
bytes: file.bytes,
|
|
3595
|
-
mimeType: cached?.mimeType ?? resolveMimeType(file.filename, void 0),
|
|
3596
|
-
sha256Hex: cached?.sha256Hex,
|
|
3597
|
-
localPath: cached?.localPath
|
|
3684
|
+
mimeType: cached?.mimeType ?? persisted?.mimeType ?? resolveMimeType(file.filename, void 0),
|
|
3685
|
+
sha256Hex: cached?.sha256Hex ?? persisted?.sha256Hex,
|
|
3686
|
+
localPath: cached?.localPath ?? persisted?.localPath
|
|
3598
3687
|
});
|
|
3688
|
+
await persistMetadataToDisk(metadata);
|
|
3689
|
+
return metadata;
|
|
3599
3690
|
}
|
|
3600
3691
|
function buildGeminiMirrorName(sha256Hex) {
|
|
3601
3692
|
return `files/${sha256Hex.slice(0, 40)}`;
|
|
@@ -3707,6 +3798,7 @@ async function materializeOpenAiFile(fileId) {
|
|
|
3707
3798
|
sha256Hex,
|
|
3708
3799
|
localPath
|
|
3709
3800
|
});
|
|
3801
|
+
await persistMetadataToDisk(updated);
|
|
3710
3802
|
return {
|
|
3711
3803
|
file: updated.file,
|
|
3712
3804
|
filename: updated.filename,
|
|
@@ -3870,7 +3962,13 @@ async function filesCreate(params) {
|
|
|
3870
3962
|
sha256Hex: sha256Hex2,
|
|
3871
3963
|
bytes: info.size
|
|
3872
3964
|
});
|
|
3873
|
-
|
|
3965
|
+
const localPath2 = await cacheFileLocally(filePath, sha256Hex2);
|
|
3966
|
+
const cached2 = recordMetadata({
|
|
3967
|
+
...uploaded2,
|
|
3968
|
+
localPath: localPath2
|
|
3969
|
+
});
|
|
3970
|
+
await persistMetadataToDisk(cached2);
|
|
3971
|
+
return cached2.file;
|
|
3874
3972
|
}
|
|
3875
3973
|
const filename = normaliseFilename(params.filename);
|
|
3876
3974
|
const bytes = toBuffer(params.data);
|
|
@@ -3884,7 +3982,13 @@ async function filesCreate(params) {
|
|
|
3884
3982
|
expiresAfterSeconds,
|
|
3885
3983
|
sha256Hex
|
|
3886
3984
|
});
|
|
3887
|
-
|
|
3985
|
+
const localPath = await cacheBufferLocally(bytes, sha256Hex);
|
|
3986
|
+
const cached = recordMetadata({
|
|
3987
|
+
...uploaded,
|
|
3988
|
+
localPath
|
|
3989
|
+
});
|
|
3990
|
+
await persistMetadataToDisk(cached);
|
|
3991
|
+
return cached.file;
|
|
3888
3992
|
}
|
|
3889
3993
|
async function filesRetrieve(fileId) {
|
|
3890
3994
|
return (await retrieveOpenAiFile(fileId)).file;
|
|
@@ -3917,6 +4021,10 @@ async function filesDelete(fileId) {
|
|
|
3917
4021
|
const response = await getOpenAiClient().files.delete(fileId);
|
|
3918
4022
|
filesState.metadataById.delete(fileId);
|
|
3919
4023
|
filesState.materializedById.delete(fileId);
|
|
4024
|
+
try {
|
|
4025
|
+
await unlink(buildCachedMetadataPath(fileId));
|
|
4026
|
+
} catch {
|
|
4027
|
+
}
|
|
3920
4028
|
return {
|
|
3921
4029
|
id: response.id,
|
|
3922
4030
|
deleted: response.deleted,
|
|
@@ -3945,6 +4053,71 @@ var files = {
|
|
|
3945
4053
|
content: filesContent
|
|
3946
4054
|
};
|
|
3947
4055
|
|
|
4056
|
+
// src/telemetry.ts
|
|
4057
|
+
var telemetryState = getRuntimeSingleton(
|
|
4058
|
+
/* @__PURE__ */ Symbol.for("@ljoukov/llm.telemetryState"),
|
|
4059
|
+
() => ({
|
|
4060
|
+
configuredTelemetry: void 0
|
|
4061
|
+
})
|
|
4062
|
+
);
|
|
4063
|
+
function configureTelemetry(telemetry = void 0) {
|
|
4064
|
+
telemetryState.configuredTelemetry = telemetry === void 0 || telemetry === false ? void 0 : telemetry;
|
|
4065
|
+
}
|
|
4066
|
+
function resetTelemetry() {
|
|
4067
|
+
telemetryState.configuredTelemetry = void 0;
|
|
4068
|
+
}
|
|
4069
|
+
function isPromiseLike2(value) {
|
|
4070
|
+
return (typeof value === "object" || typeof value === "function") && value !== null && typeof value.then === "function";
|
|
4071
|
+
}
|
|
4072
|
+
function resolveTelemetrySelection(telemetry) {
|
|
4073
|
+
if (telemetry === false) {
|
|
4074
|
+
return void 0;
|
|
4075
|
+
}
|
|
4076
|
+
if (telemetry !== void 0) {
|
|
4077
|
+
return telemetry;
|
|
4078
|
+
}
|
|
4079
|
+
return telemetryState.configuredTelemetry;
|
|
4080
|
+
}
|
|
4081
|
+
function createTelemetrySession(telemetry) {
|
|
4082
|
+
const config = resolveTelemetrySelection(telemetry);
|
|
4083
|
+
if (!config) {
|
|
4084
|
+
return void 0;
|
|
4085
|
+
}
|
|
4086
|
+
const pending = /* @__PURE__ */ new Set();
|
|
4087
|
+
const trackPromise = (promise) => {
|
|
4088
|
+
pending.add(promise);
|
|
4089
|
+
promise.finally(() => {
|
|
4090
|
+
pending.delete(promise);
|
|
4091
|
+
});
|
|
4092
|
+
};
|
|
4093
|
+
const emit = (event) => {
|
|
4094
|
+
try {
|
|
4095
|
+
const output = config.sink.emit(event);
|
|
4096
|
+
if (isPromiseLike2(output)) {
|
|
4097
|
+
const task = Promise.resolve(output).then(() => void 0).catch(() => void 0);
|
|
4098
|
+
trackPromise(task);
|
|
4099
|
+
}
|
|
4100
|
+
} catch {
|
|
4101
|
+
}
|
|
4102
|
+
};
|
|
4103
|
+
const flush = async () => {
|
|
4104
|
+
while (pending.size > 0) {
|
|
4105
|
+
await Promise.allSettled([...pending]);
|
|
4106
|
+
}
|
|
4107
|
+
if (typeof config.sink.flush === "function") {
|
|
4108
|
+
try {
|
|
4109
|
+
await config.sink.flush();
|
|
4110
|
+
} catch {
|
|
4111
|
+
}
|
|
4112
|
+
}
|
|
4113
|
+
};
|
|
4114
|
+
return {
|
|
4115
|
+
includeStreamEvents: config.includeStreamEvents === true,
|
|
4116
|
+
emit,
|
|
4117
|
+
flush
|
|
4118
|
+
};
|
|
4119
|
+
}
|
|
4120
|
+
|
|
3948
4121
|
// src/llm.ts
|
|
3949
4122
|
var toolCallContextStorage = getRuntimeSingleton(
|
|
3950
4123
|
/* @__PURE__ */ Symbol.for("@ljoukov/llm.toolCallContextStorage"),
|
|
@@ -4396,8 +4569,7 @@ function toGeminiPart(part) {
|
|
|
4396
4569
|
return {
|
|
4397
4570
|
fileData: {
|
|
4398
4571
|
fileUri: buildCanonicalGeminiFileUri(part.file_id),
|
|
4399
|
-
mimeType: inferToolOutputMimeTypeFromFilename(part.filename) ?? "application/octet-stream"
|
|
4400
|
-
displayName: part.filename ?? void 0
|
|
4572
|
+
mimeType: inferToolOutputMimeTypeFromFilename(part.filename) ?? "application/octet-stream"
|
|
4401
4573
|
}
|
|
4402
4574
|
};
|
|
4403
4575
|
}
|
|
@@ -4415,8 +4587,7 @@ function toGeminiPart(part) {
|
|
|
4415
4587
|
return {
|
|
4416
4588
|
fileData: {
|
|
4417
4589
|
fileUri: part.image_url,
|
|
4418
|
-
mimeType: inferToolOutputMimeTypeFromFilename(part.filename) ?? "application/octet-stream"
|
|
4419
|
-
displayName: part.filename ?? void 0
|
|
4590
|
+
mimeType: inferToolOutputMimeTypeFromFilename(part.filename) ?? "application/octet-stream"
|
|
4420
4591
|
}
|
|
4421
4592
|
};
|
|
4422
4593
|
}
|
|
@@ -4425,8 +4596,7 @@ function toGeminiPart(part) {
|
|
|
4425
4596
|
return {
|
|
4426
4597
|
fileData: {
|
|
4427
4598
|
fileUri: buildCanonicalGeminiFileUri(part.file_id),
|
|
4428
|
-
mimeType: inferToolOutputMimeTypeFromFilename(part.filename) ?? "application/octet-stream"
|
|
4429
|
-
displayName: part.filename ?? void 0
|
|
4599
|
+
mimeType: inferToolOutputMimeTypeFromFilename(part.filename) ?? "application/octet-stream"
|
|
4430
4600
|
}
|
|
4431
4601
|
};
|
|
4432
4602
|
}
|
|
@@ -4452,8 +4622,7 @@ function toGeminiPart(part) {
|
|
|
4452
4622
|
return {
|
|
4453
4623
|
fileData: {
|
|
4454
4624
|
fileUri: part.file_url,
|
|
4455
|
-
mimeType: inferToolOutputMimeTypeFromFilename(part.filename) ?? "application/octet-stream"
|
|
4456
|
-
displayName: part.filename ?? void 0
|
|
4625
|
+
mimeType: inferToolOutputMimeTypeFromFilename(part.filename) ?? "application/octet-stream"
|
|
4457
4626
|
}
|
|
4458
4627
|
};
|
|
4459
4628
|
}
|
|
@@ -4685,7 +4854,7 @@ async function prepareOpenAiPromptContentItem(item) {
|
|
|
4685
4854
|
mimeType,
|
|
4686
4855
|
filename
|
|
4687
4856
|
});
|
|
4688
|
-
return { type: "input_file", file_id: uploaded.fileId
|
|
4857
|
+
return { type: "input_file", file_id: uploaded.fileId };
|
|
4689
4858
|
}
|
|
4690
4859
|
if (typeof item.file_url === "string" && item.file_url.trim().toLowerCase().startsWith("data:")) {
|
|
4691
4860
|
const parsed = parseDataUrlPayload(item.file_url);
|
|
@@ -4700,7 +4869,7 @@ async function prepareOpenAiPromptContentItem(item) {
|
|
|
4700
4869
|
guessInlineDataFilename(parsed.mimeType)
|
|
4701
4870
|
)
|
|
4702
4871
|
});
|
|
4703
|
-
return { type: "input_file", file_id: uploaded.fileId
|
|
4872
|
+
return { type: "input_file", file_id: uploaded.fileId };
|
|
4704
4873
|
}
|
|
4705
4874
|
return item;
|
|
4706
4875
|
}
|
|
@@ -4765,21 +4934,16 @@ async function prepareGeminiPromptContents(contents) {
|
|
|
4765
4934
|
for (const part of content.parts ?? []) {
|
|
4766
4935
|
const canonicalFileId = parseCanonicalGeminiFileId(part.fileData?.fileUri);
|
|
4767
4936
|
if (canonicalFileId) {
|
|
4768
|
-
|
|
4937
|
+
await getCanonicalFileMetadata(canonicalFileId);
|
|
4769
4938
|
if (backend === "api") {
|
|
4770
4939
|
const mirrored = await ensureGeminiFileMirror(canonicalFileId);
|
|
4771
|
-
|
|
4772
|
-
if (metadata.filename && mirroredPart.fileData) {
|
|
4773
|
-
mirroredPart.fileData.displayName = metadata.filename;
|
|
4774
|
-
}
|
|
4775
|
-
parts.push(mirroredPart);
|
|
4940
|
+
parts.push(createPartFromUri(mirrored.uri, mirrored.mimeType));
|
|
4776
4941
|
} else {
|
|
4777
4942
|
const mirrored = await ensureVertexFileMirror(canonicalFileId);
|
|
4778
4943
|
parts.push({
|
|
4779
4944
|
fileData: {
|
|
4780
4945
|
fileUri: mirrored.fileUri,
|
|
4781
|
-
mimeType: mirrored.mimeType
|
|
4782
|
-
displayName: metadata.filename
|
|
4946
|
+
mimeType: mirrored.mimeType
|
|
4783
4947
|
}
|
|
4784
4948
|
});
|
|
4785
4949
|
}
|
|
@@ -4798,18 +4962,13 @@ async function prepareGeminiPromptContents(contents) {
|
|
|
4798
4962
|
});
|
|
4799
4963
|
if (backend === "api") {
|
|
4800
4964
|
const mirrored = await ensureGeminiFileMirror(stored.fileId);
|
|
4801
|
-
|
|
4802
|
-
if (filename && mirroredPart.fileData) {
|
|
4803
|
-
mirroredPart.fileData.displayName = filename;
|
|
4804
|
-
}
|
|
4805
|
-
parts.push(mirroredPart);
|
|
4965
|
+
parts.push(createPartFromUri(mirrored.uri, mirrored.mimeType));
|
|
4806
4966
|
} else {
|
|
4807
4967
|
const mirrored = await ensureVertexFileMirror(stored.fileId);
|
|
4808
4968
|
parts.push({
|
|
4809
4969
|
fileData: {
|
|
4810
4970
|
fileUri: mirrored.fileUri,
|
|
4811
|
-
mimeType: mirrored.mimeType
|
|
4812
|
-
displayName: filename
|
|
4971
|
+
mimeType: mirrored.mimeType
|
|
4813
4972
|
}
|
|
4814
4973
|
});
|
|
4815
4974
|
}
|
|
@@ -5330,7 +5489,7 @@ function toOpenAiInput(contents) {
|
|
|
5330
5489
|
...part.file_id ? { file_id: part.file_id } : {},
|
|
5331
5490
|
...part.file_data ? { file_data: part.file_data } : {},
|
|
5332
5491
|
...part.file_url ? { file_url: part.file_url } : {},
|
|
5333
|
-
|
|
5492
|
+
...!part.file_id && part.filename ? { filename: part.filename } : {}
|
|
5334
5493
|
});
|
|
5335
5494
|
break;
|
|
5336
5495
|
default:
|
|
@@ -5415,7 +5574,7 @@ function toChatGptInput(contents) {
|
|
|
5415
5574
|
...part.file_id ? { file_id: part.file_id } : {},
|
|
5416
5575
|
...part.file_data ? { file_data: part.file_data } : {},
|
|
5417
5576
|
...part.file_url ? { file_url: part.file_url } : {},
|
|
5418
|
-
|
|
5577
|
+
...!part.file_id && part.filename ? { filename: part.filename } : {}
|
|
5419
5578
|
});
|
|
5420
5579
|
break;
|
|
5421
5580
|
default:
|
|
@@ -5545,6 +5704,65 @@ function mergeTokenUpdates(current, next) {
|
|
|
5545
5704
|
toolUsePromptTokens: next.toolUsePromptTokens ?? current.toolUsePromptTokens
|
|
5546
5705
|
};
|
|
5547
5706
|
}
|
|
5707
|
+
function sumUsageValue(current, next) {
|
|
5708
|
+
if (typeof next !== "number" || !Number.isFinite(next)) {
|
|
5709
|
+
return current;
|
|
5710
|
+
}
|
|
5711
|
+
const normalizedNext = Math.max(0, next);
|
|
5712
|
+
if (typeof current !== "number" || !Number.isFinite(current)) {
|
|
5713
|
+
return normalizedNext;
|
|
5714
|
+
}
|
|
5715
|
+
return Math.max(0, current) + normalizedNext;
|
|
5716
|
+
}
|
|
5717
|
+
function sumUsageTokens(current, next) {
|
|
5718
|
+
if (!next) {
|
|
5719
|
+
return current;
|
|
5720
|
+
}
|
|
5721
|
+
return {
|
|
5722
|
+
promptTokens: sumUsageValue(current?.promptTokens, next.promptTokens),
|
|
5723
|
+
cachedTokens: sumUsageValue(current?.cachedTokens, next.cachedTokens),
|
|
5724
|
+
responseTokens: sumUsageValue(current?.responseTokens, next.responseTokens),
|
|
5725
|
+
responseImageTokens: sumUsageValue(current?.responseImageTokens, next.responseImageTokens),
|
|
5726
|
+
thinkingTokens: sumUsageValue(current?.thinkingTokens, next.thinkingTokens),
|
|
5727
|
+
totalTokens: sumUsageValue(current?.totalTokens, next.totalTokens),
|
|
5728
|
+
toolUsePromptTokens: sumUsageValue(current?.toolUsePromptTokens, next.toolUsePromptTokens)
|
|
5729
|
+
};
|
|
5730
|
+
}
|
|
5731
|
+
function countInlineImagesInContent(content) {
|
|
5732
|
+
if (!content) {
|
|
5733
|
+
return 0;
|
|
5734
|
+
}
|
|
5735
|
+
let count = 0;
|
|
5736
|
+
for (const part of content.parts) {
|
|
5737
|
+
if (part.type === "inlineData" && isInlineImageMime(part.mimeType)) {
|
|
5738
|
+
count += 1;
|
|
5739
|
+
}
|
|
5740
|
+
}
|
|
5741
|
+
return count;
|
|
5742
|
+
}
|
|
5743
|
+
function createLlmTelemetryEmitter(params) {
|
|
5744
|
+
const session = createTelemetrySession(params.telemetry);
|
|
5745
|
+
const callId = randomBytes(8).toString("hex");
|
|
5746
|
+
return {
|
|
5747
|
+
includeStreamEvents: session?.includeStreamEvents === true,
|
|
5748
|
+
emit: (event) => {
|
|
5749
|
+
if (!session) {
|
|
5750
|
+
return;
|
|
5751
|
+
}
|
|
5752
|
+
session.emit({
|
|
5753
|
+
...event,
|
|
5754
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5755
|
+
callId,
|
|
5756
|
+
operation: params.operation,
|
|
5757
|
+
provider: params.provider,
|
|
5758
|
+
model: params.model
|
|
5759
|
+
});
|
|
5760
|
+
},
|
|
5761
|
+
flush: async () => {
|
|
5762
|
+
await session?.flush();
|
|
5763
|
+
}
|
|
5764
|
+
};
|
|
5765
|
+
}
|
|
5548
5766
|
function toMaybeNumber(value) {
|
|
5549
5767
|
if (typeof value === "number" && Number.isFinite(value)) {
|
|
5550
5768
|
return value;
|
|
@@ -6022,8 +6240,7 @@ function buildGeminiToolOutputMediaPart(item) {
|
|
|
6022
6240
|
return {
|
|
6023
6241
|
fileData: {
|
|
6024
6242
|
fileUri: buildCanonicalGeminiFileUri(item.file_id),
|
|
6025
|
-
mimeType: inferToolOutputMimeTypeFromFilename(item.filename) ?? "application/octet-stream"
|
|
6026
|
-
displayName: item.filename ?? void 0
|
|
6243
|
+
mimeType: inferToolOutputMimeTypeFromFilename(item.filename) ?? "application/octet-stream"
|
|
6027
6244
|
}
|
|
6028
6245
|
};
|
|
6029
6246
|
}
|
|
@@ -6042,8 +6259,7 @@ function buildGeminiToolOutputMediaPart(item) {
|
|
|
6042
6259
|
return {
|
|
6043
6260
|
fileData: {
|
|
6044
6261
|
fileUri: item.image_url,
|
|
6045
|
-
mimeType: inferToolOutputMimeTypeFromFilename(item.filename) ?? "application/octet-stream"
|
|
6046
|
-
displayName: item.filename ?? void 0
|
|
6262
|
+
mimeType: inferToolOutputMimeTypeFromFilename(item.filename) ?? "application/octet-stream"
|
|
6047
6263
|
}
|
|
6048
6264
|
};
|
|
6049
6265
|
}
|
|
@@ -6052,8 +6268,7 @@ function buildGeminiToolOutputMediaPart(item) {
|
|
|
6052
6268
|
return {
|
|
6053
6269
|
fileData: {
|
|
6054
6270
|
fileUri: buildCanonicalGeminiFileUri(item.file_id),
|
|
6055
|
-
mimeType: inferToolOutputMimeTypeFromFilename(item.filename) ?? "application/octet-stream"
|
|
6056
|
-
displayName: item.filename ?? void 0
|
|
6271
|
+
mimeType: inferToolOutputMimeTypeFromFilename(item.filename) ?? "application/octet-stream"
|
|
6057
6272
|
}
|
|
6058
6273
|
};
|
|
6059
6274
|
}
|
|
@@ -6076,12 +6291,7 @@ function buildGeminiToolOutputMediaPart(item) {
|
|
|
6076
6291
|
return part;
|
|
6077
6292
|
}
|
|
6078
6293
|
if (typeof item.file_url === "string" && item.file_url.trim().length > 0 && inferredMimeType) {
|
|
6079
|
-
|
|
6080
|
-
const displayName = item.filename?.trim();
|
|
6081
|
-
if (displayName && part.fileData) {
|
|
6082
|
-
part.fileData.displayName = displayName;
|
|
6083
|
-
}
|
|
6084
|
-
return part;
|
|
6294
|
+
return createPartFromUri(item.file_url, inferredMimeType);
|
|
6085
6295
|
}
|
|
6086
6296
|
}
|
|
6087
6297
|
return null;
|
|
@@ -6962,6 +7172,10 @@ async function runTextCall(params) {
|
|
|
6962
7172
|
let responseRole;
|
|
6963
7173
|
let latestUsage;
|
|
6964
7174
|
let responseImages = 0;
|
|
7175
|
+
const pushEvent = (event) => {
|
|
7176
|
+
queue.push(event);
|
|
7177
|
+
params.onEvent?.(event);
|
|
7178
|
+
};
|
|
6965
7179
|
const pushDelta = (channel, text) => {
|
|
6966
7180
|
if (!text) {
|
|
6967
7181
|
return;
|
|
@@ -6972,7 +7186,7 @@ async function runTextCall(params) {
|
|
|
6972
7186
|
} else {
|
|
6973
7187
|
callLogger?.appendResponseDelta(text);
|
|
6974
7188
|
}
|
|
6975
|
-
|
|
7189
|
+
pushEvent({ type: "delta", channel, text });
|
|
6976
7190
|
};
|
|
6977
7191
|
const pushInline = (data, mimeType) => {
|
|
6978
7192
|
if (!data) {
|
|
@@ -7042,7 +7256,7 @@ async function runTextCall(params) {
|
|
|
7042
7256
|
}
|
|
7043
7257
|
case "response.refusal.delta": {
|
|
7044
7258
|
blocked = true;
|
|
7045
|
-
|
|
7259
|
+
pushEvent({ type: "blocked" });
|
|
7046
7260
|
break;
|
|
7047
7261
|
}
|
|
7048
7262
|
default:
|
|
@@ -7051,7 +7265,7 @@ async function runTextCall(params) {
|
|
|
7051
7265
|
}
|
|
7052
7266
|
const finalResponse = await stream.finalResponse();
|
|
7053
7267
|
modelVersion = typeof finalResponse.model === "string" ? finalResponse.model : request.model;
|
|
7054
|
-
|
|
7268
|
+
pushEvent({ type: "model", modelVersion });
|
|
7055
7269
|
if (finalResponse.error) {
|
|
7056
7270
|
const message = typeof finalResponse.error.message === "string" ? finalResponse.error.message : "OpenAI response failed";
|
|
7057
7271
|
throw new Error(message);
|
|
@@ -7115,11 +7329,11 @@ async function runTextCall(params) {
|
|
|
7115
7329
|
});
|
|
7116
7330
|
blocked = blocked || result2.blocked;
|
|
7117
7331
|
if (blocked) {
|
|
7118
|
-
|
|
7332
|
+
pushEvent({ type: "blocked" });
|
|
7119
7333
|
}
|
|
7120
7334
|
if (result2.model) {
|
|
7121
7335
|
modelVersion = providerInfo.serviceTier ? request.model : `chatgpt-${result2.model}`;
|
|
7122
|
-
|
|
7336
|
+
pushEvent({ type: "model", modelVersion });
|
|
7123
7337
|
}
|
|
7124
7338
|
latestUsage = extractChatGptUsageTokens(result2.usage);
|
|
7125
7339
|
const fallbackText = typeof result2.text === "string" ? result2.text : "";
|
|
@@ -7157,11 +7371,11 @@ async function runTextCall(params) {
|
|
|
7157
7371
|
{ signal }
|
|
7158
7372
|
);
|
|
7159
7373
|
modelVersion = typeof response.model === "string" ? response.model : request.model;
|
|
7160
|
-
|
|
7374
|
+
pushEvent({ type: "model", modelVersion });
|
|
7161
7375
|
const choice = Array.isArray(response.choices) ? response.choices[0] : void 0;
|
|
7162
7376
|
if (choice?.finish_reason === "content_filter") {
|
|
7163
7377
|
blocked = true;
|
|
7164
|
-
|
|
7378
|
+
pushEvent({ type: "blocked" });
|
|
7165
7379
|
}
|
|
7166
7380
|
const textOutput = extractFireworksMessageText(
|
|
7167
7381
|
choice?.message
|
|
@@ -7203,11 +7417,11 @@ async function runTextCall(params) {
|
|
|
7203
7417
|
for await (const chunk of stream) {
|
|
7204
7418
|
if (chunk.modelVersion) {
|
|
7205
7419
|
modelVersion = chunk.modelVersion;
|
|
7206
|
-
|
|
7420
|
+
pushEvent({ type: "model", modelVersion });
|
|
7207
7421
|
}
|
|
7208
7422
|
if (chunk.promptFeedback?.blockReason) {
|
|
7209
7423
|
blocked = true;
|
|
7210
|
-
|
|
7424
|
+
pushEvent({ type: "blocked" });
|
|
7211
7425
|
}
|
|
7212
7426
|
latestUsage = mergeTokenUpdates(
|
|
7213
7427
|
latestUsage,
|
|
@@ -7220,7 +7434,7 @@ async function runTextCall(params) {
|
|
|
7220
7434
|
const primary = candidates[0];
|
|
7221
7435
|
if (primary && isModerationFinish(primary.finishReason)) {
|
|
7222
7436
|
blocked = true;
|
|
7223
|
-
|
|
7437
|
+
pushEvent({ type: "blocked" });
|
|
7224
7438
|
}
|
|
7225
7439
|
for (const candidate of candidates) {
|
|
7226
7440
|
const candidateContent = candidate.content;
|
|
@@ -7257,7 +7471,7 @@ async function runTextCall(params) {
|
|
|
7257
7471
|
imageSize: request.imageSize
|
|
7258
7472
|
});
|
|
7259
7473
|
if (latestUsage) {
|
|
7260
|
-
|
|
7474
|
+
pushEvent({ type: "usage", usage: latestUsage, costUsd, modelVersion });
|
|
7261
7475
|
}
|
|
7262
7476
|
callLogger?.complete({
|
|
7263
7477
|
responseText: text,
|
|
@@ -7311,18 +7525,76 @@ async function runTextCall(params) {
|
|
|
7311
7525
|
});
|
|
7312
7526
|
return result;
|
|
7313
7527
|
}
|
|
7314
|
-
function
|
|
7528
|
+
function startTextStream(request, operation) {
|
|
7315
7529
|
const queue = createAsyncQueue();
|
|
7316
7530
|
const abortController = new AbortController();
|
|
7531
|
+
const provider = resolveProvider(request.model).provider;
|
|
7532
|
+
const telemetry = createLlmTelemetryEmitter({
|
|
7533
|
+
telemetry: request.telemetry,
|
|
7534
|
+
operation,
|
|
7535
|
+
provider,
|
|
7536
|
+
model: request.model
|
|
7537
|
+
});
|
|
7538
|
+
const startedAtMs = Date.now();
|
|
7539
|
+
telemetry.emit({
|
|
7540
|
+
type: "llm.call.started",
|
|
7541
|
+
inputMode: typeof request.input === "string" ? "string" : "messages",
|
|
7542
|
+
toolCount: request.tools?.length ?? 0,
|
|
7543
|
+
responseModalities: request.responseModalities
|
|
7544
|
+
});
|
|
7317
7545
|
const result = (async () => {
|
|
7546
|
+
let uploadMetrics = emptyFileUploadMetrics();
|
|
7318
7547
|
try {
|
|
7319
|
-
|
|
7548
|
+
let output;
|
|
7549
|
+
await collectFileUploadMetrics(async () => {
|
|
7550
|
+
try {
|
|
7551
|
+
output = await runTextCall({
|
|
7552
|
+
request,
|
|
7553
|
+
queue,
|
|
7554
|
+
abortController,
|
|
7555
|
+
onEvent: telemetry.includeStreamEvents ? (event) => {
|
|
7556
|
+
telemetry.emit({ type: "llm.call.stream", event });
|
|
7557
|
+
} : void 0
|
|
7558
|
+
});
|
|
7559
|
+
} finally {
|
|
7560
|
+
uploadMetrics = getCurrentFileUploadMetrics();
|
|
7561
|
+
}
|
|
7562
|
+
});
|
|
7563
|
+
if (!output) {
|
|
7564
|
+
throw new Error("LLM text call returned no result.");
|
|
7565
|
+
}
|
|
7566
|
+
telemetry.emit({
|
|
7567
|
+
type: "llm.call.completed",
|
|
7568
|
+
success: true,
|
|
7569
|
+
durationMs: Math.max(0, Date.now() - startedAtMs),
|
|
7570
|
+
modelVersion: output.modelVersion,
|
|
7571
|
+
blocked: output.blocked,
|
|
7572
|
+
usage: output.usage,
|
|
7573
|
+
costUsd: output.costUsd,
|
|
7574
|
+
outputTextChars: output.text.length,
|
|
7575
|
+
thoughtChars: output.thoughts.length,
|
|
7576
|
+
responseImages: countInlineImagesInContent(output.content),
|
|
7577
|
+
uploadCount: uploadMetrics.count,
|
|
7578
|
+
uploadBytes: uploadMetrics.totalBytes,
|
|
7579
|
+
uploadLatencyMs: uploadMetrics.totalLatencyMs
|
|
7580
|
+
});
|
|
7320
7581
|
queue.close();
|
|
7321
7582
|
return output;
|
|
7322
7583
|
} catch (error) {
|
|
7323
7584
|
const err = error instanceof Error ? error : new Error(String(error));
|
|
7585
|
+
telemetry.emit({
|
|
7586
|
+
type: "llm.call.completed",
|
|
7587
|
+
success: false,
|
|
7588
|
+
durationMs: Math.max(0, Date.now() - startedAtMs),
|
|
7589
|
+
uploadCount: uploadMetrics.count,
|
|
7590
|
+
uploadBytes: uploadMetrics.totalBytes,
|
|
7591
|
+
uploadLatencyMs: uploadMetrics.totalLatencyMs,
|
|
7592
|
+
error: err.message
|
|
7593
|
+
});
|
|
7324
7594
|
queue.fail(err);
|
|
7325
7595
|
throw err;
|
|
7596
|
+
} finally {
|
|
7597
|
+
await telemetry.flush();
|
|
7326
7598
|
}
|
|
7327
7599
|
})();
|
|
7328
7600
|
return {
|
|
@@ -7331,8 +7603,11 @@ function streamText(request) {
|
|
|
7331
7603
|
abort: () => abortController.abort()
|
|
7332
7604
|
};
|
|
7333
7605
|
}
|
|
7606
|
+
function streamText(request) {
|
|
7607
|
+
return startTextStream(request, "streamText");
|
|
7608
|
+
}
|
|
7334
7609
|
async function generateText(request) {
|
|
7335
|
-
const call =
|
|
7610
|
+
const call = startTextStream(request, "generateText");
|
|
7336
7611
|
for await (const _event of call.events) {
|
|
7337
7612
|
}
|
|
7338
7613
|
return await call.result;
|
|
@@ -7358,9 +7633,26 @@ function buildJsonSchemaConfig(request) {
|
|
|
7358
7633
|
} : void 0;
|
|
7359
7634
|
return { providerInfo, responseJsonSchema, openAiTextFormat };
|
|
7360
7635
|
}
|
|
7361
|
-
function
|
|
7636
|
+
function startJsonStream(request, operation) {
|
|
7362
7637
|
const queue = createAsyncQueue();
|
|
7363
7638
|
const abortController = new AbortController();
|
|
7639
|
+
const provider = resolveProvider(request.model).provider;
|
|
7640
|
+
const telemetry = createLlmTelemetryEmitter({
|
|
7641
|
+
telemetry: request.telemetry,
|
|
7642
|
+
operation,
|
|
7643
|
+
provider,
|
|
7644
|
+
model: request.model
|
|
7645
|
+
});
|
|
7646
|
+
const startedAtMs = Date.now();
|
|
7647
|
+
const maxAttempts = Math.max(1, Math.floor(request.maxAttempts ?? 2));
|
|
7648
|
+
const streamMode = request.streamMode ?? "partial";
|
|
7649
|
+
telemetry.emit({
|
|
7650
|
+
type: "llm.call.started",
|
|
7651
|
+
inputMode: typeof request.input === "string" ? "string" : "messages",
|
|
7652
|
+
toolCount: request.tools?.length ?? 0,
|
|
7653
|
+
maxAttempts,
|
|
7654
|
+
streamMode
|
|
7655
|
+
});
|
|
7364
7656
|
const resolveAbortSignal = () => {
|
|
7365
7657
|
if (!request.signal) {
|
|
7366
7658
|
return abortController.signal;
|
|
@@ -7379,135 +7671,155 @@ function streamJson(request) {
|
|
|
7379
7671
|
return abortController.signal;
|
|
7380
7672
|
};
|
|
7381
7673
|
const result = (async () => {
|
|
7382
|
-
|
|
7383
|
-
|
|
7384
|
-
|
|
7385
|
-
|
|
7386
|
-
|
|
7387
|
-
let openAiTextFormatForAttempt = openAiTextFormat;
|
|
7388
|
-
for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
|
|
7389
|
-
let rawText = "";
|
|
7390
|
-
let lastPartial = "";
|
|
7391
|
-
try {
|
|
7392
|
-
const call = streamText({
|
|
7393
|
-
model: request.model,
|
|
7394
|
-
input: request.input,
|
|
7395
|
-
instructions: request.instructions,
|
|
7396
|
-
tools: request.tools,
|
|
7397
|
-
responseMimeType: request.responseMimeType ?? "application/json",
|
|
7398
|
-
responseJsonSchema,
|
|
7399
|
-
thinkingLevel: request.thinkingLevel,
|
|
7400
|
-
...openAiTextFormatForAttempt ? { openAiTextFormat: openAiTextFormatForAttempt } : {},
|
|
7401
|
-
signal
|
|
7402
|
-
});
|
|
7674
|
+
let uploadMetrics = emptyFileUploadMetrics();
|
|
7675
|
+
let attemptsUsed = 0;
|
|
7676
|
+
try {
|
|
7677
|
+
let output;
|
|
7678
|
+
await collectFileUploadMetrics(async () => {
|
|
7403
7679
|
try {
|
|
7404
|
-
|
|
7405
|
-
|
|
7406
|
-
|
|
7407
|
-
|
|
7408
|
-
|
|
7409
|
-
|
|
7410
|
-
|
|
7411
|
-
|
|
7412
|
-
|
|
7413
|
-
|
|
7414
|
-
|
|
7415
|
-
|
|
7416
|
-
|
|
7417
|
-
|
|
7418
|
-
|
|
7680
|
+
const signal = resolveAbortSignal();
|
|
7681
|
+
const { providerInfo, responseJsonSchema, openAiTextFormat } = buildJsonSchemaConfig(request);
|
|
7682
|
+
const failures = [];
|
|
7683
|
+
let openAiTextFormatForAttempt = openAiTextFormat;
|
|
7684
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
|
|
7685
|
+
attemptsUsed = attempt;
|
|
7686
|
+
let rawText = "";
|
|
7687
|
+
let lastPartial = "";
|
|
7688
|
+
try {
|
|
7689
|
+
const call = streamText({
|
|
7690
|
+
model: request.model,
|
|
7691
|
+
input: request.input,
|
|
7692
|
+
instructions: request.instructions,
|
|
7693
|
+
tools: request.tools,
|
|
7694
|
+
responseMimeType: request.responseMimeType ?? "application/json",
|
|
7695
|
+
responseJsonSchema,
|
|
7696
|
+
thinkingLevel: request.thinkingLevel,
|
|
7697
|
+
...openAiTextFormatForAttempt ? { openAiTextFormat: openAiTextFormatForAttempt } : {},
|
|
7698
|
+
telemetry: false,
|
|
7699
|
+
signal
|
|
7700
|
+
});
|
|
7701
|
+
try {
|
|
7702
|
+
for await (const event of call.events) {
|
|
7703
|
+
queue.push(event);
|
|
7704
|
+
if (telemetry.includeStreamEvents) {
|
|
7705
|
+
telemetry.emit({ type: "llm.call.stream", event });
|
|
7706
|
+
}
|
|
7707
|
+
if (event.type === "delta" && event.channel === "response") {
|
|
7708
|
+
rawText += event.text;
|
|
7709
|
+
if (streamMode === "partial") {
|
|
7710
|
+
const partial = parsePartialJsonFromLlmText(rawText);
|
|
7711
|
+
if (partial !== null) {
|
|
7712
|
+
const serialized = JSON.stringify(partial);
|
|
7713
|
+
if (serialized !== lastPartial) {
|
|
7714
|
+
lastPartial = serialized;
|
|
7715
|
+
queue.push({
|
|
7716
|
+
type: "json",
|
|
7717
|
+
stage: "partial",
|
|
7718
|
+
value: partial
|
|
7719
|
+
});
|
|
7720
|
+
}
|
|
7721
|
+
}
|
|
7722
|
+
}
|
|
7419
7723
|
}
|
|
7420
7724
|
}
|
|
7725
|
+
} catch (streamError) {
|
|
7726
|
+
await call.result.catch(() => void 0);
|
|
7727
|
+
throw streamError;
|
|
7728
|
+
}
|
|
7729
|
+
const result2 = await call.result;
|
|
7730
|
+
rawText = rawText || result2.text;
|
|
7731
|
+
const cleanedText = normalizeJsonText(rawText);
|
|
7732
|
+
const repairedText = escapeNewlinesInStrings(cleanedText);
|
|
7733
|
+
const payload = JSON.parse(repairedText);
|
|
7734
|
+
const normalized = typeof request.normalizeJson === "function" ? request.normalizeJson(payload) : payload;
|
|
7735
|
+
const parsed = request.schema.parse(normalized);
|
|
7736
|
+
queue.push({ type: "json", stage: "final", value: parsed });
|
|
7737
|
+
output = { value: parsed, rawText, result: result2 };
|
|
7738
|
+
return;
|
|
7739
|
+
} catch (error) {
|
|
7740
|
+
const handled = error instanceof Error ? error : new Error(String(error));
|
|
7741
|
+
failures.push({ attempt, rawText, error: handled });
|
|
7742
|
+
if (providerInfo.provider === "chatgpt" && openAiTextFormatForAttempt) {
|
|
7743
|
+
openAiTextFormatForAttempt = void 0;
|
|
7744
|
+
}
|
|
7745
|
+
if (attempt >= maxAttempts) {
|
|
7746
|
+
throw new LlmJsonCallError(
|
|
7747
|
+
`LLM JSON call failed after ${attempt} attempt(s)`,
|
|
7748
|
+
failures
|
|
7749
|
+
);
|
|
7421
7750
|
}
|
|
7422
7751
|
}
|
|
7423
7752
|
}
|
|
7424
|
-
|
|
7425
|
-
|
|
7426
|
-
|
|
7427
|
-
}
|
|
7428
|
-
const result2 = await call.result;
|
|
7429
|
-
rawText = rawText || result2.text;
|
|
7430
|
-
const cleanedText = normalizeJsonText(rawText);
|
|
7431
|
-
const repairedText = escapeNewlinesInStrings(cleanedText);
|
|
7432
|
-
const payload = JSON.parse(repairedText);
|
|
7433
|
-
const normalized = typeof request.normalizeJson === "function" ? request.normalizeJson(payload) : payload;
|
|
7434
|
-
const parsed = request.schema.parse(normalized);
|
|
7435
|
-
queue.push({ type: "json", stage: "final", value: parsed });
|
|
7436
|
-
queue.close();
|
|
7437
|
-
return { value: parsed, rawText, result: result2 };
|
|
7438
|
-
} catch (error) {
|
|
7439
|
-
const handled = error instanceof Error ? error : new Error(String(error));
|
|
7440
|
-
failures.push({ attempt, rawText, error: handled });
|
|
7441
|
-
if (providerInfo.provider === "chatgpt" && openAiTextFormatForAttempt) {
|
|
7442
|
-
openAiTextFormatForAttempt = void 0;
|
|
7443
|
-
}
|
|
7444
|
-
if (attempt >= maxAttempts) {
|
|
7445
|
-
throw new LlmJsonCallError(`LLM JSON call failed after ${attempt} attempt(s)`, failures);
|
|
7753
|
+
throw new LlmJsonCallError("LLM JSON call failed", failures);
|
|
7754
|
+
} finally {
|
|
7755
|
+
uploadMetrics = getCurrentFileUploadMetrics();
|
|
7446
7756
|
}
|
|
7447
|
-
}
|
|
7757
|
+
});
|
|
7758
|
+
if (!output) {
|
|
7759
|
+
throw new Error("LLM JSON call returned no result.");
|
|
7760
|
+
}
|
|
7761
|
+
telemetry.emit({
|
|
7762
|
+
type: "llm.call.completed",
|
|
7763
|
+
success: true,
|
|
7764
|
+
durationMs: Math.max(0, Date.now() - startedAtMs),
|
|
7765
|
+
modelVersion: output.result.modelVersion,
|
|
7766
|
+
blocked: output.result.blocked,
|
|
7767
|
+
usage: output.result.usage,
|
|
7768
|
+
costUsd: output.result.costUsd,
|
|
7769
|
+
rawTextChars: output.rawText.length,
|
|
7770
|
+
attempts: attemptsUsed,
|
|
7771
|
+
uploadCount: uploadMetrics.count,
|
|
7772
|
+
uploadBytes: uploadMetrics.totalBytes,
|
|
7773
|
+
uploadLatencyMs: uploadMetrics.totalLatencyMs
|
|
7774
|
+
});
|
|
7775
|
+
queue.close();
|
|
7776
|
+
return output;
|
|
7777
|
+
} catch (error) {
|
|
7778
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
7779
|
+
telemetry.emit({
|
|
7780
|
+
type: "llm.call.completed",
|
|
7781
|
+
success: false,
|
|
7782
|
+
durationMs: Math.max(0, Date.now() - startedAtMs),
|
|
7783
|
+
attempts: attemptsUsed > 0 ? attemptsUsed : void 0,
|
|
7784
|
+
uploadCount: uploadMetrics.count,
|
|
7785
|
+
uploadBytes: uploadMetrics.totalBytes,
|
|
7786
|
+
uploadLatencyMs: uploadMetrics.totalLatencyMs,
|
|
7787
|
+
error: err.message
|
|
7788
|
+
});
|
|
7789
|
+
queue.fail(err);
|
|
7790
|
+
throw err;
|
|
7791
|
+
} finally {
|
|
7792
|
+
await telemetry.flush();
|
|
7448
7793
|
}
|
|
7449
|
-
|
|
7450
|
-
})().catch((error) => {
|
|
7451
|
-
const err = error instanceof Error ? error : new Error(String(error));
|
|
7452
|
-
queue.fail(err);
|
|
7453
|
-
throw err;
|
|
7454
|
-
});
|
|
7794
|
+
})();
|
|
7455
7795
|
return {
|
|
7456
7796
|
events: queue.iterable,
|
|
7457
7797
|
result,
|
|
7458
7798
|
abort: () => abortController.abort()
|
|
7459
7799
|
};
|
|
7460
7800
|
}
|
|
7801
|
+
function streamJson(request) {
|
|
7802
|
+
return startJsonStream(request, "streamJson");
|
|
7803
|
+
}
|
|
7461
7804
|
async function generateJson(request) {
|
|
7462
|
-
const
|
|
7463
|
-
|
|
7464
|
-
|
|
7465
|
-
|
|
7466
|
-
|
|
7467
|
-
|
|
7468
|
-
|
|
7469
|
-
|
|
7470
|
-
|
|
7471
|
-
|
|
7472
|
-
|
|
7473
|
-
tools: request.tools,
|
|
7474
|
-
responseMimeType: request.responseMimeType ?? "application/json",
|
|
7475
|
-
responseJsonSchema,
|
|
7476
|
-
thinkingLevel: request.thinkingLevel,
|
|
7477
|
-
...openAiTextFormatForAttempt ? { openAiTextFormat: openAiTextFormatForAttempt } : {},
|
|
7478
|
-
signal: request.signal
|
|
7479
|
-
});
|
|
7480
|
-
try {
|
|
7481
|
-
for await (const event of call.events) {
|
|
7482
|
-
request.onEvent?.(event);
|
|
7483
|
-
if (event.type === "delta" && event.channel === "response") {
|
|
7484
|
-
rawText += event.text;
|
|
7485
|
-
}
|
|
7486
|
-
}
|
|
7487
|
-
} catch (streamError) {
|
|
7488
|
-
await call.result.catch(() => void 0);
|
|
7489
|
-
throw streamError;
|
|
7490
|
-
}
|
|
7491
|
-
const result = await call.result;
|
|
7492
|
-
rawText = rawText || result.text;
|
|
7493
|
-
const cleanedText = normalizeJsonText(rawText);
|
|
7494
|
-
const repairedText = escapeNewlinesInStrings(cleanedText);
|
|
7495
|
-
const payload = JSON.parse(repairedText);
|
|
7496
|
-
const normalized = typeof request.normalizeJson === "function" ? request.normalizeJson(payload) : payload;
|
|
7497
|
-
const parsed = request.schema.parse(normalized);
|
|
7498
|
-
return { value: parsed, rawText, result };
|
|
7499
|
-
} catch (error) {
|
|
7500
|
-
const handled = error instanceof Error ? error : new Error(String(error));
|
|
7501
|
-
failures.push({ attempt, rawText, error: handled });
|
|
7502
|
-
if (providerInfo.provider === "chatgpt" && openAiTextFormatForAttempt) {
|
|
7503
|
-
openAiTextFormatForAttempt = void 0;
|
|
7504
|
-
}
|
|
7505
|
-
if (attempt >= maxAttempts) {
|
|
7506
|
-
throw new LlmJsonCallError(`LLM JSON call failed after ${attempt} attempt(s)`, failures);
|
|
7805
|
+
const call = startJsonStream(
|
|
7806
|
+
{
|
|
7807
|
+
...request,
|
|
7808
|
+
streamMode: "final"
|
|
7809
|
+
},
|
|
7810
|
+
"generateJson"
|
|
7811
|
+
);
|
|
7812
|
+
try {
|
|
7813
|
+
for await (const event of call.events) {
|
|
7814
|
+
if (event.type !== "json") {
|
|
7815
|
+
request.onEvent?.(event);
|
|
7507
7816
|
}
|
|
7508
7817
|
}
|
|
7818
|
+
} catch (streamError) {
|
|
7819
|
+
await call.result.catch(() => void 0);
|
|
7820
|
+
throw streamError;
|
|
7509
7821
|
}
|
|
7510
|
-
|
|
7822
|
+
return await call.result;
|
|
7511
7823
|
}
|
|
7512
7824
|
var DEFAULT_TOOL_LOOP_MAX_STEPS = 8;
|
|
7513
7825
|
function resolveToolLoopContents(input) {
|
|
@@ -9123,7 +9435,10 @@ function streamToolLoop(request) {
|
|
|
9123
9435
|
abort: () => abortController.abort()
|
|
9124
9436
|
};
|
|
9125
9437
|
}
|
|
9126
|
-
var
|
|
9438
|
+
var IMAGE_GRADE_VALUE_SCHEMA = z3.enum(["pass", "fail"]);
|
|
9439
|
+
var IMAGE_GRADE_SCHEMA = z3.object({
|
|
9440
|
+
grade: IMAGE_GRADE_VALUE_SCHEMA
|
|
9441
|
+
});
|
|
9127
9442
|
async function gradeGeneratedImage(params) {
|
|
9128
9443
|
const parts = [
|
|
9129
9444
|
{
|
|
@@ -9134,7 +9449,7 @@ async function gradeGeneratedImage(params) {
|
|
|
9134
9449
|
"Image prompt to grade:",
|
|
9135
9450
|
params.imagePrompt,
|
|
9136
9451
|
"",
|
|
9137
|
-
'Respond with
|
|
9452
|
+
'Respond with JSON like {"grade":"pass"} or {"grade":"fail"}.'
|
|
9138
9453
|
].join("\\n")
|
|
9139
9454
|
},
|
|
9140
9455
|
{
|
|
@@ -9143,12 +9458,13 @@ async function gradeGeneratedImage(params) {
|
|
|
9143
9458
|
mimeType: params.image.mimeType ?? "image/png"
|
|
9144
9459
|
}
|
|
9145
9460
|
];
|
|
9146
|
-
const { value } = await generateJson({
|
|
9461
|
+
const { value, result } = await generateJson({
|
|
9147
9462
|
model: params.model,
|
|
9148
9463
|
input: [{ role: "user", content: parts }],
|
|
9149
|
-
schema: IMAGE_GRADE_SCHEMA
|
|
9464
|
+
schema: IMAGE_GRADE_SCHEMA,
|
|
9465
|
+
telemetry: false
|
|
9150
9466
|
});
|
|
9151
|
-
return value;
|
|
9467
|
+
return { grade: value.grade, result };
|
|
9152
9468
|
}
|
|
9153
9469
|
async function generateImages(request) {
|
|
9154
9470
|
const maxAttempts = Math.max(1, Math.floor(request.maxAttempts ?? 4));
|
|
@@ -9168,6 +9484,19 @@ async function generateImages(request) {
|
|
|
9168
9484
|
if (!gradingPrompt) {
|
|
9169
9485
|
throw new Error("imageGradingPrompt must be a non-empty string");
|
|
9170
9486
|
}
|
|
9487
|
+
const telemetry = createLlmTelemetryEmitter({
|
|
9488
|
+
telemetry: request.telemetry,
|
|
9489
|
+
operation: "generateImages",
|
|
9490
|
+
provider: resolveProvider(request.model).provider,
|
|
9491
|
+
model: request.model
|
|
9492
|
+
});
|
|
9493
|
+
const startedAtMs = Date.now();
|
|
9494
|
+
telemetry.emit({
|
|
9495
|
+
type: "llm.call.started",
|
|
9496
|
+
imagePromptCount: promptList.length,
|
|
9497
|
+
styleImageCount: request.styleImages?.length ?? 0,
|
|
9498
|
+
maxAttempts
|
|
9499
|
+
});
|
|
9171
9500
|
const addText = (parts, text) => {
|
|
9172
9501
|
const lastPart = parts[parts.length - 1];
|
|
9173
9502
|
if (lastPart !== void 0 && lastPart.type === "text") {
|
|
@@ -9225,6 +9554,9 @@ async function generateImages(request) {
|
|
|
9225
9554
|
const inputMessages = [{ role: "user", content: buildInitialPromptParts() }];
|
|
9226
9555
|
const orderedEntries = [...promptEntries];
|
|
9227
9556
|
const resolvedImages = /* @__PURE__ */ new Map();
|
|
9557
|
+
let totalCostUsd = 0;
|
|
9558
|
+
let totalUsage;
|
|
9559
|
+
let attemptsUsed = 0;
|
|
9228
9560
|
const removeResolvedEntries = (resolved) => {
|
|
9229
9561
|
if (resolved.size === 0) {
|
|
9230
9562
|
return;
|
|
@@ -9239,70 +9571,118 @@ async function generateImages(request) {
|
|
|
9239
9571
|
}
|
|
9240
9572
|
}
|
|
9241
9573
|
};
|
|
9242
|
-
|
|
9243
|
-
|
|
9244
|
-
|
|
9245
|
-
|
|
9246
|
-
|
|
9247
|
-
|
|
9248
|
-
|
|
9249
|
-
|
|
9250
|
-
|
|
9251
|
-
|
|
9252
|
-
|
|
9253
|
-
|
|
9254
|
-
|
|
9255
|
-
|
|
9256
|
-
|
|
9257
|
-
|
|
9258
|
-
|
|
9259
|
-
|
|
9260
|
-
|
|
9261
|
-
|
|
9262
|
-
|
|
9263
|
-
|
|
9264
|
-
|
|
9265
|
-
|
|
9266
|
-
|
|
9574
|
+
let uploadMetrics = emptyFileUploadMetrics();
|
|
9575
|
+
try {
|
|
9576
|
+
await collectFileUploadMetrics(async () => {
|
|
9577
|
+
try {
|
|
9578
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
|
|
9579
|
+
attemptsUsed = attempt;
|
|
9580
|
+
const result = await generateText({
|
|
9581
|
+
model: request.model,
|
|
9582
|
+
input: inputMessages,
|
|
9583
|
+
responseModalities: ["IMAGE", "TEXT"],
|
|
9584
|
+
imageAspectRatio: request.imageAspectRatio,
|
|
9585
|
+
imageSize: request.imageSize ?? "2K",
|
|
9586
|
+
telemetry: false
|
|
9587
|
+
});
|
|
9588
|
+
totalCostUsd += result.costUsd;
|
|
9589
|
+
totalUsage = sumUsageTokens(totalUsage, result.usage);
|
|
9590
|
+
if (result.blocked || !result.content) {
|
|
9591
|
+
continue;
|
|
9592
|
+
}
|
|
9593
|
+
const images = extractImages(result.content);
|
|
9594
|
+
if (images.length > 0 && promptEntries.length > 0) {
|
|
9595
|
+
const assignedCount = Math.min(images.length, promptEntries.length);
|
|
9596
|
+
const pendingAssignments = promptEntries.slice(0, assignedCount);
|
|
9597
|
+
const assignedImages = images.slice(0, assignedCount);
|
|
9598
|
+
const gradeResults = await Promise.all(
|
|
9599
|
+
pendingAssignments.map(
|
|
9600
|
+
(entry, index) => gradeGeneratedImage({
|
|
9601
|
+
gradingPrompt,
|
|
9602
|
+
imagePrompt: entry.prompt,
|
|
9603
|
+
image: (() => {
|
|
9604
|
+
const image = assignedImages[index];
|
|
9605
|
+
if (!image) {
|
|
9606
|
+
throw new Error("Image generation returned fewer images than expected.");
|
|
9607
|
+
}
|
|
9608
|
+
return image;
|
|
9609
|
+
})(),
|
|
9610
|
+
model: "gpt-5.2"
|
|
9611
|
+
})
|
|
9612
|
+
)
|
|
9613
|
+
);
|
|
9614
|
+
const passedEntries = /* @__PURE__ */ new Set();
|
|
9615
|
+
for (let i = 0; i < gradeResults.length; i += 1) {
|
|
9616
|
+
const gradeResult = gradeResults[i];
|
|
9617
|
+
const entry = pendingAssignments[i];
|
|
9618
|
+
const image = assignedImages[i];
|
|
9619
|
+
if (!gradeResult || !entry || !image) {
|
|
9620
|
+
continue;
|
|
9267
9621
|
}
|
|
9268
|
-
|
|
9269
|
-
|
|
9270
|
-
|
|
9271
|
-
|
|
9272
|
-
|
|
9273
|
-
|
|
9274
|
-
|
|
9275
|
-
|
|
9276
|
-
|
|
9277
|
-
|
|
9278
|
-
|
|
9279
|
-
|
|
9280
|
-
|
|
9281
|
-
|
|
9282
|
-
|
|
9283
|
-
|
|
9284
|
-
|
|
9622
|
+
totalCostUsd += gradeResult.result.costUsd;
|
|
9623
|
+
totalUsage = sumUsageTokens(totalUsage, gradeResult.result.usage);
|
|
9624
|
+
if (gradeResult.grade === "pass") {
|
|
9625
|
+
resolvedImages.set(entry.index, image);
|
|
9626
|
+
passedEntries.add(entry.index);
|
|
9627
|
+
}
|
|
9628
|
+
}
|
|
9629
|
+
removeResolvedEntries(passedEntries);
|
|
9630
|
+
}
|
|
9631
|
+
if (promptEntries.length === 0) {
|
|
9632
|
+
break;
|
|
9633
|
+
}
|
|
9634
|
+
inputMessages.push({
|
|
9635
|
+
role: "assistant",
|
|
9636
|
+
content: result.content.parts
|
|
9637
|
+
});
|
|
9638
|
+
inputMessages.push({
|
|
9639
|
+
role: "user",
|
|
9640
|
+
content: buildContinuationPromptParts(promptEntries)
|
|
9641
|
+
});
|
|
9285
9642
|
}
|
|
9643
|
+
} finally {
|
|
9644
|
+
uploadMetrics = getCurrentFileUploadMetrics();
|
|
9286
9645
|
}
|
|
9287
|
-
removeResolvedEntries(passedEntries);
|
|
9288
|
-
}
|
|
9289
|
-
if (promptEntries.length === 0) {
|
|
9290
|
-
break;
|
|
9291
|
-
}
|
|
9292
|
-
inputMessages.push({
|
|
9293
|
-
role: "assistant",
|
|
9294
|
-
content: result.content.parts
|
|
9295
9646
|
});
|
|
9296
|
-
|
|
9297
|
-
|
|
9298
|
-
|
|
9299
|
-
|
|
9300
|
-
|
|
9301
|
-
|
|
9302
|
-
orderedImages.push(image);
|
|
9647
|
+
const orderedImages = [];
|
|
9648
|
+
for (const entry of orderedEntries) {
|
|
9649
|
+
const image = resolvedImages.get(entry.index);
|
|
9650
|
+
if (image) {
|
|
9651
|
+
orderedImages.push(image);
|
|
9652
|
+
}
|
|
9303
9653
|
}
|
|
9654
|
+
const outputImages = orderedImages.slice(0, numImages);
|
|
9655
|
+
telemetry.emit({
|
|
9656
|
+
type: "llm.call.completed",
|
|
9657
|
+
success: true,
|
|
9658
|
+
durationMs: Math.max(0, Date.now() - startedAtMs),
|
|
9659
|
+
usage: totalUsage,
|
|
9660
|
+
costUsd: totalCostUsd,
|
|
9661
|
+
imageCount: outputImages.length,
|
|
9662
|
+
attempts: attemptsUsed,
|
|
9663
|
+
uploadCount: uploadMetrics.count,
|
|
9664
|
+
uploadBytes: uploadMetrics.totalBytes,
|
|
9665
|
+
uploadLatencyMs: uploadMetrics.totalLatencyMs
|
|
9666
|
+
});
|
|
9667
|
+
return outputImages;
|
|
9668
|
+
} catch (error) {
|
|
9669
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
9670
|
+
telemetry.emit({
|
|
9671
|
+
type: "llm.call.completed",
|
|
9672
|
+
success: false,
|
|
9673
|
+
durationMs: Math.max(0, Date.now() - startedAtMs),
|
|
9674
|
+
usage: totalUsage,
|
|
9675
|
+
costUsd: totalCostUsd,
|
|
9676
|
+
attempts: attemptsUsed > 0 ? attemptsUsed : void 0,
|
|
9677
|
+
uploadCount: uploadMetrics.count,
|
|
9678
|
+
uploadBytes: uploadMetrics.totalBytes,
|
|
9679
|
+
uploadLatencyMs: uploadMetrics.totalLatencyMs,
|
|
9680
|
+
error: err.message
|
|
9681
|
+
});
|
|
9682
|
+
throw err;
|
|
9683
|
+
} finally {
|
|
9684
|
+
await telemetry.flush();
|
|
9304
9685
|
}
|
|
9305
|
-
return orderedImages.slice(0, numImages);
|
|
9306
9686
|
}
|
|
9307
9687
|
async function generateImageInBatches(request) {
|
|
9308
9688
|
const {
|
|
@@ -11953,7 +12333,7 @@ function isNoEntError(error) {
|
|
|
11953
12333
|
|
|
11954
12334
|
// src/agent.ts
|
|
11955
12335
|
async function runAgentLoop(request) {
|
|
11956
|
-
const telemetry =
|
|
12336
|
+
const telemetry = createTelemetrySession(request.telemetry);
|
|
11957
12337
|
const logging = createRootAgentLoggingSession(request);
|
|
11958
12338
|
try {
|
|
11959
12339
|
return await runWithAgentLoggingSession(logging, async () => {
|
|
@@ -12039,7 +12419,7 @@ async function runAgentLoopInternal(request, context) {
|
|
|
12039
12419
|
logging: _logging,
|
|
12040
12420
|
...toolLoopRequest
|
|
12041
12421
|
} = request;
|
|
12042
|
-
const telemetrySession = context.telemetry ??
|
|
12422
|
+
const telemetrySession = context.telemetry ?? createTelemetrySession(telemetry);
|
|
12043
12423
|
const loggingSession = context.logging;
|
|
12044
12424
|
const runId = randomRunId();
|
|
12045
12425
|
const startedAtMs = Date.now();
|
|
@@ -12102,15 +12482,15 @@ async function runAgentLoopInternal(request, context) {
|
|
|
12102
12482
|
].join(" ")
|
|
12103
12483
|
);
|
|
12104
12484
|
const sourceOnEvent = toolLoopRequestWithSteering.onEvent;
|
|
12105
|
-
const
|
|
12485
|
+
const includeStreamEvents = telemetrySession?.includeStreamEvents === true;
|
|
12106
12486
|
const streamEventLogger = loggingSession ? createAgentStreamEventLogger({
|
|
12107
12487
|
append: (line) => {
|
|
12108
12488
|
loggingSession.logLine(`[agent:${runId}] ${line}`);
|
|
12109
12489
|
}
|
|
12110
12490
|
}) : void 0;
|
|
12111
|
-
const wrappedOnEvent = sourceOnEvent ||
|
|
12491
|
+
const wrappedOnEvent = sourceOnEvent || includeStreamEvents ? (event) => {
|
|
12112
12492
|
sourceOnEvent?.(event);
|
|
12113
|
-
if (
|
|
12493
|
+
if (includeStreamEvents) {
|
|
12114
12494
|
emitTelemetry({ type: "agent.run.stream", event });
|
|
12115
12495
|
}
|
|
12116
12496
|
streamEventLogger?.appendEvent(event);
|
|
@@ -12348,7 +12728,7 @@ function countToolCalls(result) {
|
|
|
12348
12728
|
}
|
|
12349
12729
|
return count;
|
|
12350
12730
|
}
|
|
12351
|
-
function
|
|
12731
|
+
function sumUsageValue2(current, next) {
|
|
12352
12732
|
if (typeof next !== "number" || !Number.isFinite(next)) {
|
|
12353
12733
|
return current;
|
|
12354
12734
|
}
|
|
@@ -12366,20 +12746,17 @@ function summarizeResultUsage(result) {
|
|
|
12366
12746
|
continue;
|
|
12367
12747
|
}
|
|
12368
12748
|
summary = {
|
|
12369
|
-
promptTokens:
|
|
12370
|
-
cachedTokens:
|
|
12371
|
-
responseTokens:
|
|
12372
|
-
responseImageTokens:
|
|
12373
|
-
thinkingTokens:
|
|
12374
|
-
totalTokens:
|
|
12375
|
-
toolUsePromptTokens:
|
|
12749
|
+
promptTokens: sumUsageValue2(summary?.promptTokens, usage.promptTokens),
|
|
12750
|
+
cachedTokens: sumUsageValue2(summary?.cachedTokens, usage.cachedTokens),
|
|
12751
|
+
responseTokens: sumUsageValue2(summary?.responseTokens, usage.responseTokens),
|
|
12752
|
+
responseImageTokens: sumUsageValue2(summary?.responseImageTokens, usage.responseImageTokens),
|
|
12753
|
+
thinkingTokens: sumUsageValue2(summary?.thinkingTokens, usage.thinkingTokens),
|
|
12754
|
+
totalTokens: sumUsageValue2(summary?.totalTokens, usage.totalTokens),
|
|
12755
|
+
toolUsePromptTokens: sumUsageValue2(summary?.toolUsePromptTokens, usage.toolUsePromptTokens)
|
|
12376
12756
|
};
|
|
12377
12757
|
}
|
|
12378
12758
|
return summary;
|
|
12379
12759
|
}
|
|
12380
|
-
function isPromiseLike2(value) {
|
|
12381
|
-
return (typeof value === "object" || typeof value === "function") && value !== null && typeof value.then === "function";
|
|
12382
|
-
}
|
|
12383
12760
|
function resolveAgentLoggingSelection(value) {
|
|
12384
12761
|
if (value === false) {
|
|
12385
12762
|
return void 0;
|
|
@@ -12413,60 +12790,6 @@ function createRootAgentLoggingSession(request) {
|
|
|
12413
12790
|
mirrorToConsole: selected.mirrorToConsole !== false
|
|
12414
12791
|
});
|
|
12415
12792
|
}
|
|
12416
|
-
function isAgentTelemetrySink(value) {
|
|
12417
|
-
return typeof value === "object" && value !== null && typeof value.emit === "function";
|
|
12418
|
-
}
|
|
12419
|
-
function resolveTelemetrySelection(telemetry) {
|
|
12420
|
-
if (!telemetry) {
|
|
12421
|
-
return void 0;
|
|
12422
|
-
}
|
|
12423
|
-
if (isAgentTelemetrySink(telemetry)) {
|
|
12424
|
-
return { sink: telemetry };
|
|
12425
|
-
}
|
|
12426
|
-
if (isAgentTelemetrySink(telemetry.sink)) {
|
|
12427
|
-
return telemetry;
|
|
12428
|
-
}
|
|
12429
|
-
throw new Error("Invalid runAgentLoop telemetry config: expected a sink with emit(event).");
|
|
12430
|
-
}
|
|
12431
|
-
function createAgentTelemetrySession(telemetry) {
|
|
12432
|
-
const config = resolveTelemetrySelection(telemetry);
|
|
12433
|
-
if (!config) {
|
|
12434
|
-
return void 0;
|
|
12435
|
-
}
|
|
12436
|
-
const pending = /* @__PURE__ */ new Set();
|
|
12437
|
-
const trackPromise = (promise) => {
|
|
12438
|
-
pending.add(promise);
|
|
12439
|
-
promise.finally(() => {
|
|
12440
|
-
pending.delete(promise);
|
|
12441
|
-
});
|
|
12442
|
-
};
|
|
12443
|
-
const emit = (event) => {
|
|
12444
|
-
try {
|
|
12445
|
-
const output = config.sink.emit(event);
|
|
12446
|
-
if (isPromiseLike2(output)) {
|
|
12447
|
-
const task = Promise.resolve(output).then(() => void 0).catch(() => void 0);
|
|
12448
|
-
trackPromise(task);
|
|
12449
|
-
}
|
|
12450
|
-
} catch {
|
|
12451
|
-
}
|
|
12452
|
-
};
|
|
12453
|
-
const flush = async () => {
|
|
12454
|
-
while (pending.size > 0) {
|
|
12455
|
-
await Promise.allSettled([...pending]);
|
|
12456
|
-
}
|
|
12457
|
-
if (typeof config.sink.flush === "function") {
|
|
12458
|
-
try {
|
|
12459
|
-
await config.sink.flush();
|
|
12460
|
-
} catch {
|
|
12461
|
-
}
|
|
12462
|
-
}
|
|
12463
|
-
};
|
|
12464
|
-
return {
|
|
12465
|
-
includeLlmStreamEvents: config.includeLlmStreamEvents === true,
|
|
12466
|
-
emit,
|
|
12467
|
-
flush
|
|
12468
|
-
};
|
|
12469
|
-
}
|
|
12470
12793
|
function createAgentTelemetryEmitter(params) {
|
|
12471
12794
|
return (event) => {
|
|
12472
12795
|
if (!params.session) {
|
|
@@ -13159,6 +13482,7 @@ export {
|
|
|
13159
13482
|
applyPatch,
|
|
13160
13483
|
configureGemini,
|
|
13161
13484
|
configureModelConcurrency,
|
|
13485
|
+
configureTelemetry,
|
|
13162
13486
|
convertGooglePartsToLlmParts,
|
|
13163
13487
|
createApplyPatchTool,
|
|
13164
13488
|
createCodexApplyPatchTool,
|
|
@@ -13207,6 +13531,7 @@ export {
|
|
|
13207
13531
|
parseJsonFromLlmText,
|
|
13208
13532
|
refreshChatGptOauthToken,
|
|
13209
13533
|
resetModelConcurrencyConfig,
|
|
13534
|
+
resetTelemetry,
|
|
13210
13535
|
resolveFilesystemToolProfile,
|
|
13211
13536
|
resolveFireworksModelId,
|
|
13212
13537
|
runAgentLoop,
|