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