@picoflow/ezgraph 0.0.6 → 0.0.8
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 +247 -86
- package/dist/framework/base-graph.js +1 -1
- package/dist/framework/conversation-node.js +1 -1
- package/dist/framework/conversation-runner.js +1 -1
- package/dist/framework/decision/decision-node.js +1 -0
- package/dist/framework/decision/decision-provider.js +1 -0
- package/dist/framework/decision/decision-runner.js +1 -0
- package/dist/framework/decision/decision-types.js +1 -0
- package/dist/framework/decision/decision-validation.js +1 -0
- package/dist/framework/decision/typesafe-provider.js +1 -0
- package/dist/framework/graph-engine.js +1 -1
- package/dist/framework/graph-node.js +1 -1
- package/dist/framework/graph-state.js +1 -1
- package/dist/framework/langchain-llm-gateway.js +1 -1
- package/dist/framework/langchain-provider.adapter.js +1 -1
- package/dist/framework/node-invocation.js +1 -0
- package/dist/framework/node-outcome.js +1 -1
- package/dist/framework/node-state.js +1 -1
- package/dist/framework/persistence/session-manager.js +1 -1
- package/dist/framework/session-log.js +1 -1
- package/dist/framework/state-graph-ext.js +1 -1
- package/dist/framework/tool-response.js +1 -0
- package/dist/index.js +1 -1
- package/dist/testing/scripted-decisions.js +1 -0
- package/dist/testing/turn-harness.js +1 -1
- package/dist/testing.js +1 -1
- package/dist/types/npmlib/src/index.d.ts +11 -5
- package/dist/types/npmlib/src/testing.d.ts +1 -0
- package/dist/types/src/framework/base-graph.d.ts +28 -14
- package/dist/types/src/framework/conversation-node.d.ts +9 -15
- package/dist/types/src/framework/conversation-runner.d.ts +9 -9
- package/dist/types/src/framework/decision/decision-node.d.ts +17 -0
- package/dist/types/src/framework/decision/decision-provider.d.ts +14 -0
- package/dist/types/src/framework/decision/decision-runner.d.ts +18 -0
- package/dist/types/src/framework/decision/decision-types.d.ts +126 -0
- package/dist/types/src/framework/decision/decision-validation.d.ts +4 -0
- package/dist/types/src/framework/decision/typesafe-provider.d.ts +12 -0
- package/dist/types/src/framework/graph-engine.d.ts +4 -0
- package/dist/types/src/framework/graph-llm.config.d.ts +3 -3
- package/dist/types/src/framework/graph-node.d.ts +51 -36
- package/dist/types/src/framework/graph-registry.d.ts +10 -5
- package/dist/types/src/framework/graph-state-support.d.ts +1 -1
- package/dist/types/src/framework/graph-state.d.ts +6 -5
- package/dist/types/src/framework/langchain-provider.adapter.d.ts +2 -2
- package/dist/types/src/framework/llm-gateway.d.ts +4 -4
- package/dist/types/src/framework/model-stop-reason.d.ts +2 -2
- package/dist/types/src/framework/node-invocation.d.ts +12 -0
- package/dist/types/src/framework/node-outcome.d.ts +3 -32
- package/dist/types/src/framework/node-state.d.ts +3 -2
- package/dist/types/src/framework/openai-auth-provider.d.ts +1 -1
- package/dist/types/src/framework/persistence/session-manager.d.ts +4 -0
- package/dist/types/src/framework/session-log.d.ts +30 -0
- package/dist/types/src/framework/state-graph-ext.d.ts +4 -10
- package/dist/types/src/framework/token-usage.d.ts +2 -2
- package/dist/types/src/framework/tool-response.d.ts +29 -0
- package/dist/types/src/testing/scripted-decisions.d.ts +24 -0
- package/dist/types/src/testing/turn-harness.d.ts +2 -0
- package/package.json +11 -7
- package/dist/framework/conversation-tool-result.js +0 -1
- package/dist/types/src/framework/conversation-tool-result.d.ts +0 -19
package/README.md
CHANGED
|
@@ -1,110 +1,268 @@
|
|
|
1
1
|
# EZGraph
|
|
2
2
|
|
|
3
|
-
EZGraph is a
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
EZGraph is a TypeScript application layer for durable, guided conversations on
|
|
4
|
+
[LangGraph](https://langchain-ai.github.io/langgraphjs/). It gives customer-facing
|
|
5
|
+
chat applications an explicit contract for node-owned state, decorated tools,
|
|
6
|
+
durable sessions, and deterministic transitions—without requiring an EZGraph
|
|
7
|
+
account, cloud service, or dependency-injection framework.
|
|
7
8
|
|
|
8
|
-
|
|
9
|
-
|
|
9
|
+
Use it when a conversation is an application: it gathers validated facts,
|
|
10
|
+
applies business policy, presents exact reviews or decisions, and resumes safely
|
|
11
|
+
across requests.
|
|
10
12
|
|
|
11
|
-
|
|
13
|
+
> **Upgrading:** Version 0.0.7 contains breaking changes from 0.0.6; see the [EZGraph documentation](https://www.picoflow.io/ezgraph) before upgrading.
|
|
12
14
|
|
|
13
|
-
|
|
14
|
-
prompt or an implicit agent loop.
|
|
15
|
-
- Per-node state, named history spaces, and typed outcomes such as `stay()`,
|
|
16
|
-
`advance()`, `finish()`, and `quit()`.
|
|
17
|
-
- One versioned session document per conversation, backed by memory, SQLite,
|
|
18
|
-
MongoDB, or Azure Cosmos DB.
|
|
19
|
-
- A model and tool loop that is bounded and owned by the graph, while business
|
|
20
|
-
validation and routing remain ordinary application code.
|
|
21
|
-
- Provider adapters and a typed model catalog, with support for OpenAI,
|
|
22
|
-
Anthropic, Google, Azure OpenAI, and custom compatible endpoints.
|
|
15
|
+
## Install
|
|
23
16
|
|
|
24
|
-
|
|
25
|
-
|
|
17
|
+
```bash
|
|
18
|
+
npm install @picoflow/ezgraph zod
|
|
26
19
|
|
|
27
|
-
|
|
20
|
+
# Install only the LangChain provider adapter your application uses.
|
|
21
|
+
npm install @langchain/openai
|
|
22
|
+
# or: npm install @langchain/anthropic
|
|
23
|
+
# or: npm install @langchain/google
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Requirements:
|
|
28
27
|
|
|
29
28
|
- Node.js 22.5 or later
|
|
30
29
|
- TypeScript 5.x
|
|
31
|
-
-
|
|
30
|
+
- `experimentalDecorators` and `emitDecoratorMetadata` enabled in `tsconfig.json`
|
|
31
|
+
- `import "reflect-metadata"` once, before any module that declares `@Tool`
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
`
|
|
35
|
-
at the very top of your application entry point.
|
|
33
|
+
EZGraph is ESM. TypeScript applications should use `module` and
|
|
34
|
+
`moduleResolution` set to `NodeNext`.
|
|
36
35
|
|
|
37
|
-
##
|
|
36
|
+
## The conversation contract
|
|
38
37
|
|
|
39
|
-
|
|
40
|
-
|
|
38
|
+
Each node owns the facts it validates. A tool handler saves valid state, then
|
|
39
|
+
returns exactly one response:
|
|
41
40
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
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. |
|
|
47
48
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
`NodeNext`.
|
|
49
|
+
There is no per-turn context object, post-tool transition hook, synthetic tool
|
|
50
|
+
result, or automatic outcome router to maintain.
|
|
51
51
|
|
|
52
|
-
##
|
|
52
|
+
## Define durable state
|
|
53
53
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
`sessionId`.
|
|
54
|
+
Keep the node registry next to the graph. It is converted into the annotated
|
|
55
|
+
LangGraph state; each node channel is replaced when that node saves state.
|
|
57
56
|
|
|
58
57
|
```ts
|
|
59
|
-
import "
|
|
60
|
-
import {
|
|
61
|
-
import { WeatherGraph } from "./weather-graph.js";
|
|
58
|
+
import { createGraphStateAnnotation, type NodeStateValue } from "@picoflow/ezgraph";
|
|
59
|
+
import { CityNode } from "./nodes/city.node.js";
|
|
62
60
|
|
|
63
|
-
|
|
61
|
+
export type WeatherGraphNodes = {
|
|
62
|
+
CityNode?: NodeStateValue<{ city?: string }>;
|
|
63
|
+
WeatherNode?: NodeStateValue<{ forecast?: string }>;
|
|
64
|
+
};
|
|
64
65
|
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
66
|
+
export const WeatherGraphState = createGraphStateAnnotation(
|
|
67
|
+
CityNode.name,
|
|
68
|
+
() => ({} as WeatherGraphNodes),
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
export type WeatherGraphStateType = typeof WeatherGraphState.State;
|
|
72
|
+
```
|
|
70
73
|
|
|
71
|
-
|
|
74
|
+
## Write a conversational node
|
|
75
|
+
|
|
76
|
+
`ConversationNode` normally needs only the graph-state type. The handler below
|
|
77
|
+
validates model input in code, saves the owned state channel, and uses
|
|
78
|
+
`direct()` for an exact result.
|
|
79
|
+
|
|
80
|
+
```ts
|
|
81
|
+
import { z } from "zod";
|
|
82
|
+
import {
|
|
83
|
+
ConversationNode,
|
|
84
|
+
Tool,
|
|
85
|
+
direct,
|
|
86
|
+
stay,
|
|
87
|
+
type ToolDefinition,
|
|
88
|
+
type ToolResponse,
|
|
89
|
+
} from "@picoflow/ezgraph";
|
|
90
|
+
import type { WeatherGraphStateType } from "../weather-graph.state.js";
|
|
91
|
+
|
|
92
|
+
type CityInput = { city: string };
|
|
93
|
+
|
|
94
|
+
export class CityNode extends ConversationNode<WeatherGraphStateType> {
|
|
95
|
+
getPrompt(): string {
|
|
96
|
+
return "Collect a city, then call capture_city.";
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
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
|
+
}];
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
@Tool("capture_city")
|
|
108
|
+
async captureCity({ city }: CityInput): Promise<ToolResponse> {
|
|
109
|
+
const value = city.trim();
|
|
110
|
+
if (!value) return stay("Ask for a non-empty city.");
|
|
111
|
+
|
|
112
|
+
this.saveState({ city: value });
|
|
113
|
+
return direct(`Saved ${value}.`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
72
116
|
```
|
|
73
117
|
|
|
74
|
-
`
|
|
75
|
-
|
|
76
|
-
|
|
118
|
+
Use `go(TargetNode).withState({ ... })` when a transition also initializes the
|
|
119
|
+
target node's state. Use `withMessages()` for real model-visible messages or
|
|
120
|
+
attachments, and `withCleanup()` for cleanup tied to that tool result.
|
|
121
|
+
When a custom `GraphNode.run()` makes its own model call, attach that call's
|
|
122
|
+
token delta with `.withUsage(conversation.usage)` so a `go()` or response
|
|
123
|
+
builder preserves the session's LLM accounting.
|
|
124
|
+
Use `directTo(TargetNode, content)` when the current handler should answer now
|
|
125
|
+
and defer the target until the next user turn.
|
|
77
126
|
|
|
78
|
-
|
|
127
|
+
For deterministic policy that needs state owned by another node, read
|
|
128
|
+
`this.graph.graphState()`. To write another node's channel, use
|
|
129
|
+
`this.graph.saveNodeState(TargetNode, patch)`.
|
|
79
130
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
131
|
+
## Add typed decisions with Jev
|
|
132
|
+
|
|
133
|
+
EZGraph supports [Jev](https://typesafe.ai/) through LangChain's optional
|
|
134
|
+
`@langchain/typesafe` package. Use a provider-neutral `DecisionNode` when a
|
|
135
|
+
workflow needs a bounded classification or review rather than generated text.
|
|
136
|
+
Jev returns typed Choice, Score, and Noul answers; your application keeps
|
|
137
|
+
thresholds, routing, authorization, and side effects in code.
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
npm install @langchain/typesafe
|
|
141
|
+
```
|
|
83
142
|
|
|
84
143
|
```ts
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
144
|
+
import {
|
|
145
|
+
DecisionNode,
|
|
146
|
+
DecisionProvider,
|
|
147
|
+
GraphEngine,
|
|
148
|
+
go,
|
|
149
|
+
type DecisionAnswers,
|
|
150
|
+
type DecisionQuestionMap,
|
|
151
|
+
} from "@picoflow/ezgraph";
|
|
152
|
+
|
|
153
|
+
const TRIAGE = {
|
|
154
|
+
route: {
|
|
155
|
+
type: "choice",
|
|
156
|
+
criteria: {
|
|
157
|
+
billing: "Invoice or refund question",
|
|
158
|
+
technical: "Failure requiring diagnosis",
|
|
159
|
+
unclear: "Missing indispensable detail",
|
|
160
|
+
},
|
|
161
|
+
},
|
|
162
|
+
urgent: {
|
|
163
|
+
type: "noul",
|
|
164
|
+
instructions: "Is a production operation currently blocked?",
|
|
165
|
+
},
|
|
166
|
+
} as const satisfies DecisionQuestionMap;
|
|
167
|
+
|
|
168
|
+
class TriageNode extends DecisionNode<SupportGraphStateType, typeof TRIAGE> {
|
|
169
|
+
defineQuestions() {
|
|
170
|
+
return TRIAGE;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
override getPrompt() {
|
|
174
|
+
return "Classify the latest support request.";
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
onDecision(answers: DecisionAnswers<typeof TRIAGE>) {
|
|
178
|
+
if (answers.route.confidence < 0.75) return go(ClarifyNode);
|
|
179
|
+
if (answers.urgent.noul >= 0.9) return go(SpecialistNode);
|
|
180
|
+
return answers.route.choice === "billing"
|
|
181
|
+
? go(BillingPolicyNode)
|
|
182
|
+
: go(QuickAnswerNode);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const decisionProviders = DecisionProvider.create({
|
|
187
|
+
typesafe: { apiKey: process.env.TYPESAFE_API_KEY },
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
const engine = await GraphEngine.create({
|
|
191
|
+
graphs: [SupportGraph],
|
|
192
|
+
decisionProviders,
|
|
88
193
|
});
|
|
89
194
|
```
|
|
90
195
|
|
|
91
|
-
The
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
196
|
+
The default decision configuration uses provider `typesafe` and model
|
|
197
|
+
`jev-latest`; graphs or individual nodes can override the model, timeout, and
|
|
198
|
+
bounded retry count. Provider registration is explicit, and a missing Jev
|
|
199
|
+
provider, package, or credential fails clearly instead of falling back to a
|
|
200
|
+
chat model. Decision usage is persisted separately from chat-model tokens.
|
|
95
201
|
|
|
96
|
-
|
|
97
|
-
intended for local use, and may change independently of the supported OpenAI
|
|
98
|
-
API-key provider.
|
|
202
|
+
## Build and register the graph
|
|
99
203
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
204
|
+
Register conversational nodes explicitly. Tool-returned `go()` responses choose
|
|
205
|
+
ordinary conversational handoffs; fixed worker edges remain ordinary LangGraph
|
|
206
|
+
edges. Register `TerminateSessionNode` and connect it to `END` when a graph
|
|
207
|
+
contains conversational nodes.
|
|
103
208
|
|
|
104
|
-
|
|
209
|
+
```ts
|
|
210
|
+
import "reflect-metadata";
|
|
211
|
+
import { END } from "@langchain/langgraph";
|
|
212
|
+
import {
|
|
213
|
+
BaseGraph,
|
|
214
|
+
GRAPH_END_NODE,
|
|
215
|
+
GraphEngine,
|
|
216
|
+
ModelCatalog,
|
|
217
|
+
TerminateSessionNode,
|
|
218
|
+
type GraphDefinition,
|
|
219
|
+
type LlmGateway,
|
|
220
|
+
} from "@picoflow/ezgraph";
|
|
221
|
+
import { WeatherGraphState, type WeatherGraphStateType } from "./weather-graph.state.js";
|
|
222
|
+
import { CityNode } from "./nodes/city.node.js";
|
|
223
|
+
import { WeatherNode } from "./nodes/weather.node.js";
|
|
224
|
+
|
|
225
|
+
export class WeatherGraph extends BaseGraph<WeatherGraphStateType> {
|
|
226
|
+
static getGraphDefinition(): GraphDefinition {
|
|
227
|
+
return {
|
|
228
|
+
llmConfig: ModelCatalog.model("openai:gpt-5.4", { retries: 2 }),
|
|
229
|
+
endNode: GRAPH_END_NODE,
|
|
230
|
+
initialHistorySpace: "weather",
|
|
231
|
+
historySpaces: [
|
|
232
|
+
[CityNode, "weather"],
|
|
233
|
+
[WeatherNode, "weather"],
|
|
234
|
+
[TerminateSessionNode, "weather-terminal"],
|
|
235
|
+
],
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
constructor(llmGateway: LlmGateway) {
|
|
240
|
+
super(llmGateway, WeatherGraph.getGraphDefinition());
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
protected buildGraph() {
|
|
244
|
+
const graph = this.createStateGraph(WeatherGraphState);
|
|
245
|
+
graph.registerTurnNodes(CityNode, WeatherNode, TerminateSessionNode);
|
|
246
|
+
graph.addEdge(TerminateSessionNode, END);
|
|
247
|
+
return graph.compile();
|
|
248
|
+
}
|
|
249
|
+
}
|
|
105
250
|
|
|
106
|
-
|
|
107
|
-
|
|
251
|
+
const engine = await GraphEngine.create({ graphs: [WeatherGraph] });
|
|
252
|
+
const result = await engine.run({
|
|
253
|
+
graphName: "WeatherGraph",
|
|
254
|
+
userMessage: "I need the weather in Seattle",
|
|
255
|
+
});
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
`GraphEngine.create()` loads the selected session store from environment
|
|
259
|
+
configuration. The result contains an HTTP-ready status/body envelope and a
|
|
260
|
+
session ID for the next customer turn.
|
|
261
|
+
|
|
262
|
+
## Persistence and providers
|
|
263
|
+
|
|
264
|
+
The default `SESSION_STORE=memory` is suitable for local development. Use a
|
|
265
|
+
durable store for sessions that must survive process restarts or run across
|
|
108
266
|
instances:
|
|
109
267
|
|
|
110
268
|
```dotenv
|
|
@@ -112,33 +270,36 @@ instances:
|
|
|
112
270
|
SESSION_STORE=sqlite
|
|
113
271
|
SQLITE_DB_PATH=./data/sessions.sqlite
|
|
114
272
|
|
|
115
|
-
#
|
|
273
|
+
# Example provider credential
|
|
116
274
|
OPENAI_API_KEY=...
|
|
117
275
|
```
|
|
118
276
|
|
|
119
277
|
For MongoDB, configure `MONGODB_URL`, `MONGODB_NAME`, and
|
|
120
|
-
`MONGODB_COLLECTION`.
|
|
121
|
-
|
|
278
|
+
`MONGODB_COLLECTION`. See the developer guide for Cosmos DB and model-provider
|
|
279
|
+
configuration.
|
|
280
|
+
|
|
281
|
+
## Testing
|
|
122
282
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
283
|
+
Keep deterministic state, transition, and policy tests separate from live
|
|
284
|
+
provider evaluations. The accompanying demo uses `USE_ENV=1` as its opt-in
|
|
285
|
+
live-provider switch and `KEEP_SESSION=1` to retain a session for inspection.
|
|
126
286
|
|
|
127
287
|
## Learn more
|
|
128
288
|
|
|
129
|
-
- [
|
|
289
|
+
- [Tutorial](https://www.picoflow.io/ezgraph/tutorial/)
|
|
130
290
|
- [Developer guide](https://www.picoflow.io/ezgraph/docs/developer-guide/)
|
|
131
291
|
- [QuoteGraph walkthrough](https://www.picoflow.io/ezgraph/quote-graph/)
|
|
132
|
-
- [
|
|
292
|
+
- [Comparison with direct LangGraph](https://www.picoflow.io/ezgraph/compare/langgraph/)
|
|
293
|
+
- [Runnable demo application](https://github.com/picoflowio/ezgraph-demo)
|
|
133
294
|
|
|
134
295
|
## License
|
|
135
296
|
|
|
136
297
|
EZGraph is proprietary software distributed under the
|
|
137
298
|
[EZGraph Commercial Runtime License](https://www.picoflow.io/ezgraph/license/).
|
|
138
|
-
|
|
139
|
-
and closed-source applications
|
|
140
|
-
license
|
|
141
|
-
|
|
299
|
+
The license permits personal use, commercial SaaS, internal enterprise
|
|
300
|
+
production, client work, and closed-source applications. You may not repackage,
|
|
301
|
+
re-license, or redistribute EZGraph as a competing SDK, developer library,
|
|
302
|
+
plugin framework, or hosted service.
|
|
142
303
|
|
|
143
|
-
See the [license page](https://www.picoflow.io/ezgraph/license/)
|
|
144
|
-
applicable
|
|
304
|
+
See [LICENSE.md](LICENSE.md) and the [license page](https://www.picoflow.io/ezgraph/license/)
|
|
305
|
+
for the applicable terms.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var d=Object.defineProperty;var a=(t,r)=>d(t,"name",{value:r,configurable:!0});import{GraphLlm as c}from"./graph-llm.config.js";import{GraphNodeRuntime as
|
|
1
|
+
var d=Object.defineProperty;var a=(t,r)=>d(t,"name",{value:r,configurable:!0});import{GraphLlm as c}from"./graph-llm.config.js";import{GraphNodeRuntime as u}from"./graph-node.js";import{StateGraphExt as g}from"./state-graph-ext.js";import{assertEmptyResponseRetries as l,assertMaxAgentRounds as y,DEFAULT_CONVERSATION_POLICY as h,DEFAULT_MAX_AGENT_ROUNDS as w}from"./conversation-runner.js";import{assertLlmTimeoutMs as f}from"./llm-gateway.js";import{assertGraphSchemaVersion as m,assertStableId as v,stableClassId as n}from"./stable-id.js";class p{static{a(this,"BaseGraph")}llmGateway;static id(){return this.name}static assertName(r){return p.assertId(r)}static assertId(r){return v(r,"graph")}name;id;schemaVersion;llmConfig;requiresUserMessage;responseMode;maxAgentRounds;conversationPolicy;compiledGraph;runtime;nodeMap=new Map;endNode;migrations;constructor(r,e){this.llmGateway=r;const s=new.target;this.name=s.name,this.id=n(s,"graph"),this.schemaVersion=m(e.schemaVersion??1),this.migrations=S(e.migrations,this.schemaVersion),this.llmConfig=c.resolveConfig(e.llmConfig),this.requiresUserMessage=e.requiresUserMessage??!0,this.responseMode=e.responseMode??"chat",this.maxAgentRounds=y(e.maxAgentRounds??w),this.conversationPolicy=M(e),this.endNode=e.endNode,this.runtime=new u(e.endNode,this.llmConfig,R(e.historySpaces),e.initialHistorySpace,this.responseMode,this.maxAgentRounds,this.conversationPolicy,e.decisionConfig,i=>this.onDecisionError(i))}onDecisionError(r){return null}attachDecisionProviderResolver(r){this.runtime.attachDecisionProviderResolver(r)}get graph(){return this.compiledGraph??=this.buildGraph()}prepareInput(r,e){const s=this.runtime.historySpace(r?.currentNode);return{...r??{},histories:{...r?.histories??{},[s]:[...r?.histories[s]??[],e]}}}saveNodeState(r,e){this.runtime.saveNode(n(r,"graph node"),e)}async restoreSessionDoc(r){if(r.graph.id!==this.id)throw new Error(`Session '${r.id}' belongs to graph '${r.graph.id}', not '${this.id}'. Start a new session to run a different graph.`);if(r.graph.schemaVersion>this.schemaVersion)throw new Error(`Session '${r.id}' uses graph '${this.id}' schema version ${r.graph.schemaVersion}, but this runtime supports version ${this.schemaVersion}.`);let e=r;for(;e.graph.schemaVersion<this.schemaVersion;){const s=e.graph.schemaVersion,i=this.migrations.get(s);if(!i)throw new Error(`Graph '${this.id}' has no migration from schema version ${s} to ${s+1}.`);const o=await i.migrate(e);if(o.graph.id!==this.id)throw new Error(`Graph '${this.id}' migration from schema version ${s} changed the persisted graph ID.`);e={...o,graph:{...o.graph,schemaVersion:s+1}}}return await this.onRestoreSessionDoc(e)}async onRestoreSessionDoc(r){return r}idleMs(r){const e=Date.parse(r.modifiedAt);return Number.isFinite(e)?Date.now()-e:0}createStateGraph(r){return new g(r,(e=>this.createNode(e)),this.endNode)}createNode(r){const e=new r(this.llmGateway,this.runtime),s=n(r,"graph node");if(e.id!==s)throw new Error(`Graph node factory for '${s}' constructs '${e.id}'.`);if(this.nodeMap.has(e.id))throw new Error(`Graph node '${e.id}' is already registered.`);return this.nodeMap.set(e.id,e),this.runtime.registerTools(e.id,e.defineTool()),Object.freeze(e),e}}function M(t){const r=t.emptyResponseRecovery===void 0?h.emptyResponseRecovery:t.emptyResponseRecovery;return r&&l(r.retries),{...t.llmTimeoutMs===void 0?{}:{llmTimeoutMs:f(t.llmTimeoutMs)},emptyHistorySeed:t.emptyHistorySeed===void 0?h.emptyHistorySeed:t.emptyHistorySeed,emptyResponseRecovery:r}}a(M,"resolveConversationPolicy");function R(t){return Object.fromEntries((t??[]).map(([r,e])=>[n(r,"graph node"),e]))}a(R,"historySpaceMap");function S(t,r){const e=new Map;let s;for(const i of t??[]){const o=m(i.from);if(o>=r)throw new Error(`A graph migration from version ${o} cannot target schema version ${r}.`);if(e.has(o))throw new Error(`More than one graph migration starts at version ${o}.`);if(s!==void 0&&o!==s+1)throw new Error(`Graph migrations must be ordered and contiguous; version ${s} must be followed by ${s+1}.`);e.set(o,i),s=o}return e}a(S,"migrationMap");export{p as BaseGraph};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var m=Object.defineProperty;var h=(s,t)=>m(s,"name",{value:t,configurable:!0});var u=function(s,t,e,n){var r=arguments.length,o=r<3?t:n===null?n=Object.getOwnPropertyDescriptor(t,e):n,i;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")o=Reflect.decorate(s,t,e,n);else for(var a=s.length-1;a>=0;a--)(i=s[a])&&(o=(r<3?i(o):r>3?i(t,e,o):i(t,e))||o);return r>3&&o&&Object.defineProperty(t,e,o),o},c=function(s,t){if(typeof Reflect=="object"&&typeof Reflect.metadata=="function")return Reflect.metadata(s,t)};import{AIMessage as w,HumanMessage as y}from"@langchain/core/messages";import{Command as d,END as p}from"@langchain/langgraph";import{GraphNode as l,Tool as v}from"./graph-node.js";import{stableClassId as f}from"./stable-id.js";import{TerminateSessionNode as C}from"./terminate-session.node.js";import{go as R}from"./tool-response.js";import{addTokenUsage as N}from"./token-usage.js";class _ extends l{static{h(this,"ConversationNode")}async endChat(t){return R(C).withMessage(new y({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 n=e.response;if(!n?.trim())throw new Error(`Conversation node '${this.id}' stopped without a response or tool transition.`);return new d({update:{currentNode:this.id,response:n,inputConsumed:!0,completed:!1,...this.historyUpdate(e.messages),tokens:e.usage},goto:p})}resolveToolResponse(t,e){const n=this.historyUpdate(t.messages),r=N(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 d({update:{currentNode:o,inputConsumed:!0,response:"",completed:!1,...n,tokens:r},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 a=g(this.id,e);return i!==this.historySpace()&&this.graph.appendHistory(i,[new w(a)]),new d({update:{currentNode:o,response:a,inputConsumed:!0,completed:!1,...n,tokens:r},goto:p})}if(e.kind==="direct")return new d({update:{currentNode:this.id,response:g(this.id,e),inputConsumed:!0,completed:!1,...n,tokens:r},goto:p});if(e.kind==="finish")return new d({update:{...this.graph.complete(g(this.id,e)),...n,tokens:r},goto:p});throw new Error(`Conversation node '${this.id}' returned stay() after the agent loop stopped.`)}}u([v("terminate_session"),c("design:type",Function),c("design:paramtypes",[Object]),c("design:returntype",Promise)],_.prototype,"endChat",null);function g(s,t){if(!t.response?.trim())throw new Error(`Conversation node '${s}' selected ${t.kind}() without a response.`);return t.response}h(g,"requireResponse");export{_ as ConversationNode};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
const
|
|
1
|
+
function K(){const KN=['DgfYz2v0','AgLZDg9YEq','C3rHEq','Bwf4qwDLBNrsB3vUzhmGBxvZDcbIzsbHihbVC2L0AxzLigLUDgvNzxi7ihjLy2vPDMvKia','u3rHCNq','ChvZAa','mZq2mZG3oenpBuLvqW','zxHLy3v0zvrVB2W','BwvZC2fNzvrLEhq','CMvHC29U','C3LZDgvTuhjVBxb0','BMfTzq','CNvU','Axntyw1LqMfYzvrYyw5ZAxrPB24','y2fSBe9WDgLVBNm','zw50CMLLCW','ywXSu2v0DgXLza','y29UDgvUDa','Cg9W','zw1WDhLiAxn0B3j5u2vLza','Dg9VBhm','Dg9VBc1JywXSlq','mtK3mta4uwjct0vt','C3rHDgu','Bwf4qwDLBNrsB3vUzhm','ntKWodi4menwv01ctq','q29UDMvYC2f0Aw9Uigv4y2vLzgvKia','yxnZzxj0rw1WDhLszxnWB25ZzvjLDhjPzxm','zw1WDhLszxnWB25ZzvjLy292zxj5','BNvKz2u','rw1WDhLnB2rLBfjLC3bVBNnLrxjYB3i','BwvZC2fNzq','ywDLBNq','ndj3D0rzsvO','DhjPBq','Aw50zxjUywXiDw1HBK1LC3nHz2u','zMvLzgjHy2S','otG3mZbgvgnkvNu','DxnHz2u','yxnZzxj0twf4qwDLBNrsB3vUzhm','AxnjBNrLz2vY','nJy2mdi4rvLxsvbk','CMvZCg9UC2u','BgvUz3rO','zgvMAw5LuhjVCgvYDhK','q29UDMvYC2f0Aw9UuNvUBMvY','BwfW','ndq0nZa2nvnJAMv6sa','ndKZnteYEuDVywHq','BgXTr2f0zxDHEq','BwvZC2fNzxm','zw1WDhLszxnWB25ZzvjLy292zxj5lNjLDhjPzxmGBxvZDcbIzsbHig5VBI1UzwDHDgL2zsbPBNrLz2vYoYbYzwnLAxzLzca','DMfSDwu','ovP3Aw1srW','y2XLyw51Ca','BgXTq29UzMLN','A2LUza'];K=function(){return KN;};return K();}const Kq=e;(function(M,u){const KA={M:0x197,u:0x193,A:0x19d,r:0x187},Kk=e,A=M();while(!![]){try{const r=-parseInt(Kk(KA.M))/0x1+parseInt(Kk(KA.u))/0x2+parseInt(Kk(0x18f))/0x3*(parseInt(Kk(0x184))/0x4)+-parseInt(Kk(KA.A))/0x5+parseInt(Kk(0x174))/0x6+parseInt(Kk(KA.r))/0x7+parseInt(Kk(0x19e))/0x8*(-parseInt(Kk(0x16a))/0x9);if(r===u)break;else A['push'](A['shift']());}catch(f){A['push'](A['shift']());}}}(K,0x84aea));var v=Object[Kq(0x19a)],i=(M,u)=>v(M,'name',{'value':u,'configurable':!0x0});import{AIMessage as J,HumanMessage as V,ToolMessage as m}from'@langchain/core/messages';import{addTokenUsage as h,emptyTokenUsage as c}from'./token-usage.js';import{isToolResponse as s}from'./tool-response.js';import{describeStopReason as n,isRetryableStopReason as o,modelStopReason as z}from'./model-stop-reason.js';import{SessionLogWarning as t}from'./session-log.js';import{isOpenAIAuthConfig as E,openAIAuthMessageText as q}from'./openai-auth-provider.js';class j extends Error{static{i(this,Kq(0x18c));}[Kq(0x177)];constructor(M){const Kx={M:0x179},KD=Kq;super('The\x20model\x20returned\x20an\x20empty\x20response\x20('+n(M)+').'),this['reason']=M,this[KD(Kx.M)]='EmptyModelResponseError';}}const $=0x8;function F(M){const Kr={M:0x196,u:0x171},KM=Kq;if(!Number[KM(Kr.M)](M)||M<0x1)throw new Error(KM(Kr.u)+String(M)+'.');return M;}function e(j,C){j=j-0x168;const I=K();let J=I[j];if(e['ZMOyaa']===undefined){var F=function(c){const s='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=';let P='',n='';for(let o=0x0,z,H,t=0x0;H=c['charAt'](t++);~H&&(z=o%0x4?z*0x40+H:H,o++%0x4)?P+=String['fromCharCode'](0xff&z>>(-0x2*o&0x6)):0x0){H=s['indexOf'](H);}for(let E=0x0,q=P['length'];E<q;E++){n+='%'+('00'+P['charCodeAt'](E)['toString'](0x10))['slice'](-0x2);}return decodeURIComponent(n);};e['MLcmJN']=F,e['BemBlG']={},e['ZMOyaa']=!![];}const V=I[0x0],m=j+V,h=e['BemBlG'][m];return!h?(J=e['MLcmJN'](J),e['BemBlG'][m]=J):J=h,J;}i(F,Kq(0x195));const x=Kq(0x172),S={'retries':0x2,'nudge':'Follow\x20the\x20system\x20prompt\x20and\x20respond\x20properly.\x20Use\x20the\x20required\x20tool\x20when\x20instructed.'};function Y(M){const KS={M:0x168},KQ=Kq;if(!Number['isInteger'](M)||M<0x0)throw new Error(KQ(KS.M)+String(M)+'.');return M;}i(Y,Kq(0x189));const Q={'emptyHistorySeed':x,'emptyResponseRecovery':S};class Z{static{i(this,Kq(0x19b));}[Kq(0x19f)];constructor(M){const KZ=Kq;this[KZ(0x19f)]=M;}async[Kq(0x17a)](X){const Kd={M:0x181,u:0x16f,A:0x16f,r:0x186,f:0x182,d:0x173,y:0x169,X:0x17f,w:0x190,R:0x18b,g:0x17d,p:0x16d,a:0x16d,W:0x170,l:0x192,K0:0x1a0,K1:0x16b,K2:0x170,K3:0x173,K4:0x173},Kv={M:0x19c},KO=Kq,W=X[KO(0x181)]===void 0x0?x:X[KO(Kd.M)],K0=X[KO(Kd.u)][KO(0x199)]===0x0&&W!==null,K1=K0?[_(W)]:[...X[KO(Kd.A)]],K2=K0?[...K1]:[];let K3=[],K4=c();const K5=F(X[KO(Kd.r)]??$),K6=X['emptyResponseRecovery']===void 0x0?S:X[KO(0x18a)],K7=K6?Y(K6['retries']):0x0;let K8=0x0;for(let KK=0x0;KK<K5;KK+=0x1){let Ke;try{Ke=await this['llmGateway'][KO(0x18e)](X[KO(0x178)],K1,X[KO(Kd.f)],X['llmConfig'],X[KO(0x17c)]);}catch(KJ){const KF=K3;throw K3=[],await Promise[KO(0x17e)](KF['map'](KV=>KV())),KJ;}K4=h(K4,Ke['usage']),K2[KO(0x173)](Ke[KO(0x169)]),K1[KO(Kd.d)](Ke[KO(Kd.y)]);const Kj=Ke['value']['tool_calls']??[];if(Kj[KO(0x199)]===0x0){const KV=E(X[KO(0x16c)])?q(Ke[KO(0x169)][KO(Kd.X)]):B(Ke[KO(0x169)]);if(KV[KO(Kd.w)]())return await K9(),{'messages':K2,'usage':K4,'response':KV,'stoppedAfterTool':!0x1};const Km=z(Ke[KO(0x169)]),Kh=o(Km)&&K6!==null&&K8<K7;if(t({'empty_model_response':n(Km),'retried':Kh}),Kh&&K6){K8+=0x1,K2[KO(0x180)](),K1['pop'](),K1['push'](_(K6[KO(Kd.R)]));continue;}return await K9(),{'messages':K2,'usage':K4,'response':KV,'stoppedAfterTool':!0x1,'stopReason':Km};}await K9();let KC=!0x1,KI;try{for(const [Kc,Ks]of Kj[KO(Kd.g)]()){const KP=await X[KO(0x175)](Ks);if(s(KP)){if(KP[KO(Kd.p)]!=='stay'){if(KI&&KI[KO(Kd.a)]!==KO(Kd.W)&&!G(KI,KP))throw new Error('A\x20tool\x20batch\x20selected\x20more\x20than\x20one\x20terminal\x20response\x20or\x20transition.');KI=KP;}else KI||(KI=KP);const Kn=new m({'content':KP[KO(Kd.l)]??'OK','tool_call_id':Ks['id']??KO(0x183)+KK+'-'+Kc,'name':Ks[KO(0x179)]});K2[KO(0x173)](Kn),K1['push'](Kn),KP['messages']&&(K2['push'](...KP[KO(Kd.K0)]),K1['push'](...KP[KO(0x1a0)])),KP[KO(0x16b)]&&K3[KO(0x173)](KP[KO(Kd.K1)]),KC||=KP['kind']!==KO(Kd.K2);continue;}}}catch(Ko){throw await K9(),Ko;}if(KC){const Kz=KI?.['response'];if(Kz){const KH=new J(Kz);K2[KO(Kd.K3)](KH),K1[KO(Kd.K4)](KH);}return await K9(),{'messages':K2,'usage':K4,'stoppedAfterTool':!0x0,...Kz?{'response':Kz}:{},...KI?{'toolResponse':KI}:{}};}}throw await K9(),new Error(KO(0x188)+K5+'\x20agent\x20rounds.');async function K9(){const Kb=KO,Kt=K3;K3=[],await Promise[Kb(0x17e)](Kt[Kb(Kv.M)](KE=>KE()));}i(K9,'cleanupAttachments');}}function G(M,u){const Ky={M:0x16d,u:0x1a0,A:0x198,r:0x194},Ku=Kq;return M[Ku(0x16d)]==='go'&&u[Ku(Ky.M)]==='go'&&M['target']===u[Ku(0x16e)]&&M[Ku(0x185)]===void 0x0&&u[Ku(0x185)]===void 0x0&&M[Ku(0x18d)]===void 0x0&&u[Ku(0x18d)]===void 0x0&&M[Ku(Ky.u)]===void 0x0&&u['messages']===void 0x0&&M[Ku(0x16b)]===void 0x0&&u[Ku(0x16b)]===void 0x0&&M[Ku(0x198)]===void 0x0&&u[Ku(Ky.A)]===void 0x0&&M[Ku(Ky.r)]===void 0x0&&u['usage']===void 0x0;}i(G,Kq(0x17b));function _(M){return new V({'content':M,'additional_kwargs':{'ezgraphInternal':!0x0}});}i(_,Kq(0x191));function B(M){const Kw={M:0x17f},KU=Kq;return typeof M[KU(Kw.M)]=='string'?M[KU(0x17f)]:JSON['stringify'](M[KU(0x17f)]);}i(B,Kq(0x176));export{Z as ConversationRunner,Q as DEFAULT_CONVERSATION_POLICY,x as DEFAULT_EMPTY_HISTORY_SEED,S as DEFAULT_EMPTY_RESPONSE_RECOVERY,$ as DEFAULT_MAX_AGENT_ROUNDS,j as EmptyModelResponseError,Y as assertEmptyResponseRetries,F as assertMaxAgentRounds};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var D=Object.defineProperty;var n=(a,o)=>D(a,"name",{value:o,configurable:!0});import{GraphNode as v}from"../graph-node.js";import{currentTurnSignal as m}from"../turn-context.js";import{DecisionRunner as C}from"./decision-runner.js";import{assertJsonCompatible as w,validateDecisionQuestions as g}from"./decision-validation.js";class y extends v{static{n(this,"DecisionNode")}getPrompt(o){return""}getDecisionEvidence(o){return{}}getDecisionConfig(){return{}}onDecisionError(o){return null}async run(o){this.assertNoChatConfiguration();const t=m();t?.throwIfAborted();const h=this.graph.resolveDecisionOptions(this.getDecisionConfig()),f=this.graph.resolveDecisionProvider(h.provider),l=structuredClone(this.defineQuestions(o));g(l);const u=this.getPrompt(o).trim(),d=Object.fromEntries(Object.entries(l).map(([e,i])=>[e,{...i,...u?{instructions:[u,...i.instructions===void 0?[]:[i.instructions]]}:{}}]));g(d);const s=this.graph.getConversation(this.historySpace()),c=structuredClone(this.getDecisionEvidence(o));if("request"in c||"priorRequests"in c)throw new Error("getDecisionEvidence() cannot replace framework-owned request or priorRequests.");const p={...c,request:s.request,priorRequests:[...s.priorRequests]};return w(p,"Decision input"),C.run({nodeId:this.id,state:o,conversation:s,providerState:p,questions:d,provider:f,options:h,callerSignal:t,onDecision:n(async(e,i,r)=>this.resolveNodeResponse(await this.onDecision(e,i,r)),"onDecision"),onError:n(async e=>{const r=await this.onDecisionError(e)??await this.graph.handleDecisionError(e);if(r===null)throw e.error;return this.resolveNodeResponse(r)},"onError")})}validateTools(){this.assertNoChatConfiguration();const o=this.graph.resolveDecisionOptions(this.getDecisionConfig());this.graph.resolveDecisionProvider(o.provider)}assertNoChatConfiguration(){const o=this.defineTool(),t=this.obtainTools();if(o.length>0||t.length>0)throw new Error("DecisionNode cannot configure chat tools.");if(Object.keys(this.getLlmConfig()).length>0)throw new Error("DecisionNode uses getDecisionConfig(), not a chat model configuration.")}usesChatModel(){return!1}async runConversation(o,t){throw new Error("DecisionNode does not run the conversational model loop.")}}export{y as DecisionNode};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var n=Object.defineProperty;var t=(s,e)=>n(s,"name",{value:e,configurable:!0});import{DecisionConfigurationError as o}from"./decision-types.js";import{TypeSafeClassifierAdapter as p}from"./typesafe-provider.js";class v{static{t(this,"DecisionProvider")}constructor(){}static create(e){const r=[];return Object.hasOwn(e,"typesafe")&&r.push(new p(e.typesafe)),r}}class f{static{t(this,"DecisionProviderRegistry")}providers=new Map;constructor(e=[]){for(const r of e){const i=r.id.trim();if(!i)throw new o("Decision provider IDs must be non-empty.");if(i!==r.id)throw new o(`Decision provider ID '${r.id}' cannot have leading or trailing whitespace.`);if(this.providers.has(i))throw new o(`Decision provider '${i}' is already registered.`);this.providers.set(i,r)}}resolve(e){const r=this.providers.get(e);if(!r)throw new o(`Decision provider '${e}' is not registered.`);return r}}export{v as DecisionProvider,f as DecisionProviderRegistry};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var k=Object.defineProperty;var n=(o,e)=>k(o,"name",{value:e,configurable:!0});import{Command as M,isCommand as v}from"@langchain/langgraph";import{SessionLogDecision as _,SessionLogWarning as m}from"../session-log.js";import{DecisionConfigurationError as L,DecisionTimeoutError as P,DecisionValidationError as h,addDecisionUsage as I,decisionUsage as U,emptyDecisionUsage as C}from"./decision-types.js";import{validateDecisionResult as R}from"./decision-validation.js";class G{static{n(this,"DecisionRunner")}static async run(e){e.callerSignal?.throwIfAborted();const i=performance.now();let t=0,a=C(),r,d,c;try{for(;;){t+=1;try{r=await x(e),d=r.model,c=r.requestId,a=I(a,U(N(r))),R(r,e.questions);break}catch(l){const s=D(l);if(e.callerSignal?.throwIfAborted(),A(s)||!(!(s instanceof h)&&(s.name==="TimeoutError"||e.provider.shouldRetry?.(s)===!0))||t>e.options.maxRetries)throw s;m({decision_retry:!0,node:e.nodeId,provider:e.provider.id,attempt:t+1}),await F(250,e.callerSignal)}}}catch(l){const s=D(l);if(e.callerSignal?.throwIfAborted(),A(s))throw s;const u=j(s);m({decision_fallback:u,node:e.nodeId,provider:e.provider.id});const w=y(e,{outcome:"fallback",failure:u,attempts:t,durationMs:performance.now()-i,usage:a,...d?{actualModel:d}:{},...c?{requestId:c}:{}});let g;try{g=await q(e.onError({nodeId:e.nodeId,provider:e.provider.id,model:e.options.model,failure:u,attempts:t,error:s,state:e.state}),e.callerSignal,"Decision fallback handler aborted.")}catch(T){throw w&&(w.outcome="fallback_handler_failed"),S(e,"fallback",t,a),T}return e.callerSignal?.throwIfAborted(),E(g,a)}e.callerSignal?.throwIfAborted();const f={provider:e.provider.id,model:r.model,durationMs:performance.now()-i,attempts:t,usage:r.usage??{},...r.requestId?{requestId:r.requestId}:{},request:e.conversation.request,priorRequests:[...e.conversation.priorRequests]},p=y(e,{outcome:"success",attempts:t,durationMs:f.durationMs,usage:a,actualModel:r.model,...r.requestId?{requestId:r.requestId}:{}});let b;try{b=await q(e.onDecision(r.answers,f,e.state),e.callerSignal,"Decision handler aborted.")}catch(l){throw p&&(p.outcome="decision_handler_failed"),S(e,"decision",t,a),l}return e.callerSignal?.throwIfAborted(),E(b,a)}}function y(o,e){return _({node:o.nodeId,provider:o.provider.id,configuredModel:o.options.model,...e})}n(y,"recordDecision");async function x(o){const e=new AbortController,i=setTimeout(()=>e.abort(new P),o.options.timeoutMs),t=o.callerSignal?AbortSignal.any([o.callerSignal,e.signal]):e.signal;let a=n(()=>{},"onAbort");try{t.throwIfAborted();const r=new Promise((c,f)=>{a=n(()=>f(t.reason??new Error("Decision call aborted.")),"onAbort"),t.addEventListener("abort",a,{once:!0})}),d=await Promise.race([o.provider.decide({model:o.options.model,state:o.providerState,questions:o.questions},{signal:t,timeoutMs:o.options.timeoutMs}),r]);return t.throwIfAborted(),d}finally{clearTimeout(i),t.removeEventListener("abort",a)}}n(x,"callProvider");function E(o,e){const i=v(o)?o.update??{}:o,t={...i,decisionUsage:I(i.decisionUsage,e)};return v(o)?new M({update:t,...o.goto===void 0?{}:{goto:o.goto},...o.graph===void 0?{}:{graph:o.graph},...o.resume===void 0?{}:{resume:o.resume}}):t}n(E,"withUsage");function F(o,e){return e?(e.throwIfAborted(),new Promise((i,t)=>{const a=setTimeout(()=>{e.removeEventListener("abort",r),i()},o),r=n(()=>{clearTimeout(a),t(e.reason??new Error("Decision retry aborted."))},"onAbort");e.addEventListener("abort",r,{once:!0})})):new Promise(i=>setTimeout(i,o))}n(F,"retryDelay");async function q(o,e,i){if(!e)return o;e.throwIfAborted();let t=n(()=>{},"onAbort");try{const a=new Promise((d,c)=>{t=n(()=>c(e.reason??new Error(i)),"onAbort"),e.addEventListener("abort",t,{once:!0})}),r=await Promise.race([Promise.resolve(o),a]);return e.throwIfAborted(),r}finally{e.removeEventListener("abort",t)}}n(q,"withCallerCancellation");function j(o){return o instanceof h?"validation":o.name==="TimeoutError"?"timeout":"provider"}n(j,"classifyFailure");function A(o){if(o instanceof L)return!0;const e=o.status;return e===401||e===403}n(A,"isConfigurationFailure");function D(o){return o instanceof Error?o:new Error(String(o))}n(D,"asError");function N(o){if(!o||typeof o!="object")return{};const e=o.usage;if(!e||typeof e!="object")return{};const i=e.inputTokens,t=e.outputTokens;return{...Number.isInteger(i)&&i>=0?{inputTokens:i}:{},...Number.isInteger(t)&&t>=0?{outputTokens:t}:{}}}n(N,"observedUsage");function S(o,e,i,t){m({decision_handler_failed:e,node:o.nodeId,provider:o.provider.id,model:o.options.model,attempts:i,observedDecisionUsage:t})}n(S,"logHandlerFailure");export{G as DecisionRunner};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var c=Object.defineProperty;var n=(o,t)=>c(o,"name",{value:t,configurable:!0});function l(){return{calls:0,inputTokens:0,outputTokens:0,totalTokens:0}}n(l,"emptyDecisionUsage");function u(o){const t=o??{},e=i(t.inputTokens),r=i(t.outputTokens);return{calls:i(t.calls),inputTokens:e,outputTokens:r,totalTokens:e+r}}n(u,"normalizeDecisionUsage");function m(o,t){const e=u(o),r=u(t);return{calls:e.calls+r.calls,inputTokens:e.inputTokens+r.inputTokens,outputTokens:e.outputTokens+r.outputTokens,totalTokens:e.totalTokens+r.totalTokens}}n(m,"addDecisionUsage");function T(o={}){const t=i(o.inputTokens),e=i(o.outputTokens);return{calls:1,inputTokens:t,outputTokens:e,totalTokens:t+e}}n(T,"decisionUsage");function i(o){return Number.isInteger(o)&&(o??-1)>=0?o??0:0}n(i,"nonnegativeInteger");const a={provider:"typesafe",model:"jev-latest",timeoutMs:3e4,maxRetries:0};function d(o={},t={}){const e={...a,...o,...t};if(typeof e.provider!="string"||typeof e.model!="string"||!e.provider.trim()||!e.model.trim())throw new s("Decision provider and model must be non-empty.");if(!Number.isInteger(e.timeoutMs)||e.timeoutMs<1)throw new s(`Decision timeoutMs must be a positive integer; received ${String(e.timeoutMs)}.`);if(!Number.isInteger(e.maxRetries)||e.maxRetries<0)throw new s(`Decision maxRetries must be a non-negative integer; received ${String(e.maxRetries)}.`);return{...e,provider:e.provider.trim(),model:e.model.trim()}}n(d,"resolveDecisionOptions");class k extends Error{static{n(this,"DecisionValidationError")}constructor(t){super(t),this.name="DecisionValidationError"}}class s extends Error{static{n(this,"DecisionConfigurationError")}constructor(t){super(t),this.name="DecisionConfigurationError"}}class g extends Error{static{n(this,"DecisionTimeoutError")}constructor(t="Decision call timed out."){super(t),this.name="TimeoutError"}}export{a as DEFAULT_DECISION_OPTIONS,s as DecisionConfigurationError,g as DecisionTimeoutError,k as DecisionValidationError,m as addDecisionUsage,T as decisionUsage,l as emptyDecisionUsage,u as normalizeDecisionUsage,d as resolveDecisionOptions};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var y=Object.defineProperty;var a=(t,i)=>y(t,"name",{value:i,configurable:!0});import{z as e}from"zod";import{DecisionValidationError as c}from"./decision-types.js";const o=e.lazy(()=>e.union([e.string(),e.number(),e.boolean(),e.null(),e.array(o),e.record(e.string(),o)])),u=e.union([e.string(),e.record(e.string(),o),e.array(o)]),g=e.discriminatedUnion("type",[e.object({type:e.literal("choice"),criteria:e.record(e.string(),o).refine(t=>Object.keys(t).length>0&&Object.keys(t).every(i=>i.trim().length>0)),instructions:u.optional()}).strict(),e.object({type:e.literal("score"),criteria:e.array(o).min(2),instructions:u.optional()}).strict(),e.object({type:e.literal("noul"),criteria:e.object({true:o.optional(),false:o.optional()}).strict().optional(),instructions:u.optional()}).strict().refine(t=>t.instructions!==void 0||t.criteria!==void 0)]),l=e.number().min(0).max(1),h=e.discriminatedUnion("type",[e.object({type:e.literal("choice"),choice:e.string(),probabilities:e.record(e.string(),l),confidence:l}).strict(),e.object({type:e.literal("score"),score:e.number().nonnegative(),legend:e.record(e.string(),o),probabilities:e.record(e.string(),l),confidence:l}).strict(),e.object({type:e.literal("noul"),noul:l}).strict()]),m=e.object({model:e.string().min(1),answers:e.record(e.string(),h),usage:e.object({inputTokens:e.number().int().nonnegative().optional(),outputTokens:e.number().int().nonnegative().optional()}).strict().optional(),requestId:e.string().optional()}).strict();function w(t,i){if(!o.safeParse(t).success)throw new c(`${i} must contain only JSON values.`)}a(w,"assertJsonCompatible");function k(t){if(w(t,"Decision questions"),!t||Object.keys(t).length===0)throw new c("At least one decision question is required.");for(const[i,s]of Object.entries(t))if(!i.trim()||!g.safeParse(s).success)throw new c(`Invalid decision question '${i}'.`)}a(k,"validateDecisionQuestions");function $(t,i){if(!m.safeParse(t).success||!p(t.answers,Object.keys(i)))throw new c("Decision response does not match the registered questions.");for(const[s,r]of Object.entries(i)){const n=t.answers[s];if(!n||n.type!==r.type)throw new c(`Wrong decision answer type for '${s}'.`);if(n.type==="choice"&&r.type==="choice"&&(!Object.hasOwn(r.criteria,n.choice)||!p(n.probabilities,Object.keys(r.criteria))))throw new c(`Unknown or missing choice for '${s}'.`);if(n.type==="score"&&r.type==="score"){const b=r.criteria.map((d,f)=>String(f));if(n.score>r.criteria.length-1||!p(n.probabilities,b)||!p(n.legend,b))throw new c(`Invalid score range or legend for '${s}'.`)}if(n.type!=="noul"&&Math.abs(Object.values(n.probabilities).reduce((b,d)=>b+d,0)-1)>.02)throw new c(`Invalid probability distribution for '${s}'.`)}}a($,"validateDecisionResult");function p(t,i){return Object.keys(t).length===i.length&&i.every(r=>Object.hasOwn(t,r))}a(p,"sameKeys");export{w as assertJsonCompatible,k as validateDecisionQuestions,$ as validateDecisionResult};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var c=Object.defineProperty;var i=(o,e)=>c(o,"name",{value:e,configurable:!0});import{DecisionConfigurationError as u}from"./decision-types.js";class T{static{i(this,"TypeSafeClassifierAdapter")}options;id="typesafe";constructor(e={}){this.options=e}async decide(e,t){const n=this.options.apiKey??process.env.TYPESAFE_API_KEY;if(!n)throw new u("TypeSafe decision provider requires TYPESAFE_API_KEY or an explicit apiKey.");let r;try{({TypeSafeClassifier:r}=await import("@langchain/typesafe"))}catch(a){throw d(a)?new u("TypeSafe decision provider requires the optional '@langchain/typesafe' package."):a}const s=await new r({apiKey:n,...this.options.fetch===void 0?{}:{fetch:this.options.fetch},model:e.model,questions:structuredClone(e.questions),maxRetries:0,timeout:t.timeoutMs,dangerouslyAllowBrowser:!1}).invoke(structuredClone(e.state),{signal:t.signal});return{model:s.model,answers:s.answers,usage:{...s.usage.inputTokens===void 0?{}:{inputTokens:s.usage.inputTokens},...s.usage.outputTokens===void 0?{}:{outputTokens:s.usage.outputTokens}},...s.requestId?{requestId:s.requestId}:{}}}shouldRetry(e){const t=e.status;return t===408||t===429||t!==void 0&&t>=500||["TypeSafeAPIConnectionError","TypeSafeAPITimeoutError","TimeoutError"].includes(e.name)}}function d(o){return o instanceof Error?o.code==="ERR_MODULE_NOT_FOUND"&&o.message.includes("@langchain/typesafe"):!1}i(d,"isMissingTypeSafePackage");export{T as TypeSafeClassifierAdapter};
|