@particle-academy/fancy-flow 0.12.0 → 0.14.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/dist/capabilities-B6r1Snfj.d.ts +77 -0
- package/dist/capabilities-D-V9Rxv1.d.cts +77 -0
- package/dist/{chunk-IK5DS5JP.js → chunk-7U3EP4Q5.js} +140 -54
- package/dist/chunk-7U3EP4Q5.js.map +1 -0
- package/dist/chunk-BB45F6S3.js +38 -0
- package/dist/chunk-BB45F6S3.js.map +1 -0
- package/dist/engine.d.cts +2 -2
- package/dist/engine.d.ts +2 -2
- package/dist/index.cjs +136 -16
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -4
- package/dist/index.d.ts +5 -4
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/llm/vercel-ai.cjs +59 -0
- package/dist/llm/vercel-ai.cjs.map +1 -0
- package/dist/llm/vercel-ai.d.cts +59 -0
- package/dist/llm/vercel-ai.d.ts +59 -0
- package/dist/llm/vercel-ai.js +49 -0
- package/dist/llm/vercel-ai.js.map +1 -0
- package/dist/registry/index.d.cts +7 -79
- package/dist/registry/index.d.ts +7 -79
- package/dist/registry.cjs +138 -17
- package/dist/registry.cjs.map +1 -1
- package/dist/registry.js +2 -1
- package/dist/runtime/index.d.cts +1 -1
- package/dist/runtime/index.d.ts +1 -1
- package/dist/schema/index.d.cts +1 -1
- package/dist/schema/index.d.ts +1 -1
- package/dist/{types-DtXOwXjA.d.ts → types-CnnKPnpt.d.ts} +1 -1
- package/dist/{types-DnQ4TEHN.d.cts → types-H60tQAxr.d.cts} +1 -1
- package/dist/{types-Dg2REAz5.d.cts → types-fc0fFKkf.d.cts} +1 -1
- package/dist/{types-Dg2REAz5.d.ts → types-fc0fFKkf.d.ts} +1 -1
- package/dist/ux.d.cts +2 -2
- package/dist/ux.d.ts +2 -2
- package/package.json +19 -3
- package/dist/chunk-IK5DS5JP.js.map +0 -1
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var ai = require('ai');
|
|
4
|
+
|
|
5
|
+
// src/llm/vercel-ai.ts
|
|
6
|
+
function registerLlmClient(client) {
|
|
7
|
+
return () => {
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// src/llm/vercel-ai.ts
|
|
12
|
+
function createVercelAiLlmClient(options) {
|
|
13
|
+
return {
|
|
14
|
+
async chooseRoute(request) {
|
|
15
|
+
const model = options.resolveModel?.(request) ?? options.model;
|
|
16
|
+
const result = await ai.generateText({
|
|
17
|
+
model,
|
|
18
|
+
system: request.system,
|
|
19
|
+
prompt: buildPrompt(request),
|
|
20
|
+
output: ai.Output.choice({
|
|
21
|
+
options: request.routes.map((r) => r.port),
|
|
22
|
+
name: "route",
|
|
23
|
+
description: "The single route this input should follow."
|
|
24
|
+
}),
|
|
25
|
+
...options.callOptions ?? {}
|
|
26
|
+
});
|
|
27
|
+
const port = String(result.output ?? "");
|
|
28
|
+
return {
|
|
29
|
+
port,
|
|
30
|
+
// The SDK returns the choice, not an explanation. Say where the answer
|
|
31
|
+
// came from rather than inventing a rationale the model never gave.
|
|
32
|
+
reason: `chosen by ${describeModel(model)}`
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
function useVercelAiForLlmBranch(options) {
|
|
38
|
+
return registerLlmClient();
|
|
39
|
+
}
|
|
40
|
+
function buildPrompt(request) {
|
|
41
|
+
const described = request.routes.filter((r) => r.description);
|
|
42
|
+
if (described.length === 0) return request.prompt;
|
|
43
|
+
const lines = described.map((r) => `- ${r.port}: ${r.description}`).join("\n");
|
|
44
|
+
return `${request.prompt}
|
|
45
|
+
|
|
46
|
+
Routes:
|
|
47
|
+
${lines}`;
|
|
48
|
+
}
|
|
49
|
+
function describeModel(model) {
|
|
50
|
+
if (typeof model === "string") return model;
|
|
51
|
+
const id = model.modelId;
|
|
52
|
+
return id ?? "the configured model";
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
exports.buildPrompt = buildPrompt;
|
|
56
|
+
exports.createVercelAiLlmClient = createVercelAiLlmClient;
|
|
57
|
+
exports.useVercelAiForLlmBranch = useVercelAiForLlmBranch;
|
|
58
|
+
//# sourceMappingURL=vercel-ai.cjs.map
|
|
59
|
+
//# sourceMappingURL=vercel-ai.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/registry/capabilities.ts","../../src/llm/vercel-ai.ts"],"names":["generateText","Output"],"mappings":";;;;;AA2DO,SAAS,kBAAkB,MAAA,EAA+B;AAE/D,EAAA,OAAO,MAAM;AAC2B,EACxC,CAAA;AACF;;;ACpBO,SAAS,wBAAwB,OAAA,EAAqC;AAC3E,EAAA,OAAO;AAAA,IACL,MAAM,YAAY,OAAA,EAAS;AACzB,MAAA,MAAM,KAAA,GAAQ,OAAA,CAAQ,YAAA,GAAe,OAAO,KAAK,OAAA,CAAQ,KAAA;AAMzD,MAAA,MAAM,MAAA,GAAS,MAAMA,eAAA,CAAa;AAAA,QAChC,KAAA;AAAA,QACA,QAAQ,OAAA,CAAQ,MAAA;AAAA,QAChB,MAAA,EAAQ,YAAY,OAAO,CAAA;AAAA,QAC3B,MAAA,EAAQC,UAAO,MAAA,CAAO;AAAA,UACpB,SAAS,OAAA,CAAQ,MAAA,CAAO,IAAI,CAAC,CAAA,KAAM,EAAE,IAAI,CAAA;AAAA,UACzC,IAAA,EAAM,OAAA;AAAA,UACN,WAAA,EAAa;AAAA,SACd,CAAA;AAAA,QACD,GAAI,OAAA,CAAQ,WAAA,IAAe;AAAC,OACpB,CAAA;AAEV,MAAA,MAAM,IAAA,GAAO,MAAA,CAAQ,MAAA,CAAgC,MAAA,IAAU,EAAE,CAAA;AACjE,MAAA,OAAO;AAAA,QACL,IAAA;AAAA;AAAA;AAAA,QAGA,MAAA,EAAQ,CAAA,UAAA,EAAa,aAAA,CAAc,KAAK,CAAC,CAAA;AAAA,OAC3C;AAAA,IACF;AAAA,GACF;AACF;AAGO,SAAS,wBAAwB,OAAA,EAAsC;AAC5E,EAAA,OAAO,iBAAA,CAAkD,CAAA;AAC3D;AASO,SAAS,YAAY,OAAA,EAAkC;AAC5D,EAAA,MAAM,YAAY,OAAA,CAAQ,MAAA,CAAO,OAAO,CAAC,CAAA,KAAM,EAAE,WAAW,CAAA;AAC5D,EAAA,IAAI,SAAA,CAAU,MAAA,KAAW,CAAA,EAAG,OAAO,OAAA,CAAQ,MAAA;AAE3C,EAAA,MAAM,KAAA,GAAQ,SAAA,CAAU,GAAA,CAAI,CAAC,MAAM,CAAA,EAAA,EAAK,CAAA,CAAE,IAAI,CAAA,EAAA,EAAK,CAAA,CAAE,WAAW,CAAA,CAAE,CAAA,CAAE,KAAK,IAAI,CAAA;AAC7E,EAAA,OAAO,CAAA,EAAG,QAAQ,MAAM;;AAAA;AAAA,EAAgB,KAAK,CAAA,CAAA;AAC/C;AAEA,SAAS,cAAc,KAAA,EAA8B;AACnD,EAAA,IAAI,OAAO,KAAA,KAAU,QAAA,EAAU,OAAO,KAAA;AACtC,EAAA,MAAM,KAAM,KAAA,CAA+B,OAAA;AAC3C,EAAA,OAAO,EAAA,IAAM,sBAAA;AACf","file":"vercel-ai.cjs","sourcesContent":["import type { FlowGraph } from \"../types\";\n\n/**\n * Host capabilities — the services core nodes need but must never depend on.\n *\n * A node that imports a provider SDK forces every consumer to install it: a\n * workflow app that never calls a model should not inherit an LLM dependency.\n * So core declares the CONTRACT and the host supplies the implementation, the\n * same arrangement `renderDocumentField` already uses for documents.\n *\n * That keeps opinionated nodes in core without their opinions: `llm_branch`\n * ships the routing semantics, port derivation and config UI, while whichever\n * client the host registers — Prism, an OpenAI SDK, a local model, a fake in a\n * test — decides how the question actually gets asked.\n *\n * Registration is deliberately explicit and typed per capability rather than a\n * stringly-keyed bag, so a missing one is a clear error at the seam instead of\n * an undefined somewhere downstream.\n */\n\n// ── LLM ─────────────────────────────────────────────────────────────────────\n\nexport type LlmRoute = { port: string; description?: string };\n\nexport type LlmRouteRequest = {\n /** Optional framing for the decision. */\n system?: string;\n /** What the model is deciding about. */\n prompt: string;\n /** The ports it must choose between. */\n routes: LlmRoute[];\n provider?: string;\n model?: string;\n /** Host-resolved credential reference, never a raw key. */\n credential?: string;\n};\n\nexport type LlmRouteChoice = {\n /** Must be one of the requested route ports. */\n port: string;\n /** Why — carried down the chosen port so a run is explainable afterwards. */\n reason?: string;\n};\n\n/**\n * The only thing core asks of an LLM: given routes, pick one.\n *\n * Deliberately not a general chat interface. A narrow contract is one a host\n * can satisfy in a few lines over any SDK, and it keeps the choice\n * machine-checkable — an implementation should constrain the model to the\n * declared ports (structured output / enum) rather than parsing prose.\n */\nexport type LlmClient = {\n chooseRoute: (request: LlmRouteRequest) => Promise<LlmRouteChoice> | LlmRouteChoice;\n};\n\nlet llmClient: LlmClient | null = null;\n\n/** Install the host's LLM client. Returns an unregister function. */\nexport function registerLlmClient(client: LlmClient): () => void {\n llmClient = client;\n return () => {\n if (llmClient === client) llmClient = null;\n };\n}\n\nexport function getLlmClient(): LlmClient | null {\n return llmClient;\n}\n\n// ── Workflow resolution ─────────────────────────────────────────────────────\n\n/**\n * Resolve a workflow reference to a runnable graph.\n *\n * `subflow` names another workflow rather than embedding it, so the host owns\n * where workflows live — a database, a file, an API. Returning null means \"no\n * such workflow\", which the node reports as an error rather than silently\n * running nothing.\n */\nexport type WorkflowResolver = (ref: string) => Promise<FlowGraph | null> | FlowGraph | null;\n\nlet workflowResolver: WorkflowResolver | null = null;\n\n/** Install the host's workflow resolver. Returns an unregister function. */\nexport function registerWorkflowResolver(resolver: WorkflowResolver): () => void {\n workflowResolver = resolver;\n return () => {\n if (workflowResolver === resolver) workflowResolver = null;\n };\n}\n\nexport function getWorkflowResolver(): WorkflowResolver | null {\n return workflowResolver;\n}\n\n// ── Introspection ───────────────────────────────────────────────────────────\n\nexport type CapabilityId = \"llm\" | \"workflow_resolver\" | \"document\";\n\n/**\n * Which capabilities are currently satisfied.\n *\n * Exists so a host (or the CLI, or an agent over MCP) can answer \"what does\n * this graph need that I haven't wired?\" BEFORE a run fails halfway through.\n */\nexport function capabilityStatus(): Record<CapabilityId, boolean> {\n // Imported lazily to avoid dragging the React-dependent rich-input module\n // into the headless engine.\n let documentReady = false;\n try {\n // eslint-disable-next-line @typescript-eslint/no-var-requires, no-undef\n documentReady = Boolean((globalThis as any).__fancyFlowDocumentAdapter);\n } catch {\n documentReady = false;\n }\n return {\n llm: llmClient !== null,\n workflow_resolver: workflowResolver !== null,\n document: documentReady,\n };\n}\n","/**\n * `@particle-academy/fancy-flow/llm/vercel-ai` — an LLM client backed by the\n * Vercel AI SDK.\n *\n * Core ships the routing but never a provider, so `llm_branch` needs a client\n * before it can run. Making every consumer hand-write one is not \"fully\n * functional\" — so this adapter ships in the box, on an opt-in subpath, exactly\n * like `/rich-input` wires fancy-cms.\n *\n * The AI SDK is chosen because it fronts every provider (Anthropic, OpenAI,\n * Google, local models). Picking it does NOT force it: `registerLlmClient()`\n * takes any implementation, so a different SDK or a hand-rolled fetch works\n * just as well.\n *\n * ```ts\n * import { anthropic } from \"@ai-sdk/anthropic\";\n * import { useVercelAiForLlmBranch } from \"@particle-academy/fancy-flow/llm/vercel-ai\";\n *\n * useVercelAiForLlmBranch({ model: anthropic(\"claude-sonnet-4-5\") });\n * ```\n *\n * `ai` is an OPTIONAL peer — required only by this subpath. The main entry\n * never imports it, so a flow with no AI node pays nothing.\n */\nimport { Output, generateText, type LanguageModel } from \"ai\";\nimport { registerLlmClient, type LlmClient, type LlmRouteRequest } from \"../registry/capabilities\";\n\nexport type VercelAiOptions = {\n /** The model to route with, e.g. `anthropic(\"claude-sonnet-4-5\")`. */\n model: LanguageModel;\n /**\n * Resolve a per-node model. `llm_branch` config can name a provider/model,\n * and only the host knows how to turn those strings into a model instance —\n * core deliberately never maps a name to a provider.\n */\n resolveModel?: (request: LlmRouteRequest) => LanguageModel | undefined;\n /** Extra options merged into every call (temperature, headers, …). */\n callOptions?: Record<string, unknown>;\n};\n\n/**\n * Build the client without registering it — handy for tests, or for a host\n * that wants to wrap it.\n */\nexport function createVercelAiLlmClient(options: VercelAiOptions): LlmClient {\n return {\n async chooseRoute(request) {\n const model = options.resolveModel?.(request) ?? options.model;\n\n // Constrain the model to the declared ports instead of parsing prose.\n // A choice that can only BE one of the ports is the difference between a\n // routing decision and a guess, and it means the \"hallucinated port\"\n // guard in the node is a backstop rather than the primary defence.\n const result = await generateText({\n model,\n system: request.system,\n prompt: buildPrompt(request),\n output: Output.choice({\n options: request.routes.map((r) => r.port),\n name: \"route\",\n description: \"The single route this input should follow.\",\n }),\n ...(options.callOptions ?? {}),\n } as never);\n\n const port = String((result as { output?: unknown }).output ?? \"\");\n return {\n port,\n // The SDK returns the choice, not an explanation. Say where the answer\n // came from rather than inventing a rationale the model never gave.\n reason: `chosen by ${describeModel(model)}`,\n };\n },\n };\n}\n\n/** Build the client and install it. Returns an unregister function. */\nexport function useVercelAiForLlmBranch(options: VercelAiOptions): () => void {\n return registerLlmClient(createVercelAiLlmClient(options));\n}\n\n/**\n * Fold the route descriptions into the prompt.\n *\n * The port ids alone are often terse (`billing`, `support`); the descriptions\n * are what the author wrote to distinguish them, so a model that never sees\n * them is choosing on the strength of a label.\n */\nexport function buildPrompt(request: LlmRouteRequest): string {\n const described = request.routes.filter((r) => r.description);\n if (described.length === 0) return request.prompt;\n\n const lines = described.map((r) => `- ${r.port}: ${r.description}`).join(\"\\n\");\n return `${request.prompt}\\n\\nRoutes:\\n${lines}`;\n}\n\nfunction describeModel(model: LanguageModel): string {\n if (typeof model === \"string\") return model;\n const id = (model as { modelId?: string }).modelId;\n return id ?? \"the configured model\";\n}\n"]}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { LanguageModel } from 'ai';
|
|
2
|
+
import { L as LlmRouteRequest, a as LlmClient } from '../capabilities-D-V9Rxv1.cjs';
|
|
3
|
+
import '../types-fc0fFKkf.cjs';
|
|
4
|
+
import '@xyflow/react';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* `@particle-academy/fancy-flow/llm/vercel-ai` — an LLM client backed by the
|
|
8
|
+
* Vercel AI SDK.
|
|
9
|
+
*
|
|
10
|
+
* Core ships the routing but never a provider, so `llm_branch` needs a client
|
|
11
|
+
* before it can run. Making every consumer hand-write one is not "fully
|
|
12
|
+
* functional" — so this adapter ships in the box, on an opt-in subpath, exactly
|
|
13
|
+
* like `/rich-input` wires fancy-cms.
|
|
14
|
+
*
|
|
15
|
+
* The AI SDK is chosen because it fronts every provider (Anthropic, OpenAI,
|
|
16
|
+
* Google, local models). Picking it does NOT force it: `registerLlmClient()`
|
|
17
|
+
* takes any implementation, so a different SDK or a hand-rolled fetch works
|
|
18
|
+
* just as well.
|
|
19
|
+
*
|
|
20
|
+
* ```ts
|
|
21
|
+
* import { anthropic } from "@ai-sdk/anthropic";
|
|
22
|
+
* import { useVercelAiForLlmBranch } from "@particle-academy/fancy-flow/llm/vercel-ai";
|
|
23
|
+
*
|
|
24
|
+
* useVercelAiForLlmBranch({ model: anthropic("claude-sonnet-4-5") });
|
|
25
|
+
* ```
|
|
26
|
+
*
|
|
27
|
+
* `ai` is an OPTIONAL peer — required only by this subpath. The main entry
|
|
28
|
+
* never imports it, so a flow with no AI node pays nothing.
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
type VercelAiOptions = {
|
|
32
|
+
/** The model to route with, e.g. `anthropic("claude-sonnet-4-5")`. */
|
|
33
|
+
model: LanguageModel;
|
|
34
|
+
/**
|
|
35
|
+
* Resolve a per-node model. `llm_branch` config can name a provider/model,
|
|
36
|
+
* and only the host knows how to turn those strings into a model instance —
|
|
37
|
+
* core deliberately never maps a name to a provider.
|
|
38
|
+
*/
|
|
39
|
+
resolveModel?: (request: LlmRouteRequest) => LanguageModel | undefined;
|
|
40
|
+
/** Extra options merged into every call (temperature, headers, …). */
|
|
41
|
+
callOptions?: Record<string, unknown>;
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* Build the client without registering it — handy for tests, or for a host
|
|
45
|
+
* that wants to wrap it.
|
|
46
|
+
*/
|
|
47
|
+
declare function createVercelAiLlmClient(options: VercelAiOptions): LlmClient;
|
|
48
|
+
/** Build the client and install it. Returns an unregister function. */
|
|
49
|
+
declare function useVercelAiForLlmBranch(options: VercelAiOptions): () => void;
|
|
50
|
+
/**
|
|
51
|
+
* Fold the route descriptions into the prompt.
|
|
52
|
+
*
|
|
53
|
+
* The port ids alone are often terse (`billing`, `support`); the descriptions
|
|
54
|
+
* are what the author wrote to distinguish them, so a model that never sees
|
|
55
|
+
* them is choosing on the strength of a label.
|
|
56
|
+
*/
|
|
57
|
+
declare function buildPrompt(request: LlmRouteRequest): string;
|
|
58
|
+
|
|
59
|
+
export { type VercelAiOptions, buildPrompt, createVercelAiLlmClient, useVercelAiForLlmBranch };
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { LanguageModel } from 'ai';
|
|
2
|
+
import { L as LlmRouteRequest, a as LlmClient } from '../capabilities-B6r1Snfj.js';
|
|
3
|
+
import '../types-fc0fFKkf.js';
|
|
4
|
+
import '@xyflow/react';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* `@particle-academy/fancy-flow/llm/vercel-ai` — an LLM client backed by the
|
|
8
|
+
* Vercel AI SDK.
|
|
9
|
+
*
|
|
10
|
+
* Core ships the routing but never a provider, so `llm_branch` needs a client
|
|
11
|
+
* before it can run. Making every consumer hand-write one is not "fully
|
|
12
|
+
* functional" — so this adapter ships in the box, on an opt-in subpath, exactly
|
|
13
|
+
* like `/rich-input` wires fancy-cms.
|
|
14
|
+
*
|
|
15
|
+
* The AI SDK is chosen because it fronts every provider (Anthropic, OpenAI,
|
|
16
|
+
* Google, local models). Picking it does NOT force it: `registerLlmClient()`
|
|
17
|
+
* takes any implementation, so a different SDK or a hand-rolled fetch works
|
|
18
|
+
* just as well.
|
|
19
|
+
*
|
|
20
|
+
* ```ts
|
|
21
|
+
* import { anthropic } from "@ai-sdk/anthropic";
|
|
22
|
+
* import { useVercelAiForLlmBranch } from "@particle-academy/fancy-flow/llm/vercel-ai";
|
|
23
|
+
*
|
|
24
|
+
* useVercelAiForLlmBranch({ model: anthropic("claude-sonnet-4-5") });
|
|
25
|
+
* ```
|
|
26
|
+
*
|
|
27
|
+
* `ai` is an OPTIONAL peer — required only by this subpath. The main entry
|
|
28
|
+
* never imports it, so a flow with no AI node pays nothing.
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
type VercelAiOptions = {
|
|
32
|
+
/** The model to route with, e.g. `anthropic("claude-sonnet-4-5")`. */
|
|
33
|
+
model: LanguageModel;
|
|
34
|
+
/**
|
|
35
|
+
* Resolve a per-node model. `llm_branch` config can name a provider/model,
|
|
36
|
+
* and only the host knows how to turn those strings into a model instance —
|
|
37
|
+
* core deliberately never maps a name to a provider.
|
|
38
|
+
*/
|
|
39
|
+
resolveModel?: (request: LlmRouteRequest) => LanguageModel | undefined;
|
|
40
|
+
/** Extra options merged into every call (temperature, headers, …). */
|
|
41
|
+
callOptions?: Record<string, unknown>;
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* Build the client without registering it — handy for tests, or for a host
|
|
45
|
+
* that wants to wrap it.
|
|
46
|
+
*/
|
|
47
|
+
declare function createVercelAiLlmClient(options: VercelAiOptions): LlmClient;
|
|
48
|
+
/** Build the client and install it. Returns an unregister function. */
|
|
49
|
+
declare function useVercelAiForLlmBranch(options: VercelAiOptions): () => void;
|
|
50
|
+
/**
|
|
51
|
+
* Fold the route descriptions into the prompt.
|
|
52
|
+
*
|
|
53
|
+
* The port ids alone are often terse (`billing`, `support`); the descriptions
|
|
54
|
+
* are what the author wrote to distinguish them, so a model that never sees
|
|
55
|
+
* them is choosing on the strength of a label.
|
|
56
|
+
*/
|
|
57
|
+
declare function buildPrompt(request: LlmRouteRequest): string;
|
|
58
|
+
|
|
59
|
+
export { type VercelAiOptions, buildPrompt, createVercelAiLlmClient, useVercelAiForLlmBranch };
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { registerLlmClient } from '../chunk-BB45F6S3.js';
|
|
2
|
+
import { generateText, Output } from 'ai';
|
|
3
|
+
|
|
4
|
+
function createVercelAiLlmClient(options) {
|
|
5
|
+
return {
|
|
6
|
+
async chooseRoute(request) {
|
|
7
|
+
const model = options.resolveModel?.(request) ?? options.model;
|
|
8
|
+
const result = await generateText({
|
|
9
|
+
model,
|
|
10
|
+
system: request.system,
|
|
11
|
+
prompt: buildPrompt(request),
|
|
12
|
+
output: Output.choice({
|
|
13
|
+
options: request.routes.map((r) => r.port),
|
|
14
|
+
name: "route",
|
|
15
|
+
description: "The single route this input should follow."
|
|
16
|
+
}),
|
|
17
|
+
...options.callOptions ?? {}
|
|
18
|
+
});
|
|
19
|
+
const port = String(result.output ?? "");
|
|
20
|
+
return {
|
|
21
|
+
port,
|
|
22
|
+
// The SDK returns the choice, not an explanation. Say where the answer
|
|
23
|
+
// came from rather than inventing a rationale the model never gave.
|
|
24
|
+
reason: `chosen by ${describeModel(model)}`
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
function useVercelAiForLlmBranch(options) {
|
|
30
|
+
return registerLlmClient(createVercelAiLlmClient(options));
|
|
31
|
+
}
|
|
32
|
+
function buildPrompt(request) {
|
|
33
|
+
const described = request.routes.filter((r) => r.description);
|
|
34
|
+
if (described.length === 0) return request.prompt;
|
|
35
|
+
const lines = described.map((r) => `- ${r.port}: ${r.description}`).join("\n");
|
|
36
|
+
return `${request.prompt}
|
|
37
|
+
|
|
38
|
+
Routes:
|
|
39
|
+
${lines}`;
|
|
40
|
+
}
|
|
41
|
+
function describeModel(model) {
|
|
42
|
+
if (typeof model === "string") return model;
|
|
43
|
+
const id = model.modelId;
|
|
44
|
+
return id ?? "the configured model";
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export { buildPrompt, createVercelAiLlmClient, useVercelAiForLlmBranch };
|
|
48
|
+
//# sourceMappingURL=vercel-ai.js.map
|
|
49
|
+
//# sourceMappingURL=vercel-ai.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/llm/vercel-ai.ts"],"names":[],"mappings":";;;AA4CO,SAAS,wBAAwB,OAAA,EAAqC;AAC3E,EAAA,OAAO;AAAA,IACL,MAAM,YAAY,OAAA,EAAS;AACzB,MAAA,MAAM,KAAA,GAAQ,OAAA,CAAQ,YAAA,GAAe,OAAO,KAAK,OAAA,CAAQ,KAAA;AAMzD,MAAA,MAAM,MAAA,GAAS,MAAM,YAAA,CAAa;AAAA,QAChC,KAAA;AAAA,QACA,QAAQ,OAAA,CAAQ,MAAA;AAAA,QAChB,MAAA,EAAQ,YAAY,OAAO,CAAA;AAAA,QAC3B,MAAA,EAAQ,OAAO,MAAA,CAAO;AAAA,UACpB,SAAS,OAAA,CAAQ,MAAA,CAAO,IAAI,CAAC,CAAA,KAAM,EAAE,IAAI,CAAA;AAAA,UACzC,IAAA,EAAM,OAAA;AAAA,UACN,WAAA,EAAa;AAAA,SACd,CAAA;AAAA,QACD,GAAI,OAAA,CAAQ,WAAA,IAAe;AAAC,OACpB,CAAA;AAEV,MAAA,MAAM,IAAA,GAAO,MAAA,CAAQ,MAAA,CAAgC,MAAA,IAAU,EAAE,CAAA;AACjE,MAAA,OAAO;AAAA,QACL,IAAA;AAAA;AAAA;AAAA,QAGA,MAAA,EAAQ,CAAA,UAAA,EAAa,aAAA,CAAc,KAAK,CAAC,CAAA;AAAA,OAC3C;AAAA,IACF;AAAA,GACF;AACF;AAGO,SAAS,wBAAwB,OAAA,EAAsC;AAC5E,EAAA,OAAO,iBAAA,CAAkB,uBAAA,CAAwB,OAAO,CAAC,CAAA;AAC3D;AASO,SAAS,YAAY,OAAA,EAAkC;AAC5D,EAAA,MAAM,YAAY,OAAA,CAAQ,MAAA,CAAO,OAAO,CAAC,CAAA,KAAM,EAAE,WAAW,CAAA;AAC5D,EAAA,IAAI,SAAA,CAAU,MAAA,KAAW,CAAA,EAAG,OAAO,OAAA,CAAQ,MAAA;AAE3C,EAAA,MAAM,KAAA,GAAQ,SAAA,CAAU,GAAA,CAAI,CAAC,MAAM,CAAA,EAAA,EAAK,CAAA,CAAE,IAAI,CAAA,EAAA,EAAK,CAAA,CAAE,WAAW,CAAA,CAAE,CAAA,CAAE,KAAK,IAAI,CAAA;AAC7E,EAAA,OAAO,CAAA,EAAG,QAAQ,MAAM;;AAAA;AAAA,EAAgB,KAAK,CAAA,CAAA;AAC/C;AAEA,SAAS,cAAc,KAAA,EAA8B;AACnD,EAAA,IAAI,OAAO,KAAA,KAAU,QAAA,EAAU,OAAO,KAAA;AACtC,EAAA,MAAM,KAAM,KAAA,CAA+B,OAAA;AAC3C,EAAA,OAAO,EAAA,IAAM,sBAAA;AACf","file":"vercel-ai.js","sourcesContent":["/**\n * `@particle-academy/fancy-flow/llm/vercel-ai` — an LLM client backed by the\n * Vercel AI SDK.\n *\n * Core ships the routing but never a provider, so `llm_branch` needs a client\n * before it can run. Making every consumer hand-write one is not \"fully\n * functional\" — so this adapter ships in the box, on an opt-in subpath, exactly\n * like `/rich-input` wires fancy-cms.\n *\n * The AI SDK is chosen because it fronts every provider (Anthropic, OpenAI,\n * Google, local models). Picking it does NOT force it: `registerLlmClient()`\n * takes any implementation, so a different SDK or a hand-rolled fetch works\n * just as well.\n *\n * ```ts\n * import { anthropic } from \"@ai-sdk/anthropic\";\n * import { useVercelAiForLlmBranch } from \"@particle-academy/fancy-flow/llm/vercel-ai\";\n *\n * useVercelAiForLlmBranch({ model: anthropic(\"claude-sonnet-4-5\") });\n * ```\n *\n * `ai` is an OPTIONAL peer — required only by this subpath. The main entry\n * never imports it, so a flow with no AI node pays nothing.\n */\nimport { Output, generateText, type LanguageModel } from \"ai\";\nimport { registerLlmClient, type LlmClient, type LlmRouteRequest } from \"../registry/capabilities\";\n\nexport type VercelAiOptions = {\n /** The model to route with, e.g. `anthropic(\"claude-sonnet-4-5\")`. */\n model: LanguageModel;\n /**\n * Resolve a per-node model. `llm_branch` config can name a provider/model,\n * and only the host knows how to turn those strings into a model instance —\n * core deliberately never maps a name to a provider.\n */\n resolveModel?: (request: LlmRouteRequest) => LanguageModel | undefined;\n /** Extra options merged into every call (temperature, headers, …). */\n callOptions?: Record<string, unknown>;\n};\n\n/**\n * Build the client without registering it — handy for tests, or for a host\n * that wants to wrap it.\n */\nexport function createVercelAiLlmClient(options: VercelAiOptions): LlmClient {\n return {\n async chooseRoute(request) {\n const model = options.resolveModel?.(request) ?? options.model;\n\n // Constrain the model to the declared ports instead of parsing prose.\n // A choice that can only BE one of the ports is the difference between a\n // routing decision and a guess, and it means the \"hallucinated port\"\n // guard in the node is a backstop rather than the primary defence.\n const result = await generateText({\n model,\n system: request.system,\n prompt: buildPrompt(request),\n output: Output.choice({\n options: request.routes.map((r) => r.port),\n name: \"route\",\n description: \"The single route this input should follow.\",\n }),\n ...(options.callOptions ?? {}),\n } as never);\n\n const port = String((result as { output?: unknown }).output ?? \"\");\n return {\n port,\n // The SDK returns the choice, not an explanation. Say where the answer\n // came from rather than inventing a rationale the model never gave.\n reason: `chosen by ${describeModel(model)}`,\n };\n },\n };\n}\n\n/** Build the client and install it. Returns an unregister function. */\nexport function useVercelAiForLlmBranch(options: VercelAiOptions): () => void {\n return registerLlmClient(createVercelAiLlmClient(options));\n}\n\n/**\n * Fold the route descriptions into the prompt.\n *\n * The port ids alone are often terse (`billing`, `support`); the descriptions\n * are what the author wrote to distinguish them, so a model that never sees\n * them is choosing on the strength of a label.\n */\nexport function buildPrompt(request: LlmRouteRequest): string {\n const described = request.routes.filter((r) => r.description);\n if (described.length === 0) return request.prompt;\n\n const lines = described.map((r) => `- ${r.port}: ${r.description}`).join(\"\\n\");\n return `${request.prompt}\\n\\nRoutes:\\n${lines}`;\n}\n\nfunction describeModel(model: LanguageModel): string {\n if (typeof model === \"string\") return model;\n const id = (model as { modelId?: string }).modelId;\n return id ?? \"the configured model\";\n}\n"]}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import { a as NodeKindDefinition, P as PortSpec } from '../types-
|
|
2
|
-
export { C as ConfigField, b as CredentialConfigField, D as DocumentConfigField, E as ExpressionConfigField, J as JsonConfigField, K as KeyValueConfigField, N as NodeCategory, c as NumberConfigField, R as RenderBodyContext, d as RepeaterConfigField, e as RepeaterRowField, S as SelectConfigField, f as SwitchConfigField, T as TextConfigField, g as TextareaConfigField } from '../types-
|
|
3
|
-
import {
|
|
1
|
+
import { a as NodeKindDefinition, P as PortSpec } from '../types-H60tQAxr.cjs';
|
|
2
|
+
export { C as ConfigField, b as CredentialConfigField, D as DocumentConfigField, E as ExpressionConfigField, J as JsonConfigField, K as KeyValueConfigField, N as NodeCategory, c as NumberConfigField, R as RenderBodyContext, d as RepeaterConfigField, e as RepeaterRowField, S as SelectConfigField, f as SwitchConfigField, T as TextConfigField, g as TextareaConfigField } from '../types-H60tQAxr.cjs';
|
|
3
|
+
import { a as FlowNode, P as PortDescriptor, N as NodeExecutor } from '../types-fc0fFKkf.cjs';
|
|
4
|
+
import { b as LlmRoute } from '../capabilities-D-V9Rxv1.cjs';
|
|
5
|
+
export { C as CapabilityId, a as LlmClient, c as LlmRouteChoice, L as LlmRouteRequest, W as WorkflowResolver, d as capabilityStatus, g as getLlmClient, e as getWorkflowResolver, r as registerLlmClient, f as registerWorkflowResolver } from '../capabilities-D-V9Rxv1.cjs';
|
|
4
6
|
import * as react from 'react';
|
|
5
7
|
import { ComponentType, ReactNode } from 'react';
|
|
6
8
|
import { NodeProps, NodeTypes } from '@xyflow/react';
|
|
@@ -44,80 +46,6 @@ declare function resolveNodePorts(node: Pick<FlowNode, "data">, kind?: Pick<Node
|
|
|
44
46
|
outputs?: PortDescriptor[];
|
|
45
47
|
};
|
|
46
48
|
|
|
47
|
-
/**
|
|
48
|
-
* Host capabilities — the services core nodes need but must never depend on.
|
|
49
|
-
*
|
|
50
|
-
* A node that imports a provider SDK forces every consumer to install it: a
|
|
51
|
-
* workflow app that never calls a model should not inherit an LLM dependency.
|
|
52
|
-
* So core declares the CONTRACT and the host supplies the implementation, the
|
|
53
|
-
* same arrangement `renderDocumentField` already uses for documents.
|
|
54
|
-
*
|
|
55
|
-
* That keeps opinionated nodes in core without their opinions: `llm_branch`
|
|
56
|
-
* ships the routing semantics, port derivation and config UI, while whichever
|
|
57
|
-
* client the host registers — Prism, an OpenAI SDK, a local model, a fake in a
|
|
58
|
-
* test — decides how the question actually gets asked.
|
|
59
|
-
*
|
|
60
|
-
* Registration is deliberately explicit and typed per capability rather than a
|
|
61
|
-
* stringly-keyed bag, so a missing one is a clear error at the seam instead of
|
|
62
|
-
* an undefined somewhere downstream.
|
|
63
|
-
*/
|
|
64
|
-
type LlmRoute = {
|
|
65
|
-
port: string;
|
|
66
|
-
description?: string;
|
|
67
|
-
};
|
|
68
|
-
type LlmRouteRequest = {
|
|
69
|
-
/** Optional framing for the decision. */
|
|
70
|
-
system?: string;
|
|
71
|
-
/** What the model is deciding about. */
|
|
72
|
-
prompt: string;
|
|
73
|
-
/** The ports it must choose between. */
|
|
74
|
-
routes: LlmRoute[];
|
|
75
|
-
provider?: string;
|
|
76
|
-
model?: string;
|
|
77
|
-
/** Host-resolved credential reference, never a raw key. */
|
|
78
|
-
credential?: string;
|
|
79
|
-
};
|
|
80
|
-
type LlmRouteChoice = {
|
|
81
|
-
/** Must be one of the requested route ports. */
|
|
82
|
-
port: string;
|
|
83
|
-
/** Why — carried down the chosen port so a run is explainable afterwards. */
|
|
84
|
-
reason?: string;
|
|
85
|
-
};
|
|
86
|
-
/**
|
|
87
|
-
* The only thing core asks of an LLM: given routes, pick one.
|
|
88
|
-
*
|
|
89
|
-
* Deliberately not a general chat interface. A narrow contract is one a host
|
|
90
|
-
* can satisfy in a few lines over any SDK, and it keeps the choice
|
|
91
|
-
* machine-checkable — an implementation should constrain the model to the
|
|
92
|
-
* declared ports (structured output / enum) rather than parsing prose.
|
|
93
|
-
*/
|
|
94
|
-
type LlmClient = {
|
|
95
|
-
chooseRoute: (request: LlmRouteRequest) => Promise<LlmRouteChoice> | LlmRouteChoice;
|
|
96
|
-
};
|
|
97
|
-
/** Install the host's LLM client. Returns an unregister function. */
|
|
98
|
-
declare function registerLlmClient(client: LlmClient): () => void;
|
|
99
|
-
declare function getLlmClient(): LlmClient | null;
|
|
100
|
-
/**
|
|
101
|
-
* Resolve a workflow reference to a runnable graph.
|
|
102
|
-
*
|
|
103
|
-
* `subflow` names another workflow rather than embedding it, so the host owns
|
|
104
|
-
* where workflows live — a database, a file, an API. Returning null means "no
|
|
105
|
-
* such workflow", which the node reports as an error rather than silently
|
|
106
|
-
* running nothing.
|
|
107
|
-
*/
|
|
108
|
-
type WorkflowResolver = (ref: string) => Promise<FlowGraph | null> | FlowGraph | null;
|
|
109
|
-
/** Install the host's workflow resolver. Returns an unregister function. */
|
|
110
|
-
declare function registerWorkflowResolver(resolver: WorkflowResolver): () => void;
|
|
111
|
-
declare function getWorkflowResolver(): WorkflowResolver | null;
|
|
112
|
-
type CapabilityId = "llm" | "workflow_resolver" | "document";
|
|
113
|
-
/**
|
|
114
|
-
* Which capabilities are currently satisfied.
|
|
115
|
-
*
|
|
116
|
-
* Exists so a host (or the CLI, or an agent over MCP) can answer "what does
|
|
117
|
-
* this graph need that I haven't wired?" BEFORE a run fails halfway through.
|
|
118
|
-
*/
|
|
119
|
-
declare function capabilityStatus(): Record<CapabilityId, boolean>;
|
|
120
|
-
|
|
121
49
|
/**
|
|
122
50
|
* `@fancy/subflow` — run another workflow and bring its result home.
|
|
123
51
|
*
|
|
@@ -170,7 +98,7 @@ declare function declaredRoutes(config: Record<string, unknown>): LlmRoute[];
|
|
|
170
98
|
* route, and always loudly.
|
|
171
99
|
*/
|
|
172
100
|
declare function resolveFallbackPort(routes: LlmRoute[], fallbackEnabled: boolean): string;
|
|
173
|
-
declare const
|
|
101
|
+
declare const llmRouterExecutor: NodeExecutor;
|
|
174
102
|
|
|
175
103
|
/**
|
|
176
104
|
* Rich user input — the injection point.
|
|
@@ -302,4 +230,4 @@ declare const BUILTIN_KINDS: NodeKindDefinition[];
|
|
|
302
230
|
*/
|
|
303
231
|
declare function buildNodeTypes(): NodeTypes;
|
|
304
232
|
|
|
305
|
-
export { BUILTIN_KINDS,
|
|
233
|
+
export { BUILTIN_KINDS, DEFAULT_MAX_DEPTH, LlmRoute, NodeKindDefinition, PortSpec, RegistryNode, type RichInputAdapter, RichInputPreview, type SubflowMode, buildNodeTypes, categoryAccent, declaredRoutes, defaultConfigFor, getNodeKind, getRichInputAdapter, isRichInputEnabled, kindIds, listNodeKinds, llmRouterExecutor as llmBranchExecutor, llmRouterExecutor, nodeConfig, onNodeKindsChanged, onRichInputAdapterChanged, registerBuiltinKinds, registerNodeKind, registerRichInputAdapter, resolveFallbackPort, resolveKindId, resolveNodePorts, resolvePortSpec, subflowExecutor, subflowMode, subflowPorts, validateConfig };
|
package/dist/registry/index.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import { a as NodeKindDefinition, P as PortSpec } from '../types-
|
|
2
|
-
export { C as ConfigField, b as CredentialConfigField, D as DocumentConfigField, E as ExpressionConfigField, J as JsonConfigField, K as KeyValueConfigField, N as NodeCategory, c as NumberConfigField, R as RenderBodyContext, d as RepeaterConfigField, e as RepeaterRowField, S as SelectConfigField, f as SwitchConfigField, T as TextConfigField, g as TextareaConfigField } from '../types-
|
|
3
|
-
import {
|
|
1
|
+
import { a as NodeKindDefinition, P as PortSpec } from '../types-CnnKPnpt.js';
|
|
2
|
+
export { C as ConfigField, b as CredentialConfigField, D as DocumentConfigField, E as ExpressionConfigField, J as JsonConfigField, K as KeyValueConfigField, N as NodeCategory, c as NumberConfigField, R as RenderBodyContext, d as RepeaterConfigField, e as RepeaterRowField, S as SelectConfigField, f as SwitchConfigField, T as TextConfigField, g as TextareaConfigField } from '../types-CnnKPnpt.js';
|
|
3
|
+
import { a as FlowNode, P as PortDescriptor, N as NodeExecutor } from '../types-fc0fFKkf.js';
|
|
4
|
+
import { b as LlmRoute } from '../capabilities-B6r1Snfj.js';
|
|
5
|
+
export { C as CapabilityId, a as LlmClient, c as LlmRouteChoice, L as LlmRouteRequest, W as WorkflowResolver, d as capabilityStatus, g as getLlmClient, e as getWorkflowResolver, r as registerLlmClient, f as registerWorkflowResolver } from '../capabilities-B6r1Snfj.js';
|
|
4
6
|
import * as react from 'react';
|
|
5
7
|
import { ComponentType, ReactNode } from 'react';
|
|
6
8
|
import { NodeProps, NodeTypes } from '@xyflow/react';
|
|
@@ -44,80 +46,6 @@ declare function resolveNodePorts(node: Pick<FlowNode, "data">, kind?: Pick<Node
|
|
|
44
46
|
outputs?: PortDescriptor[];
|
|
45
47
|
};
|
|
46
48
|
|
|
47
|
-
/**
|
|
48
|
-
* Host capabilities — the services core nodes need but must never depend on.
|
|
49
|
-
*
|
|
50
|
-
* A node that imports a provider SDK forces every consumer to install it: a
|
|
51
|
-
* workflow app that never calls a model should not inherit an LLM dependency.
|
|
52
|
-
* So core declares the CONTRACT and the host supplies the implementation, the
|
|
53
|
-
* same arrangement `renderDocumentField` already uses for documents.
|
|
54
|
-
*
|
|
55
|
-
* That keeps opinionated nodes in core without their opinions: `llm_branch`
|
|
56
|
-
* ships the routing semantics, port derivation and config UI, while whichever
|
|
57
|
-
* client the host registers — Prism, an OpenAI SDK, a local model, a fake in a
|
|
58
|
-
* test — decides how the question actually gets asked.
|
|
59
|
-
*
|
|
60
|
-
* Registration is deliberately explicit and typed per capability rather than a
|
|
61
|
-
* stringly-keyed bag, so a missing one is a clear error at the seam instead of
|
|
62
|
-
* an undefined somewhere downstream.
|
|
63
|
-
*/
|
|
64
|
-
type LlmRoute = {
|
|
65
|
-
port: string;
|
|
66
|
-
description?: string;
|
|
67
|
-
};
|
|
68
|
-
type LlmRouteRequest = {
|
|
69
|
-
/** Optional framing for the decision. */
|
|
70
|
-
system?: string;
|
|
71
|
-
/** What the model is deciding about. */
|
|
72
|
-
prompt: string;
|
|
73
|
-
/** The ports it must choose between. */
|
|
74
|
-
routes: LlmRoute[];
|
|
75
|
-
provider?: string;
|
|
76
|
-
model?: string;
|
|
77
|
-
/** Host-resolved credential reference, never a raw key. */
|
|
78
|
-
credential?: string;
|
|
79
|
-
};
|
|
80
|
-
type LlmRouteChoice = {
|
|
81
|
-
/** Must be one of the requested route ports. */
|
|
82
|
-
port: string;
|
|
83
|
-
/** Why — carried down the chosen port so a run is explainable afterwards. */
|
|
84
|
-
reason?: string;
|
|
85
|
-
};
|
|
86
|
-
/**
|
|
87
|
-
* The only thing core asks of an LLM: given routes, pick one.
|
|
88
|
-
*
|
|
89
|
-
* Deliberately not a general chat interface. A narrow contract is one a host
|
|
90
|
-
* can satisfy in a few lines over any SDK, and it keeps the choice
|
|
91
|
-
* machine-checkable — an implementation should constrain the model to the
|
|
92
|
-
* declared ports (structured output / enum) rather than parsing prose.
|
|
93
|
-
*/
|
|
94
|
-
type LlmClient = {
|
|
95
|
-
chooseRoute: (request: LlmRouteRequest) => Promise<LlmRouteChoice> | LlmRouteChoice;
|
|
96
|
-
};
|
|
97
|
-
/** Install the host's LLM client. Returns an unregister function. */
|
|
98
|
-
declare function registerLlmClient(client: LlmClient): () => void;
|
|
99
|
-
declare function getLlmClient(): LlmClient | null;
|
|
100
|
-
/**
|
|
101
|
-
* Resolve a workflow reference to a runnable graph.
|
|
102
|
-
*
|
|
103
|
-
* `subflow` names another workflow rather than embedding it, so the host owns
|
|
104
|
-
* where workflows live — a database, a file, an API. Returning null means "no
|
|
105
|
-
* such workflow", which the node reports as an error rather than silently
|
|
106
|
-
* running nothing.
|
|
107
|
-
*/
|
|
108
|
-
type WorkflowResolver = (ref: string) => Promise<FlowGraph | null> | FlowGraph | null;
|
|
109
|
-
/** Install the host's workflow resolver. Returns an unregister function. */
|
|
110
|
-
declare function registerWorkflowResolver(resolver: WorkflowResolver): () => void;
|
|
111
|
-
declare function getWorkflowResolver(): WorkflowResolver | null;
|
|
112
|
-
type CapabilityId = "llm" | "workflow_resolver" | "document";
|
|
113
|
-
/**
|
|
114
|
-
* Which capabilities are currently satisfied.
|
|
115
|
-
*
|
|
116
|
-
* Exists so a host (or the CLI, or an agent over MCP) can answer "what does
|
|
117
|
-
* this graph need that I haven't wired?" BEFORE a run fails halfway through.
|
|
118
|
-
*/
|
|
119
|
-
declare function capabilityStatus(): Record<CapabilityId, boolean>;
|
|
120
|
-
|
|
121
49
|
/**
|
|
122
50
|
* `@fancy/subflow` — run another workflow and bring its result home.
|
|
123
51
|
*
|
|
@@ -170,7 +98,7 @@ declare function declaredRoutes(config: Record<string, unknown>): LlmRoute[];
|
|
|
170
98
|
* route, and always loudly.
|
|
171
99
|
*/
|
|
172
100
|
declare function resolveFallbackPort(routes: LlmRoute[], fallbackEnabled: boolean): string;
|
|
173
|
-
declare const
|
|
101
|
+
declare const llmRouterExecutor: NodeExecutor;
|
|
174
102
|
|
|
175
103
|
/**
|
|
176
104
|
* Rich user input — the injection point.
|
|
@@ -302,4 +230,4 @@ declare const BUILTIN_KINDS: NodeKindDefinition[];
|
|
|
302
230
|
*/
|
|
303
231
|
declare function buildNodeTypes(): NodeTypes;
|
|
304
232
|
|
|
305
|
-
export { BUILTIN_KINDS,
|
|
233
|
+
export { BUILTIN_KINDS, DEFAULT_MAX_DEPTH, LlmRoute, NodeKindDefinition, PortSpec, RegistryNode, type RichInputAdapter, RichInputPreview, type SubflowMode, buildNodeTypes, categoryAccent, declaredRoutes, defaultConfigFor, getNodeKind, getRichInputAdapter, isRichInputEnabled, kindIds, listNodeKinds, llmRouterExecutor as llmBranchExecutor, llmRouterExecutor, nodeConfig, onNodeKindsChanged, onRichInputAdapterChanged, registerBuiltinKinds, registerNodeKind, registerRichInputAdapter, resolveFallbackPort, resolveKindId, resolveNodePorts, resolvePortSpec, subflowExecutor, subflowMode, subflowPorts, validateConfig };
|