@ljoukov/llm 7.0.13 → 7.0.14

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 CHANGED
@@ -218,6 +218,23 @@ multiple of 16px, the long edge must be at most 3:1 relative to the short edge,
218
218
  must be between 655,360 and 8,294,400. Resolutions above 3,686,400 pixels are documented as
219
219
  experimental by OpenAI.
220
220
 
221
+ To use ChatGPT/Codex subscription-backed image generation instead of the public Images API, use
222
+ `chatgpt-gpt-image-2`:
223
+
224
+ ```ts
225
+ const images = await generateImages({
226
+ model: "chatgpt-gpt-image-2",
227
+ stylePrompt: "Warm amber desk light, deep blue night, cinematic laboratory mood.",
228
+ imagePrompts: ["A compact lab bench still life with glassware and an open notebook"],
229
+ numImages: 1,
230
+ });
231
+ ```
232
+
233
+ That path reuses the same ChatGPT auth setup as other `chatgpt-*` models and sends the request
234
+ through the ChatGPT/Codex Responses `image_generation` built-in tool. It returns PNG images. The
235
+ public Images API controls such as `imageResolution`, `imageQuality`, `outputFormat`, and
236
+ `outputCompression` are intentionally only on the `gpt-image-2` request type.
237
+
221
238
  ### Streaming (response + thoughts + usage)
222
239
 
223
240
  ```ts
@@ -495,10 +512,12 @@ console.log(result.text);
495
512
 
496
513
  `gpt-5.5-fast` and `chatgpt-gpt-5.5-fast` are also supported as convenience aliases for `gpt-5.5` with priority processing enabled (`service_tier="priority"`), matching Codex `/fast` semantics.
497
514
 
498
- Supported OpenAI text model ids are fixed literal unions in code, not arbitrary strings:
515
+ Supported OpenAI and ChatGPT model ids are fixed literal unions in code, not arbitrary strings:
499
516
 
500
- - OpenAI API: `gpt-5.5`, `gpt-5.5-fast`, `gpt-5.4`, `gpt-5.4-mini`, `gpt-5.4-nano`
501
- - ChatGPT auth: `chatgpt-gpt-5.5`, `chatgpt-gpt-5.5-fast`, `chatgpt-gpt-5.4`, `chatgpt-gpt-5.4-fast`, `chatgpt-gpt-5.4-mini`, `chatgpt-gpt-5.3-codex-spark`
517
+ - OpenAI API text: `gpt-5.5`, `gpt-5.5-fast`, `gpt-5.4`, `gpt-5.4-mini`, `gpt-5.4-nano`
518
+ - OpenAI API image: `gpt-image-2`
519
+ - ChatGPT auth text: `chatgpt-gpt-5.5`, `chatgpt-gpt-5.5-fast`, `chatgpt-gpt-5.4`, `chatgpt-gpt-5.4-fast`, `chatgpt-gpt-5.4-mini`, `chatgpt-gpt-5.3-codex-spark`
520
+ - ChatGPT auth image: `chatgpt-gpt-image-2`
502
521
 
503
522
  ## JSON outputs
504
523
 
package/dist/index.cjs CHANGED
@@ -30,6 +30,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/index.ts
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
+ CHATGPT_IMAGE_MODEL_IDS: () => CHATGPT_IMAGE_MODEL_IDS,
33
34
  CHATGPT_MODEL_IDS: () => CHATGPT_MODEL_IDS,
34
35
  CODEX_APPLY_PATCH_FREEFORM_TOOL_DESCRIPTION: () => CODEX_APPLY_PATCH_FREEFORM_TOOL_DESCRIPTION,
35
36
  CODEX_APPLY_PATCH_JSON_TOOL_DESCRIPTION: () => CODEX_APPLY_PATCH_JSON_TOOL_DESCRIPTION,
@@ -101,6 +102,7 @@ __export(index_exports, {
101
102
  generateText: () => generateText,
102
103
  getChatGptAuthProfile: () => getChatGptAuthProfile,
103
104
  getCurrentToolCallContext: () => getCurrentToolCallContext,
105
+ isChatGptImageModelId: () => isChatGptImageModelId,
104
106
  isChatGptModelId: () => isChatGptModelId,
105
107
  isExperimentalChatGptModelId: () => isExperimentalChatGptModelId,
106
108
  isFireworksModelId: () => isFireworksModelId,
@@ -118,6 +120,7 @@ __export(index_exports, {
118
120
  refreshChatGptOauthToken: () => refreshChatGptOauthToken,
119
121
  resetModelConcurrencyConfig: () => resetModelConcurrencyConfig,
120
122
  resetTelemetry: () => resetTelemetry,
123
+ resolveChatGptImageProviderModel: () => resolveChatGptImageProviderModel,
121
124
  resolveFilesystemToolProfile: () => resolveFilesystemToolProfile,
122
125
  resolveFireworksModelId: () => resolveFireworksModelId,
123
126
  runAgentLoop: () => runAgentLoop,
@@ -349,6 +352,10 @@ var OPENAI_IMAGE_MODEL_IDS = ["gpt-image-2"];
349
352
  function isOpenAiImageModelId(value) {
350
353
  return OPENAI_IMAGE_MODEL_IDS.includes(value);
351
354
  }
355
+ var CHATGPT_IMAGE_MODEL_IDS = ["chatgpt-gpt-image-2"];
356
+ function isChatGptImageModelId(value) {
357
+ return CHATGPT_IMAGE_MODEL_IDS.includes(value);
358
+ }
352
359
  var OPENAI_GPT_IMAGE_2_POPULAR_RESOLUTIONS = [
353
360
  "1024x1024",
354
361
  "1536x1024",
@@ -431,6 +438,7 @@ var CHATGPT_MODEL_IDS = [
431
438
  var FAST_MODEL_SUFFIX = "-fast";
432
439
  var OPENAI_PRIORITY_MODEL_IDS = ["gpt-5.5-fast"];
433
440
  var CHATGPT_PRIORITY_MODEL_IDS = ["chatgpt-gpt-5.5-fast", "chatgpt-gpt-5.4-fast"];
441
+ var CHATGPT_IMAGE_GENERATION_PROVIDER_MODEL = "gpt-5.4";
434
442
  var EXPERIMENTAL_CHATGPT_MODEL_PREFIX = "experimental-chatgpt-";
435
443
  function isExperimentalChatGptModelId(value) {
436
444
  return value.startsWith(EXPERIMENTAL_CHATGPT_MODEL_PREFIX) && value.length > EXPERIMENTAL_CHATGPT_MODEL_PREFIX.length;
@@ -457,6 +465,9 @@ function resolveChatGptProviderModel(model) {
457
465
  const providerModel = stripChatGptPrefix(model);
458
466
  return CHATGPT_PRIORITY_MODEL_IDS.includes(model) ? stripFastSuffix(providerModel) : providerModel;
459
467
  }
468
+ function resolveChatGptImageProviderModel(_model) {
469
+ return CHATGPT_IMAGE_GENERATION_PROVIDER_MODEL;
470
+ }
460
471
  function resolveChatGptServiceTier(model) {
461
472
  return CHATGPT_PRIORITY_MODEL_IDS.includes(model) ? "priority" : void 0;
462
473
  }
@@ -551,7 +562,7 @@ function getOpenAiPricing(modelId) {
551
562
  return void 0;
552
563
  }
553
564
  function getOpenAiImagePricing(modelId) {
554
- return isOpenAiImageModelId(modelId) ? OPENAI_GPT_IMAGE_2_PRICING : void 0;
565
+ return isOpenAiImageModelId(modelId) || isChatGptImageModelId(modelId) ? OPENAI_GPT_IMAGE_2_PRICING : void 0;
555
566
  }
556
567
 
557
568
  // src/utils/cost.ts
@@ -1826,6 +1837,8 @@ async function collectChatGptCodexStream(options) {
1826
1837
  const toolCallOrder = [];
1827
1838
  const webSearchCalls = /* @__PURE__ */ new Map();
1828
1839
  const webSearchCallOrder = [];
1840
+ const imageGenerationCalls = /* @__PURE__ */ new Map();
1841
+ const imageGenerationCallOrder = [];
1829
1842
  let text = "";
1830
1843
  const reasoningText = "";
1831
1844
  let reasoningSummaryText = "";
@@ -1896,6 +1909,20 @@ async function collectChatGptCodexStream(options) {
1896
1909
  action: item.action && typeof item.action === "object" ? item.action : void 0
1897
1910
  });
1898
1911
  }
1912
+ } else if (item.type === "image_generation_call") {
1913
+ const id = typeof item.id === "string" ? item.id : "";
1914
+ const result = typeof item.result === "string" ? item.result : "";
1915
+ if (id && result) {
1916
+ if (!imageGenerationCalls.has(id)) {
1917
+ imageGenerationCallOrder.push(id);
1918
+ }
1919
+ imageGenerationCalls.set(id, {
1920
+ id,
1921
+ status: typeof item.status === "string" ? item.status : void 0,
1922
+ revisedPrompt: typeof item.revised_prompt === "string" ? item.revised_prompt : void 0,
1923
+ result
1924
+ });
1925
+ }
1899
1926
  }
1900
1927
  }
1901
1928
  continue;
@@ -1935,12 +1962,14 @@ async function collectChatGptCodexStream(options) {
1935
1962
  }
1936
1963
  const orderedToolCalls = toolCallOrder.map((id) => toolCalls.get(id)).filter((call) => call !== void 0);
1937
1964
  const orderedWebSearchCalls = webSearchCallOrder.map((id) => webSearchCalls.get(id)).filter((call) => call !== void 0);
1965
+ const orderedImageGenerationCalls = imageGenerationCallOrder.map((id) => imageGenerationCalls.get(id)).filter((call) => call !== void 0);
1938
1966
  return {
1939
1967
  text,
1940
1968
  reasoningText,
1941
1969
  reasoningSummaryText,
1942
1970
  toolCalls: orderedToolCalls,
1943
1971
  webSearchCalls: orderedWebSearchCalls,
1972
+ imageGenerationCalls: orderedImageGenerationCalls,
1944
1973
  usage,
1945
1974
  id: responseId,
1946
1975
  model,
@@ -4652,13 +4681,17 @@ var LLM_TEXT_MODEL_IDS = [
4652
4681
  ...FIREWORKS_MODEL_IDS,
4653
4682
  ...GEMINI_TEXT_MODEL_IDS
4654
4683
  ];
4655
- var LLM_IMAGE_MODEL_IDS = [...OPENAI_IMAGE_MODEL_IDS, ...GEMINI_IMAGE_MODEL_IDS];
4684
+ var LLM_IMAGE_MODEL_IDS = [
4685
+ ...OPENAI_IMAGE_MODEL_IDS,
4686
+ ...CHATGPT_IMAGE_MODEL_IDS,
4687
+ ...GEMINI_IMAGE_MODEL_IDS
4688
+ ];
4656
4689
  var LLM_MODEL_IDS = [...LLM_TEXT_MODEL_IDS, ...LLM_IMAGE_MODEL_IDS];
4657
4690
  function isLlmTextModelId(value) {
4658
4691
  return isOpenAiModelId(value) || isChatGptModelId(value) || isFireworksModelId(value) || isGeminiTextModelId(value);
4659
4692
  }
4660
4693
  function isLlmImageModelId(value) {
4661
- return isOpenAiImageModelId(value) || isGeminiImageModelId(value);
4694
+ return isOpenAiImageModelId(value) || isChatGptImageModelId(value) || isGeminiImageModelId(value);
4662
4695
  }
4663
4696
  function isLlmModelId(value) {
4664
4697
  return isLlmTextModelId(value) || isLlmImageModelId(value);
@@ -4673,6 +4706,9 @@ var LlmJsonCallError = class extends Error {
4673
4706
  function isOpenAiGenerateImagesRequest(request) {
4674
4707
  return isOpenAiImageModelId(request.model);
4675
4708
  }
4709
+ function isChatGptGenerateImagesRequest(request) {
4710
+ return isChatGptImageModelId(request.model);
4711
+ }
4676
4712
  function tool(options) {
4677
4713
  return {
4678
4714
  type: "function",
@@ -5266,6 +5302,12 @@ function resolveProvider(model) {
5266
5302
  if (isOpenAiImageModelId(model)) {
5267
5303
  return { provider: "openai", model };
5268
5304
  }
5305
+ if (isChatGptImageModelId(model)) {
5306
+ return {
5307
+ provider: "chatgpt",
5308
+ model: resolveChatGptImageProviderModel(model)
5309
+ };
5310
+ }
5269
5311
  if (isOpenAiModelId(model)) {
5270
5312
  return {
5271
5313
  provider: "openai",
@@ -8172,6 +8214,11 @@ async function runTextCall(params) {
8172
8214
  }
8173
8215
  }, modelForProvider);
8174
8216
  } else if (provider === "chatgpt") {
8217
+ if (isChatGptImageModelId(request.model)) {
8218
+ throw new Error(
8219
+ "chatgpt-gpt-image-2 is an image generation model; use generateImages()."
8220
+ );
8221
+ }
8175
8222
  const chatGptInput = toChatGptInput(contents, {
8176
8223
  defaultMediaResolution: request.mediaResolution,
8177
8224
  model: request.model
@@ -10625,10 +10672,144 @@ async function generateImagesWithOpenAiImageApi(request) {
10625
10672
  await telemetry.flush();
10626
10673
  }
10627
10674
  }
10675
+ function buildChatGptImageInputContent(params) {
10676
+ const parts = [
10677
+ {
10678
+ type: "text",
10679
+ text: params.prompt
10680
+ }
10681
+ ];
10682
+ for (const [index, image] of (params.styleImages ?? []).entries()) {
10683
+ const mimeType = image.mimeType ?? "image/png";
10684
+ parts.push({
10685
+ type: "inlineData",
10686
+ data: image.data.toString("base64"),
10687
+ mimeType,
10688
+ filename: `style-${index + 1}.${resolveAttachmentExtension(mimeType)}`
10689
+ });
10690
+ }
10691
+ return [{ role: "user", parts }];
10692
+ }
10693
+ async function generateImagesWithChatGptImageTool(request) {
10694
+ const promptEntries = Array.from(request.imagePrompts, (rawPrompt, index) => {
10695
+ const prompt = rawPrompt.trim();
10696
+ if (!prompt) {
10697
+ throw new Error(`imagePrompts[${index}] must be a non-empty string`);
10698
+ }
10699
+ return prompt;
10700
+ });
10701
+ if (promptEntries.length === 0) {
10702
+ return [];
10703
+ }
10704
+ const providerInfo = resolveProvider(request.model);
10705
+ const telemetry = createLlmTelemetryEmitter({
10706
+ telemetry: request.telemetry,
10707
+ operation: "generateImages",
10708
+ provider: providerInfo.provider,
10709
+ model: request.model
10710
+ });
10711
+ const startedAtMs = Date.now();
10712
+ const numImagesPerPrompt = request.numImages ?? 1;
10713
+ let totalUsage;
10714
+ let costUsd = 0;
10715
+ let outputImages = 0;
10716
+ telemetry.emit({
10717
+ type: "llm.call.started",
10718
+ imagePromptCount: promptEntries.length,
10719
+ styleImageCount: request.styleImages?.length ?? 0,
10720
+ numImagesPerPrompt
10721
+ });
10722
+ try {
10723
+ const images = [];
10724
+ for (const imagePrompt of promptEntries) {
10725
+ const prompt = buildOpenAiImagePrompt({
10726
+ stylePrompt: request.stylePrompt,
10727
+ imagePrompt,
10728
+ hasStyleImages: Boolean(request.styleImages && request.styleImages.length > 0)
10729
+ });
10730
+ for (let imageIndex = 0; imageIndex < numImagesPerPrompt; imageIndex += 1) {
10731
+ const chatGptInput = toChatGptInput(
10732
+ buildChatGptImageInputContent({
10733
+ prompt,
10734
+ styleImages: request.styleImages
10735
+ }),
10736
+ { model: request.model }
10737
+ );
10738
+ const preparedInput = await maybePrepareOpenAiPromptInput(chatGptInput.input, {
10739
+ model: request.model,
10740
+ provider: "chatgpt"
10741
+ });
10742
+ const result = await collectChatGptCodexResponseWithRetry({
10743
+ request: {
10744
+ model: providerInfo.model,
10745
+ store: false,
10746
+ stream: true,
10747
+ instructions: chatGptInput.instructions ?? "Use the image_generation tool to generate exactly one PNG image. Do not return prose instead of the image.",
10748
+ input: preparedInput,
10749
+ tool_choice: "required",
10750
+ parallel_tool_calls: false,
10751
+ tools: [{ type: "image_generation", output_format: "png" }]
10752
+ },
10753
+ signal: request.signal
10754
+ });
10755
+ if (result.status && result.status !== "completed") {
10756
+ throw new Error(`ChatGPT image generation response status ${result.status}`);
10757
+ }
10758
+ if (result.imageGenerationCalls.length === 0) {
10759
+ throw new Error("ChatGPT image generation returned no image_generation_call result.");
10760
+ }
10761
+ for (const call of result.imageGenerationCalls) {
10762
+ images.push({
10763
+ mimeType: "image/png",
10764
+ data: import_node_buffer4.Buffer.from(call.result, "base64")
10765
+ });
10766
+ }
10767
+ outputImages = images.length;
10768
+ const usage = extractChatGptUsageTokens(result.usage);
10769
+ totalUsage = sumUsageTokens(totalUsage, usage);
10770
+ costUsd += estimateCallCostUsd({
10771
+ modelId: request.model,
10772
+ tokens: usage,
10773
+ responseImages: result.imageGenerationCalls.length,
10774
+ imageSize: "1024x1024",
10775
+ imageQuality: "medium"
10776
+ });
10777
+ }
10778
+ }
10779
+ telemetry.emit({
10780
+ type: "llm.call.completed",
10781
+ success: true,
10782
+ durationMs: Math.max(0, Date.now() - startedAtMs),
10783
+ modelVersion: request.model,
10784
+ usage: totalUsage,
10785
+ costUsd,
10786
+ imageCount: images.length,
10787
+ attempts: promptEntries.length * numImagesPerPrompt
10788
+ });
10789
+ return images;
10790
+ } catch (error) {
10791
+ const err = error instanceof Error ? error : new Error(String(error));
10792
+ telemetry.emit({
10793
+ type: "llm.call.completed",
10794
+ success: false,
10795
+ durationMs: Math.max(0, Date.now() - startedAtMs),
10796
+ usage: totalUsage,
10797
+ costUsd,
10798
+ imageCount: outputImages,
10799
+ error: err.message
10800
+ });
10801
+ throw err;
10802
+ } finally {
10803
+ await telemetry.flush();
10804
+ }
10805
+ }
10628
10806
  async function generateImages(request) {
10629
10807
  if (isOpenAiGenerateImagesRequest(request)) {
10630
10808
  return await generateImagesWithOpenAiImageApi(request);
10631
10809
  }
10810
+ if (isChatGptGenerateImagesRequest(request)) {
10811
+ return await generateImagesWithChatGptImageTool(request);
10812
+ }
10632
10813
  const maxAttempts = Math.max(1, Math.floor(request.maxAttempts ?? 4));
10633
10814
  const promptList = Array.from(request.imagePrompts);
10634
10815
  if (promptList.length === 0) {
@@ -14656,6 +14837,7 @@ async function runCandidateEvolution(options) {
14656
14837
  }
14657
14838
  // Annotate the CommonJS export names for ESM import in node:
14658
14839
  0 && (module.exports = {
14840
+ CHATGPT_IMAGE_MODEL_IDS,
14659
14841
  CHATGPT_MODEL_IDS,
14660
14842
  CODEX_APPLY_PATCH_FREEFORM_TOOL_DESCRIPTION,
14661
14843
  CODEX_APPLY_PATCH_JSON_TOOL_DESCRIPTION,
@@ -14727,6 +14909,7 @@ async function runCandidateEvolution(options) {
14727
14909
  generateText,
14728
14910
  getChatGptAuthProfile,
14729
14911
  getCurrentToolCallContext,
14912
+ isChatGptImageModelId,
14730
14913
  isChatGptModelId,
14731
14914
  isExperimentalChatGptModelId,
14732
14915
  isFireworksModelId,
@@ -14744,6 +14927,7 @@ async function runCandidateEvolution(options) {
14744
14927
  refreshChatGptOauthToken,
14745
14928
  resetModelConcurrencyConfig,
14746
14929
  resetTelemetry,
14930
+ resolveChatGptImageProviderModel,
14747
14931
  resolveFilesystemToolProfile,
14748
14932
  resolveFireworksModelId,
14749
14933
  runAgentLoop,