@ljoukov/llm 7.0.17 → 7.0.19

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/dist/index.d.cts CHANGED
@@ -15,8 +15,9 @@ type LlmUsageTokens = {
15
15
  readonly totalTokens?: number;
16
16
  readonly toolUsePromptTokens?: number;
17
17
  };
18
- declare function estimateCallCostUsd({ modelId, tokens, responseImages, imageSize, imageQuality, }: {
18
+ declare function estimateCallCostUsd({ modelId, pricingModelId, tokens, responseImages, imageSize, imageQuality, }: {
19
19
  modelId: string;
20
+ pricingModelId?: string;
20
21
  tokens: LlmUsageTokens | undefined;
21
22
  responseImages: number;
22
23
  imageSize?: string;
package/dist/index.d.ts CHANGED
@@ -15,8 +15,9 @@ type LlmUsageTokens = {
15
15
  readonly totalTokens?: number;
16
16
  readonly toolUsePromptTokens?: number;
17
17
  };
18
- declare function estimateCallCostUsd({ modelId, tokens, responseImages, imageSize, imageQuality, }: {
18
+ declare function estimateCallCostUsd({ modelId, pricingModelId, tokens, responseImages, imageSize, imageQuality, }: {
19
19
  modelId: string;
20
+ pricingModelId?: string;
20
21
  tokens: LlmUsageTokens | undefined;
21
22
  responseImages: number;
22
23
  imageSize?: string;
package/dist/index.js CHANGED
@@ -344,6 +344,7 @@ function resolveChatGptServiceTier(model) {
344
344
  // src/openai/pricing.ts
345
345
  var OPENAI_GPT_55_FAST_MODEL_IDS = ["gpt-5.5-fast", "chatgpt-gpt-5.5-fast"];
346
346
  var OPENAI_GPT_55_STANDARD_MODEL_IDS = ["gpt-5.5", "chatgpt-gpt-5.5"];
347
+ var OPENAI_GPT_55_CONCRETE_MODEL_ID_RE = /^(?:chatgpt-)?gpt-5\.5-\d{4}-\d{2}-\d{2}$/u;
347
348
  var OPENAI_GPT_54_FAST_MODEL_IDS = ["gpt-5.4-fast", "chatgpt-gpt-5.4-fast"];
348
349
  var OPENAI_GPT_54_MINI_MODEL_IDS = ["gpt-5.4-mini", "chatgpt-gpt-5.4-mini"];
349
350
  var OPENAI_GPT_54_NANO_MODEL_IDS = ["gpt-5.4-nano"];
@@ -410,7 +411,7 @@ function getOpenAiPricing(modelId) {
410
411
  if (OPENAI_GPT_55_FAST_MODEL_IDS.includes(modelId)) {
411
412
  return OPENAI_GPT_55_PRIORITY_PRICING;
412
413
  }
413
- if (OPENAI_GPT_55_STANDARD_MODEL_IDS.includes(modelId)) {
414
+ if (OPENAI_GPT_55_STANDARD_MODEL_IDS.includes(modelId) || OPENAI_GPT_55_CONCRETE_MODEL_ID_RE.test(modelId)) {
414
415
  return OPENAI_GPT_55_PRICING;
415
416
  }
416
417
  if (OPENAI_GPT_54_FAST_MODEL_IDS.includes(modelId)) {
@@ -443,12 +444,14 @@ function resolveUsageNumber(value) {
443
444
  }
444
445
  function estimateCallCostUsd({
445
446
  modelId,
447
+ pricingModelId,
446
448
  tokens,
447
449
  responseImages,
448
450
  imageSize,
449
451
  imageQuality
450
452
  }) {
451
- const openAiImagePricing = getOpenAiImagePricing(modelId);
453
+ const pricingModelIds = resolvePricingModelIds(modelId, pricingModelId);
454
+ const openAiImagePricing = resolvePricing(pricingModelIds, getOpenAiImagePricing);
452
455
  if (openAiImagePricing) {
453
456
  return estimateOpenAiImageCostUsd({
454
457
  pricing: openAiImagePricing,
@@ -468,7 +471,7 @@ function estimateCallCostUsd({
468
471
  const toolUsePromptTokens = resolveUsageNumber(tokens.toolUsePromptTokens);
469
472
  const promptTokenTotal = promptTokens + toolUsePromptTokens;
470
473
  const nonCachedPrompt = Math.max(0, promptTokenTotal - cachedTokens);
471
- const imagePreviewPricing = getGeminiImagePricing(modelId);
474
+ const imagePreviewPricing = resolvePricing(pricingModelIds, getGeminiImagePricing);
472
475
  if (imagePreviewPricing) {
473
476
  const resolvedImageSize = imageSize && imagePreviewPricing.imagePrices[imageSize] ? imageSize : "2K";
474
477
  const imageRate = imagePreviewPricing.imagePrices[resolvedImageSize] ?? 0;
@@ -488,7 +491,7 @@ function estimateCallCostUsd({
488
491
  const imageOutputCost = imageTokensForPricing * imagePreviewPricing.outputImageRate;
489
492
  return inputCost + cachedCost + textOutputCost + imageOutputCost;
490
493
  }
491
- const geminiPricing = getGeminiProPricing(modelId);
494
+ const geminiPricing = resolvePricing(pricingModelIds, getGeminiProPricing);
492
495
  if (geminiPricing) {
493
496
  const useHighTier = promptTokenTotal > geminiPricing.threshold;
494
497
  const inputRate = useHighTier ? geminiPricing.inputRateHigh : geminiPricing.inputRateLow;
@@ -500,7 +503,7 @@ function estimateCallCostUsd({
500
503
  const outputCost = outputTokens * outputRate;
501
504
  return inputCost + cachedCost + outputCost;
502
505
  }
503
- const fireworksPricing = getFireworksPricing(modelId);
506
+ const fireworksPricing = resolvePricing(pricingModelIds, getFireworksPricing);
504
507
  if (fireworksPricing) {
505
508
  const inputCost = nonCachedPrompt * fireworksPricing.inputRate;
506
509
  const cachedCost = cachedTokens * fireworksPricing.cachedRate;
@@ -508,7 +511,7 @@ function estimateCallCostUsd({
508
511
  const outputCost = outputTokens * fireworksPricing.outputRate;
509
512
  return inputCost + cachedCost + outputCost;
510
513
  }
511
- const openAiPricing = getOpenAiPricing(modelId);
514
+ const openAiPricing = resolvePricing(pricingModelIds, getOpenAiPricing);
512
515
  if (openAiPricing) {
513
516
  const inputCost = nonCachedPrompt * openAiPricing.inputRate;
514
517
  const cachedCost = cachedTokens * openAiPricing.cachedRate;
@@ -518,6 +521,21 @@ function estimateCallCostUsd({
518
521
  }
519
522
  return 0;
520
523
  }
524
+ function resolvePricingModelIds(modelId, pricingModelId) {
525
+ if (pricingModelId && pricingModelId !== modelId) {
526
+ return [pricingModelId, modelId];
527
+ }
528
+ return [modelId];
529
+ }
530
+ function resolvePricing(modelIds, resolve) {
531
+ for (const modelId of modelIds) {
532
+ const pricing = resolve(modelId);
533
+ if (pricing) {
534
+ return pricing;
535
+ }
536
+ }
537
+ return void 0;
538
+ }
521
539
  function estimateOpenAiImageCostUsd({
522
540
  pricing,
523
541
  responseImages,
@@ -557,6 +575,10 @@ function resolveOpenAiImagePriceResolution(imageSize) {
557
575
  import os2 from "os";
558
576
  import { TextDecoder as TextDecoder2 } from "util";
559
577
 
578
+ // src/utils/env.ts
579
+ import fs from "fs";
580
+ import path from "path";
581
+
560
582
  // src/utils/runtimeSingleton.ts
561
583
  var runtimeSingletonStoreKey = /* @__PURE__ */ Symbol.for("@ljoukov/llm.runtimeSingletonStore");
562
584
  function getRuntimeSingletonStore() {
@@ -585,16 +607,7 @@ function getRuntimeSingleton(key, create) {
585
607
  return createdValue;
586
608
  }
587
609
 
588
- // src/openai/chatgpt-auth.ts
589
- import { Buffer as Buffer2 } from "buffer";
590
- import fs2 from "fs";
591
- import os from "os";
592
- import path2 from "path";
593
- import { z } from "zod";
594
-
595
610
  // src/utils/env.ts
596
- import fs from "fs";
597
- import path from "path";
598
611
  var envState = getRuntimeSingleton(/* @__PURE__ */ Symbol.for("@ljoukov/llm.envState"), () => ({
599
612
  envLoaded: false
600
613
  }));
@@ -656,6 +669,11 @@ function parseEnvLine(line) {
656
669
  }
657
670
 
658
671
  // src/openai/chatgpt-auth.ts
672
+ import { Buffer as Buffer2 } from "buffer";
673
+ import fs2 from "fs";
674
+ import os from "os";
675
+ import path2 from "path";
676
+ import { z } from "zod";
659
677
  var CHATGPT_AUTH_TOKEN_PROVIDER_URL_ENV = "CHATGPT_AUTH_TOKEN_PROVIDER_URL";
660
678
  var CHATGPT_AUTH_TOKEN_PROVIDER_STORE_ENV = "CHATGPT_AUTH_TOKEN_PROVIDER_STORE";
661
679
  var CHATGPT_AUTH_API_KEY_ENV = "CHATGPT_AUTH_API_KEY";
@@ -1563,19 +1581,21 @@ function createAbortError(reason) {
1563
1581
 
1564
1582
  // src/openai/chatgpt-codex.ts
1565
1583
  var CHATGPT_CODEX_ENDPOINT = "https://chatgpt.com/backend-api/codex/responses";
1584
+ var CHATGPT_CODEX_ENDPOINT_ENV = "CHATGPT_CODEX_ENDPOINT";
1585
+ var CHATGPT_CODEX_PROXY_URL_ENV = "CHATGPT_CODEX_PROXY_URL";
1586
+ var CHATGPT_CODEX_PROXY_API_KEY_ENV = "CHATGPT_CODEX_PROXY_API_KEY";
1566
1587
  var CHATGPT_RESPONSES_EXPERIMENTAL_HEADER = "responses=experimental";
1567
1588
  var chatGptCodexState = getRuntimeSingleton(/* @__PURE__ */ Symbol.for("@ljoukov/llm.chatGptCodexState"), () => ({
1568
1589
  cachedResponsesWebSocketMode: null,
1569
1590
  chatGptResponsesWebSocketDisabled: false
1570
1591
  }));
1571
1592
  async function streamChatGptCodexResponse(options) {
1572
- const { access, accountId } = await getChatGptAuthProfile();
1593
+ const endpointConfig = await resolveChatGptCodexEndpointConfig();
1573
1594
  const mode = resolveChatGptResponsesWebSocketMode();
1574
1595
  const fallbackStreamFactory = () => {
1575
1596
  const streamPromise = streamChatGptCodexResponseSse({
1576
1597
  request: options.request,
1577
- access,
1578
- accountId,
1598
+ endpointConfig,
1579
1599
  sessionId: options.sessionId,
1580
1600
  signal: options.signal
1581
1601
  });
@@ -1597,15 +1617,14 @@ async function streamChatGptCodexResponse(options) {
1597
1617
  return fallbackStreamFactory();
1598
1618
  }
1599
1619
  const websocketHeaders = buildChatGptCodexHeaders({
1600
- access,
1601
- accountId,
1620
+ endpointConfig,
1602
1621
  sessionId: options.sessionId,
1603
1622
  useWebSocket: true
1604
1623
  });
1605
1624
  return createAdaptiveResponsesStream({
1606
1625
  mode,
1607
1626
  createWebSocketStream: async () => await createResponsesWebSocketStream({
1608
- url: toWebSocketUrl(CHATGPT_CODEX_ENDPOINT),
1627
+ url: toWebSocketUrl(endpointConfig.url),
1609
1628
  headers: websocketHeaders,
1610
1629
  request: options.request,
1611
1630
  signal: options.signal
@@ -1618,14 +1637,13 @@ async function streamChatGptCodexResponse(options) {
1618
1637
  }
1619
1638
  async function streamChatGptCodexResponseSse(options) {
1620
1639
  const headers = buildChatGptCodexHeaders({
1621
- access: options.access,
1622
- accountId: options.accountId,
1640
+ endpointConfig: options.endpointConfig,
1623
1641
  sessionId: options.sessionId,
1624
1642
  useWebSocket: false
1625
1643
  });
1626
1644
  headers.Accept = "text/event-stream";
1627
1645
  headers["Content-Type"] = "application/json";
1628
- const response = await fetch(CHATGPT_CODEX_ENDPOINT, {
1646
+ const response = await fetch(options.endpointConfig.url, {
1629
1647
  method: "POST",
1630
1648
  headers,
1631
1649
  body: JSON.stringify(options.request),
@@ -1645,24 +1663,66 @@ function resolveChatGptResponsesWebSocketMode() {
1645
1663
  if (chatGptCodexState.cachedResponsesWebSocketMode) {
1646
1664
  return chatGptCodexState.cachedResponsesWebSocketMode;
1647
1665
  }
1666
+ const explicitMode = process.env.CHATGPT_RESPONSES_WEBSOCKET_MODE ?? process.env.OPENAI_RESPONSES_WEBSOCKET_MODE;
1667
+ const defaultMode = resolveChatGptCodexProxyConfig() ? "off" : "auto";
1648
1668
  chatGptCodexState.cachedResponsesWebSocketMode = resolveResponsesWebSocketMode(
1649
- process.env.CHATGPT_RESPONSES_WEBSOCKET_MODE ?? process.env.OPENAI_RESPONSES_WEBSOCKET_MODE,
1650
- "auto"
1669
+ explicitMode,
1670
+ defaultMode
1651
1671
  );
1652
1672
  return chatGptCodexState.cachedResponsesWebSocketMode;
1653
1673
  }
1674
+ async function resolveChatGptCodexEndpointConfig() {
1675
+ const proxy = resolveChatGptCodexProxyConfig();
1676
+ if (proxy) {
1677
+ return proxy;
1678
+ }
1679
+ const { access, accountId } = await getChatGptAuthProfile();
1680
+ return {
1681
+ kind: "direct",
1682
+ url: resolveChatGptCodexEndpoint(),
1683
+ access,
1684
+ accountId
1685
+ };
1686
+ }
1687
+ function resolveChatGptCodexEndpoint() {
1688
+ loadLocalEnv();
1689
+ return process.env[CHATGPT_CODEX_ENDPOINT_ENV]?.trim() || CHATGPT_CODEX_ENDPOINT;
1690
+ }
1691
+ function resolveChatGptCodexProxyConfig() {
1692
+ loadLocalEnv();
1693
+ const url = process.env[CHATGPT_CODEX_PROXY_URL_ENV]?.trim();
1694
+ if (!url) {
1695
+ return null;
1696
+ }
1697
+ const apiKey = process.env[CHATGPT_CODEX_PROXY_API_KEY_ENV]?.trim();
1698
+ if (!apiKey) {
1699
+ throw new Error(
1700
+ `${CHATGPT_CODEX_PROXY_API_KEY_ENV} must be provided when ${CHATGPT_CODEX_PROXY_URL_ENV} is set.`
1701
+ );
1702
+ }
1703
+ return {
1704
+ kind: "proxy",
1705
+ url,
1706
+ apiKey
1707
+ };
1708
+ }
1654
1709
  function buildChatGptCodexHeaders(options) {
1655
1710
  const openAiBeta = options.useWebSocket ? mergeOpenAiBetaHeader(
1656
1711
  CHATGPT_RESPONSES_EXPERIMENTAL_HEADER,
1657
1712
  OPENAI_BETA_RESPONSES_WEBSOCKETS_V2
1658
1713
  ) : CHATGPT_RESPONSES_EXPERIMENTAL_HEADER;
1659
1714
  const headers = {
1660
- Authorization: `Bearer ${options.access}`,
1661
- "chatgpt-account-id": options.accountId,
1662
1715
  "OpenAI-Beta": openAiBeta,
1663
1716
  originator: "llm",
1664
1717
  "User-Agent": buildUserAgent()
1665
1718
  };
1719
+ if (options.endpointConfig.kind === "proxy") {
1720
+ headers.Authorization = `Bearer ${options.endpointConfig.apiKey}`;
1721
+ headers["x-codex-proxy-auth"] = options.endpointConfig.apiKey;
1722
+ } else {
1723
+ headers.Authorization = `Bearer ${options.endpointConfig.access}`;
1724
+ headers["chatgpt-account-id"] = options.endpointConfig.accountId;
1725
+ }
1666
1726
  if (options.sessionId) {
1667
1727
  headers.session_id = options.sessionId;
1668
1728
  }
@@ -8329,6 +8389,7 @@ async function runTextCall(params) {
8329
8389
  const outputAttachments = collectLoggedAttachmentsFromLlmParts(mergedParts, "output");
8330
8390
  const costUsd = estimateCallCostUsd({
8331
8391
  modelId: modelVersion,
8392
+ pricingModelId: request.model,
8332
8393
  tokens: latestUsage,
8333
8394
  responseImages,
8334
8395
  imageSize: request.imageSize
@@ -9072,6 +9133,7 @@ async function runToolLoop(request) {
9072
9133
  const modelCompletedAtMs = Date.now();
9073
9134
  const stepCostUsd = estimateCallCostUsd({
9074
9135
  modelId: modelVersion,
9136
+ pricingModelId: request.model,
9075
9137
  tokens: usageTokens,
9076
9138
  responseImages: 0
9077
9139
  });
@@ -9403,6 +9465,7 @@ async function runToolLoop(request) {
9403
9465
  usageTokens = extractChatGptUsageTokens(response.usage);
9404
9466
  const stepCostUsd = estimateCallCostUsd({
9405
9467
  modelId: modelVersion,
9468
+ pricingModelId: request.model,
9406
9469
  tokens: usageTokens,
9407
9470
  responseImages: 0
9408
9471
  });
@@ -9731,6 +9794,7 @@ async function runToolLoop(request) {
9731
9794
  usageTokens = extractFireworksUsageTokens(response.usage);
9732
9795
  const stepCostUsd = estimateCallCostUsd({
9733
9796
  modelId: modelVersion,
9797
+ pricingModelId: request.model,
9734
9798
  tokens: usageTokens,
9735
9799
  responseImages: 0
9736
9800
  });
@@ -10093,6 +10157,7 @@ async function runToolLoop(request) {
10093
10157
  );
10094
10158
  const stepCostUsd = estimateCallCostUsd({
10095
10159
  modelId: modelVersion,
10160
+ pricingModelId: request.model,
10096
10161
  tokens: usageTokens,
10097
10162
  responseImages: 0
10098
10163
  });