@chai-ui/core 0.6.0 → 0.6.1

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.
@@ -0,0 +1,55 @@
1
+ import { type GenerationEngine } from "./engine.js";
2
+ import { type MediaAnalysisPromptLength } from "./media-analysis-prompts.js";
3
+ import { type ModelRoutingInput, type ModelRoutingResult } from "./model-routing.js";
4
+ import { type Reasoning } from "./reasoning.js";
5
+ import type { DroppedMedia, ModelOption } from "./types.js";
6
+ export interface MediaAnalyzerAttachment extends DroppedMedia {
7
+ id: string;
8
+ }
9
+ export interface MediaAnalyzerSubmitPayload {
10
+ attachments: MediaAnalyzerAttachment[];
11
+ modelId: string | null;
12
+ /** When true, the analyzer routes among `models` at submit instead of using `modelId`. */
13
+ autoSelectModel: boolean;
14
+ /** The models offered for the attached media's kind. */
15
+ models: ModelOption[];
16
+ /** `null` until a person actually picks one — see `MediaAnalyzer`'s `promptLength` prop for why this doesn't default to "concise". */
17
+ promptLength: MediaAnalysisPromptLength | null;
18
+ }
19
+ export interface MediaAnalyzerOptions {
20
+ /**
21
+ * Overrides the nearest `ChaiProvider`'s reasoning engine. With neither,
22
+ * a mock text engine answers, so a first render works with no key.
23
+ */
24
+ engine?: GenerationEngine;
25
+ /** The model when the person hasn't picked one. Defaults to the provider's reasoning model for the media's kind. */
26
+ model?: string;
27
+ onResult?: (prompt: string) => void;
28
+ onError?: (message: string) => void;
29
+ /** Auto-select: fires with the routed model so the trigger shows it. Wire it to `MediaAnalyzer`'s `onModelChange` state. */
30
+ onModelChange?: (modelId: string) => void;
31
+ /** Replaces the routing instruction (`buildModelRoutingPrompt`). */
32
+ routingPrompt?: (input: ModelRoutingInput) => string;
33
+ /** Fires when auto-select fell back to the first listed model (no reasoning model, a failed call, or an unlisted reply). Analysis still runs. */
34
+ onRoutingFallback?: (result: Extract<ModelRoutingResult, {
35
+ status: "fallback";
36
+ }>) => void;
37
+ }
38
+ export interface MediaAnalyzerState {
39
+ analyzing: boolean;
40
+ error: string | null;
41
+ prompt: string | null;
42
+ }
43
+ /**
44
+ * `getOptions` is read on every call, so options can change between
45
+ * renders; `getProvided` is the nearest provider's reasoning, if any.
46
+ */
47
+ export declare function createMediaAnalyzerStore(getOptions: () => MediaAnalyzerOptions, getProvided?: () => Reasoning | undefined): {
48
+ getState: () => MediaAnalyzerState;
49
+ subscribe(listener: () => void): () => undefined;
50
+ submit: (payload: MediaAnalyzerSubmitPayload) => void;
51
+ cancel: () => void;
52
+ reset: () => void;
53
+ };
54
+ export type MediaAnalyzerStore = ReturnType<typeof createMediaAnalyzerStore>;
55
+ //# sourceMappingURL=media-analyzer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"media-analyzer.d.ts","sourceRoot":"","sources":["../src/media-analyzer.ts"],"names":[],"mappings":"AASA,OAAO,EAA4D,KAAK,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC9G,OAAO,EAGL,KAAK,yBAAyB,EAC/B,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAc,KAAK,iBAAiB,EAAE,KAAK,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACjG,OAAO,EAAqB,KAAK,SAAS,EAAE,MAAM,gBAAgB,CAAC;AACnE,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE5D,MAAM,WAAW,uBAAwB,SAAQ,YAAY;IAC3D,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,WAAW,0BAA0B;IACzC,WAAW,EAAE,uBAAuB,EAAE,CAAC;IACvC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,0FAA0F;IAC1F,eAAe,EAAE,OAAO,CAAC;IACzB,wDAAwD;IACxD,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,sIAAsI;IACtI,YAAY,EAAE,yBAAyB,GAAG,IAAI,CAAC;CAChD;AAED,MAAM,WAAW,oBAAoB;IACnC;;;OAGG;IACH,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B,oHAAoH;IACpH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,4HAA4H;IAC5H,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1C,oEAAoE;IACpE,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,MAAM,CAAC;IACrD,iJAAiJ;IACjJ,iBAAiB,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC,kBAAkB,EAAE;QAAE,MAAM,EAAE,UAAU,CAAA;KAAE,CAAC,KAAK,IAAI,CAAC;CAC3F;AAED,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AAID;;;GAGG;AACH,wBAAgB,wBAAwB,CACtC,UAAU,EAAE,MAAM,oBAAoB,EACtC,WAAW,GAAE,MAAM,SAAS,GAAG,SAA2B;;wBAyGpC,MAAM,IAAI;sBA3FP,0BAA0B;;;EAmGpD;AAED,MAAM,MAAM,kBAAkB,GAAG,UAAU,CAAC,OAAO,wBAAwB,CAAC,CAAC"}
@@ -0,0 +1,124 @@
1
+ /**
2
+ * The Media Analyzer's behavior with no framework: sends the attached media
3
+ * and the instruction for its kind and prompt length
4
+ * (`MEDIA_ANALYSIS_PROMPTS`) to the reasoning engine, and keeps the
5
+ * generated prompt, which fills in as the text streams. A small store
6
+ * (`getState` + `subscribe`) that `useMediaAnalyzer` (React) and the Vue
7
+ * composable wrap.
8
+ */
9
+ import { warnNoReasoningOnce } from "./composer.js";
10
+ import { createMockEngine, isAbortError, normalizeGenerationError } from "./engine.js";
11
+ import { DEFAULT_MEDIA_ANALYSIS_PROMPT_LENGTH, MEDIA_ANALYSIS_PROMPTS, } from "./media-analysis-prompts.js";
12
+ import { routeModel } from "./model-routing.js";
13
+ import { reasoningModelFor } from "./reasoning.js";
14
+ const mockTextEngine = createMockEngine({ outputKind: "text" });
15
+ /**
16
+ * `getOptions` is read on every call, so options can change between
17
+ * renders; `getProvided` is the nearest provider's reasoning, if any.
18
+ */
19
+ export function createMediaAnalyzerStore(getOptions, getProvided = () => undefined) {
20
+ let state = { analyzing: false, error: null, prompt: null };
21
+ const listeners = new Set();
22
+ const set = (next) => {
23
+ state = { ...state, ...next };
24
+ for (const l of listeners)
25
+ l();
26
+ };
27
+ // Each submit gets an id; a late reply from an earlier submit is dropped,
28
+ // and its call aborted.
29
+ let callId = 0;
30
+ let controller = null;
31
+ function submit(payload) {
32
+ const opts = getOptions();
33
+ const reasoning = getProvided();
34
+ const id = ++callId;
35
+ controller?.abort();
36
+ const { signal } = (controller = new AbortController());
37
+ set({ error: null, prompt: null });
38
+ const kind = payload.attachments[0]?.kind;
39
+ if (kind !== "image" && kind !== "video" && kind !== "audio") {
40
+ const message = "Attach an image, video, or audio file to analyze.";
41
+ set({ error: message });
42
+ opts.onError?.(message);
43
+ return;
44
+ }
45
+ const engine = opts.engine ?? reasoning?.engine ?? mockTextEngine;
46
+ const defaultModel = opts.model ?? (reasoning ? reasoningModelFor(reasoning, payload.attachments) : undefined);
47
+ const instruction = MEDIA_ANALYSIS_PROMPTS[kind][payload.promptLength ?? DEFAULT_MEDIA_ANALYSIS_PROMPT_LENGTH];
48
+ const current = () => id === callId;
49
+ const autoSelect = payload.autoSelectModel && payload.models.length > 0;
50
+ const analyze = (modelId) => engine
51
+ .generate({
52
+ modelId,
53
+ prompt: instruction,
54
+ attachments: payload.attachments,
55
+ onText: (text) => current() && set({ prompt: text }),
56
+ signal,
57
+ })
58
+ .then(({ src }) => {
59
+ if (!current())
60
+ return;
61
+ set({ prompt: src });
62
+ getOptions().onResult?.(src);
63
+ }, (err) => {
64
+ if (!current() || isAbortError(err))
65
+ return;
66
+ const { message } = normalizeGenerationError(err);
67
+ set({ error: message });
68
+ getOptions().onError?.(message);
69
+ })
70
+ .finally(() => current() && set({ analyzing: false }));
71
+ set({ analyzing: true });
72
+ if (!autoSelect) {
73
+ void analyze(payload.modelId ?? defaultModel);
74
+ return;
75
+ }
76
+ // Auto-select: the reasoning model looks at the media and picks the
77
+ // analysis model first. A routing problem falls back, never blocks.
78
+ void routeModel({
79
+ prompt: "",
80
+ outputKind: "text",
81
+ attachmentKinds: payload.attachments.map((a) => a.kind),
82
+ attachments: payload.attachments,
83
+ models: payload.models,
84
+ engine: opts.engine ?? reasoning?.engine,
85
+ model: defaultModel,
86
+ routingPrompt: opts.routingPrompt,
87
+ signal,
88
+ }).then((routed) => {
89
+ if (!current() || signal.aborted)
90
+ return;
91
+ const latest = getOptions();
92
+ if (routed.status === "fallback") {
93
+ if (routed.reason === "no-reasoning")
94
+ warnNoReasoningOnce("analysis runs on the first listed model");
95
+ latest.onRoutingFallback?.(routed);
96
+ }
97
+ latest.onModelChange?.(routed.modelId);
98
+ return analyze(routed.modelId);
99
+ });
100
+ }
101
+ /** Stops the analysis in flight (cancelling at the provider where it can), keeping any text that already streamed in. */
102
+ function cancel() {
103
+ callId += 1;
104
+ controller?.abort();
105
+ controller = null;
106
+ set({ analyzing: false });
107
+ }
108
+ /** Stops anything in flight and clears the prompt and error, e.g. when the person changes their media. */
109
+ function reset() {
110
+ cancel();
111
+ set({ error: null, prompt: null });
112
+ }
113
+ return {
114
+ getState: () => state,
115
+ subscribe(listener) {
116
+ listeners.add(listener);
117
+ return () => void listeners.delete(listener);
118
+ },
119
+ submit,
120
+ cancel,
121
+ reset,
122
+ };
123
+ }
124
+ //# sourceMappingURL=media-analyzer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"media-analyzer.js","sourceRoot":"","sources":["../src/media-analyzer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,59 @@
1
+ import type { GenerationEngine } from "./engine.js";
2
+ import type { DroppedMedia, MediaKind } from "./types.js";
3
+ /**
4
+ * The suggested reasoning model for text and images: prompt enhancement,
5
+ * auto-select routing and image analysis. A default, not a requirement —
6
+ * pass any model id `reasoningEngine` can run.
7
+ */
8
+ export declare const DEFAULT_REASONING_MODEL = "anthropic/claude-opus-5";
9
+ /**
10
+ * Suggested per-kind overrides for media Claude can't read. Gemini takes
11
+ * video and audio directly. For a cheaper option, swap in
12
+ * `"qwen/qwen3.8-omni-flash"`, which takes both too.
13
+ */
14
+ export declare const DEFAULT_REASONING_MODEL_BY_KIND: Partial<Record<MediaKind, string>>;
15
+ /** The reasoning engine and models every CHAI component shares. */
16
+ export interface Reasoning {
17
+ engine: GenerationEngine;
18
+ /** The model for text and any attachment kind without its own entry in `modelByKind`. */
19
+ model: string;
20
+ modelByKind: Partial<Record<MediaKind, string>>;
21
+ }
22
+ /** What a `ChaiProvider` takes, in any framework. */
23
+ export interface ReasoningConfig {
24
+ /** Text, image and routing model. Defaults to `DEFAULT_REASONING_MODEL`. */
25
+ reasoningModel?: string;
26
+ /**
27
+ * Per-attachment-kind models, merged over `DEFAULT_REASONING_MODEL_BY_KIND`
28
+ * one kind at a time, e.g. `{ video: "qwen/qwen3.8-omni-flash" }` keeps
29
+ * the default for audio. Map a kind to `reasoningModel`'s value to use
30
+ * one model for everything.
31
+ */
32
+ reasoningModelByKind?: Partial<Record<MediaKind, string>>;
33
+ /**
34
+ * The engine the reasoning models run on. Defaults to OpenRouter,
35
+ * independent of the generation engine: Fal's own LLM access is an
36
+ * OpenRouter proxy, so defaulting to it would double-proxy.
37
+ */
38
+ reasoningEngine?: GenerationEngine;
39
+ }
40
+ /** A provider's `Reasoning`, from its config and the engine it holds (`config.reasoningEngine`, else one OpenRouter engine it created once). */
41
+ export declare function createReasoning(config: ReasoningConfig, engine: GenerationEngine): Reasoning;
42
+ /** The reasoning engine a provider uses: the one passed, else OpenRouter's. */
43
+ export declare function reasoningEngineFor(config: ReasoningConfig): GenerationEngine;
44
+ /**
45
+ * A hook's or composable's own pair wins, but only as a pair: half of one
46
+ * never mixes with half of the provider's (e.g. a hook engine with the
47
+ * provider's model). Half a pair means no reasoning.
48
+ */
49
+ export declare function pickReasoning(own: {
50
+ reasoningEngine?: GenerationEngine;
51
+ reasoningModel?: string;
52
+ }, provided: Reasoning | undefined): Reasoning | undefined;
53
+ /**
54
+ * The reasoning model for a call with these attachments: the first
55
+ * attachment whose kind has its own model wins (a clip plus a still goes
56
+ * to the model that can read the clip), else `reasoning.model`.
57
+ */
58
+ export declare function reasoningModelFor(reasoning: Reasoning, attachments?: DroppedMedia[]): string;
59
+ //# sourceMappingURL=reasoning.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reasoning.d.ts","sourceRoot":"","sources":["../src/reasoning.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE1D;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,4BAA4B,CAAC;AAEjE;;;;GAIG;AACH,eAAO,MAAM,+BAA+B,EAAE,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,CAG9E,CAAC;AAEF,mEAAmE;AACnE,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,gBAAgB,CAAC;IACzB,yFAAyF;IACzF,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC,CAAC;CACjD;AAED,qDAAqD;AACrD,MAAM,WAAW,eAAe;IAC9B,4EAA4E;IAC5E,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;;;OAKG;IACH,oBAAoB,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC,CAAC;IAC1D;;;;OAIG;IACH,eAAe,CAAC,EAAE,gBAAgB,CAAC;CACpC;AAED,gJAAgJ;AAChJ,wBAAgB,eAAe,CAAC,MAAM,EAAE,eAAe,EAAE,MAAM,EAAE,gBAAgB,GAAG,SAAS,CAQ5F;AAED,+EAA+E;AAC/E,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,eAAe,GAAG,gBAAgB,CAE5E;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAC3B,GAAG,EAAE;IAAE,eAAe,CAAC,EAAE,gBAAgB,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,CAAA;CAAE,EACpE,QAAQ,EAAE,SAAS,GAAG,SAAS,GAC9B,SAAS,GAAG,SAAS,CAIvB;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,SAAS,EAAE,WAAW,GAAE,YAAY,EAAO,GAAG,MAAM,CAMhG"}
@@ -0,0 +1,55 @@
1
+ import { createOpenRouterEngine } from "./engines/openrouter.js";
2
+ /**
3
+ * The suggested reasoning model for text and images: prompt enhancement,
4
+ * auto-select routing and image analysis. A default, not a requirement —
5
+ * pass any model id `reasoningEngine` can run.
6
+ */
7
+ export const DEFAULT_REASONING_MODEL = "anthropic/claude-opus-5";
8
+ /**
9
+ * Suggested per-kind overrides for media Claude can't read. Gemini takes
10
+ * video and audio directly. For a cheaper option, swap in
11
+ * `"qwen/qwen3.8-omni-flash"`, which takes both too.
12
+ */
13
+ export const DEFAULT_REASONING_MODEL_BY_KIND = {
14
+ video: "google/gemini-3.8-flash",
15
+ audio: "google/gemini-3.8-flash",
16
+ };
17
+ /** A provider's `Reasoning`, from its config and the engine it holds (`config.reasoningEngine`, else one OpenRouter engine it created once). */
18
+ export function createReasoning(config, engine) {
19
+ const byKind = config.reasoningModelByKind ?? {};
20
+ const overrides = Object.fromEntries(Object.entries(byKind).filter(([, v]) => v !== undefined));
21
+ return {
22
+ engine,
23
+ model: config.reasoningModel ?? DEFAULT_REASONING_MODEL,
24
+ modelByKind: { ...DEFAULT_REASONING_MODEL_BY_KIND, ...overrides },
25
+ };
26
+ }
27
+ /** The reasoning engine a provider uses: the one passed, else OpenRouter's. */
28
+ export function reasoningEngineFor(config) {
29
+ return config.reasoningEngine ?? createOpenRouterEngine();
30
+ }
31
+ /**
32
+ * A hook's or composable's own pair wins, but only as a pair: half of one
33
+ * never mixes with half of the provider's (e.g. a hook engine with the
34
+ * provider's model). Half a pair means no reasoning.
35
+ */
36
+ export function pickReasoning(own, provided) {
37
+ const { reasoningEngine, reasoningModel } = own;
38
+ if (!reasoningEngine && !reasoningModel)
39
+ return provided;
40
+ return reasoningEngine && reasoningModel ? { engine: reasoningEngine, model: reasoningModel, modelByKind: {} } : undefined;
41
+ }
42
+ /**
43
+ * The reasoning model for a call with these attachments: the first
44
+ * attachment whose kind has its own model wins (a clip plus a still goes
45
+ * to the model that can read the clip), else `reasoning.model`.
46
+ */
47
+ export function reasoningModelFor(reasoning, attachments = []) {
48
+ for (const { kind } of attachments) {
49
+ const model = reasoning.modelByKind[kind];
50
+ if (model)
51
+ return model;
52
+ }
53
+ return reasoning.model;
54
+ }
55
+ //# sourceMappingURL=reasoning.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reasoning.js","sourceRoot":"","sources":["../src/reasoning.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AAIjE;;;;GAIG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,yBAAyB,CAAC;AAEjE;;;;GAIG;AACH,MAAM,CAAC,MAAM,+BAA+B,GAAuC;IACjF,KAAK,EAAE,yBAAyB;IAChC,KAAK,EAAE,yBAAyB;CACjC,CAAC;AA6BF,gJAAgJ;AAChJ,MAAM,UAAU,eAAe,CAAC,MAAuB,EAAE,MAAwB;IAC/E,MAAM,MAAM,GAAG,MAAM,CAAC,oBAAoB,IAAI,EAAE,CAAC;IACjD,MAAM,SAAS,GAAG,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC;IAChG,OAAO;QACL,MAAM;QACN,KAAK,EAAE,MAAM,CAAC,cAAc,IAAI,uBAAuB;QACvD,WAAW,EAAE,EAAE,GAAG,+BAA+B,EAAE,GAAG,SAAS,EAAE;KAClE,CAAC;AACJ,CAAC;AAED,+EAA+E;AAC/E,MAAM,UAAU,kBAAkB,CAAC,MAAuB;IACxD,OAAO,MAAM,CAAC,eAAe,IAAI,sBAAsB,EAAE,CAAC;AAC5D,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAC3B,GAAoE,EACpE,QAA+B;IAE/B,MAAM,EAAE,eAAe,EAAE,cAAc,EAAE,GAAG,GAAG,CAAC;IAChD,IAAI,CAAC,eAAe,IAAI,CAAC,cAAc;QAAE,OAAO,QAAQ,CAAC;IACzD,OAAO,eAAe,IAAI,cAAc,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,eAAe,EAAE,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;AAC7H,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAAC,SAAoB,EAAE,cAA8B,EAAE;IACtF,KAAK,MAAM,EAAE,IAAI,EAAE,IAAI,WAAW,EAAE,CAAC;QACnC,MAAM,KAAK,GAAG,SAAS,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;QAC1C,IAAI,KAAK;YAAE,OAAO,KAAK,CAAC;IAC1B,CAAC;IACD,OAAO,SAAS,CAAC,KAAK,CAAC;AACzB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chai-ui/core",
3
- "version": "0.6.0",
3
+ "version": "0.6.1",
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",