llmjs2 1.3.9 → 1.6.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.
Files changed (49) hide show
  1. package/README.md +31 -476
  2. package/chain/AGENT_STEP_README.md +102 -0
  3. package/chain/README.md +257 -0
  4. package/chain/WORKFLOW_README.md +85 -0
  5. package/chain/agent-step-example.js +232 -0
  6. package/chain/docs/AGENT.md +126 -0
  7. package/chain/docs/GRAPH.md +490 -0
  8. package/chain/examples.js +314 -0
  9. package/chain/index.js +31 -0
  10. package/chain/lib/agent.js +338 -0
  11. package/chain/lib/flow/agent-step.js +119 -0
  12. package/chain/lib/flow/edge.js +24 -0
  13. package/chain/lib/flow/flow.js +76 -0
  14. package/chain/lib/flow/graph.js +331 -0
  15. package/chain/lib/flow/index.js +7 -0
  16. package/chain/lib/flow/step.js +63 -0
  17. package/chain/lib/memory/in-memory.js +117 -0
  18. package/chain/lib/memory/index.js +36 -0
  19. package/chain/lib/memory/lance-memory.js +225 -0
  20. package/chain/lib/memory/sqlite-memory.js +309 -0
  21. package/chain/simple-agent-step-example.js +168 -0
  22. package/chain/workflow-example-usage.js +70 -0
  23. package/chain/workflow-example.json +59 -0
  24. package/core/README.md +485 -0
  25. package/core/cli.js +275 -0
  26. package/core/docs/BASIC_USAGE.md +62 -0
  27. package/core/docs/CLI.md +104 -0
  28. package/{docs → core/docs}/GET_STARTED.md +129 -129
  29. package/{docs → core/docs}/GUARDRAILS_GUIDE.md +734 -734
  30. package/{docs → core/docs}/README.md +47 -47
  31. package/core/docs/ROUTER_GUIDE.md +199 -0
  32. package/{docs → core/docs}/SERVER_MODE.md +358 -350
  33. package/core/index.js +115 -0
  34. package/{providers → core/providers}/ollama.js +14 -6
  35. package/{providers → core/providers}/openai.js +14 -6
  36. package/{providers → core/providers}/openrouter.js +14 -6
  37. package/core/router.js +252 -0
  38. package/{server.js → core/server.js} +15 -5
  39. package/package.json +43 -27
  40. package/cli.js +0 -195
  41. package/docs/BASIC_USAGE.md +0 -296
  42. package/docs/CLI.md +0 -455
  43. package/docs/ROUTER_GUIDE.md +0 -402
  44. package/index.js +0 -267
  45. package/router.js +0 -273
  46. package/test-completion.js +0 -99
  47. package/test.js +0 -246
  48. /package/{config.yaml → core/config.yaml} +0 -0
  49. /package/{logger.js → core/logger.js} +0 -0
@@ -0,0 +1,119 @@
1
+ const { Step } = require('./step');
2
+ const { Agent } = require('../agent');
3
+
4
+ /**
5
+ * AgentStep - A specialized step that wraps an AI agent for use in graph workflows
6
+ * Allows seamless integration of AI agents into workflow execution
7
+ */
8
+ class AgentStep extends Step {
9
+ /**
10
+ * Create a new AgentStep
11
+ * @param {Object} options - Step configuration
12
+ * @param {string} options.name - Step name
13
+ * @param {string} options.instruction - System instruction for the agent
14
+ * @param {Array} options.tools - Array of tool definitions
15
+ * @param {string} options.model - Model name (optional)
16
+ * @param {string} options.apikey - API key (optional)
17
+ * @param {Object} options.memory - Memory instance (optional)
18
+ * @param {string} options.userPrompt - Template string to map workflow context to agent input
19
+ */
20
+ constructor(options = {}) {
21
+ super({
22
+ name: options.name,
23
+ execute: options.execute || (async (context) => {
24
+ return this._defaultExecute(context);
25
+ })
26
+ });
27
+
28
+ this.agent = new Agent({
29
+ model: options.model,
30
+ instruction: options.instruction,
31
+ apikey: options.apikey,
32
+ tools: options.tools || [],
33
+ memory: options.memory
34
+ });
35
+ this.userPrompt = options.userPrompt || '';
36
+ }
37
+
38
+ /**
39
+ * Default execute method for AgentStep
40
+ * @param {Object} context - Workflow context
41
+ * @returns {Promise<Object>} Agent response mapped to workflow context
42
+ */
43
+ async _defaultExecute(context) {
44
+ if (!this.agent) {
45
+ throw new Error(`AgentStep ${this.name}: No agent provided`);
46
+ }
47
+
48
+ // Map workflow context to agent input
49
+ let agentInput;
50
+ if (this.userPrompt) {
51
+ // userPrompt is a template string with {{variable}} placeholders
52
+ agentInput = this._interpolateTemplate(this.userPrompt, context);
53
+ } else {
54
+ // Default: pass the entire context as a formatted string
55
+ agentInput = this._contextToString(context);
56
+ }
57
+
58
+ console.log(`[${this.name}] Agent input:`, agentInput);
59
+
60
+ // Call the agent
61
+ const agentResponse = await this.agent.generate(agentInput);
62
+
63
+ console.log(`[${this.name}] Agent response:`, agentResponse);
64
+
65
+ return agentResponse;
66
+ }
67
+
68
+ /**
69
+ * Convert workflow context to a readable string for agent input
70
+ * @param {Object} context - Workflow context
71
+ * @returns {string} Formatted context string
72
+ */
73
+ _contextToString(context) {
74
+ const relevantKeys = Object.keys(context).filter(key =>
75
+ !key.startsWith('_') && key !== 'executedSteps' && key !== 'results'
76
+ );
77
+
78
+ if (relevantKeys.length === 0) {
79
+ return "Please process the available data.";
80
+ }
81
+
82
+ let result = "Based on the following context:\n";
83
+ for (const key of relevantKeys) {
84
+ const value = context[key];
85
+ if (typeof value === 'object' && value !== null) {
86
+ result += `${key}: ${JSON.stringify(value, null, 2)}\n`;
87
+ } else {
88
+ result += `${key}: ${value}\n`;
89
+ }
90
+ }
91
+ result += "\nPlease provide your analysis or response.";
92
+ return result;
93
+ }
94
+
95
+ /**
96
+ * Simple template interpolation
97
+ * @param {string} template - Template string with {{variable}} placeholders
98
+ * @param {Object} context - Context object for interpolation
99
+ * @returns {string} Interpolated string
100
+ */
101
+ _interpolateTemplate(template, context) {
102
+ return template.replace(/\{\{(\w+)\}\}/g, (match, key) => {
103
+ const value = context[key];
104
+ return value !== undefined ? String(value) : match;
105
+ });
106
+ }
107
+
108
+ /**
109
+ * Get the agent instance
110
+ * @returns {Agent} The wrapped agent
111
+ */
112
+ getAgent() {
113
+ return this.agent;
114
+ }
115
+
116
+
117
+ }
118
+
119
+ module.exports = { AgentStep };
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Edge - Represents a connection between nodes in a graph
3
+ */
4
+ class Edge {
5
+ constructor(from, to, condition = null) {
6
+ this.from = from;
7
+ this.to = to;
8
+ this.condition = condition; // Optional condition function
9
+ }
10
+
11
+ /**
12
+ * Check if this edge should be followed based on result and context
13
+ */
14
+ shouldFollow(result, context) {
15
+ // Handle both direct function and {when: function} format
16
+ const conditionFn = typeof this.condition === 'function'
17
+ ? this.condition
18
+ : this.condition?.when;
19
+
20
+ return !conditionFn || conditionFn(result, context);
21
+ }
22
+ }
23
+
24
+ module.exports = { Edge };
@@ -0,0 +1,76 @@
1
+ const { Step } = require('./step');
2
+
3
+ /**
4
+ * Flow - Linear sequence of steps
5
+ */
6
+ class Flow {
7
+ constructor(name, steps = []) {
8
+ this.name = name;
9
+ this.steps = steps;
10
+ this.context = {
11
+ executedSteps: new Set(),
12
+ results: new Map(),
13
+ variables: new Map()
14
+ };
15
+ }
16
+
17
+ /**
18
+ * Add a step to the flow
19
+ */
20
+ addStep(step) {
21
+ this.steps.push(step);
22
+ }
23
+
24
+ /**
25
+ * Execute the flow sequentially
26
+ */
27
+ async execute(initialInputs = {}) {
28
+ console.log(`[${this.name}] Starting flow execution`);
29
+
30
+ this.context.variables.set('initial', initialInputs);
31
+
32
+ for (const step of this.steps) {
33
+ if (!step.canExecute(this.context)) {
34
+ throw new Error(`Cannot execute step ${step.name}: dependencies not satisfied`);
35
+ }
36
+
37
+ const inputs = this.prepareInputs(step, initialInputs);
38
+ const result = await step.execute(this.context, inputs);
39
+
40
+ this.context.executedSteps.add(step.name);
41
+ this.context.results.set(step.name, result);
42
+
43
+ // Store result in variables for use by subsequent steps
44
+ this.context.variables.set(step.name, result);
45
+ }
46
+
47
+ console.log(`[${this.name}] Flow execution completed`);
48
+ return this.getFinalResult();
49
+ }
50
+
51
+ /**
52
+ * Prepare inputs for a step based on context
53
+ */
54
+ prepareInputs(step, initialInputs) {
55
+ const inputs = { ...initialInputs };
56
+
57
+ // Include results from previous steps if needed
58
+ for (const [key, value] of this.context.variables) {
59
+ if (key !== 'initial') {
60
+ inputs[key] = value;
61
+ }
62
+ }
63
+
64
+ return inputs;
65
+ }
66
+
67
+ /**
68
+ * Get the final result of the flow
69
+ */
70
+ getFinalResult() {
71
+ if (this.steps.length === 0) return null;
72
+ return this.context.results.get(this.steps[this.steps.length - 1].name);
73
+ }
74
+ }
75
+
76
+ module.exports = { Flow };
@@ -0,0 +1,331 @@
1
+ const fs = require('fs');
2
+ const { Step } = require('./step');
3
+ const { Edge } = require('./edge');
4
+ const { AgentStep } = require('./agent-step');
5
+ const { Agent } = require('../agent');
6
+
7
+ /**
8
+ * Graph - Graph-flow execution system
9
+ */
10
+ class Graph {
11
+ constructor(options = {}) {
12
+ const normalizedOptions = typeof options === 'string' ? { name: options } : (options || {});
13
+
14
+ this.name = normalizedOptions.name || 'graph';
15
+ this.nodes = new Map(); // step name -> step
16
+ this.edges = new Map(); // from -> [Edge]
17
+ this.startNodes = new Set();
18
+ this.compiled = false;
19
+ }
20
+
21
+ /**
22
+ * Load a graph from JSON configuration file
23
+ * @param {string} path - Path to JSON configuration file
24
+ * @returns {Graph} Configured Graph instance
25
+ *
26
+ * JSON format:
27
+ * {
28
+ * "name": "graph-name",
29
+ * "steps": [
30
+ * {
31
+ * "name": "step-name",
32
+ * "execute": "async (context) => { ... }"
33
+ * },
34
+ * {
35
+ * "name": "agent-step-name",
36
+ * "type": "agent",
37
+ * "instruction": "You are a helpful assistant",
38
+ * "tools": [...],
39
+ * "model": "gpt-4",
40
+ * "userPrompt": "template string with {{variable}} placeholders"
41
+ * }
42
+ * ],
43
+ * "edges": [...]
44
+ * }
45
+ */
46
+ static load(path) {
47
+ const config = JSON.parse(fs.readFileSync(path, 'utf8'));
48
+ const { name, steps = [], edges = [] } = config;
49
+ const graph = new Graph({ name });
50
+
51
+ // Load steps
52
+ for (const stepConfig of steps) {
53
+ const { name: stepName, type, execute, instruction, tools, model, apikey, memory, userPrompt } = stepConfig;
54
+
55
+ let step;
56
+
57
+ if (type === 'agent') {
58
+ // Create agent step
59
+ if (!instruction) {
60
+ throw new Error(`Instruction required for agent step ${stepName}`);
61
+ }
62
+
63
+ step = new AgentStep({
64
+ name: stepName,
65
+ instruction,
66
+ tools,
67
+ model,
68
+ apikey,
69
+ memory,
70
+ userPrompt
71
+ });
72
+ } else {
73
+ // Create regular step
74
+ // Handle execute function - can be string or function
75
+ let executeFn;
76
+ if (typeof execute === 'string') {
77
+ // If execute is a string, create a function from it
78
+ try {
79
+ executeFn = new Function('context', `return (${execute})(context);`);
80
+ } catch (error) {
81
+ throw new Error(`Invalid execute function for step ${stepName}: ${error.message}`);
82
+ }
83
+ } else if (typeof execute === 'function') {
84
+ executeFn = execute;
85
+ } else {
86
+ throw new Error(`Execute must be a function or string for step ${stepName}`);
87
+ }
88
+
89
+ step = new Step({
90
+ name: stepName,
91
+ execute: executeFn
92
+ });
93
+ }
94
+
95
+ graph.step(step);
96
+ }
97
+
98
+ // Load edges
99
+ for (const edgeConfig of edges) {
100
+ const { from, to, condition } = edgeConfig;
101
+
102
+ let conditionFn = null;
103
+ if (condition) {
104
+ if (typeof condition === 'string') {
105
+ try {
106
+ conditionFn = new Function('result', 'context', `return (${condition})(result, context);`);
107
+ } catch (error) {
108
+ throw new Error(`Invalid condition function for edge ${from}->${to}: ${error.message}`);
109
+ }
110
+ } else if (typeof condition === 'function') {
111
+ conditionFn = condition;
112
+ }
113
+ }
114
+
115
+ graph.edge(from, to, conditionFn ? { when: conditionFn } : {});
116
+ }
117
+
118
+ return graph;
119
+ }
120
+
121
+ /**
122
+ * Add steps to the graph (fluent API)
123
+ * Can add single step or multiple steps
124
+ */
125
+ step(...steps) {
126
+ for (const step of steps) {
127
+ if (step instanceof Step) {
128
+ this.nodes.set(step.name, step);
129
+ if (!this.edges.has(step.name)) {
130
+ this.edges.set(step.name, []);
131
+ }
132
+ // Mark as potential start node if no incoming edges
133
+ this.startNodes.add(step.name);
134
+ }
135
+ }
136
+ return this;
137
+ }
138
+
139
+ /**
140
+ * Backward-compatible alias for adding a single node.
141
+ */
142
+ addNode(step) {
143
+ this.step(step);
144
+ return this;
145
+ }
146
+
147
+ /**
148
+ * Add an edge between steps (fluent API)
149
+ * Supports conditional edges with {when: condition}
150
+ * Can connect to single step or array of steps for parallel execution
151
+ */
152
+ edge(from, to, options = {}) {
153
+ if (typeof from === 'string') {
154
+ from = this.nodes.get(from);
155
+ }
156
+
157
+ if (!from) {
158
+ throw new Error('Source step must exist in the graph');
159
+ }
160
+
161
+ if (!this.edges.has(from.name)) {
162
+ this.edges.set(from.name, []);
163
+ }
164
+
165
+ // Handle both single step and array of steps
166
+ const targets = Array.isArray(to) ? to : [to];
167
+ const condition = options.when || null;
168
+
169
+ for (const target of targets) {
170
+ let targetStep = target;
171
+ if (typeof target === 'string') {
172
+ targetStep = this.nodes.get(target);
173
+ }
174
+
175
+ if (!targetStep) {
176
+ throw new Error(`Target step ${target} must exist in the graph`);
177
+ }
178
+
179
+ const edge = new Edge(from.name, targetStep.name, condition);
180
+ this.edges.get(from.name).push(edge);
181
+
182
+ // Remove target from start nodes since it has incoming edges
183
+ this.startNodes.delete(targetStep.name);
184
+ }
185
+
186
+ return this;
187
+ }
188
+
189
+ /**
190
+ * Backward-compatible alias for adding edges with an optional condition function.
191
+ */
192
+ addEdge(from, to, conditionOrOptions) {
193
+ if (typeof conditionOrOptions === 'function') {
194
+ return this.edge(from, to, { when: conditionOrOptions });
195
+ }
196
+
197
+ return this.edge(from, to, conditionOrOptions || {});
198
+ }
199
+
200
+ /**
201
+ * Compile the graph (fluent API)
202
+ */
203
+ compile() {
204
+ this.compiled = true;
205
+ console.log(`[${this.name}] Graph compiled with ${this.nodes.size} steps`);
206
+ return this;
207
+ }
208
+
209
+ /**
210
+ * Run the graph with initial context
211
+ */
212
+ async run(initialContext = {}) {
213
+ if (!this.compiled) {
214
+ throw new Error('Graph must be compiled before running. Call .compile() first.');
215
+ }
216
+
217
+ console.log(`[${this.name}] Starting graph execution`);
218
+
219
+ const context = {
220
+ ...initialContext,
221
+ executedSteps: new Set(),
222
+ results: new Map(),
223
+ pendingNodes: new Set(this.startNodes)
224
+ };
225
+
226
+ const maxIterations = 1000; // Prevent infinite loops
227
+ let iterations = 0;
228
+
229
+ while (context.pendingNodes.size > 0 && iterations < maxIterations) {
230
+ iterations++;
231
+ const currentNodeName = Array.from(context.pendingNodes)[0];
232
+ context.pendingNodes.delete(currentNodeName);
233
+
234
+ if (context.executedSteps.has(currentNodeName)) {
235
+ continue; // Already executed
236
+ }
237
+
238
+ const step = this.nodes.get(currentNodeName);
239
+ if (!step) {
240
+ throw new Error(`Step ${currentNodeName} not found in graph`);
241
+ }
242
+
243
+ // Check if all dependencies are satisfied
244
+ const dependencies = this.getDependencies(currentNodeName);
245
+ const depsSatisfied = dependencies.every(dep => context.executedSteps.has(dep));
246
+
247
+ if (!depsSatisfied) {
248
+ // Put back in pending and try later
249
+ context.pendingNodes.add(currentNodeName);
250
+ continue;
251
+ }
252
+
253
+ // Execute the step
254
+ console.log(`[${this.name}] Executing step: ${currentNodeName}`);
255
+ const result = await step.run(context, context);
256
+
257
+ // Store result in context
258
+ context[currentNodeName] = result;
259
+ context.results.set(currentNodeName, result);
260
+ context.executedSteps.add(currentNodeName);
261
+
262
+ // Determine next nodes to execute
263
+ const outgoingEdges = this.edges.get(currentNodeName) || [];
264
+ for (const edge of outgoingEdges) {
265
+ if (edge.shouldFollow(result, context)) {
266
+ context.pendingNodes.add(edge.to);
267
+ }
268
+ }
269
+ }
270
+
271
+ if (iterations >= maxIterations) {
272
+ throw new Error('Graph execution exceeded maximum iterations (possible infinite loop)');
273
+ }
274
+
275
+ console.log(`[${this.name}] Graph execution completed`);
276
+ return this.getFinalResults(context);
277
+ }
278
+
279
+ /**
280
+ * Backward-compatible execute entrypoint.
281
+ * Supports execute(startNodes, initialContext) and execute(initialContext).
282
+ */
283
+ async execute(startNodesOrContext = [], maybeInitialContext = {}) {
284
+ let startNodes = [];
285
+ let initialContext = maybeInitialContext;
286
+
287
+ if (Array.isArray(startNodesOrContext)) {
288
+ startNodes = startNodesOrContext;
289
+ } else {
290
+ initialContext = startNodesOrContext || {};
291
+ }
292
+
293
+ if (startNodes.length > 0) {
294
+ this.startNodes = new Set(startNodes);
295
+ }
296
+
297
+ if (!this.compiled) {
298
+ this.compile();
299
+ }
300
+
301
+ return this.run(initialContext);
302
+ }
303
+
304
+ /**
305
+ * Get dependencies for a node
306
+ */
307
+ getDependencies(nodeName) {
308
+ const dependencies = [];
309
+ for (const [fromNode, edges] of this.edges) {
310
+ for (const edge of edges) {
311
+ if (edge.to === nodeName) {
312
+ dependencies.push(fromNode);
313
+ }
314
+ }
315
+ }
316
+ return dependencies;
317
+ }
318
+
319
+ /**
320
+ * Get final results from graph execution
321
+ */
322
+ getFinalResults(context) {
323
+ const results = {};
324
+ for (const [nodeName, result] of context.results) {
325
+ results[nodeName] = result;
326
+ }
327
+ return results;
328
+ }
329
+ }
330
+
331
+ module.exports = { Graph };
@@ -0,0 +1,7 @@
1
+ const { Step } = require('./step');
2
+ const { Flow } = require('./flow');
3
+ const { Graph } = require('./graph');
4
+ const { Edge } = require('./edge');
5
+ const { AgentStep } = require('./agent-step');
6
+
7
+ module.exports = { Step, Flow, Graph, Edge, AgentStep };
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Step - Basic unit of work in a graph-flow system
3
+ * A step executes a function with the current context
4
+ */
5
+ class Step {
6
+ constructor(nameOrOptions = {}, config = {}) {
7
+ const options = typeof nameOrOptions === 'string'
8
+ ? { ...config, name: nameOrOptions }
9
+ : (nameOrOptions || {});
10
+
11
+ this.name = options.name;
12
+ this.dependsOn = Array.isArray(options.dependsOn) ? options.dependsOn : [];
13
+
14
+ const executeFn = typeof options.execute === 'function'
15
+ ? options.execute
16
+ : (typeof options.processor === 'function'
17
+ ? options.processor
18
+ : async () => ({}));
19
+
20
+ this._execute = executeFn;
21
+ }
22
+
23
+ /**
24
+ * Check whether this step can execute with current flow context.
25
+ * If no dependencies are configured, the step can always execute.
26
+ */
27
+ canExecute(flowContext = {}) {
28
+ if (this.dependsOn.length === 0) {
29
+ return true;
30
+ }
31
+
32
+ const executedSteps = flowContext.executedSteps;
33
+ if (!(executedSteps instanceof Set)) {
34
+ return false;
35
+ }
36
+
37
+ return this.dependsOn.every((dependency) => executedSteps.has(dependency));
38
+ }
39
+
40
+ /**
41
+ * Execute this step with the given context and inputs
42
+ */
43
+ async execute(context = {}, inputs = {}) {
44
+ return this._execute(context, inputs);
45
+ }
46
+
47
+ /**
48
+ * Run with logging and error handling
49
+ */
50
+ async run(context = {}, inputs = context) {
51
+ try {
52
+ console.log(`[${this.name}] Executing step`);
53
+ const result = await this.execute(context, inputs);
54
+ console.log(`[${this.name}] Step completed`);
55
+ return result;
56
+ } catch (error) {
57
+ console.error(`[${this.name}] Step failed:`, error.message);
58
+ throw error;
59
+ }
60
+ }
61
+ }
62
+
63
+ module.exports = { Step };