workers-ai-provider 3.2.1 → 3.3.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.
@@ -1,4 +1,4 @@
1
- //#region \0@oxc-project+runtime@0.135.0/helpers/esm/typeof.js
1
+ //#region \0@oxc-project+runtime@0.137.0/helpers/esm/typeof.js
2
2
  function _typeof(o) {
3
3
  "@babel/helpers - typeof";
4
4
  return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(o) {
@@ -8,7 +8,7 @@ function _typeof(o) {
8
8
  }, _typeof(o);
9
9
  }
10
10
  //#endregion
11
- //#region \0@oxc-project+runtime@0.135.0/helpers/esm/toPrimitive.js
11
+ //#region \0@oxc-project+runtime@0.137.0/helpers/esm/toPrimitive.js
12
12
  function toPrimitive(t, r) {
13
13
  if ("object" != _typeof(t) || !t) return t;
14
14
  var e = t[Symbol.toPrimitive];
@@ -20,13 +20,13 @@ function toPrimitive(t, r) {
20
20
  return ("string" === r ? String : Number)(t);
21
21
  }
22
22
  //#endregion
23
- //#region \0@oxc-project+runtime@0.135.0/helpers/esm/toPropertyKey.js
23
+ //#region \0@oxc-project+runtime@0.137.0/helpers/esm/toPropertyKey.js
24
24
  function toPropertyKey(t) {
25
25
  var i = toPrimitive(t, "string");
26
26
  return "symbol" == _typeof(i) ? i : i + "";
27
27
  }
28
28
  //#endregion
29
- //#region \0@oxc-project+runtime@0.135.0/helpers/esm/defineProperty.js
29
+ //#region \0@oxc-project+runtime@0.137.0/helpers/esm/defineProperty.js
30
30
  function _defineProperty(e, r, t) {
31
31
  return (r = toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
32
32
  value: t,
@@ -36,148 +36,85 @@ function _defineProperty(e, r, t) {
36
36
  }) : e[r] = t, e;
37
37
  }
38
38
  //#endregion
39
- //#region src/errors.ts
40
- /** Map an HTTP status to a {@link GatewayErrorCode} + recoverability hint. */
41
- function classifyStatus(status) {
42
- if (status === 401 || status === 403) return {
43
- code: "auth",
44
- recoverable: false
45
- };
46
- if (status === 429) return {
47
- code: "rate-limit",
48
- recoverable: true
49
- };
50
- if (status === 404) return {
51
- code: "not-found",
52
- recoverable: false
53
- };
54
- if (status === 400 || status === 422) return {
55
- code: "bad-request",
56
- recoverable: false
57
- };
58
- if (status >= 500) return {
59
- code: "provider-error",
60
- recoverable: true
61
- };
62
- return {
63
- code: "unknown",
64
- recoverable: false
65
- };
66
- }
67
- /** Best-effort extraction of a human message from a CF/provider error envelope. */
68
- function extractErrorMessage(raw) {
69
- if (typeof raw === "string") {
70
- const trimmed = raw.trim();
71
- if (!trimmed) return void 0;
72
- try {
73
- return extractErrorMessage(JSON.parse(trimmed));
74
- } catch {
75
- return trimmed.slice(0, 500);
76
- }
77
- }
78
- if (!raw || typeof raw !== "object") return void 0;
79
- const obj = raw;
80
- if (Array.isArray(obj.errors) && obj.errors.length > 0) {
81
- const first = obj.errors[0];
82
- if (typeof first?.message === "string") return first.message;
83
- }
84
- if (obj.error && typeof obj.error === "object") {
85
- const err = obj.error;
86
- if (typeof err.message === "string") return err.message;
87
- }
88
- if (typeof obj.error === "string") return obj.error;
89
- if (typeof obj.message === "string") return obj.message;
90
- }
91
- /** A single dispatch failure through AI Gateway (run or gateway path). */
92
- var WorkersAIGatewayError = class WorkersAIGatewayError extends Error {
93
- constructor(code, message, opts = {}) {
39
+ //#region ../gateway-core/src/errors.ts
40
+ var GatewayDelegateError = class extends Error {
41
+ constructor(kind, message, cause) {
94
42
  super(message);
95
- _defineProperty(this, "code", void 0);
96
- _defineProperty(this, "recoverable", void 0);
97
- _defineProperty(this, "status", void 0);
98
- _defineProperty(this, "context", void 0);
99
- _defineProperty(this, "raw", void 0);
43
+ _defineProperty(this, "kind", void 0);
100
44
  _defineProperty(this, "cause", void 0);
101
- this.name = "WorkersAIGatewayError";
102
- this.code = code;
103
- this.recoverable = opts.recoverable ?? false;
104
- this.status = opts.status ?? null;
105
- this.context = opts.context ?? {};
106
- this.raw = opts.raw;
107
- this.cause = opts.cause;
108
- }
109
- /**
110
- * Classify an arbitrary thrown value. Understands AI SDK `APICallError`
111
- * (reads `statusCode` / `responseBody` / `isRetryable`); falls back to a
112
- * recoverable `gateway-error` for transport/connection failures so a fallback
113
- * chain keeps trying.
114
- */
115
- static fromUnknown(e) {
116
- if (e instanceof WorkersAIGatewayError) return e;
117
- const obj = e && typeof e === "object" ? e : {};
118
- const status = typeof obj.statusCode === "number" ? obj.statusCode : null;
119
- const responseBody = typeof obj.responseBody === "string" ? obj.responseBody : void 0;
120
- if (status !== null) {
121
- const classified = classifyStatus(status);
122
- const recoverable = typeof obj.isRetryable === "boolean" ? obj.isRetryable : classified.recoverable;
123
- const message = extractErrorMessage(responseBody) ?? (e instanceof Error ? e.message : `Gateway dispatch failed (HTTP ${status}).`);
124
- let raw = responseBody;
125
- try {
126
- raw = responseBody ? JSON.parse(responseBody) : responseBody;
127
- } catch {}
128
- return new WorkersAIGatewayError(classified.code, message, {
129
- recoverable,
130
- status,
131
- raw,
132
- cause: e
133
- });
134
- }
135
- return new WorkersAIGatewayError("gateway-error", e instanceof Error ? e.message : String(e), {
136
- recoverable: true,
137
- cause: e
138
- });
139
- }
140
- /** Build from an HTTP `Response` (reads the body for the envelope). */
141
- static async fromResponse(resp, context = {}) {
142
- const text = await resp.text().catch(() => "");
143
- const { code, recoverable } = classifyStatus(resp.status);
144
- const message = extractErrorMessage(text) ?? `Gateway dispatch failed (HTTP ${resp.status}).`;
145
- let raw = text;
146
- try {
147
- raw = text ? JSON.parse(text) : text;
148
- } catch {}
149
- return new WorkersAIGatewayError(code, message, {
150
- recoverable,
151
- status: resp.status,
152
- raw,
153
- context: {
154
- ...context,
155
- status: resp.status,
156
- logId: resp.headers.get("cf-aig-log-id"),
157
- runId: resp.headers.get("cf-aig-run-id")
158
- }
159
- });
45
+ this.name = "GatewayDelegateError";
46
+ this.kind = kind;
47
+ this.cause = cause;
160
48
  }
161
49
  };
162
- /** Every model in a client-side fallback chain failed. */
163
- var WorkersAIFallbackError = class extends Error {
164
- constructor(attempts, message) {
165
- const tried = attempts.map((a) => a.model).join(" → ");
166
- super(message ?? `All fallback models failed: ${tried}.`);
167
- _defineProperty(this, "attempts", void 0);
168
- this.name = "WorkersAIFallbackError";
169
- this.attempts = attempts;
170
- }
171
- /** The last (most recent) attempt's error, if any. */
172
- get lastError() {
173
- for (let i = this.attempts.length - 1; i >= 0; i--) {
174
- const e = this.attempts[i].error;
175
- if (e) return e;
176
- }
50
+ //#endregion
51
+ //#region ../gateway-core/src/gateway-fetch.ts
52
+ /** JSON-encode metadata for the `cf-aig-metadata` header (`bigint` → string). */
53
+ function serializeMetadata(metadata) {
54
+ return JSON.stringify(metadata, (_k, v) => typeof v === "bigint" ? v.toString() : v);
55
+ }
56
+ /** Hop-by-hop headers that must never be forwarded to the gateway. */
57
+ const STRIP_HEADERS_BASE = ["content-length", "host"];
58
+ /** Normalize any `HeadersInit` shape into a plain object. */
59
+ function headersToObject(h) {
60
+ const out = {};
61
+ if (!h) return out;
62
+ if (h instanceof Headers) for (const [k, v] of h) out[k] = v;
63
+ else if (Array.isArray(h)) for (const [k, v] of h) out[k] = v;
64
+ else Object.assign(out, h);
65
+ return out;
66
+ }
67
+ /** Best-effort decode of a request body to text for re-parsing as JSON. */
68
+ function asText(body) {
69
+ if (typeof body === "string") return body;
70
+ if (body instanceof Uint8Array) return new TextDecoder().decode(body);
71
+ if (body instanceof ArrayBuffer) return new TextDecoder().decode(body);
72
+ return "{}";
73
+ }
74
+ /**
75
+ * Set the `cf-aig-*` cache/log/request headers on `headers` for every option
76
+ * that is defined. Mutates `headers` in place (callers pass the entry's header
77
+ * object).
78
+ */
79
+ function applyGatewayCacheHeaders(headers, opts) {
80
+ if (opts.cacheTtl !== void 0) headers["cf-aig-cache-ttl"] = String(opts.cacheTtl);
81
+ if (opts.skipCache) headers["cf-aig-skip-cache"] = "true";
82
+ if (opts.cacheKey !== void 0) headers["cf-aig-cache-key"] = opts.cacheKey;
83
+ if (opts.metadata) headers["cf-aig-metadata"] = serializeMetadata(opts.metadata);
84
+ if (opts.collectLog !== void 0) headers["cf-aig-collect-log"] = String(opts.collectLog);
85
+ if (opts.eventId !== void 0) headers["cf-aig-event-id"] = opts.eventId;
86
+ if (opts.requestTimeoutMs !== void 0) headers["cf-aig-request-timeout"] = String(opts.requestTimeoutMs);
87
+ if (opts.byokAlias !== void 0) headers["cf-aig-byok-alias"] = opts.byokAlias;
88
+ if (opts.zdr !== void 0) headers["cf-aig-zdr"] = String(opts.zdr);
89
+ if (opts.retries) {
90
+ const { maxAttempts, retryDelayMs, backoff } = opts.retries;
91
+ if (maxAttempts !== void 0) headers["cf-aig-max-attempts"] = String(maxAttempts);
92
+ if (retryDelayMs !== void 0) headers["cf-aig-retry-delay"] = String(retryDelayMs);
93
+ if (backoff !== void 0) headers["cf-aig-backoff"] = backoff;
177
94
  }
178
- };
95
+ }
96
+ /**
97
+ * Assemble a single gateway entry: strip hop-by-hop + provider-auth headers,
98
+ * layer on `extraHeaders` and `cf-aig-*` cache headers, and attach the body as
99
+ * `query`. Endpoint derivation stays with the caller because the gateway-path
100
+ * (registry host-strip) and the REST path (`/v1/` strip) differ.
101
+ */
102
+ function buildGatewayEntry(params) {
103
+ const strip = new Set(STRIP_HEADERS_BASE);
104
+ if (params.stripAuthHeaders) for (const h of params.stripAuthHeaders) strip.add(h.toLowerCase());
105
+ const headers = {};
106
+ for (const [k, v] of Object.entries(headersToObject(params.initHeaders))) if (!strip.has(k.toLowerCase())) headers[k] = v;
107
+ if (params.extraHeaders) Object.assign(headers, params.extraHeaders);
108
+ if (params.cache) applyGatewayCacheHeaders(headers, params.cache);
109
+ return {
110
+ provider: params.providerId,
111
+ endpoint: params.endpoint,
112
+ headers,
113
+ query: params.body
114
+ };
115
+ }
179
116
  //#endregion
180
- //#region src/gateway-providers.ts
117
+ //#region ../gateway-core/src/gateway-providers.ts
181
118
  /** Strip a leading `https://<host>/` prefix, leaving the endpoint path + query. */
182
119
  function hostStrip(pattern) {
183
120
  return (url) => url.replace(pattern, "");
@@ -309,8 +246,8 @@ const GATEWAY_PROVIDERS = [
309
246
  gatewayProviderId: "deepseek",
310
247
  wireFormat: "openai",
311
248
  baseURL: "https://api.deepseek.com",
312
- runCatalog: false,
313
- billing: "byok",
249
+ runCatalog: true,
250
+ billing: "unified",
314
251
  authHeaders: ["authorization"],
315
252
  hostPattern: DEEPSEEK_HOST,
316
253
  transformEndpoint: hostStrip(DEEPSEEK_HOST)
@@ -498,22 +435,478 @@ function wireableProviders() {
498
435
  return GATEWAY_PROVIDERS.filter((p) => p.wireFormat !== void 0);
499
436
  }
500
437
  //#endregion
501
- //#region src/gateway-provider.ts
502
- const STRIP_HEADERS_BASE = new Set(["content-length", "host"]);
503
- function asText(body) {
504
- if (typeof body === "string") return body;
505
- if (body instanceof Uint8Array) return new TextDecoder().decode(body);
506
- if (body instanceof ArrayBuffer) return new TextDecoder().decode(body);
507
- return "{}";
508
- }
509
- function headersToObject(h) {
510
- const out = {};
511
- if (!h) return out;
512
- if (h instanceof Headers) for (const [k, v] of h) out[k] = v;
513
- else if (Array.isArray(h)) for (const [k, v] of h) out[k] = v;
514
- else Object.assign(out, h);
438
+ //#region ../gateway-core/src/resumable-stream.ts
439
+ function concat(a, b) {
440
+ const out = new Uint8Array(new ArrayBuffer(a.length + b.length));
441
+ out.set(a, 0);
442
+ out.set(b, a.length);
515
443
  return out;
516
444
  }
445
+ /** Index just past the last `\n\n` in `buf`, or -1 if there is no complete event. */
446
+ function lastEventBoundary(buf) {
447
+ for (let i = buf.length - 2; i >= 0; i--) if (buf[i] === 10 && buf[i + 1] === 10) return i + 2;
448
+ return -1;
449
+ }
450
+ /** Count of `\n\n` terminators (= complete SSE events) in `buf`. */
451
+ function countEvents(buf) {
452
+ let n = 0;
453
+ for (let i = 0; i + 1 < buf.length; i++) if (buf[i] === 10 && buf[i + 1] === 10) {
454
+ n++;
455
+ i++;
456
+ }
457
+ return n;
458
+ }
459
+ function resumeUrl(gateway, runId, from) {
460
+ return `https://workers-binding.ai/ai-gateway/gateways/${gateway}/run/${runId}/resume?from=${from}`;
461
+ }
462
+ function createResumableStream(options) {
463
+ const { binding, gateway, runId } = options;
464
+ const maxReconnects = options.maxReconnects ?? 5;
465
+ const onExpired = options.onResumeExpired ?? "error";
466
+ let emittedEvents = options.fromEvent ?? 0;
467
+ let pending = new Uint8Array(/* @__PURE__ */ new ArrayBuffer(0));
468
+ let reconnects = 0;
469
+ let canceled = false;
470
+ let activeReader = null;
471
+ const isAborted = () => canceled || options.signal?.aborted === true;
472
+ async function fetchResume(controller) {
473
+ let res;
474
+ try {
475
+ res = await binding.fetch(resumeUrl(gateway, runId, emittedEvents), {
476
+ method: "GET",
477
+ signal: options.signal
478
+ });
479
+ } catch (fetchErr) {
480
+ controller.error(new GatewayDelegateError("dispatch", `Resume request threw at event ${emittedEvents}.`, fetchErr));
481
+ return null;
482
+ }
483
+ if (res.status === 404) {
484
+ if (onExpired === "accept-partial") {
485
+ controller.close();
486
+ return null;
487
+ }
488
+ controller.error(new GatewayDelegateError("resume-expired", `Resume buffer expired (404) at event ${emittedEvents}. The gateway buffer TTL (~5.5 min) elapsed; fall back to continuation or regeneration.`));
489
+ return null;
490
+ }
491
+ if (!res.ok || !res.body) {
492
+ controller.error(new GatewayDelegateError("dispatch", `Resume failed (${res.status}) at event ${emittedEvents}.`));
493
+ return null;
494
+ }
495
+ return res.body;
496
+ }
497
+ return new ReadableStream({
498
+ async start(controller) {
499
+ let current;
500
+ if (options.initial) current = options.initial;
501
+ else {
502
+ const body = await fetchResume(controller);
503
+ if (!body) return;
504
+ current = body;
505
+ }
506
+ for (;;) {
507
+ const reader = current.getReader();
508
+ activeReader = reader;
509
+ try {
510
+ for (;;) {
511
+ const { done, value } = await reader.read();
512
+ if (done) {
513
+ if (pending.length > 0) {
514
+ controller.enqueue(pending);
515
+ pending = new Uint8Array(/* @__PURE__ */ new ArrayBuffer(0));
516
+ }
517
+ controller.close();
518
+ return;
519
+ }
520
+ if (!value || value.length === 0) continue;
521
+ pending = concat(pending, value);
522
+ const boundary = lastEventBoundary(pending);
523
+ if (boundary > 0) {
524
+ const complete = pending.slice(0, boundary);
525
+ controller.enqueue(complete);
526
+ emittedEvents += countEvents(complete);
527
+ options.onProgress?.(emittedEvents);
528
+ pending = pending.slice(boundary);
529
+ }
530
+ }
531
+ } catch (err) {
532
+ try {
533
+ reader.releaseLock();
534
+ } catch {}
535
+ if (isAborted()) return;
536
+ if (reconnects >= maxReconnects) {
537
+ controller.error(new GatewayDelegateError("resume-expired", `Exceeded ${maxReconnects} reconnect attempts at event ${emittedEvents}.`, err));
538
+ return;
539
+ }
540
+ pending = new Uint8Array(/* @__PURE__ */ new ArrayBuffer(0));
541
+ reconnects++;
542
+ options.onReconnect?.(emittedEvents, reconnects);
543
+ const body = await fetchResume(controller);
544
+ if (!body) return;
545
+ current = body;
546
+ }
547
+ }
548
+ },
549
+ cancel(reason) {
550
+ canceled = true;
551
+ if (activeReader) activeReader.cancel(reason).catch(() => {});
552
+ }
553
+ });
554
+ }
555
+ //#endregion
556
+ //#region ../gateway-core/src/workers-ai.ts
557
+ /**
558
+ * Shared, framework-agnostic Workers AI helpers.
559
+ *
560
+ * These are the pieces that `workers-ai-provider` (native `LanguageModelV3`) and
561
+ * `@cloudflare/tanstack-ai` (OpenAI-SDK shim) both need: the SSE byte decoder,
562
+ * message normalization for the binding's stricter schema, response-text
563
+ * extraction across WAI's response shapes, and the gpt-oss forced-tool-call
564
+ * salvage.
565
+ *
566
+ * IMPORTANT — id/dependency decoupling: nothing here depends on the `ai` package
567
+ * or mints framework tool-call ids. `parseLeakedToolCalls` returns neutral
568
+ * `{ toolName, input }` records; each consumer assigns its own ids and adapts to
569
+ * its own tool-call shape. This keeps `gateway-core` free of an `ai` dependency.
570
+ */
571
+ /**
572
+ * TransformStream that decodes a raw byte stream into SSE `data:` payloads.
573
+ * Each output chunk is the string content after `data: ` (one per SSE event),
574
+ * with line buffering for partial chunks.
575
+ */
576
+ var SSEDecoder = class extends TransformStream {
577
+ constructor() {
578
+ let buffer = "";
579
+ const decoder = new TextDecoder();
580
+ const emit = (line, controller) => {
581
+ const trimmed = line.trim();
582
+ if (!trimmed) return;
583
+ if (trimmed.startsWith("data: ")) controller.enqueue(trimmed.slice(6));
584
+ else if (trimmed.startsWith("data:")) controller.enqueue(trimmed.slice(5));
585
+ };
586
+ super({
587
+ transform(chunk, controller) {
588
+ buffer += decoder.decode(chunk, { stream: true });
589
+ const lines = buffer.split("\n");
590
+ buffer = lines.pop() || "";
591
+ for (const line of lines) emit(line, controller);
592
+ },
593
+ flush(controller) {
594
+ if (buffer.trim()) emit(buffer, controller);
595
+ }
596
+ });
597
+ }
598
+ };
599
+ /**
600
+ * Normalize messages before passing to the Workers AI binding.
601
+ *
602
+ * The binding has strict schema validation that differs from the OpenAI API:
603
+ * `content` must not be `null`/`undefined` (coerced to `""`). Content arrays
604
+ * (image_url parts) pass through untouched for vision-capable models.
605
+ */
606
+ function normalizeMessagesForBinding(messages) {
607
+ return messages.map((msg) => {
608
+ const normalized = { ...msg };
609
+ if (normalized.content === null || normalized.content === void 0) normalized.content = "";
610
+ return normalized;
611
+ });
612
+ }
613
+ /**
614
+ * Extract text from a Workers AI response, handling multiple response shapes:
615
+ * - OpenAI format: `{ choices: [{ message: { content: "..." } }] }`
616
+ * - Native format: `{ response: "..." }`
617
+ * - Structured-output quirk: `{ response: { ... } }` (object) / `"{ ... }"` (JSON string)
618
+ * - Numeric `{ response: 42 }`
619
+ */
620
+ function processText(output) {
621
+ const choiceContent = output.choices?.[0]?.message?.content;
622
+ if (choiceContent != null && String(choiceContent).length > 0) return String(choiceContent);
623
+ if ("response" in output) {
624
+ const response = output.response;
625
+ if (typeof response === "object" && response !== null) return JSON.stringify(response);
626
+ if (typeof response === "number") return String(response);
627
+ if (response === null || response === void 0) return;
628
+ return String(response);
629
+ }
630
+ }
631
+ /**
632
+ * Was a specific tool forced for this request?
633
+ *
634
+ * True for both `tool_choice: "required"` and the named-function form
635
+ * `{ type: "function", function: { name } }`.
636
+ */
637
+ function isForcedToolChoice(toolChoice) {
638
+ if (toolChoice === "required") return true;
639
+ return typeof toolChoice === "object" && toolChoice !== null && toolChoice.type === "function";
640
+ }
641
+ /** Collect the requested tool names from mapped tools. */
642
+ function getToolNames(tools) {
643
+ return new Set((tools ?? []).map((tool) => tool.function?.name).filter((name) => typeof name === "string"));
644
+ }
645
+ /**
646
+ * Parse tool calls that a model leaked as JSON text instead of structured
647
+ * `tool_calls`. Shared by the non-streaming salvage and the streaming buffer.
648
+ *
649
+ * Only JSON objects whose `name` is one of `knownToolNames` are recovered;
650
+ * everything else (prose, harmony channel/role leaks like `{"name":"analysis"}`,
651
+ * hallucinated names) is ignored to avoid fabricating bogus calls.
652
+ *
653
+ * Returns neutral `{ toolName, input }` records — callers assign their own ids.
654
+ */
655
+ function parseLeakedToolCalls(text, knownToolNames) {
656
+ let parsed;
657
+ try {
658
+ parsed = JSON.parse(text.trim());
659
+ } catch {
660
+ return [];
661
+ }
662
+ const candidates = Array.isArray(parsed) ? parsed : [parsed];
663
+ const salvaged = [];
664
+ for (const candidate of candidates) {
665
+ if (typeof candidate !== "object" || candidate === null) continue;
666
+ const obj = candidate;
667
+ const name = obj.name;
668
+ if (typeof name !== "string" || !knownToolNames.has(name)) continue;
669
+ let args;
670
+ if ("arguments" in obj) args = obj.arguments;
671
+ else if ("parameters" in obj) args = obj.parameters;
672
+ else {
673
+ const { name: _name, ...rest } = obj;
674
+ args = rest;
675
+ }
676
+ salvaged.push({
677
+ toolName: name,
678
+ input: typeof args === "string" ? args : JSON.stringify(args ?? {})
679
+ });
680
+ }
681
+ return salvaged;
682
+ }
683
+ //#endregion
684
+ //#region ../gateway-core/src/workers-ai-errors.ts
685
+ /**
686
+ * Shared, dependency-free Workers AI error classification used by every
687
+ * consumer of `@cloudflare/gateway-core` (`workers-ai-provider` and
688
+ * `@cloudflare/tanstack-ai`).
689
+ *
690
+ * The Workers AI **binding** (`env.AI.run`) throws plain `Error`s whose message
691
+ * carries an internal code (e.g. `"3040: Capacity temporarily exceeded"`) but
692
+ * no HTTP status. Retry machinery (the AI SDK's `APICallError` retry, the OpenAI
693
+ * SDK's status-based retry, and our own non-chat retry loop) all key off an HTTP
694
+ * status, so we translate the internal code into the documented status and let
695
+ * each layer derive retryability from it — this is what makes transient failures
696
+ * like "out of capacity" (3040 → 429) automatically retried.
697
+ *
698
+ * This module is intentionally free of any provider-SDK types so it can be
699
+ * inlined into each consumer's bundle.
700
+ */
701
+ /**
702
+ * Workers AI internal error code → HTTP status code.
703
+ *
704
+ * Source: https://developers.cloudflare.com/workers-ai/platform/errors/
705
+ */
706
+ const WORKERS_AI_ERROR_CODE_TO_STATUS = {
707
+ 5007: 400,
708
+ 5004: 400,
709
+ 3039: 400,
710
+ 3003: 400,
711
+ 5018: 403,
712
+ 5016: 403,
713
+ 3023: 403,
714
+ 3041: 403,
715
+ 5019: 405,
716
+ 5005: 405,
717
+ 3042: 404,
718
+ 3006: 413,
719
+ 3007: 408,
720
+ 3008: 408,
721
+ 3036: 429,
722
+ 3040: 429
723
+ };
724
+ /** Read a human-readable message from any thrown value (Error, DOMException, plain object, string). */
725
+ function messageOf(error) {
726
+ if (error instanceof Error) return error.message;
727
+ if (typeof error === "string") return error;
728
+ if (error && typeof error === "object") {
729
+ const message = error.message;
730
+ if (typeof message === "string") return message;
731
+ }
732
+ return String(error);
733
+ }
734
+ /**
735
+ * Best-effort extraction of a Workers AI internal error code from a thrown
736
+ * binding error. Prefers a numeric `code` property when present, otherwise
737
+ * parses the `"<code>: <message>"` form the binding uses (optionally prefixed,
738
+ * e.g. `"InferenceUpstreamError: 3040: ..."`). Only recognized codes are
739
+ * returned, and when several `"<number>:"` groups appear (e.g. a leading
740
+ * request id) the first that maps to a known code wins, so unrelated numbers
741
+ * can't be misread as a code.
742
+ */
743
+ function parseWorkersAIErrorCode(error) {
744
+ if (error && typeof error === "object") {
745
+ const code = error.code;
746
+ if (typeof code === "number" && code in WORKERS_AI_ERROR_CODE_TO_STATUS) return code;
747
+ if (typeof code === "string") {
748
+ const parsed = Number.parseInt(code, 10);
749
+ if (Number.isFinite(parsed) && parsed in WORKERS_AI_ERROR_CODE_TO_STATUS) return parsed;
750
+ }
751
+ }
752
+ const message = messageOf(error);
753
+ for (const match of message.matchAll(/\b(\d{3,5})\s*:/g)) {
754
+ const parsed = Number.parseInt(match[1], 10);
755
+ if (parsed in WORKERS_AI_ERROR_CODE_TO_STATUS) return parsed;
756
+ }
757
+ }
758
+ /**
759
+ * True for cancellation errors that must propagate untouched (never wrapped or
760
+ * retried, so each layer's own abort detection still fires). Mirrors the AI
761
+ * SDK's `isAbortError`: a real abort from `fetch`/the binding is a
762
+ * `DOMException`, which is NOT `instanceof Error`, so both must be checked.
763
+ */
764
+ function isAbortError(error) {
765
+ return (error instanceof Error || error instanceof DOMException) && (error.name === "AbortError" || error.name === "ResponseAborted" || error.name === "TimeoutError");
766
+ }
767
+ //#endregion
768
+ //#region src/errors.ts
769
+ /** Map an HTTP status to a {@link GatewayErrorCode} + recoverability hint. */
770
+ function classifyStatus(status) {
771
+ if (status === 401 || status === 403) return {
772
+ code: "auth",
773
+ recoverable: false
774
+ };
775
+ if (status === 429) return {
776
+ code: "rate-limit",
777
+ recoverable: true
778
+ };
779
+ if (status === 404) return {
780
+ code: "not-found",
781
+ recoverable: false
782
+ };
783
+ if (status === 400 || status === 422) return {
784
+ code: "bad-request",
785
+ recoverable: false
786
+ };
787
+ if (status >= 500) return {
788
+ code: "provider-error",
789
+ recoverable: true
790
+ };
791
+ return {
792
+ code: "unknown",
793
+ recoverable: false
794
+ };
795
+ }
796
+ /** Best-effort extraction of a human message from a CF/provider error envelope. */
797
+ function extractErrorMessage(raw) {
798
+ if (typeof raw === "string") {
799
+ const trimmed = raw.trim();
800
+ if (!trimmed) return void 0;
801
+ try {
802
+ return extractErrorMessage(JSON.parse(trimmed));
803
+ } catch {
804
+ return trimmed.slice(0, 500);
805
+ }
806
+ }
807
+ if (!raw || typeof raw !== "object") return void 0;
808
+ const obj = raw;
809
+ if (Array.isArray(obj.errors) && obj.errors.length > 0) {
810
+ const first = obj.errors[0];
811
+ if (typeof first?.message === "string") return first.message;
812
+ }
813
+ if (obj.error && typeof obj.error === "object") {
814
+ const err = obj.error;
815
+ if (typeof err.message === "string") return err.message;
816
+ }
817
+ if (typeof obj.error === "string") return obj.error;
818
+ if (typeof obj.message === "string") return obj.message;
819
+ }
820
+ /** A single dispatch failure through AI Gateway (run or gateway path). */
821
+ var WorkersAIGatewayError = class WorkersAIGatewayError extends Error {
822
+ constructor(code, message, opts = {}) {
823
+ super(message);
824
+ _defineProperty(this, "code", void 0);
825
+ _defineProperty(this, "recoverable", void 0);
826
+ _defineProperty(this, "status", void 0);
827
+ _defineProperty(this, "context", void 0);
828
+ _defineProperty(this, "raw", void 0);
829
+ _defineProperty(this, "cause", void 0);
830
+ this.name = "WorkersAIGatewayError";
831
+ this.code = code;
832
+ this.recoverable = opts.recoverable ?? false;
833
+ this.status = opts.status ?? null;
834
+ this.context = opts.context ?? {};
835
+ this.raw = opts.raw;
836
+ this.cause = opts.cause;
837
+ }
838
+ /**
839
+ * Classify an arbitrary thrown value. Understands AI SDK `APICallError`
840
+ * (reads `statusCode` / `responseBody` / `isRetryable`); falls back to a
841
+ * recoverable `gateway-error` for transport/connection failures so a fallback
842
+ * chain keeps trying.
843
+ */
844
+ static fromUnknown(e) {
845
+ if (e instanceof WorkersAIGatewayError) return e;
846
+ const obj = e && typeof e === "object" ? e : {};
847
+ const status = typeof obj.statusCode === "number" ? obj.statusCode : null;
848
+ const responseBody = typeof obj.responseBody === "string" ? obj.responseBody : void 0;
849
+ if (status !== null) {
850
+ const classified = classifyStatus(status);
851
+ const recoverable = typeof obj.isRetryable === "boolean" ? obj.isRetryable : classified.recoverable;
852
+ const message = extractErrorMessage(responseBody) ?? (e instanceof Error ? e.message : `Gateway dispatch failed (HTTP ${status}).`);
853
+ let raw = responseBody;
854
+ try {
855
+ raw = responseBody ? JSON.parse(responseBody) : responseBody;
856
+ } catch {}
857
+ return new WorkersAIGatewayError(classified.code, message, {
858
+ recoverable,
859
+ status,
860
+ raw,
861
+ cause: e
862
+ });
863
+ }
864
+ return new WorkersAIGatewayError("gateway-error", e instanceof Error ? e.message : String(e), {
865
+ recoverable: true,
866
+ cause: e
867
+ });
868
+ }
869
+ /** Build from an HTTP `Response` (reads the body for the envelope). */
870
+ static async fromResponse(resp, context = {}) {
871
+ const text = await resp.text().catch(() => "");
872
+ const { code, recoverable } = classifyStatus(resp.status);
873
+ const message = extractErrorMessage(text) ?? `Gateway dispatch failed (HTTP ${resp.status}).`;
874
+ let raw = text;
875
+ try {
876
+ raw = text ? JSON.parse(text) : text;
877
+ } catch {}
878
+ return new WorkersAIGatewayError(code, message, {
879
+ recoverable,
880
+ status: resp.status,
881
+ raw,
882
+ context: {
883
+ ...context,
884
+ status: resp.status,
885
+ logId: resp.headers.get("cf-aig-log-id"),
886
+ runId: resp.headers.get("cf-aig-run-id")
887
+ }
888
+ });
889
+ }
890
+ };
891
+ /** Every model in a client-side fallback chain failed. */
892
+ var WorkersAIFallbackError = class extends Error {
893
+ constructor(attempts, message) {
894
+ const tried = attempts.map((a) => a.model).join(" → ");
895
+ super(message ?? `All fallback models failed: ${tried}.`);
896
+ _defineProperty(this, "attempts", void 0);
897
+ this.name = "WorkersAIFallbackError";
898
+ this.attempts = attempts;
899
+ }
900
+ /** The last (most recent) attempt's error, if any. */
901
+ get lastError() {
902
+ for (let i = this.attempts.length - 1; i >= 0; i--) {
903
+ const e = this.attempts[i].error;
904
+ if (e) return e;
905
+ }
906
+ }
907
+ };
908
+ //#endregion
909
+ //#region src/gateway-provider.ts
517
910
  /**
518
911
  * A `fetch` that dispatches the wrapped provider's request through AI Gateway.
519
912
  * Detects the gateway provider id from the request URL (or uses `config.provider`),
@@ -534,19 +927,18 @@ function createGatewayFetch(config) {
534
927
  const providerId = config.provider ?? info.gatewayProviderId;
535
928
  const endpoint = info?.transformEndpoint ? info.transformEndpoint(rawUrl) : rawUrl.replace(/^https?:\/\/[^/]+\//, "");
536
929
  const body = JSON.parse(asText(init?.body));
537
- const strip = new Set(STRIP_HEADERS_BASE);
538
- if (!config.byok && info) for (const h of info.authHeaders) strip.add(h.toLowerCase());
539
- const headers = {};
540
- for (const [k, v] of Object.entries(headersToObject(init?.headers))) if (!strip.has(k.toLowerCase())) headers[k] = v;
541
- if (config.extraHeaders) Object.assign(headers, config.extraHeaders);
542
- if (config.cacheTtl !== void 0) headers["cf-aig-cache-ttl"] = String(config.cacheTtl);
543
- if (config.skipCache) headers["cf-aig-skip-cache"] = "true";
544
- const entry = {
545
- provider: providerId,
930
+ const entry = buildGatewayEntry({
931
+ providerId,
546
932
  endpoint,
547
- headers,
548
- query: body
549
- };
933
+ initHeaders: init?.headers,
934
+ body,
935
+ ...!config.byok && info ? { stripAuthHeaders: info.authHeaders } : {},
936
+ ...config.extraHeaders ? { extraHeaders: config.extraHeaders } : {},
937
+ cache: {
938
+ cacheTtl: config.cacheTtl,
939
+ skipCache: config.skipCache
940
+ }
941
+ });
550
942
  const gw = config.binding.gateway(gatewayId);
551
943
  const runOptions = {};
552
944
  if (init?.signal) runOptions.signal = init.signal;
@@ -578,6 +970,6 @@ function createGatewayProvider(factory, config) {
578
970
  });
579
971
  }
580
972
  //#endregion
581
- export { findProviderBySlug as a, WorkersAIGatewayError as c, detectProviderByUrl as i, _defineProperty as l, createGatewayProvider as n, wireableProviders as o, GATEWAY_PROVIDERS as r, WorkersAIFallbackError as s, createGatewayFetch as t };
973
+ export { GatewayDelegateError as C, headersToObject as S, detectProviderByUrl as _, WORKERS_AI_ERROR_CODE_TO_STATUS as a, asText as b, parseWorkersAIErrorCode as c, isForcedToolChoice as d, normalizeMessagesForBinding as f, GATEWAY_PROVIDERS as g, createResumableStream as h, WorkersAIGatewayError as i, SSEDecoder as l, processText as m, createGatewayProvider as n, isAbortError as o, parseLeakedToolCalls as p, WorkersAIFallbackError as r, messageOf as s, createGatewayFetch as t, getToolNames as u, findProviderBySlug as v, _defineProperty as w, buildGatewayEntry as x, wireableProviders as y };
582
974
 
583
- //# sourceMappingURL=gateway-provider-1USFWm7c.mjs.map
975
+ //# sourceMappingURL=gateway-provider-CQU-v2IO.mjs.map