lovecode-ai 0.1.3 → 0.1.4
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/dist/chunk-3AHNSXQX.js +86 -0
- package/dist/{chunk-G7VQGYJW.js → chunk-7CT3XDH6.js} +5 -352
- package/dist/chunk-LJ7HTOIK.js +154 -0
- package/dist/chunk-Y3HADLWO.js +366 -0
- package/dist/config-FJNTTKR3.js +16 -0
- package/dist/env-HJQWWL6N.js +14 -0
- package/dist/{git-TBOGPTY4.js → git-FZPRJVFI.js} +2 -1
- package/dist/index.js +363 -536
- package/dist/registry-ADSIKXA4.js +22 -0
- package/package.json +1 -1
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
// src/ai/registry.ts
|
|
2
|
+
import chalk2 from "chalk";
|
|
3
|
+
|
|
4
|
+
// src/utils/logger.ts
|
|
5
|
+
import chalk from "chalk";
|
|
6
|
+
var LogLevel = /* @__PURE__ */ ((LogLevel2) => {
|
|
7
|
+
LogLevel2[LogLevel2["DEBUG"] = 0] = "DEBUG";
|
|
8
|
+
LogLevel2[LogLevel2["INFO"] = 1] = "INFO";
|
|
9
|
+
LogLevel2[LogLevel2["WARN"] = 2] = "WARN";
|
|
10
|
+
LogLevel2[LogLevel2["ERROR"] = 3] = "ERROR";
|
|
11
|
+
return LogLevel2;
|
|
12
|
+
})(LogLevel || {});
|
|
13
|
+
var LOG_LEVEL = (process.env.LOVECODE_LOG_LEVEL || "info").toUpperCase();
|
|
14
|
+
var currentLevel = LogLevel[LOG_LEVEL] ?? 1 /* INFO */;
|
|
15
|
+
var Logger = class {
|
|
16
|
+
static debug(...args) {
|
|
17
|
+
if (currentLevel <= 0 /* DEBUG */) {
|
|
18
|
+
console.error(chalk.dim("[debug]"), ...args);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
static info(...args) {
|
|
22
|
+
if (currentLevel <= 1 /* INFO */) {
|
|
23
|
+
console.error(chalk.blue("[info]"), ...args);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
static warn(...args) {
|
|
27
|
+
if (currentLevel <= 2 /* WARN */) {
|
|
28
|
+
console.error(chalk.yellow("[warn]"), ...args);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
static error(...args) {
|
|
32
|
+
if (currentLevel <= 3 /* ERROR */) {
|
|
33
|
+
console.error(chalk.red("[error]"), ...args);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
// src/ai/ollama.ts
|
|
39
|
+
var OllamaProvider = class {
|
|
40
|
+
name = "ollama";
|
|
41
|
+
async chat(messages, config) {
|
|
42
|
+
const baseUrl = config.baseUrl || "http://localhost:11434";
|
|
43
|
+
Logger.debug(`Ollama chat: ${config.model} (${messages.length} messages)`);
|
|
44
|
+
const response = await fetch(`${baseUrl}/api/chat`, {
|
|
45
|
+
method: "POST",
|
|
46
|
+
headers: { "Content-Type": "application/json" },
|
|
47
|
+
body: JSON.stringify({
|
|
48
|
+
model: config.model,
|
|
49
|
+
messages,
|
|
50
|
+
stream: false,
|
|
51
|
+
options: {
|
|
52
|
+
temperature: config.temperature ?? 0.2,
|
|
53
|
+
num_predict: config.maxTokens ?? 4096
|
|
54
|
+
}
|
|
55
|
+
})
|
|
56
|
+
});
|
|
57
|
+
if (!response.ok) {
|
|
58
|
+
throw new Error(`Ollama API error: ${response.status} ${response.statusText}`);
|
|
59
|
+
}
|
|
60
|
+
const data = await response.json();
|
|
61
|
+
return data.message?.content ?? "";
|
|
62
|
+
}
|
|
63
|
+
async *stream(messages, config) {
|
|
64
|
+
const baseUrl = config.baseUrl || "http://localhost:11434";
|
|
65
|
+
const response = await fetch(`${baseUrl}/api/chat`, {
|
|
66
|
+
method: "POST",
|
|
67
|
+
headers: { "Content-Type": "application/json" },
|
|
68
|
+
body: JSON.stringify({
|
|
69
|
+
model: config.model,
|
|
70
|
+
messages,
|
|
71
|
+
stream: true,
|
|
72
|
+
options: {
|
|
73
|
+
temperature: config.temperature ?? 0.2,
|
|
74
|
+
num_predict: config.maxTokens ?? 4096
|
|
75
|
+
}
|
|
76
|
+
})
|
|
77
|
+
});
|
|
78
|
+
if (!response.ok) {
|
|
79
|
+
throw new Error(`Ollama API error: ${response.status} ${response.statusText}`);
|
|
80
|
+
}
|
|
81
|
+
const reader = response.body?.getReader();
|
|
82
|
+
if (!reader) return;
|
|
83
|
+
const decoder = new TextDecoder();
|
|
84
|
+
let buffer = "";
|
|
85
|
+
while (true) {
|
|
86
|
+
const { done, value } = await reader.read();
|
|
87
|
+
if (done) break;
|
|
88
|
+
buffer += decoder.decode(value, { stream: true });
|
|
89
|
+
const lines = buffer.split("\n");
|
|
90
|
+
buffer = lines.pop() ?? "";
|
|
91
|
+
for (const line of lines) {
|
|
92
|
+
if (!line.trim()) continue;
|
|
93
|
+
try {
|
|
94
|
+
const parsed = JSON.parse(line);
|
|
95
|
+
if (parsed.done) return;
|
|
96
|
+
if (parsed.message?.content) {
|
|
97
|
+
yield parsed.message.content;
|
|
98
|
+
}
|
|
99
|
+
} catch {
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
async isAvailable(baseUrl) {
|
|
105
|
+
try {
|
|
106
|
+
const res = await fetch(`${baseUrl || "http://localhost:11434"}/api/tags`);
|
|
107
|
+
return res.ok;
|
|
108
|
+
} catch {
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
// src/ai/openai-like.ts
|
|
115
|
+
var OpenAILikeProvider = class {
|
|
116
|
+
name;
|
|
117
|
+
baseUrl;
|
|
118
|
+
constructor(name, baseUrl) {
|
|
119
|
+
this.name = name;
|
|
120
|
+
this.baseUrl = baseUrl.replace(/\/+$/, "");
|
|
121
|
+
}
|
|
122
|
+
async chat(messages, config) {
|
|
123
|
+
const url = `${this.baseUrl}/chat/completions`;
|
|
124
|
+
Logger.debug(`[${this.name}] chat: ${config.model} (${messages.length} messages)`);
|
|
125
|
+
const apiKey = this.getApiKey();
|
|
126
|
+
const response = await fetch(url, {
|
|
127
|
+
method: "POST",
|
|
128
|
+
headers: {
|
|
129
|
+
"Content-Type": "application/json",
|
|
130
|
+
...apiKey ? { Authorization: `Bearer ${apiKey}` } : {}
|
|
131
|
+
},
|
|
132
|
+
body: JSON.stringify({
|
|
133
|
+
model: config.model,
|
|
134
|
+
messages,
|
|
135
|
+
temperature: config.temperature ?? 0.2,
|
|
136
|
+
max_tokens: config.maxTokens ?? 4096,
|
|
137
|
+
stream: false
|
|
138
|
+
})
|
|
139
|
+
});
|
|
140
|
+
if (!response.ok) {
|
|
141
|
+
const body = await response.text().catch(() => "");
|
|
142
|
+
throw new Error(`[${this.name}] API error ${response.status}: ${body.slice(0, 200)}`);
|
|
143
|
+
}
|
|
144
|
+
const data = await response.json();
|
|
145
|
+
return data.choices?.[0]?.message?.content ?? "";
|
|
146
|
+
}
|
|
147
|
+
async *stream(messages, config) {
|
|
148
|
+
const url = `${this.baseUrl}/chat/completions`;
|
|
149
|
+
const apiKey = this.getApiKey();
|
|
150
|
+
const response = await fetch(url, {
|
|
151
|
+
method: "POST",
|
|
152
|
+
headers: {
|
|
153
|
+
"Content-Type": "application/json",
|
|
154
|
+
...apiKey ? { Authorization: `Bearer ${apiKey}` } : {}
|
|
155
|
+
},
|
|
156
|
+
body: JSON.stringify({
|
|
157
|
+
model: config.model,
|
|
158
|
+
messages,
|
|
159
|
+
temperature: config.temperature ?? 0.2,
|
|
160
|
+
max_tokens: config.maxTokens ?? 4096,
|
|
161
|
+
stream: true
|
|
162
|
+
})
|
|
163
|
+
});
|
|
164
|
+
if (!response.ok) {
|
|
165
|
+
const body = await response.text().catch(() => "");
|
|
166
|
+
throw new Error(`[${this.name}] API error ${response.status}: ${body.slice(0, 200)}`);
|
|
167
|
+
}
|
|
168
|
+
const reader = response.body?.getReader();
|
|
169
|
+
if (!reader) return;
|
|
170
|
+
const decoder = new TextDecoder();
|
|
171
|
+
let buffer = "";
|
|
172
|
+
while (true) {
|
|
173
|
+
const { done, value } = await reader.read();
|
|
174
|
+
if (done) break;
|
|
175
|
+
buffer += decoder.decode(value, { stream: true });
|
|
176
|
+
const lines = buffer.split("\n");
|
|
177
|
+
buffer = lines.pop() ?? "";
|
|
178
|
+
for (const line of lines) {
|
|
179
|
+
const trimmed = line.trim();
|
|
180
|
+
if (!trimmed || !trimmed.startsWith("data: ")) continue;
|
|
181
|
+
const jsonStr = trimmed.slice(6).trim();
|
|
182
|
+
if (jsonStr === "[DONE]") return;
|
|
183
|
+
try {
|
|
184
|
+
const parsed = JSON.parse(jsonStr);
|
|
185
|
+
const content = parsed.choices?.[0]?.delta?.content;
|
|
186
|
+
if (content) yield content;
|
|
187
|
+
} catch {
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
getApiKey() {
|
|
193
|
+
const envVar = this.name.toUpperCase();
|
|
194
|
+
return process.env[`${envVar}_API_KEY`] || process.env.OPENAI_API_KEY;
|
|
195
|
+
}
|
|
196
|
+
async isAvailable() {
|
|
197
|
+
try {
|
|
198
|
+
const res = await fetch(`${this.baseUrl}/models`, {
|
|
199
|
+
headers: this.getApiKey() ? { Authorization: `Bearer ${this.getApiKey()}` } : {}
|
|
200
|
+
});
|
|
201
|
+
return res.ok;
|
|
202
|
+
} catch {
|
|
203
|
+
return false;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
// src/ai/registry.ts
|
|
209
|
+
var registry = [
|
|
210
|
+
{
|
|
211
|
+
name: "ollama",
|
|
212
|
+
provider: new OllamaProvider(),
|
|
213
|
+
models: ["codellama", "deepseek-coder", "llama3.2", "llama3.1", "mistral", "mixtral", "phi3", "qwen2.5-coder"],
|
|
214
|
+
local: true,
|
|
215
|
+
priority: 10,
|
|
216
|
+
defaultModel: "codellama",
|
|
217
|
+
getConfig: (model) => ({
|
|
218
|
+
model,
|
|
219
|
+
baseUrl: "http://localhost:11434",
|
|
220
|
+
temperature: 0.2,
|
|
221
|
+
maxTokens: 8192
|
|
222
|
+
})
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
name: "groq",
|
|
226
|
+
provider: new OpenAILikeProvider("groq", "https://api.groq.com/openai/v1"),
|
|
227
|
+
models: ["llama3-70b-8192", "llama3-8b-8192", "mixtral-8x7b-32768", "gemma2-9b-it", "deepseek-r1-distill-llama-70b"],
|
|
228
|
+
local: false,
|
|
229
|
+
priority: 30,
|
|
230
|
+
defaultModel: "llama3-70b-8192",
|
|
231
|
+
getConfig: (model) => ({
|
|
232
|
+
model,
|
|
233
|
+
baseUrl: "https://api.groq.com/openai/v1",
|
|
234
|
+
temperature: 0.2,
|
|
235
|
+
maxTokens: 8192
|
|
236
|
+
})
|
|
237
|
+
},
|
|
238
|
+
{
|
|
239
|
+
name: "openrouter",
|
|
240
|
+
provider: new OpenAILikeProvider("openrouter", "https://openrouter.ai/api/v1"),
|
|
241
|
+
models: [
|
|
242
|
+
"google/gemini-2.0-flash-001",
|
|
243
|
+
"google/gemini-2.0-flash-lite-preview",
|
|
244
|
+
"mistralai/mistral-7b-instruct",
|
|
245
|
+
"meta-llama/llama-3.2-3b-instruct",
|
|
246
|
+
"deepseek/deepseek-chat",
|
|
247
|
+
"qwen/qwen-2.5-7b-instruct"
|
|
248
|
+
],
|
|
249
|
+
local: false,
|
|
250
|
+
priority: 40,
|
|
251
|
+
defaultModel: "google/gemini-2.0-flash-001",
|
|
252
|
+
getConfig: (model) => ({
|
|
253
|
+
model,
|
|
254
|
+
baseUrl: "https://openrouter.ai/api/v1",
|
|
255
|
+
temperature: 0.2,
|
|
256
|
+
maxTokens: 8192
|
|
257
|
+
})
|
|
258
|
+
},
|
|
259
|
+
{
|
|
260
|
+
name: "together",
|
|
261
|
+
provider: new OpenAILikeProvider("together", "https://api.together.xyz/v1"),
|
|
262
|
+
models: [
|
|
263
|
+
"mistralai/Mixtral-8x22B-Instruct-v0.1",
|
|
264
|
+
"mistralai/Mistral-7B-Instruct-v0.3",
|
|
265
|
+
"meta-llama/Llama-3.2-3B-Instruct-Turbo",
|
|
266
|
+
"meta-llama/Llama-3.2-11B-Vision-Instruct-Turbo",
|
|
267
|
+
"deepseek-ai/deepseek-coder-33b-instruct",
|
|
268
|
+
"Qwen/Qwen2.5-7B-Instruct-Turbo"
|
|
269
|
+
],
|
|
270
|
+
local: false,
|
|
271
|
+
priority: 50,
|
|
272
|
+
defaultModel: "mistralai/Mixtral-8x22B-Instruct-v0.1",
|
|
273
|
+
getConfig: (model) => ({
|
|
274
|
+
model,
|
|
275
|
+
baseUrl: "https://api.together.xyz/v1",
|
|
276
|
+
temperature: 0.2,
|
|
277
|
+
maxTokens: 8192
|
|
278
|
+
})
|
|
279
|
+
},
|
|
280
|
+
{
|
|
281
|
+
name: "huggingface",
|
|
282
|
+
provider: new OpenAILikeProvider("huggingface", "https://api-inference.huggingface.co/v1"),
|
|
283
|
+
models: [
|
|
284
|
+
"HuggingFaceH4/zephyr-7b-beta",
|
|
285
|
+
"mistralai/Mistral-7B-Instruct-v0.3",
|
|
286
|
+
"meta-llama/Meta-Llama-3-8B-Instruct",
|
|
287
|
+
"google/gemma-2-9b-it"
|
|
288
|
+
],
|
|
289
|
+
local: false,
|
|
290
|
+
priority: 60,
|
|
291
|
+
defaultModel: "HuggingFaceH4/zephyr-7b-beta",
|
|
292
|
+
getConfig: (model) => ({
|
|
293
|
+
model,
|
|
294
|
+
baseUrl: "https://api-inference.huggingface.co/v1",
|
|
295
|
+
temperature: 0.2,
|
|
296
|
+
maxTokens: 4096
|
|
297
|
+
})
|
|
298
|
+
}
|
|
299
|
+
];
|
|
300
|
+
function getProvider(name) {
|
|
301
|
+
return registry.find((p) => p.name === name);
|
|
302
|
+
}
|
|
303
|
+
function getProviderForModel(model) {
|
|
304
|
+
return registry.find((p) => p.models.includes(model) || p.defaultModel === model);
|
|
305
|
+
}
|
|
306
|
+
function getAllProviders() {
|
|
307
|
+
return [...registry];
|
|
308
|
+
}
|
|
309
|
+
function getLocalProviders() {
|
|
310
|
+
return registry.filter((p) => p.local);
|
|
311
|
+
}
|
|
312
|
+
function getRemoteProviders() {
|
|
313
|
+
return registry.filter((p) => !p.local);
|
|
314
|
+
}
|
|
315
|
+
function resolveModel(modelOrProvider) {
|
|
316
|
+
const byName = getProvider(modelOrProvider);
|
|
317
|
+
if (byName) {
|
|
318
|
+
return { entry: byName, model: byName.defaultModel };
|
|
319
|
+
}
|
|
320
|
+
const byModel = getProviderForModel(modelOrProvider);
|
|
321
|
+
if (byModel) {
|
|
322
|
+
return { entry: byModel, model: modelOrProvider };
|
|
323
|
+
}
|
|
324
|
+
const local = getLocalProviders()[0];
|
|
325
|
+
return { entry: local, model: modelOrProvider };
|
|
326
|
+
}
|
|
327
|
+
function printProviders() {
|
|
328
|
+
const lines = [chalk2.bold("\n Available Providers")];
|
|
329
|
+
for (const entry of registry) {
|
|
330
|
+
const tag = entry.local ? chalk2.green(" LOCAL ") : chalk2.blue(" CLOUD ");
|
|
331
|
+
const defaultModel = chalk2.dim(`(default: ${entry.defaultModel})`);
|
|
332
|
+
const models = entry.models.slice(0, 4).join(", ");
|
|
333
|
+
const more = entry.models.length > 4 ? chalk2.dim(` +${entry.models.length - 4} more`) : "";
|
|
334
|
+
lines.push(`
|
|
335
|
+
${tag} ${chalk2.cyan(entry.name.padEnd(12))} ${defaultModel}`);
|
|
336
|
+
lines.push(` ${chalk2.dim(models)}${more}`);
|
|
337
|
+
}
|
|
338
|
+
lines.push("");
|
|
339
|
+
return lines.join("\n");
|
|
340
|
+
}
|
|
341
|
+
function setDefaultModel(model) {
|
|
342
|
+
const resolved = resolveModel(model);
|
|
343
|
+
resolved.entry.defaultModel = resolved.model;
|
|
344
|
+
return { provider: resolved.entry.name, model: resolved.model };
|
|
345
|
+
}
|
|
346
|
+
function getDefaultModel() {
|
|
347
|
+
const highest = [...registry].sort((a, b) => {
|
|
348
|
+
if (a.local && !b.local) return -1;
|
|
349
|
+
if (!a.local && b.local) return 1;
|
|
350
|
+
return a.priority - b.priority;
|
|
351
|
+
})[0];
|
|
352
|
+
return { provider: highest.name, model: highest.defaultModel };
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
export {
|
|
356
|
+
OllamaProvider,
|
|
357
|
+
getProvider,
|
|
358
|
+
getProviderForModel,
|
|
359
|
+
getAllProviders,
|
|
360
|
+
getLocalProviders,
|
|
361
|
+
getRemoteProviders,
|
|
362
|
+
resolveModel,
|
|
363
|
+
printProviders,
|
|
364
|
+
setDefaultModel,
|
|
365
|
+
getDefaultModel
|
|
366
|
+
};
|