@particle-academy/fancy-flow 0.11.0 → 0.12.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/{chunk-YNOI7ONN.js → chunk-IK5DS5JP.js} +258 -51
- package/dist/chunk-IK5DS5JP.js.map +1 -0
- package/dist/{chunk-2NQT2A3I.js → chunk-L4AX73Q6.js} +5 -4
- package/dist/chunk-L4AX73Q6.js.map +1 -0
- package/dist/{chunk-3GMBLHTM.js → chunk-MFMRTRPO.js} +3 -3
- package/dist/{chunk-3GMBLHTM.js.map → chunk-MFMRTRPO.js.map} +1 -1
- package/dist/engine.cjs +3 -2
- package/dist/engine.cjs.map +1 -1
- package/dist/engine.d.cts +4 -2
- package/dist/engine.d.ts +4 -2
- package/dist/engine.js +1 -1
- package/dist/index.cjs +396 -215
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.js +5 -5
- package/dist/registry/index.d.cts +132 -4
- package/dist/registry/index.d.ts +132 -4
- package/dist/registry.cjs +455 -93
- 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/runtime.cjs +3 -2
- package/dist/runtime.cjs.map +1 -1
- package/dist/runtime.js +2 -2
- package/dist/schema/index.d.cts +1 -1
- package/dist/schema/index.d.ts +1 -1
- package/dist/{types-BS3Gwnkq.d.cts → types-Dg2REAz5.d.cts} +6 -0
- package/dist/{types-BS3Gwnkq.d.ts → types-Dg2REAz5.d.ts} +6 -0
- package/dist/{types-BwLr5tZV.d.cts → types-DnQ4TEHN.d.cts} +1 -1
- package/dist/{types-CsEe3EQl.d.ts → types-DtXOwXjA.d.ts} +1 -1
- package/dist/ux.d.cts +2 -2
- package/dist/ux.d.ts +2 -2
- package/package.json +1 -1
- package/dist/chunk-2NQT2A3I.js.map +0 -1
- package/dist/chunk-YNOI7ONN.js.map +0 -1
package/dist/index.d.cts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
import { ReactNode, CSSProperties, ComponentType } from 'react';
|
|
3
3
|
import { ReactFlowProps, Edge, BackgroundVariant, NodeProps, NodeTypes } from '@xyflow/react';
|
|
4
|
-
import { F as FlowNode, a as FlowGraph, b as NodeRunStatus, E as ExecutorRegistry } from './types-
|
|
5
|
-
export { A as ActionNodeData, B as BaseNodeData, D as DecisionNodeData, c as FlowEdge, d as FlowNodeData, e as FlowNodeKind, N as NodeExecutor, f as NoteNodeData, O as OutputNodeData, P as PortDescriptor, R as RunEvent, S as SubgraphNodeData, T as TriggerNodeData } from './types-
|
|
4
|
+
import { F as FlowNode, a as FlowGraph, b as NodeRunStatus, E as ExecutorRegistry } from './types-Dg2REAz5.cjs';
|
|
5
|
+
export { A as ActionNodeData, B as BaseNodeData, D as DecisionNodeData, c as FlowEdge, d as FlowNodeData, e as FlowNodeKind, N as NodeExecutor, f as NoteNodeData, O as OutputNodeData, P as PortDescriptor, R as RunEvent, S as SubgraphNodeData, T as TriggerNodeData } from './types-Dg2REAz5.cjs';
|
|
6
6
|
import { WorkflowSchema, WorkflowMetadata } from './schema/index.cjs';
|
|
7
7
|
export { ImportIssue, ImportOptions, ImportResult, WORKFLOW_SCHEMA_URL, WORKFLOW_SCHEMA_VERSION, WorkflowSchemaEdge, WorkflowSchemaNode, exportWorkflow, importWorkflow, workflowToBlob } from './schema/index.cjs';
|
|
8
|
-
import { N as NodeCategory, a as NodeKindDefinition, C as ConfigField } from './types-
|
|
9
|
-
export { D as DocumentConfigField, K as KeyValueConfigField, P as PortSpec, R as RenderBodyContext, d as RepeaterConfigField, e as RepeaterRowField } from './types-
|
|
8
|
+
import { N as NodeCategory, a as NodeKindDefinition, C as ConfigField } from './types-DnQ4TEHN.cjs';
|
|
9
|
+
export { D as DocumentConfigField, K as KeyValueConfigField, P as PortSpec, R as RenderBodyContext, d as RepeaterConfigField, e as RepeaterRowField } from './types-DnQ4TEHN.cjs';
|
|
10
10
|
import { FlowRunFeedEntry } from './runtime/index.cjs';
|
|
11
11
|
export { UseFlowRunOptions, UseFlowRunReturn, UseFlowStateReturn, applyStatusesToNodes, useFlowRun, useFlowState } from './runtime/index.cjs';
|
|
12
12
|
export { BUILTIN_KINDS, RegistryNode, RichInputAdapter, RichInputPreview, buildNodeTypes, categoryAccent, defaultConfigFor, getNodeKind, getRichInputAdapter, isRichInputEnabled, listNodeKinds, onNodeKindsChanged, onRichInputAdapterChanged, registerBuiltinKinds, registerNodeKind, registerRichInputAdapter, resolveNodePorts, resolvePortSpec, validateConfig } from './registry/index.cjs';
|
package/dist/index.d.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
import { ReactNode, CSSProperties, ComponentType } from 'react';
|
|
3
3
|
import { ReactFlowProps, Edge, BackgroundVariant, NodeProps, NodeTypes } from '@xyflow/react';
|
|
4
|
-
import { F as FlowNode, a as FlowGraph, b as NodeRunStatus, E as ExecutorRegistry } from './types-
|
|
5
|
-
export { A as ActionNodeData, B as BaseNodeData, D as DecisionNodeData, c as FlowEdge, d as FlowNodeData, e as FlowNodeKind, N as NodeExecutor, f as NoteNodeData, O as OutputNodeData, P as PortDescriptor, R as RunEvent, S as SubgraphNodeData, T as TriggerNodeData } from './types-
|
|
4
|
+
import { F as FlowNode, a as FlowGraph, b as NodeRunStatus, E as ExecutorRegistry } from './types-Dg2REAz5.js';
|
|
5
|
+
export { A as ActionNodeData, B as BaseNodeData, D as DecisionNodeData, c as FlowEdge, d as FlowNodeData, e as FlowNodeKind, N as NodeExecutor, f as NoteNodeData, O as OutputNodeData, P as PortDescriptor, R as RunEvent, S as SubgraphNodeData, T as TriggerNodeData } from './types-Dg2REAz5.js';
|
|
6
6
|
import { WorkflowSchema, WorkflowMetadata } from './schema/index.js';
|
|
7
7
|
export { ImportIssue, ImportOptions, ImportResult, WORKFLOW_SCHEMA_URL, WORKFLOW_SCHEMA_VERSION, WorkflowSchemaEdge, WorkflowSchemaNode, exportWorkflow, importWorkflow, workflowToBlob } from './schema/index.js';
|
|
8
|
-
import { N as NodeCategory, a as NodeKindDefinition, C as ConfigField } from './types-
|
|
9
|
-
export { D as DocumentConfigField, K as KeyValueConfigField, P as PortSpec, R as RenderBodyContext, d as RepeaterConfigField, e as RepeaterRowField } from './types-
|
|
8
|
+
import { N as NodeCategory, a as NodeKindDefinition, C as ConfigField } from './types-DtXOwXjA.js';
|
|
9
|
+
export { D as DocumentConfigField, K as KeyValueConfigField, P as PortSpec, R as RenderBodyContext, d as RepeaterConfigField, e as RepeaterRowField } from './types-DtXOwXjA.js';
|
|
10
10
|
import { FlowRunFeedEntry } from './runtime/index.js';
|
|
11
11
|
export { UseFlowRunOptions, UseFlowRunReturn, UseFlowStateReturn, applyStatusesToNodes, useFlowRun, useFlowState } from './runtime/index.js';
|
|
12
12
|
export { BUILTIN_KINDS, RegistryNode, RichInputAdapter, RichInputPreview, buildNodeTypes, categoryAccent, defaultConfigFor, getNodeKind, getRichInputAdapter, isRichInputEnabled, listNodeKinds, onNodeKindsChanged, onRichInputAdapterChanged, registerBuiltinKinds, registerNodeKind, registerRichInputAdapter, resolveNodePorts, resolvePortSpec, validateConfig } from './registry/index.js';
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { useFlowState, useFlowRun, applyStatusesToNodes } from './chunk-
|
|
2
|
-
export { applyStatusesToNodes, useFlowRun, useFlowState } from './chunk-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
export { BUILTIN_KINDS, RegistryNode, buildNodeTypes, registerBuiltinKinds } from './chunk-YNOI7ONN.js';
|
|
1
|
+
import { useFlowState, useFlowRun, applyStatusesToNodes } from './chunk-MFMRTRPO.js';
|
|
2
|
+
export { applyStatusesToNodes, useFlowRun, useFlowState } from './chunk-MFMRTRPO.js';
|
|
3
|
+
import { buildNodeTypes, registerBuiltinKinds } from './chunk-IK5DS5JP.js';
|
|
4
|
+
export { BUILTIN_KINDS, RegistryNode, buildNodeTypes, registerBuiltinKinds } from './chunk-IK5DS5JP.js';
|
|
6
5
|
import { ReactFlowProvider, useReactFlow, addEdge, applyEdgeChanges, applyNodeChanges, Position, Handle, index, BackgroundVariant, Background, Controls, MiniMap } from './chunk-NF6NPY5N.js';
|
|
6
|
+
export { runFlow } from './chunk-L4AX73Q6.js';
|
|
7
7
|
import { workflowToBlob, importWorkflow, exportWorkflow } from './chunk-D6W5FMCT.js';
|
|
8
8
|
export { WORKFLOW_SCHEMA_URL, WORKFLOW_SCHEMA_VERSION, exportWorkflow, importWorkflow, workflowToBlob } from './chunk-D6W5FMCT.js';
|
|
9
9
|
export { resolveNodePorts, resolvePortSpec } from './chunk-TITD5W4Y.js';
|
|
@@ -1,6 +1,6 @@
|
|
|
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 { F as FlowNode, P as PortDescriptor } from '../types-
|
|
1
|
+
import { a as NodeKindDefinition, P as PortSpec } from '../types-DnQ4TEHN.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-DnQ4TEHN.cjs';
|
|
3
|
+
import { F as FlowNode, P as PortDescriptor, a as FlowGraph, N as NodeExecutor } from '../types-Dg2REAz5.cjs';
|
|
4
4
|
import * as react from 'react';
|
|
5
5
|
import { ComponentType, ReactNode } from 'react';
|
|
6
6
|
import { NodeProps, NodeTypes } from '@xyflow/react';
|
|
@@ -44,6 +44,134 @@ declare function resolveNodePorts(node: Pick<FlowNode, "data">, kind?: Pick<Node
|
|
|
44
44
|
outputs?: PortDescriptor[];
|
|
45
45
|
};
|
|
46
46
|
|
|
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
|
+
/**
|
|
122
|
+
* `@fancy/subflow` — run another workflow and bring its result home.
|
|
123
|
+
*
|
|
124
|
+
* Core, not marketplace: it introduces no third-party dependency. It runs a
|
|
125
|
+
* child graph through the very same engine, so the only thing it needs from the
|
|
126
|
+
* host is where workflows live (`registerWorkflowResolver`).
|
|
127
|
+
*
|
|
128
|
+
* Three output modes, because both halves are genuinely useful:
|
|
129
|
+
*
|
|
130
|
+
* - `output` — the child's outputs arrive on `out` when it finishes.
|
|
131
|
+
* - `stream` — the child's events are forwarded live on `stream` as they
|
|
132
|
+
* happen, so a parent can show progress instead of a spinner.
|
|
133
|
+
* - `both` — stream while running AND deliver the final outputs.
|
|
134
|
+
*
|
|
135
|
+
* Recursion is guarded by depth. A workflow that references itself (directly or
|
|
136
|
+
* through a chain) would otherwise recurse until the stack dies, which surfaces
|
|
137
|
+
* as an opaque crash rather than "you built a loop".
|
|
138
|
+
*/
|
|
139
|
+
declare const DEFAULT_MAX_DEPTH = 8;
|
|
140
|
+
type SubflowMode = "output" | "stream" | "both";
|
|
141
|
+
declare function subflowMode(config: Record<string, unknown>): SubflowMode;
|
|
142
|
+
/** Ports follow the mode — `stream` only exists when something streams. */
|
|
143
|
+
declare function subflowPorts(config: Record<string, unknown>): {
|
|
144
|
+
id: string;
|
|
145
|
+
label: string;
|
|
146
|
+
}[];
|
|
147
|
+
declare const subflowExecutor: NodeExecutor;
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* `@fancy/llm_branch` — a SHUTTLE, not an engine.
|
|
151
|
+
*
|
|
152
|
+
* It carries the declared routes and the decision prompt out to whatever LLM
|
|
153
|
+
* client the host registered, and carries the chosen port back down the graph.
|
|
154
|
+
* It contains no provider SDK, no prompt engineering, no response parsing and
|
|
155
|
+
* no retry policy — all of that belongs to the host's client, which is what
|
|
156
|
+
* lets this node live in core without every consumer inheriting an LLM
|
|
157
|
+
* dependency.
|
|
158
|
+
*
|
|
159
|
+
* The one thing it does own is graph integrity, because that is a workflow
|
|
160
|
+
* concern rather than an AI one: a port the model invents must never route.
|
|
161
|
+
*/
|
|
162
|
+
/** Read the node's declared routes out of config. */
|
|
163
|
+
declare function declaredRoutes(config: Record<string, unknown>): LlmRoute[];
|
|
164
|
+
/**
|
|
165
|
+
* Where a run goes when the model returns a port that was never offered.
|
|
166
|
+
*
|
|
167
|
+
* Emitting on a port with no edge silently ends the branch — the worst failure
|
|
168
|
+
* mode in a workflow engine, because the run reports success having done
|
|
169
|
+
* nothing. So: the `fallback` port when it exists, else the first declared
|
|
170
|
+
* route, and always loudly.
|
|
171
|
+
*/
|
|
172
|
+
declare function resolveFallbackPort(routes: LlmRoute[], fallbackEnabled: boolean): string;
|
|
173
|
+
declare const llmBranchExecutor: NodeExecutor;
|
|
174
|
+
|
|
47
175
|
/**
|
|
48
176
|
* Rich user input — the injection point.
|
|
49
177
|
*
|
|
@@ -174,4 +302,4 @@ declare const BUILTIN_KINDS: NodeKindDefinition[];
|
|
|
174
302
|
*/
|
|
175
303
|
declare function buildNodeTypes(): NodeTypes;
|
|
176
304
|
|
|
177
|
-
export { BUILTIN_KINDS, NodeKindDefinition, PortSpec, RegistryNode, type RichInputAdapter, RichInputPreview, buildNodeTypes, categoryAccent, defaultConfigFor, getNodeKind, getRichInputAdapter, isRichInputEnabled, kindIds, listNodeKinds, nodeConfig, onNodeKindsChanged, onRichInputAdapterChanged, registerBuiltinKinds, registerNodeKind, registerRichInputAdapter, resolveKindId, resolveNodePorts, resolvePortSpec, validateConfig };
|
|
305
|
+
export { BUILTIN_KINDS, type CapabilityId, DEFAULT_MAX_DEPTH, type LlmClient, type LlmRoute, type LlmRouteChoice, type LlmRouteRequest, NodeKindDefinition, PortSpec, RegistryNode, type RichInputAdapter, RichInputPreview, type SubflowMode, type WorkflowResolver, buildNodeTypes, capabilityStatus, categoryAccent, declaredRoutes, defaultConfigFor, getLlmClient, getNodeKind, getRichInputAdapter, getWorkflowResolver, isRichInputEnabled, kindIds, listNodeKinds, llmBranchExecutor, nodeConfig, onNodeKindsChanged, onRichInputAdapterChanged, registerBuiltinKinds, registerLlmClient, registerNodeKind, registerRichInputAdapter, registerWorkflowResolver, resolveFallbackPort, resolveKindId, resolveNodePorts, resolvePortSpec, subflowExecutor, subflowMode, subflowPorts, validateConfig };
|
package/dist/registry/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
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 { F as FlowNode, P as PortDescriptor } from '../types-
|
|
1
|
+
import { a as NodeKindDefinition, P as PortSpec } from '../types-DtXOwXjA.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-DtXOwXjA.js';
|
|
3
|
+
import { F as FlowNode, P as PortDescriptor, a as FlowGraph, N as NodeExecutor } from '../types-Dg2REAz5.js';
|
|
4
4
|
import * as react from 'react';
|
|
5
5
|
import { ComponentType, ReactNode } from 'react';
|
|
6
6
|
import { NodeProps, NodeTypes } from '@xyflow/react';
|
|
@@ -44,6 +44,134 @@ declare function resolveNodePorts(node: Pick<FlowNode, "data">, kind?: Pick<Node
|
|
|
44
44
|
outputs?: PortDescriptor[];
|
|
45
45
|
};
|
|
46
46
|
|
|
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
|
+
/**
|
|
122
|
+
* `@fancy/subflow` — run another workflow and bring its result home.
|
|
123
|
+
*
|
|
124
|
+
* Core, not marketplace: it introduces no third-party dependency. It runs a
|
|
125
|
+
* child graph through the very same engine, so the only thing it needs from the
|
|
126
|
+
* host is where workflows live (`registerWorkflowResolver`).
|
|
127
|
+
*
|
|
128
|
+
* Three output modes, because both halves are genuinely useful:
|
|
129
|
+
*
|
|
130
|
+
* - `output` — the child's outputs arrive on `out` when it finishes.
|
|
131
|
+
* - `stream` — the child's events are forwarded live on `stream` as they
|
|
132
|
+
* happen, so a parent can show progress instead of a spinner.
|
|
133
|
+
* - `both` — stream while running AND deliver the final outputs.
|
|
134
|
+
*
|
|
135
|
+
* Recursion is guarded by depth. A workflow that references itself (directly or
|
|
136
|
+
* through a chain) would otherwise recurse until the stack dies, which surfaces
|
|
137
|
+
* as an opaque crash rather than "you built a loop".
|
|
138
|
+
*/
|
|
139
|
+
declare const DEFAULT_MAX_DEPTH = 8;
|
|
140
|
+
type SubflowMode = "output" | "stream" | "both";
|
|
141
|
+
declare function subflowMode(config: Record<string, unknown>): SubflowMode;
|
|
142
|
+
/** Ports follow the mode — `stream` only exists when something streams. */
|
|
143
|
+
declare function subflowPorts(config: Record<string, unknown>): {
|
|
144
|
+
id: string;
|
|
145
|
+
label: string;
|
|
146
|
+
}[];
|
|
147
|
+
declare const subflowExecutor: NodeExecutor;
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* `@fancy/llm_branch` — a SHUTTLE, not an engine.
|
|
151
|
+
*
|
|
152
|
+
* It carries the declared routes and the decision prompt out to whatever LLM
|
|
153
|
+
* client the host registered, and carries the chosen port back down the graph.
|
|
154
|
+
* It contains no provider SDK, no prompt engineering, no response parsing and
|
|
155
|
+
* no retry policy — all of that belongs to the host's client, which is what
|
|
156
|
+
* lets this node live in core without every consumer inheriting an LLM
|
|
157
|
+
* dependency.
|
|
158
|
+
*
|
|
159
|
+
* The one thing it does own is graph integrity, because that is a workflow
|
|
160
|
+
* concern rather than an AI one: a port the model invents must never route.
|
|
161
|
+
*/
|
|
162
|
+
/** Read the node's declared routes out of config. */
|
|
163
|
+
declare function declaredRoutes(config: Record<string, unknown>): LlmRoute[];
|
|
164
|
+
/**
|
|
165
|
+
* Where a run goes when the model returns a port that was never offered.
|
|
166
|
+
*
|
|
167
|
+
* Emitting on a port with no edge silently ends the branch — the worst failure
|
|
168
|
+
* mode in a workflow engine, because the run reports success having done
|
|
169
|
+
* nothing. So: the `fallback` port when it exists, else the first declared
|
|
170
|
+
* route, and always loudly.
|
|
171
|
+
*/
|
|
172
|
+
declare function resolveFallbackPort(routes: LlmRoute[], fallbackEnabled: boolean): string;
|
|
173
|
+
declare const llmBranchExecutor: NodeExecutor;
|
|
174
|
+
|
|
47
175
|
/**
|
|
48
176
|
* Rich user input — the injection point.
|
|
49
177
|
*
|
|
@@ -174,4 +302,4 @@ declare const BUILTIN_KINDS: NodeKindDefinition[];
|
|
|
174
302
|
*/
|
|
175
303
|
declare function buildNodeTypes(): NodeTypes;
|
|
176
304
|
|
|
177
|
-
export { BUILTIN_KINDS, NodeKindDefinition, PortSpec, RegistryNode, type RichInputAdapter, RichInputPreview, buildNodeTypes, categoryAccent, defaultConfigFor, getNodeKind, getRichInputAdapter, isRichInputEnabled, kindIds, listNodeKinds, nodeConfig, onNodeKindsChanged, onRichInputAdapterChanged, registerBuiltinKinds, registerNodeKind, registerRichInputAdapter, resolveKindId, resolveNodePorts, resolvePortSpec, validateConfig };
|
|
305
|
+
export { BUILTIN_KINDS, type CapabilityId, DEFAULT_MAX_DEPTH, type LlmClient, type LlmRoute, type LlmRouteChoice, type LlmRouteRequest, NodeKindDefinition, PortSpec, RegistryNode, type RichInputAdapter, RichInputPreview, type SubflowMode, type WorkflowResolver, buildNodeTypes, capabilityStatus, categoryAccent, declaredRoutes, defaultConfigFor, getLlmClient, getNodeKind, getRichInputAdapter, getWorkflowResolver, isRichInputEnabled, kindIds, listNodeKinds, llmBranchExecutor, nodeConfig, onNodeKindsChanged, onRichInputAdapterChanged, registerBuiltinKinds, registerLlmClient, registerNodeKind, registerRichInputAdapter, registerWorkflowResolver, resolveFallbackPort, resolveKindId, resolveNodePorts, resolvePortSpec, subflowExecutor, subflowMode, subflowPorts, validateConfig };
|