@picoflow/ezgraph 0.0.10 → 0.0.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +53 -33
- package/dist/framework/base-graph.js +1 -1
- package/dist/framework/graph-node.js +1 -1
- package/dist/framework/llm-node.js +1 -0
- package/dist/index.js +1 -1
- package/dist/types/npmlib/src/index.d.ts +1 -1
- package/dist/types/src/framework/base-graph.d.ts +1 -1
- package/dist/types/src/framework/graph-node.d.ts +1 -2
- package/dist/types/src/framework/{conversation-node.d.ts → llm-node.d.ts} +2 -2
- package/package.json +1 -1
- package/dist/framework/conversation-node.js +0 -1
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@ Use it when a conversation is an application: it gathers validated facts,
|
|
|
10
10
|
applies business policy, presents exact reviews or decisions, and resumes safely
|
|
11
11
|
across requests.
|
|
12
12
|
|
|
13
|
-
> **Upgrading:** Version 0.0.
|
|
13
|
+
> **Upgrading:** Version 0.0.12 contains breaking changes from 0.0.11; see the [EZGraph documentation](https://www.picoflow.io/ezgraph) before upgrading.
|
|
14
14
|
|
|
15
15
|
## Install
|
|
16
16
|
|
|
@@ -38,13 +38,13 @@ EZGraph is ESM. TypeScript applications should use `module` and
|
|
|
38
38
|
Each node owns the facts it validates. A tool handler saves valid state, then
|
|
39
39
|
returns exactly one response:
|
|
40
40
|
|
|
41
|
-
| Return
|
|
42
|
-
|
|
|
43
|
-
| `stay(feedback)`
|
|
44
|
-
| `go(TargetNode)`
|
|
45
|
-
| `direct(content)`
|
|
46
|
-
| `directTo(TargetNode, content)` | Return exact content and make the target the next user-turn node.
|
|
47
|
-
| `finish(content)`
|
|
41
|
+
| Return | Use it for |
|
|
42
|
+
| ------------------------------- | -------------------------------------------------------------------------------------- |
|
|
43
|
+
| `stay(feedback)` | Validation failed; continue collecting in the current node. |
|
|
44
|
+
| `go(TargetNode)` | Enter another registered conversational node now. |
|
|
45
|
+
| `direct(content)` | Return exact code-owned content, such as a policy decision, price table, or ticket ID. |
|
|
46
|
+
| `directTo(TargetNode, content)` | Return exact content and make the target the next user-turn node. |
|
|
47
|
+
| `finish(content)` | Return exact final content and complete the conversation. |
|
|
48
48
|
|
|
49
49
|
There is no per-turn context object, post-tool transition hook, synthetic tool
|
|
50
50
|
result, or automatic outcome router to maintain.
|
|
@@ -55,7 +55,10 @@ Keep the node registry next to the graph. It is converted into the annotated
|
|
|
55
55
|
LangGraph state; each node channel is replaced when that node saves state.
|
|
56
56
|
|
|
57
57
|
```ts
|
|
58
|
-
import {
|
|
58
|
+
import {
|
|
59
|
+
createGraphStateAnnotation,
|
|
60
|
+
type NodeStateValue,
|
|
61
|
+
} from "@picoflow/ezgraph";
|
|
59
62
|
import { CityNode } from "./nodes/city.node.js";
|
|
60
63
|
|
|
61
64
|
export type WeatherGraphNodes = {
|
|
@@ -65,22 +68,25 @@ export type WeatherGraphNodes = {
|
|
|
65
68
|
|
|
66
69
|
export const WeatherGraphState = createGraphStateAnnotation(
|
|
67
70
|
CityNode.name,
|
|
68
|
-
() => ({} as WeatherGraphNodes
|
|
71
|
+
() => ({}) as WeatherGraphNodes,
|
|
69
72
|
);
|
|
70
73
|
|
|
71
74
|
export type WeatherGraphStateType = typeof WeatherGraphState.State;
|
|
72
75
|
```
|
|
73
76
|
|
|
74
|
-
## Write
|
|
77
|
+
## Write an LLM node
|
|
75
78
|
|
|
76
|
-
`
|
|
79
|
+
`LlmNode` supports both one-request extraction and multi-turn chat. A single
|
|
80
|
+
request may contain several model/tool rounds; the node does not require another
|
|
81
|
+
user message. Return `finish()` to complete the workflow. The standard class
|
|
82
|
+
normally needs only the graph-state type. The handler below
|
|
77
83
|
validates model input in code, saves the owned state channel, and uses
|
|
78
84
|
`direct()` for an exact result.
|
|
79
85
|
|
|
80
86
|
```ts
|
|
81
87
|
import { z } from "zod";
|
|
82
88
|
import {
|
|
83
|
-
|
|
89
|
+
LlmNode,
|
|
84
90
|
Tool,
|
|
85
91
|
direct,
|
|
86
92
|
stay,
|
|
@@ -91,17 +97,19 @@ import type { WeatherGraphStateType } from "../weather-graph.state.js";
|
|
|
91
97
|
|
|
92
98
|
type CityInput = { city: string };
|
|
93
99
|
|
|
94
|
-
export class CityNode extends
|
|
100
|
+
export class CityNode extends LlmNode<WeatherGraphStateType> {
|
|
95
101
|
getPrompt(): string {
|
|
96
102
|
return "Collect a city, then call capture_city.";
|
|
97
103
|
}
|
|
98
104
|
|
|
99
105
|
defineTool(): readonly ToolDefinition<CityInput>[] {
|
|
100
|
-
return [
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
106
|
+
return [
|
|
107
|
+
{
|
|
108
|
+
name: "capture_city",
|
|
109
|
+
description: "Save a city for the forecast.",
|
|
110
|
+
schema: z.object({ city: z.string().min(2) }),
|
|
111
|
+
},
|
|
112
|
+
];
|
|
105
113
|
}
|
|
106
114
|
|
|
107
115
|
@Tool("capture_city")
|
|
@@ -130,15 +138,15 @@ For deterministic policy that needs state owned by another node, read
|
|
|
130
138
|
|
|
131
139
|
## Handle model failures
|
|
132
140
|
|
|
133
|
-
`
|
|
141
|
+
`LlmNode` supports the same response/error hooks as PicoFlow. The
|
|
134
142
|
three error hooks also exist on `BaseGraph` as graph-wide fallback policy:
|
|
135
143
|
|
|
136
|
-
| Hook
|
|
137
|
-
|
|
|
138
|
-
| `checkResponse(candidate)`
|
|
139
|
-
| `onLlmBlocked(context)`
|
|
140
|
-
| `shouldRetryLlmError(context)` | Controls retries for thrown invocation errors. Node first, then graph on `undefined`, then the configured retry budget. `false` stops immediately.
|
|
141
|
-
| `onLlmError(context)`
|
|
144
|
+
| Hook | Behavior |
|
|
145
|
+
| ------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
146
|
+
| `checkResponse(candidate)` | Receives the raw AI message before tool dispatch or acceptance. `true` rejects and retries; `false` accepts. Node-only. |
|
|
147
|
+
| `onLlmBlocked(context)` | Handles prompt blocks and blocked candidates, including candidates with text or tools. Node first, then graph on `null`; two `null` results propagate `LlmResponseBlockedError`. |
|
|
148
|
+
| `shouldRetryLlmError(context)` | Controls retries for thrown invocation errors. Node first, then graph on `undefined`, then the configured retry budget. `false` stops immediately. |
|
|
149
|
+
| `onLlmError(context)` | Handles terminal `invocation_error`, `empty_response`, or `response_rejected` failures. Node first, then graph on `null`; two `null` results propagate the failure. |
|
|
142
150
|
|
|
143
151
|
Contexts identify `nodeId`, provider and model. Ordinary error contexts also
|
|
144
152
|
include attempt counts and the stop cause; rejected/empty contexts retain
|
|
@@ -149,21 +157,25 @@ empty truncated response enters `onLlmError()` without a corrective nudge.
|
|
|
149
157
|
|
|
150
158
|
```ts
|
|
151
159
|
import {
|
|
152
|
-
|
|
160
|
+
LlmNode,
|
|
153
161
|
direct,
|
|
154
162
|
type LlmBlockedContext,
|
|
155
163
|
type LlmErrorContext,
|
|
156
164
|
type LlmErrorResponse,
|
|
157
165
|
} from "@picoflow/ezgraph";
|
|
158
166
|
|
|
159
|
-
class SupportNode extends
|
|
160
|
-
getPrompt(): string {
|
|
167
|
+
class SupportNode extends LlmNode<SupportState> {
|
|
168
|
+
getPrompt(): string {
|
|
169
|
+
return "Help the customer with their request.";
|
|
170
|
+
}
|
|
161
171
|
|
|
162
172
|
override async onLlmBlocked(_context: LlmBlockedContext) {
|
|
163
173
|
return direct("I cannot help with that request.");
|
|
164
174
|
}
|
|
165
175
|
|
|
166
|
-
override async onLlmError(
|
|
176
|
+
override async onLlmError(
|
|
177
|
+
context: LlmErrorContext,
|
|
178
|
+
): Promise<LlmErrorResponse | null> {
|
|
167
179
|
if (context.kind === "invocation_error") {
|
|
168
180
|
return direct("The service is unavailable. Please try again.");
|
|
169
181
|
}
|
|
@@ -190,7 +202,7 @@ retries. Retries wait 500 ms and honor cancellation. Empty candidates retain
|
|
|
190
202
|
Model retries happen within a round; accepted tool turns advance the agent
|
|
191
203
|
loop. Token usage includes rejected and empty candidates.
|
|
192
204
|
|
|
193
|
-
These hooks are wired into `
|
|
205
|
+
These hooks are wired into `LlmNode`'s shared loop. Plain `GraphNode`
|
|
194
206
|
and direct gateway calls retain their existing behavior, including
|
|
195
207
|
`GraphNode.onEmptyModelResponse()`. Typed `DecisionNode` validation and
|
|
196
208
|
`onDecisionError()` remain separate.
|
|
@@ -285,7 +297,10 @@ import {
|
|
|
285
297
|
type GraphDefinition,
|
|
286
298
|
type LlmGateway,
|
|
287
299
|
} from "@picoflow/ezgraph";
|
|
288
|
-
import {
|
|
300
|
+
import {
|
|
301
|
+
WeatherGraphState,
|
|
302
|
+
type WeatherGraphStateType,
|
|
303
|
+
} from "./weather-graph.state.js";
|
|
289
304
|
import { CityNode } from "./nodes/city.node.js";
|
|
290
305
|
import { WeatherNode } from "./nodes/weather.node.js";
|
|
291
306
|
|
|
@@ -294,7 +309,6 @@ export class WeatherGraph extends BaseGraph<WeatherGraphStateType> {
|
|
|
294
309
|
return {
|
|
295
310
|
llmConfig: ModelCatalog.model("openai:gpt-5.4", { retries: 2 }),
|
|
296
311
|
endNode: GRAPH_END_NODE,
|
|
297
|
-
initialHistorySpace: "weather",
|
|
298
312
|
historySpaces: [
|
|
299
313
|
[CityNode, "weather"],
|
|
300
314
|
[WeatherNode, "weather"],
|
|
@@ -322,6 +336,12 @@ const result = await engine.run({
|
|
|
322
336
|
});
|
|
323
337
|
```
|
|
324
338
|
|
|
339
|
+
The first turn uses the initial `currentNode` declared by
|
|
340
|
+
`createGraphStateAnnotation(CityNode.id(), ...)`. EZGraph resolves that cursor
|
|
341
|
+
before appending the first user message, then uses the node's `historySpaces`
|
|
342
|
+
mapping (`"weather"` here). Later turns use the persisted current node. Nodes
|
|
343
|
+
without a mapping use `"default"`; no separate initial history setting is needed.
|
|
344
|
+
|
|
325
345
|
`GraphEngine.create()` loads the selected session store from environment
|
|
326
346
|
configuration. The result contains an HTTP-ready status/body envelope and a
|
|
327
347
|
session ID for the next customer turn.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var c=Object.defineProperty;var n=(o,e)=>c(o,"name",{value:e,configurable:!0});import{GraphLlm as p}from"./graph-llm.config.js";import{GraphNodeRuntime as
|
|
1
|
+
var c=Object.defineProperty;var n=(o,e)=>c(o,"name",{value:e,configurable:!0});import{GraphLlm as p}from"./graph-llm.config.js";import{GraphNodeRuntime as u}from"./graph-node.js";import{StateGraphExt as l}from"./state-graph-ext.js";import{assertEmptyResponseRetries as g,assertMaxAgentRounds as f,DEFAULT_CONVERSATION_POLICY as h,DEFAULT_MAX_AGENT_ROUNDS as y}from"./conversation-runner.js";import{assertLlmTimeoutMs as w}from"./llm-gateway.js";import{assertGraphSchemaVersion as m,assertStableId as E,stableClassId as a}from"./stable-id.js";class d{static{n(this,"BaseGraph")}llmGateway;static id(){return this.name}static assertName(e){return d.assertId(e)}static assertId(e){return E(e,"graph")}name;id;schemaVersion;llmConfig;requiresUserMessage;responseMode;maxAgentRounds;conversationPolicy;compiledGraph;runtime;nodeMap=new Map;endNode;migrations;constructor(e,r){this.llmGateway=e;const t=new.target;this.name=t.name,this.id=a(t,"graph"),this.schemaVersion=m(r.schemaVersion??1),this.migrations=N(r.migrations,this.schemaVersion),this.llmConfig=p.resolveConfig(r.llmConfig),this.requiresUserMessage=r.requiresUserMessage??!0,this.responseMode=r.responseMode??"chat",this.maxAgentRounds=f(r.maxAgentRounds??y),this.conversationPolicy=v(r),this.endNode=r.endNode,this.runtime=new u(r.endNode,this.llmConfig,R(r.historySpaces),this.responseMode,this.maxAgentRounds,this.conversationPolicy,r.decisionConfig,s=>this.onDecisionError(s),{onLlmBlocked:n(s=>this.onLlmBlocked(s),"onLlmBlocked"),shouldRetryLlmError:n(s=>this.shouldRetryLlmError(s),"shouldRetryLlmError"),onLlmError:n(s=>this.onLlmError(s),"onLlmError")})}async onLlmBlocked(e){return null}shouldRetryLlmError(e){}async onLlmError(e){return null}onDecisionError(e){return null}attachDecisionProviderResolver(e){this.runtime.attachDecisionProviderResolver(e)}get graph(){return this.compiledGraph??=this.buildGraph()}prepareInput(e,r){const t=e?.currentNode??this.initialNodeId(),s=this.runtime.historySpace(t);return{...e??{},currentNode:t,histories:{...e?.histories??{},[s]:[...e?.histories[s]??[],r]}}}initialNodeId(){const e=this.graph.channels?.currentNode;if(!e||typeof e.fromCheckpoint!="function")throw new Error(`Graph '${this.id}' must declare a currentNode default before preparing its first message.`);let r;try{r=e.fromCheckpoint().get()}catch(t){throw new Error(`Graph '${this.id}' must declare a currentNode default before preparing its first message.`,{cause:t})}if(typeof r!="string"||!r.trim())throw new Error(`Graph '${this.id}' must declare a non-empty currentNode default.`);return r}saveNodeState(e,r){this.runtime.saveNode(a(e,"graph node"),r)}async restoreSessionDoc(e){if(e.graph.id!==this.id)throw new Error(`Session '${e.id}' belongs to graph '${e.graph.id}', not '${this.id}'. Start a new session to run a different graph.`);if(e.graph.schemaVersion>this.schemaVersion)throw new Error(`Session '${e.id}' uses graph '${this.id}' schema version ${e.graph.schemaVersion}, but this runtime supports version ${this.schemaVersion}.`);let r=e;for(;r.graph.schemaVersion<this.schemaVersion;){const t=r.graph.schemaVersion,s=this.migrations.get(t);if(!s)throw new Error(`Graph '${this.id}' has no migration from schema version ${t} to ${t+1}.`);const i=await s.migrate(r);if(i.graph.id!==this.id)throw new Error(`Graph '${this.id}' migration from schema version ${t} changed the persisted graph ID.`);r={...i,graph:{...i.graph,schemaVersion:t+1}}}return await this.onRestoreSessionDoc(r)}async onRestoreSessionDoc(e){return e}idleMs(e){const r=Date.parse(e.modifiedAt);return Number.isFinite(r)?Date.now()-r:0}createStateGraph(e){return new l(e,(r=>this.createNode(r)),this.endNode)}createNode(e){const r=new e(this.llmGateway,this.runtime),t=a(e,"graph node");if(r.id!==t)throw new Error(`Graph node factory for '${t}' constructs '${r.id}'.`);if(this.nodeMap.has(r.id))throw new Error(`Graph node '${r.id}' is already registered.`);return this.nodeMap.set(r.id,r),this.runtime.registerTools(r.id,r.defineTool()),Object.freeze(r),r}}function v(o){const e=o.emptyResponseRecovery===void 0?h.emptyResponseRecovery:o.emptyResponseRecovery;return e&&g(e.retries),{...o.llmTimeoutMs===void 0?{}:{llmTimeoutMs:w(o.llmTimeoutMs)},emptyHistorySeed:o.emptyHistorySeed===void 0?h.emptyHistorySeed:o.emptyHistorySeed,emptyResponseRecovery:e}}n(v,"resolveConversationPolicy");function R(o){return Object.fromEntries((o??[]).map(([e,r])=>[a(e,"graph node"),r]))}n(R,"historySpaceMap");function N(o,e){const r=new Map;let t;for(const s of o??[]){const i=m(s.from);if(i>=e)throw new Error(`A graph migration from version ${i} cannot target schema version ${e}.`);if(r.has(i))throw new Error(`More than one graph migration starts at version ${i}.`);if(t!==void 0&&i!==t+1)throw new Error(`Graph migrations must be ordered and contiguous; version ${t} must be followed by ${t+1}.`);r.set(i,s),t=i}return r}n(N,"migrationMap");export{d as BaseGraph};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var E=Object.defineProperty;var i=(n,e)=>E(n,"name",{value:e,configurable:!0});import"reflect-metadata";import{AIMessage as p,HumanMessage as R}from"@langchain/core/messages";import{Command as c,END as f,isCommand as w}from"@langchain/langgraph";import{GraphLlm as u}from"./graph-llm.config.js";import{GraphStateSupport as T}from"./graph-state-support.js";import{ConversationRunner as O,DEFAULT_CONVERSATION_POLICY as H,DEFAULT_MAX_AGENT_ROUNDS as L,EmptyModelResponseError as $}from"./conversation-runner.js";import{currentTurnSignal as A}from"./turn-context.js";import{combineGraphNodeUpdates as b}from"./node-outcome.js";import{parseJsonObjectResponse as S}from"./json-response.js";import{stableClassId as g}from"./stable-id.js";import{SessionLogWarning as G}from"./session-log.js";import{isToolResponse as N,stay as k}from"./tool-response.js";import{invocationGraphState as I,hasNodeInvocation as M,invocationHistory as P,invocationNodeState as _,invocationUpdate as D,appendInvocationHistory as U,replaceInvocationNodeState as j,saveInvocationNodeState as x,withNodeInvocation as J}from"./node-invocation.js";import{DecisionConfigurationError as q,resolveDecisionOptions as F}from"./decision/decision-types.js";import{GRAPH_NODE_LOCAL_STATE as fe}from"./node-outcome.js";const z=Symbol("GRAPH_NODE_CLASS"),y=Symbol("graph-node-tool-handlers");function ce(n){if(!n.trim())throw new Error("A tool handler requires a tool name.");return(e,t)=>{const o=Reflect.getOwnMetadata(y,e)??[];if(o.some(r=>r.toolName===n))throw new Error(`Tool '${n}' already has a handler.`);o.push({toolName:n,methodName:t}),Reflect.defineMetadata(y,o,e)}}i(ce,"Tool");class C{static{i(this,"GraphNode")}llmGateway;runtime;static[z]=!0;static id(){return this.name}name;id;constructor(e,t){this.llmGateway=e,this.runtime=t;const o=new.target;this.name=o.name,this.id=g(o,"graph node")}get graph(){return this.runtime}defineTool(){return[]}getLlmConfig(){return{}}route(e){throw new Error(`Graph node '${this.id}' does not define a branch route.`)}async handleTool(e,t,o){const r=this.toolHandler(t.name);if(!r)return this.rejectToolCall(t.name,`Unsupported tool '${t.name}'.`);const d=this.runtime.requireTool(r.toolName),s=X(t.args);if(!s.ok)return this.rejectToolCall(r.toolName,s.error);const a=d.schema.safeParse(s.value);if(!a.success)return this.rejectToolCall(r.toolName,Y(a.error));const h=a.data,l=this[r.methodName];if(!l)throw new Error(`Tool '${r.toolName}' is decorated on missing method '${String(r.methodName)}'.`);const m=await l.call(this,h,o,e);if(N(m))return m;throw new Error(`Tool '${r.toolName}' on '${this.id}' must return a ToolResponse.`)}rejectToolCall(e,t){return G({tool_call:t,node:this.id,tool:e}),k(JSON.stringify({accepted:!1,error:t}))}usesChatModel(){return!0}onInvalidJsonResponse(e){throw e.error}onEmptyModelResponse(e){throw new $(e.reason)}invoke=i(e=>this.runtime.withInvocation(this.id,e,async()=>{const t=this.resolveNodeResponse(await this.run(e)),o=W(t),r=await this.resolveJsonResponse(e,o);if(this.runtime.stageNodeEntries(r.nodes),this.usesChatModel()){const s=this.runtime.resolveLlmConfig(this.getLlmConfig()),a=V(s,this.runtime.resolveLlmConfig()),h=(e.nodes??{})[this.id];(!a||h?.model!==void 0)&&this.runtime.saveNode(this.id,{model:a?void 0:u.modelMetadata(s)})}const d=b(r,this.runtime.stagedUpdate());return w(t)?new c({update:d,...t.goto===void 0?{}:{goto:t.goto},...t.graph===void 0?{}:{graph:t.graph},...t.resume===void 0?{}:{resume:t.resume}}):d}),"invoke");obtainTools(){return this.toolHandlers().map(({toolName:e})=>this.runtime.requireTool(e)).sort((e,t)=>e.name==="terminate_session"?1:t.name==="terminate_session"?-1:0)}validateTools(){this.obtainTools()}llmConfig(){return this.runtime.resolveLlmConfig(this.getLlmConfig())}llmCallOptions(){return this.runtime.llmCallOptions()}async runConversation(e,t){const o=await this.runAgentLoop(e,t);if(!o.stopReason)return o;const r=await this.onEmptyModelResponse({state:e,conversation:o,reason:o.stopReason});return{...o,response:r}}conversationLlmLifecycle(){}runAgentLoop(e,t){const o=this.conversationLlmLifecycle();return new O(this.llmGateway).run({...o===void 0?{}:{lifecycle:o},systemPrompt:this.getPrompt(e),history:this.history(e),tools:this.obtainTools(),llmConfig:this.llmConfig(),executeTool:i(r=>this.handleTool(e,r,t),"executeTool"),maxAgentRounds:this.runtime.maxAgentRounds,emptyHistorySeed:this.runtime.conversationPolicy.emptyHistorySeed,emptyResponseRecovery:this.runtime.conversationPolicy.emptyResponseRecovery,callOptions:this.llmCallOptions()})}historySpace(){return this.runtime.historySpace(this.id)}history(e){return this.runtime.hasInvocation()?this.runtime.invocationHistory(this.historySpace()):e.histories[this.historySpace()]??[]}historyUpdate(e){return{histories:{[this.historySpace()]:e}}}respond(e){return{...this.runtime.respond(e),...this.historyUpdate([new p(e)])}}complete(e){return{...this.runtime.complete(e),...this.historyUpdate([new p(e)])}}resolveNodeResponse(e){if(!N(e))return e;if(e.kind==="stay")throw new Error(`Graph node '${this.id}' can only return stay() from a tool handler.`);if(e.cleanup)throw new Error(`Graph node '${this.id}' can only use withCleanup() from a tool handler.`);if(e.feedback!==void 0)throw new Error(`Graph node '${this.id}' can only use withToolFeedback() from a tool handler.`);if(e.messages&&this.graph.appendHistory(this.historySpace(),e.messages),e.kind==="go"||e.kind==="directTo"){if(!e.target)throw new Error(`Graph node '${this.id}' selected ${e.kind}() without a target.`);const t=g(e.target,"graph node"),o=this.graph.historySpace(t);if(e.state&&this.graph.saveNodeState(e.target,e.state),e.message&&this.graph.appendHistory(o,[e.message]),e.kind==="directTo"){const r=v(this.id,e);return this.graph.appendHistory(o,[new p(r)]),new c({update:{currentNode:t,response:r,inputConsumed:!0,completed:!1,...e.usage===void 0?{}:{tokens:e.usage}},goto:f})}return new c({update:{currentNode:t,response:"",inputConsumed:!0,completed:!1,...e.usage===void 0?{}:{tokens:e.usage}},goto:t})}if(e.message)throw new Error(`Graph node '${this.id}' can only use withMessage() with go() or directTo().`);if(e.kind==="direct"){const t=v(this.id,e);return this.graph.appendHistory(this.historySpace(),[new p(t)]),new c({update:{currentNode:this.id,response:t,inputConsumed:!0,completed:!1,...e.usage===void 0?{}:{tokens:e.usage}},goto:f})}if(e.kind==="finish"){const t=v(this.id,e);return this.graph.appendHistory(this.historySpace(),[new p(t)]),new c({update:{...this.graph.complete(t),...e.usage===void 0?{}:{tokens:e.usage}},goto:f})}throw new Error(`Graph node '${this.id}' returned an unsupported response kind '${e.kind}'.`)}state(e){return this.runtime.hasInvocation()?this.runtime.nodeState(this.id):e.nodes[this.id]??{}}getState(){return this.runtime.nodeState(this.id)}saveState(e){this.runtime.saveNode(this.id,e)}save(e){return this.runtime.saveNode(this.id,e)}remove(...e){return this.runtime.removeNode(this.id,...e)}toolHandler(e){return this.toolHandlers().find(t=>t.toolName===e)}async resolveJsonResponse(e,t){if(!this.runtime.expectsJsonResponse()||t.completed!==!0)return t;try{return S(t.response??""),t}catch(o){const r=await this.onInvalidJsonResponse({state:e,update:t,response:t.response??"",error:o instanceof Error?o:new Error(String(o))});return(r.completed===!0||r.response)&&S(r.response??""),r}}toolHandlers(){let e=Object.getPrototypeOf(this);const t=[];for(;e&&e!==C.prototype;){const r=Reflect.getOwnMetadata(y,e)??[];t.push(...r),e=Object.getPrototypeOf(e)}const o=new Set;for(const r of t){if(o.has(r.toolName))throw new Error(`Graph node '${this.id}' has more than one handler for tool '${r.toolName}'.`);o.add(r.toolName)}return t}}function V(n,e){return n.model===e.model&&JSON.stringify(u.parameters(n))===JSON.stringify(u.parameters(e))}i(V,"sameLlmConfig");class le extends T{static{i(this,"GraphNodeRuntime")}defaultLlmConfig;historySpaces;initialHistorySpace;responseMode;maxAgentRounds;conversationPolicy;decisionOptions;decisionErrorHandler;llmLifecycle;tools=new Map;constructor(e,t,o={},r="default",d="chat",s=L,a=H,h={},l,m){super(e),this.defaultLlmConfig=t,this.historySpaces=o,this.initialHistorySpace=r,this.responseMode=d,this.maxAgentRounds=s,this.conversationPolicy=a,this.decisionOptions=h,this.decisionErrorHandler=l,this.llmLifecycle=m}llmCallOptions(){const{llmTimeoutMs:e}=this.conversationPolicy,t=A();return{...e===void 0?{}:{timeoutMs:e},...t===void 0?{}:{signal:t}}}expectsJsonResponse(){return this.responseMode==="json"}historySpace(e){return e?this.historySpaces[e]??this.initialHistorySpace:this.initialHistorySpace}resolveLlmConfig(e={}){return u.resolveConfig(this.defaultLlmConfig,e)}resolveDecisionOptions(e={}){return F(this.decisionOptions,e)}decisionProviderResolver;attachDecisionProviderResolver(e){this.decisionProviderResolver=e}resolveDecisionProvider(e){if(!this.decisionProviderResolver)throw new q(`Decision provider '${e}' cannot be resolved before the graph is attached to an engine.`);return this.decisionProviderResolver(e)}async handleDecisionError(e){return await this.decisionErrorHandler?.(e)??null}registerTools(e,t){for(const o of t){const r=this.tools.get(o.name);if(r)throw new Error(`Tool '${o.name}' is defined by both '${r.owner}' and '${e}'.`);this.tools.set(o.name,{definition:o,owner:e})}}requireTool(e){const t=this.tools.get(e);if(!t)throw new Error(`Tool '${e}' is not defined in this graph.`);return t.definition}input(e){return this.latestInput({messages:e.histories[this.historySpace(e.currentNode)]??[]})}getConversation(e){const o=(this.hasInvocation()?this.invocationHistory(e):[]).filter(r=>R.isInstance(r)&&r.additional_kwargs?.ezgraphInternal!==!0).map(r=>typeof r.content=="string"?r.content:JSON.stringify(r.content));return{request:o.at(-1)??"",priorRequests:o.slice(-5,-1)}}respond(e){return this.responseUpdate(e)}complete(e){return this.endUpdate(e)}withInvocation(e,t,o){return J(this,e,t,o)}nodeState(e){return _(this,e)}hasInvocation(){return M(this)}graphState(){return I(this)}appendHistory(e,t){U(this,e,t)}invocationHistory(e){return P(this,e)}stagedUpdate(){return D(this)}stageNodeEntries(e){for(const[t,o]of Object.entries(e??{}))!o||typeof o!="object"||Array.isArray(o)||j(this,t,o)}saveNode(e,t){return x(this,e,t),{nodes:{[e]:this.nodeState(e)}}}saveNodeState(e,t){this.saveNode(g(e,"graph node"),t)}removeNode(e,...t){return this.saveNode(e,Object.fromEntries(t.map(o=>[o,void 0])))}}function W(n){if(!w(n))return n;if(Array.isArray(n.update))throw new Error("GraphNode Command updates must be state objects.");return n.update??{}}i(W,"commandUpdate");function v(n,e){if(!e.response?.trim())throw new Error(`Graph node '${n}' selected ${e.kind}() without a response.`);return e.response}i(v,"requireNodeResponse");function X(n){if(typeof n!="string")return{ok:!0,value:n};try{return{ok:!0,value:JSON.parse(n)}}catch(e){return{ok:!1,error:`Tool arguments were not valid JSON: ${e instanceof Error?e.message:String(e)}`}}}i(X,"coerceToolArgs");function Y(n){return n.issues.map(e=>e.path.length>0?`${e.path.join(".")}: ${e.message}`:e.message).join("; ")}i(Y,"formatToolArgumentError");export{z as GRAPH_NODE_CLASS,fe as GRAPH_NODE_LOCAL_STATE,C as GraphNode,le as GraphNodeRuntime,ce as Tool};
|
|
1
|
+
var E=Object.defineProperty;var i=(n,e)=>E(n,"name",{value:e,configurable:!0});import"reflect-metadata";import{AIMessage as p,HumanMessage as R}from"@langchain/core/messages";import{Command as c,END as u,isCommand as w}from"@langchain/langgraph";import{GraphLlm as m}from"./graph-llm.config.js";import{GraphStateSupport as T}from"./graph-state-support.js";import{ConversationRunner as O,DEFAULT_CONVERSATION_POLICY as H,DEFAULT_MAX_AGENT_ROUNDS as L,EmptyModelResponseError as $}from"./conversation-runner.js";import{currentTurnSignal as A}from"./turn-context.js";import{combineGraphNodeUpdates as b}from"./node-outcome.js";import{parseJsonObjectResponse as S}from"./json-response.js";import{stableClassId as f}from"./stable-id.js";import{SessionLogWarning as G}from"./session-log.js";import{isToolResponse as N,stay as k}from"./tool-response.js";import{invocationGraphState as I,hasNodeInvocation as M,invocationHistory as P,invocationNodeState as _,invocationUpdate as D,appendInvocationHistory as U,replaceInvocationNodeState as j,saveInvocationNodeState as x,withNodeInvocation as J}from"./node-invocation.js";import{DecisionConfigurationError as q,resolveDecisionOptions as F}from"./decision/decision-types.js";import{GRAPH_NODE_LOCAL_STATE as fe}from"./node-outcome.js";const z=Symbol("GRAPH_NODE_CLASS"),g=Symbol("graph-node-tool-handlers");function ce(n){if(!n.trim())throw new Error("A tool handler requires a tool name.");return(e,t)=>{const o=Reflect.getOwnMetadata(g,e)??[];if(o.some(r=>r.toolName===n))throw new Error(`Tool '${n}' already has a handler.`);o.push({toolName:n,methodName:t}),Reflect.defineMetadata(g,o,e)}}i(ce,"Tool");class C{static{i(this,"GraphNode")}llmGateway;runtime;static[z]=!0;static id(){return this.name}name;id;constructor(e,t){this.llmGateway=e,this.runtime=t;const o=new.target;this.name=o.name,this.id=f(o,"graph node")}get graph(){return this.runtime}defineTool(){return[]}getLlmConfig(){return{}}route(e){throw new Error(`Graph node '${this.id}' does not define a branch route.`)}async handleTool(e,t,o){const r=this.toolHandler(t.name);if(!r)return this.rejectToolCall(t.name,`Unsupported tool '${t.name}'.`);const d=this.runtime.requireTool(r.toolName),s=X(t.args);if(!s.ok)return this.rejectToolCall(r.toolName,s.error);const a=d.schema.safeParse(s.value);if(!a.success)return this.rejectToolCall(r.toolName,Y(a.error));const h=a.data,l=this[r.methodName];if(!l)throw new Error(`Tool '${r.toolName}' is decorated on missing method '${String(r.methodName)}'.`);const v=await l.call(this,h,o,e);if(N(v))return v;throw new Error(`Tool '${r.toolName}' on '${this.id}' must return a ToolResponse.`)}rejectToolCall(e,t){return G({tool_call:t,node:this.id,tool:e}),k(JSON.stringify({accepted:!1,error:t}))}usesChatModel(){return!0}onInvalidJsonResponse(e){throw e.error}onEmptyModelResponse(e){throw new $(e.reason)}invoke=i(e=>this.runtime.withInvocation(this.id,e,async()=>{const t=this.resolveNodeResponse(await this.run(e)),o=W(t),r=await this.resolveJsonResponse(e,o);if(this.runtime.stageNodeEntries(r.nodes),this.usesChatModel()){const s=this.runtime.resolveLlmConfig(this.getLlmConfig()),a=V(s,this.runtime.resolveLlmConfig()),h=(e.nodes??{})[this.id];(!a||h?.model!==void 0)&&this.runtime.saveNode(this.id,{model:a?void 0:m.modelMetadata(s)})}const d=b(r,this.runtime.stagedUpdate());return w(t)?new c({update:d,...t.goto===void 0?{}:{goto:t.goto},...t.graph===void 0?{}:{graph:t.graph},...t.resume===void 0?{}:{resume:t.resume}}):d}),"invoke");obtainTools(){return this.toolHandlers().map(({toolName:e})=>this.runtime.requireTool(e)).sort((e,t)=>e.name==="terminate_session"?1:t.name==="terminate_session"?-1:0)}validateTools(){this.obtainTools()}llmConfig(){return this.runtime.resolveLlmConfig(this.getLlmConfig())}llmCallOptions(){return this.runtime.llmCallOptions()}async runConversation(e,t){const o=await this.runAgentLoop(e,t);if(!o.stopReason)return o;const r=await this.onEmptyModelResponse({state:e,conversation:o,reason:o.stopReason});return{...o,response:r}}conversationLlmLifecycle(){}runAgentLoop(e,t){const o=this.conversationLlmLifecycle();return new O(this.llmGateway).run({...o===void 0?{}:{lifecycle:o},systemPrompt:this.getPrompt(e),history:this.history(e),tools:this.obtainTools(),llmConfig:this.llmConfig(),executeTool:i(r=>this.handleTool(e,r,t),"executeTool"),maxAgentRounds:this.runtime.maxAgentRounds,emptyHistorySeed:this.runtime.conversationPolicy.emptyHistorySeed,emptyResponseRecovery:this.runtime.conversationPolicy.emptyResponseRecovery,callOptions:this.llmCallOptions()})}historySpace(){return this.runtime.historySpace(this.id)}history(e){return this.runtime.hasInvocation()?this.runtime.invocationHistory(this.historySpace()):e.histories[this.historySpace()]??[]}historyUpdate(e){return{histories:{[this.historySpace()]:e}}}respond(e){return{...this.runtime.respond(e),...this.historyUpdate([new p(e)])}}complete(e){return{...this.runtime.complete(e),...this.historyUpdate([new p(e)])}}resolveNodeResponse(e){if(!N(e))return e;if(e.kind==="stay")throw new Error(`Graph node '${this.id}' can only return stay() from a tool handler.`);if(e.cleanup)throw new Error(`Graph node '${this.id}' can only use withCleanup() from a tool handler.`);if(e.feedback!==void 0)throw new Error(`Graph node '${this.id}' can only use withToolFeedback() from a tool handler.`);if(e.messages&&this.graph.appendHistory(this.historySpace(),e.messages),e.kind==="go"||e.kind==="directTo"){if(!e.target)throw new Error(`Graph node '${this.id}' selected ${e.kind}() without a target.`);const t=f(e.target,"graph node"),o=this.graph.historySpace(t);if(e.state&&this.graph.saveNodeState(e.target,e.state),e.message&&this.graph.appendHistory(o,[e.message]),e.kind==="directTo"){const r=y(this.id,e);return this.graph.appendHistory(o,[new p(r)]),new c({update:{currentNode:t,response:r,inputConsumed:!0,completed:!1,...e.usage===void 0?{}:{tokens:e.usage}},goto:u})}return new c({update:{currentNode:t,response:"",inputConsumed:!0,completed:!1,...e.usage===void 0?{}:{tokens:e.usage}},goto:t})}if(e.message)throw new Error(`Graph node '${this.id}' can only use withMessage() with go() or directTo().`);if(e.kind==="direct"){const t=y(this.id,e);return this.graph.appendHistory(this.historySpace(),[new p(t)]),new c({update:{currentNode:this.id,response:t,inputConsumed:!0,completed:!1,...e.usage===void 0?{}:{tokens:e.usage}},goto:u})}if(e.kind==="finish"){const t=y(this.id,e);return this.graph.appendHistory(this.historySpace(),[new p(t)]),new c({update:{...this.graph.complete(t),...e.usage===void 0?{}:{tokens:e.usage}},goto:u})}throw new Error(`Graph node '${this.id}' returned an unsupported response kind '${e.kind}'.`)}state(e){return this.runtime.hasInvocation()?this.runtime.nodeState(this.id):e.nodes[this.id]??{}}getState(){return this.runtime.nodeState(this.id)}saveState(e){this.runtime.saveNode(this.id,e)}save(e){return this.runtime.saveNode(this.id,e)}remove(...e){return this.runtime.removeNode(this.id,...e)}toolHandler(e){return this.toolHandlers().find(t=>t.toolName===e)}async resolveJsonResponse(e,t){if(!this.runtime.expectsJsonResponse()||t.completed!==!0)return t;try{return S(t.response??""),t}catch(o){const r=await this.onInvalidJsonResponse({state:e,update:t,response:t.response??"",error:o instanceof Error?o:new Error(String(o))});return(r.completed===!0||r.response)&&S(r.response??""),r}}toolHandlers(){let e=Object.getPrototypeOf(this);const t=[];for(;e&&e!==C.prototype;){const r=Reflect.getOwnMetadata(g,e)??[];t.push(...r),e=Object.getPrototypeOf(e)}const o=new Set;for(const r of t){if(o.has(r.toolName))throw new Error(`Graph node '${this.id}' has more than one handler for tool '${r.toolName}'.`);o.add(r.toolName)}return t}}function V(n,e){return n.model===e.model&&JSON.stringify(m.parameters(n))===JSON.stringify(m.parameters(e))}i(V,"sameLlmConfig");class le extends T{static{i(this,"GraphNodeRuntime")}defaultLlmConfig;historySpaces;responseMode;maxAgentRounds;conversationPolicy;decisionOptions;decisionErrorHandler;llmLifecycle;tools=new Map;constructor(e,t,o={},r="chat",d=L,s=H,a={},h,l){super(e),this.defaultLlmConfig=t,this.historySpaces=o,this.responseMode=r,this.maxAgentRounds=d,this.conversationPolicy=s,this.decisionOptions=a,this.decisionErrorHandler=h,this.llmLifecycle=l}llmCallOptions(){const{llmTimeoutMs:e}=this.conversationPolicy,t=A();return{...e===void 0?{}:{timeoutMs:e},...t===void 0?{}:{signal:t}}}expectsJsonResponse(){return this.responseMode==="json"}historySpace(e){return e?this.historySpaces[e]??"default":"default"}resolveLlmConfig(e={}){return m.resolveConfig(this.defaultLlmConfig,e)}resolveDecisionOptions(e={}){return F(this.decisionOptions,e)}decisionProviderResolver;attachDecisionProviderResolver(e){this.decisionProviderResolver=e}resolveDecisionProvider(e){if(!this.decisionProviderResolver)throw new q(`Decision provider '${e}' cannot be resolved before the graph is attached to an engine.`);return this.decisionProviderResolver(e)}async handleDecisionError(e){return await this.decisionErrorHandler?.(e)??null}registerTools(e,t){for(const o of t){const r=this.tools.get(o.name);if(r)throw new Error(`Tool '${o.name}' is defined by both '${r.owner}' and '${e}'.`);this.tools.set(o.name,{definition:o,owner:e})}}requireTool(e){const t=this.tools.get(e);if(!t)throw new Error(`Tool '${e}' is not defined in this graph.`);return t.definition}input(e){return this.latestInput({messages:e.histories[this.historySpace(e.currentNode)]??[]})}getConversation(e){const o=(this.hasInvocation()?this.invocationHistory(e):[]).filter(r=>R.isInstance(r)&&r.additional_kwargs?.ezgraphInternal!==!0).map(r=>typeof r.content=="string"?r.content:JSON.stringify(r.content));return{request:o.at(-1)??"",priorRequests:o.slice(-5,-1)}}respond(e){return this.responseUpdate(e)}complete(e){return this.endUpdate(e)}withInvocation(e,t,o){return J(this,e,t,o)}nodeState(e){return _(this,e)}hasInvocation(){return M(this)}graphState(){return I(this)}appendHistory(e,t){U(this,e,t)}invocationHistory(e){return P(this,e)}stagedUpdate(){return D(this)}stageNodeEntries(e){for(const[t,o]of Object.entries(e??{}))!o||typeof o!="object"||Array.isArray(o)||j(this,t,o)}saveNode(e,t){return x(this,e,t),{nodes:{[e]:this.nodeState(e)}}}saveNodeState(e,t){this.saveNode(f(e,"graph node"),t)}removeNode(e,...t){return this.saveNode(e,Object.fromEntries(t.map(o=>[o,void 0])))}}function W(n){if(!w(n))return n;if(Array.isArray(n.update))throw new Error("GraphNode Command updates must be state objects.");return n.update??{}}i(W,"commandUpdate");function y(n,e){if(!e.response?.trim())throw new Error(`Graph node '${n}' selected ${e.kind}() without a response.`);return e.response}i(y,"requireNodeResponse");function X(n){if(typeof n!="string")return{ok:!0,value:n};try{return{ok:!0,value:JSON.parse(n)}}catch(e){return{ok:!1,error:`Tool arguments were not valid JSON: ${e instanceof Error?e.message:String(e)}`}}}i(X,"coerceToolArgs");function Y(n){return n.issues.map(e=>e.path.length>0?`${e.path.join(".")}: ${e.message}`:e.message).join("; ")}i(Y,"formatToolArgumentError");export{z as GRAPH_NODE_CLASS,fe as GRAPH_NODE_LOCAL_STATE,C as GraphNode,le as GraphNodeRuntime,ce as Tool};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var m=Object.defineProperty;var a=(n,t)=>m(n,"name",{value:t,configurable:!0});var g=function(n,t,e,r){var s=arguments.length,o=s<3?t:r===null?r=Object.getOwnPropertyDescriptor(t,e):r,i;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")o=Reflect.decorate(n,t,e,r);else for(var d=n.length-1;d>=0;d--)(i=n[d])&&(o=(s<3?i(o):s>3?i(t,e,o):i(t,e))||o);return s>3&&o&&Object.defineProperty(t,e,o),o},l=function(n,t){if(typeof Reflect=="object"&&typeof Reflect.metadata=="function")return Reflect.metadata(n,t)};import{AIMessage as u,HumanMessage as w}from"@langchain/core/messages";import{Command as h,END as p}from"@langchain/langgraph";import{GraphNode as y,Tool as L}from"./graph-node.js";import{stableClassId as f}from"./stable-id.js";import{TerminateSessionNode as R}from"./terminate-session.node.js";import{go as k}from"./tool-response.js";import{addTokenUsage as b}from"./token-usage.js";class I extends y{static{a(this,"LlmNode")}checkResponse(t){return!1}async onLlmBlocked(t){return null}shouldRetryLlmError(t){}async onLlmError(t){return null}conversationLlmLifecycle(){return{nodeId:this.id,checkResponse:a(t=>this.checkResponse(t),"checkResponse"),shouldRetryLlmError:a(t=>{t.signal?.throwIfAborted();const e=this.shouldRetryLlmError(t);t.signal?.throwIfAborted();const r=e===void 0?this.graph.llmLifecycle?.shouldRetryLlmError(t):e;return t.signal?.throwIfAborted(),r},"shouldRetryLlmError"),onLlmBlocked:a(async t=>{t.signal?.throwIfAborted();let e=await this.onLlmBlocked(t);return t.signal?.throwIfAborted(),e===null&&(e=await this.graph.llmLifecycle?.onLlmBlocked(t)??null,t.signal?.throwIfAborted()),e},"onLlmBlocked"),onLlmError:a(async t=>{t.signal?.throwIfAborted();let e=await this.onLlmError(t);return t.signal?.throwIfAborted(),e===null&&(e=await this.graph.llmLifecycle?.onLlmError(t)??null,t.signal?.throwIfAborted()),e},"onLlmError")}}async endChat(t){return k(R).withMessage(new w({content:t.message?.trim()||"The user asked to end the conversation.",additional_kwargs:{ezgraphInternal:!0}}))}async run(t){const e=await this.runConversation(t,{});if(e.toolResponse)return this.resolveToolResponse(e,e.toolResponse);const r=e.response;if(!r?.trim())throw new Error(`LLM node '${this.id}' stopped without a response or tool transition.`);return new h({update:{currentNode:this.id,response:r,inputConsumed:!0,completed:!1,...this.historyUpdate(e.messages),tokens:e.usage},goto:p})}resolveToolResponse(t,e){const r=this.historyUpdate(t.messages),s=b(t.usage,e.usage);if(e.kind==="go"){if(!e.target)throw new Error(`LLM node '${this.id}' selected go() without a target.`);const o=f(e.target,"graph node");return e.state&&this.graph.saveNodeState(e.target,e.state),e.message&&this.graph.appendHistory(this.graph.historySpace(o),[e.message]),new h({update:{currentNode:o,inputConsumed:!0,response:"",completed:!1,...r,tokens:s},goto:o})}if(e.kind==="directTo"){if(!e.target)throw new Error(`LLM node '${this.id}' selected directTo() without a target.`);const o=f(e.target,"graph node"),i=this.graph.historySpace(o);e.state&&this.graph.saveNodeState(e.target,e.state),e.message&&this.graph.appendHistory(i,[e.message]);const d=c(this.id,e);return i!==this.historySpace()&&this.graph.appendHistory(i,[new u(d)]),new h({update:{currentNode:o,response:d,inputConsumed:!0,completed:!1,...r,tokens:s},goto:p})}if(e.kind==="direct")return new h({update:{currentNode:this.id,response:c(this.id,e),inputConsumed:!0,completed:!1,...r,tokens:s},goto:p});if(e.kind==="finish")return new h({update:{...this.graph.complete(c(this.id,e)),...r,tokens:s},goto:p});throw new Error(`LLM node '${this.id}' returned stay() after the agent loop stopped.`)}}g([L("terminate_session"),l("design:type",Function),l("design:paramtypes",[Object]),l("design:returntype",Promise)],I.prototype,"endChat",null);function c(n,t){if(!t.response?.trim())throw new Error(`LLM node '${n}' selected ${t.kind}() without a response.`);return t.response}a(c,"requireResponse");export{I as LlmNode};
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{BaseGraph as r}from"./framework/base-graph.js";import{GRAPH_NODE_CLASS as t,GraphNode as i,GraphNodeRuntime as n,Tool as a}from"./framework/graph-node.js";import{
|
|
1
|
+
import{BaseGraph as r}from"./framework/base-graph.js";import{GRAPH_NODE_CLASS as t,GraphNode as i,GraphNodeRuntime as n,Tool as a}from"./framework/graph-node.js";import{LlmNode as p}from"./framework/llm-node.js";import{LLM_GATEWAY_FACTORY as E,assertLlmTimeoutMs as f}from"./framework/llm-gateway.js";import{ConversationRunner as L,DEFAULT_CONVERSATION_POLICY as d,DEFAULT_EMPTY_HISTORY_SEED as T,DEFAULT_EMPTY_RESPONSE_RECOVERY as g,DEFAULT_MAX_AGENT_ROUNDS as l,EmptyModelResponseError as D,assertEmptyResponseRetries as R,assertMaxAgentRounds as _}from"./framework/conversation-runner.js";import{describeStopReason as C,isRetryableStopReason as M,modelStopReason as A}from"./framework/model-stop-reason.js";import{currentTurnContext as O,currentTurnSignal as u,withTurnContext as I}from"./framework/turn-context.js";import{direct as G,directTo as U,finish as h,go as y,isToolResponse as v,stay as F}from"./framework/tool-response.js";import{GraphStateSupport as H}from"./framework/graph-state-support.js";import{GRAPH_END_NODE as B,createGraphStateAnnotation as V}from"./framework/graph-state.js";import{GraphRegistry as W}from"./framework/graph-registry.js";import{GraphEngine as z}from"./framework/graph-engine.js";import{ConfigManager as J}from"./framework/config-manager.js";import{StateGraphExt as X}from"./framework/state-graph-ext.js";import{TerminateSessionNode as K}from"./framework/terminate-session.node.js";import{SessionLogError as $,SessionLogDecision as ee,SessionLogWarning as oe,createSessionLogs as re,drainSessionLogs as se,restoreSessionLogs as te,withSessionLogs as ie}from"./framework/session-log.js";import{ProviderFileManager as ae}from"./framework/llm-file.js";import{SessionManager as pe,SessionTurnError as Se,SESSION_BUSY as Ee,SESSION_CONFLICT as fe,SESSION_COMPLETED as xe,SESSION_DOCUMENT_VERSION as Le,MemorySessionDocumentContainer as de,sessionTurnLeaseMs as Te,sessionRevision as ge,isLiveSessionTurn as le,matchesSessionWritePredicate as De}from"./framework/persistence/session-manager.js";import{MongoDbSessionDocumentContainer as _e}from"./framework/persistence/mongodb-session-document.container.js";import{SqliteSessionDocumentContainer as Ce}from"./framework/persistence/sqlite-session-document.container.js";import{CosmosSessionDocumentContainer as Ae}from"./framework/persistence/cosmos-session-document.container.js";import{mergeNodeStates as Oe,replaceNodeStates as ue}from"./framework/node-state.js";import{LangChainLlmGateway as Pe}from"./framework/langchain-llm-gateway.js";import{ModelProvider as Ue}from"./framework/model-provider.js";import{langChainModelInitialization as ye}from"./framework/langchain-provider.adapter.js";import{GRAPH_LLM_PROVIDERS as Fe,GRAPH_LLM_FAMILIES as Ye,GRAPH_LLM_CAPABILITIES as He,BUILT_IN_MODEL_CATALOG as ke,GraphLlm as Be,ModelCatalog as Ve,ModelProviderRegistry as be}from"./framework/graph-llm.config.js";import{addTokenUsage as we,emptyTokenUsage as ze,modelExecution as qe,normalizeTokenUsage as Je,tokenUsageFromMessage as Qe}from"./framework/token-usage.js";import{DecisionNode as je}from"./framework/decision/decision-node.js";import{DecisionRunner as Ze}from"./framework/decision/decision-runner.js";import{DecisionProvider as eo,DecisionProviderRegistry as oo}from"./framework/decision/decision-provider.js";import{TypeSafeClassifierAdapter as so}from"./framework/decision/typesafe-provider.js";import{DecisionConfigurationError as io,DecisionTimeoutError as no,DecisionValidationError as ao,DEFAULT_DECISION_OPTIONS as mo,addDecisionUsage as po,decisionUsage as So,emptyDecisionUsage as Eo,normalizeDecisionUsage as fo,resolveDecisionOptions as xo}from"./framework/decision/decision-types.js";import{assertJsonCompatible as To,validateDecisionQuestions as go,validateDecisionResult as lo}from"./framework/decision/decision-validation.js";import{retryWithModel as Ro,LlmResponseBlockedError as _o,LlmSetupError as co}from"./framework/llm-lifecycle.js";export{ke as BUILT_IN_MODEL_CATALOG,r as BaseGraph,J as ConfigManager,L as ConversationRunner,Ae as CosmosSessionDocumentContainer,d as DEFAULT_CONVERSATION_POLICY,mo as DEFAULT_DECISION_OPTIONS,T as DEFAULT_EMPTY_HISTORY_SEED,g as DEFAULT_EMPTY_RESPONSE_RECOVERY,l as DEFAULT_MAX_AGENT_ROUNDS,io as DecisionConfigurationError,je as DecisionNode,eo as DecisionProvider,oo as DecisionProviderRegistry,Ze as DecisionRunner,no as DecisionTimeoutError,ao as DecisionValidationError,D as EmptyModelResponseError,B as GRAPH_END_NODE,He as GRAPH_LLM_CAPABILITIES,Ye as GRAPH_LLM_FAMILIES,Fe as GRAPH_LLM_PROVIDERS,t as GRAPH_NODE_CLASS,z as GraphEngine,Be as GraphLlm,i as GraphNode,n as GraphNodeRuntime,W as GraphRegistry,H as GraphStateSupport,E as LLM_GATEWAY_FACTORY,Pe as LangChainLlmGateway,p as LlmNode,_o as LlmResponseBlockedError,co as LlmSetupError,de as MemorySessionDocumentContainer,Ve as ModelCatalog,Ue as ModelProvider,be as ModelProviderRegistry,_e as MongoDbSessionDocumentContainer,ae as ProviderFileManager,Ee as SESSION_BUSY,xe as SESSION_COMPLETED,fe as SESSION_CONFLICT,Le as SESSION_DOCUMENT_VERSION,ee as SessionLogDecision,$ as SessionLogError,oe as SessionLogWarning,pe as SessionManager,Se as SessionTurnError,Ce as SqliteSessionDocumentContainer,X as StateGraphExt,K as TerminateSessionNode,a as Tool,so as TypeSafeClassifierAdapter,po as addDecisionUsage,we as addTokenUsage,R as assertEmptyResponseRetries,To as assertJsonCompatible,f as assertLlmTimeoutMs,_ as assertMaxAgentRounds,V as createGraphStateAnnotation,re as createSessionLogs,O as currentTurnContext,u as currentTurnSignal,So as decisionUsage,C as describeStopReason,G as direct,U as directTo,se as drainSessionLogs,Eo as emptyDecisionUsage,ze as emptyTokenUsage,h as finish,y as go,le as isLiveSessionTurn,M as isRetryableStopReason,v as isToolResponse,ye as langChainModelInitialization,De as matchesSessionWritePredicate,Oe as mergeNodeStates,qe as modelExecution,A as modelStopReason,fo as normalizeDecisionUsage,Je as normalizeTokenUsage,ue as replaceNodeStates,xo as resolveDecisionOptions,te as restoreSessionLogs,Ro as retryWithModel,ge as sessionRevision,Te as sessionTurnLeaseMs,F as stay,Qe as tokenUsageFromMessage,go as validateDecisionQuestions,lo as validateDecisionResult,ie as withSessionLogs,I as withTurnContext};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { BaseGraph, type GraphDefinition, type GraphNodeConstructor, type GraphSchemaMigration, type HistorySpace, } from "../../src/framework/base-graph.js";
|
|
2
2
|
export { GRAPH_NODE_CLASS, GraphNode, GraphNodeRuntime, Tool, type EmptyModelResponseContext, type GraphNodeUpdate, type GraphNodeResponse, type GraphNodeResult, type InvalidJsonResponseContext, type ToolDefinition, type ToolInput, type GraphNodeLocalState, type GraphNodeTarget, } from "../../src/framework/graph-node.js";
|
|
3
|
-
export {
|
|
3
|
+
export { LlmNode, type LlmNodeRunResult, } from "../../src/framework/llm-node.js";
|
|
4
4
|
export { LLM_GATEWAY_FACTORY, assertLlmTimeoutMs, type LlmCallOptions, type LlmGateway, type LlmGatewayFactory, type LlmGatewayTool, type LlmGatewayToolCall, } from "../../src/framework/llm-gateway.js";
|
|
5
5
|
export { ConversationRunner, DEFAULT_CONVERSATION_POLICY, DEFAULT_EMPTY_HISTORY_SEED, DEFAULT_EMPTY_RESPONSE_RECOVERY, DEFAULT_MAX_AGENT_ROUNDS, EmptyModelResponseError, assertEmptyResponseRetries, assertMaxAgentRounds, type ConversationPolicy, type ConversationRunResult, type EmptyResponseRecovery, } from "../../src/framework/conversation-runner.js";
|
|
6
6
|
export { describeStopReason, isRetryableStopReason, modelStopReason, type ModelStopCategory, type ModelStopReason, } from "../../src/framework/model-stop-reason.js";
|
|
@@ -26,7 +26,6 @@ export type GraphDefinition<NodeId extends string = string, State extends GraphS
|
|
|
26
26
|
requiresUserMessage?: boolean;
|
|
27
27
|
responseMode?: "chat" | "json";
|
|
28
28
|
historySpaces?: readonly HistorySpace[];
|
|
29
|
-
initialHistorySpace?: string;
|
|
30
29
|
maxAgentRounds?: number;
|
|
31
30
|
llmTimeoutMs?: number;
|
|
32
31
|
emptyHistorySeed?: string | null;
|
|
@@ -69,6 +68,7 @@ export declare abstract class BaseGraph<State extends GraphState> {
|
|
|
69
68
|
}): Promise<AsyncIterable<State>>;
|
|
70
69
|
};
|
|
71
70
|
prepareInput(state: State | undefined, message: HumanMessage): Partial<State>;
|
|
71
|
+
private initialNodeId;
|
|
72
72
|
saveNodeState<NodeClass extends GraphNodeTarget>(NodeClass: NodeClass, patch: Partial<GraphNodeLocalState<NodeClass>>): void;
|
|
73
73
|
restoreSessionDoc(sessionDoc: SessionDocument<State>): Promise<SessionDocument<State> | null>;
|
|
74
74
|
protected onRestoreSessionDoc(sessionDoc: SessionDocument<State>): Promise<SessionDocument<State> | null>;
|
|
@@ -78,7 +78,6 @@ export declare abstract class GraphNode<State extends GraphState, LocalState ext
|
|
|
78
78
|
export declare class GraphNodeRuntime<State extends GraphState> extends GraphStateSupport<State["currentNode"], State["nodes"]> {
|
|
79
79
|
private readonly defaultLlmConfig;
|
|
80
80
|
private readonly historySpaces;
|
|
81
|
-
private readonly initialHistorySpace;
|
|
82
81
|
private readonly responseMode;
|
|
83
82
|
readonly maxAgentRounds: number;
|
|
84
83
|
readonly conversationPolicy: ConversationPolicy;
|
|
@@ -86,7 +85,7 @@ export declare class GraphNodeRuntime<State extends GraphState> extends GraphSta
|
|
|
86
85
|
private readonly decisionErrorHandler?;
|
|
87
86
|
readonly llmLifecycle?: GraphLlmLifecycle | undefined;
|
|
88
87
|
private readonly tools;
|
|
89
|
-
constructor(endNode: State["currentNode"], defaultLlmConfig: GraphLlmConfig, historySpaces?: Readonly<Record<string, string>>,
|
|
88
|
+
constructor(endNode: State["currentNode"], defaultLlmConfig: GraphLlmConfig, historySpaces?: Readonly<Record<string, string>>, responseMode?: "chat" | "json", maxAgentRounds?: number, conversationPolicy?: ConversationPolicy, decisionOptions?: DecisionOptions, decisionErrorHandler?: ((context: DecisionErrorContext<State>) => GraphNodeResponse<State> | null | Promise<GraphNodeResponse<State> | null>) | undefined, llmLifecycle?: GraphLlmLifecycle | undefined);
|
|
90
89
|
llmCallOptions(): LlmCallOptions;
|
|
91
90
|
expectsJsonResponse(): boolean;
|
|
92
91
|
historySpace(currentNode?: string): string;
|
|
@@ -3,8 +3,8 @@ import type { ConversationRunResult } from "./conversation-runner.js";
|
|
|
3
3
|
import { GraphNode, type GraphNodeResult } from "./graph-node.js";
|
|
4
4
|
import type { GraphState } from "./graph-state.js";
|
|
5
5
|
import { type ToolResponse } from "./tool-response.js";
|
|
6
|
-
export type
|
|
7
|
-
export declare abstract class
|
|
6
|
+
export type LlmNodeRunResult = ConversationRunResult;
|
|
7
|
+
export declare abstract class LlmNode<State extends GraphState> extends GraphNode<State> {
|
|
8
8
|
checkResponse(_llmResult: string | object): boolean;
|
|
9
9
|
onLlmBlocked(_context: LlmBlockedContext): Promise<LlmBlockedResponse | null>;
|
|
10
10
|
shouldRetryLlmError(_context: LlmAttemptErrorContext): boolean | undefined;
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
var m=Object.defineProperty;var a=(n,t)=>m(n,"name",{value:t,configurable:!0});var g=function(n,t,e,r){var s=arguments.length,o=s<3?t:r===null?r=Object.getOwnPropertyDescriptor(t,e):r,i;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")o=Reflect.decorate(n,t,e,r);else for(var d=n.length-1;d>=0;d--)(i=n[d])&&(o=(s<3?i(o):s>3?i(t,e,o):i(t,e))||o);return s>3&&o&&Object.defineProperty(t,e,o),o},l=function(n,t){if(typeof Reflect=="object"&&typeof Reflect.metadata=="function")return Reflect.metadata(n,t)};import{AIMessage as u,HumanMessage as w}from"@langchain/core/messages";import{Command as h,END as p}from"@langchain/langgraph";import{GraphNode as y,Tool as R}from"./graph-node.js";import{stableClassId as f}from"./stable-id.js";import{TerminateSessionNode as v}from"./terminate-session.node.js";import{go as L}from"./tool-response.js";import{addTokenUsage as k}from"./token-usage.js";class b extends y{static{a(this,"ConversationNode")}checkResponse(t){return!1}async onLlmBlocked(t){return null}shouldRetryLlmError(t){}async onLlmError(t){return null}conversationLlmLifecycle(){return{nodeId:this.id,checkResponse:a(t=>this.checkResponse(t),"checkResponse"),shouldRetryLlmError:a(t=>{t.signal?.throwIfAborted();const e=this.shouldRetryLlmError(t);t.signal?.throwIfAborted();const r=e===void 0?this.graph.llmLifecycle?.shouldRetryLlmError(t):e;return t.signal?.throwIfAborted(),r},"shouldRetryLlmError"),onLlmBlocked:a(async t=>{t.signal?.throwIfAborted();let e=await this.onLlmBlocked(t);return t.signal?.throwIfAborted(),e===null&&(e=await this.graph.llmLifecycle?.onLlmBlocked(t)??null,t.signal?.throwIfAborted()),e},"onLlmBlocked"),onLlmError:a(async t=>{t.signal?.throwIfAborted();let e=await this.onLlmError(t);return t.signal?.throwIfAborted(),e===null&&(e=await this.graph.llmLifecycle?.onLlmError(t)??null,t.signal?.throwIfAborted()),e},"onLlmError")}}async endChat(t){return L(v).withMessage(new w({content:t.message?.trim()||"The user asked to end the conversation.",additional_kwargs:{ezgraphInternal:!0}}))}async run(t){const e=await this.runConversation(t,{});if(e.toolResponse)return this.resolveToolResponse(e,e.toolResponse);const r=e.response;if(!r?.trim())throw new Error(`Conversation node '${this.id}' stopped without a response or tool transition.`);return new h({update:{currentNode:this.id,response:r,inputConsumed:!0,completed:!1,...this.historyUpdate(e.messages),tokens:e.usage},goto:p})}resolveToolResponse(t,e){const r=this.historyUpdate(t.messages),s=k(t.usage,e.usage);if(e.kind==="go"){if(!e.target)throw new Error(`Conversation node '${this.id}' selected go() without a target.`);const o=f(e.target,"graph node");return e.state&&this.graph.saveNodeState(e.target,e.state),e.message&&this.graph.appendHistory(this.graph.historySpace(o),[e.message]),new h({update:{currentNode:o,inputConsumed:!0,response:"",completed:!1,...r,tokens:s},goto:o})}if(e.kind==="directTo"){if(!e.target)throw new Error(`Conversation node '${this.id}' selected directTo() without a target.`);const o=f(e.target,"graph node"),i=this.graph.historySpace(o);e.state&&this.graph.saveNodeState(e.target,e.state),e.message&&this.graph.appendHistory(i,[e.message]);const d=c(this.id,e);return i!==this.historySpace()&&this.graph.appendHistory(i,[new u(d)]),new h({update:{currentNode:o,response:d,inputConsumed:!0,completed:!1,...r,tokens:s},goto:p})}if(e.kind==="direct")return new h({update:{currentNode:this.id,response:c(this.id,e),inputConsumed:!0,completed:!1,...r,tokens:s},goto:p});if(e.kind==="finish")return new h({update:{...this.graph.complete(c(this.id,e)),...r,tokens:s},goto:p});throw new Error(`Conversation node '${this.id}' returned stay() after the agent loop stopped.`)}}g([R("terminate_session"),l("design:type",Function),l("design:paramtypes",[Object]),l("design:returntype",Promise)],b.prototype,"endChat",null);function c(n,t){if(!t.response?.trim())throw new Error(`Conversation node '${n}' selected ${t.kind}() without a response.`);return t.response}a(c,"requireResponse");export{b as ConversationNode};
|