@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
@@ -8,25 +8,49 @@ function isMediaProvider(provider) {
8
8
  return provider.capabilities.includes("image") || provider.capabilities.includes("video");
9
9
  }
10
10
 
11
- // src/content/domain/errors/AIErrors.ts
12
- var AIError = class _AIError extends Error {
13
- code;
14
- cause;
15
- constructor(message, code, cause) {
16
- super(message);
17
- this.name = "AIError";
18
- this.code = code;
19
- this.cause = cause;
20
- }
21
- static from(error, fallbackCode = "UNKNOWN") {
22
- if (error instanceof _AIError) return error;
23
- if (error instanceof Error) {
24
- return new _AIError(error.message, mapMessageToCode(error.message, fallbackCode), error);
25
- }
26
- return new _AIError(String(error), fallbackCode, error);
11
+ // src/core/errors/ai-error-types.ts
12
+ var AIErrorType = /* @__PURE__ */ ((AIErrorType2) => {
13
+ AIErrorType2["INVALID_API_KEY"] = "INVALID_API_KEY";
14
+ AIErrorType2["MISSING_CONFIG"] = "MISSING_CONFIG";
15
+ AIErrorType2["NETWORK_ERROR"] = "NETWORK_ERROR";
16
+ AIErrorType2["ABORT_ERROR"] = "ABORT_ERROR";
17
+ AIErrorType2["RATE_LIMIT_ERROR"] = "RATE_LIMIT_ERROR";
18
+ AIErrorType2["SERVER_ERROR"] = "SERVER_ERROR";
19
+ AIErrorType2["CLIENT_ERROR"] = "CLIENT_ERROR";
20
+ AIErrorType2["UNKNOWN_ERROR"] = "UNKNOWN_ERROR";
21
+ return AIErrorType2;
22
+ })(AIErrorType || {});
23
+
24
+ // src/core/errors/ai-error.ts
25
+ var KNOWN_ERROR_TYPES = new Set(Object.values(AIErrorType));
26
+ function mapCodeToType(code) {
27
+ switch (code) {
28
+ case "VALIDATION_ERROR":
29
+ case "CLIENT_ERROR":
30
+ return "CLIENT_ERROR" /* CLIENT_ERROR */;
31
+ case "INVALID_API_KEY":
32
+ return "INVALID_API_KEY" /* INVALID_API_KEY */;
33
+ case "MISSING_CONFIG":
34
+ case "PROVIDER_UNAVAILABLE":
35
+ return "MISSING_CONFIG" /* MISSING_CONFIG */;
36
+ case "PROVIDER_QUOTA_EXHAUSTED":
37
+ case "RATE_LIMIT_ERROR":
38
+ return "RATE_LIMIT_ERROR" /* RATE_LIMIT_ERROR */;
39
+ case "NETWORK_ERROR":
40
+ case "TIMEOUT":
41
+ return "NETWORK_ERROR" /* NETWORK_ERROR */;
42
+ case "ABORT_ERROR":
43
+ case "ABORTED":
44
+ return "ABORT_ERROR" /* ABORT_ERROR */;
45
+ case "SERVER_ERROR":
46
+ case "PROVIDER_ERROR":
47
+ case "NO_RESULT":
48
+ return "SERVER_ERROR" /* SERVER_ERROR */;
49
+ default:
50
+ return "UNKNOWN_ERROR" /* UNKNOWN_ERROR */;
27
51
  }
28
- };
29
- function mapMessageToCode(message, fallback) {
52
+ }
53
+ function mapMessageToCode(message, fallback = "UNKNOWN") {
30
54
  const lower = message.toLowerCase();
31
55
  if (lower.includes("validation")) return "VALIDATION_ERROR";
32
56
  if (lower.includes("quota")) return "PROVIDER_QUOTA_EXHAUSTED";
@@ -39,6 +63,53 @@ function mapMessageToCode(message, fallback) {
39
63
  if (lower.includes("network") || lower.includes("fetch")) return "NETWORK_ERROR";
40
64
  return fallback;
41
65
  }
66
+ var AIError = class _AIError extends Error {
67
+ type;
68
+ code;
69
+ cause;
70
+ metadata;
71
+ constructor(typeOrMessage, messageOrCode, cause, metadata = {}) {
72
+ let effectiveType;
73
+ let effectiveMessage;
74
+ let effectiveCode;
75
+ if (KNOWN_ERROR_TYPES.has(typeOrMessage)) {
76
+ effectiveType = typeOrMessage;
77
+ effectiveMessage = messageOrCode ?? "";
78
+ effectiveCode = effectiveType;
79
+ } else {
80
+ effectiveMessage = typeOrMessage;
81
+ effectiveCode = messageOrCode ?? "UNKNOWN";
82
+ effectiveType = mapCodeToType(effectiveCode);
83
+ }
84
+ super(effectiveMessage);
85
+ this.name = "AIError";
86
+ this.type = effectiveType;
87
+ this.code = effectiveCode;
88
+ this.cause = cause;
89
+ this.metadata = metadata;
90
+ }
91
+ static from(error, fallbackCode = "UNKNOWN") {
92
+ if (error instanceof _AIError) return error;
93
+ if (error instanceof Error) {
94
+ const code = mapMessageToCode(error.message, fallbackCode);
95
+ return new _AIError(error.message, code, error);
96
+ }
97
+ return new _AIError(String(error), fallbackCode, error);
98
+ }
99
+ toString() {
100
+ const statusPart = typeof this.metadata.status === "number" ? ` (HTTP ${this.metadata.status})` : "";
101
+ return `${this.name} [${this.type}]: ${this.message}${statusPart}`;
102
+ }
103
+ toJSON() {
104
+ return {
105
+ name: this.name,
106
+ type: this.type,
107
+ code: this.code,
108
+ message: this.message,
109
+ metadata: this.metadata
110
+ };
111
+ }
112
+ };
42
113
 
43
114
  // src/content/domain/limits/ProviderTimingConfig.ts
44
115
  var ProviderTimingConfig = {
@@ -113,32 +184,6 @@ var ProviderError = class extends AIError {
113
184
  providerId;
114
185
  };
115
186
 
116
- // src/core/errors/ai-error.ts
117
- var AIError2 = class extends Error {
118
- type;
119
- cause;
120
- metadata;
121
- constructor(type, message, cause, metadata = {}) {
122
- super(message);
123
- this.name = "AIError";
124
- this.type = type;
125
- this.cause = cause;
126
- this.metadata = metadata;
127
- }
128
- toString() {
129
- const statusPart = typeof this.metadata.status === "number" ? ` (HTTP ${this.metadata.status})` : "";
130
- return `${this.name} [${this.type}]: ${this.message}${statusPart}`;
131
- }
132
- toJSON() {
133
- return {
134
- name: this.name,
135
- type: this.type,
136
- message: this.message,
137
- metadata: this.metadata
138
- };
139
- }
140
- };
141
-
142
187
  // src/core/errors/error-classifier.ts
143
188
  var DEFAULT_RETRYABLE_TYPES = /* @__PURE__ */ new Set([
144
189
  "NETWORK_ERROR" /* NETWORK_ERROR */,
@@ -264,7 +309,7 @@ var ProviderFactory = class {
264
309
  }
265
310
  }
266
311
  }
267
- const failure = new AIError2(
312
+ const failure = new AIError(
268
313
  "SERVER_ERROR" /* SERVER_ERROR */,
269
314
  `All providers failed for capability 'text'`,
270
315
  lastError instanceof Error ? lastError : void 0
@@ -278,12 +323,16 @@ var ProviderFactory = class {
278
323
  let wake = null;
279
324
  let finished = false;
280
325
  let failure;
326
+ const controller = new AbortController();
327
+ const onAbort = () => controller.abort();
328
+ request.signal?.addEventListener("abort", onAbort);
329
+ const effectiveRequest = { ...request, signal: controller.signal };
281
330
  const notify = () => {
282
331
  const resolve = wake;
283
332
  wake = null;
284
333
  resolve?.();
285
334
  };
286
- const completion = this.chatStream(request, {
335
+ const completion = this.chatStream(effectiveRequest, {
287
336
  onChunk: (chunk) => {
288
337
  queue.push(chunk);
289
338
  notify();
@@ -299,18 +348,23 @@ var ProviderFactory = class {
299
348
  notify();
300
349
  }
301
350
  );
302
- while (!finished || queue.length > 0) {
303
- if (queue.length === 0 && !finished) {
304
- await new Promise((resolve) => {
305
- wake = resolve;
306
- });
307
- }
308
- while (queue.length > 0) {
309
- yield queue.shift();
351
+ try {
352
+ while (!finished || queue.length > 0) {
353
+ if (queue.length === 0 && !finished) {
354
+ await new Promise((resolve) => {
355
+ wake = resolve;
356
+ });
357
+ }
358
+ while (queue.length > 0) {
359
+ yield queue.shift();
360
+ }
361
+ if (failure) throw failure;
310
362
  }
311
- if (failure) throw failure;
363
+ await completion;
364
+ } finally {
365
+ controller.abort();
366
+ request.signal?.removeEventListener("abort", onAbort);
312
367
  }
313
- await completion;
314
368
  }
315
369
  async generateJson(request) {
316
370
  const effective = request.model || request.tier ? request : { ...request, tier: this.defaultTier };
@@ -418,7 +472,7 @@ var ProviderFactory = class {
418
472
  }
419
473
  }
420
474
  }
421
- throw new AIError2(
475
+ throw new AIError(
422
476
  "SERVER_ERROR" /* SERVER_ERROR */,
423
477
  `All providers failed for capability '${capability}'`,
424
478
  lastError instanceof Error ? lastError : void 0
@@ -447,7 +501,7 @@ var ProviderFactory = class {
447
501
  }
448
502
  }
449
503
  noProviderError(capability) {
450
- return new AIError2(
504
+ return new AIError(
451
505
  "MISSING_CONFIG" /* MISSING_CONFIG */,
452
506
  `No provider registered for capability '${capability}' \u2014 create one with createAI() and include its config block`
453
507
  );
@@ -457,15 +511,22 @@ var ProviderFactory = class {
457
511
  // src/core/constants/protocol.constants.ts
458
512
  var SSE_PREFIX = "data: ";
459
513
  var SSE_DONE_MARKER = "[DONE]";
460
- var JSON_CODE_BLOCK_PATTERN = /```json\n([\s\S]*?)\n```/;
514
+ var JSON_CODE_BLOCK_PATTERN = /```(?:json)?\s*\n?([\s\S]*?)\n?```/;
461
515
  var JSON_OBJECT_PATTERN = /\{[\s\S]*\}/;
516
+ var JSON_ARRAY_PATTERN = /\[[\s\S]*\]/;
462
517
 
463
518
  // src/core/parsers/sse-lines.ts
464
519
  function isSseDataLine(line) {
465
- return line.startsWith(SSE_PREFIX);
520
+ return line.startsWith(SSE_PREFIX) || line.startsWith("data:");
466
521
  }
467
522
  function extractSseDataPayload(line) {
468
- return line.slice(SSE_PREFIX.length);
523
+ if (line.startsWith(SSE_PREFIX)) {
524
+ return line.slice(SSE_PREFIX.length);
525
+ }
526
+ if (line.startsWith("data:")) {
527
+ return line.slice(5);
528
+ }
529
+ return line;
469
530
  }
470
531
  function isSseDoneMarker(payload) {
471
532
  return payload === SSE_DONE_MARKER;
@@ -474,7 +535,7 @@ function isSseDoneMarker(payload) {
474
535
  // src/core/parsers/json-content-extractor.ts
475
536
  function extractJsonFromCodeFence(content) {
476
537
  const match = content.match(JSON_CODE_BLOCK_PATTERN);
477
- if (match && match[1]) return match[1];
538
+ if (match && match[1]) return match[1].trim();
478
539
  return null;
479
540
  }
480
541
  function extractJsonObject(content) {
@@ -482,8 +543,27 @@ function extractJsonObject(content) {
482
543
  if (match && match[0]) return match[0];
483
544
  return null;
484
545
  }
546
+ function extractJsonArray(content) {
547
+ const match = content.match(JSON_ARRAY_PATTERN);
548
+ if (match && match[0]) return match[0];
549
+ return null;
550
+ }
551
+ function repairCommonJsonDefects(text) {
552
+ return text.replace(/(:\s*-?\d+(?:\.\d+)?)\s*"/g, "$1").replace(/(:\s*"[^"]*")"/g, "$1");
553
+ }
485
554
  function extractJsonCandidate(content) {
486
- return extractJsonFromCodeFence(content) ?? extractJsonObject(content) ?? content;
555
+ const fromFence = extractJsonFromCodeFence(content);
556
+ if (fromFence) return fromFence;
557
+ const objectIndex = content.indexOf("{");
558
+ const arrayIndex = content.indexOf("[");
559
+ if (objectIndex !== -1 && (arrayIndex === -1 || objectIndex < arrayIndex)) {
560
+ const obj = extractJsonObject(content);
561
+ if (obj) return obj;
562
+ } else if (arrayIndex !== -1) {
563
+ const arr = extractJsonArray(content);
564
+ if (arr) return arr;
565
+ }
566
+ return content.trim();
487
567
  }
488
568
 
489
569
  // src/core/parsers/json-response.parser.ts
@@ -492,12 +572,17 @@ var JsonResponseParser = class {
492
572
  const candidate = extractJsonCandidate(rawContent);
493
573
  try {
494
574
  return JSON.parse(candidate);
495
- } catch (error) {
496
- throw new AIError2(
497
- "UNKNOWN_ERROR" /* UNKNOWN_ERROR */,
498
- "Failed to parse structured response as JSON",
499
- error instanceof Error ? error : void 0
500
- );
575
+ } catch {
576
+ try {
577
+ return JSON.parse(repairCommonJsonDefects(candidate));
578
+ } catch (error) {
579
+ throw new AIError(
580
+ "UNKNOWN_ERROR" /* UNKNOWN_ERROR */,
581
+ "Failed to parse structured response as JSON",
582
+ error instanceof Error ? error : void 0,
583
+ { rawContent }
584
+ );
585
+ }
501
586
  }
502
587
  }
503
588
  };
@@ -623,7 +708,7 @@ var AnthropicHttpTransport = class {
623
708
  });
624
709
  if (!response.ok) await this.throwFromErrorResponse(response);
625
710
  if (!response.body) {
626
- throw new AIError2("NETWORK_ERROR" /* NETWORK_ERROR */, "Response body is null");
711
+ throw new AIError("NETWORK_ERROR" /* NETWORK_ERROR */, "Response body is null");
627
712
  }
628
713
  return response.body;
629
714
  } catch (error) {
@@ -656,7 +741,7 @@ var AnthropicHttpTransport = class {
656
741
  status: response.status,
657
742
  url: response.url
658
743
  };
659
- throw new AIError2(
744
+ throw new AIError(
660
745
  classifyHttpStatus(response.status),
661
746
  errorMessage,
662
747
  void 0,
@@ -664,24 +749,24 @@ var AnthropicHttpTransport = class {
664
749
  );
665
750
  }
666
751
  normalizeError(error) {
667
- if (error instanceof AIError2) return error;
752
+ if (error instanceof AIError) return error;
668
753
  if (error instanceof Error) {
669
754
  if (error.name === "AbortError") {
670
- return new AIError2(
755
+ return new AIError(
671
756
  "ABORT_ERROR" /* ABORT_ERROR */,
672
757
  "Request was aborted by the client",
673
758
  error
674
759
  );
675
760
  }
676
761
  if (error.name === "TypeError" && error.message.toLowerCase().includes("network")) {
677
- return new AIError2(
762
+ return new AIError(
678
763
  "NETWORK_ERROR" /* NETWORK_ERROR */,
679
764
  "Network error: Unable to connect to the AI service",
680
765
  error
681
766
  );
682
767
  }
683
768
  }
684
- return new AIError2(
769
+ return new AIError(
685
770
  "UNKNOWN_ERROR" /* UNKNOWN_ERROR */,
686
771
  error instanceof Error ? error.message : "Unknown error occurred",
687
772
  error instanceof Error ? error : void 0
@@ -811,11 +896,14 @@ async function consumeAnthropicStream(stream, callbacks) {
811
896
  break;
812
897
  case "message_stop":
813
898
  break;
814
- case "error":
815
- throw new AIError2(
816
- event.error.type === "rate_limit_error" ? "RATE_LIMIT_ERROR" /* RATE_LIMIT_ERROR */ : "SERVER_ERROR" /* SERVER_ERROR */,
899
+ case "error": {
900
+ const errType = event.error.type;
901
+ 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 */;
902
+ throw new AIError(
903
+ mappedType,
817
904
  event.error.message || "Anthropic stream failed mid-flight"
818
905
  );
906
+ }
819
907
  }
820
908
  };
821
909
  try {
@@ -830,8 +918,8 @@ async function consumeAnthropicStream(stream, callbacks) {
830
918
  buffer += decoder.decode();
831
919
  if (buffer.length > 0) consumeLine(buffer);
832
920
  } catch (error) {
833
- if (error instanceof AIError2) throw error;
834
- throw new AIError2(
921
+ if (error instanceof AIError) throw error;
922
+ throw new AIError(
835
923
  "NETWORK_ERROR" /* NETWORK_ERROR */,
836
924
  "Stream was interrupted",
837
925
  error instanceof Error ? error : void 0
@@ -893,7 +981,7 @@ var AnthropicProvider = class {
893
981
  const messages = [...request.messages];
894
982
  const lastUserIndex = findLastIndex(messages, (m) => m.role === "user");
895
983
  if (lastUserIndex === -1) {
896
- throw new AIError2(
984
+ throw new AIError(
897
985
  "UNKNOWN_ERROR" /* UNKNOWN_ERROR */,
898
986
  "generateStructured requires at least one user message"
899
987
  );
@@ -948,7 +1036,7 @@ var AnthropicProvider = class {
948
1036
  finishReason
949
1037
  };
950
1038
  }
951
- throw new AIError2(
1039
+ throw new AIError(
952
1040
  "SERVER_ERROR" /* SERVER_ERROR */,
953
1041
  "No content generated from Anthropic API",
954
1042
  void 0,
@@ -1153,7 +1241,7 @@ var RetryManager = class {
1153
1241
  }
1154
1242
  shouldRetry(error, options) {
1155
1243
  if (options.shouldRetry) return options.shouldRetry(error);
1156
- if (error instanceof AIError2) {
1244
+ if (error instanceof AIError) {
1157
1245
  return isRetryableErrorType(error.type, DEFAULT_ERROR_CLASSIFICATION_RULE);
1158
1246
  }
1159
1247
  const lower = error.message.toLowerCase();
@@ -1398,7 +1486,7 @@ var GroqHttpTransport = class {
1398
1486
  });
1399
1487
  if (!response.ok) await this.throwFromErrorResponse(response);
1400
1488
  if (!response.body) {
1401
- throw new AIError2("NETWORK_ERROR" /* NETWORK_ERROR */, "Response body is null");
1489
+ throw new AIError("NETWORK_ERROR" /* NETWORK_ERROR */, "Response body is null");
1402
1490
  }
1403
1491
  return response.body;
1404
1492
  } catch (error) {
@@ -1446,7 +1534,7 @@ var GroqHttpTransport = class {
1446
1534
  status: response.status,
1447
1535
  url: response.url
1448
1536
  };
1449
- throw new AIError2(
1537
+ throw new AIError(
1450
1538
  classifyHttpStatus(response.status),
1451
1539
  errorMessage,
1452
1540
  void 0,
@@ -1454,24 +1542,24 @@ var GroqHttpTransport = class {
1454
1542
  );
1455
1543
  }
1456
1544
  normalizeError(error) {
1457
- if (error instanceof AIError2) return error;
1545
+ if (error instanceof AIError) return error;
1458
1546
  if (error instanceof Error) {
1459
1547
  if (error.name === "AbortError") {
1460
- return new AIError2(
1548
+ return new AIError(
1461
1549
  "ABORT_ERROR" /* ABORT_ERROR */,
1462
1550
  "Request was aborted by the client",
1463
1551
  error
1464
1552
  );
1465
1553
  }
1466
1554
  if (error.name === "TypeError" && error.message.toLowerCase().includes("network")) {
1467
- return new AIError2(
1555
+ return new AIError(
1468
1556
  "NETWORK_ERROR" /* NETWORK_ERROR */,
1469
1557
  "Network error: Unable to connect to the AI service",
1470
1558
  error
1471
1559
  );
1472
1560
  }
1473
1561
  }
1474
- return new AIError2(
1562
+ return new AIError(
1475
1563
  "UNKNOWN_ERROR" /* UNKNOWN_ERROR */,
1476
1564
  error instanceof Error ? error.message : "Unknown error occurred",
1477
1565
  error instanceof Error ? error : void 0
@@ -1622,6 +1710,13 @@ async function consumeGroqStream(stream, callbacks) {
1622
1710
  } catch {
1623
1711
  return;
1624
1712
  }
1713
+ const errorBody = chunk.error;
1714
+ if (errorBody) {
1715
+ throw new AIError(
1716
+ errorBody.message?.toLowerCase().includes("rate limit") ? "RATE_LIMIT_ERROR" /* RATE_LIMIT_ERROR */ : "SERVER_ERROR" /* SERVER_ERROR */,
1717
+ errorBody.message || "Groq stream failed mid-flight"
1718
+ );
1719
+ }
1625
1720
  if (chunk.model) model = chunk.model;
1626
1721
  const choice = chunk.choices?.[0];
1627
1722
  const delta = choice?.delta?.content;
@@ -1636,13 +1731,6 @@ async function consumeGroqStream(stream, callbacks) {
1636
1731
  if (tailUsage) {
1637
1732
  usage = mapGroqUsage(tailUsage);
1638
1733
  }
1639
- const errorBody = chunk.error;
1640
- if (errorBody) {
1641
- throw new AIError2(
1642
- "SERVER_ERROR" /* SERVER_ERROR */,
1643
- errorBody.message || "Groq stream failed mid-flight"
1644
- );
1645
- }
1646
1734
  };
1647
1735
  try {
1648
1736
  while (true) {
@@ -1656,8 +1744,8 @@ async function consumeGroqStream(stream, callbacks) {
1656
1744
  buffer += decoder.decode();
1657
1745
  if (buffer.length > 0) consumeLine(buffer);
1658
1746
  } catch (error) {
1659
- if (error instanceof AIError2) throw error;
1660
- throw new AIError2(
1747
+ if (error instanceof AIError) throw error;
1748
+ throw new AIError(
1661
1749
  "NETWORK_ERROR" /* NETWORK_ERROR */,
1662
1750
  "Stream was interrupted",
1663
1751
  error instanceof Error ? error : void 0
@@ -1715,7 +1803,7 @@ var GroqProvider = class {
1715
1803
  const choice = response.choices?.[0];
1716
1804
  const text = choice?.message?.content ?? "";
1717
1805
  if (text.length === 0) {
1718
- throw new AIError2(
1806
+ throw new AIError(
1719
1807
  "SERVER_ERROR" /* SERVER_ERROR */,
1720
1808
  "No content generated from Groq API",
1721
1809
  void 0,
@@ -1737,7 +1825,7 @@ var GroqProvider = class {
1737
1825
  const messages = toGroqMessages(request.messages, request.systemPrompt);
1738
1826
  const lastUserIndex = findLastIndex2(messages, (m) => m.role === "user");
1739
1827
  if (lastUserIndex === -1) {
1740
- throw new AIError2(
1828
+ throw new AIError(
1741
1829
  "UNKNOWN_ERROR" /* UNKNOWN_ERROR */,
1742
1830
  "generateStructured requires at least one user message"
1743
1831
  );
@@ -1751,7 +1839,7 @@ var GroqProvider = class {
1751
1839
  const choice = response.choices?.[0];
1752
1840
  const text = choice?.message?.content ?? "";
1753
1841
  if (text.length === 0) {
1754
- throw new AIError2(
1842
+ throw new AIError(
1755
1843
  "SERVER_ERROR" /* SERVER_ERROR */,
1756
1844
  "No content generated from Groq API",
1757
1845
  void 0,
@@ -1778,7 +1866,7 @@ var GroqProvider = class {
1778
1866
  try {
1779
1867
  const outcome = await consumeGroqStream(streamBody, callbacks);
1780
1868
  if (outcome.text.length === 0) {
1781
- throw new AIError2(
1869
+ throw new AIError(
1782
1870
  "SERVER_ERROR" /* SERVER_ERROR */,
1783
1871
  "No content generated from Groq API",
1784
1872
  void 0,
@@ -1909,7 +1997,9 @@ var GeminiProvider = class extends BaseProvider {
1909
1997
  headers: this.authHeaders()
1910
1998
  }
1911
1999
  );
1912
- return response.ok ? "healthy" : "unavailable";
2000
+ if (response.ok) return "healthy";
2001
+ if (response.status === 401 || response.status === 403) return "unavailable";
2002
+ return "degraded";
1913
2003
  } catch {
1914
2004
  return "unavailable";
1915
2005
  }
@@ -1920,6 +2010,8 @@ var GeminiProvider = class extends BaseProvider {
1920
2010
  async generate(request) {
1921
2011
  try {
1922
2012
  const model = request.model ?? this.resolveModel(request.tier ?? "balanced");
2013
+ const systemMessages = request.messages.filter((m) => m.role === "system");
2014
+ const systemPrompt = request.systemPrompt && request.systemPrompt.length > 0 ? request.systemPrompt : systemMessages.length > 0 ? systemMessages.map((m) => m.content).join("\n") : void 0;
1923
2015
  const response = await this.withRetry(async () => {
1924
2016
  return await this.fetchWithTimeout(
1925
2017
  `${this.config.baseUrl}/models/${model}:generateContent`,
@@ -1927,24 +2019,30 @@ var GeminiProvider = class extends BaseProvider {
1927
2019
  method: "POST",
1928
2020
  headers: this.authHeaders(),
1929
2021
  body: JSON.stringify({
1930
- systemInstruction: request.systemPrompt && request.systemPrompt.length > 0 ? { parts: [{ text: request.systemPrompt }] } : void 0,
2022
+ systemInstruction: systemPrompt && systemPrompt.length > 0 ? { parts: [{ text: systemPrompt }] } : void 0,
1931
2023
  contents: toGeminiContents(request.messages),
1932
2024
  generationConfig: {
1933
- temperature: request.temperature || 0.7,
1934
- maxOutputTokens: request.maxTokens || 2048,
1935
- topP: request.topP || 0.9
2025
+ temperature: request.temperature ?? 0.7,
2026
+ maxOutputTokens: request.maxTokens ?? 2048,
2027
+ topP: request.topP ?? 0.9
1936
2028
  }
1937
2029
  })
1938
2030
  }
1939
2031
  );
1940
2032
  });
1941
2033
  if (!response.ok) {
1942
- throw new Error(`Gemini API error: ${response.statusText}`);
2034
+ const message = await extractErrorMessageFromResponse(response);
2035
+ throw new AIError(
2036
+ classifyHttpStatus(response.status),
2037
+ `Gemini API error (${response.status}): ${message}`,
2038
+ void 0,
2039
+ { status: response.status, model }
2040
+ );
1943
2041
  }
1944
2042
  const data = await response.json();
1945
- const text = data.candidates[0]?.content?.parts?.map((part) => part.text ?? "").join("") ?? "";
2043
+ const text = data.candidates?.[0]?.content?.parts?.map((part) => part.text ?? "").join("") ?? "";
1946
2044
  if (text.length === 0) {
1947
- throw new AIError2("SERVER_ERROR" /* SERVER_ERROR */, "No content generated from Gemini API", void 0, { model });
2045
+ throw new AIError("SERVER_ERROR" /* SERVER_ERROR */, "No content generated from Gemini API", void 0, { model });
1948
2046
  }
1949
2047
  return {
1950
2048
  text,
@@ -1955,11 +2053,11 @@ var GeminiProvider = class extends BaseProvider {
1955
2053
  } : void 0,
1956
2054
  model,
1957
2055
  providerId: this.id,
1958
- finishReason: mapGeminiFinishReason(data.candidates[0]?.finishReason)
2056
+ finishReason: mapGeminiFinishReason(data.candidates?.[0]?.finishReason)
1959
2057
  };
1960
2058
  } catch (error) {
1961
- if (error instanceof AIError2) throw error;
1962
- throw new AIError2(
2059
+ if (error instanceof AIError) throw error;
2060
+ throw new AIError(
1963
2061
  "UNKNOWN_ERROR" /* UNKNOWN_ERROR */,
1964
2062
  `Gemini text generation failed: ${error instanceof Error ? error.message : "Unknown error"}`,
1965
2063
  error instanceof Error ? error : void 0
@@ -1970,7 +2068,7 @@ var GeminiProvider = class extends BaseProvider {
1970
2068
  const messages = [...request.messages];
1971
2069
  const lastUserIndex = findLastIndex3(messages, (m) => m.role === "user");
1972
2070
  if (lastUserIndex === -1) {
1973
- throw new AIError2(
2071
+ throw new AIError(
1974
2072
  "UNKNOWN_ERROR" /* UNKNOWN_ERROR */,
1975
2073
  "generateStructured requires at least one user message"
1976
2074
  );
@@ -2007,12 +2105,18 @@ var GeminiProvider = class extends BaseProvider {
2007
2105
  );
2008
2106
  });
2009
2107
  if (!response.ok) {
2010
- throw new Error(`Gemini API error: ${response.statusText}`);
2108
+ const message = await extractErrorMessageFromResponse(response);
2109
+ throw new AIError(
2110
+ classifyHttpStatus(response.status),
2111
+ `Gemini image generation error (${response.status}): ${message}`,
2112
+ void 0,
2113
+ { status: response.status }
2114
+ );
2011
2115
  }
2012
2116
  const data = await response.json();
2013
- const imageData = data.candidates[0]?.content?.parts[0]?.inlineData;
2117
+ const imageData = data.candidates?.[0]?.content?.parts?.[0]?.inlineData;
2014
2118
  if (!imageData) {
2015
- throw new Error("No image data in response");
2119
+ throw new AIError("SERVER_ERROR" /* SERVER_ERROR */, "No image data in Gemini response");
2016
2120
  }
2017
2121
  const url = `data:${imageData.mimeType};base64,${imageData.data}`;
2018
2122
  return {
@@ -2331,17 +2435,70 @@ var PrunaProvider = class {
2331
2435
  async estimateCost(_request) {
2332
2436
  return 1;
2333
2437
  }
2334
- async generateImage(_request) {
2335
- throw new AIError2("UNKNOWN_ERROR" /* UNKNOWN_ERROR */, "Use generateWithPruna instead");
2438
+ async generateImage(request) {
2439
+ let finalUrl;
2440
+ const model = this.config.models?.textToImage;
2441
+ for await (const stage of generateWithPruna(
2442
+ { prompt: request.prompt, model, quality: request.quality },
2443
+ this.config.apiKey
2444
+ )) {
2445
+ if (stage.error) throw stage.error;
2446
+ if (stage.result?.url) finalUrl = stage.result.url;
2447
+ }
2448
+ if (!finalUrl) {
2449
+ throw new AIError("SERVER_ERROR" /* SERVER_ERROR */, "No image generated by Pruna AI");
2450
+ }
2451
+ return {
2452
+ id: Date.now().toString(),
2453
+ type: "image",
2454
+ url: finalUrl,
2455
+ metadata: { provider: this.id, model: model ?? "p-image" },
2456
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
2457
+ };
2336
2458
  }
2337
- async generateVideo(_request) {
2338
- throw new AIError2("UNKNOWN_ERROR" /* UNKNOWN_ERROR */, "Use generateWithPruna instead");
2459
+ async generateVideo(request) {
2460
+ let finalUrl;
2461
+ const model = this.config.models?.textToVideo;
2462
+ for await (const stage of generateWithPruna(
2463
+ { prompt: request.prompt, model, duration: request.duration, aspectRatio: request.aspectRatio },
2464
+ this.config.apiKey
2465
+ )) {
2466
+ if (stage.error) throw stage.error;
2467
+ if (stage.result?.url) finalUrl = stage.result.url;
2468
+ }
2469
+ if (!finalUrl) {
2470
+ throw new AIError("SERVER_ERROR" /* SERVER_ERROR */, "No video generated by Pruna AI");
2471
+ }
2472
+ return {
2473
+ id: Date.now().toString(),
2474
+ type: "video",
2475
+ url: finalUrl,
2476
+ metadata: { provider: this.id, model: model ?? "p-video" },
2477
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
2478
+ };
2339
2479
  }
2340
- async imageToVideo(_request) {
2341
- throw new AIError2("UNKNOWN_ERROR" /* UNKNOWN_ERROR */, "Use generateWithPruna instead");
2480
+ async imageToVideo(request) {
2481
+ let finalUrl;
2482
+ for await (const stage of generateWithPruna(
2483
+ { image: request.imageUrl, prompt: request.prompt, duration: request.duration, motion: request.motion },
2484
+ this.config.apiKey
2485
+ )) {
2486
+ if (stage.error) throw stage.error;
2487
+ if (stage.result?.url) finalUrl = stage.result.url;
2488
+ }
2489
+ if (!finalUrl) {
2490
+ throw new AIError("SERVER_ERROR" /* SERVER_ERROR */, "No video generated from image by Pruna AI");
2491
+ }
2492
+ return {
2493
+ id: Date.now().toString(),
2494
+ type: "video",
2495
+ url: finalUrl,
2496
+ metadata: { provider: this.id, model: "p-image-to-video" },
2497
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
2498
+ };
2342
2499
  }
2343
2500
  async videoToVideo(_request) {
2344
- throw new AIError2("UNKNOWN_ERROR" /* UNKNOWN_ERROR */, "Use generateWithPruna instead");
2501
+ throw new AIError("CLIENT_ERROR" /* CLIENT_ERROR */, "Pruna AI provider does not support video-to-video conversion");
2345
2502
  }
2346
2503
  };
2347
2504
  function createPrunaProvider(config) {