@howells/motif-sdk 5.3.0 → 5.4.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 +6 -12
- package/dist/image.js +44 -13
- package/dist/index.cjs +500 -206
- package/dist/index.d.cts +52 -23
- package/dist/index.d.ts +52 -23
- package/dist/index.js +500 -206
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -556,6 +556,7 @@ const CARRIED_FIELDS = [
|
|
|
556
556
|
"video",
|
|
557
557
|
"prompt",
|
|
558
558
|
"mask",
|
|
559
|
+
"markup",
|
|
559
560
|
"references",
|
|
560
561
|
"boxes",
|
|
561
562
|
"margin",
|
|
@@ -729,7 +730,7 @@ const MODELS = {
|
|
|
729
730
|
rank: 2
|
|
730
731
|
}
|
|
731
732
|
},
|
|
732
|
-
useCase: "Frontier generation and editing
|
|
733
|
+
useCase: "Frontier generation and editing at max reasoning effort; scored as GPT Image 2.5 until 2.6 is benchmarked"
|
|
733
734
|
},
|
|
734
735
|
defaultQuality: "max",
|
|
735
736
|
customImageSize: {
|
|
@@ -739,11 +740,11 @@ const MODELS = {
|
|
|
739
740
|
minPixels: 655360,
|
|
740
741
|
multipleOf: 16
|
|
741
742
|
},
|
|
742
|
-
editEndpoint: "openai/gpt-image-2.
|
|
743
|
-
endpoint: "openai/gpt-image-2.
|
|
743
|
+
editEndpoint: "openai/gpt-image-2.6/flare/edit",
|
|
744
|
+
endpoint: "openai/gpt-image-2.6/flare/text-to-image",
|
|
744
745
|
maxReferenceImages: 16,
|
|
745
|
-
name: "GPT Image 2.
|
|
746
|
-
pricing: "Token-based:
|
|
746
|
+
name: "GPT Image 2.6 Flare",
|
|
747
|
+
pricing: "Token-based: image $8/M input, $2/M cached, $30/M output; text $5/M input, $1.25/M cached",
|
|
747
748
|
pricePerImageUsd: null,
|
|
748
749
|
sizeMode: "image_size_enum",
|
|
749
750
|
supportsAspect: true,
|
|
@@ -751,8 +752,23 @@ const MODELS = {
|
|
|
751
752
|
supportsEdit: true,
|
|
752
753
|
supportsMaskImage: true,
|
|
753
754
|
maskImageField: "mask_url",
|
|
755
|
+
supportsMarkupImage: true,
|
|
756
|
+
autoEditSize: true,
|
|
754
757
|
supportsNumImages: true,
|
|
755
758
|
supportsOutputFormat: true,
|
|
759
|
+
supportedOutputFormats: [
|
|
760
|
+
"jpeg",
|
|
761
|
+
"png",
|
|
762
|
+
"webp",
|
|
763
|
+
"gif",
|
|
764
|
+
"svg"
|
|
765
|
+
],
|
|
766
|
+
supportedEditOutputFormats: [
|
|
767
|
+
"jpeg",
|
|
768
|
+
"png",
|
|
769
|
+
"webp",
|
|
770
|
+
"gif"
|
|
771
|
+
],
|
|
756
772
|
supportsQuality: true,
|
|
757
773
|
supportedQualities: [
|
|
758
774
|
"auto",
|
|
@@ -781,7 +797,7 @@ const MODELS = {
|
|
|
781
797
|
rank: 1
|
|
782
798
|
}
|
|
783
799
|
},
|
|
784
|
-
useCase: "Frontier generation and editing
|
|
800
|
+
useCase: "Frontier generation and editing at max reasoning effort; scored as GPT Image 2.5 until 2.6 is benchmarked"
|
|
785
801
|
},
|
|
786
802
|
defaultQuality: "max",
|
|
787
803
|
customImageSize: {
|
|
@@ -791,11 +807,11 @@ const MODELS = {
|
|
|
791
807
|
minPixels: 655360,
|
|
792
808
|
multipleOf: 16
|
|
793
809
|
},
|
|
794
|
-
editEndpoint: "openai/gpt-image-2.
|
|
795
|
-
endpoint: "openai/gpt-image-2.
|
|
810
|
+
editEndpoint: "openai/gpt-image-2.6/sunburst/edit",
|
|
811
|
+
endpoint: "openai/gpt-image-2.6/sunburst/text-to-image",
|
|
796
812
|
maxReferenceImages: 16,
|
|
797
|
-
name: "GPT Image 2.
|
|
798
|
-
pricing: "Token-based:
|
|
813
|
+
name: "GPT Image 2.6 Sunburst",
|
|
814
|
+
pricing: "Token-based: image $8/M input, $2/M cached, $30/M output; text $5/M input, $1.25/M cached",
|
|
799
815
|
pricePerImageUsd: null,
|
|
800
816
|
sizeMode: "image_size_enum",
|
|
801
817
|
supportsAspect: true,
|
|
@@ -803,8 +819,23 @@ const MODELS = {
|
|
|
803
819
|
supportsEdit: true,
|
|
804
820
|
supportsMaskImage: true,
|
|
805
821
|
maskImageField: "mask_url",
|
|
822
|
+
supportsMarkupImage: true,
|
|
823
|
+
autoEditSize: true,
|
|
806
824
|
supportsNumImages: true,
|
|
807
825
|
supportsOutputFormat: true,
|
|
826
|
+
supportedOutputFormats: [
|
|
827
|
+
"jpeg",
|
|
828
|
+
"png",
|
|
829
|
+
"webp",
|
|
830
|
+
"gif",
|
|
831
|
+
"svg"
|
|
832
|
+
],
|
|
833
|
+
supportedEditOutputFormats: [
|
|
834
|
+
"jpeg",
|
|
835
|
+
"png",
|
|
836
|
+
"webp",
|
|
837
|
+
"gif"
|
|
838
|
+
],
|
|
808
839
|
supportsQuality: true,
|
|
809
840
|
supportedQualities: [
|
|
810
841
|
"auto",
|
|
@@ -2673,6 +2704,18 @@ function requestIdFromBody(text) {
|
|
|
2673
2704
|
* error can name both what a model does support and which models support the
|
|
2674
2705
|
* option that was refused.
|
|
2675
2706
|
*/
|
|
2707
|
+
const DEFAULT_OUTPUT_FORMATS = [
|
|
2708
|
+
"jpeg",
|
|
2709
|
+
"png",
|
|
2710
|
+
"webp"
|
|
2711
|
+
];
|
|
2712
|
+
/** The output formats a Model returns, for an edit when `editing` is set.
|
|
2713
|
+
* A Model that takes no format returns none it can be asked for. */
|
|
2714
|
+
function outputFormatsOf(config, editing) {
|
|
2715
|
+
if (config.supportsOutputFormat !== true) return [];
|
|
2716
|
+
const formats = config.supportedOutputFormats ?? DEFAULT_OUTPUT_FORMATS;
|
|
2717
|
+
return editing ? config.supportedEditOutputFormats ?? formats : formats;
|
|
2718
|
+
}
|
|
2676
2719
|
/** Generation options a model can refuse, with the registry test for each. */
|
|
2677
2720
|
const OPTION_CAPABILITIES = {
|
|
2678
2721
|
aspect: (config) => (config.sizeMode ?? "aspect_ratio") !== "none",
|
|
@@ -2688,6 +2731,7 @@ const OPTION_CAPABILITIES = {
|
|
|
2688
2731
|
imageSize: (config) => config.sizeMode === "gpt_size" || config.sizeMode === "image_size_enum",
|
|
2689
2732
|
inputFidelity: (config) => config.sizeMode === "gpt_size",
|
|
2690
2733
|
limitGenerations: (config) => config.supportsLimitGenerations === true,
|
|
2734
|
+
markupImageUrl: (config) => config.supportsMarkupImage === true,
|
|
2691
2735
|
maskImageUrl: (config) => config.supportsMaskImage === true,
|
|
2692
2736
|
negativePrompt: (config) => config.supportsNegativePrompt === true,
|
|
2693
2737
|
numImages: (config) => config.supportsNumImages,
|
|
@@ -2821,6 +2865,9 @@ function validateGenerateOptions(options, config, sizeMode, hasEditImages) {
|
|
|
2821
2865
|
if (options.imagePromptStrength !== void 0 && !hasEditImages) throw new Error("imagePromptStrength requires editImageUrls");
|
|
2822
2866
|
if (options.maskImageUrl !== void 0 && config.supportsMaskImage !== true) unsupported(config, "maskImageUrl");
|
|
2823
2867
|
if (options.maskImageUrl !== void 0 && !hasEditImages) throw new Error("maskImageUrl requires editImageUrls");
|
|
2868
|
+
if (options.markupImageUrl !== void 0 && config.supportsMarkupImage !== true) unsupported(config, "markupImageUrl");
|
|
2869
|
+
if (options.markupImageUrl !== void 0 && !hasEditImages) throw new Error("markupImageUrl requires editImageUrls");
|
|
2870
|
+
if (options.markupImageUrl !== void 0 && options.maskImageUrl !== void 0) throw new Error("A markup image can't be combined with a mask.");
|
|
2824
2871
|
if (options.seed !== void 0 && config.supportsSeed !== true) unsupported(config, "seed");
|
|
2825
2872
|
if (options.outputFormat !== void 0 && config.supportsOutputFormat !== true) unsupported(config, "outputFormat");
|
|
2826
2873
|
if (options.quality !== void 0 && config.supportsQuality !== true) unsupported(config, "quality");
|
|
@@ -2860,7 +2907,7 @@ function validateGenerateOptions(options, config, sizeMode, hasEditImages) {
|
|
|
2860
2907
|
* - `none`: no size control (video models)
|
|
2861
2908
|
*/
|
|
2862
2909
|
function buildGenerateBody(options) {
|
|
2863
|
-
const { prompt, model, creative, aspect = "1:1", resolution = "2K", numImages = 1, background, editImageUrls, enableGoogleSearch, enableSafetyChecker, transparent, inputFidelity, imageSize, imagePromptStrength, limitGenerations, maskImageUrl, seed, outputFormat, quality, negativePrompt, style, syncMode, thinkingLevel, renderingSpeed, guidanceScale, numInferenceSteps, raw, enhancePrompt, safetyTolerance, enableWebSearch, expandPrompt } = options;
|
|
2910
|
+
const { prompt, model, creative, aspect = "1:1", resolution = "2K", numImages = 1, background, editImageUrls, enableGoogleSearch, enableSafetyChecker, transparent, inputFidelity, imageSize, imagePromptStrength, limitGenerations, maskImageUrl, markupImageUrl, seed, outputFormat, quality, negativePrompt, style, syncMode, thinkingLevel, renderingSpeed, guidanceScale, numInferenceSteps, raw, enhancePrompt, safetyTolerance, enableWebSearch, expandPrompt } = options;
|
|
2864
2911
|
const config = MODELS[model];
|
|
2865
2912
|
if (!config) throw new Error(`Unknown model: ${model}`);
|
|
2866
2913
|
let { endpoint } = config;
|
|
@@ -2879,7 +2926,7 @@ function buildGenerateBody(options) {
|
|
|
2879
2926
|
body.image_size = normalizedImageSize ?? (hasEditImages && !explicitAspect ? "auto" : aspectToGptSize(aspect));
|
|
2880
2927
|
break;
|
|
2881
2928
|
case "image_size_enum":
|
|
2882
|
-
body.image_size = normalizedImageSize ?? falImageSizeFor(config, aspect, resolution);
|
|
2929
|
+
body.image_size = normalizedImageSize ?? (config.autoEditSize === true && hasEditImages && !explicitAspect ? "auto" : falImageSizeFor(config, aspect, resolution));
|
|
2883
2930
|
break;
|
|
2884
2931
|
case "aspect_ratio":
|
|
2885
2932
|
if (config.supportsAspect) body.aspect_ratio = aspect;
|
|
@@ -2892,7 +2939,8 @@ function buildGenerateBody(options) {
|
|
|
2892
2939
|
if (syncMode !== void 0 && config.supportsSyncMode === true) body.sync_mode = syncMode;
|
|
2893
2940
|
if (seed !== void 0 && config.supportsSeed === true) body.seed = seed;
|
|
2894
2941
|
if (outputFormat && config.supportsOutputFormat === true && transparent !== true) {
|
|
2895
|
-
|
|
2942
|
+
const formats = outputFormatsOf(config, hasEditImages);
|
|
2943
|
+
if (!formats.includes(outputFormat)) throw new Error(`${config.name} ${hasEditImages ? "edits support" : "supports"} output formats: ${formats.join(", ")}`);
|
|
2896
2944
|
body.output_format = outputFormat;
|
|
2897
2945
|
}
|
|
2898
2946
|
if (config.supportsRaw === true && raw !== void 0) body.raw = raw;
|
|
@@ -2951,6 +2999,7 @@ function buildGenerateBody(options) {
|
|
|
2951
2999
|
if (inputFidelity) body.input_fidelity = inputFidelity;
|
|
2952
3000
|
}
|
|
2953
3001
|
if (maskImageUrl !== void 0 && maskImageUrl !== "" && config.supportsMaskImage === true) body[config.maskImageField ?? "mask_image_url"] = maskImageUrl;
|
|
3002
|
+
if (markupImageUrl !== void 0 && config.supportsMarkupImage === true) body.markup_image_url = markupImageUrl;
|
|
2954
3003
|
return {
|
|
2955
3004
|
body,
|
|
2956
3005
|
endpoint
|
|
@@ -4539,10 +4588,15 @@ async function getToolResult(exec, job) {
|
|
|
4539
4588
|
}
|
|
4540
4589
|
/** Poll a submitted job to completion and fetch its result. Submits nothing. */
|
|
4541
4590
|
async function awaitQueuedRequest(exec, job, onProgress) {
|
|
4591
|
+
let logsSeen = 0;
|
|
4542
4592
|
for (let attempt = 0; attempt < MAX_POLL_ATTEMPTS; attempt++) {
|
|
4543
4593
|
const status = await checkToolStatus(exec, job);
|
|
4544
4594
|
if (status.isErr()) return (0, neverthrow.err)(status.error);
|
|
4545
|
-
|
|
4595
|
+
const logs = status.value.logs ?? [];
|
|
4596
|
+
const fresh = logs.slice(logs.length < logsSeen ? 0 : logsSeen);
|
|
4597
|
+
logsSeen = logs.length;
|
|
4598
|
+
if (fresh.length > 0) onProgress?.(status.value.status, status.value.queuePosition, fresh);
|
|
4599
|
+
else onProgress?.(status.value.status, status.value.queuePosition);
|
|
4546
4600
|
if (status.value.status === "completed") return (await getToolResult(exec, job)).map((data) => ({
|
|
4547
4601
|
data,
|
|
4548
4602
|
requestId: job.requestId
|
|
@@ -5287,6 +5341,201 @@ function sumCosts(costs) {
|
|
|
5287
5341
|
};
|
|
5288
5342
|
}
|
|
5289
5343
|
//#endregion
|
|
5344
|
+
//#region src/image-benchmarks.ts
|
|
5345
|
+
const BOARDS = {
|
|
5346
|
+
"text-to-image": {
|
|
5347
|
+
sunburst: {
|
|
5348
|
+
elo: 1427,
|
|
5349
|
+
rank: 1,
|
|
5350
|
+
uncertainty: 5,
|
|
5351
|
+
evaluatedModel: "GPT-Image-2.5 Sunburst",
|
|
5352
|
+
preliminary: true,
|
|
5353
|
+
votes: 40323
|
|
5354
|
+
},
|
|
5355
|
+
flare: {
|
|
5356
|
+
elo: 1401,
|
|
5357
|
+
rank: 2,
|
|
5358
|
+
uncertainty: 5,
|
|
5359
|
+
evaluatedModel: "GPT-Image-2.5 Flare",
|
|
5360
|
+
preliminary: true,
|
|
5361
|
+
votes: 39218
|
|
5362
|
+
},
|
|
5363
|
+
gpt2: {
|
|
5364
|
+
elo: 1381,
|
|
5365
|
+
rank: 3,
|
|
5366
|
+
uncertainty: 4,
|
|
5367
|
+
evaluatedModel: "GPT-Image-2 (Medium)",
|
|
5368
|
+
votes: 103385
|
|
5369
|
+
},
|
|
5370
|
+
banana21: {
|
|
5371
|
+
elo: 1325,
|
|
5372
|
+
rank: 5,
|
|
5373
|
+
uncertainty: 9,
|
|
5374
|
+
evaluatedModel: "Nano Banana 2.1",
|
|
5375
|
+
preliminary: true,
|
|
5376
|
+
votes: 5290
|
|
5377
|
+
},
|
|
5378
|
+
"grok-image-2": {
|
|
5379
|
+
elo: 1317,
|
|
5380
|
+
rank: 6,
|
|
5381
|
+
uncertainty: 7,
|
|
5382
|
+
evaluatedModel: "Grok Imagine Image 2.0",
|
|
5383
|
+
preliminary: true,
|
|
5384
|
+
votes: 10902
|
|
5385
|
+
},
|
|
5386
|
+
"muse-image": {
|
|
5387
|
+
elo: 1269,
|
|
5388
|
+
rank: 8,
|
|
5389
|
+
uncertainty: 5,
|
|
5390
|
+
evaluatedModel: "Muse Image (Meta)",
|
|
5391
|
+
votes: 53444
|
|
5392
|
+
},
|
|
5393
|
+
qwen3: {
|
|
5394
|
+
elo: 1254,
|
|
5395
|
+
rank: 9,
|
|
5396
|
+
uncertainty: 6,
|
|
5397
|
+
evaluatedModel: "Qwen-Image-3.0-Pro",
|
|
5398
|
+
votes: 11444
|
|
5399
|
+
},
|
|
5400
|
+
"mai-image-2.5": {
|
|
5401
|
+
elo: 1253,
|
|
5402
|
+
rank: 10,
|
|
5403
|
+
uncertainty: 4,
|
|
5404
|
+
evaluatedModel: "MAI-Image-2.5",
|
|
5405
|
+
votes: 61286
|
|
5406
|
+
},
|
|
5407
|
+
seedream5: {
|
|
5408
|
+
elo: 1250,
|
|
5409
|
+
rank: 11,
|
|
5410
|
+
uncertainty: 4,
|
|
5411
|
+
evaluatedModel: "Seedream 5.0 Pro",
|
|
5412
|
+
votes: 105824
|
|
5413
|
+
},
|
|
5414
|
+
"banana2-lite": {
|
|
5415
|
+
elo: 1249,
|
|
5416
|
+
rank: 12,
|
|
5417
|
+
uncertainty: 6,
|
|
5418
|
+
evaluatedModel: "Nano Banana 2 Lite",
|
|
5419
|
+
votes: 17813
|
|
5420
|
+
},
|
|
5421
|
+
banana: {
|
|
5422
|
+
elo: 1246,
|
|
5423
|
+
rank: 13,
|
|
5424
|
+
uncertainty: 3,
|
|
5425
|
+
evaluatedModel: "Nano Banana Pro (2K)",
|
|
5426
|
+
votes: 188288
|
|
5427
|
+
},
|
|
5428
|
+
gpt: {
|
|
5429
|
+
elo: 1236,
|
|
5430
|
+
rank: 14,
|
|
5431
|
+
uncertainty: 3,
|
|
5432
|
+
evaluatedModel: "GPT-Image-1.5 (High Fidelity)",
|
|
5433
|
+
votes: 174012
|
|
5434
|
+
},
|
|
5435
|
+
ideogram4: {
|
|
5436
|
+
elo: 1200,
|
|
5437
|
+
rank: 15,
|
|
5438
|
+
uncertainty: 4,
|
|
5439
|
+
evaluatedModel: "Ideogram 4.0 (Quality)",
|
|
5440
|
+
votes: 72865
|
|
5441
|
+
},
|
|
5442
|
+
"flux2-flex": {
|
|
5443
|
+
elo: 1156,
|
|
5444
|
+
rank: 17,
|
|
5445
|
+
uncertainty: 3,
|
|
5446
|
+
evaluatedModel: "FLUX.2 [flex]",
|
|
5447
|
+
votes: 156876
|
|
5448
|
+
}
|
|
5449
|
+
},
|
|
5450
|
+
"image-edit-single-image": {
|
|
5451
|
+
sunburst: {
|
|
5452
|
+
elo: 1517,
|
|
5453
|
+
rank: 1,
|
|
5454
|
+
uncertainty: 5,
|
|
5455
|
+
evaluatedModel: "GPT-Image-2.5 Sunburst",
|
|
5456
|
+
preliminary: true
|
|
5457
|
+
},
|
|
5458
|
+
flare: {
|
|
5459
|
+
elo: 1482,
|
|
5460
|
+
rank: 2,
|
|
5461
|
+
uncertainty: 5,
|
|
5462
|
+
evaluatedModel: "GPT-Image-2.5 Flare",
|
|
5463
|
+
preliminary: true
|
|
5464
|
+
},
|
|
5465
|
+
gpt2: {
|
|
5466
|
+
elo: 1460,
|
|
5467
|
+
rank: 4,
|
|
5468
|
+
uncertainty: 3,
|
|
5469
|
+
evaluatedModel: "GPT-Image-2 (Medium)"
|
|
5470
|
+
},
|
|
5471
|
+
"grok-image-2": {
|
|
5472
|
+
elo: 1439,
|
|
5473
|
+
rank: 5,
|
|
5474
|
+
uncertainty: 6,
|
|
5475
|
+
evaluatedModel: "Grok Imagine Image 2.0",
|
|
5476
|
+
preliminary: true
|
|
5477
|
+
},
|
|
5478
|
+
banana21: {
|
|
5479
|
+
elo: 1427,
|
|
5480
|
+
rank: 7,
|
|
5481
|
+
uncertainty: 6,
|
|
5482
|
+
evaluatedModel: "Nano Banana 2.1",
|
|
5483
|
+
preliminary: true
|
|
5484
|
+
},
|
|
5485
|
+
"mai-image-2.5": {
|
|
5486
|
+
elo: 1400,
|
|
5487
|
+
rank: 9,
|
|
5488
|
+
uncertainty: 4,
|
|
5489
|
+
evaluatedModel: "MAI-Image-2.5"
|
|
5490
|
+
},
|
|
5491
|
+
"muse-image": {
|
|
5492
|
+
elo: 1400,
|
|
5493
|
+
rank: 10,
|
|
5494
|
+
uncertainty: 4,
|
|
5495
|
+
evaluatedModel: "Muse Image (Meta)"
|
|
5496
|
+
},
|
|
5497
|
+
"grok-image-quality": {
|
|
5498
|
+
elo: 1391,
|
|
5499
|
+
rank: 11,
|
|
5500
|
+
uncertainty: 6,
|
|
5501
|
+
evaluatedModel: "Grok Imagine Image (Quality, 20260519)"
|
|
5502
|
+
},
|
|
5503
|
+
seedream5: {
|
|
5504
|
+
elo: 1390,
|
|
5505
|
+
rank: 12,
|
|
5506
|
+
uncertainty: 3,
|
|
5507
|
+
evaluatedModel: "Seedream 5.0 Pro"
|
|
5508
|
+
},
|
|
5509
|
+
banana: {
|
|
5510
|
+
elo: 1390,
|
|
5511
|
+
rank: 13,
|
|
5512
|
+
uncertainty: 3,
|
|
5513
|
+
evaluatedModel: "Nano Banana Pro (2K)"
|
|
5514
|
+
},
|
|
5515
|
+
gpt: {
|
|
5516
|
+
elo: 1368,
|
|
5517
|
+
rank: 14,
|
|
5518
|
+
uncertainty: 3,
|
|
5519
|
+
evaluatedModel: "GPT-Image-1.5 (High Fidelity)"
|
|
5520
|
+
}
|
|
5521
|
+
}
|
|
5522
|
+
};
|
|
5523
|
+
const SOURCES = {
|
|
5524
|
+
"text-to-image": "https://arena.ai/leaderboard/image/text-to-image",
|
|
5525
|
+
"image-edit-single-image": "https://arena.ai/leaderboard/image/image-edit/single-image"
|
|
5526
|
+
};
|
|
5527
|
+
/** A published score for this route, or null when no matching score was saved. */
|
|
5528
|
+
function imageModelBenchmark(model, task) {
|
|
5529
|
+
const score = BOARDS[task][model];
|
|
5530
|
+
if (score === void 0) return null;
|
|
5531
|
+
return {
|
|
5532
|
+
...score,
|
|
5533
|
+
source: SOURCES[task],
|
|
5534
|
+
snapshotDate: "2026-10-29",
|
|
5535
|
+
task
|
|
5536
|
+
};
|
|
5537
|
+
}
|
|
5538
|
+
//#endregion
|
|
5290
5539
|
//#region src/tasks.ts
|
|
5291
5540
|
/**
|
|
5292
5541
|
* The Task registry: every job Motif does, with the Models that can do it
|
|
@@ -5314,6 +5563,194 @@ const TIERS = [
|
|
|
5314
5563
|
const DEFAULT_TIER = "balanced";
|
|
5315
5564
|
const HAND_RANKED_AT = "2026-09-16";
|
|
5316
5565
|
const IMAGE_RANKED_AT = "2026-10-29";
|
|
5566
|
+
function withImageBenchmarks(models, task) {
|
|
5567
|
+
return models.map((entry) => ({
|
|
5568
|
+
...entry,
|
|
5569
|
+
benchmark: imageModelBenchmark(entry.model, task)
|
|
5570
|
+
}));
|
|
5571
|
+
}
|
|
5572
|
+
const GENERATION_MODELS_RANKED = withImageBenchmarks([
|
|
5573
|
+
{
|
|
5574
|
+
model: "sunburst",
|
|
5575
|
+
tier: "quality"
|
|
5576
|
+
},
|
|
5577
|
+
{
|
|
5578
|
+
model: "flare",
|
|
5579
|
+
tier: "quality"
|
|
5580
|
+
},
|
|
5581
|
+
{
|
|
5582
|
+
model: "gpt2",
|
|
5583
|
+
tier: "quality"
|
|
5584
|
+
},
|
|
5585
|
+
{
|
|
5586
|
+
model: "seedream5",
|
|
5587
|
+
tier: "quality"
|
|
5588
|
+
},
|
|
5589
|
+
{
|
|
5590
|
+
model: "gpt",
|
|
5591
|
+
tier: "quality"
|
|
5592
|
+
},
|
|
5593
|
+
{
|
|
5594
|
+
model: "mai-image-2.5-pro",
|
|
5595
|
+
tier: "quality"
|
|
5596
|
+
},
|
|
5597
|
+
{
|
|
5598
|
+
model: "banana21",
|
|
5599
|
+
tier: "balanced"
|
|
5600
|
+
},
|
|
5601
|
+
{
|
|
5602
|
+
model: "grok-image-2",
|
|
5603
|
+
tier: "balanced"
|
|
5604
|
+
},
|
|
5605
|
+
{
|
|
5606
|
+
model: "muse-image",
|
|
5607
|
+
tier: "balanced"
|
|
5608
|
+
},
|
|
5609
|
+
{
|
|
5610
|
+
model: "banana2",
|
|
5611
|
+
tier: "balanced"
|
|
5612
|
+
},
|
|
5613
|
+
{
|
|
5614
|
+
model: "qwen3",
|
|
5615
|
+
tier: "balanced"
|
|
5616
|
+
},
|
|
5617
|
+
{
|
|
5618
|
+
model: "mai-image-2.5",
|
|
5619
|
+
tier: "balanced"
|
|
5620
|
+
},
|
|
5621
|
+
{
|
|
5622
|
+
model: "banana",
|
|
5623
|
+
tier: "balanced"
|
|
5624
|
+
},
|
|
5625
|
+
{
|
|
5626
|
+
model: "ideogram4",
|
|
5627
|
+
tier: "balanced"
|
|
5628
|
+
},
|
|
5629
|
+
{
|
|
5630
|
+
model: "flux2-flex",
|
|
5631
|
+
tier: "balanced"
|
|
5632
|
+
},
|
|
5633
|
+
{
|
|
5634
|
+
model: "recraft41",
|
|
5635
|
+
tier: "balanced"
|
|
5636
|
+
},
|
|
5637
|
+
{
|
|
5638
|
+
model: "flux2-pro",
|
|
5639
|
+
tier: "balanced"
|
|
5640
|
+
},
|
|
5641
|
+
{
|
|
5642
|
+
model: "krea3",
|
|
5643
|
+
tier: "balanced"
|
|
5644
|
+
},
|
|
5645
|
+
{
|
|
5646
|
+
model: "grok-image-quality",
|
|
5647
|
+
tier: "balanced"
|
|
5648
|
+
},
|
|
5649
|
+
{
|
|
5650
|
+
model: "ideogram3-transparent",
|
|
5651
|
+
requires: ["transparency"],
|
|
5652
|
+
supports: ["transparency"],
|
|
5653
|
+
tier: "balanced"
|
|
5654
|
+
},
|
|
5655
|
+
{
|
|
5656
|
+
model: "banana2-lite",
|
|
5657
|
+
tier: "fast"
|
|
5658
|
+
},
|
|
5659
|
+
{
|
|
5660
|
+
model: "grok-image",
|
|
5661
|
+
tier: "fast"
|
|
5662
|
+
},
|
|
5663
|
+
{
|
|
5664
|
+
model: "seedream5-lite",
|
|
5665
|
+
tier: "fast"
|
|
5666
|
+
},
|
|
5667
|
+
{
|
|
5668
|
+
model: "flux2-turbo",
|
|
5669
|
+
tier: "fast"
|
|
5670
|
+
}
|
|
5671
|
+
], "text-to-image");
|
|
5672
|
+
const EDITING_MODELS_RANKED = withImageBenchmarks([
|
|
5673
|
+
{
|
|
5674
|
+
model: "sunburst",
|
|
5675
|
+
tier: "quality"
|
|
5676
|
+
},
|
|
5677
|
+
{
|
|
5678
|
+
model: "flare",
|
|
5679
|
+
tier: "quality"
|
|
5680
|
+
},
|
|
5681
|
+
{
|
|
5682
|
+
model: "gpt2",
|
|
5683
|
+
tier: "quality"
|
|
5684
|
+
},
|
|
5685
|
+
{
|
|
5686
|
+
model: "seedream5",
|
|
5687
|
+
tier: "quality"
|
|
5688
|
+
},
|
|
5689
|
+
{
|
|
5690
|
+
model: "gpt",
|
|
5691
|
+
tier: "quality"
|
|
5692
|
+
},
|
|
5693
|
+
{
|
|
5694
|
+
model: "mai-image-2.5-pro",
|
|
5695
|
+
tier: "quality"
|
|
5696
|
+
},
|
|
5697
|
+
{
|
|
5698
|
+
model: "grok-image-2",
|
|
5699
|
+
tier: "balanced"
|
|
5700
|
+
},
|
|
5701
|
+
{
|
|
5702
|
+
model: "banana21",
|
|
5703
|
+
tier: "balanced"
|
|
5704
|
+
},
|
|
5705
|
+
{
|
|
5706
|
+
model: "mai-image-2.5",
|
|
5707
|
+
tier: "balanced"
|
|
5708
|
+
},
|
|
5709
|
+
{
|
|
5710
|
+
model: "muse-image",
|
|
5711
|
+
tier: "balanced"
|
|
5712
|
+
},
|
|
5713
|
+
{
|
|
5714
|
+
model: "grok-image-quality",
|
|
5715
|
+
tier: "balanced"
|
|
5716
|
+
},
|
|
5717
|
+
{
|
|
5718
|
+
model: "banana",
|
|
5719
|
+
tier: "balanced"
|
|
5720
|
+
},
|
|
5721
|
+
{
|
|
5722
|
+
model: "banana2",
|
|
5723
|
+
tier: "balanced"
|
|
5724
|
+
},
|
|
5725
|
+
{
|
|
5726
|
+
model: "krea3",
|
|
5727
|
+
tier: "balanced"
|
|
5728
|
+
},
|
|
5729
|
+
{
|
|
5730
|
+
model: "qwen3",
|
|
5731
|
+
tier: "balanced"
|
|
5732
|
+
},
|
|
5733
|
+
{
|
|
5734
|
+
model: "flux2-pro",
|
|
5735
|
+
tier: "balanced"
|
|
5736
|
+
},
|
|
5737
|
+
{
|
|
5738
|
+
model: "flux2-flex",
|
|
5739
|
+
tier: "balanced"
|
|
5740
|
+
},
|
|
5741
|
+
{
|
|
5742
|
+
model: "grok-image",
|
|
5743
|
+
tier: "fast"
|
|
5744
|
+
},
|
|
5745
|
+
{
|
|
5746
|
+
model: "banana2-lite",
|
|
5747
|
+
tier: "fast"
|
|
5748
|
+
},
|
|
5749
|
+
{
|
|
5750
|
+
model: "seedream5-lite",
|
|
5751
|
+
tier: "fast"
|
|
5752
|
+
}
|
|
5753
|
+
], "image-edit-single-image");
|
|
5317
5754
|
/** Every Task with a Model. A Task is absent here until it has at least one Model. */
|
|
5318
5755
|
const TASKS = {
|
|
5319
5756
|
animate: {
|
|
@@ -5485,106 +5922,7 @@ const TASKS = {
|
|
|
5485
5922
|
},
|
|
5486
5923
|
generate: {
|
|
5487
5924
|
basis: "Order follows Arena text-to-image (https://arena.ai/leaderboard/image/text-to-image), read 2026-10-29 (MOT-72). Sunburst and Flare lead quality at the benchmarked max setting. Balanced starts with Nano Banana 2.1 on preliminary Arena evidence, then Grok Image 2 and Muse Image. Models not yet on the board follow the ranked ones in their Tier, in their previous order. The Artificial Analysis v2 snapshot of 2026-10-07 stays as recorded benchmark data. Fast keeps Nano Banana 2 Lite, budget Grok and Seedream 5 Lite, and FLUX.2 Turbo. Design, transparency and tuned Look routes are capability exceptions. Generation with References uses the separate editing ranking. Older routes are explicit/pin/Look-only.",
|
|
5488
|
-
models:
|
|
5489
|
-
{
|
|
5490
|
-
model: "sunburst",
|
|
5491
|
-
tier: "quality"
|
|
5492
|
-
},
|
|
5493
|
-
{
|
|
5494
|
-
model: "flare",
|
|
5495
|
-
tier: "quality"
|
|
5496
|
-
},
|
|
5497
|
-
{
|
|
5498
|
-
model: "gpt2",
|
|
5499
|
-
tier: "quality"
|
|
5500
|
-
},
|
|
5501
|
-
{
|
|
5502
|
-
model: "seedream5",
|
|
5503
|
-
tier: "quality"
|
|
5504
|
-
},
|
|
5505
|
-
{
|
|
5506
|
-
model: "gpt",
|
|
5507
|
-
tier: "quality"
|
|
5508
|
-
},
|
|
5509
|
-
{
|
|
5510
|
-
model: "mai-image-2.5-pro",
|
|
5511
|
-
tier: "quality"
|
|
5512
|
-
},
|
|
5513
|
-
{
|
|
5514
|
-
model: "banana21",
|
|
5515
|
-
tier: "balanced"
|
|
5516
|
-
},
|
|
5517
|
-
{
|
|
5518
|
-
model: "grok-image-2",
|
|
5519
|
-
tier: "balanced"
|
|
5520
|
-
},
|
|
5521
|
-
{
|
|
5522
|
-
model: "muse-image",
|
|
5523
|
-
tier: "balanced"
|
|
5524
|
-
},
|
|
5525
|
-
{
|
|
5526
|
-
model: "banana2",
|
|
5527
|
-
tier: "balanced"
|
|
5528
|
-
},
|
|
5529
|
-
{
|
|
5530
|
-
model: "qwen3",
|
|
5531
|
-
tier: "balanced"
|
|
5532
|
-
},
|
|
5533
|
-
{
|
|
5534
|
-
model: "mai-image-2.5",
|
|
5535
|
-
tier: "balanced"
|
|
5536
|
-
},
|
|
5537
|
-
{
|
|
5538
|
-
model: "banana",
|
|
5539
|
-
tier: "balanced"
|
|
5540
|
-
},
|
|
5541
|
-
{
|
|
5542
|
-
model: "ideogram4",
|
|
5543
|
-
tier: "balanced"
|
|
5544
|
-
},
|
|
5545
|
-
{
|
|
5546
|
-
model: "flux2-flex",
|
|
5547
|
-
tier: "balanced"
|
|
5548
|
-
},
|
|
5549
|
-
{
|
|
5550
|
-
model: "recraft41",
|
|
5551
|
-
tier: "balanced"
|
|
5552
|
-
},
|
|
5553
|
-
{
|
|
5554
|
-
model: "flux2-pro",
|
|
5555
|
-
tier: "balanced"
|
|
5556
|
-
},
|
|
5557
|
-
{
|
|
5558
|
-
model: "krea3",
|
|
5559
|
-
tier: "balanced"
|
|
5560
|
-
},
|
|
5561
|
-
{
|
|
5562
|
-
model: "grok-image-quality",
|
|
5563
|
-
tier: "balanced"
|
|
5564
|
-
},
|
|
5565
|
-
{
|
|
5566
|
-
model: "ideogram3-transparent",
|
|
5567
|
-
requires: ["transparency"],
|
|
5568
|
-
supports: ["transparency"],
|
|
5569
|
-
tier: "balanced"
|
|
5570
|
-
},
|
|
5571
|
-
{
|
|
5572
|
-
model: "banana2-lite",
|
|
5573
|
-
tier: "fast"
|
|
5574
|
-
},
|
|
5575
|
-
{
|
|
5576
|
-
model: "grok-image",
|
|
5577
|
-
tier: "fast"
|
|
5578
|
-
},
|
|
5579
|
-
{
|
|
5580
|
-
model: "seedream5-lite",
|
|
5581
|
-
tier: "fast"
|
|
5582
|
-
},
|
|
5583
|
-
{
|
|
5584
|
-
model: "flux2-turbo",
|
|
5585
|
-
tier: "fast"
|
|
5586
|
-
}
|
|
5587
|
-
],
|
|
5925
|
+
models: GENERATION_MODELS_RANKED,
|
|
5588
5926
|
notFor: "Variations of an image you already have (vary), or a consistent set of images (series run).",
|
|
5589
5927
|
rankedAt: IMAGE_RANKED_AT,
|
|
5590
5928
|
rankedFrom: "hand",
|
|
@@ -6072,88 +6410,7 @@ const TASKS = {
|
|
|
6072
6410
|
},
|
|
6073
6411
|
vary: {
|
|
6074
6412
|
basis: "Order follows Arena image edit, single image (https://arena.ai/leaderboard/image/image-edit/single-image), read 2026-10-29 (MOT-72). Editing order differs from generation: Grok Image 2 leads balanced, ahead of Nano Banana 2.1, and Seedream 5 Pro outranks GPT Image 1.5. Arena scores Krea 3 only at its Ultra setting, which costs $0.27 to $0.35 an edit and ignores the requested size, so krea3, which runs fal's default setting, is ranked as unscored. Models not yet on the board follow the ranked ones in their Tier, in their previous order. The Artificial Analysis v2 snapshot of 2026-10-07 stays as recorded benchmark data. Sunburst and Flare use max quality. vary reuses a previously chosen Model when explicitly supplied, including retained legacy routes.",
|
|
6075
|
-
models:
|
|
6076
|
-
{
|
|
6077
|
-
model: "sunburst",
|
|
6078
|
-
tier: "quality"
|
|
6079
|
-
},
|
|
6080
|
-
{
|
|
6081
|
-
model: "flare",
|
|
6082
|
-
tier: "quality"
|
|
6083
|
-
},
|
|
6084
|
-
{
|
|
6085
|
-
model: "gpt2",
|
|
6086
|
-
tier: "quality"
|
|
6087
|
-
},
|
|
6088
|
-
{
|
|
6089
|
-
model: "seedream5",
|
|
6090
|
-
tier: "quality"
|
|
6091
|
-
},
|
|
6092
|
-
{
|
|
6093
|
-
model: "gpt",
|
|
6094
|
-
tier: "quality"
|
|
6095
|
-
},
|
|
6096
|
-
{
|
|
6097
|
-
model: "mai-image-2.5-pro",
|
|
6098
|
-
tier: "quality"
|
|
6099
|
-
},
|
|
6100
|
-
{
|
|
6101
|
-
model: "grok-image-2",
|
|
6102
|
-
tier: "balanced"
|
|
6103
|
-
},
|
|
6104
|
-
{
|
|
6105
|
-
model: "banana21",
|
|
6106
|
-
tier: "balanced"
|
|
6107
|
-
},
|
|
6108
|
-
{
|
|
6109
|
-
model: "mai-image-2.5",
|
|
6110
|
-
tier: "balanced"
|
|
6111
|
-
},
|
|
6112
|
-
{
|
|
6113
|
-
model: "muse-image",
|
|
6114
|
-
tier: "balanced"
|
|
6115
|
-
},
|
|
6116
|
-
{
|
|
6117
|
-
model: "grok-image-quality",
|
|
6118
|
-
tier: "balanced"
|
|
6119
|
-
},
|
|
6120
|
-
{
|
|
6121
|
-
model: "banana",
|
|
6122
|
-
tier: "balanced"
|
|
6123
|
-
},
|
|
6124
|
-
{
|
|
6125
|
-
model: "banana2",
|
|
6126
|
-
tier: "balanced"
|
|
6127
|
-
},
|
|
6128
|
-
{
|
|
6129
|
-
model: "krea3",
|
|
6130
|
-
tier: "balanced"
|
|
6131
|
-
},
|
|
6132
|
-
{
|
|
6133
|
-
model: "qwen3",
|
|
6134
|
-
tier: "balanced"
|
|
6135
|
-
},
|
|
6136
|
-
{
|
|
6137
|
-
model: "flux2-pro",
|
|
6138
|
-
tier: "balanced"
|
|
6139
|
-
},
|
|
6140
|
-
{
|
|
6141
|
-
model: "flux2-flex",
|
|
6142
|
-
tier: "balanced"
|
|
6143
|
-
},
|
|
6144
|
-
{
|
|
6145
|
-
model: "grok-image",
|
|
6146
|
-
tier: "fast"
|
|
6147
|
-
},
|
|
6148
|
-
{
|
|
6149
|
-
model: "banana2-lite",
|
|
6150
|
-
tier: "fast"
|
|
6151
|
-
},
|
|
6152
|
-
{
|
|
6153
|
-
model: "seedream5-lite",
|
|
6154
|
-
tier: "fast"
|
|
6155
|
-
}
|
|
6156
|
-
],
|
|
6413
|
+
models: EDITING_MODELS_RANKED,
|
|
6157
6414
|
notFor: "A specific change to an image described in words (generate with a reference), or a set of different scenes in one style (series run).",
|
|
6158
6415
|
rankedAt: IMAGE_RANKED_AT,
|
|
6159
6416
|
rankedFrom: "hand",
|
|
@@ -8352,6 +8609,7 @@ function generationProfile(config) {
|
|
|
8352
8609
|
else if (config.transparencyRoute !== void 0) keyed.set("transparency", config.transparencyRoute.apiKeyEnv);
|
|
8353
8610
|
if (config.supportsEdit) capabilities.add("references");
|
|
8354
8611
|
if (config.supportsMaskImage === true) capabilities.add("mask");
|
|
8612
|
+
if (config.supportsMarkupImage === true) capabilities.add("markup");
|
|
8355
8613
|
if ((config.sizeMode ?? "aspect_ratio") !== "none") capabilities.add("aspect");
|
|
8356
8614
|
if (config.supportsResolution) capabilities.add("resolution");
|
|
8357
8615
|
if (config.supportsNumImages) capabilities.add("count");
|
|
@@ -8364,6 +8622,10 @@ function generationProfile(config) {
|
|
|
8364
8622
|
keyedCapabilities: keyed,
|
|
8365
8623
|
keys: [FAL_KEY],
|
|
8366
8624
|
maxReferences: config.supportsEdit ? config.maxReferenceImages ?? 1 : 0,
|
|
8625
|
+
outputFormats: {
|
|
8626
|
+
edit: outputFormatsOf(config, true),
|
|
8627
|
+
text: outputFormatsOf(config, false)
|
|
8628
|
+
},
|
|
8367
8629
|
resolutions: config.supportedResolutions
|
|
8368
8630
|
};
|
|
8369
8631
|
}
|
|
@@ -8414,12 +8676,13 @@ function requirements(request) {
|
|
|
8414
8676
|
if (request.transparent === true) needed.push("transparency");
|
|
8415
8677
|
if ((request.references ?? 0) > 0) needed.push("references");
|
|
8416
8678
|
if (request.mask === true) needed.push("mask");
|
|
8679
|
+
if (request.markup === true) needed.push("markup");
|
|
8417
8680
|
if (request.aspect !== void 0) needed.push("aspect");
|
|
8418
8681
|
if (request.resolution !== void 0) needed.push("resolution");
|
|
8419
8682
|
if ((request.count ?? 1) > 1) needed.push("count");
|
|
8420
8683
|
if (request.seed === true) needed.push("seed");
|
|
8421
8684
|
if (request.negativePrompt === true) needed.push("negativePrompt");
|
|
8422
|
-
if (request.outputFormat
|
|
8685
|
+
if (request.outputFormat !== void 0) needed.push("outputFormat");
|
|
8423
8686
|
if (request.rig === true) needed.push("rig");
|
|
8424
8687
|
return needed;
|
|
8425
8688
|
}
|
|
@@ -8428,6 +8691,7 @@ function withinLimits(profile, capability, request) {
|
|
|
8428
8691
|
if (capability === "references") return (request.references ?? 0) <= profile.maxReferences;
|
|
8429
8692
|
if (capability === "aspect") return accepts(profile.aspects, request.aspect);
|
|
8430
8693
|
if (capability === "resolution") return accepts(profile.resolutions, request.resolution);
|
|
8694
|
+
if (capability === "outputFormat") return accepts((request.references ?? 0) > 0 || request.source !== void 0 ? profile.outputFormats?.edit : profile.outputFormats?.text, request.outputFormat);
|
|
8431
8695
|
return true;
|
|
8432
8696
|
}
|
|
8433
8697
|
function accepts(allowed, value) {
|
|
@@ -8482,7 +8746,11 @@ const TIER_ORDER = {
|
|
|
8482
8746
|
"fast"
|
|
8483
8747
|
]
|
|
8484
8748
|
};
|
|
8485
|
-
function describeBlock(blockedBy, missingKey, missingInput) {
|
|
8749
|
+
function describeBlock(blockedBy, request, missingKey, missingInput) {
|
|
8750
|
+
if (blockedBy === "outputFormat" && request.outputFormat !== void 0) {
|
|
8751
|
+
const editing = (request.references ?? 0) > 0 || request.source !== void 0;
|
|
8752
|
+
return `cannot return ${request.outputFormat}${editing ? " from an edit" : ""}`;
|
|
8753
|
+
}
|
|
8486
8754
|
if (missingInput === true) return `needs a ${blockedBy} input`;
|
|
8487
8755
|
if (blockedBy === "key") return `needs ${missingKey ?? "a provider key"}`;
|
|
8488
8756
|
if (blockedBy === "unknown-model") return "is not a Model for this Task";
|
|
@@ -8551,7 +8819,7 @@ function resolveFixed(task, fixed, request, needed, keys) {
|
|
|
8551
8819
|
if (result.ok) return resolved(task, model, tier, chosenBy);
|
|
8552
8820
|
const unblockers = chosenBy === "model" ? [] : ["model"];
|
|
8553
8821
|
unblockers.push(unblockerFor(result));
|
|
8554
|
-
return unresolved(task, result.blockedBy, unblockers, `${model} (${chosenBy}) ${describeBlock(result.blockedBy, result.missingKey, result.missingInput)}.`, result.missingKey);
|
|
8822
|
+
return unresolved(task, result.blockedBy, unblockers, `${model} (${chosenBy}) ${describeBlock(result.blockedBy, request, result.missingKey, result.missingInput)}.`, result.missingKey);
|
|
8555
8823
|
}
|
|
8556
8824
|
/** Old pins and tuned Looks remain usable without recommending predecessors. */
|
|
8557
8825
|
function legacyEntry(task, model, mode, tier) {
|
|
@@ -8785,10 +9053,10 @@ const OPENAI_API_KEY_ENV = "OPENAI_API_KEY";
|
|
|
8785
9053
|
* Sanity anchor: the Phase 0 benchmark measured gpt-image direct at $0.042
|
|
8786
9054
|
* (vs $0.133 via fal — full data and methodology on Linear MOT-23).
|
|
8787
9055
|
*
|
|
8788
|
-
* GPT Image 2.
|
|
9056
|
+
* GPT Image 2.6 is token-priced, with no published per-image estimate. Leave
|
|
8789
9057
|
* these models absent so cost remains unknown unless supplied by the provider.
|
|
8790
|
-
* https://developers.openai.com/api/docs/models/gpt-image-2.
|
|
8791
|
-
* https://developers.openai.com/api/docs/models/gpt-image-2.
|
|
9058
|
+
* https://developers.openai.com/api/docs/models/gpt-image-2.6-flare
|
|
9059
|
+
* https://developers.openai.com/api/docs/models/gpt-image-2.6-sunburst
|
|
8792
9060
|
*/
|
|
8793
9061
|
const OPENAI_IMAGE_PRICE_USD = { "gpt-image-1": .042 };
|
|
8794
9062
|
/**
|
|
@@ -10040,11 +10308,12 @@ function taskRequest(task, input) {
|
|
|
10040
10308
|
aspect: input.aspect,
|
|
10041
10309
|
count: input.count,
|
|
10042
10310
|
look: task === "generate" || task === "vary" ? input.look : void 0,
|
|
10311
|
+
markup: input.markup !== void 0,
|
|
10043
10312
|
mask: input.mask !== void 0,
|
|
10044
10313
|
mode: input.mode,
|
|
10045
10314
|
model: input.model,
|
|
10046
10315
|
negativePrompt: input.negativePrompt !== void 0,
|
|
10047
|
-
outputFormat: input.outputFormat
|
|
10316
|
+
outputFormat: input.outputFormat,
|
|
10048
10317
|
references,
|
|
10049
10318
|
resolution: (task === "generate" || task === "vary") && input.resolution === "2K" ? void 0 : input.resolution,
|
|
10050
10319
|
rig: input.rig,
|
|
@@ -10155,6 +10424,7 @@ const GENERATION_FIELDS = /* @__PURE__ */ new Set([
|
|
|
10155
10424
|
"aspect",
|
|
10156
10425
|
"count",
|
|
10157
10426
|
"look",
|
|
10427
|
+
"markup",
|
|
10158
10428
|
"mask",
|
|
10159
10429
|
"mood",
|
|
10160
10430
|
"negativePrompt",
|
|
@@ -10165,11 +10435,23 @@ const GENERATION_FIELDS = /* @__PURE__ */ new Set([
|
|
|
10165
10435
|
"seed",
|
|
10166
10436
|
"transparent"
|
|
10167
10437
|
]);
|
|
10438
|
+
const IMAGE_QUALITIES = [
|
|
10439
|
+
"auto",
|
|
10440
|
+
"low",
|
|
10441
|
+
"medium",
|
|
10442
|
+
"high",
|
|
10443
|
+
"xhigh",
|
|
10444
|
+
"max"
|
|
10445
|
+
];
|
|
10446
|
+
function isImageQuality(value) {
|
|
10447
|
+
return IMAGE_QUALITIES.some((level) => level === value);
|
|
10448
|
+
}
|
|
10168
10449
|
/** The TaskInput field behind each option `buildGenerateBody` can refuse. */
|
|
10169
10450
|
const FIELD_FOR_OPTION = {
|
|
10170
10451
|
aspect: "aspect",
|
|
10171
10452
|
background: "transparent",
|
|
10172
10453
|
"image editing": "references",
|
|
10454
|
+
markupImageUrl: "markup",
|
|
10173
10455
|
maskImageUrl: "mask",
|
|
10174
10456
|
negativePrompt: "negativePrompt",
|
|
10175
10457
|
numImages: "count",
|
|
@@ -10209,23 +10491,35 @@ function generationPlan(task, model, config, input) {
|
|
|
10209
10491
|
task
|
|
10210
10492
|
}));
|
|
10211
10493
|
const editImageUrls = [...task === "vary" && input.image !== void 0 ? [input.image] : [], ...input.references ?? []];
|
|
10494
|
+
if (input.markup !== void 0 && editImageUrls.length === 0) return (0, neverthrow.err)(invalidOption("Markup applies to the image being edited; pass references.", {
|
|
10495
|
+
field: "markup",
|
|
10496
|
+
requires: "references"
|
|
10497
|
+
}));
|
|
10212
10498
|
if (input.mask !== void 0 && editImageUrls.length === 0) return (0, neverthrow.err)(invalidOption("A mask applies to a reference image; pass references.", {
|
|
10213
10499
|
field: "mask",
|
|
10214
10500
|
requires: "references"
|
|
10215
10501
|
}));
|
|
10216
10502
|
if (input.transparent === true && config.supportsBackground !== true && config.transparencyRoute !== void 0) return openAiPlan(task, model, config.transparencyRoute, input, editImageUrls);
|
|
10217
|
-
if (input.outputFormat !== void 0 && config.
|
|
10503
|
+
if (input.outputFormat !== void 0 && !outputFormatsOf(config, editImageUrls.length > 0).includes(input.outputFormat)) return (0, neverthrow.err)(cannotCarry("outputFormat", model, task));
|
|
10504
|
+
const { quality, ...params } = input.params ?? {};
|
|
10505
|
+
const liftQuality = config.supportsQuality === true && quality !== void 0;
|
|
10506
|
+
if (liftQuality && !isImageQuality(quality)) return (0, neverthrow.err)(invalidOption(`params.quality must be one of ${IMAGE_QUALITIES.join(", ")}.`, {
|
|
10507
|
+
field: "params",
|
|
10508
|
+
key: "quality"
|
|
10509
|
+
}));
|
|
10218
10510
|
const options = {
|
|
10219
10511
|
aspect: input.aspect,
|
|
10220
10512
|
background: input.transparent === true ? "transparent" : void 0,
|
|
10221
10513
|
creative: creativeOf(input),
|
|
10222
10514
|
editImageUrls: editImageUrls.length > 0 ? editImageUrls : void 0,
|
|
10515
|
+
markupImageUrl: input.markup,
|
|
10223
10516
|
maskImageUrl: input.mask,
|
|
10224
10517
|
model,
|
|
10225
10518
|
negativePrompt: input.negativePrompt,
|
|
10226
10519
|
numImages: input.count,
|
|
10227
10520
|
outputFormat: input.outputFormat,
|
|
10228
10521
|
prompt: task === "vary" && (input.prompt ?? "") === "" ? VARY_PROMPT : input.prompt ?? "",
|
|
10522
|
+
quality: liftQuality && isImageQuality(quality) ? quality : void 0,
|
|
10229
10523
|
resolution: input.resolution,
|
|
10230
10524
|
seed: input.seed
|
|
10231
10525
|
};
|
|
@@ -10235,7 +10529,7 @@ function generationPlan(task, model, config, input) {
|
|
|
10235
10529
|
} catch (error) {
|
|
10236
10530
|
return (0, neverthrow.err)(generationError(error, model, task));
|
|
10237
10531
|
}
|
|
10238
|
-
const merged = mergeParams(built.body, input.params, ownedGenerationKeys(input));
|
|
10532
|
+
const merged = mergeParams(built.body, liftQuality ? params : input.params, ownedGenerationKeys(input));
|
|
10239
10533
|
if (merged.isErr()) return (0, neverthrow.err)(merged.error);
|
|
10240
10534
|
const badSize = checkParamsImageSize(config, model, task, input.params);
|
|
10241
10535
|
if (badSize !== void 0) return (0, neverthrow.err)(badSize);
|
|
@@ -10853,7 +11147,7 @@ function createMotif(config = {}) {
|
|
|
10853
11147
|
headers
|
|
10854
11148
|
};
|
|
10855
11149
|
const executor = falRequestExecutor(client.value);
|
|
10856
|
-
return (chosen.queued ? await runRequestQueued(executor, prepared, input.onProgress, input.onSubmitted) : await runRequest(executor, prepared)).map((value) => falOutput(chosen, value));
|
|
11150
|
+
return (chosen.queued || input.onSubmitted !== void 0 ? await runRequestQueued(executor, prepared, input.onProgress, input.onSubmitted) : await runRequest(executor, prepared)).map((value) => falOutput(chosen, value));
|
|
10857
11151
|
}
|
|
10858
11152
|
async function resume(task, input, requestId) {
|
|
10859
11153
|
const planned = plan(task, input);
|