@cloudflare/tanstack-ai 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. package/README.md +5 -5
  2. package/dist/_tsup-dts-rollup.d.cts +22 -14
  3. package/dist/_tsup-dts-rollup.d.ts +22 -14
  4. package/dist/adapters/anthropic.cjs +3 -3
  5. package/dist/adapters/anthropic.js +2 -2
  6. package/dist/adapters/gemini.cjs +2 -2
  7. package/dist/adapters/gemini.js +1 -1
  8. package/dist/adapters/grok.cjs +3 -3
  9. package/dist/adapters/grok.js +2 -2
  10. package/dist/adapters/openai.cjs +3 -3
  11. package/dist/adapters/openai.js +2 -2
  12. package/dist/adapters/openrouter.cjs +3 -3
  13. package/dist/adapters/openrouter.js +2 -2
  14. package/dist/adapters/workers-ai-image.cjs +3 -3
  15. package/dist/adapters/workers-ai-image.js +2 -2
  16. package/dist/adapters/workers-ai-summarize.cjs +3 -3
  17. package/dist/adapters/workers-ai-summarize.js +2 -2
  18. package/dist/adapters/workers-ai-transcription.cjs +3 -3
  19. package/dist/adapters/workers-ai-transcription.js +2 -2
  20. package/dist/adapters/workers-ai-tts.cjs +3 -3
  21. package/dist/adapters/workers-ai-tts.js +2 -2
  22. package/dist/adapters/workers-ai.cjs +3 -3
  23. package/dist/adapters/workers-ai.js +2 -2
  24. package/dist/{chunk-XI2BOYEI.js → chunk-2QN5XFI2.js} +5 -3
  25. package/dist/chunk-2QN5XFI2.js.map +1 -0
  26. package/dist/{chunk-BD4CRW3Q.js → chunk-3KUHT3K6.js} +128 -12
  27. package/dist/chunk-3KUHT3K6.js.map +1 -0
  28. package/dist/{chunk-VTDJEUFS.js → chunk-5A2F2QCL.js} +2 -2
  29. package/dist/{chunk-IWZJCLOE.cjs → chunk-7KZQWFUE.cjs} +3 -3
  30. package/dist/{chunk-IWZJCLOE.cjs.map → chunk-7KZQWFUE.cjs.map} +1 -1
  31. package/dist/{chunk-O2C4CR57.cjs → chunk-7SKFZ4SU.cjs} +8 -5
  32. package/dist/chunk-7SKFZ4SU.cjs.map +1 -0
  33. package/dist/{chunk-LBYDBPHY.cjs → chunk-ARLHMZ5B.cjs} +7 -5
  34. package/dist/chunk-ARLHMZ5B.cjs.map +1 -0
  35. package/dist/{chunk-XKSFDPDY.cjs → chunk-AYTKQRE4.cjs} +7 -5
  36. package/dist/chunk-AYTKQRE4.cjs.map +1 -0
  37. package/dist/{chunk-YIA5B3QT.js → chunk-DSBHOQQ4.js} +2 -2
  38. package/dist/{chunk-RQT7M6MU.js → chunk-DZZVI4W5.js} +5 -3
  39. package/dist/chunk-DZZVI4W5.js.map +1 -0
  40. package/dist/{chunk-U5YJQYLZ.cjs → chunk-ICHJ2G63.cjs} +3 -3
  41. package/dist/{chunk-U5YJQYLZ.cjs.map → chunk-ICHJ2G63.cjs.map} +1 -1
  42. package/dist/{chunk-7T4CVHKD.cjs → chunk-IDPLTBQX.cjs} +7 -5
  43. package/dist/chunk-IDPLTBQX.cjs.map +1 -0
  44. package/dist/{chunk-GOU66I5T.cjs → chunk-IMURYJRF.cjs} +15 -3
  45. package/dist/chunk-IMURYJRF.cjs.map +1 -0
  46. package/dist/{chunk-7AEFXYJG.js → chunk-J5DSSZTO.js} +9 -3
  47. package/dist/chunk-J5DSSZTO.js.map +1 -0
  48. package/dist/{chunk-QRHKPL75.js → chunk-JK5ZFOZZ.js} +5 -2
  49. package/dist/chunk-JK5ZFOZZ.js.map +1 -0
  50. package/dist/{chunk-BPWGWJJV.cjs → chunk-LG2I3ANT.cjs} +140 -24
  51. package/dist/chunk-LG2I3ANT.cjs.map +1 -0
  52. package/dist/{chunk-F5YJMXZR.js → chunk-O7MBRPCW.js} +14 -2
  53. package/dist/chunk-O7MBRPCW.js.map +1 -0
  54. package/dist/{chunk-2AJ6LV2D.js → chunk-OKU3GB5P.js} +5 -3
  55. package/dist/chunk-OKU3GB5P.js.map +1 -0
  56. package/dist/{chunk-PLTFCUMO.js → chunk-T5KPTTLK.js} +5 -3
  57. package/dist/chunk-T5KPTTLK.js.map +1 -0
  58. package/dist/{chunk-M6NETFDR.cjs → chunk-UWBO33IG.cjs} +5 -5
  59. package/dist/{chunk-M6NETFDR.cjs.map → chunk-UWBO33IG.cjs.map} +1 -1
  60. package/dist/{chunk-RGDUK5KX.cjs → chunk-V4LKHWJA.cjs} +9 -3
  61. package/dist/chunk-V4LKHWJA.cjs.map +1 -0
  62. package/dist/{chunk-XCNU7EEC.js → chunk-Y7W7NDVS.js} +2 -2
  63. package/dist/{chunk-LIUHRGEK.cjs → chunk-ZHTFBZXJ.cjs} +7 -5
  64. package/dist/chunk-ZHTFBZXJ.cjs.map +1 -0
  65. package/dist/index.cjs +12 -12
  66. package/dist/index.js +11 -11
  67. package/package.json +14 -14
  68. package/dist/chunk-2AJ6LV2D.js.map +0 -1
  69. package/dist/chunk-7AEFXYJG.js.map +0 -1
  70. package/dist/chunk-7T4CVHKD.cjs.map +0 -1
  71. package/dist/chunk-BD4CRW3Q.js.map +0 -1
  72. package/dist/chunk-BPWGWJJV.cjs.map +0 -1
  73. package/dist/chunk-F5YJMXZR.js.map +0 -1
  74. package/dist/chunk-GOU66I5T.cjs.map +0 -1
  75. package/dist/chunk-LBYDBPHY.cjs.map +0 -1
  76. package/dist/chunk-LIUHRGEK.cjs.map +0 -1
  77. package/dist/chunk-O2C4CR57.cjs.map +0 -1
  78. package/dist/chunk-PLTFCUMO.js.map +0 -1
  79. package/dist/chunk-QRHKPL75.js.map +0 -1
  80. package/dist/chunk-RGDUK5KX.cjs.map +0 -1
  81. package/dist/chunk-RQT7M6MU.js.map +0 -1
  82. package/dist/chunk-XI2BOYEI.js.map +0 -1
  83. package/dist/chunk-XKSFDPDY.cjs.map +0 -1
  84. /package/dist/{chunk-VTDJEUFS.js.map → chunk-5A2F2QCL.js.map} +0 -0
  85. /package/dist/{chunk-YIA5B3QT.js.map → chunk-DSBHOQQ4.js.map} +0 -0
  86. /package/dist/{chunk-XCNU7EEC.js.map → chunk-Y7W7NDVS.js.map} +0 -0
@@ -1 +1 @@
1
- {"version":3,"sources":["/home/runner/work/ai/ai/packages/tanstack-ai/dist/chunk-U5YJQYLZ.cjs","../src/adapters/openai.ts"],"names":[],"mappings":"AAAA;AACE;AACF,wDAA6B;AAC7B;AACA;ACJA;AACC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA,+CAMM;AASP,SAAS,iBAAA,CAAkB,QAAA,EAAkB,MAAA,EAA6B;AACzE,EAAA,OAAO;AAAA,IACN,MAAA,mBAAQ,MAAA,CAAO,MAAA,UAAU,UAAA;AAAA,IACzB,KAAA,EAAO,kDAAA,QAAmB,EAAU,MAAM;AAAA,EAC3C,CAAA;AACD;AAQO,SAAS,gBAAA,CAAiB,KAAA,EAAwB,MAAA,EAA6B;AACrF,EAAA,OAAO,IAAI,gCAAA,CAAkB,iBAAA,CAAkB,QAAA,EAAU,MAAM,CAAA,EAAG,KAAK,CAAA;AACxE;AAOO,SAAS,qBAAA,CAAsB,KAAA,EAAwB,MAAA,EAA6B;AAC1F,EAAA,OAAO,IAAI,qCAAA,CAAuB,iBAAA,CAAkB,QAAA,EAAU,MAAM,CAAA,EAAG,KAAK,CAAA;AAC7E;AAOO,SAAS,iBAAA,CAAkB,KAAA,EAAyB,MAAA,EAA6B;AACvF,EAAA,OAAO,IAAI,iCAAA,CAAmB,iBAAA,CAAkB,QAAA,EAAU,MAAM,CAAA,EAAG,KAAK,CAAA;AACzE;AAOO,SAAS,yBAAA,CACf,KAAA,EACA,MAAA,EACC;AACD,EAAA,OAAO,IAAI,yCAAA,CAA2B,iBAAA,CAAkB,QAAA,EAAU,MAAM,CAAA,EAAG,KAAK,CAAA;AACjF;AAOO,SAAS,eAAA,CAAgB,KAAA,EAAuB,MAAA,EAA6B;AACnF,EAAA,OAAO,IAAI,+BAAA,CAAiB,iBAAA,CAAkB,QAAA,EAAU,MAAM,CAAA,EAAG,KAAK,CAAA;AACvE;AAOO,SAAS,iBAAA,CAAkB,KAAA,EAAyB,MAAA,EAA6B;AACvF,EAAA,OAAO,IAAI,iCAAA,CAAmB,iBAAA,CAAkB,QAAA,EAAU,MAAM,CAAA,EAAG,KAAK,CAAA;AACzE;AD/CA;AACA;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACF,smBAAC","file":"/home/runner/work/ai/ai/packages/tanstack-ai/dist/chunk-U5YJQYLZ.cjs","sourcesContent":[null,"import {\n\tOpenAIImageAdapter,\n\tOpenAISummarizeAdapter,\n\tOpenAITextAdapter,\n\tOpenAITranscriptionAdapter,\n\tOpenAITTSAdapter,\n\tOpenAIVideoAdapter,\n\tOPENAI_CHAT_MODELS,\n\tOPENAI_IMAGE_MODELS,\n\tOPENAI_TRANSCRIPTION_MODELS,\n\tOPENAI_TTS_MODELS,\n\tOPENAI_VIDEO_MODELS,\n\ttype OpenAIChatModel,\n\ttype OpenAIImageModel,\n\ttype OpenAITranscriptionModel,\n\ttype OpenAITTSModel,\n\ttype OpenAIVideoModel,\n} from \"@tanstack/ai-openai\";\nimport { createGatewayFetch, type AiGatewayAdapterConfig } from \"../utils/create-fetcher\";\n\nexport type OpenAiGatewayConfig = AiGatewayAdapterConfig;\n\n/**\n * Builds an OpenAI-compatible config that injects the gateway fetch.\n * OpenAITextConfig extends OpenAI SDK's ClientOptions, which supports a `fetch` parameter.\n */\nfunction buildOpenAiConfig(provider: string, config: OpenAiGatewayConfig) {\n\treturn {\n\t\tapiKey: config.apiKey ?? \"unused\",\n\t\tfetch: createGatewayFetch(provider, config),\n\t};\n}\n\n/**\n * Creates an OpenAI chat adapter which uses Cloudflare AI Gateway.\n * Supports both binding and credential-based configurations.\n * @param model The OpenAI model to use\n * @param config Configuration options\n */\nexport function createOpenAiChat(model: OpenAIChatModel, config: OpenAiGatewayConfig) {\n\treturn new OpenAITextAdapter(buildOpenAiConfig(\"openai\", config), model);\n}\n\n/**\n * Creates an OpenAI summarize adapter which uses Cloudflare AI Gateway.\n * @param model The OpenAI model to use\n * @param config Configuration options\n */\nexport function createOpenAiSummarize(model: OpenAIChatModel, config: OpenAiGatewayConfig) {\n\treturn new OpenAISummarizeAdapter(buildOpenAiConfig(\"openai\", config), model);\n}\n\n/**\n * Creates an OpenAI image adapter which uses Cloudflare AI Gateway.\n * @param model The OpenAI image model to use\n * @param config Configuration options\n */\nexport function createOpenAiImage(model: OpenAIImageModel, config: OpenAiGatewayConfig) {\n\treturn new OpenAIImageAdapter(buildOpenAiConfig(\"openai\", config), model);\n}\n\n/**\n * Creates an OpenAI transcription adapter which uses Cloudflare AI Gateway.\n * @param model The OpenAI transcription model to use\n * @param config Configuration options\n */\nexport function createOpenAiTranscription(\n\tmodel: OpenAITranscriptionModel,\n\tconfig: OpenAiGatewayConfig,\n) {\n\treturn new OpenAITranscriptionAdapter(buildOpenAiConfig(\"openai\", config), model);\n}\n\n/**\n * Creates an OpenAI TTS adapter which uses Cloudflare AI Gateway.\n * @param model The OpenAI TTS model to use\n * @param config Configuration options\n */\nexport function createOpenAiTts(model: OpenAITTSModel, config: OpenAiGatewayConfig) {\n\treturn new OpenAITTSAdapter(buildOpenAiConfig(\"openai\", config), model);\n}\n\n/**\n * Creates an OpenAI video adapter which uses Cloudflare AI Gateway.\n * @param model The OpenAI video model to use\n * @param config Configuration options\n */\nexport function createOpenAiVideo(model: OpenAIVideoModel, config: OpenAiGatewayConfig) {\n\treturn new OpenAIVideoAdapter(buildOpenAiConfig(\"openai\", config), model);\n}\n\nexport {\n\tOPENAI_CHAT_MODELS,\n\tOPENAI_IMAGE_MODELS,\n\tOPENAI_TRANSCRIPTION_MODELS,\n\tOPENAI_TTS_MODELS,\n\tOPENAI_VIDEO_MODELS,\n\ttype OpenAIChatModel,\n\ttype OpenAIImageModel,\n\ttype OpenAITranscriptionModel,\n\ttype OpenAITTSModel,\n\ttype OpenAIVideoModel,\n};\n"]}
1
+ {"version":3,"sources":["/home/runner/work/ai/ai/packages/tanstack-ai/dist/chunk-ICHJ2G63.cjs","../src/adapters/openai.ts"],"names":[],"mappings":"AAAA;AACE;AACF,wDAA6B;AAC7B;AACA;ACJA;AACC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA,+CAMM;AASP,SAAS,iBAAA,CAAkB,QAAA,EAAkB,MAAA,EAA6B;AACzE,EAAA,OAAO;AAAA,IACN,MAAA,mBAAQ,MAAA,CAAO,MAAA,UAAU,UAAA;AAAA,IACzB,KAAA,EAAO,kDAAA,QAAmB,EAAU,MAAM;AAAA,EAC3C,CAAA;AACD;AAQO,SAAS,gBAAA,CAAiB,KAAA,EAAwB,MAAA,EAA6B;AACrF,EAAA,OAAO,IAAI,gCAAA,CAAkB,iBAAA,CAAkB,QAAA,EAAU,MAAM,CAAA,EAAG,KAAK,CAAA;AACxE;AAOO,SAAS,qBAAA,CAAsB,KAAA,EAAwB,MAAA,EAA6B;AAC1F,EAAA,OAAO,IAAI,qCAAA,CAAuB,iBAAA,CAAkB,QAAA,EAAU,MAAM,CAAA,EAAG,KAAK,CAAA;AAC7E;AAOO,SAAS,iBAAA,CAAkB,KAAA,EAAyB,MAAA,EAA6B;AACvF,EAAA,OAAO,IAAI,iCAAA,CAAmB,iBAAA,CAAkB,QAAA,EAAU,MAAM,CAAA,EAAG,KAAK,CAAA;AACzE;AAOO,SAAS,yBAAA,CACf,KAAA,EACA,MAAA,EACC;AACD,EAAA,OAAO,IAAI,yCAAA,CAA2B,iBAAA,CAAkB,QAAA,EAAU,MAAM,CAAA,EAAG,KAAK,CAAA;AACjF;AAOO,SAAS,eAAA,CAAgB,KAAA,EAAuB,MAAA,EAA6B;AACnF,EAAA,OAAO,IAAI,+BAAA,CAAiB,iBAAA,CAAkB,QAAA,EAAU,MAAM,CAAA,EAAG,KAAK,CAAA;AACvE;AAOO,SAAS,iBAAA,CAAkB,KAAA,EAAyB,MAAA,EAA6B;AACvF,EAAA,OAAO,IAAI,iCAAA,CAAmB,iBAAA,CAAkB,QAAA,EAAU,MAAM,CAAA,EAAG,KAAK,CAAA;AACzE;AD/CA;AACA;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACF,smBAAC","file":"/home/runner/work/ai/ai/packages/tanstack-ai/dist/chunk-ICHJ2G63.cjs","sourcesContent":[null,"import {\n\tOpenAIImageAdapter,\n\tOpenAISummarizeAdapter,\n\tOpenAITextAdapter,\n\tOpenAITranscriptionAdapter,\n\tOpenAITTSAdapter,\n\tOpenAIVideoAdapter,\n\tOPENAI_CHAT_MODELS,\n\tOPENAI_IMAGE_MODELS,\n\tOPENAI_TRANSCRIPTION_MODELS,\n\tOPENAI_TTS_MODELS,\n\tOPENAI_VIDEO_MODELS,\n\ttype OpenAIChatModel,\n\ttype OpenAIImageModel,\n\ttype OpenAITranscriptionModel,\n\ttype OpenAITTSModel,\n\ttype OpenAIVideoModel,\n} from \"@tanstack/ai-openai\";\nimport { createGatewayFetch, type AiGatewayAdapterConfig } from \"../utils/create-fetcher\";\n\nexport type OpenAiGatewayConfig = AiGatewayAdapterConfig;\n\n/**\n * Builds an OpenAI-compatible config that injects the gateway fetch.\n * OpenAITextConfig extends OpenAI SDK's ClientOptions, which supports a `fetch` parameter.\n */\nfunction buildOpenAiConfig(provider: string, config: OpenAiGatewayConfig) {\n\treturn {\n\t\tapiKey: config.apiKey ?? \"unused\",\n\t\tfetch: createGatewayFetch(provider, config),\n\t};\n}\n\n/**\n * Creates an OpenAI chat adapter which uses Cloudflare AI Gateway.\n * Supports both binding and credential-based configurations.\n * @param model The OpenAI model to use\n * @param config Configuration options\n */\nexport function createOpenAiChat(model: OpenAIChatModel, config: OpenAiGatewayConfig) {\n\treturn new OpenAITextAdapter(buildOpenAiConfig(\"openai\", config), model);\n}\n\n/**\n * Creates an OpenAI summarize adapter which uses Cloudflare AI Gateway.\n * @param model The OpenAI model to use\n * @param config Configuration options\n */\nexport function createOpenAiSummarize(model: OpenAIChatModel, config: OpenAiGatewayConfig) {\n\treturn new OpenAISummarizeAdapter(buildOpenAiConfig(\"openai\", config), model);\n}\n\n/**\n * Creates an OpenAI image adapter which uses Cloudflare AI Gateway.\n * @param model The OpenAI image model to use\n * @param config Configuration options\n */\nexport function createOpenAiImage(model: OpenAIImageModel, config: OpenAiGatewayConfig) {\n\treturn new OpenAIImageAdapter(buildOpenAiConfig(\"openai\", config), model);\n}\n\n/**\n * Creates an OpenAI transcription adapter which uses Cloudflare AI Gateway.\n * @param model The OpenAI transcription model to use\n * @param config Configuration options\n */\nexport function createOpenAiTranscription(\n\tmodel: OpenAITranscriptionModel,\n\tconfig: OpenAiGatewayConfig,\n) {\n\treturn new OpenAITranscriptionAdapter(buildOpenAiConfig(\"openai\", config), model);\n}\n\n/**\n * Creates an OpenAI TTS adapter which uses Cloudflare AI Gateway.\n * @param model The OpenAI TTS model to use\n * @param config Configuration options\n */\nexport function createOpenAiTts(model: OpenAITTSModel, config: OpenAiGatewayConfig) {\n\treturn new OpenAITTSAdapter(buildOpenAiConfig(\"openai\", config), model);\n}\n\n/**\n * Creates an OpenAI video adapter which uses Cloudflare AI Gateway.\n * @param model The OpenAI video model to use\n * @param config Configuration options\n */\nexport function createOpenAiVideo(model: OpenAIVideoModel, config: OpenAiGatewayConfig) {\n\treturn new OpenAIVideoAdapter(buildOpenAiConfig(\"openai\", config), model);\n}\n\nexport {\n\tOPENAI_CHAT_MODELS,\n\tOPENAI_IMAGE_MODELS,\n\tOPENAI_TRANSCRIPTION_MODELS,\n\tOPENAI_TTS_MODELS,\n\tOPENAI_VIDEO_MODELS,\n\ttype OpenAIChatModel,\n\ttype OpenAIImageModel,\n\ttype OpenAITranscriptionModel,\n\ttype OpenAITTSModel,\n\ttype OpenAIVideoModel,\n};\n"]}
@@ -5,7 +5,8 @@ var _chunk7HSUHP63cjs = require('./chunk-7HSUHP63.cjs');
5
5
 
6
6
 
7
7
 
8
- var _chunkGOU66I5Tcjs = require('./chunk-GOU66I5T.cjs');
8
+
9
+ var _chunkIMURYJRFcjs = require('./chunk-IMURYJRF.cjs');
9
10
 
10
11
 
11
12
  var _chunk4DE2IREAcjs = require('./chunk-4DE2IREA.cjs');
@@ -17,16 +18,17 @@ var WorkersAiSummarizeAdapter = class extends _adapters.BaseSummarizeAdapter {
17
18
  super({}, model);
18
19
  _chunk4DE2IREAcjs.__publicField.call(void 0, this, "name", "workers-ai-summarize");
19
20
  _chunk4DE2IREAcjs.__publicField.call(void 0, this, "adapterConfig");
21
+ _chunkIMURYJRFcjs.validateWorkersAiConfig.call(void 0, config);
20
22
  this.adapterConfig = config;
21
23
  }
22
24
  async summarize(options) {
23
25
  const { text, maxLength } = options;
24
26
  const payload = { input_text: text };
25
27
  if (maxLength != null) payload.max_length = maxLength;
26
- if (_chunkGOU66I5Tcjs.isDirectBindingConfig.call(void 0, this.adapterConfig)) {
28
+ if (_chunkIMURYJRFcjs.isDirectBindingConfig.call(void 0, this.adapterConfig)) {
27
29
  return this.summarizeViaBinding(payload);
28
30
  }
29
- if (_chunkGOU66I5Tcjs.isDirectCredentialsConfig.call(void 0, this.adapterConfig)) {
31
+ if (_chunkIMURYJRFcjs.isDirectCredentialsConfig.call(void 0, this.adapterConfig)) {
30
32
  return this.summarizeViaRest(payload);
31
33
  }
32
34
  return this.summarizeViaGateway(payload);
@@ -46,7 +48,7 @@ var WorkersAiSummarizeAdapter = class extends _adapters.BaseSummarizeAdapter {
46
48
  }
47
49
  async summarizeViaGateway(payload) {
48
50
  const gatewayConfig = this.adapterConfig;
49
- const gatewayFetch = _chunkGOU66I5Tcjs.createGatewayFetch.call(void 0, "workers-ai", gatewayConfig);
51
+ const gatewayFetch = _chunkIMURYJRFcjs.createGatewayFetch.call(void 0, "workers-ai", gatewayConfig);
50
52
  const response = await gatewayFetch("https://api.cloudflare.com/v1/ai/summarization", {
51
53
  method: "POST",
52
54
  body: JSON.stringify({
@@ -81,4 +83,4 @@ function createWorkersAiSummarize(model, config) {
81
83
 
82
84
 
83
85
  exports.WorkersAiSummarizeAdapter = WorkersAiSummarizeAdapter; exports.createWorkersAiSummarize = createWorkersAiSummarize;
84
- //# sourceMappingURL=chunk-7T4CVHKD.cjs.map
86
+ //# sourceMappingURL=chunk-IDPLTBQX.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/home/runner/work/ai/ai/packages/tanstack-ai/dist/chunk-IDPLTBQX.cjs","../src/adapters/workers-ai-summarize.ts"],"names":[],"mappings":"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","file":"/home/runner/work/ai/ai/packages/tanstack-ai/dist/chunk-IDPLTBQX.cjs","sourcesContent":[null,"import { BaseSummarizeAdapter } from \"@tanstack/ai/adapters\";\nimport type { SummarizationOptions, SummarizationResult } from \"@tanstack/ai\";\nimport {\n\ttype WorkersAiAdapterConfig,\n\ttype WorkersAiDirectBindingConfig,\n\ttype WorkersAiDirectCredentialsConfig,\n\ttype AiGatewayAdapterConfig,\n\tcreateGatewayFetch,\n\tisDirectBindingConfig,\n\tisDirectCredentialsConfig,\n\tvalidateWorkersAiConfig,\n} from \"../utils/create-fetcher\";\nimport { workersAiRestFetch } from \"../utils/workers-ai-rest\";\n\n// ---------------------------------------------------------------------------\n// Model types\n// ---------------------------------------------------------------------------\n\n/**\n * Workers AI models that support summarization.\n */\nexport type WorkersAiSummarizeModel = \"@cf/facebook/bart-large-cnn\" | (string & {});\n\n// ---------------------------------------------------------------------------\n// WorkersAiSummarizeAdapter\n// ---------------------------------------------------------------------------\n\nexport class WorkersAiSummarizeAdapter extends BaseSummarizeAdapter<WorkersAiSummarizeModel> {\n\treadonly name = \"workers-ai-summarize\" as const;\n\tprivate adapterConfig: WorkersAiAdapterConfig;\n\n\tconstructor(config: WorkersAiAdapterConfig, model: WorkersAiSummarizeModel) {\n\t\tsuper({}, model);\n\t\tvalidateWorkersAiConfig(config);\n\t\tthis.adapterConfig = config;\n\t}\n\n\tasync summarize(options: SummarizationOptions): Promise<SummarizationResult> {\n\t\tconst { text, maxLength } = options;\n\n\t\tconst payload: Record<string, unknown> = { input_text: text };\n\t\tif (maxLength != null) payload.max_length = maxLength;\n\n\t\tif (isDirectBindingConfig(this.adapterConfig)) {\n\t\t\treturn this.summarizeViaBinding(payload);\n\t\t}\n\n\t\tif (isDirectCredentialsConfig(this.adapterConfig)) {\n\t\t\treturn this.summarizeViaRest(payload);\n\t\t}\n\n\t\treturn this.summarizeViaGateway(payload);\n\t}\n\n\tprivate async summarizeViaBinding(\n\t\tpayload: Record<string, unknown>,\n\t): Promise<SummarizationResult> {\n\t\tconst ai = (this.adapterConfig as WorkersAiDirectBindingConfig).binding;\n\t\tconst result = (await ai.run(this.model, payload)) as Record<string, unknown>;\n\t\treturn this.wrapResult((result.summary as string) ?? \"\");\n\t}\n\n\tprivate async summarizeViaRest(payload: Record<string, unknown>): Promise<SummarizationResult> {\n\t\tconst config = this.adapterConfig as WorkersAiDirectCredentialsConfig;\n\t\tconst response = await workersAiRestFetch(config, this.model, payload, {\n\t\t\tlabel: \"Workers AI summarize\",\n\t\t});\n\n\t\tconst data = (await response.json()) as { result?: { summary?: string } };\n\t\treturn this.wrapResult(data.result?.summary ?? \"\");\n\t}\n\n\tprivate async summarizeViaGateway(\n\t\tpayload: Record<string, unknown>,\n\t): Promise<SummarizationResult> {\n\t\tconst gatewayConfig = this.adapterConfig as AiGatewayAdapterConfig;\n\t\tconst gatewayFetch = createGatewayFetch(\"workers-ai\", gatewayConfig);\n\n\t\t// The URL here is a placeholder — createGatewayFetch for \"workers-ai\" extracts\n\t\t// the model from the body, sets it as the endpoint, and routes through the gateway.\n\t\t// The actual URL path is not used.\n\t\tconst response = await gatewayFetch(\"https://api.cloudflare.com/v1/ai/summarization\", {\n\t\t\tmethod: \"POST\",\n\t\t\tbody: JSON.stringify({\n\t\t\t\tmodel: this.model,\n\t\t\t\t...payload,\n\t\t\t}),\n\t\t});\n\n\t\tif (!response.ok) {\n\t\t\tconst errorText = await response.text();\n\t\t\tthrow new Error(\n\t\t\t\t`Workers AI summarize gateway request failed (${response.status}): ${errorText}`,\n\t\t\t);\n\t\t}\n\n\t\tconst data = (await response.json()) as { result?: { summary?: string }; summary?: string };\n\t\treturn this.wrapResult(data.result?.summary ?? data.summary ?? \"\");\n\t}\n\n\tprivate wrapResult(summary: string): SummarizationResult {\n\t\treturn {\n\t\t\tid: this.generateId(),\n\t\t\tmodel: this.model,\n\t\t\tsummary,\n\t\t\t// BART-large-CNN doesn't return token usage\n\t\t\tusage: { promptTokens: 0, completionTokens: 0, totalTokens: 0 },\n\t\t};\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// Factory function\n// ---------------------------------------------------------------------------\n\n/**\n * Creates a Workers AI summarization adapter.\n *\n * Works with TanStack AI's `summarize()` activity function:\n * ```ts\n * import { summarize } from \"@tanstack/ai\";\n * import { createWorkersAiSummarize } from \"@cloudflare/tanstack-ai\";\n *\n * const adapter = createWorkersAiSummarize(\"@cf/facebook/bart-large-cnn\", {\n * binding: env.AI,\n * });\n *\n * const result = await summarize({ adapter, text: \"Long article here...\" });\n * // result.summary\n * ```\n *\n * Note: Factory takes `(model, config)` for ergonomics — the class constructor\n * uses `(config, model)` to match TanStack AI's upstream convention.\n */\nexport function createWorkersAiSummarize(\n\tmodel: WorkersAiSummarizeModel,\n\tconfig: WorkersAiAdapterConfig,\n) {\n\treturn new WorkersAiSummarizeAdapter(config, model);\n}\n"]}
@@ -5,6 +5,17 @@ function isDirectBindingConfig(config) {
5
5
  function isDirectCredentialsConfig(config) {
6
6
  return "accountId" in config && "apiKey" in config && !("gatewayId" in config);
7
7
  }
8
+ function isGatewayConfig(config) {
9
+ if ("gatewayId" in config) return true;
10
+ return "binding" in config && !isDirectBindingConfig(config);
11
+ }
12
+ function validateWorkersAiConfig(config) {
13
+ if (!isDirectBindingConfig(config) && !isDirectCredentialsConfig(config) && !isGatewayConfig(config)) {
14
+ throw new Error(
15
+ "Invalid Workers AI configuration: you must provide either a binding (e.g. { binding: env.AI }), credentials ({ accountId, apiKey }), or a gateway configuration ({ binding: env.AI.gateway(id) } or { accountId, gatewayId })."
16
+ );
17
+ }
18
+ }
8
19
  function createGatewayFetch(provider, config, headers = {}) {
9
20
  return (input, init) => {
10
21
  let query = {};
@@ -277,7 +288,7 @@ function transformWorkersAiStream(source, model) {
277
288
  }
278
289
  }
279
290
  } catch (e) {
280
- console.warn("[workers-ai stream] failed to parse SSE event:", data, e);
291
+ console.warn("[tanstack-ai] failed to parse SSE event:", data, e);
281
292
  }
282
293
  }
283
294
  },
@@ -311,5 +322,6 @@ function transformWorkersAiStream(source, model) {
311
322
 
312
323
 
313
324
 
314
- exports.isDirectBindingConfig = isDirectBindingConfig; exports.isDirectCredentialsConfig = isDirectCredentialsConfig; exports.createGatewayFetch = createGatewayFetch; exports.createWorkersAiBindingFetch = createWorkersAiBindingFetch;
315
- //# sourceMappingURL=chunk-GOU66I5T.cjs.map
325
+
326
+ exports.isDirectBindingConfig = isDirectBindingConfig; exports.isDirectCredentialsConfig = isDirectCredentialsConfig; exports.validateWorkersAiConfig = validateWorkersAiConfig; exports.createGatewayFetch = createGatewayFetch; exports.createWorkersAiBindingFetch = createWorkersAiBindingFetch;
327
+ //# sourceMappingURL=chunk-IMURYJRF.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/home/runner/work/ai/ai/packages/tanstack-ai/dist/chunk-IMURYJRF.cjs","../src/utils/create-fetcher.ts"],"names":[],"mappings":"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","file":"/home/runner/work/ai/ai/packages/tanstack-ai/dist/chunk-IMURYJRF.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 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// ---------------------------------------------------------------------------\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\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"]}
@@ -16,7 +16,7 @@ function buildGeminiGatewayConfig(config) {
16
16
  );
17
17
  }
18
18
  const headers = {};
19
- if (config.cfApiKey) {
19
+ if (config.apiKey && config.cfApiKey) {
20
20
  headers["cf-aig-authorization"] = `Bearer ${config.cfApiKey}`;
21
21
  }
22
22
  if (config.skipCache) {
@@ -31,8 +31,14 @@ function buildGeminiGatewayConfig(config) {
31
31
  if (typeof config.metadata === "object") {
32
32
  headers["cf-aig-metadata"] = JSON.stringify(config.metadata);
33
33
  }
34
+ const apiKey = config.apiKey ?? config.cfApiKey;
35
+ if (!apiKey) {
36
+ throw new Error(
37
+ "If you want to use BYOK or unified billing, you need to pass the Cloudflare AI Gateway API key."
38
+ );
39
+ }
34
40
  return {
35
- apiKey: config.apiKey ?? "unused",
41
+ apiKey,
36
42
  httpOptions: {
37
43
  baseUrl: `https://gateway.ai.cloudflare.com/v1/${config.accountId}/${config.gatewayId}/google-ai-studio`,
38
44
  headers: Object.keys(headers).length > 0 ? headers : void 0
@@ -62,4 +68,4 @@ export {
62
68
  createGeminiSummarize,
63
69
  createGeminiTts
64
70
  };
65
- //# sourceMappingURL=chunk-7AEFXYJG.js.map
71
+ //# sourceMappingURL=chunk-J5DSSZTO.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\n\tif (config.apiKey && config.cfApiKey) {\n\t\theaders[\"cf-aig-authorization\"] = `Bearer ${config.cfApiKey}`;\n\t}\n\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\tconst apiKey = config.apiKey ?? config.cfApiKey;\n\n\tif (!apiKey) {\n\t\tthrow new Error(\n\t\t\t\"If you want to use BYOK or unified billing, you need to pass the Cloudflare AI Gateway API key.\",\n\t\t);\n\t}\n\n\treturn {\n\t\tapiKey,\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;AAEzC,MAAI,OAAO,UAAU,OAAO,UAAU;AACrC,YAAQ,sBAAsB,IAAI,UAAU,OAAO,QAAQ;AAAA,EAC5D;AAEA,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,QAAM,SAAS,OAAO,UAAU,OAAO;AAEvC,MAAI,CAAC,QAAQ;AACZ,UAAM,IAAI;AAAA,MACT;AAAA,IACD;AAAA,EACD;AAEA,SAAO;AAAA,IACN;AAAA,IACA,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":[]}
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  createGatewayFetch
3
- } from "./chunk-F5YJMXZR.js";
3
+ } from "./chunk-O7MBRPCW.js";
4
4
 
5
5
  // src/adapters/openrouter.ts
6
6
  import {
@@ -15,6 +15,9 @@ function buildOpenRouterConfig(config) {
15
15
  });
16
16
  return {
17
17
  apiKey: config.apiKey ?? "unused",
18
+ // Cast needed: the installed @openrouter/sdk version may differ from the
19
+ // version @tanstack/ai-openrouter was built against. The HTTPClient interface
20
+ // is structurally compatible but TypeScript sees them as separate declarations.
18
21
  httpClient
19
22
  };
20
23
  }
@@ -51,4 +54,4 @@ export {
51
54
  createOpenRouterImage,
52
55
  createOpenRouterSummarize
53
56
  };
54
- //# sourceMappingURL=chunk-QRHKPL75.js.map
57
+ //# sourceMappingURL=chunk-JK5ZFOZZ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/adapters/openrouter.ts"],"sourcesContent":["import {\n\tOpenRouterTextAdapter,\n\tOpenRouterImageAdapter,\n\tOpenRouterSummarizeAdapter,\n\ttype OpenRouterConfig,\n\ttype OpenRouterImageConfig,\n\ttype OpenRouterSummarizeConfig,\n} from \"@tanstack/ai-openrouter\";\nimport { HTTPClient } from \"@openrouter/sdk\";\nimport { createGatewayFetch, type AiGatewayAdapterConfig } from \"../utils/create-fetcher\";\nimport type { AnyTextAdapter } from \"@tanstack/ai\";\n\nexport type OpenRouterGatewayConfig = AiGatewayAdapterConfig;\n\n/**\n * Build OpenRouter config that routes requests through AI Gateway.\n *\n * The OpenRouter SDK accepts an `httpClient` with a custom `fetcher`,\n * which we use to inject the AI Gateway fetch.\n */\nfunction buildOpenRouterConfig(config: OpenRouterGatewayConfig): OpenRouterConfig {\n\tconst httpClient = new HTTPClient({\n\t\tfetcher: createGatewayFetch(\"openrouter\", config),\n\t});\n\treturn {\n\t\tapiKey: config.apiKey ?? \"unused\",\n\t\t// Cast needed: the installed @openrouter/sdk version may differ from the\n\t\t// version @tanstack/ai-openrouter was built against. The HTTPClient interface\n\t\t// is structurally compatible but TypeScript sees them as separate declarations.\n\t\thttpClient: httpClient as unknown as OpenRouterConfig[\"httpClient\"],\n\t};\n}\n\n/**\n * Build OpenRouter image config that routes requests through AI Gateway.\n *\n * `OpenRouterImageConfig` extends `OpenRouterClientConfig` which declares\n * `baseURL` and `apiKey` but not `httpClient`. However, the image adapter\n * internally creates an `OpenRouter` SDK instance (which does accept\n * `httpClient` via `SDKOptions`). The double-cast is needed because the\n * declared config type is narrower than what the SDK constructor accepts.\n */\nfunction buildOpenRouterImageConfig(config: OpenRouterGatewayConfig): OpenRouterImageConfig {\n\tconst httpClient = new HTTPClient({\n\t\tfetcher: createGatewayFetch(\"openrouter\", config),\n\t});\n\treturn {\n\t\tapiKey: config.apiKey ?? \"unused\",\n\t\thttpClient,\n\t} as unknown as OpenRouterImageConfig;\n}\n\n/**\n * Build OpenRouter summarize config.\n *\n * `OpenRouterSummarizeConfig` extends `OpenRouterConfig` with optional\n * `temperature` and `maxTokens` fields. Since those are optional, a plain\n * `OpenRouterConfig` is structurally compatible — no cast needed.\n */\nfunction buildOpenRouterSummarizeConfig(\n\tconfig: OpenRouterGatewayConfig,\n): OpenRouterSummarizeConfig {\n\tconst httpClient = new HTTPClient({\n\t\tfetcher: createGatewayFetch(\"openrouter\", config),\n\t});\n\treturn {\n\t\tapiKey: config.apiKey ?? \"unused\",\n\t\thttpClient: httpClient as unknown as OpenRouterSummarizeConfig[\"httpClient\"],\n\t};\n}\n\n// ---------------------------------------------------------------------------\n// Chat model type — OpenRouter supports many models; we use a loose string type\n// ---------------------------------------------------------------------------\n\n/** OpenRouter chat model identifier. Accepts any string since OpenRouter proxies hundreds of models. */\nexport type OpenRouterChatModel = string;\n\n/** OpenRouter image model identifier. */\nexport type OpenRouterImageModel = string;\n\n/** OpenRouter summarize model identifier (same as chat models). */\nexport type OpenRouterSummarizeModel = string;\n\n// ---------------------------------------------------------------------------\n// Factory functions\n// ---------------------------------------------------------------------------\n\n/**\n * Creates an OpenRouter chat adapter which uses Cloudflare AI Gateway.\n * Supports both binding and credential-based configurations.\n *\n * @param model The model to use (e.g. \"openai/gpt-4o\", \"anthropic/claude-sonnet-4-5\")\n * @param config Configuration options\n */\nexport function createOpenRouterChat(\n\tmodel: OpenRouterChatModel,\n\tconfig: OpenRouterGatewayConfig,\n): AnyTextAdapter {\n\t// Cast needed: we accept any string model while upstream expects a literal union\n\treturn new OpenRouterTextAdapter(buildOpenRouterConfig(config), model as any);\n}\n\n/**\n * Creates an OpenRouter image adapter which uses Cloudflare AI Gateway.\n * Supports both binding and credential-based configurations.\n *\n * @param model The image model to use\n * @param config Configuration options\n */\nexport function createOpenRouterImage(\n\tmodel: OpenRouterImageModel,\n\tconfig: OpenRouterGatewayConfig,\n) {\n\t// Cast needed: we accept any string model while upstream expects a literal union\n\treturn new OpenRouterImageAdapter(buildOpenRouterImageConfig(config), model as any);\n}\n\n/**\n * Creates an OpenRouter summarize adapter which uses Cloudflare AI Gateway.\n * Supports both binding and credential-based configurations.\n *\n * @param model The model to use for summarization\n * @param config Configuration options\n */\nexport function createOpenRouterSummarize(\n\tmodel: OpenRouterSummarizeModel,\n\tconfig: OpenRouterGatewayConfig,\n) {\n\t// Cast needed: we accept any string model while upstream expects a literal union\n\treturn new OpenRouterSummarizeAdapter(buildOpenRouterSummarizeConfig(config), model as any);\n}\n"],"mappings":";;;;;AAAA;AAAA,EACC;AAAA,EACA;AAAA,EACA;AAAA,OAIM;AACP,SAAS,kBAAkB;AAY3B,SAAS,sBAAsB,QAAmD;AACjF,QAAM,aAAa,IAAI,WAAW;AAAA,IACjC,SAAS,mBAAmB,cAAc,MAAM;AAAA,EACjD,CAAC;AACD,SAAO;AAAA,IACN,QAAQ,OAAO,UAAU;AAAA;AAAA;AAAA;AAAA,IAIzB;AAAA,EACD;AACD;AAWA,SAAS,2BAA2B,QAAwD;AAC3F,QAAM,aAAa,IAAI,WAAW;AAAA,IACjC,SAAS,mBAAmB,cAAc,MAAM;AAAA,EACjD,CAAC;AACD,SAAO;AAAA,IACN,QAAQ,OAAO,UAAU;AAAA,IACzB;AAAA,EACD;AACD;AASA,SAAS,+BACR,QAC4B;AAC5B,QAAM,aAAa,IAAI,WAAW;AAAA,IACjC,SAAS,mBAAmB,cAAc,MAAM;AAAA,EACjD,CAAC;AACD,SAAO;AAAA,IACN,QAAQ,OAAO,UAAU;AAAA,IACzB;AAAA,EACD;AACD;AA0BO,SAAS,qBACf,OACA,QACiB;AAEjB,SAAO,IAAI,sBAAsB,sBAAsB,MAAM,GAAG,KAAY;AAC7E;AASO,SAAS,sBACf,OACA,QACC;AAED,SAAO,IAAI,uBAAuB,2BAA2B,MAAM,GAAG,KAAY;AACnF;AASO,SAAS,0BACf,OACA,QACC;AAED,SAAO,IAAI,2BAA2B,+BAA+B,MAAM,GAAG,KAAY;AAC3F;","names":[]}
@@ -3,7 +3,8 @@
3
3
 
4
4
 
5
5
 
6
- var _chunkGOU66I5Tcjs = require('./chunk-GOU66I5T.cjs');
6
+
7
+ var _chunkIMURYJRFcjs = require('./chunk-IMURYJRF.cjs');
7
8
 
8
9
 
9
10
  var _chunk4DE2IREAcjs = require('./chunk-4DE2IREA.cjs');
@@ -14,13 +15,14 @@ var _chunk4DE2IREAcjs = require('./chunk-4DE2IREA.cjs');
14
15
  var _adapters = require('@tanstack/ai/adapters');
15
16
  var _openai = require('openai'); var _openai2 = _interopRequireDefault(_openai);
16
17
  function buildWorkersAiClient(config) {
17
- if (_chunkGOU66I5Tcjs.isDirectBindingConfig.call(void 0, config)) {
18
+ _chunkIMURYJRFcjs.validateWorkersAiConfig.call(void 0, config);
19
+ if (_chunkIMURYJRFcjs.isDirectBindingConfig.call(void 0, config)) {
18
20
  return new (0, _openai2.default)({
19
21
  apiKey: "unused",
20
- fetch: _chunkGOU66I5Tcjs.createWorkersAiBindingFetch.call(void 0, config.binding)
22
+ fetch: _chunkIMURYJRFcjs.createWorkersAiBindingFetch.call(void 0, config.binding)
21
23
  });
22
24
  }
23
- if (_chunkGOU66I5Tcjs.isDirectCredentialsConfig.call(void 0, config)) {
25
+ if (_chunkIMURYJRFcjs.isDirectCredentialsConfig.call(void 0, config)) {
24
26
  return new (0, _openai2.default)({
25
27
  baseURL: `https://api.cloudflare.com/client/v4/accounts/${config.accountId}/ai/v1`,
26
28
  apiKey: config.apiKey
@@ -28,7 +30,7 @@ function buildWorkersAiClient(config) {
28
30
  }
29
31
  const gatewayConfig = config;
30
32
  return new (0, _openai2.default)({
31
- fetch: _chunkGOU66I5Tcjs.createGatewayFetch.call(void 0, "workers-ai", gatewayConfig),
33
+ fetch: _chunkIMURYJRFcjs.createGatewayFetch.call(void 0, "workers-ai", gatewayConfig),
32
34
  apiKey: _nullishCoalesce(gatewayConfig.apiKey, () => ( "unused"))
33
35
  });
34
36
  }
@@ -37,6 +39,31 @@ function extractTextContent(content) {
37
39
  if (typeof content === "string") return content;
38
40
  return content.filter((p) => p.type === "text").map((p) => p.content || "").join("");
39
41
  }
42
+ function buildUserContent(content) {
43
+ if (content === null) return "";
44
+ if (typeof content === "string") return content;
45
+ const hasImages = content.some((p) => p.type === "image_url" || p.type === "image");
46
+ if (!hasImages) {
47
+ return content.filter((p) => p.type === "text").map((p) => p.content || "").join("");
48
+ }
49
+ const parts = [];
50
+ for (const part of content) {
51
+ if (part.type === "text" && part.content) {
52
+ parts.push({ type: "text", text: part.content });
53
+ } else if (part.type === "image_url" && part.content) {
54
+ parts.push({
55
+ type: "image_url",
56
+ image_url: { url: part.content }
57
+ });
58
+ } else if (part.type === "image_url" && part.image_url) {
59
+ parts.push({
60
+ type: "image_url",
61
+ image_url: part.image_url
62
+ });
63
+ }
64
+ }
65
+ return parts;
66
+ }
40
67
  function buildOpenAIMessages(systemPrompts, messages, options) {
41
68
  const includeTools = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _ => _.includeToolMessages]), () => ( true));
42
69
  const openAIMessages = [];
@@ -50,7 +77,7 @@ function buildOpenAIMessages(systemPrompts, messages, options) {
50
77
  if (message.role === "user") {
51
78
  openAIMessages.push({
52
79
  role: "user",
53
- content: extractTextContent(message.content)
80
+ content: buildUserContent(message.content)
54
81
  });
55
82
  } else if (message.role === "assistant") {
56
83
  const assistantMessage = {
@@ -126,14 +153,100 @@ var WorkersAiTextAdapter = class extends _adapters.BaseTextAdapter {
126
153
  let hasReceivedFinishReason = false;
127
154
  const toolCallsInProgress = /* @__PURE__ */ new Map();
128
155
  try {
129
- const stream = await this.client.chat.completions.create({
130
- model: _nullishCoalesce(model, () => ( this.model)),
131
- messages: openAIMessages,
132
- tools: openAITools,
133
- temperature,
134
- stream: true,
135
- stream_options: { include_usage: true }
136
- });
156
+ let stream;
157
+ try {
158
+ stream = await this.client.chat.completions.create({
159
+ model: _nullishCoalesce(model, () => ( this.model)),
160
+ messages: openAIMessages,
161
+ tools: openAITools,
162
+ temperature,
163
+ stream: true,
164
+ stream_options: { include_usage: true }
165
+ });
166
+ } catch (streamError) {
167
+ console.warn(
168
+ "[tanstack-ai] Streaming failed, falling back to non-streaming:",
169
+ streamError instanceof Error ? streamError.message : streamError
170
+ );
171
+ const nonStreamResult = await this.client.chat.completions.create({
172
+ model: _nullishCoalesce(model, () => ( this.model)),
173
+ messages: openAIMessages,
174
+ tools: openAITools,
175
+ temperature
176
+ });
177
+ yield {
178
+ type: "RUN_STARTED",
179
+ runId,
180
+ model: nonStreamResult.model || model || this.model,
181
+ timestamp
182
+ };
183
+ const msg = _optionalChain([nonStreamResult, 'access', _2 => _2.choices, 'access', _3 => _3[0], 'optionalAccess', _4 => _4.message]);
184
+ if (_optionalChain([msg, 'optionalAccess', _5 => _5.content])) {
185
+ yield {
186
+ type: "TEXT_MESSAGE_START",
187
+ messageId,
188
+ model: nonStreamResult.model || model || this.model,
189
+ timestamp,
190
+ role: "assistant"
191
+ };
192
+ yield {
193
+ type: "TEXT_MESSAGE_CONTENT",
194
+ messageId,
195
+ model: nonStreamResult.model || model || this.model,
196
+ timestamp,
197
+ delta: msg.content,
198
+ content: msg.content
199
+ };
200
+ yield {
201
+ type: "TEXT_MESSAGE_END",
202
+ messageId,
203
+ model: nonStreamResult.model || model || this.model,
204
+ timestamp
205
+ };
206
+ }
207
+ if (_optionalChain([msg, 'optionalAccess', _6 => _6.tool_calls])) {
208
+ for (const tc of msg.tool_calls) {
209
+ if (tc.type !== "function") continue;
210
+ const fn = tc.function;
211
+ let parsedInput = {};
212
+ try {
213
+ parsedInput = fn.arguments ? JSON.parse(fn.arguments) : {};
214
+ } catch (e2) {
215
+ parsedInput = {};
216
+ }
217
+ yield {
218
+ type: "TOOL_CALL_START",
219
+ toolCallId: tc.id,
220
+ toolName: fn.name,
221
+ model: nonStreamResult.model || model || this.model,
222
+ timestamp,
223
+ index: 0
224
+ };
225
+ yield {
226
+ type: "TOOL_CALL_END",
227
+ toolCallId: tc.id,
228
+ toolName: fn.name,
229
+ model: nonStreamResult.model || model || this.model,
230
+ timestamp,
231
+ input: parsedInput
232
+ };
233
+ }
234
+ }
235
+ const finishReason = _optionalChain([nonStreamResult, 'access', _7 => _7.choices, 'access', _8 => _8[0], 'optionalAccess', _9 => _9.finish_reason]);
236
+ yield {
237
+ type: "RUN_FINISHED",
238
+ runId,
239
+ model: nonStreamResult.model || model || this.model,
240
+ timestamp,
241
+ usage: nonStreamResult.usage ? {
242
+ promptTokens: nonStreamResult.usage.prompt_tokens,
243
+ completionTokens: nonStreamResult.usage.completion_tokens,
244
+ totalTokens: nonStreamResult.usage.total_tokens
245
+ } : void 0,
246
+ finishReason: finishReason === "tool_calls" || finishReason === "function_call" ? "tool_calls" : _nullishCoalesce(finishReason, () => ( "stop"))
247
+ };
248
+ return;
249
+ }
137
250
  for await (const chunk of stream) {
138
251
  if (!chunk.choices || chunk.choices.length === 0) continue;
139
252
  const choice = chunk.choices[0];
@@ -197,7 +310,7 @@ var WorkersAiTextAdapter = class extends _adapters.BaseTextAdapter {
197
310
  if (!toolCallsInProgress.has(index)) {
198
311
  toolCallsInProgress.set(index, {
199
312
  id: toolCallDelta.id || "",
200
- name: _optionalChain([toolCallDelta, 'access', _2 => _2.function, 'optionalAccess', _3 => _3.name]) || "",
313
+ name: _optionalChain([toolCallDelta, 'access', _10 => _10.function, 'optionalAccess', _11 => _11.name]) || "",
201
314
  arguments: "",
202
315
  started: false
203
316
  });
@@ -206,10 +319,10 @@ var WorkersAiTextAdapter = class extends _adapters.BaseTextAdapter {
206
319
  if (toolCallDelta.id) {
207
320
  toolCall.id = toolCallDelta.id;
208
321
  }
209
- if (_optionalChain([toolCallDelta, 'access', _4 => _4.function, 'optionalAccess', _5 => _5.name])) {
322
+ if (_optionalChain([toolCallDelta, 'access', _12 => _12.function, 'optionalAccess', _13 => _13.name])) {
210
323
  toolCall.name = toolCallDelta.function.name;
211
324
  }
212
- if (_optionalChain([toolCallDelta, 'access', _6 => _6.function, 'optionalAccess', _7 => _7.arguments])) {
325
+ if (_optionalChain([toolCallDelta, 'access', _14 => _14.function, 'optionalAccess', _15 => _15.arguments])) {
213
326
  toolCall.arguments += toolCallDelta.function.arguments;
214
327
  }
215
328
  if (toolCall.id && toolCall.name && !toolCall.started) {
@@ -223,7 +336,7 @@ var WorkersAiTextAdapter = class extends _adapters.BaseTextAdapter {
223
336
  index
224
337
  };
225
338
  }
226
- if (_optionalChain([toolCallDelta, 'access', _8 => _8.function, 'optionalAccess', _9 => _9.arguments]) && toolCall.started) {
339
+ if (_optionalChain([toolCallDelta, 'access', _16 => _16.function, 'optionalAccess', _17 => _17.arguments]) && toolCall.started) {
227
340
  yield {
228
341
  type: "TOOL_CALL_ARGS",
229
342
  toolCallId: toolCall.id,
@@ -241,7 +354,7 @@ var WorkersAiTextAdapter = class extends _adapters.BaseTextAdapter {
241
354
  let parsedInput = {};
242
355
  try {
243
356
  parsedInput = toolCall.arguments ? JSON.parse(toolCall.arguments) : {};
244
- } catch (e2) {
357
+ } catch (e3) {
245
358
  parsedInput = {};
246
359
  }
247
360
  yield {
@@ -278,12 +391,15 @@ var WorkersAiTextAdapter = class extends _adapters.BaseTextAdapter {
278
391
  }
279
392
  }
280
393
  if (hasEmittedRunStarted && !hasReceivedFinishReason) {
394
+ console.warn(
395
+ "[tanstack-ai] Stream ended without finish_reason \u2014 possible truncation or connection drop"
396
+ );
281
397
  for (const [, toolCall] of toolCallsInProgress) {
282
398
  if (toolCall.started) {
283
399
  let parsedInput = {};
284
400
  try {
285
401
  parsedInput = toolCall.arguments ? JSON.parse(toolCall.arguments) : {};
286
- } catch (e3) {
402
+ } catch (e4) {
287
403
  parsedInput = {};
288
404
  }
289
405
  yield {
@@ -355,20 +471,20 @@ var WorkersAiTextAdapter = class extends _adapters.BaseTextAdapter {
355
471
  }
356
472
  }
357
473
  });
358
- const choice = _optionalChain([response, 'access', _10 => _10.choices, 'optionalAccess', _11 => _11[0]]);
474
+ const choice = _optionalChain([response, 'access', _18 => _18.choices, 'optionalAccess', _19 => _19[0]]);
359
475
  if (!choice) {
360
476
  throw new Error(
361
477
  `Workers AI structured output returned no choices: ${JSON.stringify(response)}`
362
478
  );
363
479
  }
364
- const rawContent = _nullishCoalesce(_optionalChain([choice, 'access', _12 => _12.message, 'optionalAccess', _13 => _13.content]), () => ( ""));
480
+ const rawContent = _nullishCoalesce(_optionalChain([choice, 'access', _20 => _20.message, 'optionalAccess', _21 => _21.content]), () => ( ""));
365
481
  let data;
366
482
  let rawText;
367
483
  if (typeof rawContent === "string") {
368
484
  rawText = rawContent;
369
485
  try {
370
486
  data = JSON.parse(rawText);
371
- } catch (e4) {
487
+ } catch (e5) {
372
488
  data = rawText;
373
489
  }
374
490
  } else {
@@ -386,4 +502,4 @@ function createWorkersAiChat(model, config) {
386
502
 
387
503
 
388
504
  exports.WorkersAiTextAdapter = WorkersAiTextAdapter; exports.createWorkersAiChat = createWorkersAiChat;
389
- //# sourceMappingURL=chunk-BPWGWJJV.cjs.map
505
+ //# sourceMappingURL=chunk-LG2I3ANT.cjs.map