@ljoukov/llm 2.1.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +92 -49
- package/dist/index.cjs +1259 -257
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +92 -36
- package/dist/index.d.ts +92 -36
- package/dist/index.js +1246 -256
- package/dist/index.js.map +1 -1
- package/package.json +3 -1
package/dist/index.js
CHANGED
|
@@ -73,6 +73,35 @@ function createAsyncQueue() {
|
|
|
73
73
|
return { push, close, fail, iterable: iterator() };
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
+
// src/fireworks/pricing.ts
|
|
77
|
+
var FIREWORKS_KIMI_K25_PRICING = {
|
|
78
|
+
inputRate: 0.6 / 1e6,
|
|
79
|
+
cachedRate: 0.1 / 1e6,
|
|
80
|
+
outputRate: 3 / 1e6
|
|
81
|
+
};
|
|
82
|
+
var FIREWORKS_GLM_5_PRICING = {
|
|
83
|
+
inputRate: 1 / 1e6,
|
|
84
|
+
cachedRate: 0.2 / 1e6,
|
|
85
|
+
outputRate: 3.2 / 1e6
|
|
86
|
+
};
|
|
87
|
+
var FIREWORKS_MINIMAX_M21_PRICING = {
|
|
88
|
+
inputRate: 0.3 / 1e6,
|
|
89
|
+
cachedRate: 0.15 / 1e6,
|
|
90
|
+
outputRate: 1.2 / 1e6
|
|
91
|
+
};
|
|
92
|
+
function getFireworksPricing(modelId) {
|
|
93
|
+
if (modelId.includes("kimi-k2.5") || modelId.includes("kimi-k2p5")) {
|
|
94
|
+
return FIREWORKS_KIMI_K25_PRICING;
|
|
95
|
+
}
|
|
96
|
+
if (modelId.includes("glm-5")) {
|
|
97
|
+
return FIREWORKS_GLM_5_PRICING;
|
|
98
|
+
}
|
|
99
|
+
if (modelId.includes("minimax-m2.1") || modelId.includes("minimax-m2p1")) {
|
|
100
|
+
return FIREWORKS_MINIMAX_M21_PRICING;
|
|
101
|
+
}
|
|
102
|
+
return void 0;
|
|
103
|
+
}
|
|
104
|
+
|
|
76
105
|
// src/google/pricing.ts
|
|
77
106
|
var GEMINI_3_PRO_PREVIEW_PRICING = {
|
|
78
107
|
threshold: 2e5,
|
|
@@ -130,12 +159,15 @@ var OPENAI_GPT_53_CODEX_PRICING = {
|
|
|
130
159
|
cachedRate: 0.125 / 1e6,
|
|
131
160
|
outputRate: 10 / 1e6
|
|
132
161
|
};
|
|
133
|
-
var
|
|
162
|
+
var OPENAI_GPT_5_MINI_PRICING = {
|
|
134
163
|
inputRate: 0.25 / 1e6,
|
|
135
164
|
cachedRate: 0.025 / 1e6,
|
|
136
165
|
outputRate: 2 / 1e6
|
|
137
166
|
};
|
|
138
167
|
function getOpenAiPricing(modelId) {
|
|
168
|
+
if (modelId.includes("gpt-5.3-codex-spark")) {
|
|
169
|
+
return OPENAI_GPT_5_MINI_PRICING;
|
|
170
|
+
}
|
|
139
171
|
if (modelId.includes("gpt-5.3-codex")) {
|
|
140
172
|
return OPENAI_GPT_53_CODEX_PRICING;
|
|
141
173
|
}
|
|
@@ -145,8 +177,11 @@ function getOpenAiPricing(modelId) {
|
|
|
145
177
|
if (modelId.includes("gpt-5.2")) {
|
|
146
178
|
return OPENAI_GPT_52_PRICING;
|
|
147
179
|
}
|
|
180
|
+
if (modelId.includes("gpt-5-mini")) {
|
|
181
|
+
return OPENAI_GPT_5_MINI_PRICING;
|
|
182
|
+
}
|
|
148
183
|
if (modelId.includes("gpt-5.1-codex-mini")) {
|
|
149
|
-
return
|
|
184
|
+
return OPENAI_GPT_5_MINI_PRICING;
|
|
150
185
|
}
|
|
151
186
|
return void 0;
|
|
152
187
|
}
|
|
@@ -207,6 +242,14 @@ function estimateCallCostUsd({
|
|
|
207
242
|
const outputCost = outputTokens * outputRate;
|
|
208
243
|
return inputCost + cachedCost + outputCost;
|
|
209
244
|
}
|
|
245
|
+
const fireworksPricing = getFireworksPricing(modelId);
|
|
246
|
+
if (fireworksPricing) {
|
|
247
|
+
const inputCost = nonCachedPrompt * fireworksPricing.inputRate;
|
|
248
|
+
const cachedCost = cachedTokens * fireworksPricing.cachedRate;
|
|
249
|
+
const outputTokens = responseTokens + thinkingTokens;
|
|
250
|
+
const outputCost = outputTokens * fireworksPricing.outputRate;
|
|
251
|
+
return inputCost + cachedCost + outputCost;
|
|
252
|
+
}
|
|
210
253
|
const openAiPricing = getOpenAiPricing(modelId);
|
|
211
254
|
if (openAiPricing) {
|
|
212
255
|
const inputCost = nonCachedPrompt * openAiPricing.inputRate;
|
|
@@ -219,11 +262,14 @@ function estimateCallCostUsd({
|
|
|
219
262
|
}
|
|
220
263
|
|
|
221
264
|
// src/openai/chatgpt-codex.ts
|
|
222
|
-
import
|
|
265
|
+
import os2 from "os";
|
|
223
266
|
import { TextDecoder } from "util";
|
|
224
267
|
|
|
225
268
|
// src/openai/chatgpt-auth.ts
|
|
226
269
|
import { Buffer as Buffer2 } from "buffer";
|
|
270
|
+
import fs2 from "fs";
|
|
271
|
+
import os from "os";
|
|
272
|
+
import path2 from "path";
|
|
227
273
|
import { z } from "zod";
|
|
228
274
|
|
|
229
275
|
// src/utils/env.ts
|
|
@@ -288,34 +334,30 @@ function parseEnvLine(line) {
|
|
|
288
334
|
}
|
|
289
335
|
|
|
290
336
|
// src/openai/chatgpt-auth.ts
|
|
291
|
-
var
|
|
292
|
-
var
|
|
293
|
-
var
|
|
294
|
-
var
|
|
295
|
-
var
|
|
296
|
-
var
|
|
297
|
-
var CHATGPT_ID_TOKEN_ENV = "CHATGPT_ID_TOKEN";
|
|
298
|
-
var CHATGPT_ACCESS_TOKEN_ENV = "CHATGPT_ACCESS_TOKEN";
|
|
299
|
-
var CHATGPT_REFRESH_TOKEN_ENV = "CHATGPT_REFRESH_TOKEN";
|
|
300
|
-
var CHATGPT_EXPIRES_AT_ENV = "CHATGPT_EXPIRES_AT";
|
|
337
|
+
var CHATGPT_AUTH_TOKEN_PROVIDER_URL_ENV = "CHATGPT_AUTH_TOKEN_PROVIDER_URL";
|
|
338
|
+
var CHATGPT_AUTH_TOKEN_PROVIDER_STORE_ENV = "CHATGPT_AUTH_TOKEN_PROVIDER_STORE";
|
|
339
|
+
var CHATGPT_AUTH_SERVER_URL_ENV = "CHATGPT_AUTH_SERVER_URL";
|
|
340
|
+
var CHATGPT_AUTH_SERVER_STORE_ENV = "CHATGPT_AUTH_SERVER_STORE";
|
|
341
|
+
var CHATGPT_AUTH_API_KEY_ENV = "CHATGPT_AUTH_API_KEY";
|
|
342
|
+
var CHATGPT_AUTH_TOKEN_PROVIDER_API_KEY_ENV = "CHATGPT_AUTH_TOKEN_PROVIDER_API_KEY";
|
|
301
343
|
var CHATGPT_OAUTH_CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann";
|
|
302
344
|
var CHATGPT_OAUTH_TOKEN_URL = "https://auth.openai.com/oauth/token";
|
|
303
345
|
var CHATGPT_OAUTH_REDIRECT_URI = "http://localhost:1455/auth/callback";
|
|
304
346
|
var TOKEN_EXPIRY_BUFFER_MS = 3e4;
|
|
305
|
-
var
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
347
|
+
var CodexAuthFileSchema = z.object({
|
|
348
|
+
OPENAI_API_KEY: z.string().nullable().optional(),
|
|
349
|
+
last_refresh: z.string().optional(),
|
|
350
|
+
tokens: z.object({
|
|
351
|
+
access_token: z.string().min(1).optional(),
|
|
352
|
+
refresh_token: z.string().min(1).optional(),
|
|
353
|
+
id_token: z.string().min(1).optional(),
|
|
354
|
+
account_id: z.string().min(1).optional(),
|
|
355
|
+
// Allow a bit of flexibility if the file format changes.
|
|
356
|
+
accessToken: z.string().min(1).optional(),
|
|
357
|
+
refreshToken: z.string().min(1).optional(),
|
|
358
|
+
idToken: z.string().min(1).optional(),
|
|
359
|
+
accountId: z.string().min(1).optional()
|
|
360
|
+
}).optional()
|
|
319
361
|
}).loose();
|
|
320
362
|
var RefreshResponseSchema = z.object({
|
|
321
363
|
access_token: z.string().min(1),
|
|
@@ -330,6 +372,44 @@ var ExchangeResponseSchema = z.object({
|
|
|
330
372
|
});
|
|
331
373
|
var cachedProfile = null;
|
|
332
374
|
var refreshPromise = null;
|
|
375
|
+
async function fetchChatGptAuthProfileFromTokenProvider(options) {
|
|
376
|
+
const base = options.baseUrl.replace(/\/+$/u, "");
|
|
377
|
+
const store = options.store?.trim() ? options.store.trim() : "kv";
|
|
378
|
+
const url = new URL(`${base}/v1/token`);
|
|
379
|
+
url.searchParams.set("store", store);
|
|
380
|
+
const response = await fetch(url.toString(), {
|
|
381
|
+
method: "GET",
|
|
382
|
+
headers: {
|
|
383
|
+
Authorization: `Bearer ${options.apiKey}`,
|
|
384
|
+
"x-chatgpt-auth": options.apiKey,
|
|
385
|
+
Accept: "application/json"
|
|
386
|
+
}
|
|
387
|
+
});
|
|
388
|
+
if (!response.ok) {
|
|
389
|
+
const body = await response.text();
|
|
390
|
+
throw new Error(`ChatGPT token provider request failed (${response.status}): ${body}`);
|
|
391
|
+
}
|
|
392
|
+
const payload = await response.json();
|
|
393
|
+
if (!payload || typeof payload !== "object") {
|
|
394
|
+
throw new Error("ChatGPT token provider returned invalid JSON.");
|
|
395
|
+
}
|
|
396
|
+
const accessToken = payload.accessToken ?? payload.access_token;
|
|
397
|
+
const accountId = payload.accountId ?? payload.account_id;
|
|
398
|
+
const expiresAt = payload.expiresAt ?? payload.expires_at;
|
|
399
|
+
if (typeof accessToken !== "string" || accessToken.trim().length === 0) {
|
|
400
|
+
throw new Error("ChatGPT token provider response missing accessToken.");
|
|
401
|
+
}
|
|
402
|
+
if (typeof accountId !== "string" || accountId.trim().length === 0) {
|
|
403
|
+
throw new Error("ChatGPT token provider response missing accountId.");
|
|
404
|
+
}
|
|
405
|
+
const expires = normalizeEpochMillis(expiresAt) ?? Date.now() + 5 * 6e4;
|
|
406
|
+
return {
|
|
407
|
+
access: accessToken,
|
|
408
|
+
refresh: "token_provider",
|
|
409
|
+
expires,
|
|
410
|
+
accountId
|
|
411
|
+
};
|
|
412
|
+
}
|
|
333
413
|
function encodeChatGptAuthJson(profile) {
|
|
334
414
|
const payload = {
|
|
335
415
|
access: profile.access,
|
|
@@ -368,7 +448,7 @@ async function exchangeChatGptOauthCode({
|
|
|
368
448
|
const payload = ExchangeResponseSchema.parse(await response.json());
|
|
369
449
|
return profileFromTokenResponse(payload);
|
|
370
450
|
}
|
|
371
|
-
async function refreshChatGptOauthToken(refreshToken) {
|
|
451
|
+
async function refreshChatGptOauthToken(refreshToken, fallback) {
|
|
372
452
|
const params = new URLSearchParams();
|
|
373
453
|
params.set("grant_type", "refresh_token");
|
|
374
454
|
params.set("client_id", CHATGPT_OAUTH_CLIENT_ID);
|
|
@@ -385,9 +465,35 @@ async function refreshChatGptOauthToken(refreshToken) {
|
|
|
385
465
|
throw new Error(`ChatGPT OAuth refresh failed (${response.status}): ${body}`);
|
|
386
466
|
}
|
|
387
467
|
const payload = RefreshResponseSchema.parse(await response.json());
|
|
388
|
-
return profileFromTokenResponse(payload);
|
|
468
|
+
return profileFromTokenResponse(payload, fallback);
|
|
389
469
|
}
|
|
390
470
|
async function getChatGptAuthProfile() {
|
|
471
|
+
loadLocalEnv();
|
|
472
|
+
const tokenProviderUrl = process.env[CHATGPT_AUTH_TOKEN_PROVIDER_URL_ENV] ?? process.env[CHATGPT_AUTH_SERVER_URL_ENV];
|
|
473
|
+
const tokenProviderKey = process.env[CHATGPT_AUTH_TOKEN_PROVIDER_API_KEY_ENV] ?? process.env[CHATGPT_AUTH_API_KEY_ENV];
|
|
474
|
+
if (tokenProviderUrl && tokenProviderUrl.trim().length > 0 && tokenProviderKey && tokenProviderKey.trim().length > 0) {
|
|
475
|
+
if (cachedProfile && !isExpired(cachedProfile)) {
|
|
476
|
+
return cachedProfile;
|
|
477
|
+
}
|
|
478
|
+
if (refreshPromise) {
|
|
479
|
+
return refreshPromise;
|
|
480
|
+
}
|
|
481
|
+
refreshPromise = (async () => {
|
|
482
|
+
try {
|
|
483
|
+
const store = process.env[CHATGPT_AUTH_TOKEN_PROVIDER_STORE_ENV] ?? process.env[CHATGPT_AUTH_SERVER_STORE_ENV];
|
|
484
|
+
const profile = await fetchChatGptAuthProfileFromTokenProvider({
|
|
485
|
+
baseUrl: tokenProviderUrl,
|
|
486
|
+
apiKey: tokenProviderKey,
|
|
487
|
+
store: store ?? void 0
|
|
488
|
+
});
|
|
489
|
+
cachedProfile = profile;
|
|
490
|
+
return profile;
|
|
491
|
+
} finally {
|
|
492
|
+
refreshPromise = null;
|
|
493
|
+
}
|
|
494
|
+
})();
|
|
495
|
+
return refreshPromise;
|
|
496
|
+
}
|
|
391
497
|
if (cachedProfile && !isExpired(cachedProfile)) {
|
|
392
498
|
return cachedProfile;
|
|
393
499
|
}
|
|
@@ -396,8 +502,8 @@ async function getChatGptAuthProfile() {
|
|
|
396
502
|
}
|
|
397
503
|
refreshPromise = (async () => {
|
|
398
504
|
try {
|
|
399
|
-
const baseProfile = cachedProfile ??
|
|
400
|
-
const profile = isExpired(baseProfile) ? await
|
|
505
|
+
const baseProfile = cachedProfile ?? loadAuthProfileFromCodexStore();
|
|
506
|
+
const profile = isExpired(baseProfile) ? await refreshAndPersistCodexProfile(baseProfile) : baseProfile;
|
|
401
507
|
cachedProfile = profile;
|
|
402
508
|
return profile;
|
|
403
509
|
} finally {
|
|
@@ -406,39 +512,111 @@ async function getChatGptAuthProfile() {
|
|
|
406
512
|
})();
|
|
407
513
|
return refreshPromise;
|
|
408
514
|
}
|
|
409
|
-
function
|
|
410
|
-
const
|
|
411
|
-
|
|
515
|
+
function resolveCodexHome() {
|
|
516
|
+
const codexHome = process.env.CODEX_HOME;
|
|
517
|
+
if (codexHome && codexHome.trim().length > 0) {
|
|
518
|
+
return codexHome.trim();
|
|
519
|
+
}
|
|
520
|
+
return path2.join(os.homedir(), ".codex");
|
|
521
|
+
}
|
|
522
|
+
function resolveCodexAuthJsonPath() {
|
|
523
|
+
return path2.join(resolveCodexHome(), "auth.json");
|
|
524
|
+
}
|
|
525
|
+
function loadAuthProfileFromCodexStore() {
|
|
526
|
+
const authPath = resolveCodexAuthJsonPath();
|
|
527
|
+
let raw;
|
|
528
|
+
try {
|
|
529
|
+
raw = fs2.readFileSync(authPath, "utf8");
|
|
530
|
+
} catch {
|
|
531
|
+
throw new Error(
|
|
532
|
+
`ChatGPT auth not configured. Set ${CHATGPT_AUTH_TOKEN_PROVIDER_URL_ENV}+${CHATGPT_AUTH_API_KEY_ENV} or login via Codex to create ${authPath}.`
|
|
533
|
+
);
|
|
534
|
+
}
|
|
535
|
+
let parsed;
|
|
536
|
+
try {
|
|
537
|
+
parsed = CodexAuthFileSchema.parse(JSON.parse(raw));
|
|
538
|
+
} catch (e) {
|
|
539
|
+
throw new Error(
|
|
540
|
+
`Failed to parse Codex auth store at ${authPath}. (${e?.message ?? e})`
|
|
541
|
+
);
|
|
542
|
+
}
|
|
543
|
+
const tokens = parsed.tokens;
|
|
544
|
+
if (!tokens) {
|
|
545
|
+
throw new Error(
|
|
546
|
+
`Codex auth store at ${authPath} is missing tokens. Re-login via Codex, or configure ${CHATGPT_AUTH_TOKEN_PROVIDER_URL_ENV}.`
|
|
547
|
+
);
|
|
548
|
+
}
|
|
549
|
+
const access = tokens.access_token ?? tokens.accessToken ?? void 0;
|
|
550
|
+
const refresh = tokens.refresh_token ?? tokens.refreshToken ?? void 0;
|
|
551
|
+
const idToken = tokens.id_token ?? tokens.idToken ?? void 0;
|
|
552
|
+
if (!access || !refresh) {
|
|
553
|
+
throw new Error(
|
|
554
|
+
`Codex auth store at ${authPath} is missing access_token/refresh_token. Re-login via Codex, or configure ${CHATGPT_AUTH_TOKEN_PROVIDER_URL_ENV}.`
|
|
555
|
+
);
|
|
556
|
+
}
|
|
557
|
+
const expires = extractJwtExpiry(access) ?? extractJwtExpiry(idToken ?? "") ?? Date.now() + 5 * 6e4;
|
|
558
|
+
const accountId = tokens.account_id ?? tokens.accountId ?? extractChatGptAccountId(idToken ?? "") ?? extractChatGptAccountId(access);
|
|
412
559
|
if (!accountId) {
|
|
413
|
-
throw new Error(
|
|
560
|
+
throw new Error(`Codex auth store at ${authPath} is missing chatgpt_account_id/account_id.`);
|
|
414
561
|
}
|
|
415
562
|
return {
|
|
416
|
-
access
|
|
417
|
-
refresh
|
|
563
|
+
access,
|
|
564
|
+
refresh,
|
|
418
565
|
expires,
|
|
419
566
|
accountId,
|
|
420
|
-
idToken:
|
|
567
|
+
idToken: idToken ?? void 0
|
|
421
568
|
};
|
|
422
569
|
}
|
|
423
|
-
function
|
|
424
|
-
const
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
570
|
+
async function refreshAndPersistCodexProfile(baseProfile) {
|
|
571
|
+
const refreshed = await refreshChatGptOauthToken(baseProfile.refresh, {
|
|
572
|
+
accountId: baseProfile.accountId,
|
|
573
|
+
idToken: baseProfile.idToken
|
|
574
|
+
});
|
|
575
|
+
persistCodexTokens(refreshed);
|
|
576
|
+
return refreshed;
|
|
577
|
+
}
|
|
578
|
+
function persistCodexTokens(profile) {
|
|
579
|
+
const authPath = resolveCodexAuthJsonPath();
|
|
580
|
+
const codexHome = path2.dirname(authPath);
|
|
581
|
+
let doc = {};
|
|
582
|
+
try {
|
|
583
|
+
doc = JSON.parse(fs2.readFileSync(authPath, "utf8"));
|
|
584
|
+
} catch {
|
|
585
|
+
doc = {};
|
|
586
|
+
}
|
|
587
|
+
if (!doc || typeof doc !== "object") {
|
|
588
|
+
doc = {};
|
|
428
589
|
}
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
590
|
+
if (!doc.tokens || typeof doc.tokens !== "object") {
|
|
591
|
+
doc.tokens = {};
|
|
592
|
+
}
|
|
593
|
+
doc.tokens.access_token = profile.access;
|
|
594
|
+
doc.tokens.refresh_token = profile.refresh;
|
|
595
|
+
doc.tokens.account_id = profile.accountId;
|
|
596
|
+
if (profile.idToken) {
|
|
597
|
+
doc.tokens.id_token = profile.idToken;
|
|
598
|
+
}
|
|
599
|
+
doc.last_refresh = (/* @__PURE__ */ new Date()).toISOString();
|
|
600
|
+
fs2.mkdirSync(codexHome, { recursive: true, mode: 448 });
|
|
601
|
+
const tmpPath = `${authPath}.tmp.${process.pid}.${Math.random().toString(16).slice(2)}`;
|
|
602
|
+
fs2.writeFileSync(tmpPath, `${JSON.stringify(doc, null, 2)}
|
|
603
|
+
`, { mode: 384 });
|
|
604
|
+
fs2.renameSync(tmpPath, authPath);
|
|
605
|
+
}
|
|
606
|
+
function profileFromTokenResponse(payload, fallback) {
|
|
607
|
+
const expires = Date.now() + normalizeNumber(payload.expires_in) * 1e3;
|
|
608
|
+
const fallbackAccountId = fallback?.accountId;
|
|
609
|
+
const fallbackIdToken = fallback?.idToken;
|
|
610
|
+
const accountId = extractChatGptAccountId(payload.id_token ?? "") ?? extractChatGptAccountId(payload.access_token) ?? fallbackAccountId;
|
|
433
611
|
if (!accountId) {
|
|
434
|
-
throw new Error("
|
|
612
|
+
throw new Error("Failed to extract chatgpt_account_id from access token.");
|
|
435
613
|
}
|
|
436
614
|
return {
|
|
437
|
-
access,
|
|
438
|
-
refresh,
|
|
615
|
+
access: payload.access_token,
|
|
616
|
+
refresh: payload.refresh_token,
|
|
439
617
|
expires,
|
|
440
618
|
accountId,
|
|
441
|
-
idToken:
|
|
619
|
+
idToken: payload.id_token ?? fallbackIdToken
|
|
442
620
|
};
|
|
443
621
|
}
|
|
444
622
|
function normalizeEpochMillis(value) {
|
|
@@ -467,31 +645,6 @@ function isExpired(profile) {
|
|
|
467
645
|
}
|
|
468
646
|
return Date.now() + TOKEN_EXPIRY_BUFFER_MS >= expires;
|
|
469
647
|
}
|
|
470
|
-
function loadAuthProfileFromEnv() {
|
|
471
|
-
loadLocalEnv();
|
|
472
|
-
const rawJson = process.env[CHATGPT_AUTH_JSON_ENV];
|
|
473
|
-
if (rawJson && rawJson.trim().length > 0) {
|
|
474
|
-
return normalizeAuthProfile(AuthInputSchema.parse(JSON.parse(rawJson)));
|
|
475
|
-
}
|
|
476
|
-
const rawB64 = process.env[CHATGPT_AUTH_JSON_B64_ENV];
|
|
477
|
-
if (rawB64 && rawB64.trim().length > 0) {
|
|
478
|
-
const decoded = Buffer2.from(rawB64.trim(), "base64url").toString("utf8");
|
|
479
|
-
return normalizeAuthProfile(AuthInputSchema.parse(JSON.parse(decoded)));
|
|
480
|
-
}
|
|
481
|
-
const access = process.env[CHATGPT_ACCESS_ENV] ?? process.env[CHATGPT_ACCESS_TOKEN_ENV] ?? void 0;
|
|
482
|
-
const refresh = process.env[CHATGPT_REFRESH_ENV] ?? process.env[CHATGPT_REFRESH_TOKEN_ENV] ?? void 0;
|
|
483
|
-
const expires = process.env[CHATGPT_EXPIRES_ENV] ?? process.env[CHATGPT_EXPIRES_AT_ENV] ?? void 0;
|
|
484
|
-
const accountId = process.env[CHATGPT_ACCOUNT_ID_ENV] ?? void 0;
|
|
485
|
-
const idToken = process.env[CHATGPT_ID_TOKEN_ENV] ?? void 0;
|
|
486
|
-
const parsed = AuthInputSchema.parse({
|
|
487
|
-
access,
|
|
488
|
-
refresh,
|
|
489
|
-
expires,
|
|
490
|
-
accountId,
|
|
491
|
-
idToken
|
|
492
|
-
});
|
|
493
|
-
return normalizeAuthProfile(parsed);
|
|
494
|
-
}
|
|
495
648
|
function decodeJwtPayload(token) {
|
|
496
649
|
const segments = token.split(".");
|
|
497
650
|
if (segments.length < 2) {
|
|
@@ -522,8 +675,12 @@ function extractChatGptAccountId(token) {
|
|
|
522
675
|
if (!payload || typeof payload !== "object") {
|
|
523
676
|
return void 0;
|
|
524
677
|
}
|
|
525
|
-
const
|
|
526
|
-
|
|
678
|
+
const direct = payload.chatgpt_account_id;
|
|
679
|
+
if (typeof direct === "string" && direct.length > 0) {
|
|
680
|
+
return direct;
|
|
681
|
+
}
|
|
682
|
+
const namespaced = payload["https://api.openai.com/auth"]?.chatgpt_account_id;
|
|
683
|
+
return typeof namespaced === "string" && namespaced.length > 0 ? namespaced : void 0;
|
|
527
684
|
}
|
|
528
685
|
|
|
529
686
|
// src/openai/chatgpt-codex.ts
|
|
@@ -559,7 +716,19 @@ async function streamChatGptCodexResponse(options) {
|
|
|
559
716
|
return parseEventStream(body);
|
|
560
717
|
}
|
|
561
718
|
async function collectChatGptCodexResponse(options) {
|
|
562
|
-
|
|
719
|
+
let stream;
|
|
720
|
+
try {
|
|
721
|
+
stream = await streamChatGptCodexResponse(options);
|
|
722
|
+
} catch (error) {
|
|
723
|
+
if (shouldRetryWithoutReasoningSummary(options.request, error)) {
|
|
724
|
+
stream = await streamChatGptCodexResponse({
|
|
725
|
+
...options,
|
|
726
|
+
request: removeReasoningSummary(options.request)
|
|
727
|
+
});
|
|
728
|
+
} else {
|
|
729
|
+
throw error;
|
|
730
|
+
}
|
|
731
|
+
}
|
|
563
732
|
const toolCalls = /* @__PURE__ */ new Map();
|
|
564
733
|
const toolCallOrder = [];
|
|
565
734
|
const webSearchCalls = /* @__PURE__ */ new Map();
|
|
@@ -568,6 +737,7 @@ async function collectChatGptCodexResponse(options) {
|
|
|
568
737
|
const reasoningText = "";
|
|
569
738
|
let reasoningSummaryText = "";
|
|
570
739
|
let usage;
|
|
740
|
+
let responseId;
|
|
571
741
|
let model;
|
|
572
742
|
let status;
|
|
573
743
|
let blocked = false;
|
|
@@ -608,7 +778,18 @@ async function collectChatGptCodexResponse(options) {
|
|
|
608
778
|
if (!toolCalls.has(callId)) {
|
|
609
779
|
toolCallOrder.push(callId);
|
|
610
780
|
}
|
|
611
|
-
toolCalls.set(callId, { id, callId, name, arguments: args });
|
|
781
|
+
toolCalls.set(callId, { kind: "function", id, callId, name, arguments: args });
|
|
782
|
+
}
|
|
783
|
+
} else if (item.type === "custom_tool_call") {
|
|
784
|
+
const id = typeof item.id === "string" ? item.id : "";
|
|
785
|
+
const callId = typeof item.call_id === "string" ? item.call_id : id;
|
|
786
|
+
const name = typeof item.name === "string" ? item.name : "";
|
|
787
|
+
const input = typeof item.input === "string" ? item.input : "";
|
|
788
|
+
if (callId) {
|
|
789
|
+
if (!toolCalls.has(callId)) {
|
|
790
|
+
toolCallOrder.push(callId);
|
|
791
|
+
}
|
|
792
|
+
toolCalls.set(callId, { kind: "custom", id, callId, name, input });
|
|
612
793
|
}
|
|
613
794
|
} else if (item.type === "web_search_call") {
|
|
614
795
|
const id = typeof item.id === "string" ? item.id : "";
|
|
@@ -630,6 +811,7 @@ async function collectChatGptCodexResponse(options) {
|
|
|
630
811
|
const response = event.response;
|
|
631
812
|
if (response) {
|
|
632
813
|
usage = response.usage;
|
|
814
|
+
responseId = typeof response.id === "string" ? response.id : responseId;
|
|
633
815
|
model = typeof response.model === "string" ? response.model : void 0;
|
|
634
816
|
status = typeof response.status === "string" ? response.status : void 0;
|
|
635
817
|
}
|
|
@@ -639,6 +821,7 @@ async function collectChatGptCodexResponse(options) {
|
|
|
639
821
|
const response = event.response;
|
|
640
822
|
if (response) {
|
|
641
823
|
usage = response.usage;
|
|
824
|
+
responseId = typeof response.id === "string" ? response.id : responseId;
|
|
642
825
|
model = typeof response.model === "string" ? response.model : void 0;
|
|
643
826
|
status = typeof response.status === "string" ? response.status : void 0;
|
|
644
827
|
}
|
|
@@ -648,6 +831,7 @@ async function collectChatGptCodexResponse(options) {
|
|
|
648
831
|
const response = event.response;
|
|
649
832
|
if (response) {
|
|
650
833
|
usage = response.usage;
|
|
834
|
+
responseId = typeof response.id === "string" ? response.id : responseId;
|
|
651
835
|
model = typeof response.model === "string" ? response.model : void 0;
|
|
652
836
|
status = typeof response.status === "string" ? response.status : void 0;
|
|
653
837
|
}
|
|
@@ -665,15 +849,38 @@ async function collectChatGptCodexResponse(options) {
|
|
|
665
849
|
toolCalls: orderedToolCalls,
|
|
666
850
|
webSearchCalls: orderedWebSearchCalls,
|
|
667
851
|
usage,
|
|
852
|
+
id: responseId,
|
|
668
853
|
model,
|
|
669
854
|
status,
|
|
670
855
|
blocked
|
|
671
856
|
};
|
|
672
857
|
}
|
|
858
|
+
function shouldRetryWithoutReasoningSummary(request, error) {
|
|
859
|
+
if (!request.reasoning?.summary) {
|
|
860
|
+
return false;
|
|
861
|
+
}
|
|
862
|
+
if (!(error instanceof Error)) {
|
|
863
|
+
return false;
|
|
864
|
+
}
|
|
865
|
+
const message = error.message.toLowerCase();
|
|
866
|
+
return message.includes("unsupported parameter") && message.includes("reasoning.summary");
|
|
867
|
+
}
|
|
868
|
+
function removeReasoningSummary(request) {
|
|
869
|
+
const reasoning = request.reasoning;
|
|
870
|
+
if (!reasoning?.summary) {
|
|
871
|
+
return request;
|
|
872
|
+
}
|
|
873
|
+
return {
|
|
874
|
+
...request,
|
|
875
|
+
reasoning: {
|
|
876
|
+
effort: reasoning.effort
|
|
877
|
+
}
|
|
878
|
+
};
|
|
879
|
+
}
|
|
673
880
|
function buildUserAgent() {
|
|
674
881
|
const node = process.version;
|
|
675
|
-
const platform =
|
|
676
|
-
const release =
|
|
882
|
+
const platform = os2.platform();
|
|
883
|
+
const release = os2.release();
|
|
677
884
|
return `@ljoukov/llm (node ${node}; ${platform} ${release})`;
|
|
678
885
|
}
|
|
679
886
|
async function* parseEventStream(stream) {
|
|
@@ -821,6 +1028,110 @@ function createCallScheduler(options = {}) {
|
|
|
821
1028
|
return { run };
|
|
822
1029
|
}
|
|
823
1030
|
|
|
1031
|
+
// src/fireworks/client.ts
|
|
1032
|
+
import OpenAI from "openai";
|
|
1033
|
+
import { Agent, fetch as undiciFetch } from "undici";
|
|
1034
|
+
var DEFAULT_FIREWORKS_BASE_URL = "https://api.fireworks.ai/inference/v1";
|
|
1035
|
+
var DEFAULT_FIREWORKS_TIMEOUT_MS = 15 * 6e4;
|
|
1036
|
+
var cachedClient = null;
|
|
1037
|
+
var cachedFetch = null;
|
|
1038
|
+
var cachedBaseUrl = null;
|
|
1039
|
+
var cachedApiKey = null;
|
|
1040
|
+
var cachedTimeoutMs = null;
|
|
1041
|
+
function resolveTimeoutMs() {
|
|
1042
|
+
if (cachedTimeoutMs !== null) {
|
|
1043
|
+
return cachedTimeoutMs;
|
|
1044
|
+
}
|
|
1045
|
+
const raw = process.env.FIREWORKS_TIMEOUT_MS;
|
|
1046
|
+
const parsed = raw ? Number(raw) : Number.NaN;
|
|
1047
|
+
cachedTimeoutMs = Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_FIREWORKS_TIMEOUT_MS;
|
|
1048
|
+
return cachedTimeoutMs;
|
|
1049
|
+
}
|
|
1050
|
+
function resolveBaseUrl() {
|
|
1051
|
+
if (cachedBaseUrl !== null) {
|
|
1052
|
+
return cachedBaseUrl;
|
|
1053
|
+
}
|
|
1054
|
+
loadLocalEnv();
|
|
1055
|
+
const raw = process.env.FIREWORKS_BASE_URL?.trim();
|
|
1056
|
+
cachedBaseUrl = raw && raw.length > 0 ? raw : DEFAULT_FIREWORKS_BASE_URL;
|
|
1057
|
+
return cachedBaseUrl;
|
|
1058
|
+
}
|
|
1059
|
+
function resolveApiKey() {
|
|
1060
|
+
if (cachedApiKey !== null) {
|
|
1061
|
+
return cachedApiKey;
|
|
1062
|
+
}
|
|
1063
|
+
loadLocalEnv();
|
|
1064
|
+
const raw = process.env.FIREWORKS_TOKEN ?? process.env.FIREWORKS_API_KEY;
|
|
1065
|
+
const token = raw?.trim();
|
|
1066
|
+
if (!token) {
|
|
1067
|
+
throw new Error(
|
|
1068
|
+
"FIREWORKS_TOKEN (or FIREWORKS_API_KEY) must be provided to access Fireworks APIs."
|
|
1069
|
+
);
|
|
1070
|
+
}
|
|
1071
|
+
cachedApiKey = token;
|
|
1072
|
+
return cachedApiKey;
|
|
1073
|
+
}
|
|
1074
|
+
function getFireworksFetch() {
|
|
1075
|
+
if (cachedFetch) {
|
|
1076
|
+
return cachedFetch;
|
|
1077
|
+
}
|
|
1078
|
+
const timeoutMs = resolveTimeoutMs();
|
|
1079
|
+
const dispatcher = new Agent({
|
|
1080
|
+
bodyTimeout: timeoutMs,
|
|
1081
|
+
headersTimeout: timeoutMs
|
|
1082
|
+
});
|
|
1083
|
+
cachedFetch = ((input, init) => {
|
|
1084
|
+
return undiciFetch(input, {
|
|
1085
|
+
...init ?? {},
|
|
1086
|
+
dispatcher
|
|
1087
|
+
});
|
|
1088
|
+
});
|
|
1089
|
+
return cachedFetch;
|
|
1090
|
+
}
|
|
1091
|
+
function getFireworksClient() {
|
|
1092
|
+
if (cachedClient) {
|
|
1093
|
+
return cachedClient;
|
|
1094
|
+
}
|
|
1095
|
+
cachedClient = new OpenAI({
|
|
1096
|
+
apiKey: resolveApiKey(),
|
|
1097
|
+
baseURL: resolveBaseUrl(),
|
|
1098
|
+
timeout: resolveTimeoutMs(),
|
|
1099
|
+
fetch: getFireworksFetch()
|
|
1100
|
+
});
|
|
1101
|
+
return cachedClient;
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1104
|
+
// src/fireworks/calls.ts
|
|
1105
|
+
var scheduler = createCallScheduler({
|
|
1106
|
+
maxParallelRequests: 3,
|
|
1107
|
+
minIntervalBetweenStartMs: 200,
|
|
1108
|
+
startJitterMs: 200
|
|
1109
|
+
});
|
|
1110
|
+
async function runFireworksCall(fn) {
|
|
1111
|
+
return scheduler.run(async () => fn(getFireworksClient()));
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1114
|
+
// src/fireworks/models.ts
|
|
1115
|
+
var FIREWORKS_MODEL_IDS = ["kimi-k2.5", "glm-5", "minimax-m2.1"];
|
|
1116
|
+
var FIREWORKS_DEFAULT_KIMI_MODEL = "kimi-k2.5";
|
|
1117
|
+
var FIREWORKS_DEFAULT_GLM_MODEL = "glm-5";
|
|
1118
|
+
var FIREWORKS_DEFAULT_MINIMAX_MODEL = "minimax-m2.1";
|
|
1119
|
+
var FIREWORKS_CANONICAL_MODEL_IDS = {
|
|
1120
|
+
"kimi-k2.5": "accounts/fireworks/models/kimi-k2p5",
|
|
1121
|
+
"glm-5": "accounts/fireworks/models/glm-5",
|
|
1122
|
+
"minimax-m2.1": "accounts/fireworks/models/minimax-m2p1"
|
|
1123
|
+
};
|
|
1124
|
+
function isFireworksModelId(value) {
|
|
1125
|
+
return FIREWORKS_MODEL_IDS.includes(value.trim());
|
|
1126
|
+
}
|
|
1127
|
+
function resolveFireworksModelId(model) {
|
|
1128
|
+
const trimmed = model.trim();
|
|
1129
|
+
if (!isFireworksModelId(trimmed)) {
|
|
1130
|
+
return void 0;
|
|
1131
|
+
}
|
|
1132
|
+
return FIREWORKS_CANONICAL_MODEL_IDS[trimmed];
|
|
1133
|
+
}
|
|
1134
|
+
|
|
824
1135
|
// src/google/client.ts
|
|
825
1136
|
import { GoogleGenAI } from "@google/genai";
|
|
826
1137
|
|
|
@@ -889,6 +1200,7 @@ function getGoogleAuthOptions(scopes) {
|
|
|
889
1200
|
// src/google/client.ts
|
|
890
1201
|
var GEMINI_MODEL_IDS = [
|
|
891
1202
|
"gemini-3-pro-preview",
|
|
1203
|
+
"gemini-3-flash-preview",
|
|
892
1204
|
"gemini-2.5-pro",
|
|
893
1205
|
"gemini-flash-latest",
|
|
894
1206
|
"gemini-flash-lite-latest"
|
|
@@ -1128,7 +1440,7 @@ function retryDelayMs(attempt) {
|
|
|
1128
1440
|
const jitter = Math.floor(Math.random() * 200);
|
|
1129
1441
|
return base + jitter;
|
|
1130
1442
|
}
|
|
1131
|
-
var
|
|
1443
|
+
var scheduler2 = createCallScheduler({
|
|
1132
1444
|
maxParallelRequests: 3,
|
|
1133
1445
|
minIntervalBetweenStartMs: 200,
|
|
1134
1446
|
startJitterMs: 200,
|
|
@@ -1144,46 +1456,46 @@ var scheduler = createCallScheduler({
|
|
|
1144
1456
|
}
|
|
1145
1457
|
});
|
|
1146
1458
|
async function runGeminiCall(fn) {
|
|
1147
|
-
return
|
|
1459
|
+
return scheduler2.run(async () => fn(await getGeminiClient()));
|
|
1148
1460
|
}
|
|
1149
1461
|
|
|
1150
1462
|
// src/openai/client.ts
|
|
1151
|
-
import
|
|
1152
|
-
import { Agent, fetch as
|
|
1153
|
-
var
|
|
1154
|
-
var
|
|
1155
|
-
var
|
|
1156
|
-
var
|
|
1463
|
+
import OpenAI2 from "openai";
|
|
1464
|
+
import { Agent as Agent2, fetch as undiciFetch2 } from "undici";
|
|
1465
|
+
var cachedApiKey2 = null;
|
|
1466
|
+
var cachedClient2 = null;
|
|
1467
|
+
var cachedFetch2 = null;
|
|
1468
|
+
var cachedTimeoutMs2 = null;
|
|
1157
1469
|
var DEFAULT_OPENAI_TIMEOUT_MS = 15 * 6e4;
|
|
1158
1470
|
function resolveOpenAiTimeoutMs() {
|
|
1159
|
-
if (
|
|
1160
|
-
return
|
|
1471
|
+
if (cachedTimeoutMs2 !== null) {
|
|
1472
|
+
return cachedTimeoutMs2;
|
|
1161
1473
|
}
|
|
1162
1474
|
const raw = process.env.OPENAI_STREAM_TIMEOUT_MS ?? process.env.OPENAI_TIMEOUT_MS;
|
|
1163
1475
|
const parsed = raw ? Number(raw) : Number.NaN;
|
|
1164
|
-
|
|
1165
|
-
return
|
|
1476
|
+
cachedTimeoutMs2 = Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_OPENAI_TIMEOUT_MS;
|
|
1477
|
+
return cachedTimeoutMs2;
|
|
1166
1478
|
}
|
|
1167
1479
|
function getOpenAiFetch() {
|
|
1168
|
-
if (
|
|
1169
|
-
return
|
|
1480
|
+
if (cachedFetch2) {
|
|
1481
|
+
return cachedFetch2;
|
|
1170
1482
|
}
|
|
1171
1483
|
const timeoutMs = resolveOpenAiTimeoutMs();
|
|
1172
|
-
const dispatcher = new
|
|
1484
|
+
const dispatcher = new Agent2({
|
|
1173
1485
|
bodyTimeout: timeoutMs,
|
|
1174
1486
|
headersTimeout: timeoutMs
|
|
1175
1487
|
});
|
|
1176
|
-
|
|
1177
|
-
return
|
|
1488
|
+
cachedFetch2 = ((input, init) => {
|
|
1489
|
+
return undiciFetch2(input, {
|
|
1178
1490
|
...init ?? {},
|
|
1179
1491
|
dispatcher
|
|
1180
1492
|
});
|
|
1181
1493
|
});
|
|
1182
|
-
return
|
|
1494
|
+
return cachedFetch2;
|
|
1183
1495
|
}
|
|
1184
1496
|
function getOpenAiApiKey() {
|
|
1185
|
-
if (
|
|
1186
|
-
return
|
|
1497
|
+
if (cachedApiKey2 !== null) {
|
|
1498
|
+
return cachedApiKey2;
|
|
1187
1499
|
}
|
|
1188
1500
|
loadLocalEnv();
|
|
1189
1501
|
const raw = process.env.OPENAI_API_KEY;
|
|
@@ -1191,32 +1503,32 @@ function getOpenAiApiKey() {
|
|
|
1191
1503
|
if (!value) {
|
|
1192
1504
|
throw new Error("OPENAI_API_KEY must be provided to access OpenAI APIs.");
|
|
1193
1505
|
}
|
|
1194
|
-
|
|
1195
|
-
return
|
|
1506
|
+
cachedApiKey2 = value;
|
|
1507
|
+
return cachedApiKey2;
|
|
1196
1508
|
}
|
|
1197
1509
|
function getOpenAiClient() {
|
|
1198
|
-
if (
|
|
1199
|
-
return
|
|
1510
|
+
if (cachedClient2) {
|
|
1511
|
+
return cachedClient2;
|
|
1200
1512
|
}
|
|
1201
1513
|
const apiKey = getOpenAiApiKey();
|
|
1202
1514
|
const timeoutMs = resolveOpenAiTimeoutMs();
|
|
1203
|
-
|
|
1515
|
+
cachedClient2 = new OpenAI2({
|
|
1204
1516
|
apiKey,
|
|
1205
1517
|
fetch: getOpenAiFetch(),
|
|
1206
1518
|
timeout: timeoutMs
|
|
1207
1519
|
});
|
|
1208
|
-
return
|
|
1520
|
+
return cachedClient2;
|
|
1209
1521
|
}
|
|
1210
1522
|
|
|
1211
1523
|
// src/openai/calls.ts
|
|
1212
1524
|
var DEFAULT_OPENAI_REASONING_EFFORT = "medium";
|
|
1213
|
-
var
|
|
1525
|
+
var scheduler3 = createCallScheduler({
|
|
1214
1526
|
maxParallelRequests: 3,
|
|
1215
1527
|
minIntervalBetweenStartMs: 200,
|
|
1216
1528
|
startJitterMs: 200
|
|
1217
1529
|
});
|
|
1218
1530
|
async function runOpenAiCall(fn) {
|
|
1219
|
-
return
|
|
1531
|
+
return scheduler3.run(async () => fn(getOpenAiClient()));
|
|
1220
1532
|
}
|
|
1221
1533
|
|
|
1222
1534
|
// src/llm.ts
|
|
@@ -1232,7 +1544,16 @@ var LlmJsonCallError = class extends Error {
|
|
|
1232
1544
|
}
|
|
1233
1545
|
};
|
|
1234
1546
|
function tool(options) {
|
|
1235
|
-
return
|
|
1547
|
+
return {
|
|
1548
|
+
type: "function",
|
|
1549
|
+
...options
|
|
1550
|
+
};
|
|
1551
|
+
}
|
|
1552
|
+
function customTool(options) {
|
|
1553
|
+
return {
|
|
1554
|
+
type: "custom",
|
|
1555
|
+
...options
|
|
1556
|
+
};
|
|
1236
1557
|
}
|
|
1237
1558
|
function isPlainRecord(value) {
|
|
1238
1559
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -1578,6 +1899,10 @@ function resolveProvider(model) {
|
|
|
1578
1899
|
if (model.startsWith("gemini-")) {
|
|
1579
1900
|
return { provider: "gemini", model };
|
|
1580
1901
|
}
|
|
1902
|
+
const fireworksModel = resolveFireworksModelId(model);
|
|
1903
|
+
if (fireworksModel) {
|
|
1904
|
+
return { provider: "fireworks", model: fireworksModel };
|
|
1905
|
+
}
|
|
1581
1906
|
return { provider: "openai", model };
|
|
1582
1907
|
}
|
|
1583
1908
|
function isOpenAiCodexModel(modelId) {
|
|
@@ -1607,6 +1932,27 @@ function toOpenAiReasoningEffort(effort) {
|
|
|
1607
1932
|
function resolveOpenAiVerbosity(modelId) {
|
|
1608
1933
|
return isOpenAiCodexModel(modelId) ? "medium" : "high";
|
|
1609
1934
|
}
|
|
1935
|
+
function isRetryableChatGptTransportError(error) {
|
|
1936
|
+
if (!(error instanceof Error)) {
|
|
1937
|
+
return false;
|
|
1938
|
+
}
|
|
1939
|
+
const message = error.message.toLowerCase();
|
|
1940
|
+
return message === "terminated" || message.includes("socket hang up") || message.includes("fetch failed") || message.includes("network");
|
|
1941
|
+
}
|
|
1942
|
+
async function collectChatGptCodexResponseWithRetry(options, maxAttempts = 2) {
|
|
1943
|
+
let attempt = 1;
|
|
1944
|
+
while (true) {
|
|
1945
|
+
try {
|
|
1946
|
+
return await collectChatGptCodexResponse(options);
|
|
1947
|
+
} catch (error) {
|
|
1948
|
+
if (attempt >= maxAttempts || !isRetryableChatGptTransportError(error)) {
|
|
1949
|
+
throw error;
|
|
1950
|
+
}
|
|
1951
|
+
await new Promise((resolve) => setTimeout(resolve, 250 * attempt));
|
|
1952
|
+
attempt += 1;
|
|
1953
|
+
}
|
|
1954
|
+
}
|
|
1955
|
+
}
|
|
1610
1956
|
function isInlineImageMime(mimeType) {
|
|
1611
1957
|
if (!mimeType) {
|
|
1612
1958
|
return false;
|
|
@@ -2189,6 +2535,53 @@ function toChatGptInput(contents) {
|
|
|
2189
2535
|
input
|
|
2190
2536
|
};
|
|
2191
2537
|
}
|
|
2538
|
+
function toFireworksMessages(contents, options) {
|
|
2539
|
+
const systemMessages = [];
|
|
2540
|
+
const messages = [];
|
|
2541
|
+
if (options?.responseMimeType === "application/json") {
|
|
2542
|
+
systemMessages.push("Return valid JSON only. Do not include markdown or prose outside JSON.");
|
|
2543
|
+
}
|
|
2544
|
+
if (options?.responseJsonSchema) {
|
|
2545
|
+
systemMessages.push(`Target JSON schema:
|
|
2546
|
+
${JSON.stringify(options.responseJsonSchema)}`);
|
|
2547
|
+
}
|
|
2548
|
+
for (const content of contents) {
|
|
2549
|
+
const text = content.parts.map((part) => {
|
|
2550
|
+
if (part.type === "text") {
|
|
2551
|
+
return part.text;
|
|
2552
|
+
}
|
|
2553
|
+
const mimeType = part.mimeType ?? "application/octet-stream";
|
|
2554
|
+
if (isInlineImageMime(mimeType)) {
|
|
2555
|
+
return `[image:${mimeType}]`;
|
|
2556
|
+
}
|
|
2557
|
+
return `[file:${mimeType}]`;
|
|
2558
|
+
}).join("\n").trim();
|
|
2559
|
+
if (content.role === "system" || content.role === "developer") {
|
|
2560
|
+
if (text.length > 0) {
|
|
2561
|
+
systemMessages.push(text);
|
|
2562
|
+
}
|
|
2563
|
+
continue;
|
|
2564
|
+
}
|
|
2565
|
+
if (content.role === "tool" || content.role === "assistant") {
|
|
2566
|
+
messages.push({
|
|
2567
|
+
role: "assistant",
|
|
2568
|
+
content: text.length > 0 ? text : "(empty content)"
|
|
2569
|
+
});
|
|
2570
|
+
continue;
|
|
2571
|
+
}
|
|
2572
|
+
messages.push({
|
|
2573
|
+
role: "user",
|
|
2574
|
+
content: text.length > 0 ? text : "(empty content)"
|
|
2575
|
+
});
|
|
2576
|
+
}
|
|
2577
|
+
if (systemMessages.length > 0) {
|
|
2578
|
+
messages.unshift({
|
|
2579
|
+
role: "system",
|
|
2580
|
+
content: systemMessages.join("\n\n")
|
|
2581
|
+
});
|
|
2582
|
+
}
|
|
2583
|
+
return messages;
|
|
2584
|
+
}
|
|
2192
2585
|
function toGeminiTools(tools) {
|
|
2193
2586
|
if (!tools || tools.length === 0) {
|
|
2194
2587
|
return void 0;
|
|
@@ -2362,6 +2755,41 @@ function extractChatGptUsageTokens(usage) {
|
|
|
2362
2755
|
totalTokens
|
|
2363
2756
|
};
|
|
2364
2757
|
}
|
|
2758
|
+
function extractFireworksUsageTokens(usage) {
|
|
2759
|
+
if (!usage || typeof usage !== "object") {
|
|
2760
|
+
return void 0;
|
|
2761
|
+
}
|
|
2762
|
+
const promptTokens = toMaybeNumber(
|
|
2763
|
+
usage.prompt_tokens ?? usage.input_tokens
|
|
2764
|
+
);
|
|
2765
|
+
const cachedTokens = toMaybeNumber(
|
|
2766
|
+
usage.prompt_tokens_details?.cached_tokens ?? usage.input_tokens_details?.cached_tokens
|
|
2767
|
+
);
|
|
2768
|
+
const outputTokensRaw = toMaybeNumber(
|
|
2769
|
+
usage.completion_tokens ?? usage.output_tokens
|
|
2770
|
+
);
|
|
2771
|
+
const reasoningTokens = toMaybeNumber(
|
|
2772
|
+
usage.completion_tokens_details?.reasoning_tokens ?? usage.output_tokens_details?.reasoning_tokens
|
|
2773
|
+
);
|
|
2774
|
+
const totalTokens = toMaybeNumber(
|
|
2775
|
+
usage.total_tokens ?? usage.totalTokenCount
|
|
2776
|
+
);
|
|
2777
|
+
let responseTokens;
|
|
2778
|
+
if (outputTokensRaw !== void 0) {
|
|
2779
|
+
const adjusted = outputTokensRaw - (reasoningTokens ?? 0);
|
|
2780
|
+
responseTokens = adjusted >= 0 ? adjusted : 0;
|
|
2781
|
+
}
|
|
2782
|
+
if (promptTokens === void 0 && cachedTokens === void 0 && responseTokens === void 0 && reasoningTokens === void 0 && totalTokens === void 0) {
|
|
2783
|
+
return void 0;
|
|
2784
|
+
}
|
|
2785
|
+
return {
|
|
2786
|
+
promptTokens,
|
|
2787
|
+
cachedTokens,
|
|
2788
|
+
responseTokens,
|
|
2789
|
+
thinkingTokens: reasoningTokens,
|
|
2790
|
+
totalTokens
|
|
2791
|
+
};
|
|
2792
|
+
}
|
|
2365
2793
|
var MODERATION_FINISH_REASONS = /* @__PURE__ */ new Set([
|
|
2366
2794
|
FinishReason.SAFETY,
|
|
2367
2795
|
FinishReason.BLOCKLIST,
|
|
@@ -2400,8 +2828,8 @@ function parseOpenAiToolArguments(raw) {
|
|
|
2400
2828
|
function formatZodIssues(issues) {
|
|
2401
2829
|
const messages = [];
|
|
2402
2830
|
for (const issue of issues) {
|
|
2403
|
-
const
|
|
2404
|
-
messages.push(`${
|
|
2831
|
+
const path6 = issue.path.length > 0 ? issue.path.map(String).join(".") : "input";
|
|
2832
|
+
messages.push(`${path6}: ${issue.message}`);
|
|
2405
2833
|
}
|
|
2406
2834
|
return messages.join("; ");
|
|
2407
2835
|
}
|
|
@@ -2417,7 +2845,7 @@ function buildToolErrorOutput(message, issues) {
|
|
|
2417
2845
|
return output;
|
|
2418
2846
|
}
|
|
2419
2847
|
async function executeToolCall(params) {
|
|
2420
|
-
const { toolName, tool: tool2, rawInput, parseError } = params;
|
|
2848
|
+
const { callKind, toolName, tool: tool2, rawInput, parseError } = params;
|
|
2421
2849
|
if (!tool2) {
|
|
2422
2850
|
const message = `Unknown tool: ${toolName}`;
|
|
2423
2851
|
return {
|
|
@@ -2425,6 +2853,39 @@ async function executeToolCall(params) {
|
|
|
2425
2853
|
outputPayload: buildToolErrorOutput(message)
|
|
2426
2854
|
};
|
|
2427
2855
|
}
|
|
2856
|
+
if (callKind === "custom") {
|
|
2857
|
+
if (!isCustomTool(tool2)) {
|
|
2858
|
+
const message = `Tool ${toolName} was called as custom_tool_call but is declared as function.`;
|
|
2859
|
+
const outputPayload = buildToolErrorOutput(message);
|
|
2860
|
+
return {
|
|
2861
|
+
result: { toolName, input: rawInput, output: outputPayload, error: message },
|
|
2862
|
+
outputPayload
|
|
2863
|
+
};
|
|
2864
|
+
}
|
|
2865
|
+
const input = typeof rawInput === "string" ? rawInput : String(rawInput ?? "");
|
|
2866
|
+
try {
|
|
2867
|
+
const output = await tool2.execute(input);
|
|
2868
|
+
return {
|
|
2869
|
+
result: { toolName, input, output },
|
|
2870
|
+
outputPayload: output
|
|
2871
|
+
};
|
|
2872
|
+
} catch (error) {
|
|
2873
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2874
|
+
const outputPayload = buildToolErrorOutput(`Tool ${toolName} failed: ${message}`);
|
|
2875
|
+
return {
|
|
2876
|
+
result: { toolName, input, output: outputPayload, error: message },
|
|
2877
|
+
outputPayload
|
|
2878
|
+
};
|
|
2879
|
+
}
|
|
2880
|
+
}
|
|
2881
|
+
if (isCustomTool(tool2)) {
|
|
2882
|
+
const message = `Tool ${toolName} was called as function_call but is declared as custom.`;
|
|
2883
|
+
const outputPayload = buildToolErrorOutput(message);
|
|
2884
|
+
return {
|
|
2885
|
+
result: { toolName, input: rawInput, output: outputPayload, error: message },
|
|
2886
|
+
outputPayload
|
|
2887
|
+
};
|
|
2888
|
+
}
|
|
2428
2889
|
if (parseError) {
|
|
2429
2890
|
const message = `Invalid JSON for tool ${toolName}: ${parseError}`;
|
|
2430
2891
|
return {
|
|
@@ -2481,8 +2942,12 @@ function normalizeChatGptToolIds(params) {
|
|
|
2481
2942
|
rawItemId = nextItemId ?? rawItemId;
|
|
2482
2943
|
}
|
|
2483
2944
|
const callValue = sanitizeChatGptToolId(rawCallId || rawItemId || randomBytes(8).toString("hex"));
|
|
2484
|
-
let itemValue = sanitizeChatGptToolId(rawItemId ||
|
|
2485
|
-
if (
|
|
2945
|
+
let itemValue = sanitizeChatGptToolId(rawItemId || callValue);
|
|
2946
|
+
if (params.callKind === "custom") {
|
|
2947
|
+
if (!itemValue.startsWith("ctc")) {
|
|
2948
|
+
itemValue = `ctc_${itemValue}`;
|
|
2949
|
+
}
|
|
2950
|
+
} else if (!itemValue.startsWith("fc")) {
|
|
2486
2951
|
itemValue = `fc-${itemValue}`;
|
|
2487
2952
|
}
|
|
2488
2953
|
return { callId: callValue, itemId: itemValue };
|
|
@@ -2547,7 +3012,7 @@ function extractOpenAiResponseParts(response) {
|
|
|
2547
3012
|
}
|
|
2548
3013
|
return { parts, blocked };
|
|
2549
3014
|
}
|
|
2550
|
-
function
|
|
3015
|
+
function extractOpenAiToolCalls(output) {
|
|
2551
3016
|
const calls = [];
|
|
2552
3017
|
if (!Array.isArray(output)) {
|
|
2553
3018
|
return calls;
|
|
@@ -2556,22 +3021,78 @@ function extractOpenAiFunctionCalls(output) {
|
|
|
2556
3021
|
if (!item || typeof item !== "object") {
|
|
2557
3022
|
continue;
|
|
2558
3023
|
}
|
|
2559
|
-
|
|
3024
|
+
const itemType = item.type;
|
|
3025
|
+
if (itemType === "function_call") {
|
|
2560
3026
|
const name = typeof item.name === "string" ? item.name : "";
|
|
2561
3027
|
const args = typeof item.arguments === "string" ? item.arguments : "";
|
|
2562
3028
|
const call_id = typeof item.call_id === "string" ? item.call_id : "";
|
|
2563
3029
|
const id = typeof item.id === "string" ? item.id : void 0;
|
|
2564
3030
|
if (name && call_id) {
|
|
2565
|
-
calls.push({ name, arguments: args, call_id, id });
|
|
3031
|
+
calls.push({ kind: "function", name, arguments: args, call_id, id });
|
|
3032
|
+
}
|
|
3033
|
+
continue;
|
|
3034
|
+
}
|
|
3035
|
+
if (itemType === "custom_tool_call") {
|
|
3036
|
+
const name = typeof item.name === "string" ? item.name : "";
|
|
3037
|
+
const input = typeof item.input === "string" ? item.input : "";
|
|
3038
|
+
const call_id = typeof item.call_id === "string" ? item.call_id : "";
|
|
3039
|
+
const id = typeof item.id === "string" ? item.id : void 0;
|
|
3040
|
+
if (name && call_id) {
|
|
3041
|
+
calls.push({ kind: "custom", name, input, call_id, id });
|
|
2566
3042
|
}
|
|
2567
3043
|
}
|
|
2568
3044
|
}
|
|
2569
3045
|
return calls;
|
|
2570
3046
|
}
|
|
3047
|
+
function extractFireworksMessageText(message) {
|
|
3048
|
+
if (!message || typeof message !== "object") {
|
|
3049
|
+
return "";
|
|
3050
|
+
}
|
|
3051
|
+
const content = message.content;
|
|
3052
|
+
if (typeof content === "string") {
|
|
3053
|
+
return content;
|
|
3054
|
+
}
|
|
3055
|
+
if (!Array.isArray(content)) {
|
|
3056
|
+
return "";
|
|
3057
|
+
}
|
|
3058
|
+
let text = "";
|
|
3059
|
+
for (const part of content) {
|
|
3060
|
+
const textPart = part.text;
|
|
3061
|
+
if (typeof textPart === "string") {
|
|
3062
|
+
text += textPart;
|
|
3063
|
+
}
|
|
3064
|
+
}
|
|
3065
|
+
return text;
|
|
3066
|
+
}
|
|
3067
|
+
function extractFireworksToolCalls(message) {
|
|
3068
|
+
if (!message || typeof message !== "object") {
|
|
3069
|
+
return [];
|
|
3070
|
+
}
|
|
3071
|
+
const toolCalls = message.tool_calls;
|
|
3072
|
+
if (!Array.isArray(toolCalls)) {
|
|
3073
|
+
return [];
|
|
3074
|
+
}
|
|
3075
|
+
const calls = [];
|
|
3076
|
+
for (const call of toolCalls) {
|
|
3077
|
+
if (!call || typeof call !== "object") {
|
|
3078
|
+
continue;
|
|
3079
|
+
}
|
|
3080
|
+
const id = typeof call.id === "string" ? call.id : "";
|
|
3081
|
+
const fn = call.function;
|
|
3082
|
+
const name = fn && typeof fn === "object" && typeof fn.name === "string" ? fn.name ?? "" : "";
|
|
3083
|
+
const args = fn && typeof fn === "object" && typeof fn.arguments === "string" ? fn.arguments ?? "" : "";
|
|
3084
|
+
if (id && name) {
|
|
3085
|
+
calls.push({ id, name, arguments: args });
|
|
3086
|
+
}
|
|
3087
|
+
}
|
|
3088
|
+
return calls;
|
|
3089
|
+
}
|
|
2571
3090
|
function resolveGeminiThinkingConfig(modelId) {
|
|
2572
3091
|
switch (modelId) {
|
|
2573
3092
|
case "gemini-3-pro-preview":
|
|
2574
3093
|
return { includeThoughts: true };
|
|
3094
|
+
case "gemini-3-flash-preview":
|
|
3095
|
+
return { includeThoughts: true, thinkingBudget: 16384 };
|
|
2575
3096
|
case "gemini-2.5-pro":
|
|
2576
3097
|
return { includeThoughts: true, thinkingBudget: 32768 };
|
|
2577
3098
|
case "gemini-flash-latest":
|
|
@@ -2747,7 +3268,7 @@ async function runTextCall(params) {
|
|
|
2747
3268
|
};
|
|
2748
3269
|
let sawResponseDelta = false;
|
|
2749
3270
|
let sawThoughtDelta = false;
|
|
2750
|
-
const result = await
|
|
3271
|
+
const result = await collectChatGptCodexResponseWithRetry({
|
|
2751
3272
|
request: requestPayload,
|
|
2752
3273
|
signal,
|
|
2753
3274
|
onDelta: (delta) => {
|
|
@@ -2778,6 +3299,47 @@ async function runTextCall(params) {
|
|
|
2778
3299
|
if (!sawResponseDelta && fallbackText.length > 0) {
|
|
2779
3300
|
pushDelta("response", fallbackText);
|
|
2780
3301
|
}
|
|
3302
|
+
} else if (provider === "fireworks") {
|
|
3303
|
+
if (request.tools && request.tools.length > 0) {
|
|
3304
|
+
throw new Error(
|
|
3305
|
+
"Fireworks provider does not support provider-native tools in generateText; use runToolLoop for function tools."
|
|
3306
|
+
);
|
|
3307
|
+
}
|
|
3308
|
+
const fireworksMessages = toFireworksMessages(contents, {
|
|
3309
|
+
responseMimeType: request.responseMimeType,
|
|
3310
|
+
responseJsonSchema: request.responseJsonSchema
|
|
3311
|
+
});
|
|
3312
|
+
await runFireworksCall(async (client) => {
|
|
3313
|
+
const responseFormat = request.responseJsonSchema ? {
|
|
3314
|
+
type: "json_schema",
|
|
3315
|
+
json_schema: {
|
|
3316
|
+
name: "llm-response",
|
|
3317
|
+
schema: request.responseJsonSchema
|
|
3318
|
+
}
|
|
3319
|
+
} : request.responseMimeType === "application/json" ? { type: "json_object" } : void 0;
|
|
3320
|
+
const response = await client.chat.completions.create(
|
|
3321
|
+
{
|
|
3322
|
+
model: modelForProvider,
|
|
3323
|
+
messages: fireworksMessages,
|
|
3324
|
+
...responseFormat ? { response_format: responseFormat } : {}
|
|
3325
|
+
},
|
|
3326
|
+
{ signal }
|
|
3327
|
+
);
|
|
3328
|
+
modelVersion = typeof response.model === "string" ? response.model : request.model;
|
|
3329
|
+
queue.push({ type: "model", modelVersion });
|
|
3330
|
+
const choice = Array.isArray(response.choices) ? response.choices[0] : void 0;
|
|
3331
|
+
if (choice?.finish_reason === "content_filter") {
|
|
3332
|
+
blocked = true;
|
|
3333
|
+
queue.push({ type: "blocked" });
|
|
3334
|
+
}
|
|
3335
|
+
const textOutput = extractFireworksMessageText(
|
|
3336
|
+
choice?.message
|
|
3337
|
+
);
|
|
3338
|
+
if (textOutput.length > 0) {
|
|
3339
|
+
pushDelta("response", textOutput);
|
|
3340
|
+
}
|
|
3341
|
+
latestUsage = extractFireworksUsageTokens(response.usage);
|
|
3342
|
+
});
|
|
2781
3343
|
} else {
|
|
2782
3344
|
const geminiContents = contents.map(convertLlmContentToGeminiContent);
|
|
2783
3345
|
const config = {
|
|
@@ -2902,11 +3464,12 @@ function buildJsonSchemaConfig(request) {
|
|
|
2902
3464
|
const schemaName = (request.openAiSchemaName ?? "llm-response").trim() || "llm-response";
|
|
2903
3465
|
const providerInfo = resolveProvider(request.model);
|
|
2904
3466
|
const isOpenAiVariant = providerInfo.provider === "openai" || providerInfo.provider === "chatgpt";
|
|
3467
|
+
const isGeminiVariant = providerInfo.provider === "gemini";
|
|
2905
3468
|
const baseJsonSchema = zodToJsonSchema(request.schema, {
|
|
2906
3469
|
name: schemaName,
|
|
2907
3470
|
target: isOpenAiVariant ? "openAi" : "jsonSchema7"
|
|
2908
3471
|
});
|
|
2909
|
-
const responseJsonSchema = isOpenAiVariant ? resolveOpenAiSchemaRoot(baseJsonSchema) : addGeminiPropertyOrdering(baseJsonSchema);
|
|
3472
|
+
const responseJsonSchema = isOpenAiVariant ? resolveOpenAiSchemaRoot(baseJsonSchema) : isGeminiVariant ? addGeminiPropertyOrdering(baseJsonSchema) : resolveOpenAiSchemaRoot(baseJsonSchema);
|
|
2910
3473
|
if (isOpenAiVariant && !isJsonSchemaObject(responseJsonSchema)) {
|
|
2911
3474
|
throw new Error("OpenAI structured outputs require a JSON object schema at the root.");
|
|
2912
3475
|
}
|
|
@@ -3073,15 +3636,46 @@ var DEFAULT_TOOL_LOOP_MAX_STEPS = 8;
|
|
|
3073
3636
|
function resolveToolLoopContents(input) {
|
|
3074
3637
|
return resolveTextContents(input);
|
|
3075
3638
|
}
|
|
3076
|
-
function
|
|
3639
|
+
function isCustomTool(toolDef) {
|
|
3640
|
+
return toolDef.type === "custom";
|
|
3641
|
+
}
|
|
3642
|
+
function buildOpenAiToolsFromToolSet(tools) {
|
|
3077
3643
|
const toolEntries = Object.entries(tools);
|
|
3078
|
-
return toolEntries.map(([name, toolDef]) =>
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3644
|
+
return toolEntries.map(([name, toolDef]) => {
|
|
3645
|
+
if (isCustomTool(toolDef)) {
|
|
3646
|
+
return {
|
|
3647
|
+
type: "custom",
|
|
3648
|
+
name,
|
|
3649
|
+
description: toolDef.description ?? void 0,
|
|
3650
|
+
...toolDef.format ? { format: toolDef.format } : {}
|
|
3651
|
+
};
|
|
3652
|
+
}
|
|
3653
|
+
return {
|
|
3654
|
+
type: "function",
|
|
3655
|
+
name,
|
|
3656
|
+
description: toolDef.description ?? void 0,
|
|
3657
|
+
parameters: buildOpenAiToolSchema(toolDef.inputSchema, name),
|
|
3658
|
+
strict: true
|
|
3659
|
+
};
|
|
3660
|
+
});
|
|
3661
|
+
}
|
|
3662
|
+
function buildFireworksToolsFromToolSet(tools) {
|
|
3663
|
+
const toolEntries = Object.entries(tools);
|
|
3664
|
+
return toolEntries.map(([name, toolDef]) => {
|
|
3665
|
+
if (isCustomTool(toolDef)) {
|
|
3666
|
+
throw new Error(
|
|
3667
|
+
`Fireworks provider does not support custom/freeform tools (${name}). Use JSON function tools instead.`
|
|
3668
|
+
);
|
|
3669
|
+
}
|
|
3670
|
+
return {
|
|
3671
|
+
type: "function",
|
|
3672
|
+
function: {
|
|
3673
|
+
name,
|
|
3674
|
+
description: toolDef.description ?? void 0,
|
|
3675
|
+
parameters: buildOpenAiToolSchema(toolDef.inputSchema, name)
|
|
3676
|
+
}
|
|
3677
|
+
};
|
|
3678
|
+
});
|
|
3085
3679
|
}
|
|
3086
3680
|
function buildOpenAiToolSchema(schema, name) {
|
|
3087
3681
|
const rawSchema = zodToJsonSchema(schema, { name, target: "openAi" });
|
|
@@ -3093,11 +3687,18 @@ function buildOpenAiToolSchema(schema, name) {
|
|
|
3093
3687
|
}
|
|
3094
3688
|
function buildGeminiFunctionDeclarations(tools) {
|
|
3095
3689
|
const toolEntries = Object.entries(tools);
|
|
3096
|
-
const functionDeclarations = toolEntries.map(([name, toolDef]) =>
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
|
|
3690
|
+
const functionDeclarations = toolEntries.map(([name, toolDef]) => {
|
|
3691
|
+
if (isCustomTool(toolDef)) {
|
|
3692
|
+
throw new Error(
|
|
3693
|
+
`Gemini provider does not support custom/freeform tools (${name}). Use JSON function tools instead.`
|
|
3694
|
+
);
|
|
3695
|
+
}
|
|
3696
|
+
return {
|
|
3697
|
+
name,
|
|
3698
|
+
description: toolDef.description ?? "",
|
|
3699
|
+
parametersJsonSchema: buildGeminiToolSchema(toolDef.inputSchema, name)
|
|
3700
|
+
};
|
|
3701
|
+
});
|
|
3101
3702
|
return [{ functionDeclarations }];
|
|
3102
3703
|
}
|
|
3103
3704
|
function buildGeminiToolSchema(schema, name) {
|
|
@@ -3158,9 +3759,9 @@ async function runToolLoop(request) {
|
|
|
3158
3759
|
let finalText = "";
|
|
3159
3760
|
let finalThoughts = "";
|
|
3160
3761
|
if (providerInfo.provider === "openai") {
|
|
3161
|
-
const
|
|
3762
|
+
const openAiAgentTools = buildOpenAiToolsFromToolSet(request.tools);
|
|
3162
3763
|
const openAiNativeTools = toOpenAiTools(request.modelTools);
|
|
3163
|
-
const openAiTools = openAiNativeTools ? [...openAiNativeTools, ...
|
|
3764
|
+
const openAiTools = openAiNativeTools ? [...openAiNativeTools, ...openAiAgentTools] : [...openAiAgentTools];
|
|
3164
3765
|
const reasoningEffort = resolveOpenAiReasoningEffort(
|
|
3165
3766
|
providerInfo.model,
|
|
3166
3767
|
request.openAiReasoningEffort
|
|
@@ -3252,9 +3853,9 @@ async function runToolLoop(request) {
|
|
|
3252
3853
|
if (usageTokens) {
|
|
3253
3854
|
emitEvent({ type: "usage", usage: usageTokens, costUsd: stepCostUsd, modelVersion });
|
|
3254
3855
|
}
|
|
3255
|
-
const
|
|
3856
|
+
const responseToolCalls = extractOpenAiToolCalls(finalResponse.output);
|
|
3256
3857
|
const stepToolCalls = [];
|
|
3257
|
-
if (
|
|
3858
|
+
if (responseToolCalls.length === 0) {
|
|
3258
3859
|
finalText = responseText;
|
|
3259
3860
|
finalThoughts = reasoningSummary;
|
|
3260
3861
|
steps.push({
|
|
@@ -3268,10 +3869,21 @@ async function runToolLoop(request) {
|
|
|
3268
3869
|
});
|
|
3269
3870
|
return { text: finalText, thoughts: finalThoughts, steps, totalCostUsd };
|
|
3270
3871
|
}
|
|
3271
|
-
const callInputs =
|
|
3872
|
+
const callInputs = responseToolCalls.map((call, index) => {
|
|
3272
3873
|
const toolIndex = index + 1;
|
|
3273
3874
|
const toolId = buildToolLogId(turn, toolIndex);
|
|
3274
3875
|
const toolName = call.name;
|
|
3876
|
+
if (call.kind === "custom") {
|
|
3877
|
+
return {
|
|
3878
|
+
call,
|
|
3879
|
+
toolName,
|
|
3880
|
+
value: call.input,
|
|
3881
|
+
parseError: void 0,
|
|
3882
|
+
toolId,
|
|
3883
|
+
turn,
|
|
3884
|
+
toolIndex
|
|
3885
|
+
};
|
|
3886
|
+
}
|
|
3275
3887
|
const { value, error: parseError } = parseOpenAiToolArguments(call.arguments);
|
|
3276
3888
|
return { call, toolName, value, parseError, toolId, turn, toolIndex };
|
|
3277
3889
|
});
|
|
@@ -3286,6 +3898,7 @@ async function runToolLoop(request) {
|
|
|
3286
3898
|
},
|
|
3287
3899
|
async () => {
|
|
3288
3900
|
const { result, outputPayload } = await executeToolCall({
|
|
3901
|
+
callKind: entry.call.kind,
|
|
3289
3902
|
toolName: entry.toolName,
|
|
3290
3903
|
tool: request.tools[entry.toolName],
|
|
3291
3904
|
rawInput: entry.value,
|
|
@@ -3299,11 +3912,19 @@ async function runToolLoop(request) {
|
|
|
3299
3912
|
const toolOutputs = [];
|
|
3300
3913
|
for (const { entry, result, outputPayload } of callResults) {
|
|
3301
3914
|
stepToolCalls.push({ ...result, callId: entry.call.call_id });
|
|
3302
|
-
|
|
3303
|
-
|
|
3304
|
-
|
|
3305
|
-
|
|
3306
|
-
|
|
3915
|
+
if (entry.call.kind === "custom") {
|
|
3916
|
+
toolOutputs.push({
|
|
3917
|
+
type: "custom_tool_call_output",
|
|
3918
|
+
call_id: entry.call.call_id,
|
|
3919
|
+
output: mergeToolOutput(outputPayload)
|
|
3920
|
+
});
|
|
3921
|
+
} else {
|
|
3922
|
+
toolOutputs.push({
|
|
3923
|
+
type: "function_call_output",
|
|
3924
|
+
call_id: entry.call.call_id,
|
|
3925
|
+
output: mergeToolOutput(outputPayload)
|
|
3926
|
+
});
|
|
3927
|
+
}
|
|
3307
3928
|
}
|
|
3308
3929
|
steps.push({
|
|
3309
3930
|
step: steps.length + 1,
|
|
@@ -3320,24 +3941,28 @@ async function runToolLoop(request) {
|
|
|
3320
3941
|
throw new Error(`Tool loop exceeded max steps (${maxSteps}) without final response.`);
|
|
3321
3942
|
}
|
|
3322
3943
|
if (providerInfo.provider === "chatgpt") {
|
|
3323
|
-
const
|
|
3944
|
+
const openAiAgentTools = buildOpenAiToolsFromToolSet(request.tools);
|
|
3324
3945
|
const openAiNativeTools = toOpenAiTools(request.modelTools);
|
|
3325
|
-
const openAiTools = openAiNativeTools ? [...openAiNativeTools, ...
|
|
3946
|
+
const openAiTools = openAiNativeTools ? [...openAiNativeTools, ...openAiAgentTools] : [...openAiAgentTools];
|
|
3326
3947
|
const reasoningEffort = resolveOpenAiReasoningEffort(
|
|
3327
3948
|
request.model,
|
|
3328
3949
|
request.openAiReasoningEffort
|
|
3329
3950
|
);
|
|
3330
3951
|
const toolLoopInput = toChatGptInput(contents);
|
|
3952
|
+
const conversationId = `tool-loop-${randomBytes(8).toString("hex")}`;
|
|
3953
|
+
const promptCacheKey = conversationId;
|
|
3331
3954
|
let input = [...toolLoopInput.input];
|
|
3332
3955
|
for (let stepIndex = 0; stepIndex < maxSteps; stepIndex += 1) {
|
|
3333
3956
|
const turn = stepIndex + 1;
|
|
3334
|
-
const response = await
|
|
3957
|
+
const response = await collectChatGptCodexResponseWithRetry({
|
|
3958
|
+
sessionId: conversationId,
|
|
3335
3959
|
request: {
|
|
3336
3960
|
model: providerInfo.model,
|
|
3337
3961
|
store: false,
|
|
3338
3962
|
stream: true,
|
|
3339
3963
|
instructions: toolLoopInput.instructions ?? "You are a helpful assistant.",
|
|
3340
3964
|
input,
|
|
3965
|
+
prompt_cache_key: promptCacheKey,
|
|
3341
3966
|
include: ["reasoning.encrypted_content"],
|
|
3342
3967
|
tools: openAiTools,
|
|
3343
3968
|
tool_choice: "auto",
|
|
@@ -3368,8 +3993,8 @@ async function runToolLoop(request) {
|
|
|
3368
3993
|
totalCostUsd += stepCostUsd;
|
|
3369
3994
|
const responseText = (response.text ?? "").trim();
|
|
3370
3995
|
const reasoningSummaryText = (response.reasoningSummaryText ?? "").trim();
|
|
3371
|
-
const
|
|
3372
|
-
if (
|
|
3996
|
+
const responseToolCalls = response.toolCalls ?? [];
|
|
3997
|
+
if (responseToolCalls.length === 0) {
|
|
3373
3998
|
finalText = responseText;
|
|
3374
3999
|
finalThoughts = reasoningSummaryText;
|
|
3375
4000
|
steps.push({
|
|
@@ -3385,12 +4010,16 @@ async function runToolLoop(request) {
|
|
|
3385
4010
|
}
|
|
3386
4011
|
const toolCalls = [];
|
|
3387
4012
|
const toolOutputs = [];
|
|
3388
|
-
const callInputs =
|
|
4013
|
+
const callInputs = responseToolCalls.map((call, index) => {
|
|
3389
4014
|
const toolIndex = index + 1;
|
|
3390
4015
|
const toolId = buildToolLogId(turn, toolIndex);
|
|
3391
4016
|
const toolName = call.name;
|
|
3392
|
-
const { value, error: parseError } = parseOpenAiToolArguments(call.arguments);
|
|
3393
|
-
const ids = normalizeChatGptToolIds({
|
|
4017
|
+
const { value, error: parseError } = call.kind === "custom" ? { value: call.input, error: void 0 } : parseOpenAiToolArguments(call.arguments);
|
|
4018
|
+
const ids = normalizeChatGptToolIds({
|
|
4019
|
+
callKind: call.kind,
|
|
4020
|
+
callId: call.callId,
|
|
4021
|
+
itemId: call.id
|
|
4022
|
+
});
|
|
3394
4023
|
return { call, toolName, value, parseError, ids, toolId, turn, toolIndex };
|
|
3395
4024
|
});
|
|
3396
4025
|
const callResults = await Promise.all(
|
|
@@ -3404,6 +4033,7 @@ async function runToolLoop(request) {
|
|
|
3404
4033
|
},
|
|
3405
4034
|
async () => {
|
|
3406
4035
|
const { result, outputPayload } = await executeToolCall({
|
|
4036
|
+
callKind: entry.call.kind,
|
|
3407
4037
|
toolName: entry.toolName,
|
|
3408
4038
|
tool: request.tools[entry.toolName],
|
|
3409
4039
|
rawInput: entry.value,
|
|
@@ -3416,19 +4046,35 @@ async function runToolLoop(request) {
|
|
|
3416
4046
|
);
|
|
3417
4047
|
for (const { entry, result, outputPayload } of callResults) {
|
|
3418
4048
|
toolCalls.push({ ...result, callId: entry.ids.callId });
|
|
3419
|
-
|
|
3420
|
-
|
|
3421
|
-
|
|
3422
|
-
|
|
3423
|
-
|
|
3424
|
-
|
|
3425
|
-
|
|
3426
|
-
|
|
3427
|
-
|
|
3428
|
-
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
|
|
4049
|
+
if (entry.call.kind === "custom") {
|
|
4050
|
+
toolOutputs.push({
|
|
4051
|
+
type: "custom_tool_call",
|
|
4052
|
+
id: entry.ids.itemId,
|
|
4053
|
+
call_id: entry.ids.callId,
|
|
4054
|
+
name: entry.toolName,
|
|
4055
|
+
input: entry.call.input,
|
|
4056
|
+
status: "completed"
|
|
4057
|
+
});
|
|
4058
|
+
toolOutputs.push({
|
|
4059
|
+
type: "custom_tool_call_output",
|
|
4060
|
+
call_id: entry.ids.callId,
|
|
4061
|
+
output: mergeToolOutput(outputPayload)
|
|
4062
|
+
});
|
|
4063
|
+
} else {
|
|
4064
|
+
toolOutputs.push({
|
|
4065
|
+
type: "function_call",
|
|
4066
|
+
id: entry.ids.itemId,
|
|
4067
|
+
call_id: entry.ids.callId,
|
|
4068
|
+
name: entry.toolName,
|
|
4069
|
+
arguments: entry.call.arguments,
|
|
4070
|
+
status: "completed"
|
|
4071
|
+
});
|
|
4072
|
+
toolOutputs.push({
|
|
4073
|
+
type: "function_call_output",
|
|
4074
|
+
call_id: entry.ids.callId,
|
|
4075
|
+
output: mergeToolOutput(outputPayload)
|
|
4076
|
+
});
|
|
4077
|
+
}
|
|
3432
4078
|
}
|
|
3433
4079
|
steps.push({
|
|
3434
4080
|
step: steps.length + 1,
|
|
@@ -3443,6 +4089,134 @@ async function runToolLoop(request) {
|
|
|
3443
4089
|
}
|
|
3444
4090
|
throw new Error(`Tool loop exceeded max steps (${maxSteps}) without final response.`);
|
|
3445
4091
|
}
|
|
4092
|
+
if (providerInfo.provider === "fireworks") {
|
|
4093
|
+
if (request.modelTools && request.modelTools.length > 0) {
|
|
4094
|
+
throw new Error(
|
|
4095
|
+
"Fireworks provider does not support provider-native modelTools in runToolLoop."
|
|
4096
|
+
);
|
|
4097
|
+
}
|
|
4098
|
+
const fireworksTools = buildFireworksToolsFromToolSet(request.tools);
|
|
4099
|
+
const messages = toFireworksMessages(contents);
|
|
4100
|
+
for (let stepIndex = 0; stepIndex < maxSteps; stepIndex += 1) {
|
|
4101
|
+
const turn = stepIndex + 1;
|
|
4102
|
+
const response = await runFireworksCall(async (client) => {
|
|
4103
|
+
return await client.chat.completions.create(
|
|
4104
|
+
{
|
|
4105
|
+
model: providerInfo.model,
|
|
4106
|
+
messages,
|
|
4107
|
+
tools: fireworksTools,
|
|
4108
|
+
tool_choice: "auto",
|
|
4109
|
+
parallel_tool_calls: true
|
|
4110
|
+
},
|
|
4111
|
+
{ signal: request.signal }
|
|
4112
|
+
);
|
|
4113
|
+
});
|
|
4114
|
+
const modelVersion = typeof response.model === "string" ? response.model : request.model;
|
|
4115
|
+
request.onEvent?.({ type: "model", modelVersion });
|
|
4116
|
+
const choice = Array.isArray(response.choices) ? response.choices[0] : void 0;
|
|
4117
|
+
if (choice?.finish_reason === "content_filter") {
|
|
4118
|
+
request.onEvent?.({ type: "blocked" });
|
|
4119
|
+
}
|
|
4120
|
+
const message = choice?.message;
|
|
4121
|
+
const responseText = extractFireworksMessageText(message).trim();
|
|
4122
|
+
if (responseText.length > 0) {
|
|
4123
|
+
request.onEvent?.({ type: "delta", channel: "response", text: responseText });
|
|
4124
|
+
}
|
|
4125
|
+
const usageTokens = extractFireworksUsageTokens(response.usage);
|
|
4126
|
+
const stepCostUsd = estimateCallCostUsd({
|
|
4127
|
+
modelId: modelVersion,
|
|
4128
|
+
tokens: usageTokens,
|
|
4129
|
+
responseImages: 0
|
|
4130
|
+
});
|
|
4131
|
+
totalCostUsd += stepCostUsd;
|
|
4132
|
+
if (usageTokens) {
|
|
4133
|
+
request.onEvent?.({
|
|
4134
|
+
type: "usage",
|
|
4135
|
+
usage: usageTokens,
|
|
4136
|
+
costUsd: stepCostUsd,
|
|
4137
|
+
modelVersion
|
|
4138
|
+
});
|
|
4139
|
+
}
|
|
4140
|
+
const responseToolCalls = extractFireworksToolCalls(message);
|
|
4141
|
+
if (responseToolCalls.length === 0) {
|
|
4142
|
+
finalText = responseText;
|
|
4143
|
+
finalThoughts = "";
|
|
4144
|
+
steps.push({
|
|
4145
|
+
step: steps.length + 1,
|
|
4146
|
+
modelVersion,
|
|
4147
|
+
text: responseText || void 0,
|
|
4148
|
+
thoughts: void 0,
|
|
4149
|
+
toolCalls: [],
|
|
4150
|
+
usage: usageTokens,
|
|
4151
|
+
costUsd: stepCostUsd
|
|
4152
|
+
});
|
|
4153
|
+
return { text: finalText, thoughts: finalThoughts, steps, totalCostUsd };
|
|
4154
|
+
}
|
|
4155
|
+
const stepToolCalls = [];
|
|
4156
|
+
const callInputs = responseToolCalls.map((call, index) => {
|
|
4157
|
+
const toolIndex = index + 1;
|
|
4158
|
+
const toolId = buildToolLogId(turn, toolIndex);
|
|
4159
|
+
const { value, error: parseError } = parseOpenAiToolArguments(call.arguments);
|
|
4160
|
+
return { call, toolName: call.name, value, parseError, toolId, turn, toolIndex };
|
|
4161
|
+
});
|
|
4162
|
+
const callResults = await Promise.all(
|
|
4163
|
+
callInputs.map(async (entry) => {
|
|
4164
|
+
return await toolCallContextStorage.run(
|
|
4165
|
+
{
|
|
4166
|
+
toolName: entry.toolName,
|
|
4167
|
+
toolId: entry.toolId,
|
|
4168
|
+
turn: entry.turn,
|
|
4169
|
+
toolIndex: entry.toolIndex
|
|
4170
|
+
},
|
|
4171
|
+
async () => {
|
|
4172
|
+
const { result, outputPayload } = await executeToolCall({
|
|
4173
|
+
callKind: "function",
|
|
4174
|
+
toolName: entry.toolName,
|
|
4175
|
+
tool: request.tools[entry.toolName],
|
|
4176
|
+
rawInput: entry.value,
|
|
4177
|
+
parseError: entry.parseError
|
|
4178
|
+
});
|
|
4179
|
+
return { entry, result, outputPayload };
|
|
4180
|
+
}
|
|
4181
|
+
);
|
|
4182
|
+
})
|
|
4183
|
+
);
|
|
4184
|
+
const assistantToolCalls = [];
|
|
4185
|
+
const toolMessages = [];
|
|
4186
|
+
for (const { entry, result, outputPayload } of callResults) {
|
|
4187
|
+
stepToolCalls.push({ ...result, callId: entry.call.id });
|
|
4188
|
+
assistantToolCalls.push({
|
|
4189
|
+
id: entry.call.id,
|
|
4190
|
+
type: "function",
|
|
4191
|
+
function: {
|
|
4192
|
+
name: entry.toolName,
|
|
4193
|
+
arguments: entry.call.arguments
|
|
4194
|
+
}
|
|
4195
|
+
});
|
|
4196
|
+
toolMessages.push({
|
|
4197
|
+
role: "tool",
|
|
4198
|
+
tool_call_id: entry.call.id,
|
|
4199
|
+
content: mergeToolOutput(outputPayload)
|
|
4200
|
+
});
|
|
4201
|
+
}
|
|
4202
|
+
steps.push({
|
|
4203
|
+
step: steps.length + 1,
|
|
4204
|
+
modelVersion,
|
|
4205
|
+
text: responseText || void 0,
|
|
4206
|
+
thoughts: void 0,
|
|
4207
|
+
toolCalls: stepToolCalls,
|
|
4208
|
+
usage: usageTokens,
|
|
4209
|
+
costUsd: stepCostUsd
|
|
4210
|
+
});
|
|
4211
|
+
messages.push({
|
|
4212
|
+
role: "assistant",
|
|
4213
|
+
...responseText.length > 0 ? { content: responseText } : {},
|
|
4214
|
+
tool_calls: assistantToolCalls
|
|
4215
|
+
});
|
|
4216
|
+
messages.push(...toolMessages);
|
|
4217
|
+
}
|
|
4218
|
+
throw new Error(`Tool loop exceeded max steps (${maxSteps}) without final response.`);
|
|
4219
|
+
}
|
|
3446
4220
|
const geminiFunctionTools = buildGeminiFunctionDeclarations(request.tools);
|
|
3447
4221
|
const geminiNativeTools = toGeminiTools(request.modelTools);
|
|
3448
4222
|
const geminiTools = geminiNativeTools ? geminiNativeTools.concat(geminiFunctionTools) : geminiFunctionTools;
|
|
@@ -3592,6 +4366,7 @@ async function runToolLoop(request) {
|
|
|
3592
4366
|
},
|
|
3593
4367
|
async () => {
|
|
3594
4368
|
const { result, outputPayload } = await executeToolCall({
|
|
4369
|
+
callKind: "function",
|
|
3595
4370
|
toolName: entry.toolName,
|
|
3596
4371
|
tool: request.tools[entry.toolName],
|
|
3597
4372
|
rawInput: entry.rawInput
|
|
@@ -3871,26 +4646,26 @@ ${lines}`;
|
|
|
3871
4646
|
}
|
|
3872
4647
|
|
|
3873
4648
|
// src/tools/filesystemTools.ts
|
|
3874
|
-
import
|
|
4649
|
+
import path5 from "path";
|
|
3875
4650
|
import { z as z5 } from "zod";
|
|
3876
4651
|
|
|
3877
4652
|
// src/tools/applyPatch.ts
|
|
3878
|
-
import
|
|
4653
|
+
import path4 from "path";
|
|
3879
4654
|
import { z as z4 } from "zod";
|
|
3880
4655
|
|
|
3881
4656
|
// src/tools/filesystem.ts
|
|
3882
|
-
import { promises as
|
|
3883
|
-
import
|
|
4657
|
+
import { promises as fs3 } from "fs";
|
|
4658
|
+
import path3 from "path";
|
|
3884
4659
|
var InMemoryAgentFilesystem = class {
|
|
3885
4660
|
#files = /* @__PURE__ */ new Map();
|
|
3886
4661
|
#dirs = /* @__PURE__ */ new Map();
|
|
3887
4662
|
#clock = 0;
|
|
3888
4663
|
constructor(initialFiles = {}) {
|
|
3889
|
-
const root =
|
|
4664
|
+
const root = path3.resolve("/");
|
|
3890
4665
|
this.#dirs.set(root, { mtimeMs: this.#nextMtime() });
|
|
3891
4666
|
for (const [filePath, content] of Object.entries(initialFiles)) {
|
|
3892
|
-
const absolutePath =
|
|
3893
|
-
this.#ensureDirSync(
|
|
4667
|
+
const absolutePath = path3.resolve(filePath);
|
|
4668
|
+
this.#ensureDirSync(path3.dirname(absolutePath));
|
|
3894
4669
|
this.#files.set(absolutePath, {
|
|
3895
4670
|
content,
|
|
3896
4671
|
mtimeMs: this.#nextMtime()
|
|
@@ -3898,7 +4673,7 @@ var InMemoryAgentFilesystem = class {
|
|
|
3898
4673
|
}
|
|
3899
4674
|
}
|
|
3900
4675
|
async readTextFile(filePath) {
|
|
3901
|
-
const absolutePath =
|
|
4676
|
+
const absolutePath = path3.resolve(filePath);
|
|
3902
4677
|
const file = this.#files.get(absolutePath);
|
|
3903
4678
|
if (!file) {
|
|
3904
4679
|
throw createNoSuchFileError("open", absolutePath);
|
|
@@ -3906,24 +4681,24 @@ var InMemoryAgentFilesystem = class {
|
|
|
3906
4681
|
return file.content;
|
|
3907
4682
|
}
|
|
3908
4683
|
async writeTextFile(filePath, content) {
|
|
3909
|
-
const absolutePath =
|
|
3910
|
-
const parentPath =
|
|
4684
|
+
const absolutePath = path3.resolve(filePath);
|
|
4685
|
+
const parentPath = path3.dirname(absolutePath);
|
|
3911
4686
|
if (!this.#dirs.has(parentPath)) {
|
|
3912
4687
|
throw createNoSuchFileError("open", parentPath);
|
|
3913
4688
|
}
|
|
3914
4689
|
this.#files.set(absolutePath, { content, mtimeMs: this.#nextMtime() });
|
|
3915
4690
|
}
|
|
3916
4691
|
async deleteFile(filePath) {
|
|
3917
|
-
const absolutePath =
|
|
4692
|
+
const absolutePath = path3.resolve(filePath);
|
|
3918
4693
|
if (!this.#files.delete(absolutePath)) {
|
|
3919
4694
|
throw createNoSuchFileError("unlink", absolutePath);
|
|
3920
4695
|
}
|
|
3921
4696
|
}
|
|
3922
4697
|
async ensureDir(directoryPath) {
|
|
3923
|
-
this.#ensureDirSync(
|
|
4698
|
+
this.#ensureDirSync(path3.resolve(directoryPath));
|
|
3924
4699
|
}
|
|
3925
4700
|
async readDir(directoryPath) {
|
|
3926
|
-
const absolutePath =
|
|
4701
|
+
const absolutePath = path3.resolve(directoryPath);
|
|
3927
4702
|
const directory = this.#dirs.get(absolutePath);
|
|
3928
4703
|
if (!directory) {
|
|
3929
4704
|
throw createNoSuchFileError("scandir", absolutePath);
|
|
@@ -3934,10 +4709,10 @@ var InMemoryAgentFilesystem = class {
|
|
|
3934
4709
|
if (dirPath === absolutePath) {
|
|
3935
4710
|
continue;
|
|
3936
4711
|
}
|
|
3937
|
-
if (
|
|
4712
|
+
if (path3.dirname(dirPath) !== absolutePath) {
|
|
3938
4713
|
continue;
|
|
3939
4714
|
}
|
|
3940
|
-
const name =
|
|
4715
|
+
const name = path3.basename(dirPath);
|
|
3941
4716
|
if (seenNames.has(name)) {
|
|
3942
4717
|
continue;
|
|
3943
4718
|
}
|
|
@@ -3950,10 +4725,10 @@ var InMemoryAgentFilesystem = class {
|
|
|
3950
4725
|
});
|
|
3951
4726
|
}
|
|
3952
4727
|
for (const [filePath, fileRecord] of this.#files.entries()) {
|
|
3953
|
-
if (
|
|
4728
|
+
if (path3.dirname(filePath) !== absolutePath) {
|
|
3954
4729
|
continue;
|
|
3955
4730
|
}
|
|
3956
|
-
const name =
|
|
4731
|
+
const name = path3.basename(filePath);
|
|
3957
4732
|
if (seenNames.has(name)) {
|
|
3958
4733
|
continue;
|
|
3959
4734
|
}
|
|
@@ -3969,7 +4744,7 @@ var InMemoryAgentFilesystem = class {
|
|
|
3969
4744
|
return entries;
|
|
3970
4745
|
}
|
|
3971
4746
|
async stat(entryPath) {
|
|
3972
|
-
const absolutePath =
|
|
4747
|
+
const absolutePath = path3.resolve(entryPath);
|
|
3973
4748
|
const file = this.#files.get(absolutePath);
|
|
3974
4749
|
if (file) {
|
|
3975
4750
|
return { kind: "file", mtimeMs: file.mtimeMs };
|
|
@@ -3985,7 +4760,7 @@ var InMemoryAgentFilesystem = class {
|
|
|
3985
4760
|
return Object.fromEntries(entries.map(([filePath, record]) => [filePath, record.content]));
|
|
3986
4761
|
}
|
|
3987
4762
|
#ensureDirSync(directoryPath) {
|
|
3988
|
-
const absolutePath =
|
|
4763
|
+
const absolutePath = path3.resolve(directoryPath);
|
|
3989
4764
|
const parts = [];
|
|
3990
4765
|
let cursor = absolutePath;
|
|
3991
4766
|
for (; ; ) {
|
|
@@ -3993,7 +4768,7 @@ var InMemoryAgentFilesystem = class {
|
|
|
3993
4768
|
break;
|
|
3994
4769
|
}
|
|
3995
4770
|
parts.push(cursor);
|
|
3996
|
-
const parent =
|
|
4771
|
+
const parent = path3.dirname(cursor);
|
|
3997
4772
|
if (parent === cursor) {
|
|
3998
4773
|
break;
|
|
3999
4774
|
}
|
|
@@ -4016,18 +4791,18 @@ var InMemoryAgentFilesystem = class {
|
|
|
4016
4791
|
};
|
|
4017
4792
|
function createNodeAgentFilesystem() {
|
|
4018
4793
|
return {
|
|
4019
|
-
readTextFile: async (filePath) =>
|
|
4020
|
-
writeTextFile: async (filePath, content) =>
|
|
4021
|
-
deleteFile: async (filePath) =>
|
|
4794
|
+
readTextFile: async (filePath) => fs3.readFile(filePath, "utf8"),
|
|
4795
|
+
writeTextFile: async (filePath, content) => fs3.writeFile(filePath, content, "utf8"),
|
|
4796
|
+
deleteFile: async (filePath) => fs3.unlink(filePath),
|
|
4022
4797
|
ensureDir: async (directoryPath) => {
|
|
4023
|
-
await
|
|
4798
|
+
await fs3.mkdir(directoryPath, { recursive: true });
|
|
4024
4799
|
},
|
|
4025
4800
|
readDir: async (directoryPath) => {
|
|
4026
|
-
const entries = await
|
|
4801
|
+
const entries = await fs3.readdir(directoryPath, { withFileTypes: true });
|
|
4027
4802
|
const result = [];
|
|
4028
4803
|
for (const entry of entries) {
|
|
4029
|
-
const entryPath =
|
|
4030
|
-
const stats = await
|
|
4804
|
+
const entryPath = path3.resolve(directoryPath, entry.name);
|
|
4805
|
+
const stats = await fs3.lstat(entryPath);
|
|
4031
4806
|
result.push({
|
|
4032
4807
|
name: entry.name,
|
|
4033
4808
|
path: entryPath,
|
|
@@ -4038,7 +4813,7 @@ function createNodeAgentFilesystem() {
|
|
|
4038
4813
|
return result;
|
|
4039
4814
|
},
|
|
4040
4815
|
stat: async (entryPath) => {
|
|
4041
|
-
const stats = await
|
|
4816
|
+
const stats = await fs3.lstat(entryPath);
|
|
4042
4817
|
return {
|
|
4043
4818
|
kind: statsToKind(stats),
|
|
4044
4819
|
mtimeMs: stats.mtimeMs
|
|
@@ -4080,12 +4855,104 @@ var UPDATE_FILE_PREFIX = "*** Update File: ";
|
|
|
4080
4855
|
var MOVE_TO_PREFIX = "*** Move to: ";
|
|
4081
4856
|
var END_OF_FILE_LINE = "*** End of File";
|
|
4082
4857
|
var DEFAULT_MAX_PATCH_BYTES = 1024 * 1024;
|
|
4858
|
+
var CODEX_APPLY_PATCH_INPUT_DESCRIPTION = "The entire contents of the apply_patch command";
|
|
4859
|
+
var CODEX_APPLY_PATCH_FREEFORM_TOOL_DESCRIPTION = "Use the `apply_patch` tool to edit files. This is a FREEFORM tool, so do not wrap the patch in JSON.";
|
|
4860
|
+
var CODEX_APPLY_PATCH_LARK_GRAMMAR = [
|
|
4861
|
+
"start: begin_patch hunk+ end_patch",
|
|
4862
|
+
'begin_patch: "*** Begin Patch" LF',
|
|
4863
|
+
'end_patch: "*** End Patch" LF?',
|
|
4864
|
+
"",
|
|
4865
|
+
"hunk: add_hunk | delete_hunk | update_hunk",
|
|
4866
|
+
'add_hunk: "*** Add File: " filename LF add_line+',
|
|
4867
|
+
'delete_hunk: "*** Delete File: " filename LF',
|
|
4868
|
+
'update_hunk: "*** Update File: " filename LF change_move? change?',
|
|
4869
|
+
"",
|
|
4870
|
+
"filename: /(.+)/",
|
|
4871
|
+
'add_line: "+" /(.*)/ LF -> line',
|
|
4872
|
+
"",
|
|
4873
|
+
'change_move: "*** Move to: " filename LF',
|
|
4874
|
+
"change: (change_context | change_line)+ eof_line?",
|
|
4875
|
+
'change_context: ("@@" | "@@ " /(.+)/) LF',
|
|
4876
|
+
'change_line: ("+" | "-" | " ") /(.*)/ LF',
|
|
4877
|
+
'eof_line: "*** End of File" LF',
|
|
4878
|
+
"",
|
|
4879
|
+
"%import common.LF"
|
|
4880
|
+
].join("\n");
|
|
4881
|
+
var CODEX_APPLY_PATCH_JSON_TOOL_DESCRIPTION = [
|
|
4882
|
+
"Use the `apply_patch` tool to edit files.",
|
|
4883
|
+
"Your patch language is a stripped\u2011down, file\u2011oriented diff format designed to be easy to parse and safe to apply. You can think of it as a high\u2011level envelope:",
|
|
4884
|
+
"",
|
|
4885
|
+
"*** Begin Patch",
|
|
4886
|
+
"[ one or more file sections ]",
|
|
4887
|
+
"*** End Patch",
|
|
4888
|
+
"",
|
|
4889
|
+
"Within that envelope, you get a sequence of file operations.",
|
|
4890
|
+
"You MUST include a header to specify the action you are taking.",
|
|
4891
|
+
"Each operation starts with one of three headers:",
|
|
4892
|
+
"",
|
|
4893
|
+
"*** Add File: <path> - create a new file. Every following line is a + line (the initial contents).",
|
|
4894
|
+
"*** Delete File: <path> - remove an existing file. Nothing follows.",
|
|
4895
|
+
"*** Update File: <path> - patch an existing file in place (optionally with a rename).",
|
|
4896
|
+
"",
|
|
4897
|
+
"May be immediately followed by *** Move to: <new path> if you want to rename the file.",
|
|
4898
|
+
"Then one or more \u201Chunks\u201D, each introduced by @@ (optionally followed by a hunk header).",
|
|
4899
|
+
"Within a hunk each line starts with:",
|
|
4900
|
+
"",
|
|
4901
|
+
"For instructions on [context_before] and [context_after]:",
|
|
4902
|
+
"- By default, show 3 lines of code immediately above and 3 lines immediately below each change. If a change is within 3 lines of a previous change, do NOT duplicate the first change\u2019s [context_after] lines in the second change\u2019s [context_before] lines.",
|
|
4903
|
+
"- If 3 lines of context is insufficient to uniquely identify the snippet of code within the file, use the @@ operator to indicate the class or function to which the snippet belongs. For instance, we might have:",
|
|
4904
|
+
"@@ class BaseClass",
|
|
4905
|
+
"[3 lines of pre-context]",
|
|
4906
|
+
"- [old_code]",
|
|
4907
|
+
"+ [new_code]",
|
|
4908
|
+
"[3 lines of post-context]",
|
|
4909
|
+
"",
|
|
4910
|
+
"- If a code block is repeated so many times in a class or function such that even a single `@@` statement and 3 lines of context cannot uniquely identify the snippet of code, you can use multiple `@@` statements to jump to the right context. For instance:",
|
|
4911
|
+
"",
|
|
4912
|
+
"@@ class BaseClass",
|
|
4913
|
+
"@@ def method():",
|
|
4914
|
+
"[3 lines of pre-context]",
|
|
4915
|
+
"- [old_code]",
|
|
4916
|
+
"+ [new_code]",
|
|
4917
|
+
"[3 lines of post-context]",
|
|
4918
|
+
"",
|
|
4919
|
+
"The full grammar definition is below:",
|
|
4920
|
+
"Patch := Begin { FileOp } End",
|
|
4921
|
+
'Begin := "*** Begin Patch" NEWLINE',
|
|
4922
|
+
'End := "*** End Patch" NEWLINE',
|
|
4923
|
+
"FileOp := AddFile | DeleteFile | UpdateFile",
|
|
4924
|
+
'AddFile := "*** Add File: " path NEWLINE { "+" line NEWLINE }',
|
|
4925
|
+
'DeleteFile := "*** Delete File: " path NEWLINE',
|
|
4926
|
+
'UpdateFile := "*** Update File: " path NEWLINE [ MoveTo ] { Hunk }',
|
|
4927
|
+
'MoveTo := "*** Move to: " newPath NEWLINE',
|
|
4928
|
+
'Hunk := "@@" [ header ] NEWLINE { HunkLine } [ "*** End of File" NEWLINE ]',
|
|
4929
|
+
'HunkLine := (" " | "-" | "+") text NEWLINE',
|
|
4930
|
+
"",
|
|
4931
|
+
"A full patch can combine several operations:",
|
|
4932
|
+
"",
|
|
4933
|
+
"*** Begin Patch",
|
|
4934
|
+
"*** Add File: hello.txt",
|
|
4935
|
+
"+Hello world",
|
|
4936
|
+
"*** Update File: src/app.py",
|
|
4937
|
+
"*** Move to: src/main.py",
|
|
4938
|
+
"@@ def greet():",
|
|
4939
|
+
'-print("Hi")',
|
|
4940
|
+
'+print("Hello, world!")',
|
|
4941
|
+
"*** Delete File: obsolete.txt",
|
|
4942
|
+
"*** End Patch",
|
|
4943
|
+
"",
|
|
4944
|
+
"It is important to remember:",
|
|
4945
|
+
"",
|
|
4946
|
+
"- You must include a header with your intended action (Add/Delete/Update)",
|
|
4947
|
+
"- You must prefix new lines with `+` even when creating a new file",
|
|
4948
|
+
"- File references can only be relative, NEVER ABSOLUTE."
|
|
4949
|
+
].join("\n");
|
|
4083
4950
|
var applyPatchToolInputSchema = z4.object({
|
|
4084
|
-
input: z4.string().min(1).describe(
|
|
4951
|
+
input: z4.string().min(1).describe(CODEX_APPLY_PATCH_INPUT_DESCRIPTION)
|
|
4085
4952
|
});
|
|
4086
4953
|
function createApplyPatchTool(options = {}) {
|
|
4087
4954
|
return tool({
|
|
4088
|
-
description: options.description ??
|
|
4955
|
+
description: options.description ?? CODEX_APPLY_PATCH_JSON_TOOL_DESCRIPTION,
|
|
4089
4956
|
inputSchema: applyPatchToolInputSchema,
|
|
4090
4957
|
execute: async ({ input }) => applyPatch({
|
|
4091
4958
|
patch: input,
|
|
@@ -4098,7 +4965,7 @@ function createApplyPatchTool(options = {}) {
|
|
|
4098
4965
|
});
|
|
4099
4966
|
}
|
|
4100
4967
|
async function applyPatch(request) {
|
|
4101
|
-
const cwd =
|
|
4968
|
+
const cwd = path4.resolve(request.cwd ?? process.cwd());
|
|
4102
4969
|
const adapter = request.fs ?? createNodeAgentFilesystem();
|
|
4103
4970
|
const allowOutsideCwd = request.allowOutsideCwd === true;
|
|
4104
4971
|
const patchBytes = Buffer.byteLength(request.patch, "utf8");
|
|
@@ -4120,7 +4987,7 @@ async function applyPatch(request) {
|
|
|
4120
4987
|
kind: "add",
|
|
4121
4988
|
path: absolutePath2
|
|
4122
4989
|
});
|
|
4123
|
-
await adapter.ensureDir(
|
|
4990
|
+
await adapter.ensureDir(path4.dirname(absolutePath2));
|
|
4124
4991
|
await adapter.writeTextFile(absolutePath2, operation.content);
|
|
4125
4992
|
added.push(toDisplayPath(absolutePath2, cwd));
|
|
4126
4993
|
continue;
|
|
@@ -4154,7 +5021,7 @@ async function applyPatch(request) {
|
|
|
4154
5021
|
fromPath: absolutePath,
|
|
4155
5022
|
toPath: destinationPath
|
|
4156
5023
|
});
|
|
4157
|
-
await adapter.ensureDir(
|
|
5024
|
+
await adapter.ensureDir(path4.dirname(destinationPath));
|
|
4158
5025
|
await adapter.writeTextFile(destinationPath, next);
|
|
4159
5026
|
await adapter.deleteFile(absolutePath);
|
|
4160
5027
|
modified.push(toDisplayPath(destinationPath, cwd));
|
|
@@ -4185,22 +5052,22 @@ function resolvePatchPath(rawPath, cwd, allowOutsideCwd) {
|
|
|
4185
5052
|
if (trimmed.length === 0) {
|
|
4186
5053
|
throw new Error("apply_patch failed: empty file path");
|
|
4187
5054
|
}
|
|
4188
|
-
const absolutePath =
|
|
5055
|
+
const absolutePath = path4.isAbsolute(trimmed) ? path4.resolve(trimmed) : path4.resolve(cwd, trimmed);
|
|
4189
5056
|
if (!allowOutsideCwd && !isPathInsideCwd(absolutePath, cwd)) {
|
|
4190
5057
|
throw new Error(`apply_patch failed: path "${trimmed}" resolves outside cwd "${cwd}"`);
|
|
4191
5058
|
}
|
|
4192
5059
|
return absolutePath;
|
|
4193
5060
|
}
|
|
4194
5061
|
function isPathInsideCwd(candidatePath, cwd) {
|
|
4195
|
-
const relative =
|
|
4196
|
-
return relative === "" || !relative.startsWith("..") && !
|
|
5062
|
+
const relative = path4.relative(cwd, candidatePath);
|
|
5063
|
+
return relative === "" || !relative.startsWith("..") && !path4.isAbsolute(relative);
|
|
4197
5064
|
}
|
|
4198
5065
|
function toDisplayPath(absolutePath, cwd) {
|
|
4199
|
-
const relative =
|
|
5066
|
+
const relative = path4.relative(cwd, absolutePath);
|
|
4200
5067
|
if (relative === "") {
|
|
4201
5068
|
return ".";
|
|
4202
5069
|
}
|
|
4203
|
-
if (!relative.startsWith("..") && !
|
|
5070
|
+
if (!relative.startsWith("..") && !path4.isAbsolute(relative)) {
|
|
4204
5071
|
return relative;
|
|
4205
5072
|
}
|
|
4206
5073
|
return absolutePath;
|
|
@@ -4480,6 +5347,8 @@ function formatSummary(added, modified, deleted) {
|
|
|
4480
5347
|
|
|
4481
5348
|
// src/tools/filesystemTools.ts
|
|
4482
5349
|
var DEFAULT_READ_FILE_LINE_LIMIT = 2e3;
|
|
5350
|
+
var DEFAULT_READ_FILES_LINE_LIMIT = 200;
|
|
5351
|
+
var DEFAULT_READ_FILES_CHAR_LIMIT = 4e3;
|
|
4483
5352
|
var DEFAULT_LIST_DIR_LIMIT = 25;
|
|
4484
5353
|
var DEFAULT_LIST_DIR_DEPTH = 2;
|
|
4485
5354
|
var DEFAULT_GREP_LIMIT = 100;
|
|
@@ -4513,12 +5382,29 @@ var codexGrepFilesInputSchema = z5.object({
|
|
|
4513
5382
|
limit: z5.number().int().min(1).optional().describe("Maximum number of file paths to return (defaults to 100).")
|
|
4514
5383
|
});
|
|
4515
5384
|
var applyPatchInputSchema = z5.object({
|
|
4516
|
-
input: z5.string().min(1)
|
|
5385
|
+
input: z5.string().min(1).describe(CODEX_APPLY_PATCH_INPUT_DESCRIPTION)
|
|
4517
5386
|
});
|
|
4518
5387
|
var geminiReadFileInputSchema = z5.object({
|
|
4519
5388
|
file_path: z5.string().min(1),
|
|
4520
|
-
offset: z5.number().int().min(0).
|
|
4521
|
-
limit: z5.number().int().min(1).
|
|
5389
|
+
offset: z5.number().int().min(0).nullish(),
|
|
5390
|
+
limit: z5.number().int().min(1).nullish()
|
|
5391
|
+
});
|
|
5392
|
+
var geminiReadFilesInputSchema = z5.object({
|
|
5393
|
+
paths: z5.array(z5.string().min(1)).min(1),
|
|
5394
|
+
line_offset: z5.number().int().min(0).nullish(),
|
|
5395
|
+
line_limit: z5.number().int().min(1).nullish(),
|
|
5396
|
+
char_offset: z5.number().int().min(0).nullish(),
|
|
5397
|
+
char_limit: z5.number().int().min(1).nullish(),
|
|
5398
|
+
include_line_numbers: z5.boolean().nullish()
|
|
5399
|
+
}).superRefine((value, context) => {
|
|
5400
|
+
const hasLineWindow = value.line_offset !== void 0 || value.line_limit !== void 0;
|
|
5401
|
+
const hasCharWindow = value.char_offset !== void 0 || value.char_limit !== void 0;
|
|
5402
|
+
if (hasLineWindow && hasCharWindow) {
|
|
5403
|
+
context.addIssue({
|
|
5404
|
+
code: z5.ZodIssueCode.custom,
|
|
5405
|
+
message: "Use either line_* or char_* window arguments, not both."
|
|
5406
|
+
});
|
|
5407
|
+
}
|
|
4522
5408
|
});
|
|
4523
5409
|
var geminiWriteFileInputSchema = z5.object({
|
|
4524
5410
|
file_path: z5.string().min(1),
|
|
@@ -4529,31 +5415,41 @@ var geminiReplaceInputSchema = z5.object({
|
|
|
4529
5415
|
instruction: z5.string().min(1),
|
|
4530
5416
|
old_string: z5.string(),
|
|
4531
5417
|
new_string: z5.string(),
|
|
4532
|
-
expected_replacements: z5.number().int().min(1).
|
|
5418
|
+
expected_replacements: z5.number().int().min(1).nullish()
|
|
4533
5419
|
});
|
|
4534
5420
|
var geminiListDirectoryInputSchema = z5.object({
|
|
4535
5421
|
dir_path: z5.string().min(1),
|
|
4536
|
-
ignore: z5.array(z5.string()).
|
|
5422
|
+
ignore: z5.array(z5.string()).nullish(),
|
|
4537
5423
|
file_filtering_options: z5.object({
|
|
4538
|
-
respect_git_ignore: z5.boolean().
|
|
4539
|
-
respect_gemini_ignore: z5.boolean().
|
|
4540
|
-
}).
|
|
5424
|
+
respect_git_ignore: z5.boolean().nullish(),
|
|
5425
|
+
respect_gemini_ignore: z5.boolean().nullish()
|
|
5426
|
+
}).nullish()
|
|
5427
|
+
});
|
|
5428
|
+
var geminiRgSearchInputSchema = z5.object({
|
|
5429
|
+
pattern: z5.string().min(1),
|
|
5430
|
+
path: z5.string().nullish(),
|
|
5431
|
+
glob: z5.string().nullish(),
|
|
5432
|
+
case_sensitive: z5.boolean().nullish(),
|
|
5433
|
+
exclude_pattern: z5.string().nullish(),
|
|
5434
|
+
names_only: z5.boolean().nullish(),
|
|
5435
|
+
max_matches_per_file: z5.number().int().min(1).nullish(),
|
|
5436
|
+
max_results: z5.number().int().min(1).nullish()
|
|
4541
5437
|
});
|
|
4542
5438
|
var geminiGrepSearchInputSchema = z5.object({
|
|
4543
5439
|
pattern: z5.string().min(1),
|
|
4544
|
-
dir_path: z5.string().
|
|
4545
|
-
include: z5.string().
|
|
4546
|
-
exclude_pattern: z5.string().
|
|
4547
|
-
names_only: z5.boolean().
|
|
4548
|
-
max_matches_per_file: z5.number().int().min(1).
|
|
4549
|
-
total_max_matches: z5.number().int().min(1).
|
|
5440
|
+
dir_path: z5.string().nullish(),
|
|
5441
|
+
include: z5.string().nullish(),
|
|
5442
|
+
exclude_pattern: z5.string().nullish(),
|
|
5443
|
+
names_only: z5.boolean().nullish(),
|
|
5444
|
+
max_matches_per_file: z5.number().int().min(1).nullish(),
|
|
5445
|
+
total_max_matches: z5.number().int().min(1).nullish()
|
|
4550
5446
|
});
|
|
4551
5447
|
var geminiGlobInputSchema = z5.object({
|
|
4552
5448
|
pattern: z5.string().min(1),
|
|
4553
|
-
dir_path: z5.string().
|
|
4554
|
-
case_sensitive: z5.boolean().
|
|
4555
|
-
respect_git_ignore: z5.boolean().
|
|
4556
|
-
respect_gemini_ignore: z5.boolean().
|
|
5449
|
+
dir_path: z5.string().nullish(),
|
|
5450
|
+
case_sensitive: z5.boolean().nullish(),
|
|
5451
|
+
respect_git_ignore: z5.boolean().nullish(),
|
|
5452
|
+
respect_gemini_ignore: z5.boolean().nullish()
|
|
4557
5453
|
});
|
|
4558
5454
|
function resolveFilesystemToolProfile(model, profile = "auto") {
|
|
4559
5455
|
if (profile !== "auto") {
|
|
@@ -4597,7 +5493,7 @@ function createCodexFilesystemToolSet(options = {}) {
|
|
|
4597
5493
|
}
|
|
4598
5494
|
function createGeminiFilesystemToolSet(options = {}) {
|
|
4599
5495
|
return {
|
|
4600
|
-
read_file:
|
|
5496
|
+
read_file: createGeminiReadFileTool(options),
|
|
4601
5497
|
write_file: createWriteFileTool(options),
|
|
4602
5498
|
replace: createReplaceTool(options),
|
|
4603
5499
|
list_directory: createListDirectoryTool(options),
|
|
@@ -4609,10 +5505,14 @@ function createModelAgnosticFilesystemToolSet(options = {}) {
|
|
|
4609
5505
|
return createGeminiFilesystemToolSet(options);
|
|
4610
5506
|
}
|
|
4611
5507
|
function createCodexApplyPatchTool(options = {}) {
|
|
4612
|
-
return
|
|
4613
|
-
description:
|
|
4614
|
-
|
|
4615
|
-
|
|
5508
|
+
return customTool({
|
|
5509
|
+
description: CODEX_APPLY_PATCH_FREEFORM_TOOL_DESCRIPTION,
|
|
5510
|
+
format: {
|
|
5511
|
+
type: "grammar",
|
|
5512
|
+
syntax: "lark",
|
|
5513
|
+
definition: CODEX_APPLY_PATCH_LARK_GRAMMAR
|
|
5514
|
+
},
|
|
5515
|
+
execute: async (input) => {
|
|
4616
5516
|
const runtime = resolveRuntime(options);
|
|
4617
5517
|
const result = await applyPatch({
|
|
4618
5518
|
patch: input,
|
|
@@ -4656,13 +5556,20 @@ function createGrepFilesTool(options = {}) {
|
|
|
4656
5556
|
execute: async (input) => grepFilesCodex(input, options)
|
|
4657
5557
|
});
|
|
4658
5558
|
}
|
|
4659
|
-
function
|
|
5559
|
+
function createGeminiReadFileTool(options = {}) {
|
|
4660
5560
|
return tool({
|
|
4661
|
-
description: "Reads and returns content of a specified file.",
|
|
5561
|
+
description: "Reads and returns the content of a specified file. Supports optional 0-based line offset and line limit.",
|
|
4662
5562
|
inputSchema: geminiReadFileInputSchema,
|
|
4663
5563
|
execute: async (input) => readFileGemini(input, options)
|
|
4664
5564
|
});
|
|
4665
5565
|
}
|
|
5566
|
+
function createReadFilesTool(options = {}) {
|
|
5567
|
+
return tool({
|
|
5568
|
+
description: "Reads one or more files with optional line-based or character-based slicing, similar to a controlled head/tail view.",
|
|
5569
|
+
inputSchema: geminiReadFilesInputSchema,
|
|
5570
|
+
execute: async (input) => readFilesGemini(input, options)
|
|
5571
|
+
});
|
|
5572
|
+
}
|
|
4666
5573
|
function createWriteFileTool(options = {}) {
|
|
4667
5574
|
return tool({
|
|
4668
5575
|
description: "Writes content to a specified file in the local filesystem.",
|
|
@@ -4691,6 +5598,13 @@ function createGrepSearchTool(options = {}) {
|
|
|
4691
5598
|
execute: async (input) => grepSearchGemini(input, options)
|
|
4692
5599
|
});
|
|
4693
5600
|
}
|
|
5601
|
+
function createRgSearchTool(options = {}) {
|
|
5602
|
+
return tool({
|
|
5603
|
+
description: "Searches for a regex pattern within file contents.",
|
|
5604
|
+
inputSchema: geminiRgSearchInputSchema,
|
|
5605
|
+
execute: async (input) => rgSearchGemini(input, options)
|
|
5606
|
+
});
|
|
5607
|
+
}
|
|
4694
5608
|
function createGlobTool(options = {}) {
|
|
4695
5609
|
return tool({
|
|
4696
5610
|
description: "Finds files matching glob patterns, sorted by modification time (newest first).",
|
|
@@ -4700,7 +5614,7 @@ function createGlobTool(options = {}) {
|
|
|
4700
5614
|
}
|
|
4701
5615
|
async function readFileCodex(input, options) {
|
|
4702
5616
|
const runtime = resolveRuntime(options);
|
|
4703
|
-
if (!
|
|
5617
|
+
if (!path5.isAbsolute(input.file_path)) {
|
|
4704
5618
|
throw new Error("file_path must be an absolute path");
|
|
4705
5619
|
}
|
|
4706
5620
|
const filePath = resolvePathWithPolicy(input.file_path, runtime.cwd, runtime.allowOutsideCwd);
|
|
@@ -4751,7 +5665,7 @@ async function readFileCodex(input, options) {
|
|
|
4751
5665
|
}
|
|
4752
5666
|
async function listDirectoryCodex(input, options) {
|
|
4753
5667
|
const runtime = resolveRuntime(options);
|
|
4754
|
-
if (!
|
|
5668
|
+
if (!path5.isAbsolute(input.dir_path)) {
|
|
4755
5669
|
throw new Error("dir_path must be an absolute path");
|
|
4756
5670
|
}
|
|
4757
5671
|
const dirPath = resolvePathWithPolicy(input.dir_path, runtime.cwd, runtime.allowOutsideCwd);
|
|
@@ -4848,9 +5762,6 @@ async function readFileGemini(input, options) {
|
|
|
4848
5762
|
path: filePath
|
|
4849
5763
|
});
|
|
4850
5764
|
const content = await runtime.filesystem.readTextFile(filePath);
|
|
4851
|
-
if (input.offset === void 0 && input.limit === void 0) {
|
|
4852
|
-
return content;
|
|
4853
|
-
}
|
|
4854
5765
|
const lines = splitLines(content);
|
|
4855
5766
|
const offset = Math.max(0, input.offset ?? 0);
|
|
4856
5767
|
const limit = input.limit ?? DEFAULT_READ_FILE_LINE_LIMIT;
|
|
@@ -4858,7 +5769,59 @@ async function readFileGemini(input, options) {
|
|
|
4858
5769
|
return "";
|
|
4859
5770
|
}
|
|
4860
5771
|
const end = Math.min(lines.length, offset + limit);
|
|
4861
|
-
return lines.slice(offset, end).
|
|
5772
|
+
return lines.slice(offset, end).map(
|
|
5773
|
+
(line, index) => `L${offset + index + 1}: ${truncateAtCodePointBoundary(line ?? "", runtime.maxLineLength)}`
|
|
5774
|
+
).join("\n");
|
|
5775
|
+
}
|
|
5776
|
+
async function readFilesGemini(input, options) {
|
|
5777
|
+
const runtime = resolveRuntime(options);
|
|
5778
|
+
const useCharWindow = input.char_offset !== void 0 || input.char_limit !== void 0;
|
|
5779
|
+
const lineOffset = Math.max(0, input.line_offset ?? 0);
|
|
5780
|
+
const lineLimit = input.line_limit ?? DEFAULT_READ_FILES_LINE_LIMIT;
|
|
5781
|
+
const charOffset = Math.max(0, input.char_offset ?? 0);
|
|
5782
|
+
const charLimit = input.char_limit ?? DEFAULT_READ_FILES_CHAR_LIMIT;
|
|
5783
|
+
const includeLineNumbers = input.include_line_numbers !== false;
|
|
5784
|
+
const sections = [];
|
|
5785
|
+
for (const rawPath of input.paths) {
|
|
5786
|
+
const filePath = resolvePathWithPolicy(rawPath, runtime.cwd, runtime.allowOutsideCwd);
|
|
5787
|
+
await runAccessHook2(runtime, {
|
|
5788
|
+
cwd: runtime.cwd,
|
|
5789
|
+
tool: "read_files",
|
|
5790
|
+
action: "read",
|
|
5791
|
+
path: filePath
|
|
5792
|
+
});
|
|
5793
|
+
const content = await runtime.filesystem.readTextFile(filePath);
|
|
5794
|
+
const displayPath = normalizeSlashes(toDisplayPath2(filePath, runtime.cwd));
|
|
5795
|
+
sections.push(`==> ${displayPath} <==`);
|
|
5796
|
+
if (useCharWindow) {
|
|
5797
|
+
if (charOffset >= content.length) {
|
|
5798
|
+
sections.push("");
|
|
5799
|
+
continue;
|
|
5800
|
+
}
|
|
5801
|
+
const end2 = Math.min(content.length, charOffset + charLimit);
|
|
5802
|
+
sections.push(content.slice(charOffset, end2));
|
|
5803
|
+
continue;
|
|
5804
|
+
}
|
|
5805
|
+
const lines = splitLines(content);
|
|
5806
|
+
if (lineOffset >= lines.length) {
|
|
5807
|
+
sections.push("");
|
|
5808
|
+
continue;
|
|
5809
|
+
}
|
|
5810
|
+
const end = Math.min(lines.length, lineOffset + lineLimit);
|
|
5811
|
+
const selected = lines.slice(lineOffset, end);
|
|
5812
|
+
if (includeLineNumbers) {
|
|
5813
|
+
for (let index = 0; index < selected.length; index += 1) {
|
|
5814
|
+
const lineNumber = lineOffset + index + 1;
|
|
5815
|
+
const line = selected[index] ?? "";
|
|
5816
|
+
sections.push(
|
|
5817
|
+
`L${lineNumber}: ${truncateAtCodePointBoundary(line, runtime.maxLineLength)}`
|
|
5818
|
+
);
|
|
5819
|
+
}
|
|
5820
|
+
continue;
|
|
5821
|
+
}
|
|
5822
|
+
sections.push(selected.join("\n"));
|
|
5823
|
+
}
|
|
5824
|
+
return sections.join("\n");
|
|
4862
5825
|
}
|
|
4863
5826
|
async function writeFileGemini(input, options) {
|
|
4864
5827
|
const runtime = resolveRuntime(options);
|
|
@@ -4869,7 +5832,7 @@ async function writeFileGemini(input, options) {
|
|
|
4869
5832
|
action: "write",
|
|
4870
5833
|
path: filePath
|
|
4871
5834
|
});
|
|
4872
|
-
await runtime.filesystem.ensureDir(
|
|
5835
|
+
await runtime.filesystem.ensureDir(path5.dirname(filePath));
|
|
4873
5836
|
await runtime.filesystem.writeTextFile(filePath, input.content);
|
|
4874
5837
|
return `Successfully wrote file: ${toDisplayPath2(filePath, runtime.cwd)}`;
|
|
4875
5838
|
}
|
|
@@ -4890,7 +5853,7 @@ async function replaceFileContentGemini(input, options) {
|
|
|
4890
5853
|
originalContent = await runtime.filesystem.readTextFile(filePath);
|
|
4891
5854
|
} catch (error) {
|
|
4892
5855
|
if (isNoEntError(error) && oldValue.length === 0) {
|
|
4893
|
-
await runtime.filesystem.ensureDir(
|
|
5856
|
+
await runtime.filesystem.ensureDir(path5.dirname(filePath));
|
|
4894
5857
|
await runtime.filesystem.writeTextFile(filePath, newValue);
|
|
4895
5858
|
return `Successfully wrote new file: ${toDisplayPath2(filePath, runtime.cwd)}`;
|
|
4896
5859
|
}
|
|
@@ -4941,25 +5904,25 @@ async function listDirectoryGemini(input, options) {
|
|
|
4941
5904
|
return label;
|
|
4942
5905
|
}).join("\n");
|
|
4943
5906
|
}
|
|
4944
|
-
async function
|
|
5907
|
+
async function rgSearchGemini(input, options, toolName = "rg_search") {
|
|
4945
5908
|
const runtime = resolveRuntime(options);
|
|
4946
5909
|
const pattern = input.pattern.trim();
|
|
4947
5910
|
if (pattern.length === 0) {
|
|
4948
5911
|
throw new Error("pattern must not be empty");
|
|
4949
5912
|
}
|
|
4950
|
-
const
|
|
5913
|
+
const glob = input.glob?.trim();
|
|
4951
5914
|
const searchPath = resolvePathWithPolicy(
|
|
4952
|
-
input.
|
|
5915
|
+
input.path ?? runtime.cwd,
|
|
4953
5916
|
runtime.cwd,
|
|
4954
5917
|
runtime.allowOutsideCwd
|
|
4955
5918
|
);
|
|
4956
5919
|
await runAccessHook2(runtime, {
|
|
4957
5920
|
cwd: runtime.cwd,
|
|
4958
|
-
tool:
|
|
5921
|
+
tool: toolName,
|
|
4959
5922
|
action: "search",
|
|
4960
5923
|
path: searchPath,
|
|
4961
5924
|
pattern,
|
|
4962
|
-
include
|
|
5925
|
+
include: glob
|
|
4963
5926
|
});
|
|
4964
5927
|
const searchPathInfo = await runtime.filesystem.stat(searchPath);
|
|
4965
5928
|
const filesToScan = await collectSearchFiles({
|
|
@@ -4968,10 +5931,10 @@ async function grepSearchGemini(input, options) {
|
|
|
4968
5931
|
rootKind: searchPathInfo.kind,
|
|
4969
5932
|
maxScannedFiles: runtime.grepMaxScannedFiles
|
|
4970
5933
|
});
|
|
4971
|
-
const matcher =
|
|
4972
|
-
const patternRegex = compileRegex(pattern);
|
|
5934
|
+
const matcher = glob ? createGlobMatcher(glob) : null;
|
|
5935
|
+
const patternRegex = compileRegex(pattern, input.case_sensitive === true ? "m" : "im");
|
|
4973
5936
|
const excludeRegex = input.exclude_pattern ? compileRegex(input.exclude_pattern) : null;
|
|
4974
|
-
const totalMaxMatches = input.
|
|
5937
|
+
const totalMaxMatches = input.max_results ?? DEFAULT_GREP_LIMIT;
|
|
4975
5938
|
const perFileMaxMatches = input.max_matches_per_file ?? Number.POSITIVE_INFINITY;
|
|
4976
5939
|
const matches = [];
|
|
4977
5940
|
const fileMatches = /* @__PURE__ */ new Set();
|
|
@@ -5026,6 +5989,21 @@ async function grepSearchGemini(input, options) {
|
|
|
5026
5989
|
}
|
|
5027
5990
|
return matches.slice(0, totalMaxMatches).map((match) => `${match.filePath}:${match.lineNumber}:${match.line ?? ""}`).join("\n");
|
|
5028
5991
|
}
|
|
5992
|
+
async function grepSearchGemini(input, options) {
|
|
5993
|
+
return rgSearchGemini(
|
|
5994
|
+
{
|
|
5995
|
+
pattern: input.pattern,
|
|
5996
|
+
path: input.dir_path,
|
|
5997
|
+
glob: input.include,
|
|
5998
|
+
exclude_pattern: input.exclude_pattern,
|
|
5999
|
+
names_only: input.names_only,
|
|
6000
|
+
max_matches_per_file: input.max_matches_per_file,
|
|
6001
|
+
max_results: input.total_max_matches
|
|
6002
|
+
},
|
|
6003
|
+
options,
|
|
6004
|
+
"grep_search"
|
|
6005
|
+
);
|
|
6006
|
+
}
|
|
5029
6007
|
async function globFilesGemini(input, options) {
|
|
5030
6008
|
const runtime = resolveRuntime(options);
|
|
5031
6009
|
const dirPath = resolvePathWithPolicy(
|
|
@@ -5053,7 +6031,7 @@ async function globFilesGemini(input, options) {
|
|
|
5053
6031
|
});
|
|
5054
6032
|
const matched = [];
|
|
5055
6033
|
for (const filePath of files) {
|
|
5056
|
-
const relativePath = normalizeSlashes(
|
|
6034
|
+
const relativePath = normalizeSlashes(path5.relative(dirPath, filePath));
|
|
5057
6035
|
if (!matcher(relativePath)) {
|
|
5058
6036
|
continue;
|
|
5059
6037
|
}
|
|
@@ -5067,11 +6045,11 @@ async function globFilesGemini(input, options) {
|
|
|
5067
6045
|
return "No files found.";
|
|
5068
6046
|
}
|
|
5069
6047
|
matched.sort((left, right) => right.mtimeMs - left.mtimeMs);
|
|
5070
|
-
return matched.map((entry) => normalizeSlashes(
|
|
6048
|
+
return matched.map((entry) => normalizeSlashes(toDisplayPath2(entry.filePath, runtime.cwd))).join("\n");
|
|
5071
6049
|
}
|
|
5072
6050
|
function resolveRuntime(options) {
|
|
5073
6051
|
return {
|
|
5074
|
-
cwd:
|
|
6052
|
+
cwd: path5.resolve(options.cwd ?? process.cwd()),
|
|
5075
6053
|
filesystem: options.fs ?? createNodeAgentFilesystem(),
|
|
5076
6054
|
allowOutsideCwd: options.allowOutsideCwd === true,
|
|
5077
6055
|
checkAccess: options.checkAccess,
|
|
@@ -5102,22 +6080,22 @@ function mapApplyPatchAction(action) {
|
|
|
5102
6080
|
return "move";
|
|
5103
6081
|
}
|
|
5104
6082
|
function resolvePathWithPolicy(inputPath, cwd, allowOutsideCwd) {
|
|
5105
|
-
const absolutePath =
|
|
6083
|
+
const absolutePath = path5.isAbsolute(inputPath) ? path5.resolve(inputPath) : path5.resolve(cwd, inputPath);
|
|
5106
6084
|
if (!allowOutsideCwd && !isPathInsideCwd2(absolutePath, cwd)) {
|
|
5107
6085
|
throw new Error(`path "${inputPath}" resolves outside cwd "${cwd}"`);
|
|
5108
6086
|
}
|
|
5109
6087
|
return absolutePath;
|
|
5110
6088
|
}
|
|
5111
6089
|
function isPathInsideCwd2(candidatePath, cwd) {
|
|
5112
|
-
const relative =
|
|
5113
|
-
return relative === "" || !relative.startsWith("..") && !
|
|
6090
|
+
const relative = path5.relative(cwd, candidatePath);
|
|
6091
|
+
return relative === "" || !relative.startsWith("..") && !path5.isAbsolute(relative);
|
|
5114
6092
|
}
|
|
5115
6093
|
function toDisplayPath2(absolutePath, cwd) {
|
|
5116
|
-
const relative =
|
|
6094
|
+
const relative = path5.relative(cwd, absolutePath);
|
|
5117
6095
|
if (relative === "") {
|
|
5118
6096
|
return ".";
|
|
5119
6097
|
}
|
|
5120
|
-
if (!relative.startsWith("..") && !
|
|
6098
|
+
if (!relative.startsWith("..") && !path5.isAbsolute(relative)) {
|
|
5121
6099
|
return relative;
|
|
5122
6100
|
}
|
|
5123
6101
|
return absolutePath;
|
|
@@ -5327,9 +6305,9 @@ async function collectSearchFiles(params) {
|
|
|
5327
6305
|
}
|
|
5328
6306
|
return files;
|
|
5329
6307
|
}
|
|
5330
|
-
function compileRegex(pattern) {
|
|
6308
|
+
function compileRegex(pattern, flags = "m") {
|
|
5331
6309
|
try {
|
|
5332
|
-
return new RegExp(pattern,
|
|
6310
|
+
return new RegExp(pattern, flags);
|
|
5333
6311
|
} catch (error) {
|
|
5334
6312
|
const message = error instanceof Error ? error.message : String(error);
|
|
5335
6313
|
throw new Error(`invalid regex pattern: ${message}`);
|
|
@@ -5344,7 +6322,7 @@ function createGlobMatcher(pattern, caseSensitive = false) {
|
|
|
5344
6322
|
}));
|
|
5345
6323
|
return (candidatePath) => {
|
|
5346
6324
|
const normalizedPath = normalizeSlashes(candidatePath);
|
|
5347
|
-
const basename =
|
|
6325
|
+
const basename = path5.posix.basename(normalizedPath);
|
|
5348
6326
|
return compiled.some(
|
|
5349
6327
|
(entry) => entry.regex.test(entry.applyToBasename ? basename : normalizedPath)
|
|
5350
6328
|
);
|
|
@@ -5507,6 +6485,13 @@ function mergeToolSets(base, extra) {
|
|
|
5507
6485
|
return merged;
|
|
5508
6486
|
}
|
|
5509
6487
|
export {
|
|
6488
|
+
CODEX_APPLY_PATCH_FREEFORM_TOOL_DESCRIPTION,
|
|
6489
|
+
CODEX_APPLY_PATCH_JSON_TOOL_DESCRIPTION,
|
|
6490
|
+
CODEX_APPLY_PATCH_LARK_GRAMMAR,
|
|
6491
|
+
FIREWORKS_DEFAULT_GLM_MODEL,
|
|
6492
|
+
FIREWORKS_DEFAULT_KIMI_MODEL,
|
|
6493
|
+
FIREWORKS_DEFAULT_MINIMAX_MODEL,
|
|
6494
|
+
FIREWORKS_MODEL_IDS,
|
|
5510
6495
|
InMemoryAgentFilesystem,
|
|
5511
6496
|
LlmJsonCallError,
|
|
5512
6497
|
appendMarkdownSourcesSection,
|
|
@@ -5519,6 +6504,7 @@ export {
|
|
|
5519
6504
|
createCodexReadFileTool,
|
|
5520
6505
|
createFilesystemToolSetForModel,
|
|
5521
6506
|
createGeminiFilesystemToolSet,
|
|
6507
|
+
createGeminiReadFileTool,
|
|
5522
6508
|
createGlobTool,
|
|
5523
6509
|
createGrepFilesTool,
|
|
5524
6510
|
createGrepSearchTool,
|
|
@@ -5527,9 +6513,11 @@ export {
|
|
|
5527
6513
|
createListDirectoryTool,
|
|
5528
6514
|
createModelAgnosticFilesystemToolSet,
|
|
5529
6515
|
createNodeAgentFilesystem,
|
|
5530
|
-
|
|
6516
|
+
createReadFilesTool,
|
|
5531
6517
|
createReplaceTool,
|
|
6518
|
+
createRgSearchTool,
|
|
5532
6519
|
createWriteFileTool,
|
|
6520
|
+
customTool,
|
|
5533
6521
|
encodeChatGptAuthJson,
|
|
5534
6522
|
encodeChatGptAuthJsonB64,
|
|
5535
6523
|
estimateCallCostUsd,
|
|
@@ -5540,12 +6528,14 @@ export {
|
|
|
5540
6528
|
generateText,
|
|
5541
6529
|
getChatGptAuthProfile,
|
|
5542
6530
|
getCurrentToolCallContext,
|
|
6531
|
+
isFireworksModelId,
|
|
5543
6532
|
isGeminiModelId,
|
|
5544
6533
|
loadEnvFromFile,
|
|
5545
6534
|
loadLocalEnv,
|
|
5546
6535
|
parseJsonFromLlmText,
|
|
5547
6536
|
refreshChatGptOauthToken,
|
|
5548
6537
|
resolveFilesystemToolProfile,
|
|
6538
|
+
resolveFireworksModelId,
|
|
5549
6539
|
runAgentLoop,
|
|
5550
6540
|
runToolLoop,
|
|
5551
6541
|
sanitisePartForLogging,
|