@howells/motif-sdk 5.3.0 → 6.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 +9 -15
- package/dist/image.js +45 -87
- package/dist/index.cjs +506 -1224
- package/dist/index.d.cts +56 -34
- package/dist/index.d.ts +56 -34
- package/dist/index.js +507 -1224
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -44,7 +44,7 @@ The Task client curates 24 generation Models, with separate rankings for generat
|
|
|
44
44
|
|
|
45
45
|
These are the first choices when the request's capabilities and available keys permit them. Explicit Models, Looks and pins take precedence. Selected specialists remain for typography, transparency, control and inexpensive drafts. Archived predecessors remain available through explicit Models, pins and existing Looks, including `flux2-dev` for streaming.
|
|
46
46
|
|
|
47
|
-
Nano Banana 2.1 (`banana21`) supports up to 14 References and 1K/2K/4K output, with no 0.5K output. Its balanced placement uses preliminary Arena evidence. Sunburst and Flare
|
|
47
|
+
Nano Banana 2.1 (`banana21`) supports up to 14 References and 1K/2K/4K output, with no 0.5K output. Its balanced placement uses preliminary Arena evidence. Sunburst and Flare run GPT Image 2.6 on fal and default to `quality: "max"`; `params: { quality: "high" }` with an explicit `model` overrides it. Both also return an animated GIF (`outputFormat: "gif"`, with `params.frame_count` and `params.frame_duration_ms`) or, from text, an SVG (`outputFormat: "svg"`), and take `markup`: a transparent PNG of arrows, circles and notes the size of the image being edited, which guides a vary or an edit with References. Asking for a format or markup limits the choice to Models that support it. The direct provider image layer described below has its own adapter capabilities and chooses no Model.
|
|
48
48
|
|
|
49
49
|
Token-billed Task plans, including Sunburst, Flare, Nano Banana 2.1, Banana Lite and MAI 2.5, report `cost: { usd: null, basis: "unknown" }`; a published representative price is not an exact total. The [curation audit](https://github.com/howells/motif/blob/main/docs/research/image-model-curation-2026-10-07.md) records the current rankings, specialist roles and provider coverage gaps.
|
|
50
50
|
|
|
@@ -73,14 +73,14 @@ if (plan.isOk()) {
|
|
|
73
73
|
|
|
74
74
|
## Stream a Task
|
|
75
75
|
|
|
76
|
-
`stream(task, input, options?)` opens a direct inference stream on the resolved route. Streaming is explicitly supported for GPT Image
|
|
76
|
+
`stream(task, input, options?)` opens a direct inference stream on the resolved route. Streaming is explicitly supported for GPT Image 1.5 and FLUX.2 Dev, including their edit routes. Other routes return `STREAMING_UNSUPPORTED` before any provider request; Motif never substitutes another model or retries a stream.
|
|
77
77
|
|
|
78
78
|
```ts
|
|
79
79
|
const controller = new AbortController();
|
|
80
80
|
const opened = await motif.stream(
|
|
81
81
|
"generate",
|
|
82
82
|
{
|
|
83
|
-
model: "
|
|
83
|
+
model: "gpt",
|
|
84
84
|
prompt: "editorial product photo",
|
|
85
85
|
references: ["https://example.com/reference.png"],
|
|
86
86
|
},
|
|
@@ -110,7 +110,7 @@ For a local checkout, `node packages/motif-sdk/examples/stream.mjs --dry-run` pr
|
|
|
110
110
|
- `ASPECT_RATIOS`, `RESOLUTIONS`, `FORMAT_PRESETS` - shared sizing metadata.
|
|
111
111
|
- `LOOKS`, `CREATIVE_TAXONOMY`, `enrichPrompt`, `validateCreativeDirection` - house looks and moods.
|
|
112
112
|
- `formatCost`, `sumCosts` - cost formatting and totals.
|
|
113
|
-
- `getFalKeyFromEnv
|
|
113
|
+
- `getFalKeyFromEnv` - `@howells/envy` backed key parsing.
|
|
114
114
|
- Re-exported `neverthrow` helpers: `ok`, `err`, `Result`, `ResultAsync`.
|
|
115
115
|
|
|
116
116
|
## Common Types
|
|
@@ -161,28 +161,28 @@ Four providers are implemented, each reading its own API key from the environmen
|
|
|
161
161
|
| Provider | Env var | Notes |
|
|
162
162
|
| --- | --- | --- |
|
|
163
163
|
| `openrouter` | `OPENROUTER_API_KEY` | Default provider; Gemini gen + edit through OpenRouter (`google/*`), no masks |
|
|
164
|
-
| `openai` | `OPENAI_API_KEY` | GPT Image 2.
|
|
164
|
+
| `openai` | `OPENAI_API_KEY` | GPT Image 2.6 Flare and Sunburst |
|
|
165
165
|
| `replicate` | `REPLICATE_API_TOKEN` | flux-1.1-pro-ultra |
|
|
166
166
|
| `fal` | `FAL_KEY` | fal-hosted adapter |
|
|
167
167
|
|
|
168
168
|
`generate()` and `edit()` take a provider model id; Task resolution chooses which model to pass, not the image layer. Every result carries a normalized per-call `cost: { usd, source }`.
|
|
169
169
|
|
|
170
|
-
Both `gpt-image-2.
|
|
170
|
+
Both `gpt-image-2.6-flare` and `gpt-image-2.6-sunburst` support generation and multi-image editing:
|
|
171
171
|
|
|
172
172
|
```ts
|
|
173
173
|
const image = createMotifImage({ defaultProvider: "openai" });
|
|
174
174
|
const generated = await image.generate({
|
|
175
175
|
prompt: "A ceramic vase in soft window light",
|
|
176
|
-
model: "gpt-image-2.
|
|
176
|
+
model: "gpt-image-2.6-flare",
|
|
177
177
|
});
|
|
178
178
|
const refined = await image.edit({
|
|
179
179
|
images: [referenceBytes],
|
|
180
180
|
instruction: "Change only the vase glaze to deep green",
|
|
181
|
-
model: "gpt-image-2.
|
|
181
|
+
model: "gpt-image-2.6-sunburst",
|
|
182
182
|
});
|
|
183
183
|
```
|
|
184
184
|
|
|
185
|
-
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.
|
|
185
|
+
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.6-flare) and [Sunburst](https://developers.openai.com/api/docs/models/gpt-image-2.6-sunburst) model pages. Set quality (`auto`, `low`, `medium`, `high`, `xhigh` or `max`) and `background: "transparent"` through `providerOptions.openai`. The Task client and the CLI run the same models on fal as `flare` and `sunburst`, with up to 16 edit references, masks and transparent backgrounds. Fal generation estimates are `null` (metered).
|
|
186
186
|
|
|
187
187
|
### Best-of-N with an injectable judge
|
|
188
188
|
|
|
@@ -218,9 +218,3 @@ If some candidates fail, the judge sees only the survivors; if all `n` fail, `be
|
|
|
218
218
|
pnpm --filter @howells/motif-sdk test
|
|
219
219
|
pnpm --filter @howells/motif-sdk typecheck
|
|
220
220
|
```
|
|
221
|
-
|
|
222
|
-
Live fal canaries are opt-in:
|
|
223
|
-
|
|
224
|
-
```bash
|
|
225
|
-
RUN_FAL_CANARY=1 pnpm --filter @howells/motif-sdk test -- tests/fal-canary.test.ts
|
|
226
|
-
```
|
package/dist/image.js
CHANGED
|
@@ -73,7 +73,7 @@ const MODELS = {
|
|
|
73
73
|
rank: 2
|
|
74
74
|
}
|
|
75
75
|
},
|
|
76
|
-
useCase: "Frontier generation and editing
|
|
76
|
+
useCase: "Frontier generation and editing at max reasoning effort; scored as GPT Image 2.5 until 2.6 is benchmarked"
|
|
77
77
|
},
|
|
78
78
|
defaultQuality: "max",
|
|
79
79
|
customImageSize: {
|
|
@@ -83,11 +83,11 @@ const MODELS = {
|
|
|
83
83
|
minPixels: 655360,
|
|
84
84
|
multipleOf: 16
|
|
85
85
|
},
|
|
86
|
-
editEndpoint: "openai/gpt-image-2.
|
|
87
|
-
endpoint: "openai/gpt-image-2.
|
|
86
|
+
editEndpoint: "openai/gpt-image-2.6/flare/edit",
|
|
87
|
+
endpoint: "openai/gpt-image-2.6/flare/text-to-image",
|
|
88
88
|
maxReferenceImages: 16,
|
|
89
|
-
name: "GPT Image 2.
|
|
90
|
-
pricing: "Token-based:
|
|
89
|
+
name: "GPT Image 2.6 Flare",
|
|
90
|
+
pricing: "Token-based: image $8/M input, $2/M cached, $30/M output; text $5/M input, $1.25/M cached",
|
|
91
91
|
pricePerImageUsd: null,
|
|
92
92
|
sizeMode: "image_size_enum",
|
|
93
93
|
supportsAspect: true,
|
|
@@ -95,8 +95,23 @@ const MODELS = {
|
|
|
95
95
|
supportsEdit: true,
|
|
96
96
|
supportsMaskImage: true,
|
|
97
97
|
maskImageField: "mask_url",
|
|
98
|
+
supportsMarkupImage: true,
|
|
99
|
+
autoEditSize: true,
|
|
98
100
|
supportsNumImages: true,
|
|
99
101
|
supportsOutputFormat: true,
|
|
102
|
+
supportedOutputFormats: [
|
|
103
|
+
"jpeg",
|
|
104
|
+
"png",
|
|
105
|
+
"webp",
|
|
106
|
+
"gif",
|
|
107
|
+
"svg"
|
|
108
|
+
],
|
|
109
|
+
supportedEditOutputFormats: [
|
|
110
|
+
"jpeg",
|
|
111
|
+
"png",
|
|
112
|
+
"webp",
|
|
113
|
+
"gif"
|
|
114
|
+
],
|
|
100
115
|
supportsQuality: true,
|
|
101
116
|
supportedQualities: [
|
|
102
117
|
"auto",
|
|
@@ -125,7 +140,7 @@ const MODELS = {
|
|
|
125
140
|
rank: 1
|
|
126
141
|
}
|
|
127
142
|
},
|
|
128
|
-
useCase: "Frontier generation and editing
|
|
143
|
+
useCase: "Frontier generation and editing at max reasoning effort; scored as GPT Image 2.5 until 2.6 is benchmarked"
|
|
129
144
|
},
|
|
130
145
|
defaultQuality: "max",
|
|
131
146
|
customImageSize: {
|
|
@@ -135,11 +150,11 @@ const MODELS = {
|
|
|
135
150
|
minPixels: 655360,
|
|
136
151
|
multipleOf: 16
|
|
137
152
|
},
|
|
138
|
-
editEndpoint: "openai/gpt-image-2.
|
|
139
|
-
endpoint: "openai/gpt-image-2.
|
|
153
|
+
editEndpoint: "openai/gpt-image-2.6/sunburst/edit",
|
|
154
|
+
endpoint: "openai/gpt-image-2.6/sunburst/text-to-image",
|
|
140
155
|
maxReferenceImages: 16,
|
|
141
|
-
name: "GPT Image 2.
|
|
142
|
-
pricing: "Token-based:
|
|
156
|
+
name: "GPT Image 2.6 Sunburst",
|
|
157
|
+
pricing: "Token-based: image $8/M input, $2/M cached, $30/M output; text $5/M input, $1.25/M cached",
|
|
143
158
|
pricePerImageUsd: null,
|
|
144
159
|
sizeMode: "image_size_enum",
|
|
145
160
|
supportsAspect: true,
|
|
@@ -147,8 +162,23 @@ const MODELS = {
|
|
|
147
162
|
supportsEdit: true,
|
|
148
163
|
supportsMaskImage: true,
|
|
149
164
|
maskImageField: "mask_url",
|
|
165
|
+
supportsMarkupImage: true,
|
|
166
|
+
autoEditSize: true,
|
|
150
167
|
supportsNumImages: true,
|
|
151
168
|
supportsOutputFormat: true,
|
|
169
|
+
supportedOutputFormats: [
|
|
170
|
+
"jpeg",
|
|
171
|
+
"png",
|
|
172
|
+
"webp",
|
|
173
|
+
"gif",
|
|
174
|
+
"svg"
|
|
175
|
+
],
|
|
176
|
+
supportedEditOutputFormats: [
|
|
177
|
+
"jpeg",
|
|
178
|
+
"png",
|
|
179
|
+
"webp",
|
|
180
|
+
"gif"
|
|
181
|
+
],
|
|
152
182
|
supportsQuality: true,
|
|
153
183
|
supportedQualities: [
|
|
154
184
|
"auto",
|
|
@@ -163,77 +193,6 @@ const MODELS = {
|
|
|
163
193
|
type: "generation",
|
|
164
194
|
useQueue: true
|
|
165
195
|
},
|
|
166
|
-
gpt2: {
|
|
167
|
-
benchmark: {
|
|
168
|
-
artificialAnalysis: {
|
|
169
|
-
editing: {
|
|
170
|
-
elo: 1125,
|
|
171
|
-
rank: 5
|
|
172
|
-
},
|
|
173
|
-
snapshotDate: AA_IMAGE_LEADERBOARD_SNAPSHOT,
|
|
174
|
-
sourceUrls: AA_IMAGE_SOURCES,
|
|
175
|
-
textToImage: {
|
|
176
|
-
elo: 1172,
|
|
177
|
-
rank: 3
|
|
178
|
-
}
|
|
179
|
-
},
|
|
180
|
-
speed: {
|
|
181
|
-
medianSeconds: 200.7,
|
|
182
|
-
p95Seconds: 232.6,
|
|
183
|
-
source: "artificial-analysis-models"
|
|
184
|
-
},
|
|
185
|
-
tiers: {
|
|
186
|
-
price: "ultra",
|
|
187
|
-
quality: "frontier",
|
|
188
|
-
speed: "very_slow"
|
|
189
|
-
},
|
|
190
|
-
useCase: "Established frontier generation and transparent PNGs"
|
|
191
|
-
},
|
|
192
|
-
customImageSize: {
|
|
193
|
-
maxEdge: 3840,
|
|
194
|
-
maxPixels: 8294400,
|
|
195
|
-
maxRatio: 3,
|
|
196
|
-
minPixels: 655360,
|
|
197
|
-
multipleOf: 16
|
|
198
|
-
},
|
|
199
|
-
editEndpoint: "openai/gpt-image-2/edit",
|
|
200
|
-
endpoint: "openai/gpt-image-2",
|
|
201
|
-
falPricing: {
|
|
202
|
-
checkedAt: FAL_PRICING_CHECKED_AT,
|
|
203
|
-
currency: "USD",
|
|
204
|
-
endpointId: "openai/gpt-image-2",
|
|
205
|
-
estimatedCostPerImageUsd: .211,
|
|
206
|
-
source: "fal-pricing-api",
|
|
207
|
-
unit: "units",
|
|
208
|
-
unitPrice: 1
|
|
209
|
-
},
|
|
210
|
-
maxReferenceImages: 16,
|
|
211
|
-
name: "GPT Image 2",
|
|
212
|
-
pricePerImageUsd: .211,
|
|
213
|
-
pricing: "$0.211",
|
|
214
|
-
sizeMode: "image_size_enum",
|
|
215
|
-
supportsAspect: true,
|
|
216
|
-
streaming: {
|
|
217
|
-
generation: true,
|
|
218
|
-
edit: true
|
|
219
|
-
},
|
|
220
|
-
supportsEdit: true,
|
|
221
|
-
supportsMaskImage: true,
|
|
222
|
-
maskImageField: "mask_url",
|
|
223
|
-
supportsNumImages: true,
|
|
224
|
-
supportsOutputFormat: true,
|
|
225
|
-
supportsQuality: true,
|
|
226
|
-
supportsResolution: false,
|
|
227
|
-
supportsSyncMode: true,
|
|
228
|
-
transparencyRoute: {
|
|
229
|
-
apiKeyEnv: "OPENAI_API_KEY",
|
|
230
|
-
model: "gpt-image-2",
|
|
231
|
-
provider: "openai",
|
|
232
|
-
supportsEdit: true
|
|
233
|
-
},
|
|
234
|
-
type: "generation",
|
|
235
|
-
useQueue: true
|
|
236
|
-
},
|
|
237
196
|
gpt: {
|
|
238
197
|
benchmark: {
|
|
239
198
|
artificialAnalysis: {
|
|
@@ -1814,7 +1773,6 @@ const MODELS = {
|
|
|
1814
1773
|
[
|
|
1815
1774
|
"sunburst",
|
|
1816
1775
|
"flare",
|
|
1817
|
-
"gpt2",
|
|
1818
1776
|
"gpt",
|
|
1819
1777
|
"banana21",
|
|
1820
1778
|
"banana2",
|
|
@@ -1854,6 +1812,7 @@ const OPTION_CAPABILITIES = {
|
|
|
1854
1812
|
imageSize: (config) => config.sizeMode === "gpt_size" || config.sizeMode === "image_size_enum",
|
|
1855
1813
|
inputFidelity: (config) => config.sizeMode === "gpt_size",
|
|
1856
1814
|
limitGenerations: (config) => config.supportsLimitGenerations === true,
|
|
1815
|
+
markupImageUrl: (config) => config.supportsMarkupImage === true,
|
|
1857
1816
|
maskImageUrl: (config) => config.supportsMaskImage === true,
|
|
1858
1817
|
negativePrompt: (config) => config.supportsNegativePrompt === true,
|
|
1859
1818
|
numImages: (config) => config.supportsNumImages,
|
|
@@ -1956,7 +1915,7 @@ Object.keys(OPTION_CAPABILITIES).filter(isModelOption);
|
|
|
1956
1915
|
experimental: true,
|
|
1957
1916
|
id: "illustration",
|
|
1958
1917
|
label: "Editorial illustration",
|
|
1959
|
-
model: "
|
|
1918
|
+
model: "flare"
|
|
1960
1919
|
}
|
|
1961
1920
|
],
|
|
1962
1921
|
mood: [
|
|
@@ -3546,7 +3505,6 @@ const FAL_IMAGE_PRICE_USD = {
|
|
|
3546
3505
|
"fal-ai/flux-2": MODELS["flux2-dev"]?.pricePerImageUsd ?? .012,
|
|
3547
3506
|
"fal-ai/flux-2/turbo": MODELS["flux2-turbo"]?.pricePerImageUsd ?? .008,
|
|
3548
3507
|
[FAL_GPT_IMAGE_MODEL]: MODELS.gpt?.pricePerImageUsd ?? .133,
|
|
3549
|
-
"openai/gpt-image-2": MODELS.gpt2?.pricePerImageUsd ?? .211,
|
|
3550
3508
|
"fal-ai/nano-banana-2": MODELS.banana2?.pricePerImageUsd ?? .08,
|
|
3551
3509
|
"fal-ai/nano-banana-pro": MODELS.banana?.pricePerImageUsd ?? .15,
|
|
3552
3510
|
"fal-ai/gemini-25-flash-image": MODELS.gemini?.pricePerImageUsd ?? .0398,
|
|
@@ -3621,10 +3579,10 @@ const OPENAI_API_KEY_ENV = "OPENAI_API_KEY";
|
|
|
3621
3579
|
* Sanity anchor: the Phase 0 benchmark measured gpt-image direct at $0.042
|
|
3622
3580
|
* (vs $0.133 via fal — full data and methodology on Linear MOT-23).
|
|
3623
3581
|
*
|
|
3624
|
-
* GPT Image 2.
|
|
3582
|
+
* GPT Image 2.6 is token-priced, with no published per-image estimate. Leave
|
|
3625
3583
|
* these models absent so cost remains unknown unless supplied by the provider.
|
|
3626
|
-
* https://developers.openai.com/api/docs/models/gpt-image-2.
|
|
3627
|
-
* https://developers.openai.com/api/docs/models/gpt-image-2.
|
|
3584
|
+
* https://developers.openai.com/api/docs/models/gpt-image-2.6-flare
|
|
3585
|
+
* https://developers.openai.com/api/docs/models/gpt-image-2.6-sunburst
|
|
3628
3586
|
*/
|
|
3629
3587
|
const OPENAI_IMAGE_PRICE_USD = { "gpt-image-1": .042 };
|
|
3630
3588
|
/**
|