@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.cjs
CHANGED
|
@@ -441,6 +441,34 @@ function estimateCallCostUsd({
|
|
|
441
441
|
var import_node_os2 = __toESM(require("os"), 1);
|
|
442
442
|
var import_node_util = require("util");
|
|
443
443
|
|
|
444
|
+
// src/utils/runtimeSingleton.ts
|
|
445
|
+
var runtimeSingletonStoreKey = /* @__PURE__ */ Symbol.for("@ljoukov/llm.runtimeSingletonStore");
|
|
446
|
+
function getRuntimeSingletonStore() {
|
|
447
|
+
const globalObject = globalThis;
|
|
448
|
+
const existingStore = globalObject[runtimeSingletonStoreKey];
|
|
449
|
+
if (existingStore) {
|
|
450
|
+
return existingStore;
|
|
451
|
+
}
|
|
452
|
+
const store = /* @__PURE__ */ new Map();
|
|
453
|
+
Object.defineProperty(globalObject, runtimeSingletonStoreKey, {
|
|
454
|
+
value: store,
|
|
455
|
+
enumerable: false,
|
|
456
|
+
configurable: false,
|
|
457
|
+
writable: false
|
|
458
|
+
});
|
|
459
|
+
return store;
|
|
460
|
+
}
|
|
461
|
+
function getRuntimeSingleton(key, create) {
|
|
462
|
+
const store = getRuntimeSingletonStore();
|
|
463
|
+
const existingValue = store.get(key);
|
|
464
|
+
if (existingValue !== void 0) {
|
|
465
|
+
return existingValue;
|
|
466
|
+
}
|
|
467
|
+
const createdValue = create();
|
|
468
|
+
store.set(key, createdValue);
|
|
469
|
+
return createdValue;
|
|
470
|
+
}
|
|
471
|
+
|
|
444
472
|
// src/openai/chatgpt-auth.ts
|
|
445
473
|
var import_node_buffer = require("buffer");
|
|
446
474
|
var import_node_fs2 = __toESM(require("fs"), 1);
|
|
@@ -451,14 +479,16 @@ var import_zod = require("zod");
|
|
|
451
479
|
// src/utils/env.ts
|
|
452
480
|
var import_node_fs = __toESM(require("fs"), 1);
|
|
453
481
|
var import_node_path = __toESM(require("path"), 1);
|
|
454
|
-
var
|
|
482
|
+
var envState = getRuntimeSingleton(/* @__PURE__ */ Symbol.for("@ljoukov/llm.envState"), () => ({
|
|
483
|
+
envLoaded: false
|
|
484
|
+
}));
|
|
455
485
|
function loadLocalEnv() {
|
|
456
|
-
if (envLoaded) {
|
|
486
|
+
if (envState.envLoaded) {
|
|
457
487
|
return;
|
|
458
488
|
}
|
|
459
489
|
const envPath = import_node_path.default.join(process.cwd(), ".env.local");
|
|
460
490
|
loadEnvFromFile(envPath, { override: false });
|
|
461
|
-
envLoaded = true;
|
|
491
|
+
envState.envLoaded = true;
|
|
462
492
|
}
|
|
463
493
|
function loadEnvFromFile(filePath, { override = false } = {}) {
|
|
464
494
|
let content;
|
|
@@ -544,8 +574,10 @@ var ExchangeResponseSchema = import_zod.z.object({
|
|
|
544
574
|
expires_in: import_zod.z.union([import_zod.z.number(), import_zod.z.string()]),
|
|
545
575
|
id_token: import_zod.z.string().optional()
|
|
546
576
|
});
|
|
547
|
-
var
|
|
548
|
-
|
|
577
|
+
var chatGptAuthState = getRuntimeSingleton(/* @__PURE__ */ Symbol.for("@ljoukov/llm.chatGptAuthState"), () => ({
|
|
578
|
+
cachedProfile: null,
|
|
579
|
+
refreshPromise: null
|
|
580
|
+
}));
|
|
549
581
|
async function fetchChatGptAuthProfileFromTokenProvider(options) {
|
|
550
582
|
const base = options.baseUrl.replace(/\/+$/u, "");
|
|
551
583
|
const store = options.store?.trim() ? options.store.trim() : "kv";
|
|
@@ -646,13 +678,13 @@ async function getChatGptAuthProfile() {
|
|
|
646
678
|
const tokenProviderUrl = process.env[CHATGPT_AUTH_TOKEN_PROVIDER_URL_ENV];
|
|
647
679
|
const tokenProviderKey = process.env[CHATGPT_AUTH_TOKEN_PROVIDER_API_KEY_ENV] ?? process.env[CHATGPT_AUTH_API_KEY_ENV];
|
|
648
680
|
if (tokenProviderUrl && tokenProviderUrl.trim().length > 0 && tokenProviderKey && tokenProviderKey.trim().length > 0) {
|
|
649
|
-
if (cachedProfile && !isExpired(cachedProfile)) {
|
|
650
|
-
return cachedProfile;
|
|
681
|
+
if (chatGptAuthState.cachedProfile && !isExpired(chatGptAuthState.cachedProfile)) {
|
|
682
|
+
return chatGptAuthState.cachedProfile;
|
|
651
683
|
}
|
|
652
|
-
if (refreshPromise) {
|
|
653
|
-
return refreshPromise;
|
|
684
|
+
if (chatGptAuthState.refreshPromise) {
|
|
685
|
+
return chatGptAuthState.refreshPromise;
|
|
654
686
|
}
|
|
655
|
-
refreshPromise = (async () => {
|
|
687
|
+
chatGptAuthState.refreshPromise = (async () => {
|
|
656
688
|
try {
|
|
657
689
|
const store = process.env[CHATGPT_AUTH_TOKEN_PROVIDER_STORE_ENV];
|
|
658
690
|
const profile = await fetchChatGptAuthProfileFromTokenProvider({
|
|
@@ -660,31 +692,31 @@ async function getChatGptAuthProfile() {
|
|
|
660
692
|
apiKey: tokenProviderKey,
|
|
661
693
|
store: store ?? void 0
|
|
662
694
|
});
|
|
663
|
-
cachedProfile = profile;
|
|
695
|
+
chatGptAuthState.cachedProfile = profile;
|
|
664
696
|
return profile;
|
|
665
697
|
} finally {
|
|
666
|
-
refreshPromise = null;
|
|
698
|
+
chatGptAuthState.refreshPromise = null;
|
|
667
699
|
}
|
|
668
700
|
})();
|
|
669
|
-
return refreshPromise;
|
|
701
|
+
return chatGptAuthState.refreshPromise;
|
|
670
702
|
}
|
|
671
|
-
if (cachedProfile && !isExpired(cachedProfile)) {
|
|
672
|
-
return cachedProfile;
|
|
703
|
+
if (chatGptAuthState.cachedProfile && !isExpired(chatGptAuthState.cachedProfile)) {
|
|
704
|
+
return chatGptAuthState.cachedProfile;
|
|
673
705
|
}
|
|
674
|
-
if (refreshPromise) {
|
|
675
|
-
return refreshPromise;
|
|
706
|
+
if (chatGptAuthState.refreshPromise) {
|
|
707
|
+
return chatGptAuthState.refreshPromise;
|
|
676
708
|
}
|
|
677
|
-
refreshPromise = (async () => {
|
|
709
|
+
chatGptAuthState.refreshPromise = (async () => {
|
|
678
710
|
try {
|
|
679
|
-
const baseProfile = cachedProfile ?? loadAuthProfileFromCodexStore();
|
|
711
|
+
const baseProfile = chatGptAuthState.cachedProfile ?? loadAuthProfileFromCodexStore();
|
|
680
712
|
const profile = isExpired(baseProfile) ? await refreshAndPersistCodexProfile(baseProfile) : baseProfile;
|
|
681
|
-
cachedProfile = profile;
|
|
713
|
+
chatGptAuthState.cachedProfile = profile;
|
|
682
714
|
return profile;
|
|
683
715
|
} finally {
|
|
684
|
-
refreshPromise = null;
|
|
716
|
+
chatGptAuthState.refreshPromise = null;
|
|
685
717
|
}
|
|
686
718
|
})();
|
|
687
|
-
return refreshPromise;
|
|
719
|
+
return chatGptAuthState.refreshPromise;
|
|
688
720
|
}
|
|
689
721
|
function resolveCodexHome() {
|
|
690
722
|
const codexHome = process.env.CODEX_HOME;
|
|
@@ -1416,8 +1448,10 @@ function createAbortError(reason) {
|
|
|
1416
1448
|
// src/openai/chatgpt-codex.ts
|
|
1417
1449
|
var CHATGPT_CODEX_ENDPOINT = "https://chatgpt.com/backend-api/codex/responses";
|
|
1418
1450
|
var CHATGPT_RESPONSES_EXPERIMENTAL_HEADER = "responses=experimental";
|
|
1419
|
-
var
|
|
1420
|
-
|
|
1451
|
+
var chatGptCodexState = getRuntimeSingleton(/* @__PURE__ */ Symbol.for("@ljoukov/llm.chatGptCodexState"), () => ({
|
|
1452
|
+
cachedResponsesWebSocketMode: null,
|
|
1453
|
+
chatGptResponsesWebSocketDisabled: false
|
|
1454
|
+
}));
|
|
1421
1455
|
async function streamChatGptCodexResponse(options) {
|
|
1422
1456
|
const { access, accountId } = await getChatGptAuthProfile();
|
|
1423
1457
|
const mode = resolveChatGptResponsesWebSocketMode();
|
|
@@ -1443,7 +1477,7 @@ async function streamChatGptCodexResponse(options) {
|
|
|
1443
1477
|
}
|
|
1444
1478
|
};
|
|
1445
1479
|
};
|
|
1446
|
-
if (mode === "off" || chatGptResponsesWebSocketDisabled) {
|
|
1480
|
+
if (mode === "off" || chatGptCodexState.chatGptResponsesWebSocketDisabled) {
|
|
1447
1481
|
return fallbackStreamFactory();
|
|
1448
1482
|
}
|
|
1449
1483
|
const websocketHeaders = buildChatGptCodexHeaders({
|
|
@@ -1462,7 +1496,7 @@ async function streamChatGptCodexResponse(options) {
|
|
|
1462
1496
|
}),
|
|
1463
1497
|
createFallbackStream: fallbackStreamFactory,
|
|
1464
1498
|
onWebSocketFallback: () => {
|
|
1465
|
-
chatGptResponsesWebSocketDisabled = true;
|
|
1499
|
+
chatGptCodexState.chatGptResponsesWebSocketDisabled = true;
|
|
1466
1500
|
}
|
|
1467
1501
|
});
|
|
1468
1502
|
}
|
|
@@ -1492,14 +1526,14 @@ async function streamChatGptCodexResponseSse(options) {
|
|
|
1492
1526
|
return parseEventStream(body);
|
|
1493
1527
|
}
|
|
1494
1528
|
function resolveChatGptResponsesWebSocketMode() {
|
|
1495
|
-
if (cachedResponsesWebSocketMode) {
|
|
1496
|
-
return cachedResponsesWebSocketMode;
|
|
1529
|
+
if (chatGptCodexState.cachedResponsesWebSocketMode) {
|
|
1530
|
+
return chatGptCodexState.cachedResponsesWebSocketMode;
|
|
1497
1531
|
}
|
|
1498
|
-
cachedResponsesWebSocketMode = resolveResponsesWebSocketMode(
|
|
1532
|
+
chatGptCodexState.cachedResponsesWebSocketMode = resolveResponsesWebSocketMode(
|
|
1499
1533
|
process.env.CHATGPT_RESPONSES_WEBSOCKET_MODE ?? process.env.OPENAI_RESPONSES_WEBSOCKET_MODE,
|
|
1500
1534
|
"auto"
|
|
1501
1535
|
);
|
|
1502
|
-
return cachedResponsesWebSocketMode;
|
|
1536
|
+
return chatGptCodexState.cachedResponsesWebSocketMode;
|
|
1503
1537
|
}
|
|
1504
1538
|
function buildChatGptCodexHeaders(options) {
|
|
1505
1539
|
const openAiBeta = options.useWebSocket ? mergeOpenAiBetaHeader(
|
|
@@ -1537,8 +1571,8 @@ async function collectChatGptCodexResponse(options) {
|
|
|
1537
1571
|
}
|
|
1538
1572
|
});
|
|
1539
1573
|
} catch (error) {
|
|
1540
|
-
if (!sawAnyDelta && !retriedViaSseFallback && shouldRetryViaSseFallback(error) && !chatGptResponsesWebSocketDisabled) {
|
|
1541
|
-
chatGptResponsesWebSocketDisabled = true;
|
|
1574
|
+
if (!sawAnyDelta && !retriedViaSseFallback && shouldRetryViaSseFallback(error) && !chatGptCodexState.chatGptResponsesWebSocketDisabled) {
|
|
1575
|
+
chatGptCodexState.chatGptResponsesWebSocketDisabled = true;
|
|
1542
1576
|
retriedViaSseFallback = true;
|
|
1543
1577
|
continue;
|
|
1544
1578
|
}
|
|
@@ -1774,7 +1808,12 @@ var MODEL_CONCURRENCY_PROVIDERS = [
|
|
|
1774
1808
|
"google",
|
|
1775
1809
|
"fireworks"
|
|
1776
1810
|
];
|
|
1777
|
-
var
|
|
1811
|
+
var modelConcurrencyState = getRuntimeSingleton(
|
|
1812
|
+
/* @__PURE__ */ Symbol.for("@ljoukov/llm.modelConcurrencyState"),
|
|
1813
|
+
() => ({
|
|
1814
|
+
configuredModelConcurrency: normalizeModelConcurrencyConfig({})
|
|
1815
|
+
})
|
|
1816
|
+
);
|
|
1778
1817
|
function clampModelConcurrencyCap(value) {
|
|
1779
1818
|
if (!Number.isFinite(value)) {
|
|
1780
1819
|
return DEFAULT_MODEL_CONCURRENCY_CAP;
|
|
@@ -1848,14 +1887,14 @@ function resolveDefaultProviderCap(provider, modelId) {
|
|
|
1848
1887
|
return DEFAULT_FIREWORKS_MODEL_CONCURRENCY_CAP;
|
|
1849
1888
|
}
|
|
1850
1889
|
function configureModelConcurrency(config = {}) {
|
|
1851
|
-
configuredModelConcurrency = normalizeModelConcurrencyConfig(config);
|
|
1890
|
+
modelConcurrencyState.configuredModelConcurrency = normalizeModelConcurrencyConfig(config);
|
|
1852
1891
|
}
|
|
1853
1892
|
function resetModelConcurrencyConfig() {
|
|
1854
|
-
configuredModelConcurrency = normalizeModelConcurrencyConfig({});
|
|
1893
|
+
modelConcurrencyState.configuredModelConcurrency = normalizeModelConcurrencyConfig({});
|
|
1855
1894
|
}
|
|
1856
1895
|
function resolveModelConcurrencyCap(options) {
|
|
1857
1896
|
const modelId = options.modelId ? normalizeModelIdForConfig(options.modelId) : void 0;
|
|
1858
|
-
const config = options.config ? normalizeModelConcurrencyConfig(options.config) : configuredModelConcurrency;
|
|
1897
|
+
const config = options.config ? normalizeModelConcurrencyConfig(options.config) : modelConcurrencyState.configuredModelConcurrency;
|
|
1859
1898
|
const providerModelCap = modelId ? config.providerModelCaps[options.provider].get(modelId) : void 0;
|
|
1860
1899
|
if (providerModelCap !== void 0) {
|
|
1861
1900
|
return providerModelCap;
|
|
@@ -2089,32 +2128,37 @@ var import_openai = __toESM(require("openai"), 1);
|
|
|
2089
2128
|
var import_undici = require("undici");
|
|
2090
2129
|
var DEFAULT_FIREWORKS_BASE_URL = "https://api.fireworks.ai/inference/v1";
|
|
2091
2130
|
var DEFAULT_FIREWORKS_TIMEOUT_MS = 15 * 6e4;
|
|
2092
|
-
var
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2131
|
+
var fireworksClientState = getRuntimeSingleton(
|
|
2132
|
+
/* @__PURE__ */ Symbol.for("@ljoukov/llm.fireworksClientState"),
|
|
2133
|
+
() => ({
|
|
2134
|
+
cachedClient: null,
|
|
2135
|
+
cachedFetch: null,
|
|
2136
|
+
cachedBaseUrl: null,
|
|
2137
|
+
cachedApiKey: null,
|
|
2138
|
+
cachedTimeoutMs: null
|
|
2139
|
+
})
|
|
2140
|
+
);
|
|
2097
2141
|
function resolveTimeoutMs() {
|
|
2098
|
-
if (cachedTimeoutMs !== null) {
|
|
2099
|
-
return cachedTimeoutMs;
|
|
2142
|
+
if (fireworksClientState.cachedTimeoutMs !== null) {
|
|
2143
|
+
return fireworksClientState.cachedTimeoutMs;
|
|
2100
2144
|
}
|
|
2101
2145
|
const raw = process.env.FIREWORKS_TIMEOUT_MS;
|
|
2102
2146
|
const parsed = raw ? Number(raw) : Number.NaN;
|
|
2103
|
-
cachedTimeoutMs = Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_FIREWORKS_TIMEOUT_MS;
|
|
2104
|
-
return cachedTimeoutMs;
|
|
2147
|
+
fireworksClientState.cachedTimeoutMs = Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_FIREWORKS_TIMEOUT_MS;
|
|
2148
|
+
return fireworksClientState.cachedTimeoutMs;
|
|
2105
2149
|
}
|
|
2106
2150
|
function resolveBaseUrl() {
|
|
2107
|
-
if (cachedBaseUrl !== null) {
|
|
2108
|
-
return cachedBaseUrl;
|
|
2151
|
+
if (fireworksClientState.cachedBaseUrl !== null) {
|
|
2152
|
+
return fireworksClientState.cachedBaseUrl;
|
|
2109
2153
|
}
|
|
2110
2154
|
loadLocalEnv();
|
|
2111
2155
|
const raw = process.env.FIREWORKS_BASE_URL?.trim();
|
|
2112
|
-
cachedBaseUrl = raw && raw.length > 0 ? raw : DEFAULT_FIREWORKS_BASE_URL;
|
|
2113
|
-
return cachedBaseUrl;
|
|
2156
|
+
fireworksClientState.cachedBaseUrl = raw && raw.length > 0 ? raw : DEFAULT_FIREWORKS_BASE_URL;
|
|
2157
|
+
return fireworksClientState.cachedBaseUrl;
|
|
2114
2158
|
}
|
|
2115
2159
|
function resolveApiKey() {
|
|
2116
|
-
if (cachedApiKey !== null) {
|
|
2117
|
-
return cachedApiKey;
|
|
2160
|
+
if (fireworksClientState.cachedApiKey !== null) {
|
|
2161
|
+
return fireworksClientState.cachedApiKey;
|
|
2118
2162
|
}
|
|
2119
2163
|
loadLocalEnv();
|
|
2120
2164
|
const raw = process.env.FIREWORKS_TOKEN ?? process.env.FIREWORKS_API_KEY;
|
|
@@ -2124,46 +2168,51 @@ function resolveApiKey() {
|
|
|
2124
2168
|
"FIREWORKS_TOKEN (or FIREWORKS_API_KEY) must be provided to access Fireworks APIs."
|
|
2125
2169
|
);
|
|
2126
2170
|
}
|
|
2127
|
-
cachedApiKey = token;
|
|
2128
|
-
return cachedApiKey;
|
|
2171
|
+
fireworksClientState.cachedApiKey = token;
|
|
2172
|
+
return fireworksClientState.cachedApiKey;
|
|
2129
2173
|
}
|
|
2130
2174
|
function getFireworksFetch() {
|
|
2131
|
-
if (cachedFetch) {
|
|
2132
|
-
return cachedFetch;
|
|
2175
|
+
if (fireworksClientState.cachedFetch) {
|
|
2176
|
+
return fireworksClientState.cachedFetch;
|
|
2133
2177
|
}
|
|
2134
2178
|
const timeoutMs = resolveTimeoutMs();
|
|
2135
2179
|
const dispatcher = new import_undici.Agent({
|
|
2136
2180
|
bodyTimeout: timeoutMs,
|
|
2137
2181
|
headersTimeout: timeoutMs
|
|
2138
2182
|
});
|
|
2139
|
-
cachedFetch = ((input, init) => {
|
|
2183
|
+
fireworksClientState.cachedFetch = ((input, init) => {
|
|
2140
2184
|
return (0, import_undici.fetch)(input, {
|
|
2141
2185
|
...init ?? {},
|
|
2142
2186
|
dispatcher
|
|
2143
2187
|
});
|
|
2144
2188
|
});
|
|
2145
|
-
return cachedFetch;
|
|
2189
|
+
return fireworksClientState.cachedFetch;
|
|
2146
2190
|
}
|
|
2147
2191
|
function getFireworksClient() {
|
|
2148
|
-
if (cachedClient) {
|
|
2149
|
-
return cachedClient;
|
|
2192
|
+
if (fireworksClientState.cachedClient) {
|
|
2193
|
+
return fireworksClientState.cachedClient;
|
|
2150
2194
|
}
|
|
2151
|
-
cachedClient = new import_openai.default({
|
|
2195
|
+
fireworksClientState.cachedClient = new import_openai.default({
|
|
2152
2196
|
apiKey: resolveApiKey(),
|
|
2153
2197
|
baseURL: resolveBaseUrl(),
|
|
2154
2198
|
timeout: resolveTimeoutMs(),
|
|
2155
2199
|
fetch: getFireworksFetch()
|
|
2156
2200
|
});
|
|
2157
|
-
return cachedClient;
|
|
2201
|
+
return fireworksClientState.cachedClient;
|
|
2158
2202
|
}
|
|
2159
2203
|
|
|
2160
2204
|
// src/fireworks/calls.ts
|
|
2161
2205
|
var DEFAULT_SCHEDULER_KEY = "__default__";
|
|
2162
|
-
var
|
|
2206
|
+
var fireworksCallState = getRuntimeSingleton(
|
|
2207
|
+
/* @__PURE__ */ Symbol.for("@ljoukov/llm.fireworksCallState"),
|
|
2208
|
+
() => ({
|
|
2209
|
+
schedulerByModel: /* @__PURE__ */ new Map()
|
|
2210
|
+
})
|
|
2211
|
+
);
|
|
2163
2212
|
function getSchedulerForModel(modelId) {
|
|
2164
2213
|
const normalizedModelId = modelId?.trim();
|
|
2165
2214
|
const schedulerKey = normalizedModelId && normalizedModelId.length > 0 ? normalizedModelId : DEFAULT_SCHEDULER_KEY;
|
|
2166
|
-
const existing = schedulerByModel.get(schedulerKey);
|
|
2215
|
+
const existing = fireworksCallState.schedulerByModel.get(schedulerKey);
|
|
2167
2216
|
if (existing) {
|
|
2168
2217
|
return existing;
|
|
2169
2218
|
}
|
|
@@ -2175,7 +2224,7 @@ function getSchedulerForModel(modelId) {
|
|
|
2175
2224
|
minIntervalBetweenStartMs: 200,
|
|
2176
2225
|
startJitterMs: 200
|
|
2177
2226
|
});
|
|
2178
|
-
schedulerByModel.set(schedulerKey, created);
|
|
2227
|
+
fireworksCallState.schedulerByModel.set(schedulerKey, created);
|
|
2179
2228
|
return created;
|
|
2180
2229
|
}
|
|
2181
2230
|
async function runFireworksCall(fn, modelId, runOptions) {
|
|
@@ -2222,7 +2271,10 @@ var ServiceAccountSchema = import_zod2.z.object({
|
|
|
2222
2271
|
privateKey: private_key.replace(/\\n/g, "\n"),
|
|
2223
2272
|
tokenUri: token_uri
|
|
2224
2273
|
}));
|
|
2225
|
-
var
|
|
2274
|
+
var googleAuthState = getRuntimeSingleton(/* @__PURE__ */ Symbol.for("@ljoukov/llm.googleAuthState"), () => ({
|
|
2275
|
+
cachedServiceAccount: null,
|
|
2276
|
+
authClientCache: /* @__PURE__ */ new Map()
|
|
2277
|
+
}));
|
|
2226
2278
|
function parseGoogleServiceAccount(input) {
|
|
2227
2279
|
let parsed;
|
|
2228
2280
|
try {
|
|
@@ -2233,16 +2285,16 @@ function parseGoogleServiceAccount(input) {
|
|
|
2233
2285
|
return ServiceAccountSchema.parse(parsed);
|
|
2234
2286
|
}
|
|
2235
2287
|
function getGoogleServiceAccount() {
|
|
2236
|
-
if (cachedServiceAccount) {
|
|
2237
|
-
return cachedServiceAccount;
|
|
2288
|
+
if (googleAuthState.cachedServiceAccount) {
|
|
2289
|
+
return googleAuthState.cachedServiceAccount;
|
|
2238
2290
|
}
|
|
2239
2291
|
loadLocalEnv();
|
|
2240
2292
|
const raw = process.env.GOOGLE_SERVICE_ACCOUNT_JSON;
|
|
2241
2293
|
if (!raw || raw.trim().length === 0) {
|
|
2242
2294
|
throw new Error("GOOGLE_SERVICE_ACCOUNT_JSON must be provided for Google APIs access.");
|
|
2243
2295
|
}
|
|
2244
|
-
cachedServiceAccount = parseGoogleServiceAccount(raw);
|
|
2245
|
-
return cachedServiceAccount;
|
|
2296
|
+
googleAuthState.cachedServiceAccount = parseGoogleServiceAccount(raw);
|
|
2297
|
+
return googleAuthState.cachedServiceAccount;
|
|
2246
2298
|
}
|
|
2247
2299
|
function normaliseScopes(scopes) {
|
|
2248
2300
|
if (!scopes) {
|
|
@@ -2294,8 +2346,10 @@ function isGeminiImageModelId(value) {
|
|
|
2294
2346
|
}
|
|
2295
2347
|
var CLOUD_PLATFORM_SCOPE = "https://www.googleapis.com/auth/cloud-platform";
|
|
2296
2348
|
var DEFAULT_VERTEX_LOCATION = "global";
|
|
2297
|
-
var
|
|
2298
|
-
|
|
2349
|
+
var geminiClientState = getRuntimeSingleton(/* @__PURE__ */ Symbol.for("@ljoukov/llm.geminiClientState"), () => ({
|
|
2350
|
+
geminiConfiguration: {},
|
|
2351
|
+
clientPromise: void 0
|
|
2352
|
+
}));
|
|
2299
2353
|
function normaliseConfigValue(value) {
|
|
2300
2354
|
if (value === void 0 || value === null) {
|
|
2301
2355
|
return void 0;
|
|
@@ -2306,14 +2360,14 @@ function normaliseConfigValue(value) {
|
|
|
2306
2360
|
function configureGemini(options = {}) {
|
|
2307
2361
|
const nextProjectId = normaliseConfigValue(options.projectId);
|
|
2308
2362
|
const nextLocation = normaliseConfigValue(options.location);
|
|
2309
|
-
geminiConfiguration = {
|
|
2310
|
-
projectId: nextProjectId !== void 0 ? nextProjectId : geminiConfiguration.projectId,
|
|
2311
|
-
location: nextLocation !== void 0 ? nextLocation : geminiConfiguration.location
|
|
2363
|
+
geminiClientState.geminiConfiguration = {
|
|
2364
|
+
projectId: nextProjectId !== void 0 ? nextProjectId : geminiClientState.geminiConfiguration.projectId,
|
|
2365
|
+
location: nextLocation !== void 0 ? nextLocation : geminiClientState.geminiConfiguration.location
|
|
2312
2366
|
};
|
|
2313
|
-
clientPromise = void 0;
|
|
2367
|
+
geminiClientState.clientPromise = void 0;
|
|
2314
2368
|
}
|
|
2315
2369
|
function resolveProjectId() {
|
|
2316
|
-
const override = geminiConfiguration.projectId;
|
|
2370
|
+
const override = geminiClientState.geminiConfiguration.projectId;
|
|
2317
2371
|
if (override) {
|
|
2318
2372
|
return override;
|
|
2319
2373
|
}
|
|
@@ -2321,15 +2375,15 @@ function resolveProjectId() {
|
|
|
2321
2375
|
return serviceAccount.projectId;
|
|
2322
2376
|
}
|
|
2323
2377
|
function resolveLocation() {
|
|
2324
|
-
const override = geminiConfiguration.location;
|
|
2378
|
+
const override = geminiClientState.geminiConfiguration.location;
|
|
2325
2379
|
if (override) {
|
|
2326
2380
|
return override;
|
|
2327
2381
|
}
|
|
2328
2382
|
return DEFAULT_VERTEX_LOCATION;
|
|
2329
2383
|
}
|
|
2330
2384
|
async function getGeminiClient() {
|
|
2331
|
-
if (!clientPromise) {
|
|
2332
|
-
clientPromise = Promise.resolve().then(() => {
|
|
2385
|
+
if (!geminiClientState.clientPromise) {
|
|
2386
|
+
geminiClientState.clientPromise = Promise.resolve().then(() => {
|
|
2333
2387
|
const projectId = resolveProjectId();
|
|
2334
2388
|
const location = resolveLocation();
|
|
2335
2389
|
const googleAuthOptions = getGoogleAuthOptions(CLOUD_PLATFORM_SCOPE);
|
|
@@ -2341,7 +2395,7 @@ async function getGeminiClient() {
|
|
|
2341
2395
|
});
|
|
2342
2396
|
});
|
|
2343
2397
|
}
|
|
2344
|
-
return clientPromise;
|
|
2398
|
+
return geminiClientState.clientPromise;
|
|
2345
2399
|
}
|
|
2346
2400
|
|
|
2347
2401
|
// src/google/calls.ts
|
|
@@ -2537,11 +2591,13 @@ function retryDelayMs(attempt) {
|
|
|
2537
2591
|
return base + jitter;
|
|
2538
2592
|
}
|
|
2539
2593
|
var DEFAULT_SCHEDULER_KEY2 = "__default__";
|
|
2540
|
-
var
|
|
2594
|
+
var googleCallState = getRuntimeSingleton(/* @__PURE__ */ Symbol.for("@ljoukov/llm.googleCallState"), () => ({
|
|
2595
|
+
schedulerByModel: /* @__PURE__ */ new Map()
|
|
2596
|
+
}));
|
|
2541
2597
|
function getSchedulerForModel2(modelId) {
|
|
2542
2598
|
const normalizedModelId = modelId?.trim();
|
|
2543
2599
|
const schedulerKey = normalizedModelId && normalizedModelId.length > 0 ? normalizedModelId : DEFAULT_SCHEDULER_KEY2;
|
|
2544
|
-
const existing =
|
|
2600
|
+
const existing = googleCallState.schedulerByModel.get(schedulerKey);
|
|
2545
2601
|
if (existing) {
|
|
2546
2602
|
return existing;
|
|
2547
2603
|
}
|
|
@@ -2564,7 +2620,7 @@ function getSchedulerForModel2(modelId) {
|
|
|
2564
2620
|
}
|
|
2565
2621
|
}
|
|
2566
2622
|
});
|
|
2567
|
-
|
|
2623
|
+
googleCallState.schedulerByModel.set(schedulerKey, created);
|
|
2568
2624
|
return created;
|
|
2569
2625
|
}
|
|
2570
2626
|
async function runGeminiCall(fn, modelId, runOptions) {
|
|
@@ -2574,53 +2630,55 @@ async function runGeminiCall(fn, modelId, runOptions) {
|
|
|
2574
2630
|
// src/openai/client.ts
|
|
2575
2631
|
var import_openai2 = __toESM(require("openai"), 1);
|
|
2576
2632
|
var import_undici2 = require("undici");
|
|
2577
|
-
var
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2633
|
+
var openAiClientState = getRuntimeSingleton(/* @__PURE__ */ Symbol.for("@ljoukov/llm.openAiClientState"), () => ({
|
|
2634
|
+
cachedApiKey: null,
|
|
2635
|
+
cachedClient: null,
|
|
2636
|
+
cachedFetch: null,
|
|
2637
|
+
cachedTimeoutMs: null,
|
|
2638
|
+
openAiResponsesWebSocketMode: null,
|
|
2639
|
+
openAiResponsesWebSocketDisabled: false
|
|
2640
|
+
}));
|
|
2583
2641
|
var DEFAULT_OPENAI_TIMEOUT_MS = 15 * 6e4;
|
|
2584
2642
|
function resolveOpenAiTimeoutMs() {
|
|
2585
|
-
if (
|
|
2586
|
-
return
|
|
2643
|
+
if (openAiClientState.cachedTimeoutMs !== null) {
|
|
2644
|
+
return openAiClientState.cachedTimeoutMs;
|
|
2587
2645
|
}
|
|
2588
2646
|
const raw = process.env.OPENAI_STREAM_TIMEOUT_MS ?? process.env.OPENAI_TIMEOUT_MS;
|
|
2589
2647
|
const parsed = raw ? Number(raw) : Number.NaN;
|
|
2590
|
-
|
|
2591
|
-
return
|
|
2648
|
+
openAiClientState.cachedTimeoutMs = Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_OPENAI_TIMEOUT_MS;
|
|
2649
|
+
return openAiClientState.cachedTimeoutMs;
|
|
2592
2650
|
}
|
|
2593
2651
|
function getOpenAiFetch() {
|
|
2594
|
-
if (
|
|
2595
|
-
return
|
|
2652
|
+
if (openAiClientState.cachedFetch) {
|
|
2653
|
+
return openAiClientState.cachedFetch;
|
|
2596
2654
|
}
|
|
2597
2655
|
const timeoutMs = resolveOpenAiTimeoutMs();
|
|
2598
2656
|
const dispatcher = new import_undici2.Agent({
|
|
2599
2657
|
bodyTimeout: timeoutMs,
|
|
2600
2658
|
headersTimeout: timeoutMs
|
|
2601
2659
|
});
|
|
2602
|
-
|
|
2660
|
+
openAiClientState.cachedFetch = ((input, init) => {
|
|
2603
2661
|
return (0, import_undici2.fetch)(input, {
|
|
2604
2662
|
...init ?? {},
|
|
2605
2663
|
dispatcher
|
|
2606
2664
|
});
|
|
2607
2665
|
});
|
|
2608
|
-
return
|
|
2666
|
+
return openAiClientState.cachedFetch;
|
|
2609
2667
|
}
|
|
2610
2668
|
function resolveOpenAiBaseUrl() {
|
|
2611
2669
|
loadLocalEnv();
|
|
2612
2670
|
return process.env.OPENAI_BASE_URL?.trim() || "https://api.openai.com/v1";
|
|
2613
2671
|
}
|
|
2614
2672
|
function resolveOpenAiResponsesWebSocketMode() {
|
|
2615
|
-
if (openAiResponsesWebSocketMode) {
|
|
2616
|
-
return openAiResponsesWebSocketMode;
|
|
2673
|
+
if (openAiClientState.openAiResponsesWebSocketMode) {
|
|
2674
|
+
return openAiClientState.openAiResponsesWebSocketMode;
|
|
2617
2675
|
}
|
|
2618
2676
|
loadLocalEnv();
|
|
2619
|
-
openAiResponsesWebSocketMode = resolveResponsesWebSocketMode(
|
|
2677
|
+
openAiClientState.openAiResponsesWebSocketMode = resolveResponsesWebSocketMode(
|
|
2620
2678
|
process.env.OPENAI_RESPONSES_WEBSOCKET_MODE,
|
|
2621
2679
|
"auto"
|
|
2622
2680
|
);
|
|
2623
|
-
return openAiResponsesWebSocketMode;
|
|
2681
|
+
return openAiClientState.openAiResponsesWebSocketMode;
|
|
2624
2682
|
}
|
|
2625
2683
|
function wrapFallbackStream(stream) {
|
|
2626
2684
|
return {
|
|
@@ -2673,7 +2731,7 @@ function installResponsesWebSocketTransport(client, apiKey) {
|
|
|
2673
2731
|
responsesApi.stream = (request, options) => {
|
|
2674
2732
|
const mode = resolveOpenAiResponsesWebSocketMode();
|
|
2675
2733
|
const fallbackStreamFactory = () => wrapFallbackStream(originalStream(request, options));
|
|
2676
|
-
if (mode === "off" || openAiResponsesWebSocketDisabled) {
|
|
2734
|
+
if (mode === "off" || openAiClientState.openAiResponsesWebSocketDisabled) {
|
|
2677
2735
|
return fallbackStreamFactory();
|
|
2678
2736
|
}
|
|
2679
2737
|
const signal = options && typeof options === "object" ? options.signal ?? void 0 : void 0;
|
|
@@ -2692,15 +2750,15 @@ function installResponsesWebSocketTransport(client, apiKey) {
|
|
|
2692
2750
|
createFallbackStream: fallbackStreamFactory,
|
|
2693
2751
|
onWebSocketFallback: (error) => {
|
|
2694
2752
|
if (isResponsesWebSocketUnsupportedError(error)) {
|
|
2695
|
-
openAiResponsesWebSocketDisabled = true;
|
|
2753
|
+
openAiClientState.openAiResponsesWebSocketDisabled = true;
|
|
2696
2754
|
}
|
|
2697
2755
|
}
|
|
2698
2756
|
});
|
|
2699
2757
|
};
|
|
2700
2758
|
}
|
|
2701
2759
|
function getOpenAiApiKey() {
|
|
2702
|
-
if (
|
|
2703
|
-
return
|
|
2760
|
+
if (openAiClientState.cachedApiKey !== null) {
|
|
2761
|
+
return openAiClientState.cachedApiKey;
|
|
2704
2762
|
}
|
|
2705
2763
|
loadLocalEnv();
|
|
2706
2764
|
const raw = process.env.OPENAI_API_KEY;
|
|
@@ -2708,33 +2766,35 @@ function getOpenAiApiKey() {
|
|
|
2708
2766
|
if (!value) {
|
|
2709
2767
|
throw new Error("OPENAI_API_KEY must be provided to access OpenAI APIs.");
|
|
2710
2768
|
}
|
|
2711
|
-
|
|
2712
|
-
return
|
|
2769
|
+
openAiClientState.cachedApiKey = value;
|
|
2770
|
+
return openAiClientState.cachedApiKey;
|
|
2713
2771
|
}
|
|
2714
2772
|
function getOpenAiClient() {
|
|
2715
|
-
if (
|
|
2716
|
-
return
|
|
2773
|
+
if (openAiClientState.cachedClient) {
|
|
2774
|
+
return openAiClientState.cachedClient;
|
|
2717
2775
|
}
|
|
2718
2776
|
loadLocalEnv();
|
|
2719
2777
|
const apiKey = getOpenAiApiKey();
|
|
2720
2778
|
const timeoutMs = resolveOpenAiTimeoutMs();
|
|
2721
|
-
|
|
2779
|
+
openAiClientState.cachedClient = new import_openai2.default({
|
|
2722
2780
|
apiKey,
|
|
2723
2781
|
fetch: getOpenAiFetch(),
|
|
2724
2782
|
timeout: timeoutMs
|
|
2725
2783
|
});
|
|
2726
|
-
installResponsesWebSocketTransport(
|
|
2727
|
-
return
|
|
2784
|
+
installResponsesWebSocketTransport(openAiClientState.cachedClient, apiKey);
|
|
2785
|
+
return openAiClientState.cachedClient;
|
|
2728
2786
|
}
|
|
2729
2787
|
|
|
2730
2788
|
// src/openai/calls.ts
|
|
2731
2789
|
var DEFAULT_OPENAI_REASONING_EFFORT = "medium";
|
|
2732
2790
|
var DEFAULT_SCHEDULER_KEY3 = "__default__";
|
|
2733
|
-
var
|
|
2791
|
+
var openAiCallState = getRuntimeSingleton(/* @__PURE__ */ Symbol.for("@ljoukov/llm.openAiCallState"), () => ({
|
|
2792
|
+
schedulerByModel: /* @__PURE__ */ new Map()
|
|
2793
|
+
}));
|
|
2734
2794
|
function getSchedulerForModel3(modelId) {
|
|
2735
2795
|
const normalizedModelId = modelId?.trim();
|
|
2736
2796
|
const schedulerKey = normalizedModelId && normalizedModelId.length > 0 ? normalizedModelId : DEFAULT_SCHEDULER_KEY3;
|
|
2737
|
-
const existing =
|
|
2797
|
+
const existing = openAiCallState.schedulerByModel.get(schedulerKey);
|
|
2738
2798
|
if (existing) {
|
|
2739
2799
|
return existing;
|
|
2740
2800
|
}
|
|
@@ -2746,7 +2806,7 @@ function getSchedulerForModel3(modelId) {
|
|
|
2746
2806
|
minIntervalBetweenStartMs: 200,
|
|
2747
2807
|
startJitterMs: 200
|
|
2748
2808
|
});
|
|
2749
|
-
|
|
2809
|
+
openAiCallState.schedulerByModel.set(schedulerKey, created);
|
|
2750
2810
|
return created;
|
|
2751
2811
|
}
|
|
2752
2812
|
async function runOpenAiCall(fn, modelId, runOptions) {
|
|
@@ -2818,6 +2878,9 @@ function ensureTrailingNewline(value) {
|
|
|
2818
2878
|
return value.endsWith("\n") ? value : `${value}
|
|
2819
2879
|
`;
|
|
2820
2880
|
}
|
|
2881
|
+
function hasNonEmptyText(value) {
|
|
2882
|
+
return typeof value === "string" && value.length > 0;
|
|
2883
|
+
}
|
|
2821
2884
|
function redactDataUrlPayload(value) {
|
|
2822
2885
|
if (!value.toLowerCase().startsWith("data:")) {
|
|
2823
2886
|
return value;
|
|
@@ -3066,6 +3129,25 @@ var AgentLoggingSessionImpl = class {
|
|
|
3066
3129
|
}
|
|
3067
3130
|
this.enqueueLineWrite(timestamped);
|
|
3068
3131
|
}
|
|
3132
|
+
async writeAttachments(baseDir, attachments) {
|
|
3133
|
+
const usedNames = /* @__PURE__ */ new Set();
|
|
3134
|
+
for (const attachment of attachments ?? []) {
|
|
3135
|
+
let filename = normalisePathSegment(attachment.filename);
|
|
3136
|
+
if (!filename.includes(".")) {
|
|
3137
|
+
filename = `${filename}.bin`;
|
|
3138
|
+
}
|
|
3139
|
+
const ext = import_node_path3.default.extname(filename);
|
|
3140
|
+
const base = ext.length > 0 ? filename.slice(0, -ext.length) : filename;
|
|
3141
|
+
let candidate = filename;
|
|
3142
|
+
let duplicateIndex = 2;
|
|
3143
|
+
while (usedNames.has(candidate)) {
|
|
3144
|
+
candidate = `${base}-${duplicateIndex.toString()}${ext}`;
|
|
3145
|
+
duplicateIndex += 1;
|
|
3146
|
+
}
|
|
3147
|
+
usedNames.add(candidate);
|
|
3148
|
+
await (0, import_promises.writeFile)(import_node_path3.default.join(baseDir, candidate), attachment.bytes);
|
|
3149
|
+
}
|
|
3150
|
+
}
|
|
3069
3151
|
startLlmCall(input) {
|
|
3070
3152
|
const callNumber = this.callCounter + 1;
|
|
3071
3153
|
this.callCounter = callNumber;
|
|
@@ -3078,6 +3160,9 @@ var AgentLoggingSessionImpl = class {
|
|
|
3078
3160
|
);
|
|
3079
3161
|
const responsePath = import_node_path3.default.join(baseDir, "response.txt");
|
|
3080
3162
|
const thoughtsPath = import_node_path3.default.join(baseDir, "thoughts.txt");
|
|
3163
|
+
const toolCallPath = import_node_path3.default.join(baseDir, "tool_call.txt");
|
|
3164
|
+
const toolCallResponsePath = import_node_path3.default.join(baseDir, "tool_call_response.txt");
|
|
3165
|
+
const errorPath = import_node_path3.default.join(baseDir, "error.txt");
|
|
3081
3166
|
const responseMetadataPath = import_node_path3.default.join(baseDir, "response.metadata.json");
|
|
3082
3167
|
let chain = this.ensureReady.then(async () => {
|
|
3083
3168
|
await (0, import_promises.mkdir)(baseDir, { recursive: true });
|
|
@@ -3099,22 +3184,13 @@ var AgentLoggingSessionImpl = class {
|
|
|
3099
3184
|
`,
|
|
3100
3185
|
"utf8"
|
|
3101
3186
|
);
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
|
|
3109
|
-
const base = ext.length > 0 ? filename.slice(0, -ext.length) : filename;
|
|
3110
|
-
let candidate = filename;
|
|
3111
|
-
let duplicateIndex = 2;
|
|
3112
|
-
while (usedNames.has(candidate)) {
|
|
3113
|
-
candidate = `${base}-${duplicateIndex.toString()}${ext}`;
|
|
3114
|
-
duplicateIndex += 1;
|
|
3115
|
-
}
|
|
3116
|
-
usedNames.add(candidate);
|
|
3117
|
-
await (0, import_promises.writeFile)(import_node_path3.default.join(baseDir, candidate), attachment.bytes);
|
|
3187
|
+
await this.writeAttachments(baseDir, input.attachments);
|
|
3188
|
+
if (hasNonEmptyText(input.toolCallResponseText)) {
|
|
3189
|
+
await (0, import_promises.writeFile)(
|
|
3190
|
+
toolCallResponsePath,
|
|
3191
|
+
ensureTrailingNewline(input.toolCallResponseText),
|
|
3192
|
+
"utf8"
|
|
3193
|
+
);
|
|
3118
3194
|
}
|
|
3119
3195
|
}).catch(() => void 0);
|
|
3120
3196
|
this.track(chain);
|
|
@@ -3142,18 +3218,25 @@ var AgentLoggingSessionImpl = class {
|
|
|
3142
3218
|
await (0, import_promises.appendFile)(responsePath, text, "utf8");
|
|
3143
3219
|
});
|
|
3144
3220
|
},
|
|
3145
|
-
complete: (
|
|
3221
|
+
complete: (options) => {
|
|
3146
3222
|
if (closed) {
|
|
3147
3223
|
return;
|
|
3148
3224
|
}
|
|
3149
3225
|
closed = true;
|
|
3150
3226
|
enqueue(async () => {
|
|
3227
|
+
if (hasNonEmptyText(options?.responseText)) {
|
|
3228
|
+
await (0, import_promises.writeFile)(responsePath, options.responseText, "utf8");
|
|
3229
|
+
}
|
|
3230
|
+
if (hasNonEmptyText(options?.toolCallText)) {
|
|
3231
|
+
await (0, import_promises.writeFile)(toolCallPath, ensureTrailingNewline(options.toolCallText), "utf8");
|
|
3232
|
+
}
|
|
3233
|
+
await this.writeAttachments(baseDir, options?.attachments);
|
|
3151
3234
|
const payload = {
|
|
3152
3235
|
capturedAt: toIsoNow(),
|
|
3153
3236
|
status: "completed"
|
|
3154
3237
|
};
|
|
3155
|
-
if (metadata) {
|
|
3156
|
-
const sanitised = sanitiseLogValue(metadata);
|
|
3238
|
+
if (options?.metadata) {
|
|
3239
|
+
const sanitised = sanitiseLogValue(options.metadata);
|
|
3157
3240
|
if (sanitised && typeof sanitised === "object" && !Array.isArray(sanitised)) {
|
|
3158
3241
|
Object.assign(payload, sanitised);
|
|
3159
3242
|
} else if (sanitised !== void 0) {
|
|
@@ -3164,19 +3247,27 @@ var AgentLoggingSessionImpl = class {
|
|
|
3164
3247
|
`, "utf8");
|
|
3165
3248
|
});
|
|
3166
3249
|
},
|
|
3167
|
-
fail: (error,
|
|
3250
|
+
fail: (error, options) => {
|
|
3168
3251
|
if (closed) {
|
|
3169
3252
|
return;
|
|
3170
3253
|
}
|
|
3171
3254
|
closed = true;
|
|
3172
3255
|
enqueue(async () => {
|
|
3256
|
+
if (hasNonEmptyText(options?.responseText)) {
|
|
3257
|
+
await (0, import_promises.writeFile)(responsePath, options.responseText, "utf8");
|
|
3258
|
+
}
|
|
3259
|
+
if (hasNonEmptyText(options?.toolCallText)) {
|
|
3260
|
+
await (0, import_promises.writeFile)(toolCallPath, ensureTrailingNewline(options.toolCallText), "utf8");
|
|
3261
|
+
}
|
|
3262
|
+
await this.writeAttachments(baseDir, options?.attachments);
|
|
3263
|
+
await (0, import_promises.writeFile)(errorPath, ensureTrailingNewline(toErrorMessage(error)), "utf8");
|
|
3173
3264
|
const payload = {
|
|
3174
3265
|
capturedAt: toIsoNow(),
|
|
3175
3266
|
status: "failed",
|
|
3176
3267
|
error: toErrorMessage(error)
|
|
3177
3268
|
};
|
|
3178
|
-
if (metadata) {
|
|
3179
|
-
const sanitised = sanitiseLogValue(metadata);
|
|
3269
|
+
if (options?.metadata) {
|
|
3270
|
+
const sanitised = sanitiseLogValue(options.metadata);
|
|
3180
3271
|
if (sanitised && typeof sanitised === "object" && !Array.isArray(sanitised)) {
|
|
3181
3272
|
Object.assign(payload, sanitised);
|
|
3182
3273
|
} else if (sanitised !== void 0) {
|
|
@@ -3201,7 +3292,10 @@ var AgentLoggingSessionImpl = class {
|
|
|
3201
3292
|
}
|
|
3202
3293
|
}
|
|
3203
3294
|
};
|
|
3204
|
-
var loggingSessionStorage =
|
|
3295
|
+
var loggingSessionStorage = getRuntimeSingleton(
|
|
3296
|
+
/* @__PURE__ */ Symbol.for("@ljoukov/llm.agentLogging.sessionStorage"),
|
|
3297
|
+
() => new import_node_async_hooks.AsyncLocalStorage()
|
|
3298
|
+
);
|
|
3205
3299
|
function createAgentLoggingSession(config) {
|
|
3206
3300
|
return new AgentLoggingSessionImpl(config);
|
|
3207
3301
|
}
|
|
@@ -3216,7 +3310,10 @@ function getCurrentAgentLoggingSession() {
|
|
|
3216
3310
|
}
|
|
3217
3311
|
|
|
3218
3312
|
// src/llm.ts
|
|
3219
|
-
var toolCallContextStorage =
|
|
3313
|
+
var toolCallContextStorage = getRuntimeSingleton(
|
|
3314
|
+
/* @__PURE__ */ Symbol.for("@ljoukov/llm.toolCallContextStorage"),
|
|
3315
|
+
() => new import_node_async_hooks2.AsyncLocalStorage()
|
|
3316
|
+
);
|
|
3220
3317
|
function getCurrentToolCallContext() {
|
|
3221
3318
|
return toolCallContextStorage.getStore() ?? null;
|
|
3222
3319
|
}
|
|
@@ -5019,7 +5116,10 @@ function resolveAttachmentExtension(mimeType) {
|
|
|
5019
5116
|
}
|
|
5020
5117
|
}
|
|
5021
5118
|
}
|
|
5022
|
-
function
|
|
5119
|
+
function buildLoggedAttachmentFilename(prefix, index, mimeType) {
|
|
5120
|
+
return `${prefix}-${index.toString()}.${resolveAttachmentExtension(mimeType)}`;
|
|
5121
|
+
}
|
|
5122
|
+
function decodeDataUrlAttachment(value, options) {
|
|
5023
5123
|
const trimmed = value.trim();
|
|
5024
5124
|
if (!trimmed.toLowerCase().startsWith("data:")) {
|
|
5025
5125
|
return null;
|
|
@@ -5035,7 +5135,7 @@ function decodeDataUrlAttachment(value, basename) {
|
|
|
5035
5135
|
try {
|
|
5036
5136
|
const bytes = isBase64 ? import_node_buffer3.Buffer.from(payload, "base64") : import_node_buffer3.Buffer.from(decodeURIComponent(payload), "utf8");
|
|
5037
5137
|
return {
|
|
5038
|
-
filename:
|
|
5138
|
+
filename: buildLoggedAttachmentFilename(options.prefix, options.index, mimeType),
|
|
5039
5139
|
bytes
|
|
5040
5140
|
};
|
|
5041
5141
|
} catch {
|
|
@@ -5044,10 +5144,10 @@ function decodeDataUrlAttachment(value, basename) {
|
|
|
5044
5144
|
}
|
|
5045
5145
|
function collectPayloadAttachments(value, options) {
|
|
5046
5146
|
if (typeof value === "string") {
|
|
5047
|
-
const attachment = decodeDataUrlAttachment(
|
|
5048
|
-
|
|
5049
|
-
|
|
5050
|
-
);
|
|
5147
|
+
const attachment = decodeDataUrlAttachment(value, {
|
|
5148
|
+
prefix: options.prefix,
|
|
5149
|
+
index: options.counter
|
|
5150
|
+
});
|
|
5051
5151
|
if (attachment) {
|
|
5052
5152
|
options.attachments.push(attachment);
|
|
5053
5153
|
options.counter += 1;
|
|
@@ -5072,7 +5172,7 @@ function collectPayloadAttachments(value, options) {
|
|
|
5072
5172
|
if (typeof record.data === "string" && mimeType) {
|
|
5073
5173
|
try {
|
|
5074
5174
|
options.attachments.push({
|
|
5075
|
-
filename:
|
|
5175
|
+
filename: buildLoggedAttachmentFilename(options.prefix, options.counter, mimeType),
|
|
5076
5176
|
bytes: decodeInlineDataBuffer(record.data)
|
|
5077
5177
|
});
|
|
5078
5178
|
options.counter += 1;
|
|
@@ -5092,27 +5192,166 @@ function serialiseRequestPayloadForLogging(value) {
|
|
|
5092
5192
|
`;
|
|
5093
5193
|
}
|
|
5094
5194
|
}
|
|
5195
|
+
function serialiseLogArtifactText(value) {
|
|
5196
|
+
if (value === null || value === void 0) {
|
|
5197
|
+
return void 0;
|
|
5198
|
+
}
|
|
5199
|
+
if (typeof value === "string") {
|
|
5200
|
+
if (value.length === 0) {
|
|
5201
|
+
return void 0;
|
|
5202
|
+
}
|
|
5203
|
+
return value.endsWith("\n") ? value : `${value}
|
|
5204
|
+
`;
|
|
5205
|
+
}
|
|
5206
|
+
if (Array.isArray(value) && value.length === 0) {
|
|
5207
|
+
return void 0;
|
|
5208
|
+
}
|
|
5209
|
+
if (isPlainRecord(value) && Object.keys(value).length === 0) {
|
|
5210
|
+
return void 0;
|
|
5211
|
+
}
|
|
5212
|
+
try {
|
|
5213
|
+
return `${JSON.stringify(sanitiseLogValue(value), null, 2)}
|
|
5214
|
+
`;
|
|
5215
|
+
} catch {
|
|
5216
|
+
return `${String(value)}
|
|
5217
|
+
`;
|
|
5218
|
+
}
|
|
5219
|
+
}
|
|
5220
|
+
function collectLoggedAttachmentsFromLlmParts(parts, prefix) {
|
|
5221
|
+
const attachments = [];
|
|
5222
|
+
let index = 1;
|
|
5223
|
+
for (const part of parts) {
|
|
5224
|
+
if (part.type !== "inlineData") {
|
|
5225
|
+
continue;
|
|
5226
|
+
}
|
|
5227
|
+
attachments.push({
|
|
5228
|
+
filename: buildLoggedAttachmentFilename(prefix, index, part.mimeType),
|
|
5229
|
+
bytes: decodeInlineDataBuffer(part.data)
|
|
5230
|
+
});
|
|
5231
|
+
index += 1;
|
|
5232
|
+
}
|
|
5233
|
+
return attachments;
|
|
5234
|
+
}
|
|
5235
|
+
function collectLoggedAttachmentsFromGeminiParts(parts, prefix) {
|
|
5236
|
+
return collectLoggedAttachmentsFromLlmParts(convertGooglePartsToLlmParts(parts), prefix);
|
|
5237
|
+
}
|
|
5238
|
+
function extractToolCallResponseTextFromOpenAiInput(input) {
|
|
5239
|
+
if (!Array.isArray(input)) {
|
|
5240
|
+
return void 0;
|
|
5241
|
+
}
|
|
5242
|
+
const responses = input.filter((item) => isPlainRecord(item)).flatMap((item) => {
|
|
5243
|
+
const type = typeof item.type === "string" ? item.type : "";
|
|
5244
|
+
if (type !== "function_call_output" && type !== "custom_tool_call_output") {
|
|
5245
|
+
return [];
|
|
5246
|
+
}
|
|
5247
|
+
return [
|
|
5248
|
+
{
|
|
5249
|
+
type,
|
|
5250
|
+
callId: typeof item.call_id === "string" ? item.call_id : void 0,
|
|
5251
|
+
output: "output" in item ? sanitiseLogValue(item.output) : void 0
|
|
5252
|
+
}
|
|
5253
|
+
];
|
|
5254
|
+
});
|
|
5255
|
+
return serialiseLogArtifactText(responses);
|
|
5256
|
+
}
|
|
5257
|
+
function extractToolCallResponseTextFromFireworksMessages(messages) {
|
|
5258
|
+
if (!Array.isArray(messages)) {
|
|
5259
|
+
return void 0;
|
|
5260
|
+
}
|
|
5261
|
+
const responses = messages.filter((message) => isPlainRecord(message)).flatMap((message) => {
|
|
5262
|
+
if (message.role !== "tool") {
|
|
5263
|
+
return [];
|
|
5264
|
+
}
|
|
5265
|
+
return [
|
|
5266
|
+
{
|
|
5267
|
+
toolCallId: typeof message.tool_call_id === "string" ? message.tool_call_id : void 0,
|
|
5268
|
+
content: sanitiseLogValue(message.content)
|
|
5269
|
+
}
|
|
5270
|
+
];
|
|
5271
|
+
});
|
|
5272
|
+
return serialiseLogArtifactText(responses);
|
|
5273
|
+
}
|
|
5274
|
+
function extractToolCallResponseTextFromGeminiContents(contents) {
|
|
5275
|
+
if (!Array.isArray(contents)) {
|
|
5276
|
+
return void 0;
|
|
5277
|
+
}
|
|
5278
|
+
const responses = [];
|
|
5279
|
+
for (const content of contents) {
|
|
5280
|
+
if (!content || typeof content !== "object") {
|
|
5281
|
+
continue;
|
|
5282
|
+
}
|
|
5283
|
+
const parts = content.parts;
|
|
5284
|
+
if (!Array.isArray(parts)) {
|
|
5285
|
+
continue;
|
|
5286
|
+
}
|
|
5287
|
+
for (const part of parts) {
|
|
5288
|
+
if (!part || typeof part !== "object") {
|
|
5289
|
+
continue;
|
|
5290
|
+
}
|
|
5291
|
+
const functionResponse = part.functionResponse;
|
|
5292
|
+
if (functionResponse) {
|
|
5293
|
+
responses.push(sanitiseLogValue(functionResponse));
|
|
5294
|
+
}
|
|
5295
|
+
}
|
|
5296
|
+
}
|
|
5297
|
+
return serialiseLogArtifactText(responses);
|
|
5298
|
+
}
|
|
5299
|
+
function serialiseOpenAiStyleToolCallsForLogging(calls) {
|
|
5300
|
+
return serialiseLogArtifactText(
|
|
5301
|
+
calls.map((call) => {
|
|
5302
|
+
if (call.kind === "custom") {
|
|
5303
|
+
return {
|
|
5304
|
+
kind: call.kind,
|
|
5305
|
+
name: call.name,
|
|
5306
|
+
callId: call.callId,
|
|
5307
|
+
itemId: call.itemId,
|
|
5308
|
+
input: call.input
|
|
5309
|
+
};
|
|
5310
|
+
}
|
|
5311
|
+
const { value, error } = parseOpenAiToolArguments(call.arguments);
|
|
5312
|
+
return {
|
|
5313
|
+
kind: call.kind,
|
|
5314
|
+
name: call.name,
|
|
5315
|
+
callId: call.callId,
|
|
5316
|
+
itemId: call.itemId,
|
|
5317
|
+
arguments: value,
|
|
5318
|
+
...error ? { parseError: error, rawArguments: call.arguments } : {}
|
|
5319
|
+
};
|
|
5320
|
+
})
|
|
5321
|
+
);
|
|
5322
|
+
}
|
|
5323
|
+
function serialiseGeminiToolCallsForLogging(calls) {
|
|
5324
|
+
return serialiseLogArtifactText(
|
|
5325
|
+
calls.map((call) => ({
|
|
5326
|
+
name: call.name ?? "unknown",
|
|
5327
|
+
callId: typeof call.id === "string" ? call.id : void 0,
|
|
5328
|
+
arguments: sanitiseLogValue(call.args ?? {})
|
|
5329
|
+
}))
|
|
5330
|
+
);
|
|
5331
|
+
}
|
|
5095
5332
|
function startLlmCallLoggerFromContents(options) {
|
|
5096
5333
|
const session = getCurrentAgentLoggingSession();
|
|
5097
5334
|
if (!session) {
|
|
5098
5335
|
return void 0;
|
|
5099
5336
|
}
|
|
5100
5337
|
const attachments = [];
|
|
5338
|
+
let attachmentIndex = 1;
|
|
5101
5339
|
const sections = [];
|
|
5102
5340
|
for (const [messageIndex, message] of options.contents.entries()) {
|
|
5103
5341
|
sections.push(`### message_${(messageIndex + 1).toString()} role=${message.role}`);
|
|
5104
|
-
for (const
|
|
5342
|
+
for (const part of message.parts) {
|
|
5105
5343
|
if (part.type === "text") {
|
|
5106
5344
|
const channel = part.thought === true ? "thought" : "response";
|
|
5107
5345
|
sections.push(`[text:${channel}]`);
|
|
5108
5346
|
sections.push(part.text);
|
|
5109
5347
|
continue;
|
|
5110
5348
|
}
|
|
5111
|
-
const filename =
|
|
5349
|
+
const filename = buildLoggedAttachmentFilename("input", attachmentIndex, part.mimeType);
|
|
5112
5350
|
attachments.push({
|
|
5113
5351
|
filename,
|
|
5114
5352
|
bytes: decodeInlineDataBuffer(part.data)
|
|
5115
5353
|
});
|
|
5354
|
+
attachmentIndex += 1;
|
|
5116
5355
|
sections.push(
|
|
5117
5356
|
`[inlineData] file=${filename} mime=${part.mimeType ?? "application/octet-stream"} bytes=${attachments[attachments.length - 1]?.bytes.byteLength ?? 0}`
|
|
5118
5357
|
);
|
|
@@ -5156,11 +5395,18 @@ function startLlmCallLoggerFromPayload(options) {
|
|
|
5156
5395
|
}
|
|
5157
5396
|
const attachments = [];
|
|
5158
5397
|
collectPayloadAttachments(options.requestPayload, {
|
|
5159
|
-
prefix:
|
|
5398
|
+
prefix: "input",
|
|
5160
5399
|
attachments,
|
|
5161
5400
|
seen: /* @__PURE__ */ new WeakSet(),
|
|
5162
5401
|
counter: 1
|
|
5163
5402
|
});
|
|
5403
|
+
const toolCallResponseText = options.provider === "openai" || options.provider === "chatgpt" ? extractToolCallResponseTextFromOpenAiInput(
|
|
5404
|
+
options.requestPayload.input
|
|
5405
|
+
) : options.provider === "fireworks" ? extractToolCallResponseTextFromFireworksMessages(
|
|
5406
|
+
options.requestPayload.messages
|
|
5407
|
+
) : extractToolCallResponseTextFromGeminiContents(
|
|
5408
|
+
options.requestPayload.contents
|
|
5409
|
+
);
|
|
5164
5410
|
return session.startLlmCall({
|
|
5165
5411
|
provider: options.provider,
|
|
5166
5412
|
modelId: options.modelId,
|
|
@@ -5169,7 +5415,8 @@ function startLlmCallLoggerFromPayload(options) {
|
|
|
5169
5415
|
step: options.step,
|
|
5170
5416
|
...getCurrentToolCallContext() ? { toolContext: getCurrentToolCallContext() } : {}
|
|
5171
5417
|
},
|
|
5172
|
-
attachments
|
|
5418
|
+
attachments,
|
|
5419
|
+
toolCallResponseText
|
|
5173
5420
|
});
|
|
5174
5421
|
}
|
|
5175
5422
|
async function runTextCall(params) {
|
|
@@ -5474,6 +5721,7 @@ async function runTextCall(params) {
|
|
|
5474
5721
|
const mergedParts = mergeConsecutiveTextParts(responseParts);
|
|
5475
5722
|
const content = mergedParts.length > 0 ? { role: responseRole ?? "assistant", parts: mergedParts } : void 0;
|
|
5476
5723
|
const { text, thoughts } = extractTextByChannel(content);
|
|
5724
|
+
const outputAttachments = collectLoggedAttachmentsFromLlmParts(mergedParts, "output");
|
|
5477
5725
|
const costUsd = estimateCallCostUsd({
|
|
5478
5726
|
modelId: modelVersion,
|
|
5479
5727
|
tokens: latestUsage,
|
|
@@ -5484,16 +5732,20 @@ async function runTextCall(params) {
|
|
|
5484
5732
|
queue.push({ type: "usage", usage: latestUsage, costUsd, modelVersion });
|
|
5485
5733
|
}
|
|
5486
5734
|
callLogger?.complete({
|
|
5487
|
-
|
|
5488
|
-
|
|
5489
|
-
|
|
5490
|
-
|
|
5491
|
-
|
|
5492
|
-
|
|
5493
|
-
|
|
5494
|
-
|
|
5495
|
-
|
|
5496
|
-
|
|
5735
|
+
responseText: text,
|
|
5736
|
+
attachments: outputAttachments,
|
|
5737
|
+
metadata: {
|
|
5738
|
+
provider,
|
|
5739
|
+
model: request.model,
|
|
5740
|
+
modelVersion,
|
|
5741
|
+
blocked,
|
|
5742
|
+
costUsd,
|
|
5743
|
+
usage: latestUsage,
|
|
5744
|
+
grounding: grounding ? sanitiseLogValue(grounding) : void 0,
|
|
5745
|
+
responseChars: text.length,
|
|
5746
|
+
thoughtChars: thoughts.length,
|
|
5747
|
+
responseImages
|
|
5748
|
+
}
|
|
5497
5749
|
});
|
|
5498
5750
|
return {
|
|
5499
5751
|
provider,
|
|
@@ -5508,14 +5760,21 @@ async function runTextCall(params) {
|
|
|
5508
5760
|
grounding
|
|
5509
5761
|
};
|
|
5510
5762
|
} catch (error) {
|
|
5763
|
+
const partialParts = mergeConsecutiveTextParts(responseParts);
|
|
5764
|
+
const partialContent = partialParts.length > 0 ? { role: responseRole ?? "assistant", parts: partialParts } : void 0;
|
|
5765
|
+
const { text: partialText } = extractTextByChannel(partialContent);
|
|
5511
5766
|
callLogger?.fail(error, {
|
|
5512
|
-
|
|
5513
|
-
|
|
5514
|
-
|
|
5515
|
-
|
|
5516
|
-
|
|
5517
|
-
|
|
5518
|
-
|
|
5767
|
+
responseText: partialText,
|
|
5768
|
+
attachments: collectLoggedAttachmentsFromLlmParts(partialParts, "output"),
|
|
5769
|
+
metadata: {
|
|
5770
|
+
provider,
|
|
5771
|
+
model: request.model,
|
|
5772
|
+
modelVersion,
|
|
5773
|
+
blocked,
|
|
5774
|
+
usage: latestUsage,
|
|
5775
|
+
partialResponseParts: responseParts.length,
|
|
5776
|
+
responseImages
|
|
5777
|
+
}
|
|
5519
5778
|
});
|
|
5520
5779
|
throw error;
|
|
5521
5780
|
}
|
|
@@ -5722,7 +5981,10 @@ var DEFAULT_TOOL_LOOP_MAX_STEPS = 8;
|
|
|
5722
5981
|
function resolveToolLoopContents(input) {
|
|
5723
5982
|
return resolveTextContents(input);
|
|
5724
5983
|
}
|
|
5725
|
-
var toolLoopSteeringInternals =
|
|
5984
|
+
var toolLoopSteeringInternals = getRuntimeSingleton(
|
|
5985
|
+
/* @__PURE__ */ Symbol.for("@ljoukov/llm.toolLoopSteeringInternals"),
|
|
5986
|
+
() => /* @__PURE__ */ new WeakMap()
|
|
5987
|
+
);
|
|
5726
5988
|
function createToolLoopSteeringChannel() {
|
|
5727
5989
|
const pending = [];
|
|
5728
5990
|
let closed = false;
|
|
@@ -5982,6 +6244,9 @@ async function runToolLoop(request) {
|
|
|
5982
6244
|
let usageTokens;
|
|
5983
6245
|
let thoughtDeltaEmitted = false;
|
|
5984
6246
|
let blocked = false;
|
|
6247
|
+
let responseText = "";
|
|
6248
|
+
let reasoningSummary = "";
|
|
6249
|
+
let stepToolCallText;
|
|
5985
6250
|
const stepRequestPayload = {
|
|
5986
6251
|
model: providerInfo.model,
|
|
5987
6252
|
input,
|
|
@@ -6074,8 +6339,8 @@ async function runToolLoop(request) {
|
|
|
6074
6339
|
throw new Error(message);
|
|
6075
6340
|
}
|
|
6076
6341
|
usageTokens = extractOpenAiUsageTokens(finalResponse.usage);
|
|
6077
|
-
|
|
6078
|
-
|
|
6342
|
+
responseText = extractOpenAiResponseParts(finalResponse).parts.filter((p) => p.type === "text" && p.thought !== true).map((p) => p.text).join("").trim();
|
|
6343
|
+
reasoningSummary = extractOpenAiReasoningSummary(finalResponse).trim();
|
|
6079
6344
|
if (!thoughtDeltaEmitted && reasoningSummary.length > 0) {
|
|
6080
6345
|
stepCallLogger?.appendThoughtDelta(reasoningSummary);
|
|
6081
6346
|
emitEvent({ type: "delta", channel: "thought", text: reasoningSummary });
|
|
@@ -6091,6 +6356,23 @@ async function runToolLoop(request) {
|
|
|
6091
6356
|
emitEvent({ type: "usage", usage: usageTokens, costUsd: stepCostUsd, modelVersion });
|
|
6092
6357
|
}
|
|
6093
6358
|
const responseToolCalls = extractOpenAiToolCalls(finalResponse.output);
|
|
6359
|
+
stepToolCallText = serialiseOpenAiStyleToolCallsForLogging(
|
|
6360
|
+
responseToolCalls.map(
|
|
6361
|
+
(call) => call.kind === "custom" ? {
|
|
6362
|
+
kind: call.kind,
|
|
6363
|
+
name: call.name,
|
|
6364
|
+
input: call.input,
|
|
6365
|
+
callId: call.call_id,
|
|
6366
|
+
itemId: call.id
|
|
6367
|
+
} : {
|
|
6368
|
+
kind: call.kind,
|
|
6369
|
+
name: call.name,
|
|
6370
|
+
arguments: call.arguments,
|
|
6371
|
+
callId: call.call_id,
|
|
6372
|
+
itemId: call.id
|
|
6373
|
+
}
|
|
6374
|
+
)
|
|
6375
|
+
);
|
|
6094
6376
|
const stepToolCalls = [];
|
|
6095
6377
|
if (responseToolCalls.length === 0) {
|
|
6096
6378
|
const steeringInput2 = steeringInternal?.drainPendingContents() ?? [];
|
|
@@ -6118,17 +6400,20 @@ async function runToolLoop(request) {
|
|
|
6118
6400
|
timing: timing2
|
|
6119
6401
|
});
|
|
6120
6402
|
stepCallLogger?.complete({
|
|
6121
|
-
|
|
6122
|
-
|
|
6123
|
-
|
|
6124
|
-
|
|
6125
|
-
|
|
6126
|
-
|
|
6127
|
-
|
|
6128
|
-
|
|
6129
|
-
|
|
6130
|
-
|
|
6131
|
-
|
|
6403
|
+
responseText,
|
|
6404
|
+
metadata: {
|
|
6405
|
+
provider: "openai",
|
|
6406
|
+
model: request.model,
|
|
6407
|
+
modelVersion,
|
|
6408
|
+
step: turn,
|
|
6409
|
+
usage: usageTokens,
|
|
6410
|
+
costUsd: stepCostUsd,
|
|
6411
|
+
blocked,
|
|
6412
|
+
responseChars: responseText.length,
|
|
6413
|
+
thoughtChars: reasoningSummary.length,
|
|
6414
|
+
toolCalls: 0,
|
|
6415
|
+
finalStep: steeringItems2.length === 0
|
|
6416
|
+
}
|
|
6132
6417
|
});
|
|
6133
6418
|
if (steeringItems2.length === 0) {
|
|
6134
6419
|
return { text: finalText, thoughts: finalThoughts, steps, totalCostUsd };
|
|
@@ -6251,28 +6536,36 @@ async function runToolLoop(request) {
|
|
|
6251
6536
|
const steeringInput = steeringInternal?.drainPendingContents() ?? [];
|
|
6252
6537
|
const steeringItems = steeringInput.length > 0 ? toOpenAiInput(steeringInput) : [];
|
|
6253
6538
|
stepCallLogger?.complete({
|
|
6254
|
-
|
|
6255
|
-
|
|
6256
|
-
|
|
6257
|
-
|
|
6258
|
-
|
|
6259
|
-
|
|
6260
|
-
|
|
6261
|
-
|
|
6262
|
-
|
|
6263
|
-
|
|
6264
|
-
|
|
6539
|
+
responseText,
|
|
6540
|
+
toolCallText: stepToolCallText,
|
|
6541
|
+
metadata: {
|
|
6542
|
+
provider: "openai",
|
|
6543
|
+
model: request.model,
|
|
6544
|
+
modelVersion,
|
|
6545
|
+
step: turn,
|
|
6546
|
+
usage: usageTokens,
|
|
6547
|
+
costUsd: stepCostUsd,
|
|
6548
|
+
blocked,
|
|
6549
|
+
responseChars: responseText.length,
|
|
6550
|
+
thoughtChars: reasoningSummary.length,
|
|
6551
|
+
toolCalls: stepToolCalls.length,
|
|
6552
|
+
finalStep: false
|
|
6553
|
+
}
|
|
6265
6554
|
});
|
|
6266
6555
|
previousResponseId = finalResponse.id;
|
|
6267
6556
|
input = steeringItems.length > 0 ? toolOutputs.concat(steeringItems) : toolOutputs;
|
|
6268
6557
|
} catch (error) {
|
|
6269
6558
|
stepCallLogger?.fail(error, {
|
|
6270
|
-
|
|
6271
|
-
|
|
6272
|
-
|
|
6273
|
-
|
|
6274
|
-
|
|
6275
|
-
|
|
6559
|
+
responseText,
|
|
6560
|
+
toolCallText: stepToolCallText,
|
|
6561
|
+
metadata: {
|
|
6562
|
+
provider: "openai",
|
|
6563
|
+
model: request.model,
|
|
6564
|
+
modelVersion,
|
|
6565
|
+
step: turn,
|
|
6566
|
+
usage: usageTokens,
|
|
6567
|
+
blocked
|
|
6568
|
+
}
|
|
6276
6569
|
});
|
|
6277
6570
|
throw error;
|
|
6278
6571
|
}
|
|
@@ -6298,6 +6591,7 @@ async function runToolLoop(request) {
|
|
|
6298
6591
|
let usageTokens;
|
|
6299
6592
|
let responseText = "";
|
|
6300
6593
|
let reasoningSummaryText = "";
|
|
6594
|
+
let stepToolCallText;
|
|
6301
6595
|
const markFirstModelEvent = () => {
|
|
6302
6596
|
if (firstModelEventAtMs === void 0) {
|
|
6303
6597
|
firstModelEventAtMs = Date.now();
|
|
@@ -6366,6 +6660,23 @@ async function runToolLoop(request) {
|
|
|
6366
6660
|
stepCallLogger?.appendResponseDelta(responseText);
|
|
6367
6661
|
}
|
|
6368
6662
|
const responseToolCalls = response.toolCalls ?? [];
|
|
6663
|
+
stepToolCallText = serialiseOpenAiStyleToolCallsForLogging(
|
|
6664
|
+
responseToolCalls.map(
|
|
6665
|
+
(call) => call.kind === "custom" ? {
|
|
6666
|
+
kind: call.kind,
|
|
6667
|
+
name: call.name,
|
|
6668
|
+
input: call.input,
|
|
6669
|
+
callId: call.callId,
|
|
6670
|
+
itemId: call.id
|
|
6671
|
+
} : {
|
|
6672
|
+
kind: call.kind,
|
|
6673
|
+
name: call.name,
|
|
6674
|
+
arguments: call.arguments,
|
|
6675
|
+
callId: call.callId,
|
|
6676
|
+
itemId: call.id
|
|
6677
|
+
}
|
|
6678
|
+
)
|
|
6679
|
+
);
|
|
6369
6680
|
if (responseToolCalls.length === 0) {
|
|
6370
6681
|
const steeringInput2 = steeringInternal?.drainPendingContents() ?? [];
|
|
6371
6682
|
const steeringItems2 = steeringInput2.length > 0 ? toChatGptInput(steeringInput2).input : [];
|
|
@@ -6391,16 +6702,19 @@ async function runToolLoop(request) {
|
|
|
6391
6702
|
timing: timing2
|
|
6392
6703
|
});
|
|
6393
6704
|
stepCallLogger?.complete({
|
|
6394
|
-
|
|
6395
|
-
|
|
6396
|
-
|
|
6397
|
-
|
|
6398
|
-
|
|
6399
|
-
|
|
6400
|
-
|
|
6401
|
-
|
|
6402
|
-
|
|
6403
|
-
|
|
6705
|
+
responseText,
|
|
6706
|
+
metadata: {
|
|
6707
|
+
provider: "chatgpt",
|
|
6708
|
+
model: request.model,
|
|
6709
|
+
modelVersion,
|
|
6710
|
+
step: turn,
|
|
6711
|
+
usage: usageTokens,
|
|
6712
|
+
costUsd: stepCostUsd,
|
|
6713
|
+
responseChars: responseText.length,
|
|
6714
|
+
thoughtChars: reasoningSummaryText.length,
|
|
6715
|
+
toolCalls: 0,
|
|
6716
|
+
finalStep: steeringItems2.length === 0
|
|
6717
|
+
}
|
|
6404
6718
|
});
|
|
6405
6719
|
if (steeringItems2.length === 0) {
|
|
6406
6720
|
return { text: finalText, thoughts: finalThoughts, steps, totalCostUsd };
|
|
@@ -6533,25 +6847,33 @@ async function runToolLoop(request) {
|
|
|
6533
6847
|
const steeringInput = steeringInternal?.drainPendingContents() ?? [];
|
|
6534
6848
|
const steeringItems = steeringInput.length > 0 ? toChatGptInput(steeringInput).input : [];
|
|
6535
6849
|
stepCallLogger?.complete({
|
|
6536
|
-
|
|
6537
|
-
|
|
6538
|
-
|
|
6539
|
-
|
|
6540
|
-
|
|
6541
|
-
|
|
6542
|
-
|
|
6543
|
-
|
|
6544
|
-
|
|
6545
|
-
|
|
6850
|
+
responseText,
|
|
6851
|
+
toolCallText: stepToolCallText,
|
|
6852
|
+
metadata: {
|
|
6853
|
+
provider: "chatgpt",
|
|
6854
|
+
model: request.model,
|
|
6855
|
+
modelVersion,
|
|
6856
|
+
step: turn,
|
|
6857
|
+
usage: usageTokens,
|
|
6858
|
+
costUsd: stepCostUsd,
|
|
6859
|
+
responseChars: responseText.length,
|
|
6860
|
+
thoughtChars: reasoningSummaryText.length,
|
|
6861
|
+
toolCalls: toolCalls.length,
|
|
6862
|
+
finalStep: false
|
|
6863
|
+
}
|
|
6546
6864
|
});
|
|
6547
6865
|
input = steeringItems.length > 0 ? input.concat(toolOutputs, steeringItems) : input.concat(toolOutputs);
|
|
6548
6866
|
} catch (error) {
|
|
6549
6867
|
stepCallLogger?.fail(error, {
|
|
6550
|
-
|
|
6551
|
-
|
|
6552
|
-
|
|
6553
|
-
|
|
6554
|
-
|
|
6868
|
+
responseText,
|
|
6869
|
+
toolCallText: stepToolCallText,
|
|
6870
|
+
metadata: {
|
|
6871
|
+
provider: "chatgpt",
|
|
6872
|
+
model: request.model,
|
|
6873
|
+
modelVersion,
|
|
6874
|
+
step: turn,
|
|
6875
|
+
usage: usageTokens
|
|
6876
|
+
}
|
|
6555
6877
|
});
|
|
6556
6878
|
throw error;
|
|
6557
6879
|
}
|
|
@@ -6574,6 +6896,7 @@ async function runToolLoop(request) {
|
|
|
6574
6896
|
let usageTokens;
|
|
6575
6897
|
let responseText = "";
|
|
6576
6898
|
let blocked = false;
|
|
6899
|
+
let stepToolCallText;
|
|
6577
6900
|
const stepRequestPayload = {
|
|
6578
6901
|
model: providerInfo.model,
|
|
6579
6902
|
messages,
|
|
@@ -6638,6 +6961,14 @@ async function runToolLoop(request) {
|
|
|
6638
6961
|
});
|
|
6639
6962
|
}
|
|
6640
6963
|
const responseToolCalls = extractFireworksToolCalls(message);
|
|
6964
|
+
stepToolCallText = serialiseOpenAiStyleToolCallsForLogging(
|
|
6965
|
+
responseToolCalls.map((call) => ({
|
|
6966
|
+
kind: "function",
|
|
6967
|
+
name: call.name,
|
|
6968
|
+
arguments: call.arguments,
|
|
6969
|
+
callId: call.id
|
|
6970
|
+
}))
|
|
6971
|
+
);
|
|
6641
6972
|
if (responseToolCalls.length === 0) {
|
|
6642
6973
|
const steeringInput2 = steeringInternal?.drainPendingContents() ?? [];
|
|
6643
6974
|
const steeringMessages = steeringInput2.length > 0 ? toFireworksMessages(steeringInput2) : [];
|
|
@@ -6663,17 +6994,20 @@ async function runToolLoop(request) {
|
|
|
6663
6994
|
timing: timing2
|
|
6664
6995
|
});
|
|
6665
6996
|
stepCallLogger?.complete({
|
|
6666
|
-
|
|
6667
|
-
|
|
6668
|
-
|
|
6669
|
-
|
|
6670
|
-
|
|
6671
|
-
|
|
6672
|
-
|
|
6673
|
-
|
|
6674
|
-
|
|
6675
|
-
|
|
6676
|
-
|
|
6997
|
+
responseText,
|
|
6998
|
+
metadata: {
|
|
6999
|
+
provider: "fireworks",
|
|
7000
|
+
model: request.model,
|
|
7001
|
+
modelVersion,
|
|
7002
|
+
step: turn,
|
|
7003
|
+
usage: usageTokens,
|
|
7004
|
+
costUsd: stepCostUsd,
|
|
7005
|
+
blocked,
|
|
7006
|
+
responseChars: responseText.length,
|
|
7007
|
+
thoughtChars: 0,
|
|
7008
|
+
toolCalls: 0,
|
|
7009
|
+
finalStep: steeringMessages.length === 0
|
|
7010
|
+
}
|
|
6677
7011
|
});
|
|
6678
7012
|
if (steeringMessages.length === 0) {
|
|
6679
7013
|
return { text: finalText, thoughts: finalThoughts, steps, totalCostUsd };
|
|
@@ -6785,17 +7119,21 @@ async function runToolLoop(request) {
|
|
|
6785
7119
|
timing
|
|
6786
7120
|
});
|
|
6787
7121
|
stepCallLogger?.complete({
|
|
6788
|
-
|
|
6789
|
-
|
|
6790
|
-
|
|
6791
|
-
|
|
6792
|
-
|
|
6793
|
-
|
|
6794
|
-
|
|
6795
|
-
|
|
6796
|
-
|
|
6797
|
-
|
|
6798
|
-
|
|
7122
|
+
responseText,
|
|
7123
|
+
toolCallText: stepToolCallText,
|
|
7124
|
+
metadata: {
|
|
7125
|
+
provider: "fireworks",
|
|
7126
|
+
model: request.model,
|
|
7127
|
+
modelVersion,
|
|
7128
|
+
step: turn,
|
|
7129
|
+
usage: usageTokens,
|
|
7130
|
+
costUsd: stepCostUsd,
|
|
7131
|
+
blocked,
|
|
7132
|
+
responseChars: responseText.length,
|
|
7133
|
+
thoughtChars: 0,
|
|
7134
|
+
toolCalls: stepToolCalls.length,
|
|
7135
|
+
finalStep: false
|
|
7136
|
+
}
|
|
6799
7137
|
});
|
|
6800
7138
|
messages.push({
|
|
6801
7139
|
role: "assistant",
|
|
@@ -6809,12 +7147,16 @@ async function runToolLoop(request) {
|
|
|
6809
7147
|
}
|
|
6810
7148
|
} catch (error) {
|
|
6811
7149
|
stepCallLogger?.fail(error, {
|
|
6812
|
-
|
|
6813
|
-
|
|
6814
|
-
|
|
6815
|
-
|
|
6816
|
-
|
|
6817
|
-
|
|
7150
|
+
responseText,
|
|
7151
|
+
toolCallText: stepToolCallText,
|
|
7152
|
+
metadata: {
|
|
7153
|
+
provider: "fireworks",
|
|
7154
|
+
model: request.model,
|
|
7155
|
+
modelVersion,
|
|
7156
|
+
step: turn,
|
|
7157
|
+
usage: usageTokens,
|
|
7158
|
+
blocked
|
|
7159
|
+
}
|
|
6818
7160
|
});
|
|
6819
7161
|
throw error;
|
|
6820
7162
|
}
|
|
@@ -6834,6 +7176,7 @@ async function runToolLoop(request) {
|
|
|
6834
7176
|
let usageTokens;
|
|
6835
7177
|
let responseText = "";
|
|
6836
7178
|
let thoughtsText = "";
|
|
7179
|
+
let stepToolCallText;
|
|
6837
7180
|
const markFirstModelEvent = () => {
|
|
6838
7181
|
if (firstModelEventAtMs === void 0) {
|
|
6839
7182
|
firstModelEventAtMs = Date.now();
|
|
@@ -6954,12 +7297,17 @@ async function runToolLoop(request) {
|
|
|
6954
7297
|
modelVersion = response.modelVersion ?? request.model;
|
|
6955
7298
|
responseText = response.responseText.trim();
|
|
6956
7299
|
thoughtsText = response.thoughtsText.trim();
|
|
7300
|
+
const responseOutputAttachments = collectLoggedAttachmentsFromGeminiParts(
|
|
7301
|
+
response.modelParts,
|
|
7302
|
+
"output"
|
|
7303
|
+
);
|
|
6957
7304
|
const stepCostUsd = estimateCallCostUsd({
|
|
6958
7305
|
modelId: modelVersion,
|
|
6959
7306
|
tokens: usageTokens,
|
|
6960
7307
|
responseImages: 0
|
|
6961
7308
|
});
|
|
6962
7309
|
totalCostUsd += stepCostUsd;
|
|
7310
|
+
stepToolCallText = serialiseGeminiToolCallsForLogging(response.functionCalls);
|
|
6963
7311
|
if (response.functionCalls.length === 0) {
|
|
6964
7312
|
const steeringInput2 = steeringInternal?.drainPendingContents() ?? [];
|
|
6965
7313
|
finalText = responseText;
|
|
@@ -6985,16 +7333,20 @@ async function runToolLoop(request) {
|
|
|
6985
7333
|
timing: timing2
|
|
6986
7334
|
});
|
|
6987
7335
|
stepCallLogger?.complete({
|
|
6988
|
-
|
|
6989
|
-
|
|
6990
|
-
|
|
6991
|
-
|
|
6992
|
-
|
|
6993
|
-
|
|
6994
|
-
|
|
6995
|
-
|
|
6996
|
-
|
|
6997
|
-
|
|
7336
|
+
responseText,
|
|
7337
|
+
attachments: responseOutputAttachments,
|
|
7338
|
+
metadata: {
|
|
7339
|
+
provider: "gemini",
|
|
7340
|
+
model: request.model,
|
|
7341
|
+
modelVersion,
|
|
7342
|
+
step: turn,
|
|
7343
|
+
usage: usageTokens,
|
|
7344
|
+
costUsd: stepCostUsd,
|
|
7345
|
+
responseChars: responseText.length,
|
|
7346
|
+
thoughtChars: thoughtsText.length,
|
|
7347
|
+
toolCalls: 0,
|
|
7348
|
+
finalStep: steeringInput2.length === 0
|
|
7349
|
+
}
|
|
6998
7350
|
});
|
|
6999
7351
|
if (steeringInput2.length === 0) {
|
|
7000
7352
|
return { text: finalText, thoughts: finalThoughts, steps, totalCostUsd };
|
|
@@ -7121,16 +7473,21 @@ async function runToolLoop(request) {
|
|
|
7121
7473
|
timing
|
|
7122
7474
|
});
|
|
7123
7475
|
stepCallLogger?.complete({
|
|
7124
|
-
|
|
7125
|
-
|
|
7126
|
-
|
|
7127
|
-
|
|
7128
|
-
|
|
7129
|
-
|
|
7130
|
-
|
|
7131
|
-
|
|
7132
|
-
|
|
7133
|
-
|
|
7476
|
+
responseText,
|
|
7477
|
+
attachments: responseOutputAttachments,
|
|
7478
|
+
toolCallText: stepToolCallText,
|
|
7479
|
+
metadata: {
|
|
7480
|
+
provider: "gemini",
|
|
7481
|
+
model: request.model,
|
|
7482
|
+
modelVersion,
|
|
7483
|
+
step: turn,
|
|
7484
|
+
usage: usageTokens,
|
|
7485
|
+
costUsd: stepCostUsd,
|
|
7486
|
+
responseChars: responseText.length,
|
|
7487
|
+
thoughtChars: thoughtsText.length,
|
|
7488
|
+
toolCalls: toolCalls.length,
|
|
7489
|
+
finalStep: false
|
|
7490
|
+
}
|
|
7134
7491
|
});
|
|
7135
7492
|
geminiContents.push({ role: "user", parts: responseParts });
|
|
7136
7493
|
const steeringInput = steeringInternal?.drainPendingContents() ?? [];
|
|
@@ -7139,13 +7496,17 @@ async function runToolLoop(request) {
|
|
|
7139
7496
|
}
|
|
7140
7497
|
} catch (error) {
|
|
7141
7498
|
stepCallLogger?.fail(error, {
|
|
7142
|
-
|
|
7143
|
-
|
|
7144
|
-
|
|
7145
|
-
|
|
7146
|
-
|
|
7147
|
-
|
|
7148
|
-
|
|
7499
|
+
responseText,
|
|
7500
|
+
toolCallText: stepToolCallText,
|
|
7501
|
+
metadata: {
|
|
7502
|
+
provider: "gemini",
|
|
7503
|
+
model: request.model,
|
|
7504
|
+
modelVersion,
|
|
7505
|
+
step: turn,
|
|
7506
|
+
usage: usageTokens,
|
|
7507
|
+
responseChars: responseText.length,
|
|
7508
|
+
thoughtChars: thoughtsText.length
|
|
7509
|
+
}
|
|
7149
7510
|
});
|
|
7150
7511
|
throw error;
|
|
7151
7512
|
}
|