@particle-academy/fancy-flow 0.27.0 → 0.28.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/index.d.cts CHANGED
@@ -15,7 +15,7 @@ import { FlowRunFeedEntry } from './runtime/index.cjs';
15
15
  export { HistoryController, UseFlowHistoryReturn, UseFlowRunOptions, UseFlowRunReturn, UseFlowStateReturn, applyOutputsToNodes, applyStatusesToNodes, createHistory, useFlowHistory, useFlowRun, useFlowState } from './runtime/index.cjs';
16
16
  export { R as RunOptions, a as RunResult, r as runFlow } from './run-flow-AXzOR-Wq.cjs';
17
17
  export { L as LEGACY_PAUSE_PREFIXES, a as PAUSE_PREFIX, P as PauseAwaiting, b as PauseSignal, d as decodePause, e as encodePause, i as isPause, p as pauseForHuman } from './pause-9iT4tCEV.cjs';
18
- import './capabilities-exVeADXz.cjs';
18
+ import './capabilities-BLOeLACV.cjs';
19
19
 
20
20
  type FlowCanvasProps = Omit<ReactFlowProps<FlowNode, Edge>, "nodes" | "edges" | "height"> & {
21
21
  nodes: FlowNode[];
package/dist/index.d.ts CHANGED
@@ -15,7 +15,7 @@ import { FlowRunFeedEntry } from './runtime/index.js';
15
15
  export { HistoryController, UseFlowHistoryReturn, UseFlowRunOptions, UseFlowRunReturn, UseFlowStateReturn, applyOutputsToNodes, applyStatusesToNodes, createHistory, useFlowHistory, useFlowRun, useFlowState } from './runtime/index.js';
16
16
  export { R as RunOptions, a as RunResult, r as runFlow } from './run-flow-9U-gaYVF.js';
17
17
  export { L as LEGACY_PAUSE_PREFIXES, a as PAUSE_PREFIX, P as PauseAwaiting, b as PauseSignal, d as decodePause, e as encodePause, i as isPause, p as pauseForHuman } from './pause-9iT4tCEV.js';
18
- import './capabilities-85Mjn4uW.js';
18
+ import './capabilities-BH7zmSQQ.js';
19
19
 
20
20
  type FlowCanvasProps = Omit<ReactFlowProps<FlowNode, Edge>, "nodes" | "edges" | "height"> & {
21
21
  nodes: FlowNode[];
package/dist/index.js CHANGED
@@ -1,11 +1,11 @@
1
- import { useFlowState, useFlowRun, useFlowHistory, applyOutputsToNodes, applyStatusesToNodes } from './chunk-OFW7GN3Y.js';
2
- export { applyOutputsToNodes, applyStatusesToNodes, createHistory, useFlowHistory, useFlowRun, useFlowState } from './chunk-OFW7GN3Y.js';
3
- import { buildNodeTypes, FlowEditorProvider, registerBuiltinKinds, NoteNode, createConnectionValidator } from './chunk-YOFCVO2W.js';
4
- export { ANY_PORT_TYPE, BUILTIN_KINDS, LaneNode, NoteNode, RegistryNode, buildNodeTypes, createConnectionValidator, defaultPortCompatibility, registerBuiltinKinds, useFlowEditor, useFlowEditorOptional } from './chunk-YOFCVO2W.js';
1
+ import { useFlowState, useFlowRun, useFlowHistory, applyOutputsToNodes, applyStatusesToNodes } from './chunk-HHFOHHAQ.js';
2
+ export { applyOutputsToNodes, applyStatusesToNodes, createHistory, useFlowHistory, useFlowRun, useFlowState } from './chunk-HHFOHHAQ.js';
3
+ import { buildNodeTypes, FlowEditorProvider, registerBuiltinKinds, NoteNode, createConnectionValidator } from './chunk-THU5LW22.js';
4
+ export { ANY_PORT_TYPE, BUILTIN_KINDS, LaneNode, NoteNode, RegistryNode, buildNodeTypes, createConnectionValidator, defaultPortCompatibility, registerBuiltinKinds, useFlowEditor, useFlowEditorOptional } from './chunk-THU5LW22.js';
5
5
  import { ReactFlowProvider, useReactFlow, addEdge, applyEdgeChanges, applyNodeChanges, Position, Handle, reconnectEdge, index, BackgroundVariant, Background, Controls, MiniMap, ViewportPortal } from './chunk-OWENS2H5.js';
6
6
  export { LEGACY_PAUSE_PREFIXES, PAUSE_PREFIX, decodePause, encodePause, isPause, pauseForHuman } from './chunk-UEOE6B52.js';
7
7
  import './chunk-USL4FMFU.js';
8
- export { runFlow } from './chunk-U2VJB7HR.js';
8
+ export { runFlow } from './chunk-RE7T5GKQ.js';
9
9
  import { workflowToBlob, importWorkflow, exportWorkflow } from './chunk-N5ICD2WZ.js';
10
10
  export { WORKFLOW_SCHEMA_URL, WORKFLOW_SCHEMA_VERSION, exportWorkflow, importWorkflow, migrateSchema, workflowToBlob } from './chunk-N5ICD2WZ.js';
11
11
  export { resolveNodePorts, resolvePortSpec } from './chunk-TITD5W4Y.js';
@@ -0,0 +1,72 @@
1
+ 'use strict';
2
+
3
+ // src/registry/capabilities.ts
4
+ function registerLlmClient(client) {
5
+ return () => {
6
+ };
7
+ }
8
+
9
+ // src/llm/prism.ts
10
+ function createPrismLlmClient(options) {
11
+ const doFetch = options.fetch ?? globalThis.fetch;
12
+ return {
13
+ async chooseRoute(request) {
14
+ if (typeof doFetch !== "function") {
15
+ throw new Error(
16
+ "fancy-flow/llm/prism: no fetch available. Pass `fetch` in the options for SSR or Node < 18."
17
+ );
18
+ }
19
+ const headers = {
20
+ "content-type": "application/json",
21
+ accept: "application/json",
22
+ ...options.headers ?? {}
23
+ };
24
+ if (options.csrf !== false) {
25
+ const token = readXsrfCookie();
26
+ if (token && !("x-xsrf-token" in lower(headers))) headers["X-XSRF-TOKEN"] = token;
27
+ }
28
+ const response = await doFetch(options.endpoint, {
29
+ method: "POST",
30
+ headers,
31
+ credentials: options.credentials ?? "same-origin",
32
+ body: JSON.stringify(request)
33
+ });
34
+ if (!response.ok) {
35
+ throw new Error(
36
+ `fancy-flow/llm/prism: ${options.endpoint} answered ${response.status}. The route must return { port, reason? } as JSON.`
37
+ );
38
+ }
39
+ const body = await response.json();
40
+ const port = typeof body?.port === "string" ? body.port : "";
41
+ const declared = request.routes.map((r) => r.port);
42
+ if (!declared.includes(port)) {
43
+ throw new Error(
44
+ `fancy-flow/llm/prism: ${options.endpoint} returned port ${JSON.stringify(port)}, which is not one of the declared routes (${declared.join(", ") || "none"}).`
45
+ );
46
+ }
47
+ return {
48
+ port,
49
+ reason: typeof body?.reason === "string" && body.reason ? body.reason : "chosen by Prism"
50
+ };
51
+ }
52
+ };
53
+ }
54
+ function usePrismForLlmBranch(options) {
55
+ return registerLlmClient(createPrismLlmClient(options));
56
+ }
57
+ var lower = (h) => Object.fromEntries(Object.entries(h).map(([k, v]) => [k.toLowerCase(), v]));
58
+ function readXsrfCookie() {
59
+ if (typeof document === "undefined") return null;
60
+ const match = document.cookie.match(/(?:^|;\s*)XSRF-TOKEN=([^;]+)/);
61
+ if (!match) return null;
62
+ try {
63
+ return decodeURIComponent(match[1]);
64
+ } catch {
65
+ return match[1];
66
+ }
67
+ }
68
+
69
+ exports.createPrismLlmClient = createPrismLlmClient;
70
+ exports.usePrismForLlmBranch = usePrismForLlmBranch;
71
+ //# sourceMappingURL=prism.cjs.map
72
+ //# sourceMappingURL=prism.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/registry/capabilities.ts","../../src/llm/prism.ts"],"names":[],"mappings":"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","file":"prism.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 * Why a workflow reference could not be resolved.\n *\n * `missing` and `version-mismatch` are deliberately distinct. Collapsing them\n * into a bare null makes \"no such workflow\" indistinguishable from \"that\n * workflow exists, but it is not the one you pinned\" — and the second wants an\n * error naming both versions, because it is the interesting failure.\n */\nexport type WorkflowResolutionFailure = {\n reason: \"missing\" | \"version-mismatch\";\n /** The version the host actually holds, when it holds one. */\n available?: number;\n message?: string;\n};\n\nexport type WorkflowResolution = FlowGraph | WorkflowResolutionFailure | null;\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.\n *\n * ## Why `version` is here\n *\n * A workflow another workflow depends on is an INTERFACE, and interfaces need\n * pins. Without a version, a parent goes on calling `invoice-triage`, someone\n * edits that child, and the parent now runs different logic having reported\n * success the whole time — correct-looking, no error, wrong behaviour. The same\n * failure family as the 0.9.0 routing divergence.\n *\n * The parameter lives on the resolver rather than being encoded into the ref\n * string (`invoice-triage@3`) because a stringly-typed protocol is one every\n * host invents differently — the \"three vocabularies for one node\" problem.\n *\n * Raised by the MOIC Suite consumer, whose `workflow_ref` pins versions and\n * fails loudly on mismatch. Their point: a host COULD NOT implement pinning\n * before this, because the node had no way to ask and the resolver no way to\n * receive.\n *\n * Returning `null` still means \"no such workflow\". Return a\n * {@link WorkflowResolutionFailure} to distinguish a version mismatch.\n */\nexport type WorkflowResolver = (\n ref: string,\n version?: number,\n) => Promise<WorkflowResolution> | WorkflowResolution;\n\n/** Narrow a resolver's return value to an explicit failure. */\nexport function isResolutionFailure(value: WorkflowResolution): value is WorkflowResolutionFailure {\n return (\n typeof value === \"object\" &&\n value !== null &&\n \"reason\" in value &&\n (value as WorkflowResolutionFailure).reason !== undefined\n );\n}\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/prism` — route `llm_branch` through a\n * Prism-backed endpoint on your own server.\n *\n * Prism is a PHP library, so there is nothing to import here and no SDK to\n * install: this adapter POSTs the routing question to a route you own, and that\n * route answers it with `Prism\\Prism`. That keeps provider config, API keys,\n * fallbacks and token accounting in exactly one place — the same place the rest\n * of a Laravel app already uses — instead of adding a second LLM stack in the\n * browser.\n *\n * It is also the lighter of the two shipped adapters. `/llm/vercel-ai` needs the\n * `ai` package as an optional peer; this one needs nothing but `fetch`.\n *\n * ```ts\n * import { usePrismForLlmBranch } from \"@particle-academy/fancy-flow/llm/prism\";\n *\n * usePrismForLlmBranch({ endpoint: \"/api/flow/llm-route\" });\n * ```\n *\n * The endpoint receives the `LlmRouteRequest` as JSON and must answer with\n * `{ \"port\": \"<one of the requested ports>\", \"reason\": \"…\" }`. That is the same\n * shape `fancy-flow-php`'s `LlmRouteRequest` / `LlmRouteChoice` already model,\n * so ONE route can serve both the editor's preview runs and server-side\n * execution — the two runtimes ask the identical question.\n *\n * A sketch of the Laravel side:\n *\n * ```php\n * Route::post('/api/flow/llm-route', function (Request $r) {\n * $request = LlmRouteRequest::fromArray($r->all());\n * return response()->json(app(PrismLlmClient::class)->chooseRoute($request));\n * });\n * ```\n */\nimport { registerLlmClient, type LlmClient, type LlmRouteChoice, type LlmRouteRequest } from \"../registry/capabilities\";\n\nexport type PrismLlmOptions = {\n /** The route on your server that answers the routing question. */\n endpoint: string;\n /** Extra headers merged into the request (auth, tenancy, …). */\n headers?: Record<string, string>;\n /**\n * Cookie policy. Defaults to `\"same-origin\"` so a session-authenticated\n * Laravel route works without extra wiring.\n */\n credentials?: RequestCredentials;\n /**\n * Send Laravel's `X-XSRF-TOKEN` header, read from the `XSRF-TOKEN` cookie.\n * Default `true` — without it a session-auth POST is rejected by the CSRF\n * middleware, which is the first thing that goes wrong otherwise.\n */\n csrf?: boolean;\n /** Swap the fetch implementation (tests, SSR, an instrumented client). */\n fetch?: typeof globalThis.fetch;\n};\n\n/**\n * Build the client without registering it — handy for tests, or for a host that\n * wants to wrap it.\n */\nexport function createPrismLlmClient(options: PrismLlmOptions): LlmClient {\n const doFetch = options.fetch ?? globalThis.fetch;\n\n return {\n async chooseRoute(request: LlmRouteRequest): Promise<LlmRouteChoice> {\n if (typeof doFetch !== \"function\") {\n throw new Error(\n \"fancy-flow/llm/prism: no fetch available. Pass `fetch` in the options for SSR or Node < 18.\",\n );\n }\n\n const headers: Record<string, string> = {\n \"content-type\": \"application/json\",\n accept: \"application/json\",\n ...(options.headers ?? {}),\n };\n\n if (options.csrf !== false) {\n const token = readXsrfCookie();\n if (token && !(\"x-xsrf-token\" in lower(headers))) headers[\"X-XSRF-TOKEN\"] = token;\n }\n\n const response = await doFetch(options.endpoint, {\n method: \"POST\",\n headers,\n credentials: options.credentials ?? \"same-origin\",\n body: JSON.stringify(request),\n });\n\n if (!response.ok) {\n throw new Error(\n `fancy-flow/llm/prism: ${options.endpoint} answered ${response.status}. ` +\n \"The route must return { port, reason? } as JSON.\",\n );\n }\n\n const body = (await response.json()) as Partial<LlmRouteChoice> | null;\n const port = typeof body?.port === \"string\" ? body.port : \"\";\n\n // A port nobody declared must NEVER route. Emitting on a port with no edge\n // silently ends the branch and the run reports success having done\n // nothing, so a bad answer has to fail loudly here rather than quietly\n // downstream. The node's own fallback handling is the backstop, not this.\n const declared = request.routes.map((r) => r.port);\n if (!declared.includes(port)) {\n throw new Error(\n `fancy-flow/llm/prism: ${options.endpoint} returned port ${JSON.stringify(port)}, ` +\n `which is not one of the declared routes (${declared.join(\", \") || \"none\"}).`,\n );\n }\n\n return {\n port,\n reason: typeof body?.reason === \"string\" && body.reason ? body.reason : \"chosen by Prism\",\n };\n },\n };\n}\n\n/** Build the client and install it. Returns an unregister function. */\nexport function usePrismForLlmBranch(options: PrismLlmOptions): () => void {\n return registerLlmClient(createPrismLlmClient(options));\n}\n\nconst lower = (h: Record<string, string>): Record<string, string> =>\n Object.fromEntries(Object.entries(h).map(([k, v]) => [k.toLowerCase(), v]));\n\n/** Laravel URL-encodes the XSRF cookie; the header wants it decoded. */\nfunction readXsrfCookie(): string | null {\n if (typeof document === \"undefined\") return null;\n const match = document.cookie.match(/(?:^|;\\s*)XSRF-TOKEN=([^;]+)/);\n if (!match) return null;\n try {\n return decodeURIComponent(match[1]!);\n } catch {\n return match[1]!;\n }\n}\n"]}
@@ -0,0 +1,68 @@
1
+ import { L as LlmClient } from '../capabilities-BLOeLACV.cjs';
2
+ import '../types-CMSrWVYM.cjs';
3
+ import '@xyflow/react';
4
+
5
+ /**
6
+ * `@particle-academy/fancy-flow/llm/prism` — route `llm_branch` through a
7
+ * Prism-backed endpoint on your own server.
8
+ *
9
+ * Prism is a PHP library, so there is nothing to import here and no SDK to
10
+ * install: this adapter POSTs the routing question to a route you own, and that
11
+ * route answers it with `Prism\Prism`. That keeps provider config, API keys,
12
+ * fallbacks and token accounting in exactly one place — the same place the rest
13
+ * of a Laravel app already uses — instead of adding a second LLM stack in the
14
+ * browser.
15
+ *
16
+ * It is also the lighter of the two shipped adapters. `/llm/vercel-ai` needs the
17
+ * `ai` package as an optional peer; this one needs nothing but `fetch`.
18
+ *
19
+ * ```ts
20
+ * import { usePrismForLlmBranch } from "@particle-academy/fancy-flow/llm/prism";
21
+ *
22
+ * usePrismForLlmBranch({ endpoint: "/api/flow/llm-route" });
23
+ * ```
24
+ *
25
+ * The endpoint receives the `LlmRouteRequest` as JSON and must answer with
26
+ * `{ "port": "<one of the requested ports>", "reason": "…" }`. That is the same
27
+ * shape `fancy-flow-php`'s `LlmRouteRequest` / `LlmRouteChoice` already model,
28
+ * so ONE route can serve both the editor's preview runs and server-side
29
+ * execution — the two runtimes ask the identical question.
30
+ *
31
+ * A sketch of the Laravel side:
32
+ *
33
+ * ```php
34
+ * Route::post('/api/flow/llm-route', function (Request $r) {
35
+ * $request = LlmRouteRequest::fromArray($r->all());
36
+ * return response()->json(app(PrismLlmClient::class)->chooseRoute($request));
37
+ * });
38
+ * ```
39
+ */
40
+
41
+ type PrismLlmOptions = {
42
+ /** The route on your server that answers the routing question. */
43
+ endpoint: string;
44
+ /** Extra headers merged into the request (auth, tenancy, …). */
45
+ headers?: Record<string, string>;
46
+ /**
47
+ * Cookie policy. Defaults to `"same-origin"` so a session-authenticated
48
+ * Laravel route works without extra wiring.
49
+ */
50
+ credentials?: RequestCredentials;
51
+ /**
52
+ * Send Laravel's `X-XSRF-TOKEN` header, read from the `XSRF-TOKEN` cookie.
53
+ * Default `true` — without it a session-auth POST is rejected by the CSRF
54
+ * middleware, which is the first thing that goes wrong otherwise.
55
+ */
56
+ csrf?: boolean;
57
+ /** Swap the fetch implementation (tests, SSR, an instrumented client). */
58
+ fetch?: typeof globalThis.fetch;
59
+ };
60
+ /**
61
+ * Build the client without registering it — handy for tests, or for a host that
62
+ * wants to wrap it.
63
+ */
64
+ declare function createPrismLlmClient(options: PrismLlmOptions): LlmClient;
65
+ /** Build the client and install it. Returns an unregister function. */
66
+ declare function usePrismForLlmBranch(options: PrismLlmOptions): () => void;
67
+
68
+ export { type PrismLlmOptions, createPrismLlmClient, usePrismForLlmBranch };
@@ -0,0 +1,68 @@
1
+ import { L as LlmClient } from '../capabilities-BH7zmSQQ.js';
2
+ import '../types-CMSrWVYM.js';
3
+ import '@xyflow/react';
4
+
5
+ /**
6
+ * `@particle-academy/fancy-flow/llm/prism` — route `llm_branch` through a
7
+ * Prism-backed endpoint on your own server.
8
+ *
9
+ * Prism is a PHP library, so there is nothing to import here and no SDK to
10
+ * install: this adapter POSTs the routing question to a route you own, and that
11
+ * route answers it with `Prism\Prism`. That keeps provider config, API keys,
12
+ * fallbacks and token accounting in exactly one place — the same place the rest
13
+ * of a Laravel app already uses — instead of adding a second LLM stack in the
14
+ * browser.
15
+ *
16
+ * It is also the lighter of the two shipped adapters. `/llm/vercel-ai` needs the
17
+ * `ai` package as an optional peer; this one needs nothing but `fetch`.
18
+ *
19
+ * ```ts
20
+ * import { usePrismForLlmBranch } from "@particle-academy/fancy-flow/llm/prism";
21
+ *
22
+ * usePrismForLlmBranch({ endpoint: "/api/flow/llm-route" });
23
+ * ```
24
+ *
25
+ * The endpoint receives the `LlmRouteRequest` as JSON and must answer with
26
+ * `{ "port": "<one of the requested ports>", "reason": "…" }`. That is the same
27
+ * shape `fancy-flow-php`'s `LlmRouteRequest` / `LlmRouteChoice` already model,
28
+ * so ONE route can serve both the editor's preview runs and server-side
29
+ * execution — the two runtimes ask the identical question.
30
+ *
31
+ * A sketch of the Laravel side:
32
+ *
33
+ * ```php
34
+ * Route::post('/api/flow/llm-route', function (Request $r) {
35
+ * $request = LlmRouteRequest::fromArray($r->all());
36
+ * return response()->json(app(PrismLlmClient::class)->chooseRoute($request));
37
+ * });
38
+ * ```
39
+ */
40
+
41
+ type PrismLlmOptions = {
42
+ /** The route on your server that answers the routing question. */
43
+ endpoint: string;
44
+ /** Extra headers merged into the request (auth, tenancy, …). */
45
+ headers?: Record<string, string>;
46
+ /**
47
+ * Cookie policy. Defaults to `"same-origin"` so a session-authenticated
48
+ * Laravel route works without extra wiring.
49
+ */
50
+ credentials?: RequestCredentials;
51
+ /**
52
+ * Send Laravel's `X-XSRF-TOKEN` header, read from the `XSRF-TOKEN` cookie.
53
+ * Default `true` — without it a session-auth POST is rejected by the CSRF
54
+ * middleware, which is the first thing that goes wrong otherwise.
55
+ */
56
+ csrf?: boolean;
57
+ /** Swap the fetch implementation (tests, SSR, an instrumented client). */
58
+ fetch?: typeof globalThis.fetch;
59
+ };
60
+ /**
61
+ * Build the client without registering it — handy for tests, or for a host that
62
+ * wants to wrap it.
63
+ */
64
+ declare function createPrismLlmClient(options: PrismLlmOptions): LlmClient;
65
+ /** Build the client and install it. Returns an unregister function. */
66
+ declare function usePrismForLlmBranch(options: PrismLlmOptions): () => void;
67
+
68
+ export { type PrismLlmOptions, createPrismLlmClient, usePrismForLlmBranch };
@@ -0,0 +1,65 @@
1
+ import { registerLlmClient } from '../chunk-USL4FMFU.js';
2
+
3
+ // src/llm/prism.ts
4
+ function createPrismLlmClient(options) {
5
+ const doFetch = options.fetch ?? globalThis.fetch;
6
+ return {
7
+ async chooseRoute(request) {
8
+ if (typeof doFetch !== "function") {
9
+ throw new Error(
10
+ "fancy-flow/llm/prism: no fetch available. Pass `fetch` in the options for SSR or Node < 18."
11
+ );
12
+ }
13
+ const headers = {
14
+ "content-type": "application/json",
15
+ accept: "application/json",
16
+ ...options.headers ?? {}
17
+ };
18
+ if (options.csrf !== false) {
19
+ const token = readXsrfCookie();
20
+ if (token && !("x-xsrf-token" in lower(headers))) headers["X-XSRF-TOKEN"] = token;
21
+ }
22
+ const response = await doFetch(options.endpoint, {
23
+ method: "POST",
24
+ headers,
25
+ credentials: options.credentials ?? "same-origin",
26
+ body: JSON.stringify(request)
27
+ });
28
+ if (!response.ok) {
29
+ throw new Error(
30
+ `fancy-flow/llm/prism: ${options.endpoint} answered ${response.status}. The route must return { port, reason? } as JSON.`
31
+ );
32
+ }
33
+ const body = await response.json();
34
+ const port = typeof body?.port === "string" ? body.port : "";
35
+ const declared = request.routes.map((r) => r.port);
36
+ if (!declared.includes(port)) {
37
+ throw new Error(
38
+ `fancy-flow/llm/prism: ${options.endpoint} returned port ${JSON.stringify(port)}, which is not one of the declared routes (${declared.join(", ") || "none"}).`
39
+ );
40
+ }
41
+ return {
42
+ port,
43
+ reason: typeof body?.reason === "string" && body.reason ? body.reason : "chosen by Prism"
44
+ };
45
+ }
46
+ };
47
+ }
48
+ function usePrismForLlmBranch(options) {
49
+ return registerLlmClient(createPrismLlmClient(options));
50
+ }
51
+ var lower = (h) => Object.fromEntries(Object.entries(h).map(([k, v]) => [k.toLowerCase(), v]));
52
+ function readXsrfCookie() {
53
+ if (typeof document === "undefined") return null;
54
+ const match = document.cookie.match(/(?:^|;\s*)XSRF-TOKEN=([^;]+)/);
55
+ if (!match) return null;
56
+ try {
57
+ return decodeURIComponent(match[1]);
58
+ } catch {
59
+ return match[1];
60
+ }
61
+ }
62
+
63
+ export { createPrismLlmClient, usePrismForLlmBranch };
64
+ //# sourceMappingURL=prism.js.map
65
+ //# sourceMappingURL=prism.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/llm/prism.ts"],"names":[],"mappings":"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","file":"prism.js","sourcesContent":["/**\n * `@particle-academy/fancy-flow/llm/prism` — route `llm_branch` through a\n * Prism-backed endpoint on your own server.\n *\n * Prism is a PHP library, so there is nothing to import here and no SDK to\n * install: this adapter POSTs the routing question to a route you own, and that\n * route answers it with `Prism\\Prism`. That keeps provider config, API keys,\n * fallbacks and token accounting in exactly one place — the same place the rest\n * of a Laravel app already uses — instead of adding a second LLM stack in the\n * browser.\n *\n * It is also the lighter of the two shipped adapters. `/llm/vercel-ai` needs the\n * `ai` package as an optional peer; this one needs nothing but `fetch`.\n *\n * ```ts\n * import { usePrismForLlmBranch } from \"@particle-academy/fancy-flow/llm/prism\";\n *\n * usePrismForLlmBranch({ endpoint: \"/api/flow/llm-route\" });\n * ```\n *\n * The endpoint receives the `LlmRouteRequest` as JSON and must answer with\n * `{ \"port\": \"<one of the requested ports>\", \"reason\": \"…\" }`. That is the same\n * shape `fancy-flow-php`'s `LlmRouteRequest` / `LlmRouteChoice` already model,\n * so ONE route can serve both the editor's preview runs and server-side\n * execution — the two runtimes ask the identical question.\n *\n * A sketch of the Laravel side:\n *\n * ```php\n * Route::post('/api/flow/llm-route', function (Request $r) {\n * $request = LlmRouteRequest::fromArray($r->all());\n * return response()->json(app(PrismLlmClient::class)->chooseRoute($request));\n * });\n * ```\n */\nimport { registerLlmClient, type LlmClient, type LlmRouteChoice, type LlmRouteRequest } from \"../registry/capabilities\";\n\nexport type PrismLlmOptions = {\n /** The route on your server that answers the routing question. */\n endpoint: string;\n /** Extra headers merged into the request (auth, tenancy, …). */\n headers?: Record<string, string>;\n /**\n * Cookie policy. Defaults to `\"same-origin\"` so a session-authenticated\n * Laravel route works without extra wiring.\n */\n credentials?: RequestCredentials;\n /**\n * Send Laravel's `X-XSRF-TOKEN` header, read from the `XSRF-TOKEN` cookie.\n * Default `true` — without it a session-auth POST is rejected by the CSRF\n * middleware, which is the first thing that goes wrong otherwise.\n */\n csrf?: boolean;\n /** Swap the fetch implementation (tests, SSR, an instrumented client). */\n fetch?: typeof globalThis.fetch;\n};\n\n/**\n * Build the client without registering it — handy for tests, or for a host that\n * wants to wrap it.\n */\nexport function createPrismLlmClient(options: PrismLlmOptions): LlmClient {\n const doFetch = options.fetch ?? globalThis.fetch;\n\n return {\n async chooseRoute(request: LlmRouteRequest): Promise<LlmRouteChoice> {\n if (typeof doFetch !== \"function\") {\n throw new Error(\n \"fancy-flow/llm/prism: no fetch available. Pass `fetch` in the options for SSR or Node < 18.\",\n );\n }\n\n const headers: Record<string, string> = {\n \"content-type\": \"application/json\",\n accept: \"application/json\",\n ...(options.headers ?? {}),\n };\n\n if (options.csrf !== false) {\n const token = readXsrfCookie();\n if (token && !(\"x-xsrf-token\" in lower(headers))) headers[\"X-XSRF-TOKEN\"] = token;\n }\n\n const response = await doFetch(options.endpoint, {\n method: \"POST\",\n headers,\n credentials: options.credentials ?? \"same-origin\",\n body: JSON.stringify(request),\n });\n\n if (!response.ok) {\n throw new Error(\n `fancy-flow/llm/prism: ${options.endpoint} answered ${response.status}. ` +\n \"The route must return { port, reason? } as JSON.\",\n );\n }\n\n const body = (await response.json()) as Partial<LlmRouteChoice> | null;\n const port = typeof body?.port === \"string\" ? body.port : \"\";\n\n // A port nobody declared must NEVER route. Emitting on a port with no edge\n // silently ends the branch and the run reports success having done\n // nothing, so a bad answer has to fail loudly here rather than quietly\n // downstream. The node's own fallback handling is the backstop, not this.\n const declared = request.routes.map((r) => r.port);\n if (!declared.includes(port)) {\n throw new Error(\n `fancy-flow/llm/prism: ${options.endpoint} returned port ${JSON.stringify(port)}, ` +\n `which is not one of the declared routes (${declared.join(\", \") || \"none\"}).`,\n );\n }\n\n return {\n port,\n reason: typeof body?.reason === \"string\" && body.reason ? body.reason : \"chosen by Prism\",\n };\n },\n };\n}\n\n/** Build the client and install it. Returns an unregister function. */\nexport function usePrismForLlmBranch(options: PrismLlmOptions): () => void {\n return registerLlmClient(createPrismLlmClient(options));\n}\n\nconst lower = (h: Record<string, string>): Record<string, string> =>\n Object.fromEntries(Object.entries(h).map(([k, v]) => [k.toLowerCase(), v]));\n\n/** Laravel URL-encodes the XSRF cookie; the header wants it decoded. */\nfunction readXsrfCookie(): string | null {\n if (typeof document === \"undefined\") return null;\n const match = document.cookie.match(/(?:^|;\\s*)XSRF-TOKEN=([^;]+)/);\n if (!match) return null;\n try {\n return decodeURIComponent(match[1]!);\n } catch {\n return match[1]!;\n }\n}\n"]}
@@ -1,5 +1,5 @@
1
1
  import { LanguageModel } from 'ai';
2
- import { L as LlmRouteRequest, a as LlmClient } from '../capabilities-exVeADXz.cjs';
2
+ import { a as LlmRouteRequest, L as LlmClient } from '../capabilities-BLOeLACV.cjs';
3
3
  import '../types-CMSrWVYM.cjs';
4
4
  import '@xyflow/react';
5
5
 
@@ -1,5 +1,5 @@
1
1
  import { LanguageModel } from 'ai';
2
- import { L as LlmRouteRequest, a as LlmClient } from '../capabilities-85Mjn4uW.js';
2
+ import { a as LlmRouteRequest, L as LlmClient } from '../capabilities-BH7zmSQQ.js';
3
3
  import '../types-CMSrWVYM.js';
4
4
  import '@xyflow/react';
5
5
 
@@ -2,8 +2,8 @@ import { a as NodeKindDefinition, P as PortSpec } from '../types-B_pxRqfw.cjs';
2
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-B_pxRqfw.cjs';
3
3
  import { P as PortDescriptor, a as FlowNode, N as NodeExecutor } from '../types-CMSrWVYM.cjs';
4
4
  import { Connection, Edge, NodeProps, NodeTypes } from '@xyflow/react';
5
- import { b as LlmRoute } from '../capabilities-exVeADXz.cjs';
6
- export { C as CapabilityId, a as LlmClient, c as LlmRouteChoice, L as LlmRouteRequest, W as WorkflowResolution, d as WorkflowResolutionFailure, e as WorkflowResolver, f as capabilityStatus, g as getLlmClient, h as getWorkflowResolver, i as isResolutionFailure, r as registerLlmClient, j as registerWorkflowResolver } from '../capabilities-exVeADXz.cjs';
5
+ import { b as LlmRoute } from '../capabilities-BLOeLACV.cjs';
6
+ export { C as CapabilityId, L as LlmClient, c as LlmRouteChoice, a as LlmRouteRequest, W as WorkflowResolution, d as WorkflowResolutionFailure, e as WorkflowResolver, f as capabilityStatus, g as getLlmClient, h as getWorkflowResolver, i as isResolutionFailure, r as registerLlmClient, j as registerWorkflowResolver } from '../capabilities-BLOeLACV.cjs';
7
7
  export { L as LEGACY_PAUSE_PREFIXES, a as PAUSE_PREFIX, P as PauseAwaiting, b as PauseSignal, d as decodePause, e as encodePause, i as isPause, p as pauseForHuman } from '../pause-9iT4tCEV.cjs';
8
8
  import * as react from 'react';
9
9
  import { ComponentType, ReactNode } from 'react';
@@ -2,8 +2,8 @@ import { a as NodeKindDefinition, P as PortSpec } from '../types-BGtR3k9J.js';
2
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-BGtR3k9J.js';
3
3
  import { P as PortDescriptor, a as FlowNode, N as NodeExecutor } from '../types-CMSrWVYM.js';
4
4
  import { Connection, Edge, NodeProps, NodeTypes } from '@xyflow/react';
5
- import { b as LlmRoute } from '../capabilities-85Mjn4uW.js';
6
- export { C as CapabilityId, a as LlmClient, c as LlmRouteChoice, L as LlmRouteRequest, W as WorkflowResolution, d as WorkflowResolutionFailure, e as WorkflowResolver, f as capabilityStatus, g as getLlmClient, h as getWorkflowResolver, i as isResolutionFailure, r as registerLlmClient, j as registerWorkflowResolver } from '../capabilities-85Mjn4uW.js';
5
+ import { b as LlmRoute } from '../capabilities-BH7zmSQQ.js';
6
+ export { C as CapabilityId, L as LlmClient, c as LlmRouteChoice, a as LlmRouteRequest, W as WorkflowResolution, d as WorkflowResolutionFailure, e as WorkflowResolver, f as capabilityStatus, g as getLlmClient, h as getWorkflowResolver, i as isResolutionFailure, r as registerLlmClient, j as registerWorkflowResolver } from '../capabilities-BH7zmSQQ.js';
7
7
  export { L as LEGACY_PAUSE_PREFIXES, a as PAUSE_PREFIX, P as PauseAwaiting, b as PauseSignal, d as decodePause, e as encodePause, i as isPause, p as pauseForHuman } from '../pause-9iT4tCEV.js';
8
8
  import * as react from 'react';
9
9
  import { ComponentType, ReactNode } from 'react';
package/dist/registry.cjs CHANGED
@@ -416,8 +416,9 @@ function collectInputs(node, incoming, portValues, initial) {
416
416
  const inputs = { ...initial[node.id] ?? {} };
417
417
  for (const e of incoming) {
418
418
  const portId = e.targetHandle ?? "in";
419
- const val = portValues.get(`${e.source}:${e.sourceHandle ?? "out"}`);
420
- inputs[portId] = val;
419
+ const key = `${e.source}:${e.sourceHandle ?? "out"}`;
420
+ if (!portValues.has(key)) continue;
421
+ inputs[portId] = portValues.get(key);
421
422
  }
422
423
  return inputs;
423
424
  }