@umituz/web-ai 1.0.0 → 1.0.1

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 (53) 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 +393 -186
  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 +393 -186
  18. package/dist/content/index.js.map +1 -1
  19. package/dist/core/index.cjs +112 -15
  20. package/dist/core/index.cjs.map +1 -1
  21. package/dist/core/index.d.cts +5 -33
  22. package/dist/core/index.d.ts +5 -33
  23. package/dist/core/index.js +111 -16
  24. package/dist/core/index.js.map +1 -1
  25. package/dist/{create-ai-DGZFBOA1.d.cts → create-ai-DOI9jD3v.d.cts} +4 -21
  26. package/dist/{create-ai-BhNaz86i.d.ts → create-ai-DXEoke8-.d.ts} +4 -21
  27. package/dist/hooks/index.cjs +359 -169
  28. package/dist/hooks/index.cjs.map +1 -1
  29. package/dist/hooks/index.d.cts +6 -6
  30. package/dist/hooks/index.d.ts +6 -6
  31. package/dist/hooks/index.js +359 -169
  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 +245 -94
  36. package/dist/index.cjs.map +1 -1
  37. package/dist/index.d.cts +6 -6
  38. package/dist/index.d.ts +6 -6
  39. package/dist/index.js +244 -95
  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 +281 -124
  44. package/dist/providers/index.cjs.map +1 -1
  45. package/dist/providers/index.d.cts +11 -12
  46. package/dist/providers/index.d.ts +11 -12
  47. package/dist/providers/index.js +281 -124
  48. package/dist/providers/index.js.map +1 -1
  49. package/dist/wizard/index.d.cts +1 -1
  50. package/dist/wizard/index.d.ts +1 -1
  51. package/package.json +4 -1
  52. package/dist/index-DjX9ZKuM.d.cts +0 -14
  53. 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,32 +182,6 @@ 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;
119
- 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
- };
137
- }
138
- };
139
-
140
185
  // src/core/errors/error-classifier.ts
141
186
  var DEFAULT_RETRYABLE_TYPES = /* @__PURE__ */ new Set([
142
187
  "NETWORK_ERROR" /* NETWORK_ERROR */,
@@ -262,7 +307,7 @@ var ProviderFactory = class {
262
307
  }
263
308
  }
264
309
  }
265
- const failure = new AIError2(
310
+ const failure = new AIError(
266
311
  "SERVER_ERROR" /* SERVER_ERROR */,
267
312
  `All providers failed for capability 'text'`,
268
313
  lastError instanceof Error ? lastError : void 0
@@ -276,12 +321,16 @@ var ProviderFactory = class {
276
321
  let wake = null;
277
322
  let finished = false;
278
323
  let failure;
324
+ const controller = new AbortController();
325
+ const onAbort = () => controller.abort();
326
+ request.signal?.addEventListener("abort", onAbort);
327
+ const effectiveRequest = { ...request, signal: controller.signal };
279
328
  const notify = () => {
280
329
  const resolve = wake;
281
330
  wake = null;
282
331
  resolve?.();
283
332
  };
284
- const completion = this.chatStream(request, {
333
+ const completion = this.chatStream(effectiveRequest, {
285
334
  onChunk: (chunk) => {
286
335
  queue.push(chunk);
287
336
  notify();
@@ -297,18 +346,23 @@ var ProviderFactory = class {
297
346
  notify();
298
347
  }
299
348
  );
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();
349
+ try {
350
+ while (!finished || queue.length > 0) {
351
+ if (queue.length === 0 && !finished) {
352
+ await new Promise((resolve) => {
353
+ wake = resolve;
354
+ });
355
+ }
356
+ while (queue.length > 0) {
357
+ yield queue.shift();
358
+ }
359
+ if (failure) throw failure;
308
360
  }
309
- if (failure) throw failure;
361
+ await completion;
362
+ } finally {
363
+ controller.abort();
364
+ request.signal?.removeEventListener("abort", onAbort);
310
365
  }
311
- await completion;
312
366
  }
313
367
  async generateJson(request) {
314
368
  const effective = request.model || request.tier ? request : { ...request, tier: this.defaultTier };
@@ -416,7 +470,7 @@ var ProviderFactory = class {
416
470
  }
417
471
  }
418
472
  }
419
- throw new AIError2(
473
+ throw new AIError(
420
474
  "SERVER_ERROR" /* SERVER_ERROR */,
421
475
  `All providers failed for capability '${capability}'`,
422
476
  lastError instanceof Error ? lastError : void 0
@@ -445,7 +499,7 @@ var ProviderFactory = class {
445
499
  }
446
500
  }
447
501
  noProviderError(capability) {
448
- return new AIError2(
502
+ return new AIError(
449
503
  "MISSING_CONFIG" /* MISSING_CONFIG */,
450
504
  `No provider registered for capability '${capability}' \u2014 create one with createAI() and include its config block`
451
505
  );
@@ -455,15 +509,22 @@ var ProviderFactory = class {
455
509
  // src/core/constants/protocol.constants.ts
456
510
  var SSE_PREFIX = "data: ";
457
511
  var SSE_DONE_MARKER = "[DONE]";
458
- var JSON_CODE_BLOCK_PATTERN = /```json\n([\s\S]*?)\n```/;
512
+ var JSON_CODE_BLOCK_PATTERN = /```(?:json)?\s*\n?([\s\S]*?)\n?```/;
459
513
  var JSON_OBJECT_PATTERN = /\{[\s\S]*\}/;
514
+ var JSON_ARRAY_PATTERN = /\[[\s\S]*\]/;
460
515
 
461
516
  // src/core/parsers/sse-lines.ts
462
517
  function isSseDataLine(line) {
463
- return line.startsWith(SSE_PREFIX);
518
+ return line.startsWith(SSE_PREFIX) || line.startsWith("data:");
464
519
  }
465
520
  function extractSseDataPayload(line) {
466
- return line.slice(SSE_PREFIX.length);
521
+ if (line.startsWith(SSE_PREFIX)) {
522
+ return line.slice(SSE_PREFIX.length);
523
+ }
524
+ if (line.startsWith("data:")) {
525
+ return line.slice(5);
526
+ }
527
+ return line;
467
528
  }
468
529
  function isSseDoneMarker(payload) {
469
530
  return payload === SSE_DONE_MARKER;
@@ -472,7 +533,7 @@ function isSseDoneMarker(payload) {
472
533
  // src/core/parsers/json-content-extractor.ts
473
534
  function extractJsonFromCodeFence(content) {
474
535
  const match = content.match(JSON_CODE_BLOCK_PATTERN);
475
- if (match && match[1]) return match[1];
536
+ if (match && match[1]) return match[1].trim();
476
537
  return null;
477
538
  }
478
539
  function extractJsonObject(content) {
@@ -480,8 +541,27 @@ function extractJsonObject(content) {
480
541
  if (match && match[0]) return match[0];
481
542
  return null;
482
543
  }
544
+ function extractJsonArray(content) {
545
+ const match = content.match(JSON_ARRAY_PATTERN);
546
+ if (match && match[0]) return match[0];
547
+ return null;
548
+ }
549
+ function repairCommonJsonDefects(text) {
550
+ return text.replace(/(:\s*-?\d+(?:\.\d+)?)\s*"/g, "$1").replace(/(:\s*"[^"]*")"/g, "$1");
551
+ }
483
552
  function extractJsonCandidate(content) {
484
- return extractJsonFromCodeFence(content) ?? extractJsonObject(content) ?? content;
553
+ const fromFence = extractJsonFromCodeFence(content);
554
+ if (fromFence) return fromFence;
555
+ const objectIndex = content.indexOf("{");
556
+ const arrayIndex = content.indexOf("[");
557
+ if (objectIndex !== -1 && (arrayIndex === -1 || objectIndex < arrayIndex)) {
558
+ const obj = extractJsonObject(content);
559
+ if (obj) return obj;
560
+ } else if (arrayIndex !== -1) {
561
+ const arr = extractJsonArray(content);
562
+ if (arr) return arr;
563
+ }
564
+ return content.trim();
485
565
  }
486
566
 
487
567
  // src/core/parsers/json-response.parser.ts
@@ -490,12 +570,17 @@ var JsonResponseParser = class {
490
570
  const candidate = extractJsonCandidate(rawContent);
491
571
  try {
492
572
  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
- );
573
+ } catch {
574
+ try {
575
+ return JSON.parse(repairCommonJsonDefects(candidate));
576
+ } catch (error) {
577
+ throw new AIError(
578
+ "UNKNOWN_ERROR" /* UNKNOWN_ERROR */,
579
+ "Failed to parse structured response as JSON",
580
+ error instanceof Error ? error : void 0,
581
+ { rawContent }
582
+ );
583
+ }
499
584
  }
500
585
  }
501
586
  };
@@ -621,7 +706,7 @@ var AnthropicHttpTransport = class {
621
706
  });
622
707
  if (!response.ok) await this.throwFromErrorResponse(response);
623
708
  if (!response.body) {
624
- throw new AIError2("NETWORK_ERROR" /* NETWORK_ERROR */, "Response body is null");
709
+ throw new AIError("NETWORK_ERROR" /* NETWORK_ERROR */, "Response body is null");
625
710
  }
626
711
  return response.body;
627
712
  } catch (error) {
@@ -654,7 +739,7 @@ var AnthropicHttpTransport = class {
654
739
  status: response.status,
655
740
  url: response.url
656
741
  };
657
- throw new AIError2(
742
+ throw new AIError(
658
743
  classifyHttpStatus(response.status),
659
744
  errorMessage,
660
745
  void 0,
@@ -662,24 +747,24 @@ var AnthropicHttpTransport = class {
662
747
  );
663
748
  }
664
749
  normalizeError(error) {
665
- if (error instanceof AIError2) return error;
750
+ if (error instanceof AIError) return error;
666
751
  if (error instanceof Error) {
667
752
  if (error.name === "AbortError") {
668
- return new AIError2(
753
+ return new AIError(
669
754
  "ABORT_ERROR" /* ABORT_ERROR */,
670
755
  "Request was aborted by the client",
671
756
  error
672
757
  );
673
758
  }
674
759
  if (error.name === "TypeError" && error.message.toLowerCase().includes("network")) {
675
- return new AIError2(
760
+ return new AIError(
676
761
  "NETWORK_ERROR" /* NETWORK_ERROR */,
677
762
  "Network error: Unable to connect to the AI service",
678
763
  error
679
764
  );
680
765
  }
681
766
  }
682
- return new AIError2(
767
+ return new AIError(
683
768
  "UNKNOWN_ERROR" /* UNKNOWN_ERROR */,
684
769
  error instanceof Error ? error.message : "Unknown error occurred",
685
770
  error instanceof Error ? error : void 0
@@ -809,11 +894,14 @@ async function consumeAnthropicStream(stream, callbacks) {
809
894
  break;
810
895
  case "message_stop":
811
896
  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 */,
897
+ case "error": {
898
+ const errType = event.error.type;
899
+ 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 */;
900
+ throw new AIError(
901
+ mappedType,
815
902
  event.error.message || "Anthropic stream failed mid-flight"
816
903
  );
904
+ }
817
905
  }
818
906
  };
819
907
  try {
@@ -828,8 +916,8 @@ async function consumeAnthropicStream(stream, callbacks) {
828
916
  buffer += decoder.decode();
829
917
  if (buffer.length > 0) consumeLine(buffer);
830
918
  } catch (error) {
831
- if (error instanceof AIError2) throw error;
832
- throw new AIError2(
919
+ if (error instanceof AIError) throw error;
920
+ throw new AIError(
833
921
  "NETWORK_ERROR" /* NETWORK_ERROR */,
834
922
  "Stream was interrupted",
835
923
  error instanceof Error ? error : void 0
@@ -891,7 +979,7 @@ var AnthropicProvider = class {
891
979
  const messages = [...request.messages];
892
980
  const lastUserIndex = findLastIndex(messages, (m) => m.role === "user");
893
981
  if (lastUserIndex === -1) {
894
- throw new AIError2(
982
+ throw new AIError(
895
983
  "UNKNOWN_ERROR" /* UNKNOWN_ERROR */,
896
984
  "generateStructured requires at least one user message"
897
985
  );
@@ -946,7 +1034,7 @@ var AnthropicProvider = class {
946
1034
  finishReason
947
1035
  };
948
1036
  }
949
- throw new AIError2(
1037
+ throw new AIError(
950
1038
  "SERVER_ERROR" /* SERVER_ERROR */,
951
1039
  "No content generated from Anthropic API",
952
1040
  void 0,
@@ -1151,7 +1239,7 @@ var RetryManager = class {
1151
1239
  }
1152
1240
  shouldRetry(error, options) {
1153
1241
  if (options.shouldRetry) return options.shouldRetry(error);
1154
- if (error instanceof AIError2) {
1242
+ if (error instanceof AIError) {
1155
1243
  return isRetryableErrorType(error.type, DEFAULT_ERROR_CLASSIFICATION_RULE);
1156
1244
  }
1157
1245
  const lower = error.message.toLowerCase();
@@ -1396,7 +1484,7 @@ var GroqHttpTransport = class {
1396
1484
  });
1397
1485
  if (!response.ok) await this.throwFromErrorResponse(response);
1398
1486
  if (!response.body) {
1399
- throw new AIError2("NETWORK_ERROR" /* NETWORK_ERROR */, "Response body is null");
1487
+ throw new AIError("NETWORK_ERROR" /* NETWORK_ERROR */, "Response body is null");
1400
1488
  }
1401
1489
  return response.body;
1402
1490
  } catch (error) {
@@ -1444,7 +1532,7 @@ var GroqHttpTransport = class {
1444
1532
  status: response.status,
1445
1533
  url: response.url
1446
1534
  };
1447
- throw new AIError2(
1535
+ throw new AIError(
1448
1536
  classifyHttpStatus(response.status),
1449
1537
  errorMessage,
1450
1538
  void 0,
@@ -1452,24 +1540,24 @@ var GroqHttpTransport = class {
1452
1540
  );
1453
1541
  }
1454
1542
  normalizeError(error) {
1455
- if (error instanceof AIError2) return error;
1543
+ if (error instanceof AIError) return error;
1456
1544
  if (error instanceof Error) {
1457
1545
  if (error.name === "AbortError") {
1458
- return new AIError2(
1546
+ return new AIError(
1459
1547
  "ABORT_ERROR" /* ABORT_ERROR */,
1460
1548
  "Request was aborted by the client",
1461
1549
  error
1462
1550
  );
1463
1551
  }
1464
1552
  if (error.name === "TypeError" && error.message.toLowerCase().includes("network")) {
1465
- return new AIError2(
1553
+ return new AIError(
1466
1554
  "NETWORK_ERROR" /* NETWORK_ERROR */,
1467
1555
  "Network error: Unable to connect to the AI service",
1468
1556
  error
1469
1557
  );
1470
1558
  }
1471
1559
  }
1472
- return new AIError2(
1560
+ return new AIError(
1473
1561
  "UNKNOWN_ERROR" /* UNKNOWN_ERROR */,
1474
1562
  error instanceof Error ? error.message : "Unknown error occurred",
1475
1563
  error instanceof Error ? error : void 0
@@ -1620,6 +1708,13 @@ async function consumeGroqStream(stream, callbacks) {
1620
1708
  } catch {
1621
1709
  return;
1622
1710
  }
1711
+ const errorBody = chunk.error;
1712
+ if (errorBody) {
1713
+ throw new AIError(
1714
+ errorBody.message?.toLowerCase().includes("rate limit") ? "RATE_LIMIT_ERROR" /* RATE_LIMIT_ERROR */ : "SERVER_ERROR" /* SERVER_ERROR */,
1715
+ errorBody.message || "Groq stream failed mid-flight"
1716
+ );
1717
+ }
1623
1718
  if (chunk.model) model = chunk.model;
1624
1719
  const choice = chunk.choices?.[0];
1625
1720
  const delta = choice?.delta?.content;
@@ -1634,13 +1729,6 @@ async function consumeGroqStream(stream, callbacks) {
1634
1729
  if (tailUsage) {
1635
1730
  usage = mapGroqUsage(tailUsage);
1636
1731
  }
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
1732
  };
1645
1733
  try {
1646
1734
  while (true) {
@@ -1654,8 +1742,8 @@ async function consumeGroqStream(stream, callbacks) {
1654
1742
  buffer += decoder.decode();
1655
1743
  if (buffer.length > 0) consumeLine(buffer);
1656
1744
  } catch (error) {
1657
- if (error instanceof AIError2) throw error;
1658
- throw new AIError2(
1745
+ if (error instanceof AIError) throw error;
1746
+ throw new AIError(
1659
1747
  "NETWORK_ERROR" /* NETWORK_ERROR */,
1660
1748
  "Stream was interrupted",
1661
1749
  error instanceof Error ? error : void 0
@@ -1713,7 +1801,7 @@ var GroqProvider = class {
1713
1801
  const choice = response.choices?.[0];
1714
1802
  const text = choice?.message?.content ?? "";
1715
1803
  if (text.length === 0) {
1716
- throw new AIError2(
1804
+ throw new AIError(
1717
1805
  "SERVER_ERROR" /* SERVER_ERROR */,
1718
1806
  "No content generated from Groq API",
1719
1807
  void 0,
@@ -1735,7 +1823,7 @@ var GroqProvider = class {
1735
1823
  const messages = toGroqMessages(request.messages, request.systemPrompt);
1736
1824
  const lastUserIndex = findLastIndex2(messages, (m) => m.role === "user");
1737
1825
  if (lastUserIndex === -1) {
1738
- throw new AIError2(
1826
+ throw new AIError(
1739
1827
  "UNKNOWN_ERROR" /* UNKNOWN_ERROR */,
1740
1828
  "generateStructured requires at least one user message"
1741
1829
  );
@@ -1749,7 +1837,7 @@ var GroqProvider = class {
1749
1837
  const choice = response.choices?.[0];
1750
1838
  const text = choice?.message?.content ?? "";
1751
1839
  if (text.length === 0) {
1752
- throw new AIError2(
1840
+ throw new AIError(
1753
1841
  "SERVER_ERROR" /* SERVER_ERROR */,
1754
1842
  "No content generated from Groq API",
1755
1843
  void 0,
@@ -1776,7 +1864,7 @@ var GroqProvider = class {
1776
1864
  try {
1777
1865
  const outcome = await consumeGroqStream(streamBody, callbacks);
1778
1866
  if (outcome.text.length === 0) {
1779
- throw new AIError2(
1867
+ throw new AIError(
1780
1868
  "SERVER_ERROR" /* SERVER_ERROR */,
1781
1869
  "No content generated from Groq API",
1782
1870
  void 0,
@@ -1907,7 +1995,9 @@ var GeminiProvider = class extends BaseProvider {
1907
1995
  headers: this.authHeaders()
1908
1996
  }
1909
1997
  );
1910
- return response.ok ? "healthy" : "unavailable";
1998
+ if (response.ok) return "healthy";
1999
+ if (response.status === 401 || response.status === 403) return "unavailable";
2000
+ return "degraded";
1911
2001
  } catch {
1912
2002
  return "unavailable";
1913
2003
  }
@@ -1918,6 +2008,8 @@ var GeminiProvider = class extends BaseProvider {
1918
2008
  async generate(request) {
1919
2009
  try {
1920
2010
  const model = request.model ?? this.resolveModel(request.tier ?? "balanced");
2011
+ const systemMessages = request.messages.filter((m) => m.role === "system");
2012
+ const systemPrompt = request.systemPrompt && request.systemPrompt.length > 0 ? request.systemPrompt : systemMessages.length > 0 ? systemMessages.map((m) => m.content).join("\n") : void 0;
1921
2013
  const response = await this.withRetry(async () => {
1922
2014
  return await this.fetchWithTimeout(
1923
2015
  `${this.config.baseUrl}/models/${model}:generateContent`,
@@ -1925,24 +2017,30 @@ var GeminiProvider = class extends BaseProvider {
1925
2017
  method: "POST",
1926
2018
  headers: this.authHeaders(),
1927
2019
  body: JSON.stringify({
1928
- systemInstruction: request.systemPrompt && request.systemPrompt.length > 0 ? { parts: [{ text: request.systemPrompt }] } : void 0,
2020
+ systemInstruction: systemPrompt && systemPrompt.length > 0 ? { parts: [{ text: systemPrompt }] } : void 0,
1929
2021
  contents: toGeminiContents(request.messages),
1930
2022
  generationConfig: {
1931
- temperature: request.temperature || 0.7,
1932
- maxOutputTokens: request.maxTokens || 2048,
1933
- topP: request.topP || 0.9
2023
+ temperature: request.temperature ?? 0.7,
2024
+ maxOutputTokens: request.maxTokens ?? 2048,
2025
+ topP: request.topP ?? 0.9
1934
2026
  }
1935
2027
  })
1936
2028
  }
1937
2029
  );
1938
2030
  });
1939
2031
  if (!response.ok) {
1940
- throw new Error(`Gemini API error: ${response.statusText}`);
2032
+ const message = await extractErrorMessageFromResponse(response);
2033
+ throw new AIError(
2034
+ classifyHttpStatus(response.status),
2035
+ `Gemini API error (${response.status}): ${message}`,
2036
+ void 0,
2037
+ { status: response.status, model }
2038
+ );
1941
2039
  }
1942
2040
  const data = await response.json();
1943
- const text = data.candidates[0]?.content?.parts?.map((part) => part.text ?? "").join("") ?? "";
2041
+ const text = data.candidates?.[0]?.content?.parts?.map((part) => part.text ?? "").join("") ?? "";
1944
2042
  if (text.length === 0) {
1945
- throw new AIError2("SERVER_ERROR" /* SERVER_ERROR */, "No content generated from Gemini API", void 0, { model });
2043
+ throw new AIError("SERVER_ERROR" /* SERVER_ERROR */, "No content generated from Gemini API", void 0, { model });
1946
2044
  }
1947
2045
  return {
1948
2046
  text,
@@ -1953,11 +2051,11 @@ var GeminiProvider = class extends BaseProvider {
1953
2051
  } : void 0,
1954
2052
  model,
1955
2053
  providerId: this.id,
1956
- finishReason: mapGeminiFinishReason(data.candidates[0]?.finishReason)
2054
+ finishReason: mapGeminiFinishReason(data.candidates?.[0]?.finishReason)
1957
2055
  };
1958
2056
  } catch (error) {
1959
- if (error instanceof AIError2) throw error;
1960
- throw new AIError2(
2057
+ if (error instanceof AIError) throw error;
2058
+ throw new AIError(
1961
2059
  "UNKNOWN_ERROR" /* UNKNOWN_ERROR */,
1962
2060
  `Gemini text generation failed: ${error instanceof Error ? error.message : "Unknown error"}`,
1963
2061
  error instanceof Error ? error : void 0
@@ -1968,7 +2066,7 @@ var GeminiProvider = class extends BaseProvider {
1968
2066
  const messages = [...request.messages];
1969
2067
  const lastUserIndex = findLastIndex3(messages, (m) => m.role === "user");
1970
2068
  if (lastUserIndex === -1) {
1971
- throw new AIError2(
2069
+ throw new AIError(
1972
2070
  "UNKNOWN_ERROR" /* UNKNOWN_ERROR */,
1973
2071
  "generateStructured requires at least one user message"
1974
2072
  );
@@ -2005,12 +2103,18 @@ var GeminiProvider = class extends BaseProvider {
2005
2103
  );
2006
2104
  });
2007
2105
  if (!response.ok) {
2008
- throw new Error(`Gemini API error: ${response.statusText}`);
2106
+ const message = await extractErrorMessageFromResponse(response);
2107
+ throw new AIError(
2108
+ classifyHttpStatus(response.status),
2109
+ `Gemini image generation error (${response.status}): ${message}`,
2110
+ void 0,
2111
+ { status: response.status }
2112
+ );
2009
2113
  }
2010
2114
  const data = await response.json();
2011
- const imageData = data.candidates[0]?.content?.parts[0]?.inlineData;
2115
+ const imageData = data.candidates?.[0]?.content?.parts?.[0]?.inlineData;
2012
2116
  if (!imageData) {
2013
- throw new Error("No image data in response");
2117
+ throw new AIError("SERVER_ERROR" /* SERVER_ERROR */, "No image data in Gemini response");
2014
2118
  }
2015
2119
  const url = `data:${imageData.mimeType};base64,${imageData.data}`;
2016
2120
  return {
@@ -2329,17 +2433,70 @@ var PrunaProvider = class {
2329
2433
  async estimateCost(_request) {
2330
2434
  return 1;
2331
2435
  }
2332
- async generateImage(_request) {
2333
- throw new AIError2("UNKNOWN_ERROR" /* UNKNOWN_ERROR */, "Use generateWithPruna instead");
2436
+ async generateImage(request) {
2437
+ let finalUrl;
2438
+ const model = this.config.models?.textToImage;
2439
+ for await (const stage of generateWithPruna(
2440
+ { prompt: request.prompt, model, quality: request.quality },
2441
+ this.config.apiKey
2442
+ )) {
2443
+ if (stage.error) throw stage.error;
2444
+ if (stage.result?.url) finalUrl = stage.result.url;
2445
+ }
2446
+ if (!finalUrl) {
2447
+ throw new AIError("SERVER_ERROR" /* SERVER_ERROR */, "No image generated by Pruna AI");
2448
+ }
2449
+ return {
2450
+ id: Date.now().toString(),
2451
+ type: "image",
2452
+ url: finalUrl,
2453
+ metadata: { provider: this.id, model: model ?? "p-image" },
2454
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
2455
+ };
2334
2456
  }
2335
- async generateVideo(_request) {
2336
- throw new AIError2("UNKNOWN_ERROR" /* UNKNOWN_ERROR */, "Use generateWithPruna instead");
2457
+ async generateVideo(request) {
2458
+ let finalUrl;
2459
+ const model = this.config.models?.textToVideo;
2460
+ for await (const stage of generateWithPruna(
2461
+ { prompt: request.prompt, model, duration: request.duration, aspectRatio: request.aspectRatio },
2462
+ this.config.apiKey
2463
+ )) {
2464
+ if (stage.error) throw stage.error;
2465
+ if (stage.result?.url) finalUrl = stage.result.url;
2466
+ }
2467
+ if (!finalUrl) {
2468
+ throw new AIError("SERVER_ERROR" /* SERVER_ERROR */, "No video generated by Pruna AI");
2469
+ }
2470
+ return {
2471
+ id: Date.now().toString(),
2472
+ type: "video",
2473
+ url: finalUrl,
2474
+ metadata: { provider: this.id, model: model ?? "p-video" },
2475
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
2476
+ };
2337
2477
  }
2338
- async imageToVideo(_request) {
2339
- throw new AIError2("UNKNOWN_ERROR" /* UNKNOWN_ERROR */, "Use generateWithPruna instead");
2478
+ async imageToVideo(request) {
2479
+ let finalUrl;
2480
+ for await (const stage of generateWithPruna(
2481
+ { image: request.imageUrl, prompt: request.prompt, duration: request.duration, motion: request.motion },
2482
+ this.config.apiKey
2483
+ )) {
2484
+ if (stage.error) throw stage.error;
2485
+ if (stage.result?.url) finalUrl = stage.result.url;
2486
+ }
2487
+ if (!finalUrl) {
2488
+ throw new AIError("SERVER_ERROR" /* SERVER_ERROR */, "No video generated from image by Pruna AI");
2489
+ }
2490
+ return {
2491
+ id: Date.now().toString(),
2492
+ type: "video",
2493
+ url: finalUrl,
2494
+ metadata: { provider: this.id, model: "p-image-to-video" },
2495
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
2496
+ };
2340
2497
  }
2341
2498
  async videoToVideo(_request) {
2342
- throw new AIError2("UNKNOWN_ERROR" /* UNKNOWN_ERROR */, "Use generateWithPruna instead");
2499
+ throw new AIError("CLIENT_ERROR" /* CLIENT_ERROR */, "Pruna AI provider does not support video-to-video conversion");
2343
2500
  }
2344
2501
  };
2345
2502
  function createPrunaProvider(config) {