@cloudflare/tanstack-ai 0.0.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (127) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +282 -0
  3. package/dist/_tsup-dts-rollup.d.cts +820 -0
  4. package/dist/_tsup-dts-rollup.d.ts +820 -0
  5. package/dist/adapters/anthropic.cjs +13 -0
  6. package/dist/adapters/anthropic.cjs.map +1 -0
  7. package/dist/adapters/anthropic.d.cts +5 -0
  8. package/dist/adapters/anthropic.d.ts +5 -0
  9. package/dist/adapters/anthropic.js +13 -0
  10. package/dist/adapters/anthropic.js.map +1 -0
  11. package/dist/adapters/gemini.cjs +22 -0
  12. package/dist/adapters/gemini.cjs.map +1 -0
  13. package/dist/adapters/gemini.d.cts +14 -0
  14. package/dist/adapters/gemini.d.ts +14 -0
  15. package/dist/adapters/gemini.js +22 -0
  16. package/dist/adapters/gemini.js.map +1 -0
  17. package/dist/adapters/grok.cjs +17 -0
  18. package/dist/adapters/grok.cjs.map +1 -0
  19. package/dist/adapters/grok.d.cts +9 -0
  20. package/dist/adapters/grok.d.ts +9 -0
  21. package/dist/adapters/grok.js +17 -0
  22. package/dist/adapters/grok.js.map +1 -0
  23. package/dist/adapters/openai.cjs +29 -0
  24. package/dist/adapters/openai.cjs.map +1 -0
  25. package/dist/adapters/openai.d.cts +17 -0
  26. package/dist/adapters/openai.d.ts +17 -0
  27. package/dist/adapters/openai.js +29 -0
  28. package/dist/adapters/openai.js.map +1 -0
  29. package/dist/adapters/openrouter.cjs +13 -0
  30. package/dist/adapters/openrouter.cjs.map +1 -0
  31. package/dist/adapters/openrouter.d.cts +7 -0
  32. package/dist/adapters/openrouter.d.ts +7 -0
  33. package/dist/adapters/openrouter.js +13 -0
  34. package/dist/adapters/openrouter.js.map +1 -0
  35. package/dist/adapters/workers-ai-image.cjs +13 -0
  36. package/dist/adapters/workers-ai-image.cjs.map +1 -0
  37. package/dist/adapters/workers-ai-image.d.cts +3 -0
  38. package/dist/adapters/workers-ai-image.d.ts +3 -0
  39. package/dist/adapters/workers-ai-image.js +13 -0
  40. package/dist/adapters/workers-ai-image.js.map +1 -0
  41. package/dist/adapters/workers-ai-summarize.cjs +12 -0
  42. package/dist/adapters/workers-ai-summarize.cjs.map +1 -0
  43. package/dist/adapters/workers-ai-summarize.d.cts +3 -0
  44. package/dist/adapters/workers-ai-summarize.d.ts +3 -0
  45. package/dist/adapters/workers-ai-summarize.js +12 -0
  46. package/dist/adapters/workers-ai-summarize.js.map +1 -0
  47. package/dist/adapters/workers-ai-transcription.cjs +13 -0
  48. package/dist/adapters/workers-ai-transcription.cjs.map +1 -0
  49. package/dist/adapters/workers-ai-transcription.d.cts +3 -0
  50. package/dist/adapters/workers-ai-transcription.d.ts +3 -0
  51. package/dist/adapters/workers-ai-transcription.js +13 -0
  52. package/dist/adapters/workers-ai-transcription.js.map +1 -0
  53. package/dist/adapters/workers-ai-tts.cjs +13 -0
  54. package/dist/adapters/workers-ai-tts.cjs.map +1 -0
  55. package/dist/adapters/workers-ai-tts.d.cts +3 -0
  56. package/dist/adapters/workers-ai-tts.d.ts +3 -0
  57. package/dist/adapters/workers-ai-tts.js +13 -0
  58. package/dist/adapters/workers-ai-tts.js.map +1 -0
  59. package/dist/adapters/workers-ai.cjs +11 -0
  60. package/dist/adapters/workers-ai.cjs.map +1 -0
  61. package/dist/adapters/workers-ai.d.cts +3 -0
  62. package/dist/adapters/workers-ai.d.ts +3 -0
  63. package/dist/adapters/workers-ai.js +11 -0
  64. package/dist/adapters/workers-ai.js.map +1 -0
  65. package/dist/chunk-2RO3A3R4.js +96 -0
  66. package/dist/chunk-2RO3A3R4.js.map +1 -0
  67. package/dist/chunk-2VII5BK2.js +42 -0
  68. package/dist/chunk-2VII5BK2.js.map +1 -0
  69. package/dist/chunk-3VQDXJLW.cjs +46 -0
  70. package/dist/chunk-3VQDXJLW.cjs.map +1 -0
  71. package/dist/chunk-4ACSLQDI.cjs +216 -0
  72. package/dist/chunk-4ACSLQDI.cjs.map +1 -0
  73. package/dist/chunk-4DE2IREA.cjs +8 -0
  74. package/dist/chunk-4DE2IREA.cjs.map +1 -0
  75. package/dist/chunk-5YEJ5ZRQ.js +503 -0
  76. package/dist/chunk-5YEJ5ZRQ.js.map +1 -0
  77. package/dist/chunk-6FBIXTAL.cjs +315 -0
  78. package/dist/chunk-6FBIXTAL.cjs.map +1 -0
  79. package/dist/chunk-6OXP4IVS.cjs +48 -0
  80. package/dist/chunk-6OXP4IVS.cjs.map +1 -0
  81. package/dist/chunk-7AEFXYJG.js +65 -0
  82. package/dist/chunk-7AEFXYJG.js.map +1 -0
  83. package/dist/chunk-7HSUHP63.cjs +42 -0
  84. package/dist/chunk-7HSUHP63.cjs.map +1 -0
  85. package/dist/chunk-AHXFO2BB.cjs +31 -0
  86. package/dist/chunk-AHXFO2BB.cjs.map +1 -0
  87. package/dist/chunk-ALUCJNDE.js +109 -0
  88. package/dist/chunk-ALUCJNDE.js.map +1 -0
  89. package/dist/chunk-AN23SOZX.cjs +96 -0
  90. package/dist/chunk-AN23SOZX.cjs.map +1 -0
  91. package/dist/chunk-F75IZQCM.js +48 -0
  92. package/dist/chunk-F75IZQCM.js.map +1 -0
  93. package/dist/chunk-GL2EQLMI.cjs +109 -0
  94. package/dist/chunk-GL2EQLMI.cjs.map +1 -0
  95. package/dist/chunk-M64PETK7.cjs +84 -0
  96. package/dist/chunk-M64PETK7.cjs.map +1 -0
  97. package/dist/chunk-OV65IEEY.cjs +57 -0
  98. package/dist/chunk-OV65IEEY.cjs.map +1 -0
  99. package/dist/chunk-PAVBM57P.js +57 -0
  100. package/dist/chunk-PAVBM57P.js.map +1 -0
  101. package/dist/chunk-R2MRGLZ4.js +84 -0
  102. package/dist/chunk-R2MRGLZ4.js.map +1 -0
  103. package/dist/chunk-RGDUK5KX.cjs +65 -0
  104. package/dist/chunk-RGDUK5KX.cjs.map +1 -0
  105. package/dist/chunk-S3ALRROX.js +57 -0
  106. package/dist/chunk-S3ALRROX.js.map +1 -0
  107. package/dist/chunk-SFZAUNHJ.cjs +57 -0
  108. package/dist/chunk-SFZAUNHJ.cjs.map +1 -0
  109. package/dist/chunk-SIOQQHXS.js +31 -0
  110. package/dist/chunk-SIOQQHXS.js.map +1 -0
  111. package/dist/chunk-UUFEOQ6B.js +315 -0
  112. package/dist/chunk-UUFEOQ6B.js.map +1 -0
  113. package/dist/chunk-V6TY7KAL.js +8 -0
  114. package/dist/chunk-V6TY7KAL.js.map +1 -0
  115. package/dist/chunk-VV3JFKAN.js +216 -0
  116. package/dist/chunk-VV3JFKAN.js.map +1 -0
  117. package/dist/chunk-VZJHRPOT.cjs +503 -0
  118. package/dist/chunk-VZJHRPOT.cjs.map +1 -0
  119. package/dist/chunk-XU7YEPML.js +46 -0
  120. package/dist/chunk-XU7YEPML.js.map +1 -0
  121. package/dist/index.cjs +97 -0
  122. package/dist/index.cjs.map +1 -0
  123. package/dist/index.d.cts +64 -0
  124. package/dist/index.d.ts +64 -0
  125. package/dist/index.js +97 -0
  126. package/dist/index.js.map +1 -0
  127. package/package.json +119 -10
@@ -0,0 +1,315 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }// src/utils/create-fetcher.ts
2
+ function isDirectBindingConfig(config) {
3
+ return "binding" in config && typeof config.binding.gateway === "function";
4
+ }
5
+ function isDirectCredentialsConfig(config) {
6
+ return "accountId" in config && "apiKey" in config && !("gatewayId" in config);
7
+ }
8
+ function createGatewayFetch(provider, config, headers = {}) {
9
+ return (input, init) => {
10
+ let query = {};
11
+ const url = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
12
+ const urlObj = new URL(url);
13
+ const endpoint = urlObj.pathname.replace(/^\/v1\//, "").replace(/^\//, "") + urlObj.search;
14
+ if (_optionalChain([init, 'optionalAccess', _ => _.body])) {
15
+ try {
16
+ query = JSON.parse(init.body);
17
+ } catch (e2) {
18
+ query = { _raw: init.body };
19
+ }
20
+ }
21
+ const cacheHeaders = {};
22
+ if ("skipCache" in config && config.skipCache) {
23
+ cacheHeaders["cf-aig-skip-cache"] = "true";
24
+ }
25
+ if (typeof config.cacheTtl === "number") {
26
+ cacheHeaders["cf-aig-cache-ttl"] = String(config.cacheTtl);
27
+ }
28
+ if (typeof config.customCacheKey === "string") {
29
+ cacheHeaders["cf-aig-cache-key"] = config.customCacheKey;
30
+ }
31
+ if (typeof config.metadata === "object") {
32
+ cacheHeaders["cf-aig-metadata"] = JSON.stringify(config.metadata);
33
+ }
34
+ const request = {
35
+ provider,
36
+ endpoint,
37
+ headers: {
38
+ ..._optionalChain([init, 'optionalAccess', _2 => _2.headers]),
39
+ ...headers,
40
+ ...cacheHeaders,
41
+ "Content-Type": "application/json"
42
+ },
43
+ query
44
+ };
45
+ if (provider === "workers-ai") {
46
+ request.endpoint = query.model;
47
+ delete query.model;
48
+ delete query.instructions;
49
+ }
50
+ if (config.apiKey) {
51
+ request.headers["authorization"] = `Bearer ${config.apiKey}`;
52
+ }
53
+ if ("binding" in config) {
54
+ return config.binding.run(request);
55
+ }
56
+ return fetch(
57
+ `https://gateway.ai.cloudflare.com/v1/${config.accountId}/${config.gatewayId}`,
58
+ {
59
+ ...init,
60
+ headers: {
61
+ "Content-Type": "application/json",
62
+ ...headers,
63
+ ...cacheHeaders,
64
+ ...config.cfApiKey ? { "cf-aig-authorization": `Bearer ${config.cfApiKey}` } : {}
65
+ },
66
+ body: JSON.stringify(request)
67
+ }
68
+ );
69
+ };
70
+ }
71
+ function normalizeMessagesForBinding(messages) {
72
+ return messages.map((msg) => {
73
+ const normalized = { ...msg };
74
+ if (normalized.content === null || normalized.content === void 0) {
75
+ normalized.content = "";
76
+ }
77
+ if (normalized.tool_call_id && typeof normalized.tool_call_id === "string") {
78
+ normalized.tool_call_id = sanitizeToolCallId(normalized.tool_call_id);
79
+ }
80
+ if (Array.isArray(normalized.tool_calls)) {
81
+ normalized.tool_calls = normalized.tool_calls.map(
82
+ (tc) => {
83
+ if (tc.id && typeof tc.id === "string") {
84
+ return { ...tc, id: sanitizeToolCallId(tc.id) };
85
+ }
86
+ return tc;
87
+ }
88
+ );
89
+ }
90
+ return normalized;
91
+ });
92
+ }
93
+ function sanitizeToolCallId(id) {
94
+ const alphanumeric = id.replace(/[^a-zA-Z0-9]/g, "");
95
+ return alphanumeric.slice(0, 9).padEnd(9, "0");
96
+ }
97
+ function createWorkersAiBindingFetch(binding) {
98
+ return async (_input, init) => {
99
+ if (!_optionalChain([init, 'optionalAccess', _3 => _3.body])) {
100
+ return new Response("No body", { status: 400 });
101
+ }
102
+ let body;
103
+ try {
104
+ body = JSON.parse(init.body);
105
+ } catch (e3) {
106
+ return new Response("Invalid JSON body", { status: 400 });
107
+ }
108
+ const model = body.model;
109
+ const stream = body.stream;
110
+ const inputs = {};
111
+ if (body.messages) {
112
+ inputs.messages = normalizeMessagesForBinding(
113
+ body.messages
114
+ );
115
+ }
116
+ if (body.tools) inputs.tools = body.tools;
117
+ if (typeof body.temperature === "number") inputs.temperature = body.temperature;
118
+ if (typeof body.max_tokens === "number") inputs.max_tokens = body.max_tokens;
119
+ if (body.response_format) inputs.response_format = body.response_format;
120
+ if (stream) inputs.stream = true;
121
+ const result = await binding.run(model, inputs);
122
+ if (stream && result instanceof ReadableStream) {
123
+ const transformed = transformWorkersAiStream(
124
+ result,
125
+ model
126
+ );
127
+ return new Response(transformed, {
128
+ headers: {
129
+ "Content-Type": "text/event-stream",
130
+ "Cache-Control": "no-cache"
131
+ }
132
+ });
133
+ }
134
+ const responseObj = typeof result === "object" && result !== null ? result : { response: String(result) };
135
+ const responseText = typeof responseObj.response === "string" ? responseObj.response : "";
136
+ const message = {
137
+ role: "assistant",
138
+ content: responseText
139
+ };
140
+ let finishReason = "stop";
141
+ if (Array.isArray(responseObj.tool_calls) && responseObj.tool_calls.length > 0) {
142
+ finishReason = "tool_calls";
143
+ message.tool_calls = responseObj.tool_calls.map(
144
+ (tc) => ({
145
+ id: sanitizeToolCallId(tc.id || crypto.randomUUID()),
146
+ type: "function",
147
+ function: {
148
+ name: _optionalChain([tc, 'access', _4 => _4.function, 'optionalAccess', _5 => _5.name]) || tc.name || "",
149
+ arguments: typeof (_nullishCoalesce(_optionalChain([tc, 'access', _6 => _6.function, 'optionalAccess', _7 => _7.arguments]), () => ( tc.arguments))) === "string" ? _nullishCoalesce(_optionalChain([tc, 'access', _8 => _8.function, 'optionalAccess', _9 => _9.arguments]), () => ( tc.arguments)) : JSON.stringify(_nullishCoalesce(_nullishCoalesce(_optionalChain([tc, 'access', _10 => _10.function, 'optionalAccess', _11 => _11.arguments]), () => ( tc.arguments)), () => ( {})))
150
+ }
151
+ })
152
+ );
153
+ }
154
+ const openAiResponse = {
155
+ id: `workers-ai-${crypto.randomUUID()}`,
156
+ object: "chat.completion",
157
+ created: Math.floor(Date.now() / 1e3),
158
+ model,
159
+ choices: [{ index: 0, message, finish_reason: finishReason }]
160
+ };
161
+ return new Response(JSON.stringify(openAiResponse), {
162
+ headers: { "Content-Type": "application/json" }
163
+ });
164
+ };
165
+ }
166
+ function transformWorkersAiStream(source, model) {
167
+ const decoder = new TextDecoder();
168
+ const encoder = new TextEncoder();
169
+ const streamId = `workers-ai-${crypto.randomUUID()}`;
170
+ const created = Math.floor(Date.now() / 1e3);
171
+ let buffer = "";
172
+ let hasToolCalls = false;
173
+ let isOpenAiFormat = false;
174
+ const emittedToolCallStart = /* @__PURE__ */ new Set();
175
+ return source.pipeThrough(
176
+ new TransformStream({
177
+ transform(chunk, controller) {
178
+ buffer += decoder.decode(chunk, { stream: true });
179
+ const lines = buffer.split("\n");
180
+ buffer = lines.pop() || "";
181
+ for (const line of lines) {
182
+ const trimmed = line.trim();
183
+ if (!trimmed || !trimmed.startsWith("data: ")) continue;
184
+ const data = trimmed.slice(6);
185
+ if (data === "[DONE]") continue;
186
+ try {
187
+ const parsed = JSON.parse(data);
188
+ if (parsed.choices !== void 0) {
189
+ isOpenAiFormat = true;
190
+ const choice = _optionalChain([parsed, 'access', _12 => _12.choices, 'optionalAccess', _13 => _13[0]]);
191
+ if (_optionalChain([choice, 'optionalAccess', _14 => _14.delta, 'optionalAccess', _15 => _15.tool_calls])) {
192
+ hasToolCalls = true;
193
+ for (const tc of choice.delta.tool_calls) {
194
+ if (tc.id && typeof tc.id === "string") {
195
+ tc.id = sanitizeToolCallId(tc.id);
196
+ }
197
+ }
198
+ }
199
+ if (_optionalChain([choice, 'optionalAccess', _16 => _16.finish_reason]) === "tool_calls") {
200
+ hasToolCalls = true;
201
+ }
202
+ controller.enqueue(
203
+ encoder.encode(`data: ${JSON.stringify(parsed)}
204
+
205
+ `)
206
+ );
207
+ continue;
208
+ }
209
+ if (parsed.response != null && parsed.response !== "") {
210
+ const openAiChunk = {
211
+ id: streamId,
212
+ object: "chat.completion.chunk",
213
+ created,
214
+ model,
215
+ choices: [
216
+ {
217
+ index: 0,
218
+ delta: { content: parsed.response },
219
+ finish_reason: null
220
+ }
221
+ ]
222
+ };
223
+ controller.enqueue(
224
+ encoder.encode(`data: ${JSON.stringify(openAiChunk)}
225
+
226
+ `)
227
+ );
228
+ }
229
+ if (Array.isArray(parsed.tool_calls) && parsed.tool_calls.length > 0) {
230
+ for (const tc of parsed.tool_calls) {
231
+ const tcIndex = _nullishCoalesce(tc.index, () => ( 0));
232
+ const tcName = _nullishCoalesce(_nullishCoalesce(_optionalChain([tc, 'access', _17 => _17.function, 'optionalAccess', _18 => _18.name]), () => ( tc.name)), () => ( null));
233
+ const tcArgs = _nullishCoalesce(_nullishCoalesce(_optionalChain([tc, 'access', _19 => _19.function, 'optionalAccess', _20 => _20.arguments]), () => ( tc.arguments)), () => ( null));
234
+ const tcId = _nullishCoalesce(tc.id, () => ( null));
235
+ if (!tcId && !tcName && (!tcArgs || tcArgs === "")) continue;
236
+ hasToolCalls = true;
237
+ const toolCallDelta = {
238
+ index: tcIndex
239
+ };
240
+ if (!emittedToolCallStart.has(tcIndex)) {
241
+ emittedToolCallStart.add(tcIndex);
242
+ const rawId = tcId || `call${streamId}${tcIndex}`;
243
+ toolCallDelta.id = sanitizeToolCallId(rawId);
244
+ toolCallDelta.type = "function";
245
+ toolCallDelta.function = {
246
+ name: tcName || "",
247
+ // Include arguments if they arrive in the same chunk
248
+ arguments: tcArgs != null ? typeof tcArgs === "string" ? tcArgs : JSON.stringify(tcArgs) : ""
249
+ };
250
+ } else {
251
+ if (tcArgs != null && tcArgs !== "") {
252
+ toolCallDelta.function = {
253
+ arguments: typeof tcArgs === "string" ? tcArgs : JSON.stringify(tcArgs)
254
+ };
255
+ } else {
256
+ continue;
257
+ }
258
+ }
259
+ const toolChunk = {
260
+ id: streamId,
261
+ object: "chat.completion.chunk",
262
+ created,
263
+ model,
264
+ choices: [
265
+ {
266
+ index: 0,
267
+ delta: { tool_calls: [toolCallDelta] },
268
+ finish_reason: null
269
+ }
270
+ ]
271
+ };
272
+ controller.enqueue(
273
+ encoder.encode(`data: ${JSON.stringify(toolChunk)}
274
+
275
+ `)
276
+ );
277
+ }
278
+ }
279
+ } catch (e) {
280
+ console.warn("[tanstack-ai] failed to parse SSE event:", data, e);
281
+ }
282
+ }
283
+ },
284
+ flush(controller) {
285
+ if (!isOpenAiFormat) {
286
+ const finalChunk = {
287
+ id: streamId,
288
+ object: "chat.completion.chunk",
289
+ created,
290
+ model,
291
+ choices: [
292
+ {
293
+ index: 0,
294
+ delta: {},
295
+ finish_reason: hasToolCalls ? "tool_calls" : "stop"
296
+ }
297
+ ]
298
+ };
299
+ controller.enqueue(encoder.encode(`data: ${JSON.stringify(finalChunk)}
300
+
301
+ `));
302
+ }
303
+ controller.enqueue(encoder.encode("data: [DONE]\n\n"));
304
+ }
305
+ })
306
+ );
307
+ }
308
+
309
+
310
+
311
+
312
+
313
+
314
+ exports.isDirectBindingConfig = isDirectBindingConfig; exports.isDirectCredentialsConfig = isDirectCredentialsConfig; exports.createGatewayFetch = createGatewayFetch; exports.createWorkersAiBindingFetch = createWorkersAiBindingFetch;
315
+ //# sourceMappingURL=chunk-6FBIXTAL.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/home/runner/work/ai/ai/packages/tanstack-ai/dist/chunk-6FBIXTAL.cjs","../src/utils/create-fetcher.ts"],"names":[],"mappings":"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","file":"/home/runner/work/ai/ai/packages/tanstack-ai/dist/chunk-6FBIXTAL.cjs","sourcesContent":[null,"// ---------------------------------------------------------------------------\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 interface 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\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\t/**\n\t * The Cloudflare AI Gateway API Key, required if your Gateway is authenticated.\n\t */\n\tcfApiKey?: string;\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// ---------------------------------------------------------------------------\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// 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\trequest.endpoint = query.model as string;\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 config.binding.run(request);\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 be a string (not null)\n * - `tool_call_id` must match `[a-zA-Z0-9]{9}` pattern\n *\n * This function patches these fields so that the full tool-call round-trip works\n * even though the binding's own generated IDs may not pass its validation.\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\t// Normalize tool_call_id on tool messages\n\t\tif (normalized.tool_call_id && typeof normalized.tool_call_id === \"string\") {\n\t\t\tnormalized.tool_call_id = sanitizeToolCallId(normalized.tool_call_id);\n\t\t}\n\n\t\t// Normalize tool_calls[].id on assistant messages\n\t\tif (Array.isArray(normalized.tool_calls)) {\n\t\t\tnormalized.tool_calls = (normalized.tool_calls as Record<string, unknown>[]).map(\n\t\t\t\t(tc) => {\n\t\t\t\t\tif (tc.id && typeof tc.id === \"string\") {\n\t\t\t\t\t\treturn { ...tc, id: sanitizeToolCallId(tc.id) };\n\t\t\t\t\t}\n\t\t\t\t\treturn tc;\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\n\t\treturn normalized;\n\t});\n}\n\n/**\n * Strip non-alphanumeric characters and ensure the ID is exactly 9 chars,\n * matching Workers AI's `[a-zA-Z0-9]{9}` validation pattern.\n *\n * **Why this exists:** The Workers AI binding validates `tool_call_id` with\n * a strict `[a-zA-Z0-9]{9}` regex, but it *generates* IDs like\n * `chatcmpl-tool-875d3ec6179676ae` (with dashes, >9 chars). Those IDs are\n * then rejected when sent back in a follow-up request. This is a known\n * Workers AI issue — see workers-ai.md (Issue 3). Once the Workers AI team\n * fixes the validation, this function becomes an idempotent no-op for\n * IDs that already match the pattern.\n */\nfunction sanitizeToolCallId(id: string): string {\n\tconst alphanumeric = id.replace(/[^a-zA-Z0-9]/g, \"\");\n\t// Pad with zeros if too short, truncate if too long\n\treturn alphanumeric.slice(0, 9).padEnd(9, \"0\");\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(binding: WorkersAiBinding): 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\tconst result = await binding.run(model, inputs);\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 = typeof responseObj.response === \"string\" ? responseObj.response : \"\";\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: sanitizeToolCallId(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 which tool call indices we've already emitted an `id` for,\n\t// so subsequent argument deltas don't duplicate the id/type/name fields.\n\tconst emittedToolCallStart = new Set<number>();\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 with only tool_call_id\n\t\t\t\t\t\t\t// sanitization for any tool calls present.\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\tif (tc.id && typeof tc.id === \"string\") {\n\t\t\t\t\t\t\t\t\t\ttc.id = sanitizeToolCallId(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 (!emittedToolCallStart.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\t// Use sanitizeToolCallId so the ID survives round-trip through\n\t\t\t\t\t\t\t\t\t// the binding's strict `[a-zA-Z0-9]{9}` validation.\n\t\t\t\t\t\t\t\t\temittedToolCallStart.add(tcIndex);\n\t\t\t\t\t\t\t\t\tconst rawId = tcId || `call${streamId}${tcIndex}`;\n\t\t\t\t\t\t\t\t\ttoolCallDelta.id = sanitizeToolCallId(rawId);\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"]}
@@ -0,0 +1,48 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } }
2
+
3
+ var _chunk6FBIXTALcjs = require('./chunk-6FBIXTAL.cjs');
4
+
5
+ // src/adapters/grok.ts
6
+
7
+
8
+
9
+
10
+
11
+
12
+ var _aigrok = require('@tanstack/ai-grok');
13
+ function createGrokChat(model, config) {
14
+ return new (0, _aigrok.GrokTextAdapter)(
15
+ {
16
+ apiKey: _nullishCoalesce(config.apiKey, () => ( "unused")),
17
+ fetch: _chunk6FBIXTALcjs.createGatewayFetch.call(void 0, "grok", config)
18
+ },
19
+ model
20
+ );
21
+ }
22
+ function createGrokImage(model, config) {
23
+ return new (0, _aigrok.GrokImageAdapter)(
24
+ {
25
+ apiKey: _nullishCoalesce(config.apiKey, () => ( "unused")),
26
+ fetch: _chunk6FBIXTALcjs.createGatewayFetch.call(void 0, "grok", config)
27
+ },
28
+ model
29
+ );
30
+ }
31
+ function createGrokSummarize(model, config) {
32
+ return new (0, _aigrok.GrokSummarizeAdapter)(
33
+ {
34
+ apiKey: _nullishCoalesce(config.apiKey, () => ( "unused")),
35
+ fetch: _chunk6FBIXTALcjs.createGatewayFetch.call(void 0, "grok", config)
36
+ },
37
+ model
38
+ );
39
+ }
40
+
41
+
42
+
43
+
44
+
45
+
46
+
47
+ exports.GROK_CHAT_MODELS = _aigrok.GROK_CHAT_MODELS; exports.GROK_IMAGE_MODELS = _aigrok.GROK_IMAGE_MODELS; exports.createGrokChat = createGrokChat; exports.createGrokImage = createGrokImage; exports.createGrokSummarize = createGrokSummarize;
48
+ //# sourceMappingURL=chunk-6OXP4IVS.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/home/runner/work/ai/ai/packages/tanstack-ai/dist/chunk-6OXP4IVS.cjs","../src/adapters/grok.ts"],"names":[],"mappings":"AAAA;AACE;AACF,wDAA6B;AAC7B;AACA;ACJA;AACC;AACA;AACA;AACA;AACA;AAAA,2CAIM;AAYA,SAAS,cAAA,CAAe,KAAA,EAAsB,MAAA,EAA2B;AAC/E,EAAA,OAAO,IAAI,4BAAA;AAAA,IACV;AAAA,MACC,MAAA,mBAAQ,MAAA,CAAO,MAAA,UAAU,UAAA;AAAA,MACzB,KAAA,EAAO,kDAAA,MAAmB,EAAQ,MAAM;AAAA,IACzC,CAAA;AAAA,IACA;AAAA,EACD,CAAA;AACD;AAMO,SAAS,eAAA,CAAgB,KAAA,EAAuB,MAAA,EAA2B;AACjF,EAAA,OAAO,IAAI,6BAAA;AAAA,IACV;AAAA,MACC,MAAA,mBAAQ,MAAA,CAAO,MAAA,UAAU,UAAA;AAAA,MACzB,KAAA,EAAO,kDAAA,MAAmB,EAAQ,MAAM;AAAA,IACzC,CAAA;AAAA,IACA;AAAA,EACD,CAAA;AACD;AAMO,SAAS,mBAAA,CAAoB,KAAA,EAA2B,MAAA,EAA2B;AACzF,EAAA,OAAO,IAAI,iCAAA;AAAA,IACV;AAAA,MACC,MAAA,mBAAQ,MAAA,CAAO,MAAA,UAAU,UAAA;AAAA,MACzB,KAAA,EAAO,kDAAA,MAAmB,EAAQ,MAAM;AAAA,IACzC,CAAA;AAAA,IACA;AAAA,EACD,CAAA;AACD;ADlBA;AACA;AACE;AACA;AACA;AACA;AACA;AACF,kPAAC","file":"/home/runner/work/ai/ai/packages/tanstack-ai/dist/chunk-6OXP4IVS.cjs","sourcesContent":[null,"import {\n\tGrokTextAdapter,\n\tGrokImageAdapter,\n\tGrokSummarizeAdapter,\n\tGROK_CHAT_MODELS,\n\tGROK_IMAGE_MODELS,\n\ttype GrokChatModel,\n\ttype GrokImageModel,\n\ttype GrokSummarizeModel,\n} from \"@tanstack/ai-grok\";\nimport { createGatewayFetch, type AiGatewayAdapterConfig } from \"../utils/create-fetcher\";\n\nexport type GrokGatewayConfig = AiGatewayAdapterConfig;\n\n/**\n * Creates a Grok chat adapter which uses Cloudflare AI Gateway.\n * Supports both binding and credential-based configurations.\n *\n * Since GrokTextConfig extends the OpenAI SDK's ClientOptions,\n * we can inject the gateway fetch directly — no subclassing needed.\n */\nexport function createGrokChat(model: GrokChatModel, config: GrokGatewayConfig) {\n\treturn new GrokTextAdapter(\n\t\t{\n\t\t\tapiKey: config.apiKey ?? \"unused\",\n\t\t\tfetch: createGatewayFetch(\"grok\", config),\n\t\t},\n\t\tmodel,\n\t);\n}\n\n/**\n * Creates a Grok image adapter which uses Cloudflare AI Gateway.\n * Supports both binding and credential-based configurations.\n */\nexport function createGrokImage(model: GrokImageModel, config: GrokGatewayConfig) {\n\treturn new GrokImageAdapter(\n\t\t{\n\t\t\tapiKey: config.apiKey ?? \"unused\",\n\t\t\tfetch: createGatewayFetch(\"grok\", config),\n\t\t},\n\t\tmodel,\n\t);\n}\n\n/**\n * Creates a Grok summarize adapter which uses Cloudflare AI Gateway.\n * Supports both binding and credential-based configurations.\n */\nexport function createGrokSummarize(model: GrokSummarizeModel, config: GrokGatewayConfig) {\n\treturn new GrokSummarizeAdapter(\n\t\t{\n\t\t\tapiKey: config.apiKey ?? \"unused\",\n\t\t\tfetch: createGatewayFetch(\"grok\", config),\n\t\t},\n\t\tmodel,\n\t);\n}\n\nexport {\n\tGROK_CHAT_MODELS,\n\tGROK_IMAGE_MODELS,\n\ttype GrokChatModel,\n\ttype GrokImageModel,\n\ttype GrokSummarizeModel,\n};\n"]}
@@ -0,0 +1,65 @@
1
+ // src/adapters/gemini.ts
2
+ import {
3
+ GeminiTextAdapter,
4
+ GeminiImageAdapter,
5
+ GeminiSummarizeAdapter,
6
+ GeminiTTSAdapter,
7
+ GeminiTextModels,
8
+ GeminiImageModels,
9
+ GeminiSummarizeModels,
10
+ GeminiTTSModels
11
+ } from "@tanstack/ai-gemini";
12
+ function buildGeminiGatewayConfig(config) {
13
+ if ("binding" in config) {
14
+ throw new Error(
15
+ "Gemini adapters do not support binding config. The Google GenAI SDK does not support a custom fetch function \u2014 only credential-based config ({ accountId, gatewayId }) is supported. See https://github.com/googleapis/js-genai/issues/999"
16
+ );
17
+ }
18
+ const headers = {};
19
+ if (config.cfApiKey) {
20
+ headers["cf-aig-authorization"] = `Bearer ${config.cfApiKey}`;
21
+ }
22
+ if (config.skipCache) {
23
+ headers["cf-aig-skip-cache"] = "true";
24
+ }
25
+ if (typeof config.cacheTtl === "number") {
26
+ headers["cf-aig-cache-ttl"] = String(config.cacheTtl);
27
+ }
28
+ if (typeof config.customCacheKey === "string") {
29
+ headers["cf-aig-cache-key"] = config.customCacheKey;
30
+ }
31
+ if (typeof config.metadata === "object") {
32
+ headers["cf-aig-metadata"] = JSON.stringify(config.metadata);
33
+ }
34
+ return {
35
+ apiKey: config.apiKey ?? "unused",
36
+ httpOptions: {
37
+ baseUrl: `https://gateway.ai.cloudflare.com/v1/${config.accountId}/${config.gatewayId}/google-ai-studio`,
38
+ headers: Object.keys(headers).length > 0 ? headers : void 0
39
+ }
40
+ };
41
+ }
42
+ function createGeminiChat(model, config) {
43
+ return new GeminiTextAdapter(buildGeminiGatewayConfig(config), model);
44
+ }
45
+ function createGeminiImage(model, config) {
46
+ return new GeminiImageAdapter(buildGeminiGatewayConfig(config), model);
47
+ }
48
+ function createGeminiSummarize(model, config) {
49
+ return new GeminiSummarizeAdapter(buildGeminiGatewayConfig(config), model);
50
+ }
51
+ function createGeminiTts(model, config) {
52
+ return new GeminiTTSAdapter(buildGeminiGatewayConfig(config), model);
53
+ }
54
+
55
+ export {
56
+ GeminiTextModels,
57
+ GeminiImageModels,
58
+ GeminiSummarizeModels,
59
+ GeminiTTSModels,
60
+ createGeminiChat,
61
+ createGeminiImage,
62
+ createGeminiSummarize,
63
+ createGeminiTts
64
+ };
65
+ //# sourceMappingURL=chunk-7AEFXYJG.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/adapters/gemini.ts"],"sourcesContent":["import {\n\tGeminiTextAdapter,\n\tGeminiImageAdapter,\n\tGeminiSummarizeAdapter,\n\tGeminiTTSAdapter,\n\tGeminiTextModels,\n\tGeminiImageModels,\n\tGeminiSummarizeModels,\n\tGeminiTTSModels,\n\ttype GeminiTextModel,\n\ttype GeminiImageModel,\n\ttype GeminiSummarizeModel,\n} from \"@tanstack/ai-gemini\";\n\n/** Derived from GeminiTTSModels since @tanstack/ai-gemini doesn't export a GeminiTTSModel type. */\nexport type GeminiTTSModel = (typeof GeminiTTSModels)[number];\nimport type { AnyTextAdapter } from \"@tanstack/ai\";\nimport type { AiGatewayCredentialsConfig, AiGatewayConfig } from \"../utils/create-fetcher\";\n\n/**\n * Gemini-specific gateway config (credentials only, no binding support).\n * Includes cache control options from AiGatewayConfig.\n * See {@link https://github.com/googleapis/js-genai/issues/999 | googleapis/js-genai#999}.\n */\nexport type GeminiGatewayConfig = AiGatewayCredentialsConfig & AiGatewayConfig;\n\n/**\n * Build Gemini client config that routes through AI Gateway.\n * Since GeminiClientConfig extends GoogleGenAIOptions, we can inject\n * httpOptions.baseUrl directly — no subclassing needed.\n *\n * The Google GenAI SDK doesn't support a custom `fetch` override,\n * so we set the baseUrl to the AI Gateway endpoint for Google AI Studio.\n *\n * Tracking issue: https://github.com/googleapis/js-genai/issues/999\n */\nfunction buildGeminiGatewayConfig(config: GeminiGatewayConfig) {\n\t// Runtime guard: catch binding configs that bypass TypeScript (JS callers, `as any`, etc.)\n\t// We integrate with the Gemini SDK via `httpOptions` (baseUrl + headers), which allows\n\t// gateway routing and cache control but not request interception. A binding config\n\t// requires a custom `fetch` to route through the AI Gateway binding, and the Google\n\t// GenAI SDK doesn't support that yet.\n\tif (\"binding\" in config) {\n\t\tthrow new Error(\n\t\t\t\"Gemini adapters do not support binding config. \" +\n\t\t\t\t\"The Google GenAI SDK does not support a custom fetch function — \" +\n\t\t\t\t\"only credential-based config ({ accountId, gatewayId }) is supported. \" +\n\t\t\t\t\"See https://github.com/googleapis/js-genai/issues/999\",\n\t\t);\n\t}\n\n\tconst headers: Record<string, string> = {};\n\tif (config.cfApiKey) {\n\t\theaders[\"cf-aig-authorization\"] = `Bearer ${config.cfApiKey}`;\n\t}\n\tif (config.skipCache) {\n\t\theaders[\"cf-aig-skip-cache\"] = \"true\";\n\t}\n\tif (typeof config.cacheTtl === \"number\") {\n\t\theaders[\"cf-aig-cache-ttl\"] = String(config.cacheTtl);\n\t}\n\tif (typeof config.customCacheKey === \"string\") {\n\t\theaders[\"cf-aig-cache-key\"] = config.customCacheKey;\n\t}\n\tif (typeof config.metadata === \"object\") {\n\t\theaders[\"cf-aig-metadata\"] = JSON.stringify(config.metadata);\n\t}\n\n\treturn {\n\t\tapiKey: config.apiKey ?? \"unused\",\n\t\thttpOptions: {\n\t\t\tbaseUrl: `https://gateway.ai.cloudflare.com/v1/${config.accountId}/${config.gatewayId}/google-ai-studio`,\n\t\t\theaders: Object.keys(headers).length > 0 ? headers : undefined,\n\t\t},\n\t};\n}\n\n/** Alias for consistency with other providers (AnthropicChatModel, GrokChatModel, etc.) */\nexport type GeminiChatModel = GeminiTextModel;\n\n/**\n * Creates a Gemini adapter which uses Cloudflare AI Gateway.\n * Does not support the AI binding (Google GenAI SDK lacks custom fetch support).\n * See {@link https://github.com/googleapis/js-genai/issues/999 | googleapis/js-genai#999}.\n * @param model The Gemini model to use\n * @param config Configuration options (credentials only)\n */\nexport function createGeminiChat(\n\tmodel: GeminiChatModel,\n\tconfig: GeminiGatewayConfig,\n): AnyTextAdapter {\n\treturn new GeminiTextAdapter(buildGeminiGatewayConfig(config), model);\n}\n\n/**\n * Creates a Gemini Image adapter which uses Cloudflare AI Gateway.\n * Does not support the AI binding (Google GenAI SDK lacks custom fetch support).\n * See {@link https://github.com/googleapis/js-genai/issues/999 | googleapis/js-genai#999}.\n * @param model The Gemini model to use\n * @param config Configuration options (credentials only)\n */\nexport function createGeminiImage(model: GeminiImageModel, config: GeminiGatewayConfig) {\n\treturn new GeminiImageAdapter(buildGeminiGatewayConfig(config), model);\n}\n\n/**\n * Creates a Gemini Summarize adapter which uses Cloudflare AI Gateway.\n * Does not support the AI binding (Google GenAI SDK lacks custom fetch support).\n * See {@link https://github.com/googleapis/js-genai/issues/999 | googleapis/js-genai#999}.\n * @param model The Gemini model to use\n * @param config Configuration options (credentials only)\n */\nexport function createGeminiSummarize(model: GeminiSummarizeModel, config: GeminiGatewayConfig) {\n\treturn new GeminiSummarizeAdapter(buildGeminiGatewayConfig(config), model);\n}\n\n/**\n * Creates a Gemini TTS adapter which uses Cloudflare AI Gateway.\n * Does not support the AI binding (Google GenAI SDK lacks custom fetch support).\n * See {@link https://github.com/googleapis/js-genai/issues/999 | googleapis/js-genai#999}.\n *\n * @experimental Gemini TTS is an experimental feature and may change.\n * @param model The Gemini TTS model to use\n * @param config Configuration options (credentials only)\n */\nexport function createGeminiTts(model: GeminiTTSModel, config: GeminiGatewayConfig) {\n\treturn new GeminiTTSAdapter(buildGeminiGatewayConfig(config), model);\n}\n\nexport {\n\tGeminiTextModels,\n\tGeminiImageModels,\n\tGeminiSummarizeModels,\n\tGeminiTTSModels,\n\ttype GeminiTextModel,\n\ttype GeminiImageModel,\n\ttype GeminiSummarizeModel,\n};\n"],"mappings":";AAAA;AAAA,EACC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAIM;AAwBP,SAAS,yBAAyB,QAA6B;AAM9D,MAAI,aAAa,QAAQ;AACxB,UAAM,IAAI;AAAA,MACT;AAAA,IAID;AAAA,EACD;AAEA,QAAM,UAAkC,CAAC;AACzC,MAAI,OAAO,UAAU;AACpB,YAAQ,sBAAsB,IAAI,UAAU,OAAO,QAAQ;AAAA,EAC5D;AACA,MAAI,OAAO,WAAW;AACrB,YAAQ,mBAAmB,IAAI;AAAA,EAChC;AACA,MAAI,OAAO,OAAO,aAAa,UAAU;AACxC,YAAQ,kBAAkB,IAAI,OAAO,OAAO,QAAQ;AAAA,EACrD;AACA,MAAI,OAAO,OAAO,mBAAmB,UAAU;AAC9C,YAAQ,kBAAkB,IAAI,OAAO;AAAA,EACtC;AACA,MAAI,OAAO,OAAO,aAAa,UAAU;AACxC,YAAQ,iBAAiB,IAAI,KAAK,UAAU,OAAO,QAAQ;AAAA,EAC5D;AAEA,SAAO;AAAA,IACN,QAAQ,OAAO,UAAU;AAAA,IACzB,aAAa;AAAA,MACZ,SAAS,wCAAwC,OAAO,SAAS,IAAI,OAAO,SAAS;AAAA,MACrF,SAAS,OAAO,KAAK,OAAO,EAAE,SAAS,IAAI,UAAU;AAAA,IACtD;AAAA,EACD;AACD;AAYO,SAAS,iBACf,OACA,QACiB;AACjB,SAAO,IAAI,kBAAkB,yBAAyB,MAAM,GAAG,KAAK;AACrE;AASO,SAAS,kBAAkB,OAAyB,QAA6B;AACvF,SAAO,IAAI,mBAAmB,yBAAyB,MAAM,GAAG,KAAK;AACtE;AASO,SAAS,sBAAsB,OAA6B,QAA6B;AAC/F,SAAO,IAAI,uBAAuB,yBAAyB,MAAM,GAAG,KAAK;AAC1E;AAWO,SAAS,gBAAgB,OAAuB,QAA6B;AACnF,SAAO,IAAI,iBAAiB,yBAAyB,MAAM,GAAG,KAAK;AACpE;","names":[]}
@@ -0,0 +1,42 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }// src/utils/workers-ai-rest.ts
2
+ var WORKERS_AI_REST_BASE = "https://api.cloudflare.com/client/v4/accounts";
3
+ async function workersAiRestFetch(config, model, body, options) {
4
+ const response = await fetch(`${WORKERS_AI_REST_BASE}/${config.accountId}/ai/run/${model}`, {
5
+ method: "POST",
6
+ headers: {
7
+ Authorization: `Bearer ${config.apiKey}`,
8
+ "Content-Type": "application/json"
9
+ },
10
+ body: JSON.stringify(body),
11
+ signal: _optionalChain([options, 'optionalAccess', _ => _.signal])
12
+ });
13
+ if (!response.ok) {
14
+ const errorText = await response.text();
15
+ const label = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _2 => _2.label]), () => ( "Workers AI"));
16
+ throw new Error(`${label} request failed (${response.status}): ${errorText}`);
17
+ }
18
+ return response;
19
+ }
20
+ async function workersAiRestFetchBinary(config, model, audioBytes, contentType, options) {
21
+ const response = await fetch(`${WORKERS_AI_REST_BASE}/${config.accountId}/ai/run/${model}`, {
22
+ method: "POST",
23
+ headers: {
24
+ Authorization: `Bearer ${config.apiKey}`,
25
+ "Content-Type": contentType
26
+ },
27
+ body: audioBytes,
28
+ signal: _optionalChain([options, 'optionalAccess', _3 => _3.signal])
29
+ });
30
+ if (!response.ok) {
31
+ const errorText = await response.text();
32
+ const label = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _4 => _4.label]), () => ( "Workers AI"));
33
+ throw new Error(`${label} request failed (${response.status}): ${errorText}`);
34
+ }
35
+ return response;
36
+ }
37
+
38
+
39
+
40
+
41
+ exports.workersAiRestFetch = workersAiRestFetch; exports.workersAiRestFetchBinary = workersAiRestFetchBinary;
42
+ //# sourceMappingURL=chunk-7HSUHP63.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/home/runner/work/ai/ai/packages/tanstack-ai/dist/chunk-7HSUHP63.cjs","../src/utils/workers-ai-rest.ts"],"names":[],"mappings":"AAAA;ACMA,IAAM,qBAAA,EAAuB,+CAAA;AAkB7B,MAAA,SAAsB,kBAAA,CACrB,MAAA,EACA,KAAA,EACA,IAAA,EACA,OAAA,EACoB;AACpB,EAAA,MAAM,SAAA,EAAW,MAAM,KAAA,CAAM,CAAA,EAAA;AACpB,IAAA;AACC,IAAA;AACiB,MAAA;AACT,MAAA;AACjB,IAAA;AACyB,IAAA;AACR,IAAA;AACjB,EAAA;AAEiB,EAAA;AACO,IAAA;AACD,IAAA;AACC,IAAA;AACzB,EAAA;AAEO,EAAA;AACR;AAgBsB;AAOQ,EAAA;AACpB,IAAA;AACC,IAAA;AACiB,MAAA;AACT,MAAA;AACjB,IAAA;AACM,IAAA;AACW,IAAA;AACjB,EAAA;AAEiB,EAAA;AACO,IAAA;AACD,IAAA;AACC,IAAA;AACzB,EAAA;AAEO,EAAA;AACR;ADnDgC;AACA;AACA;AACA;AACA","file":"/home/runner/work/ai/ai/packages/tanstack-ai/dist/chunk-7HSUHP63.cjs","sourcesContent":[null,"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"]}
@@ -0,0 +1,31 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } }
2
+
3
+ var _chunk6FBIXTALcjs = require('./chunk-6FBIXTAL.cjs');
4
+
5
+ // src/adapters/anthropic.ts
6
+
7
+
8
+
9
+
10
+ var _aianthropic = require('@tanstack/ai-anthropic');
11
+ function buildAnthropicConfig(config) {
12
+ return {
13
+ apiKey: _nullishCoalesce(config.apiKey, () => ( "unused")),
14
+ fetch: _chunk6FBIXTALcjs.createGatewayFetch.call(void 0, "anthropic", config, {
15
+ "anthropic-version": _nullishCoalesce(config.anthropicVersion, () => ( "2023-06-01"))
16
+ })
17
+ };
18
+ }
19
+ function createAnthropicChat(model, config) {
20
+ return new (0, _aianthropic.AnthropicTextAdapter)(buildAnthropicConfig(config), model);
21
+ }
22
+ function createAnthropicSummarize(model, config) {
23
+ return new (0, _aianthropic.AnthropicSummarizeAdapter)(buildAnthropicConfig(config), model);
24
+ }
25
+
26
+
27
+
28
+
29
+
30
+ exports.ANTHROPIC_MODELS = _aianthropic.ANTHROPIC_MODELS; exports.createAnthropicChat = createAnthropicChat; exports.createAnthropicSummarize = createAnthropicSummarize;
31
+ //# sourceMappingURL=chunk-AHXFO2BB.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/home/runner/work/ai/ai/packages/tanstack-ai/dist/chunk-AHXFO2BB.cjs","../src/adapters/anthropic.ts"],"names":[],"mappings":"AAAA;AACE;AACF,wDAA6B;AAC7B;AACA;ACJA;AACC;AACA;AACA;AAAA,qDAEM;AAMP,SAAS,oBAAA,CAAqB,MAAA,EAAgC;AAC7D,EAAA,OAAO;AAAA,IACN,MAAA,mBAAQ,MAAA,CAAO,MAAA,UAAU,UAAA;AAAA,IACzB,KAAA,EAAO,kDAAA,WAAmB,EAAa,MAAA,EAAQ;AAAA,MAC9C,mBAAA,mBAAqB,MAAA,CAAO,gBAAA,UAAoB;AAAA,IACjD,CAAC;AAAA,EACF,CAAA;AACD;AASO,SAAS,mBAAA,CACf,KAAA,EACA,MAAA,EACiB;AACjB,EAAA,OAAO,IAAI,sCAAA,CAAqB,oBAAA,CAAqB,MAAM,CAAA,EAAG,KAAK,CAAA;AACpE;AAMO,SAAS,wBAAA,CACf,KAAA,EACA,MAAA,EACC;AACD,EAAA,OAAO,IAAI,2CAAA,CAA0B,oBAAA,CAAqB,MAAM,CAAA,EAAG,KAAK,CAAA;AACzE;ADnBA;AACA;AACE;AACA;AACA;AACF,yKAAC","file":"/home/runner/work/ai/ai/packages/tanstack-ai/dist/chunk-AHXFO2BB.cjs","sourcesContent":[null,"import {\n\tAnthropicTextAdapter,\n\tAnthropicSummarizeAdapter,\n\tANTHROPIC_MODELS,\n\ttype AnthropicChatModel,\n} from \"@tanstack/ai-anthropic\";\nimport type { AnyTextAdapter } from \"@tanstack/ai\";\nimport { createGatewayFetch, type AiGatewayAdapterConfig } from \"../utils/create-fetcher\";\n\nexport type AnthropicGatewayConfig = AiGatewayAdapterConfig & { anthropicVersion?: string };\n\nfunction buildAnthropicConfig(config: AnthropicGatewayConfig) {\n\treturn {\n\t\tapiKey: config.apiKey ?? \"unused\",\n\t\tfetch: createGatewayFetch(\"anthropic\", config, {\n\t\t\t\"anthropic-version\": config.anthropicVersion ?? \"2023-06-01\",\n\t\t}),\n\t};\n}\n\n/**\n * Creates an Anthropic chat adapter which uses Cloudflare AI Gateway.\n * Supports both binding and credential-based configurations.\n *\n * Since AnthropicTextConfig extends the Anthropic SDK's ClientOptions,\n * we can inject the gateway fetch directly — no subclassing needed.\n */\nexport function createAnthropicChat(\n\tmodel: AnthropicChatModel,\n\tconfig: AnthropicGatewayConfig,\n): AnyTextAdapter {\n\treturn new AnthropicTextAdapter(buildAnthropicConfig(config), model);\n}\n\n/**\n * Creates an Anthropic summarize adapter which uses Cloudflare AI Gateway.\n * Supports both binding and credential-based configurations.\n */\nexport function createAnthropicSummarize(\n\tmodel: AnthropicChatModel,\n\tconfig: AnthropicGatewayConfig,\n) {\n\treturn new AnthropicSummarizeAdapter(buildAnthropicConfig(config), model);\n}\n\nexport { ANTHROPIC_MODELS, type AnthropicChatModel };\n"]}