@howells/motif-sdk 7.0.0 → 7.2.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 +1 -1
- package/dist/image.d.ts +17 -22
- package/dist/image.js +19 -36
- package/dist/index.cjs +55 -72
- package/dist/index.d.cts +17 -26
- package/dist/index.d.ts +17 -26
- package/dist/index.js +55 -72
- package/dist/registry.d.ts +5 -12
- package/dist/registry.js +11 -20
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -34,7 +34,7 @@ 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 23 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.
|
|
37
|
+
The Task client curates 23 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`. Sunburst and Flare run GPT Image 2.6; Arena has not scored 2.6 yet, so their benchmarks are the GPT Image 2.5 scores, and each carries a `note` saying so. A generation with `references` uses the editing ranking.
|
|
38
38
|
|
|
39
39
|
| Tier | Generation default | Editing default |
|
|
40
40
|
| -------------------- | ------------------ | ----------------- |
|
package/dist/image.d.ts
CHANGED
|
@@ -1,11 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@howells/motif-sdk/image` — provider-agnostic image generation + editing.
|
|
3
|
+
*
|
|
4
|
+
* ESM-only subpath export, built on the Vercel AI SDK image interface
|
|
5
|
+
* (`generateImage`, `@ai-sdk/*`). The caller names the provider and model;
|
|
6
|
+
* reuses the SDK's Result convention (`Result<T, MotifError>` — no
|
|
7
|
+
* thrown exceptions). Gemini is reached through OpenRouter.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```ts
|
|
11
|
+
* import { createMotifImage } from "@howells/motif-sdk/image";
|
|
12
|
+
*
|
|
13
|
+
* const img = createMotifImage({ defaultProvider: "openrouter" });
|
|
14
|
+
* const r = await img.generate({ model: "gemini-3.1-flash-image", prompt: "a bare concrete wall" });
|
|
15
|
+
* if (r.isOk()) console.log(r.value.images[0].mediaType, r.value.cost);
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
1
18
|
import { ImageModel, generateImage } from "ai";
|
|
2
19
|
import { Result } from "neverthrow";
|
|
3
|
-
//#region src/types.d.ts
|
|
4
20
|
/** ─── Configuration ──────────────────────────────────────────── */
|
|
5
21
|
/** The network seam: a `fetch`-shaped function. */
|
|
6
22
|
type FalFetch = (url: string, init: RequestInit) => Promise<Response>;
|
|
7
|
-
//#endregion
|
|
8
|
-
//#region src/errors.d.ts
|
|
9
23
|
/**
|
|
10
24
|
* `MotifError` and its coercion helper.
|
|
11
25
|
*
|
|
@@ -24,8 +38,6 @@ declare class MotifError extends Error {
|
|
|
24
38
|
readonly details?: Record<string, unknown>;
|
|
25
39
|
constructor(message: string, status: number, code?: string, requestId?: string, details?: Record<string, unknown>);
|
|
26
40
|
}
|
|
27
|
-
//#endregion
|
|
28
|
-
//#region src/image/types.d.ts
|
|
29
41
|
/**
|
|
30
42
|
* Image provider id. All four Phase 1b adapters are implemented
|
|
31
43
|
* (`openrouter`, `openai`, `replicate`, `fal`); the type keeps an open union tail so
|
|
@@ -240,8 +252,6 @@ interface MotifImageClient {
|
|
|
240
252
|
*/
|
|
241
253
|
bestOfN: (opts: BestOfNOptions) => Promise<Result<BestOfNResult, MotifError>>;
|
|
242
254
|
}
|
|
243
|
-
//#endregion
|
|
244
|
-
//#region src/image/deps.d.ts
|
|
245
255
|
/** A model resolver: builds an AI SDK `ImageModel` for a (provider, model, key). */
|
|
246
256
|
type ResolveImageModel = (provider: ImageProviderId, modelId: string, apiKey?: string, fetch?: FalFetch) => ImageModel;
|
|
247
257
|
/** Internal dependency-injection seam (default: real `generateImage` + adapters). */
|
|
@@ -249,8 +259,6 @@ interface MotifImageDeps {
|
|
|
249
259
|
generateImage?: typeof generateImage;
|
|
250
260
|
resolveModel?: ResolveImageModel;
|
|
251
261
|
}
|
|
252
|
-
//#endregion
|
|
253
|
-
//#region src/image/provider.d.ts
|
|
254
262
|
/**
|
|
255
263
|
* A single image provider. A thin wrapper over the provider's `@ai-sdk/*` image
|
|
256
264
|
* model, plus the metadata the layer needs to resolve keys and meter spend.
|
|
@@ -292,24 +300,14 @@ export declare const PROVIDERS: Record<ImageProviderId, ImageProviderAdapter>;
|
|
|
292
300
|
* provider (callers catch this into a `Result.err`).
|
|
293
301
|
*/
|
|
294
302
|
export declare function getProviderAdapter(provider: ImageProviderId): ImageProviderAdapter;
|
|
295
|
-
//#endregion
|
|
296
|
-
//#region src/image/openrouter.d.ts
|
|
297
303
|
/** Env var read for the OpenRouter API key when `apiKey` is not supplied in config. */
|
|
298
304
|
export declare const OPENROUTER_API_KEY_ENV = "OPENROUTER_API_KEY";
|
|
299
|
-
//#endregion
|
|
300
|
-
//#region src/image/openai.d.ts
|
|
301
305
|
/** Env var read for the OpenAI API key when `apiKey` is not supplied in config. */
|
|
302
306
|
export declare const OPENAI_API_KEY_ENV = "OPENAI_API_KEY";
|
|
303
|
-
//#endregion
|
|
304
|
-
//#region src/image/replicate.d.ts
|
|
305
307
|
/** Env var read for the Replicate API token when `apiToken` is not in config. */
|
|
306
308
|
export declare const REPLICATE_API_KEY_ENV = "REPLICATE_API_TOKEN";
|
|
307
|
-
//#endregion
|
|
308
|
-
//#region src/image/fal.d.ts
|
|
309
309
|
/** Env var read for the fal key when `apiKey` is not supplied in config. */
|
|
310
310
|
export declare const FAL_API_KEY_ENV = "FAL_KEY";
|
|
311
|
-
//#endregion
|
|
312
|
-
//#region src/image/cost.d.ts
|
|
313
311
|
/**
|
|
314
312
|
* Extract a provider-reported total cost from a `generateImage` result's
|
|
315
313
|
* `providerMetadata`, if the provider surfaces a numeric `cost`. The shape is
|
|
@@ -323,8 +321,6 @@ export declare function providerPricePerImageUsd(provider: ImageProviderId, mode
|
|
|
323
321
|
* then the static table (× image count), then unknown.
|
|
324
322
|
*/
|
|
325
323
|
export declare function costForImages(provider: ImageProviderId, modelId: string, providerMetadata: unknown, imageCount: number): ImageCost;
|
|
326
|
-
//#endregion
|
|
327
|
-
//#region src/image/index.d.ts
|
|
328
324
|
/**
|
|
329
325
|
* Create a provider-agnostic image client.
|
|
330
326
|
*
|
|
@@ -333,5 +329,4 @@ export declare function costForImages(provider: ImageProviderId, modelId: string
|
|
|
333
329
|
* and the built-in provider adapters; tests inject fakes here to run offline.
|
|
334
330
|
*/
|
|
335
331
|
export declare function createMotifImage(config?: MotifImageConfig, deps?: MotifImageDeps): MotifImageClient;
|
|
336
|
-
//#endregion
|
|
337
332
|
export type { BestOfNOptions, BestOfNResult, EditImageOptions, GenerateImageOptions, ImageCost, ImageCostSource, ImageJudge, ImageProviderAdapter, ImageProviderId, MotifImageClient, MotifImageConfig, MotifImageFile, MotifImageResult };
|
package/dist/image.js
CHANGED
|
@@ -3,7 +3,7 @@ import { err, ok } from "neverthrow";
|
|
|
3
3
|
import { createFal } from "@ai-sdk/fal";
|
|
4
4
|
import { createOpenAI } from "@ai-sdk/openai";
|
|
5
5
|
import { createReplicate } from "@ai-sdk/replicate";
|
|
6
|
-
|
|
6
|
+
// src/errors.ts
|
|
7
7
|
/**
|
|
8
8
|
* `MotifError` and its coercion helper.
|
|
9
9
|
*
|
|
@@ -49,8 +49,7 @@ function falHttpError(status, body, requestId) {
|
|
|
49
49
|
if (isFalAccountLocked(status, body)) return new MotifError(`fal account is locked because it is out of credit (fal said: ${body})`, status, ACCOUNT_LOCKED, requestId);
|
|
50
50
|
return new MotifError(`Request failed: ${status} ${body}`, status, void 0, requestId);
|
|
51
51
|
}
|
|
52
|
-
|
|
53
|
-
//#region src/models.ts
|
|
52
|
+
// src/models.ts
|
|
54
53
|
const AA_IMAGE_LEADERBOARD_SNAPSHOT = "2026-10-07";
|
|
55
54
|
const AA_IMAGE_SOURCES = ["https://artificialanalysis.ai/image/leaderboard/text-to-image", "https://artificialanalysis.ai/image/leaderboard/editing"];
|
|
56
55
|
const FAL_PRICING_CHECKED_AT = "2026-05-12";
|
|
@@ -120,7 +119,7 @@ const MODELS = {
|
|
|
120
119
|
"xhigh",
|
|
121
120
|
"max"
|
|
122
121
|
],
|
|
123
|
-
supportsResolution:
|
|
122
|
+
supportsResolution: true,
|
|
124
123
|
supportsSyncMode: true,
|
|
125
124
|
type: "generation",
|
|
126
125
|
useQueue: true
|
|
@@ -187,7 +186,7 @@ const MODELS = {
|
|
|
187
186
|
"xhigh",
|
|
188
187
|
"max"
|
|
189
188
|
],
|
|
190
|
-
supportsResolution:
|
|
189
|
+
supportsResolution: true,
|
|
191
190
|
supportsSyncMode: true,
|
|
192
191
|
type: "generation",
|
|
193
192
|
useQueue: true
|
|
@@ -1219,8 +1218,7 @@ const MODELS = {
|
|
|
1219
1218
|
"ideogram3-transparent",
|
|
1220
1219
|
"recraft41"
|
|
1221
1220
|
].filter((id) => MODELS[id]?.supportsEdit === true);
|
|
1222
|
-
|
|
1223
|
-
//#region src/capabilities.ts
|
|
1221
|
+
// src/capabilities.ts
|
|
1224
1222
|
/** Generation options a model can refuse, with the registry test for each. */
|
|
1225
1223
|
const OPTION_CAPABILITIES = {
|
|
1226
1224
|
aspect: (config) => (config.sizeMode ?? "aspect_ratio") !== "none",
|
|
@@ -1377,8 +1375,7 @@ Object.keys(OPTION_CAPABILITIES).filter(isModelOption);
|
|
|
1377
1375
|
}
|
|
1378
1376
|
]
|
|
1379
1377
|
}).look;
|
|
1380
|
-
|
|
1381
|
-
//#region src/tool-registry/analysis.ts
|
|
1378
|
+
// src/tool-registry/analysis.ts
|
|
1382
1379
|
const ANALYSIS_TOOLS = {
|
|
1383
1380
|
"got-ocr": {
|
|
1384
1381
|
category: "analysis",
|
|
@@ -1492,8 +1489,7 @@ const ANALYSIS_TOOLS = {
|
|
|
1492
1489
|
task: "vision moderation"
|
|
1493
1490
|
}
|
|
1494
1491
|
};
|
|
1495
|
-
|
|
1496
|
-
//#region src/tool-registry/assets.ts
|
|
1492
|
+
// src/tool-registry/assets.ts
|
|
1497
1493
|
const ASSET_TOOLS = {
|
|
1498
1494
|
"hunyuan3d-v3": {
|
|
1499
1495
|
category: "3d",
|
|
@@ -1811,8 +1807,7 @@ const ASSET_TOOLS = {
|
|
|
1811
1807
|
task: "single-image 3D reconstruction"
|
|
1812
1808
|
}
|
|
1813
1809
|
};
|
|
1814
|
-
|
|
1815
|
-
//#region src/tool-registry/background.ts
|
|
1810
|
+
// src/tool-registry/background.ts
|
|
1816
1811
|
const BACKGROUND_TOOLS = {
|
|
1817
1812
|
"ben-v2": {
|
|
1818
1813
|
category: "background",
|
|
@@ -2187,8 +2182,7 @@ const BACKGROUND_TOOLS = {
|
|
|
2187
2182
|
task: "video enhancement"
|
|
2188
2183
|
}
|
|
2189
2184
|
};
|
|
2190
|
-
|
|
2191
|
-
//#region src/tool-registry/editing.ts
|
|
2185
|
+
// src/tool-registry/editing.ts
|
|
2192
2186
|
const EDITING_TOOLS = {
|
|
2193
2187
|
"bria-eraser": {
|
|
2194
2188
|
category: "erase",
|
|
@@ -2459,8 +2453,7 @@ const EDITING_TOOLS = {
|
|
|
2459
2453
|
task: "virtual garment try-on"
|
|
2460
2454
|
}
|
|
2461
2455
|
};
|
|
2462
|
-
|
|
2463
|
-
//#region src/tool-registry/structure.ts
|
|
2456
|
+
// src/tool-registry/structure.ts
|
|
2464
2457
|
const STRUCTURE_TOOLS = {
|
|
2465
2458
|
"depth-anything": {
|
|
2466
2459
|
category: "preprocess",
|
|
@@ -2852,8 +2845,7 @@ const STRUCTURE_TOOLS = {
|
|
|
2852
2845
|
task: "depth preprocessing"
|
|
2853
2846
|
}
|
|
2854
2847
|
};
|
|
2855
|
-
|
|
2856
|
-
//#region src/tools.ts
|
|
2848
|
+
// src/tools.ts
|
|
2857
2849
|
const FAL_TOOLS = {
|
|
2858
2850
|
...ANALYSIS_TOOLS,
|
|
2859
2851
|
...ASSET_TOOLS,
|
|
@@ -2862,8 +2854,7 @@ const FAL_TOOLS = {
|
|
|
2862
2854
|
...STRUCTURE_TOOLS
|
|
2863
2855
|
};
|
|
2864
2856
|
Object.keys(FAL_TOOLS);
|
|
2865
|
-
|
|
2866
|
-
//#region src/image/fetch.ts
|
|
2857
|
+
// src/image/fetch.ts
|
|
2867
2858
|
function urlOf(input) {
|
|
2868
2859
|
if (typeof input === "string") return input;
|
|
2869
2860
|
return input instanceof URL ? input.href : input.url;
|
|
@@ -2873,8 +2864,7 @@ function toProviderFetch(fetch) {
|
|
|
2873
2864
|
if (fetch === void 0) return {};
|
|
2874
2865
|
return { fetch: async (input, init) => await fetch(urlOf(input), init ?? {}) };
|
|
2875
2866
|
}
|
|
2876
|
-
|
|
2877
|
-
//#region src/image/fal.ts
|
|
2867
|
+
// src/image/fal.ts
|
|
2878
2868
|
/**
|
|
2879
2869
|
* fal provider adapter.
|
|
2880
2870
|
*
|
|
@@ -2960,8 +2950,7 @@ const falAdapter = {
|
|
|
2960
2950
|
resolveModel: resolveModel$3,
|
|
2961
2951
|
priceUsdByModel: FAL_IMAGE_PRICE_USD
|
|
2962
2952
|
};
|
|
2963
|
-
|
|
2964
|
-
//#region src/image/openai.ts
|
|
2953
|
+
// src/image/openai.ts
|
|
2965
2954
|
/**
|
|
2966
2955
|
* OpenAI (gpt-image) provider adapter.
|
|
2967
2956
|
*
|
|
@@ -3007,8 +2996,7 @@ const openaiAdapter = {
|
|
|
3007
2996
|
resolveModel: resolveModel$2,
|
|
3008
2997
|
priceUsdByModel: OPENAI_IMAGE_PRICE_USD
|
|
3009
2998
|
};
|
|
3010
|
-
|
|
3011
|
-
//#region src/image/openrouter.ts
|
|
2999
|
+
// src/image/openrouter.ts
|
|
3012
3000
|
/** Env var read for the OpenRouter API key when `apiKey` is not supplied in config. */
|
|
3013
3001
|
const OPENROUTER_API_KEY_ENV = "OPENROUTER_API_KEY";
|
|
3014
3002
|
const OPENROUTER_IMAGES_URL = "https://openrouter.ai/api/v1/images";
|
|
@@ -3171,8 +3159,7 @@ const openrouterAdapter = {
|
|
|
3171
3159
|
resolveModel: resolveModel$1,
|
|
3172
3160
|
priceUsdByModel: OPENROUTER_IMAGE_PRICE_USD
|
|
3173
3161
|
};
|
|
3174
|
-
|
|
3175
|
-
//#region src/image/replicate.ts
|
|
3162
|
+
// src/image/replicate.ts
|
|
3176
3163
|
/**
|
|
3177
3164
|
* Replicate provider adapter.
|
|
3178
3165
|
*
|
|
@@ -3207,8 +3194,7 @@ function resolveModel(modelId, apiKey, fetch) {
|
|
|
3207
3194
|
...toProviderFetch(fetch)
|
|
3208
3195
|
}).image(modelId);
|
|
3209
3196
|
}
|
|
3210
|
-
|
|
3211
|
-
//#region src/image/provider.ts
|
|
3197
|
+
// src/image/provider.ts
|
|
3212
3198
|
/**
|
|
3213
3199
|
* The provider registry. Every id in {@link ImageProviderId}'s closed part maps
|
|
3214
3200
|
* to its adapter; the open union tail means a lookup can still miss, so access
|
|
@@ -3234,8 +3220,7 @@ function getProviderAdapter(provider) {
|
|
|
3234
3220
|
if (adapter === void 0) throw new MotifError(`Unsupported image provider: ${provider}`, 0);
|
|
3235
3221
|
return adapter;
|
|
3236
3222
|
}
|
|
3237
|
-
|
|
3238
|
-
//#region src/image/cost.ts
|
|
3223
|
+
// src/image/cost.ts
|
|
3239
3224
|
/**
|
|
3240
3225
|
* Per-call cost tracking for the image layer.
|
|
3241
3226
|
*
|
|
@@ -3305,8 +3290,7 @@ function costForCalls(provider, modelId, callMetadata, imageCount) {
|
|
|
3305
3290
|
source: "unknown"
|
|
3306
3291
|
};
|
|
3307
3292
|
}
|
|
3308
|
-
|
|
3309
|
-
//#region src/image/index.ts
|
|
3293
|
+
// src/image/index.ts
|
|
3310
3294
|
/**
|
|
3311
3295
|
* `@howells/motif-sdk/image` — provider-agnostic image generation + editing.
|
|
3312
3296
|
*
|
|
@@ -3578,5 +3562,4 @@ function toMotifError(error) {
|
|
|
3578
3562
|
if (isFalAccountLocked(status, body)) return falHttpError(status, body);
|
|
3579
3563
|
return new MotifError(message, status, code);
|
|
3580
3564
|
}
|
|
3581
|
-
//#endregion
|
|
3582
3565
|
export { FAL_API_KEY_ENV, OPENAI_API_KEY_ENV, OPENROUTER_API_KEY_ENV, PROVIDERS, REPLICATE_API_KEY_ENV, costForImages, costFromProviderMetadata, createMotifImage, getProviderAdapter, providerPricePerImageUsd };
|