@ljoukov/llm 7.0.12 → 7.0.13
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 +36 -0
- package/dist/index.cjs +388 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +86 -23
- package/dist/index.d.ts +86 -23
- package/dist/index.js +374 -6
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -182,6 +182,42 @@ console.log(result.text);
|
|
|
182
182
|
console.log(result.usage, result.costUsd);
|
|
183
183
|
```
|
|
184
184
|
|
|
185
|
+
### Image Generation
|
|
186
|
+
|
|
187
|
+
```ts
|
|
188
|
+
import {
|
|
189
|
+
generateImages,
|
|
190
|
+
type LlmOpenAiImageResolution,
|
|
191
|
+
OPENAI_GPT_IMAGE_2_QUALITY_LEVELS,
|
|
192
|
+
OPENAI_GPT_IMAGE_2_RESOLUTIONS,
|
|
193
|
+
OPENAI_GPT_IMAGE_2_SIZE_CONSTRAINTS,
|
|
194
|
+
} from "@ljoukov/llm";
|
|
195
|
+
|
|
196
|
+
const customResolution = "1440x960" satisfies LlmOpenAiImageResolution;
|
|
197
|
+
|
|
198
|
+
const images = await generateImages({
|
|
199
|
+
model: "gpt-image-2",
|
|
200
|
+
stylePrompt: "Warm amber desk light, deep blue night, cinematic laboratory mood.",
|
|
201
|
+
imagePrompts: ["A compact lab bench still life with glassware and an open notebook"],
|
|
202
|
+
imageResolution: customResolution,
|
|
203
|
+
imageQuality: "low",
|
|
204
|
+
numImages: 1,
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
console.log(OPENAI_GPT_IMAGE_2_RESOLUTIONS, OPENAI_GPT_IMAGE_2_QUALITY_LEVELS);
|
|
208
|
+
console.log(OPENAI_GPT_IMAGE_2_SIZE_CONSTRAINTS);
|
|
209
|
+
console.log(images[0]?.mimeType, images[0]?.data.byteLength);
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
`generateImages()` is typed as a discriminated union by `model`: `gpt-image-2` requests use
|
|
213
|
+
`imageResolution`, while Gemini image requests use `imageSize` (`"1K" | "2K" | "4K"`). For
|
|
214
|
+
`gpt-image-2`, `OPENAI_GPT_IMAGE_2_RESOLUTIONS` exposes the documented popular presets plus
|
|
215
|
+
`"auto"`; custom literal `WIDTHxHEIGHT` resolutions are also accepted when they satisfy
|
|
216
|
+
`OPENAI_GPT_IMAGE_2_SIZE_CONSTRAINTS`: each edge must be at most 3840px, each edge must be a
|
|
217
|
+
multiple of 16px, the long edge must be at most 3:1 relative to the short edge, and total pixels
|
|
218
|
+
must be between 655,360 and 8,294,400. Resolutions above 3,686,400 pixels are documented as
|
|
219
|
+
experimental by OpenAI.
|
|
220
|
+
|
|
185
221
|
### Streaming (response + thoughts + usage)
|
|
186
222
|
|
|
187
223
|
```ts
|
package/dist/index.cjs
CHANGED
|
@@ -50,6 +50,17 @@ __export(index_exports, {
|
|
|
50
50
|
LLM_MODEL_IDS: () => LLM_MODEL_IDS,
|
|
51
51
|
LLM_TEXT_MODEL_IDS: () => LLM_TEXT_MODEL_IDS,
|
|
52
52
|
LlmJsonCallError: () => LlmJsonCallError,
|
|
53
|
+
OPENAI_GPT_IMAGE_2_AUTO_RESOLUTION: () => OPENAI_GPT_IMAGE_2_AUTO_RESOLUTION,
|
|
54
|
+
OPENAI_GPT_IMAGE_2_BACKGROUNDS: () => OPENAI_GPT_IMAGE_2_BACKGROUNDS,
|
|
55
|
+
OPENAI_GPT_IMAGE_2_MODERATION_LEVELS: () => OPENAI_GPT_IMAGE_2_MODERATION_LEVELS,
|
|
56
|
+
OPENAI_GPT_IMAGE_2_NUM_IMAGES: () => OPENAI_GPT_IMAGE_2_NUM_IMAGES,
|
|
57
|
+
OPENAI_GPT_IMAGE_2_OUTPUT_FORMATS: () => OPENAI_GPT_IMAGE_2_OUTPUT_FORMATS,
|
|
58
|
+
OPENAI_GPT_IMAGE_2_PARTIAL_IMAGE_COUNTS: () => OPENAI_GPT_IMAGE_2_PARTIAL_IMAGE_COUNTS,
|
|
59
|
+
OPENAI_GPT_IMAGE_2_POPULAR_RESOLUTIONS: () => OPENAI_GPT_IMAGE_2_POPULAR_RESOLUTIONS,
|
|
60
|
+
OPENAI_GPT_IMAGE_2_QUALITY_LEVELS: () => OPENAI_GPT_IMAGE_2_QUALITY_LEVELS,
|
|
61
|
+
OPENAI_GPT_IMAGE_2_RESOLUTIONS: () => OPENAI_GPT_IMAGE_2_RESOLUTIONS,
|
|
62
|
+
OPENAI_GPT_IMAGE_2_SIZE_CONSTRAINTS: () => OPENAI_GPT_IMAGE_2_SIZE_CONSTRAINTS,
|
|
63
|
+
OPENAI_IMAGE_MODEL_IDS: () => OPENAI_IMAGE_MODEL_IDS,
|
|
53
64
|
OPENAI_MODEL_IDS: () => OPENAI_MODEL_IDS,
|
|
54
65
|
appendMarkdownSourcesSection: () => appendMarkdownSourcesSection,
|
|
55
66
|
applyPatch: () => applyPatch,
|
|
@@ -99,6 +110,7 @@ __export(index_exports, {
|
|
|
99
110
|
isLlmImageModelId: () => isLlmImageModelId,
|
|
100
111
|
isLlmModelId: () => isLlmModelId,
|
|
101
112
|
isLlmTextModelId: () => isLlmTextModelId,
|
|
113
|
+
isOpenAiImageModelId: () => isOpenAiImageModelId,
|
|
102
114
|
isOpenAiModelId: () => isOpenAiModelId,
|
|
103
115
|
loadEnvFromFile: () => loadEnvFromFile,
|
|
104
116
|
loadLocalEnv: () => loadLocalEnv,
|
|
@@ -118,7 +130,8 @@ __export(index_exports, {
|
|
|
118
130
|
streamToolLoop: () => streamToolLoop,
|
|
119
131
|
stripCodexCitationMarkers: () => stripCodexCitationMarkers,
|
|
120
132
|
toGeminiJsonSchema: () => toGeminiJsonSchema,
|
|
121
|
-
tool: () => tool
|
|
133
|
+
tool: () => tool,
|
|
134
|
+
validateOpenAiGptImage2Resolution: () => validateOpenAiGptImage2Resolution
|
|
122
135
|
});
|
|
123
136
|
module.exports = __toCommonJS(index_exports);
|
|
124
137
|
|
|
@@ -130,6 +143,7 @@ var import_node_path5 = __toESM(require("path"), 1);
|
|
|
130
143
|
var import_genai2 = require("@google/genai");
|
|
131
144
|
var import_zod_to_json_schema = require("@alcyone-labs/zod-to-json-schema");
|
|
132
145
|
var import_zod3 = require("zod");
|
|
146
|
+
var import_openai3 = require("openai");
|
|
133
147
|
|
|
134
148
|
// src/utils/asyncQueue.ts
|
|
135
149
|
function createAsyncQueue() {
|
|
@@ -331,6 +345,81 @@ var OPENAI_MODEL_IDS = [
|
|
|
331
345
|
function isOpenAiModelId(value) {
|
|
332
346
|
return OPENAI_MODEL_IDS.includes(value);
|
|
333
347
|
}
|
|
348
|
+
var OPENAI_IMAGE_MODEL_IDS = ["gpt-image-2"];
|
|
349
|
+
function isOpenAiImageModelId(value) {
|
|
350
|
+
return OPENAI_IMAGE_MODEL_IDS.includes(value);
|
|
351
|
+
}
|
|
352
|
+
var OPENAI_GPT_IMAGE_2_POPULAR_RESOLUTIONS = [
|
|
353
|
+
"1024x1024",
|
|
354
|
+
"1536x1024",
|
|
355
|
+
"1024x1536",
|
|
356
|
+
"2048x2048",
|
|
357
|
+
"2048x1152",
|
|
358
|
+
"3840x2160",
|
|
359
|
+
"2160x3840"
|
|
360
|
+
];
|
|
361
|
+
var OPENAI_GPT_IMAGE_2_AUTO_RESOLUTION = "auto";
|
|
362
|
+
var OPENAI_GPT_IMAGE_2_RESOLUTIONS = [
|
|
363
|
+
...OPENAI_GPT_IMAGE_2_POPULAR_RESOLUTIONS,
|
|
364
|
+
OPENAI_GPT_IMAGE_2_AUTO_RESOLUTION
|
|
365
|
+
];
|
|
366
|
+
var OPENAI_GPT_IMAGE_2_SIZE_CONSTRAINTS = {
|
|
367
|
+
maxEdgePixels: 3840,
|
|
368
|
+
edgeMultiplePixels: 16,
|
|
369
|
+
maxLongToShortEdgeRatio: 3,
|
|
370
|
+
minTotalPixels: 655360,
|
|
371
|
+
maxTotalPixels: 8294400,
|
|
372
|
+
experimentalTotalPixelsThreshold: 3686400
|
|
373
|
+
};
|
|
374
|
+
function validateOpenAiGptImage2Resolution(value) {
|
|
375
|
+
if (value === OPENAI_GPT_IMAGE_2_AUTO_RESOLUTION) {
|
|
376
|
+
return { valid: true };
|
|
377
|
+
}
|
|
378
|
+
const match = /^([1-9]\d*)x([1-9]\d*)$/.exec(value);
|
|
379
|
+
if (!match) {
|
|
380
|
+
return { valid: false, reason: 'Expected "auto" or a WIDTHxHEIGHT pixel string.' };
|
|
381
|
+
}
|
|
382
|
+
const width = Number(match[1]);
|
|
383
|
+
const height = Number(match[2]);
|
|
384
|
+
if (!Number.isSafeInteger(width) || !Number.isSafeInteger(height)) {
|
|
385
|
+
return { valid: false, reason: "Width and height must be safe integer pixel counts." };
|
|
386
|
+
}
|
|
387
|
+
const constraints = OPENAI_GPT_IMAGE_2_SIZE_CONSTRAINTS;
|
|
388
|
+
if (width > constraints.maxEdgePixels || height > constraints.maxEdgePixels) {
|
|
389
|
+
return {
|
|
390
|
+
valid: false,
|
|
391
|
+
reason: `Width and height must each be at most ${constraints.maxEdgePixels}px.`
|
|
392
|
+
};
|
|
393
|
+
}
|
|
394
|
+
if (width % constraints.edgeMultiplePixels !== 0 || height % constraints.edgeMultiplePixels !== 0) {
|
|
395
|
+
return {
|
|
396
|
+
valid: false,
|
|
397
|
+
reason: `Width and height must each be multiples of ${constraints.edgeMultiplePixels}px.`
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
const totalPixels = width * height;
|
|
401
|
+
if (totalPixels < constraints.minTotalPixels || totalPixels > constraints.maxTotalPixels) {
|
|
402
|
+
return {
|
|
403
|
+
valid: false,
|
|
404
|
+
reason: `Total pixels must be between ${constraints.minTotalPixels} and ${constraints.maxTotalPixels}.`
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
const longEdge = Math.max(width, height);
|
|
408
|
+
const shortEdge = Math.min(width, height);
|
|
409
|
+
if (longEdge / shortEdge > constraints.maxLongToShortEdgeRatio) {
|
|
410
|
+
return {
|
|
411
|
+
valid: false,
|
|
412
|
+
reason: `The long edge must be at most ${constraints.maxLongToShortEdgeRatio}:1 relative to the short edge.`
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
return { valid: true };
|
|
416
|
+
}
|
|
417
|
+
var OPENAI_GPT_IMAGE_2_QUALITY_LEVELS = ["low", "medium", "high", "auto"];
|
|
418
|
+
var OPENAI_GPT_IMAGE_2_OUTPUT_FORMATS = ["png", "jpeg", "webp"];
|
|
419
|
+
var OPENAI_GPT_IMAGE_2_BACKGROUNDS = ["opaque", "auto"];
|
|
420
|
+
var OPENAI_GPT_IMAGE_2_MODERATION_LEVELS = ["low", "auto"];
|
|
421
|
+
var OPENAI_GPT_IMAGE_2_PARTIAL_IMAGE_COUNTS = [0, 1, 2, 3];
|
|
422
|
+
var OPENAI_GPT_IMAGE_2_NUM_IMAGES = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
|
334
423
|
var CHATGPT_MODEL_IDS = [
|
|
335
424
|
"chatgpt-gpt-5.5",
|
|
336
425
|
"chatgpt-gpt-5.5-fast",
|
|
@@ -413,6 +502,27 @@ var OPENAI_GPT_54_NANO_PRICING = {
|
|
|
413
502
|
cachedRate: 5e-3 / 1e6,
|
|
414
503
|
outputRate: 0.4 / 1e6
|
|
415
504
|
};
|
|
505
|
+
var OPENAI_GPT_IMAGE_2_PRICING = {
|
|
506
|
+
defaultQuality: "medium",
|
|
507
|
+
defaultResolution: "1024x1024",
|
|
508
|
+
imagePrices: {
|
|
509
|
+
low: {
|
|
510
|
+
"1024x1024": 6e-3,
|
|
511
|
+
"1024x1536": 5e-3,
|
|
512
|
+
"1536x1024": 5e-3
|
|
513
|
+
},
|
|
514
|
+
medium: {
|
|
515
|
+
"1024x1024": 0.053,
|
|
516
|
+
"1024x1536": 0.041,
|
|
517
|
+
"1536x1024": 0.041
|
|
518
|
+
},
|
|
519
|
+
high: {
|
|
520
|
+
"1024x1024": 0.211,
|
|
521
|
+
"1024x1536": 0.165,
|
|
522
|
+
"1536x1024": 0.165
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
};
|
|
416
526
|
function getOpenAiPricing(modelId) {
|
|
417
527
|
if (isExperimentalChatGptModelId(modelId)) {
|
|
418
528
|
return OPENAI_GPT_54_PRICING;
|
|
@@ -440,6 +550,9 @@ function getOpenAiPricing(modelId) {
|
|
|
440
550
|
}
|
|
441
551
|
return void 0;
|
|
442
552
|
}
|
|
553
|
+
function getOpenAiImagePricing(modelId) {
|
|
554
|
+
return isOpenAiImageModelId(modelId) ? OPENAI_GPT_IMAGE_2_PRICING : void 0;
|
|
555
|
+
}
|
|
443
556
|
|
|
444
557
|
// src/utils/cost.ts
|
|
445
558
|
function resolveUsageNumber(value) {
|
|
@@ -452,8 +565,18 @@ function estimateCallCostUsd({
|
|
|
452
565
|
modelId,
|
|
453
566
|
tokens,
|
|
454
567
|
responseImages,
|
|
455
|
-
imageSize
|
|
568
|
+
imageSize,
|
|
569
|
+
imageQuality
|
|
456
570
|
}) {
|
|
571
|
+
const openAiImagePricing = getOpenAiImagePricing(modelId);
|
|
572
|
+
if (openAiImagePricing) {
|
|
573
|
+
return estimateOpenAiImageCostUsd({
|
|
574
|
+
pricing: openAiImagePricing,
|
|
575
|
+
responseImages,
|
|
576
|
+
imageSize,
|
|
577
|
+
imageQuality
|
|
578
|
+
});
|
|
579
|
+
}
|
|
457
580
|
if (!tokens) {
|
|
458
581
|
return 0;
|
|
459
582
|
}
|
|
@@ -515,6 +638,40 @@ function estimateCallCostUsd({
|
|
|
515
638
|
}
|
|
516
639
|
return 0;
|
|
517
640
|
}
|
|
641
|
+
function estimateOpenAiImageCostUsd({
|
|
642
|
+
pricing,
|
|
643
|
+
responseImages,
|
|
644
|
+
imageSize,
|
|
645
|
+
imageQuality
|
|
646
|
+
}) {
|
|
647
|
+
if (responseImages <= 0) {
|
|
648
|
+
return 0;
|
|
649
|
+
}
|
|
650
|
+
const quality = imageQuality === "low" || imageQuality === "medium" || imageQuality === "high" ? imageQuality : pricing.defaultQuality;
|
|
651
|
+
const resolution = resolveOpenAiImagePriceResolution(imageSize) ?? pricing.defaultResolution;
|
|
652
|
+
return responseImages * pricing.imagePrices[quality][resolution];
|
|
653
|
+
}
|
|
654
|
+
function resolveOpenAiImagePriceResolution(imageSize) {
|
|
655
|
+
if (imageSize === "1024x1024" || imageSize === "1024x1536" || imageSize === "1536x1024") {
|
|
656
|
+
return imageSize;
|
|
657
|
+
}
|
|
658
|
+
if (!imageSize || imageSize === "auto") {
|
|
659
|
+
return void 0;
|
|
660
|
+
}
|
|
661
|
+
const match = /^(\d+)x(\d+)$/.exec(imageSize);
|
|
662
|
+
if (!match) {
|
|
663
|
+
return void 0;
|
|
664
|
+
}
|
|
665
|
+
const width = Number(match[1]);
|
|
666
|
+
const height = Number(match[2]);
|
|
667
|
+
if (!Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0) {
|
|
668
|
+
return void 0;
|
|
669
|
+
}
|
|
670
|
+
if (width === height) {
|
|
671
|
+
return "1024x1024";
|
|
672
|
+
}
|
|
673
|
+
return width > height ? "1536x1024" : "1024x1536";
|
|
674
|
+
}
|
|
518
675
|
|
|
519
676
|
// src/openai/chatgpt-codex.ts
|
|
520
677
|
var import_node_os2 = __toESM(require("os"), 1);
|
|
@@ -4495,13 +4652,13 @@ var LLM_TEXT_MODEL_IDS = [
|
|
|
4495
4652
|
...FIREWORKS_MODEL_IDS,
|
|
4496
4653
|
...GEMINI_TEXT_MODEL_IDS
|
|
4497
4654
|
];
|
|
4498
|
-
var LLM_IMAGE_MODEL_IDS = [...GEMINI_IMAGE_MODEL_IDS];
|
|
4655
|
+
var LLM_IMAGE_MODEL_IDS = [...OPENAI_IMAGE_MODEL_IDS, ...GEMINI_IMAGE_MODEL_IDS];
|
|
4499
4656
|
var LLM_MODEL_IDS = [...LLM_TEXT_MODEL_IDS, ...LLM_IMAGE_MODEL_IDS];
|
|
4500
4657
|
function isLlmTextModelId(value) {
|
|
4501
4658
|
return isOpenAiModelId(value) || isChatGptModelId(value) || isFireworksModelId(value) || isGeminiTextModelId(value);
|
|
4502
4659
|
}
|
|
4503
4660
|
function isLlmImageModelId(value) {
|
|
4504
|
-
return isGeminiImageModelId(value);
|
|
4661
|
+
return isOpenAiImageModelId(value) || isGeminiImageModelId(value);
|
|
4505
4662
|
}
|
|
4506
4663
|
function isLlmModelId(value) {
|
|
4507
4664
|
return isLlmTextModelId(value) || isLlmImageModelId(value);
|
|
@@ -4513,6 +4670,9 @@ var LlmJsonCallError = class extends Error {
|
|
|
4513
4670
|
this.name = "LlmJsonCallError";
|
|
4514
4671
|
}
|
|
4515
4672
|
};
|
|
4673
|
+
function isOpenAiGenerateImagesRequest(request) {
|
|
4674
|
+
return isOpenAiImageModelId(request.model);
|
|
4675
|
+
}
|
|
4516
4676
|
function tool(options) {
|
|
4517
4677
|
return {
|
|
4518
4678
|
type: "function",
|
|
@@ -5103,6 +5263,9 @@ function resolveProvider(model) {
|
|
|
5103
5263
|
return { provider: "fireworks", model: fireworksModel };
|
|
5104
5264
|
}
|
|
5105
5265
|
}
|
|
5266
|
+
if (isOpenAiImageModelId(model)) {
|
|
5267
|
+
return { provider: "openai", model };
|
|
5268
|
+
}
|
|
5106
5269
|
if (isOpenAiModelId(model)) {
|
|
5107
5270
|
return {
|
|
5108
5271
|
provider: "openai",
|
|
@@ -5110,7 +5273,7 @@ function resolveProvider(model) {
|
|
|
5110
5273
|
serviceTier: resolveOpenAiServiceTier(model)
|
|
5111
5274
|
};
|
|
5112
5275
|
}
|
|
5113
|
-
throw new Error(`Unsupported
|
|
5276
|
+
throw new Error(`Unsupported model: ${model}`);
|
|
5114
5277
|
}
|
|
5115
5278
|
function isOpenAiCodexModel(modelId) {
|
|
5116
5279
|
return modelId.includes("codex");
|
|
@@ -6266,8 +6429,11 @@ function mergeTokenUpdates(current, next) {
|
|
|
6266
6429
|
}
|
|
6267
6430
|
return {
|
|
6268
6431
|
promptTokens: next.promptTokens ?? current.promptTokens,
|
|
6432
|
+
promptTextTokens: next.promptTextTokens ?? current.promptTextTokens,
|
|
6433
|
+
promptImageTokens: next.promptImageTokens ?? current.promptImageTokens,
|
|
6269
6434
|
cachedTokens: next.cachedTokens ?? current.cachedTokens,
|
|
6270
6435
|
responseTokens: next.responseTokens ?? current.responseTokens,
|
|
6436
|
+
responseTextTokens: next.responseTextTokens ?? current.responseTextTokens,
|
|
6271
6437
|
responseImageTokens: next.responseImageTokens ?? current.responseImageTokens,
|
|
6272
6438
|
thinkingTokens: next.thinkingTokens ?? current.thinkingTokens,
|
|
6273
6439
|
totalTokens: next.totalTokens ?? current.totalTokens,
|
|
@@ -6290,8 +6456,11 @@ function sumUsageTokens(current, next) {
|
|
|
6290
6456
|
}
|
|
6291
6457
|
return {
|
|
6292
6458
|
promptTokens: sumUsageValue(current?.promptTokens, next.promptTokens),
|
|
6459
|
+
promptTextTokens: sumUsageValue(current?.promptTextTokens, next.promptTextTokens),
|
|
6460
|
+
promptImageTokens: sumUsageValue(current?.promptImageTokens, next.promptImageTokens),
|
|
6293
6461
|
cachedTokens: sumUsageValue(current?.cachedTokens, next.cachedTokens),
|
|
6294
6462
|
responseTokens: sumUsageValue(current?.responseTokens, next.responseTokens),
|
|
6463
|
+
responseTextTokens: sumUsageValue(current?.responseTextTokens, next.responseTextTokens),
|
|
6295
6464
|
responseImageTokens: sumUsageValue(current?.responseImageTokens, next.responseImageTokens),
|
|
6296
6465
|
thinkingTokens: sumUsageValue(current?.thinkingTokens, next.thinkingTokens),
|
|
6297
6466
|
totalTokens: sumUsageValue(current?.totalTokens, next.totalTokens),
|
|
@@ -6406,10 +6575,22 @@ function extractOpenAiUsageTokens(usage) {
|
|
|
6406
6575
|
const cachedTokens = toMaybeNumber(
|
|
6407
6576
|
usage.input_tokens_details?.cached_tokens
|
|
6408
6577
|
);
|
|
6578
|
+
const promptTextTokens = toMaybeNumber(
|
|
6579
|
+
usage.input_tokens_details?.text_tokens
|
|
6580
|
+
);
|
|
6581
|
+
const promptImageTokens = toMaybeNumber(
|
|
6582
|
+
usage.input_tokens_details?.image_tokens
|
|
6583
|
+
);
|
|
6409
6584
|
const outputTokensRaw = toMaybeNumber(usage.output_tokens);
|
|
6410
6585
|
const reasoningTokens = toMaybeNumber(
|
|
6411
6586
|
usage.output_tokens_details?.reasoning_tokens
|
|
6412
6587
|
);
|
|
6588
|
+
const responseTextTokens = toMaybeNumber(
|
|
6589
|
+
usage.output_tokens_details?.text_tokens
|
|
6590
|
+
);
|
|
6591
|
+
const responseImageTokens = toMaybeNumber(
|
|
6592
|
+
usage.output_tokens_details?.image_tokens
|
|
6593
|
+
);
|
|
6413
6594
|
const totalTokens = toMaybeNumber(usage.total_tokens);
|
|
6414
6595
|
let responseTokens;
|
|
6415
6596
|
if (outputTokensRaw !== void 0) {
|
|
@@ -6421,8 +6602,12 @@ function extractOpenAiUsageTokens(usage) {
|
|
|
6421
6602
|
}
|
|
6422
6603
|
return {
|
|
6423
6604
|
promptTokens,
|
|
6605
|
+
promptTextTokens,
|
|
6606
|
+
promptImageTokens,
|
|
6424
6607
|
cachedTokens,
|
|
6425
6608
|
responseTokens,
|
|
6609
|
+
responseTextTokens,
|
|
6610
|
+
responseImageTokens,
|
|
6426
6611
|
thinkingTokens: reasoningTokens,
|
|
6427
6612
|
totalTokens
|
|
6428
6613
|
};
|
|
@@ -7898,6 +8083,9 @@ async function runTextCall(params) {
|
|
|
7898
8083
|
const { result } = await collectFileUploadMetrics(async () => {
|
|
7899
8084
|
try {
|
|
7900
8085
|
if (provider === "openai") {
|
|
8086
|
+
if (isOpenAiImageModelId(request.model)) {
|
|
8087
|
+
throw new Error("gpt-image-2 is an image generation model; use generateImages().");
|
|
8088
|
+
}
|
|
7901
8089
|
const openAiInput = await maybePrepareOpenAiPromptInput(
|
|
7902
8090
|
toOpenAiInput(contents, {
|
|
7903
8091
|
defaultMediaResolution: request.mediaResolution,
|
|
@@ -10263,7 +10451,184 @@ async function gradeGeneratedImage(params) {
|
|
|
10263
10451
|
});
|
|
10264
10452
|
return { grade: value.grade, result };
|
|
10265
10453
|
}
|
|
10454
|
+
function resolveOpenAiImageMimeType(outputFormat) {
|
|
10455
|
+
switch (outputFormat) {
|
|
10456
|
+
case "jpeg":
|
|
10457
|
+
return "image/jpeg";
|
|
10458
|
+
case "webp":
|
|
10459
|
+
return "image/webp";
|
|
10460
|
+
case "png":
|
|
10461
|
+
case void 0:
|
|
10462
|
+
return "image/png";
|
|
10463
|
+
}
|
|
10464
|
+
}
|
|
10465
|
+
function buildOpenAiImagePrompt(params) {
|
|
10466
|
+
return [
|
|
10467
|
+
"Follow the requested visual style.",
|
|
10468
|
+
"",
|
|
10469
|
+
"Style:",
|
|
10470
|
+
params.stylePrompt.trim(),
|
|
10471
|
+
...params.hasStyleImages ? [
|
|
10472
|
+
"",
|
|
10473
|
+
"Use the attached reference image or images for palette, lighting, mood, composition, and material feel."
|
|
10474
|
+
] : [],
|
|
10475
|
+
"",
|
|
10476
|
+
"Image:",
|
|
10477
|
+
params.imagePrompt.trim()
|
|
10478
|
+
].filter((line) => line.length > 0).join("\n");
|
|
10479
|
+
}
|
|
10480
|
+
function resolveOpenAiImageRequestParams(request) {
|
|
10481
|
+
if (request.partialImages !== void 0) {
|
|
10482
|
+
throw new Error("partialImages is only supported for streaming image generation.");
|
|
10483
|
+
}
|
|
10484
|
+
if (request.outputCompression !== void 0 && (!Number.isInteger(request.outputCompression) || request.outputCompression < 0 || request.outputCompression > 100)) {
|
|
10485
|
+
throw new Error("outputCompression must be an integer from 0 to 100.");
|
|
10486
|
+
}
|
|
10487
|
+
if (request.outputCompression !== void 0 && request.outputFormat !== "jpeg" && request.outputFormat !== "webp") {
|
|
10488
|
+
throw new Error("outputCompression requires outputFormat to be jpeg or webp.");
|
|
10489
|
+
}
|
|
10490
|
+
const size = request.imageResolution ?? "auto";
|
|
10491
|
+
const sizeValidation = validateOpenAiGptImage2Resolution(size);
|
|
10492
|
+
if (!sizeValidation.valid) {
|
|
10493
|
+
throw new Error(
|
|
10494
|
+
`imageResolution ${JSON.stringify(size)} is not supported by gpt-image-2: ${sizeValidation.reason}`
|
|
10495
|
+
);
|
|
10496
|
+
}
|
|
10497
|
+
return {
|
|
10498
|
+
size,
|
|
10499
|
+
quality: request.imageQuality ?? "auto",
|
|
10500
|
+
outputFormat: request.outputFormat,
|
|
10501
|
+
n: request.numImages ?? 1,
|
|
10502
|
+
background: request.background,
|
|
10503
|
+
moderation: request.moderation
|
|
10504
|
+
};
|
|
10505
|
+
}
|
|
10506
|
+
async function createOpenAiStyleImageFiles(styleImages) {
|
|
10507
|
+
if (!styleImages || styleImages.length === 0) {
|
|
10508
|
+
return void 0;
|
|
10509
|
+
}
|
|
10510
|
+
return await Promise.all(
|
|
10511
|
+
styleImages.map(async (image, index) => {
|
|
10512
|
+
const mimeType = image.mimeType ?? "image/png";
|
|
10513
|
+
const extension = resolveAttachmentExtension(mimeType);
|
|
10514
|
+
return await (0, import_openai3.toFile)(image.data, `style-${index + 1}.${extension}`, { type: mimeType });
|
|
10515
|
+
})
|
|
10516
|
+
);
|
|
10517
|
+
}
|
|
10518
|
+
async function generateImagesWithOpenAiImageApi(request) {
|
|
10519
|
+
const promptEntries = Array.from(request.imagePrompts, (rawPrompt, index) => {
|
|
10520
|
+
const prompt = rawPrompt.trim();
|
|
10521
|
+
if (!prompt) {
|
|
10522
|
+
throw new Error(`imagePrompts[${index}] must be a non-empty string`);
|
|
10523
|
+
}
|
|
10524
|
+
return prompt;
|
|
10525
|
+
});
|
|
10526
|
+
if (promptEntries.length === 0) {
|
|
10527
|
+
return [];
|
|
10528
|
+
}
|
|
10529
|
+
const provider = resolveProvider(request.model).provider;
|
|
10530
|
+
const telemetry = createLlmTelemetryEmitter({
|
|
10531
|
+
telemetry: request.telemetry,
|
|
10532
|
+
operation: "generateImages",
|
|
10533
|
+
provider,
|
|
10534
|
+
model: request.model
|
|
10535
|
+
});
|
|
10536
|
+
const startedAtMs = Date.now();
|
|
10537
|
+
const params = resolveOpenAiImageRequestParams(request);
|
|
10538
|
+
const styleImages = await createOpenAiStyleImageFiles(request.styleImages);
|
|
10539
|
+
const hasStyleImages = Boolean(styleImages && styleImages.length > 0);
|
|
10540
|
+
const outputMimeType = resolveOpenAiImageMimeType(params.outputFormat);
|
|
10541
|
+
let totalUsage;
|
|
10542
|
+
let costUsd = 0;
|
|
10543
|
+
let outputImages = 0;
|
|
10544
|
+
telemetry.emit({
|
|
10545
|
+
type: "llm.call.started",
|
|
10546
|
+
imagePromptCount: promptEntries.length,
|
|
10547
|
+
styleImageCount: request.styleImages?.length ?? 0,
|
|
10548
|
+
numImagesPerPrompt: params.n
|
|
10549
|
+
});
|
|
10550
|
+
try {
|
|
10551
|
+
const images = [];
|
|
10552
|
+
for (const imagePrompt of promptEntries) {
|
|
10553
|
+
const prompt = buildOpenAiImagePrompt({
|
|
10554
|
+
stylePrompt: request.stylePrompt,
|
|
10555
|
+
imagePrompt,
|
|
10556
|
+
hasStyleImages
|
|
10557
|
+
});
|
|
10558
|
+
const response = await runOpenAiCall(async (client) => {
|
|
10559
|
+
const payload = {
|
|
10560
|
+
model: request.model,
|
|
10561
|
+
prompt,
|
|
10562
|
+
n: params.n,
|
|
10563
|
+
size: params.size,
|
|
10564
|
+
quality: params.quality,
|
|
10565
|
+
...params.outputFormat ? { output_format: params.outputFormat } : {},
|
|
10566
|
+
...request.outputCompression !== void 0 ? { output_compression: request.outputCompression } : {},
|
|
10567
|
+
...params.background ? { background: params.background } : {},
|
|
10568
|
+
...params.moderation ? { moderation: params.moderation } : {}
|
|
10569
|
+
};
|
|
10570
|
+
if (styleImages && styleImages.length > 0) {
|
|
10571
|
+
return await client.images.edit(
|
|
10572
|
+
{
|
|
10573
|
+
...payload,
|
|
10574
|
+
image: styleImages
|
|
10575
|
+
},
|
|
10576
|
+
{ signal: request.signal }
|
|
10577
|
+
);
|
|
10578
|
+
}
|
|
10579
|
+
return await client.images.generate(payload, { signal: request.signal });
|
|
10580
|
+
}, request.model);
|
|
10581
|
+
const data = Array.isArray(response.data) ? response.data ?? [] : [];
|
|
10582
|
+
for (const item of data) {
|
|
10583
|
+
if (typeof item.b64_json !== "string" || item.b64_json.length === 0) {
|
|
10584
|
+
continue;
|
|
10585
|
+
}
|
|
10586
|
+
images.push({
|
|
10587
|
+
mimeType: outputMimeType,
|
|
10588
|
+
data: import_node_buffer4.Buffer.from(item.b64_json, "base64")
|
|
10589
|
+
});
|
|
10590
|
+
}
|
|
10591
|
+
outputImages = images.length;
|
|
10592
|
+
const usage = extractOpenAiUsageTokens(response.usage);
|
|
10593
|
+
totalUsage = sumUsageTokens(totalUsage, usage);
|
|
10594
|
+
costUsd += estimateCallCostUsd({
|
|
10595
|
+
modelId: request.model,
|
|
10596
|
+
tokens: usage,
|
|
10597
|
+
responseImages: data.length,
|
|
10598
|
+
imageSize: params.size,
|
|
10599
|
+
imageQuality: params.quality
|
|
10600
|
+
});
|
|
10601
|
+
}
|
|
10602
|
+
telemetry.emit({
|
|
10603
|
+
type: "llm.call.completed",
|
|
10604
|
+
success: true,
|
|
10605
|
+
durationMs: Math.max(0, Date.now() - startedAtMs),
|
|
10606
|
+
usage: totalUsage,
|
|
10607
|
+
costUsd,
|
|
10608
|
+
imageCount: images.length,
|
|
10609
|
+
attempts: promptEntries.length
|
|
10610
|
+
});
|
|
10611
|
+
return images;
|
|
10612
|
+
} catch (error) {
|
|
10613
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
10614
|
+
telemetry.emit({
|
|
10615
|
+
type: "llm.call.completed",
|
|
10616
|
+
success: false,
|
|
10617
|
+
durationMs: Math.max(0, Date.now() - startedAtMs),
|
|
10618
|
+
usage: totalUsage,
|
|
10619
|
+
costUsd,
|
|
10620
|
+
imageCount: outputImages,
|
|
10621
|
+
error: err.message
|
|
10622
|
+
});
|
|
10623
|
+
throw err;
|
|
10624
|
+
} finally {
|
|
10625
|
+
await telemetry.flush();
|
|
10626
|
+
}
|
|
10627
|
+
}
|
|
10266
10628
|
async function generateImages(request) {
|
|
10629
|
+
if (isOpenAiGenerateImagesRequest(request)) {
|
|
10630
|
+
return await generateImagesWithOpenAiImageApi(request);
|
|
10631
|
+
}
|
|
10267
10632
|
const maxAttempts = Math.max(1, Math.floor(request.maxAttempts ?? 4));
|
|
10268
10633
|
const promptList = Array.from(request.imagePrompts);
|
|
10269
10634
|
if (promptList.length === 0) {
|
|
@@ -10277,7 +10642,7 @@ async function generateImages(request) {
|
|
|
10277
10642
|
}
|
|
10278
10643
|
return { index: arrayIndex + 1, prompt: trimmedPrompt };
|
|
10279
10644
|
});
|
|
10280
|
-
const gradingPrompt = request.imageGradingPrompt
|
|
10645
|
+
const gradingPrompt = request.imageGradingPrompt?.trim() ?? "";
|
|
10281
10646
|
if (!gradingPrompt) {
|
|
10282
10647
|
throw new Error("imageGradingPrompt must be a non-empty string");
|
|
10283
10648
|
}
|
|
@@ -13575,8 +13940,11 @@ function summarizeResultUsage(result) {
|
|
|
13575
13940
|
}
|
|
13576
13941
|
summary = {
|
|
13577
13942
|
promptTokens: sumUsageValue2(summary?.promptTokens, usage.promptTokens),
|
|
13943
|
+
promptTextTokens: sumUsageValue2(summary?.promptTextTokens, usage.promptTextTokens),
|
|
13944
|
+
promptImageTokens: sumUsageValue2(summary?.promptImageTokens, usage.promptImageTokens),
|
|
13578
13945
|
cachedTokens: sumUsageValue2(summary?.cachedTokens, usage.cachedTokens),
|
|
13579
13946
|
responseTokens: sumUsageValue2(summary?.responseTokens, usage.responseTokens),
|
|
13947
|
+
responseTextTokens: sumUsageValue2(summary?.responseTextTokens, usage.responseTextTokens),
|
|
13580
13948
|
responseImageTokens: sumUsageValue2(summary?.responseImageTokens, usage.responseImageTokens),
|
|
13581
13949
|
thinkingTokens: sumUsageValue2(summary?.thinkingTokens, usage.thinkingTokens),
|
|
13582
13950
|
totalTokens: sumUsageValue2(summary?.totalTokens, usage.totalTokens),
|
|
@@ -14308,6 +14676,17 @@ async function runCandidateEvolution(options) {
|
|
|
14308
14676
|
LLM_MODEL_IDS,
|
|
14309
14677
|
LLM_TEXT_MODEL_IDS,
|
|
14310
14678
|
LlmJsonCallError,
|
|
14679
|
+
OPENAI_GPT_IMAGE_2_AUTO_RESOLUTION,
|
|
14680
|
+
OPENAI_GPT_IMAGE_2_BACKGROUNDS,
|
|
14681
|
+
OPENAI_GPT_IMAGE_2_MODERATION_LEVELS,
|
|
14682
|
+
OPENAI_GPT_IMAGE_2_NUM_IMAGES,
|
|
14683
|
+
OPENAI_GPT_IMAGE_2_OUTPUT_FORMATS,
|
|
14684
|
+
OPENAI_GPT_IMAGE_2_PARTIAL_IMAGE_COUNTS,
|
|
14685
|
+
OPENAI_GPT_IMAGE_2_POPULAR_RESOLUTIONS,
|
|
14686
|
+
OPENAI_GPT_IMAGE_2_QUALITY_LEVELS,
|
|
14687
|
+
OPENAI_GPT_IMAGE_2_RESOLUTIONS,
|
|
14688
|
+
OPENAI_GPT_IMAGE_2_SIZE_CONSTRAINTS,
|
|
14689
|
+
OPENAI_IMAGE_MODEL_IDS,
|
|
14311
14690
|
OPENAI_MODEL_IDS,
|
|
14312
14691
|
appendMarkdownSourcesSection,
|
|
14313
14692
|
applyPatch,
|
|
@@ -14357,6 +14736,7 @@ async function runCandidateEvolution(options) {
|
|
|
14357
14736
|
isLlmImageModelId,
|
|
14358
14737
|
isLlmModelId,
|
|
14359
14738
|
isLlmTextModelId,
|
|
14739
|
+
isOpenAiImageModelId,
|
|
14360
14740
|
isOpenAiModelId,
|
|
14361
14741
|
loadEnvFromFile,
|
|
14362
14742
|
loadLocalEnv,
|
|
@@ -14376,6 +14756,7 @@ async function runCandidateEvolution(options) {
|
|
|
14376
14756
|
streamToolLoop,
|
|
14377
14757
|
stripCodexCitationMarkers,
|
|
14378
14758
|
toGeminiJsonSchema,
|
|
14379
|
-
tool
|
|
14759
|
+
tool,
|
|
14760
|
+
validateOpenAiGptImage2Resolution
|
|
14380
14761
|
});
|
|
14381
14762
|
//# sourceMappingURL=index.cjs.map
|