@cloudflare/tanstack-ai 0.1.9 → 0.1.10

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 (84) hide show
  1. package/README.md +5 -5
  2. package/dist/adapters/anthropic.cjs +2 -3
  3. package/dist/adapters/anthropic.cjs.map +1 -1
  4. package/dist/adapters/anthropic.d.cts +4 -3
  5. package/dist/adapters/anthropic.d.mts +4 -3
  6. package/dist/adapters/anthropic.mjs +3 -3
  7. package/dist/adapters/anthropic.mjs.map +1 -1
  8. package/dist/adapters/gemini.cjs +2 -3
  9. package/dist/adapters/gemini.cjs.map +1 -1
  10. package/dist/adapters/gemini.d.cts +6 -6
  11. package/dist/adapters/gemini.d.mts +6 -6
  12. package/dist/adapters/gemini.mjs +4 -3
  13. package/dist/adapters/gemini.mjs.map +1 -1
  14. package/dist/adapters/grok.cjs +2 -6
  15. package/dist/adapters/grok.cjs.map +1 -1
  16. package/dist/adapters/grok.d.cts +5 -4
  17. package/dist/adapters/grok.d.mts +5 -4
  18. package/dist/adapters/grok.mjs +3 -6
  19. package/dist/adapters/grok.mjs.map +1 -1
  20. package/dist/adapters/openai.cjs +2 -3
  21. package/dist/adapters/openai.cjs.map +1 -1
  22. package/dist/adapters/openai.d.cts +6 -5
  23. package/dist/adapters/openai.d.mts +6 -5
  24. package/dist/adapters/openai.mjs +3 -3
  25. package/dist/adapters/openai.mjs.map +1 -1
  26. package/dist/adapters/openrouter.cjs +3 -17
  27. package/dist/adapters/openrouter.cjs.map +1 -1
  28. package/dist/adapters/openrouter.d.cts +4 -4
  29. package/dist/adapters/openrouter.d.mts +4 -4
  30. package/dist/adapters/openrouter.mjs +4 -17
  31. package/dist/adapters/openrouter.mjs.map +1 -1
  32. package/dist/adapters/workers-ai-image.cjs +5 -6
  33. package/dist/adapters/workers-ai-image.cjs.map +1 -1
  34. package/dist/adapters/workers-ai-image.d.cts +1 -1
  35. package/dist/adapters/workers-ai-image.d.mts +2 -2
  36. package/dist/adapters/workers-ai-image.mjs +5 -5
  37. package/dist/adapters/workers-ai-image.mjs.map +1 -1
  38. package/dist/adapters/workers-ai-summarize.cjs +3 -4
  39. package/dist/adapters/workers-ai-summarize.cjs.map +1 -1
  40. package/dist/adapters/workers-ai-summarize.d.cts +1 -1
  41. package/dist/adapters/workers-ai-summarize.d.mts +2 -2
  42. package/dist/adapters/workers-ai-summarize.mjs +3 -3
  43. package/dist/adapters/workers-ai-summarize.mjs.map +1 -1
  44. package/dist/adapters/workers-ai-transcription.cjs +5 -6
  45. package/dist/adapters/workers-ai-transcription.cjs.map +1 -1
  46. package/dist/adapters/workers-ai-transcription.d.cts +1 -1
  47. package/dist/adapters/workers-ai-transcription.d.mts +2 -2
  48. package/dist/adapters/workers-ai-transcription.mjs +5 -5
  49. package/dist/adapters/workers-ai-transcription.mjs.map +1 -1
  50. package/dist/adapters/workers-ai-tts.cjs +5 -6
  51. package/dist/adapters/workers-ai-tts.cjs.map +1 -1
  52. package/dist/adapters/workers-ai-tts.d.cts +1 -1
  53. package/dist/adapters/workers-ai-tts.d.mts +2 -2
  54. package/dist/adapters/workers-ai-tts.mjs +5 -5
  55. package/dist/adapters/workers-ai-tts.mjs.map +1 -1
  56. package/dist/adapters/workers-ai.cjs +520 -3
  57. package/dist/adapters/workers-ai.cjs.map +1 -0
  58. package/dist/adapters/workers-ai.d.cts +2 -2
  59. package/dist/adapters/workers-ai.d.mts +3 -3
  60. package/dist/adapters/workers-ai.mjs +56 -41
  61. package/dist/adapters/workers-ai.mjs.map +1 -1
  62. package/dist/{binary-p4H_N_3M.mjs → binary-B5YCVsro.mjs} +1 -1
  63. package/dist/{binary-C9FAYwZj.cjs.map → binary-B5YCVsro.mjs.map} +1 -1
  64. package/dist/{binary-C9FAYwZj.cjs → binary-CZhr1_2n.cjs} +1 -1
  65. package/dist/{binary-p4H_N_3M.mjs.map → binary-CZhr1_2n.cjs.map} +1 -1
  66. package/dist/{create-fetcher-CeUOJgrh.mjs → create-fetcher-BnSnCaHf.mjs} +1 -1
  67. package/dist/{create-fetcher-By-hTiT9.cjs.map → create-fetcher-BnSnCaHf.mjs.map} +1 -1
  68. package/dist/{create-fetcher-6p6heb85.d.mts → create-fetcher-Bp5yCNaO.d.cts} +1 -1
  69. package/dist/{create-fetcher-vAQ8WW-p.d.cts → create-fetcher-Bp5yCNaO.d.mts} +1 -1
  70. package/dist/{create-fetcher-By-hTiT9.cjs → create-fetcher-YVxWCTVL.cjs} +1 -1
  71. package/dist/{create-fetcher-CeUOJgrh.mjs.map → create-fetcher-YVxWCTVL.cjs.map} +1 -1
  72. package/dist/{defineProperty-CbyrzcbA.mjs → defineProperty-BC3bEcwq.mjs} +4 -4
  73. package/dist/{defineProperty-DQoAg20E.cjs → defineProperty-In8g9yAD.cjs} +4 -4
  74. package/dist/index.cjs +2 -3
  75. package/dist/index.d.cts +3 -3
  76. package/dist/index.d.mts +3 -3
  77. package/dist/index.mjs +2 -2
  78. package/dist/{workers-ai-rest-GKy2r7eG.mjs → workers-ai-rest-B6_yU35Q.mjs} +1 -1
  79. package/dist/{workers-ai-rest-CkNCtBwv.cjs.map → workers-ai-rest-B6_yU35Q.mjs.map} +1 -1
  80. package/dist/{workers-ai-rest-CkNCtBwv.cjs → workers-ai-rest-_MXOmyDs.cjs} +1 -1
  81. package/dist/{workers-ai-rest-GKy2r7eG.mjs.map → workers-ai-rest-_MXOmyDs.cjs.map} +1 -1
  82. package/package.json +9 -9
  83. package/dist/workers-ai-BOZ5iyhY.cjs +0 -521
  84. package/dist/workers-ai-BOZ5iyhY.cjs.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"create-fetcher-By-hTiT9.cjs","names":[],"sources":["../src/utils/create-fetcher.ts"],"sourcesContent":["// ---------------------------------------------------------------------------\n// AI Gateway types (for third-party providers + Workers AI through gateway)\n// ---------------------------------------------------------------------------\n\nexport interface CloudflareAiGateway {\n\trun(request: unknown): Promise<Response>;\n}\n\nexport interface AiGatewayBindingConfig {\n\t/**\n\t * The AI Gateway binding\n\t * @example\n\t * env.AI.gateway('my-gateway-id')\n\t */\n\tbinding: CloudflareAiGateway;\n\t/**\n\t * The Provider API Key if you want to manually pass it, ignore if using Unified Billing or BYOK.\n\t */\n\tapiKey?: string;\n}\n\nexport type AiGatewayCredentialsConfig = {\n\t/**\n\t * The Cloudflare account ID\n\t */\n\taccountId: string;\n\t/**\n\t * The AI Gateway ID\n\t */\n\tgatewayId: string;\n} & (\n\t| {\n\t\t\t/** Cloudflare API Key for AI Gateway */\n\t\t\tcfApiKey: string;\n\t\t\tapiKey?: string;\n\t }\n\t| {\n\t\t\t/** Provider API Key */\n\t\t\tapiKey: string;\n\t\t\t/** Cloudflare API Key for AI Gateway */\n\t\t\tcfApiKey?: string;\n\t }\n);\n\nexport interface AiGatewayConfig {\n\tskipCache?: boolean;\n\tcacheTtl?: number;\n\tcustomCacheKey?: string;\n\tmetadata?: Record<string, unknown>;\n}\n\nexport type AiGatewayAdapterConfig = (AiGatewayBindingConfig | AiGatewayCredentialsConfig) &\n\tAiGatewayConfig;\n\n// ---------------------------------------------------------------------------\n// Plain Workers AI types (direct binding or REST, no gateway)\n// ---------------------------------------------------------------------------\n\n/**\n * The Workers AI binding interface (env.AI).\n * Accepts a model name and inputs, returns results directly.\n * Includes `gateway()` which is present on `env.AI` but not on `env.AI.gateway(id)`,\n * enabling structural discrimination from `CloudflareAiGateway`.\n */\nexport interface WorkersAiBinding {\n\trun(\n\t\tmodel: string,\n\t\tinputs: Record<string, unknown>,\n\t\toptions?: Record<string, unknown>,\n\t): Promise<unknown>;\n\tgateway(gatewayId: string): CloudflareAiGateway;\n}\n\nexport interface WorkersAiDirectBindingConfig {\n\t/**\n\t * The Workers AI binding (env.AI).\n\t * @example\n\t * { binding: env.AI }\n\t */\n\tbinding: WorkersAiBinding;\n}\n\nexport interface WorkersAiDirectCredentialsConfig {\n\t/**\n\t * The Cloudflare account ID\n\t */\n\taccountId: string;\n\t/**\n\t * The Cloudflare API key for Workers AI\n\t */\n\tapiKey: string;\n}\n\n/**\n * Config for Workers AI adapters. Supports four modes:\n * - Plain binding: `{ binding: env.AI }`\n * - Plain REST: `{ accountId, apiKey }`\n * - AI Gateway binding: `{ binding: env.AI.gateway(id) }`\n * - AI Gateway REST: `{ accountId, gatewayId, ... }`\n *\n * The third union member intersects `AiGatewayAdapterConfig` with `{ apiKey?: string }`.\n * For the gateway binding variant, `AiGatewayBindingConfig` already includes `apiKey?`,\n * so the intersection is redundant there. For the gateway credentials variant, this\n * `apiKey` represents the Workers AI token (used in the `Authorization` header to the\n * upstream provider), distinct from `cfApiKey` (used in the `cf-aig-authorization`\n * header for authenticated gateways).\n */\nexport type WorkersAiAdapterConfig = (\n\t| WorkersAiDirectBindingConfig\n\t| WorkersAiDirectCredentialsConfig\n\t| (AiGatewayAdapterConfig & { apiKey?: string })\n) & {\n\t/**\n\t * Session affinity key for prefix-cache optimization.\n\t * Routes requests with the same key to the same backend replica.\n\t */\n\tsessionAffinity?: string;\n};\n\n// ---------------------------------------------------------------------------\n// Config detection helpers\n// ---------------------------------------------------------------------------\n\n/** Returns true if this is a plain Workers AI binding config (`{ binding: env.AI }`) */\nexport function isDirectBindingConfig(\n\tconfig: WorkersAiAdapterConfig,\n): config is WorkersAiDirectBindingConfig {\n\t// env.AI has a .gateway() method; env.AI.gateway(id) does not.\n\t// This distinguishes direct bindings from AI Gateway bindings.\n\treturn (\n\t\t\"binding\" in config &&\n\t\ttypeof (config.binding as unknown as Record<string, unknown>).gateway === \"function\"\n\t);\n}\n\n/** Returns true if this is a plain Workers AI REST config (accountId + apiKey, no gatewayId) */\nexport function isDirectCredentialsConfig(\n\tconfig: WorkersAiAdapterConfig,\n): config is WorkersAiDirectCredentialsConfig {\n\treturn \"accountId\" in config && \"apiKey\" in config && !(\"gatewayId\" in config);\n}\n\n/** Returns true if this is an AI Gateway config (has gateway binding or `gatewayId`) */\nexport function isGatewayConfig(config: WorkersAiAdapterConfig): config is AiGatewayAdapterConfig {\n\tif (\"gatewayId\" in config) return true;\n\t// Has `binding` but NOT a direct Workers AI binding (no .gateway method)\n\treturn \"binding\" in config && !isDirectBindingConfig(config);\n}\n\n// ---------------------------------------------------------------------------\n// Config validation\n// ---------------------------------------------------------------------------\n\n/**\n * Validates that a WorkersAiAdapterConfig contains a valid configuration.\n * Throws an error if neither a binding, credentials (accountId + apiKey),\n * nor a gateway configuration is provided.\n */\nexport function validateWorkersAiConfig(config: WorkersAiAdapterConfig): void {\n\tif (\n\t\t!isDirectBindingConfig(config) &&\n\t\t!isDirectCredentialsConfig(config) &&\n\t\t!isGatewayConfig(config)\n\t) {\n\t\tthrow new Error(\n\t\t\t\"Invalid Workers AI configuration: you must provide either a binding (e.g. { binding: env.AI }), \" +\n\t\t\t\t\"credentials ({ accountId, apiKey }), or a gateway configuration ({ binding: env.AI.gateway(id) } \" +\n\t\t\t\t\"or { accountId, gatewayId }).\",\n\t\t);\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// createGatewayFetch -- for routing through AI Gateway\n// ---------------------------------------------------------------------------\n\nexport function createGatewayFetch(\n\tprovider: string,\n\tconfig: AiGatewayAdapterConfig,\n\theaders: Record<string, string> = {},\n): typeof fetch {\n\treturn (input, init) => {\n\t\tlet query: Record<string, unknown> = {};\n\n\t\tconst url =\n\t\t\ttypeof input === \"string\" ? input : input instanceof URL ? input.href : input.url;\n\t\tconst urlObj = new URL(url);\n\n\t\t// Extract endpoint path (remove /v1/ prefix if present)\n\t\tconst endpoint = urlObj.pathname.replace(/^\\/v1\\//, \"\").replace(/^\\//, \"\") + urlObj.search;\n\n\t\tif (init?.body) {\n\t\t\ttry {\n\t\t\t\tquery = JSON.parse(init.body as string);\n\t\t\t} catch {\n\t\t\t\tquery = { _raw: init.body };\n\t\t\t}\n\t\t}\n\n\t\tconst cacheHeaders: Record<string, string> = {};\n\n\t\tif (\"skipCache\" in config && config.skipCache) {\n\t\t\tcacheHeaders[\"cf-aig-skip-cache\"] = \"true\";\n\t\t}\n\n\t\tif (typeof config.cacheTtl === \"number\") {\n\t\t\tcacheHeaders[\"cf-aig-cache-ttl\"] = String(config.cacheTtl);\n\t\t}\n\n\t\tif (typeof config.customCacheKey === \"string\") {\n\t\t\tcacheHeaders[\"cf-aig-cache-key\"] = config.customCacheKey;\n\t\t}\n\n\t\tif (typeof config.metadata === \"object\") {\n\t\t\tcacheHeaders[\"cf-aig-metadata\"] = JSON.stringify(config.metadata);\n\t\t}\n\n\t\tconst request: {\n\t\t\tprovider: string;\n\t\t\tendpoint: string;\n\t\t\theaders: Record<string, string>;\n\t\t\tquery: Record<string, unknown>;\n\t\t} = {\n\t\t\tprovider,\n\t\t\tendpoint,\n\t\t\theaders: {\n\t\t\t\t...(init?.headers as Record<string, string> | undefined),\n\t\t\t\t...headers,\n\t\t\t\t...cacheHeaders,\n\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t},\n\t\t\tquery,\n\t\t};\n\n\t\tif (provider === \"workers-ai\") {\n\t\t\tif (!request.endpoint.startsWith(\"run/\")) {\n\t\t\t\trequest.endpoint = `run/${query.model}`;\n\t\t\t}\n\t\t\tdelete query.model;\n\t\t\tdelete query.instructions;\n\t\t}\n\n\t\tif (config.apiKey) {\n\t\t\trequest.headers[\"authorization\"] = `Bearer ${config.apiKey}`;\n\t\t}\n\n\t\tif (\"binding\" in config) {\n\t\t\treturn (\n\t\t\t\tconfig.binding as {\n\t\t\t\t\trun(req: unknown, opts?: { signal?: AbortSignal }): Promise<Response>;\n\t\t\t\t}\n\t\t\t).run(request, { signal: init?.signal ?? undefined });\n\t\t}\n\n\t\treturn fetch(\n\t\t\t`https://gateway.ai.cloudflare.com/v1/${config.accountId}/${config.gatewayId}`,\n\t\t\t{\n\t\t\t\t...init,\n\t\t\t\theaders: {\n\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\t...headers,\n\t\t\t\t\t...cacheHeaders,\n\t\t\t\t\t...(config.cfApiKey\n\t\t\t\t\t\t? { \"cf-aig-authorization\": `Bearer ${config.cfApiKey}` }\n\t\t\t\t\t\t: {}),\n\t\t\t\t},\n\t\t\t\tbody: JSON.stringify(request),\n\t\t\t},\n\t\t);\n\t};\n}\n\n// ---------------------------------------------------------------------------\n// createWorkersAiBindingFetch -- shim that makes env.AI look like an OpenAI endpoint\n// ---------------------------------------------------------------------------\n\n/**\n * Normalize messages before passing to Workers AI binding.\n *\n * The binding has strict schema validation that may differ from the OpenAI API:\n * - `content` must not be null\n *\n * Content arrays (with image_url parts) are passed through as-is since the\n * Workers AI binding accepts them at runtime for vision-capable models.\n */\nfunction normalizeMessagesForBinding(\n\tmessages: Record<string, unknown>[],\n): Record<string, unknown>[] {\n\treturn messages.map((msg) => {\n\t\tconst normalized = { ...msg };\n\n\t\t// content: null → content: \"\"\n\t\tif (normalized.content === null || normalized.content === undefined) {\n\t\t\tnormalized.content = \"\";\n\t\t}\n\n\t\treturn normalized;\n\t});\n}\n\n/**\n * Creates a fetch function that intercepts OpenAI SDK requests and translates them\n * to Workers AI binding calls (env.AI.run). This allows the WorkersAiTextAdapter\n * to use the OpenAI SDK against a plain Workers AI binding.\n *\n * NOTE: The `input` URL parameter is intentionally ignored. The model name and all\n * request parameters are extracted from the JSON body, matching Workers AI's\n * `binding.run(model, inputs)` calling convention.\n */\nexport function createWorkersAiBindingFetch(\n\tbinding: WorkersAiBinding,\n\toptions?: { extraHeaders?: Record<string, string> },\n): typeof fetch {\n\treturn async (_input, init) => {\n\t\tif (!init?.body) {\n\t\t\treturn new Response(\"No body\", { status: 400 });\n\t\t}\n\n\t\tlet body: Record<string, unknown>;\n\t\ttry {\n\t\t\tbody = JSON.parse(init.body as string);\n\t\t} catch {\n\t\t\treturn new Response(\"Invalid JSON body\", { status: 400 });\n\t\t}\n\n\t\tconst model = body.model as string;\n\t\tconst stream = body.stream as boolean | undefined;\n\n\t\t// Build Workers AI inputs from OpenAI format\n\t\tconst inputs: Record<string, unknown> = {};\n\t\tif (body.messages) {\n\t\t\tinputs.messages = normalizeMessagesForBinding(\n\t\t\t\tbody.messages as Record<string, unknown>[],\n\t\t\t);\n\t\t}\n\t\tif (body.tools) inputs.tools = body.tools;\n\t\tif (typeof body.temperature === \"number\") inputs.temperature = body.temperature;\n\t\tif (typeof body.max_tokens === \"number\") inputs.max_tokens = body.max_tokens;\n\t\tif (body.response_format) inputs.response_format = body.response_format;\n\t\tif (stream) inputs.stream = true;\n\n\t\t// Workers AI-specific reasoning controls. These belong on the INPUTS object\n\t\t// passed to binding.run(model, inputs), not on the options (3rd) arg.\n\t\t// See https://github.com/cloudflare/ai/issues/503.\n\t\t//\n\t\t// `reasoning_effort: null` is a valid value (disables reasoning on models\n\t\t// that support it), so we check `!== undefined` rather than truthiness.\n\t\tif (body.reasoning_effort !== undefined) {\n\t\t\tinputs.reasoning_effort = body.reasoning_effort;\n\t\t}\n\t\tif (body.chat_template_kwargs !== undefined) {\n\t\t\tinputs.chat_template_kwargs = body.chat_template_kwargs;\n\t\t}\n\n\t\tconst runOptions: Record<string, unknown> = {};\n\t\tif (options?.extraHeaders) runOptions.extraHeaders = options.extraHeaders;\n\t\tif (init?.signal) runOptions.signal = init.signal;\n\n\t\tconst result = await binding.run(\n\t\t\tmodel,\n\t\t\tinputs,\n\t\t\tObject.keys(runOptions).length > 0 ? runOptions : undefined,\n\t\t);\n\n\t\tif (stream && result instanceof ReadableStream) {\n\t\t\t// Workers AI returns an SSE stream with `data: {\"response\":\"chunk\"}` format.\n\t\t\t// Transform it to OpenAI-compatible SSE format.\n\t\t\tconst transformed = transformWorkersAiStream(\n\t\t\t\tresult as ReadableStream<Uint8Array>,\n\t\t\t\tmodel,\n\t\t\t);\n\t\t\treturn new Response(transformed, {\n\t\t\t\theaders: {\n\t\t\t\t\t\"Content-Type\": \"text/event-stream\",\n\t\t\t\t\t\"Cache-Control\": \"no-cache\",\n\t\t\t\t},\n\t\t\t});\n\t\t}\n\n\t\t// Graceful degradation: some models return a complete (non-streaming)\n\t\t// response even when `stream: true` is requested. Fall through to the\n\t\t// non-streaming wrapper which produces a valid OpenAI Chat Completion\n\t\t// response that the SDK can consume.\n\n\t\t// Non-streaming: Workers AI returns { response: \"text\", tool_calls?: [...] }\n\t\t// Wrap into OpenAI Chat Completion format.\n\t\tconst responseObj =\n\t\t\ttypeof result === \"object\" && result !== null\n\t\t\t\t? (result as Record<string, unknown>)\n\t\t\t\t: { response: String(result) };\n\n\t\tconst responseText =\n\t\t\ttypeof responseObj.response === \"string\"\n\t\t\t\t? responseObj.response\n\t\t\t\t: typeof responseObj.response === \"object\" && responseObj.response !== null\n\t\t\t\t\t? JSON.stringify(responseObj.response)\n\t\t\t\t\t: \"\";\n\n\t\tconst message: Record<string, unknown> = {\n\t\t\trole: \"assistant\",\n\t\t\tcontent: responseText,\n\t\t};\n\t\tlet finishReason = \"stop\";\n\n\t\t// Handle tool calls if present in Workers AI response\n\t\tif (Array.isArray(responseObj.tool_calls) && responseObj.tool_calls.length > 0) {\n\t\t\tfinishReason = \"tool_calls\";\n\t\t\tmessage.tool_calls = responseObj.tool_calls.map(\n\t\t\t\t(tc: {\n\t\t\t\t\tid?: string;\n\t\t\t\t\tname?: string;\n\t\t\t\t\targuments: unknown;\n\t\t\t\t\tfunction?: { name: string; arguments?: unknown };\n\t\t\t\t}) => ({\n\t\t\t\t\tid: tc.id || crypto.randomUUID(),\n\t\t\t\t\ttype: \"function\",\n\t\t\t\t\tfunction: {\n\t\t\t\t\t\tname: tc.function?.name || tc.name || \"\",\n\t\t\t\t\t\targuments:\n\t\t\t\t\t\t\ttypeof (tc.function?.arguments ?? tc.arguments) === \"string\"\n\t\t\t\t\t\t\t\t? ((tc.function?.arguments ?? tc.arguments) as string)\n\t\t\t\t\t\t\t\t: JSON.stringify(tc.function?.arguments ?? tc.arguments ?? {}),\n\t\t\t\t\t},\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\n\t\tconst openAiResponse = {\n\t\t\tid: `workers-ai-${crypto.randomUUID()}`,\n\t\t\tobject: \"chat.completion\",\n\t\t\tcreated: Math.floor(Date.now() / 1000),\n\t\t\tmodel,\n\t\t\tchoices: [{ index: 0, message, finish_reason: finishReason }],\n\t\t};\n\n\t\treturn new Response(JSON.stringify(openAiResponse), {\n\t\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\t});\n\t};\n}\n\n// ---------------------------------------------------------------------------\n// Stream transformer: Workers AI SSE -> OpenAI-compatible SSE\n// Uses TransformStream for proper backpressure.\n// ---------------------------------------------------------------------------\n\n/**\n * Transforms a Workers AI SSE stream (data: {\"response\":\"chunk\"}) into\n * an OpenAI-compatible SSE stream (data: {\"choices\":[{\"delta\":{\"content\":\"chunk\"}}]}).\n *\n * Workers AI binding streams tool calls in an OpenAI-like nested format:\n * { tool_calls: [{ id, type, index, function: { name, arguments } }] }\n * Arguments are streamed incrementally across multiple SSE chunks, so the\n * transformer must forward them as incremental deltas rather than a single blob.\n */\nfunction transformWorkersAiStream(\n\tsource: ReadableStream<Uint8Array>,\n\tmodel: string,\n): ReadableStream<Uint8Array> {\n\tconst decoder = new TextDecoder();\n\tconst encoder = new TextEncoder();\n\t// Generate a stable ID and timestamp for the entire stream, matching OpenAI's\n\t// convention where all chunks in a single response share the same id/created.\n\tconst streamId = `workers-ai-${crypto.randomUUID()}`;\n\tconst created = Math.floor(Date.now() / 1000);\n\tlet buffer = \"\";\n\tlet hasToolCalls = false;\n\t// When true, the source stream is already in OpenAI format (some models\n\t// like Qwen3, Kimi K2.5 stream OpenAI-compatible SSE through the binding).\n\t// In that case, flush() should only emit [DONE] and skip the finish chunk.\n\tlet isOpenAiFormat = false;\n\t// Track tool call state per index: store the generated/assigned ID so that\n\t// subsequent argument deltas use the same ID (matching the working streaming.ts pattern).\n\tconst toolCallState = new Map<number, { id: string; name: string }>();\n\n\treturn source.pipeThrough(\n\t\tnew TransformStream<Uint8Array, Uint8Array>({\n\t\t\ttransform(chunk, controller) {\n\t\t\t\tbuffer += decoder.decode(chunk, { stream: true });\n\t\t\t\tconst lines = buffer.split(\"\\n\");\n\t\t\t\tbuffer = lines.pop() || \"\";\n\n\t\t\t\tfor (const line of lines) {\n\t\t\t\t\tconst trimmed = line.trim();\n\t\t\t\t\tif (!trimmed || !trimmed.startsWith(\"data: \")) continue;\n\t\t\t\t\tconst data = trimmed.slice(6);\n\n\t\t\t\t\t// Swallow source [DONE]; we emit our own in flush()\n\t\t\t\t\tif (data === \"[DONE]\") continue;\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst parsed = JSON.parse(data);\n\n\t\t\t\t\t\t// Some models (Qwen3, Kimi K2.5) return OpenAI-compatible format\n\t\t\t\t\t\t// directly through the binding, with `choices[].delta.content` and\n\t\t\t\t\t\t// optional `reasoning_content`. Detect this and pass through as-is.\n\t\t\t\t\t\tif (parsed.choices !== undefined) {\n\t\t\t\t\t\t\t// Already OpenAI format — pass through but ensure each tool call\n\t\t\t\t\t\t\t// index gets a unique, stable ID across all chunks.\n\t\t\t\t\t\t\tisOpenAiFormat = true;\n\t\t\t\t\t\t\tconst choice = parsed.choices?.[0];\n\t\t\t\t\t\t\tif (choice?.delta?.tool_calls) {\n\t\t\t\t\t\t\t\thasToolCalls = true;\n\t\t\t\t\t\t\t\tfor (const tc of choice.delta.tool_calls) {\n\t\t\t\t\t\t\t\t\tconst tcIndex = tc.index ?? 0;\n\t\t\t\t\t\t\t\t\tif (!toolCallState.has(tcIndex)) {\n\t\t\t\t\t\t\t\t\t\t// First chunk for this index — generate/store unique ID\n\t\t\t\t\t\t\t\t\t\tconst id = tc.id || `call${streamId}${tcIndex}`;\n\t\t\t\t\t\t\t\t\t\ttoolCallState.set(tcIndex, {\n\t\t\t\t\t\t\t\t\t\t\tid,\n\t\t\t\t\t\t\t\t\t\t\tname: tc.function?.name || \"\",\n\t\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t\t\ttc.id = id;\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\t// Subsequent chunk — reuse stored ID, remove id from delta\n\t\t\t\t\t\t\t\t\t\t// (OpenAI format only sends id in first chunk)\n\t\t\t\t\t\t\t\t\t\tdelete tc.id;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (choice?.finish_reason === \"tool_calls\") {\n\t\t\t\t\t\t\t\thasToolCalls = true;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tcontroller.enqueue(\n\t\t\t\t\t\t\t\tencoder.encode(`data: ${JSON.stringify(parsed)}\\n\\n`),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// --- Workers AI native format handling below ---\n\n\t\t\t\t\t\t// Text content\n\t\t\t\t\t\tif (parsed.response != null && parsed.response !== \"\") {\n\t\t\t\t\t\t\tconst openAiChunk = {\n\t\t\t\t\t\t\t\tid: streamId,\n\t\t\t\t\t\t\t\tobject: \"chat.completion.chunk\",\n\t\t\t\t\t\t\t\tcreated,\n\t\t\t\t\t\t\t\tmodel,\n\t\t\t\t\t\t\t\tchoices: [\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tindex: 0,\n\t\t\t\t\t\t\t\t\t\tdelta: { content: parsed.response },\n\t\t\t\t\t\t\t\t\t\tfinish_reason: null,\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\tcontroller.enqueue(\n\t\t\t\t\t\t\t\tencoder.encode(`data: ${JSON.stringify(openAiChunk)}\\n\\n`),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Tool calls — Workers AI binding streams these incrementally:\n\t\t\t\t\t\t// Chunk A: { id, type, index, function: { name } } — start\n\t\t\t\t\t\t// Chunk B: { index, function: { arguments: \"partial...\" } } — args delta\n\t\t\t\t\t\t// Chunk C: { index, function: { arguments: \"rest...\" } } — args delta\n\t\t\t\t\t\t// Chunk D: { id: null, type: null, index, function: { name: null, arguments: \"\" } } — finalize (skip)\n\t\t\t\t\t\tif (Array.isArray(parsed.tool_calls) && parsed.tool_calls.length > 0) {\n\t\t\t\t\t\t\tfor (const tc of parsed.tool_calls) {\n\t\t\t\t\t\t\t\tconst tcIndex = tc.index ?? 0;\n\n\t\t\t\t\t\t\t\t// Resolve name and arguments from either nested or flat format\n\t\t\t\t\t\t\t\tconst tcName = tc.function?.name ?? tc.name ?? null;\n\t\t\t\t\t\t\t\tconst tcArgs = tc.function?.arguments ?? tc.arguments ?? null;\n\t\t\t\t\t\t\t\tconst tcId = tc.id ?? null;\n\n\t\t\t\t\t\t\t\t// Skip finalization chunks where everything is null/empty\n\t\t\t\t\t\t\t\tif (!tcId && !tcName && (!tcArgs || tcArgs === \"\")) continue;\n\n\t\t\t\t\t\t\t\thasToolCalls = true;\n\n\t\t\t\t\t\t\t\t// Build the OpenAI-compatible tool_calls delta\n\t\t\t\t\t\t\t\tconst toolCallDelta: Record<string, unknown> = {\n\t\t\t\t\t\t\t\t\tindex: tcIndex,\n\t\t\t\t\t\t\t\t};\n\n\t\t\t\t\t\t\t\tif (!toolCallState.has(tcIndex)) {\n\t\t\t\t\t\t\t\t\t// First chunk for this tool call index — emit id, type, name.\n\t\t\t\t\t\t\t\t\tconst id = tcId || `call${streamId}${tcIndex}`;\n\t\t\t\t\t\t\t\t\ttoolCallState.set(tcIndex, { id, name: tcName || \"\" });\n\t\t\t\t\t\t\t\t\ttoolCallDelta.id = id;\n\t\t\t\t\t\t\t\t\ttoolCallDelta.type = \"function\";\n\t\t\t\t\t\t\t\t\ttoolCallDelta.function = {\n\t\t\t\t\t\t\t\t\t\tname: tcName || \"\",\n\t\t\t\t\t\t\t\t\t\t// Include arguments if they arrive in the same chunk\n\t\t\t\t\t\t\t\t\t\targuments:\n\t\t\t\t\t\t\t\t\t\t\ttcArgs != null\n\t\t\t\t\t\t\t\t\t\t\t\t? typeof tcArgs === \"string\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t? tcArgs\n\t\t\t\t\t\t\t\t\t\t\t\t\t: JSON.stringify(tcArgs)\n\t\t\t\t\t\t\t\t\t\t\t\t: \"\",\n\t\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t// Subsequent chunks — only include arguments delta\n\t\t\t\t\t\t\t\t\tif (tcArgs != null && tcArgs !== \"\") {\n\t\t\t\t\t\t\t\t\t\ttoolCallDelta.function = {\n\t\t\t\t\t\t\t\t\t\t\targuments:\n\t\t\t\t\t\t\t\t\t\t\t\ttypeof tcArgs === \"string\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t? tcArgs\n\t\t\t\t\t\t\t\t\t\t\t\t\t: JSON.stringify(tcArgs),\n\t\t\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\tcontinue; // Nothing useful to forward\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tconst toolChunk = {\n\t\t\t\t\t\t\t\t\tid: streamId,\n\t\t\t\t\t\t\t\t\tobject: \"chat.completion.chunk\",\n\t\t\t\t\t\t\t\t\tcreated,\n\t\t\t\t\t\t\t\t\tmodel,\n\t\t\t\t\t\t\t\t\tchoices: [\n\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\tindex: 0,\n\t\t\t\t\t\t\t\t\t\t\tdelta: { tool_calls: [toolCallDelta] },\n\t\t\t\t\t\t\t\t\t\t\tfinish_reason: null,\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\tcontroller.enqueue(\n\t\t\t\t\t\t\t\t\tencoder.encode(`data: ${JSON.stringify(toolChunk)}\\n\\n`),\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (e) {\n\t\t\t\t\t\t// Log malformed SSE events for debugging; don't break the stream.\n\t\t\t\t\t\tconsole.warn(\"[tanstack-ai] failed to parse SSE event:\", data, e);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t},\n\t\t\tflush(controller) {\n\t\t\t\tif (!isOpenAiFormat) {\n\t\t\t\t\t// Workers AI native format: emit a finish chunk with stop/tool_calls\n\t\t\t\t\tconst finalChunk = {\n\t\t\t\t\t\tid: streamId,\n\t\t\t\t\t\tobject: \"chat.completion.chunk\",\n\t\t\t\t\t\tcreated,\n\t\t\t\t\t\tmodel,\n\t\t\t\t\t\tchoices: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tindex: 0,\n\t\t\t\t\t\t\t\tdelta: {},\n\t\t\t\t\t\t\t\tfinish_reason: hasToolCalls ? \"tool_calls\" : \"stop\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t\t\t};\n\t\t\t\t\tcontroller.enqueue(encoder.encode(`data: ${JSON.stringify(finalChunk)}\\n\\n`));\n\t\t\t\t}\n\t\t\t\t// OpenAI format already includes its own finish_reason in the stream.\n\t\t\t\t// Either way, emit a [DONE] sentinel.\n\t\t\t\tcontroller.enqueue(encoder.encode(\"data: [DONE]\\n\\n\"));\n\t\t\t},\n\t\t}),\n\t);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"create-fetcher-BnSnCaHf.mjs","names":[],"sources":["../src/utils/create-fetcher.ts"],"sourcesContent":["// ---------------------------------------------------------------------------\n// AI Gateway types (for third-party providers + Workers AI through gateway)\n// ---------------------------------------------------------------------------\n\nexport interface CloudflareAiGateway {\n\trun(request: unknown): Promise<Response>;\n}\n\nexport interface AiGatewayBindingConfig {\n\t/**\n\t * The AI Gateway binding\n\t * @example\n\t * env.AI.gateway('my-gateway-id')\n\t */\n\tbinding: CloudflareAiGateway;\n\t/**\n\t * The Provider API Key if you want to manually pass it, ignore if using Unified Billing or BYOK.\n\t */\n\tapiKey?: string;\n}\n\nexport type AiGatewayCredentialsConfig = {\n\t/**\n\t * The Cloudflare account ID\n\t */\n\taccountId: string;\n\t/**\n\t * The AI Gateway ID\n\t */\n\tgatewayId: string;\n} & (\n\t| {\n\t\t\t/** Cloudflare API Key for AI Gateway */\n\t\t\tcfApiKey: string;\n\t\t\tapiKey?: string;\n\t }\n\t| {\n\t\t\t/** Provider API Key */\n\t\t\tapiKey: string;\n\t\t\t/** Cloudflare API Key for AI Gateway */\n\t\t\tcfApiKey?: string;\n\t }\n);\n\nexport interface AiGatewayConfig {\n\tskipCache?: boolean;\n\tcacheTtl?: number;\n\tcustomCacheKey?: string;\n\tmetadata?: Record<string, unknown>;\n}\n\nexport type AiGatewayAdapterConfig = (AiGatewayBindingConfig | AiGatewayCredentialsConfig) &\n\tAiGatewayConfig;\n\n// ---------------------------------------------------------------------------\n// Plain Workers AI types (direct binding or REST, no gateway)\n// ---------------------------------------------------------------------------\n\n/**\n * The Workers AI binding interface (env.AI).\n * Accepts a model name and inputs, returns results directly.\n * Includes `gateway()` which is present on `env.AI` but not on `env.AI.gateway(id)`,\n * enabling structural discrimination from `CloudflareAiGateway`.\n */\nexport interface WorkersAiBinding {\n\trun(\n\t\tmodel: string,\n\t\tinputs: Record<string, unknown>,\n\t\toptions?: Record<string, unknown>,\n\t): Promise<unknown>;\n\tgateway(gatewayId: string): CloudflareAiGateway;\n}\n\nexport interface WorkersAiDirectBindingConfig {\n\t/**\n\t * The Workers AI binding (env.AI).\n\t * @example\n\t * { binding: env.AI }\n\t */\n\tbinding: WorkersAiBinding;\n}\n\nexport interface WorkersAiDirectCredentialsConfig {\n\t/**\n\t * The Cloudflare account ID\n\t */\n\taccountId: string;\n\t/**\n\t * The Cloudflare API key for Workers AI\n\t */\n\tapiKey: string;\n}\n\n/**\n * Config for Workers AI adapters. Supports four modes:\n * - Plain binding: `{ binding: env.AI }`\n * - Plain REST: `{ accountId, apiKey }`\n * - AI Gateway binding: `{ binding: env.AI.gateway(id) }`\n * - AI Gateway REST: `{ accountId, gatewayId, ... }`\n *\n * The third union member intersects `AiGatewayAdapterConfig` with `{ apiKey?: string }`.\n * For the gateway binding variant, `AiGatewayBindingConfig` already includes `apiKey?`,\n * so the intersection is redundant there. For the gateway credentials variant, this\n * `apiKey` represents the Workers AI token (used in the `Authorization` header to the\n * upstream provider), distinct from `cfApiKey` (used in the `cf-aig-authorization`\n * header for authenticated gateways).\n */\nexport type WorkersAiAdapterConfig = (\n\t| WorkersAiDirectBindingConfig\n\t| WorkersAiDirectCredentialsConfig\n\t| (AiGatewayAdapterConfig & { apiKey?: string })\n) & {\n\t/**\n\t * Session affinity key for prefix-cache optimization.\n\t * Routes requests with the same key to the same backend replica.\n\t */\n\tsessionAffinity?: string;\n};\n\n// ---------------------------------------------------------------------------\n// Config detection helpers\n// ---------------------------------------------------------------------------\n\n/** Returns true if this is a plain Workers AI binding config (`{ binding: env.AI }`) */\nexport function isDirectBindingConfig(\n\tconfig: WorkersAiAdapterConfig,\n): config is WorkersAiDirectBindingConfig {\n\t// env.AI has a .gateway() method; env.AI.gateway(id) does not.\n\t// This distinguishes direct bindings from AI Gateway bindings.\n\treturn (\n\t\t\"binding\" in config &&\n\t\ttypeof (config.binding as unknown as Record<string, unknown>).gateway === \"function\"\n\t);\n}\n\n/** Returns true if this is a plain Workers AI REST config (accountId + apiKey, no gatewayId) */\nexport function isDirectCredentialsConfig(\n\tconfig: WorkersAiAdapterConfig,\n): config is WorkersAiDirectCredentialsConfig {\n\treturn \"accountId\" in config && \"apiKey\" in config && !(\"gatewayId\" in config);\n}\n\n/** Returns true if this is an AI Gateway config (has gateway binding or `gatewayId`) */\nexport function isGatewayConfig(config: WorkersAiAdapterConfig): config is AiGatewayAdapterConfig {\n\tif (\"gatewayId\" in config) return true;\n\t// Has `binding` but NOT a direct Workers AI binding (no .gateway method)\n\treturn \"binding\" in config && !isDirectBindingConfig(config);\n}\n\n// ---------------------------------------------------------------------------\n// Config validation\n// ---------------------------------------------------------------------------\n\n/**\n * Validates that a WorkersAiAdapterConfig contains a valid configuration.\n * Throws an error if neither a binding, credentials (accountId + apiKey),\n * nor a gateway configuration is provided.\n */\nexport function validateWorkersAiConfig(config: WorkersAiAdapterConfig): void {\n\tif (\n\t\t!isDirectBindingConfig(config) &&\n\t\t!isDirectCredentialsConfig(config) &&\n\t\t!isGatewayConfig(config)\n\t) {\n\t\tthrow new Error(\n\t\t\t\"Invalid Workers AI configuration: you must provide either a binding (e.g. { binding: env.AI }), \" +\n\t\t\t\t\"credentials ({ accountId, apiKey }), or a gateway configuration ({ binding: env.AI.gateway(id) } \" +\n\t\t\t\t\"or { accountId, gatewayId }).\",\n\t\t);\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// createGatewayFetch -- for routing through AI Gateway\n// ---------------------------------------------------------------------------\n\nexport function createGatewayFetch(\n\tprovider: string,\n\tconfig: AiGatewayAdapterConfig,\n\theaders: Record<string, string> = {},\n): typeof fetch {\n\treturn (input, init) => {\n\t\tlet query: Record<string, unknown> = {};\n\n\t\tconst url =\n\t\t\ttypeof input === \"string\" ? input : input instanceof URL ? input.href : input.url;\n\t\tconst urlObj = new URL(url);\n\n\t\t// Extract endpoint path (remove /v1/ prefix if present)\n\t\tconst endpoint = urlObj.pathname.replace(/^\\/v1\\//, \"\").replace(/^\\//, \"\") + urlObj.search;\n\n\t\tif (init?.body) {\n\t\t\ttry {\n\t\t\t\tquery = JSON.parse(init.body as string);\n\t\t\t} catch {\n\t\t\t\tquery = { _raw: init.body };\n\t\t\t}\n\t\t}\n\n\t\tconst cacheHeaders: Record<string, string> = {};\n\n\t\tif (\"skipCache\" in config && config.skipCache) {\n\t\t\tcacheHeaders[\"cf-aig-skip-cache\"] = \"true\";\n\t\t}\n\n\t\tif (typeof config.cacheTtl === \"number\") {\n\t\t\tcacheHeaders[\"cf-aig-cache-ttl\"] = String(config.cacheTtl);\n\t\t}\n\n\t\tif (typeof config.customCacheKey === \"string\") {\n\t\t\tcacheHeaders[\"cf-aig-cache-key\"] = config.customCacheKey;\n\t\t}\n\n\t\tif (typeof config.metadata === \"object\") {\n\t\t\tcacheHeaders[\"cf-aig-metadata\"] = JSON.stringify(config.metadata);\n\t\t}\n\n\t\tconst request: {\n\t\t\tprovider: string;\n\t\t\tendpoint: string;\n\t\t\theaders: Record<string, string>;\n\t\t\tquery: Record<string, unknown>;\n\t\t} = {\n\t\t\tprovider,\n\t\t\tendpoint,\n\t\t\theaders: {\n\t\t\t\t...(init?.headers as Record<string, string> | undefined),\n\t\t\t\t...headers,\n\t\t\t\t...cacheHeaders,\n\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t},\n\t\t\tquery,\n\t\t};\n\n\t\tif (provider === \"workers-ai\") {\n\t\t\tif (!request.endpoint.startsWith(\"run/\")) {\n\t\t\t\trequest.endpoint = `run/${query.model}`;\n\t\t\t}\n\t\t\tdelete query.model;\n\t\t\tdelete query.instructions;\n\t\t}\n\n\t\tif (config.apiKey) {\n\t\t\trequest.headers[\"authorization\"] = `Bearer ${config.apiKey}`;\n\t\t}\n\n\t\tif (\"binding\" in config) {\n\t\t\treturn (\n\t\t\t\tconfig.binding as {\n\t\t\t\t\trun(req: unknown, opts?: { signal?: AbortSignal }): Promise<Response>;\n\t\t\t\t}\n\t\t\t).run(request, { signal: init?.signal ?? undefined });\n\t\t}\n\n\t\treturn fetch(\n\t\t\t`https://gateway.ai.cloudflare.com/v1/${config.accountId}/${config.gatewayId}`,\n\t\t\t{\n\t\t\t\t...init,\n\t\t\t\theaders: {\n\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\t...headers,\n\t\t\t\t\t...cacheHeaders,\n\t\t\t\t\t...(config.cfApiKey\n\t\t\t\t\t\t? { \"cf-aig-authorization\": `Bearer ${config.cfApiKey}` }\n\t\t\t\t\t\t: {}),\n\t\t\t\t},\n\t\t\t\tbody: JSON.stringify(request),\n\t\t\t},\n\t\t);\n\t};\n}\n\n// ---------------------------------------------------------------------------\n// createWorkersAiBindingFetch -- shim that makes env.AI look like an OpenAI endpoint\n// ---------------------------------------------------------------------------\n\n/**\n * Normalize messages before passing to Workers AI binding.\n *\n * The binding has strict schema validation that may differ from the OpenAI API:\n * - `content` must not be null\n *\n * Content arrays (with image_url parts) are passed through as-is since the\n * Workers AI binding accepts them at runtime for vision-capable models.\n */\nfunction normalizeMessagesForBinding(\n\tmessages: Record<string, unknown>[],\n): Record<string, unknown>[] {\n\treturn messages.map((msg) => {\n\t\tconst normalized = { ...msg };\n\n\t\t// content: null → content: \"\"\n\t\tif (normalized.content === null || normalized.content === undefined) {\n\t\t\tnormalized.content = \"\";\n\t\t}\n\n\t\treturn normalized;\n\t});\n}\n\n/**\n * Creates a fetch function that intercepts OpenAI SDK requests and translates them\n * to Workers AI binding calls (env.AI.run). This allows the WorkersAiTextAdapter\n * to use the OpenAI SDK against a plain Workers AI binding.\n *\n * NOTE: The `input` URL parameter is intentionally ignored. The model name and all\n * request parameters are extracted from the JSON body, matching Workers AI's\n * `binding.run(model, inputs)` calling convention.\n */\nexport function createWorkersAiBindingFetch(\n\tbinding: WorkersAiBinding,\n\toptions?: { extraHeaders?: Record<string, string> },\n): typeof fetch {\n\treturn async (_input, init) => {\n\t\tif (!init?.body) {\n\t\t\treturn new Response(\"No body\", { status: 400 });\n\t\t}\n\n\t\tlet body: Record<string, unknown>;\n\t\ttry {\n\t\t\tbody = JSON.parse(init.body as string);\n\t\t} catch {\n\t\t\treturn new Response(\"Invalid JSON body\", { status: 400 });\n\t\t}\n\n\t\tconst model = body.model as string;\n\t\tconst stream = body.stream as boolean | undefined;\n\n\t\t// Build Workers AI inputs from OpenAI format\n\t\tconst inputs: Record<string, unknown> = {};\n\t\tif (body.messages) {\n\t\t\tinputs.messages = normalizeMessagesForBinding(\n\t\t\t\tbody.messages as Record<string, unknown>[],\n\t\t\t);\n\t\t}\n\t\tif (body.tools) inputs.tools = body.tools;\n\t\tif (typeof body.temperature === \"number\") inputs.temperature = body.temperature;\n\t\tif (typeof body.max_tokens === \"number\") inputs.max_tokens = body.max_tokens;\n\t\tif (body.response_format) inputs.response_format = body.response_format;\n\t\tif (stream) inputs.stream = true;\n\n\t\t// Workers AI-specific reasoning controls. These belong on the INPUTS object\n\t\t// passed to binding.run(model, inputs), not on the options (3rd) arg.\n\t\t// See https://github.com/cloudflare/ai/issues/503.\n\t\t//\n\t\t// `reasoning_effort: null` is a valid value (disables reasoning on models\n\t\t// that support it), so we check `!== undefined` rather than truthiness.\n\t\tif (body.reasoning_effort !== undefined) {\n\t\t\tinputs.reasoning_effort = body.reasoning_effort;\n\t\t}\n\t\tif (body.chat_template_kwargs !== undefined) {\n\t\t\tinputs.chat_template_kwargs = body.chat_template_kwargs;\n\t\t}\n\n\t\tconst runOptions: Record<string, unknown> = {};\n\t\tif (options?.extraHeaders) runOptions.extraHeaders = options.extraHeaders;\n\t\tif (init?.signal) runOptions.signal = init.signal;\n\n\t\tconst result = await binding.run(\n\t\t\tmodel,\n\t\t\tinputs,\n\t\t\tObject.keys(runOptions).length > 0 ? runOptions : undefined,\n\t\t);\n\n\t\tif (stream && result instanceof ReadableStream) {\n\t\t\t// Workers AI returns an SSE stream with `data: {\"response\":\"chunk\"}` format.\n\t\t\t// Transform it to OpenAI-compatible SSE format.\n\t\t\tconst transformed = transformWorkersAiStream(\n\t\t\t\tresult as ReadableStream<Uint8Array>,\n\t\t\t\tmodel,\n\t\t\t);\n\t\t\treturn new Response(transformed, {\n\t\t\t\theaders: {\n\t\t\t\t\t\"Content-Type\": \"text/event-stream\",\n\t\t\t\t\t\"Cache-Control\": \"no-cache\",\n\t\t\t\t},\n\t\t\t});\n\t\t}\n\n\t\t// Graceful degradation: some models return a complete (non-streaming)\n\t\t// response even when `stream: true` is requested. Fall through to the\n\t\t// non-streaming wrapper which produces a valid OpenAI Chat Completion\n\t\t// response that the SDK can consume.\n\n\t\t// Non-streaming: Workers AI returns { response: \"text\", tool_calls?: [...] }\n\t\t// Wrap into OpenAI Chat Completion format.\n\t\tconst responseObj =\n\t\t\ttypeof result === \"object\" && result !== null\n\t\t\t\t? (result as Record<string, unknown>)\n\t\t\t\t: { response: String(result) };\n\n\t\tconst responseText =\n\t\t\ttypeof responseObj.response === \"string\"\n\t\t\t\t? responseObj.response\n\t\t\t\t: typeof responseObj.response === \"object\" && responseObj.response !== null\n\t\t\t\t\t? JSON.stringify(responseObj.response)\n\t\t\t\t\t: \"\";\n\n\t\tconst message: Record<string, unknown> = {\n\t\t\trole: \"assistant\",\n\t\t\tcontent: responseText,\n\t\t};\n\t\tlet finishReason = \"stop\";\n\n\t\t// Handle tool calls if present in Workers AI response\n\t\tif (Array.isArray(responseObj.tool_calls) && responseObj.tool_calls.length > 0) {\n\t\t\tfinishReason = \"tool_calls\";\n\t\t\tmessage.tool_calls = responseObj.tool_calls.map(\n\t\t\t\t(tc: {\n\t\t\t\t\tid?: string;\n\t\t\t\t\tname?: string;\n\t\t\t\t\targuments: unknown;\n\t\t\t\t\tfunction?: { name: string; arguments?: unknown };\n\t\t\t\t}) => ({\n\t\t\t\t\tid: tc.id || crypto.randomUUID(),\n\t\t\t\t\ttype: \"function\",\n\t\t\t\t\tfunction: {\n\t\t\t\t\t\tname: tc.function?.name || tc.name || \"\",\n\t\t\t\t\t\targuments:\n\t\t\t\t\t\t\ttypeof (tc.function?.arguments ?? tc.arguments) === \"string\"\n\t\t\t\t\t\t\t\t? ((tc.function?.arguments ?? tc.arguments) as string)\n\t\t\t\t\t\t\t\t: JSON.stringify(tc.function?.arguments ?? tc.arguments ?? {}),\n\t\t\t\t\t},\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\n\t\tconst openAiResponse = {\n\t\t\tid: `workers-ai-${crypto.randomUUID()}`,\n\t\t\tobject: \"chat.completion\",\n\t\t\tcreated: Math.floor(Date.now() / 1000),\n\t\t\tmodel,\n\t\t\tchoices: [{ index: 0, message, finish_reason: finishReason }],\n\t\t};\n\n\t\treturn new Response(JSON.stringify(openAiResponse), {\n\t\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\t});\n\t};\n}\n\n// ---------------------------------------------------------------------------\n// Stream transformer: Workers AI SSE -> OpenAI-compatible SSE\n// Uses TransformStream for proper backpressure.\n// ---------------------------------------------------------------------------\n\n/**\n * Transforms a Workers AI SSE stream (data: {\"response\":\"chunk\"}) into\n * an OpenAI-compatible SSE stream (data: {\"choices\":[{\"delta\":{\"content\":\"chunk\"}}]}).\n *\n * Workers AI binding streams tool calls in an OpenAI-like nested format:\n * { tool_calls: [{ id, type, index, function: { name, arguments } }] }\n * Arguments are streamed incrementally across multiple SSE chunks, so the\n * transformer must forward them as incremental deltas rather than a single blob.\n */\nfunction transformWorkersAiStream(\n\tsource: ReadableStream<Uint8Array>,\n\tmodel: string,\n): ReadableStream<Uint8Array> {\n\tconst decoder = new TextDecoder();\n\tconst encoder = new TextEncoder();\n\t// Generate a stable ID and timestamp for the entire stream, matching OpenAI's\n\t// convention where all chunks in a single response share the same id/created.\n\tconst streamId = `workers-ai-${crypto.randomUUID()}`;\n\tconst created = Math.floor(Date.now() / 1000);\n\tlet buffer = \"\";\n\tlet hasToolCalls = false;\n\t// When true, the source stream is already in OpenAI format (some models\n\t// like Qwen3, Kimi K2.5 stream OpenAI-compatible SSE through the binding).\n\t// In that case, flush() should only emit [DONE] and skip the finish chunk.\n\tlet isOpenAiFormat = false;\n\t// Track tool call state per index: store the generated/assigned ID so that\n\t// subsequent argument deltas use the same ID (matching the working streaming.ts pattern).\n\tconst toolCallState = new Map<number, { id: string; name: string }>();\n\n\treturn source.pipeThrough(\n\t\tnew TransformStream<Uint8Array, Uint8Array>({\n\t\t\ttransform(chunk, controller) {\n\t\t\t\tbuffer += decoder.decode(chunk, { stream: true });\n\t\t\t\tconst lines = buffer.split(\"\\n\");\n\t\t\t\tbuffer = lines.pop() || \"\";\n\n\t\t\t\tfor (const line of lines) {\n\t\t\t\t\tconst trimmed = line.trim();\n\t\t\t\t\tif (!trimmed || !trimmed.startsWith(\"data: \")) continue;\n\t\t\t\t\tconst data = trimmed.slice(6);\n\n\t\t\t\t\t// Swallow source [DONE]; we emit our own in flush()\n\t\t\t\t\tif (data === \"[DONE]\") continue;\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst parsed = JSON.parse(data);\n\n\t\t\t\t\t\t// Some models (Qwen3, Kimi K2.5) return OpenAI-compatible format\n\t\t\t\t\t\t// directly through the binding, with `choices[].delta.content` and\n\t\t\t\t\t\t// optional `reasoning_content`. Detect this and pass through as-is.\n\t\t\t\t\t\tif (parsed.choices !== undefined) {\n\t\t\t\t\t\t\t// Already OpenAI format — pass through but ensure each tool call\n\t\t\t\t\t\t\t// index gets a unique, stable ID across all chunks.\n\t\t\t\t\t\t\tisOpenAiFormat = true;\n\t\t\t\t\t\t\tconst choice = parsed.choices?.[0];\n\t\t\t\t\t\t\tif (choice?.delta?.tool_calls) {\n\t\t\t\t\t\t\t\thasToolCalls = true;\n\t\t\t\t\t\t\t\tfor (const tc of choice.delta.tool_calls) {\n\t\t\t\t\t\t\t\t\tconst tcIndex = tc.index ?? 0;\n\t\t\t\t\t\t\t\t\tif (!toolCallState.has(tcIndex)) {\n\t\t\t\t\t\t\t\t\t\t// First chunk for this index — generate/store unique ID\n\t\t\t\t\t\t\t\t\t\tconst id = tc.id || `call${streamId}${tcIndex}`;\n\t\t\t\t\t\t\t\t\t\ttoolCallState.set(tcIndex, {\n\t\t\t\t\t\t\t\t\t\t\tid,\n\t\t\t\t\t\t\t\t\t\t\tname: tc.function?.name || \"\",\n\t\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t\t\ttc.id = id;\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\t// Subsequent chunk — reuse stored ID, remove id from delta\n\t\t\t\t\t\t\t\t\t\t// (OpenAI format only sends id in first chunk)\n\t\t\t\t\t\t\t\t\t\tdelete tc.id;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (choice?.finish_reason === \"tool_calls\") {\n\t\t\t\t\t\t\t\thasToolCalls = true;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tcontroller.enqueue(\n\t\t\t\t\t\t\t\tencoder.encode(`data: ${JSON.stringify(parsed)}\\n\\n`),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// --- Workers AI native format handling below ---\n\n\t\t\t\t\t\t// Text content\n\t\t\t\t\t\tif (parsed.response != null && parsed.response !== \"\") {\n\t\t\t\t\t\t\tconst openAiChunk = {\n\t\t\t\t\t\t\t\tid: streamId,\n\t\t\t\t\t\t\t\tobject: \"chat.completion.chunk\",\n\t\t\t\t\t\t\t\tcreated,\n\t\t\t\t\t\t\t\tmodel,\n\t\t\t\t\t\t\t\tchoices: [\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tindex: 0,\n\t\t\t\t\t\t\t\t\t\tdelta: { content: parsed.response },\n\t\t\t\t\t\t\t\t\t\tfinish_reason: null,\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\tcontroller.enqueue(\n\t\t\t\t\t\t\t\tencoder.encode(`data: ${JSON.stringify(openAiChunk)}\\n\\n`),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Tool calls — Workers AI binding streams these incrementally:\n\t\t\t\t\t\t// Chunk A: { id, type, index, function: { name } } — start\n\t\t\t\t\t\t// Chunk B: { index, function: { arguments: \"partial...\" } } — args delta\n\t\t\t\t\t\t// Chunk C: { index, function: { arguments: \"rest...\" } } — args delta\n\t\t\t\t\t\t// Chunk D: { id: null, type: null, index, function: { name: null, arguments: \"\" } } — finalize (skip)\n\t\t\t\t\t\tif (Array.isArray(parsed.tool_calls) && parsed.tool_calls.length > 0) {\n\t\t\t\t\t\t\tfor (const tc of parsed.tool_calls) {\n\t\t\t\t\t\t\t\tconst tcIndex = tc.index ?? 0;\n\n\t\t\t\t\t\t\t\t// Resolve name and arguments from either nested or flat format\n\t\t\t\t\t\t\t\tconst tcName = tc.function?.name ?? tc.name ?? null;\n\t\t\t\t\t\t\t\tconst tcArgs = tc.function?.arguments ?? tc.arguments ?? null;\n\t\t\t\t\t\t\t\tconst tcId = tc.id ?? null;\n\n\t\t\t\t\t\t\t\t// Skip finalization chunks where everything is null/empty\n\t\t\t\t\t\t\t\tif (!tcId && !tcName && (!tcArgs || tcArgs === \"\")) continue;\n\n\t\t\t\t\t\t\t\thasToolCalls = true;\n\n\t\t\t\t\t\t\t\t// Build the OpenAI-compatible tool_calls delta\n\t\t\t\t\t\t\t\tconst toolCallDelta: Record<string, unknown> = {\n\t\t\t\t\t\t\t\t\tindex: tcIndex,\n\t\t\t\t\t\t\t\t};\n\n\t\t\t\t\t\t\t\tif (!toolCallState.has(tcIndex)) {\n\t\t\t\t\t\t\t\t\t// First chunk for this tool call index — emit id, type, name.\n\t\t\t\t\t\t\t\t\tconst id = tcId || `call${streamId}${tcIndex}`;\n\t\t\t\t\t\t\t\t\ttoolCallState.set(tcIndex, { id, name: tcName || \"\" });\n\t\t\t\t\t\t\t\t\ttoolCallDelta.id = id;\n\t\t\t\t\t\t\t\t\ttoolCallDelta.type = \"function\";\n\t\t\t\t\t\t\t\t\ttoolCallDelta.function = {\n\t\t\t\t\t\t\t\t\t\tname: tcName || \"\",\n\t\t\t\t\t\t\t\t\t\t// Include arguments if they arrive in the same chunk\n\t\t\t\t\t\t\t\t\t\targuments:\n\t\t\t\t\t\t\t\t\t\t\ttcArgs != null\n\t\t\t\t\t\t\t\t\t\t\t\t? typeof tcArgs === \"string\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t? tcArgs\n\t\t\t\t\t\t\t\t\t\t\t\t\t: JSON.stringify(tcArgs)\n\t\t\t\t\t\t\t\t\t\t\t\t: \"\",\n\t\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t// Subsequent chunks — only include arguments delta\n\t\t\t\t\t\t\t\t\tif (tcArgs != null && tcArgs !== \"\") {\n\t\t\t\t\t\t\t\t\t\ttoolCallDelta.function = {\n\t\t\t\t\t\t\t\t\t\t\targuments:\n\t\t\t\t\t\t\t\t\t\t\t\ttypeof tcArgs === \"string\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t? tcArgs\n\t\t\t\t\t\t\t\t\t\t\t\t\t: JSON.stringify(tcArgs),\n\t\t\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\tcontinue; // Nothing useful to forward\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tconst toolChunk = {\n\t\t\t\t\t\t\t\t\tid: streamId,\n\t\t\t\t\t\t\t\t\tobject: \"chat.completion.chunk\",\n\t\t\t\t\t\t\t\t\tcreated,\n\t\t\t\t\t\t\t\t\tmodel,\n\t\t\t\t\t\t\t\t\tchoices: [\n\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\tindex: 0,\n\t\t\t\t\t\t\t\t\t\t\tdelta: { tool_calls: [toolCallDelta] },\n\t\t\t\t\t\t\t\t\t\t\tfinish_reason: null,\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\tcontroller.enqueue(\n\t\t\t\t\t\t\t\t\tencoder.encode(`data: ${JSON.stringify(toolChunk)}\\n\\n`),\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (e) {\n\t\t\t\t\t\t// Log malformed SSE events for debugging; don't break the stream.\n\t\t\t\t\t\tconsole.warn(\"[tanstack-ai] failed to parse SSE event:\", data, e);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t},\n\t\t\tflush(controller) {\n\t\t\t\tif (!isOpenAiFormat) {\n\t\t\t\t\t// Workers AI native format: emit a finish chunk with stop/tool_calls\n\t\t\t\t\tconst finalChunk = {\n\t\t\t\t\t\tid: streamId,\n\t\t\t\t\t\tobject: \"chat.completion.chunk\",\n\t\t\t\t\t\tcreated,\n\t\t\t\t\t\tmodel,\n\t\t\t\t\t\tchoices: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tindex: 0,\n\t\t\t\t\t\t\t\tdelta: {},\n\t\t\t\t\t\t\t\tfinish_reason: hasToolCalls ? \"tool_calls\" : \"stop\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t\t\t};\n\t\t\t\t\tcontroller.enqueue(encoder.encode(`data: ${JSON.stringify(finalChunk)}\\n\\n`));\n\t\t\t\t}\n\t\t\t\t// OpenAI format already includes its own finish_reason in the stream.\n\t\t\t\t// Either way, emit a [DONE] sentinel.\n\t\t\t\tcontroller.enqueue(encoder.encode(\"data: [DONE]\\n\\n\"));\n\t\t\t},\n\t\t}),\n\t);\n}\n"],"mappings":";;AA4HA,SAAgB,sBACf,QACyC;CAGzC,OACC,aAAa,UACb,OAAQ,OAAO,QAA+C,YAAY;AAE5E;;AAGA,SAAgB,0BACf,QAC6C;CAC7C,OAAO,eAAe,UAAU,YAAY,UAAU,EAAE,eAAe;AACxE;;AAGA,SAAgB,gBAAgB,QAAkE;CACjG,IAAI,eAAe,QAAQ,OAAO;CAElC,OAAO,aAAa,UAAU,CAAC,sBAAsB,MAAM;AAC5D;;;;;;AAWA,SAAgB,wBAAwB,QAAsC;CAC7E,IACC,CAAC,sBAAsB,MAAM,KAC7B,CAAC,0BAA0B,MAAM,KACjC,CAAC,gBAAgB,MAAM,GAEvB,MAAM,IAAI,MACT,gOAGD;AAEF;AAMA,SAAgB,mBACf,UACA,QACA,UAAkC,CAAC,GACpB;CACf,QAAQ,OAAO,SAAS;EACvB,IAAI,QAAiC,CAAC;EAEtC,MAAM,MACL,OAAO,UAAU,WAAW,QAAQ,iBAAiB,MAAM,MAAM,OAAO,MAAM;EAC/E,MAAM,SAAS,IAAI,IAAI,GAAG;EAG1B,MAAM,WAAW,OAAO,SAAS,QAAQ,WAAW,EAAE,CAAC,CAAC,QAAQ,OAAO,EAAE,IAAI,OAAO;EAEpF,IAAI,MAAM,MACT,IAAI;GACH,QAAQ,KAAK,MAAM,KAAK,IAAc;EACvC,QAAQ;GACP,QAAQ,EAAE,MAAM,KAAK,KAAK;EAC3B;EAGD,MAAM,eAAuC,CAAC;EAE9C,IAAI,eAAe,UAAU,OAAO,WACnC,aAAa,uBAAuB;EAGrC,IAAI,OAAO,OAAO,aAAa,UAC9B,aAAa,sBAAsB,OAAO,OAAO,QAAQ;EAG1D,IAAI,OAAO,OAAO,mBAAmB,UACpC,aAAa,sBAAsB,OAAO;EAG3C,IAAI,OAAO,OAAO,aAAa,UAC9B,aAAa,qBAAqB,KAAK,UAAU,OAAO,QAAQ;EAGjE,MAAM,UAKF;GACH;GACA;GACA,SAAS;IACR,GAAI,MAAM;IACV,GAAG;IACH,GAAG;IACH,gBAAgB;GACjB;GACA;EACD;EAEA,IAAI,aAAa,cAAc;GAC9B,IAAI,CAAC,QAAQ,SAAS,WAAW,MAAM,GACtC,QAAQ,WAAW,OAAO,MAAM;GAEjC,OAAO,MAAM;GACb,OAAO,MAAM;EACd;EAEA,IAAI,OAAO,QACV,QAAQ,QAAQ,mBAAmB,UAAU,OAAO;EAGrD,IAAI,aAAa,QAChB,OACC,OAAO,QAGN,IAAI,SAAS,EAAE,QAAQ,MAAM,UAAU,KAAA,EAAU,CAAC;EAGrD,OAAO,MACN,wCAAwC,OAAO,UAAU,GAAG,OAAO,aACnE;GACC,GAAG;GACH,SAAS;IACR,gBAAgB;IAChB,GAAG;IACH,GAAG;IACH,GAAI,OAAO,WACR,EAAE,wBAAwB,UAAU,OAAO,WAAW,IACtD,CAAC;GACL;GACA,MAAM,KAAK,UAAU,OAAO;EAC7B,CACD;CACD;AACD;;;;;;;;;;AAeA,SAAS,4BACR,UAC4B;CAC5B,OAAO,SAAS,KAAK,QAAQ;EAC5B,MAAM,aAAa,EAAE,GAAG,IAAI;EAG5B,IAAI,WAAW,YAAY,QAAQ,WAAW,YAAY,KAAA,GACzD,WAAW,UAAU;EAGtB,OAAO;CACR,CAAC;AACF;;;;;;;;;;AAWA,SAAgB,4BACf,SACA,SACe;CACf,OAAO,OAAO,QAAQ,SAAS;EAC9B,IAAI,CAAC,MAAM,MACV,OAAO,IAAI,SAAS,WAAW,EAAE,QAAQ,IAAI,CAAC;EAG/C,IAAI;EACJ,IAAI;GACH,OAAO,KAAK,MAAM,KAAK,IAAc;EACtC,QAAQ;GACP,OAAO,IAAI,SAAS,qBAAqB,EAAE,QAAQ,IAAI,CAAC;EACzD;EAEA,MAAM,QAAQ,KAAK;EACnB,MAAM,SAAS,KAAK;EAGpB,MAAM,SAAkC,CAAC;EACzC,IAAI,KAAK,UACR,OAAO,WAAW,4BACjB,KAAK,QACN;EAED,IAAI,KAAK,OAAO,OAAO,QAAQ,KAAK;EACpC,IAAI,OAAO,KAAK,gBAAgB,UAAU,OAAO,cAAc,KAAK;EACpE,IAAI,OAAO,KAAK,eAAe,UAAU,OAAO,aAAa,KAAK;EAClE,IAAI,KAAK,iBAAiB,OAAO,kBAAkB,KAAK;EACxD,IAAI,QAAQ,OAAO,SAAS;EAQ5B,IAAI,KAAK,qBAAqB,KAAA,GAC7B,OAAO,mBAAmB,KAAK;EAEhC,IAAI,KAAK,yBAAyB,KAAA,GACjC,OAAO,uBAAuB,KAAK;EAGpC,MAAM,aAAsC,CAAC;EAC7C,IAAI,SAAS,cAAc,WAAW,eAAe,QAAQ;EAC7D,IAAI,MAAM,QAAQ,WAAW,SAAS,KAAK;EAE3C,MAAM,SAAS,MAAM,QAAQ,IAC5B,OACA,QACA,OAAO,KAAK,UAAU,CAAC,CAAC,SAAS,IAAI,aAAa,KAAA,CACnD;EAEA,IAAI,UAAU,kBAAkB,gBAAgB;GAG/C,MAAM,cAAc,yBACnB,QACA,KACD;GACA,OAAO,IAAI,SAAS,aAAa,EAChC,SAAS;IACR,gBAAgB;IAChB,iBAAiB;GAClB,EACD,CAAC;EACF;EASA,MAAM,cACL,OAAO,WAAW,YAAY,WAAW,OACrC,SACD,EAAE,UAAU,OAAO,MAAM,EAAE;EAS/B,MAAM,UAAmC;GACxC,MAAM;GACN,SARA,OAAO,YAAY,aAAa,WAC7B,YAAY,WACZ,OAAO,YAAY,aAAa,YAAY,YAAY,aAAa,OACpE,KAAK,UAAU,YAAY,QAAQ,IACnC;EAKL;EACA,IAAI,eAAe;EAGnB,IAAI,MAAM,QAAQ,YAAY,UAAU,KAAK,YAAY,WAAW,SAAS,GAAG;GAC/E,eAAe;GACf,QAAQ,aAAa,YAAY,WAAW,KAC1C,QAKM;IACN,IAAI,GAAG,MAAM,OAAO,WAAW;IAC/B,MAAM;IACN,UAAU;KACT,MAAM,GAAG,UAAU,QAAQ,GAAG,QAAQ;KACtC,WACC,QAAQ,GAAG,UAAU,aAAa,GAAG,eAAe,WAC/C,GAAG,UAAU,aAAa,GAAG,YAC/B,KAAK,UAAU,GAAG,UAAU,aAAa,GAAG,aAAa,CAAC,CAAC;IAChE;GACD,EACD;EACD;EAEA,MAAM,iBAAiB;GACtB,IAAI,cAAc,OAAO,WAAW;GACpC,QAAQ;GACR,SAAS,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;GACrC;GACA,SAAS,CAAC;IAAE,OAAO;IAAG;IAAS,eAAe;GAAa,CAAC;EAC7D;EAEA,OAAO,IAAI,SAAS,KAAK,UAAU,cAAc,GAAG,EACnD,SAAS,EAAE,gBAAgB,mBAAmB,EAC/C,CAAC;CACF;AACD;;;;;;;;;;AAgBA,SAAS,yBACR,QACA,OAC6B;CAC7B,MAAM,UAAU,IAAI,YAAY;CAChC,MAAM,UAAU,IAAI,YAAY;CAGhC,MAAM,WAAW,cAAc,OAAO,WAAW;CACjD,MAAM,UAAU,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;CAC5C,IAAI,SAAS;CACb,IAAI,eAAe;CAInB,IAAI,iBAAiB;CAGrB,MAAM,gCAAgB,IAAI,IAA0C;CAEpE,OAAO,OAAO,YACb,IAAI,gBAAwC;EAC3C,UAAU,OAAO,YAAY;GAC5B,UAAU,QAAQ,OAAO,OAAO,EAAE,QAAQ,KAAK,CAAC;GAChD,MAAM,QAAQ,OAAO,MAAM,IAAI;GAC/B,SAAS,MAAM,IAAI,KAAK;GAExB,KAAK,MAAM,QAAQ,OAAO;IACzB,MAAM,UAAU,KAAK,KAAK;IAC1B,IAAI,CAAC,WAAW,CAAC,QAAQ,WAAW,QAAQ,GAAG;IAC/C,MAAM,OAAO,QAAQ,MAAM,CAAC;IAG5B,IAAI,SAAS,UAAU;IAEvB,IAAI;KACH,MAAM,SAAS,KAAK,MAAM,IAAI;KAK9B,IAAI,OAAO,YAAY,KAAA,GAAW;MAGjC,iBAAiB;MACjB,MAAM,SAAS,OAAO,UAAU;MAChC,IAAI,QAAQ,OAAO,YAAY;OAC9B,eAAe;OACf,KAAK,MAAM,MAAM,OAAO,MAAM,YAAY;QACzC,MAAM,UAAU,GAAG,SAAS;QAC5B,IAAI,CAAC,cAAc,IAAI,OAAO,GAAG;SAEhC,MAAM,KAAK,GAAG,MAAM,OAAO,WAAW;SACtC,cAAc,IAAI,SAAS;UAC1B;UACA,MAAM,GAAG,UAAU,QAAQ;SAC5B,CAAC;SACD,GAAG,KAAK;QACT,OAGC,OAAO,GAAG;OAEZ;MACD;MACA,IAAI,QAAQ,kBAAkB,cAC7B,eAAe;MAEhB,WAAW,QACV,QAAQ,OAAO,SAAS,KAAK,UAAU,MAAM,EAAE,KAAK,CACrD;MACA;KACD;KAKA,IAAI,OAAO,YAAY,QAAQ,OAAO,aAAa,IAAI;MACtD,MAAM,cAAc;OACnB,IAAI;OACJ,QAAQ;OACR;OACA;OACA,SAAS,CACR;QACC,OAAO;QACP,OAAO,EAAE,SAAS,OAAO,SAAS;QAClC,eAAe;OAChB,CACD;MACD;MACA,WAAW,QACV,QAAQ,OAAO,SAAS,KAAK,UAAU,WAAW,EAAE,KAAK,CAC1D;KACD;KAOA,IAAI,MAAM,QAAQ,OAAO,UAAU,KAAK,OAAO,WAAW,SAAS,GAClE,KAAK,MAAM,MAAM,OAAO,YAAY;MACnC,MAAM,UAAU,GAAG,SAAS;MAG5B,MAAM,SAAS,GAAG,UAAU,QAAQ,GAAG,QAAQ;MAC/C,MAAM,SAAS,GAAG,UAAU,aAAa,GAAG,aAAa;MACzD,MAAM,OAAO,GAAG,MAAM;MAGtB,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,UAAU,WAAW,KAAK;MAEpD,eAAe;MAGf,MAAM,gBAAyC,EAC9C,OAAO,QACR;MAEA,IAAI,CAAC,cAAc,IAAI,OAAO,GAAG;OAEhC,MAAM,KAAK,QAAQ,OAAO,WAAW;OACrC,cAAc,IAAI,SAAS;QAAE;QAAI,MAAM,UAAU;OAAG,CAAC;OACrD,cAAc,KAAK;OACnB,cAAc,OAAO;OACrB,cAAc,WAAW;QACxB,MAAM,UAAU;QAEhB,WACC,UAAU,OACP,OAAO,WAAW,WACjB,SACA,KAAK,UAAU,MAAM,IACtB;OACL;MACD,OAEC,IAAI,UAAU,QAAQ,WAAW,IAChC,cAAc,WAAW,EACxB,WACC,OAAO,WAAW,WACf,SACA,KAAK,UAAU,MAAM,EAC1B;WAEA;MAIF,MAAM,YAAY;OACjB,IAAI;OACJ,QAAQ;OACR;OACA;OACA,SAAS,CACR;QACC,OAAO;QACP,OAAO,EAAE,YAAY,CAAC,aAAa,EAAE;QACrC,eAAe;OAChB,CACD;MACD;MACA,WAAW,QACV,QAAQ,OAAO,SAAS,KAAK,UAAU,SAAS,EAAE,KAAK,CACxD;KACD;IAEF,SAAS,GAAG;KAEX,QAAQ,KAAK,4CAA4C,MAAM,CAAC;IACjE;GACD;EACD;EACA,MAAM,YAAY;GACjB,IAAI,CAAC,gBAAgB;IAEpB,MAAM,aAAa;KAClB,IAAI;KACJ,QAAQ;KACR;KACA;KACA,SAAS,CACR;MACC,OAAO;MACP,OAAO,CAAC;MACR,eAAe,eAAe,eAAe;KAC9C,CACD;IACD;IACA,WAAW,QAAQ,QAAQ,OAAO,SAAS,KAAK,UAAU,UAAU,EAAE,KAAK,CAAC;GAC7E;GAGA,WAAW,QAAQ,QAAQ,OAAO,kBAAkB,CAAC;EACtD;CACD,CAAC,CACF;AACD"}
@@ -90,4 +90,4 @@ type WorkersAiAdapterConfig = (WorkersAiDirectBindingConfig | WorkersAiDirectCre
90
90
  };
91
91
  //#endregion
92
92
  export { WorkersAiAdapterConfig as i, AiGatewayConfig as n, AiGatewayCredentialsConfig as r, AiGatewayAdapterConfig as t };
93
- //# sourceMappingURL=create-fetcher-6p6heb85.d.mts.map
93
+ //# sourceMappingURL=create-fetcher-Bp5yCNaO.d.cts.map
@@ -90,4 +90,4 @@ type WorkersAiAdapterConfig = (WorkersAiDirectBindingConfig | WorkersAiDirectCre
90
90
  };
91
91
  //#endregion
92
92
  export { WorkersAiAdapterConfig as i, AiGatewayConfig as n, AiGatewayCredentialsConfig as r, AiGatewayAdapterConfig as t };
93
- //# sourceMappingURL=create-fetcher-vAQ8WW-p.d.cts.map
93
+ //# sourceMappingURL=create-fetcher-Bp5yCNaO.d.mts.map
@@ -309,4 +309,4 @@ Object.defineProperty(exports, "validateWorkersAiConfig", {
309
309
  }
310
310
  });
311
311
 
312
- //# sourceMappingURL=create-fetcher-By-hTiT9.cjs.map
312
+ //# sourceMappingURL=create-fetcher-YVxWCTVL.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"create-fetcher-CeUOJgrh.mjs","names":[],"sources":["../src/utils/create-fetcher.ts"],"sourcesContent":["// ---------------------------------------------------------------------------\n// AI Gateway types (for third-party providers + Workers AI through gateway)\n// ---------------------------------------------------------------------------\n\nexport interface CloudflareAiGateway {\n\trun(request: unknown): Promise<Response>;\n}\n\nexport interface AiGatewayBindingConfig {\n\t/**\n\t * The AI Gateway binding\n\t * @example\n\t * env.AI.gateway('my-gateway-id')\n\t */\n\tbinding: CloudflareAiGateway;\n\t/**\n\t * The Provider API Key if you want to manually pass it, ignore if using Unified Billing or BYOK.\n\t */\n\tapiKey?: string;\n}\n\nexport type AiGatewayCredentialsConfig = {\n\t/**\n\t * The Cloudflare account ID\n\t */\n\taccountId: string;\n\t/**\n\t * The AI Gateway ID\n\t */\n\tgatewayId: string;\n} & (\n\t| {\n\t\t\t/** Cloudflare API Key for AI Gateway */\n\t\t\tcfApiKey: string;\n\t\t\tapiKey?: string;\n\t }\n\t| {\n\t\t\t/** Provider API Key */\n\t\t\tapiKey: string;\n\t\t\t/** Cloudflare API Key for AI Gateway */\n\t\t\tcfApiKey?: string;\n\t }\n);\n\nexport interface AiGatewayConfig {\n\tskipCache?: boolean;\n\tcacheTtl?: number;\n\tcustomCacheKey?: string;\n\tmetadata?: Record<string, unknown>;\n}\n\nexport type AiGatewayAdapterConfig = (AiGatewayBindingConfig | AiGatewayCredentialsConfig) &\n\tAiGatewayConfig;\n\n// ---------------------------------------------------------------------------\n// Plain Workers AI types (direct binding or REST, no gateway)\n// ---------------------------------------------------------------------------\n\n/**\n * The Workers AI binding interface (env.AI).\n * Accepts a model name and inputs, returns results directly.\n * Includes `gateway()` which is present on `env.AI` but not on `env.AI.gateway(id)`,\n * enabling structural discrimination from `CloudflareAiGateway`.\n */\nexport interface WorkersAiBinding {\n\trun(\n\t\tmodel: string,\n\t\tinputs: Record<string, unknown>,\n\t\toptions?: Record<string, unknown>,\n\t): Promise<unknown>;\n\tgateway(gatewayId: string): CloudflareAiGateway;\n}\n\nexport interface WorkersAiDirectBindingConfig {\n\t/**\n\t * The Workers AI binding (env.AI).\n\t * @example\n\t * { binding: env.AI }\n\t */\n\tbinding: WorkersAiBinding;\n}\n\nexport interface WorkersAiDirectCredentialsConfig {\n\t/**\n\t * The Cloudflare account ID\n\t */\n\taccountId: string;\n\t/**\n\t * The Cloudflare API key for Workers AI\n\t */\n\tapiKey: string;\n}\n\n/**\n * Config for Workers AI adapters. Supports four modes:\n * - Plain binding: `{ binding: env.AI }`\n * - Plain REST: `{ accountId, apiKey }`\n * - AI Gateway binding: `{ binding: env.AI.gateway(id) }`\n * - AI Gateway REST: `{ accountId, gatewayId, ... }`\n *\n * The third union member intersects `AiGatewayAdapterConfig` with `{ apiKey?: string }`.\n * For the gateway binding variant, `AiGatewayBindingConfig` already includes `apiKey?`,\n * so the intersection is redundant there. For the gateway credentials variant, this\n * `apiKey` represents the Workers AI token (used in the `Authorization` header to the\n * upstream provider), distinct from `cfApiKey` (used in the `cf-aig-authorization`\n * header for authenticated gateways).\n */\nexport type WorkersAiAdapterConfig = (\n\t| WorkersAiDirectBindingConfig\n\t| WorkersAiDirectCredentialsConfig\n\t| (AiGatewayAdapterConfig & { apiKey?: string })\n) & {\n\t/**\n\t * Session affinity key for prefix-cache optimization.\n\t * Routes requests with the same key to the same backend replica.\n\t */\n\tsessionAffinity?: string;\n};\n\n// ---------------------------------------------------------------------------\n// Config detection helpers\n// ---------------------------------------------------------------------------\n\n/** Returns true if this is a plain Workers AI binding config (`{ binding: env.AI }`) */\nexport function isDirectBindingConfig(\n\tconfig: WorkersAiAdapterConfig,\n): config is WorkersAiDirectBindingConfig {\n\t// env.AI has a .gateway() method; env.AI.gateway(id) does not.\n\t// This distinguishes direct bindings from AI Gateway bindings.\n\treturn (\n\t\t\"binding\" in config &&\n\t\ttypeof (config.binding as unknown as Record<string, unknown>).gateway === \"function\"\n\t);\n}\n\n/** Returns true if this is a plain Workers AI REST config (accountId + apiKey, no gatewayId) */\nexport function isDirectCredentialsConfig(\n\tconfig: WorkersAiAdapterConfig,\n): config is WorkersAiDirectCredentialsConfig {\n\treturn \"accountId\" in config && \"apiKey\" in config && !(\"gatewayId\" in config);\n}\n\n/** Returns true if this is an AI Gateway config (has gateway binding or `gatewayId`) */\nexport function isGatewayConfig(config: WorkersAiAdapterConfig): config is AiGatewayAdapterConfig {\n\tif (\"gatewayId\" in config) return true;\n\t// Has `binding` but NOT a direct Workers AI binding (no .gateway method)\n\treturn \"binding\" in config && !isDirectBindingConfig(config);\n}\n\n// ---------------------------------------------------------------------------\n// Config validation\n// ---------------------------------------------------------------------------\n\n/**\n * Validates that a WorkersAiAdapterConfig contains a valid configuration.\n * Throws an error if neither a binding, credentials (accountId + apiKey),\n * nor a gateway configuration is provided.\n */\nexport function validateWorkersAiConfig(config: WorkersAiAdapterConfig): void {\n\tif (\n\t\t!isDirectBindingConfig(config) &&\n\t\t!isDirectCredentialsConfig(config) &&\n\t\t!isGatewayConfig(config)\n\t) {\n\t\tthrow new Error(\n\t\t\t\"Invalid Workers AI configuration: you must provide either a binding (e.g. { binding: env.AI }), \" +\n\t\t\t\t\"credentials ({ accountId, apiKey }), or a gateway configuration ({ binding: env.AI.gateway(id) } \" +\n\t\t\t\t\"or { accountId, gatewayId }).\",\n\t\t);\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// createGatewayFetch -- for routing through AI Gateway\n// ---------------------------------------------------------------------------\n\nexport function createGatewayFetch(\n\tprovider: string,\n\tconfig: AiGatewayAdapterConfig,\n\theaders: Record<string, string> = {},\n): typeof fetch {\n\treturn (input, init) => {\n\t\tlet query: Record<string, unknown> = {};\n\n\t\tconst url =\n\t\t\ttypeof input === \"string\" ? input : input instanceof URL ? input.href : input.url;\n\t\tconst urlObj = new URL(url);\n\n\t\t// Extract endpoint path (remove /v1/ prefix if present)\n\t\tconst endpoint = urlObj.pathname.replace(/^\\/v1\\//, \"\").replace(/^\\//, \"\") + urlObj.search;\n\n\t\tif (init?.body) {\n\t\t\ttry {\n\t\t\t\tquery = JSON.parse(init.body as string);\n\t\t\t} catch {\n\t\t\t\tquery = { _raw: init.body };\n\t\t\t}\n\t\t}\n\n\t\tconst cacheHeaders: Record<string, string> = {};\n\n\t\tif (\"skipCache\" in config && config.skipCache) {\n\t\t\tcacheHeaders[\"cf-aig-skip-cache\"] = \"true\";\n\t\t}\n\n\t\tif (typeof config.cacheTtl === \"number\") {\n\t\t\tcacheHeaders[\"cf-aig-cache-ttl\"] = String(config.cacheTtl);\n\t\t}\n\n\t\tif (typeof config.customCacheKey === \"string\") {\n\t\t\tcacheHeaders[\"cf-aig-cache-key\"] = config.customCacheKey;\n\t\t}\n\n\t\tif (typeof config.metadata === \"object\") {\n\t\t\tcacheHeaders[\"cf-aig-metadata\"] = JSON.stringify(config.metadata);\n\t\t}\n\n\t\tconst request: {\n\t\t\tprovider: string;\n\t\t\tendpoint: string;\n\t\t\theaders: Record<string, string>;\n\t\t\tquery: Record<string, unknown>;\n\t\t} = {\n\t\t\tprovider,\n\t\t\tendpoint,\n\t\t\theaders: {\n\t\t\t\t...(init?.headers as Record<string, string> | undefined),\n\t\t\t\t...headers,\n\t\t\t\t...cacheHeaders,\n\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t},\n\t\t\tquery,\n\t\t};\n\n\t\tif (provider === \"workers-ai\") {\n\t\t\tif (!request.endpoint.startsWith(\"run/\")) {\n\t\t\t\trequest.endpoint = `run/${query.model}`;\n\t\t\t}\n\t\t\tdelete query.model;\n\t\t\tdelete query.instructions;\n\t\t}\n\n\t\tif (config.apiKey) {\n\t\t\trequest.headers[\"authorization\"] = `Bearer ${config.apiKey}`;\n\t\t}\n\n\t\tif (\"binding\" in config) {\n\t\t\treturn (\n\t\t\t\tconfig.binding as {\n\t\t\t\t\trun(req: unknown, opts?: { signal?: AbortSignal }): Promise<Response>;\n\t\t\t\t}\n\t\t\t).run(request, { signal: init?.signal ?? undefined });\n\t\t}\n\n\t\treturn fetch(\n\t\t\t`https://gateway.ai.cloudflare.com/v1/${config.accountId}/${config.gatewayId}`,\n\t\t\t{\n\t\t\t\t...init,\n\t\t\t\theaders: {\n\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\t...headers,\n\t\t\t\t\t...cacheHeaders,\n\t\t\t\t\t...(config.cfApiKey\n\t\t\t\t\t\t? { \"cf-aig-authorization\": `Bearer ${config.cfApiKey}` }\n\t\t\t\t\t\t: {}),\n\t\t\t\t},\n\t\t\t\tbody: JSON.stringify(request),\n\t\t\t},\n\t\t);\n\t};\n}\n\n// ---------------------------------------------------------------------------\n// createWorkersAiBindingFetch -- shim that makes env.AI look like an OpenAI endpoint\n// ---------------------------------------------------------------------------\n\n/**\n * Normalize messages before passing to Workers AI binding.\n *\n * The binding has strict schema validation that may differ from the OpenAI API:\n * - `content` must not be null\n *\n * Content arrays (with image_url parts) are passed through as-is since the\n * Workers AI binding accepts them at runtime for vision-capable models.\n */\nfunction normalizeMessagesForBinding(\n\tmessages: Record<string, unknown>[],\n): Record<string, unknown>[] {\n\treturn messages.map((msg) => {\n\t\tconst normalized = { ...msg };\n\n\t\t// content: null → content: \"\"\n\t\tif (normalized.content === null || normalized.content === undefined) {\n\t\t\tnormalized.content = \"\";\n\t\t}\n\n\t\treturn normalized;\n\t});\n}\n\n/**\n * Creates a fetch function that intercepts OpenAI SDK requests and translates them\n * to Workers AI binding calls (env.AI.run). This allows the WorkersAiTextAdapter\n * to use the OpenAI SDK against a plain Workers AI binding.\n *\n * NOTE: The `input` URL parameter is intentionally ignored. The model name and all\n * request parameters are extracted from the JSON body, matching Workers AI's\n * `binding.run(model, inputs)` calling convention.\n */\nexport function createWorkersAiBindingFetch(\n\tbinding: WorkersAiBinding,\n\toptions?: { extraHeaders?: Record<string, string> },\n): typeof fetch {\n\treturn async (_input, init) => {\n\t\tif (!init?.body) {\n\t\t\treturn new Response(\"No body\", { status: 400 });\n\t\t}\n\n\t\tlet body: Record<string, unknown>;\n\t\ttry {\n\t\t\tbody = JSON.parse(init.body as string);\n\t\t} catch {\n\t\t\treturn new Response(\"Invalid JSON body\", { status: 400 });\n\t\t}\n\n\t\tconst model = body.model as string;\n\t\tconst stream = body.stream as boolean | undefined;\n\n\t\t// Build Workers AI inputs from OpenAI format\n\t\tconst inputs: Record<string, unknown> = {};\n\t\tif (body.messages) {\n\t\t\tinputs.messages = normalizeMessagesForBinding(\n\t\t\t\tbody.messages as Record<string, unknown>[],\n\t\t\t);\n\t\t}\n\t\tif (body.tools) inputs.tools = body.tools;\n\t\tif (typeof body.temperature === \"number\") inputs.temperature = body.temperature;\n\t\tif (typeof body.max_tokens === \"number\") inputs.max_tokens = body.max_tokens;\n\t\tif (body.response_format) inputs.response_format = body.response_format;\n\t\tif (stream) inputs.stream = true;\n\n\t\t// Workers AI-specific reasoning controls. These belong on the INPUTS object\n\t\t// passed to binding.run(model, inputs), not on the options (3rd) arg.\n\t\t// See https://github.com/cloudflare/ai/issues/503.\n\t\t//\n\t\t// `reasoning_effort: null` is a valid value (disables reasoning on models\n\t\t// that support it), so we check `!== undefined` rather than truthiness.\n\t\tif (body.reasoning_effort !== undefined) {\n\t\t\tinputs.reasoning_effort = body.reasoning_effort;\n\t\t}\n\t\tif (body.chat_template_kwargs !== undefined) {\n\t\t\tinputs.chat_template_kwargs = body.chat_template_kwargs;\n\t\t}\n\n\t\tconst runOptions: Record<string, unknown> = {};\n\t\tif (options?.extraHeaders) runOptions.extraHeaders = options.extraHeaders;\n\t\tif (init?.signal) runOptions.signal = init.signal;\n\n\t\tconst result = await binding.run(\n\t\t\tmodel,\n\t\t\tinputs,\n\t\t\tObject.keys(runOptions).length > 0 ? runOptions : undefined,\n\t\t);\n\n\t\tif (stream && result instanceof ReadableStream) {\n\t\t\t// Workers AI returns an SSE stream with `data: {\"response\":\"chunk\"}` format.\n\t\t\t// Transform it to OpenAI-compatible SSE format.\n\t\t\tconst transformed = transformWorkersAiStream(\n\t\t\t\tresult as ReadableStream<Uint8Array>,\n\t\t\t\tmodel,\n\t\t\t);\n\t\t\treturn new Response(transformed, {\n\t\t\t\theaders: {\n\t\t\t\t\t\"Content-Type\": \"text/event-stream\",\n\t\t\t\t\t\"Cache-Control\": \"no-cache\",\n\t\t\t\t},\n\t\t\t});\n\t\t}\n\n\t\t// Graceful degradation: some models return a complete (non-streaming)\n\t\t// response even when `stream: true` is requested. Fall through to the\n\t\t// non-streaming wrapper which produces a valid OpenAI Chat Completion\n\t\t// response that the SDK can consume.\n\n\t\t// Non-streaming: Workers AI returns { response: \"text\", tool_calls?: [...] }\n\t\t// Wrap into OpenAI Chat Completion format.\n\t\tconst responseObj =\n\t\t\ttypeof result === \"object\" && result !== null\n\t\t\t\t? (result as Record<string, unknown>)\n\t\t\t\t: { response: String(result) };\n\n\t\tconst responseText =\n\t\t\ttypeof responseObj.response === \"string\"\n\t\t\t\t? responseObj.response\n\t\t\t\t: typeof responseObj.response === \"object\" && responseObj.response !== null\n\t\t\t\t\t? JSON.stringify(responseObj.response)\n\t\t\t\t\t: \"\";\n\n\t\tconst message: Record<string, unknown> = {\n\t\t\trole: \"assistant\",\n\t\t\tcontent: responseText,\n\t\t};\n\t\tlet finishReason = \"stop\";\n\n\t\t// Handle tool calls if present in Workers AI response\n\t\tif (Array.isArray(responseObj.tool_calls) && responseObj.tool_calls.length > 0) {\n\t\t\tfinishReason = \"tool_calls\";\n\t\t\tmessage.tool_calls = responseObj.tool_calls.map(\n\t\t\t\t(tc: {\n\t\t\t\t\tid?: string;\n\t\t\t\t\tname?: string;\n\t\t\t\t\targuments: unknown;\n\t\t\t\t\tfunction?: { name: string; arguments?: unknown };\n\t\t\t\t}) => ({\n\t\t\t\t\tid: tc.id || crypto.randomUUID(),\n\t\t\t\t\ttype: \"function\",\n\t\t\t\t\tfunction: {\n\t\t\t\t\t\tname: tc.function?.name || tc.name || \"\",\n\t\t\t\t\t\targuments:\n\t\t\t\t\t\t\ttypeof (tc.function?.arguments ?? tc.arguments) === \"string\"\n\t\t\t\t\t\t\t\t? ((tc.function?.arguments ?? tc.arguments) as string)\n\t\t\t\t\t\t\t\t: JSON.stringify(tc.function?.arguments ?? tc.arguments ?? {}),\n\t\t\t\t\t},\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\n\t\tconst openAiResponse = {\n\t\t\tid: `workers-ai-${crypto.randomUUID()}`,\n\t\t\tobject: \"chat.completion\",\n\t\t\tcreated: Math.floor(Date.now() / 1000),\n\t\t\tmodel,\n\t\t\tchoices: [{ index: 0, message, finish_reason: finishReason }],\n\t\t};\n\n\t\treturn new Response(JSON.stringify(openAiResponse), {\n\t\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\t});\n\t};\n}\n\n// ---------------------------------------------------------------------------\n// Stream transformer: Workers AI SSE -> OpenAI-compatible SSE\n// Uses TransformStream for proper backpressure.\n// ---------------------------------------------------------------------------\n\n/**\n * Transforms a Workers AI SSE stream (data: {\"response\":\"chunk\"}) into\n * an OpenAI-compatible SSE stream (data: {\"choices\":[{\"delta\":{\"content\":\"chunk\"}}]}).\n *\n * Workers AI binding streams tool calls in an OpenAI-like nested format:\n * { tool_calls: [{ id, type, index, function: { name, arguments } }] }\n * Arguments are streamed incrementally across multiple SSE chunks, so the\n * transformer must forward them as incremental deltas rather than a single blob.\n */\nfunction transformWorkersAiStream(\n\tsource: ReadableStream<Uint8Array>,\n\tmodel: string,\n): ReadableStream<Uint8Array> {\n\tconst decoder = new TextDecoder();\n\tconst encoder = new TextEncoder();\n\t// Generate a stable ID and timestamp for the entire stream, matching OpenAI's\n\t// convention where all chunks in a single response share the same id/created.\n\tconst streamId = `workers-ai-${crypto.randomUUID()}`;\n\tconst created = Math.floor(Date.now() / 1000);\n\tlet buffer = \"\";\n\tlet hasToolCalls = false;\n\t// When true, the source stream is already in OpenAI format (some models\n\t// like Qwen3, Kimi K2.5 stream OpenAI-compatible SSE through the binding).\n\t// In that case, flush() should only emit [DONE] and skip the finish chunk.\n\tlet isOpenAiFormat = false;\n\t// Track tool call state per index: store the generated/assigned ID so that\n\t// subsequent argument deltas use the same ID (matching the working streaming.ts pattern).\n\tconst toolCallState = new Map<number, { id: string; name: string }>();\n\n\treturn source.pipeThrough(\n\t\tnew TransformStream<Uint8Array, Uint8Array>({\n\t\t\ttransform(chunk, controller) {\n\t\t\t\tbuffer += decoder.decode(chunk, { stream: true });\n\t\t\t\tconst lines = buffer.split(\"\\n\");\n\t\t\t\tbuffer = lines.pop() || \"\";\n\n\t\t\t\tfor (const line of lines) {\n\t\t\t\t\tconst trimmed = line.trim();\n\t\t\t\t\tif (!trimmed || !trimmed.startsWith(\"data: \")) continue;\n\t\t\t\t\tconst data = trimmed.slice(6);\n\n\t\t\t\t\t// Swallow source [DONE]; we emit our own in flush()\n\t\t\t\t\tif (data === \"[DONE]\") continue;\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst parsed = JSON.parse(data);\n\n\t\t\t\t\t\t// Some models (Qwen3, Kimi K2.5) return OpenAI-compatible format\n\t\t\t\t\t\t// directly through the binding, with `choices[].delta.content` and\n\t\t\t\t\t\t// optional `reasoning_content`. Detect this and pass through as-is.\n\t\t\t\t\t\tif (parsed.choices !== undefined) {\n\t\t\t\t\t\t\t// Already OpenAI format — pass through but ensure each tool call\n\t\t\t\t\t\t\t// index gets a unique, stable ID across all chunks.\n\t\t\t\t\t\t\tisOpenAiFormat = true;\n\t\t\t\t\t\t\tconst choice = parsed.choices?.[0];\n\t\t\t\t\t\t\tif (choice?.delta?.tool_calls) {\n\t\t\t\t\t\t\t\thasToolCalls = true;\n\t\t\t\t\t\t\t\tfor (const tc of choice.delta.tool_calls) {\n\t\t\t\t\t\t\t\t\tconst tcIndex = tc.index ?? 0;\n\t\t\t\t\t\t\t\t\tif (!toolCallState.has(tcIndex)) {\n\t\t\t\t\t\t\t\t\t\t// First chunk for this index — generate/store unique ID\n\t\t\t\t\t\t\t\t\t\tconst id = tc.id || `call${streamId}${tcIndex}`;\n\t\t\t\t\t\t\t\t\t\ttoolCallState.set(tcIndex, {\n\t\t\t\t\t\t\t\t\t\t\tid,\n\t\t\t\t\t\t\t\t\t\t\tname: tc.function?.name || \"\",\n\t\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t\t\ttc.id = id;\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\t// Subsequent chunk — reuse stored ID, remove id from delta\n\t\t\t\t\t\t\t\t\t\t// (OpenAI format only sends id in first chunk)\n\t\t\t\t\t\t\t\t\t\tdelete tc.id;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (choice?.finish_reason === \"tool_calls\") {\n\t\t\t\t\t\t\t\thasToolCalls = true;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tcontroller.enqueue(\n\t\t\t\t\t\t\t\tencoder.encode(`data: ${JSON.stringify(parsed)}\\n\\n`),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// --- Workers AI native format handling below ---\n\n\t\t\t\t\t\t// Text content\n\t\t\t\t\t\tif (parsed.response != null && parsed.response !== \"\") {\n\t\t\t\t\t\t\tconst openAiChunk = {\n\t\t\t\t\t\t\t\tid: streamId,\n\t\t\t\t\t\t\t\tobject: \"chat.completion.chunk\",\n\t\t\t\t\t\t\t\tcreated,\n\t\t\t\t\t\t\t\tmodel,\n\t\t\t\t\t\t\t\tchoices: [\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tindex: 0,\n\t\t\t\t\t\t\t\t\t\tdelta: { content: parsed.response },\n\t\t\t\t\t\t\t\t\t\tfinish_reason: null,\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\tcontroller.enqueue(\n\t\t\t\t\t\t\t\tencoder.encode(`data: ${JSON.stringify(openAiChunk)}\\n\\n`),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Tool calls — Workers AI binding streams these incrementally:\n\t\t\t\t\t\t// Chunk A: { id, type, index, function: { name } } — start\n\t\t\t\t\t\t// Chunk B: { index, function: { arguments: \"partial...\" } } — args delta\n\t\t\t\t\t\t// Chunk C: { index, function: { arguments: \"rest...\" } } — args delta\n\t\t\t\t\t\t// Chunk D: { id: null, type: null, index, function: { name: null, arguments: \"\" } } — finalize (skip)\n\t\t\t\t\t\tif (Array.isArray(parsed.tool_calls) && parsed.tool_calls.length > 0) {\n\t\t\t\t\t\t\tfor (const tc of parsed.tool_calls) {\n\t\t\t\t\t\t\t\tconst tcIndex = tc.index ?? 0;\n\n\t\t\t\t\t\t\t\t// Resolve name and arguments from either nested or flat format\n\t\t\t\t\t\t\t\tconst tcName = tc.function?.name ?? tc.name ?? null;\n\t\t\t\t\t\t\t\tconst tcArgs = tc.function?.arguments ?? tc.arguments ?? null;\n\t\t\t\t\t\t\t\tconst tcId = tc.id ?? null;\n\n\t\t\t\t\t\t\t\t// Skip finalization chunks where everything is null/empty\n\t\t\t\t\t\t\t\tif (!tcId && !tcName && (!tcArgs || tcArgs === \"\")) continue;\n\n\t\t\t\t\t\t\t\thasToolCalls = true;\n\n\t\t\t\t\t\t\t\t// Build the OpenAI-compatible tool_calls delta\n\t\t\t\t\t\t\t\tconst toolCallDelta: Record<string, unknown> = {\n\t\t\t\t\t\t\t\t\tindex: tcIndex,\n\t\t\t\t\t\t\t\t};\n\n\t\t\t\t\t\t\t\tif (!toolCallState.has(tcIndex)) {\n\t\t\t\t\t\t\t\t\t// First chunk for this tool call index — emit id, type, name.\n\t\t\t\t\t\t\t\t\tconst id = tcId || `call${streamId}${tcIndex}`;\n\t\t\t\t\t\t\t\t\ttoolCallState.set(tcIndex, { id, name: tcName || \"\" });\n\t\t\t\t\t\t\t\t\ttoolCallDelta.id = id;\n\t\t\t\t\t\t\t\t\ttoolCallDelta.type = \"function\";\n\t\t\t\t\t\t\t\t\ttoolCallDelta.function = {\n\t\t\t\t\t\t\t\t\t\tname: tcName || \"\",\n\t\t\t\t\t\t\t\t\t\t// Include arguments if they arrive in the same chunk\n\t\t\t\t\t\t\t\t\t\targuments:\n\t\t\t\t\t\t\t\t\t\t\ttcArgs != null\n\t\t\t\t\t\t\t\t\t\t\t\t? typeof tcArgs === \"string\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t? tcArgs\n\t\t\t\t\t\t\t\t\t\t\t\t\t: JSON.stringify(tcArgs)\n\t\t\t\t\t\t\t\t\t\t\t\t: \"\",\n\t\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t// Subsequent chunks — only include arguments delta\n\t\t\t\t\t\t\t\t\tif (tcArgs != null && tcArgs !== \"\") {\n\t\t\t\t\t\t\t\t\t\ttoolCallDelta.function = {\n\t\t\t\t\t\t\t\t\t\t\targuments:\n\t\t\t\t\t\t\t\t\t\t\t\ttypeof tcArgs === \"string\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t? tcArgs\n\t\t\t\t\t\t\t\t\t\t\t\t\t: JSON.stringify(tcArgs),\n\t\t\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\tcontinue; // Nothing useful to forward\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tconst toolChunk = {\n\t\t\t\t\t\t\t\t\tid: streamId,\n\t\t\t\t\t\t\t\t\tobject: \"chat.completion.chunk\",\n\t\t\t\t\t\t\t\t\tcreated,\n\t\t\t\t\t\t\t\t\tmodel,\n\t\t\t\t\t\t\t\t\tchoices: [\n\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\tindex: 0,\n\t\t\t\t\t\t\t\t\t\t\tdelta: { tool_calls: [toolCallDelta] },\n\t\t\t\t\t\t\t\t\t\t\tfinish_reason: null,\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\tcontroller.enqueue(\n\t\t\t\t\t\t\t\t\tencoder.encode(`data: ${JSON.stringify(toolChunk)}\\n\\n`),\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (e) {\n\t\t\t\t\t\t// Log malformed SSE events for debugging; don't break the stream.\n\t\t\t\t\t\tconsole.warn(\"[tanstack-ai] failed to parse SSE event:\", data, e);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t},\n\t\t\tflush(controller) {\n\t\t\t\tif (!isOpenAiFormat) {\n\t\t\t\t\t// Workers AI native format: emit a finish chunk with stop/tool_calls\n\t\t\t\t\tconst finalChunk = {\n\t\t\t\t\t\tid: streamId,\n\t\t\t\t\t\tobject: \"chat.completion.chunk\",\n\t\t\t\t\t\tcreated,\n\t\t\t\t\t\tmodel,\n\t\t\t\t\t\tchoices: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tindex: 0,\n\t\t\t\t\t\t\t\tdelta: {},\n\t\t\t\t\t\t\t\tfinish_reason: hasToolCalls ? \"tool_calls\" : \"stop\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t\t\t};\n\t\t\t\t\tcontroller.enqueue(encoder.encode(`data: ${JSON.stringify(finalChunk)}\\n\\n`));\n\t\t\t\t}\n\t\t\t\t// OpenAI format already includes its own finish_reason in the stream.\n\t\t\t\t// Either way, emit a [DONE] sentinel.\n\t\t\t\tcontroller.enqueue(encoder.encode(\"data: [DONE]\\n\\n\"));\n\t\t\t},\n\t\t}),\n\t);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"create-fetcher-YVxWCTVL.cjs","names":[],"sources":["../src/utils/create-fetcher.ts"],"sourcesContent":["// ---------------------------------------------------------------------------\n// AI Gateway types (for third-party providers + Workers AI through gateway)\n// ---------------------------------------------------------------------------\n\nexport interface CloudflareAiGateway {\n\trun(request: unknown): Promise<Response>;\n}\n\nexport interface AiGatewayBindingConfig {\n\t/**\n\t * The AI Gateway binding\n\t * @example\n\t * env.AI.gateway('my-gateway-id')\n\t */\n\tbinding: CloudflareAiGateway;\n\t/**\n\t * The Provider API Key if you want to manually pass it, ignore if using Unified Billing or BYOK.\n\t */\n\tapiKey?: string;\n}\n\nexport type AiGatewayCredentialsConfig = {\n\t/**\n\t * The Cloudflare account ID\n\t */\n\taccountId: string;\n\t/**\n\t * The AI Gateway ID\n\t */\n\tgatewayId: string;\n} & (\n\t| {\n\t\t\t/** Cloudflare API Key for AI Gateway */\n\t\t\tcfApiKey: string;\n\t\t\tapiKey?: string;\n\t }\n\t| {\n\t\t\t/** Provider API Key */\n\t\t\tapiKey: string;\n\t\t\t/** Cloudflare API Key for AI Gateway */\n\t\t\tcfApiKey?: string;\n\t }\n);\n\nexport interface AiGatewayConfig {\n\tskipCache?: boolean;\n\tcacheTtl?: number;\n\tcustomCacheKey?: string;\n\tmetadata?: Record<string, unknown>;\n}\n\nexport type AiGatewayAdapterConfig = (AiGatewayBindingConfig | AiGatewayCredentialsConfig) &\n\tAiGatewayConfig;\n\n// ---------------------------------------------------------------------------\n// Plain Workers AI types (direct binding or REST, no gateway)\n// ---------------------------------------------------------------------------\n\n/**\n * The Workers AI binding interface (env.AI).\n * Accepts a model name and inputs, returns results directly.\n * Includes `gateway()` which is present on `env.AI` but not on `env.AI.gateway(id)`,\n * enabling structural discrimination from `CloudflareAiGateway`.\n */\nexport interface WorkersAiBinding {\n\trun(\n\t\tmodel: string,\n\t\tinputs: Record<string, unknown>,\n\t\toptions?: Record<string, unknown>,\n\t): Promise<unknown>;\n\tgateway(gatewayId: string): CloudflareAiGateway;\n}\n\nexport interface WorkersAiDirectBindingConfig {\n\t/**\n\t * The Workers AI binding (env.AI).\n\t * @example\n\t * { binding: env.AI }\n\t */\n\tbinding: WorkersAiBinding;\n}\n\nexport interface WorkersAiDirectCredentialsConfig {\n\t/**\n\t * The Cloudflare account ID\n\t */\n\taccountId: string;\n\t/**\n\t * The Cloudflare API key for Workers AI\n\t */\n\tapiKey: string;\n}\n\n/**\n * Config for Workers AI adapters. Supports four modes:\n * - Plain binding: `{ binding: env.AI }`\n * - Plain REST: `{ accountId, apiKey }`\n * - AI Gateway binding: `{ binding: env.AI.gateway(id) }`\n * - AI Gateway REST: `{ accountId, gatewayId, ... }`\n *\n * The third union member intersects `AiGatewayAdapterConfig` with `{ apiKey?: string }`.\n * For the gateway binding variant, `AiGatewayBindingConfig` already includes `apiKey?`,\n * so the intersection is redundant there. For the gateway credentials variant, this\n * `apiKey` represents the Workers AI token (used in the `Authorization` header to the\n * upstream provider), distinct from `cfApiKey` (used in the `cf-aig-authorization`\n * header for authenticated gateways).\n */\nexport type WorkersAiAdapterConfig = (\n\t| WorkersAiDirectBindingConfig\n\t| WorkersAiDirectCredentialsConfig\n\t| (AiGatewayAdapterConfig & { apiKey?: string })\n) & {\n\t/**\n\t * Session affinity key for prefix-cache optimization.\n\t * Routes requests with the same key to the same backend replica.\n\t */\n\tsessionAffinity?: string;\n};\n\n// ---------------------------------------------------------------------------\n// Config detection helpers\n// ---------------------------------------------------------------------------\n\n/** Returns true if this is a plain Workers AI binding config (`{ binding: env.AI }`) */\nexport function isDirectBindingConfig(\n\tconfig: WorkersAiAdapterConfig,\n): config is WorkersAiDirectBindingConfig {\n\t// env.AI has a .gateway() method; env.AI.gateway(id) does not.\n\t// This distinguishes direct bindings from AI Gateway bindings.\n\treturn (\n\t\t\"binding\" in config &&\n\t\ttypeof (config.binding as unknown as Record<string, unknown>).gateway === \"function\"\n\t);\n}\n\n/** Returns true if this is a plain Workers AI REST config (accountId + apiKey, no gatewayId) */\nexport function isDirectCredentialsConfig(\n\tconfig: WorkersAiAdapterConfig,\n): config is WorkersAiDirectCredentialsConfig {\n\treturn \"accountId\" in config && \"apiKey\" in config && !(\"gatewayId\" in config);\n}\n\n/** Returns true if this is an AI Gateway config (has gateway binding or `gatewayId`) */\nexport function isGatewayConfig(config: WorkersAiAdapterConfig): config is AiGatewayAdapterConfig {\n\tif (\"gatewayId\" in config) return true;\n\t// Has `binding` but NOT a direct Workers AI binding (no .gateway method)\n\treturn \"binding\" in config && !isDirectBindingConfig(config);\n}\n\n// ---------------------------------------------------------------------------\n// Config validation\n// ---------------------------------------------------------------------------\n\n/**\n * Validates that a WorkersAiAdapterConfig contains a valid configuration.\n * Throws an error if neither a binding, credentials (accountId + apiKey),\n * nor a gateway configuration is provided.\n */\nexport function validateWorkersAiConfig(config: WorkersAiAdapterConfig): void {\n\tif (\n\t\t!isDirectBindingConfig(config) &&\n\t\t!isDirectCredentialsConfig(config) &&\n\t\t!isGatewayConfig(config)\n\t) {\n\t\tthrow new Error(\n\t\t\t\"Invalid Workers AI configuration: you must provide either a binding (e.g. { binding: env.AI }), \" +\n\t\t\t\t\"credentials ({ accountId, apiKey }), or a gateway configuration ({ binding: env.AI.gateway(id) } \" +\n\t\t\t\t\"or { accountId, gatewayId }).\",\n\t\t);\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// createGatewayFetch -- for routing through AI Gateway\n// ---------------------------------------------------------------------------\n\nexport function createGatewayFetch(\n\tprovider: string,\n\tconfig: AiGatewayAdapterConfig,\n\theaders: Record<string, string> = {},\n): typeof fetch {\n\treturn (input, init) => {\n\t\tlet query: Record<string, unknown> = {};\n\n\t\tconst url =\n\t\t\ttypeof input === \"string\" ? input : input instanceof URL ? input.href : input.url;\n\t\tconst urlObj = new URL(url);\n\n\t\t// Extract endpoint path (remove /v1/ prefix if present)\n\t\tconst endpoint = urlObj.pathname.replace(/^\\/v1\\//, \"\").replace(/^\\//, \"\") + urlObj.search;\n\n\t\tif (init?.body) {\n\t\t\ttry {\n\t\t\t\tquery = JSON.parse(init.body as string);\n\t\t\t} catch {\n\t\t\t\tquery = { _raw: init.body };\n\t\t\t}\n\t\t}\n\n\t\tconst cacheHeaders: Record<string, string> = {};\n\n\t\tif (\"skipCache\" in config && config.skipCache) {\n\t\t\tcacheHeaders[\"cf-aig-skip-cache\"] = \"true\";\n\t\t}\n\n\t\tif (typeof config.cacheTtl === \"number\") {\n\t\t\tcacheHeaders[\"cf-aig-cache-ttl\"] = String(config.cacheTtl);\n\t\t}\n\n\t\tif (typeof config.customCacheKey === \"string\") {\n\t\t\tcacheHeaders[\"cf-aig-cache-key\"] = config.customCacheKey;\n\t\t}\n\n\t\tif (typeof config.metadata === \"object\") {\n\t\t\tcacheHeaders[\"cf-aig-metadata\"] = JSON.stringify(config.metadata);\n\t\t}\n\n\t\tconst request: {\n\t\t\tprovider: string;\n\t\t\tendpoint: string;\n\t\t\theaders: Record<string, string>;\n\t\t\tquery: Record<string, unknown>;\n\t\t} = {\n\t\t\tprovider,\n\t\t\tendpoint,\n\t\t\theaders: {\n\t\t\t\t...(init?.headers as Record<string, string> | undefined),\n\t\t\t\t...headers,\n\t\t\t\t...cacheHeaders,\n\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t},\n\t\t\tquery,\n\t\t};\n\n\t\tif (provider === \"workers-ai\") {\n\t\t\tif (!request.endpoint.startsWith(\"run/\")) {\n\t\t\t\trequest.endpoint = `run/${query.model}`;\n\t\t\t}\n\t\t\tdelete query.model;\n\t\t\tdelete query.instructions;\n\t\t}\n\n\t\tif (config.apiKey) {\n\t\t\trequest.headers[\"authorization\"] = `Bearer ${config.apiKey}`;\n\t\t}\n\n\t\tif (\"binding\" in config) {\n\t\t\treturn (\n\t\t\t\tconfig.binding as {\n\t\t\t\t\trun(req: unknown, opts?: { signal?: AbortSignal }): Promise<Response>;\n\t\t\t\t}\n\t\t\t).run(request, { signal: init?.signal ?? undefined });\n\t\t}\n\n\t\treturn fetch(\n\t\t\t`https://gateway.ai.cloudflare.com/v1/${config.accountId}/${config.gatewayId}`,\n\t\t\t{\n\t\t\t\t...init,\n\t\t\t\theaders: {\n\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\t...headers,\n\t\t\t\t\t...cacheHeaders,\n\t\t\t\t\t...(config.cfApiKey\n\t\t\t\t\t\t? { \"cf-aig-authorization\": `Bearer ${config.cfApiKey}` }\n\t\t\t\t\t\t: {}),\n\t\t\t\t},\n\t\t\t\tbody: JSON.stringify(request),\n\t\t\t},\n\t\t);\n\t};\n}\n\n// ---------------------------------------------------------------------------\n// createWorkersAiBindingFetch -- shim that makes env.AI look like an OpenAI endpoint\n// ---------------------------------------------------------------------------\n\n/**\n * Normalize messages before passing to Workers AI binding.\n *\n * The binding has strict schema validation that may differ from the OpenAI API:\n * - `content` must not be null\n *\n * Content arrays (with image_url parts) are passed through as-is since the\n * Workers AI binding accepts them at runtime for vision-capable models.\n */\nfunction normalizeMessagesForBinding(\n\tmessages: Record<string, unknown>[],\n): Record<string, unknown>[] {\n\treturn messages.map((msg) => {\n\t\tconst normalized = { ...msg };\n\n\t\t// content: null → content: \"\"\n\t\tif (normalized.content === null || normalized.content === undefined) {\n\t\t\tnormalized.content = \"\";\n\t\t}\n\n\t\treturn normalized;\n\t});\n}\n\n/**\n * Creates a fetch function that intercepts OpenAI SDK requests and translates them\n * to Workers AI binding calls (env.AI.run). This allows the WorkersAiTextAdapter\n * to use the OpenAI SDK against a plain Workers AI binding.\n *\n * NOTE: The `input` URL parameter is intentionally ignored. The model name and all\n * request parameters are extracted from the JSON body, matching Workers AI's\n * `binding.run(model, inputs)` calling convention.\n */\nexport function createWorkersAiBindingFetch(\n\tbinding: WorkersAiBinding,\n\toptions?: { extraHeaders?: Record<string, string> },\n): typeof fetch {\n\treturn async (_input, init) => {\n\t\tif (!init?.body) {\n\t\t\treturn new Response(\"No body\", { status: 400 });\n\t\t}\n\n\t\tlet body: Record<string, unknown>;\n\t\ttry {\n\t\t\tbody = JSON.parse(init.body as string);\n\t\t} catch {\n\t\t\treturn new Response(\"Invalid JSON body\", { status: 400 });\n\t\t}\n\n\t\tconst model = body.model as string;\n\t\tconst stream = body.stream as boolean | undefined;\n\n\t\t// Build Workers AI inputs from OpenAI format\n\t\tconst inputs: Record<string, unknown> = {};\n\t\tif (body.messages) {\n\t\t\tinputs.messages = normalizeMessagesForBinding(\n\t\t\t\tbody.messages as Record<string, unknown>[],\n\t\t\t);\n\t\t}\n\t\tif (body.tools) inputs.tools = body.tools;\n\t\tif (typeof body.temperature === \"number\") inputs.temperature = body.temperature;\n\t\tif (typeof body.max_tokens === \"number\") inputs.max_tokens = body.max_tokens;\n\t\tif (body.response_format) inputs.response_format = body.response_format;\n\t\tif (stream) inputs.stream = true;\n\n\t\t// Workers AI-specific reasoning controls. These belong on the INPUTS object\n\t\t// passed to binding.run(model, inputs), not on the options (3rd) arg.\n\t\t// See https://github.com/cloudflare/ai/issues/503.\n\t\t//\n\t\t// `reasoning_effort: null` is a valid value (disables reasoning on models\n\t\t// that support it), so we check `!== undefined` rather than truthiness.\n\t\tif (body.reasoning_effort !== undefined) {\n\t\t\tinputs.reasoning_effort = body.reasoning_effort;\n\t\t}\n\t\tif (body.chat_template_kwargs !== undefined) {\n\t\t\tinputs.chat_template_kwargs = body.chat_template_kwargs;\n\t\t}\n\n\t\tconst runOptions: Record<string, unknown> = {};\n\t\tif (options?.extraHeaders) runOptions.extraHeaders = options.extraHeaders;\n\t\tif (init?.signal) runOptions.signal = init.signal;\n\n\t\tconst result = await binding.run(\n\t\t\tmodel,\n\t\t\tinputs,\n\t\t\tObject.keys(runOptions).length > 0 ? runOptions : undefined,\n\t\t);\n\n\t\tif (stream && result instanceof ReadableStream) {\n\t\t\t// Workers AI returns an SSE stream with `data: {\"response\":\"chunk\"}` format.\n\t\t\t// Transform it to OpenAI-compatible SSE format.\n\t\t\tconst transformed = transformWorkersAiStream(\n\t\t\t\tresult as ReadableStream<Uint8Array>,\n\t\t\t\tmodel,\n\t\t\t);\n\t\t\treturn new Response(transformed, {\n\t\t\t\theaders: {\n\t\t\t\t\t\"Content-Type\": \"text/event-stream\",\n\t\t\t\t\t\"Cache-Control\": \"no-cache\",\n\t\t\t\t},\n\t\t\t});\n\t\t}\n\n\t\t// Graceful degradation: some models return a complete (non-streaming)\n\t\t// response even when `stream: true` is requested. Fall through to the\n\t\t// non-streaming wrapper which produces a valid OpenAI Chat Completion\n\t\t// response that the SDK can consume.\n\n\t\t// Non-streaming: Workers AI returns { response: \"text\", tool_calls?: [...] }\n\t\t// Wrap into OpenAI Chat Completion format.\n\t\tconst responseObj =\n\t\t\ttypeof result === \"object\" && result !== null\n\t\t\t\t? (result as Record<string, unknown>)\n\t\t\t\t: { response: String(result) };\n\n\t\tconst responseText =\n\t\t\ttypeof responseObj.response === \"string\"\n\t\t\t\t? responseObj.response\n\t\t\t\t: typeof responseObj.response === \"object\" && responseObj.response !== null\n\t\t\t\t\t? JSON.stringify(responseObj.response)\n\t\t\t\t\t: \"\";\n\n\t\tconst message: Record<string, unknown> = {\n\t\t\trole: \"assistant\",\n\t\t\tcontent: responseText,\n\t\t};\n\t\tlet finishReason = \"stop\";\n\n\t\t// Handle tool calls if present in Workers AI response\n\t\tif (Array.isArray(responseObj.tool_calls) && responseObj.tool_calls.length > 0) {\n\t\t\tfinishReason = \"tool_calls\";\n\t\t\tmessage.tool_calls = responseObj.tool_calls.map(\n\t\t\t\t(tc: {\n\t\t\t\t\tid?: string;\n\t\t\t\t\tname?: string;\n\t\t\t\t\targuments: unknown;\n\t\t\t\t\tfunction?: { name: string; arguments?: unknown };\n\t\t\t\t}) => ({\n\t\t\t\t\tid: tc.id || crypto.randomUUID(),\n\t\t\t\t\ttype: \"function\",\n\t\t\t\t\tfunction: {\n\t\t\t\t\t\tname: tc.function?.name || tc.name || \"\",\n\t\t\t\t\t\targuments:\n\t\t\t\t\t\t\ttypeof (tc.function?.arguments ?? tc.arguments) === \"string\"\n\t\t\t\t\t\t\t\t? ((tc.function?.arguments ?? tc.arguments) as string)\n\t\t\t\t\t\t\t\t: JSON.stringify(tc.function?.arguments ?? tc.arguments ?? {}),\n\t\t\t\t\t},\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\n\t\tconst openAiResponse = {\n\t\t\tid: `workers-ai-${crypto.randomUUID()}`,\n\t\t\tobject: \"chat.completion\",\n\t\t\tcreated: Math.floor(Date.now() / 1000),\n\t\t\tmodel,\n\t\t\tchoices: [{ index: 0, message, finish_reason: finishReason }],\n\t\t};\n\n\t\treturn new Response(JSON.stringify(openAiResponse), {\n\t\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\t});\n\t};\n}\n\n// ---------------------------------------------------------------------------\n// Stream transformer: Workers AI SSE -> OpenAI-compatible SSE\n// Uses TransformStream for proper backpressure.\n// ---------------------------------------------------------------------------\n\n/**\n * Transforms a Workers AI SSE stream (data: {\"response\":\"chunk\"}) into\n * an OpenAI-compatible SSE stream (data: {\"choices\":[{\"delta\":{\"content\":\"chunk\"}}]}).\n *\n * Workers AI binding streams tool calls in an OpenAI-like nested format:\n * { tool_calls: [{ id, type, index, function: { name, arguments } }] }\n * Arguments are streamed incrementally across multiple SSE chunks, so the\n * transformer must forward them as incremental deltas rather than a single blob.\n */\nfunction transformWorkersAiStream(\n\tsource: ReadableStream<Uint8Array>,\n\tmodel: string,\n): ReadableStream<Uint8Array> {\n\tconst decoder = new TextDecoder();\n\tconst encoder = new TextEncoder();\n\t// Generate a stable ID and timestamp for the entire stream, matching OpenAI's\n\t// convention where all chunks in a single response share the same id/created.\n\tconst streamId = `workers-ai-${crypto.randomUUID()}`;\n\tconst created = Math.floor(Date.now() / 1000);\n\tlet buffer = \"\";\n\tlet hasToolCalls = false;\n\t// When true, the source stream is already in OpenAI format (some models\n\t// like Qwen3, Kimi K2.5 stream OpenAI-compatible SSE through the binding).\n\t// In that case, flush() should only emit [DONE] and skip the finish chunk.\n\tlet isOpenAiFormat = false;\n\t// Track tool call state per index: store the generated/assigned ID so that\n\t// subsequent argument deltas use the same ID (matching the working streaming.ts pattern).\n\tconst toolCallState = new Map<number, { id: string; name: string }>();\n\n\treturn source.pipeThrough(\n\t\tnew TransformStream<Uint8Array, Uint8Array>({\n\t\t\ttransform(chunk, controller) {\n\t\t\t\tbuffer += decoder.decode(chunk, { stream: true });\n\t\t\t\tconst lines = buffer.split(\"\\n\");\n\t\t\t\tbuffer = lines.pop() || \"\";\n\n\t\t\t\tfor (const line of lines) {\n\t\t\t\t\tconst trimmed = line.trim();\n\t\t\t\t\tif (!trimmed || !trimmed.startsWith(\"data: \")) continue;\n\t\t\t\t\tconst data = trimmed.slice(6);\n\n\t\t\t\t\t// Swallow source [DONE]; we emit our own in flush()\n\t\t\t\t\tif (data === \"[DONE]\") continue;\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst parsed = JSON.parse(data);\n\n\t\t\t\t\t\t// Some models (Qwen3, Kimi K2.5) return OpenAI-compatible format\n\t\t\t\t\t\t// directly through the binding, with `choices[].delta.content` and\n\t\t\t\t\t\t// optional `reasoning_content`. Detect this and pass through as-is.\n\t\t\t\t\t\tif (parsed.choices !== undefined) {\n\t\t\t\t\t\t\t// Already OpenAI format — pass through but ensure each tool call\n\t\t\t\t\t\t\t// index gets a unique, stable ID across all chunks.\n\t\t\t\t\t\t\tisOpenAiFormat = true;\n\t\t\t\t\t\t\tconst choice = parsed.choices?.[0];\n\t\t\t\t\t\t\tif (choice?.delta?.tool_calls) {\n\t\t\t\t\t\t\t\thasToolCalls = true;\n\t\t\t\t\t\t\t\tfor (const tc of choice.delta.tool_calls) {\n\t\t\t\t\t\t\t\t\tconst tcIndex = tc.index ?? 0;\n\t\t\t\t\t\t\t\t\tif (!toolCallState.has(tcIndex)) {\n\t\t\t\t\t\t\t\t\t\t// First chunk for this index — generate/store unique ID\n\t\t\t\t\t\t\t\t\t\tconst id = tc.id || `call${streamId}${tcIndex}`;\n\t\t\t\t\t\t\t\t\t\ttoolCallState.set(tcIndex, {\n\t\t\t\t\t\t\t\t\t\t\tid,\n\t\t\t\t\t\t\t\t\t\t\tname: tc.function?.name || \"\",\n\t\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t\t\ttc.id = id;\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\t// Subsequent chunk — reuse stored ID, remove id from delta\n\t\t\t\t\t\t\t\t\t\t// (OpenAI format only sends id in first chunk)\n\t\t\t\t\t\t\t\t\t\tdelete tc.id;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (choice?.finish_reason === \"tool_calls\") {\n\t\t\t\t\t\t\t\thasToolCalls = true;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tcontroller.enqueue(\n\t\t\t\t\t\t\t\tencoder.encode(`data: ${JSON.stringify(parsed)}\\n\\n`),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// --- Workers AI native format handling below ---\n\n\t\t\t\t\t\t// Text content\n\t\t\t\t\t\tif (parsed.response != null && parsed.response !== \"\") {\n\t\t\t\t\t\t\tconst openAiChunk = {\n\t\t\t\t\t\t\t\tid: streamId,\n\t\t\t\t\t\t\t\tobject: \"chat.completion.chunk\",\n\t\t\t\t\t\t\t\tcreated,\n\t\t\t\t\t\t\t\tmodel,\n\t\t\t\t\t\t\t\tchoices: [\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tindex: 0,\n\t\t\t\t\t\t\t\t\t\tdelta: { content: parsed.response },\n\t\t\t\t\t\t\t\t\t\tfinish_reason: null,\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\tcontroller.enqueue(\n\t\t\t\t\t\t\t\tencoder.encode(`data: ${JSON.stringify(openAiChunk)}\\n\\n`),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Tool calls — Workers AI binding streams these incrementally:\n\t\t\t\t\t\t// Chunk A: { id, type, index, function: { name } } — start\n\t\t\t\t\t\t// Chunk B: { index, function: { arguments: \"partial...\" } } — args delta\n\t\t\t\t\t\t// Chunk C: { index, function: { arguments: \"rest...\" } } — args delta\n\t\t\t\t\t\t// Chunk D: { id: null, type: null, index, function: { name: null, arguments: \"\" } } — finalize (skip)\n\t\t\t\t\t\tif (Array.isArray(parsed.tool_calls) && parsed.tool_calls.length > 0) {\n\t\t\t\t\t\t\tfor (const tc of parsed.tool_calls) {\n\t\t\t\t\t\t\t\tconst tcIndex = tc.index ?? 0;\n\n\t\t\t\t\t\t\t\t// Resolve name and arguments from either nested or flat format\n\t\t\t\t\t\t\t\tconst tcName = tc.function?.name ?? tc.name ?? null;\n\t\t\t\t\t\t\t\tconst tcArgs = tc.function?.arguments ?? tc.arguments ?? null;\n\t\t\t\t\t\t\t\tconst tcId = tc.id ?? null;\n\n\t\t\t\t\t\t\t\t// Skip finalization chunks where everything is null/empty\n\t\t\t\t\t\t\t\tif (!tcId && !tcName && (!tcArgs || tcArgs === \"\")) continue;\n\n\t\t\t\t\t\t\t\thasToolCalls = true;\n\n\t\t\t\t\t\t\t\t// Build the OpenAI-compatible tool_calls delta\n\t\t\t\t\t\t\t\tconst toolCallDelta: Record<string, unknown> = {\n\t\t\t\t\t\t\t\t\tindex: tcIndex,\n\t\t\t\t\t\t\t\t};\n\n\t\t\t\t\t\t\t\tif (!toolCallState.has(tcIndex)) {\n\t\t\t\t\t\t\t\t\t// First chunk for this tool call index — emit id, type, name.\n\t\t\t\t\t\t\t\t\tconst id = tcId || `call${streamId}${tcIndex}`;\n\t\t\t\t\t\t\t\t\ttoolCallState.set(tcIndex, { id, name: tcName || \"\" });\n\t\t\t\t\t\t\t\t\ttoolCallDelta.id = id;\n\t\t\t\t\t\t\t\t\ttoolCallDelta.type = \"function\";\n\t\t\t\t\t\t\t\t\ttoolCallDelta.function = {\n\t\t\t\t\t\t\t\t\t\tname: tcName || \"\",\n\t\t\t\t\t\t\t\t\t\t// Include arguments if they arrive in the same chunk\n\t\t\t\t\t\t\t\t\t\targuments:\n\t\t\t\t\t\t\t\t\t\t\ttcArgs != null\n\t\t\t\t\t\t\t\t\t\t\t\t? typeof tcArgs === \"string\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t? tcArgs\n\t\t\t\t\t\t\t\t\t\t\t\t\t: JSON.stringify(tcArgs)\n\t\t\t\t\t\t\t\t\t\t\t\t: \"\",\n\t\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t// Subsequent chunks — only include arguments delta\n\t\t\t\t\t\t\t\t\tif (tcArgs != null && tcArgs !== \"\") {\n\t\t\t\t\t\t\t\t\t\ttoolCallDelta.function = {\n\t\t\t\t\t\t\t\t\t\t\targuments:\n\t\t\t\t\t\t\t\t\t\t\t\ttypeof tcArgs === \"string\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t? tcArgs\n\t\t\t\t\t\t\t\t\t\t\t\t\t: JSON.stringify(tcArgs),\n\t\t\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\tcontinue; // Nothing useful to forward\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tconst toolChunk = {\n\t\t\t\t\t\t\t\t\tid: streamId,\n\t\t\t\t\t\t\t\t\tobject: \"chat.completion.chunk\",\n\t\t\t\t\t\t\t\t\tcreated,\n\t\t\t\t\t\t\t\t\tmodel,\n\t\t\t\t\t\t\t\t\tchoices: [\n\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\tindex: 0,\n\t\t\t\t\t\t\t\t\t\t\tdelta: { tool_calls: [toolCallDelta] },\n\t\t\t\t\t\t\t\t\t\t\tfinish_reason: null,\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\tcontroller.enqueue(\n\t\t\t\t\t\t\t\t\tencoder.encode(`data: ${JSON.stringify(toolChunk)}\\n\\n`),\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (e) {\n\t\t\t\t\t\t// Log malformed SSE events for debugging; don't break the stream.\n\t\t\t\t\t\tconsole.warn(\"[tanstack-ai] failed to parse SSE event:\", data, e);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t},\n\t\t\tflush(controller) {\n\t\t\t\tif (!isOpenAiFormat) {\n\t\t\t\t\t// Workers AI native format: emit a finish chunk with stop/tool_calls\n\t\t\t\t\tconst finalChunk = {\n\t\t\t\t\t\tid: streamId,\n\t\t\t\t\t\tobject: \"chat.completion.chunk\",\n\t\t\t\t\t\tcreated,\n\t\t\t\t\t\tmodel,\n\t\t\t\t\t\tchoices: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tindex: 0,\n\t\t\t\t\t\t\t\tdelta: {},\n\t\t\t\t\t\t\t\tfinish_reason: hasToolCalls ? \"tool_calls\" : \"stop\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t\t\t};\n\t\t\t\t\tcontroller.enqueue(encoder.encode(`data: ${JSON.stringify(finalChunk)}\\n\\n`));\n\t\t\t\t}\n\t\t\t\t// OpenAI format already includes its own finish_reason in the stream.\n\t\t\t\t// Either way, emit a [DONE] sentinel.\n\t\t\t\tcontroller.enqueue(encoder.encode(\"data: [DONE]\\n\\n\"));\n\t\t\t},\n\t\t}),\n\t);\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- //#region \0@oxc-project+runtime@0.122.0/helpers/typeof.js
1
+ //#region \0@oxc-project+runtime@0.135.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.122.0/helpers/toPrimitive.js
11
+ //#region \0@oxc-project+runtime@0.135.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.122.0/helpers/toPropertyKey.js
23
+ //#region \0@oxc-project+runtime@0.135.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.122.0/helpers/defineProperty.js
29
+ //#region \0@oxc-project+runtime@0.135.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,
@@ -1,4 +1,4 @@
1
- //#region \0@oxc-project+runtime@0.122.0/helpers/typeof.js
1
+ //#region \0@oxc-project+runtime@0.135.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.122.0/helpers/toPrimitive.js
11
+ //#region \0@oxc-project+runtime@0.135.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.122.0/helpers/toPropertyKey.js
23
+ //#region \0@oxc-project+runtime@0.135.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.122.0/helpers/defineProperty.js
29
+ //#region \0@oxc-project+runtime@0.135.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,
package/dist/index.cjs CHANGED
@@ -1,10 +1,10 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_workers_ai = require("./workers-ai-BOZ5iyhY.cjs");
3
2
  const require_adapters_anthropic = require("./adapters/anthropic.cjs");
4
3
  const require_adapters_gemini = require("./adapters/gemini.cjs");
5
4
  const require_adapters_grok = require("./adapters/grok.cjs");
6
5
  const require_adapters_openai = require("./adapters/openai.cjs");
7
6
  const require_adapters_openrouter = require("./adapters/openrouter.cjs");
7
+ const require_adapters_workers_ai = require("./adapters/workers-ai.cjs");
8
8
  const require_adapters_workers_ai_image = require("./adapters/workers-ai-image.cjs");
9
9
  const require_adapters_workers_ai_transcription = require("./adapters/workers-ai-transcription.cjs");
10
10
  const require_adapters_workers_ai_tts = require("./adapters/workers-ai-tts.cjs");
@@ -17,7 +17,6 @@ exports.ANTHROPIC_MODELS = _tanstack_ai_anthropic.ANTHROPIC_MODELS;
17
17
  exports.GROK_CHAT_MODELS = _tanstack_ai_grok.GROK_CHAT_MODELS;
18
18
  exports.GROK_IMAGE_MODELS = _tanstack_ai_grok.GROK_IMAGE_MODELS;
19
19
  exports.GeminiImageModels = _tanstack_ai_gemini.GeminiImageModels;
20
- exports.GeminiSummarizeModels = _tanstack_ai_gemini.GeminiSummarizeModels;
21
20
  exports.GeminiTTSModels = _tanstack_ai_gemini.GeminiTTSModels;
22
21
  exports.GeminiTextModels = _tanstack_ai_gemini.GeminiTextModels;
23
22
  exports.OPENAI_CHAT_MODELS = _tanstack_ai_openai.OPENAI_CHAT_MODELS;
@@ -43,7 +42,7 @@ exports.createOpenAiVideo = require_adapters_openai.createOpenAiVideo;
43
42
  exports.createOpenRouterChat = require_adapters_openrouter.createOpenRouterChat;
44
43
  exports.createOpenRouterImage = require_adapters_openrouter.createOpenRouterImage;
45
44
  exports.createOpenRouterSummarize = require_adapters_openrouter.createOpenRouterSummarize;
46
- exports.createWorkersAiChat = require_workers_ai.createWorkersAiChat;
45
+ exports.createWorkersAiChat = require_adapters_workers_ai.createWorkersAiChat;
47
46
  exports.createWorkersAiImage = require_adapters_workers_ai_image.createWorkersAiImage;
48
47
  exports.createWorkersAiSummarize = require_adapters_workers_ai_summarize.createWorkersAiSummarize;
49
48
  exports.createWorkersAiTranscription = require_adapters_workers_ai_transcription.createWorkersAiTranscription;
package/dist/index.d.cts CHANGED
@@ -1,6 +1,6 @@
1
- import { i as WorkersAiAdapterConfig, t as AiGatewayAdapterConfig } from "./create-fetcher-vAQ8WW-p.cjs";
1
+ import { i as WorkersAiAdapterConfig, t as AiGatewayAdapterConfig } from "./create-fetcher-Bp5yCNaO.cjs";
2
2
  import { ANTHROPIC_MODELS, AnthropicChatModel, AnthropicGatewayConfig, createAnthropicChat, createAnthropicSummarize } from "./adapters/anthropic.cjs";
3
- import { GeminiChatModel, GeminiGatewayConfig, GeminiImageModel, GeminiImageModels, GeminiSummarizeModel, GeminiSummarizeModels, GeminiTTSModel, GeminiTTSModels, GeminiTextModel, GeminiTextModels, createGeminiChat, createGeminiImage, createGeminiSummarize, createGeminiTts } from "./adapters/gemini.cjs";
3
+ import { GeminiChatModel, GeminiGatewayConfig, GeminiImageModel, GeminiImageModels, GeminiSummarizeModel, GeminiTTSModel, GeminiTTSModels, GeminiTextModel, GeminiTextModels, createGeminiChat, createGeminiImage, createGeminiSummarize, createGeminiTts } from "./adapters/gemini.cjs";
4
4
  import { GROK_CHAT_MODELS, GROK_IMAGE_MODELS, GrokChatModel, GrokGatewayConfig, GrokImageModel, GrokSummarizeModel, createGrokChat, createGrokImage, createGrokSummarize } from "./adapters/grok.cjs";
5
5
  import { OPENAI_CHAT_MODELS, OPENAI_IMAGE_MODELS, OPENAI_TRANSCRIPTION_MODELS, OPENAI_TTS_MODELS, OPENAI_VIDEO_MODELS, OpenAIChatModel, OpenAIImageModel, OpenAITTSModel, OpenAITranscriptionModel, OpenAIVideoModel, OpenAiGatewayConfig, createOpenAiChat, createOpenAiImage, createOpenAiSummarize, createOpenAiTranscription, createOpenAiTts, createOpenAiVideo } from "./adapters/openai.cjs";
6
6
  import { OpenRouterChatModel, OpenRouterGatewayConfig, OpenRouterImageModel, OpenRouterSummarizeModel, createOpenRouterChat, createOpenRouterImage, createOpenRouterSummarize } from "./adapters/openrouter.cjs";
@@ -9,4 +9,4 @@ import { WorkersAiSummarizeModel, createWorkersAiSummarize } from "./adapters/wo
9
9
  import { WorkersAiTranscriptionModel, createWorkersAiTranscription } from "./adapters/workers-ai-transcription.cjs";
10
10
  import { WorkersAiTTSModel, createWorkersAiTts } from "./adapters/workers-ai-tts.cjs";
11
11
  import { WorkersAiTextModel, WorkersAiTextModelOptions, createWorkersAiChat } from "./adapters/workers-ai.cjs";
12
- export { ANTHROPIC_MODELS, type AiGatewayAdapterConfig, type AnthropicChatModel, type AnthropicGatewayConfig, GROK_CHAT_MODELS, GROK_IMAGE_MODELS, type GeminiChatModel, type GeminiGatewayConfig, type GeminiImageModel, GeminiImageModels, type GeminiSummarizeModel, GeminiSummarizeModels, type GeminiTTSModel, GeminiTTSModels, type GeminiTextModel, GeminiTextModels, type GrokChatModel, type GrokGatewayConfig, type GrokImageModel, type GrokSummarizeModel, OPENAI_CHAT_MODELS, OPENAI_IMAGE_MODELS, OPENAI_TRANSCRIPTION_MODELS, OPENAI_TTS_MODELS, OPENAI_VIDEO_MODELS, type OpenAIChatModel, type OpenAIImageModel, type OpenAITTSModel, type OpenAITranscriptionModel, type OpenAIVideoModel, type OpenAiGatewayConfig, type OpenRouterChatModel, type OpenRouterGatewayConfig, type OpenRouterImageModel, type OpenRouterSummarizeModel, type WorkersAiAdapterConfig, type WorkersAiImageModel, type WorkersAiSummarizeModel, type WorkersAiTTSModel, type WorkersAiTextModel, type WorkersAiTextModelOptions, type WorkersAiTranscriptionModel, createAnthropicChat, createAnthropicSummarize, createGeminiChat, createGeminiImage, createGeminiSummarize, createGeminiTts, createGrokChat, createGrokImage, createGrokSummarize, createOpenAiChat, createOpenAiImage, createOpenAiSummarize, createOpenAiTranscription, createOpenAiTts, createOpenAiVideo, createOpenRouterChat, createOpenRouterImage, createOpenRouterSummarize, createWorkersAiChat, createWorkersAiImage, createWorkersAiSummarize, createWorkersAiTranscription, createWorkersAiTts };
12
+ export { ANTHROPIC_MODELS, type AiGatewayAdapterConfig, type AnthropicChatModel, type AnthropicGatewayConfig, GROK_CHAT_MODELS, GROK_IMAGE_MODELS, type GeminiChatModel, type GeminiGatewayConfig, type GeminiImageModel, GeminiImageModels, type GeminiSummarizeModel, type GeminiTTSModel, GeminiTTSModels, type GeminiTextModel, GeminiTextModels, type GrokChatModel, type GrokGatewayConfig, type GrokImageModel, type GrokSummarizeModel, OPENAI_CHAT_MODELS, OPENAI_IMAGE_MODELS, OPENAI_TRANSCRIPTION_MODELS, OPENAI_TTS_MODELS, OPENAI_VIDEO_MODELS, type OpenAIChatModel, type OpenAIImageModel, type OpenAITTSModel, type OpenAITranscriptionModel, type OpenAIVideoModel, type OpenAiGatewayConfig, type OpenRouterChatModel, type OpenRouterGatewayConfig, type OpenRouterImageModel, type OpenRouterSummarizeModel, type WorkersAiAdapterConfig, type WorkersAiImageModel, type WorkersAiSummarizeModel, type WorkersAiTTSModel, type WorkersAiTextModel, type WorkersAiTextModelOptions, type WorkersAiTranscriptionModel, createAnthropicChat, createAnthropicSummarize, createGeminiChat, createGeminiImage, createGeminiSummarize, createGeminiTts, createGrokChat, createGrokImage, createGrokSummarize, createOpenAiChat, createOpenAiImage, createOpenAiSummarize, createOpenAiTranscription, createOpenAiTts, createOpenAiVideo, createOpenRouterChat, createOpenRouterImage, createOpenRouterSummarize, createWorkersAiChat, createWorkersAiImage, createWorkersAiSummarize, createWorkersAiTranscription, createWorkersAiTts };
package/dist/index.d.mts CHANGED
@@ -1,6 +1,6 @@
1
- import { i as WorkersAiAdapterConfig, t as AiGatewayAdapterConfig } from "./create-fetcher-6p6heb85.mjs";
1
+ import { i as WorkersAiAdapterConfig, t as AiGatewayAdapterConfig } from "./create-fetcher-Bp5yCNaO.mjs";
2
2
  import { ANTHROPIC_MODELS, AnthropicChatModel, AnthropicGatewayConfig, createAnthropicChat, createAnthropicSummarize } from "./adapters/anthropic.mjs";
3
- import { GeminiChatModel, GeminiGatewayConfig, GeminiImageModel, GeminiImageModels, GeminiSummarizeModel, GeminiSummarizeModels, GeminiTTSModel, GeminiTTSModels, GeminiTextModel, GeminiTextModels, createGeminiChat, createGeminiImage, createGeminiSummarize, createGeminiTts } from "./adapters/gemini.mjs";
3
+ import { GeminiChatModel, GeminiGatewayConfig, GeminiImageModel, GeminiImageModels, GeminiSummarizeModel, GeminiTTSModel, GeminiTTSModels, GeminiTextModel, GeminiTextModels, createGeminiChat, createGeminiImage, createGeminiSummarize, createGeminiTts } from "./adapters/gemini.mjs";
4
4
  import { GROK_CHAT_MODELS, GROK_IMAGE_MODELS, GrokChatModel, GrokGatewayConfig, GrokImageModel, GrokSummarizeModel, createGrokChat, createGrokImage, createGrokSummarize } from "./adapters/grok.mjs";
5
5
  import { OPENAI_CHAT_MODELS, OPENAI_IMAGE_MODELS, OPENAI_TRANSCRIPTION_MODELS, OPENAI_TTS_MODELS, OPENAI_VIDEO_MODELS, OpenAIChatModel, OpenAIImageModel, OpenAITTSModel, OpenAITranscriptionModel, OpenAIVideoModel, OpenAiGatewayConfig, createOpenAiChat, createOpenAiImage, createOpenAiSummarize, createOpenAiTranscription, createOpenAiTts, createOpenAiVideo } from "./adapters/openai.mjs";
6
6
  import { OpenRouterChatModel, OpenRouterGatewayConfig, OpenRouterImageModel, OpenRouterSummarizeModel, createOpenRouterChat, createOpenRouterImage, createOpenRouterSummarize } from "./adapters/openrouter.mjs";
@@ -9,4 +9,4 @@ import { WorkersAiSummarizeModel, createWorkersAiSummarize } from "./adapters/wo
9
9
  import { WorkersAiTranscriptionModel, createWorkersAiTranscription } from "./adapters/workers-ai-transcription.mjs";
10
10
  import { WorkersAiTTSModel, createWorkersAiTts } from "./adapters/workers-ai-tts.mjs";
11
11
  import { WorkersAiTextModel, WorkersAiTextModelOptions, createWorkersAiChat } from "./adapters/workers-ai.mjs";
12
- export { ANTHROPIC_MODELS, type AiGatewayAdapterConfig, type AnthropicChatModel, type AnthropicGatewayConfig, GROK_CHAT_MODELS, GROK_IMAGE_MODELS, type GeminiChatModel, type GeminiGatewayConfig, type GeminiImageModel, GeminiImageModels, type GeminiSummarizeModel, GeminiSummarizeModels, type GeminiTTSModel, GeminiTTSModels, type GeminiTextModel, GeminiTextModels, type GrokChatModel, type GrokGatewayConfig, type GrokImageModel, type GrokSummarizeModel, OPENAI_CHAT_MODELS, OPENAI_IMAGE_MODELS, OPENAI_TRANSCRIPTION_MODELS, OPENAI_TTS_MODELS, OPENAI_VIDEO_MODELS, type OpenAIChatModel, type OpenAIImageModel, type OpenAITTSModel, type OpenAITranscriptionModel, type OpenAIVideoModel, type OpenAiGatewayConfig, type OpenRouterChatModel, type OpenRouterGatewayConfig, type OpenRouterImageModel, type OpenRouterSummarizeModel, type WorkersAiAdapterConfig, type WorkersAiImageModel, type WorkersAiSummarizeModel, type WorkersAiTTSModel, type WorkersAiTextModel, type WorkersAiTextModelOptions, type WorkersAiTranscriptionModel, createAnthropicChat, createAnthropicSummarize, createGeminiChat, createGeminiImage, createGeminiSummarize, createGeminiTts, createGrokChat, createGrokImage, createGrokSummarize, createOpenAiChat, createOpenAiImage, createOpenAiSummarize, createOpenAiTranscription, createOpenAiTts, createOpenAiVideo, createOpenRouterChat, createOpenRouterImage, createOpenRouterSummarize, createWorkersAiChat, createWorkersAiImage, createWorkersAiSummarize, createWorkersAiTranscription, createWorkersAiTts };
12
+ export { ANTHROPIC_MODELS, type AiGatewayAdapterConfig, type AnthropicChatModel, type AnthropicGatewayConfig, GROK_CHAT_MODELS, GROK_IMAGE_MODELS, type GeminiChatModel, type GeminiGatewayConfig, type GeminiImageModel, GeminiImageModels, type GeminiSummarizeModel, type GeminiTTSModel, GeminiTTSModels, type GeminiTextModel, GeminiTextModels, type GrokChatModel, type GrokGatewayConfig, type GrokImageModel, type GrokSummarizeModel, OPENAI_CHAT_MODELS, OPENAI_IMAGE_MODELS, OPENAI_TRANSCRIPTION_MODELS, OPENAI_TTS_MODELS, OPENAI_VIDEO_MODELS, type OpenAIChatModel, type OpenAIImageModel, type OpenAITTSModel, type OpenAITranscriptionModel, type OpenAIVideoModel, type OpenAiGatewayConfig, type OpenRouterChatModel, type OpenRouterGatewayConfig, type OpenRouterImageModel, type OpenRouterSummarizeModel, type WorkersAiAdapterConfig, type WorkersAiImageModel, type WorkersAiSummarizeModel, type WorkersAiTTSModel, type WorkersAiTextModel, type WorkersAiTextModelOptions, type WorkersAiTranscriptionModel, createAnthropicChat, createAnthropicSummarize, createGeminiChat, createGeminiImage, createGeminiSummarize, createGeminiTts, createGrokChat, createGrokImage, createGrokSummarize, createOpenAiChat, createOpenAiImage, createOpenAiSummarize, createOpenAiTranscription, createOpenAiTts, createOpenAiVideo, createOpenRouterChat, createOpenRouterImage, createOpenRouterSummarize, createWorkersAiChat, createWorkersAiImage, createWorkersAiSummarize, createWorkersAiTranscription, createWorkersAiTts };
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { ANTHROPIC_MODELS, createAnthropicChat, createAnthropicSummarize } from "./adapters/anthropic.mjs";
2
- import { GeminiImageModels, GeminiSummarizeModels, GeminiTTSModels, GeminiTextModels, createGeminiChat, createGeminiImage, createGeminiSummarize, createGeminiTts } from "./adapters/gemini.mjs";
2
+ import { GeminiImageModels, GeminiTTSModels, GeminiTextModels, createGeminiChat, createGeminiImage, createGeminiSummarize, createGeminiTts } from "./adapters/gemini.mjs";
3
3
  import { GROK_CHAT_MODELS, GROK_IMAGE_MODELS, createGrokChat, createGrokImage, createGrokSummarize } from "./adapters/grok.mjs";
4
4
  import { OPENAI_CHAT_MODELS, OPENAI_IMAGE_MODELS, OPENAI_TRANSCRIPTION_MODELS, OPENAI_TTS_MODELS, OPENAI_VIDEO_MODELS, createOpenAiChat, createOpenAiImage, createOpenAiSummarize, createOpenAiTranscription, createOpenAiTts, createOpenAiVideo } from "./adapters/openai.mjs";
5
5
  import { createOpenRouterChat, createOpenRouterImage, createOpenRouterSummarize } from "./adapters/openrouter.mjs";
@@ -8,4 +8,4 @@ import { createWorkersAiImage } from "./adapters/workers-ai-image.mjs";
8
8
  import { createWorkersAiTranscription } from "./adapters/workers-ai-transcription.mjs";
9
9
  import { createWorkersAiTts } from "./adapters/workers-ai-tts.mjs";
10
10
  import { createWorkersAiSummarize } from "./adapters/workers-ai-summarize.mjs";
11
- export { ANTHROPIC_MODELS, GROK_CHAT_MODELS, GROK_IMAGE_MODELS, GeminiImageModels, GeminiSummarizeModels, GeminiTTSModels, GeminiTextModels, OPENAI_CHAT_MODELS, OPENAI_IMAGE_MODELS, OPENAI_TRANSCRIPTION_MODELS, OPENAI_TTS_MODELS, OPENAI_VIDEO_MODELS, createAnthropicChat, createAnthropicSummarize, createGeminiChat, createGeminiImage, createGeminiSummarize, createGeminiTts, createGrokChat, createGrokImage, createGrokSummarize, createOpenAiChat, createOpenAiImage, createOpenAiSummarize, createOpenAiTranscription, createOpenAiTts, createOpenAiVideo, createOpenRouterChat, createOpenRouterImage, createOpenRouterSummarize, createWorkersAiChat, createWorkersAiImage, createWorkersAiSummarize, createWorkersAiTranscription, createWorkersAiTts };
11
+ export { ANTHROPIC_MODELS, GROK_CHAT_MODELS, GROK_IMAGE_MODELS, GeminiImageModels, GeminiTTSModels, GeminiTextModels, OPENAI_CHAT_MODELS, OPENAI_IMAGE_MODELS, OPENAI_TRANSCRIPTION_MODELS, OPENAI_TTS_MODELS, OPENAI_VIDEO_MODELS, createAnthropicChat, createAnthropicSummarize, createGeminiChat, createGeminiImage, createGeminiSummarize, createGeminiTts, createGrokChat, createGrokImage, createGrokSummarize, createOpenAiChat, createOpenAiImage, createOpenAiSummarize, createOpenAiTranscription, createOpenAiTts, createOpenAiVideo, createOpenRouterChat, createOpenRouterImage, createOpenRouterSummarize, createWorkersAiChat, createWorkersAiImage, createWorkersAiSummarize, createWorkersAiTranscription, createWorkersAiTts };
@@ -71,4 +71,4 @@ async function workersAiRestFetchBinary(config, model, audioBytes, contentType,
71
71
  //#endregion
72
72
  export { workersAiRestFetchBinary as n, workersAiRestFetch as t };
73
73
 
74
- //# sourceMappingURL=workers-ai-rest-GKy2r7eG.mjs.map
74
+ //# sourceMappingURL=workers-ai-rest-B6_yU35Q.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"workers-ai-rest-CkNCtBwv.cjs","names":[],"sources":["../src/utils/workers-ai-rest.ts"],"sourcesContent":["import type { WorkersAiDirectCredentialsConfig } from \"./create-fetcher\";\n\n/**\n * Workers AI REST API base URL.\n * All model endpoints follow the pattern: `${BASE_URL}/${accountId}/ai/run/${model}`\n */\nconst WORKERS_AI_REST_BASE = \"https://api.cloudflare.com/client/v4/accounts\";\n\n/**\n * Make a REST API call to Workers AI.\n *\n * Handles the common pattern shared by all Workers AI adapters:\n * - Build the URL from account ID and model name\n * - Set Authorization and Content-Type headers\n * - Check response.ok and throw a descriptive error on failure\n *\n * @param config Credentials config with accountId and apiKey\n * @param model Workers AI model name (e.g. \"@cf/stabilityai/stable-diffusion-xl-base-1.0\")\n * @param body JSON request body\n * @param options Optional settings:\n * - `label` — human-readable label for error messages (default: \"Workers AI\")\n * - `signal` — AbortSignal for request cancellation / timeout\n * @returns The raw Response object — caller is responsible for parsing\n */\nexport async function workersAiRestFetch(\n\tconfig: WorkersAiDirectCredentialsConfig,\n\tmodel: string,\n\tbody: Record<string, unknown>,\n\toptions?: { label?: string; signal?: AbortSignal },\n): Promise<Response> {\n\tconst response = await fetch(`${WORKERS_AI_REST_BASE}/${config.accountId}/ai/run/${model}`, {\n\t\tmethod: \"POST\",\n\t\theaders: {\n\t\t\tAuthorization: `Bearer ${config.apiKey}`,\n\t\t\t\"Content-Type\": \"application/json\",\n\t\t},\n\t\tbody: JSON.stringify(body),\n\t\tsignal: options?.signal,\n\t});\n\n\tif (!response.ok) {\n\t\tconst errorText = await response.text();\n\t\tconst label = options?.label ?? \"Workers AI\";\n\t\tthrow new Error(`${label} request failed (${response.status}): ${errorText}`);\n\t}\n\n\treturn response;\n}\n\n/**\n * Make a binary REST API call to Workers AI.\n *\n * Some models (e.g. `@cf/deepgram/nova-3`) require raw binary audio with an\n * appropriate `Content-Type` header instead of JSON. This function sends the\n * audio bytes directly as the request body.\n *\n * @param config Credentials config with accountId and apiKey\n * @param model Workers AI model name\n * @param audioBytes Raw audio bytes\n * @param contentType MIME type of the audio (e.g. \"audio/wav\")\n * @param options Optional settings\n * @returns The raw Response object\n */\nexport async function workersAiRestFetchBinary(\n\tconfig: WorkersAiDirectCredentialsConfig,\n\tmodel: string,\n\taudioBytes: Uint8Array,\n\tcontentType: string,\n\toptions?: { label?: string; signal?: AbortSignal },\n): Promise<Response> {\n\tconst response = await fetch(`${WORKERS_AI_REST_BASE}/${config.accountId}/ai/run/${model}`, {\n\t\tmethod: \"POST\",\n\t\theaders: {\n\t\t\tAuthorization: `Bearer ${config.apiKey}`,\n\t\t\t\"Content-Type\": contentType,\n\t\t},\n\t\tbody: audioBytes,\n\t\tsignal: options?.signal,\n\t});\n\n\tif (!response.ok) {\n\t\tconst errorText = await response.text();\n\t\tconst label = options?.label ?? \"Workers AI\";\n\t\tthrow new Error(`${label} request failed (${response.status}): ${errorText}`);\n\t}\n\n\treturn response;\n}\n"],"mappings":";;;;;AAMA,MAAM,uBAAuB;;;;;;;;;;;;;;;;;AAkB7B,eAAsB,mBACrB,QACA,OACA,MACA,SACoB;CACpB,MAAM,WAAW,MAAM,MAAM,GAAG,qBAAqB,GAAG,OAAO,UAAU,UAAU,SAAS;EAC3F,QAAQ;EACR,SAAS;GACR,eAAe,UAAU,OAAO;GAChC,gBAAgB;GAChB;EACD,MAAM,KAAK,UAAU,KAAK;EAC1B,QAAQ,SAAS;EACjB,CAAC;AAEF,KAAI,CAAC,SAAS,IAAI;EACjB,MAAM,YAAY,MAAM,SAAS,MAAM;EACvC,MAAM,QAAQ,SAAS,SAAS;AAChC,QAAM,IAAI,MAAM,GAAG,MAAM,mBAAmB,SAAS,OAAO,KAAK,YAAY;;AAG9E,QAAO;;;;;;;;;;;;;;;;AAiBR,eAAsB,yBACrB,QACA,OACA,YACA,aACA,SACoB;CACpB,MAAM,WAAW,MAAM,MAAM,GAAG,qBAAqB,GAAG,OAAO,UAAU,UAAU,SAAS;EAC3F,QAAQ;EACR,SAAS;GACR,eAAe,UAAU,OAAO;GAChC,gBAAgB;GAChB;EACD,MAAM;EACN,QAAQ,SAAS;EACjB,CAAC;AAEF,KAAI,CAAC,SAAS,IAAI;EACjB,MAAM,YAAY,MAAM,SAAS,MAAM;EACvC,MAAM,QAAQ,SAAS,SAAS;AAChC,QAAM,IAAI,MAAM,GAAG,MAAM,mBAAmB,SAAS,OAAO,KAAK,YAAY;;AAG9E,QAAO"}
1
+ {"version":3,"file":"workers-ai-rest-B6_yU35Q.mjs","names":[],"sources":["../src/utils/workers-ai-rest.ts"],"sourcesContent":["import type { WorkersAiDirectCredentialsConfig } from \"./create-fetcher\";\n\n/**\n * Workers AI REST API base URL.\n * All model endpoints follow the pattern: `${BASE_URL}/${accountId}/ai/run/${model}`\n */\nconst WORKERS_AI_REST_BASE = \"https://api.cloudflare.com/client/v4/accounts\";\n\n/**\n * Make a REST API call to Workers AI.\n *\n * Handles the common pattern shared by all Workers AI adapters:\n * - Build the URL from account ID and model name\n * - Set Authorization and Content-Type headers\n * - Check response.ok and throw a descriptive error on failure\n *\n * @param config Credentials config with accountId and apiKey\n * @param model Workers AI model name (e.g. \"@cf/stabilityai/stable-diffusion-xl-base-1.0\")\n * @param body JSON request body\n * @param options Optional settings:\n * - `label` — human-readable label for error messages (default: \"Workers AI\")\n * - `signal` — AbortSignal for request cancellation / timeout\n * @returns The raw Response object — caller is responsible for parsing\n */\nexport async function workersAiRestFetch(\n\tconfig: WorkersAiDirectCredentialsConfig,\n\tmodel: string,\n\tbody: Record<string, unknown>,\n\toptions?: { label?: string; signal?: AbortSignal },\n): Promise<Response> {\n\tconst response = await fetch(`${WORKERS_AI_REST_BASE}/${config.accountId}/ai/run/${model}`, {\n\t\tmethod: \"POST\",\n\t\theaders: {\n\t\t\tAuthorization: `Bearer ${config.apiKey}`,\n\t\t\t\"Content-Type\": \"application/json\",\n\t\t},\n\t\tbody: JSON.stringify(body),\n\t\tsignal: options?.signal,\n\t});\n\n\tif (!response.ok) {\n\t\tconst errorText = await response.text();\n\t\tconst label = options?.label ?? \"Workers AI\";\n\t\tthrow new Error(`${label} request failed (${response.status}): ${errorText}`);\n\t}\n\n\treturn response;\n}\n\n/**\n * Make a binary REST API call to Workers AI.\n *\n * Some models (e.g. `@cf/deepgram/nova-3`) require raw binary audio with an\n * appropriate `Content-Type` header instead of JSON. This function sends the\n * audio bytes directly as the request body.\n *\n * @param config Credentials config with accountId and apiKey\n * @param model Workers AI model name\n * @param audioBytes Raw audio bytes\n * @param contentType MIME type of the audio (e.g. \"audio/wav\")\n * @param options Optional settings\n * @returns The raw Response object\n */\nexport async function workersAiRestFetchBinary(\n\tconfig: WorkersAiDirectCredentialsConfig,\n\tmodel: string,\n\taudioBytes: Uint8Array,\n\tcontentType: string,\n\toptions?: { label?: string; signal?: AbortSignal },\n): Promise<Response> {\n\tconst response = await fetch(`${WORKERS_AI_REST_BASE}/${config.accountId}/ai/run/${model}`, {\n\t\tmethod: \"POST\",\n\t\theaders: {\n\t\t\tAuthorization: `Bearer ${config.apiKey}`,\n\t\t\t\"Content-Type\": contentType,\n\t\t},\n\t\tbody: audioBytes,\n\t\tsignal: options?.signal,\n\t});\n\n\tif (!response.ok) {\n\t\tconst errorText = await response.text();\n\t\tconst label = options?.label ?? \"Workers AI\";\n\t\tthrow new Error(`${label} request failed (${response.status}): ${errorText}`);\n\t}\n\n\treturn response;\n}\n"],"mappings":";;;;;AAMA,MAAM,uBAAuB;;;;;;;;;;;;;;;;;AAkB7B,eAAsB,mBACrB,QACA,OACA,MACA,SACoB;CACpB,MAAM,WAAW,MAAM,MAAM,GAAG,qBAAqB,GAAG,OAAO,UAAU,UAAU,SAAS;EAC3F,QAAQ;EACR,SAAS;GACR,eAAe,UAAU,OAAO;GAChC,gBAAgB;EACjB;EACA,MAAM,KAAK,UAAU,IAAI;EACzB,QAAQ,SAAS;CAClB,CAAC;CAED,IAAI,CAAC,SAAS,IAAI;EACjB,MAAM,YAAY,MAAM,SAAS,KAAK;EACtC,MAAM,QAAQ,SAAS,SAAS;EAChC,MAAM,IAAI,MAAM,GAAG,MAAM,mBAAmB,SAAS,OAAO,KAAK,WAAW;CAC7E;CAEA,OAAO;AACR;;;;;;;;;;;;;;;AAgBA,eAAsB,yBACrB,QACA,OACA,YACA,aACA,SACoB;CACpB,MAAM,WAAW,MAAM,MAAM,GAAG,qBAAqB,GAAG,OAAO,UAAU,UAAU,SAAS;EAC3F,QAAQ;EACR,SAAS;GACR,eAAe,UAAU,OAAO;GAChC,gBAAgB;EACjB;EACA,MAAM;EACN,QAAQ,SAAS;CAClB,CAAC;CAED,IAAI,CAAC,SAAS,IAAI;EACjB,MAAM,YAAY,MAAM,SAAS,KAAK;EACtC,MAAM,QAAQ,SAAS,SAAS;EAChC,MAAM,IAAI,MAAM,GAAG,MAAM,mBAAmB,SAAS,OAAO,KAAK,WAAW;CAC7E;CAEA,OAAO;AACR"}
@@ -82,4 +82,4 @@ Object.defineProperty(exports, "workersAiRestFetchBinary", {
82
82
  }
83
83
  });
84
84
 
85
- //# sourceMappingURL=workers-ai-rest-CkNCtBwv.cjs.map
85
+ //# sourceMappingURL=workers-ai-rest-_MXOmyDs.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"workers-ai-rest-GKy2r7eG.mjs","names":[],"sources":["../src/utils/workers-ai-rest.ts"],"sourcesContent":["import type { WorkersAiDirectCredentialsConfig } from \"./create-fetcher\";\n\n/**\n * Workers AI REST API base URL.\n * All model endpoints follow the pattern: `${BASE_URL}/${accountId}/ai/run/${model}`\n */\nconst WORKERS_AI_REST_BASE = \"https://api.cloudflare.com/client/v4/accounts\";\n\n/**\n * Make a REST API call to Workers AI.\n *\n * Handles the common pattern shared by all Workers AI adapters:\n * - Build the URL from account ID and model name\n * - Set Authorization and Content-Type headers\n * - Check response.ok and throw a descriptive error on failure\n *\n * @param config Credentials config with accountId and apiKey\n * @param model Workers AI model name (e.g. \"@cf/stabilityai/stable-diffusion-xl-base-1.0\")\n * @param body JSON request body\n * @param options Optional settings:\n * - `label` — human-readable label for error messages (default: \"Workers AI\")\n * - `signal` — AbortSignal for request cancellation / timeout\n * @returns The raw Response object — caller is responsible for parsing\n */\nexport async function workersAiRestFetch(\n\tconfig: WorkersAiDirectCredentialsConfig,\n\tmodel: string,\n\tbody: Record<string, unknown>,\n\toptions?: { label?: string; signal?: AbortSignal },\n): Promise<Response> {\n\tconst response = await fetch(`${WORKERS_AI_REST_BASE}/${config.accountId}/ai/run/${model}`, {\n\t\tmethod: \"POST\",\n\t\theaders: {\n\t\t\tAuthorization: `Bearer ${config.apiKey}`,\n\t\t\t\"Content-Type\": \"application/json\",\n\t\t},\n\t\tbody: JSON.stringify(body),\n\t\tsignal: options?.signal,\n\t});\n\n\tif (!response.ok) {\n\t\tconst errorText = await response.text();\n\t\tconst label = options?.label ?? \"Workers AI\";\n\t\tthrow new Error(`${label} request failed (${response.status}): ${errorText}`);\n\t}\n\n\treturn response;\n}\n\n/**\n * Make a binary REST API call to Workers AI.\n *\n * Some models (e.g. `@cf/deepgram/nova-3`) require raw binary audio with an\n * appropriate `Content-Type` header instead of JSON. This function sends the\n * audio bytes directly as the request body.\n *\n * @param config Credentials config with accountId and apiKey\n * @param model Workers AI model name\n * @param audioBytes Raw audio bytes\n * @param contentType MIME type of the audio (e.g. \"audio/wav\")\n * @param options Optional settings\n * @returns The raw Response object\n */\nexport async function workersAiRestFetchBinary(\n\tconfig: WorkersAiDirectCredentialsConfig,\n\tmodel: string,\n\taudioBytes: Uint8Array,\n\tcontentType: string,\n\toptions?: { label?: string; signal?: AbortSignal },\n): Promise<Response> {\n\tconst response = await fetch(`${WORKERS_AI_REST_BASE}/${config.accountId}/ai/run/${model}`, {\n\t\tmethod: \"POST\",\n\t\theaders: {\n\t\t\tAuthorization: `Bearer ${config.apiKey}`,\n\t\t\t\"Content-Type\": contentType,\n\t\t},\n\t\tbody: audioBytes,\n\t\tsignal: options?.signal,\n\t});\n\n\tif (!response.ok) {\n\t\tconst errorText = await response.text();\n\t\tconst label = options?.label ?? \"Workers AI\";\n\t\tthrow new Error(`${label} request failed (${response.status}): ${errorText}`);\n\t}\n\n\treturn response;\n}\n"],"mappings":";;;;;AAMA,MAAM,uBAAuB;;;;;;;;;;;;;;;;;AAkB7B,eAAsB,mBACrB,QACA,OACA,MACA,SACoB;CACpB,MAAM,WAAW,MAAM,MAAM,GAAG,qBAAqB,GAAG,OAAO,UAAU,UAAU,SAAS;EAC3F,QAAQ;EACR,SAAS;GACR,eAAe,UAAU,OAAO;GAChC,gBAAgB;GAChB;EACD,MAAM,KAAK,UAAU,KAAK;EAC1B,QAAQ,SAAS;EACjB,CAAC;AAEF,KAAI,CAAC,SAAS,IAAI;EACjB,MAAM,YAAY,MAAM,SAAS,MAAM;EACvC,MAAM,QAAQ,SAAS,SAAS;AAChC,QAAM,IAAI,MAAM,GAAG,MAAM,mBAAmB,SAAS,OAAO,KAAK,YAAY;;AAG9E,QAAO;;;;;;;;;;;;;;;;AAiBR,eAAsB,yBACrB,QACA,OACA,YACA,aACA,SACoB;CACpB,MAAM,WAAW,MAAM,MAAM,GAAG,qBAAqB,GAAG,OAAO,UAAU,UAAU,SAAS;EAC3F,QAAQ;EACR,SAAS;GACR,eAAe,UAAU,OAAO;GAChC,gBAAgB;GAChB;EACD,MAAM;EACN,QAAQ,SAAS;EACjB,CAAC;AAEF,KAAI,CAAC,SAAS,IAAI;EACjB,MAAM,YAAY,MAAM,SAAS,MAAM;EACvC,MAAM,QAAQ,SAAS,SAAS;AAChC,QAAM,IAAI,MAAM,GAAG,MAAM,mBAAmB,SAAS,OAAO,KAAK,YAAY;;AAG9E,QAAO"}
1
+ {"version":3,"file":"workers-ai-rest-_MXOmyDs.cjs","names":[],"sources":["../src/utils/workers-ai-rest.ts"],"sourcesContent":["import type { WorkersAiDirectCredentialsConfig } from \"./create-fetcher\";\n\n/**\n * Workers AI REST API base URL.\n * All model endpoints follow the pattern: `${BASE_URL}/${accountId}/ai/run/${model}`\n */\nconst WORKERS_AI_REST_BASE = \"https://api.cloudflare.com/client/v4/accounts\";\n\n/**\n * Make a REST API call to Workers AI.\n *\n * Handles the common pattern shared by all Workers AI adapters:\n * - Build the URL from account ID and model name\n * - Set Authorization and Content-Type headers\n * - Check response.ok and throw a descriptive error on failure\n *\n * @param config Credentials config with accountId and apiKey\n * @param model Workers AI model name (e.g. \"@cf/stabilityai/stable-diffusion-xl-base-1.0\")\n * @param body JSON request body\n * @param options Optional settings:\n * - `label` — human-readable label for error messages (default: \"Workers AI\")\n * - `signal` — AbortSignal for request cancellation / timeout\n * @returns The raw Response object — caller is responsible for parsing\n */\nexport async function workersAiRestFetch(\n\tconfig: WorkersAiDirectCredentialsConfig,\n\tmodel: string,\n\tbody: Record<string, unknown>,\n\toptions?: { label?: string; signal?: AbortSignal },\n): Promise<Response> {\n\tconst response = await fetch(`${WORKERS_AI_REST_BASE}/${config.accountId}/ai/run/${model}`, {\n\t\tmethod: \"POST\",\n\t\theaders: {\n\t\t\tAuthorization: `Bearer ${config.apiKey}`,\n\t\t\t\"Content-Type\": \"application/json\",\n\t\t},\n\t\tbody: JSON.stringify(body),\n\t\tsignal: options?.signal,\n\t});\n\n\tif (!response.ok) {\n\t\tconst errorText = await response.text();\n\t\tconst label = options?.label ?? \"Workers AI\";\n\t\tthrow new Error(`${label} request failed (${response.status}): ${errorText}`);\n\t}\n\n\treturn response;\n}\n\n/**\n * Make a binary REST API call to Workers AI.\n *\n * Some models (e.g. `@cf/deepgram/nova-3`) require raw binary audio with an\n * appropriate `Content-Type` header instead of JSON. This function sends the\n * audio bytes directly as the request body.\n *\n * @param config Credentials config with accountId and apiKey\n * @param model Workers AI model name\n * @param audioBytes Raw audio bytes\n * @param contentType MIME type of the audio (e.g. \"audio/wav\")\n * @param options Optional settings\n * @returns The raw Response object\n */\nexport async function workersAiRestFetchBinary(\n\tconfig: WorkersAiDirectCredentialsConfig,\n\tmodel: string,\n\taudioBytes: Uint8Array,\n\tcontentType: string,\n\toptions?: { label?: string; signal?: AbortSignal },\n): Promise<Response> {\n\tconst response = await fetch(`${WORKERS_AI_REST_BASE}/${config.accountId}/ai/run/${model}`, {\n\t\tmethod: \"POST\",\n\t\theaders: {\n\t\t\tAuthorization: `Bearer ${config.apiKey}`,\n\t\t\t\"Content-Type\": contentType,\n\t\t},\n\t\tbody: audioBytes,\n\t\tsignal: options?.signal,\n\t});\n\n\tif (!response.ok) {\n\t\tconst errorText = await response.text();\n\t\tconst label = options?.label ?? \"Workers AI\";\n\t\tthrow new Error(`${label} request failed (${response.status}): ${errorText}`);\n\t}\n\n\treturn response;\n}\n"],"mappings":";;;;;AAMA,MAAM,uBAAuB;;;;;;;;;;;;;;;;;AAkB7B,eAAsB,mBACrB,QACA,OACA,MACA,SACoB;CACpB,MAAM,WAAW,MAAM,MAAM,GAAG,qBAAqB,GAAG,OAAO,UAAU,UAAU,SAAS;EAC3F,QAAQ;EACR,SAAS;GACR,eAAe,UAAU,OAAO;GAChC,gBAAgB;EACjB;EACA,MAAM,KAAK,UAAU,IAAI;EACzB,QAAQ,SAAS;CAClB,CAAC;CAED,IAAI,CAAC,SAAS,IAAI;EACjB,MAAM,YAAY,MAAM,SAAS,KAAK;EACtC,MAAM,QAAQ,SAAS,SAAS;EAChC,MAAM,IAAI,MAAM,GAAG,MAAM,mBAAmB,SAAS,OAAO,KAAK,WAAW;CAC7E;CAEA,OAAO;AACR;;;;;;;;;;;;;;;AAgBA,eAAsB,yBACrB,QACA,OACA,YACA,aACA,SACoB;CACpB,MAAM,WAAW,MAAM,MAAM,GAAG,qBAAqB,GAAG,OAAO,UAAU,UAAU,SAAS;EAC3F,QAAQ;EACR,SAAS;GACR,eAAe,UAAU,OAAO;GAChC,gBAAgB;EACjB;EACA,MAAM;EACN,QAAQ,SAAS;CAClB,CAAC;CAED,IAAI,CAAC,SAAS,IAAI;EACjB,MAAM,YAAY,MAAM,SAAS,KAAK;EACtC,MAAM,QAAQ,SAAS,SAAS;EAChC,MAAM,IAAI,MAAM,GAAG,MAAM,mBAAmB,SAAS,OAAO,KAAK,WAAW;CAC7E;CAEA,OAAO;AACR"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cloudflare/tanstack-ai",
3
- "version": "0.1.9",
3
+ "version": "0.1.10",
4
4
  "description": "Use TanStack AI with Cloudflare Workers AI and AI Gateway",
5
5
  "keywords": [
6
6
  "ai",
@@ -121,21 +121,21 @@
121
121
  "access": "public"
122
122
  },
123
123
  "dependencies": {
124
- "openai": "^6.33.0"
124
+ "openai": "^6.42.0"
125
125
  },
126
126
  "devDependencies": {
127
- "@cloudflare/workers-types": "^4.20260402.1",
128
- "@microsoft/api-extractor": "^7.58.0",
129
- "@tanstack/ai": "^0.9.2",
130
- "dotenv": "^17.4.0"
127
+ "@cloudflare/workers-types": "^4.20260613.1",
128
+ "@microsoft/api-extractor": "^7.58.9",
129
+ "@tanstack/ai": "^0.28.0",
130
+ "dotenv": "^17.4.2"
131
131
  },
132
132
  "peerDependencies": {
133
133
  "@tanstack/ai": ">=0.8.0 <1.0.0"
134
134
  },
135
135
  "optionalDependencies": {
136
- "@anthropic-ai/sdk": "^0.82.0",
137
- "@google/genai": "^1.48.0",
138
- "@openrouter/sdk": "^0.10.2",
136
+ "@anthropic-ai/sdk": "^0.104.1",
137
+ "@google/genai": "^2.8.0",
138
+ "@openrouter/sdk": "^0.12.79",
139
139
  "@tanstack/ai-anthropic": ">=0.7.1 <1.0.0",
140
140
  "@tanstack/ai-gemini": ">=0.8.4 <1.0.0",
141
141
  "@tanstack/ai-grok": ">=0.6.3 <1.0.0",