@ljoukov/llm 4.1.1 → 5.0.2
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 +519 -261
- 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 +517 -261
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -53,6 +53,7 @@ __export(index_exports, {
|
|
|
53
53
|
applyPatch: () => applyPatch,
|
|
54
54
|
configureGemini: () => configureGemini,
|
|
55
55
|
configureModelConcurrency: () => configureModelConcurrency,
|
|
56
|
+
configureTelemetry: () => configureTelemetry,
|
|
56
57
|
convertGooglePartsToLlmParts: () => convertGooglePartsToLlmParts,
|
|
57
58
|
createApplyPatchTool: () => createApplyPatchTool,
|
|
58
59
|
createCodexApplyPatchTool: () => createCodexApplyPatchTool,
|
|
@@ -101,6 +102,7 @@ __export(index_exports, {
|
|
|
101
102
|
parseJsonFromLlmText: () => parseJsonFromLlmText,
|
|
102
103
|
refreshChatGptOauthToken: () => refreshChatGptOauthToken,
|
|
103
104
|
resetModelConcurrencyConfig: () => resetModelConcurrencyConfig,
|
|
105
|
+
resetTelemetry: () => resetTelemetry,
|
|
104
106
|
resolveFilesystemToolProfile: () => resolveFilesystemToolProfile,
|
|
105
107
|
resolveFireworksModelId: () => resolveFireworksModelId,
|
|
106
108
|
runAgentLoop: () => runAgentLoop,
|
|
@@ -4165,6 +4167,71 @@ var files = {
|
|
|
4165
4167
|
content: filesContent
|
|
4166
4168
|
};
|
|
4167
4169
|
|
|
4170
|
+
// src/telemetry.ts
|
|
4171
|
+
var telemetryState = getRuntimeSingleton(
|
|
4172
|
+
/* @__PURE__ */ Symbol.for("@ljoukov/llm.telemetryState"),
|
|
4173
|
+
() => ({
|
|
4174
|
+
configuredTelemetry: void 0
|
|
4175
|
+
})
|
|
4176
|
+
);
|
|
4177
|
+
function configureTelemetry(telemetry = void 0) {
|
|
4178
|
+
telemetryState.configuredTelemetry = telemetry === void 0 || telemetry === false ? void 0 : telemetry;
|
|
4179
|
+
}
|
|
4180
|
+
function resetTelemetry() {
|
|
4181
|
+
telemetryState.configuredTelemetry = void 0;
|
|
4182
|
+
}
|
|
4183
|
+
function isPromiseLike2(value) {
|
|
4184
|
+
return (typeof value === "object" || typeof value === "function") && value !== null && typeof value.then === "function";
|
|
4185
|
+
}
|
|
4186
|
+
function resolveTelemetrySelection(telemetry) {
|
|
4187
|
+
if (telemetry === false) {
|
|
4188
|
+
return void 0;
|
|
4189
|
+
}
|
|
4190
|
+
if (telemetry !== void 0) {
|
|
4191
|
+
return telemetry;
|
|
4192
|
+
}
|
|
4193
|
+
return telemetryState.configuredTelemetry;
|
|
4194
|
+
}
|
|
4195
|
+
function createTelemetrySession(telemetry) {
|
|
4196
|
+
const config = resolveTelemetrySelection(telemetry);
|
|
4197
|
+
if (!config) {
|
|
4198
|
+
return void 0;
|
|
4199
|
+
}
|
|
4200
|
+
const pending = /* @__PURE__ */ new Set();
|
|
4201
|
+
const trackPromise = (promise) => {
|
|
4202
|
+
pending.add(promise);
|
|
4203
|
+
promise.finally(() => {
|
|
4204
|
+
pending.delete(promise);
|
|
4205
|
+
});
|
|
4206
|
+
};
|
|
4207
|
+
const emit = (event) => {
|
|
4208
|
+
try {
|
|
4209
|
+
const output = config.sink.emit(event);
|
|
4210
|
+
if (isPromiseLike2(output)) {
|
|
4211
|
+
const task = Promise.resolve(output).then(() => void 0).catch(() => void 0);
|
|
4212
|
+
trackPromise(task);
|
|
4213
|
+
}
|
|
4214
|
+
} catch {
|
|
4215
|
+
}
|
|
4216
|
+
};
|
|
4217
|
+
const flush = async () => {
|
|
4218
|
+
while (pending.size > 0) {
|
|
4219
|
+
await Promise.allSettled([...pending]);
|
|
4220
|
+
}
|
|
4221
|
+
if (typeof config.sink.flush === "function") {
|
|
4222
|
+
try {
|
|
4223
|
+
await config.sink.flush();
|
|
4224
|
+
} catch {
|
|
4225
|
+
}
|
|
4226
|
+
}
|
|
4227
|
+
};
|
|
4228
|
+
return {
|
|
4229
|
+
includeStreamEvents: config.includeStreamEvents === true,
|
|
4230
|
+
emit,
|
|
4231
|
+
flush
|
|
4232
|
+
};
|
|
4233
|
+
}
|
|
4234
|
+
|
|
4168
4235
|
// src/llm.ts
|
|
4169
4236
|
var toolCallContextStorage = getRuntimeSingleton(
|
|
4170
4237
|
/* @__PURE__ */ Symbol.for("@ljoukov/llm.toolCallContextStorage"),
|
|
@@ -5751,6 +5818,65 @@ function mergeTokenUpdates(current, next) {
|
|
|
5751
5818
|
toolUsePromptTokens: next.toolUsePromptTokens ?? current.toolUsePromptTokens
|
|
5752
5819
|
};
|
|
5753
5820
|
}
|
|
5821
|
+
function sumUsageValue(current, next) {
|
|
5822
|
+
if (typeof next !== "number" || !Number.isFinite(next)) {
|
|
5823
|
+
return current;
|
|
5824
|
+
}
|
|
5825
|
+
const normalizedNext = Math.max(0, next);
|
|
5826
|
+
if (typeof current !== "number" || !Number.isFinite(current)) {
|
|
5827
|
+
return normalizedNext;
|
|
5828
|
+
}
|
|
5829
|
+
return Math.max(0, current) + normalizedNext;
|
|
5830
|
+
}
|
|
5831
|
+
function sumUsageTokens(current, next) {
|
|
5832
|
+
if (!next) {
|
|
5833
|
+
return current;
|
|
5834
|
+
}
|
|
5835
|
+
return {
|
|
5836
|
+
promptTokens: sumUsageValue(current?.promptTokens, next.promptTokens),
|
|
5837
|
+
cachedTokens: sumUsageValue(current?.cachedTokens, next.cachedTokens),
|
|
5838
|
+
responseTokens: sumUsageValue(current?.responseTokens, next.responseTokens),
|
|
5839
|
+
responseImageTokens: sumUsageValue(current?.responseImageTokens, next.responseImageTokens),
|
|
5840
|
+
thinkingTokens: sumUsageValue(current?.thinkingTokens, next.thinkingTokens),
|
|
5841
|
+
totalTokens: sumUsageValue(current?.totalTokens, next.totalTokens),
|
|
5842
|
+
toolUsePromptTokens: sumUsageValue(current?.toolUsePromptTokens, next.toolUsePromptTokens)
|
|
5843
|
+
};
|
|
5844
|
+
}
|
|
5845
|
+
function countInlineImagesInContent(content) {
|
|
5846
|
+
if (!content) {
|
|
5847
|
+
return 0;
|
|
5848
|
+
}
|
|
5849
|
+
let count = 0;
|
|
5850
|
+
for (const part of content.parts) {
|
|
5851
|
+
if (part.type === "inlineData" && isInlineImageMime(part.mimeType)) {
|
|
5852
|
+
count += 1;
|
|
5853
|
+
}
|
|
5854
|
+
}
|
|
5855
|
+
return count;
|
|
5856
|
+
}
|
|
5857
|
+
function createLlmTelemetryEmitter(params) {
|
|
5858
|
+
const session = createTelemetrySession(params.telemetry);
|
|
5859
|
+
const callId = (0, import_node_crypto2.randomBytes)(8).toString("hex");
|
|
5860
|
+
return {
|
|
5861
|
+
includeStreamEvents: session?.includeStreamEvents === true,
|
|
5862
|
+
emit: (event) => {
|
|
5863
|
+
if (!session) {
|
|
5864
|
+
return;
|
|
5865
|
+
}
|
|
5866
|
+
session.emit({
|
|
5867
|
+
...event,
|
|
5868
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5869
|
+
callId,
|
|
5870
|
+
operation: params.operation,
|
|
5871
|
+
provider: params.provider,
|
|
5872
|
+
model: params.model
|
|
5873
|
+
});
|
|
5874
|
+
},
|
|
5875
|
+
flush: async () => {
|
|
5876
|
+
await session?.flush();
|
|
5877
|
+
}
|
|
5878
|
+
};
|
|
5879
|
+
}
|
|
5754
5880
|
function toMaybeNumber(value) {
|
|
5755
5881
|
if (typeof value === "number" && Number.isFinite(value)) {
|
|
5756
5882
|
return value;
|
|
@@ -5971,6 +6097,23 @@ function toOpenAiToolOutput(value) {
|
|
|
5971
6097
|
}
|
|
5972
6098
|
return mergeToolOutput(value);
|
|
5973
6099
|
}
|
|
6100
|
+
function toChatGptToolOutput(value) {
|
|
6101
|
+
const toolOutput = toOpenAiToolOutput(value);
|
|
6102
|
+
if (typeof toolOutput === "string") {
|
|
6103
|
+
return toolOutput;
|
|
6104
|
+
}
|
|
6105
|
+
return toolOutput.map((item) => {
|
|
6106
|
+
if (item.type !== "input_image") {
|
|
6107
|
+
return item;
|
|
6108
|
+
}
|
|
6109
|
+
return {
|
|
6110
|
+
type: "input_image",
|
|
6111
|
+
...item.file_id ? { file_id: item.file_id } : {},
|
|
6112
|
+
...item.image_url ? { image_url: item.image_url } : {},
|
|
6113
|
+
...item.detail ? { detail: item.detail } : {}
|
|
6114
|
+
};
|
|
6115
|
+
});
|
|
6116
|
+
}
|
|
5974
6117
|
function toGeminiToolOutputItems(value) {
|
|
5975
6118
|
if (isLlmToolOutputContentItem(value)) {
|
|
5976
6119
|
return [value];
|
|
@@ -7160,6 +7303,10 @@ async function runTextCall(params) {
|
|
|
7160
7303
|
let responseRole;
|
|
7161
7304
|
let latestUsage;
|
|
7162
7305
|
let responseImages = 0;
|
|
7306
|
+
const pushEvent = (event) => {
|
|
7307
|
+
queue.push(event);
|
|
7308
|
+
params.onEvent?.(event);
|
|
7309
|
+
};
|
|
7163
7310
|
const pushDelta = (channel, text) => {
|
|
7164
7311
|
if (!text) {
|
|
7165
7312
|
return;
|
|
@@ -7170,7 +7317,7 @@ async function runTextCall(params) {
|
|
|
7170
7317
|
} else {
|
|
7171
7318
|
callLogger?.appendResponseDelta(text);
|
|
7172
7319
|
}
|
|
7173
|
-
|
|
7320
|
+
pushEvent({ type: "delta", channel, text });
|
|
7174
7321
|
};
|
|
7175
7322
|
const pushInline = (data, mimeType) => {
|
|
7176
7323
|
if (!data) {
|
|
@@ -7240,7 +7387,7 @@ async function runTextCall(params) {
|
|
|
7240
7387
|
}
|
|
7241
7388
|
case "response.refusal.delta": {
|
|
7242
7389
|
blocked = true;
|
|
7243
|
-
|
|
7390
|
+
pushEvent({ type: "blocked" });
|
|
7244
7391
|
break;
|
|
7245
7392
|
}
|
|
7246
7393
|
default:
|
|
@@ -7249,7 +7396,7 @@ async function runTextCall(params) {
|
|
|
7249
7396
|
}
|
|
7250
7397
|
const finalResponse = await stream.finalResponse();
|
|
7251
7398
|
modelVersion = typeof finalResponse.model === "string" ? finalResponse.model : request.model;
|
|
7252
|
-
|
|
7399
|
+
pushEvent({ type: "model", modelVersion });
|
|
7253
7400
|
if (finalResponse.error) {
|
|
7254
7401
|
const message = typeof finalResponse.error.message === "string" ? finalResponse.error.message : "OpenAI response failed";
|
|
7255
7402
|
throw new Error(message);
|
|
@@ -7313,11 +7460,11 @@ async function runTextCall(params) {
|
|
|
7313
7460
|
});
|
|
7314
7461
|
blocked = blocked || result2.blocked;
|
|
7315
7462
|
if (blocked) {
|
|
7316
|
-
|
|
7463
|
+
pushEvent({ type: "blocked" });
|
|
7317
7464
|
}
|
|
7318
7465
|
if (result2.model) {
|
|
7319
7466
|
modelVersion = providerInfo.serviceTier ? request.model : `chatgpt-${result2.model}`;
|
|
7320
|
-
|
|
7467
|
+
pushEvent({ type: "model", modelVersion });
|
|
7321
7468
|
}
|
|
7322
7469
|
latestUsage = extractChatGptUsageTokens(result2.usage);
|
|
7323
7470
|
const fallbackText = typeof result2.text === "string" ? result2.text : "";
|
|
@@ -7355,11 +7502,11 @@ async function runTextCall(params) {
|
|
|
7355
7502
|
{ signal }
|
|
7356
7503
|
);
|
|
7357
7504
|
modelVersion = typeof response.model === "string" ? response.model : request.model;
|
|
7358
|
-
|
|
7505
|
+
pushEvent({ type: "model", modelVersion });
|
|
7359
7506
|
const choice = Array.isArray(response.choices) ? response.choices[0] : void 0;
|
|
7360
7507
|
if (choice?.finish_reason === "content_filter") {
|
|
7361
7508
|
blocked = true;
|
|
7362
|
-
|
|
7509
|
+
pushEvent({ type: "blocked" });
|
|
7363
7510
|
}
|
|
7364
7511
|
const textOutput = extractFireworksMessageText(
|
|
7365
7512
|
choice?.message
|
|
@@ -7401,11 +7548,11 @@ async function runTextCall(params) {
|
|
|
7401
7548
|
for await (const chunk of stream) {
|
|
7402
7549
|
if (chunk.modelVersion) {
|
|
7403
7550
|
modelVersion = chunk.modelVersion;
|
|
7404
|
-
|
|
7551
|
+
pushEvent({ type: "model", modelVersion });
|
|
7405
7552
|
}
|
|
7406
7553
|
if (chunk.promptFeedback?.blockReason) {
|
|
7407
7554
|
blocked = true;
|
|
7408
|
-
|
|
7555
|
+
pushEvent({ type: "blocked" });
|
|
7409
7556
|
}
|
|
7410
7557
|
latestUsage = mergeTokenUpdates(
|
|
7411
7558
|
latestUsage,
|
|
@@ -7418,7 +7565,7 @@ async function runTextCall(params) {
|
|
|
7418
7565
|
const primary = candidates[0];
|
|
7419
7566
|
if (primary && isModerationFinish(primary.finishReason)) {
|
|
7420
7567
|
blocked = true;
|
|
7421
|
-
|
|
7568
|
+
pushEvent({ type: "blocked" });
|
|
7422
7569
|
}
|
|
7423
7570
|
for (const candidate of candidates) {
|
|
7424
7571
|
const candidateContent = candidate.content;
|
|
@@ -7455,7 +7602,7 @@ async function runTextCall(params) {
|
|
|
7455
7602
|
imageSize: request.imageSize
|
|
7456
7603
|
});
|
|
7457
7604
|
if (latestUsage) {
|
|
7458
|
-
|
|
7605
|
+
pushEvent({ type: "usage", usage: latestUsage, costUsd, modelVersion });
|
|
7459
7606
|
}
|
|
7460
7607
|
callLogger?.complete({
|
|
7461
7608
|
responseText: text,
|
|
@@ -7509,18 +7656,76 @@ async function runTextCall(params) {
|
|
|
7509
7656
|
});
|
|
7510
7657
|
return result;
|
|
7511
7658
|
}
|
|
7512
|
-
function
|
|
7659
|
+
function startTextStream(request, operation) {
|
|
7513
7660
|
const queue = createAsyncQueue();
|
|
7514
7661
|
const abortController = new AbortController();
|
|
7662
|
+
const provider = resolveProvider(request.model).provider;
|
|
7663
|
+
const telemetry = createLlmTelemetryEmitter({
|
|
7664
|
+
telemetry: request.telemetry,
|
|
7665
|
+
operation,
|
|
7666
|
+
provider,
|
|
7667
|
+
model: request.model
|
|
7668
|
+
});
|
|
7669
|
+
const startedAtMs = Date.now();
|
|
7670
|
+
telemetry.emit({
|
|
7671
|
+
type: "llm.call.started",
|
|
7672
|
+
inputMode: typeof request.input === "string" ? "string" : "messages",
|
|
7673
|
+
toolCount: request.tools?.length ?? 0,
|
|
7674
|
+
responseModalities: request.responseModalities
|
|
7675
|
+
});
|
|
7515
7676
|
const result = (async () => {
|
|
7677
|
+
let uploadMetrics = emptyFileUploadMetrics();
|
|
7516
7678
|
try {
|
|
7517
|
-
|
|
7679
|
+
let output;
|
|
7680
|
+
await collectFileUploadMetrics(async () => {
|
|
7681
|
+
try {
|
|
7682
|
+
output = await runTextCall({
|
|
7683
|
+
request,
|
|
7684
|
+
queue,
|
|
7685
|
+
abortController,
|
|
7686
|
+
onEvent: telemetry.includeStreamEvents ? (event) => {
|
|
7687
|
+
telemetry.emit({ type: "llm.call.stream", event });
|
|
7688
|
+
} : void 0
|
|
7689
|
+
});
|
|
7690
|
+
} finally {
|
|
7691
|
+
uploadMetrics = getCurrentFileUploadMetrics();
|
|
7692
|
+
}
|
|
7693
|
+
});
|
|
7694
|
+
if (!output) {
|
|
7695
|
+
throw new Error("LLM text call returned no result.");
|
|
7696
|
+
}
|
|
7697
|
+
telemetry.emit({
|
|
7698
|
+
type: "llm.call.completed",
|
|
7699
|
+
success: true,
|
|
7700
|
+
durationMs: Math.max(0, Date.now() - startedAtMs),
|
|
7701
|
+
modelVersion: output.modelVersion,
|
|
7702
|
+
blocked: output.blocked,
|
|
7703
|
+
usage: output.usage,
|
|
7704
|
+
costUsd: output.costUsd,
|
|
7705
|
+
outputTextChars: output.text.length,
|
|
7706
|
+
thoughtChars: output.thoughts.length,
|
|
7707
|
+
responseImages: countInlineImagesInContent(output.content),
|
|
7708
|
+
uploadCount: uploadMetrics.count,
|
|
7709
|
+
uploadBytes: uploadMetrics.totalBytes,
|
|
7710
|
+
uploadLatencyMs: uploadMetrics.totalLatencyMs
|
|
7711
|
+
});
|
|
7518
7712
|
queue.close();
|
|
7519
7713
|
return output;
|
|
7520
7714
|
} catch (error) {
|
|
7521
7715
|
const err = error instanceof Error ? error : new Error(String(error));
|
|
7716
|
+
telemetry.emit({
|
|
7717
|
+
type: "llm.call.completed",
|
|
7718
|
+
success: false,
|
|
7719
|
+
durationMs: Math.max(0, Date.now() - startedAtMs),
|
|
7720
|
+
uploadCount: uploadMetrics.count,
|
|
7721
|
+
uploadBytes: uploadMetrics.totalBytes,
|
|
7722
|
+
uploadLatencyMs: uploadMetrics.totalLatencyMs,
|
|
7723
|
+
error: err.message
|
|
7724
|
+
});
|
|
7522
7725
|
queue.fail(err);
|
|
7523
7726
|
throw err;
|
|
7727
|
+
} finally {
|
|
7728
|
+
await telemetry.flush();
|
|
7524
7729
|
}
|
|
7525
7730
|
})();
|
|
7526
7731
|
return {
|
|
@@ -7529,8 +7734,11 @@ function streamText(request) {
|
|
|
7529
7734
|
abort: () => abortController.abort()
|
|
7530
7735
|
};
|
|
7531
7736
|
}
|
|
7737
|
+
function streamText(request) {
|
|
7738
|
+
return startTextStream(request, "streamText");
|
|
7739
|
+
}
|
|
7532
7740
|
async function generateText(request) {
|
|
7533
|
-
const call =
|
|
7741
|
+
const call = startTextStream(request, "generateText");
|
|
7534
7742
|
for await (const _event of call.events) {
|
|
7535
7743
|
}
|
|
7536
7744
|
return await call.result;
|
|
@@ -7556,9 +7764,26 @@ function buildJsonSchemaConfig(request) {
|
|
|
7556
7764
|
} : void 0;
|
|
7557
7765
|
return { providerInfo, responseJsonSchema, openAiTextFormat };
|
|
7558
7766
|
}
|
|
7559
|
-
function
|
|
7767
|
+
function startJsonStream(request, operation) {
|
|
7560
7768
|
const queue = createAsyncQueue();
|
|
7561
7769
|
const abortController = new AbortController();
|
|
7770
|
+
const provider = resolveProvider(request.model).provider;
|
|
7771
|
+
const telemetry = createLlmTelemetryEmitter({
|
|
7772
|
+
telemetry: request.telemetry,
|
|
7773
|
+
operation,
|
|
7774
|
+
provider,
|
|
7775
|
+
model: request.model
|
|
7776
|
+
});
|
|
7777
|
+
const startedAtMs = Date.now();
|
|
7778
|
+
const maxAttempts = Math.max(1, Math.floor(request.maxAttempts ?? 2));
|
|
7779
|
+
const streamMode = request.streamMode ?? "partial";
|
|
7780
|
+
telemetry.emit({
|
|
7781
|
+
type: "llm.call.started",
|
|
7782
|
+
inputMode: typeof request.input === "string" ? "string" : "messages",
|
|
7783
|
+
toolCount: request.tools?.length ?? 0,
|
|
7784
|
+
maxAttempts,
|
|
7785
|
+
streamMode
|
|
7786
|
+
});
|
|
7562
7787
|
const resolveAbortSignal = () => {
|
|
7563
7788
|
if (!request.signal) {
|
|
7564
7789
|
return abortController.signal;
|
|
@@ -7577,135 +7802,155 @@ function streamJson(request) {
|
|
|
7577
7802
|
return abortController.signal;
|
|
7578
7803
|
};
|
|
7579
7804
|
const result = (async () => {
|
|
7580
|
-
|
|
7581
|
-
|
|
7582
|
-
|
|
7583
|
-
|
|
7584
|
-
|
|
7585
|
-
let openAiTextFormatForAttempt = openAiTextFormat;
|
|
7586
|
-
for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
|
|
7587
|
-
let rawText = "";
|
|
7588
|
-
let lastPartial = "";
|
|
7589
|
-
try {
|
|
7590
|
-
const call = streamText({
|
|
7591
|
-
model: request.model,
|
|
7592
|
-
input: request.input,
|
|
7593
|
-
instructions: request.instructions,
|
|
7594
|
-
tools: request.tools,
|
|
7595
|
-
responseMimeType: request.responseMimeType ?? "application/json",
|
|
7596
|
-
responseJsonSchema,
|
|
7597
|
-
thinkingLevel: request.thinkingLevel,
|
|
7598
|
-
...openAiTextFormatForAttempt ? { openAiTextFormat: openAiTextFormatForAttempt } : {},
|
|
7599
|
-
signal
|
|
7600
|
-
});
|
|
7805
|
+
let uploadMetrics = emptyFileUploadMetrics();
|
|
7806
|
+
let attemptsUsed = 0;
|
|
7807
|
+
try {
|
|
7808
|
+
let output;
|
|
7809
|
+
await collectFileUploadMetrics(async () => {
|
|
7601
7810
|
try {
|
|
7602
|
-
|
|
7603
|
-
|
|
7604
|
-
|
|
7605
|
-
|
|
7606
|
-
|
|
7607
|
-
|
|
7608
|
-
|
|
7609
|
-
|
|
7610
|
-
|
|
7611
|
-
|
|
7612
|
-
|
|
7613
|
-
|
|
7614
|
-
|
|
7615
|
-
|
|
7616
|
-
|
|
7811
|
+
const signal = resolveAbortSignal();
|
|
7812
|
+
const { providerInfo, responseJsonSchema, openAiTextFormat } = buildJsonSchemaConfig(request);
|
|
7813
|
+
const failures = [];
|
|
7814
|
+
let openAiTextFormatForAttempt = openAiTextFormat;
|
|
7815
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
|
|
7816
|
+
attemptsUsed = attempt;
|
|
7817
|
+
let rawText = "";
|
|
7818
|
+
let lastPartial = "";
|
|
7819
|
+
try {
|
|
7820
|
+
const call = streamText({
|
|
7821
|
+
model: request.model,
|
|
7822
|
+
input: request.input,
|
|
7823
|
+
instructions: request.instructions,
|
|
7824
|
+
tools: request.tools,
|
|
7825
|
+
responseMimeType: request.responseMimeType ?? "application/json",
|
|
7826
|
+
responseJsonSchema,
|
|
7827
|
+
thinkingLevel: request.thinkingLevel,
|
|
7828
|
+
...openAiTextFormatForAttempt ? { openAiTextFormat: openAiTextFormatForAttempt } : {},
|
|
7829
|
+
telemetry: false,
|
|
7830
|
+
signal
|
|
7831
|
+
});
|
|
7832
|
+
try {
|
|
7833
|
+
for await (const event of call.events) {
|
|
7834
|
+
queue.push(event);
|
|
7835
|
+
if (telemetry.includeStreamEvents) {
|
|
7836
|
+
telemetry.emit({ type: "llm.call.stream", event });
|
|
7837
|
+
}
|
|
7838
|
+
if (event.type === "delta" && event.channel === "response") {
|
|
7839
|
+
rawText += event.text;
|
|
7840
|
+
if (streamMode === "partial") {
|
|
7841
|
+
const partial = parsePartialJsonFromLlmText(rawText);
|
|
7842
|
+
if (partial !== null) {
|
|
7843
|
+
const serialized = JSON.stringify(partial);
|
|
7844
|
+
if (serialized !== lastPartial) {
|
|
7845
|
+
lastPartial = serialized;
|
|
7846
|
+
queue.push({
|
|
7847
|
+
type: "json",
|
|
7848
|
+
stage: "partial",
|
|
7849
|
+
value: partial
|
|
7850
|
+
});
|
|
7851
|
+
}
|
|
7852
|
+
}
|
|
7853
|
+
}
|
|
7617
7854
|
}
|
|
7618
7855
|
}
|
|
7856
|
+
} catch (streamError) {
|
|
7857
|
+
await call.result.catch(() => void 0);
|
|
7858
|
+
throw streamError;
|
|
7859
|
+
}
|
|
7860
|
+
const result2 = await call.result;
|
|
7861
|
+
rawText = rawText || result2.text;
|
|
7862
|
+
const cleanedText = normalizeJsonText(rawText);
|
|
7863
|
+
const repairedText = escapeNewlinesInStrings(cleanedText);
|
|
7864
|
+
const payload = JSON.parse(repairedText);
|
|
7865
|
+
const normalized = typeof request.normalizeJson === "function" ? request.normalizeJson(payload) : payload;
|
|
7866
|
+
const parsed = request.schema.parse(normalized);
|
|
7867
|
+
queue.push({ type: "json", stage: "final", value: parsed });
|
|
7868
|
+
output = { value: parsed, rawText, result: result2 };
|
|
7869
|
+
return;
|
|
7870
|
+
} catch (error) {
|
|
7871
|
+
const handled = error instanceof Error ? error : new Error(String(error));
|
|
7872
|
+
failures.push({ attempt, rawText, error: handled });
|
|
7873
|
+
if (providerInfo.provider === "chatgpt" && openAiTextFormatForAttempt) {
|
|
7874
|
+
openAiTextFormatForAttempt = void 0;
|
|
7875
|
+
}
|
|
7876
|
+
if (attempt >= maxAttempts) {
|
|
7877
|
+
throw new LlmJsonCallError(
|
|
7878
|
+
`LLM JSON call failed after ${attempt} attempt(s)`,
|
|
7879
|
+
failures
|
|
7880
|
+
);
|
|
7619
7881
|
}
|
|
7620
7882
|
}
|
|
7621
7883
|
}
|
|
7622
|
-
|
|
7623
|
-
|
|
7624
|
-
|
|
7625
|
-
}
|
|
7626
|
-
const result2 = await call.result;
|
|
7627
|
-
rawText = rawText || result2.text;
|
|
7628
|
-
const cleanedText = normalizeJsonText(rawText);
|
|
7629
|
-
const repairedText = escapeNewlinesInStrings(cleanedText);
|
|
7630
|
-
const payload = JSON.parse(repairedText);
|
|
7631
|
-
const normalized = typeof request.normalizeJson === "function" ? request.normalizeJson(payload) : payload;
|
|
7632
|
-
const parsed = request.schema.parse(normalized);
|
|
7633
|
-
queue.push({ type: "json", stage: "final", value: parsed });
|
|
7634
|
-
queue.close();
|
|
7635
|
-
return { value: parsed, rawText, result: result2 };
|
|
7636
|
-
} catch (error) {
|
|
7637
|
-
const handled = error instanceof Error ? error : new Error(String(error));
|
|
7638
|
-
failures.push({ attempt, rawText, error: handled });
|
|
7639
|
-
if (providerInfo.provider === "chatgpt" && openAiTextFormatForAttempt) {
|
|
7640
|
-
openAiTextFormatForAttempt = void 0;
|
|
7641
|
-
}
|
|
7642
|
-
if (attempt >= maxAttempts) {
|
|
7643
|
-
throw new LlmJsonCallError(`LLM JSON call failed after ${attempt} attempt(s)`, failures);
|
|
7884
|
+
throw new LlmJsonCallError("LLM JSON call failed", failures);
|
|
7885
|
+
} finally {
|
|
7886
|
+
uploadMetrics = getCurrentFileUploadMetrics();
|
|
7644
7887
|
}
|
|
7645
|
-
}
|
|
7888
|
+
});
|
|
7889
|
+
if (!output) {
|
|
7890
|
+
throw new Error("LLM JSON call returned no result.");
|
|
7891
|
+
}
|
|
7892
|
+
telemetry.emit({
|
|
7893
|
+
type: "llm.call.completed",
|
|
7894
|
+
success: true,
|
|
7895
|
+
durationMs: Math.max(0, Date.now() - startedAtMs),
|
|
7896
|
+
modelVersion: output.result.modelVersion,
|
|
7897
|
+
blocked: output.result.blocked,
|
|
7898
|
+
usage: output.result.usage,
|
|
7899
|
+
costUsd: output.result.costUsd,
|
|
7900
|
+
rawTextChars: output.rawText.length,
|
|
7901
|
+
attempts: attemptsUsed,
|
|
7902
|
+
uploadCount: uploadMetrics.count,
|
|
7903
|
+
uploadBytes: uploadMetrics.totalBytes,
|
|
7904
|
+
uploadLatencyMs: uploadMetrics.totalLatencyMs
|
|
7905
|
+
});
|
|
7906
|
+
queue.close();
|
|
7907
|
+
return output;
|
|
7908
|
+
} catch (error) {
|
|
7909
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
7910
|
+
telemetry.emit({
|
|
7911
|
+
type: "llm.call.completed",
|
|
7912
|
+
success: false,
|
|
7913
|
+
durationMs: Math.max(0, Date.now() - startedAtMs),
|
|
7914
|
+
attempts: attemptsUsed > 0 ? attemptsUsed : void 0,
|
|
7915
|
+
uploadCount: uploadMetrics.count,
|
|
7916
|
+
uploadBytes: uploadMetrics.totalBytes,
|
|
7917
|
+
uploadLatencyMs: uploadMetrics.totalLatencyMs,
|
|
7918
|
+
error: err.message
|
|
7919
|
+
});
|
|
7920
|
+
queue.fail(err);
|
|
7921
|
+
throw err;
|
|
7922
|
+
} finally {
|
|
7923
|
+
await telemetry.flush();
|
|
7646
7924
|
}
|
|
7647
|
-
|
|
7648
|
-
})().catch((error) => {
|
|
7649
|
-
const err = error instanceof Error ? error : new Error(String(error));
|
|
7650
|
-
queue.fail(err);
|
|
7651
|
-
throw err;
|
|
7652
|
-
});
|
|
7925
|
+
})();
|
|
7653
7926
|
return {
|
|
7654
7927
|
events: queue.iterable,
|
|
7655
7928
|
result,
|
|
7656
7929
|
abort: () => abortController.abort()
|
|
7657
7930
|
};
|
|
7658
7931
|
}
|
|
7932
|
+
function streamJson(request) {
|
|
7933
|
+
return startJsonStream(request, "streamJson");
|
|
7934
|
+
}
|
|
7659
7935
|
async function generateJson(request) {
|
|
7660
|
-
const
|
|
7661
|
-
|
|
7662
|
-
|
|
7663
|
-
|
|
7664
|
-
|
|
7665
|
-
|
|
7666
|
-
|
|
7667
|
-
|
|
7668
|
-
|
|
7669
|
-
|
|
7670
|
-
|
|
7671
|
-
tools: request.tools,
|
|
7672
|
-
responseMimeType: request.responseMimeType ?? "application/json",
|
|
7673
|
-
responseJsonSchema,
|
|
7674
|
-
thinkingLevel: request.thinkingLevel,
|
|
7675
|
-
...openAiTextFormatForAttempt ? { openAiTextFormat: openAiTextFormatForAttempt } : {},
|
|
7676
|
-
signal: request.signal
|
|
7677
|
-
});
|
|
7678
|
-
try {
|
|
7679
|
-
for await (const event of call.events) {
|
|
7680
|
-
request.onEvent?.(event);
|
|
7681
|
-
if (event.type === "delta" && event.channel === "response") {
|
|
7682
|
-
rawText += event.text;
|
|
7683
|
-
}
|
|
7684
|
-
}
|
|
7685
|
-
} catch (streamError) {
|
|
7686
|
-
await call.result.catch(() => void 0);
|
|
7687
|
-
throw streamError;
|
|
7688
|
-
}
|
|
7689
|
-
const result = await call.result;
|
|
7690
|
-
rawText = rawText || result.text;
|
|
7691
|
-
const cleanedText = normalizeJsonText(rawText);
|
|
7692
|
-
const repairedText = escapeNewlinesInStrings(cleanedText);
|
|
7693
|
-
const payload = JSON.parse(repairedText);
|
|
7694
|
-
const normalized = typeof request.normalizeJson === "function" ? request.normalizeJson(payload) : payload;
|
|
7695
|
-
const parsed = request.schema.parse(normalized);
|
|
7696
|
-
return { value: parsed, rawText, result };
|
|
7697
|
-
} catch (error) {
|
|
7698
|
-
const handled = error instanceof Error ? error : new Error(String(error));
|
|
7699
|
-
failures.push({ attempt, rawText, error: handled });
|
|
7700
|
-
if (providerInfo.provider === "chatgpt" && openAiTextFormatForAttempt) {
|
|
7701
|
-
openAiTextFormatForAttempt = void 0;
|
|
7702
|
-
}
|
|
7703
|
-
if (attempt >= maxAttempts) {
|
|
7704
|
-
throw new LlmJsonCallError(`LLM JSON call failed after ${attempt} attempt(s)`, failures);
|
|
7936
|
+
const call = startJsonStream(
|
|
7937
|
+
{
|
|
7938
|
+
...request,
|
|
7939
|
+
streamMode: "final"
|
|
7940
|
+
},
|
|
7941
|
+
"generateJson"
|
|
7942
|
+
);
|
|
7943
|
+
try {
|
|
7944
|
+
for await (const event of call.events) {
|
|
7945
|
+
if (event.type !== "json") {
|
|
7946
|
+
request.onEvent?.(event);
|
|
7705
7947
|
}
|
|
7706
7948
|
}
|
|
7949
|
+
} catch (streamError) {
|
|
7950
|
+
await call.result.catch(() => void 0);
|
|
7951
|
+
throw streamError;
|
|
7707
7952
|
}
|
|
7708
|
-
|
|
7953
|
+
return await call.result;
|
|
7709
7954
|
}
|
|
7710
7955
|
var DEFAULT_TOOL_LOOP_MAX_STEPS = 8;
|
|
7711
7956
|
function resolveToolLoopContents(input) {
|
|
@@ -8553,7 +8798,7 @@ async function runToolLoop(request) {
|
|
|
8553
8798
|
toolOutputs.push({
|
|
8554
8799
|
type: "custom_tool_call_output",
|
|
8555
8800
|
call_id: entry.ids.callId,
|
|
8556
|
-
output:
|
|
8801
|
+
output: toChatGptToolOutput(outputPayload)
|
|
8557
8802
|
});
|
|
8558
8803
|
} else {
|
|
8559
8804
|
toolOutputs.push({
|
|
@@ -8567,7 +8812,7 @@ async function runToolLoop(request) {
|
|
|
8567
8812
|
toolOutputs.push({
|
|
8568
8813
|
type: "function_call_output",
|
|
8569
8814
|
call_id: entry.ids.callId,
|
|
8570
|
-
output:
|
|
8815
|
+
output: toChatGptToolOutput(outputPayload)
|
|
8571
8816
|
});
|
|
8572
8817
|
}
|
|
8573
8818
|
}
|
|
@@ -9321,7 +9566,10 @@ function streamToolLoop(request) {
|
|
|
9321
9566
|
abort: () => abortController.abort()
|
|
9322
9567
|
};
|
|
9323
9568
|
}
|
|
9324
|
-
var
|
|
9569
|
+
var IMAGE_GRADE_VALUE_SCHEMA = import_zod3.z.enum(["pass", "fail"]);
|
|
9570
|
+
var IMAGE_GRADE_SCHEMA = import_zod3.z.object({
|
|
9571
|
+
grade: IMAGE_GRADE_VALUE_SCHEMA
|
|
9572
|
+
});
|
|
9325
9573
|
async function gradeGeneratedImage(params) {
|
|
9326
9574
|
const parts = [
|
|
9327
9575
|
{
|
|
@@ -9332,7 +9580,7 @@ async function gradeGeneratedImage(params) {
|
|
|
9332
9580
|
"Image prompt to grade:",
|
|
9333
9581
|
params.imagePrompt,
|
|
9334
9582
|
"",
|
|
9335
|
-
'Respond with
|
|
9583
|
+
'Respond with JSON like {"grade":"pass"} or {"grade":"fail"}.'
|
|
9336
9584
|
].join("\\n")
|
|
9337
9585
|
},
|
|
9338
9586
|
{
|
|
@@ -9341,12 +9589,13 @@ async function gradeGeneratedImage(params) {
|
|
|
9341
9589
|
mimeType: params.image.mimeType ?? "image/png"
|
|
9342
9590
|
}
|
|
9343
9591
|
];
|
|
9344
|
-
const { value } = await generateJson({
|
|
9592
|
+
const { value, result } = await generateJson({
|
|
9345
9593
|
model: params.model,
|
|
9346
9594
|
input: [{ role: "user", content: parts }],
|
|
9347
|
-
schema: IMAGE_GRADE_SCHEMA
|
|
9595
|
+
schema: IMAGE_GRADE_SCHEMA,
|
|
9596
|
+
telemetry: false
|
|
9348
9597
|
});
|
|
9349
|
-
return value;
|
|
9598
|
+
return { grade: value.grade, result };
|
|
9350
9599
|
}
|
|
9351
9600
|
async function generateImages(request) {
|
|
9352
9601
|
const maxAttempts = Math.max(1, Math.floor(request.maxAttempts ?? 4));
|
|
@@ -9366,6 +9615,19 @@ async function generateImages(request) {
|
|
|
9366
9615
|
if (!gradingPrompt) {
|
|
9367
9616
|
throw new Error("imageGradingPrompt must be a non-empty string");
|
|
9368
9617
|
}
|
|
9618
|
+
const telemetry = createLlmTelemetryEmitter({
|
|
9619
|
+
telemetry: request.telemetry,
|
|
9620
|
+
operation: "generateImages",
|
|
9621
|
+
provider: resolveProvider(request.model).provider,
|
|
9622
|
+
model: request.model
|
|
9623
|
+
});
|
|
9624
|
+
const startedAtMs = Date.now();
|
|
9625
|
+
telemetry.emit({
|
|
9626
|
+
type: "llm.call.started",
|
|
9627
|
+
imagePromptCount: promptList.length,
|
|
9628
|
+
styleImageCount: request.styleImages?.length ?? 0,
|
|
9629
|
+
maxAttempts
|
|
9630
|
+
});
|
|
9369
9631
|
const addText = (parts, text) => {
|
|
9370
9632
|
const lastPart = parts[parts.length - 1];
|
|
9371
9633
|
if (lastPart !== void 0 && lastPart.type === "text") {
|
|
@@ -9423,6 +9685,9 @@ async function generateImages(request) {
|
|
|
9423
9685
|
const inputMessages = [{ role: "user", content: buildInitialPromptParts() }];
|
|
9424
9686
|
const orderedEntries = [...promptEntries];
|
|
9425
9687
|
const resolvedImages = /* @__PURE__ */ new Map();
|
|
9688
|
+
let totalCostUsd = 0;
|
|
9689
|
+
let totalUsage;
|
|
9690
|
+
let attemptsUsed = 0;
|
|
9426
9691
|
const removeResolvedEntries = (resolved) => {
|
|
9427
9692
|
if (resolved.size === 0) {
|
|
9428
9693
|
return;
|
|
@@ -9437,70 +9702,118 @@ async function generateImages(request) {
|
|
|
9437
9702
|
}
|
|
9438
9703
|
}
|
|
9439
9704
|
};
|
|
9440
|
-
|
|
9441
|
-
|
|
9442
|
-
|
|
9443
|
-
|
|
9444
|
-
|
|
9445
|
-
|
|
9446
|
-
|
|
9447
|
-
|
|
9448
|
-
|
|
9449
|
-
|
|
9450
|
-
|
|
9451
|
-
|
|
9452
|
-
|
|
9453
|
-
|
|
9454
|
-
|
|
9455
|
-
|
|
9456
|
-
|
|
9457
|
-
|
|
9458
|
-
|
|
9459
|
-
|
|
9460
|
-
|
|
9461
|
-
|
|
9462
|
-
|
|
9463
|
-
|
|
9464
|
-
|
|
9705
|
+
let uploadMetrics = emptyFileUploadMetrics();
|
|
9706
|
+
try {
|
|
9707
|
+
await collectFileUploadMetrics(async () => {
|
|
9708
|
+
try {
|
|
9709
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
|
|
9710
|
+
attemptsUsed = attempt;
|
|
9711
|
+
const result = await generateText({
|
|
9712
|
+
model: request.model,
|
|
9713
|
+
input: inputMessages,
|
|
9714
|
+
responseModalities: ["IMAGE", "TEXT"],
|
|
9715
|
+
imageAspectRatio: request.imageAspectRatio,
|
|
9716
|
+
imageSize: request.imageSize ?? "2K",
|
|
9717
|
+
telemetry: false
|
|
9718
|
+
});
|
|
9719
|
+
totalCostUsd += result.costUsd;
|
|
9720
|
+
totalUsage = sumUsageTokens(totalUsage, result.usage);
|
|
9721
|
+
if (result.blocked || !result.content) {
|
|
9722
|
+
continue;
|
|
9723
|
+
}
|
|
9724
|
+
const images = extractImages(result.content);
|
|
9725
|
+
if (images.length > 0 && promptEntries.length > 0) {
|
|
9726
|
+
const assignedCount = Math.min(images.length, promptEntries.length);
|
|
9727
|
+
const pendingAssignments = promptEntries.slice(0, assignedCount);
|
|
9728
|
+
const assignedImages = images.slice(0, assignedCount);
|
|
9729
|
+
const gradeResults = await Promise.all(
|
|
9730
|
+
pendingAssignments.map(
|
|
9731
|
+
(entry, index) => gradeGeneratedImage({
|
|
9732
|
+
gradingPrompt,
|
|
9733
|
+
imagePrompt: entry.prompt,
|
|
9734
|
+
image: (() => {
|
|
9735
|
+
const image = assignedImages[index];
|
|
9736
|
+
if (!image) {
|
|
9737
|
+
throw new Error("Image generation returned fewer images than expected.");
|
|
9738
|
+
}
|
|
9739
|
+
return image;
|
|
9740
|
+
})(),
|
|
9741
|
+
model: "gpt-5.2"
|
|
9742
|
+
})
|
|
9743
|
+
)
|
|
9744
|
+
);
|
|
9745
|
+
const passedEntries = /* @__PURE__ */ new Set();
|
|
9746
|
+
for (let i = 0; i < gradeResults.length; i += 1) {
|
|
9747
|
+
const gradeResult = gradeResults[i];
|
|
9748
|
+
const entry = pendingAssignments[i];
|
|
9749
|
+
const image = assignedImages[i];
|
|
9750
|
+
if (!gradeResult || !entry || !image) {
|
|
9751
|
+
continue;
|
|
9465
9752
|
}
|
|
9466
|
-
|
|
9467
|
-
|
|
9468
|
-
|
|
9469
|
-
|
|
9470
|
-
|
|
9471
|
-
|
|
9472
|
-
|
|
9473
|
-
|
|
9474
|
-
|
|
9475
|
-
|
|
9476
|
-
|
|
9477
|
-
|
|
9478
|
-
|
|
9479
|
-
|
|
9480
|
-
|
|
9481
|
-
|
|
9482
|
-
|
|
9753
|
+
totalCostUsd += gradeResult.result.costUsd;
|
|
9754
|
+
totalUsage = sumUsageTokens(totalUsage, gradeResult.result.usage);
|
|
9755
|
+
if (gradeResult.grade === "pass") {
|
|
9756
|
+
resolvedImages.set(entry.index, image);
|
|
9757
|
+
passedEntries.add(entry.index);
|
|
9758
|
+
}
|
|
9759
|
+
}
|
|
9760
|
+
removeResolvedEntries(passedEntries);
|
|
9761
|
+
}
|
|
9762
|
+
if (promptEntries.length === 0) {
|
|
9763
|
+
break;
|
|
9764
|
+
}
|
|
9765
|
+
inputMessages.push({
|
|
9766
|
+
role: "assistant",
|
|
9767
|
+
content: result.content.parts
|
|
9768
|
+
});
|
|
9769
|
+
inputMessages.push({
|
|
9770
|
+
role: "user",
|
|
9771
|
+
content: buildContinuationPromptParts(promptEntries)
|
|
9772
|
+
});
|
|
9483
9773
|
}
|
|
9774
|
+
} finally {
|
|
9775
|
+
uploadMetrics = getCurrentFileUploadMetrics();
|
|
9484
9776
|
}
|
|
9485
|
-
removeResolvedEntries(passedEntries);
|
|
9486
|
-
}
|
|
9487
|
-
if (promptEntries.length === 0) {
|
|
9488
|
-
break;
|
|
9489
|
-
}
|
|
9490
|
-
inputMessages.push({
|
|
9491
|
-
role: "assistant",
|
|
9492
|
-
content: result.content.parts
|
|
9493
9777
|
});
|
|
9494
|
-
|
|
9495
|
-
|
|
9496
|
-
|
|
9497
|
-
|
|
9498
|
-
|
|
9499
|
-
|
|
9500
|
-
orderedImages.push(image);
|
|
9778
|
+
const orderedImages = [];
|
|
9779
|
+
for (const entry of orderedEntries) {
|
|
9780
|
+
const image = resolvedImages.get(entry.index);
|
|
9781
|
+
if (image) {
|
|
9782
|
+
orderedImages.push(image);
|
|
9783
|
+
}
|
|
9501
9784
|
}
|
|
9785
|
+
const outputImages = orderedImages.slice(0, numImages);
|
|
9786
|
+
telemetry.emit({
|
|
9787
|
+
type: "llm.call.completed",
|
|
9788
|
+
success: true,
|
|
9789
|
+
durationMs: Math.max(0, Date.now() - startedAtMs),
|
|
9790
|
+
usage: totalUsage,
|
|
9791
|
+
costUsd: totalCostUsd,
|
|
9792
|
+
imageCount: outputImages.length,
|
|
9793
|
+
attempts: attemptsUsed,
|
|
9794
|
+
uploadCount: uploadMetrics.count,
|
|
9795
|
+
uploadBytes: uploadMetrics.totalBytes,
|
|
9796
|
+
uploadLatencyMs: uploadMetrics.totalLatencyMs
|
|
9797
|
+
});
|
|
9798
|
+
return outputImages;
|
|
9799
|
+
} catch (error) {
|
|
9800
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
9801
|
+
telemetry.emit({
|
|
9802
|
+
type: "llm.call.completed",
|
|
9803
|
+
success: false,
|
|
9804
|
+
durationMs: Math.max(0, Date.now() - startedAtMs),
|
|
9805
|
+
usage: totalUsage,
|
|
9806
|
+
costUsd: totalCostUsd,
|
|
9807
|
+
attempts: attemptsUsed > 0 ? attemptsUsed : void 0,
|
|
9808
|
+
uploadCount: uploadMetrics.count,
|
|
9809
|
+
uploadBytes: uploadMetrics.totalBytes,
|
|
9810
|
+
uploadLatencyMs: uploadMetrics.totalLatencyMs,
|
|
9811
|
+
error: err.message
|
|
9812
|
+
});
|
|
9813
|
+
throw err;
|
|
9814
|
+
} finally {
|
|
9815
|
+
await telemetry.flush();
|
|
9502
9816
|
}
|
|
9503
|
-
return orderedImages.slice(0, numImages);
|
|
9504
9817
|
}
|
|
9505
9818
|
async function generateImageInBatches(request) {
|
|
9506
9819
|
const {
|
|
@@ -12151,7 +12464,7 @@ function isNoEntError(error) {
|
|
|
12151
12464
|
|
|
12152
12465
|
// src/agent.ts
|
|
12153
12466
|
async function runAgentLoop(request) {
|
|
12154
|
-
const telemetry =
|
|
12467
|
+
const telemetry = createTelemetrySession(request.telemetry);
|
|
12155
12468
|
const logging = createRootAgentLoggingSession(request);
|
|
12156
12469
|
try {
|
|
12157
12470
|
return await runWithAgentLoggingSession(logging, async () => {
|
|
@@ -12237,7 +12550,7 @@ async function runAgentLoopInternal(request, context) {
|
|
|
12237
12550
|
logging: _logging,
|
|
12238
12551
|
...toolLoopRequest
|
|
12239
12552
|
} = request;
|
|
12240
|
-
const telemetrySession = context.telemetry ??
|
|
12553
|
+
const telemetrySession = context.telemetry ?? createTelemetrySession(telemetry);
|
|
12241
12554
|
const loggingSession = context.logging;
|
|
12242
12555
|
const runId = randomRunId();
|
|
12243
12556
|
const startedAtMs = Date.now();
|
|
@@ -12300,15 +12613,15 @@ async function runAgentLoopInternal(request, context) {
|
|
|
12300
12613
|
].join(" ")
|
|
12301
12614
|
);
|
|
12302
12615
|
const sourceOnEvent = toolLoopRequestWithSteering.onEvent;
|
|
12303
|
-
const
|
|
12616
|
+
const includeStreamEvents = telemetrySession?.includeStreamEvents === true;
|
|
12304
12617
|
const streamEventLogger = loggingSession ? createAgentStreamEventLogger({
|
|
12305
12618
|
append: (line) => {
|
|
12306
12619
|
loggingSession.logLine(`[agent:${runId}] ${line}`);
|
|
12307
12620
|
}
|
|
12308
12621
|
}) : void 0;
|
|
12309
|
-
const wrappedOnEvent = sourceOnEvent ||
|
|
12622
|
+
const wrappedOnEvent = sourceOnEvent || includeStreamEvents ? (event) => {
|
|
12310
12623
|
sourceOnEvent?.(event);
|
|
12311
|
-
if (
|
|
12624
|
+
if (includeStreamEvents) {
|
|
12312
12625
|
emitTelemetry({ type: "agent.run.stream", event });
|
|
12313
12626
|
}
|
|
12314
12627
|
streamEventLogger?.appendEvent(event);
|
|
@@ -12546,7 +12859,7 @@ function countToolCalls(result) {
|
|
|
12546
12859
|
}
|
|
12547
12860
|
return count;
|
|
12548
12861
|
}
|
|
12549
|
-
function
|
|
12862
|
+
function sumUsageValue2(current, next) {
|
|
12550
12863
|
if (typeof next !== "number" || !Number.isFinite(next)) {
|
|
12551
12864
|
return current;
|
|
12552
12865
|
}
|
|
@@ -12564,20 +12877,17 @@ function summarizeResultUsage(result) {
|
|
|
12564
12877
|
continue;
|
|
12565
12878
|
}
|
|
12566
12879
|
summary = {
|
|
12567
|
-
promptTokens:
|
|
12568
|
-
cachedTokens:
|
|
12569
|
-
responseTokens:
|
|
12570
|
-
responseImageTokens:
|
|
12571
|
-
thinkingTokens:
|
|
12572
|
-
totalTokens:
|
|
12573
|
-
toolUsePromptTokens:
|
|
12880
|
+
promptTokens: sumUsageValue2(summary?.promptTokens, usage.promptTokens),
|
|
12881
|
+
cachedTokens: sumUsageValue2(summary?.cachedTokens, usage.cachedTokens),
|
|
12882
|
+
responseTokens: sumUsageValue2(summary?.responseTokens, usage.responseTokens),
|
|
12883
|
+
responseImageTokens: sumUsageValue2(summary?.responseImageTokens, usage.responseImageTokens),
|
|
12884
|
+
thinkingTokens: sumUsageValue2(summary?.thinkingTokens, usage.thinkingTokens),
|
|
12885
|
+
totalTokens: sumUsageValue2(summary?.totalTokens, usage.totalTokens),
|
|
12886
|
+
toolUsePromptTokens: sumUsageValue2(summary?.toolUsePromptTokens, usage.toolUsePromptTokens)
|
|
12574
12887
|
};
|
|
12575
12888
|
}
|
|
12576
12889
|
return summary;
|
|
12577
12890
|
}
|
|
12578
|
-
function isPromiseLike2(value) {
|
|
12579
|
-
return (typeof value === "object" || typeof value === "function") && value !== null && typeof value.then === "function";
|
|
12580
|
-
}
|
|
12581
12891
|
function resolveAgentLoggingSelection(value) {
|
|
12582
12892
|
if (value === false) {
|
|
12583
12893
|
return void 0;
|
|
@@ -12611,60 +12921,6 @@ function createRootAgentLoggingSession(request) {
|
|
|
12611
12921
|
mirrorToConsole: selected.mirrorToConsole !== false
|
|
12612
12922
|
});
|
|
12613
12923
|
}
|
|
12614
|
-
function isAgentTelemetrySink(value) {
|
|
12615
|
-
return typeof value === "object" && value !== null && typeof value.emit === "function";
|
|
12616
|
-
}
|
|
12617
|
-
function resolveTelemetrySelection(telemetry) {
|
|
12618
|
-
if (!telemetry) {
|
|
12619
|
-
return void 0;
|
|
12620
|
-
}
|
|
12621
|
-
if (isAgentTelemetrySink(telemetry)) {
|
|
12622
|
-
return { sink: telemetry };
|
|
12623
|
-
}
|
|
12624
|
-
if (isAgentTelemetrySink(telemetry.sink)) {
|
|
12625
|
-
return telemetry;
|
|
12626
|
-
}
|
|
12627
|
-
throw new Error("Invalid runAgentLoop telemetry config: expected a sink with emit(event).");
|
|
12628
|
-
}
|
|
12629
|
-
function createAgentTelemetrySession(telemetry) {
|
|
12630
|
-
const config = resolveTelemetrySelection(telemetry);
|
|
12631
|
-
if (!config) {
|
|
12632
|
-
return void 0;
|
|
12633
|
-
}
|
|
12634
|
-
const pending = /* @__PURE__ */ new Set();
|
|
12635
|
-
const trackPromise = (promise) => {
|
|
12636
|
-
pending.add(promise);
|
|
12637
|
-
promise.finally(() => {
|
|
12638
|
-
pending.delete(promise);
|
|
12639
|
-
});
|
|
12640
|
-
};
|
|
12641
|
-
const emit = (event) => {
|
|
12642
|
-
try {
|
|
12643
|
-
const output = config.sink.emit(event);
|
|
12644
|
-
if (isPromiseLike2(output)) {
|
|
12645
|
-
const task = Promise.resolve(output).then(() => void 0).catch(() => void 0);
|
|
12646
|
-
trackPromise(task);
|
|
12647
|
-
}
|
|
12648
|
-
} catch {
|
|
12649
|
-
}
|
|
12650
|
-
};
|
|
12651
|
-
const flush = async () => {
|
|
12652
|
-
while (pending.size > 0) {
|
|
12653
|
-
await Promise.allSettled([...pending]);
|
|
12654
|
-
}
|
|
12655
|
-
if (typeof config.sink.flush === "function") {
|
|
12656
|
-
try {
|
|
12657
|
-
await config.sink.flush();
|
|
12658
|
-
} catch {
|
|
12659
|
-
}
|
|
12660
|
-
}
|
|
12661
|
-
};
|
|
12662
|
-
return {
|
|
12663
|
-
includeLlmStreamEvents: config.includeLlmStreamEvents === true,
|
|
12664
|
-
emit,
|
|
12665
|
-
flush
|
|
12666
|
-
};
|
|
12667
|
-
}
|
|
12668
12924
|
function createAgentTelemetryEmitter(params) {
|
|
12669
12925
|
return (event) => {
|
|
12670
12926
|
if (!params.session) {
|
|
@@ -13358,6 +13614,7 @@ async function runCandidateEvolution(options) {
|
|
|
13358
13614
|
applyPatch,
|
|
13359
13615
|
configureGemini,
|
|
13360
13616
|
configureModelConcurrency,
|
|
13617
|
+
configureTelemetry,
|
|
13361
13618
|
convertGooglePartsToLlmParts,
|
|
13362
13619
|
createApplyPatchTool,
|
|
13363
13620
|
createCodexApplyPatchTool,
|
|
@@ -13406,6 +13663,7 @@ async function runCandidateEvolution(options) {
|
|
|
13406
13663
|
parseJsonFromLlmText,
|
|
13407
13664
|
refreshChatGptOauthToken,
|
|
13408
13665
|
resetModelConcurrencyConfig,
|
|
13666
|
+
resetTelemetry,
|
|
13409
13667
|
resolveFilesystemToolProfile,
|
|
13410
13668
|
resolveFireworksModelId,
|
|
13411
13669
|
runAgentLoop,
|