@howells/motif-sdk 1.3.1 → 2.0.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 +18 -1
- package/dist/image.d.ts +4 -1
- package/dist/image.js +71 -10
- package/dist/index.cjs +71 -1
- package/dist/index.d.cts +11 -8
- package/dist/index.d.ts +11 -8
- package/dist/index.js +71 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -171,12 +171,29 @@ Four providers are implemented, each reading its own API key from the environmen
|
|
|
171
171
|
| Provider | Env var | Notes |
|
|
172
172
|
| --- | --- | --- |
|
|
173
173
|
| `google` | `GOOGLE_GENERATIVE_AI_API_KEY` | Default provider; Gemini gen + edit |
|
|
174
|
-
| `openai` | `OPENAI_API_KEY` |
|
|
174
|
+
| `openai` | `OPENAI_API_KEY` | GPT Image 2.5 Flare (fast/balanced), Sunburst (quality/hero) |
|
|
175
175
|
| `replicate` | `REPLICATE_API_TOKEN` | flux-1.1-pro-ultra |
|
|
176
176
|
| `fal` | `FAL_KEY` | fal-hosted adapter |
|
|
177
177
|
|
|
178
178
|
`generate()` and `edit()` accept `tier` (`"fast" | "balanced" | "quality" | "hero"`) to resolve a model per provider, or an explicit `model` id. Every result carries a normalized per-call `cost: { usd, source }`.
|
|
179
179
|
|
|
180
|
+
For OpenAI, `fast` and `balanced` (the default tier) select `gpt-image-2.5-flare`; `quality` and `hero` select `gpt-image-2.5-sunburst`. This updates the previous OpenAI tier default of `gpt-image-1`; pass that explicit model to retain it. Both new models support generation and multi-image editing:
|
|
181
|
+
|
|
182
|
+
```ts
|
|
183
|
+
const image = createMotifImage({ defaultProvider: "openai" });
|
|
184
|
+
const generated = await image.generate({
|
|
185
|
+
prompt: "A ceramic vase in soft window light",
|
|
186
|
+
model: "gpt-image-2.5-flare",
|
|
187
|
+
});
|
|
188
|
+
const refined = await image.edit({
|
|
189
|
+
images: [referenceBytes],
|
|
190
|
+
instruction: "Change only the vase glaze to deep green",
|
|
191
|
+
model: "gpt-image-2.5-sunburst",
|
|
192
|
+
});
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
These models use token-based billing. Motif has no static per-image estimate for them, so `cost` is `{ usd: 0, source: "unknown" }` unless the provider supplies a cost; this does not mean generation is free. See the official [Flare](https://developers.openai.com/api/docs/models/gpt-image-2.5-flare) and [Sunburst](https://developers.openai.com/api/docs/models/gpt-image-2.5-sunburst) model pages. The installed OpenAI adapter accepts `low`, `medium`, `high`, and `auto` quality; the new `xhigh` and `max` settings require a future adapter update. The fal-backed CLI and `FalClient` expose these models as `flare` and `sunburst`. Fal supports `xhigh` and `max`, up to 16 edit references, masks and transparent backgrounds. Fal generation estimates are `null` (metered), including `estimateCost()` and queued jobs.
|
|
196
|
+
|
|
180
197
|
### Best-of-N with an injectable judge
|
|
181
198
|
|
|
182
199
|
`bestOfN()` generates `n` candidates in parallel and picks a winner. It reuses the same options as `generate()` (text→image) or `edit()` (pass `images` for the edit path), plus `n` and an optional `judge`. When a `seed` is given each candidate uses `seed + index`, so the N vary. The judge is a caller-provided function — the layer takes no text-client dependency, so it pairs well with `@howells/ai`'s vision client but does not require it. Omit the judge and candidate 0 wins.
|
package/dist/image.d.ts
CHANGED
|
@@ -304,7 +304,10 @@ export declare const GOOGLE_TIER_MODELS: Readonly<Record<ImageTier, string>>;
|
|
|
304
304
|
export declare const GOOGLE_API_KEY_ENV = "GOOGLE_GENERATIVE_AI_API_KEY";
|
|
305
305
|
//#endregion
|
|
306
306
|
//#region src/image/openai.d.ts
|
|
307
|
-
/**
|
|
307
|
+
/**
|
|
308
|
+
* Flare favors speed for everyday generation; Sunburst favors editing precision.
|
|
309
|
+
* Explicit model ids still override tiers, including older GPT Image models.
|
|
310
|
+
*/
|
|
308
311
|
export declare const OPENAI_TIER_MODELS: Readonly<Record<ImageTier, string>>;
|
|
309
312
|
/** Env var read for the OpenAI API key when `apiKey` is not supplied in config. */
|
|
310
313
|
export declare const OPENAI_API_KEY_ENV = "OPENAI_API_KEY";
|
package/dist/image.js
CHANGED
|
@@ -10,6 +10,64 @@ const AA_IMAGE_SOURCES = ["https://artificialanalysis.ai/image/leaderboard/text-
|
|
|
10
10
|
const FAL_PRICING_CHECKED_AT = "2026-05-12";
|
|
11
11
|
const FAL_PRICING_CHECKED_JUL_2026 = "2026-07-11";
|
|
12
12
|
const MODELS = {
|
|
13
|
+
flare: {
|
|
14
|
+
editEndpoint: "openai/gpt-image-2.5/flare/edit",
|
|
15
|
+
endpoint: "openai/gpt-image-2.5/flare/text-to-image",
|
|
16
|
+
maxReferenceImages: 16,
|
|
17
|
+
name: "GPT Image 2.5 Flare",
|
|
18
|
+
pricing: "Token-based: text $5/M input, $1.25/M cached, $10/M output; image $8/M input, $2/M cached, $30/M output",
|
|
19
|
+
pricePerImageUsd: null,
|
|
20
|
+
sizeMode: "image_size_enum",
|
|
21
|
+
supportsAspect: true,
|
|
22
|
+
supportsBackground: true,
|
|
23
|
+
supportsEdit: true,
|
|
24
|
+
supportsMaskImage: true,
|
|
25
|
+
maskImageField: "mask_url",
|
|
26
|
+
supportsNumImages: true,
|
|
27
|
+
supportsOutputFormat: true,
|
|
28
|
+
supportsQuality: true,
|
|
29
|
+
supportedQualities: [
|
|
30
|
+
"auto",
|
|
31
|
+
"low",
|
|
32
|
+
"medium",
|
|
33
|
+
"high",
|
|
34
|
+
"xhigh",
|
|
35
|
+
"max"
|
|
36
|
+
],
|
|
37
|
+
supportsResolution: false,
|
|
38
|
+
supportsSyncMode: true,
|
|
39
|
+
type: "generation",
|
|
40
|
+
useQueue: true
|
|
41
|
+
},
|
|
42
|
+
sunburst: {
|
|
43
|
+
editEndpoint: "openai/gpt-image-2.5/sunburst/edit",
|
|
44
|
+
endpoint: "openai/gpt-image-2.5/sunburst/text-to-image",
|
|
45
|
+
maxReferenceImages: 16,
|
|
46
|
+
name: "GPT Image 2.5 Sunburst",
|
|
47
|
+
pricing: "Token-based: text $5/M input, $1.25/M cached, $10/M output; image $8/M input, $2/M cached, $30/M output",
|
|
48
|
+
pricePerImageUsd: null,
|
|
49
|
+
sizeMode: "image_size_enum",
|
|
50
|
+
supportsAspect: true,
|
|
51
|
+
supportsBackground: true,
|
|
52
|
+
supportsEdit: true,
|
|
53
|
+
supportsMaskImage: true,
|
|
54
|
+
maskImageField: "mask_url",
|
|
55
|
+
supportsNumImages: true,
|
|
56
|
+
supportsOutputFormat: true,
|
|
57
|
+
supportsQuality: true,
|
|
58
|
+
supportedQualities: [
|
|
59
|
+
"auto",
|
|
60
|
+
"low",
|
|
61
|
+
"medium",
|
|
62
|
+
"high",
|
|
63
|
+
"xhigh",
|
|
64
|
+
"max"
|
|
65
|
+
],
|
|
66
|
+
supportsResolution: false,
|
|
67
|
+
supportsSyncMode: true,
|
|
68
|
+
type: "generation",
|
|
69
|
+
useQueue: true
|
|
70
|
+
},
|
|
13
71
|
gpt2: {
|
|
14
72
|
benchmark: {
|
|
15
73
|
artificialAnalysis: {
|
|
@@ -1104,6 +1162,8 @@ const MODELS = {
|
|
|
1104
1162
|
}
|
|
1105
1163
|
};
|
|
1106
1164
|
[
|
|
1165
|
+
"flare",
|
|
1166
|
+
"sunburst",
|
|
1107
1167
|
"gpt2",
|
|
1108
1168
|
"gpt",
|
|
1109
1169
|
"banana2",
|
|
@@ -2573,18 +2633,14 @@ const googleAdapter = {
|
|
|
2573
2633
|
* invokes `model.doGenerate`.
|
|
2574
2634
|
*/
|
|
2575
2635
|
/**
|
|
2576
|
-
*
|
|
2577
|
-
*
|
|
2578
|
-
* (low/medium/high/auto) passed via `providerOptions.openai` (Phase 1c); until
|
|
2579
|
-
* then the tier only selects this one model.
|
|
2636
|
+
* Flare favors speed for everyday generation; Sunburst favors editing precision.
|
|
2637
|
+
* Explicit model ids still override tiers, including older GPT Image models.
|
|
2580
2638
|
*/
|
|
2581
|
-
const OPENAI_MODEL = "gpt-image-1";
|
|
2582
|
-
/** Tier → OpenAI image model id (all tiers → the single gpt-image model). */
|
|
2583
2639
|
const OPENAI_TIER_MODELS = {
|
|
2584
|
-
fast:
|
|
2585
|
-
balanced:
|
|
2586
|
-
quality:
|
|
2587
|
-
hero:
|
|
2640
|
+
fast: "gpt-image-2.5-flare",
|
|
2641
|
+
balanced: "gpt-image-2.5-flare",
|
|
2642
|
+
quality: "gpt-image-2.5-sunburst",
|
|
2643
|
+
hero: "gpt-image-2.5-sunburst"
|
|
2588
2644
|
};
|
|
2589
2645
|
/** Env var read for the OpenAI API key when `apiKey` is not supplied in config. */
|
|
2590
2646
|
const OPENAI_API_KEY_ENV = "OPENAI_API_KEY";
|
|
@@ -2597,6 +2653,11 @@ const OPENAI_API_KEY_ENV = "OPENAI_API_KEY";
|
|
|
2597
2653
|
* Source: https://platform.openai.com/docs/pricing (image generation)
|
|
2598
2654
|
* Sanity anchor: the Phase 0 benchmark measured gpt-image direct at $0.042
|
|
2599
2655
|
* (vs $0.133 via fal — see docs/design/provider-agnostic-image-layer.md §10).
|
|
2656
|
+
*
|
|
2657
|
+
* GPT Image 2.5 is token-priced, with no published per-image estimate. Leave
|
|
2658
|
+
* these models absent so cost remains unknown unless supplied by the provider.
|
|
2659
|
+
* https://developers.openai.com/api/docs/models/gpt-image-2.5-flare
|
|
2660
|
+
* https://developers.openai.com/api/docs/models/gpt-image-2.5-sunburst
|
|
2600
2661
|
*/
|
|
2601
2662
|
const OPENAI_IMAGE_PRICE_USD = { "gpt-image-1": .042 };
|
|
2602
2663
|
/**
|
package/dist/index.cjs
CHANGED
|
@@ -112,6 +112,64 @@ const AA_IMAGE_SOURCES = ["https://artificialanalysis.ai/image/leaderboard/text-
|
|
|
112
112
|
const FAL_PRICING_CHECKED_AT = "2026-05-12";
|
|
113
113
|
const FAL_PRICING_CHECKED_JUL_2026 = "2026-07-11";
|
|
114
114
|
const MODELS = {
|
|
115
|
+
flare: {
|
|
116
|
+
editEndpoint: "openai/gpt-image-2.5/flare/edit",
|
|
117
|
+
endpoint: "openai/gpt-image-2.5/flare/text-to-image",
|
|
118
|
+
maxReferenceImages: 16,
|
|
119
|
+
name: "GPT Image 2.5 Flare",
|
|
120
|
+
pricing: "Token-based: text $5/M input, $1.25/M cached, $10/M output; image $8/M input, $2/M cached, $30/M output",
|
|
121
|
+
pricePerImageUsd: null,
|
|
122
|
+
sizeMode: "image_size_enum",
|
|
123
|
+
supportsAspect: true,
|
|
124
|
+
supportsBackground: true,
|
|
125
|
+
supportsEdit: true,
|
|
126
|
+
supportsMaskImage: true,
|
|
127
|
+
maskImageField: "mask_url",
|
|
128
|
+
supportsNumImages: true,
|
|
129
|
+
supportsOutputFormat: true,
|
|
130
|
+
supportsQuality: true,
|
|
131
|
+
supportedQualities: [
|
|
132
|
+
"auto",
|
|
133
|
+
"low",
|
|
134
|
+
"medium",
|
|
135
|
+
"high",
|
|
136
|
+
"xhigh",
|
|
137
|
+
"max"
|
|
138
|
+
],
|
|
139
|
+
supportsResolution: false,
|
|
140
|
+
supportsSyncMode: true,
|
|
141
|
+
type: "generation",
|
|
142
|
+
useQueue: true
|
|
143
|
+
},
|
|
144
|
+
sunburst: {
|
|
145
|
+
editEndpoint: "openai/gpt-image-2.5/sunburst/edit",
|
|
146
|
+
endpoint: "openai/gpt-image-2.5/sunburst/text-to-image",
|
|
147
|
+
maxReferenceImages: 16,
|
|
148
|
+
name: "GPT Image 2.5 Sunburst",
|
|
149
|
+
pricing: "Token-based: text $5/M input, $1.25/M cached, $10/M output; image $8/M input, $2/M cached, $30/M output",
|
|
150
|
+
pricePerImageUsd: null,
|
|
151
|
+
sizeMode: "image_size_enum",
|
|
152
|
+
supportsAspect: true,
|
|
153
|
+
supportsBackground: true,
|
|
154
|
+
supportsEdit: true,
|
|
155
|
+
supportsMaskImage: true,
|
|
156
|
+
maskImageField: "mask_url",
|
|
157
|
+
supportsNumImages: true,
|
|
158
|
+
supportsOutputFormat: true,
|
|
159
|
+
supportsQuality: true,
|
|
160
|
+
supportedQualities: [
|
|
161
|
+
"auto",
|
|
162
|
+
"low",
|
|
163
|
+
"medium",
|
|
164
|
+
"high",
|
|
165
|
+
"xhigh",
|
|
166
|
+
"max"
|
|
167
|
+
],
|
|
168
|
+
supportsResolution: false,
|
|
169
|
+
supportsSyncMode: true,
|
|
170
|
+
type: "generation",
|
|
171
|
+
useQueue: true
|
|
172
|
+
},
|
|
115
173
|
gpt2: {
|
|
116
174
|
benchmark: {
|
|
117
175
|
artificialAnalysis: {
|
|
@@ -1206,6 +1264,8 @@ const MODELS = {
|
|
|
1206
1264
|
}
|
|
1207
1265
|
};
|
|
1208
1266
|
const GENERATION_MODELS = [
|
|
1267
|
+
"flare",
|
|
1268
|
+
"sunburst",
|
|
1209
1269
|
"gpt2",
|
|
1210
1270
|
"gpt",
|
|
1211
1271
|
"banana2",
|
|
@@ -1288,6 +1348,7 @@ const IDEOGRAM_STYLES = [
|
|
|
1288
1348
|
/** Estimate cost based on model and settings */
|
|
1289
1349
|
function estimateCost(model, resolution, numImages = 1) {
|
|
1290
1350
|
const configuredPrice = MODELS[model]?.pricePerImageUsd;
|
|
1351
|
+
if (configuredPrice === null) return null;
|
|
1291
1352
|
if (configuredPrice !== void 0) {
|
|
1292
1353
|
if ((model === "banana" || model === "gemini3") && resolution === "4K") return configuredPrice * 2 * numImages;
|
|
1293
1354
|
if (model === "seedream5" && (resolution === "2K" || resolution === "4K")) return configuredPrice * 2 * numImages;
|
|
@@ -1513,6 +1574,15 @@ function validateGenerateOptions(options, config, sizeMode, hasEditImages) {
|
|
|
1513
1574
|
if (options.seed !== void 0 && config.supportsSeed !== true) unsupported(config, "seed");
|
|
1514
1575
|
if (options.outputFormat !== void 0 && config.supportsOutputFormat !== true) unsupported(config, "outputFormat");
|
|
1515
1576
|
if (options.quality !== void 0 && config.supportsQuality !== true) unsupported(config, "quality");
|
|
1577
|
+
if (options.quality !== void 0) {
|
|
1578
|
+
const qualities = config.supportedQualities ?? [
|
|
1579
|
+
"auto",
|
|
1580
|
+
"low",
|
|
1581
|
+
"medium",
|
|
1582
|
+
"high"
|
|
1583
|
+
];
|
|
1584
|
+
if (!qualities.includes(options.quality)) throw new Error(`${config.name} quality must be one of ${qualities.join(", ")}`);
|
|
1585
|
+
}
|
|
1516
1586
|
if (options.negativePrompt !== void 0 && config.supportsNegativePrompt !== true) unsupported(config, "negativePrompt");
|
|
1517
1587
|
if (options.style !== void 0 && config.supportsStyle !== true) unsupported(config, "style");
|
|
1518
1588
|
if (options.syncMode !== void 0 && config.supportsSyncMode !== true) unsupported(config, "syncMode");
|
|
@@ -1631,7 +1701,7 @@ function buildGenerateBody(options) {
|
|
|
1631
1701
|
body.image_urls = editImages;
|
|
1632
1702
|
if (inputFidelity) body.input_fidelity = inputFidelity;
|
|
1633
1703
|
}
|
|
1634
|
-
if (maskImageUrl !== void 0 && maskImageUrl !== "" && config.supportsMaskImage === true) body.mask_image_url = maskImageUrl;
|
|
1704
|
+
if (maskImageUrl !== void 0 && maskImageUrl !== "" && config.supportsMaskImage === true) body[config.maskImageField ?? "mask_image_url"] = maskImageUrl;
|
|
1635
1705
|
return {
|
|
1636
1706
|
body,
|
|
1637
1707
|
endpoint
|
package/dist/index.d.cts
CHANGED
|
@@ -145,7 +145,7 @@ type Resolution = "0.5K" | "1K" | "2K" | "4K";
|
|
|
145
145
|
type ModelType = "generation" | "utility" | "video";
|
|
146
146
|
type ImageOutputFormat = "jpeg" | "png" | "webp";
|
|
147
147
|
type BackgroundMode = "auto" | "transparent" | "opaque";
|
|
148
|
-
type ImageQuality = "auto" | "low" | "medium" | "high";
|
|
148
|
+
type ImageQuality = "auto" | "low" | "medium" | "high" | "xhigh" | "max";
|
|
149
149
|
type ThinkingLevel = "minimal" | "high";
|
|
150
150
|
type GptImageSize = "auto" | "1024x1024" | "1536x1024" | "1024x1536";
|
|
151
151
|
type FalImageSizePreset = "auto" | "square_hd" | "square" | "portrait_4_3" | "portrait_16_9" | "landscape_4_3" | "landscape_16_9";
|
|
@@ -201,7 +201,8 @@ interface ModelConfig {
|
|
|
201
201
|
maxReferenceImages?: number;
|
|
202
202
|
name: string;
|
|
203
203
|
pricing: string;
|
|
204
|
-
|
|
204
|
+
/** Null means token-metered with no per-image estimate. */
|
|
205
|
+
pricePerImageUsd?: number | null;
|
|
205
206
|
/** How this model accepts dimensions (default: "aspect_ratio") */
|
|
206
207
|
sizeMode?: SizeMode;
|
|
207
208
|
supportsAspect: boolean;
|
|
@@ -215,11 +216,13 @@ interface ModelConfig {
|
|
|
215
216
|
supportsInferenceSteps?: boolean;
|
|
216
217
|
supportsLimitGenerations?: boolean;
|
|
217
218
|
supportsMaskImage?: boolean;
|
|
219
|
+
maskImageField?: "mask_image_url" | "mask_url";
|
|
218
220
|
supportsNegativePrompt?: boolean;
|
|
219
221
|
supportsNumImages: boolean;
|
|
220
222
|
supportsOutputFormat?: boolean;
|
|
221
223
|
supportedOutputFormats?: readonly ImageOutputFormat[];
|
|
222
224
|
supportsQuality?: boolean;
|
|
225
|
+
supportedQualities?: readonly ImageQuality[];
|
|
223
226
|
supportsRaw?: boolean;
|
|
224
227
|
supportsRenderingSpeed?: boolean;
|
|
225
228
|
supportsResolution: boolean;
|
|
@@ -373,7 +376,7 @@ interface MotifResponse {
|
|
|
373
376
|
/** ─── Queue Types ────────────────────────────────────────────── */
|
|
374
377
|
interface QueuedJob {
|
|
375
378
|
endpoint: string;
|
|
376
|
-
estimatedCost: number;
|
|
379
|
+
estimatedCost: number | null;
|
|
377
380
|
requestId: string;
|
|
378
381
|
}
|
|
379
382
|
interface JobStatus {
|
|
@@ -428,7 +431,7 @@ export declare const FORMAT_PRESETS: Record<string, {
|
|
|
428
431
|
//#endregion
|
|
429
432
|
//#region src/cost.d.ts
|
|
430
433
|
/** Estimate cost based on model and settings */
|
|
431
|
-
export declare function estimateCost(model: string, resolution?: Resolution, numImages?: number): number;
|
|
434
|
+
export declare function estimateCost(model: string, resolution?: Resolution, numImages?: number): number | null;
|
|
432
435
|
/** Estimate cost for video generation */
|
|
433
436
|
export declare function estimateVideoCost(durationSeconds?: number, generateAudio?: boolean): number;
|
|
434
437
|
//#endregion
|
|
@@ -485,9 +488,9 @@ export declare const VIDEO_IMAGE_TO_VIDEO_TOP_15: LeaderboardSnapshot;
|
|
|
485
488
|
//#endregion
|
|
486
489
|
//#region src/models.d.ts
|
|
487
490
|
export declare const MODELS: Record<string, ModelConfig>;
|
|
488
|
-
export declare const GENERATION_MODELS: readonly ["gpt2", "gpt", "banana2", "banana", "gemini", "gemini3", "seedream4", "seedream45", "seedream5", "seedream5-lite", "flux2-max", "flux2-pro", "flux2-flex", "flux2-dev", "flux2-turbo", "flux", "flux-fast", "recraft", "recraft4", "ideogram", "ideogram4", "grok-image", "qwen", "qwen3"];
|
|
491
|
+
export declare const GENERATION_MODELS: readonly ["flare", "sunburst", "gpt2", "gpt", "banana2", "banana", "gemini", "gemini3", "seedream4", "seedream45", "seedream5", "seedream5-lite", "flux2-max", "flux2-pro", "flux2-flex", "flux2-dev", "flux2-turbo", "flux", "flux-fast", "recraft", "recraft4", "ideogram", "ideogram4", "grok-image", "qwen", "qwen3"];
|
|
489
492
|
/** Models whose fal endpoints support image editing (vary/edit flows). */
|
|
490
|
-
export declare const EDIT_CAPABLE_MODELS: ("banana" | "banana2" | "flux" | "flux-fast" | "flux2-dev" | "flux2-flex" | "flux2-max" | "flux2-pro" | "flux2-turbo" | "gemini" | "gemini3" | "gpt" | "gpt2" | "grok-image" | "ideogram" | "ideogram4" | "qwen" | "qwen3" | "recraft" | "recraft4" | "seedream4" | "seedream45" | "seedream5" | "seedream5-lite")[];
|
|
493
|
+
export declare const EDIT_CAPABLE_MODELS: ("banana" | "banana2" | "flare" | "flux" | "flux-fast" | "flux2-dev" | "flux2-flex" | "flux2-max" | "flux2-pro" | "flux2-turbo" | "gemini" | "gemini3" | "gpt" | "gpt2" | "grok-image" | "ideogram" | "ideogram4" | "qwen" | "qwen3" | "recraft" | "recraft4" | "seedream4" | "seedream45" | "seedream5" | "seedream5-lite" | "sunburst")[];
|
|
491
494
|
export declare const UTILITY_MODELS: readonly ["clarity", "crystal", "rmbg", "bria"];
|
|
492
495
|
export declare const VIDEO_MODELS: readonly ["kling"];
|
|
493
496
|
type GenerationModelName = (typeof GENERATION_MODELS)[number];
|
|
@@ -608,7 +611,7 @@ export declare class FalClient {
|
|
|
608
611
|
*/
|
|
609
612
|
deletePayloads(requestId: string): Promise<Result$1<void, MotifError>>;
|
|
610
613
|
/** Estimate cost for a generation (no API call). */
|
|
611
|
-
estimateCost(model: string, resolution?: Resolution, numImages?: number): number;
|
|
614
|
+
estimateCost(model: string, resolution?: Resolution, numImages?: number): number | null;
|
|
612
615
|
/** Build the fal.ai request body without sending it. */
|
|
613
616
|
buildRequestBody(options: GenerateOptions): {
|
|
614
617
|
endpoint: string;
|
|
@@ -617,7 +620,7 @@ export declare class FalClient {
|
|
|
617
620
|
/** Model registry. */
|
|
618
621
|
get models(): Record<string, ModelConfig>;
|
|
619
622
|
/** Generation model keys. */
|
|
620
|
-
get generationModels(): readonly ["gpt2", "gpt", "banana2", "banana", "gemini", "gemini3", "seedream4", "seedream45", "seedream5", "seedream5-lite", "flux2-max", "flux2-pro", "flux2-flex", "flux2-dev", "flux2-turbo", "flux", "flux-fast", "recraft", "recraft4", "ideogram", "ideogram4", "grok-image", "qwen", "qwen3"];
|
|
623
|
+
get generationModels(): readonly ["flare", "sunburst", "gpt2", "gpt", "banana2", "banana", "gemini", "gemini3", "seedream4", "seedream45", "seedream5", "seedream5-lite", "flux2-max", "flux2-pro", "flux2-flex", "flux2-dev", "flux2-turbo", "flux", "flux-fast", "recraft", "recraft4", "ideogram", "ideogram4", "grok-image", "qwen", "qwen3"];
|
|
621
624
|
/** Utility model keys. */
|
|
622
625
|
get utilityModels(): readonly ["clarity", "crystal", "rmbg", "bria"];
|
|
623
626
|
/** Registered fal utility/tool endpoints. */
|
package/dist/index.d.ts
CHANGED
|
@@ -145,7 +145,7 @@ type Resolution = "0.5K" | "1K" | "2K" | "4K";
|
|
|
145
145
|
type ModelType = "generation" | "utility" | "video";
|
|
146
146
|
type ImageOutputFormat = "jpeg" | "png" | "webp";
|
|
147
147
|
type BackgroundMode = "auto" | "transparent" | "opaque";
|
|
148
|
-
type ImageQuality = "auto" | "low" | "medium" | "high";
|
|
148
|
+
type ImageQuality = "auto" | "low" | "medium" | "high" | "xhigh" | "max";
|
|
149
149
|
type ThinkingLevel = "minimal" | "high";
|
|
150
150
|
type GptImageSize = "auto" | "1024x1024" | "1536x1024" | "1024x1536";
|
|
151
151
|
type FalImageSizePreset = "auto" | "square_hd" | "square" | "portrait_4_3" | "portrait_16_9" | "landscape_4_3" | "landscape_16_9";
|
|
@@ -201,7 +201,8 @@ interface ModelConfig {
|
|
|
201
201
|
maxReferenceImages?: number;
|
|
202
202
|
name: string;
|
|
203
203
|
pricing: string;
|
|
204
|
-
|
|
204
|
+
/** Null means token-metered with no per-image estimate. */
|
|
205
|
+
pricePerImageUsd?: number | null;
|
|
205
206
|
/** How this model accepts dimensions (default: "aspect_ratio") */
|
|
206
207
|
sizeMode?: SizeMode;
|
|
207
208
|
supportsAspect: boolean;
|
|
@@ -215,11 +216,13 @@ interface ModelConfig {
|
|
|
215
216
|
supportsInferenceSteps?: boolean;
|
|
216
217
|
supportsLimitGenerations?: boolean;
|
|
217
218
|
supportsMaskImage?: boolean;
|
|
219
|
+
maskImageField?: "mask_image_url" | "mask_url";
|
|
218
220
|
supportsNegativePrompt?: boolean;
|
|
219
221
|
supportsNumImages: boolean;
|
|
220
222
|
supportsOutputFormat?: boolean;
|
|
221
223
|
supportedOutputFormats?: readonly ImageOutputFormat[];
|
|
222
224
|
supportsQuality?: boolean;
|
|
225
|
+
supportedQualities?: readonly ImageQuality[];
|
|
223
226
|
supportsRaw?: boolean;
|
|
224
227
|
supportsRenderingSpeed?: boolean;
|
|
225
228
|
supportsResolution: boolean;
|
|
@@ -373,7 +376,7 @@ interface MotifResponse {
|
|
|
373
376
|
/** ─── Queue Types ────────────────────────────────────────────── */
|
|
374
377
|
interface QueuedJob {
|
|
375
378
|
endpoint: string;
|
|
376
|
-
estimatedCost: number;
|
|
379
|
+
estimatedCost: number | null;
|
|
377
380
|
requestId: string;
|
|
378
381
|
}
|
|
379
382
|
interface JobStatus {
|
|
@@ -428,7 +431,7 @@ export declare const FORMAT_PRESETS: Record<string, {
|
|
|
428
431
|
//#endregion
|
|
429
432
|
//#region src/cost.d.ts
|
|
430
433
|
/** Estimate cost based on model and settings */
|
|
431
|
-
export declare function estimateCost(model: string, resolution?: Resolution, numImages?: number): number;
|
|
434
|
+
export declare function estimateCost(model: string, resolution?: Resolution, numImages?: number): number | null;
|
|
432
435
|
/** Estimate cost for video generation */
|
|
433
436
|
export declare function estimateVideoCost(durationSeconds?: number, generateAudio?: boolean): number;
|
|
434
437
|
//#endregion
|
|
@@ -485,9 +488,9 @@ export declare const VIDEO_IMAGE_TO_VIDEO_TOP_15: LeaderboardSnapshot;
|
|
|
485
488
|
//#endregion
|
|
486
489
|
//#region src/models.d.ts
|
|
487
490
|
export declare const MODELS: Record<string, ModelConfig>;
|
|
488
|
-
export declare const GENERATION_MODELS: readonly ["gpt2", "gpt", "banana2", "banana", "gemini", "gemini3", "seedream4", "seedream45", "seedream5", "seedream5-lite", "flux2-max", "flux2-pro", "flux2-flex", "flux2-dev", "flux2-turbo", "flux", "flux-fast", "recraft", "recraft4", "ideogram", "ideogram4", "grok-image", "qwen", "qwen3"];
|
|
491
|
+
export declare const GENERATION_MODELS: readonly ["flare", "sunburst", "gpt2", "gpt", "banana2", "banana", "gemini", "gemini3", "seedream4", "seedream45", "seedream5", "seedream5-lite", "flux2-max", "flux2-pro", "flux2-flex", "flux2-dev", "flux2-turbo", "flux", "flux-fast", "recraft", "recraft4", "ideogram", "ideogram4", "grok-image", "qwen", "qwen3"];
|
|
489
492
|
/** Models whose fal endpoints support image editing (vary/edit flows). */
|
|
490
|
-
export declare const EDIT_CAPABLE_MODELS: ("banana" | "banana2" | "flux" | "flux-fast" | "flux2-dev" | "flux2-flex" | "flux2-max" | "flux2-pro" | "flux2-turbo" | "gemini" | "gemini3" | "gpt" | "gpt2" | "grok-image" | "ideogram" | "ideogram4" | "qwen" | "qwen3" | "recraft" | "recraft4" | "seedream4" | "seedream45" | "seedream5" | "seedream5-lite")[];
|
|
493
|
+
export declare const EDIT_CAPABLE_MODELS: ("banana" | "banana2" | "flare" | "flux" | "flux-fast" | "flux2-dev" | "flux2-flex" | "flux2-max" | "flux2-pro" | "flux2-turbo" | "gemini" | "gemini3" | "gpt" | "gpt2" | "grok-image" | "ideogram" | "ideogram4" | "qwen" | "qwen3" | "recraft" | "recraft4" | "seedream4" | "seedream45" | "seedream5" | "seedream5-lite" | "sunburst")[];
|
|
491
494
|
export declare const UTILITY_MODELS: readonly ["clarity", "crystal", "rmbg", "bria"];
|
|
492
495
|
export declare const VIDEO_MODELS: readonly ["kling"];
|
|
493
496
|
type GenerationModelName = (typeof GENERATION_MODELS)[number];
|
|
@@ -608,7 +611,7 @@ export declare class FalClient {
|
|
|
608
611
|
*/
|
|
609
612
|
deletePayloads(requestId: string): Promise<Result$1<void, MotifError>>;
|
|
610
613
|
/** Estimate cost for a generation (no API call). */
|
|
611
|
-
estimateCost(model: string, resolution?: Resolution, numImages?: number): number;
|
|
614
|
+
estimateCost(model: string, resolution?: Resolution, numImages?: number): number | null;
|
|
612
615
|
/** Build the fal.ai request body without sending it. */
|
|
613
616
|
buildRequestBody(options: GenerateOptions): {
|
|
614
617
|
endpoint: string;
|
|
@@ -617,7 +620,7 @@ export declare class FalClient {
|
|
|
617
620
|
/** Model registry. */
|
|
618
621
|
get models(): Record<string, ModelConfig>;
|
|
619
622
|
/** Generation model keys. */
|
|
620
|
-
get generationModels(): readonly ["gpt2", "gpt", "banana2", "banana", "gemini", "gemini3", "seedream4", "seedream45", "seedream5", "seedream5-lite", "flux2-max", "flux2-pro", "flux2-flex", "flux2-dev", "flux2-turbo", "flux", "flux-fast", "recraft", "recraft4", "ideogram", "ideogram4", "grok-image", "qwen", "qwen3"];
|
|
623
|
+
get generationModels(): readonly ["flare", "sunburst", "gpt2", "gpt", "banana2", "banana", "gemini", "gemini3", "seedream4", "seedream45", "seedream5", "seedream5-lite", "flux2-max", "flux2-pro", "flux2-flex", "flux2-dev", "flux2-turbo", "flux", "flux-fast", "recraft", "recraft4", "ideogram", "ideogram4", "grok-image", "qwen", "qwen3"];
|
|
621
624
|
/** Utility model keys. */
|
|
622
625
|
get utilityModels(): readonly ["clarity", "crystal", "rmbg", "bria"];
|
|
623
626
|
/** Registered fal utility/tool endpoints. */
|
package/dist/index.js
CHANGED
|
@@ -111,6 +111,64 @@ const AA_IMAGE_SOURCES = ["https://artificialanalysis.ai/image/leaderboard/text-
|
|
|
111
111
|
const FAL_PRICING_CHECKED_AT = "2026-05-12";
|
|
112
112
|
const FAL_PRICING_CHECKED_JUL_2026 = "2026-07-11";
|
|
113
113
|
const MODELS = {
|
|
114
|
+
flare: {
|
|
115
|
+
editEndpoint: "openai/gpt-image-2.5/flare/edit",
|
|
116
|
+
endpoint: "openai/gpt-image-2.5/flare/text-to-image",
|
|
117
|
+
maxReferenceImages: 16,
|
|
118
|
+
name: "GPT Image 2.5 Flare",
|
|
119
|
+
pricing: "Token-based: text $5/M input, $1.25/M cached, $10/M output; image $8/M input, $2/M cached, $30/M output",
|
|
120
|
+
pricePerImageUsd: null,
|
|
121
|
+
sizeMode: "image_size_enum",
|
|
122
|
+
supportsAspect: true,
|
|
123
|
+
supportsBackground: true,
|
|
124
|
+
supportsEdit: true,
|
|
125
|
+
supportsMaskImage: true,
|
|
126
|
+
maskImageField: "mask_url",
|
|
127
|
+
supportsNumImages: true,
|
|
128
|
+
supportsOutputFormat: true,
|
|
129
|
+
supportsQuality: true,
|
|
130
|
+
supportedQualities: [
|
|
131
|
+
"auto",
|
|
132
|
+
"low",
|
|
133
|
+
"medium",
|
|
134
|
+
"high",
|
|
135
|
+
"xhigh",
|
|
136
|
+
"max"
|
|
137
|
+
],
|
|
138
|
+
supportsResolution: false,
|
|
139
|
+
supportsSyncMode: true,
|
|
140
|
+
type: "generation",
|
|
141
|
+
useQueue: true
|
|
142
|
+
},
|
|
143
|
+
sunburst: {
|
|
144
|
+
editEndpoint: "openai/gpt-image-2.5/sunburst/edit",
|
|
145
|
+
endpoint: "openai/gpt-image-2.5/sunburst/text-to-image",
|
|
146
|
+
maxReferenceImages: 16,
|
|
147
|
+
name: "GPT Image 2.5 Sunburst",
|
|
148
|
+
pricing: "Token-based: text $5/M input, $1.25/M cached, $10/M output; image $8/M input, $2/M cached, $30/M output",
|
|
149
|
+
pricePerImageUsd: null,
|
|
150
|
+
sizeMode: "image_size_enum",
|
|
151
|
+
supportsAspect: true,
|
|
152
|
+
supportsBackground: true,
|
|
153
|
+
supportsEdit: true,
|
|
154
|
+
supportsMaskImage: true,
|
|
155
|
+
maskImageField: "mask_url",
|
|
156
|
+
supportsNumImages: true,
|
|
157
|
+
supportsOutputFormat: true,
|
|
158
|
+
supportsQuality: true,
|
|
159
|
+
supportedQualities: [
|
|
160
|
+
"auto",
|
|
161
|
+
"low",
|
|
162
|
+
"medium",
|
|
163
|
+
"high",
|
|
164
|
+
"xhigh",
|
|
165
|
+
"max"
|
|
166
|
+
],
|
|
167
|
+
supportsResolution: false,
|
|
168
|
+
supportsSyncMode: true,
|
|
169
|
+
type: "generation",
|
|
170
|
+
useQueue: true
|
|
171
|
+
},
|
|
114
172
|
gpt2: {
|
|
115
173
|
benchmark: {
|
|
116
174
|
artificialAnalysis: {
|
|
@@ -1205,6 +1263,8 @@ const MODELS = {
|
|
|
1205
1263
|
}
|
|
1206
1264
|
};
|
|
1207
1265
|
const GENERATION_MODELS = [
|
|
1266
|
+
"flare",
|
|
1267
|
+
"sunburst",
|
|
1208
1268
|
"gpt2",
|
|
1209
1269
|
"gpt",
|
|
1210
1270
|
"banana2",
|
|
@@ -1287,6 +1347,7 @@ const IDEOGRAM_STYLES = [
|
|
|
1287
1347
|
/** Estimate cost based on model and settings */
|
|
1288
1348
|
function estimateCost(model, resolution, numImages = 1) {
|
|
1289
1349
|
const configuredPrice = MODELS[model]?.pricePerImageUsd;
|
|
1350
|
+
if (configuredPrice === null) return null;
|
|
1290
1351
|
if (configuredPrice !== void 0) {
|
|
1291
1352
|
if ((model === "banana" || model === "gemini3") && resolution === "4K") return configuredPrice * 2 * numImages;
|
|
1292
1353
|
if (model === "seedream5" && (resolution === "2K" || resolution === "4K")) return configuredPrice * 2 * numImages;
|
|
@@ -1512,6 +1573,15 @@ function validateGenerateOptions(options, config, sizeMode, hasEditImages) {
|
|
|
1512
1573
|
if (options.seed !== void 0 && config.supportsSeed !== true) unsupported(config, "seed");
|
|
1513
1574
|
if (options.outputFormat !== void 0 && config.supportsOutputFormat !== true) unsupported(config, "outputFormat");
|
|
1514
1575
|
if (options.quality !== void 0 && config.supportsQuality !== true) unsupported(config, "quality");
|
|
1576
|
+
if (options.quality !== void 0) {
|
|
1577
|
+
const qualities = config.supportedQualities ?? [
|
|
1578
|
+
"auto",
|
|
1579
|
+
"low",
|
|
1580
|
+
"medium",
|
|
1581
|
+
"high"
|
|
1582
|
+
];
|
|
1583
|
+
if (!qualities.includes(options.quality)) throw new Error(`${config.name} quality must be one of ${qualities.join(", ")}`);
|
|
1584
|
+
}
|
|
1515
1585
|
if (options.negativePrompt !== void 0 && config.supportsNegativePrompt !== true) unsupported(config, "negativePrompt");
|
|
1516
1586
|
if (options.style !== void 0 && config.supportsStyle !== true) unsupported(config, "style");
|
|
1517
1587
|
if (options.syncMode !== void 0 && config.supportsSyncMode !== true) unsupported(config, "syncMode");
|
|
@@ -1630,7 +1700,7 @@ function buildGenerateBody(options) {
|
|
|
1630
1700
|
body.image_urls = editImages;
|
|
1631
1701
|
if (inputFidelity) body.input_fidelity = inputFidelity;
|
|
1632
1702
|
}
|
|
1633
|
-
if (maskImageUrl !== void 0 && maskImageUrl !== "" && config.supportsMaskImage === true) body.mask_image_url = maskImageUrl;
|
|
1703
|
+
if (maskImageUrl !== void 0 && maskImageUrl !== "" && config.supportsMaskImage === true) body[config.maskImageField ?? "mask_image_url"] = maskImageUrl;
|
|
1634
1704
|
return {
|
|
1635
1705
|
body,
|
|
1636
1706
|
endpoint
|