@picoflow/ezgraph 0.0.11 → 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 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.7 contains breaking changes from 0.0.6; see the [EZGraph documentation](https://www.picoflow.io/ezgraph) before upgrading.
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 | 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. |
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 { createGraphStateAnnotation, type NodeStateValue } from "@picoflow/ezgraph";
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 a conversational node
77
+ ## Write an LLM node
75
78
 
76
- `ConversationNode` normally needs only the graph-state type. The handler below
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
- ConversationNode,
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 ConversationNode<WeatherGraphStateType> {
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
- name: "capture_city",
102
- description: "Save a city for the forecast.",
103
- schema: z.object({ city: z.string().min(2) }),
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
- `ConversationNode` supports the same response/error hooks as PicoFlow. The
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 | Behavior |
137
- | --- | --- |
138
- | `checkResponse(candidate)` | Receives the raw AI message before tool dispatch or acceptance. `true` rejects and retries; `false` accepts. Node-only. |
139
- | `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`. |
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)` | Handles terminal `invocation_error`, `empty_response`, or `response_rejected` failures. Node first, then graph on `null`; two `null` results propagate the failure. |
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
- ConversationNode,
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 ConversationNode<SupportState> {
160
- getPrompt(): string { return "Help the customer with their request."; }
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(context: LlmErrorContext): Promise<LlmErrorResponse | null> {
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 `ConversationNode`'s shared loop. Plain `GraphNode`
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 { WeatherGraphState, type WeatherGraphStateType } from "./weather-graph.state.js";
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
 
@@ -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{ConversationNode as m}from"./framework/conversation-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 D,EmptyModelResponseError as l,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 me,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 De,matchesSessionWritePredicate as le}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 po,addDecisionUsage as mo,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 Do}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,m as ConversationNode,L as ConversationRunner,Ae as CosmosSessionDocumentContainer,d as DEFAULT_CONVERSATION_POLICY,po as DEFAULT_DECISION_OPTIONS,T as DEFAULT_EMPTY_HISTORY_SEED,g as DEFAULT_EMPTY_RESPONSE_RECOVERY,D 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,l 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,_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,me as SessionManager,Se as SessionTurnError,Ce as SqliteSessionDocumentContainer,X as StateGraphExt,K as TerminateSessionNode,a as Tool,so as TypeSafeClassifierAdapter,mo 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,De as isLiveSessionTurn,M as isRetryableStopReason,v as isToolResponse,ye as langChainModelInitialization,le 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,Do as validateDecisionResult,ie as withSessionLogs,I as withTurnContext};
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 { ConversationNode, type ConversationNodeRunResult, } from "../../src/framework/conversation-node.js";
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";
@@ -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 ConversationNodeRunResult = ConversationRunResult;
7
- export declare abstract class ConversationNode<State extends GraphState> extends GraphNode<State> {
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,6 +1,6 @@
1
1
  {
2
2
  "name": "@picoflow/ezgraph",
3
- "version": "0.0.11",
3
+ "version": "0.0.12",
4
4
  "description": "Provider-neutral graph primitives built on LangGraph and LangChain.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -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};