@ljoukov/llm 4.0.6 → 4.0.8
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 +4 -3
- package/dist/index.cjs +639 -278
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +639 -278
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -329,6 +329,34 @@ function estimateCallCostUsd({
|
|
|
329
329
|
import os2 from "os";
|
|
330
330
|
import { TextDecoder } from "util";
|
|
331
331
|
|
|
332
|
+
// src/utils/runtimeSingleton.ts
|
|
333
|
+
var runtimeSingletonStoreKey = /* @__PURE__ */ Symbol.for("@ljoukov/llm.runtimeSingletonStore");
|
|
334
|
+
function getRuntimeSingletonStore() {
|
|
335
|
+
const globalObject = globalThis;
|
|
336
|
+
const existingStore = globalObject[runtimeSingletonStoreKey];
|
|
337
|
+
if (existingStore) {
|
|
338
|
+
return existingStore;
|
|
339
|
+
}
|
|
340
|
+
const store = /* @__PURE__ */ new Map();
|
|
341
|
+
Object.defineProperty(globalObject, runtimeSingletonStoreKey, {
|
|
342
|
+
value: store,
|
|
343
|
+
enumerable: false,
|
|
344
|
+
configurable: false,
|
|
345
|
+
writable: false
|
|
346
|
+
});
|
|
347
|
+
return store;
|
|
348
|
+
}
|
|
349
|
+
function getRuntimeSingleton(key, create) {
|
|
350
|
+
const store = getRuntimeSingletonStore();
|
|
351
|
+
const existingValue = store.get(key);
|
|
352
|
+
if (existingValue !== void 0) {
|
|
353
|
+
return existingValue;
|
|
354
|
+
}
|
|
355
|
+
const createdValue = create();
|
|
356
|
+
store.set(key, createdValue);
|
|
357
|
+
return createdValue;
|
|
358
|
+
}
|
|
359
|
+
|
|
332
360
|
// src/openai/chatgpt-auth.ts
|
|
333
361
|
import { Buffer as Buffer2 } from "buffer";
|
|
334
362
|
import fs2 from "fs";
|
|
@@ -339,14 +367,16 @@ import { z } from "zod";
|
|
|
339
367
|
// src/utils/env.ts
|
|
340
368
|
import fs from "fs";
|
|
341
369
|
import path from "path";
|
|
342
|
-
var
|
|
370
|
+
var envState = getRuntimeSingleton(/* @__PURE__ */ Symbol.for("@ljoukov/llm.envState"), () => ({
|
|
371
|
+
envLoaded: false
|
|
372
|
+
}));
|
|
343
373
|
function loadLocalEnv() {
|
|
344
|
-
if (envLoaded) {
|
|
374
|
+
if (envState.envLoaded) {
|
|
345
375
|
return;
|
|
346
376
|
}
|
|
347
377
|
const envPath = path.join(process.cwd(), ".env.local");
|
|
348
378
|
loadEnvFromFile(envPath, { override: false });
|
|
349
|
-
envLoaded = true;
|
|
379
|
+
envState.envLoaded = true;
|
|
350
380
|
}
|
|
351
381
|
function loadEnvFromFile(filePath, { override = false } = {}) {
|
|
352
382
|
let content;
|
|
@@ -432,8 +462,10 @@ var ExchangeResponseSchema = z.object({
|
|
|
432
462
|
expires_in: z.union([z.number(), z.string()]),
|
|
433
463
|
id_token: z.string().optional()
|
|
434
464
|
});
|
|
435
|
-
var
|
|
436
|
-
|
|
465
|
+
var chatGptAuthState = getRuntimeSingleton(/* @__PURE__ */ Symbol.for("@ljoukov/llm.chatGptAuthState"), () => ({
|
|
466
|
+
cachedProfile: null,
|
|
467
|
+
refreshPromise: null
|
|
468
|
+
}));
|
|
437
469
|
async function fetchChatGptAuthProfileFromTokenProvider(options) {
|
|
438
470
|
const base = options.baseUrl.replace(/\/+$/u, "");
|
|
439
471
|
const store = options.store?.trim() ? options.store.trim() : "kv";
|
|
@@ -534,13 +566,13 @@ async function getChatGptAuthProfile() {
|
|
|
534
566
|
const tokenProviderUrl = process.env[CHATGPT_AUTH_TOKEN_PROVIDER_URL_ENV];
|
|
535
567
|
const tokenProviderKey = process.env[CHATGPT_AUTH_TOKEN_PROVIDER_API_KEY_ENV] ?? process.env[CHATGPT_AUTH_API_KEY_ENV];
|
|
536
568
|
if (tokenProviderUrl && tokenProviderUrl.trim().length > 0 && tokenProviderKey && tokenProviderKey.trim().length > 0) {
|
|
537
|
-
if (cachedProfile && !isExpired(cachedProfile)) {
|
|
538
|
-
return cachedProfile;
|
|
569
|
+
if (chatGptAuthState.cachedProfile && !isExpired(chatGptAuthState.cachedProfile)) {
|
|
570
|
+
return chatGptAuthState.cachedProfile;
|
|
539
571
|
}
|
|
540
|
-
if (refreshPromise) {
|
|
541
|
-
return refreshPromise;
|
|
572
|
+
if (chatGptAuthState.refreshPromise) {
|
|
573
|
+
return chatGptAuthState.refreshPromise;
|
|
542
574
|
}
|
|
543
|
-
refreshPromise = (async () => {
|
|
575
|
+
chatGptAuthState.refreshPromise = (async () => {
|
|
544
576
|
try {
|
|
545
577
|
const store = process.env[CHATGPT_AUTH_TOKEN_PROVIDER_STORE_ENV];
|
|
546
578
|
const profile = await fetchChatGptAuthProfileFromTokenProvider({
|
|
@@ -548,31 +580,31 @@ async function getChatGptAuthProfile() {
|
|
|
548
580
|
apiKey: tokenProviderKey,
|
|
549
581
|
store: store ?? void 0
|
|
550
582
|
});
|
|
551
|
-
cachedProfile = profile;
|
|
583
|
+
chatGptAuthState.cachedProfile = profile;
|
|
552
584
|
return profile;
|
|
553
585
|
} finally {
|
|
554
|
-
refreshPromise = null;
|
|
586
|
+
chatGptAuthState.refreshPromise = null;
|
|
555
587
|
}
|
|
556
588
|
})();
|
|
557
|
-
return refreshPromise;
|
|
589
|
+
return chatGptAuthState.refreshPromise;
|
|
558
590
|
}
|
|
559
|
-
if (cachedProfile && !isExpired(cachedProfile)) {
|
|
560
|
-
return cachedProfile;
|
|
591
|
+
if (chatGptAuthState.cachedProfile && !isExpired(chatGptAuthState.cachedProfile)) {
|
|
592
|
+
return chatGptAuthState.cachedProfile;
|
|
561
593
|
}
|
|
562
|
-
if (refreshPromise) {
|
|
563
|
-
return refreshPromise;
|
|
594
|
+
if (chatGptAuthState.refreshPromise) {
|
|
595
|
+
return chatGptAuthState.refreshPromise;
|
|
564
596
|
}
|
|
565
|
-
refreshPromise = (async () => {
|
|
597
|
+
chatGptAuthState.refreshPromise = (async () => {
|
|
566
598
|
try {
|
|
567
|
-
const baseProfile = cachedProfile ?? loadAuthProfileFromCodexStore();
|
|
599
|
+
const baseProfile = chatGptAuthState.cachedProfile ?? loadAuthProfileFromCodexStore();
|
|
568
600
|
const profile = isExpired(baseProfile) ? await refreshAndPersistCodexProfile(baseProfile) : baseProfile;
|
|
569
|
-
cachedProfile = profile;
|
|
601
|
+
chatGptAuthState.cachedProfile = profile;
|
|
570
602
|
return profile;
|
|
571
603
|
} finally {
|
|
572
|
-
refreshPromise = null;
|
|
604
|
+
chatGptAuthState.refreshPromise = null;
|
|
573
605
|
}
|
|
574
606
|
})();
|
|
575
|
-
return refreshPromise;
|
|
607
|
+
return chatGptAuthState.refreshPromise;
|
|
576
608
|
}
|
|
577
609
|
function resolveCodexHome() {
|
|
578
610
|
const codexHome = process.env.CODEX_HOME;
|
|
@@ -1304,8 +1336,10 @@ function createAbortError(reason) {
|
|
|
1304
1336
|
// src/openai/chatgpt-codex.ts
|
|
1305
1337
|
var CHATGPT_CODEX_ENDPOINT = "https://chatgpt.com/backend-api/codex/responses";
|
|
1306
1338
|
var CHATGPT_RESPONSES_EXPERIMENTAL_HEADER = "responses=experimental";
|
|
1307
|
-
var
|
|
1308
|
-
|
|
1339
|
+
var chatGptCodexState = getRuntimeSingleton(/* @__PURE__ */ Symbol.for("@ljoukov/llm.chatGptCodexState"), () => ({
|
|
1340
|
+
cachedResponsesWebSocketMode: null,
|
|
1341
|
+
chatGptResponsesWebSocketDisabled: false
|
|
1342
|
+
}));
|
|
1309
1343
|
async function streamChatGptCodexResponse(options) {
|
|
1310
1344
|
const { access, accountId } = await getChatGptAuthProfile();
|
|
1311
1345
|
const mode = resolveChatGptResponsesWebSocketMode();
|
|
@@ -1331,7 +1365,7 @@ async function streamChatGptCodexResponse(options) {
|
|
|
1331
1365
|
}
|
|
1332
1366
|
};
|
|
1333
1367
|
};
|
|
1334
|
-
if (mode === "off" || chatGptResponsesWebSocketDisabled) {
|
|
1368
|
+
if (mode === "off" || chatGptCodexState.chatGptResponsesWebSocketDisabled) {
|
|
1335
1369
|
return fallbackStreamFactory();
|
|
1336
1370
|
}
|
|
1337
1371
|
const websocketHeaders = buildChatGptCodexHeaders({
|
|
@@ -1350,7 +1384,7 @@ async function streamChatGptCodexResponse(options) {
|
|
|
1350
1384
|
}),
|
|
1351
1385
|
createFallbackStream: fallbackStreamFactory,
|
|
1352
1386
|
onWebSocketFallback: () => {
|
|
1353
|
-
chatGptResponsesWebSocketDisabled = true;
|
|
1387
|
+
chatGptCodexState.chatGptResponsesWebSocketDisabled = true;
|
|
1354
1388
|
}
|
|
1355
1389
|
});
|
|
1356
1390
|
}
|
|
@@ -1380,14 +1414,14 @@ async function streamChatGptCodexResponseSse(options) {
|
|
|
1380
1414
|
return parseEventStream(body);
|
|
1381
1415
|
}
|
|
1382
1416
|
function resolveChatGptResponsesWebSocketMode() {
|
|
1383
|
-
if (cachedResponsesWebSocketMode) {
|
|
1384
|
-
return cachedResponsesWebSocketMode;
|
|
1417
|
+
if (chatGptCodexState.cachedResponsesWebSocketMode) {
|
|
1418
|
+
return chatGptCodexState.cachedResponsesWebSocketMode;
|
|
1385
1419
|
}
|
|
1386
|
-
cachedResponsesWebSocketMode = resolveResponsesWebSocketMode(
|
|
1420
|
+
chatGptCodexState.cachedResponsesWebSocketMode = resolveResponsesWebSocketMode(
|
|
1387
1421
|
process.env.CHATGPT_RESPONSES_WEBSOCKET_MODE ?? process.env.OPENAI_RESPONSES_WEBSOCKET_MODE,
|
|
1388
1422
|
"auto"
|
|
1389
1423
|
);
|
|
1390
|
-
return cachedResponsesWebSocketMode;
|
|
1424
|
+
return chatGptCodexState.cachedResponsesWebSocketMode;
|
|
1391
1425
|
}
|
|
1392
1426
|
function buildChatGptCodexHeaders(options) {
|
|
1393
1427
|
const openAiBeta = options.useWebSocket ? mergeOpenAiBetaHeader(
|
|
@@ -1425,8 +1459,8 @@ async function collectChatGptCodexResponse(options) {
|
|
|
1425
1459
|
}
|
|
1426
1460
|
});
|
|
1427
1461
|
} catch (error) {
|
|
1428
|
-
if (!sawAnyDelta && !retriedViaSseFallback && shouldRetryViaSseFallback(error) && !chatGptResponsesWebSocketDisabled) {
|
|
1429
|
-
chatGptResponsesWebSocketDisabled = true;
|
|
1462
|
+
if (!sawAnyDelta && !retriedViaSseFallback && shouldRetryViaSseFallback(error) && !chatGptCodexState.chatGptResponsesWebSocketDisabled) {
|
|
1463
|
+
chatGptCodexState.chatGptResponsesWebSocketDisabled = true;
|
|
1430
1464
|
retriedViaSseFallback = true;
|
|
1431
1465
|
continue;
|
|
1432
1466
|
}
|
|
@@ -1662,7 +1696,12 @@ var MODEL_CONCURRENCY_PROVIDERS = [
|
|
|
1662
1696
|
"google",
|
|
1663
1697
|
"fireworks"
|
|
1664
1698
|
];
|
|
1665
|
-
var
|
|
1699
|
+
var modelConcurrencyState = getRuntimeSingleton(
|
|
1700
|
+
/* @__PURE__ */ Symbol.for("@ljoukov/llm.modelConcurrencyState"),
|
|
1701
|
+
() => ({
|
|
1702
|
+
configuredModelConcurrency: normalizeModelConcurrencyConfig({})
|
|
1703
|
+
})
|
|
1704
|
+
);
|
|
1666
1705
|
function clampModelConcurrencyCap(value) {
|
|
1667
1706
|
if (!Number.isFinite(value)) {
|
|
1668
1707
|
return DEFAULT_MODEL_CONCURRENCY_CAP;
|
|
@@ -1736,14 +1775,14 @@ function resolveDefaultProviderCap(provider, modelId) {
|
|
|
1736
1775
|
return DEFAULT_FIREWORKS_MODEL_CONCURRENCY_CAP;
|
|
1737
1776
|
}
|
|
1738
1777
|
function configureModelConcurrency(config = {}) {
|
|
1739
|
-
configuredModelConcurrency = normalizeModelConcurrencyConfig(config);
|
|
1778
|
+
modelConcurrencyState.configuredModelConcurrency = normalizeModelConcurrencyConfig(config);
|
|
1740
1779
|
}
|
|
1741
1780
|
function resetModelConcurrencyConfig() {
|
|
1742
|
-
configuredModelConcurrency = normalizeModelConcurrencyConfig({});
|
|
1781
|
+
modelConcurrencyState.configuredModelConcurrency = normalizeModelConcurrencyConfig({});
|
|
1743
1782
|
}
|
|
1744
1783
|
function resolveModelConcurrencyCap(options) {
|
|
1745
1784
|
const modelId = options.modelId ? normalizeModelIdForConfig(options.modelId) : void 0;
|
|
1746
|
-
const config = options.config ? normalizeModelConcurrencyConfig(options.config) : configuredModelConcurrency;
|
|
1785
|
+
const config = options.config ? normalizeModelConcurrencyConfig(options.config) : modelConcurrencyState.configuredModelConcurrency;
|
|
1747
1786
|
const providerModelCap = modelId ? config.providerModelCaps[options.provider].get(modelId) : void 0;
|
|
1748
1787
|
if (providerModelCap !== void 0) {
|
|
1749
1788
|
return providerModelCap;
|
|
@@ -1977,32 +2016,37 @@ import OpenAI from "openai";
|
|
|
1977
2016
|
import { Agent, fetch as undiciFetch } from "undici";
|
|
1978
2017
|
var DEFAULT_FIREWORKS_BASE_URL = "https://api.fireworks.ai/inference/v1";
|
|
1979
2018
|
var DEFAULT_FIREWORKS_TIMEOUT_MS = 15 * 6e4;
|
|
1980
|
-
var
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
2019
|
+
var fireworksClientState = getRuntimeSingleton(
|
|
2020
|
+
/* @__PURE__ */ Symbol.for("@ljoukov/llm.fireworksClientState"),
|
|
2021
|
+
() => ({
|
|
2022
|
+
cachedClient: null,
|
|
2023
|
+
cachedFetch: null,
|
|
2024
|
+
cachedBaseUrl: null,
|
|
2025
|
+
cachedApiKey: null,
|
|
2026
|
+
cachedTimeoutMs: null
|
|
2027
|
+
})
|
|
2028
|
+
);
|
|
1985
2029
|
function resolveTimeoutMs() {
|
|
1986
|
-
if (cachedTimeoutMs !== null) {
|
|
1987
|
-
return cachedTimeoutMs;
|
|
2030
|
+
if (fireworksClientState.cachedTimeoutMs !== null) {
|
|
2031
|
+
return fireworksClientState.cachedTimeoutMs;
|
|
1988
2032
|
}
|
|
1989
2033
|
const raw = process.env.FIREWORKS_TIMEOUT_MS;
|
|
1990
2034
|
const parsed = raw ? Number(raw) : Number.NaN;
|
|
1991
|
-
cachedTimeoutMs = Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_FIREWORKS_TIMEOUT_MS;
|
|
1992
|
-
return cachedTimeoutMs;
|
|
2035
|
+
fireworksClientState.cachedTimeoutMs = Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_FIREWORKS_TIMEOUT_MS;
|
|
2036
|
+
return fireworksClientState.cachedTimeoutMs;
|
|
1993
2037
|
}
|
|
1994
2038
|
function resolveBaseUrl() {
|
|
1995
|
-
if (cachedBaseUrl !== null) {
|
|
1996
|
-
return cachedBaseUrl;
|
|
2039
|
+
if (fireworksClientState.cachedBaseUrl !== null) {
|
|
2040
|
+
return fireworksClientState.cachedBaseUrl;
|
|
1997
2041
|
}
|
|
1998
2042
|
loadLocalEnv();
|
|
1999
2043
|
const raw = process.env.FIREWORKS_BASE_URL?.trim();
|
|
2000
|
-
cachedBaseUrl = raw && raw.length > 0 ? raw : DEFAULT_FIREWORKS_BASE_URL;
|
|
2001
|
-
return cachedBaseUrl;
|
|
2044
|
+
fireworksClientState.cachedBaseUrl = raw && raw.length > 0 ? raw : DEFAULT_FIREWORKS_BASE_URL;
|
|
2045
|
+
return fireworksClientState.cachedBaseUrl;
|
|
2002
2046
|
}
|
|
2003
2047
|
function resolveApiKey() {
|
|
2004
|
-
if (cachedApiKey !== null) {
|
|
2005
|
-
return cachedApiKey;
|
|
2048
|
+
if (fireworksClientState.cachedApiKey !== null) {
|
|
2049
|
+
return fireworksClientState.cachedApiKey;
|
|
2006
2050
|
}
|
|
2007
2051
|
loadLocalEnv();
|
|
2008
2052
|
const raw = process.env.FIREWORKS_TOKEN ?? process.env.FIREWORKS_API_KEY;
|
|
@@ -2012,46 +2056,51 @@ function resolveApiKey() {
|
|
|
2012
2056
|
"FIREWORKS_TOKEN (or FIREWORKS_API_KEY) must be provided to access Fireworks APIs."
|
|
2013
2057
|
);
|
|
2014
2058
|
}
|
|
2015
|
-
cachedApiKey = token;
|
|
2016
|
-
return cachedApiKey;
|
|
2059
|
+
fireworksClientState.cachedApiKey = token;
|
|
2060
|
+
return fireworksClientState.cachedApiKey;
|
|
2017
2061
|
}
|
|
2018
2062
|
function getFireworksFetch() {
|
|
2019
|
-
if (cachedFetch) {
|
|
2020
|
-
return cachedFetch;
|
|
2063
|
+
if (fireworksClientState.cachedFetch) {
|
|
2064
|
+
return fireworksClientState.cachedFetch;
|
|
2021
2065
|
}
|
|
2022
2066
|
const timeoutMs = resolveTimeoutMs();
|
|
2023
2067
|
const dispatcher = new Agent({
|
|
2024
2068
|
bodyTimeout: timeoutMs,
|
|
2025
2069
|
headersTimeout: timeoutMs
|
|
2026
2070
|
});
|
|
2027
|
-
cachedFetch = ((input, init) => {
|
|
2071
|
+
fireworksClientState.cachedFetch = ((input, init) => {
|
|
2028
2072
|
return undiciFetch(input, {
|
|
2029
2073
|
...init ?? {},
|
|
2030
2074
|
dispatcher
|
|
2031
2075
|
});
|
|
2032
2076
|
});
|
|
2033
|
-
return cachedFetch;
|
|
2077
|
+
return fireworksClientState.cachedFetch;
|
|
2034
2078
|
}
|
|
2035
2079
|
function getFireworksClient() {
|
|
2036
|
-
if (cachedClient) {
|
|
2037
|
-
return cachedClient;
|
|
2080
|
+
if (fireworksClientState.cachedClient) {
|
|
2081
|
+
return fireworksClientState.cachedClient;
|
|
2038
2082
|
}
|
|
2039
|
-
cachedClient = new OpenAI({
|
|
2083
|
+
fireworksClientState.cachedClient = new OpenAI({
|
|
2040
2084
|
apiKey: resolveApiKey(),
|
|
2041
2085
|
baseURL: resolveBaseUrl(),
|
|
2042
2086
|
timeout: resolveTimeoutMs(),
|
|
2043
2087
|
fetch: getFireworksFetch()
|
|
2044
2088
|
});
|
|
2045
|
-
return cachedClient;
|
|
2089
|
+
return fireworksClientState.cachedClient;
|
|
2046
2090
|
}
|
|
2047
2091
|
|
|
2048
2092
|
// src/fireworks/calls.ts
|
|
2049
2093
|
var DEFAULT_SCHEDULER_KEY = "__default__";
|
|
2050
|
-
var
|
|
2094
|
+
var fireworksCallState = getRuntimeSingleton(
|
|
2095
|
+
/* @__PURE__ */ Symbol.for("@ljoukov/llm.fireworksCallState"),
|
|
2096
|
+
() => ({
|
|
2097
|
+
schedulerByModel: /* @__PURE__ */ new Map()
|
|
2098
|
+
})
|
|
2099
|
+
);
|
|
2051
2100
|
function getSchedulerForModel(modelId) {
|
|
2052
2101
|
const normalizedModelId = modelId?.trim();
|
|
2053
2102
|
const schedulerKey = normalizedModelId && normalizedModelId.length > 0 ? normalizedModelId : DEFAULT_SCHEDULER_KEY;
|
|
2054
|
-
const existing = schedulerByModel.get(schedulerKey);
|
|
2103
|
+
const existing = fireworksCallState.schedulerByModel.get(schedulerKey);
|
|
2055
2104
|
if (existing) {
|
|
2056
2105
|
return existing;
|
|
2057
2106
|
}
|
|
@@ -2063,7 +2112,7 @@ function getSchedulerForModel(modelId) {
|
|
|
2063
2112
|
minIntervalBetweenStartMs: 200,
|
|
2064
2113
|
startJitterMs: 200
|
|
2065
2114
|
});
|
|
2066
|
-
schedulerByModel.set(schedulerKey, created);
|
|
2115
|
+
fireworksCallState.schedulerByModel.set(schedulerKey, created);
|
|
2067
2116
|
return created;
|
|
2068
2117
|
}
|
|
2069
2118
|
async function runFireworksCall(fn, modelId, runOptions) {
|
|
@@ -2110,7 +2159,10 @@ var ServiceAccountSchema = z2.object({
|
|
|
2110
2159
|
privateKey: private_key.replace(/\\n/g, "\n"),
|
|
2111
2160
|
tokenUri: token_uri
|
|
2112
2161
|
}));
|
|
2113
|
-
var
|
|
2162
|
+
var googleAuthState = getRuntimeSingleton(/* @__PURE__ */ Symbol.for("@ljoukov/llm.googleAuthState"), () => ({
|
|
2163
|
+
cachedServiceAccount: null,
|
|
2164
|
+
authClientCache: /* @__PURE__ */ new Map()
|
|
2165
|
+
}));
|
|
2114
2166
|
function parseGoogleServiceAccount(input) {
|
|
2115
2167
|
let parsed;
|
|
2116
2168
|
try {
|
|
@@ -2121,16 +2173,16 @@ function parseGoogleServiceAccount(input) {
|
|
|
2121
2173
|
return ServiceAccountSchema.parse(parsed);
|
|
2122
2174
|
}
|
|
2123
2175
|
function getGoogleServiceAccount() {
|
|
2124
|
-
if (cachedServiceAccount) {
|
|
2125
|
-
return cachedServiceAccount;
|
|
2176
|
+
if (googleAuthState.cachedServiceAccount) {
|
|
2177
|
+
return googleAuthState.cachedServiceAccount;
|
|
2126
2178
|
}
|
|
2127
2179
|
loadLocalEnv();
|
|
2128
2180
|
const raw = process.env.GOOGLE_SERVICE_ACCOUNT_JSON;
|
|
2129
2181
|
if (!raw || raw.trim().length === 0) {
|
|
2130
2182
|
throw new Error("GOOGLE_SERVICE_ACCOUNT_JSON must be provided for Google APIs access.");
|
|
2131
2183
|
}
|
|
2132
|
-
cachedServiceAccount = parseGoogleServiceAccount(raw);
|
|
2133
|
-
return cachedServiceAccount;
|
|
2184
|
+
googleAuthState.cachedServiceAccount = parseGoogleServiceAccount(raw);
|
|
2185
|
+
return googleAuthState.cachedServiceAccount;
|
|
2134
2186
|
}
|
|
2135
2187
|
function normaliseScopes(scopes) {
|
|
2136
2188
|
if (!scopes) {
|
|
@@ -2182,8 +2234,10 @@ function isGeminiImageModelId(value) {
|
|
|
2182
2234
|
}
|
|
2183
2235
|
var CLOUD_PLATFORM_SCOPE = "https://www.googleapis.com/auth/cloud-platform";
|
|
2184
2236
|
var DEFAULT_VERTEX_LOCATION = "global";
|
|
2185
|
-
var
|
|
2186
|
-
|
|
2237
|
+
var geminiClientState = getRuntimeSingleton(/* @__PURE__ */ Symbol.for("@ljoukov/llm.geminiClientState"), () => ({
|
|
2238
|
+
geminiConfiguration: {},
|
|
2239
|
+
clientPromise: void 0
|
|
2240
|
+
}));
|
|
2187
2241
|
function normaliseConfigValue(value) {
|
|
2188
2242
|
if (value === void 0 || value === null) {
|
|
2189
2243
|
return void 0;
|
|
@@ -2194,14 +2248,14 @@ function normaliseConfigValue(value) {
|
|
|
2194
2248
|
function configureGemini(options = {}) {
|
|
2195
2249
|
const nextProjectId = normaliseConfigValue(options.projectId);
|
|
2196
2250
|
const nextLocation = normaliseConfigValue(options.location);
|
|
2197
|
-
geminiConfiguration = {
|
|
2198
|
-
projectId: nextProjectId !== void 0 ? nextProjectId : geminiConfiguration.projectId,
|
|
2199
|
-
location: nextLocation !== void 0 ? nextLocation : geminiConfiguration.location
|
|
2251
|
+
geminiClientState.geminiConfiguration = {
|
|
2252
|
+
projectId: nextProjectId !== void 0 ? nextProjectId : geminiClientState.geminiConfiguration.projectId,
|
|
2253
|
+
location: nextLocation !== void 0 ? nextLocation : geminiClientState.geminiConfiguration.location
|
|
2200
2254
|
};
|
|
2201
|
-
clientPromise = void 0;
|
|
2255
|
+
geminiClientState.clientPromise = void 0;
|
|
2202
2256
|
}
|
|
2203
2257
|
function resolveProjectId() {
|
|
2204
|
-
const override = geminiConfiguration.projectId;
|
|
2258
|
+
const override = geminiClientState.geminiConfiguration.projectId;
|
|
2205
2259
|
if (override) {
|
|
2206
2260
|
return override;
|
|
2207
2261
|
}
|
|
@@ -2209,15 +2263,15 @@ function resolveProjectId() {
|
|
|
2209
2263
|
return serviceAccount.projectId;
|
|
2210
2264
|
}
|
|
2211
2265
|
function resolveLocation() {
|
|
2212
|
-
const override = geminiConfiguration.location;
|
|
2266
|
+
const override = geminiClientState.geminiConfiguration.location;
|
|
2213
2267
|
if (override) {
|
|
2214
2268
|
return override;
|
|
2215
2269
|
}
|
|
2216
2270
|
return DEFAULT_VERTEX_LOCATION;
|
|
2217
2271
|
}
|
|
2218
2272
|
async function getGeminiClient() {
|
|
2219
|
-
if (!clientPromise) {
|
|
2220
|
-
clientPromise = Promise.resolve().then(() => {
|
|
2273
|
+
if (!geminiClientState.clientPromise) {
|
|
2274
|
+
geminiClientState.clientPromise = Promise.resolve().then(() => {
|
|
2221
2275
|
const projectId = resolveProjectId();
|
|
2222
2276
|
const location = resolveLocation();
|
|
2223
2277
|
const googleAuthOptions = getGoogleAuthOptions(CLOUD_PLATFORM_SCOPE);
|
|
@@ -2229,7 +2283,7 @@ async function getGeminiClient() {
|
|
|
2229
2283
|
});
|
|
2230
2284
|
});
|
|
2231
2285
|
}
|
|
2232
|
-
return clientPromise;
|
|
2286
|
+
return geminiClientState.clientPromise;
|
|
2233
2287
|
}
|
|
2234
2288
|
|
|
2235
2289
|
// src/google/calls.ts
|
|
@@ -2425,11 +2479,13 @@ function retryDelayMs(attempt) {
|
|
|
2425
2479
|
return base + jitter;
|
|
2426
2480
|
}
|
|
2427
2481
|
var DEFAULT_SCHEDULER_KEY2 = "__default__";
|
|
2428
|
-
var
|
|
2482
|
+
var googleCallState = getRuntimeSingleton(/* @__PURE__ */ Symbol.for("@ljoukov/llm.googleCallState"), () => ({
|
|
2483
|
+
schedulerByModel: /* @__PURE__ */ new Map()
|
|
2484
|
+
}));
|
|
2429
2485
|
function getSchedulerForModel2(modelId) {
|
|
2430
2486
|
const normalizedModelId = modelId?.trim();
|
|
2431
2487
|
const schedulerKey = normalizedModelId && normalizedModelId.length > 0 ? normalizedModelId : DEFAULT_SCHEDULER_KEY2;
|
|
2432
|
-
const existing =
|
|
2488
|
+
const existing = googleCallState.schedulerByModel.get(schedulerKey);
|
|
2433
2489
|
if (existing) {
|
|
2434
2490
|
return existing;
|
|
2435
2491
|
}
|
|
@@ -2452,7 +2508,7 @@ function getSchedulerForModel2(modelId) {
|
|
|
2452
2508
|
}
|
|
2453
2509
|
}
|
|
2454
2510
|
});
|
|
2455
|
-
|
|
2511
|
+
googleCallState.schedulerByModel.set(schedulerKey, created);
|
|
2456
2512
|
return created;
|
|
2457
2513
|
}
|
|
2458
2514
|
async function runGeminiCall(fn, modelId, runOptions) {
|
|
@@ -2462,53 +2518,55 @@ async function runGeminiCall(fn, modelId, runOptions) {
|
|
|
2462
2518
|
// src/openai/client.ts
|
|
2463
2519
|
import OpenAI2 from "openai";
|
|
2464
2520
|
import { Agent as Agent2, fetch as undiciFetch2 } from "undici";
|
|
2465
|
-
var
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2521
|
+
var openAiClientState = getRuntimeSingleton(/* @__PURE__ */ Symbol.for("@ljoukov/llm.openAiClientState"), () => ({
|
|
2522
|
+
cachedApiKey: null,
|
|
2523
|
+
cachedClient: null,
|
|
2524
|
+
cachedFetch: null,
|
|
2525
|
+
cachedTimeoutMs: null,
|
|
2526
|
+
openAiResponsesWebSocketMode: null,
|
|
2527
|
+
openAiResponsesWebSocketDisabled: false
|
|
2528
|
+
}));
|
|
2471
2529
|
var DEFAULT_OPENAI_TIMEOUT_MS = 15 * 6e4;
|
|
2472
2530
|
function resolveOpenAiTimeoutMs() {
|
|
2473
|
-
if (
|
|
2474
|
-
return
|
|
2531
|
+
if (openAiClientState.cachedTimeoutMs !== null) {
|
|
2532
|
+
return openAiClientState.cachedTimeoutMs;
|
|
2475
2533
|
}
|
|
2476
2534
|
const raw = process.env.OPENAI_STREAM_TIMEOUT_MS ?? process.env.OPENAI_TIMEOUT_MS;
|
|
2477
2535
|
const parsed = raw ? Number(raw) : Number.NaN;
|
|
2478
|
-
|
|
2479
|
-
return
|
|
2536
|
+
openAiClientState.cachedTimeoutMs = Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_OPENAI_TIMEOUT_MS;
|
|
2537
|
+
return openAiClientState.cachedTimeoutMs;
|
|
2480
2538
|
}
|
|
2481
2539
|
function getOpenAiFetch() {
|
|
2482
|
-
if (
|
|
2483
|
-
return
|
|
2540
|
+
if (openAiClientState.cachedFetch) {
|
|
2541
|
+
return openAiClientState.cachedFetch;
|
|
2484
2542
|
}
|
|
2485
2543
|
const timeoutMs = resolveOpenAiTimeoutMs();
|
|
2486
2544
|
const dispatcher = new Agent2({
|
|
2487
2545
|
bodyTimeout: timeoutMs,
|
|
2488
2546
|
headersTimeout: timeoutMs
|
|
2489
2547
|
});
|
|
2490
|
-
|
|
2548
|
+
openAiClientState.cachedFetch = ((input, init) => {
|
|
2491
2549
|
return undiciFetch2(input, {
|
|
2492
2550
|
...init ?? {},
|
|
2493
2551
|
dispatcher
|
|
2494
2552
|
});
|
|
2495
2553
|
});
|
|
2496
|
-
return
|
|
2554
|
+
return openAiClientState.cachedFetch;
|
|
2497
2555
|
}
|
|
2498
2556
|
function resolveOpenAiBaseUrl() {
|
|
2499
2557
|
loadLocalEnv();
|
|
2500
2558
|
return process.env.OPENAI_BASE_URL?.trim() || "https://api.openai.com/v1";
|
|
2501
2559
|
}
|
|
2502
2560
|
function resolveOpenAiResponsesWebSocketMode() {
|
|
2503
|
-
if (openAiResponsesWebSocketMode) {
|
|
2504
|
-
return openAiResponsesWebSocketMode;
|
|
2561
|
+
if (openAiClientState.openAiResponsesWebSocketMode) {
|
|
2562
|
+
return openAiClientState.openAiResponsesWebSocketMode;
|
|
2505
2563
|
}
|
|
2506
2564
|
loadLocalEnv();
|
|
2507
|
-
openAiResponsesWebSocketMode = resolveResponsesWebSocketMode(
|
|
2565
|
+
openAiClientState.openAiResponsesWebSocketMode = resolveResponsesWebSocketMode(
|
|
2508
2566
|
process.env.OPENAI_RESPONSES_WEBSOCKET_MODE,
|
|
2509
2567
|
"auto"
|
|
2510
2568
|
);
|
|
2511
|
-
return openAiResponsesWebSocketMode;
|
|
2569
|
+
return openAiClientState.openAiResponsesWebSocketMode;
|
|
2512
2570
|
}
|
|
2513
2571
|
function wrapFallbackStream(stream) {
|
|
2514
2572
|
return {
|
|
@@ -2561,7 +2619,7 @@ function installResponsesWebSocketTransport(client, apiKey) {
|
|
|
2561
2619
|
responsesApi.stream = (request, options) => {
|
|
2562
2620
|
const mode = resolveOpenAiResponsesWebSocketMode();
|
|
2563
2621
|
const fallbackStreamFactory = () => wrapFallbackStream(originalStream(request, options));
|
|
2564
|
-
if (mode === "off" || openAiResponsesWebSocketDisabled) {
|
|
2622
|
+
if (mode === "off" || openAiClientState.openAiResponsesWebSocketDisabled) {
|
|
2565
2623
|
return fallbackStreamFactory();
|
|
2566
2624
|
}
|
|
2567
2625
|
const signal = options && typeof options === "object" ? options.signal ?? void 0 : void 0;
|
|
@@ -2580,15 +2638,15 @@ function installResponsesWebSocketTransport(client, apiKey) {
|
|
|
2580
2638
|
createFallbackStream: fallbackStreamFactory,
|
|
2581
2639
|
onWebSocketFallback: (error) => {
|
|
2582
2640
|
if (isResponsesWebSocketUnsupportedError(error)) {
|
|
2583
|
-
openAiResponsesWebSocketDisabled = true;
|
|
2641
|
+
openAiClientState.openAiResponsesWebSocketDisabled = true;
|
|
2584
2642
|
}
|
|
2585
2643
|
}
|
|
2586
2644
|
});
|
|
2587
2645
|
};
|
|
2588
2646
|
}
|
|
2589
2647
|
function getOpenAiApiKey() {
|
|
2590
|
-
if (
|
|
2591
|
-
return
|
|
2648
|
+
if (openAiClientState.cachedApiKey !== null) {
|
|
2649
|
+
return openAiClientState.cachedApiKey;
|
|
2592
2650
|
}
|
|
2593
2651
|
loadLocalEnv();
|
|
2594
2652
|
const raw = process.env.OPENAI_API_KEY;
|
|
@@ -2596,33 +2654,35 @@ function getOpenAiApiKey() {
|
|
|
2596
2654
|
if (!value) {
|
|
2597
2655
|
throw new Error("OPENAI_API_KEY must be provided to access OpenAI APIs.");
|
|
2598
2656
|
}
|
|
2599
|
-
|
|
2600
|
-
return
|
|
2657
|
+
openAiClientState.cachedApiKey = value;
|
|
2658
|
+
return openAiClientState.cachedApiKey;
|
|
2601
2659
|
}
|
|
2602
2660
|
function getOpenAiClient() {
|
|
2603
|
-
if (
|
|
2604
|
-
return
|
|
2661
|
+
if (openAiClientState.cachedClient) {
|
|
2662
|
+
return openAiClientState.cachedClient;
|
|
2605
2663
|
}
|
|
2606
2664
|
loadLocalEnv();
|
|
2607
2665
|
const apiKey = getOpenAiApiKey();
|
|
2608
2666
|
const timeoutMs = resolveOpenAiTimeoutMs();
|
|
2609
|
-
|
|
2667
|
+
openAiClientState.cachedClient = new OpenAI2({
|
|
2610
2668
|
apiKey,
|
|
2611
2669
|
fetch: getOpenAiFetch(),
|
|
2612
2670
|
timeout: timeoutMs
|
|
2613
2671
|
});
|
|
2614
|
-
installResponsesWebSocketTransport(
|
|
2615
|
-
return
|
|
2672
|
+
installResponsesWebSocketTransport(openAiClientState.cachedClient, apiKey);
|
|
2673
|
+
return openAiClientState.cachedClient;
|
|
2616
2674
|
}
|
|
2617
2675
|
|
|
2618
2676
|
// src/openai/calls.ts
|
|
2619
2677
|
var DEFAULT_OPENAI_REASONING_EFFORT = "medium";
|
|
2620
2678
|
var DEFAULT_SCHEDULER_KEY3 = "__default__";
|
|
2621
|
-
var
|
|
2679
|
+
var openAiCallState = getRuntimeSingleton(/* @__PURE__ */ Symbol.for("@ljoukov/llm.openAiCallState"), () => ({
|
|
2680
|
+
schedulerByModel: /* @__PURE__ */ new Map()
|
|
2681
|
+
}));
|
|
2622
2682
|
function getSchedulerForModel3(modelId) {
|
|
2623
2683
|
const normalizedModelId = modelId?.trim();
|
|
2624
2684
|
const schedulerKey = normalizedModelId && normalizedModelId.length > 0 ? normalizedModelId : DEFAULT_SCHEDULER_KEY3;
|
|
2625
|
-
const existing =
|
|
2685
|
+
const existing = openAiCallState.schedulerByModel.get(schedulerKey);
|
|
2626
2686
|
if (existing) {
|
|
2627
2687
|
return existing;
|
|
2628
2688
|
}
|
|
@@ -2634,7 +2694,7 @@ function getSchedulerForModel3(modelId) {
|
|
|
2634
2694
|
minIntervalBetweenStartMs: 200,
|
|
2635
2695
|
startJitterMs: 200
|
|
2636
2696
|
});
|
|
2637
|
-
|
|
2697
|
+
openAiCallState.schedulerByModel.set(schedulerKey, created);
|
|
2638
2698
|
return created;
|
|
2639
2699
|
}
|
|
2640
2700
|
async function runOpenAiCall(fn, modelId, runOptions) {
|
|
@@ -2706,6 +2766,9 @@ function ensureTrailingNewline(value) {
|
|
|
2706
2766
|
return value.endsWith("\n") ? value : `${value}
|
|
2707
2767
|
`;
|
|
2708
2768
|
}
|
|
2769
|
+
function hasNonEmptyText(value) {
|
|
2770
|
+
return typeof value === "string" && value.length > 0;
|
|
2771
|
+
}
|
|
2709
2772
|
function redactDataUrlPayload(value) {
|
|
2710
2773
|
if (!value.toLowerCase().startsWith("data:")) {
|
|
2711
2774
|
return value;
|
|
@@ -2954,6 +3017,25 @@ var AgentLoggingSessionImpl = class {
|
|
|
2954
3017
|
}
|
|
2955
3018
|
this.enqueueLineWrite(timestamped);
|
|
2956
3019
|
}
|
|
3020
|
+
async writeAttachments(baseDir, attachments) {
|
|
3021
|
+
const usedNames = /* @__PURE__ */ new Set();
|
|
3022
|
+
for (const attachment of attachments ?? []) {
|
|
3023
|
+
let filename = normalisePathSegment(attachment.filename);
|
|
3024
|
+
if (!filename.includes(".")) {
|
|
3025
|
+
filename = `${filename}.bin`;
|
|
3026
|
+
}
|
|
3027
|
+
const ext = path3.extname(filename);
|
|
3028
|
+
const base = ext.length > 0 ? filename.slice(0, -ext.length) : filename;
|
|
3029
|
+
let candidate = filename;
|
|
3030
|
+
let duplicateIndex = 2;
|
|
3031
|
+
while (usedNames.has(candidate)) {
|
|
3032
|
+
candidate = `${base}-${duplicateIndex.toString()}${ext}`;
|
|
3033
|
+
duplicateIndex += 1;
|
|
3034
|
+
}
|
|
3035
|
+
usedNames.add(candidate);
|
|
3036
|
+
await writeFile(path3.join(baseDir, candidate), attachment.bytes);
|
|
3037
|
+
}
|
|
3038
|
+
}
|
|
2957
3039
|
startLlmCall(input) {
|
|
2958
3040
|
const callNumber = this.callCounter + 1;
|
|
2959
3041
|
this.callCounter = callNumber;
|
|
@@ -2966,6 +3048,9 @@ var AgentLoggingSessionImpl = class {
|
|
|
2966
3048
|
);
|
|
2967
3049
|
const responsePath = path3.join(baseDir, "response.txt");
|
|
2968
3050
|
const thoughtsPath = path3.join(baseDir, "thoughts.txt");
|
|
3051
|
+
const toolCallPath = path3.join(baseDir, "tool_call.txt");
|
|
3052
|
+
const toolCallResponsePath = path3.join(baseDir, "tool_call_response.txt");
|
|
3053
|
+
const errorPath = path3.join(baseDir, "error.txt");
|
|
2969
3054
|
const responseMetadataPath = path3.join(baseDir, "response.metadata.json");
|
|
2970
3055
|
let chain = this.ensureReady.then(async () => {
|
|
2971
3056
|
await mkdir(baseDir, { recursive: true });
|
|
@@ -2987,22 +3072,13 @@ var AgentLoggingSessionImpl = class {
|
|
|
2987
3072
|
`,
|
|
2988
3073
|
"utf8"
|
|
2989
3074
|
);
|
|
2990
|
-
|
|
2991
|
-
|
|
2992
|
-
|
|
2993
|
-
|
|
2994
|
-
|
|
2995
|
-
|
|
2996
|
-
|
|
2997
|
-
const base = ext.length > 0 ? filename.slice(0, -ext.length) : filename;
|
|
2998
|
-
let candidate = filename;
|
|
2999
|
-
let duplicateIndex = 2;
|
|
3000
|
-
while (usedNames.has(candidate)) {
|
|
3001
|
-
candidate = `${base}-${duplicateIndex.toString()}${ext}`;
|
|
3002
|
-
duplicateIndex += 1;
|
|
3003
|
-
}
|
|
3004
|
-
usedNames.add(candidate);
|
|
3005
|
-
await writeFile(path3.join(baseDir, candidate), attachment.bytes);
|
|
3075
|
+
await this.writeAttachments(baseDir, input.attachments);
|
|
3076
|
+
if (hasNonEmptyText(input.toolCallResponseText)) {
|
|
3077
|
+
await writeFile(
|
|
3078
|
+
toolCallResponsePath,
|
|
3079
|
+
ensureTrailingNewline(input.toolCallResponseText),
|
|
3080
|
+
"utf8"
|
|
3081
|
+
);
|
|
3006
3082
|
}
|
|
3007
3083
|
}).catch(() => void 0);
|
|
3008
3084
|
this.track(chain);
|
|
@@ -3030,18 +3106,25 @@ var AgentLoggingSessionImpl = class {
|
|
|
3030
3106
|
await appendFile(responsePath, text, "utf8");
|
|
3031
3107
|
});
|
|
3032
3108
|
},
|
|
3033
|
-
complete: (
|
|
3109
|
+
complete: (options) => {
|
|
3034
3110
|
if (closed) {
|
|
3035
3111
|
return;
|
|
3036
3112
|
}
|
|
3037
3113
|
closed = true;
|
|
3038
3114
|
enqueue(async () => {
|
|
3115
|
+
if (hasNonEmptyText(options?.responseText)) {
|
|
3116
|
+
await writeFile(responsePath, options.responseText, "utf8");
|
|
3117
|
+
}
|
|
3118
|
+
if (hasNonEmptyText(options?.toolCallText)) {
|
|
3119
|
+
await writeFile(toolCallPath, ensureTrailingNewline(options.toolCallText), "utf8");
|
|
3120
|
+
}
|
|
3121
|
+
await this.writeAttachments(baseDir, options?.attachments);
|
|
3039
3122
|
const payload = {
|
|
3040
3123
|
capturedAt: toIsoNow(),
|
|
3041
3124
|
status: "completed"
|
|
3042
3125
|
};
|
|
3043
|
-
if (metadata) {
|
|
3044
|
-
const sanitised = sanitiseLogValue(metadata);
|
|
3126
|
+
if (options?.metadata) {
|
|
3127
|
+
const sanitised = sanitiseLogValue(options.metadata);
|
|
3045
3128
|
if (sanitised && typeof sanitised === "object" && !Array.isArray(sanitised)) {
|
|
3046
3129
|
Object.assign(payload, sanitised);
|
|
3047
3130
|
} else if (sanitised !== void 0) {
|
|
@@ -3052,19 +3135,27 @@ var AgentLoggingSessionImpl = class {
|
|
|
3052
3135
|
`, "utf8");
|
|
3053
3136
|
});
|
|
3054
3137
|
},
|
|
3055
|
-
fail: (error,
|
|
3138
|
+
fail: (error, options) => {
|
|
3056
3139
|
if (closed) {
|
|
3057
3140
|
return;
|
|
3058
3141
|
}
|
|
3059
3142
|
closed = true;
|
|
3060
3143
|
enqueue(async () => {
|
|
3144
|
+
if (hasNonEmptyText(options?.responseText)) {
|
|
3145
|
+
await writeFile(responsePath, options.responseText, "utf8");
|
|
3146
|
+
}
|
|
3147
|
+
if (hasNonEmptyText(options?.toolCallText)) {
|
|
3148
|
+
await writeFile(toolCallPath, ensureTrailingNewline(options.toolCallText), "utf8");
|
|
3149
|
+
}
|
|
3150
|
+
await this.writeAttachments(baseDir, options?.attachments);
|
|
3151
|
+
await writeFile(errorPath, ensureTrailingNewline(toErrorMessage(error)), "utf8");
|
|
3061
3152
|
const payload = {
|
|
3062
3153
|
capturedAt: toIsoNow(),
|
|
3063
3154
|
status: "failed",
|
|
3064
3155
|
error: toErrorMessage(error)
|
|
3065
3156
|
};
|
|
3066
|
-
if (metadata) {
|
|
3067
|
-
const sanitised = sanitiseLogValue(metadata);
|
|
3157
|
+
if (options?.metadata) {
|
|
3158
|
+
const sanitised = sanitiseLogValue(options.metadata);
|
|
3068
3159
|
if (sanitised && typeof sanitised === "object" && !Array.isArray(sanitised)) {
|
|
3069
3160
|
Object.assign(payload, sanitised);
|
|
3070
3161
|
} else if (sanitised !== void 0) {
|
|
@@ -3089,7 +3180,10 @@ var AgentLoggingSessionImpl = class {
|
|
|
3089
3180
|
}
|
|
3090
3181
|
}
|
|
3091
3182
|
};
|
|
3092
|
-
var loggingSessionStorage =
|
|
3183
|
+
var loggingSessionStorage = getRuntimeSingleton(
|
|
3184
|
+
/* @__PURE__ */ Symbol.for("@ljoukov/llm.agentLogging.sessionStorage"),
|
|
3185
|
+
() => new AsyncLocalStorage()
|
|
3186
|
+
);
|
|
3093
3187
|
function createAgentLoggingSession(config) {
|
|
3094
3188
|
return new AgentLoggingSessionImpl(config);
|
|
3095
3189
|
}
|
|
@@ -3104,7 +3198,10 @@ function getCurrentAgentLoggingSession() {
|
|
|
3104
3198
|
}
|
|
3105
3199
|
|
|
3106
3200
|
// src/llm.ts
|
|
3107
|
-
var toolCallContextStorage =
|
|
3201
|
+
var toolCallContextStorage = getRuntimeSingleton(
|
|
3202
|
+
/* @__PURE__ */ Symbol.for("@ljoukov/llm.toolCallContextStorage"),
|
|
3203
|
+
() => new AsyncLocalStorage2()
|
|
3204
|
+
);
|
|
3108
3205
|
function getCurrentToolCallContext() {
|
|
3109
3206
|
return toolCallContextStorage.getStore() ?? null;
|
|
3110
3207
|
}
|
|
@@ -4907,7 +5004,10 @@ function resolveAttachmentExtension(mimeType) {
|
|
|
4907
5004
|
}
|
|
4908
5005
|
}
|
|
4909
5006
|
}
|
|
4910
|
-
function
|
|
5007
|
+
function buildLoggedAttachmentFilename(prefix, index, mimeType) {
|
|
5008
|
+
return `${prefix}-${index.toString()}.${resolveAttachmentExtension(mimeType)}`;
|
|
5009
|
+
}
|
|
5010
|
+
function decodeDataUrlAttachment(value, options) {
|
|
4911
5011
|
const trimmed = value.trim();
|
|
4912
5012
|
if (!trimmed.toLowerCase().startsWith("data:")) {
|
|
4913
5013
|
return null;
|
|
@@ -4923,7 +5023,7 @@ function decodeDataUrlAttachment(value, basename) {
|
|
|
4923
5023
|
try {
|
|
4924
5024
|
const bytes = isBase64 ? Buffer4.from(payload, "base64") : Buffer4.from(decodeURIComponent(payload), "utf8");
|
|
4925
5025
|
return {
|
|
4926
|
-
filename:
|
|
5026
|
+
filename: buildLoggedAttachmentFilename(options.prefix, options.index, mimeType),
|
|
4927
5027
|
bytes
|
|
4928
5028
|
};
|
|
4929
5029
|
} catch {
|
|
@@ -4932,10 +5032,10 @@ function decodeDataUrlAttachment(value, basename) {
|
|
|
4932
5032
|
}
|
|
4933
5033
|
function collectPayloadAttachments(value, options) {
|
|
4934
5034
|
if (typeof value === "string") {
|
|
4935
|
-
const attachment = decodeDataUrlAttachment(
|
|
4936
|
-
|
|
4937
|
-
|
|
4938
|
-
);
|
|
5035
|
+
const attachment = decodeDataUrlAttachment(value, {
|
|
5036
|
+
prefix: options.prefix,
|
|
5037
|
+
index: options.counter
|
|
5038
|
+
});
|
|
4939
5039
|
if (attachment) {
|
|
4940
5040
|
options.attachments.push(attachment);
|
|
4941
5041
|
options.counter += 1;
|
|
@@ -4960,7 +5060,7 @@ function collectPayloadAttachments(value, options) {
|
|
|
4960
5060
|
if (typeof record.data === "string" && mimeType) {
|
|
4961
5061
|
try {
|
|
4962
5062
|
options.attachments.push({
|
|
4963
|
-
filename:
|
|
5063
|
+
filename: buildLoggedAttachmentFilename(options.prefix, options.counter, mimeType),
|
|
4964
5064
|
bytes: decodeInlineDataBuffer(record.data)
|
|
4965
5065
|
});
|
|
4966
5066
|
options.counter += 1;
|
|
@@ -4980,27 +5080,166 @@ function serialiseRequestPayloadForLogging(value) {
|
|
|
4980
5080
|
`;
|
|
4981
5081
|
}
|
|
4982
5082
|
}
|
|
5083
|
+
function serialiseLogArtifactText(value) {
|
|
5084
|
+
if (value === null || value === void 0) {
|
|
5085
|
+
return void 0;
|
|
5086
|
+
}
|
|
5087
|
+
if (typeof value === "string") {
|
|
5088
|
+
if (value.length === 0) {
|
|
5089
|
+
return void 0;
|
|
5090
|
+
}
|
|
5091
|
+
return value.endsWith("\n") ? value : `${value}
|
|
5092
|
+
`;
|
|
5093
|
+
}
|
|
5094
|
+
if (Array.isArray(value) && value.length === 0) {
|
|
5095
|
+
return void 0;
|
|
5096
|
+
}
|
|
5097
|
+
if (isPlainRecord(value) && Object.keys(value).length === 0) {
|
|
5098
|
+
return void 0;
|
|
5099
|
+
}
|
|
5100
|
+
try {
|
|
5101
|
+
return `${JSON.stringify(sanitiseLogValue(value), null, 2)}
|
|
5102
|
+
`;
|
|
5103
|
+
} catch {
|
|
5104
|
+
return `${String(value)}
|
|
5105
|
+
`;
|
|
5106
|
+
}
|
|
5107
|
+
}
|
|
5108
|
+
function collectLoggedAttachmentsFromLlmParts(parts, prefix) {
|
|
5109
|
+
const attachments = [];
|
|
5110
|
+
let index = 1;
|
|
5111
|
+
for (const part of parts) {
|
|
5112
|
+
if (part.type !== "inlineData") {
|
|
5113
|
+
continue;
|
|
5114
|
+
}
|
|
5115
|
+
attachments.push({
|
|
5116
|
+
filename: buildLoggedAttachmentFilename(prefix, index, part.mimeType),
|
|
5117
|
+
bytes: decodeInlineDataBuffer(part.data)
|
|
5118
|
+
});
|
|
5119
|
+
index += 1;
|
|
5120
|
+
}
|
|
5121
|
+
return attachments;
|
|
5122
|
+
}
|
|
5123
|
+
function collectLoggedAttachmentsFromGeminiParts(parts, prefix) {
|
|
5124
|
+
return collectLoggedAttachmentsFromLlmParts(convertGooglePartsToLlmParts(parts), prefix);
|
|
5125
|
+
}
|
|
5126
|
+
function extractToolCallResponseTextFromOpenAiInput(input) {
|
|
5127
|
+
if (!Array.isArray(input)) {
|
|
5128
|
+
return void 0;
|
|
5129
|
+
}
|
|
5130
|
+
const responses = input.filter((item) => isPlainRecord(item)).flatMap((item) => {
|
|
5131
|
+
const type = typeof item.type === "string" ? item.type : "";
|
|
5132
|
+
if (type !== "function_call_output" && type !== "custom_tool_call_output") {
|
|
5133
|
+
return [];
|
|
5134
|
+
}
|
|
5135
|
+
return [
|
|
5136
|
+
{
|
|
5137
|
+
type,
|
|
5138
|
+
callId: typeof item.call_id === "string" ? item.call_id : void 0,
|
|
5139
|
+
output: "output" in item ? sanitiseLogValue(item.output) : void 0
|
|
5140
|
+
}
|
|
5141
|
+
];
|
|
5142
|
+
});
|
|
5143
|
+
return serialiseLogArtifactText(responses);
|
|
5144
|
+
}
|
|
5145
|
+
function extractToolCallResponseTextFromFireworksMessages(messages) {
|
|
5146
|
+
if (!Array.isArray(messages)) {
|
|
5147
|
+
return void 0;
|
|
5148
|
+
}
|
|
5149
|
+
const responses = messages.filter((message) => isPlainRecord(message)).flatMap((message) => {
|
|
5150
|
+
if (message.role !== "tool") {
|
|
5151
|
+
return [];
|
|
5152
|
+
}
|
|
5153
|
+
return [
|
|
5154
|
+
{
|
|
5155
|
+
toolCallId: typeof message.tool_call_id === "string" ? message.tool_call_id : void 0,
|
|
5156
|
+
content: sanitiseLogValue(message.content)
|
|
5157
|
+
}
|
|
5158
|
+
];
|
|
5159
|
+
});
|
|
5160
|
+
return serialiseLogArtifactText(responses);
|
|
5161
|
+
}
|
|
5162
|
+
function extractToolCallResponseTextFromGeminiContents(contents) {
|
|
5163
|
+
if (!Array.isArray(contents)) {
|
|
5164
|
+
return void 0;
|
|
5165
|
+
}
|
|
5166
|
+
const responses = [];
|
|
5167
|
+
for (const content of contents) {
|
|
5168
|
+
if (!content || typeof content !== "object") {
|
|
5169
|
+
continue;
|
|
5170
|
+
}
|
|
5171
|
+
const parts = content.parts;
|
|
5172
|
+
if (!Array.isArray(parts)) {
|
|
5173
|
+
continue;
|
|
5174
|
+
}
|
|
5175
|
+
for (const part of parts) {
|
|
5176
|
+
if (!part || typeof part !== "object") {
|
|
5177
|
+
continue;
|
|
5178
|
+
}
|
|
5179
|
+
const functionResponse = part.functionResponse;
|
|
5180
|
+
if (functionResponse) {
|
|
5181
|
+
responses.push(sanitiseLogValue(functionResponse));
|
|
5182
|
+
}
|
|
5183
|
+
}
|
|
5184
|
+
}
|
|
5185
|
+
return serialiseLogArtifactText(responses);
|
|
5186
|
+
}
|
|
5187
|
+
function serialiseOpenAiStyleToolCallsForLogging(calls) {
|
|
5188
|
+
return serialiseLogArtifactText(
|
|
5189
|
+
calls.map((call) => {
|
|
5190
|
+
if (call.kind === "custom") {
|
|
5191
|
+
return {
|
|
5192
|
+
kind: call.kind,
|
|
5193
|
+
name: call.name,
|
|
5194
|
+
callId: call.callId,
|
|
5195
|
+
itemId: call.itemId,
|
|
5196
|
+
input: call.input
|
|
5197
|
+
};
|
|
5198
|
+
}
|
|
5199
|
+
const { value, error } = parseOpenAiToolArguments(call.arguments);
|
|
5200
|
+
return {
|
|
5201
|
+
kind: call.kind,
|
|
5202
|
+
name: call.name,
|
|
5203
|
+
callId: call.callId,
|
|
5204
|
+
itemId: call.itemId,
|
|
5205
|
+
arguments: value,
|
|
5206
|
+
...error ? { parseError: error, rawArguments: call.arguments } : {}
|
|
5207
|
+
};
|
|
5208
|
+
})
|
|
5209
|
+
);
|
|
5210
|
+
}
|
|
5211
|
+
function serialiseGeminiToolCallsForLogging(calls) {
|
|
5212
|
+
return serialiseLogArtifactText(
|
|
5213
|
+
calls.map((call) => ({
|
|
5214
|
+
name: call.name ?? "unknown",
|
|
5215
|
+
callId: typeof call.id === "string" ? call.id : void 0,
|
|
5216
|
+
arguments: sanitiseLogValue(call.args ?? {})
|
|
5217
|
+
}))
|
|
5218
|
+
);
|
|
5219
|
+
}
|
|
4983
5220
|
function startLlmCallLoggerFromContents(options) {
|
|
4984
5221
|
const session = getCurrentAgentLoggingSession();
|
|
4985
5222
|
if (!session) {
|
|
4986
5223
|
return void 0;
|
|
4987
5224
|
}
|
|
4988
5225
|
const attachments = [];
|
|
5226
|
+
let attachmentIndex = 1;
|
|
4989
5227
|
const sections = [];
|
|
4990
5228
|
for (const [messageIndex, message] of options.contents.entries()) {
|
|
4991
5229
|
sections.push(`### message_${(messageIndex + 1).toString()} role=${message.role}`);
|
|
4992
|
-
for (const
|
|
5230
|
+
for (const part of message.parts) {
|
|
4993
5231
|
if (part.type === "text") {
|
|
4994
5232
|
const channel = part.thought === true ? "thought" : "response";
|
|
4995
5233
|
sections.push(`[text:${channel}]`);
|
|
4996
5234
|
sections.push(part.text);
|
|
4997
5235
|
continue;
|
|
4998
5236
|
}
|
|
4999
|
-
const filename =
|
|
5237
|
+
const filename = buildLoggedAttachmentFilename("input", attachmentIndex, part.mimeType);
|
|
5000
5238
|
attachments.push({
|
|
5001
5239
|
filename,
|
|
5002
5240
|
bytes: decodeInlineDataBuffer(part.data)
|
|
5003
5241
|
});
|
|
5242
|
+
attachmentIndex += 1;
|
|
5004
5243
|
sections.push(
|
|
5005
5244
|
`[inlineData] file=${filename} mime=${part.mimeType ?? "application/octet-stream"} bytes=${attachments[attachments.length - 1]?.bytes.byteLength ?? 0}`
|
|
5006
5245
|
);
|
|
@@ -5044,11 +5283,18 @@ function startLlmCallLoggerFromPayload(options) {
|
|
|
5044
5283
|
}
|
|
5045
5284
|
const attachments = [];
|
|
5046
5285
|
collectPayloadAttachments(options.requestPayload, {
|
|
5047
|
-
prefix:
|
|
5286
|
+
prefix: "input",
|
|
5048
5287
|
attachments,
|
|
5049
5288
|
seen: /* @__PURE__ */ new WeakSet(),
|
|
5050
5289
|
counter: 1
|
|
5051
5290
|
});
|
|
5291
|
+
const toolCallResponseText = options.provider === "openai" || options.provider === "chatgpt" ? extractToolCallResponseTextFromOpenAiInput(
|
|
5292
|
+
options.requestPayload.input
|
|
5293
|
+
) : options.provider === "fireworks" ? extractToolCallResponseTextFromFireworksMessages(
|
|
5294
|
+
options.requestPayload.messages
|
|
5295
|
+
) : extractToolCallResponseTextFromGeminiContents(
|
|
5296
|
+
options.requestPayload.contents
|
|
5297
|
+
);
|
|
5052
5298
|
return session.startLlmCall({
|
|
5053
5299
|
provider: options.provider,
|
|
5054
5300
|
modelId: options.modelId,
|
|
@@ -5057,7 +5303,8 @@ function startLlmCallLoggerFromPayload(options) {
|
|
|
5057
5303
|
step: options.step,
|
|
5058
5304
|
...getCurrentToolCallContext() ? { toolContext: getCurrentToolCallContext() } : {}
|
|
5059
5305
|
},
|
|
5060
|
-
attachments
|
|
5306
|
+
attachments,
|
|
5307
|
+
toolCallResponseText
|
|
5061
5308
|
});
|
|
5062
5309
|
}
|
|
5063
5310
|
async function runTextCall(params) {
|
|
@@ -5362,6 +5609,7 @@ async function runTextCall(params) {
|
|
|
5362
5609
|
const mergedParts = mergeConsecutiveTextParts(responseParts);
|
|
5363
5610
|
const content = mergedParts.length > 0 ? { role: responseRole ?? "assistant", parts: mergedParts } : void 0;
|
|
5364
5611
|
const { text, thoughts } = extractTextByChannel(content);
|
|
5612
|
+
const outputAttachments = collectLoggedAttachmentsFromLlmParts(mergedParts, "output");
|
|
5365
5613
|
const costUsd = estimateCallCostUsd({
|
|
5366
5614
|
modelId: modelVersion,
|
|
5367
5615
|
tokens: latestUsage,
|
|
@@ -5372,16 +5620,20 @@ async function runTextCall(params) {
|
|
|
5372
5620
|
queue.push({ type: "usage", usage: latestUsage, costUsd, modelVersion });
|
|
5373
5621
|
}
|
|
5374
5622
|
callLogger?.complete({
|
|
5375
|
-
|
|
5376
|
-
|
|
5377
|
-
|
|
5378
|
-
|
|
5379
|
-
|
|
5380
|
-
|
|
5381
|
-
|
|
5382
|
-
|
|
5383
|
-
|
|
5384
|
-
|
|
5623
|
+
responseText: text,
|
|
5624
|
+
attachments: outputAttachments,
|
|
5625
|
+
metadata: {
|
|
5626
|
+
provider,
|
|
5627
|
+
model: request.model,
|
|
5628
|
+
modelVersion,
|
|
5629
|
+
blocked,
|
|
5630
|
+
costUsd,
|
|
5631
|
+
usage: latestUsage,
|
|
5632
|
+
grounding: grounding ? sanitiseLogValue(grounding) : void 0,
|
|
5633
|
+
responseChars: text.length,
|
|
5634
|
+
thoughtChars: thoughts.length,
|
|
5635
|
+
responseImages
|
|
5636
|
+
}
|
|
5385
5637
|
});
|
|
5386
5638
|
return {
|
|
5387
5639
|
provider,
|
|
@@ -5396,14 +5648,21 @@ async function runTextCall(params) {
|
|
|
5396
5648
|
grounding
|
|
5397
5649
|
};
|
|
5398
5650
|
} catch (error) {
|
|
5651
|
+
const partialParts = mergeConsecutiveTextParts(responseParts);
|
|
5652
|
+
const partialContent = partialParts.length > 0 ? { role: responseRole ?? "assistant", parts: partialParts } : void 0;
|
|
5653
|
+
const { text: partialText } = extractTextByChannel(partialContent);
|
|
5399
5654
|
callLogger?.fail(error, {
|
|
5400
|
-
|
|
5401
|
-
|
|
5402
|
-
|
|
5403
|
-
|
|
5404
|
-
|
|
5405
|
-
|
|
5406
|
-
|
|
5655
|
+
responseText: partialText,
|
|
5656
|
+
attachments: collectLoggedAttachmentsFromLlmParts(partialParts, "output"),
|
|
5657
|
+
metadata: {
|
|
5658
|
+
provider,
|
|
5659
|
+
model: request.model,
|
|
5660
|
+
modelVersion,
|
|
5661
|
+
blocked,
|
|
5662
|
+
usage: latestUsage,
|
|
5663
|
+
partialResponseParts: responseParts.length,
|
|
5664
|
+
responseImages
|
|
5665
|
+
}
|
|
5407
5666
|
});
|
|
5408
5667
|
throw error;
|
|
5409
5668
|
}
|
|
@@ -5610,7 +5869,10 @@ var DEFAULT_TOOL_LOOP_MAX_STEPS = 8;
|
|
|
5610
5869
|
function resolveToolLoopContents(input) {
|
|
5611
5870
|
return resolveTextContents(input);
|
|
5612
5871
|
}
|
|
5613
|
-
var toolLoopSteeringInternals =
|
|
5872
|
+
var toolLoopSteeringInternals = getRuntimeSingleton(
|
|
5873
|
+
/* @__PURE__ */ Symbol.for("@ljoukov/llm.toolLoopSteeringInternals"),
|
|
5874
|
+
() => /* @__PURE__ */ new WeakMap()
|
|
5875
|
+
);
|
|
5614
5876
|
function createToolLoopSteeringChannel() {
|
|
5615
5877
|
const pending = [];
|
|
5616
5878
|
let closed = false;
|
|
@@ -5870,6 +6132,9 @@ async function runToolLoop(request) {
|
|
|
5870
6132
|
let usageTokens;
|
|
5871
6133
|
let thoughtDeltaEmitted = false;
|
|
5872
6134
|
let blocked = false;
|
|
6135
|
+
let responseText = "";
|
|
6136
|
+
let reasoningSummary = "";
|
|
6137
|
+
let stepToolCallText;
|
|
5873
6138
|
const stepRequestPayload = {
|
|
5874
6139
|
model: providerInfo.model,
|
|
5875
6140
|
input,
|
|
@@ -5962,8 +6227,8 @@ async function runToolLoop(request) {
|
|
|
5962
6227
|
throw new Error(message);
|
|
5963
6228
|
}
|
|
5964
6229
|
usageTokens = extractOpenAiUsageTokens(finalResponse.usage);
|
|
5965
|
-
|
|
5966
|
-
|
|
6230
|
+
responseText = extractOpenAiResponseParts(finalResponse).parts.filter((p) => p.type === "text" && p.thought !== true).map((p) => p.text).join("").trim();
|
|
6231
|
+
reasoningSummary = extractOpenAiReasoningSummary(finalResponse).trim();
|
|
5967
6232
|
if (!thoughtDeltaEmitted && reasoningSummary.length > 0) {
|
|
5968
6233
|
stepCallLogger?.appendThoughtDelta(reasoningSummary);
|
|
5969
6234
|
emitEvent({ type: "delta", channel: "thought", text: reasoningSummary });
|
|
@@ -5979,6 +6244,23 @@ async function runToolLoop(request) {
|
|
|
5979
6244
|
emitEvent({ type: "usage", usage: usageTokens, costUsd: stepCostUsd, modelVersion });
|
|
5980
6245
|
}
|
|
5981
6246
|
const responseToolCalls = extractOpenAiToolCalls(finalResponse.output);
|
|
6247
|
+
stepToolCallText = serialiseOpenAiStyleToolCallsForLogging(
|
|
6248
|
+
responseToolCalls.map(
|
|
6249
|
+
(call) => call.kind === "custom" ? {
|
|
6250
|
+
kind: call.kind,
|
|
6251
|
+
name: call.name,
|
|
6252
|
+
input: call.input,
|
|
6253
|
+
callId: call.call_id,
|
|
6254
|
+
itemId: call.id
|
|
6255
|
+
} : {
|
|
6256
|
+
kind: call.kind,
|
|
6257
|
+
name: call.name,
|
|
6258
|
+
arguments: call.arguments,
|
|
6259
|
+
callId: call.call_id,
|
|
6260
|
+
itemId: call.id
|
|
6261
|
+
}
|
|
6262
|
+
)
|
|
6263
|
+
);
|
|
5982
6264
|
const stepToolCalls = [];
|
|
5983
6265
|
if (responseToolCalls.length === 0) {
|
|
5984
6266
|
const steeringInput2 = steeringInternal?.drainPendingContents() ?? [];
|
|
@@ -6006,17 +6288,20 @@ async function runToolLoop(request) {
|
|
|
6006
6288
|
timing: timing2
|
|
6007
6289
|
});
|
|
6008
6290
|
stepCallLogger?.complete({
|
|
6009
|
-
|
|
6010
|
-
|
|
6011
|
-
|
|
6012
|
-
|
|
6013
|
-
|
|
6014
|
-
|
|
6015
|
-
|
|
6016
|
-
|
|
6017
|
-
|
|
6018
|
-
|
|
6019
|
-
|
|
6291
|
+
responseText,
|
|
6292
|
+
metadata: {
|
|
6293
|
+
provider: "openai",
|
|
6294
|
+
model: request.model,
|
|
6295
|
+
modelVersion,
|
|
6296
|
+
step: turn,
|
|
6297
|
+
usage: usageTokens,
|
|
6298
|
+
costUsd: stepCostUsd,
|
|
6299
|
+
blocked,
|
|
6300
|
+
responseChars: responseText.length,
|
|
6301
|
+
thoughtChars: reasoningSummary.length,
|
|
6302
|
+
toolCalls: 0,
|
|
6303
|
+
finalStep: steeringItems2.length === 0
|
|
6304
|
+
}
|
|
6020
6305
|
});
|
|
6021
6306
|
if (steeringItems2.length === 0) {
|
|
6022
6307
|
return { text: finalText, thoughts: finalThoughts, steps, totalCostUsd };
|
|
@@ -6139,28 +6424,36 @@ async function runToolLoop(request) {
|
|
|
6139
6424
|
const steeringInput = steeringInternal?.drainPendingContents() ?? [];
|
|
6140
6425
|
const steeringItems = steeringInput.length > 0 ? toOpenAiInput(steeringInput) : [];
|
|
6141
6426
|
stepCallLogger?.complete({
|
|
6142
|
-
|
|
6143
|
-
|
|
6144
|
-
|
|
6145
|
-
|
|
6146
|
-
|
|
6147
|
-
|
|
6148
|
-
|
|
6149
|
-
|
|
6150
|
-
|
|
6151
|
-
|
|
6152
|
-
|
|
6427
|
+
responseText,
|
|
6428
|
+
toolCallText: stepToolCallText,
|
|
6429
|
+
metadata: {
|
|
6430
|
+
provider: "openai",
|
|
6431
|
+
model: request.model,
|
|
6432
|
+
modelVersion,
|
|
6433
|
+
step: turn,
|
|
6434
|
+
usage: usageTokens,
|
|
6435
|
+
costUsd: stepCostUsd,
|
|
6436
|
+
blocked,
|
|
6437
|
+
responseChars: responseText.length,
|
|
6438
|
+
thoughtChars: reasoningSummary.length,
|
|
6439
|
+
toolCalls: stepToolCalls.length,
|
|
6440
|
+
finalStep: false
|
|
6441
|
+
}
|
|
6153
6442
|
});
|
|
6154
6443
|
previousResponseId = finalResponse.id;
|
|
6155
6444
|
input = steeringItems.length > 0 ? toolOutputs.concat(steeringItems) : toolOutputs;
|
|
6156
6445
|
} catch (error) {
|
|
6157
6446
|
stepCallLogger?.fail(error, {
|
|
6158
|
-
|
|
6159
|
-
|
|
6160
|
-
|
|
6161
|
-
|
|
6162
|
-
|
|
6163
|
-
|
|
6447
|
+
responseText,
|
|
6448
|
+
toolCallText: stepToolCallText,
|
|
6449
|
+
metadata: {
|
|
6450
|
+
provider: "openai",
|
|
6451
|
+
model: request.model,
|
|
6452
|
+
modelVersion,
|
|
6453
|
+
step: turn,
|
|
6454
|
+
usage: usageTokens,
|
|
6455
|
+
blocked
|
|
6456
|
+
}
|
|
6164
6457
|
});
|
|
6165
6458
|
throw error;
|
|
6166
6459
|
}
|
|
@@ -6186,6 +6479,7 @@ async function runToolLoop(request) {
|
|
|
6186
6479
|
let usageTokens;
|
|
6187
6480
|
let responseText = "";
|
|
6188
6481
|
let reasoningSummaryText = "";
|
|
6482
|
+
let stepToolCallText;
|
|
6189
6483
|
const markFirstModelEvent = () => {
|
|
6190
6484
|
if (firstModelEventAtMs === void 0) {
|
|
6191
6485
|
firstModelEventAtMs = Date.now();
|
|
@@ -6254,6 +6548,23 @@ async function runToolLoop(request) {
|
|
|
6254
6548
|
stepCallLogger?.appendResponseDelta(responseText);
|
|
6255
6549
|
}
|
|
6256
6550
|
const responseToolCalls = response.toolCalls ?? [];
|
|
6551
|
+
stepToolCallText = serialiseOpenAiStyleToolCallsForLogging(
|
|
6552
|
+
responseToolCalls.map(
|
|
6553
|
+
(call) => call.kind === "custom" ? {
|
|
6554
|
+
kind: call.kind,
|
|
6555
|
+
name: call.name,
|
|
6556
|
+
input: call.input,
|
|
6557
|
+
callId: call.callId,
|
|
6558
|
+
itemId: call.id
|
|
6559
|
+
} : {
|
|
6560
|
+
kind: call.kind,
|
|
6561
|
+
name: call.name,
|
|
6562
|
+
arguments: call.arguments,
|
|
6563
|
+
callId: call.callId,
|
|
6564
|
+
itemId: call.id
|
|
6565
|
+
}
|
|
6566
|
+
)
|
|
6567
|
+
);
|
|
6257
6568
|
if (responseToolCalls.length === 0) {
|
|
6258
6569
|
const steeringInput2 = steeringInternal?.drainPendingContents() ?? [];
|
|
6259
6570
|
const steeringItems2 = steeringInput2.length > 0 ? toChatGptInput(steeringInput2).input : [];
|
|
@@ -6279,16 +6590,19 @@ async function runToolLoop(request) {
|
|
|
6279
6590
|
timing: timing2
|
|
6280
6591
|
});
|
|
6281
6592
|
stepCallLogger?.complete({
|
|
6282
|
-
|
|
6283
|
-
|
|
6284
|
-
|
|
6285
|
-
|
|
6286
|
-
|
|
6287
|
-
|
|
6288
|
-
|
|
6289
|
-
|
|
6290
|
-
|
|
6291
|
-
|
|
6593
|
+
responseText,
|
|
6594
|
+
metadata: {
|
|
6595
|
+
provider: "chatgpt",
|
|
6596
|
+
model: request.model,
|
|
6597
|
+
modelVersion,
|
|
6598
|
+
step: turn,
|
|
6599
|
+
usage: usageTokens,
|
|
6600
|
+
costUsd: stepCostUsd,
|
|
6601
|
+
responseChars: responseText.length,
|
|
6602
|
+
thoughtChars: reasoningSummaryText.length,
|
|
6603
|
+
toolCalls: 0,
|
|
6604
|
+
finalStep: steeringItems2.length === 0
|
|
6605
|
+
}
|
|
6292
6606
|
});
|
|
6293
6607
|
if (steeringItems2.length === 0) {
|
|
6294
6608
|
return { text: finalText, thoughts: finalThoughts, steps, totalCostUsd };
|
|
@@ -6421,25 +6735,33 @@ async function runToolLoop(request) {
|
|
|
6421
6735
|
const steeringInput = steeringInternal?.drainPendingContents() ?? [];
|
|
6422
6736
|
const steeringItems = steeringInput.length > 0 ? toChatGptInput(steeringInput).input : [];
|
|
6423
6737
|
stepCallLogger?.complete({
|
|
6424
|
-
|
|
6425
|
-
|
|
6426
|
-
|
|
6427
|
-
|
|
6428
|
-
|
|
6429
|
-
|
|
6430
|
-
|
|
6431
|
-
|
|
6432
|
-
|
|
6433
|
-
|
|
6738
|
+
responseText,
|
|
6739
|
+
toolCallText: stepToolCallText,
|
|
6740
|
+
metadata: {
|
|
6741
|
+
provider: "chatgpt",
|
|
6742
|
+
model: request.model,
|
|
6743
|
+
modelVersion,
|
|
6744
|
+
step: turn,
|
|
6745
|
+
usage: usageTokens,
|
|
6746
|
+
costUsd: stepCostUsd,
|
|
6747
|
+
responseChars: responseText.length,
|
|
6748
|
+
thoughtChars: reasoningSummaryText.length,
|
|
6749
|
+
toolCalls: toolCalls.length,
|
|
6750
|
+
finalStep: false
|
|
6751
|
+
}
|
|
6434
6752
|
});
|
|
6435
6753
|
input = steeringItems.length > 0 ? input.concat(toolOutputs, steeringItems) : input.concat(toolOutputs);
|
|
6436
6754
|
} catch (error) {
|
|
6437
6755
|
stepCallLogger?.fail(error, {
|
|
6438
|
-
|
|
6439
|
-
|
|
6440
|
-
|
|
6441
|
-
|
|
6442
|
-
|
|
6756
|
+
responseText,
|
|
6757
|
+
toolCallText: stepToolCallText,
|
|
6758
|
+
metadata: {
|
|
6759
|
+
provider: "chatgpt",
|
|
6760
|
+
model: request.model,
|
|
6761
|
+
modelVersion,
|
|
6762
|
+
step: turn,
|
|
6763
|
+
usage: usageTokens
|
|
6764
|
+
}
|
|
6443
6765
|
});
|
|
6444
6766
|
throw error;
|
|
6445
6767
|
}
|
|
@@ -6462,6 +6784,7 @@ async function runToolLoop(request) {
|
|
|
6462
6784
|
let usageTokens;
|
|
6463
6785
|
let responseText = "";
|
|
6464
6786
|
let blocked = false;
|
|
6787
|
+
let stepToolCallText;
|
|
6465
6788
|
const stepRequestPayload = {
|
|
6466
6789
|
model: providerInfo.model,
|
|
6467
6790
|
messages,
|
|
@@ -6526,6 +6849,14 @@ async function runToolLoop(request) {
|
|
|
6526
6849
|
});
|
|
6527
6850
|
}
|
|
6528
6851
|
const responseToolCalls = extractFireworksToolCalls(message);
|
|
6852
|
+
stepToolCallText = serialiseOpenAiStyleToolCallsForLogging(
|
|
6853
|
+
responseToolCalls.map((call) => ({
|
|
6854
|
+
kind: "function",
|
|
6855
|
+
name: call.name,
|
|
6856
|
+
arguments: call.arguments,
|
|
6857
|
+
callId: call.id
|
|
6858
|
+
}))
|
|
6859
|
+
);
|
|
6529
6860
|
if (responseToolCalls.length === 0) {
|
|
6530
6861
|
const steeringInput2 = steeringInternal?.drainPendingContents() ?? [];
|
|
6531
6862
|
const steeringMessages = steeringInput2.length > 0 ? toFireworksMessages(steeringInput2) : [];
|
|
@@ -6551,17 +6882,20 @@ async function runToolLoop(request) {
|
|
|
6551
6882
|
timing: timing2
|
|
6552
6883
|
});
|
|
6553
6884
|
stepCallLogger?.complete({
|
|
6554
|
-
|
|
6555
|
-
|
|
6556
|
-
|
|
6557
|
-
|
|
6558
|
-
|
|
6559
|
-
|
|
6560
|
-
|
|
6561
|
-
|
|
6562
|
-
|
|
6563
|
-
|
|
6564
|
-
|
|
6885
|
+
responseText,
|
|
6886
|
+
metadata: {
|
|
6887
|
+
provider: "fireworks",
|
|
6888
|
+
model: request.model,
|
|
6889
|
+
modelVersion,
|
|
6890
|
+
step: turn,
|
|
6891
|
+
usage: usageTokens,
|
|
6892
|
+
costUsd: stepCostUsd,
|
|
6893
|
+
blocked,
|
|
6894
|
+
responseChars: responseText.length,
|
|
6895
|
+
thoughtChars: 0,
|
|
6896
|
+
toolCalls: 0,
|
|
6897
|
+
finalStep: steeringMessages.length === 0
|
|
6898
|
+
}
|
|
6565
6899
|
});
|
|
6566
6900
|
if (steeringMessages.length === 0) {
|
|
6567
6901
|
return { text: finalText, thoughts: finalThoughts, steps, totalCostUsd };
|
|
@@ -6673,17 +7007,21 @@ async function runToolLoop(request) {
|
|
|
6673
7007
|
timing
|
|
6674
7008
|
});
|
|
6675
7009
|
stepCallLogger?.complete({
|
|
6676
|
-
|
|
6677
|
-
|
|
6678
|
-
|
|
6679
|
-
|
|
6680
|
-
|
|
6681
|
-
|
|
6682
|
-
|
|
6683
|
-
|
|
6684
|
-
|
|
6685
|
-
|
|
6686
|
-
|
|
7010
|
+
responseText,
|
|
7011
|
+
toolCallText: stepToolCallText,
|
|
7012
|
+
metadata: {
|
|
7013
|
+
provider: "fireworks",
|
|
7014
|
+
model: request.model,
|
|
7015
|
+
modelVersion,
|
|
7016
|
+
step: turn,
|
|
7017
|
+
usage: usageTokens,
|
|
7018
|
+
costUsd: stepCostUsd,
|
|
7019
|
+
blocked,
|
|
7020
|
+
responseChars: responseText.length,
|
|
7021
|
+
thoughtChars: 0,
|
|
7022
|
+
toolCalls: stepToolCalls.length,
|
|
7023
|
+
finalStep: false
|
|
7024
|
+
}
|
|
6687
7025
|
});
|
|
6688
7026
|
messages.push({
|
|
6689
7027
|
role: "assistant",
|
|
@@ -6697,12 +7035,16 @@ async function runToolLoop(request) {
|
|
|
6697
7035
|
}
|
|
6698
7036
|
} catch (error) {
|
|
6699
7037
|
stepCallLogger?.fail(error, {
|
|
6700
|
-
|
|
6701
|
-
|
|
6702
|
-
|
|
6703
|
-
|
|
6704
|
-
|
|
6705
|
-
|
|
7038
|
+
responseText,
|
|
7039
|
+
toolCallText: stepToolCallText,
|
|
7040
|
+
metadata: {
|
|
7041
|
+
provider: "fireworks",
|
|
7042
|
+
model: request.model,
|
|
7043
|
+
modelVersion,
|
|
7044
|
+
step: turn,
|
|
7045
|
+
usage: usageTokens,
|
|
7046
|
+
blocked
|
|
7047
|
+
}
|
|
6706
7048
|
});
|
|
6707
7049
|
throw error;
|
|
6708
7050
|
}
|
|
@@ -6722,6 +7064,7 @@ async function runToolLoop(request) {
|
|
|
6722
7064
|
let usageTokens;
|
|
6723
7065
|
let responseText = "";
|
|
6724
7066
|
let thoughtsText = "";
|
|
7067
|
+
let stepToolCallText;
|
|
6725
7068
|
const markFirstModelEvent = () => {
|
|
6726
7069
|
if (firstModelEventAtMs === void 0) {
|
|
6727
7070
|
firstModelEventAtMs = Date.now();
|
|
@@ -6842,12 +7185,17 @@ async function runToolLoop(request) {
|
|
|
6842
7185
|
modelVersion = response.modelVersion ?? request.model;
|
|
6843
7186
|
responseText = response.responseText.trim();
|
|
6844
7187
|
thoughtsText = response.thoughtsText.trim();
|
|
7188
|
+
const responseOutputAttachments = collectLoggedAttachmentsFromGeminiParts(
|
|
7189
|
+
response.modelParts,
|
|
7190
|
+
"output"
|
|
7191
|
+
);
|
|
6845
7192
|
const stepCostUsd = estimateCallCostUsd({
|
|
6846
7193
|
modelId: modelVersion,
|
|
6847
7194
|
tokens: usageTokens,
|
|
6848
7195
|
responseImages: 0
|
|
6849
7196
|
});
|
|
6850
7197
|
totalCostUsd += stepCostUsd;
|
|
7198
|
+
stepToolCallText = serialiseGeminiToolCallsForLogging(response.functionCalls);
|
|
6851
7199
|
if (response.functionCalls.length === 0) {
|
|
6852
7200
|
const steeringInput2 = steeringInternal?.drainPendingContents() ?? [];
|
|
6853
7201
|
finalText = responseText;
|
|
@@ -6873,16 +7221,20 @@ async function runToolLoop(request) {
|
|
|
6873
7221
|
timing: timing2
|
|
6874
7222
|
});
|
|
6875
7223
|
stepCallLogger?.complete({
|
|
6876
|
-
|
|
6877
|
-
|
|
6878
|
-
|
|
6879
|
-
|
|
6880
|
-
|
|
6881
|
-
|
|
6882
|
-
|
|
6883
|
-
|
|
6884
|
-
|
|
6885
|
-
|
|
7224
|
+
responseText,
|
|
7225
|
+
attachments: responseOutputAttachments,
|
|
7226
|
+
metadata: {
|
|
7227
|
+
provider: "gemini",
|
|
7228
|
+
model: request.model,
|
|
7229
|
+
modelVersion,
|
|
7230
|
+
step: turn,
|
|
7231
|
+
usage: usageTokens,
|
|
7232
|
+
costUsd: stepCostUsd,
|
|
7233
|
+
responseChars: responseText.length,
|
|
7234
|
+
thoughtChars: thoughtsText.length,
|
|
7235
|
+
toolCalls: 0,
|
|
7236
|
+
finalStep: steeringInput2.length === 0
|
|
7237
|
+
}
|
|
6886
7238
|
});
|
|
6887
7239
|
if (steeringInput2.length === 0) {
|
|
6888
7240
|
return { text: finalText, thoughts: finalThoughts, steps, totalCostUsd };
|
|
@@ -7009,16 +7361,21 @@ async function runToolLoop(request) {
|
|
|
7009
7361
|
timing
|
|
7010
7362
|
});
|
|
7011
7363
|
stepCallLogger?.complete({
|
|
7012
|
-
|
|
7013
|
-
|
|
7014
|
-
|
|
7015
|
-
|
|
7016
|
-
|
|
7017
|
-
|
|
7018
|
-
|
|
7019
|
-
|
|
7020
|
-
|
|
7021
|
-
|
|
7364
|
+
responseText,
|
|
7365
|
+
attachments: responseOutputAttachments,
|
|
7366
|
+
toolCallText: stepToolCallText,
|
|
7367
|
+
metadata: {
|
|
7368
|
+
provider: "gemini",
|
|
7369
|
+
model: request.model,
|
|
7370
|
+
modelVersion,
|
|
7371
|
+
step: turn,
|
|
7372
|
+
usage: usageTokens,
|
|
7373
|
+
costUsd: stepCostUsd,
|
|
7374
|
+
responseChars: responseText.length,
|
|
7375
|
+
thoughtChars: thoughtsText.length,
|
|
7376
|
+
toolCalls: toolCalls.length,
|
|
7377
|
+
finalStep: false
|
|
7378
|
+
}
|
|
7022
7379
|
});
|
|
7023
7380
|
geminiContents.push({ role: "user", parts: responseParts });
|
|
7024
7381
|
const steeringInput = steeringInternal?.drainPendingContents() ?? [];
|
|
@@ -7027,13 +7384,17 @@ async function runToolLoop(request) {
|
|
|
7027
7384
|
}
|
|
7028
7385
|
} catch (error) {
|
|
7029
7386
|
stepCallLogger?.fail(error, {
|
|
7030
|
-
|
|
7031
|
-
|
|
7032
|
-
|
|
7033
|
-
|
|
7034
|
-
|
|
7035
|
-
|
|
7036
|
-
|
|
7387
|
+
responseText,
|
|
7388
|
+
toolCallText: stepToolCallText,
|
|
7389
|
+
metadata: {
|
|
7390
|
+
provider: "gemini",
|
|
7391
|
+
model: request.model,
|
|
7392
|
+
modelVersion,
|
|
7393
|
+
step: turn,
|
|
7394
|
+
usage: usageTokens,
|
|
7395
|
+
responseChars: responseText.length,
|
|
7396
|
+
thoughtChars: thoughtsText.length
|
|
7397
|
+
}
|
|
7037
7398
|
});
|
|
7038
7399
|
throw error;
|
|
7039
7400
|
}
|