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