llmjs2 1.1.0 → 1.3.0

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.
@@ -1,88 +1,120 @@
1
- const OLLAMA_API_URL = 'http://localhost:11434/api/chat';
2
- const REQUEST_TIMEOUT = 180000; // 60 seconds
3
-
4
- /**
5
- * Ollama completion provider
6
- * @param {string} model - Model name (e.g., "llama2")
7
- * @param {array} messages - Array of message objects with role and content
8
- * @param {string} [apiKey] - Optional API key
9
- * @param {array} [tools] - Optional array of tool definitions
10
- * @returns {Promise<string|object>} The completion result (string or object with tool calls)
11
- */
12
- export async function ollamaCompletion(model, messages, apiKey, tools) {
13
- const key = apiKey || process.env.OLLAMA_API_KEY;
14
-
15
- if (!key) {
16
- throw new Error('Ollama API key is required. Provide it as parameter or set OLLAMA_API_KEY environment variable.');
17
- }
18
-
19
- const requestBody = {
20
- model: model,
21
- messages: messages,
22
- stream: false
23
- };
24
-
25
-
26
-
27
- if (tools && tools.length > 0) {
28
- requestBody.tools = tools;
29
- }
30
-
31
-
32
-
33
-
34
- const controller = new AbortController();
35
- const timeoutId = setTimeout(() => controller.abort(), REQUEST_TIMEOUT);
36
-
37
-
38
-
39
-
40
- try {
41
- const response = await fetch(OLLAMA_API_URL, {
42
- method: 'POST',
43
- headers: {
44
- 'Content-Type': 'application/json',
45
- 'Authorization': `Bearer ${key}`
46
- },
47
- body: JSON.stringify(requestBody),
48
- signal: controller.signal
49
- });
50
-
51
-
52
-
53
- clearTimeout(timeoutId);
54
-
55
- if (!response.ok) {
56
- const errorText = await response.text();
57
- throw new Error(`Ollama API error (${response.status}): ${errorText}`);
58
- }
59
-
60
- const data = await response.json();
61
-
62
- if (!data.message) {
63
- throw new Error('Invalid response format from Ollama API');
64
- }
65
-
66
- // Return tool calls if present, otherwise return content
67
- if (data.message.tool_calls && data.message.tool_calls.length > 0) {
68
- return {
69
- content: data.message.content || '',
70
- tool_calls: data.message.tool_calls
71
- };
72
- }
73
-
74
- return data.message.content || '';
75
- } catch (error) {
76
- clearTimeout(timeoutId);
77
-
78
- if (error.name === 'AbortError') {
79
- throw new Error('Ollama request timed out after 60 seconds');
80
- }
81
-
82
- if (error.message.includes('Ollama API error')) {
83
- throw error;
84
- }
85
-
86
- throw new Error(`Ollama request failed: ${error.message}`);
87
- }
88
- }
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) {
12
+ if (!this.apiKey) {
13
+ throw new Error('Ollama API key is required. Set OLLAMA_API_KEY environment variable or pass apiKey in config.');
14
+ }
15
+
16
+ const postData = JSON.stringify(data);
17
+
18
+ const parsedUrl = new URL(this.baseURL);
19
+
20
+ const options = {
21
+ hostname: parsedUrl.hostname,
22
+ port: parsedUrl.port || 443,
23
+ path: parsedUrl.pathname + parsedUrl.search,
24
+ method: 'POST',
25
+ headers: {
26
+ 'Content-Type': 'application/json',
27
+ 'Authorization': `Bearer ${this.apiKey}`,
28
+ 'Content-Length': Buffer.byteLength(postData)
29
+ }
30
+ };
31
+
32
+ return new Promise((resolve, reject) => {
33
+ const req = https.request(options, (res) => {
34
+ let body = '';
35
+
36
+ res.on('data', (chunk) => {
37
+ body += chunk;
38
+ });
39
+
40
+ res.on('end', () => {
41
+ try {
42
+ if (res.statusCode >= 200 && res.statusCode < 300) {
43
+ const response = JSON.parse(body);
44
+ resolve(response);
45
+ } else {
46
+ const error = JSON.parse(body);
47
+ reject(new Error(`Ollama API error (${res.statusCode}): ${error.error?.message || error.message || body }`));
48
+ }
49
+ } catch (parseError) {
50
+ reject(new Error(`Failed to parse Ollama response: ${parseError.message}`));
51
+ }
52
+ });
53
+ });
54
+
55
+ req.on('error', (error) => {
56
+ reject(new Error(`Ollama request failed: ${error.message}`));
57
+ });
58
+
59
+ req.setTimeout(this.timeout, () => {
60
+ req.destroy();
61
+ reject(new Error('Ollama request timed out'));
62
+ });
63
+
64
+ req.write(postData);
65
+ req.end();
66
+ });
67
+ }
68
+
69
+ async createCompletion(messages, options = {}) {
70
+ const data = {
71
+ model: options.model || this.defaultModel,
72
+ messages: messages,
73
+ stream: false, // We handle non-streaming for simplicity
74
+ format: options.format,
75
+ options: {
76
+ temperature: options.temperature || 0.7,
77
+ top_p: options.topP,
78
+ num_predict: options.maxTokens,
79
+ repeat_penalty: options.frequencyPenalty,
80
+ presence_penalty: options.presencePenalty,
81
+ stop: options.stop,
82
+ num_ctx: options.numCtx || 4096
83
+ },
84
+ tools: options.tools,
85
+ keep_alive: options.keepAlive || '5m'
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
+ if (data.options) {
96
+ Object.keys(data.options).forEach(key => {
97
+ if (data.options[key] === undefined) {
98
+ delete data.options[key];
99
+ }
100
+ });
101
+ }
102
+
103
+ const response = await this.makeRequest(data);
104
+
105
+ return {
106
+ content: response.message?.content || '',
107
+ role: response.message?.role || 'assistant',
108
+ toolCalls: response.message?.tool_calls,
109
+ usage: {
110
+ prompt_eval_count: response.prompt_eval_count,
111
+ eval_count: response.eval_count,
112
+ total_duration: response.total_duration
113
+ },
114
+ model: response.model,
115
+ finishReason: response.done_reason || (response.done ? 'stop' : null)
116
+ };
117
+ }
118
+ }
119
+
120
+ module.exports = OllamaProvider;
@@ -0,0 +1,104 @@
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) {
12
+ if (!this.apiKey) {
13
+ throw new Error('OpenAI API key is required. Set OPENAI_API_KEY environment variable or pass apiKey in config.');
14
+ }
15
+
16
+ const requestUrl = `${this.baseURL}${endpoint}`;
17
+ const postData = JSON.stringify(data);
18
+
19
+ const parsedUrl = new URL(requestUrl);
20
+
21
+ const options = {
22
+ hostname: parsedUrl.hostname,
23
+ port: parsedUrl.port || 443,
24
+ path: parsedUrl.pathname + parsedUrl.search,
25
+ method: 'POST',
26
+ headers: {
27
+ 'Content-Type': 'application/json',
28
+ 'Authorization': `Bearer ${this.apiKey}`,
29
+ 'Content-Length': Buffer.byteLength(postData)
30
+ }
31
+ };
32
+
33
+ return new Promise((resolve, reject) => {
34
+ const req = https.request(options, (res) => {
35
+ let body = '';
36
+
37
+ res.on('data', (chunk) => {
38
+ body += chunk;
39
+ });
40
+
41
+ res.on('end', () => {
42
+ try {
43
+ if (res.statusCode >= 200 && res.statusCode < 300) {
44
+ const response = JSON.parse(body);
45
+ resolve(response);
46
+ } else {
47
+ const error = JSON.parse(body);
48
+ reject(new Error(`OpenAI API error (${res.statusCode}): ${error.error?.message || 'Unknown error'}`));
49
+ }
50
+ } catch (parseError) {
51
+ reject(new Error(`Failed to parse OpenAI response: ${parseError.message}`));
52
+ }
53
+ });
54
+ });
55
+
56
+ req.on('error', (error) => {
57
+ reject(new Error(`OpenAI request failed: ${error.message}`));
58
+ });
59
+
60
+ req.setTimeout(this.timeout, () => {
61
+ req.destroy();
62
+ reject(new Error('OpenAI request timed out'));
63
+ });
64
+
65
+ req.write(postData);
66
+ req.end();
67
+ });
68
+ }
69
+
70
+ async createCompletion(messages, options = {}) {
71
+ const data = {
72
+ model: options.model || this.defaultModel,
73
+ messages: messages,
74
+ temperature: options.temperature || 0.7,
75
+ max_tokens: options.maxTokens,
76
+ top_p: options.topP,
77
+ frequency_penalty: options.frequencyPenalty,
78
+ presence_penalty: options.presencePenalty,
79
+ stop: options.stop,
80
+ tools: options.tools,
81
+ tool_choice: options.toolChoice
82
+ };
83
+
84
+ // Remove undefined values
85
+ Object.keys(data).forEach(key => {
86
+ if (data[key] === undefined) {
87
+ delete data[key];
88
+ }
89
+ });
90
+
91
+ const response = await this.makeRequest('/chat/completions', data);
92
+
93
+ return {
94
+ content: response.choices[0]?.message?.content || '',
95
+ role: response.choices[0]?.message?.role || 'assistant',
96
+ toolCalls: response.choices[0]?.message?.tool_calls,
97
+ usage: response.usage,
98
+ model: response.model,
99
+ finishReason: response.choices[0]?.finish_reason
100
+ };
101
+ }
102
+ }
103
+
104
+ module.exports = OpenAIProvider;
@@ -1,79 +1,113 @@
1
- const OPENROUTER_API_URL = 'https://openrouter.ai/api/v1/chat/completions';
2
- const REQUEST_TIMEOUT = 180000; // 60 seconds
3
-
4
- /**
5
- * OpenRouter completion provider
6
- * @param {string} model - Model name (e.g., "meta-llama/llama-2-70b-chat")
7
- * @param {array} messages - Array of message objects with role and content
8
- * @param {string} [apiKey] - Optional API key
9
- * @param {array} [tools] - Optional array of tool definitions
10
- * @returns {Promise<string|object>} The completion result (string or object with tool calls)
11
- */
12
- export async function openrouterCompletion(model, messages, apiKey, tools) {
13
- const key = apiKey || process.env.OPEN_ROUTER_API_KEY;
14
-
15
- if (!key) {
16
- throw new Error('OpenRouter API key is required. Provide it as parameter or set OPEN_ROUTER_API_KEY environment variable.');
17
- }
18
-
19
- const requestBody = {
20
- model: model,
21
- messages: messages
22
- };
23
-
24
- if (tools && tools.length > 0) {
25
- requestBody.tools = tools;
26
- }
27
-
28
- const controller = new AbortController();
29
- const timeoutId = setTimeout(() => controller.abort(), REQUEST_TIMEOUT);
30
-
31
- try {
32
- const response = await fetch(OPENROUTER_API_URL, {
33
- method: 'POST',
34
- headers: {
35
- 'Content-Type': 'application/json',
36
- 'Authorization': `Bearer ${key}`
37
- },
38
- body: JSON.stringify(requestBody),
39
- signal: controller.signal
40
- });
41
-
42
- clearTimeout(timeoutId);
43
-
44
- if (!response.ok) {
45
- const errorText = await response.text();
46
- throw new Error(`OpenRouter API error (${response.status}): ${errorText}`);
47
- }
48
-
49
- const data = await response.json();
50
-
51
- if (!data.choices || !data.choices[0] || !data.choices[0].message) {
52
- throw new Error('Invalid response format from OpenRouter API');
53
- }
54
-
55
- const message = data.choices[0].message;
56
-
57
- // Return tool calls if present, otherwise return content
58
- if (message.tool_calls && message.tool_calls.length > 0) {
59
- return {
60
- content: message.content || '',
61
- tool_calls: message.tool_calls
62
- };
63
- }
64
-
65
- return message.content || '';
66
- } catch (error) {
67
- clearTimeout(timeoutId);
68
-
69
- if (error.name === 'AbortError') {
70
- throw new Error('OpenRouter request timed out after 60 seconds');
71
- }
72
-
73
- if (error.message.includes('OpenRouter API error')) {
74
- throw error;
75
- }
76
-
77
- throw new Error(`OpenRouter request failed: ${error.message}`);
78
- }
79
- }
1
+ const https = require('https');
2
+
3
+ class OpenRouterProvider {
4
+ constructor(config = {}) {
5
+ this.baseURL = config.baseURL || process.env.OPEN_ROUTER_BASE_URL || 'https://openrouter.ai/api/v1/chat/completions';
6
+ this.apiKey = config.apiKey || process.env.OPEN_ROUTER_API_KEY;
7
+ this.defaultModel = config.defaultModel || process.env.OPEN_ROUTER_DEFAULT_MODEL || 'openrouter/free';
8
+ this.timeout = config.timeout || 60000; // 60 seconds
9
+ this.config = config; // Store entire config for additional properties like referer, title
10
+ }
11
+
12
+ async makeRequest(data) {
13
+ if (!this.apiKey) {
14
+ throw new Error('OpenRouter API key is required. Set OPEN_ROUTER_API_KEY environment variable or pass apiKey in config.');
15
+ }
16
+
17
+ const postData = JSON.stringify(data);
18
+
19
+ const parsedUrl = new URL(this.baseURL);
20
+
21
+ const options = {
22
+ hostname: parsedUrl.hostname,
23
+ port: parsedUrl.port || 443,
24
+ path: parsedUrl.pathname + parsedUrl.search,
25
+ method: 'POST',
26
+ headers: {
27
+ 'Content-Type': 'application/json',
28
+ 'Authorization': `Bearer ${this.apiKey}`,
29
+ 'Content-Length': Buffer.byteLength(postData),
30
+ 'HTTP-Referer': this.config.referer || process.env.OPEN_ROUTER_REFERER || '',
31
+ 'X-Title': this.config.title || process.env.OPEN_ROUTER_TITLE || ''
32
+ }
33
+ };
34
+
35
+ // Remove empty headers
36
+ if (!options.headers['HTTP-Referer']) delete options.headers['HTTP-Referer'];
37
+ if (!options.headers['X-Title']) delete options.headers['X-Title'];
38
+
39
+ return new Promise((resolve, reject) => {
40
+ const req = https.request(options, (res) => {
41
+ let body = '';
42
+
43
+ res.on('data', (chunk) => {
44
+ body += chunk;
45
+ });
46
+
47
+ res.on('end', () => {
48
+ try {
49
+ if (res.statusCode >= 200 && res.statusCode < 300) {
50
+ const response = JSON.parse(body);
51
+ resolve(response);
52
+ } else {
53
+ const error = JSON.parse(body);
54
+ reject(new Error(`OpenRouter API error (${res.statusCode}): ${error.error?.message || 'Unknown error'}`));
55
+ }
56
+ } catch (parseError) {
57
+ reject(new Error(`Failed to parse OpenRouter response: ${parseError.message}`));
58
+ }
59
+ });
60
+ });
61
+
62
+ req.on('error', (error) => {
63
+ reject(new Error(`OpenRouter request failed: ${error.message}`));
64
+ });
65
+
66
+ req.setTimeout(this.timeout, () => {
67
+ req.destroy();
68
+ reject(new Error('OpenRouter request timed out'));
69
+ });
70
+
71
+ req.write(postData);
72
+ req.end();
73
+ });
74
+ }
75
+
76
+ async createCompletion(messages, options = {}) {
77
+ const data = {
78
+ model: options.model || this.defaultModel,
79
+ messages: messages,
80
+ temperature: options.temperature || 0.7,
81
+ max_tokens: options.maxTokens,
82
+ top_p: options.topP,
83
+ frequency_penalty: options.frequencyPenalty,
84
+ presence_penalty: options.presencePenalty,
85
+ stop: options.stop,
86
+ tools: options.tools,
87
+ tool_choice: options.toolChoice,
88
+ transforms: options.transforms,
89
+ models: options.models,
90
+ route: options.route
91
+ };
92
+
93
+ // Remove undefined values
94
+ Object.keys(data).forEach(key => {
95
+ if (data[key] === undefined) {
96
+ delete data[key];
97
+ }
98
+ });
99
+
100
+ const response = await this.makeRequest(data);
101
+
102
+ return {
103
+ content: response.choices[0]?.message?.content || '',
104
+ role: response.choices[0]?.message?.role || 'assistant',
105
+ toolCalls: response.choices[0]?.message?.tool_calls,
106
+ usage: response.usage,
107
+ model: response.model,
108
+ finishReason: response.choices[0]?.finish_reason
109
+ };
110
+ }
111
+ }
112
+
113
+ module.exports = OpenRouterProvider;