llmjs2 1.7.1 → 2.0.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 (45) hide show
  1. package/README.md +116 -18
  2. package/dist/index.d.mts +146 -0
  3. package/dist/index.d.ts +146 -0
  4. package/dist/index.js +1242 -0
  5. package/dist/index.mjs +1211 -0
  6. package/package.json +32 -58
  7. package/chain/AGENT_STEP_README.md +0 -102
  8. package/chain/README.md +0 -257
  9. package/chain/WORKFLOW_README.md +0 -85
  10. package/chain/agent-step-example.js +0 -232
  11. package/chain/docs/AGENT.md +0 -126
  12. package/chain/docs/GRAPH.md +0 -490
  13. package/chain/examples.js +0 -314
  14. package/chain/index.js +0 -31
  15. package/chain/lib/agent.js +0 -338
  16. package/chain/lib/flow/agent-step.js +0 -119
  17. package/chain/lib/flow/edge.js +0 -24
  18. package/chain/lib/flow/flow.js +0 -76
  19. package/chain/lib/flow/graph.js +0 -331
  20. package/chain/lib/flow/index.js +0 -7
  21. package/chain/lib/flow/step.js +0 -63
  22. package/chain/lib/memory/in-memory.js +0 -117
  23. package/chain/lib/memory/index.js +0 -36
  24. package/chain/lib/memory/lance-memory.js +0 -225
  25. package/chain/lib/memory/sqlite-memory.js +0 -309
  26. package/chain/simple-agent-step-example.js +0 -168
  27. package/chain/workflow-example-usage.js +0 -70
  28. package/chain/workflow-example.json +0 -59
  29. package/core/README.md +0 -485
  30. package/core/cli.js +0 -275
  31. package/core/config.yaml +0 -149
  32. package/core/docs/BASIC_USAGE.md +0 -62
  33. package/core/docs/CLI.md +0 -104
  34. package/core/docs/GET_STARTED.md +0 -129
  35. package/core/docs/GUARDRAILS_GUIDE.md +0 -734
  36. package/core/docs/README.md +0 -47
  37. package/core/docs/ROUTER_GUIDE.md +0 -199
  38. package/core/docs/SERVER_MODE.md +0 -358
  39. package/core/index.js +0 -115
  40. package/core/logger.js +0 -115
  41. package/core/providers/ollama.js +0 -128
  42. package/core/providers/openai.js +0 -112
  43. package/core/providers/openrouter.js +0 -206
  44. package/core/router.js +0 -252
  45. package/core/server.js +0 -203
package/core/logger.js DELETED
@@ -1,115 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * Simple logging utility for llmjs2
5
- * Provides structured logging with timestamps and levels
6
- */
7
-
8
- class Logger {
9
- constructor() {
10
- this.levels = {
11
- DEBUG: 0, // Shows all logs
12
- INFO: 1, // Shows INFO and ERROR only
13
- WARN: 2, // Shows WARN, INFO, and ERROR
14
- ERROR: 3, // Shows ERROR only
15
- FATAL: 4
16
- };
17
-
18
- // Set level based on LLMJS2_LOG environment variable
19
- this.setLevelFromEnv();
20
- }
21
-
22
- setLevelFromEnv() {
23
- const envLevel = process.env.LLMJS2_LOG;
24
- if (!envLevel) {
25
- // Default to no logging if not set
26
- this.currentLevel = 99; // Higher than any level
27
- return;
28
- }
29
-
30
- const upperEnvLevel = envLevel.toUpperCase();
31
- if (this.levels[upperEnvLevel] !== undefined) {
32
- this.currentLevel = this.levels[upperEnvLevel];
33
- } else {
34
- // Default to INFO if invalid value
35
- this.currentLevel = this.levels.INFO;
36
- }
37
- }
38
-
39
- setLevel(level) {
40
- if (this.levels[level] !== undefined) {
41
- this.currentLevel = this.levels[level];
42
- }
43
- }
44
-
45
- formatMessage(level, message, data = null) {
46
- const timestamp = new Date().toISOString();
47
- let output = `[${timestamp}] [${level}] ${message}`;
48
-
49
- if (data) {
50
- output += '\n' + JSON.stringify(data, null, 2);
51
- }
52
-
53
- return output;
54
- }
55
-
56
- shouldLog(level) {
57
- const levelValue = this.levels[level];
58
-
59
- // No logging if not configured
60
- if (this.currentLevel === 99) return false;
61
-
62
- // Custom logic based on user's requirements
63
- if (this.currentLevel === this.levels.DEBUG) {
64
- // Debug shows all levels
65
- return true;
66
- } else if (this.currentLevel === this.levels.INFO) {
67
- // Info shows INFO and ERROR only
68
- return level === 'INFO' || level === 'ERROR';
69
- } else if (this.currentLevel === this.levels.WARN) {
70
- // Warn shows WARN, INFO, and ERROR
71
- return level === 'WARN' || level === 'INFO' || level === 'ERROR';
72
- } else if (this.currentLevel === this.levels.ERROR) {
73
- // Error shows ERROR only
74
- return level === 'ERROR';
75
- }
76
-
77
- return false;
78
- }
79
-
80
- debug(message, data = null) {
81
- if (this.shouldLog('DEBUG')) {
82
- console.log(this.formatMessage('DEBUG', message, data));
83
- }
84
- }
85
-
86
- info(message, data = null) {
87
- if (this.shouldLog('INFO')) {
88
- console.log(this.formatMessage('INFO', message, data));
89
- }
90
- }
91
-
92
- warn(message, data = null) {
93
- if (this.shouldLog('WARN')) {
94
- console.warn(this.formatMessage('WARN', message, data));
95
- }
96
- }
97
-
98
- error(message, data = null) {
99
- if (this.shouldLog('ERROR')) {
100
- console.error(this.formatMessage('ERROR', message, data));
101
- }
102
- }
103
-
104
- fatal(message, data = null) {
105
- if (this.shouldLog('FATAL')) {
106
- console.error(this.formatMessage('FATAL', message, data));
107
- }
108
- }
109
- }
110
-
111
- // Create singleton logger instance
112
- const logger = new Logger();
113
-
114
- // Export for use in other modules
115
- module.exports = logger;
@@ -1,128 +0,0 @@
1
- const https = require('https');
2
-
3
- class OllamaProvider {
4
- constructor(config = {}) {
5
- this.baseURL = config.baseURL || process.env.OLLAMA_BASE_URL || 'https://ollama.com/api/chat';
6
- this.apiKey = config.apiKey || process.env.OLLAMA_API_KEY;
7
- this.defaultModel = config.defaultModel || process.env.OLLAMA_DEFAULT_MODEL || 'minimax-m2.5:cloud';
8
- this.timeout = config.timeout || 120000; // 2 minutes (LLMs can be slow)
9
- }
10
-
11
- async makeRequest(data, requestOptions = {}) {
12
- const apiKey = requestOptions.apiKey || this.apiKey;
13
- const baseURL = requestOptions.baseURL || this.baseURL;
14
- const timeout = requestOptions.timeout || this.timeout;
15
-
16
- if (!apiKey) {
17
- throw new Error('Ollama API key is required. Set OLLAMA_API_KEY environment variable or pass apiKey in config.');
18
- }
19
-
20
- const postData = JSON.stringify(data);
21
-
22
- const parsedUrl = new URL(baseURL);
23
-
24
- const options = {
25
- hostname: parsedUrl.hostname,
26
- port: parsedUrl.port || 443,
27
- path: parsedUrl.pathname + parsedUrl.search,
28
- method: 'POST',
29
- headers: {
30
- 'Content-Type': 'application/json',
31
- 'Authorization': `Bearer ${apiKey}`,
32
- 'Content-Length': Buffer.byteLength(postData)
33
- }
34
- };
35
-
36
- return new Promise((resolve, reject) => {
37
- const req = https.request(options, (res) => {
38
- let body = '';
39
-
40
- res.on('data', (chunk) => {
41
- body += chunk;
42
- });
43
-
44
- res.on('end', () => {
45
- try {
46
- if (res.statusCode >= 200 && res.statusCode < 300) {
47
- const response = JSON.parse(body);
48
- resolve(response);
49
- } else {
50
- const error = JSON.parse(body);
51
- reject(new Error(`Ollama API error (${res.statusCode}): ${error.error?.message || error.message || body }`));
52
- }
53
- } catch (parseError) {
54
- reject(new Error(`Failed to parse Ollama response: ${parseError.message}`));
55
- }
56
- });
57
- });
58
-
59
- req.on('error', (error) => {
60
- reject(new Error(`Ollama request failed: ${error.message}`));
61
- });
62
-
63
- req.setTimeout(timeout, () => {
64
- req.destroy();
65
- reject(new Error('Ollama request timed out'));
66
- });
67
-
68
- req.write(postData);
69
- req.end();
70
- });
71
- }
72
-
73
- async createCompletion(messages, options = {}) {
74
- const data = {
75
- model: options.model || this.defaultModel,
76
- messages: messages,
77
- stream: false, // We handle non-streaming for simplicity
78
- format: options.format,
79
- options: {
80
- temperature: options.temperature || 0.7,
81
- top_p: options.topP,
82
- num_predict: options.maxTokens,
83
- repeat_penalty: options.frequencyPenalty,
84
- presence_penalty: options.presencePenalty,
85
- stop: options.stop,
86
- num_ctx: options.numCtx || 4096
87
- },
88
- tools: options.tools,
89
- keep_alive: options.keepAlive || '5m'
90
- };
91
-
92
- // Remove undefined values
93
- Object.keys(data).forEach(key => {
94
- if (data[key] === undefined) {
95
- delete data[key];
96
- }
97
- });
98
-
99
- if (data.options) {
100
- Object.keys(data.options).forEach(key => {
101
- if (data.options[key] === undefined) {
102
- delete data.options[key];
103
- }
104
- });
105
- }
106
-
107
- const response = await this.makeRequest(data, {
108
- apiKey: options.apiKey,
109
- baseURL: options.baseURL,
110
- timeout: options.timeout
111
- });
112
-
113
- return {
114
- content: response.message?.content || '',
115
- role: response.message?.role || 'assistant',
116
- tool_calls: response.message?.tool_calls,
117
- usage: {
118
- prompt_eval_count: response.prompt_eval_count,
119
- eval_count: response.eval_count,
120
- total_duration: response.total_duration
121
- },
122
- model: response.model,
123
- finishReason: response.done_reason || (response.done ? 'stop' : null)
124
- };
125
- }
126
- }
127
-
128
- module.exports = OllamaProvider;
@@ -1,112 +0,0 @@
1
- const https = require('https');
2
-
3
- class OpenAIProvider {
4
- constructor(config = {}) {
5
- this.baseURL = config.baseURL || process.env.OPENAI_BASE_URL || 'https://api.openai.com/v1';
6
- this.apiKey = config.apiKey || process.env.OPENAI_API_KEY;
7
- this.defaultModel = config.defaultModel || process.env.OPENAI_DEFAULT_MODEL || 'gpt-3.5-turbo';
8
- this.timeout = config.timeout || 60000; // 60 seconds
9
- }
10
-
11
- async makeRequest(endpoint, data, requestOptions = {}) {
12
- const apiKey = requestOptions.apiKey || this.apiKey;
13
- const baseURL = requestOptions.baseURL || this.baseURL;
14
- const timeout = requestOptions.timeout || this.timeout;
15
-
16
- if (!apiKey) {
17
- throw new Error('OpenAI API key is required. Set OPENAI_API_KEY environment variable or pass apiKey in config.');
18
- }
19
-
20
- const requestUrl = `${baseURL}${endpoint}`;
21
- const postData = JSON.stringify(data);
22
-
23
- const parsedUrl = new URL(requestUrl);
24
-
25
- const options = {
26
- hostname: parsedUrl.hostname,
27
- port: parsedUrl.port || 443,
28
- path: parsedUrl.pathname + parsedUrl.search,
29
- method: 'POST',
30
- headers: {
31
- 'Content-Type': 'application/json',
32
- 'Authorization': `Bearer ${apiKey}`,
33
- 'Content-Length': Buffer.byteLength(postData)
34
- }
35
- };
36
-
37
- return new Promise((resolve, reject) => {
38
- const req = https.request(options, (res) => {
39
- let body = '';
40
-
41
- res.on('data', (chunk) => {
42
- body += chunk;
43
- });
44
-
45
- res.on('end', () => {
46
- try {
47
- if (res.statusCode >= 200 && res.statusCode < 300) {
48
- const response = JSON.parse(body);
49
- resolve(response);
50
- } else {
51
- const error = JSON.parse(body);
52
- reject(new Error(`OpenAI API error (${res.statusCode}): ${error.error?.message || 'Unknown error'}`));
53
- }
54
- } catch (parseError) {
55
- reject(new Error(`Failed to parse OpenAI response: ${parseError.message}`));
56
- }
57
- });
58
- });
59
-
60
- req.on('error', (error) => {
61
- reject(new Error(`OpenAI request failed: ${error.message}`));
62
- });
63
-
64
- req.setTimeout(timeout, () => {
65
- req.destroy();
66
- reject(new Error('OpenAI request timed out'));
67
- });
68
-
69
- req.write(postData);
70
- req.end();
71
- });
72
- }
73
-
74
- async createCompletion(messages, options = {}) {
75
- const data = {
76
- model: options.model || this.defaultModel,
77
- messages: messages,
78
- temperature: options.temperature || 0.7,
79
- max_tokens: options.maxTokens,
80
- top_p: options.topP,
81
- frequency_penalty: options.frequencyPenalty,
82
- presence_penalty: options.presencePenalty,
83
- stop: options.stop,
84
- tools: options.tools,
85
- tool_choice: options.toolChoice
86
- };
87
-
88
- // Remove undefined values
89
- Object.keys(data).forEach(key => {
90
- if (data[key] === undefined) {
91
- delete data[key];
92
- }
93
- });
94
-
95
- const response = await this.makeRequest('/chat/completions', data, {
96
- apiKey: options.apiKey,
97
- baseURL: options.baseURL,
98
- timeout: options.timeout
99
- });
100
-
101
- return {
102
- content: response.choices[0]?.message?.content || '',
103
- role: response.choices[0]?.message?.role || 'assistant',
104
- tool_calls: response.choices[0]?.message?.tool_calls,
105
- usage: response.usage,
106
- model: response.model,
107
- finishReason: response.choices[0]?.finish_reason
108
- };
109
- }
110
- }
111
-
112
- module.exports = OpenAIProvider;
@@ -1,206 +0,0 @@
1
- const https = require('https');
2
-
3
- function asNonEmptyString(value) {
4
- if (typeof value !== 'string') {
5
- return '';
6
- }
7
- return value.trim();
8
- }
9
-
10
- function firstNonEmptyString(...values) {
11
- for (const value of values) {
12
- const normalized = asNonEmptyString(value);
13
- if (normalized) {
14
- return normalized;
15
- }
16
- }
17
- return '';
18
- }
19
-
20
- function isValidHttpUrl(value) {
21
- const normalized = asNonEmptyString(value);
22
- if (!normalized) {
23
- return false;
24
- }
25
-
26
- try {
27
- const parsed = new URL(normalized);
28
- return parsed.protocol === 'https:' || parsed.protocol === 'http:';
29
- } catch (_error) {
30
- return false;
31
- }
32
- }
33
-
34
- function normalizeOpenRouterModel(model) {
35
- let normalized = asNonEmptyString(model);
36
-
37
- if (!normalized) {
38
- return '';
39
- }
40
-
41
- // Only inspect the first provider separator and preserve the rest of the path.
42
- const firstSlashIndex = normalized.indexOf('/');
43
- if (firstSlashIndex === -1) {
44
- return `openrouter/${normalized}`;
45
- }
46
-
47
- const provider = normalized.substring(0, firstSlashIndex).trim().toLowerCase();
48
- if (provider === 'openrouter') {
49
- const remainder = normalized.substring(firstSlashIndex + 1).trim();
50
-
51
- if (!remainder) {
52
- return '';
53
- }
54
-
55
- // Keep "openrouter/free" and collapse "openrouter/openrouter/free" to "openrouter/free".
56
- if (remainder.includes('/')) {
57
- normalized = remainder;
58
- } else {
59
- normalized = `openrouter/${remainder}`;
60
- }
61
- }
62
-
63
- return normalized;
64
- }
65
-
66
- class OpenRouterProvider {
67
- constructor(config = {}) {
68
- const configuredBaseURL = firstNonEmptyString(config.baseURL, process.env.OPEN_ROUTER_BASE_URL);
69
- this.baseURL = isValidHttpUrl(configuredBaseURL)
70
- ? configuredBaseURL
71
- : 'https://openrouter.ai/api/v1/chat/completions';
72
-
73
- this.apiKey = firstNonEmptyString(config.apiKey, process.env.OPEN_ROUTER_API_KEY);
74
- const configuredDefaultModel = firstNonEmptyString(config.defaultModel, process.env.OPEN_ROUTER_DEFAULT_MODEL) || 'openrouter/openrouter/free';
75
- this.defaultModel = configuredDefaultModel;
76
- this.timeout = config.timeout || 60000; // 60 seconds
77
- this.config = config; // Store entire config for additional properties like referer, title
78
- }
79
-
80
- async makeRequest(data, requestOptions = {}) {
81
- const apiKey = firstNonEmptyString(requestOptions.apiKey, this.apiKey);
82
- const baseURL = firstNonEmptyString(requestOptions.baseURL, this.baseURL);
83
- const timeout = requestOptions.timeout || this.timeout;
84
- const referer = firstNonEmptyString(requestOptions.referer, this.config.referer, process.env.OPEN_ROUTER_REFERER);
85
- const title = firstNonEmptyString(requestOptions.title, this.config.title, process.env.OPEN_ROUTER_TITLE);
86
-
87
- if (!apiKey) {
88
- throw new Error('OpenRouter API key is required. Set OPEN_ROUTER_API_KEY environment variable or pass apiKey in config.');
89
- }
90
-
91
- if (!isValidHttpUrl(baseURL)) {
92
- throw new Error('OpenRouter base URL is invalid. Set OPEN_ROUTER_BASE_URL to a valid http(s) URL.');
93
- }
94
-
95
- const postData = JSON.stringify(data);
96
-
97
- const parsedUrl = new URL(baseURL);
98
-
99
- const headers = {
100
- 'Content-Type': 'application/json',
101
- 'Authorization': `Bearer ${apiKey}`,
102
- 'Content-Length': Buffer.byteLength(postData)
103
- };
104
-
105
- if (isValidHttpUrl(referer)) {
106
- headers['HTTP-Referer'] = referer;
107
- }
108
-
109
- if (title) {
110
- headers['X-Title'] = title;
111
- }
112
-
113
- const options = {
114
- hostname: parsedUrl.hostname,
115
- port: parsedUrl.port || 443,
116
- path: parsedUrl.pathname + parsedUrl.search,
117
- method: 'POST',
118
- headers
119
- };
120
-
121
-
122
- return new Promise((resolve, reject) => {
123
- const req = https.request(options, (res) => {
124
- let body = '';
125
-
126
- res.on('data', (chunk) => {
127
- body += chunk;
128
- });
129
-
130
- res.on('end', () => {
131
- try {
132
- if (res.statusCode >= 200 && res.statusCode < 300) {
133
- const response = JSON.parse(body);
134
- resolve(response);
135
- } else {
136
- const error = JSON.parse(body);
137
- reject(new Error(`OpenRouter API error (${res.statusCode}): ${error.error?.message || 'Unknown error'}`));
138
- }
139
- } catch (parseError) {
140
- reject(new Error(`Failed to parse OpenRouter response: ${parseError.message}`));
141
- }
142
- });
143
- });
144
-
145
- req.on('error', (error) => {
146
- reject(new Error(`OpenRouter request failed: ${error.message}`));
147
- });
148
-
149
- req.setTimeout(timeout, () => {
150
- req.destroy();
151
- reject(new Error('OpenRouter request timed out'));
152
- });
153
-
154
- req.write(postData);
155
- req.end();
156
- });
157
- }
158
-
159
- async createCompletion(messages, options = {}) {
160
- const normalizedModel = normalizeOpenRouterModel(options.model || this.defaultModel);
161
-
162
- if (!normalizedModel) {
163
- throw new Error('OpenRouter model is invalid. Provide a non-empty model, for example "openrouter/free" or "meta-llama/llama-3.1-8b-instruct:free".');
164
- }
165
-
166
- const data = {
167
- model: normalizedModel,
168
- messages: messages,
169
- temperature: options.temperature || 0.7,
170
- max_tokens: options.maxTokens,
171
- top_p: options.topP,
172
- frequency_penalty: options.frequencyPenalty,
173
- presence_penalty: options.presencePenalty,
174
- stop: options.stop,
175
- tools: options.tools,
176
- tool_choice: options.toolChoice,
177
- transforms: options.transforms,
178
- models: options.models,
179
- route: options.route
180
- };
181
-
182
- // Remove undefined values
183
- Object.keys(data).forEach(key => {
184
- if (data[key] === undefined) {
185
- delete data[key];
186
- }
187
- });
188
-
189
- const response = await this.makeRequest(data, {
190
- apiKey: options.apiKey,
191
- baseURL: options.baseURL,
192
- timeout: options.timeout
193
- });
194
-
195
- return {
196
- content: response.choices[0]?.message?.content || '',
197
- role: response.choices[0]?.message?.role || 'assistant',
198
- tool_calls: response.choices[0]?.message?.tool_calls,
199
- usage: response.usage,
200
- model: response.model,
201
- finishReason: response.choices[0]?.finish_reason
202
- };
203
- }
204
- }
205
-
206
- module.exports = OpenRouterProvider;