@howells/motif-sdk 5.2.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 CHANGED
@@ -34,17 +34,17 @@ Every Task function returns `Result<TaskOutput, MotifError>` from `neverthrow` a
34
34
 
35
35
  ## Curated Models and Tiers
36
36
 
37
- The Task client curates 21 generation Models, with separate rankings for generation and editing. `TASKS.generate` and `TASKS.vary` expose the current choices, Tiers and evidence in `basis` and `rankedAt`. A generation with `references` uses the editing ranking.
37
+ The Task client curates 24 generation Models, with separate rankings for generation and editing. Both follow Arena, read 29 October 2026. `TASKS.generate` and `TASKS.vary` expose the current choices, Tiers and evidence in `basis` and `rankedAt`. A generation with `references` uses the editing ranking.
38
38
 
39
39
  | Tier | Generation default | Editing default |
40
40
  | -------------------- | ------------------ | ----------------- |
41
41
  | `quality` | Sunburst at `max` | Sunburst at `max` |
42
- | `balanced` (default) | Nano Banana 2.1 | Nano Banana 2.1 |
42
+ | `balanced` (default) | Nano Banana 2.1 | Grok Image 2 |
43
43
  | `fast` | Nano Banana 2 Lite | Grok |
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 default to the benchmarked `max` quality on fal; `params: { quality: "high" }` with an explicit `model` overrides it. The direct provider image layer described below has its own adapter capabilities and chooses no Model.
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
 
@@ -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.5 Flare and Sunburst |
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.5-flare` and `gpt-image-2.5-sunburst` support generation and multi-image editing:
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.5-flare",
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.5-sunburst",
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.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 Task client and the CLI reach these models on fal as `flare` and `sunburst`. Fal supports `xhigh` and `max`, up to 16 edit references, masks and transparent backgrounds. Fal generation estimates are `null` (metered).
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
@@ -57,6 +57,7 @@ const FAL_PRICING_CHECKED_AT = "2026-05-12";
57
57
  const FAL_PRICING_CHECKED_JUL_2026 = "2026-07-11";
58
58
  const FAL_PRICING_CHECKED_AUG_2026 = "2026-08-23";
59
59
  const FAL_PRICING_CHECKED_SEP_2026 = "2026-09-16";
60
+ const FAL_PRICING_CHECKED_OCT_2026 = "2026-10-29";
60
61
  const MODELS = {
61
62
  flare: {
62
63
  benchmark: {
@@ -72,7 +73,7 @@ const MODELS = {
72
73
  rank: 2
73
74
  }
74
75
  },
75
- useCase: "Frontier generation and editing, evaluated at max quality"
76
+ useCase: "Frontier generation and editing at max reasoning effort; scored as GPT Image 2.5 until 2.6 is benchmarked"
76
77
  },
77
78
  defaultQuality: "max",
78
79
  customImageSize: {
@@ -82,11 +83,11 @@ const MODELS = {
82
83
  minPixels: 655360,
83
84
  multipleOf: 16
84
85
  },
85
- editEndpoint: "openai/gpt-image-2.5/flare/edit",
86
- endpoint: "openai/gpt-image-2.5/flare/text-to-image",
86
+ editEndpoint: "openai/gpt-image-2.6/flare/edit",
87
+ endpoint: "openai/gpt-image-2.6/flare/text-to-image",
87
88
  maxReferenceImages: 16,
88
- name: "GPT Image 2.5 Flare",
89
- pricing: "Token-based: text $5/M input, $1.25/M cached, $10/M output; image $8/M input, $2/M cached, $30/M output",
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",
90
91
  pricePerImageUsd: null,
91
92
  sizeMode: "image_size_enum",
92
93
  supportsAspect: true,
@@ -94,8 +95,23 @@ const MODELS = {
94
95
  supportsEdit: true,
95
96
  supportsMaskImage: true,
96
97
  maskImageField: "mask_url",
98
+ supportsMarkupImage: true,
99
+ autoEditSize: true,
97
100
  supportsNumImages: true,
98
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
+ ],
99
115
  supportsQuality: true,
100
116
  supportedQualities: [
101
117
  "auto",
@@ -124,7 +140,7 @@ const MODELS = {
124
140
  rank: 1
125
141
  }
126
142
  },
127
- useCase: "Frontier generation and editing, evaluated at max quality"
143
+ useCase: "Frontier generation and editing at max reasoning effort; scored as GPT Image 2.5 until 2.6 is benchmarked"
128
144
  },
129
145
  defaultQuality: "max",
130
146
  customImageSize: {
@@ -134,11 +150,11 @@ const MODELS = {
134
150
  minPixels: 655360,
135
151
  multipleOf: 16
136
152
  },
137
- editEndpoint: "openai/gpt-image-2.5/sunburst/edit",
138
- endpoint: "openai/gpt-image-2.5/sunburst/text-to-image",
153
+ editEndpoint: "openai/gpt-image-2.6/sunburst/edit",
154
+ endpoint: "openai/gpt-image-2.6/sunburst/text-to-image",
139
155
  maxReferenceImages: 16,
140
- name: "GPT Image 2.5 Sunburst",
141
- pricing: "Token-based: text $5/M input, $1.25/M cached, $10/M output; image $8/M input, $2/M cached, $30/M output",
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",
142
158
  pricePerImageUsd: null,
143
159
  sizeMode: "image_size_enum",
144
160
  supportsAspect: true,
@@ -146,8 +162,23 @@ const MODELS = {
146
162
  supportsEdit: true,
147
163
  supportsMaskImage: true,
148
164
  maskImageField: "mask_url",
165
+ supportsMarkupImage: true,
166
+ autoEditSize: true,
149
167
  supportsNumImages: true,
150
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
+ ],
151
182
  supportsQuality: true,
152
183
  supportedQualities: [
153
184
  "auto",
@@ -1413,6 +1444,7 @@ const MODELS = {
1413
1444
  maxPixels: 4194304,
1414
1445
  minPixels: 262144
1415
1446
  },
1447
+ editEndpoint: "alibaba/qwen-image-3/edit",
1416
1448
  endpoint: "alibaba/qwen-image-3/text-to-image",
1417
1449
  falPricing: {
1418
1450
  checkedAt: FAL_PRICING_CHECKED_SEP_2026,
@@ -1423,12 +1455,13 @@ const MODELS = {
1423
1455
  unit: "images",
1424
1456
  unitPrice: .04
1425
1457
  },
1458
+ maxReferenceImages: 3,
1426
1459
  name: "Qwen Image 3",
1427
1460
  pricePerImageUsd: .04,
1428
1461
  pricing: "$0.04 (1K) / $0.075 (2K)",
1429
1462
  sizeMode: "image_size_enum",
1430
1463
  supportsAspect: true,
1431
- supportsEdit: false,
1464
+ supportsEdit: true,
1432
1465
  supportsNegativePrompt: true,
1433
1466
  supportsNumImages: true,
1434
1467
  supportsOutputFormat: true,
@@ -1631,6 +1664,120 @@ const MODELS = {
1631
1664
  supportsSyncMode: true,
1632
1665
  type: "generation"
1633
1666
  },
1667
+ "muse-image": {
1668
+ benchmark: { useCase: "Low-cost generation and multi-reference editing; Arena #8 generation / #10 editing, 2026-10-29" },
1669
+ editEndpoint: "meta/muse-image/edit",
1670
+ endpoint: "meta/muse-image/text-to-image",
1671
+ falPricing: {
1672
+ checkedAt: FAL_PRICING_CHECKED_OCT_2026,
1673
+ currency: "USD",
1674
+ endpointId: "meta/muse-image/text-to-image",
1675
+ estimatedCostPerImageUsd: .01,
1676
+ source: "fal-pricing-api",
1677
+ unit: "images",
1678
+ unitPrice: .01
1679
+ },
1680
+ maxReferenceImages: 10,
1681
+ name: "Meta Muse Image",
1682
+ pricePerImageUsd: .01,
1683
+ pricing: "$0.01",
1684
+ sizeMode: "aspect_ratio",
1685
+ supportedAspects: [
1686
+ "1:1",
1687
+ "4:3",
1688
+ "3:4",
1689
+ "16:9",
1690
+ "9:16",
1691
+ "3:2",
1692
+ "2:3",
1693
+ "4:5",
1694
+ "5:4",
1695
+ "21:9",
1696
+ "4:1",
1697
+ "1:4",
1698
+ "8:1",
1699
+ "1:8"
1700
+ ],
1701
+ supportsAspect: true,
1702
+ supportsEdit: true,
1703
+ supportsNumImages: true,
1704
+ supportsOutputFormat: true,
1705
+ supportsResolution: false,
1706
+ supportsSyncMode: true,
1707
+ type: "generation"
1708
+ },
1709
+ krea3: {
1710
+ benchmark: { useCase: "Reference editing with prompt planning. Motif sends fal's defaults (berry, medium reasoning), which Arena has not scored; Arena's 1411 editing score (2026-10-29) is for Ultra, available with --param reasoning_level=ultra" },
1711
+ customImageSize: {
1712
+ maxEdge: 3200,
1713
+ maxPixels: 4220160,
1714
+ minEdge: 256,
1715
+ multipleOf: 16
1716
+ },
1717
+ editEndpoint: "krea/v3/edit",
1718
+ endpoint: "krea/v3/text-to-image",
1719
+ falPricing: {
1720
+ checkedAt: FAL_PRICING_CHECKED_OCT_2026,
1721
+ currency: "USD",
1722
+ endpointId: "krea/v3/text-to-image",
1723
+ estimatedCostPerImageUsd: .025,
1724
+ source: "fal-pricing-api",
1725
+ unit: "megapixels",
1726
+ unitPrice: .025
1727
+ },
1728
+ maxReferenceImages: 8,
1729
+ name: "Krea 3",
1730
+ pricePerImageUsd: .025,
1731
+ pricing: "$0.025/MP (edits also bill each reference)",
1732
+ sizeMode: "image_size_enum",
1733
+ supportedOutputFormats: ["png", "jpeg"],
1734
+ supportsAspect: true,
1735
+ supportsEdit: true,
1736
+ supportsNegativePrompt: true,
1737
+ supportsNumImages: false,
1738
+ supportsOutputFormat: true,
1739
+ supportsResolution: false,
1740
+ supportsSafetyChecker: true,
1741
+ supportsSeed: true,
1742
+ supportsSyncMode: true,
1743
+ type: "generation"
1744
+ },
1745
+ "grok-image-quality": {
1746
+ benchmark: { useCase: "Detailed generation and editing with stronger text rendering; Arena #11 editing, 2026-10-29" },
1747
+ editEndpoint: "xai/grok-imagine-image/quality/edit",
1748
+ endpoint: "xai/grok-imagine-image/quality/text-to-image",
1749
+ falPricing: {
1750
+ checkedAt: FAL_PRICING_CHECKED_OCT_2026,
1751
+ currency: "USD",
1752
+ endpointId: "xai/grok-imagine-image/quality/text-to-image",
1753
+ estimatedCostPerImageUsd: .05,
1754
+ source: "fal-pricing-api",
1755
+ unit: "images",
1756
+ unitPrice: .05
1757
+ },
1758
+ maxReferenceImages: 3,
1759
+ name: "Grok Imagine Image Quality",
1760
+ pricePerImageUsd: .05,
1761
+ pricing: "$0.05 (1K) / $0.07 (2K)",
1762
+ sizeMode: "aspect_ratio",
1763
+ supportedAspects: [
1764
+ "16:9",
1765
+ "4:3",
1766
+ "3:2",
1767
+ "1:1",
1768
+ "2:3",
1769
+ "3:4",
1770
+ "9:16"
1771
+ ],
1772
+ supportedResolutions: ["1K", "2K"],
1773
+ supportsAspect: true,
1774
+ supportsEdit: true,
1775
+ supportsNumImages: true,
1776
+ supportsOutputFormat: true,
1777
+ supportsResolution: true,
1778
+ supportsSyncMode: true,
1779
+ type: "generation"
1780
+ },
1634
1781
  kling: {
1635
1782
  endpoint: "fal-ai/kling-video/v3/pro/image-to-video",
1636
1783
  name: "Kling v3 Pro",
@@ -1708,7 +1855,10 @@ const MODELS = {
1708
1855
  "qwen3",
1709
1856
  "banana2-lite",
1710
1857
  "grok-image-2",
1858
+ "grok-image-quality",
1711
1859
  "grok-image",
1860
+ "muse-image",
1861
+ "krea3",
1712
1862
  "flux2-flex",
1713
1863
  "flux2-pro",
1714
1864
  "flux2-turbo",
@@ -1734,6 +1884,7 @@ const OPTION_CAPABILITIES = {
1734
1884
  imageSize: (config) => config.sizeMode === "gpt_size" || config.sizeMode === "image_size_enum",
1735
1885
  inputFidelity: (config) => config.sizeMode === "gpt_size",
1736
1886
  limitGenerations: (config) => config.supportsLimitGenerations === true,
1887
+ markupImageUrl: (config) => config.supportsMarkupImage === true,
1737
1888
  maskImageUrl: (config) => config.supportsMaskImage === true,
1738
1889
  negativePrompt: (config) => config.supportsNegativePrompt === true,
1739
1890
  numImages: (config) => config.supportsNumImages,
@@ -3442,6 +3593,8 @@ const FAL_IMAGE_PRICE_USD = {
3442
3593
  "ideogram/v4": MODELS.ideogram4?.pricePerImageUsd ?? .03,
3443
3594
  "xai/grok-imagine-image": MODELS["grok-image"]?.pricePerImageUsd ?? .02,
3444
3595
  "xai/grok-imagine-image/v2.0/text-to-image": MODELS["grok-image-2"]?.pricePerImageUsd ?? .06,
3596
+ "xai/grok-imagine-image/quality/text-to-image": MODELS["grok-image-quality"]?.pricePerImageUsd ?? .05,
3597
+ "meta/muse-image/text-to-image": MODELS["muse-image"]?.pricePerImageUsd ?? .01,
3445
3598
  "microsoft/mai-image-2.5-pro": MODELS["mai-image-2.5-pro"]?.pricePerImageUsd ?? .17,
3446
3599
  "fal-ai/ideogram/v3/generate-transparent": MODELS["ideogram3-transparent"]?.pricePerImageUsd ?? .06,
3447
3600
  "alibaba/qwen-image-3/text-to-image": MODELS.qwen3?.pricePerImageUsd ?? .04,
@@ -3499,10 +3652,10 @@ const OPENAI_API_KEY_ENV = "OPENAI_API_KEY";
3499
3652
  * Sanity anchor: the Phase 0 benchmark measured gpt-image direct at $0.042
3500
3653
  * (vs $0.133 via fal — full data and methodology on Linear MOT-23).
3501
3654
  *
3502
- * GPT Image 2.5 is token-priced, with no published per-image estimate. Leave
3655
+ * GPT Image 2.6 is token-priced, with no published per-image estimate. Leave
3503
3656
  * these models absent so cost remains unknown unless supplied by the provider.
3504
- * https://developers.openai.com/api/docs/models/gpt-image-2.5-flare
3505
- * https://developers.openai.com/api/docs/models/gpt-image-2.5-sunburst
3657
+ * https://developers.openai.com/api/docs/models/gpt-image-2.6-flare
3658
+ * https://developers.openai.com/api/docs/models/gpt-image-2.6-sunburst
3506
3659
  */
3507
3660
  const OPENAI_IMAGE_PRICE_USD = { "gpt-image-1": .042 };
3508
3661
  /**