@jxsuite/ai 0.37.1 → 0.37.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jxsuite/ai",
3
- "version": "0.37.1",
3
+ "version": "0.37.2",
4
4
  "description": "AI infrastructure for Jx Suite — streaming LLM client abstraction, tool registry, and reactive chat state",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -28,8 +28,8 @@
28
28
  "upgrade": "bun update --latest && bun install"
29
29
  },
30
30
  "dependencies": {
31
- "@jxsuite/protocol": "^2.3.0",
32
- "@vue/reactivity": "3.5.42"
31
+ "@jxsuite/protocol": "^2.3.1",
32
+ "@vue/reactivity": "3.5.43"
33
33
  },
34
34
  "devDependencies": {
35
35
  "@happy-dom/global-registrator": "^20.14.5"
@@ -144,6 +144,45 @@ interface ErrorResponseBody {
144
144
  * reading a sentence and deciding what it meant.
145
145
  */
146
146
  code?: string;
147
+ /**
148
+ * An array-shaped envelope, as Cloudflare's own REST API uses for a BYOK base URL pointed
149
+ * directly at it: `{ success: false, errors: [{ code, message }] }`, with no top-level `error`
150
+ * key at all. Reachable only when `baseUrl` bypasses the Studio proxy and talks to such a
151
+ * provider directly — the proxy itself normalizes this shape before it ever reaches a client.
152
+ */
153
+ errors?: { code?: number; message?: string }[];
154
+ }
155
+
156
+ /**
157
+ * Extract a human-readable message from an error body, falling back to the raw body (or `fallback`
158
+ * when the body is empty).
159
+ */
160
+ function extractErrorMessage(rawBody: string, fallback: string): string {
161
+ if (!rawBody) {
162
+ return fallback;
163
+ }
164
+ try {
165
+ const { error, errors } = JSON.parse(rawBody) as ErrorResponseBody;
166
+ if (typeof error === "string") {
167
+ return error;
168
+ }
169
+ if (error) {
170
+ const { message } = error;
171
+ if (message) {
172
+ return message;
173
+ }
174
+ }
175
+ const [first] = errors ?? [];
176
+ if (first) {
177
+ const { message } = first;
178
+ if (message) {
179
+ return message;
180
+ }
181
+ }
182
+ } catch {
183
+ /* Not JSON — use the raw body. */
184
+ }
185
+ return rawBody;
147
186
  }
148
187
 
149
188
  // ─── Type definitions ────────────────────────────────────────────────────────
@@ -275,9 +314,10 @@ export function createOpenAIStreamingClient({
275
314
  } catch {
276
315
  /* Ignore */
277
316
  }
317
+ const message = extractErrorMessage(errorBody, response.statusText);
278
318
  yield {
279
319
  type: "error",
280
- message: `API error ${response.status}: ${errorBody || response.statusText}`,
320
+ message: `API error ${response.status}: ${message}`,
281
321
  code: String(response.status),
282
322
  };
283
323
  return;
@@ -543,23 +583,17 @@ export function createProxyStreamingClient({
543
583
  } catch {
544
584
  /* Ignore */
545
585
  }
546
- // Try to extract a clean message from JSON error bodies (e.g. the proxy's
547
- // { error: "..." } shape or OpenAI's { error: { message: "..." } }).
548
- let cleanMessage = errorBody || response.statusText;
586
+ // Extract a clean message from the JSON error body — see extractErrorMessage.
587
+ const cleanMessage = extractErrorMessage(errorBody, response.statusText);
549
588
  /* The body's machine code beats the status. `code` was `String(response.status)`
550
589
  unconditionally, so a proxy answering 401 `{ code: "cf_reconnect_required" }` reached the
551
590
  client as `"401"` — indistinguishable from a bad BYOK key, and the one reading that would
552
591
  have put a Reconnect button on screen was thrown away at the only place it arrived. */
553
592
  let machineCode = "";
554
593
  try {
555
- const parsed = JSON.parse(errorBody) as ErrorResponseBody;
556
- if (typeof parsed.error === "string") {
557
- cleanMessage = parsed.error;
558
- } else if (parsed.error?.message) {
559
- cleanMessage = parsed.error.message;
560
- }
561
- if (typeof parsed.code === "string" && parsed.code) {
562
- machineCode = parsed.code;
594
+ const { code } = JSON.parse(errorBody) as ErrorResponseBody;
595
+ if (typeof code === "string" && code) {
596
+ machineCode = code;
563
597
  }
564
598
  } catch {
565
599
  /* Not JSON — use the raw body. */