@lupaflow/providers 0.1.0 → 0.1.2
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/index.cjs +657 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +73 -0
- package/dist/index.d.ts +73 -0
- package/dist/index.js +617 -0
- package/dist/index.js.map +1 -0
- package/package.json +6 -5
- package/tsconfig.json +0 -8
- package/tsup.config.ts +0 -9
package/dist/index.js
ADDED
|
@@ -0,0 +1,617 @@
|
|
|
1
|
+
// src/registry.ts
|
|
2
|
+
import { ConfigError } from "@lupaflow/core";
|
|
3
|
+
var ProviderRegistry = class {
|
|
4
|
+
providers = /* @__PURE__ */ new Map();
|
|
5
|
+
instances = /* @__PURE__ */ new Map();
|
|
6
|
+
register(name, ctor) {
|
|
7
|
+
this.providers.set(name, ctor);
|
|
8
|
+
}
|
|
9
|
+
get(name, config) {
|
|
10
|
+
if (!config && this.instances.has(name)) {
|
|
11
|
+
return this.instances.get(name);
|
|
12
|
+
}
|
|
13
|
+
const ctor = this.providers.get(name);
|
|
14
|
+
if (!ctor) {
|
|
15
|
+
throw new ConfigError(`Unknown provider: "${name}"`, {
|
|
16
|
+
available: Array.from(this.providers.keys())
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
const instance = new ctor(config || {});
|
|
20
|
+
if (!config) {
|
|
21
|
+
this.instances.set(name, instance);
|
|
22
|
+
}
|
|
23
|
+
return instance;
|
|
24
|
+
}
|
|
25
|
+
has(name) {
|
|
26
|
+
return this.providers.has(name);
|
|
27
|
+
}
|
|
28
|
+
list() {
|
|
29
|
+
return Array.from(this.providers.keys());
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
var providerRegistry = new ProviderRegistry();
|
|
33
|
+
|
|
34
|
+
// src/openrouter.ts
|
|
35
|
+
import OpenAI from "openai";
|
|
36
|
+
import { ProviderError } from "@lupaflow/core";
|
|
37
|
+
import { getOpenRouterKey } from "@lupaflow/secrets";
|
|
38
|
+
import { globalEventBus } from "@lupaflow/core";
|
|
39
|
+
var OpenRouterProvider = class {
|
|
40
|
+
name = "openrouter";
|
|
41
|
+
defaultModel = "openai/gpt-4o-mini";
|
|
42
|
+
config;
|
|
43
|
+
client;
|
|
44
|
+
constructor(config = {}) {
|
|
45
|
+
this.config = config;
|
|
46
|
+
this.client = new OpenAI({
|
|
47
|
+
apiKey: config.apiKey || getOpenRouterKey(),
|
|
48
|
+
baseURL: config.baseUrl || "https://openrouter.ai/api/v1",
|
|
49
|
+
defaultHeaders: {
|
|
50
|
+
"HTTP-Referer": "https://lupaflow.dev",
|
|
51
|
+
"X-Title": "LupaFlow"
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
validateConfig() {
|
|
56
|
+
try {
|
|
57
|
+
getOpenRouterKey();
|
|
58
|
+
return true;
|
|
59
|
+
} catch {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
buildMessages(request) {
|
|
64
|
+
const messages = [];
|
|
65
|
+
if (request.systemPrompt) {
|
|
66
|
+
messages.push({ role: "system", content: request.systemPrompt });
|
|
67
|
+
}
|
|
68
|
+
for (const m of request.messages) {
|
|
69
|
+
if (m.role === "assistant" && m.toolCalls?.length) {
|
|
70
|
+
messages.push({
|
|
71
|
+
role: "assistant",
|
|
72
|
+
content: m.content,
|
|
73
|
+
tool_calls: m.toolCalls
|
|
74
|
+
});
|
|
75
|
+
} else if (m.role === "tool") {
|
|
76
|
+
messages.push({
|
|
77
|
+
role: "tool",
|
|
78
|
+
tool_call_id: m.toolCallId || "",
|
|
79
|
+
content: m.content || ""
|
|
80
|
+
});
|
|
81
|
+
} else {
|
|
82
|
+
messages.push({ role: m.role, content: m.content });
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return messages;
|
|
86
|
+
}
|
|
87
|
+
buildTools(tools) {
|
|
88
|
+
return tools?.map((t) => ({
|
|
89
|
+
type: "function",
|
|
90
|
+
function: {
|
|
91
|
+
name: t.definition.name,
|
|
92
|
+
description: t.definition.description,
|
|
93
|
+
parameters: {
|
|
94
|
+
type: "object",
|
|
95
|
+
properties: t.definition.parameters,
|
|
96
|
+
required: t.definition.required
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}));
|
|
100
|
+
}
|
|
101
|
+
async complete(request) {
|
|
102
|
+
const start = Date.now();
|
|
103
|
+
const model = request.model || this.config.model || this.defaultModel;
|
|
104
|
+
await globalEventBus.emit("provider:start", {
|
|
105
|
+
provider: this.name,
|
|
106
|
+
model
|
|
107
|
+
});
|
|
108
|
+
try {
|
|
109
|
+
const messages = this.buildMessages(request);
|
|
110
|
+
const tools = this.buildTools(request.tools);
|
|
111
|
+
const response = await this.client.chat.completions.create({
|
|
112
|
+
model,
|
|
113
|
+
messages,
|
|
114
|
+
tools,
|
|
115
|
+
temperature: request.temperature ?? this.config.temperature,
|
|
116
|
+
max_tokens: request.maxTokens ?? this.config.maxTokens,
|
|
117
|
+
top_p: request.topP ?? this.config.topP
|
|
118
|
+
});
|
|
119
|
+
const choice = response.choices[0];
|
|
120
|
+
const latencyMs = Date.now() - start;
|
|
121
|
+
const result = {
|
|
122
|
+
content: choice.message.content,
|
|
123
|
+
toolCalls: (choice.message.tool_calls || []).map((tc) => ({
|
|
124
|
+
id: tc.id,
|
|
125
|
+
name: tc.function.name,
|
|
126
|
+
args: JSON.parse(tc.function.arguments)
|
|
127
|
+
})),
|
|
128
|
+
usage: {
|
|
129
|
+
promptTokens: response.usage?.prompt_tokens || 0,
|
|
130
|
+
completionTokens: response.usage?.completion_tokens || 0,
|
|
131
|
+
totalTokens: response.usage?.total_tokens || 0
|
|
132
|
+
},
|
|
133
|
+
model: response.model,
|
|
134
|
+
latencyMs,
|
|
135
|
+
finishReason: choice.finish_reason || "stop"
|
|
136
|
+
};
|
|
137
|
+
await globalEventBus.emit("provider:complete", {
|
|
138
|
+
provider: this.name,
|
|
139
|
+
model,
|
|
140
|
+
tokens: result.usage.totalTokens,
|
|
141
|
+
latencyMs
|
|
142
|
+
});
|
|
143
|
+
return result;
|
|
144
|
+
} catch (err) {
|
|
145
|
+
const latencyMs = Date.now() - start;
|
|
146
|
+
await globalEventBus.emit("provider:error", {
|
|
147
|
+
provider: this.name,
|
|
148
|
+
model,
|
|
149
|
+
error: err.message
|
|
150
|
+
});
|
|
151
|
+
throw new ProviderError(this.name, err.message, { model, latencyMs });
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
async *streamComplete(request) {
|
|
155
|
+
const start = Date.now();
|
|
156
|
+
const model = request.model || this.config.model || this.defaultModel;
|
|
157
|
+
await globalEventBus.emit("provider:start", {
|
|
158
|
+
provider: this.name,
|
|
159
|
+
model
|
|
160
|
+
});
|
|
161
|
+
try {
|
|
162
|
+
const messages = this.buildMessages(request);
|
|
163
|
+
const tools = this.buildTools(request.tools);
|
|
164
|
+
const stream = await this.client.chat.completions.create({
|
|
165
|
+
model,
|
|
166
|
+
messages,
|
|
167
|
+
tools,
|
|
168
|
+
temperature: request.temperature ?? this.config.temperature,
|
|
169
|
+
max_tokens: request.maxTokens ?? this.config.maxTokens,
|
|
170
|
+
top_p: request.topP ?? this.config.topP,
|
|
171
|
+
stream: true
|
|
172
|
+
});
|
|
173
|
+
let fullContent = "";
|
|
174
|
+
const accToolCalls = /* @__PURE__ */ new Map();
|
|
175
|
+
for await (const chunk of stream) {
|
|
176
|
+
const delta = chunk.choices[0]?.delta;
|
|
177
|
+
if (delta?.content) {
|
|
178
|
+
fullContent += delta.content;
|
|
179
|
+
yield { type: "text", text: delta.content };
|
|
180
|
+
}
|
|
181
|
+
if (delta?.tool_calls) {
|
|
182
|
+
for (const tc of delta.tool_calls) {
|
|
183
|
+
if (!accToolCalls.has(tc.index)) {
|
|
184
|
+
accToolCalls.set(tc.index, { id: "", name: "", args: "" });
|
|
185
|
+
}
|
|
186
|
+
const entry = accToolCalls.get(tc.index);
|
|
187
|
+
if (tc.id) entry.id = tc.id;
|
|
188
|
+
if (tc.function?.name) entry.name = tc.function.name;
|
|
189
|
+
if (tc.function?.arguments) entry.args += tc.function.arguments;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
if (chunk.choices[0]?.finish_reason) {
|
|
193
|
+
const finishReason = chunk.choices[0].finish_reason;
|
|
194
|
+
const toolCalls = Array.from(accToolCalls.values()).filter((tc) => tc.id && tc.name).map((tc) => ({
|
|
195
|
+
id: tc.id,
|
|
196
|
+
name: tc.name,
|
|
197
|
+
args: JSON.parse(tc.args || "{}")
|
|
198
|
+
}));
|
|
199
|
+
yield {
|
|
200
|
+
type: "finish",
|
|
201
|
+
content: fullContent,
|
|
202
|
+
toolCalls,
|
|
203
|
+
usage: {
|
|
204
|
+
promptTokens: chunk.usage?.prompt_tokens || 0,
|
|
205
|
+
completionTokens: chunk.usage?.completion_tokens || 0,
|
|
206
|
+
totalTokens: chunk.usage?.total_tokens || 0
|
|
207
|
+
},
|
|
208
|
+
model,
|
|
209
|
+
finishReason
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
await globalEventBus.emit("provider:complete", {
|
|
214
|
+
provider: this.name,
|
|
215
|
+
model,
|
|
216
|
+
tokens: 0,
|
|
217
|
+
latencyMs: Date.now() - start
|
|
218
|
+
});
|
|
219
|
+
} catch (err) {
|
|
220
|
+
await globalEventBus.emit("provider:error", {
|
|
221
|
+
provider: this.name,
|
|
222
|
+
model,
|
|
223
|
+
error: err.message
|
|
224
|
+
});
|
|
225
|
+
throw new ProviderError(this.name, err.message, { model, latencyMs: Date.now() - start });
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
async getModels() {
|
|
229
|
+
try {
|
|
230
|
+
const response = await fetch("https://openrouter.ai/api/v1/models");
|
|
231
|
+
const data = await response.json();
|
|
232
|
+
return data.data?.map((m) => m.id) || [];
|
|
233
|
+
} catch {
|
|
234
|
+
return [this.defaultModel];
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
// src/google.ts
|
|
240
|
+
import { GoogleGenerativeAI } from "@google/generative-ai";
|
|
241
|
+
import { ProviderError as ProviderError2 } from "@lupaflow/core";
|
|
242
|
+
import { getGoogleKey } from "@lupaflow/secrets";
|
|
243
|
+
import { globalEventBus as globalEventBus2 } from "@lupaflow/core";
|
|
244
|
+
var GoogleProvider = class {
|
|
245
|
+
name = "google";
|
|
246
|
+
defaultModel = "gemini-1.5-flash";
|
|
247
|
+
config;
|
|
248
|
+
client;
|
|
249
|
+
constructor(config = {}) {
|
|
250
|
+
this.config = config;
|
|
251
|
+
this.client = new GoogleGenerativeAI(config.apiKey || getGoogleKey());
|
|
252
|
+
}
|
|
253
|
+
validateConfig() {
|
|
254
|
+
try {
|
|
255
|
+
getGoogleKey();
|
|
256
|
+
return true;
|
|
257
|
+
} catch {
|
|
258
|
+
return false;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
buildContents(request) {
|
|
262
|
+
const contents = [];
|
|
263
|
+
for (const msg of request.messages) {
|
|
264
|
+
if (msg.role === "system") continue;
|
|
265
|
+
const role = msg.role === "assistant" ? "model" : "user";
|
|
266
|
+
contents.push({
|
|
267
|
+
role,
|
|
268
|
+
parts: [{ text: msg.content || "" }]
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
return contents;
|
|
272
|
+
}
|
|
273
|
+
async complete(request) {
|
|
274
|
+
const start = Date.now();
|
|
275
|
+
const model = request.model || this.config.model || this.defaultModel;
|
|
276
|
+
await globalEventBus2.emit("provider:start", {
|
|
277
|
+
provider: this.name,
|
|
278
|
+
model
|
|
279
|
+
});
|
|
280
|
+
try {
|
|
281
|
+
const genModel = this.client.getGenerativeModel({ model });
|
|
282
|
+
const systemInstruction = request.systemPrompt;
|
|
283
|
+
const contents = this.buildContents(request);
|
|
284
|
+
const requestOptions = {
|
|
285
|
+
contents,
|
|
286
|
+
generationConfig: {
|
|
287
|
+
temperature: request.temperature ?? this.config.temperature,
|
|
288
|
+
maxOutputTokens: request.maxTokens ?? this.config.maxTokens,
|
|
289
|
+
topP: request.topP ?? this.config.topP
|
|
290
|
+
}
|
|
291
|
+
};
|
|
292
|
+
if (systemInstruction) {
|
|
293
|
+
requestOptions.systemInstruction = {
|
|
294
|
+
role: "user",
|
|
295
|
+
parts: [{ text: systemInstruction }]
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
const result = await genModel.generateContent(requestOptions);
|
|
299
|
+
const latencyMs = Date.now() - start;
|
|
300
|
+
const response = result.response;
|
|
301
|
+
const text = response.text();
|
|
302
|
+
const usage = response.usageMetadata || {};
|
|
303
|
+
const totalTokens = (usage.promptTokenCount || 0) + (usage.candidatesTokenCount || 0);
|
|
304
|
+
const completionResponse = {
|
|
305
|
+
content: text,
|
|
306
|
+
toolCalls: [],
|
|
307
|
+
usage: {
|
|
308
|
+
promptTokens: usage.promptTokenCount || 0,
|
|
309
|
+
completionTokens: usage.candidatesTokenCount || 0,
|
|
310
|
+
totalTokens
|
|
311
|
+
},
|
|
312
|
+
model,
|
|
313
|
+
latencyMs,
|
|
314
|
+
finishReason: "stop"
|
|
315
|
+
};
|
|
316
|
+
await globalEventBus2.emit("provider:complete", {
|
|
317
|
+
provider: this.name,
|
|
318
|
+
model,
|
|
319
|
+
tokens: totalTokens,
|
|
320
|
+
latencyMs
|
|
321
|
+
});
|
|
322
|
+
return completionResponse;
|
|
323
|
+
} catch (err) {
|
|
324
|
+
const latencyMs = Date.now() - start;
|
|
325
|
+
await globalEventBus2.emit("provider:error", {
|
|
326
|
+
provider: this.name,
|
|
327
|
+
model,
|
|
328
|
+
error: err.message
|
|
329
|
+
});
|
|
330
|
+
throw new ProviderError2(this.name, err.message, { model, latencyMs });
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
async *streamComplete(request) {
|
|
334
|
+
const start = Date.now();
|
|
335
|
+
const model = request.model || this.config.model || this.defaultModel;
|
|
336
|
+
await globalEventBus2.emit("provider:start", {
|
|
337
|
+
provider: this.name,
|
|
338
|
+
model
|
|
339
|
+
});
|
|
340
|
+
try {
|
|
341
|
+
const genModel = this.client.getGenerativeModel({ model });
|
|
342
|
+
const systemInstruction = request.systemPrompt;
|
|
343
|
+
const contents = this.buildContents(request);
|
|
344
|
+
const requestOptions = {
|
|
345
|
+
contents,
|
|
346
|
+
generationConfig: {
|
|
347
|
+
temperature: request.temperature ?? this.config.temperature,
|
|
348
|
+
maxOutputTokens: request.maxTokens ?? this.config.maxTokens,
|
|
349
|
+
topP: request.topP ?? this.config.topP
|
|
350
|
+
}
|
|
351
|
+
};
|
|
352
|
+
if (systemInstruction) {
|
|
353
|
+
requestOptions.systemInstruction = {
|
|
354
|
+
role: "user",
|
|
355
|
+
parts: [{ text: systemInstruction }]
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
const result = await genModel.generateContentStream(requestOptions);
|
|
359
|
+
let fullContent = "";
|
|
360
|
+
for await (const chunk of result.stream) {
|
|
361
|
+
const text = chunk.text();
|
|
362
|
+
if (text) {
|
|
363
|
+
fullContent += text;
|
|
364
|
+
yield { type: "text", text };
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
const response = await result.response;
|
|
368
|
+
const usage = response.usageMetadata || {};
|
|
369
|
+
const totalTokens = (usage.promptTokenCount || 0) + (usage.candidatesTokenCount || 0);
|
|
370
|
+
yield {
|
|
371
|
+
type: "finish",
|
|
372
|
+
content: fullContent,
|
|
373
|
+
toolCalls: [],
|
|
374
|
+
usage: {
|
|
375
|
+
promptTokens: usage.promptTokenCount || 0,
|
|
376
|
+
completionTokens: usage.candidatesTokenCount || 0,
|
|
377
|
+
totalTokens
|
|
378
|
+
},
|
|
379
|
+
model,
|
|
380
|
+
finishReason: "stop"
|
|
381
|
+
};
|
|
382
|
+
await globalEventBus2.emit("provider:complete", {
|
|
383
|
+
provider: this.name,
|
|
384
|
+
model,
|
|
385
|
+
tokens: totalTokens,
|
|
386
|
+
latencyMs: Date.now() - start
|
|
387
|
+
});
|
|
388
|
+
} catch (err) {
|
|
389
|
+
await globalEventBus2.emit("provider:error", {
|
|
390
|
+
provider: this.name,
|
|
391
|
+
model,
|
|
392
|
+
error: err.message
|
|
393
|
+
});
|
|
394
|
+
throw new ProviderError2(this.name, err.message, { model, latencyMs: Date.now() - start });
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
async getModels() {
|
|
398
|
+
return ["gemini-1.5-flash", "gemini-1.5-pro", "gemini-2.0-flash-exp"];
|
|
399
|
+
}
|
|
400
|
+
};
|
|
401
|
+
|
|
402
|
+
// src/groq.ts
|
|
403
|
+
import OpenAI2 from "openai";
|
|
404
|
+
import { ProviderError as ProviderError3 } from "@lupaflow/core";
|
|
405
|
+
import { getGroqKey } from "@lupaflow/secrets";
|
|
406
|
+
import { globalEventBus as globalEventBus3 } from "@lupaflow/core";
|
|
407
|
+
var GroqProvider = class {
|
|
408
|
+
name = "groq";
|
|
409
|
+
defaultModel = "llama-3.3-70b-versatile";
|
|
410
|
+
config;
|
|
411
|
+
client;
|
|
412
|
+
constructor(config = {}) {
|
|
413
|
+
this.config = config;
|
|
414
|
+
this.client = new OpenAI2({
|
|
415
|
+
apiKey: config.apiKey || getGroqKey(),
|
|
416
|
+
baseURL: config.baseUrl || "https://api.groq.com/openai/v1"
|
|
417
|
+
});
|
|
418
|
+
}
|
|
419
|
+
validateConfig() {
|
|
420
|
+
try {
|
|
421
|
+
getGroqKey();
|
|
422
|
+
return true;
|
|
423
|
+
} catch {
|
|
424
|
+
return false;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
buildMessages(request) {
|
|
428
|
+
const messages = [];
|
|
429
|
+
if (request.systemPrompt) {
|
|
430
|
+
messages.push({ role: "system", content: request.systemPrompt });
|
|
431
|
+
}
|
|
432
|
+
for (const m of request.messages) {
|
|
433
|
+
if (m.role === "assistant" && m.toolCalls?.length) {
|
|
434
|
+
messages.push({
|
|
435
|
+
role: "assistant",
|
|
436
|
+
content: m.content,
|
|
437
|
+
tool_calls: m.toolCalls
|
|
438
|
+
});
|
|
439
|
+
} else if (m.role === "tool") {
|
|
440
|
+
messages.push({
|
|
441
|
+
role: "tool",
|
|
442
|
+
tool_call_id: m.toolCallId || "",
|
|
443
|
+
content: m.content || ""
|
|
444
|
+
});
|
|
445
|
+
} else {
|
|
446
|
+
messages.push({ role: m.role, content: m.content });
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
return messages;
|
|
450
|
+
}
|
|
451
|
+
buildTools(tools) {
|
|
452
|
+
return tools?.map((t) => ({
|
|
453
|
+
type: "function",
|
|
454
|
+
function: {
|
|
455
|
+
name: t.definition.name,
|
|
456
|
+
description: t.definition.description,
|
|
457
|
+
parameters: {
|
|
458
|
+
type: "object",
|
|
459
|
+
properties: t.definition.parameters,
|
|
460
|
+
required: t.definition.required
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
}));
|
|
464
|
+
}
|
|
465
|
+
async complete(request) {
|
|
466
|
+
const start = Date.now();
|
|
467
|
+
const model = request.model || this.config.model || this.defaultModel;
|
|
468
|
+
await globalEventBus3.emit("provider:start", {
|
|
469
|
+
provider: this.name,
|
|
470
|
+
model
|
|
471
|
+
});
|
|
472
|
+
try {
|
|
473
|
+
const messages = this.buildMessages(request);
|
|
474
|
+
const tools = this.buildTools(request.tools);
|
|
475
|
+
const response = await this.client.chat.completions.create({
|
|
476
|
+
model,
|
|
477
|
+
messages,
|
|
478
|
+
tools,
|
|
479
|
+
temperature: request.temperature ?? this.config.temperature,
|
|
480
|
+
max_tokens: request.maxTokens ?? this.config.maxTokens,
|
|
481
|
+
top_p: request.topP ?? this.config.topP
|
|
482
|
+
});
|
|
483
|
+
const choice = response.choices[0];
|
|
484
|
+
const latencyMs = Date.now() - start;
|
|
485
|
+
const result = {
|
|
486
|
+
content: choice.message.content,
|
|
487
|
+
toolCalls: (choice.message.tool_calls || []).map((tc) => ({
|
|
488
|
+
id: tc.id,
|
|
489
|
+
name: tc.function.name,
|
|
490
|
+
args: JSON.parse(tc.function.arguments)
|
|
491
|
+
})),
|
|
492
|
+
usage: {
|
|
493
|
+
promptTokens: response.usage?.prompt_tokens || 0,
|
|
494
|
+
completionTokens: response.usage?.completion_tokens || 0,
|
|
495
|
+
totalTokens: response.usage?.total_tokens || 0
|
|
496
|
+
},
|
|
497
|
+
model: response.model,
|
|
498
|
+
latencyMs,
|
|
499
|
+
finishReason: choice.finish_reason || "stop"
|
|
500
|
+
};
|
|
501
|
+
await globalEventBus3.emit("provider:complete", {
|
|
502
|
+
provider: this.name,
|
|
503
|
+
model,
|
|
504
|
+
tokens: result.usage.totalTokens,
|
|
505
|
+
latencyMs
|
|
506
|
+
});
|
|
507
|
+
return result;
|
|
508
|
+
} catch (err) {
|
|
509
|
+
const latencyMs = Date.now() - start;
|
|
510
|
+
await globalEventBus3.emit("provider:error", {
|
|
511
|
+
provider: this.name,
|
|
512
|
+
model,
|
|
513
|
+
error: err.message
|
|
514
|
+
});
|
|
515
|
+
throw new ProviderError3(this.name, err.message, { model, latencyMs });
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
async *streamComplete(request) {
|
|
519
|
+
const start = Date.now();
|
|
520
|
+
const model = request.model || this.config.model || this.defaultModel;
|
|
521
|
+
await globalEventBus3.emit("provider:start", {
|
|
522
|
+
provider: this.name,
|
|
523
|
+
model
|
|
524
|
+
});
|
|
525
|
+
try {
|
|
526
|
+
const messages = this.buildMessages(request);
|
|
527
|
+
const tools = this.buildTools(request.tools);
|
|
528
|
+
const stream = await this.client.chat.completions.create({
|
|
529
|
+
model,
|
|
530
|
+
messages,
|
|
531
|
+
tools,
|
|
532
|
+
temperature: request.temperature ?? this.config.temperature,
|
|
533
|
+
max_tokens: request.maxTokens ?? this.config.maxTokens,
|
|
534
|
+
top_p: request.topP ?? this.config.topP,
|
|
535
|
+
stream: true
|
|
536
|
+
});
|
|
537
|
+
let fullContent = "";
|
|
538
|
+
const accToolCalls = /* @__PURE__ */ new Map();
|
|
539
|
+
for await (const chunk of stream) {
|
|
540
|
+
const delta = chunk.choices[0]?.delta;
|
|
541
|
+
if (delta?.content) {
|
|
542
|
+
fullContent += delta.content;
|
|
543
|
+
yield { type: "text", text: delta.content };
|
|
544
|
+
}
|
|
545
|
+
if (delta?.tool_calls) {
|
|
546
|
+
for (const tc of delta.tool_calls) {
|
|
547
|
+
if (!accToolCalls.has(tc.index)) {
|
|
548
|
+
accToolCalls.set(tc.index, { id: "", name: "", args: "" });
|
|
549
|
+
}
|
|
550
|
+
const entry = accToolCalls.get(tc.index);
|
|
551
|
+
if (tc.id) entry.id = tc.id;
|
|
552
|
+
if (tc.function?.name) entry.name = tc.function.name;
|
|
553
|
+
if (tc.function?.arguments) entry.args += tc.function.arguments;
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
if (chunk.choices[0]?.finish_reason) {
|
|
557
|
+
const finishReason = chunk.choices[0].finish_reason;
|
|
558
|
+
const toolCalls = Array.from(accToolCalls.values()).filter((tc) => tc.id && tc.name).map((tc) => ({
|
|
559
|
+
id: tc.id,
|
|
560
|
+
name: tc.name,
|
|
561
|
+
args: JSON.parse(tc.args || "{}")
|
|
562
|
+
}));
|
|
563
|
+
yield {
|
|
564
|
+
type: "finish",
|
|
565
|
+
content: fullContent,
|
|
566
|
+
toolCalls,
|
|
567
|
+
usage: {
|
|
568
|
+
promptTokens: chunk.usage?.prompt_tokens || 0,
|
|
569
|
+
completionTokens: chunk.usage?.completion_tokens || 0,
|
|
570
|
+
totalTokens: chunk.usage?.total_tokens || 0
|
|
571
|
+
},
|
|
572
|
+
model,
|
|
573
|
+
finishReason
|
|
574
|
+
};
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
await globalEventBus3.emit("provider:complete", {
|
|
578
|
+
provider: this.name,
|
|
579
|
+
model,
|
|
580
|
+
tokens: 0,
|
|
581
|
+
latencyMs: Date.now() - start
|
|
582
|
+
});
|
|
583
|
+
} catch (err) {
|
|
584
|
+
await globalEventBus3.emit("provider:error", {
|
|
585
|
+
provider: this.name,
|
|
586
|
+
model,
|
|
587
|
+
error: err.message
|
|
588
|
+
});
|
|
589
|
+
throw new ProviderError3(this.name, err.message, { model, latencyMs: Date.now() - start });
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
async getModels() {
|
|
593
|
+
try {
|
|
594
|
+
const response = await fetch("https://api.groq.com/openai/v1/models", {
|
|
595
|
+
headers: {
|
|
596
|
+
Authorization: `Bearer ${getGroqKey()}`
|
|
597
|
+
}
|
|
598
|
+
});
|
|
599
|
+
const data = await response.json();
|
|
600
|
+
return data.data?.map((m) => m.id) || [];
|
|
601
|
+
} catch {
|
|
602
|
+
return [this.defaultModel];
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
};
|
|
606
|
+
|
|
607
|
+
// src/index.ts
|
|
608
|
+
providerRegistry.register("openrouter", OpenRouterProvider);
|
|
609
|
+
providerRegistry.register("google", GoogleProvider);
|
|
610
|
+
providerRegistry.register("groq", GroqProvider);
|
|
611
|
+
export {
|
|
612
|
+
GoogleProvider,
|
|
613
|
+
GroqProvider,
|
|
614
|
+
OpenRouterProvider,
|
|
615
|
+
providerRegistry
|
|
616
|
+
};
|
|
617
|
+
//# sourceMappingURL=index.js.map
|