@chai-ui/core 0.5.0 → 0.6.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
@@ -26,9 +26,35 @@ binding would depend on this package the same way.
26
26
  - `engines/fal.ts` — `createFalEngine`, media generation through Fal.ai (the default).
27
27
  - `engines/openrouter.ts` — `createOpenRouterEngine`, text/reasoning through OpenRouter (prompt enhancement, media analysis).
28
28
  - `media-analysis-prompts.ts` — the instruction text `MediaAnalyzer` sends, per media kind × prompt length.
29
+ - `suggested-models.ts` — the suggested model lists per use case (Fal for generation, OpenRouter for analysis), each model with its verified aspect ratios and Fal input format; `suggestedModels(useCase)`.
30
+ - `check-suggested-models.ts` — `checkSuggestedModels()`: checks those lists against Fal's and OpenRouter's catalogs as they are today. No key, no model run.
29
31
  - `model-routing.ts` — auto-select: `buildModelRoutingPrompt`, `parseModelRoutingReply`, and `routeModel`, which falls back to the first listed model instead of throwing.
30
32
  - `server/` — `createChaiHandler` and `toNodeHandler`, exported from `@chai-ui/core/server` (server-only).
31
33
 
34
+ ## Suggested models, and checking them
35
+
36
+ The components fill their Model menus from the suggested lists when a
37
+ builder passes none (see `@chai-ui/react`'s README). Each Fal model in them
38
+ carries `falInput`, read from its Fal schema: where the image goes
39
+ (`image_url`, `image_urls` or `start_image_url`) and how the aspect ratio
40
+ is sent (`aspect_ratio`, or a named `image_size`). `buildFalRequestBody`
41
+ uses it; for a model outside the lists it falls back to guessing from the
42
+ id.
43
+
44
+ The lists change only through package releases. `checkSuggestedModels()`
45
+ compares them with the providers right now, from a browser or Node, with
46
+ no key:
47
+
48
+ ```ts
49
+ const { ok, checked, problems } = await checkSuggestedModels();
50
+ ```
51
+
52
+ It reports a model that's gone or no longer active, a Fal model that no
53
+ longer takes an aspect ratio or image field Chai sends or now requires a
54
+ field Chai doesn't send, and an OpenRouter model that no longer accepts its
55
+ media kind. Fal's catalog is read 10 models a page (`api.fal.ai/v1/models`),
56
+ OpenRouter's from `openrouter.ai/api/v1/models`.
57
+
32
58
  ## The server route: `@chai-ui/core/server`
33
59
 
34
60
  The built-in engines never hold an API key. They call one route on your
@@ -0,0 +1,31 @@
1
+ /** One way a provider no longer matches a suggested model. */
2
+ export interface SuggestedModelProblem {
3
+ provider: "fal" | "openrouter";
4
+ /** The model's id, or `"*"` when the provider couldn't be reached at all. */
5
+ modelId: string;
6
+ problem: string;
7
+ }
8
+ export interface SuggestedModelsCheck {
9
+ /** True when every suggested model still matches its provider. */
10
+ ok: boolean;
11
+ /** How many models were checked. */
12
+ checked: number;
13
+ problems: SuggestedModelProblem[];
14
+ }
15
+ /**
16
+ * Checks the suggested model lists against Fal and OpenRouter as they are
17
+ * today: each model still exists and is active; for Fal, it still takes
18
+ * the aspect ratios and image field Chai sends, and needs no other field
19
+ * Chai doesn't send; for OpenRouter, it still accepts the media kind it's
20
+ * listed for. Run it while setting up your engine. It needs no API key,
21
+ * runs no model and costs nothing; both catalogs are public and allow
22
+ * calls from a browser.
23
+ *
24
+ * The lists themselves only change through package releases. A problem
25
+ * here means a provider changed since: pick another model, or update the
26
+ * package once a release catches up.
27
+ */
28
+ export declare function checkSuggestedModels(options?: {
29
+ fetchImpl?: typeof fetch;
30
+ }): Promise<SuggestedModelsCheck>;
31
+ //# sourceMappingURL=check-suggested-models.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check-suggested-models.d.ts","sourceRoot":"","sources":["../src/check-suggested-models.ts"],"names":[],"mappings":"AAIA,8DAA8D;AAC9D,MAAM,WAAW,qBAAqB;IACpC,QAAQ,EAAE,KAAK,GAAG,YAAY,CAAC;IAC/B,6EAA6E;IAC7E,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,oBAAoB;IACnC,kEAAkE;IAClE,EAAE,EAAE,OAAO,CAAC;IACZ,oCAAoC;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,qBAAqB,EAAE,CAAC;CACnC;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,oBAAoB,CAAC,OAAO,GAAE;IAAE,SAAS,CAAC,EAAE,OAAO,KAAK,CAAA;CAAO,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAQpH"}
@@ -0,0 +1,123 @@
1
+ import { SUGGESTED_ANALYSIS_MODELS, SUGGESTED_FAL_MODELS } from "./suggested-models.js";
2
+ import { FAL_NAMED_IMAGE_SIZES } from "./engines/fal.js";
3
+ /**
4
+ * Checks the suggested model lists against Fal and OpenRouter as they are
5
+ * today: each model still exists and is active; for Fal, it still takes
6
+ * the aspect ratios and image field Chai sends, and needs no other field
7
+ * Chai doesn't send; for OpenRouter, it still accepts the media kind it's
8
+ * listed for. Run it while setting up your engine. It needs no API key,
9
+ * runs no model and costs nothing; both catalogs are public and allow
10
+ * calls from a browser.
11
+ *
12
+ * The lists themselves only change through package releases. A problem
13
+ * here means a provider changed since: pick another model, or update the
14
+ * package once a release catches up.
15
+ */
16
+ export async function checkSuggestedModels(options = {}) {
17
+ const fetchImpl = (input, init) => (options.fetchImpl ?? fetch)(input, init);
18
+ const fal = [...SUGGESTED_FAL_MODELS.values()];
19
+ const analysis = Object.entries(SUGGESTED_ANALYSIS_MODELS).flatMap(([kind, models]) => (models ?? []).map((model) => ({ kind: kind, model })));
20
+ const problems = [...(await checkFal(fal, fetchImpl)), ...(await checkOpenRouter(analysis, fetchImpl))];
21
+ return { ok: problems.length === 0, checked: fal.length + analysis.length, problems };
22
+ }
23
+ async function checkFal(models, fetchImpl) {
24
+ const query = models.map((m) => `endpoint_id=${encodeURIComponent(m.id)}`).join("&");
25
+ let found;
26
+ try {
27
+ // Fal answers 10 models a page, with a cursor for the next.
28
+ found = [];
29
+ let cursor;
30
+ do {
31
+ const res = await fetchImpl(`https://api.fal.ai/v1/models?expand=openapi-3.0&${query}${cursor ? `&cursor=${encodeURIComponent(cursor)}` : ""}`);
32
+ if (!res.ok)
33
+ throw new Error(`${res.status} ${res.statusText}`);
34
+ const page = (await res.json());
35
+ found.push(...(page.models ?? []));
36
+ cursor = page.has_more ? page.next_cursor : undefined;
37
+ } while (cursor);
38
+ }
39
+ catch (err) {
40
+ return [{ provider: "fal", modelId: "*", problem: `Couldn't reach Fal's model catalog: ${err.message}` }];
41
+ }
42
+ const problems = [];
43
+ const add = (modelId, problem) => problems.push({ provider: "fal", modelId, problem });
44
+ for (const model of models) {
45
+ const entry = found.find((f) => f.endpoint_id === model.id);
46
+ if (!entry) {
47
+ add(model.id, "Not in Fal's catalog any more.");
48
+ continue;
49
+ }
50
+ if (entry.metadata?.status && entry.metadata.status !== "active")
51
+ add(model.id, `Fal lists it as "${entry.metadata.status}".`);
52
+ const input = inputSchema(entry.openapi);
53
+ if (!input)
54
+ continue;
55
+ const props = input.schema.properties ?? {};
56
+ const imageField = model.falInput?.image;
57
+ if (imageField && !props[imageField])
58
+ add(model.id, `No longer takes "${imageField}" for the image.`);
59
+ const ratioField = model.falInput?.aspectRatio;
60
+ if (model.aspectRatios && ratioField) {
61
+ const accepted = enumOf(props[ratioField], input.resolve);
62
+ if (!props[ratioField])
63
+ add(model.id, `No longer takes "${ratioField}".`);
64
+ else if (accepted) {
65
+ const wanted = ratioField === "image_size" ? model.aspectRatios.map((r) => FAL_NAMED_IMAGE_SIZES[r] ?? r) : model.aspectRatios;
66
+ const missing = wanted.filter((v) => !accepted.includes(v));
67
+ if (missing.length > 0)
68
+ add(model.id, `No longer takes these ${ratioField} values: ${missing.join(", ")}.`);
69
+ }
70
+ }
71
+ const sent = new Set(["prompt", imageField ?? "", ratioField ?? ""]);
72
+ for (const name of input.schema.required ?? []) {
73
+ if (!sent.has(name) && props[name]?.default === undefined)
74
+ add(model.id, `Now requires "${name}", which Chai doesn't send.`);
75
+ }
76
+ }
77
+ return problems;
78
+ }
79
+ async function checkOpenRouter(models, fetchImpl) {
80
+ let catalog;
81
+ try {
82
+ const res = await fetchImpl("https://openrouter.ai/api/v1/models");
83
+ if (!res.ok)
84
+ throw new Error(`${res.status} ${res.statusText}`);
85
+ catalog = (await res.json()).data ?? [];
86
+ }
87
+ catch (err) {
88
+ return [{ provider: "openrouter", modelId: "*", problem: `Couldn't reach OpenRouter's model list: ${err.message}` }];
89
+ }
90
+ const problems = [];
91
+ for (const { kind, model } of models) {
92
+ const entry = catalog.find((c) => c.id === model.id);
93
+ if (!entry)
94
+ problems.push({ provider: "openrouter", modelId: model.id, problem: "Not in OpenRouter's catalog any more." });
95
+ else if (!entry.architecture?.input_modalities?.includes(kind))
96
+ problems.push({ provider: "openrouter", modelId: model.id, problem: `No longer accepts ${kind}.` });
97
+ }
98
+ return problems;
99
+ }
100
+ /** The model's request schema, from its OpenAPI document, with a resolver for `$ref`s. */
101
+ function inputSchema(openapi) {
102
+ const schemas = openapi?.components?.schemas ?? {};
103
+ const resolve = (s) => (s.$ref ? (schemas[s.$ref.split("/").pop()] ?? s) : s);
104
+ for (const path of Object.values(openapi?.paths ?? {})) {
105
+ const body = path.post?.requestBody?.content?.["application/json"]?.schema;
106
+ if (body)
107
+ return { schema: resolve(body), resolve };
108
+ }
109
+ return null;
110
+ }
111
+ /** The allowed values of a field, wherever its schema keeps them. */
112
+ function enumOf(field, resolve) {
113
+ if (!field)
114
+ return null;
115
+ for (const s of [field, ...(field.anyOf ?? []), ...(field.allOf ?? [])].map(resolve)) {
116
+ if (s.enum)
117
+ return s.enum;
118
+ if (s.const !== undefined)
119
+ return [s.const];
120
+ }
121
+ return null;
122
+ }
123
+ //# sourceMappingURL=check-suggested-models.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check-suggested-models.js","sourceRoot":"","sources":["../src/check-suggested-models.ts"],"names":[],"mappings":"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"}
@@ -59,11 +59,13 @@ export declare function describeFalError(json: {
59
59
  error?: unknown;
60
60
  detail?: unknown;
61
61
  }, fallback: string): string;
62
+ /** Fal's named `image_size` values for the ratios they cover (FLUX.1, GPT Image 2). */
63
+ export declare const FAL_NAMED_IMAGE_SIZES: Record<string, string>;
62
64
  /**
63
- * Builds the one Fal request body CHAI sends: the prompt, plus the attached
64
- * images. Most models take one `image_url` (for image-to-video and
65
- * edit-shaped models); the models in `IMAGE_LIST_MODELS` take every image
66
- * as `image_urls`.
65
+ * Builds the one Fal request body CHAI sends: the prompt, the aspect ratio
66
+ * and the attached images, each in the field the model expects. A
67
+ * suggested model says which (`ModelOption.falInput`, from its Fal
68
+ * schema); for any other model the field is guessed from its id.
67
69
  */
68
70
  export declare function buildFalRequestBody(args: {
69
71
  prompt: string;
@@ -1 +1 @@
1
- {"version":3,"file":"fal.d.ts","sourceRoot":"","sources":["../../src/engines/fal.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAA6D,KAAK,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAChH,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE3D,kHAAkH;AAClH,KAAK,aAAa,GAAG,OAAO,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;AAEhD,MAAM,WAAW,gBAAgB;IAC/B,uGAAuG;IACvG,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gHAAgH;IAChH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,8HAA8H;IAC9H,WAAW,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC,CAAC;IACrD,8EAA8E;IAC9E,UAAU,CAAC,EAAE,aAAa,CAAC;IAC3B,2EAA2E;IAC3E,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,mHAAmH;IACnH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,4DAA4D;IAC5D,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CAC1B;AA6BD,UAAU,iBAAiB;IACzB,MAAM,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC3B,KAAK,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;IACxB,KAAK,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAGD,2KAA2K;AAC3K,wBAAgB,gBAAgB,CAAC,IAAI,EAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAStG;AAyBD;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE;IACxC,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,YAAY,EAAE,CAAC;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAsB1B;AAED,iIAAiI;AACjI,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,iBAAiB,EAAE,IAAI,EAAE,aAAa,GAAG,MAAM,GAAG,SAAS,CAIpG;AA0DD;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,OAAO,GAAE,gBAAqB,GAAG,gBAAgB,CAyDhF;AAED;;;;;;GAMG;AACH,wBAAsB,OAAO,CAC3B,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,OAAO,KAAK,CAAA;CAAO,GAC5E,OAAO,CAAC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CA4BnE"}
1
+ {"version":3,"file":"fal.d.ts","sourceRoot":"","sources":["../../src/engines/fal.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAA6D,KAAK,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAChH,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAG3D,kHAAkH;AAClH,KAAK,aAAa,GAAG,OAAO,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;AAEhD,MAAM,WAAW,gBAAgB;IAC/B,uGAAuG;IACvG,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gHAAgH;IAChH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,8HAA8H;IAC9H,WAAW,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC,CAAC;IACrD,8EAA8E;IAC9E,UAAU,CAAC,EAAE,aAAa,CAAC;IAC3B,2EAA2E;IAC3E,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,mHAAmH;IACnH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,4DAA4D;IAC5D,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CAC1B;AA6BD,UAAU,iBAAiB;IACzB,MAAM,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC3B,KAAK,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;IACxB,KAAK,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAGD,2KAA2K;AAC3K,wBAAgB,gBAAgB,CAAC,IAAI,EAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAStG;AASD,uFAAuF;AACvF,eAAO,MAAM,qBAAqB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAMxD,CAAC;AASF;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE;IACxC,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,YAAY,EAAE,CAAC;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAsB1B;AAED,iIAAiI;AACjI,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,iBAAiB,EAAE,IAAI,EAAE,aAAa,GAAG,MAAM,GAAG,SAAS,CAIpG;AA0DD;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,OAAO,GAAE,gBAAqB,GAAG,gBAAgB,CAyDhF;AAED;;;;;;GAMG;AACH,wBAAsB,OAAO,CAC3B,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,OAAO,KAAK,CAAA;CAAO,GAC5E,OAAO,CAAC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CA4BnE"}
@@ -21,6 +21,7 @@
21
21
  * key into this file would put it in the browser bundle; don't add one.
22
22
  */
23
23
  import { abortError, abortableSleep, isAbortError, readEvaluations } from "../engine.js";
24
+ import { SUGGESTED_FAL_MODELS } from "../suggested-models.js";
24
25
  /**
25
26
  * Fal model ids verified live against fal.ai's own docs/catalog
26
27
  * 2026-09-24, not invented. No default for "audio": CHAI has no audio
@@ -48,40 +49,39 @@ export function describeFalError(json, fallback) {
48
49
  }
49
50
  }
50
51
  /**
51
- * Fal models that take their input images as an `image_urls` list (the
52
- * image being edited first) instead of a single `image_url`. Verified
53
- * against Fal's catalog 2026-10-03.
52
+ * Fal models outside the suggested lists that take their input images as an
53
+ * `image_urls` list (the image being edited first). Suggested models say
54
+ * so themselves (`ModelOption.falInput`).
54
55
  */
55
56
  const IMAGE_LIST_MODELS = new Set(["blackforestlabs/flux-3/edit-image"]);
56
- /** FLUX's `image_size` names for the ratios it takes (fal.ai/models/fal-ai/flux/schnell/api). */
57
- const FLUX_IMAGE_SIZES = {
57
+ /** Fal's named `image_size` values for the ratios they cover (FLUX.1, GPT Image 2). */
58
+ export const FAL_NAMED_IMAGE_SIZES = {
58
59
  "1:1": "square_hd",
59
60
  "4:3": "landscape_4_3",
60
61
  "16:9": "landscape_16_9",
61
62
  "3:4": "portrait_4_3",
62
63
  "9:16": "portrait_16_9",
63
64
  };
64
- /** GPT Image's `image_size` pixel sizes for the ratios it takes (fal.ai/models/fal-ai/gpt-image-1/text-to-image/api). */
65
- const GPT_IMAGE_SIZES = {
65
+ /** GPT Image 1's `image_size` pixel sizes for the ratios it takes (fal.ai/models/fal-ai/gpt-image-1/text-to-image/api). */
66
+ const GPT_IMAGE_1_SIZES = {
66
67
  "1:1": "1024x1024",
67
68
  "3:2": "1536x1024",
68
69
  "2:3": "1024x1536",
69
70
  };
70
71
  /**
71
- * Builds the one Fal request body CHAI sends: the prompt, plus the attached
72
- * images. Most models take one `image_url` (for image-to-video and
73
- * edit-shaped models); the models in `IMAGE_LIST_MODELS` take every image
74
- * as `image_urls`.
72
+ * Builds the one Fal request body CHAI sends: the prompt, the aspect ratio
73
+ * and the attached images, each in the field the model expects. A
74
+ * suggested model says which (`ModelOption.falInput`, from its Fal
75
+ * schema); for any other model the field is guessed from its id.
75
76
  */
76
77
  export function buildFalRequestBody(args) {
77
78
  const body = { prompt: args.prompt };
79
+ const known = args.model ? SUGGESTED_FAL_MODELS.get(args.model)?.falInput : undefined;
78
80
  if (args.aspectRatio) {
79
- // FLUX takes a named size and GPT Image a pixel size; the others take
80
- // the ratio itself.
81
- const sizes = args.model?.startsWith("fal-ai/flux/")
82
- ? FLUX_IMAGE_SIZES
83
- : args.model?.startsWith("fal-ai/gpt-image")
84
- ? GPT_IMAGE_SIZES
81
+ const sizes = args.model?.startsWith("fal-ai/gpt-image")
82
+ ? GPT_IMAGE_1_SIZES
83
+ : (known?.aspectRatio ?? (args.model?.startsWith("fal-ai/flux/") ? "image_size" : "aspect_ratio")) === "image_size"
84
+ ? FAL_NAMED_IMAGE_SIZES
85
85
  : null;
86
86
  if (sizes) {
87
87
  const size = sizes[args.aspectRatio];
@@ -95,10 +95,11 @@ export function buildFalRequestBody(args) {
95
95
  const images = (args.attachments ?? []).filter((a) => a.kind === "image").map((a) => a.src);
96
96
  if (images.length === 0)
97
97
  return body;
98
- if (args.model && IMAGE_LIST_MODELS.has(args.model))
98
+ const field = known?.image ?? (args.model && IMAGE_LIST_MODELS.has(args.model) ? "image_urls" : "image_url");
99
+ if (field === "image_urls")
99
100
  body.image_urls = images;
100
101
  else
101
- body.image_url = images[0];
102
+ body[field] = images[0];
102
103
  return body;
103
104
  }
104
105
  /** Pulls the generated media URL out of Fal's result JSON for the given output kind — the response shape differs by modality. */
@@ -1 +1 @@
1
- {"version":3,"file":"fal.js","sourceRoot":"","sources":["../../src/engines/fal.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,YAAY,EAAE,eAAe,EAAyB,MAAM,cAAc,CAAC;AAuBhH;;;;;GAKG;AACH,MAAM,qBAAqB,GAA2C;IACpE,KAAK,EAAE,qBAAqB;IAC5B,KAAK,EAAE,8BAA8B;CACtC,CAAC;AAEF,MAAM,wBAAwB,GAAG,IAAI,CAAC;AACtC,MAAM,kBAAkB,GAAG,OAAO,CAAC;AAyBnC,2KAA2K;AAC3K,MAAM,UAAU,gBAAgB,CAAC,IAA2C,EAAE,QAAgB;IAC5F,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,MAAM,CAAC;IACtC,IAAI,CAAC,GAAG;QAAE,OAAO,QAAQ,CAAC;IAC1B,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,GAAG,CAAC;IACxC,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;IAC7B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,QAAQ,CAAC;IAClB,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,iBAAiB,GAAG,IAAI,GAAG,CAAC,CAAC,mCAAmC,CAAC,CAAC,CAAC;AAEzE,iGAAiG;AACjG,MAAM,gBAAgB,GAA2B;IAC/C,KAAK,EAAE,WAAW;IAClB,KAAK,EAAE,eAAe;IACtB,MAAM,EAAE,gBAAgB;IACxB,KAAK,EAAE,cAAc;IACrB,MAAM,EAAE,eAAe;CACxB,CAAC;AAEF,yHAAyH;AACzH,MAAM,eAAe,GAA2B;IAC9C,KAAK,EAAE,WAAW;IAClB,KAAK,EAAE,WAAW;IAClB,KAAK,EAAE,WAAW;CACnB,CAAC;AAEF;;;;;GAKG;AACH,MAAM,UAAU,mBAAmB,CAAC,IAKnC;IACC,MAAM,IAAI,GAA4B,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC;IAC9D,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;QACrB,sEAAsE;QACtE,oBAAoB;QACpB,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,EAAE,UAAU,CAAC,cAAc,CAAC;YAClD,CAAC,CAAC,gBAAgB;YAClB,CAAC,CAAC,IAAI,CAAC,KAAK,EAAE,UAAU,CAAC,kBAAkB,CAAC;gBAC1C,CAAC,CAAC,eAAe;gBACjB,CAAC,CAAC,IAAI,CAAC;QACX,IAAI,KAAK,EAAE,CAAC;YACV,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;YACrC,IAAI,IAAI;gBAAE,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;QACnC,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,WAAW,CAAC;QACvC,CAAC;IACH,CAAC;IACD,MAAM,MAAM,GAAG,CAAC,IAAI,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IAC5F,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACrC,IAAI,IAAI,CAAC,KAAK,IAAI,iBAAiB,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC;QAAE,IAAI,CAAC,UAAU,GAAG,MAAM,CAAC;;QACzE,IAAI,CAAC,SAAS,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;IAChC,OAAO,IAAI,CAAC;AACd,CAAC;AAED,iIAAiI;AACjI,MAAM,UAAU,mBAAmB,CAAC,IAAuB,EAAE,IAAmB;IAC9E,IAAI,IAAI,KAAK,OAAO;QAAE,OAAO,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC;IACnD,IAAI,IAAI,KAAK,OAAO;QAAE,OAAO,IAAI,CAAC,KAAK,EAAE,GAAG,CAAC;IAC7C,OAAO,IAAI,CAAC,KAAK,EAAE,GAAG,IAAI,IAAI,CAAC,SAAS,CAAC;AAC3C,CAAC;AAED,KAAK,UAAU,YAAY,CAAC,IAK3B;IACC,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,GAAG,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,KAAK,EAAE,EAAE;QACjE,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;QAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC;KAChC,CAAC,CAAC;IACH,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAsB,CAAC;IACvE,IAAI,CAAC,GAAG,CAAC,EAAE,IAAI,IAAI,CAAC,KAAK;QAAE,MAAM,IAAI,KAAK,CAAC,gBAAgB,CAAC,IAAI,EAAE,GAAG,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC;IACtG,IAAI,CAAC,IAAI,CAAC,UAAU;QAAE,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAC;IAC3E,OAAO,EAAE,SAAS,EAAE,IAAI,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,UAAU,EAAE,CAAC;AAC3E,CAAC;AAED,6FAA6F;AAC7F,KAAK,UAAU,WAAW,CAAC,IAS1B;IACC,IAAI,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;IACzB,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC;IAC7C,OAAO,MAAM,KAAK,WAAW,EAAE,CAAC;QAC9B,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,QAAQ,EAAE,CAAC;YAC1B,MAAM,IAAI,KAAK,CAAC,mBAAmB,IAAI,CAAC,SAAS,oCAAoC,CAAC,CAAC;QACzF,CAAC;QACD,MAAM,cAAc,CAAC,IAAI,CAAC,cAAc,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;QACvD,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,GAAG,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,KAAK,aAAa,IAAI,CAAC,SAAS,SAAS,CAAC,CAAC;QACrG,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAsB,CAAC;QACvE,IAAI,CAAC,GAAG,CAAC,EAAE,IAAI,IAAI,CAAC,KAAK;YAAE,MAAM,IAAI,KAAK,CAAC,gBAAgB,CAAC,IAAI,EAAE,GAAG,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC;QACtG,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC;IACjC,CAAC;IACD,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,GAAG,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,KAAK,aAAa,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC;IAC9F,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAsB,CAAC;IACvE,IAAI,CAAC,GAAG,CAAC,EAAE,IAAI,IAAI,CAAC,KAAK;QAAE,MAAM,IAAI,KAAK,CAAC,gBAAgB,CAAC,IAAI,EAAE,GAAG,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC;IACtG,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;GAKG;AACH,KAAK,UAAU,YAAY,CAAC,IAAqF;IAC/G,MAAM,IAAI,CAAC,SAAS,CAAC,GAAG,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,KAAK,aAAa,IAAI,CAAC,SAAS,SAAS,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;AAC9H,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAAC,UAA4B,EAAE;IAC5D,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,eAAe,CAAC;IACrD,2EAA2E;IAC3E,iEAAiE;IACjE,MAAM,SAAS,GAAiB,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,SAAS,IAAI,KAAK,CAAC,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IAC3F,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,OAAO,CAAC;IACjD,MAAM,cAAc,GAAG,OAAO,CAAC,cAAc,IAAI,wBAAwB,CAAC;IAC1E,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,kBAAkB,CAAC;IAE1D,OAAO;QACL,KAAK,CAAC,QAAQ,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,EAAE;YAClE,MAAM,KAAK,GACT,OAAO,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC,UAAU,CAAC,IAAI,OAAO,CAAC,KAAK,IAAI,qBAAqB,CAAC,UAAU,CAAC,CAAC;YACrG,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,MAAM,IAAI,KAAK,CACb,kDAAkD,UAAU,uCAAuC,UAAU,KAAK,CACnH,CAAC;YACJ,CAAC;YAED,MAAM,IAAI,GAAG,mBAAmB,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC,CAAC;YAC9E,IAAI,MAAM,EAAE,OAAO;gBAAE,MAAM,UAAU,EAAE,CAAC;YAExC,IAAI,CAAC;gBACH,qEAAqE;gBACrE,uCAAuC;gBACvC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,MAAM,YAAY,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC;gBACvF,IAAI,MAAM,EAAE,OAAO,EAAE,CAAC;oBACpB,MAAM,YAAY,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,CAAC,CAAC;oBAC9D,MAAM,UAAU,EAAE,CAAC;gBACrB,CAAC;gBACD,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC;oBAC/B,QAAQ;oBACR,KAAK;oBACL,SAAS;oBACT,MAAM;oBACN,SAAS;oBACT,cAAc;oBACd,SAAS;oBACT,MAAM;iBACP,CAAC,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;oBACrB,IAAI,YAAY,CAAC,GAAG,CAAC;wBAAE,MAAM,YAAY,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,CAAC,CAAC;oBACrF,MAAM,GAAG,CAAC;gBACZ,CAAC,CAAC,CAAC;gBACH,MAAM,GAAG,GAAG,mBAAmB,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;gBACpD,IAAI,CAAC,GAAG;oBAAE,MAAM,IAAI,KAAK,CAAC,MAAM,UAAU,2BAA2B,CAAC,CAAC;gBACvE,gEAAgE;gBAChE,mEAAmE;gBACnE,+DAA+D;gBAC/D,+CAA+C;gBAC/C,sDAAsD;gBACtD,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,UAAU,EAAE,WAAW,EAAE,eAAe,CAAC,MAAM,CAAC,EAAE,CAAC;YACzE,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,IAAI,YAAY,CAAC,GAAG,CAAC;oBAAE,MAAM,GAAG,CAAC;gBACjC,MAAM,IAAI,KAAK,CAAC,cAAc,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YACpF,CAAC;QACH,CAAC;KACF,CAAC;AACJ,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,OAAO,CAC3B,UAA2E,EAAE;IAE7E,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,eAAe,CAAC;IACrD,2EAA2E;IAC3E,iEAAiE;IACjE,MAAM,SAAS,GAAiB,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,SAAS,IAAI,KAAK,CAAC,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IAC3F,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,qBAAqB,CAAC,KAAM,CAAC;IAC5D,IAAI,CAAC;QACH,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,MAAM,YAAY,CAAC;YAC/C,QAAQ;YACR,KAAK;YACL,IAAI,EAAE,mBAAmB,CAAC,EAAE,MAAM,EAAE,2CAA2C,EAAE,CAAC;YAClF,SAAS;SACV,CAAC,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC;YAC/B,QAAQ;YACR,KAAK;YACL,SAAS;YACT,MAAM;YACN,SAAS;YACT,cAAc,EAAE,IAAI;YACpB,SAAS,EAAE,MAAM;SAClB,CAAC,CAAC;QACH,MAAM,GAAG,GAAG,mBAAmB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QACjD,IAAI,CAAC,GAAG;YAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,mCAAmC,EAAE,CAAC;QAC3E,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC;IAC3B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;IAChF,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"fal.js","sourceRoot":"","sources":["../../src/engines/fal.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -6,4 +6,6 @@ export * from "./engines/openrouter.js";
6
6
  export * from "./engines/fal.js";
7
7
  export * from "./model-routing.js";
8
8
  export * from "./region-edit.js";
9
+ export * from "./suggested-models.js";
10
+ export * from "./check-suggested-models.js";
9
11
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,UAAU,CAAC;AACzB,cAAc,6BAA6B,CAAC;AAC5C,cAAc,yBAAyB,CAAC;AACxC,cAAc,kBAAkB,CAAC;AACjC,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,UAAU,CAAC;AACzB,cAAc,6BAA6B,CAAC;AAC5C,cAAc,yBAAyB,CAAC;AACxC,cAAc,kBAAkB,CAAC;AACjC,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC;AACjC,cAAc,uBAAuB,CAAC;AACtC,cAAc,6BAA6B,CAAC"}
package/dist/index.js CHANGED
@@ -6,4 +6,6 @@ export * from "./engines/openrouter.js";
6
6
  export * from "./engines/fal.js";
7
7
  export * from "./model-routing.js";
8
8
  export * from "./region-edit.js";
9
+ export * from "./suggested-models.js";
10
+ export * from "./check-suggested-models.js";
9
11
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,UAAU,CAAC;AACzB,cAAc,6BAA6B,CAAC;AAC5C,cAAc,yBAAyB,CAAC;AACxC,cAAc,kBAAkB,CAAC;AACjC,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,UAAU,CAAC;AACzB,cAAc,6BAA6B,CAAC;AAC5C,cAAc,yBAAyB,CAAC;AACxC,cAAc,kBAAkB,CAAC;AACjC,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC;AACjC,cAAc,uBAAuB,CAAC;AACtC,cAAc,6BAA6B,CAAC"}
@@ -0,0 +1,23 @@
1
+ import type { GenerationUseCase, MediaKind, ModelOption } from "./types.js";
2
+ /** Create image, from a prompt. */
3
+ export declare const SUGGESTED_IMAGE_MODELS: ModelOption[];
4
+ /** Create video, from a prompt alone. */
5
+ export declare const SUGGESTED_TEXT_TO_VIDEO_MODELS: ModelOption[];
6
+ /** Create video, starting from an attached image. The video follows the image's shape, so none lists aspect ratios. */
7
+ export declare const SUGGESTED_IMAGE_TO_VIDEO_MODELS: ModelOption[];
8
+ /**
9
+ * Edit an image. Flux 3 Image reads marked regions as boxes; the others get
10
+ * them described in words. An edit keeps the image's shape, so no aspect
11
+ * ratios.
12
+ */
13
+ export declare const SUGGESTED_EDIT_MODELS: ModelOption[];
14
+ /**
15
+ * MediaAnalyzer's models per media kind, on OpenRouter. The first in each
16
+ * is `ChaiProvider`'s default reasoning model for that kind.
17
+ */
18
+ export declare const SUGGESTED_ANALYSIS_MODELS: Partial<Record<MediaKind, ModelOption[]>>;
19
+ /** The suggested Fal models for a generation use case, or none (text requests use the reasoning model). */
20
+ export declare function suggestedModels(useCase: GenerationUseCase | undefined): ModelOption[];
21
+ /** Every suggested Fal model, by id: how the Fal engine knows each one's inputs. */
22
+ export declare const SUGGESTED_FAL_MODELS: ReadonlyMap<string, ModelOption>;
23
+ //# sourceMappingURL=suggested-models.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"suggested-models.d.ts","sourceRoot":"","sources":["../src/suggested-models.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAuB5E,mCAAmC;AACnC,eAAO,MAAM,sBAAsB,EAAE,WAAW,EAa/C,CAAC;AAEF,yCAAyC;AACzC,eAAO,MAAM,8BAA8B,EAAE,WAAW,EAKvD,CAAC;AAEF,uHAAuH;AACvH,eAAO,MAAM,+BAA+B,EAAE,WAAW,EAKxD,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,EAAE,WAAW,EAY9C,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,yBAAyB,EAAE,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE,WAAW,EAAE,CAAC,CAqB/E,CAAC;AAEF,2GAA2G;AAC3G,wBAAgB,eAAe,CAAC,OAAO,EAAE,iBAAiB,GAAG,SAAS,GAAG,WAAW,EAAE,CAarF;AAED,oFAAoF;AACpF,eAAO,MAAM,oBAAoB,EAAE,WAAW,CAAC,MAAM,EAAE,WAAW,CAEjE,CAAC"}
@@ -0,0 +1,110 @@
1
+ import { DEFAULT_MAX_REGIONS } from "./region-edit.js";
2
+ /**
3
+ * Suggested models, so builders don't have to write model lists. Composer
4
+ * and MediaAnalyzer use them when no `models` is passed; passing `models`
5
+ * still replaces them. The first model in each list is the default pick.
6
+ *
7
+ * Generation runs on Fal (the default engine), analysis on OpenRouter (the
8
+ * reasoning engine). Every entry was checked against the provider's own
9
+ * catalog: Fal's model schemas (`api.fal.ai/v1/models?expand=openapi-3.0`)
10
+ * for the aspect ratios and the image field, OpenRouter's model list for
11
+ * the input kinds. They change only through package releases; run
12
+ * `checkSuggestedModels()` to see whether a provider has changed since.
13
+ *
14
+ * Checked 2026-10-06.
15
+ */
16
+ const NANO_BANANA_RATIOS = ["21:9", "16:9", "3:2", "4:3", "5:4", "1:1", "4:5", "3:4", "2:3", "9:16"];
17
+ const FLUX_3_RATIOS = ["21:9", "2:1", "16:9", "3:2", "7:5", "4:3", "5:4", "1:1", "4:5", "3:4", "5:7", "2:3", "9:16", "1:2"];
18
+ /** The ratios a named `image_size` covers (FLUX.1, GPT Image 2). */
19
+ const IMAGE_SIZE_RATIOS = ["1:1", "4:3", "16:9", "3:4", "9:16"];
20
+ /** Create image, from a prompt. */
21
+ export const SUGGESTED_IMAGE_MODELS = [
22
+ { id: "fal-ai/nano-banana-pro", label: "Nano Banana Pro", provider: "Google", speed: "standard", aspectRatios: NANO_BANANA_RATIOS, falInput: { aspectRatio: "aspect_ratio" } },
23
+ {
24
+ id: "fal-ai/nano-banana-2",
25
+ label: "Nano Banana 2",
26
+ provider: "Google",
27
+ speed: "fast",
28
+ aspectRatios: [...NANO_BANANA_RATIOS, "4:1", "1:4", "8:1", "1:8"],
29
+ falInput: { aspectRatio: "aspect_ratio" },
30
+ },
31
+ { id: "blackforestlabs/flux-3/text-to-image", label: "Flux 3 Image", provider: "Black Forest Labs", speed: "standard", aspectRatios: FLUX_3_RATIOS, falInput: { aspectRatio: "aspect_ratio" } },
32
+ { id: "openai/gpt-image-2", label: "GPT Image 2", provider: "OpenAI", speed: "slow", aspectRatios: IMAGE_SIZE_RATIOS, falInput: { aspectRatio: "image_size" } },
33
+ { id: "fal-ai/flux/schnell", label: "FLUX.1 schnell", provider: "Black Forest Labs", speed: "fast", aspectRatios: IMAGE_SIZE_RATIOS, falInput: { aspectRatio: "image_size" } },
34
+ ];
35
+ /** Create video, from a prompt alone. */
36
+ export const SUGGESTED_TEXT_TO_VIDEO_MODELS = [
37
+ { id: "minimax/h3-max/text-to-video", label: "MiniMax H3 Max", provider: "MiniMax", speed: "slow", aspectRatios: ["21:9", "16:9", "4:3", "1:1", "3:4", "9:16"], falInput: { aspectRatio: "aspect_ratio" } },
38
+ { id: "bytedance/seedance-2.5/text-to-video", label: "Seedance 2.5", provider: "ByteDance", speed: "slow", aspectRatios: ["21:9", "16:9", "4:3", "1:1", "3:4", "9:16"], falInput: { aspectRatio: "aspect_ratio" } },
39
+ { id: "fal-ai/kling-video/v3/pro/text-to-video", label: "Kling v3 Pro", provider: "Kling", speed: "slow", aspectRatios: ["16:9", "9:16", "1:1"], falInput: { aspectRatio: "aspect_ratio" } },
40
+ { id: "fal-ai/veo3.1/fast", label: "Veo 3.1 Fast", provider: "Google", speed: "standard", aspectRatios: ["16:9", "9:16"], falInput: { aspectRatio: "aspect_ratio" } },
41
+ ];
42
+ /** Create video, starting from an attached image. The video follows the image's shape, so none lists aspect ratios. */
43
+ export const SUGGESTED_IMAGE_TO_VIDEO_MODELS = [
44
+ { id: "fal-ai/kling-video/v3/pro/image-to-video", label: "Kling v3 Pro", provider: "Kling", speed: "slow", falInput: { image: "start_image_url" } },
45
+ { id: "minimax/h3-max/image-to-video", label: "MiniMax H3 Max", provider: "MiniMax", speed: "slow", falInput: { image: "image_url" } },
46
+ { id: "bytedance/seedance-2.5/image-to-video", label: "Seedance 2.5", provider: "ByteDance", speed: "slow", falInput: { image: "image_url" } },
47
+ { id: "fal-ai/veo3.1/fast/image-to-video", label: "Veo 3.1 Fast", provider: "Google", speed: "standard", falInput: { image: "image_url" } },
48
+ ];
49
+ /**
50
+ * Edit an image. Flux 3 Image reads marked regions as boxes; the others get
51
+ * them described in words. An edit keeps the image's shape, so no aspect
52
+ * ratios.
53
+ */
54
+ export const SUGGESTED_EDIT_MODELS = [
55
+ {
56
+ id: "blackforestlabs/flux-3/edit-image",
57
+ label: "Flux 3 Image",
58
+ provider: "Black Forest Labs",
59
+ speed: "standard",
60
+ regionFormat: "flux-3-boxes",
61
+ maxRegions: DEFAULT_MAX_REGIONS,
62
+ falInput: { image: "image_urls" },
63
+ },
64
+ { id: "fal-ai/nano-banana-pro/edit", label: "Nano Banana Pro", provider: "Google", speed: "standard", falInput: { image: "image_urls" } },
65
+ { id: "openai/gpt-image-2/edit", label: "GPT Image 2", provider: "OpenAI", speed: "slow", falInput: { image: "image_urls" } },
66
+ ];
67
+ /**
68
+ * MediaAnalyzer's models per media kind, on OpenRouter. The first in each
69
+ * is `ChaiProvider`'s default reasoning model for that kind.
70
+ */
71
+ export const SUGGESTED_ANALYSIS_MODELS = {
72
+ image: [
73
+ { id: "anthropic/claude-opus-5", label: "Claude Opus 5", provider: "Anthropic", speed: "standard" },
74
+ { id: "openai/gpt-5.2", label: "GPT-5.2", provider: "OpenAI", speed: "standard" },
75
+ { id: "google/gemini-3.5-flash", label: "Gemini 3.5 Flash", provider: "Google", speed: "fast" },
76
+ { id: "meta-llama/llama-4-maverick", label: "Llama 4 Maverick", provider: "Meta", speed: "standard" },
77
+ { id: "x-ai/grok-4.5", label: "Grok 4.5", provider: "xAI", speed: "standard" },
78
+ ],
79
+ video: [
80
+ { id: "google/gemini-3.8-flash", label: "Gemini 3.8 Flash", provider: "Google", speed: "fast" },
81
+ { id: "google/gemini-3.5-flash", label: "Gemini 3.5 Flash", provider: "Google", speed: "fast" },
82
+ { id: "amazon/nova-2-lite-v1", label: "Nova 2 Lite", provider: "Amazon", speed: "fast" },
83
+ { id: "bytedance-seed/seed-2.0-lite", label: "Seed 2.0 Lite", provider: "ByteDance", speed: "fast" },
84
+ { id: "moonshotai/kimi-k3", label: "Kimi K3", provider: "Moonshot", speed: "standard" },
85
+ ],
86
+ audio: [
87
+ { id: "google/gemini-3.8-flash", label: "Gemini 3.8 Flash", provider: "Google", speed: "fast" },
88
+ { id: "openai/gpt-audio", label: "GPT Audio", provider: "OpenAI", speed: "standard" },
89
+ { id: "mistralai/voxtral-small-24b-2507", label: "Voxtral Small 24B", provider: "Mistral", speed: "standard" },
90
+ { id: "xiaomi/mimo-v2.5", label: "MiMo V2.5", provider: "Xiaomi", speed: "standard" },
91
+ ],
92
+ };
93
+ /** The suggested Fal models for a generation use case, or none (text requests use the reasoning model). */
94
+ export function suggestedModels(useCase) {
95
+ switch (useCase) {
96
+ case "text-to-image":
97
+ return SUGGESTED_IMAGE_MODELS;
98
+ case "text-to-video":
99
+ return SUGGESTED_TEXT_TO_VIDEO_MODELS;
100
+ case "image-to-video":
101
+ return SUGGESTED_IMAGE_TO_VIDEO_MODELS;
102
+ case "image-edit":
103
+ return SUGGESTED_EDIT_MODELS;
104
+ default:
105
+ return [];
106
+ }
107
+ }
108
+ /** Every suggested Fal model, by id: how the Fal engine knows each one's inputs. */
109
+ export const SUGGESTED_FAL_MODELS = new Map([...SUGGESTED_IMAGE_MODELS, ...SUGGESTED_TEXT_TO_VIDEO_MODELS, ...SUGGESTED_IMAGE_TO_VIDEO_MODELS, ...SUGGESTED_EDIT_MODELS].map((m) => [m.id, m]));
110
+ //# sourceMappingURL=suggested-models.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"suggested-models.js","sourceRoot":"","sources":["../src/suggested-models.ts"],"names":[],"mappings":"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"}
package/dist/types.d.ts CHANGED
@@ -87,6 +87,13 @@ export interface ModelOption {
87
87
  regionFormat?: RegionFormat;
88
88
  /** For image-edit models: the most regions one edit takes. `EditCard` disables "New region" at this many. */
89
89
  maxRegions?: number;
90
+ /**
91
+ * How a Fal model takes its inputs, from its Fal schema: which field the
92
+ * attached image goes in, and whether the aspect ratio is sent as
93
+ * `aspect_ratio` or as a named `image_size`. The suggested lists carry it;
94
+ * without it the Fal engine guesses from the id.
95
+ */
96
+ falInput?: FalInputFormat;
90
97
  /**
91
98
  * The output aspect ratios the model takes, e.g. `["1:1", "16:9", "9:16"]`.
92
99
  * Composer's Aspect ratio menu offers only ratios the selected models
@@ -96,6 +103,11 @@ export interface ModelOption {
96
103
  */
97
104
  aspectRatios?: string[];
98
105
  }
106
+ /** See `ModelOption.falInput`. */
107
+ export interface FalInputFormat {
108
+ image?: "image_url" | "image_urls" | "start_image_url";
109
+ aspectRatio?: "aspect_ratio" | "image_size";
110
+ }
99
111
  /** How an edit's regions reach the model — see `ModelOption.regionFormat`. */
100
112
  export type RegionFormat = "flux-3-boxes" | "text";
101
113
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,6DAA6D;AAC7D,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,GAAG,MAAM,CAAC;AAE7D;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,SAAS,CAAC;IAChB,kFAAkF;IAClF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,8DAA8D;IAC9D,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;;;GAMG;AACH,MAAM,WAAW,KAAK;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;GAKG;AACH,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,OAAO,CAAC;CACjB;AAED;;;;GAIG;AACH,MAAM,WAAW,cAAc,CAAC,CAAC,GAAG,OAAO;IACzC,KAAK,EAAE,CAAC,CAAC;IACT,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,IAAI,CAAC;IAC5B,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,oFAAoF;AACpF,MAAM,WAAW,eAAe,CAAC,CAAC;IAChC,KAAK,EAAE,CAAC,CAAC;IACT,KAAK,EAAE,MAAM,CAAC;CACf;AAED,iDAAiD;AACjD,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX;;;;;OAKG;IACH,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,wEAAwE;IACxE,KAAK,EAAE,MAAM,GAAG,UAAU,GAAG,MAAM,CAAC;IACpC;;;;;;OAMG;IACH,qBAAqB,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1D;;;;;OAKG;IACH,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,6GAA6G;IAC7G,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;;;OAMG;IACH,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;CACzB;AAED,8EAA8E;AAC9E,MAAM,MAAM,YAAY,GAAG,cAAc,GAAG,MAAM,CAAC;AAEnD;;;;;;;GAOG;AACH,MAAM,MAAM,iBAAiB,GACzB,eAAe,GACf,YAAY,GACZ,eAAe,GACf,gBAAgB,GAChB,YAAY,GACZ,QAAQ,GACR,cAAc,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,6DAA6D;AAC7D,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,GAAG,MAAM,CAAC;AAE7D;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,SAAS,CAAC;IAChB,kFAAkF;IAClF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,8DAA8D;IAC9D,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;;;GAMG;AACH,MAAM,WAAW,KAAK;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;GAKG;AACH,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,OAAO,CAAC;CACjB;AAED;;;;GAIG;AACH,MAAM,WAAW,cAAc,CAAC,CAAC,GAAG,OAAO;IACzC,KAAK,EAAE,CAAC,CAAC;IACT,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,IAAI,CAAC;IAC5B,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,oFAAoF;AACpF,MAAM,WAAW,eAAe,CAAC,CAAC;IAChC,KAAK,EAAE,CAAC,CAAC;IACT,KAAK,EAAE,MAAM,CAAC;CACf;AAED,iDAAiD;AACjD,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX;;;;;OAKG;IACH,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,wEAAwE;IACxE,KAAK,EAAE,MAAM,GAAG,UAAU,GAAG,MAAM,CAAC;IACpC;;;;;;OAMG;IACH,qBAAqB,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1D;;;;;OAKG;IACH,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,6GAA6G;IAC7G,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B;;;;;;OAMG;IACH,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;CACzB;AAED,kCAAkC;AAClC,MAAM,WAAW,cAAc;IAC7B,KAAK,CAAC,EAAE,WAAW,GAAG,YAAY,GAAG,iBAAiB,CAAC;IACvD,WAAW,CAAC,EAAE,cAAc,GAAG,YAAY,CAAC;CAC7C;AAED,8EAA8E;AAC9E,MAAM,MAAM,YAAY,GAAG,cAAc,GAAG,MAAM,CAAC;AAEnD;;;;;;;GAOG;AACH,MAAM,MAAM,iBAAiB,GACzB,eAAe,GACf,YAAY,GACZ,eAAe,GACf,gBAAgB,GAChB,YAAY,GACZ,QAAQ,GACR,cAAc,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chai-ui/core",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "CHAI UI headless core — the Request/Run/Result data model and pluggable generation engines (Fal.ai, OpenRouter, mock). No framework, no rendering.",
5
5
  "license": "MIT",
6
6
  "author": "kazarazat",
@@ -0,0 +1,147 @@
1
+ import type { MediaKind, ModelOption } from "./types.js";
2
+ import { SUGGESTED_ANALYSIS_MODELS, SUGGESTED_FAL_MODELS } from "./suggested-models.js";
3
+ import { FAL_NAMED_IMAGE_SIZES } from "./engines/fal.js";
4
+
5
+ /** One way a provider no longer matches a suggested model. */
6
+ export interface SuggestedModelProblem {
7
+ provider: "fal" | "openrouter";
8
+ /** The model's id, or `"*"` when the provider couldn't be reached at all. */
9
+ modelId: string;
10
+ problem: string;
11
+ }
12
+
13
+ export interface SuggestedModelsCheck {
14
+ /** True when every suggested model still matches its provider. */
15
+ ok: boolean;
16
+ /** How many models were checked. */
17
+ checked: number;
18
+ problems: SuggestedModelProblem[];
19
+ }
20
+
21
+ /**
22
+ * Checks the suggested model lists against Fal and OpenRouter as they are
23
+ * today: each model still exists and is active; for Fal, it still takes
24
+ * the aspect ratios and image field Chai sends, and needs no other field
25
+ * Chai doesn't send; for OpenRouter, it still accepts the media kind it's
26
+ * listed for. Run it while setting up your engine. It needs no API key,
27
+ * runs no model and costs nothing; both catalogs are public and allow
28
+ * calls from a browser.
29
+ *
30
+ * The lists themselves only change through package releases. A problem
31
+ * here means a provider changed since: pick another model, or update the
32
+ * package once a release catches up.
33
+ */
34
+ export async function checkSuggestedModels(options: { fetchImpl?: typeof fetch } = {}): Promise<SuggestedModelsCheck> {
35
+ const fetchImpl: typeof fetch = (input, init) => (options.fetchImpl ?? fetch)(input, init);
36
+ const fal = [...SUGGESTED_FAL_MODELS.values()];
37
+ const analysis = Object.entries(SUGGESTED_ANALYSIS_MODELS).flatMap(([kind, models]) =>
38
+ (models ?? []).map((model) => ({ kind: kind as MediaKind, model }))
39
+ );
40
+ const problems = [...(await checkFal(fal, fetchImpl)), ...(await checkOpenRouter(analysis, fetchImpl))];
41
+ return { ok: problems.length === 0, checked: fal.length + analysis.length, problems };
42
+ }
43
+
44
+ type Schema = { properties?: Record<string, Schema>; required?: string[]; enum?: unknown[]; const?: unknown; default?: unknown; anyOf?: Schema[]; allOf?: Schema[]; $ref?: string };
45
+
46
+ async function checkFal(models: ModelOption[], fetchImpl: typeof fetch): Promise<SuggestedModelProblem[]> {
47
+ const query = models.map((m) => `endpoint_id=${encodeURIComponent(m.id)}`).join("&");
48
+ let found: { endpoint_id: string; metadata?: { status?: string }; openapi?: OpenApi }[];
49
+ try {
50
+ // Fal answers 10 models a page, with a cursor for the next.
51
+ found = [];
52
+ let cursor: string | undefined;
53
+ do {
54
+ const res = await fetchImpl(
55
+ `https://api.fal.ai/v1/models?expand=openapi-3.0&${query}${cursor ? `&cursor=${encodeURIComponent(cursor)}` : ""}`
56
+ );
57
+ if (!res.ok) throw new Error(`${res.status} ${res.statusText}`);
58
+ const page = (await res.json()) as { models?: typeof found; has_more?: boolean; next_cursor?: string };
59
+ found.push(...(page.models ?? []));
60
+ cursor = page.has_more ? page.next_cursor : undefined;
61
+ } while (cursor);
62
+ } catch (err) {
63
+ return [{ provider: "fal", modelId: "*", problem: `Couldn't reach Fal's model catalog: ${(err as Error).message}` }];
64
+ }
65
+
66
+ const problems: SuggestedModelProblem[] = [];
67
+ const add = (modelId: string, problem: string) => problems.push({ provider: "fal", modelId, problem });
68
+ for (const model of models) {
69
+ const entry = found.find((f) => f.endpoint_id === model.id);
70
+ if (!entry) {
71
+ add(model.id, "Not in Fal's catalog any more.");
72
+ continue;
73
+ }
74
+ if (entry.metadata?.status && entry.metadata.status !== "active") add(model.id, `Fal lists it as "${entry.metadata.status}".`);
75
+ const input = inputSchema(entry.openapi);
76
+ if (!input) continue;
77
+ const props = input.schema.properties ?? {};
78
+
79
+ const imageField = model.falInput?.image;
80
+ if (imageField && !props[imageField]) add(model.id, `No longer takes "${imageField}" for the image.`);
81
+
82
+ const ratioField = model.falInput?.aspectRatio;
83
+ if (model.aspectRatios && ratioField) {
84
+ const accepted = enumOf(props[ratioField], input.resolve);
85
+ if (!props[ratioField]) add(model.id, `No longer takes "${ratioField}".`);
86
+ else if (accepted) {
87
+ const wanted = ratioField === "image_size" ? model.aspectRatios.map((r) => FAL_NAMED_IMAGE_SIZES[r] ?? r) : model.aspectRatios;
88
+ const missing = wanted.filter((v) => !accepted.includes(v));
89
+ if (missing.length > 0) add(model.id, `No longer takes these ${ratioField} values: ${missing.join(", ")}.`);
90
+ }
91
+ }
92
+
93
+ const sent = new Set(["prompt", imageField ?? "", ratioField ?? ""]);
94
+ for (const name of input.schema.required ?? []) {
95
+ if (!sent.has(name) && props[name]?.default === undefined) add(model.id, `Now requires "${name}", which Chai doesn't send.`);
96
+ }
97
+ }
98
+ return problems;
99
+ }
100
+
101
+ async function checkOpenRouter(
102
+ models: { kind: MediaKind; model: ModelOption }[],
103
+ fetchImpl: typeof fetch
104
+ ): Promise<SuggestedModelProblem[]> {
105
+ let catalog: { id: string; architecture?: { input_modalities?: string[] } }[];
106
+ try {
107
+ const res = await fetchImpl("https://openrouter.ai/api/v1/models");
108
+ if (!res.ok) throw new Error(`${res.status} ${res.statusText}`);
109
+ catalog = ((await res.json()) as { data?: typeof catalog }).data ?? [];
110
+ } catch (err) {
111
+ return [{ provider: "openrouter", modelId: "*", problem: `Couldn't reach OpenRouter's model list: ${(err as Error).message}` }];
112
+ }
113
+ const problems: SuggestedModelProblem[] = [];
114
+ for (const { kind, model } of models) {
115
+ const entry = catalog.find((c) => c.id === model.id);
116
+ if (!entry) problems.push({ provider: "openrouter", modelId: model.id, problem: "Not in OpenRouter's catalog any more." });
117
+ else if (!entry.architecture?.input_modalities?.includes(kind))
118
+ problems.push({ provider: "openrouter", modelId: model.id, problem: `No longer accepts ${kind}.` });
119
+ }
120
+ return problems;
121
+ }
122
+
123
+ type OpenApi = {
124
+ paths?: Record<string, { post?: { requestBody?: { content?: Record<string, { schema?: Schema }> } } }>;
125
+ components?: { schemas?: Record<string, Schema> };
126
+ };
127
+
128
+ /** The model's request schema, from its OpenAPI document, with a resolver for `$ref`s. */
129
+ function inputSchema(openapi: OpenApi | undefined): { schema: Schema; resolve: (s: Schema) => Schema } | null {
130
+ const schemas = openapi?.components?.schemas ?? {};
131
+ const resolve = (s: Schema): Schema => (s.$ref ? (schemas[s.$ref.split("/").pop()!] ?? s) : s);
132
+ for (const path of Object.values(openapi?.paths ?? {})) {
133
+ const body = path.post?.requestBody?.content?.["application/json"]?.schema;
134
+ if (body) return { schema: resolve(body), resolve };
135
+ }
136
+ return null;
137
+ }
138
+
139
+ /** The allowed values of a field, wherever its schema keeps them. */
140
+ function enumOf(field: Schema | undefined, resolve: (s: Schema) => Schema): unknown[] | null {
141
+ if (!field) return null;
142
+ for (const s of [field, ...(field.anyOf ?? []), ...(field.allOf ?? [])].map(resolve)) {
143
+ if (s.enum) return s.enum;
144
+ if (s.const !== undefined) return [s.const];
145
+ }
146
+ return null;
147
+ }
@@ -23,6 +23,7 @@
23
23
 
24
24
  import { abortError, abortableSleep, isAbortError, readEvaluations, type GenerationEngine } from "../engine.js";
25
25
  import type { DroppedMedia, MediaKind } from "../types.js";
26
+ import { SUGGESTED_FAL_MODELS } from "../suggested-models.js";
26
27
 
27
28
  /** Output kinds this engine can produce — text goes through a reasoning engine instead (see file doc comment). */
28
29
  type FalOutputKind = Exclude<MediaKind, "text">;
@@ -94,14 +95,14 @@ export function describeFalError(json: { error?: unknown; detail?: unknown }, fa
94
95
  }
95
96
 
96
97
  /**
97
- * Fal models that take their input images as an `image_urls` list (the
98
- * image being edited first) instead of a single `image_url`. Verified
99
- * against Fal's catalog 2026-10-03.
98
+ * Fal models outside the suggested lists that take their input images as an
99
+ * `image_urls` list (the image being edited first). Suggested models say
100
+ * so themselves (`ModelOption.falInput`).
100
101
  */
101
102
  const IMAGE_LIST_MODELS = new Set(["blackforestlabs/flux-3/edit-image"]);
102
103
 
103
- /** FLUX's `image_size` names for the ratios it takes (fal.ai/models/fal-ai/flux/schnell/api). */
104
- const FLUX_IMAGE_SIZES: Record<string, string> = {
104
+ /** Fal's named `image_size` values for the ratios they cover (FLUX.1, GPT Image 2). */
105
+ export const FAL_NAMED_IMAGE_SIZES: Record<string, string> = {
105
106
  "1:1": "square_hd",
106
107
  "4:3": "landscape_4_3",
107
108
  "16:9": "landscape_16_9",
@@ -109,18 +110,18 @@ const FLUX_IMAGE_SIZES: Record<string, string> = {
109
110
  "9:16": "portrait_16_9",
110
111
  };
111
112
 
112
- /** GPT Image's `image_size` pixel sizes for the ratios it takes (fal.ai/models/fal-ai/gpt-image-1/text-to-image/api). */
113
- const GPT_IMAGE_SIZES: Record<string, string> = {
113
+ /** GPT Image 1's `image_size` pixel sizes for the ratios it takes (fal.ai/models/fal-ai/gpt-image-1/text-to-image/api). */
114
+ const GPT_IMAGE_1_SIZES: Record<string, string> = {
114
115
  "1:1": "1024x1024",
115
116
  "3:2": "1536x1024",
116
117
  "2:3": "1024x1536",
117
118
  };
118
119
 
119
120
  /**
120
- * Builds the one Fal request body CHAI sends: the prompt, plus the attached
121
- * images. Most models take one `image_url` (for image-to-video and
122
- * edit-shaped models); the models in `IMAGE_LIST_MODELS` take every image
123
- * as `image_urls`.
121
+ * Builds the one Fal request body CHAI sends: the prompt, the aspect ratio
122
+ * and the attached images, each in the field the model expects. A
123
+ * suggested model says which (`ModelOption.falInput`, from its Fal
124
+ * schema); for any other model the field is guessed from its id.
124
125
  */
125
126
  export function buildFalRequestBody(args: {
126
127
  prompt: string;
@@ -129,13 +130,12 @@ export function buildFalRequestBody(args: {
129
130
  aspectRatio?: string;
130
131
  }): Record<string, unknown> {
131
132
  const body: Record<string, unknown> = { prompt: args.prompt };
133
+ const known = args.model ? SUGGESTED_FAL_MODELS.get(args.model)?.falInput : undefined;
132
134
  if (args.aspectRatio) {
133
- // FLUX takes a named size and GPT Image a pixel size; the others take
134
- // the ratio itself.
135
- const sizes = args.model?.startsWith("fal-ai/flux/")
136
- ? FLUX_IMAGE_SIZES
137
- : args.model?.startsWith("fal-ai/gpt-image")
138
- ? GPT_IMAGE_SIZES
135
+ const sizes = args.model?.startsWith("fal-ai/gpt-image")
136
+ ? GPT_IMAGE_1_SIZES
137
+ : (known?.aspectRatio ?? (args.model?.startsWith("fal-ai/flux/") ? "image_size" : "aspect_ratio")) === "image_size"
138
+ ? FAL_NAMED_IMAGE_SIZES
139
139
  : null;
140
140
  if (sizes) {
141
141
  const size = sizes[args.aspectRatio];
@@ -146,8 +146,9 @@ export function buildFalRequestBody(args: {
146
146
  }
147
147
  const images = (args.attachments ?? []).filter((a) => a.kind === "image").map((a) => a.src);
148
148
  if (images.length === 0) return body;
149
- if (args.model && IMAGE_LIST_MODELS.has(args.model)) body.image_urls = images;
150
- else body.image_url = images[0];
149
+ const field = known?.image ?? (args.model && IMAGE_LIST_MODELS.has(args.model) ? "image_urls" : "image_url");
150
+ if (field === "image_urls") body.image_urls = images;
151
+ else body[field] = images[0];
151
152
  return body;
152
153
  }
153
154
 
package/src/index.ts CHANGED
@@ -6,3 +6,5 @@ export * from "./engines/openrouter.js";
6
6
  export * from "./engines/fal.js";
7
7
  export * from "./model-routing.js";
8
8
  export * from "./region-edit.js";
9
+ export * from "./suggested-models.js";
10
+ export * from "./check-suggested-models.js";
@@ -0,0 +1,121 @@
1
+ import type { GenerationUseCase, MediaKind, ModelOption } from "./types.js";
2
+ import { DEFAULT_MAX_REGIONS } from "./region-edit.js";
3
+
4
+ /**
5
+ * Suggested models, so builders don't have to write model lists. Composer
6
+ * and MediaAnalyzer use them when no `models` is passed; passing `models`
7
+ * still replaces them. The first model in each list is the default pick.
8
+ *
9
+ * Generation runs on Fal (the default engine), analysis on OpenRouter (the
10
+ * reasoning engine). Every entry was checked against the provider's own
11
+ * catalog: Fal's model schemas (`api.fal.ai/v1/models?expand=openapi-3.0`)
12
+ * for the aspect ratios and the image field, OpenRouter's model list for
13
+ * the input kinds. They change only through package releases; run
14
+ * `checkSuggestedModels()` to see whether a provider has changed since.
15
+ *
16
+ * Checked 2026-10-06.
17
+ */
18
+
19
+ const NANO_BANANA_RATIOS = ["21:9", "16:9", "3:2", "4:3", "5:4", "1:1", "4:5", "3:4", "2:3", "9:16"];
20
+ const FLUX_3_RATIOS = ["21:9", "2:1", "16:9", "3:2", "7:5", "4:3", "5:4", "1:1", "4:5", "3:4", "5:7", "2:3", "9:16", "1:2"];
21
+ /** The ratios a named `image_size` covers (FLUX.1, GPT Image 2). */
22
+ const IMAGE_SIZE_RATIOS = ["1:1", "4:3", "16:9", "3:4", "9:16"];
23
+
24
+ /** Create image, from a prompt. */
25
+ export const SUGGESTED_IMAGE_MODELS: ModelOption[] = [
26
+ { id: "fal-ai/nano-banana-pro", label: "Nano Banana Pro", provider: "Google", speed: "standard", aspectRatios: NANO_BANANA_RATIOS, falInput: { aspectRatio: "aspect_ratio" } },
27
+ {
28
+ id: "fal-ai/nano-banana-2",
29
+ label: "Nano Banana 2",
30
+ provider: "Google",
31
+ speed: "fast",
32
+ aspectRatios: [...NANO_BANANA_RATIOS, "4:1", "1:4", "8:1", "1:8"],
33
+ falInput: { aspectRatio: "aspect_ratio" },
34
+ },
35
+ { id: "blackforestlabs/flux-3/text-to-image", label: "Flux 3 Image", provider: "Black Forest Labs", speed: "standard", aspectRatios: FLUX_3_RATIOS, falInput: { aspectRatio: "aspect_ratio" } },
36
+ { id: "openai/gpt-image-2", label: "GPT Image 2", provider: "OpenAI", speed: "slow", aspectRatios: IMAGE_SIZE_RATIOS, falInput: { aspectRatio: "image_size" } },
37
+ { id: "fal-ai/flux/schnell", label: "FLUX.1 schnell", provider: "Black Forest Labs", speed: "fast", aspectRatios: IMAGE_SIZE_RATIOS, falInput: { aspectRatio: "image_size" } },
38
+ ];
39
+
40
+ /** Create video, from a prompt alone. */
41
+ export const SUGGESTED_TEXT_TO_VIDEO_MODELS: ModelOption[] = [
42
+ { id: "minimax/h3-max/text-to-video", label: "MiniMax H3 Max", provider: "MiniMax", speed: "slow", aspectRatios: ["21:9", "16:9", "4:3", "1:1", "3:4", "9:16"], falInput: { aspectRatio: "aspect_ratio" } },
43
+ { id: "bytedance/seedance-2.5/text-to-video", label: "Seedance 2.5", provider: "ByteDance", speed: "slow", aspectRatios: ["21:9", "16:9", "4:3", "1:1", "3:4", "9:16"], falInput: { aspectRatio: "aspect_ratio" } },
44
+ { id: "fal-ai/kling-video/v3/pro/text-to-video", label: "Kling v3 Pro", provider: "Kling", speed: "slow", aspectRatios: ["16:9", "9:16", "1:1"], falInput: { aspectRatio: "aspect_ratio" } },
45
+ { id: "fal-ai/veo3.1/fast", label: "Veo 3.1 Fast", provider: "Google", speed: "standard", aspectRatios: ["16:9", "9:16"], falInput: { aspectRatio: "aspect_ratio" } },
46
+ ];
47
+
48
+ /** Create video, starting from an attached image. The video follows the image's shape, so none lists aspect ratios. */
49
+ export const SUGGESTED_IMAGE_TO_VIDEO_MODELS: ModelOption[] = [
50
+ { id: "fal-ai/kling-video/v3/pro/image-to-video", label: "Kling v3 Pro", provider: "Kling", speed: "slow", falInput: { image: "start_image_url" } },
51
+ { id: "minimax/h3-max/image-to-video", label: "MiniMax H3 Max", provider: "MiniMax", speed: "slow", falInput: { image: "image_url" } },
52
+ { id: "bytedance/seedance-2.5/image-to-video", label: "Seedance 2.5", provider: "ByteDance", speed: "slow", falInput: { image: "image_url" } },
53
+ { id: "fal-ai/veo3.1/fast/image-to-video", label: "Veo 3.1 Fast", provider: "Google", speed: "standard", falInput: { image: "image_url" } },
54
+ ];
55
+
56
+ /**
57
+ * Edit an image. Flux 3 Image reads marked regions as boxes; the others get
58
+ * them described in words. An edit keeps the image's shape, so no aspect
59
+ * ratios.
60
+ */
61
+ export const SUGGESTED_EDIT_MODELS: ModelOption[] = [
62
+ {
63
+ id: "blackforestlabs/flux-3/edit-image",
64
+ label: "Flux 3 Image",
65
+ provider: "Black Forest Labs",
66
+ speed: "standard",
67
+ regionFormat: "flux-3-boxes",
68
+ maxRegions: DEFAULT_MAX_REGIONS,
69
+ falInput: { image: "image_urls" },
70
+ },
71
+ { id: "fal-ai/nano-banana-pro/edit", label: "Nano Banana Pro", provider: "Google", speed: "standard", falInput: { image: "image_urls" } },
72
+ { id: "openai/gpt-image-2/edit", label: "GPT Image 2", provider: "OpenAI", speed: "slow", falInput: { image: "image_urls" } },
73
+ ];
74
+
75
+ /**
76
+ * MediaAnalyzer's models per media kind, on OpenRouter. The first in each
77
+ * is `ChaiProvider`'s default reasoning model for that kind.
78
+ */
79
+ export const SUGGESTED_ANALYSIS_MODELS: Partial<Record<MediaKind, ModelOption[]>> = {
80
+ image: [
81
+ { id: "anthropic/claude-opus-5", label: "Claude Opus 5", provider: "Anthropic", speed: "standard" },
82
+ { id: "openai/gpt-5.2", label: "GPT-5.2", provider: "OpenAI", speed: "standard" },
83
+ { id: "google/gemini-3.5-flash", label: "Gemini 3.5 Flash", provider: "Google", speed: "fast" },
84
+ { id: "meta-llama/llama-4-maverick", label: "Llama 4 Maverick", provider: "Meta", speed: "standard" },
85
+ { id: "x-ai/grok-4.5", label: "Grok 4.5", provider: "xAI", speed: "standard" },
86
+ ],
87
+ video: [
88
+ { id: "google/gemini-3.8-flash", label: "Gemini 3.8 Flash", provider: "Google", speed: "fast" },
89
+ { id: "google/gemini-3.5-flash", label: "Gemini 3.5 Flash", provider: "Google", speed: "fast" },
90
+ { id: "amazon/nova-2-lite-v1", label: "Nova 2 Lite", provider: "Amazon", speed: "fast" },
91
+ { id: "bytedance-seed/seed-2.0-lite", label: "Seed 2.0 Lite", provider: "ByteDance", speed: "fast" },
92
+ { id: "moonshotai/kimi-k3", label: "Kimi K3", provider: "Moonshot", speed: "standard" },
93
+ ],
94
+ audio: [
95
+ { id: "google/gemini-3.8-flash", label: "Gemini 3.8 Flash", provider: "Google", speed: "fast" },
96
+ { id: "openai/gpt-audio", label: "GPT Audio", provider: "OpenAI", speed: "standard" },
97
+ { id: "mistralai/voxtral-small-24b-2507", label: "Voxtral Small 24B", provider: "Mistral", speed: "standard" },
98
+ { id: "xiaomi/mimo-v2.5", label: "MiMo V2.5", provider: "Xiaomi", speed: "standard" },
99
+ ],
100
+ };
101
+
102
+ /** The suggested Fal models for a generation use case, or none (text requests use the reasoning model). */
103
+ export function suggestedModels(useCase: GenerationUseCase | undefined): ModelOption[] {
104
+ switch (useCase) {
105
+ case "text-to-image":
106
+ return SUGGESTED_IMAGE_MODELS;
107
+ case "text-to-video":
108
+ return SUGGESTED_TEXT_TO_VIDEO_MODELS;
109
+ case "image-to-video":
110
+ return SUGGESTED_IMAGE_TO_VIDEO_MODELS;
111
+ case "image-edit":
112
+ return SUGGESTED_EDIT_MODELS;
113
+ default:
114
+ return [];
115
+ }
116
+ }
117
+
118
+ /** Every suggested Fal model, by id: how the Fal engine knows each one's inputs. */
119
+ export const SUGGESTED_FAL_MODELS: ReadonlyMap<string, ModelOption> = new Map(
120
+ [...SUGGESTED_IMAGE_MODELS, ...SUGGESTED_TEXT_TO_VIDEO_MODELS, ...SUGGESTED_IMAGE_TO_VIDEO_MODELS, ...SUGGESTED_EDIT_MODELS].map((m) => [m.id, m])
121
+ );
package/src/types.ts CHANGED
@@ -91,6 +91,13 @@ export interface ModelOption {
91
91
  regionFormat?: RegionFormat;
92
92
  /** For image-edit models: the most regions one edit takes. `EditCard` disables "New region" at this many. */
93
93
  maxRegions?: number;
94
+ /**
95
+ * How a Fal model takes its inputs, from its Fal schema: which field the
96
+ * attached image goes in, and whether the aspect ratio is sent as
97
+ * `aspect_ratio` or as a named `image_size`. The suggested lists carry it;
98
+ * without it the Fal engine guesses from the id.
99
+ */
100
+ falInput?: FalInputFormat;
94
101
  /**
95
102
  * The output aspect ratios the model takes, e.g. `["1:1", "16:9", "9:16"]`.
96
103
  * Composer's Aspect ratio menu offers only ratios the selected models
@@ -101,6 +108,12 @@ export interface ModelOption {
101
108
  aspectRatios?: string[];
102
109
  }
103
110
 
111
+ /** See `ModelOption.falInput`. */
112
+ export interface FalInputFormat {
113
+ image?: "image_url" | "image_urls" | "start_image_url";
114
+ aspectRatio?: "aspect_ratio" | "image_size";
115
+ }
116
+
104
117
  /** How an edit's regions reach the model — see `ModelOption.regionFormat`. */
105
118
  export type RegionFormat = "flux-3-boxes" | "text";
106
119