@kortyx/runtime 0.5.0 → 0.5.1
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/CHANGELOG.md +9 -0
- package/dist/index.d.ts +127 -11
- package/dist/index.js +1303 -25
- package/dist/index.js.map +1 -0
- package/package.json +6 -5
- package/dist/checkpointer.d.ts +0 -3
- package/dist/checkpointer.d.ts.map +0 -1
- package/dist/checkpointer.js +0 -20
- package/dist/framework/adapter.d.ts +0 -21
- package/dist/framework/adapter.d.ts.map +0 -1
- package/dist/framework/adapter.js +0 -74
- package/dist/framework/in-memory-checkpointer.d.ts +0 -9
- package/dist/framework/in-memory-checkpointer.d.ts.map +0 -1
- package/dist/framework/in-memory-checkpointer.js +0 -134
- package/dist/framework/pending-requests.d.ts +0 -33
- package/dist/framework/pending-requests.d.ts.map +0 -1
- package/dist/framework/pending-requests.js +0 -32
- package/dist/framework/redis/pending-request-store.d.ts +0 -8
- package/dist/framework/redis/pending-request-store.d.ts.map +0 -1
- package/dist/framework/redis/pending-request-store.js +0 -30
- package/dist/framework/redis/redis-checkpointer.d.ts +0 -13
- package/dist/framework/redis/redis-checkpointer.d.ts.map +0 -1
- package/dist/framework/redis/redis-checkpointer.js +0 -159
- package/dist/framework/redis/redis-client.d.ts +0 -13
- package/dist/framework/redis/redis-client.d.ts.map +0 -1
- package/dist/framework/redis/redis-client.js +0 -181
- package/dist/framework/redis/redis-store.d.ts +0 -20
- package/dist/framework/redis/redis-store.d.ts.map +0 -1
- package/dist/framework/redis/redis-store.js +0 -107
- package/dist/graph/create-langgraph.d.ts +0 -17
- package/dist/graph/create-langgraph.d.ts.map +0 -1
- package/dist/graph/create-langgraph.js +0 -312
- package/dist/index.d.ts.map +0 -1
- package/dist/interrupt/tokens.d.ts +0 -3
- package/dist/interrupt/tokens.d.ts.map +0 -1
- package/dist/interrupt/tokens.js +0 -11
- package/dist/node-loader.d.ts +0 -7
- package/dist/node-loader.d.ts.map +0 -1
- package/dist/node-loader.js +0 -58
- package/dist/node-registry.d.ts +0 -7
- package/dist/node-registry.d.ts.map +0 -1
- package/dist/node-registry.js +0 -27
- package/dist/registry/file-registry.d.ts +0 -9
- package/dist/registry/file-registry.d.ts.map +0 -1
- package/dist/registry/file-registry.js +0 -105
- package/dist/registry/in-memory-registry.d.ts +0 -6
- package/dist/registry/in-memory-registry.d.ts.map +0 -1
- package/dist/registry/in-memory-registry.js +0 -25
- package/dist/registry/interface.d.ts +0 -12
- package/dist/registry/interface.d.ts.map +0 -1
- package/dist/registry/interface.js +0 -2
- package/dist/state/build-initial-state.d.ts +0 -9
- package/dist/state/build-initial-state.d.ts.map +0 -1
- package/dist/state/build-initial-state.js +0 -18
|
@@ -1,312 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.createLangGraph = createLangGraph;
|
|
4
|
-
const hooks_1 = require("@kortyx/hooks");
|
|
5
|
-
const internal_1 = require("@kortyx/hooks/internal");
|
|
6
|
-
const utils_1 = require("@kortyx/utils");
|
|
7
|
-
const langgraph_1 = require("@langchain/langgraph");
|
|
8
|
-
const checkpointer_1 = require("../checkpointer");
|
|
9
|
-
const node_loader_1 = require("../node-loader");
|
|
10
|
-
async function createLangGraph(workflow, config) {
|
|
11
|
-
const StateAnnotation = langgraph_1.Annotation.Root({
|
|
12
|
-
input: (langgraph_1.Annotation),
|
|
13
|
-
output: (langgraph_1.Annotation),
|
|
14
|
-
lastNode: (langgraph_1.Annotation),
|
|
15
|
-
lastCondition: (langgraph_1.Annotation),
|
|
16
|
-
lastIntent: (langgraph_1.Annotation),
|
|
17
|
-
memory: (0, langgraph_1.Annotation)({
|
|
18
|
-
reducer: (l, r) => (0, utils_1.deepMergeWithArrayOverwrite)(l ?? {}, r ?? {}),
|
|
19
|
-
default: () => ({}),
|
|
20
|
-
}),
|
|
21
|
-
config: (langgraph_1.Annotation),
|
|
22
|
-
transitionTo: (langgraph_1.Annotation),
|
|
23
|
-
data: (0, langgraph_1.Annotation)({
|
|
24
|
-
reducer: (l, r) => (0, utils_1.deepMergeWithArrayOverwrite)(l ?? {}, r ?? {}),
|
|
25
|
-
default: () => ({}),
|
|
26
|
-
}),
|
|
27
|
-
conversationHistory: (0, langgraph_1.Annotation)({
|
|
28
|
-
reducer: (l, r) => [...(l ?? []), ...(r ?? [])],
|
|
29
|
-
default: () => [],
|
|
30
|
-
}),
|
|
31
|
-
awaitingHumanInput: (langgraph_1.Annotation),
|
|
32
|
-
});
|
|
33
|
-
const builder = new langgraph_1.StateGraph(StateAnnotation);
|
|
34
|
-
const edgeApi = builder;
|
|
35
|
-
const workflowName = workflow.id;
|
|
36
|
-
const runtimeConfig = config;
|
|
37
|
-
if (!runtimeConfig.emit) {
|
|
38
|
-
runtimeConfig.emit = () => { };
|
|
39
|
-
}
|
|
40
|
-
const isRecord = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
41
|
-
const toRecordInput = (value) => {
|
|
42
|
-
if (isRecord(value))
|
|
43
|
-
return value;
|
|
44
|
-
if (value === undefined)
|
|
45
|
-
return {};
|
|
46
|
-
return { rawInput: value };
|
|
47
|
-
};
|
|
48
|
-
const buildNodeConfig = (params, nodeBehavior) => {
|
|
49
|
-
const model = isRecord(params?.model) ? params.model : undefined;
|
|
50
|
-
const tool = isRecord(params?.tool) ? params.tool : undefined;
|
|
51
|
-
const behaviorFromParams = isRecord(params?.behavior)
|
|
52
|
-
? params.behavior
|
|
53
|
-
: undefined;
|
|
54
|
-
const behavior = {
|
|
55
|
-
...(behaviorFromParams ?? {}),
|
|
56
|
-
...(nodeBehavior ?? {}),
|
|
57
|
-
};
|
|
58
|
-
const options = params ?? undefined;
|
|
59
|
-
return {
|
|
60
|
-
...(model ? { model } : {}),
|
|
61
|
-
...(tool ? { tool } : {}),
|
|
62
|
-
...(Object.keys(behavior).length > 0 ? { behavior } : {}),
|
|
63
|
-
...(options ? { options } : {}),
|
|
64
|
-
};
|
|
65
|
-
};
|
|
66
|
-
for (const [nodeId, nodeDef] of Object.entries(workflow.nodes ?? {})) {
|
|
67
|
-
const resolvedRun = await (0, node_loader_1.resolveNodeHandler)({
|
|
68
|
-
run: nodeDef.run,
|
|
69
|
-
workflow,
|
|
70
|
-
});
|
|
71
|
-
const nodeParams = (nodeDef.params ?? undefined);
|
|
72
|
-
const nodeConfig = buildNodeConfig(nodeParams, (nodeDef.behavior ?? undefined));
|
|
73
|
-
const behavior = nodeConfig.behavior ?? {};
|
|
74
|
-
builder.addNode(nodeId, async (state) => {
|
|
75
|
-
const ctx = {
|
|
76
|
-
graph: { name: workflowName, node: nodeId },
|
|
77
|
-
config: nodeConfig,
|
|
78
|
-
emit: (event, payload) => {
|
|
79
|
-
runtimeConfig.emit(event, payload);
|
|
80
|
-
},
|
|
81
|
-
error: (message) => {
|
|
82
|
-
runtimeConfig.emit("error", {
|
|
83
|
-
node: nodeId,
|
|
84
|
-
message,
|
|
85
|
-
});
|
|
86
|
-
},
|
|
87
|
-
awaitInterrupt: (interruptConfig) => {
|
|
88
|
-
const { kind, question } = interruptConfig;
|
|
89
|
-
const isMulti = kind === "multi-choice" ||
|
|
90
|
-
(interruptConfig.kind !== "text" &&
|
|
91
|
-
interruptConfig.multiple === true);
|
|
92
|
-
const payload = {
|
|
93
|
-
kind,
|
|
94
|
-
multiple: isMulti,
|
|
95
|
-
question,
|
|
96
|
-
...(interruptConfig.kind !== "text"
|
|
97
|
-
? { options: interruptConfig.options }
|
|
98
|
-
: {}),
|
|
99
|
-
};
|
|
100
|
-
runtimeConfig.emit("interrupt", {
|
|
101
|
-
node: nodeId,
|
|
102
|
-
workflow: workflowName,
|
|
103
|
-
input: payload,
|
|
104
|
-
});
|
|
105
|
-
const resumed = (0, langgraph_1.interrupt)(payload);
|
|
106
|
-
if (isMulti) {
|
|
107
|
-
if (Array.isArray(resumed)) {
|
|
108
|
-
return resumed
|
|
109
|
-
.map((v) => typeof v === "string"
|
|
110
|
-
? v
|
|
111
|
-
: v && typeof v.id === "string"
|
|
112
|
-
? v.id
|
|
113
|
-
: "")
|
|
114
|
-
.filter(Boolean);
|
|
115
|
-
}
|
|
116
|
-
if (typeof resumed === "string")
|
|
117
|
-
return [resumed];
|
|
118
|
-
return [];
|
|
119
|
-
}
|
|
120
|
-
if (typeof resumed === "string")
|
|
121
|
-
return resumed;
|
|
122
|
-
if (resumed && typeof resumed.id === "string")
|
|
123
|
-
return String(resumed.id);
|
|
124
|
-
return "";
|
|
125
|
-
},
|
|
126
|
-
speak: async (args) => {
|
|
127
|
-
const providerId = args.model?.provider ?? nodeConfig.model?.provider ?? "google";
|
|
128
|
-
const modelName = args.model?.name ?? nodeConfig.model?.name ?? "gemini-2.5-flash";
|
|
129
|
-
const temperature = args.model?.temperature ?? nodeConfig.model?.temperature ?? 0.3;
|
|
130
|
-
const getProvider = runtimeConfig.getProvider;
|
|
131
|
-
if (!getProvider) {
|
|
132
|
-
throw new Error("Runtime config is missing getProvider; wire a provider factory into @kortyx/runtime.");
|
|
133
|
-
}
|
|
134
|
-
const result = await (0, internal_1.runReasonEngine)({
|
|
135
|
-
getProvider,
|
|
136
|
-
model: {
|
|
137
|
-
providerId,
|
|
138
|
-
modelId: modelName,
|
|
139
|
-
},
|
|
140
|
-
input: args.user ?? "",
|
|
141
|
-
system: args.system,
|
|
142
|
-
temperature,
|
|
143
|
-
stream: true,
|
|
144
|
-
emit: true,
|
|
145
|
-
nodeId,
|
|
146
|
-
emitEvent: ctx.emit,
|
|
147
|
-
});
|
|
148
|
-
return result.text;
|
|
149
|
-
},
|
|
150
|
-
};
|
|
151
|
-
if (behavior.checkpoint && config?.onCheckpoint) {
|
|
152
|
-
config.onCheckpoint({ nodeId, state });
|
|
153
|
-
}
|
|
154
|
-
const maxAttempts = behavior.retry?.maxAttempts ?? 1;
|
|
155
|
-
let attempt = 0;
|
|
156
|
-
let nodeResult;
|
|
157
|
-
let hookMemoryUpdates = null;
|
|
158
|
-
let retryHookMemory = null;
|
|
159
|
-
while (attempt < maxAttempts) {
|
|
160
|
-
try {
|
|
161
|
-
attempt++;
|
|
162
|
-
const attemptState = retryHookMemory && typeof retryHookMemory === "object"
|
|
163
|
-
? {
|
|
164
|
-
...state,
|
|
165
|
-
memory: (0, utils_1.deepMergeWithArrayOverwrite)((state.memory ?? {}), retryHookMemory),
|
|
166
|
-
}
|
|
167
|
-
: state;
|
|
168
|
-
const hookContext = {
|
|
169
|
-
node: ctx,
|
|
170
|
-
state: attemptState,
|
|
171
|
-
...(runtimeConfig.getProvider
|
|
172
|
-
? { getProvider: runtimeConfig.getProvider }
|
|
173
|
-
: {}),
|
|
174
|
-
...(runtimeConfig.memoryAdapter
|
|
175
|
-
? { memoryAdapter: runtimeConfig.memoryAdapter }
|
|
176
|
-
: {}),
|
|
177
|
-
};
|
|
178
|
-
const hookRun = await (0, hooks_1.runWithHookContext)(hookContext, async () => resolvedRun({
|
|
179
|
-
input: state.input,
|
|
180
|
-
params: (nodeParams ?? {}),
|
|
181
|
-
}));
|
|
182
|
-
nodeResult = hookRun.result;
|
|
183
|
-
hookMemoryUpdates = hookRun.memoryUpdates;
|
|
184
|
-
break;
|
|
185
|
-
}
|
|
186
|
-
catch (err) {
|
|
187
|
-
const patch = err?.__kortyxMemoryUpdates;
|
|
188
|
-
if (patch && typeof patch === "object") {
|
|
189
|
-
retryHookMemory = (0, utils_1.deepMergeWithArrayOverwrite)((retryHookMemory ?? {}), patch);
|
|
190
|
-
}
|
|
191
|
-
const hasMore = attempt < maxAttempts;
|
|
192
|
-
const delayMs = behavior.retry?.delayMs ?? 0;
|
|
193
|
-
if (hasMore && delayMs > 0) {
|
|
194
|
-
await new Promise((r) => setTimeout(r, delayMs));
|
|
195
|
-
continue;
|
|
196
|
-
}
|
|
197
|
-
if (!hasMore)
|
|
198
|
-
throw err;
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
const res = (nodeResult ?? {});
|
|
202
|
-
if (hookMemoryUpdates) {
|
|
203
|
-
res.infra = {
|
|
204
|
-
...(res.infra ?? {}),
|
|
205
|
-
memory: (0, utils_1.deepMergeWithArrayOverwrite)((res.infra?.memory ?? {}) || {}, hookMemoryUpdates),
|
|
206
|
-
};
|
|
207
|
-
}
|
|
208
|
-
const uiMessage = res.ui?.message;
|
|
209
|
-
const shouldRespond = typeof uiMessage === "string" && uiMessage.trim().length > 0;
|
|
210
|
-
if (shouldRespond) {
|
|
211
|
-
ctx.emit("message", { node: nodeId, content: uiMessage });
|
|
212
|
-
}
|
|
213
|
-
if (res.transitionTo) {
|
|
214
|
-
ctx.emit("transition", {
|
|
215
|
-
transitionTo: res.transitionTo,
|
|
216
|
-
payload: res.data ?? {},
|
|
217
|
-
});
|
|
218
|
-
}
|
|
219
|
-
const updates = {};
|
|
220
|
-
if (typeof res.condition === "string")
|
|
221
|
-
updates.lastCondition = res.condition;
|
|
222
|
-
if (typeof res.intent === "string")
|
|
223
|
-
updates.lastIntent = res.intent;
|
|
224
|
-
if (res.data && typeof res.data === "object") {
|
|
225
|
-
updates.input = (0, utils_1.deepMergeWithArrayOverwrite)(toRecordInput(state.input), res.data);
|
|
226
|
-
updates.data = (0, utils_1.deepMergeWithArrayOverwrite)((state.data ?? {}), res.data);
|
|
227
|
-
}
|
|
228
|
-
if (res.infra?.memory && typeof res.infra.memory === "object") {
|
|
229
|
-
updates.memory = (0, utils_1.deepMergeWithArrayOverwrite)((state.memory ?? {}), res.infra.memory);
|
|
230
|
-
}
|
|
231
|
-
if (res.ui) {
|
|
232
|
-
updates.ui = {
|
|
233
|
-
message: typeof res.ui.message === "string"
|
|
234
|
-
? res.ui.message
|
|
235
|
-
: (state.ui?.message ?? ""),
|
|
236
|
-
structured: (0, utils_1.deepMergeWithArrayOverwrite)((state.ui?.structured ?? {}), (res.ui.structured ?? {})),
|
|
237
|
-
};
|
|
238
|
-
}
|
|
239
|
-
if (shouldRespond && uiMessage) {
|
|
240
|
-
updates.conversationHistory = [
|
|
241
|
-
...(state.conversationHistory ?? []),
|
|
242
|
-
{
|
|
243
|
-
node: nodeId,
|
|
244
|
-
message: uiMessage,
|
|
245
|
-
timestamp: new Date().toISOString(),
|
|
246
|
-
},
|
|
247
|
-
];
|
|
248
|
-
}
|
|
249
|
-
if (res.ui?.structured) {
|
|
250
|
-
const payload = res.ui.structured;
|
|
251
|
-
const inferredType = payload && typeof payload === "object" && "jobs" in payload
|
|
252
|
-
? "jobs"
|
|
253
|
-
: typeof payload.summary === "string" ||
|
|
254
|
-
payload.topJobs
|
|
255
|
-
? "summary"
|
|
256
|
-
: "generic";
|
|
257
|
-
ctx.emit("structured_data", {
|
|
258
|
-
node: nodeId,
|
|
259
|
-
dataType: inferredType,
|
|
260
|
-
data: payload,
|
|
261
|
-
});
|
|
262
|
-
}
|
|
263
|
-
return updates;
|
|
264
|
-
});
|
|
265
|
-
}
|
|
266
|
-
const condGroups = {};
|
|
267
|
-
const plainEdges = [];
|
|
268
|
-
for (const edge of workflow.edges) {
|
|
269
|
-
const [from, to, condition] = edge;
|
|
270
|
-
if (condition && typeof condition.when === "string" && condition.when) {
|
|
271
|
-
if (!condGroups[from])
|
|
272
|
-
condGroups[from] = [];
|
|
273
|
-
condGroups[from].push({ when: condition.when, to });
|
|
274
|
-
}
|
|
275
|
-
else {
|
|
276
|
-
plainEdges.push([from, to]);
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
for (const [from, to] of plainEdges) {
|
|
280
|
-
edgeApi.addEdge(from, to);
|
|
281
|
-
}
|
|
282
|
-
for (const [from, rules] of Object.entries(condGroups)) {
|
|
283
|
-
const mapping = Object.fromEntries(rules.map((r) => [r.when, r.to]));
|
|
284
|
-
const fallbackKey = `__default__:${from}`;
|
|
285
|
-
mapping[fallbackKey] = "__end__";
|
|
286
|
-
edgeApi.addConditionalEdges(from, (s) => {
|
|
287
|
-
const trigger = s.lastCondition ?? s.lastIntent;
|
|
288
|
-
if (trigger && Object.hasOwn(mapping, trigger)) {
|
|
289
|
-
return trigger;
|
|
290
|
-
}
|
|
291
|
-
return fallbackKey;
|
|
292
|
-
}, mapping);
|
|
293
|
-
}
|
|
294
|
-
const sessionId = config?.session?.id ?? "__default__";
|
|
295
|
-
const checkpointer = config.checkpointer ?? (0, checkpointer_1.getCheckpointer)(sessionId);
|
|
296
|
-
const graph = builder.compile({ checkpointer });
|
|
297
|
-
const runtimeGraph = graph;
|
|
298
|
-
runtimeGraph.name = workflow.id;
|
|
299
|
-
runtimeGraph.config = config;
|
|
300
|
-
runtimeGraph.resume = async (state, input) => {
|
|
301
|
-
const resumed = { ...state, input, awaitingHumanInput: false };
|
|
302
|
-
return graph.invoke(resumed);
|
|
303
|
-
};
|
|
304
|
-
runtimeGraph.timeTravelTo = async (state, checkpointId) => {
|
|
305
|
-
const checkpoints = state.memory?.checkpoints;
|
|
306
|
-
const snapshot = checkpoints?.[checkpointId];
|
|
307
|
-
if (!snapshot)
|
|
308
|
-
throw new Error(`Checkpoint '${checkpointId}' not found`);
|
|
309
|
-
return graph.invoke(snapshot.snapshot);
|
|
310
|
-
};
|
|
311
|
-
return runtimeGraph;
|
|
312
|
-
}
|
package/dist/index.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,qBAAqB,CAAC;AACpC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,0BAA0B,CAAC;AACzC,cAAc,oBAAoB,CAAC;AACnC,cAAc,iBAAiB,CAAC;AAChC,cAAc,0BAA0B,CAAC;AACzC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,sBAAsB,CAAC;AACrC,cAAc,6BAA6B,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../../src/interrupt/tokens.ts"],"names":[],"mappings":"AAEA,wBAAgB,eAAe,IAAI,MAAM,CAExC;AAED,wBAAgB,aAAa,CAAC,MAAM,SAAQ,GAAG,MAAM,CAEpD"}
|
package/dist/interrupt/tokens.js
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.makeResumeToken = makeResumeToken;
|
|
4
|
-
exports.makeRequestId = makeRequestId;
|
|
5
|
-
const uuid_1 = require("uuid");
|
|
6
|
-
function makeResumeToken() {
|
|
7
|
-
return (0, uuid_1.v7)();
|
|
8
|
-
}
|
|
9
|
-
function makeRequestId(prefix = "req") {
|
|
10
|
-
return `${prefix}-${(0, uuid_1.v7)()}`;
|
|
11
|
-
}
|
package/dist/node-loader.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"node-loader.d.ts","sourceRoot":"","sources":["../src/node-loader.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAsD/D,wBAAsB,kBAAkB,CAAC,IAAI,EAAE;IAC7C,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,kBAAkB,CAAC;IAC9B,GAAG,CAAC,EAAE,MAAM,CAAC;CACd,GAAG,OAAO,CAAC,MAAM,CAAC,CA0BlB"}
|
package/dist/node-loader.js
DELETED
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.resolveNodeHandler = resolveNodeHandler;
|
|
4
|
-
const node_path_1 = require("node:path");
|
|
5
|
-
const node_url_1 = require("node:url");
|
|
6
|
-
const node_registry_1 = require("./node-registry");
|
|
7
|
-
const dynamicImport = new Function("specifier", "return import(specifier)");
|
|
8
|
-
function looksLikePath(specifier) {
|
|
9
|
-
if (specifier.startsWith("file:"))
|
|
10
|
-
return true;
|
|
11
|
-
if (specifier.startsWith("./") || specifier.startsWith("../"))
|
|
12
|
-
return true;
|
|
13
|
-
if (specifier.startsWith("/"))
|
|
14
|
-
return true;
|
|
15
|
-
return specifier.includes("/");
|
|
16
|
-
}
|
|
17
|
-
function getWorkflowBaseDir(workflow, cwd) {
|
|
18
|
-
const meta = workflow?.metadata;
|
|
19
|
-
const filePath = meta?.__filePath;
|
|
20
|
-
if (typeof filePath === "string" && filePath.length > 0) {
|
|
21
|
-
return (0, node_path_1.dirname)(filePath);
|
|
22
|
-
}
|
|
23
|
-
return cwd;
|
|
24
|
-
}
|
|
25
|
-
function pickNodeExport(mod, exportName) {
|
|
26
|
-
if (exportName) {
|
|
27
|
-
const candidate = mod[exportName];
|
|
28
|
-
if (typeof candidate === "function")
|
|
29
|
-
return candidate;
|
|
30
|
-
throw new Error(`Node export "${exportName}" not found or not a function.`);
|
|
31
|
-
}
|
|
32
|
-
const defaultExport = mod.default;
|
|
33
|
-
if (typeof defaultExport === "function")
|
|
34
|
-
return defaultExport;
|
|
35
|
-
const fnExports = Object.entries(mod).filter(([, v]) => typeof v === "function");
|
|
36
|
-
if (fnExports.length === 1)
|
|
37
|
-
return fnExports[0][1];
|
|
38
|
-
const names = fnExports.map(([k]) => k).sort();
|
|
39
|
-
throw new Error(`Node module must export a default function or exactly one named function (found: ${names.join(", ") || "none"}).`);
|
|
40
|
-
}
|
|
41
|
-
async function resolveNodeHandler(args) {
|
|
42
|
-
const { run, workflow } = args;
|
|
43
|
-
const cwd = args.cwd ?? process.cwd();
|
|
44
|
-
if (typeof run === "function")
|
|
45
|
-
return run;
|
|
46
|
-
if (looksLikePath(run)) {
|
|
47
|
-
const [moduleRef, exportName] = run.split("#", 2);
|
|
48
|
-
const baseDir = getWorkflowBaseDir(workflow, cwd);
|
|
49
|
-
const specifier = moduleRef.startsWith("file:")
|
|
50
|
-
? moduleRef
|
|
51
|
-
: (0, node_path_1.isAbsolute)(moduleRef)
|
|
52
|
-
? (0, node_url_1.pathToFileURL)(moduleRef).href
|
|
53
|
-
: (0, node_url_1.pathToFileURL)((0, node_path_1.resolve)(baseDir, moduleRef)).href;
|
|
54
|
-
const mod = await dynamicImport(specifier);
|
|
55
|
-
return pickNodeExport(mod, exportName);
|
|
56
|
-
}
|
|
57
|
-
return (0, node_registry_1.resolveNode)(run);
|
|
58
|
-
}
|
package/dist/node-registry.d.ts
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import type { NodeFn } from "@kortyx/core";
|
|
2
|
-
export declare function registerNode(id: string, fn: NodeFn): void;
|
|
3
|
-
export declare function getRegisteredNode(id: string): NodeFn | null;
|
|
4
|
-
export declare function listRegisteredNodes(): string[];
|
|
5
|
-
export declare function clearRegisteredNodes(): void;
|
|
6
|
-
export declare function resolveNode(id: string): NodeFn;
|
|
7
|
-
//# sourceMappingURL=node-registry.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"node-registry.d.ts","sourceRoot":"","sources":["../src/node-registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAI3C,wBAAgB,YAAY,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI,CAEzD;AAED,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAE3D;AAED,wBAAgB,mBAAmB,IAAI,MAAM,EAAE,CAE9C;AAED,wBAAgB,oBAAoB,IAAI,IAAI,CAE3C;AAED,wBAAgB,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAQ9C"}
|
package/dist/node-registry.js
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.registerNode = registerNode;
|
|
4
|
-
exports.getRegisteredNode = getRegisteredNode;
|
|
5
|
-
exports.listRegisteredNodes = listRegisteredNodes;
|
|
6
|
-
exports.clearRegisteredNodes = clearRegisteredNodes;
|
|
7
|
-
exports.resolveNode = resolveNode;
|
|
8
|
-
const registry = new Map();
|
|
9
|
-
function registerNode(id, fn) {
|
|
10
|
-
registry.set(id, fn);
|
|
11
|
-
}
|
|
12
|
-
function getRegisteredNode(id) {
|
|
13
|
-
return registry.get(id) ?? null;
|
|
14
|
-
}
|
|
15
|
-
function listRegisteredNodes() {
|
|
16
|
-
return [...registry.keys()].sort();
|
|
17
|
-
}
|
|
18
|
-
function clearRegisteredNodes() {
|
|
19
|
-
registry.clear();
|
|
20
|
-
}
|
|
21
|
-
function resolveNode(id) {
|
|
22
|
-
const fn = registry.get(id);
|
|
23
|
-
if (!fn) {
|
|
24
|
-
throw new Error(`Node '${id}' is not registered. Call registerNode('${id}', fn) before running workflows that reference it.`);
|
|
25
|
-
}
|
|
26
|
-
return fn;
|
|
27
|
-
}
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import type { WorkflowRegistry } from "./interface";
|
|
2
|
-
export interface FileWorkflowRegistryOptions {
|
|
3
|
-
workflowsDir: string;
|
|
4
|
-
fallbackId?: string;
|
|
5
|
-
cache?: boolean;
|
|
6
|
-
extensions?: string[];
|
|
7
|
-
}
|
|
8
|
-
export declare function createFileWorkflowRegistry(options: FileWorkflowRegistryOptions): WorkflowRegistry;
|
|
9
|
-
//# sourceMappingURL=file-registry.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"file-registry.d.ts","sourceRoot":"","sources":["../../src/registry/file-registry.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEpD,MAAM,WAAW,2BAA2B;IAC1C,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;CACvB;AA0BD,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,2BAA2B,GACnC,gBAAgB,CAgHlB"}
|
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.createFileWorkflowRegistry = createFileWorkflowRegistry;
|
|
4
|
-
const core_1 = require("@kortyx/core");
|
|
5
|
-
const promises_1 = require("fs/promises");
|
|
6
|
-
const path_1 = require("path");
|
|
7
|
-
const DEFAULT_EXTENSIONS = [
|
|
8
|
-
".workflow.ts",
|
|
9
|
-
".workflow.mts",
|
|
10
|
-
".workflow.js",
|
|
11
|
-
".workflow.mjs",
|
|
12
|
-
".workflow.json",
|
|
13
|
-
".workflow.yml",
|
|
14
|
-
".workflow.yaml",
|
|
15
|
-
];
|
|
16
|
-
function isWorkflowFile(name, extensions) {
|
|
17
|
-
return extensions.some((ext) => name.endsWith(ext));
|
|
18
|
-
}
|
|
19
|
-
function isDeclarativeWorkflowFile(name) {
|
|
20
|
-
return (name.endsWith(".workflow.json") ||
|
|
21
|
-
name.endsWith(".workflow.yml") ||
|
|
22
|
-
name.endsWith(".workflow.yaml"));
|
|
23
|
-
}
|
|
24
|
-
function createFileWorkflowRegistry(options) {
|
|
25
|
-
const workflowsDir = options.workflowsDir;
|
|
26
|
-
const fallbackId = options.fallbackId ?? "general-chat";
|
|
27
|
-
const extensions = options.extensions ?? DEFAULT_EXTENSIONS;
|
|
28
|
-
const shouldCache = options.cache ?? true;
|
|
29
|
-
let workflowCache = null;
|
|
30
|
-
async function loadWorkflows() {
|
|
31
|
-
if (shouldCache && workflowCache)
|
|
32
|
-
return workflowCache;
|
|
33
|
-
const files = await (0, promises_1.readdir)(workflowsDir, { withFileTypes: true });
|
|
34
|
-
const workflowFiles = files
|
|
35
|
-
.filter((entry) => entry.isFile())
|
|
36
|
-
.map((entry) => entry.name)
|
|
37
|
-
.filter((file) => isWorkflowFile(file, extensions));
|
|
38
|
-
const workflows = {};
|
|
39
|
-
for (const file of workflowFiles) {
|
|
40
|
-
try {
|
|
41
|
-
const modulePath = (0, path_1.join)(workflowsDir, file);
|
|
42
|
-
if (isDeclarativeWorkflowFile(file)) {
|
|
43
|
-
const raw = await (0, promises_1.readFile)(modulePath, "utf8");
|
|
44
|
-
const workflow = (0, core_1.loadWorkflow)(raw);
|
|
45
|
-
workflow.metadata = {
|
|
46
|
-
...(workflow.metadata ?? {}),
|
|
47
|
-
__filePath: modulePath,
|
|
48
|
-
};
|
|
49
|
-
workflows[workflow.id] = workflow;
|
|
50
|
-
continue;
|
|
51
|
-
}
|
|
52
|
-
const module = await import(modulePath);
|
|
53
|
-
const workflowExport = Object.values(module).find((exp) => {
|
|
54
|
-
const obj = exp;
|
|
55
|
-
return (exp !== null &&
|
|
56
|
-
typeof exp === "object" &&
|
|
57
|
-
typeof obj.id === "string" &&
|
|
58
|
-
typeof obj.nodes === "object" &&
|
|
59
|
-
obj.nodes !== null &&
|
|
60
|
-
Array.isArray(obj.edges));
|
|
61
|
-
});
|
|
62
|
-
if (workflowExport) {
|
|
63
|
-
workflowExport.metadata = {
|
|
64
|
-
...(workflowExport.metadata ?? {}),
|
|
65
|
-
__filePath: modulePath,
|
|
66
|
-
};
|
|
67
|
-
workflows[workflowExport.id] = workflowExport;
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
catch (error) {
|
|
71
|
-
console.warn(`Failed to load workflow from ${file}:`, error);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
if (shouldCache)
|
|
75
|
-
workflowCache = workflows;
|
|
76
|
-
return workflows;
|
|
77
|
-
}
|
|
78
|
-
async function list() {
|
|
79
|
-
const workflows = await loadWorkflows();
|
|
80
|
-
return Object.values(workflows);
|
|
81
|
-
}
|
|
82
|
-
async function get(id) {
|
|
83
|
-
const workflows = await loadWorkflows();
|
|
84
|
-
return workflows[id] ?? null;
|
|
85
|
-
}
|
|
86
|
-
async function select(id, selectOptions) {
|
|
87
|
-
const workflows = await loadWorkflows();
|
|
88
|
-
const fallback = selectOptions?.fallbackId ?? fallbackId;
|
|
89
|
-
const selected = workflows[id] ?? (fallback ? workflows[fallback] : null);
|
|
90
|
-
if (!selected) {
|
|
91
|
-
throw new Error(`No workflow found for id "${id}" and no fallback "${fallback}" workflow available`);
|
|
92
|
-
}
|
|
93
|
-
return selected;
|
|
94
|
-
}
|
|
95
|
-
async function refresh() {
|
|
96
|
-
workflowCache = null;
|
|
97
|
-
await loadWorkflows();
|
|
98
|
-
}
|
|
99
|
-
return {
|
|
100
|
-
list,
|
|
101
|
-
get,
|
|
102
|
-
select,
|
|
103
|
-
refresh,
|
|
104
|
-
};
|
|
105
|
-
}
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import type { WorkflowDefinition } from "@kortyx/core";
|
|
2
|
-
import type { WorkflowRegistry } from "./interface";
|
|
3
|
-
export declare function createInMemoryWorkflowRegistry(workflows: WorkflowDefinition[], options?: {
|
|
4
|
-
fallbackId?: string;
|
|
5
|
-
}): WorkflowRegistry;
|
|
6
|
-
//# sourceMappingURL=in-memory-registry.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"in-memory-registry.d.ts","sourceRoot":"","sources":["../../src/registry/in-memory-registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AACvD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEpD,wBAAgB,8BAA8B,CAC5C,SAAS,EAAE,kBAAkB,EAAE,EAC/B,OAAO,CAAC,EAAE;IAAE,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,GAChC,gBAAgB,CAwBlB"}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.createInMemoryWorkflowRegistry = createInMemoryWorkflowRegistry;
|
|
4
|
-
function createInMemoryWorkflowRegistry(workflows, options) {
|
|
5
|
-
const byId = new Map();
|
|
6
|
-
for (const wf of workflows)
|
|
7
|
-
byId.set(wf.id, wf);
|
|
8
|
-
const fallbackId = options?.fallbackId ?? "general-chat";
|
|
9
|
-
return {
|
|
10
|
-
async list() {
|
|
11
|
-
return Array.from(byId.values());
|
|
12
|
-
},
|
|
13
|
-
async get(id) {
|
|
14
|
-
return byId.get(id) ?? null;
|
|
15
|
-
},
|
|
16
|
-
async select(id, selectOptions) {
|
|
17
|
-
const fallback = selectOptions?.fallbackId ?? fallbackId;
|
|
18
|
-
const wf = byId.get(id) ?? (fallback ? byId.get(fallback) : undefined);
|
|
19
|
-
if (!wf) {
|
|
20
|
-
throw new Error(`Workflow "${id}" not found${fallback ? ` (fallback "${fallback}" missing too)` : ""}.`);
|
|
21
|
-
}
|
|
22
|
-
return wf;
|
|
23
|
-
},
|
|
24
|
-
};
|
|
25
|
-
}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import type { WorkflowDefinition } from "@kortyx/core";
|
|
2
|
-
type AnyWorkflow = WorkflowDefinition;
|
|
3
|
-
export interface WorkflowRegistry {
|
|
4
|
-
list: () => Promise<AnyWorkflow[]>;
|
|
5
|
-
get: (id: string) => Promise<AnyWorkflow | null>;
|
|
6
|
-
select: (id: string, options?: {
|
|
7
|
-
fallbackId?: string;
|
|
8
|
-
}) => Promise<AnyWorkflow>;
|
|
9
|
-
refresh?: () => Promise<void>;
|
|
10
|
-
}
|
|
11
|
-
export {};
|
|
12
|
-
//# sourceMappingURL=interface.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"interface.d.ts","sourceRoot":"","sources":["../../src/registry/interface.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAEvD,KAAK,WAAW,GAAG,kBAAkB,CAAC;AAEtC,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC;IACnC,GAAG,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IACjD,MAAM,EAAE,CACN,EAAE,EAAE,MAAM,EACV,OAAO,CAAC,EAAE;QAAE,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,KAC9B,OAAO,CAAC,WAAW,CAAC,CAAC;IAC1B,OAAO,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/B"}
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import type { GraphState, MemoryEnvelope } from "@kortyx/core";
|
|
2
|
-
export interface InitialStateArgs<Config = unknown> {
|
|
3
|
-
input: unknown;
|
|
4
|
-
memory: MemoryEnvelope;
|
|
5
|
-
config: Config;
|
|
6
|
-
defaultWorkflowId?: string;
|
|
7
|
-
}
|
|
8
|
-
export declare function buildInitialGraphState<Config>({ input, memory, config, defaultWorkflowId, }: InitialStateArgs<Config>): Promise<GraphState>;
|
|
9
|
-
//# sourceMappingURL=build-initial-state.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"build-initial-state.d.ts","sourceRoot":"","sources":["../../src/state/build-initial-state.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAM/D,MAAM,WAAW,gBAAgB,CAAC,MAAM,GAAG,OAAO;IAChD,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,cAAc,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,wBAAsB,sBAAsB,CAAC,MAAM,EAAE,EACnD,KAAK,EACL,MAAM,EACN,MAAM,EACN,iBAAiB,GAClB,EAAE,gBAAgB,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,UAAU,CAAC,CAkBhD"}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.buildInitialGraphState = buildInitialGraphState;
|
|
4
|
-
async function buildInitialGraphState({ input, memory, config, defaultWorkflowId, }) {
|
|
5
|
-
const currentWorkflow = memory.currentWorkflow || defaultWorkflowId;
|
|
6
|
-
if (!currentWorkflow) {
|
|
7
|
-
throw new Error("No workflow selected. Provide defaultWorkflowId (or set memory.currentWorkflow) before starting the graph.");
|
|
8
|
-
}
|
|
9
|
-
return {
|
|
10
|
-
input,
|
|
11
|
-
lastNode: "__start__",
|
|
12
|
-
memory,
|
|
13
|
-
config: config,
|
|
14
|
-
conversationHistory: [],
|
|
15
|
-
currentWorkflow,
|
|
16
|
-
awaitingHumanInput: false,
|
|
17
|
-
};
|
|
18
|
-
}
|