@prompd/cli 0.4.9 → 0.4.11

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.
Files changed (60) hide show
  1. package/dist/commands/ask.d.ts +3 -0
  2. package/dist/commands/ask.d.ts.map +1 -0
  3. package/dist/commands/ask.js +121 -0
  4. package/dist/commands/ask.js.map +1 -0
  5. package/dist/commands/mcp.d.ts.map +1 -1
  6. package/dist/commands/mcp.js +192 -42
  7. package/dist/commands/mcp.js.map +1 -1
  8. package/dist/commands/run.d.ts.map +1 -1
  9. package/dist/commands/run.js +62 -4
  10. package/dist/commands/run.js.map +1 -1
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +2 -0
  13. package/dist/index.js.map +1 -1
  14. package/dist/lib/compiler/package-resolver.d.ts.map +1 -1
  15. package/dist/lib/compiler/package-resolver.js +5 -1
  16. package/dist/lib/compiler/package-resolver.js.map +1 -1
  17. package/dist/lib/executor.d.ts +16 -6
  18. package/dist/lib/executor.d.ts.map +1 -1
  19. package/dist/lib/executor.js +121 -245
  20. package/dist/lib/executor.js.map +1 -1
  21. package/dist/lib/index.d.ts +4 -2
  22. package/dist/lib/index.d.ts.map +1 -1
  23. package/dist/lib/index.js +21 -4
  24. package/dist/lib/index.js.map +1 -1
  25. package/dist/lib/mcp.d.ts +13 -2
  26. package/dist/lib/mcp.d.ts.map +1 -1
  27. package/dist/lib/mcp.js +447 -77
  28. package/dist/lib/mcp.js.map +1 -1
  29. package/dist/lib/nodeTypeRegistry.d.ts.map +1 -1
  30. package/dist/lib/nodeTypeRegistry.js +5 -1
  31. package/dist/lib/nodeTypeRegistry.js.map +1 -1
  32. package/dist/lib/providers/base.d.ts +92 -0
  33. package/dist/lib/providers/base.d.ts.map +1 -0
  34. package/dist/lib/providers/base.js +741 -0
  35. package/dist/lib/providers/base.js.map +1 -0
  36. package/dist/lib/providers/factory.d.ts +37 -0
  37. package/dist/lib/providers/factory.d.ts.map +1 -0
  38. package/dist/lib/providers/factory.js +82 -0
  39. package/dist/lib/providers/factory.js.map +1 -0
  40. package/dist/lib/providers/index.d.ts +12 -0
  41. package/dist/lib/providers/index.d.ts.map +1 -0
  42. package/dist/lib/providers/index.js +25 -0
  43. package/dist/lib/providers/index.js.map +1 -0
  44. package/dist/lib/providers/types.d.ts +129 -0
  45. package/dist/lib/providers/types.d.ts.map +1 -0
  46. package/dist/lib/providers/types.js +156 -0
  47. package/dist/lib/providers/types.js.map +1 -0
  48. package/dist/lib/workflowExecutor.d.ts +12 -1
  49. package/dist/lib/workflowExecutor.d.ts.map +1 -1
  50. package/dist/lib/workflowExecutor.js +771 -304
  51. package/dist/lib/workflowExecutor.js.map +1 -1
  52. package/dist/lib/workflowParser.d.ts.map +1 -1
  53. package/dist/lib/workflowParser.js +2 -0
  54. package/dist/lib/workflowParser.js.map +1 -1
  55. package/dist/lib/workflowTypes.d.ts +19 -1
  56. package/dist/lib/workflowTypes.d.ts.map +1 -1
  57. package/dist/lib/workflowTypes.js.map +1 -1
  58. package/dist/types/index.d.ts +3 -0
  59. package/dist/types/index.d.ts.map +1 -1
  60. package/package.json +6 -1
@@ -0,0 +1,741 @@
1
+ "use strict";
2
+ /**
3
+ * Base Provider Classes
4
+ *
5
+ * Abstract base class and concrete provider implementations for all supported LLM APIs.
6
+ * Uses axios for HTTP transport (Node.js environment).
7
+ * The frontend mirrors this structure but uses electronFetch for CORS bypass.
8
+ */
9
+ var __importDefault = (this && this.__importDefault) || function (mod) {
10
+ return (mod && mod.__esModule) ? mod : { "default": mod };
11
+ };
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.CohereProvider = exports.GoogleGeminiProvider = exports.AnthropicProvider = exports.OpenAICompatibleProvider = exports.BaseProvider = void 0;
14
+ const axios_1 = __importDefault(require("axios"));
15
+ /**
16
+ * Abstract base class for LLM providers
17
+ */
18
+ class BaseProvider {
19
+ constructor(config) {
20
+ this.config = config;
21
+ }
22
+ /**
23
+ * List available models from config
24
+ */
25
+ listModels() {
26
+ return this.config.models || [];
27
+ }
28
+ /**
29
+ * Helper to create a standard error result
30
+ */
31
+ createErrorResult(error, duration) {
32
+ return {
33
+ success: false,
34
+ error,
35
+ usage: { promptTokens: 0, completionTokens: 0, totalTokens: 0 },
36
+ duration
37
+ };
38
+ }
39
+ /**
40
+ * Helper to create a success result
41
+ */
42
+ createSuccessResult(response, usage, duration) {
43
+ return {
44
+ success: true,
45
+ response,
46
+ usage,
47
+ duration
48
+ };
49
+ }
50
+ /**
51
+ * Extract error message from an axios error response
52
+ */
53
+ extractAxiosError(error) {
54
+ if (axios_1.default.isAxiosError(error)) {
55
+ const axErr = error;
56
+ const data = axErr.response?.data;
57
+ if (data && typeof data === 'object') {
58
+ const errObj = data.error;
59
+ if (errObj && typeof errObj.message === 'string') {
60
+ return errObj.message;
61
+ }
62
+ if (typeof data.message === 'string') {
63
+ return data.message;
64
+ }
65
+ }
66
+ return axErr.message;
67
+ }
68
+ if (error instanceof Error) {
69
+ return error.message;
70
+ }
71
+ return 'Unknown error';
72
+ }
73
+ }
74
+ exports.BaseProvider = BaseProvider;
75
+ /**
76
+ * OpenAI-compatible provider base class
77
+ *
78
+ * Many providers (Groq, Mistral, Together, Perplexity, DeepSeek, Ollama)
79
+ * use the OpenAI chat completions API format. This class handles all of them.
80
+ */
81
+ class OpenAICompatibleProvider extends BaseProvider {
82
+ constructor(config, baseUrlOverride) {
83
+ super(config);
84
+ this.name = config.name;
85
+ this.displayName = config.displayName;
86
+ this.baseUrl = baseUrlOverride || config.baseUrl;
87
+ }
88
+ async execute(request) {
89
+ const startTime = Date.now();
90
+ try {
91
+ // Use the Responses API for image generation (OpenAI only, not other compatible providers)
92
+ if (request.enableImageGeneration && this.name === 'openai') {
93
+ return await this.executeWithResponses(request, startTime);
94
+ }
95
+ const messages = [];
96
+ // For JSON mode, ensure "json" appears in the system prompt (OpenAI requirement)
97
+ const systemPrompt = request.mode === 'json'
98
+ ? (request.systemPrompt ? `${request.systemPrompt}\n\nRespond with valid JSON.` : 'Respond with valid JSON.')
99
+ : request.systemPrompt;
100
+ if (systemPrompt) {
101
+ messages.push({ role: 'system', content: systemPrompt });
102
+ }
103
+ messages.push({ role: 'user', content: request.prompt });
104
+ const body = {
105
+ model: request.model,
106
+ messages,
107
+ stream: false
108
+ };
109
+ if (request.maxTokens) {
110
+ body.max_tokens = request.maxTokens;
111
+ }
112
+ if (request.temperature !== undefined) {
113
+ body.temperature = request.temperature;
114
+ }
115
+ // JSON mode - request structured JSON output
116
+ if (request.mode === 'json') {
117
+ body.response_format = { type: 'json_object' };
118
+ }
119
+ const headers = {
120
+ 'Content-Type': 'application/json'
121
+ };
122
+ if (request.apiKey) {
123
+ headers['Authorization'] = `Bearer ${request.apiKey}`;
124
+ }
125
+ const response = await (0, axios_1.default)({
126
+ method: 'POST',
127
+ url: `${this.baseUrl}/chat/completions`,
128
+ headers,
129
+ data: body,
130
+ timeout: 120000
131
+ });
132
+ const data = response.data;
133
+ const duration = Date.now() - startTime;
134
+ // Handle both string responses and multimodal content arrays
135
+ // Models like GPT-4o can return image content blocks alongside text
136
+ let content = '';
137
+ const messageContent = data.choices?.[0]?.message?.content;
138
+ if (typeof messageContent === 'string') {
139
+ content = messageContent;
140
+ }
141
+ else if (Array.isArray(messageContent)) {
142
+ for (const block of messageContent) {
143
+ if (block.type === 'text') {
144
+ content += block.text || '';
145
+ }
146
+ else if (block.type === 'image_url' && block.image_url?.url) {
147
+ content += `\n\n![generated image](${block.image_url.url})\n\n`;
148
+ }
149
+ }
150
+ }
151
+ const usage = {
152
+ promptTokens: data.usage?.prompt_tokens || 0,
153
+ completionTokens: data.usage?.completion_tokens || 0,
154
+ totalTokens: data.usage?.total_tokens || 0
155
+ };
156
+ return this.createSuccessResult(content, usage, duration);
157
+ }
158
+ catch (error) {
159
+ const duration = Date.now() - startTime;
160
+ return this.createErrorResult(this.extractAxiosError(error), duration);
161
+ }
162
+ }
163
+ async *stream(request) {
164
+ const messages = [];
165
+ // For JSON mode, ensure "json" appears in the system prompt (OpenAI requirement)
166
+ const systemPrompt = request.mode === 'json'
167
+ ? (request.systemPrompt ? `${request.systemPrompt}\n\nRespond with valid JSON.` : 'Respond with valid JSON.')
168
+ : request.systemPrompt;
169
+ if (systemPrompt) {
170
+ messages.push({ role: 'system', content: systemPrompt });
171
+ }
172
+ messages.push({ role: 'user', content: request.prompt });
173
+ const body = {
174
+ model: request.model,
175
+ messages,
176
+ stream: true
177
+ };
178
+ if (request.maxTokens) {
179
+ body.max_tokens = request.maxTokens;
180
+ }
181
+ if (request.temperature !== undefined) {
182
+ body.temperature = request.temperature;
183
+ }
184
+ // JSON mode - request structured JSON output
185
+ if (request.mode === 'json') {
186
+ body.response_format = { type: 'json_object' };
187
+ }
188
+ const headers = {
189
+ 'Content-Type': 'application/json'
190
+ };
191
+ if (request.apiKey) {
192
+ headers['Authorization'] = `Bearer ${request.apiKey}`;
193
+ }
194
+ const response = await (0, axios_1.default)({
195
+ method: 'POST',
196
+ url: `${this.baseUrl}/chat/completions`,
197
+ headers,
198
+ data: body,
199
+ timeout: 120000,
200
+ responseType: 'stream'
201
+ });
202
+ const nodeStream = response.data;
203
+ let buffer = '';
204
+ let totalUsage;
205
+ for await (const chunk of nodeStream) {
206
+ buffer += chunk.toString();
207
+ const lines = buffer.split('\n');
208
+ buffer = lines.pop() || '';
209
+ for (const line of lines) {
210
+ const trimmed = line.trim();
211
+ if (!trimmed || trimmed === 'data: [DONE]')
212
+ continue;
213
+ if (!trimmed.startsWith('data: '))
214
+ continue;
215
+ try {
216
+ const json = JSON.parse(trimmed.slice(6));
217
+ const delta = json.choices?.[0]?.delta?.content || '';
218
+ // Capture usage if present (some providers send on last chunk)
219
+ if (json.usage) {
220
+ totalUsage = {
221
+ promptTokens: json.usage.prompt_tokens || 0,
222
+ completionTokens: json.usage.completion_tokens || 0,
223
+ totalTokens: json.usage.total_tokens || 0
224
+ };
225
+ }
226
+ if (delta) {
227
+ yield { content: delta, done: false };
228
+ }
229
+ }
230
+ catch {
231
+ // Skip invalid JSON lines
232
+ }
233
+ }
234
+ }
235
+ yield { content: '', done: true, usage: totalUsage };
236
+ }
237
+ /**
238
+ * Execute using OpenAI's Responses API for image generation
239
+ * Uses POST /v1/responses with tools: [{"type": "image_generation"}]
240
+ */
241
+ async executeWithResponses(request, startTime) {
242
+ const input = [];
243
+ if (request.systemPrompt) {
244
+ input.push({ role: 'developer', content: request.systemPrompt });
245
+ }
246
+ input.push({ role: 'user', content: request.prompt });
247
+ const body = {
248
+ model: request.model,
249
+ input,
250
+ tools: [{ type: 'image_generation' }]
251
+ };
252
+ if (request.maxTokens) {
253
+ body.max_output_tokens = request.maxTokens;
254
+ }
255
+ if (request.temperature !== undefined) {
256
+ body.temperature = request.temperature;
257
+ }
258
+ try {
259
+ const response = await (0, axios_1.default)({
260
+ method: 'POST',
261
+ url: `${this.baseUrl}/responses`,
262
+ headers: {
263
+ 'Content-Type': 'application/json',
264
+ 'Authorization': `Bearer ${request.apiKey}`
265
+ },
266
+ data: body,
267
+ timeout: 120000
268
+ });
269
+ const data = response.data;
270
+ const duration = Date.now() - startTime;
271
+ // Parse the Responses API output format
272
+ // Output is an array of items: message (text), image_generation_call (images)
273
+ let content = '';
274
+ const outputItems = data.output || [];
275
+ for (const item of outputItems) {
276
+ if (item.type === 'message') {
277
+ const parts = item.content || [];
278
+ for (const part of parts) {
279
+ if (part.type === 'output_text') {
280
+ content += part.text || '';
281
+ }
282
+ }
283
+ }
284
+ else if (item.type === 'image_generation_call' && item.result) {
285
+ content += `\n\n![generated image](data:image/png;base64,${item.result})\n\n`;
286
+ }
287
+ }
288
+ const usage = {
289
+ promptTokens: data.usage?.input_tokens || 0,
290
+ completionTokens: data.usage?.output_tokens || 0,
291
+ totalTokens: (data.usage?.input_tokens || 0) + (data.usage?.output_tokens || 0)
292
+ };
293
+ return this.createSuccessResult(content, usage, duration);
294
+ }
295
+ catch (error) {
296
+ const duration = Date.now() - startTime;
297
+ return this.createErrorResult(this.extractAxiosError(error), duration);
298
+ }
299
+ }
300
+ }
301
+ exports.OpenAICompatibleProvider = OpenAICompatibleProvider;
302
+ /**
303
+ * Anthropic provider using the Messages API
304
+ */
305
+ class AnthropicProvider extends BaseProvider {
306
+ constructor() {
307
+ super(...arguments);
308
+ this.name = 'anthropic';
309
+ this.displayName = 'Anthropic';
310
+ this.baseUrl = 'https://api.anthropic.com';
311
+ }
312
+ async execute(request) {
313
+ const startTime = Date.now();
314
+ try {
315
+ const messages = [
316
+ { role: 'user', content: request.prompt }
317
+ ];
318
+ // For thinking mode, ensure minimum 1024 tokens for both max_tokens and budget
319
+ const isThinking = request.mode === 'thinking';
320
+ const maxTokens = isThinking
321
+ ? Math.max(1024, request.maxTokens || 4096)
322
+ : (request.maxTokens || 4096);
323
+ const body = {
324
+ model: request.model,
325
+ max_tokens: maxTokens,
326
+ messages
327
+ };
328
+ if (request.systemPrompt) {
329
+ body.system = request.systemPrompt;
330
+ }
331
+ if (request.temperature !== undefined) {
332
+ body.temperature = request.temperature;
333
+ }
334
+ // Extended thinking mode - uses budget_tokens (minimum 1024)
335
+ if (isThinking) {
336
+ body.thinking = { type: 'enabled', budget_tokens: maxTokens };
337
+ }
338
+ const response = await (0, axios_1.default)({
339
+ method: 'POST',
340
+ url: `${this.baseUrl}/v1/messages`,
341
+ headers: {
342
+ 'Content-Type': 'application/json',
343
+ 'x-api-key': request.apiKey,
344
+ 'anthropic-version': '2023-06-01'
345
+ },
346
+ data: body,
347
+ timeout: 120000
348
+ });
349
+ const data = response.data;
350
+ const duration = Date.now() - startTime;
351
+ // Anthropic returns content as an array of blocks
352
+ // With thinking mode, there may be thinking blocks followed by text blocks
353
+ let content = '';
354
+ if (data.content && Array.isArray(data.content)) {
355
+ for (const block of data.content) {
356
+ if (block.type === 'text') {
357
+ content += block.text || '';
358
+ }
359
+ else if (block.type === 'thinking') {
360
+ content += block.thinking || '';
361
+ }
362
+ else if (block.type === 'image' && block.source?.data) {
363
+ const mimeType = block.source.media_type || 'image/png';
364
+ content += `\n\n![generated image](data:${mimeType};base64,${block.source.data})\n\n`;
365
+ }
366
+ }
367
+ }
368
+ const usage = {
369
+ promptTokens: data.usage?.input_tokens || 0,
370
+ completionTokens: data.usage?.output_tokens || 0,
371
+ totalTokens: (data.usage?.input_tokens || 0) + (data.usage?.output_tokens || 0)
372
+ };
373
+ return this.createSuccessResult(content, usage, duration);
374
+ }
375
+ catch (error) {
376
+ const duration = Date.now() - startTime;
377
+ return this.createErrorResult(this.extractAxiosError(error), duration);
378
+ }
379
+ }
380
+ async *stream(request) {
381
+ const messages = [
382
+ { role: 'user', content: request.prompt }
383
+ ];
384
+ // For thinking mode, ensure minimum 1024 tokens for both max_tokens and budget
385
+ const isThinking = request.mode === 'thinking';
386
+ const maxTokens = isThinking
387
+ ? Math.max(1024, request.maxTokens || 4096)
388
+ : (request.maxTokens || 4096);
389
+ const body = {
390
+ model: request.model,
391
+ max_tokens: maxTokens,
392
+ messages,
393
+ stream: true
394
+ };
395
+ if (request.systemPrompt) {
396
+ body.system = request.systemPrompt;
397
+ }
398
+ if (request.temperature !== undefined) {
399
+ body.temperature = request.temperature;
400
+ }
401
+ // Extended thinking mode - uses budget_tokens (minimum 1024)
402
+ if (isThinking) {
403
+ body.thinking = { type: 'enabled', budget_tokens: maxTokens };
404
+ }
405
+ const response = await (0, axios_1.default)({
406
+ method: 'POST',
407
+ url: `${this.baseUrl}/v1/messages`,
408
+ headers: {
409
+ 'Content-Type': 'application/json',
410
+ 'x-api-key': request.apiKey,
411
+ 'anthropic-version': '2023-06-01'
412
+ },
413
+ data: body,
414
+ timeout: 120000,
415
+ responseType: 'stream'
416
+ });
417
+ const nodeStream = response.data;
418
+ let buffer = '';
419
+ let totalUsage;
420
+ for await (const chunk of nodeStream) {
421
+ buffer += chunk.toString();
422
+ const lines = buffer.split('\n');
423
+ buffer = lines.pop() || '';
424
+ for (const line of lines) {
425
+ const trimmed = line.trim();
426
+ if (!trimmed.startsWith('data: '))
427
+ continue;
428
+ try {
429
+ const json = JSON.parse(trimmed.slice(6));
430
+ if (json.type === 'message_start' && json.message?.usage) {
431
+ // Capture input tokens from message_start (comes first)
432
+ totalUsage = {
433
+ promptTokens: json.message.usage.input_tokens || 0,
434
+ completionTokens: 0,
435
+ totalTokens: json.message.usage.input_tokens || 0
436
+ };
437
+ }
438
+ else if (json.type === 'content_block_delta') {
439
+ // Handle both regular text and thinking text deltas
440
+ const delta = json.delta?.text || json.delta?.thinking || '';
441
+ if (delta) {
442
+ yield { content: delta, done: false };
443
+ }
444
+ }
445
+ else if (json.type === 'message_delta' && json.usage) {
446
+ // Update with output tokens (comes at end)
447
+ const outputTokens = json.usage.output_tokens || 0;
448
+ if (totalUsage) {
449
+ totalUsage.completionTokens = outputTokens;
450
+ totalUsage.totalTokens = totalUsage.promptTokens + outputTokens;
451
+ }
452
+ else {
453
+ totalUsage = {
454
+ promptTokens: 0,
455
+ completionTokens: outputTokens,
456
+ totalTokens: outputTokens
457
+ };
458
+ }
459
+ }
460
+ }
461
+ catch {
462
+ // Skip invalid JSON
463
+ }
464
+ }
465
+ }
466
+ yield { content: '', done: true, usage: totalUsage };
467
+ }
468
+ }
469
+ exports.AnthropicProvider = AnthropicProvider;
470
+ /**
471
+ * Google Gemini provider using the Generative Language API
472
+ */
473
+ class GoogleGeminiProvider extends BaseProvider {
474
+ constructor() {
475
+ super(...arguments);
476
+ this.name = 'google';
477
+ this.displayName = 'Google Gemini';
478
+ this.baseUrl = 'https://generativelanguage.googleapis.com';
479
+ }
480
+ async execute(request) {
481
+ const startTime = Date.now();
482
+ try {
483
+ const contents = [
484
+ {
485
+ parts: [{ text: request.prompt }]
486
+ }
487
+ ];
488
+ const body = {
489
+ contents
490
+ };
491
+ if (request.systemPrompt) {
492
+ body.systemInstruction = { parts: [{ text: request.systemPrompt }] };
493
+ }
494
+ const generationConfig = {};
495
+ if (request.maxTokens) {
496
+ generationConfig.maxOutputTokens = request.maxTokens;
497
+ }
498
+ if (request.temperature !== undefined) {
499
+ generationConfig.temperature = request.temperature;
500
+ }
501
+ // Enable image output modality for models that support it
502
+ if (request.enableImageGeneration) {
503
+ generationConfig.responseModalities = ['TEXT', 'IMAGE'];
504
+ }
505
+ if (Object.keys(generationConfig).length > 0) {
506
+ body.generationConfig = generationConfig;
507
+ }
508
+ const url = `${this.baseUrl}/v1beta/models/${request.model}:generateContent?key=${request.apiKey}`;
509
+ const response = await (0, axios_1.default)({
510
+ method: 'POST',
511
+ url,
512
+ headers: {
513
+ 'Content-Type': 'application/json'
514
+ },
515
+ data: body,
516
+ timeout: 120000
517
+ });
518
+ const data = response.data;
519
+ const duration = Date.now() - startTime;
520
+ // Extract text and images from candidates
521
+ let content = '';
522
+ const parts = data.candidates?.[0]?.content?.parts || [];
523
+ for (const part of parts) {
524
+ if (part.text) {
525
+ content += part.text;
526
+ }
527
+ else if (part.inline_data?.data) {
528
+ const mimeType = part.inline_data.mime_type || 'image/png';
529
+ content += `\n\n![generated image](data:${mimeType};base64,${part.inline_data.data})\n\n`;
530
+ }
531
+ }
532
+ const usageMetadata = data.usageMetadata || {};
533
+ const usage = {
534
+ promptTokens: usageMetadata.promptTokenCount || 0,
535
+ completionTokens: usageMetadata.candidatesTokenCount || 0,
536
+ totalTokens: usageMetadata.totalTokenCount || 0
537
+ };
538
+ return this.createSuccessResult(content, usage, duration);
539
+ }
540
+ catch (error) {
541
+ const duration = Date.now() - startTime;
542
+ return this.createErrorResult(this.extractAxiosError(error), duration);
543
+ }
544
+ }
545
+ async *stream(request) {
546
+ const contents = [
547
+ {
548
+ parts: [{ text: request.prompt }]
549
+ }
550
+ ];
551
+ const body = {
552
+ contents
553
+ };
554
+ if (request.systemPrompt) {
555
+ body.systemInstruction = { parts: [{ text: request.systemPrompt }] };
556
+ }
557
+ const generationConfig = {};
558
+ if (request.maxTokens) {
559
+ generationConfig.maxOutputTokens = request.maxTokens;
560
+ }
561
+ if (request.temperature !== undefined) {
562
+ generationConfig.temperature = request.temperature;
563
+ }
564
+ if (request.enableImageGeneration) {
565
+ generationConfig.responseModalities = ['TEXT', 'IMAGE'];
566
+ }
567
+ if (Object.keys(generationConfig).length > 0) {
568
+ body.generationConfig = generationConfig;
569
+ }
570
+ const url = `${this.baseUrl}/v1beta/models/${request.model}:streamGenerateContent?alt=sse&key=${request.apiKey}`;
571
+ const response = await (0, axios_1.default)({
572
+ method: 'POST',
573
+ url,
574
+ headers: {
575
+ 'Content-Type': 'application/json'
576
+ },
577
+ data: body,
578
+ timeout: 120000,
579
+ responseType: 'stream'
580
+ });
581
+ const nodeStream = response.data;
582
+ let buffer = '';
583
+ let totalUsage;
584
+ for await (const chunk of nodeStream) {
585
+ buffer += chunk.toString();
586
+ const lines = buffer.split('\n');
587
+ buffer = lines.pop() || '';
588
+ for (const line of lines) {
589
+ const trimmed = line.trim();
590
+ if (!trimmed.startsWith('data: '))
591
+ continue;
592
+ try {
593
+ const json = JSON.parse(trimmed.slice(6));
594
+ // Handle both text and image parts in streamed chunks
595
+ const parts = json.candidates?.[0]?.content?.parts || [];
596
+ let chunkContent = '';
597
+ for (const part of parts) {
598
+ if (part.text) {
599
+ chunkContent += part.text;
600
+ }
601
+ else if (part.inline_data?.data) {
602
+ const mimeType = part.inline_data.mime_type || 'image/png';
603
+ chunkContent += `\n\n![generated image](data:${mimeType};base64,${part.inline_data.data})\n\n`;
604
+ }
605
+ }
606
+ if (json.usageMetadata) {
607
+ totalUsage = {
608
+ promptTokens: json.usageMetadata.promptTokenCount || 0,
609
+ completionTokens: json.usageMetadata.candidatesTokenCount || 0,
610
+ totalTokens: json.usageMetadata.totalTokenCount || 0
611
+ };
612
+ }
613
+ if (chunkContent) {
614
+ yield { content: chunkContent, done: false };
615
+ }
616
+ }
617
+ catch {
618
+ // Skip invalid JSON
619
+ }
620
+ }
621
+ }
622
+ yield { content: '', done: true, usage: totalUsage };
623
+ }
624
+ }
625
+ exports.GoogleGeminiProvider = GoogleGeminiProvider;
626
+ /**
627
+ * Cohere provider using the Chat API
628
+ */
629
+ class CohereProvider extends BaseProvider {
630
+ constructor() {
631
+ super(...arguments);
632
+ this.name = 'cohere';
633
+ this.displayName = 'Cohere';
634
+ this.baseUrl = 'https://api.cohere.ai';
635
+ }
636
+ async execute(request) {
637
+ const startTime = Date.now();
638
+ try {
639
+ const body = {
640
+ model: request.model,
641
+ message: request.prompt
642
+ };
643
+ if (request.systemPrompt) {
644
+ body.preamble = request.systemPrompt;
645
+ }
646
+ if (request.maxTokens) {
647
+ body.max_tokens = request.maxTokens;
648
+ }
649
+ if (request.temperature !== undefined) {
650
+ body.temperature = request.temperature;
651
+ }
652
+ const response = await (0, axios_1.default)({
653
+ method: 'POST',
654
+ url: `${this.baseUrl}/v1/chat`,
655
+ headers: {
656
+ 'Content-Type': 'application/json',
657
+ 'Authorization': `Bearer ${request.apiKey}`
658
+ },
659
+ data: body,
660
+ timeout: 120000
661
+ });
662
+ const data = response.data;
663
+ const duration = Date.now() - startTime;
664
+ const content = data.text || '';
665
+ const tokens = data.meta?.tokens || {};
666
+ const usage = {
667
+ promptTokens: tokens.input_tokens || 0,
668
+ completionTokens: tokens.output_tokens || 0,
669
+ totalTokens: (tokens.input_tokens || 0) + (tokens.output_tokens || 0)
670
+ };
671
+ return this.createSuccessResult(content, usage, duration);
672
+ }
673
+ catch (error) {
674
+ const duration = Date.now() - startTime;
675
+ return this.createErrorResult(this.extractAxiosError(error), duration);
676
+ }
677
+ }
678
+ async *stream(request) {
679
+ const body = {
680
+ model: request.model,
681
+ message: request.prompt,
682
+ stream: true
683
+ };
684
+ if (request.systemPrompt) {
685
+ body.preamble = request.systemPrompt;
686
+ }
687
+ if (request.maxTokens) {
688
+ body.max_tokens = request.maxTokens;
689
+ }
690
+ if (request.temperature !== undefined) {
691
+ body.temperature = request.temperature;
692
+ }
693
+ const response = await (0, axios_1.default)({
694
+ method: 'POST',
695
+ url: `${this.baseUrl}/v1/chat`,
696
+ headers: {
697
+ 'Content-Type': 'application/json',
698
+ 'Authorization': `Bearer ${request.apiKey}`
699
+ },
700
+ data: body,
701
+ timeout: 120000,
702
+ responseType: 'stream'
703
+ });
704
+ const nodeStream = response.data;
705
+ let buffer = '';
706
+ let totalUsage;
707
+ for await (const chunk of nodeStream) {
708
+ buffer += chunk.toString();
709
+ const lines = buffer.split('\n');
710
+ buffer = lines.pop() || '';
711
+ for (const line of lines) {
712
+ const trimmed = line.trim();
713
+ if (!trimmed)
714
+ continue;
715
+ try {
716
+ const json = JSON.parse(trimmed);
717
+ if (json.event_type === 'text-generation') {
718
+ const text = json.text || '';
719
+ if (text) {
720
+ yield { content: text, done: false };
721
+ }
722
+ }
723
+ else if (json.event_type === 'stream-end' && json.response?.meta?.tokens) {
724
+ const tokens = json.response.meta.tokens;
725
+ totalUsage = {
726
+ promptTokens: tokens.input_tokens || 0,
727
+ completionTokens: tokens.output_tokens || 0,
728
+ totalTokens: (tokens.input_tokens || 0) + (tokens.output_tokens || 0)
729
+ };
730
+ }
731
+ }
732
+ catch {
733
+ // Skip invalid JSON
734
+ }
735
+ }
736
+ }
737
+ yield { content: '', done: true, usage: totalUsage };
738
+ }
739
+ }
740
+ exports.CohereProvider = CohereProvider;
741
+ //# sourceMappingURL=base.js.map