@ljoukov/llm 7.0.13 → 7.0.15

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
 
@@ -680,6 +699,57 @@ console.log(result.text);
680
699
 
681
700
  `{ type: "shell" }` uses OpenAI hosted shell containers by default. It is only supported for OpenAI API models; ChatGPT-authenticated, Gemini, and Fireworks providers reject it.
682
701
 
702
+ When the shell writes artifacts under `/mnt/data`, `generateText()` returns the
703
+ OpenAI container reference and the library can retrieve the resulting files:
704
+
705
+ ```ts
706
+ import { downloadOpenAiContainerFile, generateText, listOpenAiContainerFiles } from "@ljoukov/llm";
707
+
708
+ const result = await generateText({
709
+ model: "gpt-5.5",
710
+ input:
711
+ "Use the shell to write /mnt/data/report.txt containing 'hello'. Reply only when done.",
712
+ tools: [{ type: "shell" }],
713
+ });
714
+
715
+ const containerId = result.openAi?.containers.find((c) => c.toolType === "shell")?.containerId;
716
+ if (!containerId) throw new Error("The response did not include a hosted shell container.");
717
+
718
+ const files = await listOpenAiContainerFiles(containerId);
719
+ const report = files.find((file) => file.path === "/mnt/data/report.txt");
720
+ if (!report) throw new Error("The shell did not create report.txt.");
721
+
722
+ const bytes = await downloadOpenAiContainerFile({
723
+ containerId,
724
+ fileId: report.id,
725
+ });
726
+ ```
727
+
728
+ For persistent multi-step shell work, create a container first, upload any input
729
+ assets, then pass it back as a `container-reference`:
730
+
731
+ ```ts
732
+ import {
733
+ createOpenAiContainer,
734
+ generateText,
735
+ uploadOpenAiContainerFile,
736
+ } from "@ljoukov/llm";
737
+
738
+ const container = await createOpenAiContainer({ name: "latex-build", memoryLimit: "1g" });
739
+ const cover = await uploadOpenAiContainerFile({
740
+ containerId: container.id,
741
+ filename: "cover.png",
742
+ data: coverPngBytes,
743
+ mimeType: "image/png",
744
+ });
745
+
746
+ await generateText({
747
+ model: "gpt-5.5",
748
+ input: `Use ${cover.path}, write LaTeX, run xelatex, and save /mnt/data/article.pdf.`,
749
+ tools: [{ type: "shell", environment: { type: "container-reference", containerId: container.id } }],
750
+ });
751
+ ```
752
+
683
753
  ### Runtime Tools (`runToolLoop()`)
684
754
 
685
755
  Use this when the model should call your local runtime functions.
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,
@@ -83,12 +84,16 @@ __export(index_exports, {
83
84
  createListDirectoryTool: () => createListDirectoryTool,
84
85
  createModelAgnosticFilesystemToolSet: () => createModelAgnosticFilesystemToolSet,
85
86
  createNodeAgentFilesystem: () => createNodeAgentFilesystem,
87
+ createOpenAiContainer: () => createOpenAiContainer,
86
88
  createReplaceTool: () => createReplaceTool,
87
89
  createRgSearchTool: () => createRgSearchTool,
88
90
  createToolLoopSteeringChannel: () => createToolLoopSteeringChannel,
89
91
  createViewImageTool: () => createViewImageTool,
90
92
  createWriteFileTool: () => createWriteFileTool,
91
93
  customTool: () => customTool,
94
+ deleteOpenAiContainer: () => deleteOpenAiContainer,
95
+ downloadOpenAiContainerFile: () => downloadOpenAiContainerFile,
96
+ downloadOpenAiContainerFileText: () => downloadOpenAiContainerFileText,
92
97
  emptyFileUploadMetrics: () => emptyFileUploadMetrics,
93
98
  encodeChatGptAuthJson: () => encodeChatGptAuthJson,
94
99
  encodeChatGptAuthJsonB64: () => encodeChatGptAuthJsonB64,
@@ -101,6 +106,7 @@ __export(index_exports, {
101
106
  generateText: () => generateText,
102
107
  getChatGptAuthProfile: () => getChatGptAuthProfile,
103
108
  getCurrentToolCallContext: () => getCurrentToolCallContext,
109
+ isChatGptImageModelId: () => isChatGptImageModelId,
104
110
  isChatGptModelId: () => isChatGptModelId,
105
111
  isExperimentalChatGptModelId: () => isExperimentalChatGptModelId,
106
112
  isFireworksModelId: () => isFireworksModelId,
@@ -112,12 +118,14 @@ __export(index_exports, {
112
118
  isLlmTextModelId: () => isLlmTextModelId,
113
119
  isOpenAiImageModelId: () => isOpenAiImageModelId,
114
120
  isOpenAiModelId: () => isOpenAiModelId,
121
+ listOpenAiContainerFiles: () => listOpenAiContainerFiles,
115
122
  loadEnvFromFile: () => loadEnvFromFile,
116
123
  loadLocalEnv: () => loadLocalEnv,
117
124
  parseJsonFromLlmText: () => parseJsonFromLlmText,
118
125
  refreshChatGptOauthToken: () => refreshChatGptOauthToken,
119
126
  resetModelConcurrencyConfig: () => resetModelConcurrencyConfig,
120
127
  resetTelemetry: () => resetTelemetry,
128
+ resolveChatGptImageProviderModel: () => resolveChatGptImageProviderModel,
121
129
  resolveFilesystemToolProfile: () => resolveFilesystemToolProfile,
122
130
  resolveFireworksModelId: () => resolveFireworksModelId,
123
131
  runAgentLoop: () => runAgentLoop,
@@ -131,6 +139,7 @@ __export(index_exports, {
131
139
  stripCodexCitationMarkers: () => stripCodexCitationMarkers,
132
140
  toGeminiJsonSchema: () => toGeminiJsonSchema,
133
141
  tool: () => tool,
142
+ uploadOpenAiContainerFile: () => uploadOpenAiContainerFile,
134
143
  validateOpenAiGptImage2Resolution: () => validateOpenAiGptImage2Resolution
135
144
  });
136
145
  module.exports = __toCommonJS(index_exports);
@@ -349,6 +358,10 @@ var OPENAI_IMAGE_MODEL_IDS = ["gpt-image-2"];
349
358
  function isOpenAiImageModelId(value) {
350
359
  return OPENAI_IMAGE_MODEL_IDS.includes(value);
351
360
  }
361
+ var CHATGPT_IMAGE_MODEL_IDS = ["chatgpt-gpt-image-2"];
362
+ function isChatGptImageModelId(value) {
363
+ return CHATGPT_IMAGE_MODEL_IDS.includes(value);
364
+ }
352
365
  var OPENAI_GPT_IMAGE_2_POPULAR_RESOLUTIONS = [
353
366
  "1024x1024",
354
367
  "1536x1024",
@@ -431,6 +444,7 @@ var CHATGPT_MODEL_IDS = [
431
444
  var FAST_MODEL_SUFFIX = "-fast";
432
445
  var OPENAI_PRIORITY_MODEL_IDS = ["gpt-5.5-fast"];
433
446
  var CHATGPT_PRIORITY_MODEL_IDS = ["chatgpt-gpt-5.5-fast", "chatgpt-gpt-5.4-fast"];
447
+ var CHATGPT_IMAGE_GENERATION_PROVIDER_MODEL = "gpt-5.4";
434
448
  var EXPERIMENTAL_CHATGPT_MODEL_PREFIX = "experimental-chatgpt-";
435
449
  function isExperimentalChatGptModelId(value) {
436
450
  return value.startsWith(EXPERIMENTAL_CHATGPT_MODEL_PREFIX) && value.length > EXPERIMENTAL_CHATGPT_MODEL_PREFIX.length;
@@ -457,6 +471,9 @@ function resolveChatGptProviderModel(model) {
457
471
  const providerModel = stripChatGptPrefix(model);
458
472
  return CHATGPT_PRIORITY_MODEL_IDS.includes(model) ? stripFastSuffix(providerModel) : providerModel;
459
473
  }
474
+ function resolveChatGptImageProviderModel(_model) {
475
+ return CHATGPT_IMAGE_GENERATION_PROVIDER_MODEL;
476
+ }
460
477
  function resolveChatGptServiceTier(model) {
461
478
  return CHATGPT_PRIORITY_MODEL_IDS.includes(model) ? "priority" : void 0;
462
479
  }
@@ -551,7 +568,7 @@ function getOpenAiPricing(modelId) {
551
568
  return void 0;
552
569
  }
553
570
  function getOpenAiImagePricing(modelId) {
554
- return isOpenAiImageModelId(modelId) ? OPENAI_GPT_IMAGE_2_PRICING : void 0;
571
+ return isOpenAiImageModelId(modelId) || isChatGptImageModelId(modelId) ? OPENAI_GPT_IMAGE_2_PRICING : void 0;
555
572
  }
556
573
 
557
574
  // src/utils/cost.ts
@@ -1826,6 +1843,8 @@ async function collectChatGptCodexStream(options) {
1826
1843
  const toolCallOrder = [];
1827
1844
  const webSearchCalls = /* @__PURE__ */ new Map();
1828
1845
  const webSearchCallOrder = [];
1846
+ const imageGenerationCalls = /* @__PURE__ */ new Map();
1847
+ const imageGenerationCallOrder = [];
1829
1848
  let text = "";
1830
1849
  const reasoningText = "";
1831
1850
  let reasoningSummaryText = "";
@@ -1896,6 +1915,20 @@ async function collectChatGptCodexStream(options) {
1896
1915
  action: item.action && typeof item.action === "object" ? item.action : void 0
1897
1916
  });
1898
1917
  }
1918
+ } else if (item.type === "image_generation_call") {
1919
+ const id = typeof item.id === "string" ? item.id : "";
1920
+ const result = typeof item.result === "string" ? item.result : "";
1921
+ if (id && result) {
1922
+ if (!imageGenerationCalls.has(id)) {
1923
+ imageGenerationCallOrder.push(id);
1924
+ }
1925
+ imageGenerationCalls.set(id, {
1926
+ id,
1927
+ status: typeof item.status === "string" ? item.status : void 0,
1928
+ revisedPrompt: typeof item.revised_prompt === "string" ? item.revised_prompt : void 0,
1929
+ result
1930
+ });
1931
+ }
1899
1932
  }
1900
1933
  }
1901
1934
  continue;
@@ -1935,12 +1968,14 @@ async function collectChatGptCodexStream(options) {
1935
1968
  }
1936
1969
  const orderedToolCalls = toolCallOrder.map((id) => toolCalls.get(id)).filter((call) => call !== void 0);
1937
1970
  const orderedWebSearchCalls = webSearchCallOrder.map((id) => webSearchCalls.get(id)).filter((call) => call !== void 0);
1971
+ const orderedImageGenerationCalls = imageGenerationCallOrder.map((id) => imageGenerationCalls.get(id)).filter((call) => call !== void 0);
1938
1972
  return {
1939
1973
  text,
1940
1974
  reasoningText,
1941
1975
  reasoningSummaryText,
1942
1976
  toolCalls: orderedToolCalls,
1943
1977
  webSearchCalls: orderedWebSearchCalls,
1978
+ imageGenerationCalls: orderedImageGenerationCalls,
1944
1979
  usage,
1945
1980
  id: responseId,
1946
1981
  model,
@@ -4652,13 +4687,17 @@ var LLM_TEXT_MODEL_IDS = [
4652
4687
  ...FIREWORKS_MODEL_IDS,
4653
4688
  ...GEMINI_TEXT_MODEL_IDS
4654
4689
  ];
4655
- var LLM_IMAGE_MODEL_IDS = [...OPENAI_IMAGE_MODEL_IDS, ...GEMINI_IMAGE_MODEL_IDS];
4690
+ var LLM_IMAGE_MODEL_IDS = [
4691
+ ...OPENAI_IMAGE_MODEL_IDS,
4692
+ ...CHATGPT_IMAGE_MODEL_IDS,
4693
+ ...GEMINI_IMAGE_MODEL_IDS
4694
+ ];
4656
4695
  var LLM_MODEL_IDS = [...LLM_TEXT_MODEL_IDS, ...LLM_IMAGE_MODEL_IDS];
4657
4696
  function isLlmTextModelId(value) {
4658
4697
  return isOpenAiModelId(value) || isChatGptModelId(value) || isFireworksModelId(value) || isGeminiTextModelId(value);
4659
4698
  }
4660
4699
  function isLlmImageModelId(value) {
4661
- return isOpenAiImageModelId(value) || isGeminiImageModelId(value);
4700
+ return isOpenAiImageModelId(value) || isChatGptImageModelId(value) || isGeminiImageModelId(value);
4662
4701
  }
4663
4702
  function isLlmModelId(value) {
4664
4703
  return isLlmTextModelId(value) || isLlmImageModelId(value);
@@ -4673,6 +4712,9 @@ var LlmJsonCallError = class extends Error {
4673
4712
  function isOpenAiGenerateImagesRequest(request) {
4674
4713
  return isOpenAiImageModelId(request.model);
4675
4714
  }
4715
+ function isChatGptGenerateImagesRequest(request) {
4716
+ return isChatGptImageModelId(request.model);
4717
+ }
4676
4718
  function tool(options) {
4677
4719
  return {
4678
4720
  type: "function",
@@ -5266,6 +5308,12 @@ function resolveProvider(model) {
5266
5308
  if (isOpenAiImageModelId(model)) {
5267
5309
  return { provider: "openai", model };
5268
5310
  }
5311
+ if (isChatGptImageModelId(model)) {
5312
+ return {
5313
+ provider: "chatgpt",
5314
+ model: resolveChatGptImageProviderModel(model)
5315
+ };
5316
+ }
5269
5317
  if (isOpenAiModelId(model)) {
5270
5318
  return {
5271
5319
  provider: "openai",
@@ -6420,6 +6468,50 @@ function toOpenAiTools(tools, options) {
6420
6468
  }
6421
6469
  });
6422
6470
  }
6471
+ function extractOpenAiResponseMetadata(response) {
6472
+ if (!response || typeof response !== "object") {
6473
+ return void 0;
6474
+ }
6475
+ const record = response;
6476
+ const responseId = typeof record.id === "string" ? record.id : void 0;
6477
+ const output = Array.isArray(record.output) ? record.output : [];
6478
+ const containers = [];
6479
+ const seen = /* @__PURE__ */ new Set();
6480
+ const addContainer = (container) => {
6481
+ const key = `${container.toolType}:${container.containerId}:${container.itemId ?? ""}:${container.callId ?? ""}`;
6482
+ if (seen.has(key)) {
6483
+ return;
6484
+ }
6485
+ seen.add(key);
6486
+ containers.push(container);
6487
+ };
6488
+ for (const item of output) {
6489
+ if (!item || typeof item !== "object") {
6490
+ continue;
6491
+ }
6492
+ const itemRecord = item;
6493
+ const itemId = typeof itemRecord.id === "string" ? itemRecord.id : void 0;
6494
+ const callId = typeof itemRecord.call_id === "string" ? itemRecord.call_id : void 0;
6495
+ if (itemRecord.type === "shell_call") {
6496
+ const environment = itemRecord.environment && typeof itemRecord.environment === "object" ? itemRecord.environment : void 0;
6497
+ const containerId = environment?.type === "container_reference" && typeof environment.container_id === "string" ? environment.container_id : void 0;
6498
+ if (containerId) {
6499
+ addContainer({ containerId, toolType: "shell", itemId, callId });
6500
+ }
6501
+ continue;
6502
+ }
6503
+ if (itemRecord.type === "code_interpreter_call") {
6504
+ const containerId = typeof itemRecord.container_id === "string" ? itemRecord.container_id : void 0;
6505
+ if (containerId) {
6506
+ addContainer({ containerId, toolType: "code_interpreter", itemId, callId });
6507
+ }
6508
+ }
6509
+ }
6510
+ if (!responseId && containers.length === 0) {
6511
+ return void 0;
6512
+ }
6513
+ return { ...responseId ? { responseId } : {}, containers };
6514
+ }
6423
6515
  function mergeTokenUpdates(current, next) {
6424
6516
  if (!next) {
6425
6517
  return current;
@@ -8031,6 +8123,7 @@ async function runTextCall(params) {
8031
8123
  let modelVersion = request.model;
8032
8124
  let blocked = false;
8033
8125
  let grounding;
8126
+ let openAi;
8034
8127
  const responseParts = [];
8035
8128
  let responseRole;
8036
8129
  let latestUsage;
@@ -8141,6 +8234,7 @@ async function runTextCall(params) {
8141
8234
  }
8142
8235
  }
8143
8236
  const finalResponse = await stream.finalResponse();
8237
+ openAi = extractOpenAiResponseMetadata(finalResponse);
8144
8238
  modelVersion = typeof finalResponse.model === "string" ? finalResponse.model : request.model;
8145
8239
  pushEvent({ type: "model", modelVersion });
8146
8240
  if (finalResponse.error) {
@@ -8172,6 +8266,11 @@ async function runTextCall(params) {
8172
8266
  }
8173
8267
  }, modelForProvider);
8174
8268
  } else if (provider === "chatgpt") {
8269
+ if (isChatGptImageModelId(request.model)) {
8270
+ throw new Error(
8271
+ "chatgpt-gpt-image-2 is an image generation model; use generateImages()."
8272
+ );
8273
+ }
8175
8274
  const chatGptInput = toChatGptInput(contents, {
8176
8275
  defaultMediaResolution: request.mediaResolution,
8177
8276
  model: request.model
@@ -8379,6 +8478,7 @@ async function runTextCall(params) {
8379
8478
  costUsd,
8380
8479
  usage: latestUsage,
8381
8480
  grounding: grounding ? sanitiseLogValue(grounding) : void 0,
8481
+ openAi: openAi ? sanitiseLogValue(openAi) : void 0,
8382
8482
  responseChars: text.length,
8383
8483
  thoughtChars: thoughts.length,
8384
8484
  responseImages,
@@ -8395,7 +8495,8 @@ async function runTextCall(params) {
8395
8495
  blocked,
8396
8496
  usage: latestUsage,
8397
8497
  costUsd,
8398
- grounding
8498
+ grounding,
8499
+ openAi
8399
8500
  };
8400
8501
  } catch (error) {
8401
8502
  const partialParts = mergeConsecutiveTextParts(responseParts);
@@ -10625,10 +10726,144 @@ async function generateImagesWithOpenAiImageApi(request) {
10625
10726
  await telemetry.flush();
10626
10727
  }
10627
10728
  }
10729
+ function buildChatGptImageInputContent(params) {
10730
+ const parts = [
10731
+ {
10732
+ type: "text",
10733
+ text: params.prompt
10734
+ }
10735
+ ];
10736
+ for (const [index, image] of (params.styleImages ?? []).entries()) {
10737
+ const mimeType = image.mimeType ?? "image/png";
10738
+ parts.push({
10739
+ type: "inlineData",
10740
+ data: image.data.toString("base64"),
10741
+ mimeType,
10742
+ filename: `style-${index + 1}.${resolveAttachmentExtension(mimeType)}`
10743
+ });
10744
+ }
10745
+ return [{ role: "user", parts }];
10746
+ }
10747
+ async function generateImagesWithChatGptImageTool(request) {
10748
+ const promptEntries = Array.from(request.imagePrompts, (rawPrompt, index) => {
10749
+ const prompt = rawPrompt.trim();
10750
+ if (!prompt) {
10751
+ throw new Error(`imagePrompts[${index}] must be a non-empty string`);
10752
+ }
10753
+ return prompt;
10754
+ });
10755
+ if (promptEntries.length === 0) {
10756
+ return [];
10757
+ }
10758
+ const providerInfo = resolveProvider(request.model);
10759
+ const telemetry = createLlmTelemetryEmitter({
10760
+ telemetry: request.telemetry,
10761
+ operation: "generateImages",
10762
+ provider: providerInfo.provider,
10763
+ model: request.model
10764
+ });
10765
+ const startedAtMs = Date.now();
10766
+ const numImagesPerPrompt = request.numImages ?? 1;
10767
+ let totalUsage;
10768
+ let costUsd = 0;
10769
+ let outputImages = 0;
10770
+ telemetry.emit({
10771
+ type: "llm.call.started",
10772
+ imagePromptCount: promptEntries.length,
10773
+ styleImageCount: request.styleImages?.length ?? 0,
10774
+ numImagesPerPrompt
10775
+ });
10776
+ try {
10777
+ const images = [];
10778
+ for (const imagePrompt of promptEntries) {
10779
+ const prompt = buildOpenAiImagePrompt({
10780
+ stylePrompt: request.stylePrompt,
10781
+ imagePrompt,
10782
+ hasStyleImages: Boolean(request.styleImages && request.styleImages.length > 0)
10783
+ });
10784
+ for (let imageIndex = 0; imageIndex < numImagesPerPrompt; imageIndex += 1) {
10785
+ const chatGptInput = toChatGptInput(
10786
+ buildChatGptImageInputContent({
10787
+ prompt,
10788
+ styleImages: request.styleImages
10789
+ }),
10790
+ { model: request.model }
10791
+ );
10792
+ const preparedInput = await maybePrepareOpenAiPromptInput(chatGptInput.input, {
10793
+ model: request.model,
10794
+ provider: "chatgpt"
10795
+ });
10796
+ const result = await collectChatGptCodexResponseWithRetry({
10797
+ request: {
10798
+ model: providerInfo.model,
10799
+ store: false,
10800
+ stream: true,
10801
+ instructions: chatGptInput.instructions ?? "Use the image_generation tool to generate exactly one PNG image. Do not return prose instead of the image.",
10802
+ input: preparedInput,
10803
+ tool_choice: "required",
10804
+ parallel_tool_calls: false,
10805
+ tools: [{ type: "image_generation", output_format: "png" }]
10806
+ },
10807
+ signal: request.signal
10808
+ });
10809
+ if (result.status && result.status !== "completed") {
10810
+ throw new Error(`ChatGPT image generation response status ${result.status}`);
10811
+ }
10812
+ if (result.imageGenerationCalls.length === 0) {
10813
+ throw new Error("ChatGPT image generation returned no image_generation_call result.");
10814
+ }
10815
+ for (const call of result.imageGenerationCalls) {
10816
+ images.push({
10817
+ mimeType: "image/png",
10818
+ data: import_node_buffer4.Buffer.from(call.result, "base64")
10819
+ });
10820
+ }
10821
+ outputImages = images.length;
10822
+ const usage = extractChatGptUsageTokens(result.usage);
10823
+ totalUsage = sumUsageTokens(totalUsage, usage);
10824
+ costUsd += estimateCallCostUsd({
10825
+ modelId: request.model,
10826
+ tokens: usage,
10827
+ responseImages: result.imageGenerationCalls.length,
10828
+ imageSize: "1024x1024",
10829
+ imageQuality: "medium"
10830
+ });
10831
+ }
10832
+ }
10833
+ telemetry.emit({
10834
+ type: "llm.call.completed",
10835
+ success: true,
10836
+ durationMs: Math.max(0, Date.now() - startedAtMs),
10837
+ modelVersion: request.model,
10838
+ usage: totalUsage,
10839
+ costUsd,
10840
+ imageCount: images.length,
10841
+ attempts: promptEntries.length * numImagesPerPrompt
10842
+ });
10843
+ return images;
10844
+ } catch (error) {
10845
+ const err = error instanceof Error ? error : new Error(String(error));
10846
+ telemetry.emit({
10847
+ type: "llm.call.completed",
10848
+ success: false,
10849
+ durationMs: Math.max(0, Date.now() - startedAtMs),
10850
+ usage: totalUsage,
10851
+ costUsd,
10852
+ imageCount: outputImages,
10853
+ error: err.message
10854
+ });
10855
+ throw err;
10856
+ } finally {
10857
+ await telemetry.flush();
10858
+ }
10859
+ }
10628
10860
  async function generateImages(request) {
10629
10861
  if (isOpenAiGenerateImagesRequest(request)) {
10630
10862
  return await generateImagesWithOpenAiImageApi(request);
10631
10863
  }
10864
+ if (isChatGptGenerateImagesRequest(request)) {
10865
+ return await generateImagesWithChatGptImageTool(request);
10866
+ }
10632
10867
  const maxAttempts = Math.max(1, Math.floor(request.maxAttempts ?? 4));
10633
10868
  const promptList = Array.from(request.imagePrompts);
10634
10869
  if (promptList.length === 0) {
@@ -10910,6 +11145,104 @@ function appendMarkdownSourcesSection(value, sources) {
10910
11145
  ${lines}`;
10911
11146
  }
10912
11147
 
11148
+ // src/openai/containers.ts
11149
+ var import_openai4 = require("openai");
11150
+ function toOpenAiContainerNetworkPolicy(policy) {
11151
+ if (!policy) {
11152
+ return void 0;
11153
+ }
11154
+ if (policy.type === "disabled") {
11155
+ return { type: "disabled" };
11156
+ }
11157
+ return {
11158
+ type: "allowlist",
11159
+ allowed_domains: Array.from(policy.allowedDomains),
11160
+ ...policy.domainSecrets ? {
11161
+ domain_secrets: policy.domainSecrets.map((secret) => ({
11162
+ domain: secret.domain,
11163
+ name: secret.name,
11164
+ value: secret.value
11165
+ }))
11166
+ } : {}
11167
+ };
11168
+ }
11169
+ function toContainer(container) {
11170
+ return {
11171
+ id: String(container.id),
11172
+ name: typeof container.name === "string" ? container.name : "",
11173
+ status: typeof container.status === "string" ? container.status : "",
11174
+ ...typeof container.created_at === "number" ? { createdAt: container.created_at } : {},
11175
+ ...typeof container.last_active_at === "number" ? { lastActiveAt: container.last_active_at } : {},
11176
+ ...typeof container.memory_limit === "string" ? { memoryLimit: container.memory_limit } : {}
11177
+ };
11178
+ }
11179
+ function toContainerFile(file) {
11180
+ return {
11181
+ id: String(file.id),
11182
+ containerId: typeof file.container_id === "string" ? file.container_id : "",
11183
+ path: typeof file.path === "string" ? file.path : "",
11184
+ ...typeof file.bytes === "number" || file.bytes === null ? { bytes: file.bytes } : {},
11185
+ ...typeof file.created_at === "number" ? { createdAt: file.created_at } : {},
11186
+ ...typeof file.source === "string" ? { source: file.source } : {}
11187
+ };
11188
+ }
11189
+ async function createOpenAiContainer(options) {
11190
+ const container = await runOpenAiCall(
11191
+ async (client) => await client.containers.create({
11192
+ name: options.name,
11193
+ ...options.fileIds ? { file_ids: Array.from(options.fileIds) } : {},
11194
+ ...options.memoryLimit ? { memory_limit: options.memoryLimit } : {},
11195
+ ...options.networkPolicy ? { network_policy: toOpenAiContainerNetworkPolicy(options.networkPolicy) } : {},
11196
+ ...options.expiresAfterMinutes ? {
11197
+ expires_after: {
11198
+ anchor: "last_active_at",
11199
+ minutes: options.expiresAfterMinutes
11200
+ }
11201
+ } : {}
11202
+ }),
11203
+ "openai-containers"
11204
+ );
11205
+ return toContainer(container);
11206
+ }
11207
+ async function deleteOpenAiContainer(containerId) {
11208
+ await runOpenAiCall(
11209
+ async (client) => await client.containers.delete(containerId),
11210
+ "openai-containers"
11211
+ );
11212
+ }
11213
+ async function listOpenAiContainerFiles(containerId) {
11214
+ const files2 = [];
11215
+ await runOpenAiCall(async (client) => {
11216
+ for await (const file of client.containers.files.list(containerId)) {
11217
+ files2.push(toContainerFile(file));
11218
+ }
11219
+ }, "openai-containers");
11220
+ return files2;
11221
+ }
11222
+ async function uploadOpenAiContainerFile(upload) {
11223
+ const file = await (0, import_openai4.toFile)(upload.data, upload.filename, {
11224
+ ...upload.mimeType ? { type: upload.mimeType } : {}
11225
+ });
11226
+ const created = await runOpenAiCall(
11227
+ async (client) => await client.containers.files.create(upload.containerId, { file }),
11228
+ "openai-containers"
11229
+ );
11230
+ return toContainerFile(created);
11231
+ }
11232
+ async function downloadOpenAiContainerFile(params) {
11233
+ const response = await runOpenAiCall(
11234
+ async (client) => await client.containers.files.content.retrieve(params.fileId, {
11235
+ container_id: params.containerId
11236
+ }),
11237
+ "openai-containers"
11238
+ );
11239
+ return new Uint8Array(await response.arrayBuffer());
11240
+ }
11241
+ async function downloadOpenAiContainerFileText(params) {
11242
+ const bytes = await downloadOpenAiContainerFile(params);
11243
+ return new TextDecoder().decode(bytes);
11244
+ }
11245
+
10913
11246
  // src/agent.ts
10914
11247
  var import_node_crypto4 = require("crypto");
10915
11248
  var import_node_path9 = __toESM(require("path"), 1);
@@ -14656,6 +14989,7 @@ async function runCandidateEvolution(options) {
14656
14989
  }
14657
14990
  // Annotate the CommonJS export names for ESM import in node:
14658
14991
  0 && (module.exports = {
14992
+ CHATGPT_IMAGE_MODEL_IDS,
14659
14993
  CHATGPT_MODEL_IDS,
14660
14994
  CODEX_APPLY_PATCH_FREEFORM_TOOL_DESCRIPTION,
14661
14995
  CODEX_APPLY_PATCH_JSON_TOOL_DESCRIPTION,
@@ -14709,12 +15043,16 @@ async function runCandidateEvolution(options) {
14709
15043
  createListDirectoryTool,
14710
15044
  createModelAgnosticFilesystemToolSet,
14711
15045
  createNodeAgentFilesystem,
15046
+ createOpenAiContainer,
14712
15047
  createReplaceTool,
14713
15048
  createRgSearchTool,
14714
15049
  createToolLoopSteeringChannel,
14715
15050
  createViewImageTool,
14716
15051
  createWriteFileTool,
14717
15052
  customTool,
15053
+ deleteOpenAiContainer,
15054
+ downloadOpenAiContainerFile,
15055
+ downloadOpenAiContainerFileText,
14718
15056
  emptyFileUploadMetrics,
14719
15057
  encodeChatGptAuthJson,
14720
15058
  encodeChatGptAuthJsonB64,
@@ -14727,6 +15065,7 @@ async function runCandidateEvolution(options) {
14727
15065
  generateText,
14728
15066
  getChatGptAuthProfile,
14729
15067
  getCurrentToolCallContext,
15068
+ isChatGptImageModelId,
14730
15069
  isChatGptModelId,
14731
15070
  isExperimentalChatGptModelId,
14732
15071
  isFireworksModelId,
@@ -14738,12 +15077,14 @@ async function runCandidateEvolution(options) {
14738
15077
  isLlmTextModelId,
14739
15078
  isOpenAiImageModelId,
14740
15079
  isOpenAiModelId,
15080
+ listOpenAiContainerFiles,
14741
15081
  loadEnvFromFile,
14742
15082
  loadLocalEnv,
14743
15083
  parseJsonFromLlmText,
14744
15084
  refreshChatGptOauthToken,
14745
15085
  resetModelConcurrencyConfig,
14746
15086
  resetTelemetry,
15087
+ resolveChatGptImageProviderModel,
14747
15088
  resolveFilesystemToolProfile,
14748
15089
  resolveFireworksModelId,
14749
15090
  runAgentLoop,
@@ -14757,6 +15098,7 @@ async function runCandidateEvolution(options) {
14757
15098
  stripCodexCitationMarkers,
14758
15099
  toGeminiJsonSchema,
14759
15100
  tool,
15101
+ uploadOpenAiContainerFile,
14760
15102
  validateOpenAiGptImage2Resolution
14761
15103
  });
14762
15104
  //# sourceMappingURL=index.cjs.map