@umituz/web-ai 1.0.0 → 1.0.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.
Files changed (55) hide show
  1. package/dist/{ABTesting-DDqNhHtP.d.ts → AIErrors-B2ogd1PB.d.ts} +14 -1
  2. package/dist/{ABTesting-DWBtKTMQ.d.cts → AIErrors-C5EasV_c.d.cts} +14 -1
  3. package/dist/{ProviderConfig-a55tLhWT.d.cts → ProviderConfig-DIFa9nmG.d.cts} +10 -1
  4. package/dist/{ProviderConfig-a55tLhWT.d.ts → ProviderConfig-DIFa9nmG.d.ts} +10 -1
  5. package/dist/chat/index.cjs +114 -23
  6. package/dist/chat/index.cjs.map +1 -1
  7. package/dist/chat/index.d.cts +4 -4
  8. package/dist/chat/index.d.ts +4 -4
  9. package/dist/chat/index.js +114 -23
  10. package/dist/chat/index.js.map +1 -1
  11. package/dist/{chat.service-DBKq9I2c.d.cts → chat.service-CL1qtYxe.d.cts} +7 -7
  12. package/dist/{chat.service-RQn8UTXD.d.ts → chat.service-q05GnhEm.d.ts} +7 -7
  13. package/dist/content/index.cjs +506 -193
  14. package/dist/content/index.cjs.map +1 -1
  15. package/dist/content/index.d.cts +7 -8
  16. package/dist/content/index.d.ts +7 -8
  17. package/dist/content/index.js +506 -193
  18. package/dist/content/index.js.map +1 -1
  19. package/dist/core/index.cjs +122 -15
  20. package/dist/core/index.cjs.map +1 -1
  21. package/dist/core/index.d.cts +13 -31
  22. package/dist/core/index.d.ts +13 -31
  23. package/dist/core/index.js +119 -16
  24. package/dist/core/index.js.map +1 -1
  25. package/dist/{create-ai-BhNaz86i.d.ts → create-ai-9PW752z8.d.ts} +14 -21
  26. package/dist/{create-ai-DGZFBOA1.d.cts → create-ai-CXYKPK6G.d.cts} +14 -21
  27. package/dist/hooks/index.cjs +631 -179
  28. package/dist/hooks/index.cjs.map +1 -1
  29. package/dist/hooks/index.d.cts +33 -7
  30. package/dist/hooks/index.d.ts +33 -7
  31. package/dist/hooks/index.js +631 -180
  32. package/dist/hooks/index.js.map +1 -1
  33. package/dist/index-DIHz8_OJ.d.ts +48 -0
  34. package/dist/index-DKHNcMbl.d.cts +48 -0
  35. package/dist/index.cjs +666 -104
  36. package/dist/index.cjs.map +1 -1
  37. package/dist/index.d.cts +8 -7
  38. package/dist/index.d.ts +8 -7
  39. package/dist/index.js +645 -105
  40. package/dist/index.js.map +1 -1
  41. package/dist/{provider.factory-COsnprLH.d.cts → provider.factory-BxsfK4WW.d.cts} +1 -1
  42. package/dist/{provider.factory-CpWFyjbw.d.ts → provider.factory-DfDfk0dK.d.ts} +1 -1
  43. package/dist/providers/index.cjs +973 -130
  44. package/dist/providers/index.cjs.map +1 -1
  45. package/dist/providers/index.d.cts +84 -18
  46. package/dist/providers/index.d.ts +84 -18
  47. package/dist/providers/index.js +956 -131
  48. package/dist/providers/index.js.map +1 -1
  49. package/dist/text-generation.service-BQO3Tvz2.d.cts +66 -0
  50. package/dist/text-generation.service-CcYQM6Ch.d.ts +66 -0
  51. package/dist/wizard/index.d.cts +1 -1
  52. package/dist/wizard/index.d.ts +1 -1
  53. package/package.json +9 -1
  54. package/dist/index-DjX9ZKuM.d.cts +0 -14
  55. package/dist/index-DxLvE588.d.ts +0 -14
@@ -6,25 +6,49 @@ function isMediaProvider(provider) {
6
6
  return provider.capabilities.includes("image") || provider.capabilities.includes("video");
7
7
  }
8
8
 
9
- // src/content/domain/errors/AIErrors.ts
10
- var AIError = class _AIError extends Error {
11
- code;
12
- cause;
13
- constructor(message, code, cause) {
14
- super(message);
15
- this.name = "AIError";
16
- this.code = code;
17
- this.cause = cause;
18
- }
19
- static from(error, fallbackCode = "UNKNOWN") {
20
- if (error instanceof _AIError) return error;
21
- if (error instanceof Error) {
22
- return new _AIError(error.message, mapMessageToCode(error.message, fallbackCode), error);
23
- }
24
- return new _AIError(String(error), fallbackCode, error);
9
+ // src/core/errors/ai-error-types.ts
10
+ var AIErrorType = /* @__PURE__ */ ((AIErrorType2) => {
11
+ AIErrorType2["INVALID_API_KEY"] = "INVALID_API_KEY";
12
+ AIErrorType2["MISSING_CONFIG"] = "MISSING_CONFIG";
13
+ AIErrorType2["NETWORK_ERROR"] = "NETWORK_ERROR";
14
+ AIErrorType2["ABORT_ERROR"] = "ABORT_ERROR";
15
+ AIErrorType2["RATE_LIMIT_ERROR"] = "RATE_LIMIT_ERROR";
16
+ AIErrorType2["SERVER_ERROR"] = "SERVER_ERROR";
17
+ AIErrorType2["CLIENT_ERROR"] = "CLIENT_ERROR";
18
+ AIErrorType2["UNKNOWN_ERROR"] = "UNKNOWN_ERROR";
19
+ return AIErrorType2;
20
+ })(AIErrorType || {});
21
+
22
+ // src/core/errors/ai-error.ts
23
+ var KNOWN_ERROR_TYPES = new Set(Object.values(AIErrorType));
24
+ function mapCodeToType(code) {
25
+ switch (code) {
26
+ case "VALIDATION_ERROR":
27
+ case "CLIENT_ERROR":
28
+ return "CLIENT_ERROR" /* CLIENT_ERROR */;
29
+ case "INVALID_API_KEY":
30
+ return "INVALID_API_KEY" /* INVALID_API_KEY */;
31
+ case "MISSING_CONFIG":
32
+ case "PROVIDER_UNAVAILABLE":
33
+ return "MISSING_CONFIG" /* MISSING_CONFIG */;
34
+ case "PROVIDER_QUOTA_EXHAUSTED":
35
+ case "RATE_LIMIT_ERROR":
36
+ return "RATE_LIMIT_ERROR" /* RATE_LIMIT_ERROR */;
37
+ case "NETWORK_ERROR":
38
+ case "TIMEOUT":
39
+ return "NETWORK_ERROR" /* NETWORK_ERROR */;
40
+ case "ABORT_ERROR":
41
+ case "ABORTED":
42
+ return "ABORT_ERROR" /* ABORT_ERROR */;
43
+ case "SERVER_ERROR":
44
+ case "PROVIDER_ERROR":
45
+ case "NO_RESULT":
46
+ return "SERVER_ERROR" /* SERVER_ERROR */;
47
+ default:
48
+ return "UNKNOWN_ERROR" /* UNKNOWN_ERROR */;
25
49
  }
26
- };
27
- function mapMessageToCode(message, fallback) {
50
+ }
51
+ function mapMessageToCode(message, fallback = "UNKNOWN") {
28
52
  const lower = message.toLowerCase();
29
53
  if (lower.includes("validation")) return "VALIDATION_ERROR";
30
54
  if (lower.includes("quota")) return "PROVIDER_QUOTA_EXHAUSTED";
@@ -37,6 +61,53 @@ function mapMessageToCode(message, fallback) {
37
61
  if (lower.includes("network") || lower.includes("fetch")) return "NETWORK_ERROR";
38
62
  return fallback;
39
63
  }
64
+ var AIError = class _AIError extends Error {
65
+ type;
66
+ code;
67
+ cause;
68
+ metadata;
69
+ constructor(typeOrMessage, messageOrCode, cause, metadata = {}) {
70
+ let effectiveType;
71
+ let effectiveMessage;
72
+ let effectiveCode;
73
+ if (KNOWN_ERROR_TYPES.has(typeOrMessage)) {
74
+ effectiveType = typeOrMessage;
75
+ effectiveMessage = messageOrCode ?? "";
76
+ effectiveCode = effectiveType;
77
+ } else {
78
+ effectiveMessage = typeOrMessage;
79
+ effectiveCode = messageOrCode ?? "UNKNOWN";
80
+ effectiveType = mapCodeToType(effectiveCode);
81
+ }
82
+ super(effectiveMessage);
83
+ this.name = "AIError";
84
+ this.type = effectiveType;
85
+ this.code = effectiveCode;
86
+ this.cause = cause;
87
+ this.metadata = metadata;
88
+ }
89
+ static from(error, fallbackCode = "UNKNOWN") {
90
+ if (error instanceof _AIError) return error;
91
+ if (error instanceof Error) {
92
+ const code = mapMessageToCode(error.message, fallbackCode);
93
+ return new _AIError(error.message, code, error);
94
+ }
95
+ return new _AIError(String(error), fallbackCode, error);
96
+ }
97
+ toString() {
98
+ const statusPart = typeof this.metadata.status === "number" ? ` (HTTP ${this.metadata.status})` : "";
99
+ return `${this.name} [${this.type}]: ${this.message}${statusPart}`;
100
+ }
101
+ toJSON() {
102
+ return {
103
+ name: this.name,
104
+ type: this.type,
105
+ code: this.code,
106
+ message: this.message,
107
+ metadata: this.metadata
108
+ };
109
+ }
110
+ };
40
111
 
41
112
  // src/content/domain/limits/ProviderTimingConfig.ts
42
113
  var ProviderTimingConfig = {
@@ -111,29 +182,11 @@ var ProviderError = class extends AIError {
111
182
  providerId;
112
183
  };
113
184
 
114
- // src/core/errors/ai-error.ts
115
- var AIError2 = class extends Error {
116
- type;
117
- cause;
118
- metadata;
185
+ // src/core/errors/groq-error.ts
186
+ var GroqError = class extends AIError {
119
187
  constructor(type, message, cause, metadata = {}) {
120
- super(message);
121
- this.name = "AIError";
122
- this.type = type;
123
- this.cause = cause;
124
- this.metadata = metadata;
125
- }
126
- toString() {
127
- const statusPart = typeof this.metadata.status === "number" ? ` (HTTP ${this.metadata.status})` : "";
128
- return `${this.name} [${this.type}]: ${this.message}${statusPart}`;
129
- }
130
- toJSON() {
131
- return {
132
- name: this.name,
133
- type: this.type,
134
- message: this.message,
135
- metadata: this.metadata
136
- };
188
+ super(type, message, cause, metadata);
189
+ this.name = "GroqError";
137
190
  }
138
191
  };
139
192
 
@@ -262,7 +315,7 @@ var ProviderFactory = class {
262
315
  }
263
316
  }
264
317
  }
265
- const failure = new AIError2(
318
+ const failure = new AIError(
266
319
  "SERVER_ERROR" /* SERVER_ERROR */,
267
320
  `All providers failed for capability 'text'`,
268
321
  lastError instanceof Error ? lastError : void 0
@@ -276,12 +329,16 @@ var ProviderFactory = class {
276
329
  let wake = null;
277
330
  let finished = false;
278
331
  let failure;
332
+ const controller = new AbortController();
333
+ const onAbort = () => controller.abort();
334
+ request.signal?.addEventListener("abort", onAbort);
335
+ const effectiveRequest = { ...request, signal: controller.signal };
279
336
  const notify = () => {
280
337
  const resolve = wake;
281
338
  wake = null;
282
339
  resolve?.();
283
340
  };
284
- const completion = this.chatStream(request, {
341
+ const completion = this.chatStream(effectiveRequest, {
285
342
  onChunk: (chunk) => {
286
343
  queue.push(chunk);
287
344
  notify();
@@ -297,18 +354,23 @@ var ProviderFactory = class {
297
354
  notify();
298
355
  }
299
356
  );
300
- while (!finished || queue.length > 0) {
301
- if (queue.length === 0 && !finished) {
302
- await new Promise((resolve) => {
303
- wake = resolve;
304
- });
305
- }
306
- while (queue.length > 0) {
307
- yield queue.shift();
357
+ try {
358
+ while (!finished || queue.length > 0) {
359
+ if (queue.length === 0 && !finished) {
360
+ await new Promise((resolve) => {
361
+ wake = resolve;
362
+ });
363
+ }
364
+ while (queue.length > 0) {
365
+ yield queue.shift();
366
+ }
367
+ if (failure) throw failure;
308
368
  }
309
- if (failure) throw failure;
369
+ await completion;
370
+ } finally {
371
+ controller.abort();
372
+ request.signal?.removeEventListener("abort", onAbort);
310
373
  }
311
- await completion;
312
374
  }
313
375
  async generateJson(request) {
314
376
  const effective = request.model || request.tier ? request : { ...request, tier: this.defaultTier };
@@ -416,7 +478,7 @@ var ProviderFactory = class {
416
478
  }
417
479
  }
418
480
  }
419
- throw new AIError2(
481
+ throw new AIError(
420
482
  "SERVER_ERROR" /* SERVER_ERROR */,
421
483
  `All providers failed for capability '${capability}'`,
422
484
  lastError instanceof Error ? lastError : void 0
@@ -445,7 +507,7 @@ var ProviderFactory = class {
445
507
  }
446
508
  }
447
509
  noProviderError(capability) {
448
- return new AIError2(
510
+ return new AIError(
449
511
  "MISSING_CONFIG" /* MISSING_CONFIG */,
450
512
  `No provider registered for capability '${capability}' \u2014 create one with createAI() and include its config block`
451
513
  );
@@ -455,24 +517,81 @@ var ProviderFactory = class {
455
517
  // src/core/constants/protocol.constants.ts
456
518
  var SSE_PREFIX = "data: ";
457
519
  var SSE_DONE_MARKER = "[DONE]";
458
- var JSON_CODE_BLOCK_PATTERN = /```json\n([\s\S]*?)\n```/;
520
+ var JSON_CODE_BLOCK_PATTERN = /```(?:json)?\s*\n?([\s\S]*?)\n?```/;
459
521
  var JSON_OBJECT_PATTERN = /\{[\s\S]*\}/;
522
+ var JSON_ARRAY_PATTERN = /\[[\s\S]*\]/;
460
523
 
461
524
  // src/core/parsers/sse-lines.ts
462
525
  function isSseDataLine(line) {
463
- return line.startsWith(SSE_PREFIX);
526
+ return line.startsWith(SSE_PREFIX) || line.startsWith("data:");
464
527
  }
465
528
  function extractSseDataPayload(line) {
466
- return line.slice(SSE_PREFIX.length);
529
+ if (line.startsWith(SSE_PREFIX)) {
530
+ return line.slice(SSE_PREFIX.length);
531
+ }
532
+ if (line.startsWith("data:")) {
533
+ return line.slice(5);
534
+ }
535
+ return line;
467
536
  }
468
537
  function isSseDoneMarker(payload) {
469
538
  return payload === SSE_DONE_MARKER;
470
539
  }
471
540
 
541
+ // src/core/parsers/streaming-response.parser.ts
542
+ var StreamingResponseParser = class {
543
+ constructor(callbacks, extractDelta) {
544
+ this.callbacks = callbacks;
545
+ this.extractDelta = extractDelta;
546
+ }
547
+ callbacks;
548
+ extractDelta;
549
+ decoder = new TextDecoder();
550
+ async parse(stream) {
551
+ const reader = stream.getReader();
552
+ const contentChunks = [];
553
+ let buffer = "";
554
+ const consumeLine = (rawLine) => {
555
+ const line = rawLine.endsWith("\r") ? rawLine.slice(0, -1) : rawLine;
556
+ const trimmed = line.trim();
557
+ if (trimmed.length === 0 || !isSseDataLine(trimmed)) return;
558
+ const payload = extractSseDataPayload(trimmed);
559
+ if (isSseDoneMarker(payload)) return;
560
+ const content = this.extractDelta(payload);
561
+ if (content !== null) {
562
+ contentChunks.push(content);
563
+ this.callbacks.onChunk(content);
564
+ }
565
+ };
566
+ try {
567
+ while (true) {
568
+ const { done, value } = await reader.read();
569
+ if (done) break;
570
+ buffer += this.decoder.decode(value, { stream: true });
571
+ const lines = buffer.split("\n");
572
+ buffer = lines.pop() ?? "";
573
+ for (const line of lines) consumeLine(line);
574
+ }
575
+ buffer += this.decoder.decode();
576
+ if (buffer.length > 0) consumeLine(buffer);
577
+ } catch (error) {
578
+ if (error instanceof AIError) throw error;
579
+ throw new AIError(
580
+ "NETWORK_ERROR" /* NETWORK_ERROR */,
581
+ "Stream was interrupted",
582
+ error instanceof Error ? error : void 0
583
+ );
584
+ } finally {
585
+ reader.releaseLock();
586
+ }
587
+ return contentChunks.join("");
588
+ }
589
+ };
590
+
472
591
  // src/core/parsers/json-content-extractor.ts
473
592
  function extractJsonFromCodeFence(content) {
474
593
  const match = content.match(JSON_CODE_BLOCK_PATTERN);
475
- if (match && match[1]) return match[1];
594
+ if (match && match[1]) return match[1].trim();
476
595
  return null;
477
596
  }
478
597
  function extractJsonObject(content) {
@@ -480,8 +599,27 @@ function extractJsonObject(content) {
480
599
  if (match && match[0]) return match[0];
481
600
  return null;
482
601
  }
602
+ function extractJsonArray(content) {
603
+ const match = content.match(JSON_ARRAY_PATTERN);
604
+ if (match && match[0]) return match[0];
605
+ return null;
606
+ }
607
+ function repairCommonJsonDefects(text) {
608
+ return text.replace(/(:\s*-?\d+(?:\.\d+)?)\s*"/g, "$1").replace(/(:\s*"[^"]*")"/g, "$1");
609
+ }
483
610
  function extractJsonCandidate(content) {
484
- return extractJsonFromCodeFence(content) ?? extractJsonObject(content) ?? content;
611
+ const fromFence = extractJsonFromCodeFence(content);
612
+ if (fromFence) return fromFence;
613
+ const objectIndex = content.indexOf("{");
614
+ const arrayIndex = content.indexOf("[");
615
+ if (objectIndex !== -1 && (arrayIndex === -1 || objectIndex < arrayIndex)) {
616
+ const obj = extractJsonObject(content);
617
+ if (obj) return obj;
618
+ } else if (arrayIndex !== -1) {
619
+ const arr = extractJsonArray(content);
620
+ if (arr) return arr;
621
+ }
622
+ return content.trim();
485
623
  }
486
624
 
487
625
  // src/core/parsers/json-response.parser.ts
@@ -490,12 +628,17 @@ var JsonResponseParser = class {
490
628
  const candidate = extractJsonCandidate(rawContent);
491
629
  try {
492
630
  return JSON.parse(candidate);
493
- } catch (error) {
494
- throw new AIError2(
495
- "UNKNOWN_ERROR" /* UNKNOWN_ERROR */,
496
- "Failed to parse structured response as JSON",
497
- error instanceof Error ? error : void 0
498
- );
631
+ } catch {
632
+ try {
633
+ return JSON.parse(repairCommonJsonDefects(candidate));
634
+ } catch (error) {
635
+ throw new AIError(
636
+ "UNKNOWN_ERROR" /* UNKNOWN_ERROR */,
637
+ "Failed to parse structured response as JSON",
638
+ error instanceof Error ? error : void 0,
639
+ { rawContent }
640
+ );
641
+ }
499
642
  }
500
643
  }
501
644
  };
@@ -621,7 +764,7 @@ var AnthropicHttpTransport = class {
621
764
  });
622
765
  if (!response.ok) await this.throwFromErrorResponse(response);
623
766
  if (!response.body) {
624
- throw new AIError2("NETWORK_ERROR" /* NETWORK_ERROR */, "Response body is null");
767
+ throw new AIError("NETWORK_ERROR" /* NETWORK_ERROR */, "Response body is null");
625
768
  }
626
769
  return response.body;
627
770
  } catch (error) {
@@ -654,7 +797,7 @@ var AnthropicHttpTransport = class {
654
797
  status: response.status,
655
798
  url: response.url
656
799
  };
657
- throw new AIError2(
800
+ throw new AIError(
658
801
  classifyHttpStatus(response.status),
659
802
  errorMessage,
660
803
  void 0,
@@ -662,24 +805,24 @@ var AnthropicHttpTransport = class {
662
805
  );
663
806
  }
664
807
  normalizeError(error) {
665
- if (error instanceof AIError2) return error;
808
+ if (error instanceof AIError) return error;
666
809
  if (error instanceof Error) {
667
810
  if (error.name === "AbortError") {
668
- return new AIError2(
811
+ return new AIError(
669
812
  "ABORT_ERROR" /* ABORT_ERROR */,
670
813
  "Request was aborted by the client",
671
814
  error
672
815
  );
673
816
  }
674
817
  if (error.name === "TypeError" && error.message.toLowerCase().includes("network")) {
675
- return new AIError2(
818
+ return new AIError(
676
819
  "NETWORK_ERROR" /* NETWORK_ERROR */,
677
820
  "Network error: Unable to connect to the AI service",
678
821
  error
679
822
  );
680
823
  }
681
824
  }
682
- return new AIError2(
825
+ return new AIError(
683
826
  "UNKNOWN_ERROR" /* UNKNOWN_ERROR */,
684
827
  error instanceof Error ? error.message : "Unknown error occurred",
685
828
  error instanceof Error ? error : void 0
@@ -809,11 +952,14 @@ async function consumeAnthropicStream(stream, callbacks) {
809
952
  break;
810
953
  case "message_stop":
811
954
  break;
812
- case "error":
813
- throw new AIError2(
814
- event.error.type === "rate_limit_error" ? "RATE_LIMIT_ERROR" /* RATE_LIMIT_ERROR */ : "SERVER_ERROR" /* SERVER_ERROR */,
955
+ case "error": {
956
+ const errType = event.error.type;
957
+ const mappedType = errType === "rate_limit_error" ? "RATE_LIMIT_ERROR" /* RATE_LIMIT_ERROR */ : errType === "authentication_error" ? "INVALID_API_KEY" /* INVALID_API_KEY */ : errType === "invalid_request_error" ? "CLIENT_ERROR" /* CLIENT_ERROR */ : "SERVER_ERROR" /* SERVER_ERROR */;
958
+ throw new AIError(
959
+ mappedType,
815
960
  event.error.message || "Anthropic stream failed mid-flight"
816
961
  );
962
+ }
817
963
  }
818
964
  };
819
965
  try {
@@ -828,8 +974,8 @@ async function consumeAnthropicStream(stream, callbacks) {
828
974
  buffer += decoder.decode();
829
975
  if (buffer.length > 0) consumeLine(buffer);
830
976
  } catch (error) {
831
- if (error instanceof AIError2) throw error;
832
- throw new AIError2(
977
+ if (error instanceof AIError) throw error;
978
+ throw new AIError(
833
979
  "NETWORK_ERROR" /* NETWORK_ERROR */,
834
980
  "Stream was interrupted",
835
981
  error instanceof Error ? error : void 0
@@ -891,7 +1037,7 @@ var AnthropicProvider = class {
891
1037
  const messages = [...request.messages];
892
1038
  const lastUserIndex = findLastIndex(messages, (m) => m.role === "user");
893
1039
  if (lastUserIndex === -1) {
894
- throw new AIError2(
1040
+ throw new AIError(
895
1041
  "UNKNOWN_ERROR" /* UNKNOWN_ERROR */,
896
1042
  "generateStructured requires at least one user message"
897
1043
  );
@@ -946,7 +1092,7 @@ var AnthropicProvider = class {
946
1092
  finishReason
947
1093
  };
948
1094
  }
949
- throw new AIError2(
1095
+ throw new AIError(
950
1096
  "SERVER_ERROR" /* SERVER_ERROR */,
951
1097
  "No content generated from Anthropic API",
952
1098
  void 0,
@@ -1100,6 +1246,12 @@ function calculateExponentialBackoff(params) {
1100
1246
  }
1101
1247
 
1102
1248
  // src/core/constants/identifier.constants.ts
1249
+ var MESSAGE_ID_PREFIX = "msg-";
1250
+ var CHAT_MESSAGE_ID_SUFFIX_LENGTH = 9;
1251
+ var CACHE_KEY_TYPE_COMPLETION = "completion";
1252
+ var CACHE_KEY_TYPE_CHAT = "chat";
1253
+ var USER_ROLE = "user";
1254
+ var ASSISTANT_ROLE = "assistant";
1103
1255
  var QUEUE_PRIORITY_USER_REQUEST = 10;
1104
1256
  var DEFAULT_RETRY_MAX_ATTEMPTS = 3;
1105
1257
  var DEFAULT_RETRY_INITIAL_DELAY_MS = 1e3;
@@ -1151,7 +1303,7 @@ var RetryManager = class {
1151
1303
  }
1152
1304
  shouldRetry(error, options) {
1153
1305
  if (options.shouldRetry) return options.shouldRetry(error);
1154
- if (error instanceof AIError2) {
1306
+ if (error instanceof AIError) {
1155
1307
  return isRetryableErrorType(error.type, DEFAULT_ERROR_CLASSIFICATION_RULE);
1156
1308
  }
1157
1309
  const lower = error.message.toLowerCase();
@@ -1273,6 +1425,27 @@ var RequestDeduplicator = class {
1273
1425
  }
1274
1426
  };
1275
1427
 
1428
+ // src/core/utils/api-key-validator.ts
1429
+ var DEFAULT_API_KEY_RULE = {
1430
+ minLength: 10
1431
+ };
1432
+ function validateApiKey(apiKey, rule = DEFAULT_API_KEY_RULE) {
1433
+ if (!apiKey) {
1434
+ return { valid: false, reason: "API key is required" };
1435
+ }
1436
+ const trimmed = apiKey.trim();
1437
+ if (trimmed.length === 0) {
1438
+ return { valid: false, reason: "API key cannot be empty" };
1439
+ }
1440
+ if (trimmed.length < rule.minLength) {
1441
+ return {
1442
+ valid: false,
1443
+ reason: `API key must be at least ${rule.minLength} characters`
1444
+ };
1445
+ }
1446
+ return { valid: true };
1447
+ }
1448
+
1276
1449
  // src/providers/groq/groq.constants.ts
1277
1450
  var GROQ_MODELS = {
1278
1451
  LLAMA_3_1_8B_INSTANT: "llama-3.1-8b-instant",
@@ -1296,12 +1469,22 @@ var DEFAULT_GENERATION_CONFIG = {
1296
1469
  maxTokens: 1024,
1297
1470
  topP: 1
1298
1471
  };
1472
+ var DEFAULT_MODELS = {
1473
+ TEXT: GROQ_MODELS.LLAMA_3_1_8B_INSTANT,
1474
+ FAST: GROQ_MODELS.LLAMA_3_1_8B_INSTANT,
1475
+ EXPERIMENTAL: GROQ_MODELS.GPT_OSS_20B
1476
+ };
1299
1477
  var GROQ_BASE_URL = "https://api.groq.com/openai/v1";
1478
+ var DEFAULT_BASE_URL = GROQ_BASE_URL;
1300
1479
  var CHAT_COMPLETIONS_PATH = "/chat/completions";
1480
+ var API_ENDPOINTS = {
1481
+ CHAT: CHAT_COMPLETIONS_PATH
1482
+ };
1301
1483
  var GROQ_TIMEOUTS = {
1302
1484
  DEFAULT: 3e4,
1303
1485
  STREAMING: 6e4
1304
1486
  };
1487
+ var TIMEOUTS = GROQ_TIMEOUTS;
1305
1488
  var HTTP_HEADERS = {
1306
1489
  CONTENT_TYPE: "Content-Type",
1307
1490
  APPLICATION_JSON: "application/json",
@@ -1360,13 +1543,65 @@ var GroqChatRequestBuilder = class _GroqChatRequestBuilder {
1360
1543
  }
1361
1544
  };
1362
1545
 
1546
+ // src/providers/groq/groq-config-store.ts
1547
+ var GroqConfigStore = class {
1548
+ config = null;
1549
+ initialize(config) {
1550
+ const validation = validateApiKey(config.apiKey, { minLength: 1 });
1551
+ if (!validation.valid) {
1552
+ throw new GroqError("INVALID_API_KEY" /* INVALID_API_KEY */, validation.reason ?? "Invalid API key");
1553
+ }
1554
+ const normalized = {
1555
+ apiKey: config.apiKey.trim(),
1556
+ baseUrl: config.baseUrl ?? DEFAULT_BASE_URL,
1557
+ timeoutMs: config.timeoutMs ?? TIMEOUTS.DEFAULT,
1558
+ ...config.models !== void 0 ? { models: config.models } : {},
1559
+ ...config.textModel !== void 0 ? { textModel: config.textModel } : {}
1560
+ };
1561
+ this.config = normalized;
1562
+ return normalized;
1563
+ }
1564
+ get() {
1565
+ if (!this.config) {
1566
+ throw new GroqError(
1567
+ "MISSING_CONFIG" /* MISSING_CONFIG */,
1568
+ "Client not initialized. Call configureProvider first."
1569
+ );
1570
+ }
1571
+ return this.config;
1572
+ }
1573
+ getOrNull() {
1574
+ return this.config;
1575
+ }
1576
+ isInitialized() {
1577
+ return this.config !== null;
1578
+ }
1579
+ setApiKey(apiKey) {
1580
+ const current = this.get();
1581
+ const validation = validateApiKey(apiKey, { minLength: 1 });
1582
+ if (!validation.valid) {
1583
+ throw new GroqError("INVALID_API_KEY" /* INVALID_API_KEY */, validation.reason ?? "Invalid API key");
1584
+ }
1585
+ this.config = { ...current, apiKey: apiKey.trim() };
1586
+ }
1587
+ reset() {
1588
+ this.config = null;
1589
+ }
1590
+ };
1591
+
1363
1592
  // src/providers/groq/groq-http-transport.ts
1364
1593
  var GroqHttpTransport = class {
1365
- constructor(config) {
1366
- this.config = config;
1594
+ constructor(configSource) {
1595
+ this.configSource = configSource;
1367
1596
  }
1368
- config;
1597
+ configSource;
1369
1598
  pendingAbortControllers = /* @__PURE__ */ new Set();
1599
+ getConfig() {
1600
+ if ("get" in this.configSource && typeof this.configSource.get === "function") {
1601
+ return this.configSource.get();
1602
+ }
1603
+ return this.configSource;
1604
+ }
1370
1605
  cancelPendingRequests() {
1371
1606
  for (const controller of this.pendingAbortControllers) {
1372
1607
  controller.abort();
@@ -1381,7 +1616,7 @@ var GroqHttpTransport = class {
1381
1616
  }
1382
1617
  async streamChatCompletion(request, options = {}) {
1383
1618
  const url = `${this.baseUrl()}${CHAT_COMPLETIONS_PATH}`;
1384
- const timeout = this.config.timeoutMs ?? GROQ_TIMEOUTS.STREAMING;
1619
+ const timeout = this.getConfig().timeoutMs ?? GROQ_TIMEOUTS.STREAMING;
1385
1620
  const controller = new AbortController();
1386
1621
  const timeoutId = setTimeout(() => controller.abort(), timeout);
1387
1622
  const onExternalAbort = () => controller.abort();
@@ -1396,7 +1631,7 @@ var GroqHttpTransport = class {
1396
1631
  });
1397
1632
  if (!response.ok) await this.throwFromErrorResponse(response);
1398
1633
  if (!response.body) {
1399
- throw new AIError2("NETWORK_ERROR" /* NETWORK_ERROR */, "Response body is null");
1634
+ throw new AIError("NETWORK_ERROR" /* NETWORK_ERROR */, "Response body is null");
1400
1635
  }
1401
1636
  return response.body;
1402
1637
  } catch (error) {
@@ -1408,11 +1643,11 @@ var GroqHttpTransport = class {
1408
1643
  }
1409
1644
  }
1410
1645
  baseUrl() {
1411
- return this.config.baseUrl ?? GROQ_BASE_URL;
1646
+ return this.getConfig().baseUrl ?? GROQ_BASE_URL;
1412
1647
  }
1413
1648
  async request(endpoint, body) {
1414
1649
  const url = `${this.baseUrl()}${endpoint}`;
1415
- const timeout = this.config.timeoutMs ?? GROQ_TIMEOUTS.DEFAULT;
1650
+ const timeout = this.getConfig().timeoutMs ?? GROQ_TIMEOUTS.DEFAULT;
1416
1651
  const controller = new AbortController();
1417
1652
  const timeoutId = setTimeout(() => controller.abort(), timeout);
1418
1653
  this.pendingAbortControllers.add(controller);
@@ -1435,7 +1670,7 @@ var GroqHttpTransport = class {
1435
1670
  buildHeaders() {
1436
1671
  return {
1437
1672
  [HTTP_HEADERS.CONTENT_TYPE]: HTTP_HEADERS.APPLICATION_JSON,
1438
- [HTTP_HEADERS.AUTHORIZATION]: `${HTTP_HEADERS.BEARER_PREFIX}${this.config.apiKey}`
1673
+ [HTTP_HEADERS.AUTHORIZATION]: `${HTTP_HEADERS.BEARER_PREFIX}${this.getConfig().apiKey}`
1439
1674
  };
1440
1675
  }
1441
1676
  async throwFromErrorResponse(response) {
@@ -1444,7 +1679,7 @@ var GroqHttpTransport = class {
1444
1679
  status: response.status,
1445
1680
  url: response.url
1446
1681
  };
1447
- throw new AIError2(
1682
+ throw new AIError(
1448
1683
  classifyHttpStatus(response.status),
1449
1684
  errorMessage,
1450
1685
  void 0,
@@ -1452,24 +1687,24 @@ var GroqHttpTransport = class {
1452
1687
  );
1453
1688
  }
1454
1689
  normalizeError(error) {
1455
- if (error instanceof AIError2) return error;
1690
+ if (error instanceof AIError) return error;
1456
1691
  if (error instanceof Error) {
1457
1692
  if (error.name === "AbortError") {
1458
- return new AIError2(
1693
+ return new AIError(
1459
1694
  "ABORT_ERROR" /* ABORT_ERROR */,
1460
1695
  "Request was aborted by the client",
1461
1696
  error
1462
1697
  );
1463
1698
  }
1464
1699
  if (error.name === "TypeError" && error.message.toLowerCase().includes("network")) {
1465
- return new AIError2(
1700
+ return new AIError(
1466
1701
  "NETWORK_ERROR" /* NETWORK_ERROR */,
1467
1702
  "Network error: Unable to connect to the AI service",
1468
1703
  error
1469
1704
  );
1470
1705
  }
1471
1706
  }
1472
- return new AIError2(
1707
+ return new AIError(
1473
1708
  "UNKNOWN_ERROR" /* UNKNOWN_ERROR */,
1474
1709
  error instanceof Error ? error.message : "Unknown error occurred",
1475
1710
  error instanceof Error ? error : void 0
@@ -1537,21 +1772,40 @@ var CompositeExecutor = class {
1537
1772
 
1538
1773
  // src/providers/groq/groq-http-client.ts
1539
1774
  var GroqHttpClient = class {
1775
+ configStore;
1540
1776
  transport;
1541
1777
  executor;
1542
- constructor(config, deps = {}) {
1543
- this.transport = deps.transport ?? new GroqHttpTransport(config);
1778
+ constructor(configOrDeps, maybeDeps = {}) {
1779
+ const isConfig = typeof configOrDeps === "object" && configOrDeps !== null && "apiKey" in configOrDeps && typeof configOrDeps.apiKey === "string";
1780
+ const deps = isConfig ? maybeDeps : configOrDeps ?? {};
1781
+ this.configStore = deps.configStore ?? new GroqConfigStore();
1782
+ if (isConfig) {
1783
+ this.configStore.initialize(configOrDeps);
1784
+ }
1785
+ this.transport = deps.transport ?? new GroqHttpTransport(this.configStore);
1544
1786
  const retryManager2 = deps.retryManager ?? new RetryManager();
1545
1787
  const queue = deps.queue ?? new RequestQueue();
1546
1788
  const deduplicator = deps.deduplicator ?? new RequestDeduplicator();
1547
1789
  this.executor = new CompositeExecutor([
1548
1790
  new DedupeExecutor(deduplicator, {
1549
- generateKey: (request) => deduplicatorKey(request, config.apiKey)
1791
+ generateKey: (request) => deduplicatorKey(request, this.configStore.isInitialized() ? this.configStore.get().apiKey : "")
1550
1792
  }),
1551
1793
  new RetryExecutor(retryManager2),
1552
1794
  new QueueExecutor(queue, QUEUE_PRIORITY_USER_REQUEST)
1553
1795
  ]);
1554
1796
  }
1797
+ initialize(config) {
1798
+ this.configStore.initialize(config);
1799
+ }
1800
+ isInitialized() {
1801
+ return this.configStore.isInitialized();
1802
+ }
1803
+ getApiKey() {
1804
+ return this.configStore.get().apiKey;
1805
+ }
1806
+ setApiKey(apiKey) {
1807
+ this.configStore.setApiKey(apiKey);
1808
+ }
1555
1809
  cancelPendingRequests() {
1556
1810
  this.transport.cancelPendingRequests();
1557
1811
  }
@@ -1564,6 +1818,10 @@ var GroqHttpClient = class {
1564
1818
  streamChatCompletion(request, options) {
1565
1819
  return this.transport.streamChatCompletion(request, options);
1566
1820
  }
1821
+ reset() {
1822
+ this.transport.cancelPendingRequests();
1823
+ this.configStore.reset();
1824
+ }
1567
1825
  };
1568
1826
  function deduplicatorKey(request, apiKey) {
1569
1827
  const { model, messages, stream: _stream, ...rest } = request;
@@ -1620,6 +1878,13 @@ async function consumeGroqStream(stream, callbacks) {
1620
1878
  } catch {
1621
1879
  return;
1622
1880
  }
1881
+ const errorBody = chunk.error;
1882
+ if (errorBody) {
1883
+ throw new AIError(
1884
+ errorBody.message?.toLowerCase().includes("rate limit") ? "RATE_LIMIT_ERROR" /* RATE_LIMIT_ERROR */ : "SERVER_ERROR" /* SERVER_ERROR */,
1885
+ errorBody.message || "Groq stream failed mid-flight"
1886
+ );
1887
+ }
1623
1888
  if (chunk.model) model = chunk.model;
1624
1889
  const choice = chunk.choices?.[0];
1625
1890
  const delta = choice?.delta?.content;
@@ -1634,13 +1899,6 @@ async function consumeGroqStream(stream, callbacks) {
1634
1899
  if (tailUsage) {
1635
1900
  usage = mapGroqUsage(tailUsage);
1636
1901
  }
1637
- const errorBody = chunk.error;
1638
- if (errorBody) {
1639
- throw new AIError2(
1640
- "SERVER_ERROR" /* SERVER_ERROR */,
1641
- errorBody.message || "Groq stream failed mid-flight"
1642
- );
1643
- }
1644
1902
  };
1645
1903
  try {
1646
1904
  while (true) {
@@ -1654,8 +1912,8 @@ async function consumeGroqStream(stream, callbacks) {
1654
1912
  buffer += decoder.decode();
1655
1913
  if (buffer.length > 0) consumeLine(buffer);
1656
1914
  } catch (error) {
1657
- if (error instanceof AIError2) throw error;
1658
- throw new AIError2(
1915
+ if (error instanceof AIError) throw error;
1916
+ throw new AIError(
1659
1917
  "NETWORK_ERROR" /* NETWORK_ERROR */,
1660
1918
  "Stream was interrupted",
1661
1919
  error instanceof Error ? error : void 0
@@ -1713,7 +1971,7 @@ var GroqProvider = class {
1713
1971
  const choice = response.choices?.[0];
1714
1972
  const text = choice?.message?.content ?? "";
1715
1973
  if (text.length === 0) {
1716
- throw new AIError2(
1974
+ throw new AIError(
1717
1975
  "SERVER_ERROR" /* SERVER_ERROR */,
1718
1976
  "No content generated from Groq API",
1719
1977
  void 0,
@@ -1735,7 +1993,7 @@ var GroqProvider = class {
1735
1993
  const messages = toGroqMessages(request.messages, request.systemPrompt);
1736
1994
  const lastUserIndex = findLastIndex2(messages, (m) => m.role === "user");
1737
1995
  if (lastUserIndex === -1) {
1738
- throw new AIError2(
1996
+ throw new AIError(
1739
1997
  "UNKNOWN_ERROR" /* UNKNOWN_ERROR */,
1740
1998
  "generateStructured requires at least one user message"
1741
1999
  );
@@ -1749,7 +2007,7 @@ var GroqProvider = class {
1749
2007
  const choice = response.choices?.[0];
1750
2008
  const text = choice?.message?.content ?? "";
1751
2009
  if (text.length === 0) {
1752
- throw new AIError2(
2010
+ throw new AIError(
1753
2011
  "SERVER_ERROR" /* SERVER_ERROR */,
1754
2012
  "No content generated from Groq API",
1755
2013
  void 0,
@@ -1776,7 +2034,7 @@ var GroqProvider = class {
1776
2034
  try {
1777
2035
  const outcome = await consumeGroqStream(streamBody, callbacks);
1778
2036
  if (outcome.text.length === 0) {
1779
- throw new AIError2(
2037
+ throw new AIError(
1780
2038
  "SERVER_ERROR" /* SERVER_ERROR */,
1781
2039
  "No content generated from Groq API",
1782
2040
  void 0,
@@ -1907,7 +2165,9 @@ var GeminiProvider = class extends BaseProvider {
1907
2165
  headers: this.authHeaders()
1908
2166
  }
1909
2167
  );
1910
- return response.ok ? "healthy" : "unavailable";
2168
+ if (response.ok) return "healthy";
2169
+ if (response.status === 401 || response.status === 403) return "unavailable";
2170
+ return "degraded";
1911
2171
  } catch {
1912
2172
  return "unavailable";
1913
2173
  }
@@ -1918,6 +2178,8 @@ var GeminiProvider = class extends BaseProvider {
1918
2178
  async generate(request) {
1919
2179
  try {
1920
2180
  const model = request.model ?? this.resolveModel(request.tier ?? "balanced");
2181
+ const systemMessages = request.messages.filter((m) => m.role === "system");
2182
+ const systemPrompt = request.systemPrompt && request.systemPrompt.length > 0 ? request.systemPrompt : systemMessages.length > 0 ? systemMessages.map((m) => m.content).join("\n") : void 0;
1921
2183
  const response = await this.withRetry(async () => {
1922
2184
  return await this.fetchWithTimeout(
1923
2185
  `${this.config.baseUrl}/models/${model}:generateContent`,
@@ -1925,24 +2187,30 @@ var GeminiProvider = class extends BaseProvider {
1925
2187
  method: "POST",
1926
2188
  headers: this.authHeaders(),
1927
2189
  body: JSON.stringify({
1928
- systemInstruction: request.systemPrompt && request.systemPrompt.length > 0 ? { parts: [{ text: request.systemPrompt }] } : void 0,
2190
+ systemInstruction: systemPrompt && systemPrompt.length > 0 ? { parts: [{ text: systemPrompt }] } : void 0,
1929
2191
  contents: toGeminiContents(request.messages),
1930
2192
  generationConfig: {
1931
- temperature: request.temperature || 0.7,
1932
- maxOutputTokens: request.maxTokens || 2048,
1933
- topP: request.topP || 0.9
2193
+ temperature: request.temperature ?? 0.7,
2194
+ maxOutputTokens: request.maxTokens ?? 2048,
2195
+ topP: request.topP ?? 0.9
1934
2196
  }
1935
2197
  })
1936
2198
  }
1937
2199
  );
1938
2200
  });
1939
2201
  if (!response.ok) {
1940
- throw new Error(`Gemini API error: ${response.statusText}`);
2202
+ const message = await extractErrorMessageFromResponse(response);
2203
+ throw new AIError(
2204
+ classifyHttpStatus(response.status),
2205
+ `Gemini API error (${response.status}): ${message}`,
2206
+ void 0,
2207
+ { status: response.status, model }
2208
+ );
1941
2209
  }
1942
2210
  const data = await response.json();
1943
- const text = data.candidates[0]?.content?.parts?.map((part) => part.text ?? "").join("") ?? "";
2211
+ const text = data.candidates?.[0]?.content?.parts?.map((part) => part.text ?? "").join("") ?? "";
1944
2212
  if (text.length === 0) {
1945
- throw new AIError2("SERVER_ERROR" /* SERVER_ERROR */, "No content generated from Gemini API", void 0, { model });
2213
+ throw new AIError("SERVER_ERROR" /* SERVER_ERROR */, "No content generated from Gemini API", void 0, { model });
1946
2214
  }
1947
2215
  return {
1948
2216
  text,
@@ -1953,11 +2221,11 @@ var GeminiProvider = class extends BaseProvider {
1953
2221
  } : void 0,
1954
2222
  model,
1955
2223
  providerId: this.id,
1956
- finishReason: mapGeminiFinishReason(data.candidates[0]?.finishReason)
2224
+ finishReason: mapGeminiFinishReason(data.candidates?.[0]?.finishReason)
1957
2225
  };
1958
2226
  } catch (error) {
1959
- if (error instanceof AIError2) throw error;
1960
- throw new AIError2(
2227
+ if (error instanceof AIError) throw error;
2228
+ throw new AIError(
1961
2229
  "UNKNOWN_ERROR" /* UNKNOWN_ERROR */,
1962
2230
  `Gemini text generation failed: ${error instanceof Error ? error.message : "Unknown error"}`,
1963
2231
  error instanceof Error ? error : void 0
@@ -1968,7 +2236,7 @@ var GeminiProvider = class extends BaseProvider {
1968
2236
  const messages = [...request.messages];
1969
2237
  const lastUserIndex = findLastIndex3(messages, (m) => m.role === "user");
1970
2238
  if (lastUserIndex === -1) {
1971
- throw new AIError2(
2239
+ throw new AIError(
1972
2240
  "UNKNOWN_ERROR" /* UNKNOWN_ERROR */,
1973
2241
  "generateStructured requires at least one user message"
1974
2242
  );
@@ -2005,12 +2273,18 @@ var GeminiProvider = class extends BaseProvider {
2005
2273
  );
2006
2274
  });
2007
2275
  if (!response.ok) {
2008
- throw new Error(`Gemini API error: ${response.statusText}`);
2276
+ const message = await extractErrorMessageFromResponse(response);
2277
+ throw new AIError(
2278
+ classifyHttpStatus(response.status),
2279
+ `Gemini image generation error (${response.status}): ${message}`,
2280
+ void 0,
2281
+ { status: response.status }
2282
+ );
2009
2283
  }
2010
2284
  const data = await response.json();
2011
- const imageData = data.candidates[0]?.content?.parts[0]?.inlineData;
2285
+ const imageData = data.candidates?.[0]?.content?.parts?.[0]?.inlineData;
2012
2286
  if (!imageData) {
2013
- throw new Error("No image data in response");
2287
+ throw new AIError("SERVER_ERROR" /* SERVER_ERROR */, "No image data in Gemini response");
2014
2288
  }
2015
2289
  const url = `data:${imageData.mimeType};base64,${imageData.data}`;
2016
2290
  return {
@@ -2329,17 +2603,70 @@ var PrunaProvider = class {
2329
2603
  async estimateCost(_request) {
2330
2604
  return 1;
2331
2605
  }
2332
- async generateImage(_request) {
2333
- throw new AIError2("UNKNOWN_ERROR" /* UNKNOWN_ERROR */, "Use generateWithPruna instead");
2606
+ async generateImage(request) {
2607
+ let finalUrl;
2608
+ const model = this.config.models?.textToImage;
2609
+ for await (const stage of generateWithPruna(
2610
+ { prompt: request.prompt, model, quality: request.quality },
2611
+ this.config.apiKey
2612
+ )) {
2613
+ if (stage.error) throw stage.error;
2614
+ if (stage.result?.url) finalUrl = stage.result.url;
2615
+ }
2616
+ if (!finalUrl) {
2617
+ throw new AIError("SERVER_ERROR" /* SERVER_ERROR */, "No image generated by Pruna AI");
2618
+ }
2619
+ return {
2620
+ id: Date.now().toString(),
2621
+ type: "image",
2622
+ url: finalUrl,
2623
+ metadata: { provider: this.id, model: model ?? "p-image" },
2624
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
2625
+ };
2334
2626
  }
2335
- async generateVideo(_request) {
2336
- throw new AIError2("UNKNOWN_ERROR" /* UNKNOWN_ERROR */, "Use generateWithPruna instead");
2627
+ async generateVideo(request) {
2628
+ let finalUrl;
2629
+ const model = this.config.models?.textToVideo;
2630
+ for await (const stage of generateWithPruna(
2631
+ { prompt: request.prompt, model, duration: request.duration, aspectRatio: request.aspectRatio },
2632
+ this.config.apiKey
2633
+ )) {
2634
+ if (stage.error) throw stage.error;
2635
+ if (stage.result?.url) finalUrl = stage.result.url;
2636
+ }
2637
+ if (!finalUrl) {
2638
+ throw new AIError("SERVER_ERROR" /* SERVER_ERROR */, "No video generated by Pruna AI");
2639
+ }
2640
+ return {
2641
+ id: Date.now().toString(),
2642
+ type: "video",
2643
+ url: finalUrl,
2644
+ metadata: { provider: this.id, model: model ?? "p-video" },
2645
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
2646
+ };
2337
2647
  }
2338
- async imageToVideo(_request) {
2339
- throw new AIError2("UNKNOWN_ERROR" /* UNKNOWN_ERROR */, "Use generateWithPruna instead");
2648
+ async imageToVideo(request) {
2649
+ let finalUrl;
2650
+ for await (const stage of generateWithPruna(
2651
+ { image: request.imageUrl, prompt: request.prompt, duration: request.duration, motion: request.motion },
2652
+ this.config.apiKey
2653
+ )) {
2654
+ if (stage.error) throw stage.error;
2655
+ if (stage.result?.url) finalUrl = stage.result.url;
2656
+ }
2657
+ if (!finalUrl) {
2658
+ throw new AIError("SERVER_ERROR" /* SERVER_ERROR */, "No video generated from image by Pruna AI");
2659
+ }
2660
+ return {
2661
+ id: Date.now().toString(),
2662
+ type: "video",
2663
+ url: finalUrl,
2664
+ metadata: { provider: this.id, model: "p-image-to-video" },
2665
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
2666
+ };
2340
2667
  }
2341
2668
  async videoToVideo(_request) {
2342
- throw new AIError2("UNKNOWN_ERROR" /* UNKNOWN_ERROR */, "Use generateWithPruna instead");
2669
+ throw new AIError("CLIENT_ERROR" /* CLIENT_ERROR */, "Pruna AI provider does not support video-to-video conversion");
2343
2670
  }
2344
2671
  };
2345
2672
  function createPrunaProvider(config) {
@@ -2371,6 +2698,504 @@ function createAI(config = {}) {
2371
2698
  return factory;
2372
2699
  }
2373
2700
 
2374
- export { ANTHROPIC_API_VERSION, ANTHROPIC_DEFAULT_MAX_TOKENS, ANTHROPIC_MODELS, ANTHROPIC_TIERS, AnthropicHttpTransport, AnthropicProvider, AnthropicRequestBuilder, BaseProvider, FalProvider, GEMINI_TIERS, GROQ_MODELS, GROQ_TIERS, GeminiProvider, GroqChatRequestBuilder, GroqHttpClient, GroqHttpTransport, GroqProvider, ProviderError, ProviderFactory, PrunaProvider, consumeAnthropicStream, consumeGroqStream, createAI, createFalProvider, createGeminiProvider, createPrunaProvider, defaultEffortForModel, extractContentFromSsePayload, generateWithPruna, isMediaProvider, isTextProvider, mapAnthropicStopReason, toGroqMessages };
2701
+ // src/providers/groq/groq-messages.ts
2702
+ var createUserMessage = (content) => ({
2703
+ role: "user",
2704
+ content
2705
+ });
2706
+ var createAssistantMessage = (content) => ({
2707
+ role: "assistant",
2708
+ content
2709
+ });
2710
+ var createSystemMessage = (content) => ({
2711
+ role: "system",
2712
+ content
2713
+ });
2714
+ var createMessage = (role, content) => ({
2715
+ role,
2716
+ content
2717
+ });
2718
+ var promptToMessages = (prompt) => [
2719
+ createUserMessage(prompt)
2720
+ ];
2721
+ var extractTextFromMessages = (messages) => messages.map((m) => m.content).join("\n\n");
2722
+ var formatMessagesForDisplay = (messages) => messages.map((m) => `[${m.role.toUpperCase()}]: ${m.content}`).join("\n\n---\n\n");
2723
+
2724
+ // src/providers/groq/groq-structured-json.ts
2725
+ async function requestStructuredJson(client, request) {
2726
+ const chatRequest = {
2727
+ model: request.model,
2728
+ messages: [createUserMessage(request.instructions)],
2729
+ temperature: request.temperature,
2730
+ max_tokens: request.maxTokens,
2731
+ response_format: { type: "json_object" },
2732
+ ...request.reasoningEffort !== void 0 ? { reasoning_effort: request.reasoningEffort } : {}
2733
+ };
2734
+ const response = await client.postChatCompletion(chatRequest);
2735
+ const content = response.choices[0]?.message?.content ?? "";
2736
+ const candidate = extractJsonCandidate(content);
2737
+ try {
2738
+ return JSON.parse(candidate);
2739
+ } catch (err) {
2740
+ throw new GroqError(
2741
+ "UNKNOWN_ERROR" /* UNKNOWN_ERROR */,
2742
+ "model response contained no parsable json object",
2743
+ err instanceof Error ? err : void 0
2744
+ );
2745
+ }
2746
+ }
2747
+
2748
+ // src/providers/groq/text-generation.service.ts
2749
+ var STRUCTURED_OUTPUT_TEMPERATURE = 0.1;
2750
+ var TextGenerationService = class {
2751
+ constructor(httpClient, cache = new CacheManager(), defaultModel = DEFAULT_MODELS.TEXT) {
2752
+ this.httpClient = httpClient;
2753
+ this.cache = cache;
2754
+ this.defaultModel = defaultModel;
2755
+ }
2756
+ httpClient;
2757
+ cache;
2758
+ defaultModel;
2759
+ id = "groq-text-generation";
2760
+ name = "Groq Text Generation";
2761
+ capabilities = ["text"];
2762
+ jsonParser = new JsonResponseParser();
2763
+ generateCompletion(prompt, options = {}) {
2764
+ const model = options.model ?? this.defaultModel;
2765
+ return this.executeWithCache(
2766
+ {
2767
+ type: CACHE_KEY_TYPE_COMPLETION,
2768
+ model,
2769
+ prompt,
2770
+ config: options.generationConfig
2771
+ },
2772
+ async () => {
2773
+ const request = GroqChatRequestBuilder.for(model).withMessages([createUserMessage(prompt)]).withConfig(options.generationConfig).build();
2774
+ const response = await this.httpClient.postChatCompletion(request);
2775
+ return this.extractContent(response.choices[0]?.message?.content);
2776
+ }
2777
+ );
2778
+ }
2779
+ generateChatCompletion(messages, options = {}) {
2780
+ const model = options.model ?? this.defaultModel;
2781
+ return this.executeWithCache(
2782
+ {
2783
+ type: CACHE_KEY_TYPE_CHAT,
2784
+ model,
2785
+ messages: [...messages],
2786
+ config: options.generationConfig
2787
+ },
2788
+ async () => {
2789
+ const request = GroqChatRequestBuilder.for(model).withMessages(messages).withConfig(options.generationConfig).build();
2790
+ const response = await this.httpClient.postChatCompletion(request);
2791
+ return this.extractContent(response.choices[0]?.message?.content);
2792
+ }
2793
+ );
2794
+ }
2795
+ generateStructured(promptOrRequest, options) {
2796
+ if (typeof promptOrRequest === "string") {
2797
+ const model2 = options?.model ?? this.defaultModel;
2798
+ const jsonPrompt = StructuredPromptBuilder.for(promptOrRequest).withSchema(options?.schema).build();
2799
+ const request = GroqChatRequestBuilder.for(model2).withMessages([createUserMessage(jsonPrompt)]).withConfig({
2800
+ ...options?.generationConfig,
2801
+ temperature: STRUCTURED_OUTPUT_TEMPERATURE
2802
+ }).build();
2803
+ return this.httpClient.postChatCompletion(request).then((response) => {
2804
+ const content = this.extractContent(response.choices[0]?.message?.content);
2805
+ return this.jsonParser.parse(content);
2806
+ });
2807
+ }
2808
+ const req = promptOrRequest;
2809
+ const model = req.model ?? (req.tier ? this.resolveModel(req.tier) : this.defaultModel);
2810
+ const schemaObj = req.schema;
2811
+ const messages = [];
2812
+ if (req.systemPrompt) {
2813
+ messages.push({ role: "system", content: req.systemPrompt });
2814
+ }
2815
+ for (const m of req.messages) {
2816
+ messages.push({ role: m.role, content: m.content });
2817
+ }
2818
+ const lastMsg = messages[messages.length - 1];
2819
+ if (lastMsg) {
2820
+ const structuredPrompt = StructuredPromptBuilder.for(lastMsg.content).withSchema(schemaObj).build();
2821
+ messages[messages.length - 1] = { ...lastMsg, content: structuredPrompt };
2822
+ }
2823
+ const groqReq = GroqChatRequestBuilder.for(model).withMessages(messages).withConfig({
2824
+ temperature: req.temperature ?? STRUCTURED_OUTPUT_TEMPERATURE,
2825
+ maxTokens: req.maxTokens
2826
+ }).build();
2827
+ return this.httpClient.postChatCompletion(groqReq).then((response) => {
2828
+ const content = this.extractContent(response.choices[0]?.message?.content);
2829
+ const parsed = this.jsonParser.parse(content);
2830
+ const genResult = {
2831
+ text: content,
2832
+ model: response.model,
2833
+ providerId: this.id,
2834
+ finishReason: response.choices[0]?.finish_reason ?? "stop",
2835
+ usage: response.usage ? {
2836
+ inputTokens: response.usage.prompt_tokens,
2837
+ outputTokens: response.usage.completion_tokens,
2838
+ totalTokens: response.usage.total_tokens
2839
+ } : void 0
2840
+ };
2841
+ return { value: parsed, result: genResult };
2842
+ });
2843
+ }
2844
+ streamCompletion(prompt, callbacks, options = {}) {
2845
+ return this.streamChatCompletion([createUserMessage(prompt)], callbacks, options);
2846
+ }
2847
+ async streamChatCompletion(messages, callbacks, options = {}) {
2848
+ const model = options.model ?? this.defaultModel;
2849
+ const request = GroqChatRequestBuilder.for(model).withMessages(messages).withConfig(options.generationConfig).withStream(true).build();
2850
+ const parser = new StreamingResponseParser(
2851
+ {
2852
+ onChunk: (chunk) => callbacks.onChunk?.(chunk)
2853
+ },
2854
+ extractContentFromSsePayload
2855
+ );
2856
+ try {
2857
+ const stream = await this.httpClient.streamChatCompletion(request, {
2858
+ signal: options.signal
2859
+ });
2860
+ const fullText = await parser.parse(stream);
2861
+ callbacks.onComplete?.(fullText);
2862
+ return fullText;
2863
+ } catch (error) {
2864
+ const err = error instanceof Error ? error : new Error(String(error));
2865
+ callbacks.onError?.(err);
2866
+ throw err;
2867
+ }
2868
+ }
2869
+ clearCache() {
2870
+ this.cache.clear();
2871
+ }
2872
+ // --- ITextProvider implementation ---
2873
+ resolveModel(tier) {
2874
+ return GROQ_TIERS[tier] ?? this.defaultModel;
2875
+ }
2876
+ async generate(request) {
2877
+ const messages = [];
2878
+ if (request.systemPrompt) {
2879
+ messages.push({ role: "system", content: request.systemPrompt });
2880
+ }
2881
+ for (const m of request.messages) {
2882
+ messages.push({ role: m.role, content: m.content });
2883
+ }
2884
+ const model = request.model ?? (request.tier ? this.resolveModel(request.tier) : this.defaultModel);
2885
+ const groqReq = GroqChatRequestBuilder.for(model).withMessages(messages).withConfig({
2886
+ temperature: request.temperature,
2887
+ maxTokens: request.maxTokens
2888
+ }).build();
2889
+ const resp = await this.httpClient.postChatCompletion(groqReq);
2890
+ const content = resp.choices[0]?.message?.content ?? "";
2891
+ return {
2892
+ text: content,
2893
+ finishReason: resp.choices[0]?.finish_reason ?? "stop",
2894
+ model: resp.model,
2895
+ providerId: this.id,
2896
+ usage: resp.usage ? {
2897
+ inputTokens: resp.usage.prompt_tokens,
2898
+ outputTokens: resp.usage.completion_tokens,
2899
+ totalTokens: resp.usage.total_tokens
2900
+ } : void 0
2901
+ };
2902
+ }
2903
+ async stream(request, callbacks) {
2904
+ const messages = [];
2905
+ if (request.systemPrompt) {
2906
+ messages.push({ role: "system", content: request.systemPrompt });
2907
+ }
2908
+ for (const m of request.messages) {
2909
+ messages.push({ role: m.role, content: m.content });
2910
+ }
2911
+ const model = request.model ?? (request.tier ? this.resolveModel(request.tier) : this.defaultModel);
2912
+ const text = await this.streamChatCompletion(
2913
+ messages,
2914
+ {
2915
+ onChunk: callbacks.onChunk,
2916
+ onError: callbacks.onError
2917
+ },
2918
+ {
2919
+ model,
2920
+ signal: request.signal,
2921
+ generationConfig: {
2922
+ temperature: request.temperature,
2923
+ maxTokens: request.maxTokens
2924
+ }
2925
+ }
2926
+ );
2927
+ const result = {
2928
+ text,
2929
+ finishReason: "stop",
2930
+ model,
2931
+ providerId: this.id
2932
+ };
2933
+ callbacks.onComplete?.(result);
2934
+ return result;
2935
+ }
2936
+ async countTokens(text) {
2937
+ return Math.max(1, Math.ceil(text.length / 4));
2938
+ }
2939
+ async estimateCost(_request) {
2940
+ return 0;
2941
+ }
2942
+ async healthCheck() {
2943
+ try {
2944
+ await this.generateCompletion("ping", {
2945
+ generationConfig: { maxTokens: 1 }
2946
+ });
2947
+ return "healthy";
2948
+ } catch {
2949
+ return "unavailable";
2950
+ }
2951
+ }
2952
+ async executeWithCache(params, factory) {
2953
+ const key = this.cache.generateKey(params);
2954
+ const cached = this.cache.get(key);
2955
+ if (cached !== null) {
2956
+ return cached;
2957
+ }
2958
+ const result = await factory();
2959
+ this.cache.set(key, result);
2960
+ return result;
2961
+ }
2962
+ extractContent(content) {
2963
+ if (!content) {
2964
+ throw new GroqError(
2965
+ "UNKNOWN_ERROR" /* UNKNOWN_ERROR */,
2966
+ "No content generated from Groq API"
2967
+ );
2968
+ }
2969
+ return content;
2970
+ }
2971
+ };
2972
+
2973
+ // src/core/calculations/random-base36-suffix.ts
2974
+ var BASE_36_RADIX = 36;
2975
+ var DECIMAL_OFFSET = 2;
2976
+ function generateRandomBase36Suffix(length, random = Math.random) {
2977
+ if (length <= 0) return "";
2978
+ const raw = random().toString(BASE_36_RADIX).slice(DECIMAL_OFFSET);
2979
+ if (raw.length >= length) return raw.slice(0, length);
2980
+ let result = raw;
2981
+ while (result.length < length) {
2982
+ result += random().toString(BASE_36_RADIX).slice(DECIMAL_OFFSET);
2983
+ }
2984
+ return result.slice(0, length);
2985
+ }
2986
+
2987
+ // src/chat/utils/chat-id-generator.ts
2988
+ var ID_PART_DELIMITER = "-";
2989
+ var ChatIdGenerator = class {
2990
+ counter = 0;
2991
+ randomLength;
2992
+ constructor(randomLength = CHAT_MESSAGE_ID_SUFFIX_LENGTH) {
2993
+ this.randomLength = randomLength;
2994
+ }
2995
+ next(sender) {
2996
+ this.counter += 1;
2997
+ const random = generateRandomBase36Suffix(this.randomLength);
2998
+ return [
2999
+ MESSAGE_ID_PREFIX,
3000
+ Date.now(),
3001
+ sender,
3002
+ this.counter,
3003
+ random
3004
+ ].join(ID_PART_DELIMITER);
3005
+ }
3006
+ };
3007
+ var chatIdGenerator = new ChatIdGenerator();
3008
+
3009
+ // src/chat/utils/message-formatter.ts
3010
+ var MessageFormatter = class {
3011
+ constructor(idGenerator = chatIdGenerator) {
3012
+ this.idGenerator = idGenerator;
3013
+ }
3014
+ idGenerator;
3015
+ /** Maps chat-domain history to neutral core messages; system senders are
3016
+ * dropped — the system prompt travels on the request's own slot. */
3017
+ toCoreMessages(messages) {
3018
+ const coreMessages = [];
3019
+ for (const message of messages) {
3020
+ if (message.sender === "system") continue;
3021
+ coreMessages.push({
3022
+ role: message.sender === "user" ? USER_ROLE : ASSISTANT_ROLE,
3023
+ content: message.content
3024
+ });
3025
+ }
3026
+ return coreMessages;
3027
+ }
3028
+ toChatMessage(content, sender) {
3029
+ return {
3030
+ id: this.idGenerator.next(sender),
3031
+ sender,
3032
+ content: content.trim(),
3033
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
3034
+ type: "text"
3035
+ };
3036
+ }
3037
+ };
3038
+ var messageFormatter = new MessageFormatter();
3039
+
3040
+ // src/chat/utils/prompt-strategy.ts
3041
+ var DEFAULT_PROMPT_STRATEGY = {
3042
+ resolve: (config) => {
3043
+ if (config.systemPrompt && config.systemPrompt.length > 0) {
3044
+ return config.systemPrompt;
3045
+ }
3046
+ return void 0;
3047
+ }
3048
+ };
3049
+
3050
+ // src/chat/services/chat.service.ts
3051
+ var DEFAULT_CONFIG = {
3052
+ temperature: 0.8,
3053
+ maxTokens: 500
3054
+ };
3055
+ var ChatService = class {
3056
+ constructor(engine) {
3057
+ this.engine = engine;
3058
+ }
3059
+ engine;
3060
+ config = DEFAULT_CONFIG;
3061
+ promptStrategy = DEFAULT_PROMPT_STRATEGY;
3062
+ storage = null;
3063
+ initialize(config) {
3064
+ this.config = { ...DEFAULT_CONFIG, ...config };
3065
+ }
3066
+ updateConfig(config) {
3067
+ this.config = { ...this.config, ...config };
3068
+ }
3069
+ setPromptStrategy(strategy) {
3070
+ this.promptStrategy = strategy;
3071
+ }
3072
+ setStorage(storage) {
3073
+ this.storage = storage;
3074
+ }
3075
+ async getConversation(conversationId) {
3076
+ const storage = this.requireStorage();
3077
+ const messages = await storage.getMessages(conversationId);
3078
+ if (messages.length === 0) return null;
3079
+ const last = messages[messages.length - 1];
3080
+ const first = messages[0];
3081
+ const companionId = String(first.metadata?.["companionId"] ?? "unknown");
3082
+ const companionName = String(first.metadata?.["companionName"] ?? "Unknown");
3083
+ return {
3084
+ id: conversationId,
3085
+ companionId,
3086
+ companionName,
3087
+ lastMessage: last,
3088
+ messageCount: messages.length,
3089
+ updatedAt: last.timestamp
3090
+ };
3091
+ }
3092
+ async getConversations() {
3093
+ if (this.storage && "getConversations" in this.storage && typeof this.storage.getConversations === "function") {
3094
+ return this.storage.getConversations();
3095
+ }
3096
+ return [];
3097
+ }
3098
+ async sendMessage(input, signal) {
3099
+ const userMessage = {
3100
+ ...messageFormatter.toChatMessage(input.text, "user"),
3101
+ type: input.type ?? "text",
3102
+ ...input.imageUrl !== void 0 ? { imageUrl: input.imageUrl } : {}
3103
+ };
3104
+ const aiResponse = await this.generateAIResponse(
3105
+ input.companionId,
3106
+ input.text,
3107
+ [],
3108
+ signal
3109
+ );
3110
+ return { userMessage, aiResponse };
3111
+ }
3112
+ async generateAIResponse(companionId, userMessage, context = [], signal) {
3113
+ const result = await this.run({
3114
+ ...this.buildRequest(userMessage, context, signal)
3115
+ });
3116
+ return {
3117
+ ...messageFormatter.toChatMessage(result.text, "assistant"),
3118
+ metadata: {
3119
+ companionId,
3120
+ model: result.model,
3121
+ usage: {
3122
+ input_tokens: result.usage?.inputTokens,
3123
+ output_tokens: result.usage?.outputTokens,
3124
+ total_tokens: result.usage?.totalTokens
3125
+ },
3126
+ finishReason: result.finishReason
3127
+ }
3128
+ };
3129
+ }
3130
+ async generateStreamingResponse(userMessage, context, onChunk, signal) {
3131
+ const request = this.buildRequest(userMessage, context, signal);
3132
+ const result = await this.runStream(request, { onChunk });
3133
+ return result.text;
3134
+ }
3135
+ buildRequest(userMessage, context, signal) {
3136
+ const messages = messageFormatter.toCoreMessages(context);
3137
+ messages.push({ role: "user", content: userMessage });
3138
+ return {
3139
+ messages,
3140
+ systemPrompt: this.resolveSystemPrompt(),
3141
+ model: this.config.model,
3142
+ tier: this.config.tier,
3143
+ temperature: this.config.temperature,
3144
+ maxTokens: this.config.maxTokens,
3145
+ ...signal !== void 0 ? { signal } : {}
3146
+ };
3147
+ }
3148
+ resolveSystemPrompt() {
3149
+ const prompt = this.promptStrategy.resolve(this.config);
3150
+ return prompt && prompt.length > 0 ? prompt : void 0;
3151
+ }
3152
+ async run(request) {
3153
+ if (this.engine instanceof ProviderFactory) {
3154
+ return this.engine.chat(request);
3155
+ }
3156
+ return this.engine.generate(request);
3157
+ }
3158
+ async runStream(request, callbacks) {
3159
+ if (this.engine instanceof ProviderFactory) {
3160
+ return this.engine.chatStream(request, callbacks);
3161
+ }
3162
+ return this.engine.stream(request, callbacks);
3163
+ }
3164
+ requireStorage() {
3165
+ if (!this.storage) {
3166
+ throw new AIError(
3167
+ "MISSING_CONFIG" /* MISSING_CONFIG */,
3168
+ "ChatService.getConversation requires setStorage() to be called first."
3169
+ );
3170
+ }
3171
+ return this.storage;
3172
+ }
3173
+ };
3174
+
3175
+ // src/providers/groq/groq-container.ts
3176
+ var container = null;
3177
+ function configureProvider(config) {
3178
+ const configStore = new GroqConfigStore();
3179
+ const transport = new GroqHttpTransport(configStore);
3180
+ const httpClient = new GroqHttpClient({ configStore, transport });
3181
+ httpClient.initialize(config);
3182
+ const textGeneration = new TextGenerationService(
3183
+ httpClient,
3184
+ new CacheManager(),
3185
+ config.textModel
3186
+ );
3187
+ const chat = new ChatService(textGeneration);
3188
+ container = { groqHttpClient: httpClient, textGeneration, chat };
3189
+ return container;
3190
+ }
3191
+ function getProvider() {
3192
+ return container;
3193
+ }
3194
+ function resetProvider() {
3195
+ container?.groqHttpClient.reset();
3196
+ container = null;
3197
+ }
3198
+
3199
+ export { ANTHROPIC_API_VERSION, ANTHROPIC_DEFAULT_MAX_TOKENS, ANTHROPIC_MODELS, ANTHROPIC_TIERS, API_ENDPOINTS, AnthropicHttpTransport, AnthropicProvider, AnthropicRequestBuilder, BaseProvider, DEFAULT_BASE_URL, DEFAULT_GENERATION_CONFIG, DEFAULT_MODELS, FalProvider, GEMINI_TIERS, GROQ_MODELS, GROQ_TIERS, GeminiProvider, GroqChatRequestBuilder, GroqConfigStore, GroqHttpClient, GroqHttpTransport, GroqProvider, ProviderError, ProviderFactory, PrunaProvider, TIMEOUTS, TextGenerationService, configureProvider, consumeAnthropicStream, consumeGroqStream, createAI, createAssistantMessage, createFalProvider, createGeminiProvider, createMessage, createPrunaProvider, createSystemMessage, createUserMessage, defaultEffortForModel, extractContentFromSsePayload, extractTextFromMessages, formatMessagesForDisplay, generateWithPruna, getProvider, isMediaProvider, isTextProvider, mapAnthropicStopReason, promptToMessages, requestStructuredJson, resetProvider, toGroqMessages };
2375
3200
  //# sourceMappingURL=index.js.map
2376
3201
  //# sourceMappingURL=index.js.map