provider-kit 0.1.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.
- package/README.md +126 -0
- package/package.json +19 -0
- package/src/core/persistent-config.js +107 -0
- package/src/index.js +16 -0
- package/src/providers/anthropic-adapter.js +368 -0
- package/src/providers/azure-adapter.js +323 -0
- package/src/providers/bedrock-adapter.js +388 -0
- package/src/providers/cohere-adapter.js +319 -0
- package/src/providers/gemini-adapter.js +282 -0
- package/src/providers/local-provider.js +185 -0
- package/src/providers/openai-compatible.js +840 -0
- package/src/providers/provider-error-adapter.js +100 -0
- package/src/providers/provider-manager.js +321 -0
- package/src/providers/provider-registry.js +316 -0
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { ProviderError } from './provider-error-adapter.js';
|
|
2
|
+
import { spawn } from 'child_process';
|
|
3
|
+
import { Readable } from 'stream';
|
|
4
|
+
|
|
5
|
+
export class LocalAiProvider {
|
|
6
|
+
constructor(id, name) {
|
|
7
|
+
this.id = id;
|
|
8
|
+
this.name = name;
|
|
9
|
+
this.mode = 'command';
|
|
10
|
+
this.command = null;
|
|
11
|
+
this.args = [];
|
|
12
|
+
this.endpoint = null;
|
|
13
|
+
this.connected = false;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async connect(config) {
|
|
17
|
+
this.mode = config.mode || 'command';
|
|
18
|
+
this.command = config.command;
|
|
19
|
+
this.args = config.args || [];
|
|
20
|
+
this.endpoint = config.endpoint;
|
|
21
|
+
|
|
22
|
+
if (this.mode === 'command') {
|
|
23
|
+
if (!this.command) {
|
|
24
|
+
throw new ProviderError('Command is required for command mode');
|
|
25
|
+
}
|
|
26
|
+
this.connected = true;
|
|
27
|
+
} else {
|
|
28
|
+
if (!this.endpoint) {
|
|
29
|
+
throw new ProviderError('Endpoint is required for API mode');
|
|
30
|
+
}
|
|
31
|
+
await this.verifyApiConnection();
|
|
32
|
+
this.connected = true;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async disconnect() {
|
|
39
|
+
this.connected = false;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async verifyApiConnection() {
|
|
43
|
+
try {
|
|
44
|
+
const response = await fetch(this.endpoint, {
|
|
45
|
+
method: 'GET',
|
|
46
|
+
headers: { 'Content-Type': 'application/json' }
|
|
47
|
+
});
|
|
48
|
+
if (!response.ok) {
|
|
49
|
+
throw new ProviderError(`API error: ${response.status}`);
|
|
50
|
+
}
|
|
51
|
+
} catch (e) {
|
|
52
|
+
throw new ProviderError(`Cannot connect to ${this.endpoint}: ${e.message}`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async chat(model, messages) {
|
|
57
|
+
if (this.mode === 'command') {
|
|
58
|
+
return this.chatViaCommand(messages);
|
|
59
|
+
} else {
|
|
60
|
+
return this.chatViaApi(model, messages);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async chatViaCommand(messages) {
|
|
65
|
+
const systemPrompt = messages.find(m => m.role === 'system')?.content;
|
|
66
|
+
let userMessage = messages.filter(m => m.role === 'user').map(m => m.content).join('\n');
|
|
67
|
+
|
|
68
|
+
if (systemPrompt) {
|
|
69
|
+
userMessage = `[System: ${systemPrompt}]\n\n${userMessage}`;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return new Promise((resolve, reject) => {
|
|
73
|
+
const args = [...this.args, userMessage];
|
|
74
|
+
|
|
75
|
+
let stdout = '';
|
|
76
|
+
let stderr = '';
|
|
77
|
+
|
|
78
|
+
const child = spawn(this.command, args, {
|
|
79
|
+
shell: true,
|
|
80
|
+
timeout: 120000,
|
|
81
|
+
windowsHide: true
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
if (child.stdout) {
|
|
85
|
+
child.stdout.on('data', (data) => {
|
|
86
|
+
stdout += data.toString();
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (child.stderr) {
|
|
91
|
+
child.stderr.on('data', (data) => {
|
|
92
|
+
stderr += data.toString();
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
child.on('error', (error) => {
|
|
97
|
+
reject(new ProviderError(`Failed to start ${this.command}: ${error.message}`));
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
child.on('close', (code) => {
|
|
101
|
+
if (code !== 0 && stderr) {
|
|
102
|
+
console.error(`[${this.name}] stderr: ${stderr}`);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
resolve({
|
|
106
|
+
id: crypto.randomUUID(),
|
|
107
|
+
model: this.name,
|
|
108
|
+
content: stdout.trim() || stderr.trim(),
|
|
109
|
+
usage: {
|
|
110
|
+
prompt_tokens: userMessage.length,
|
|
111
|
+
completion_tokens: stdout.length
|
|
112
|
+
},
|
|
113
|
+
created: Date.now()
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
setTimeout(() => {
|
|
118
|
+
child.kill();
|
|
119
|
+
reject(new ProviderError('Command timed out after 120 seconds'));
|
|
120
|
+
}, 120000);
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
async chatViaApi(model, messages) {
|
|
125
|
+
const systemPrompt = messages.find(m => m.role === 'system')?.content;
|
|
126
|
+
const filteredMessages = messages.filter(m => m.role !== 'system');
|
|
127
|
+
|
|
128
|
+
const requestBody = {
|
|
129
|
+
model: model || 'default',
|
|
130
|
+
messages: filteredMessages,
|
|
131
|
+
stream: false
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
if (systemPrompt) {
|
|
135
|
+
requestBody.system = systemPrompt;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
try {
|
|
139
|
+
const response = await fetch(this.endpoint, {
|
|
140
|
+
method: 'POST',
|
|
141
|
+
headers: { 'Content-Type': 'application/json' },
|
|
142
|
+
body: JSON.stringify(requestBody)
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
if (!response.ok) {
|
|
146
|
+
const error = await response.json().catch(() => ({}));
|
|
147
|
+
throw new ProviderError(error.error?.message || `API error: ${response.status}`);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const data = await response.json();
|
|
151
|
+
|
|
152
|
+
return {
|
|
153
|
+
id: data.id || crypto.randomUUID(),
|
|
154
|
+
model: data.model || this.name,
|
|
155
|
+
content: this.extractContent(data),
|
|
156
|
+
usage: data.usage || {},
|
|
157
|
+
created: Date.now()
|
|
158
|
+
};
|
|
159
|
+
} catch (e) {
|
|
160
|
+
throw new ProviderError(`Chat failed: ${e.message}`);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
extractContent(data) {
|
|
165
|
+
if (typeof data.content === 'string') {
|
|
166
|
+
return data.content;
|
|
167
|
+
}
|
|
168
|
+
if (data.choices?.[0]?.message?.content) {
|
|
169
|
+
return data.choices[0].message.content;
|
|
170
|
+
}
|
|
171
|
+
if (data.response) {
|
|
172
|
+
return data.response;
|
|
173
|
+
}
|
|
174
|
+
return JSON.stringify(data);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
getModels() {
|
|
178
|
+
return [this.name];
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export function createLocalProvider(type, config) {
|
|
183
|
+
const provider = new LocalAiProvider(type, type);
|
|
184
|
+
return provider;
|
|
185
|
+
}
|