@juspay/neurolink 9.64.0 → 9.65.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 (324) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +18 -17
  3. package/dist/adapters/providerImageAdapter.js +29 -1
  4. package/dist/adapters/replicate/auth.d.ts +19 -0
  5. package/dist/adapters/replicate/auth.js +32 -0
  6. package/dist/adapters/replicate/predictionLifecycle.d.ts +46 -0
  7. package/dist/adapters/replicate/predictionLifecycle.js +283 -0
  8. package/dist/adapters/video/klingVideoHandler.d.ts +37 -0
  9. package/dist/adapters/video/klingVideoHandler.js +305 -0
  10. package/dist/adapters/video/replicateVideoHandler.d.ts +29 -0
  11. package/dist/adapters/video/replicateVideoHandler.js +157 -0
  12. package/dist/adapters/video/runwayVideoHandler.d.ts +32 -0
  13. package/dist/adapters/video/runwayVideoHandler.js +316 -0
  14. package/dist/adapters/video/vertexVideoHandler.d.ts +19 -1
  15. package/dist/adapters/video/vertexVideoHandler.js +33 -9
  16. package/dist/agent/directTools.js +11 -3
  17. package/dist/autoresearch/runner.js +8 -2
  18. package/dist/avatar/index.d.ts +13 -0
  19. package/dist/avatar/index.js +13 -0
  20. package/dist/avatar/providers/DIDAvatar.d.ts +49 -0
  21. package/dist/avatar/providers/DIDAvatar.js +501 -0
  22. package/dist/avatar/providers/HeyGenAvatar.d.ts +30 -0
  23. package/dist/avatar/providers/HeyGenAvatar.js +337 -0
  24. package/dist/avatar/providers/ReplicateAvatar.d.ts +36 -0
  25. package/dist/avatar/providers/ReplicateAvatar.js +267 -0
  26. package/dist/browser/neurolink.min.js +624 -601
  27. package/dist/cli/commands/mcp.js +29 -0
  28. package/dist/cli/commands/proxy.js +24 -5
  29. package/dist/cli/factories/commandFactory.d.ts +11 -1
  30. package/dist/cli/factories/commandFactory.js +291 -38
  31. package/dist/constants/contextWindows.js +101 -0
  32. package/dist/constants/enums.d.ts +273 -2
  33. package/dist/constants/enums.js +290 -1
  34. package/dist/constants/videoErrors.d.ts +4 -0
  35. package/dist/constants/videoErrors.js +4 -0
  36. package/dist/core/baseProvider.d.ts +22 -2
  37. package/dist/core/baseProvider.js +217 -11
  38. package/dist/core/constants.d.ts +12 -0
  39. package/dist/core/constants.js +72 -1
  40. package/dist/evaluation/index.d.ts +2 -0
  41. package/dist/evaluation/index.js +4 -0
  42. package/dist/factories/providerFactory.js +7 -1
  43. package/dist/factories/providerRegistry.js +202 -5
  44. package/dist/features/ppt/contentPlanner.js +42 -14
  45. package/dist/index.d.ts +9 -1
  46. package/dist/index.js +16 -1
  47. package/dist/lib/adapters/providerImageAdapter.js +29 -1
  48. package/dist/lib/adapters/replicate/auth.d.ts +19 -0
  49. package/dist/lib/adapters/replicate/auth.js +33 -0
  50. package/dist/lib/adapters/replicate/predictionLifecycle.d.ts +46 -0
  51. package/dist/lib/adapters/replicate/predictionLifecycle.js +284 -0
  52. package/dist/lib/adapters/video/klingVideoHandler.d.ts +37 -0
  53. package/dist/lib/adapters/video/klingVideoHandler.js +306 -0
  54. package/dist/lib/adapters/video/replicateVideoHandler.d.ts +29 -0
  55. package/dist/lib/adapters/video/replicateVideoHandler.js +158 -0
  56. package/dist/lib/adapters/video/runwayVideoHandler.d.ts +32 -0
  57. package/dist/lib/adapters/video/runwayVideoHandler.js +317 -0
  58. package/dist/lib/adapters/video/vertexVideoHandler.d.ts +19 -1
  59. package/dist/lib/adapters/video/vertexVideoHandler.js +33 -9
  60. package/dist/lib/agent/directTools.js +11 -3
  61. package/dist/lib/autoresearch/runner.js +8 -2
  62. package/dist/lib/avatar/index.d.ts +13 -0
  63. package/dist/lib/avatar/index.js +14 -0
  64. package/dist/lib/avatar/providers/DIDAvatar.d.ts +49 -0
  65. package/dist/lib/avatar/providers/DIDAvatar.js +502 -0
  66. package/dist/lib/avatar/providers/HeyGenAvatar.d.ts +30 -0
  67. package/dist/lib/avatar/providers/HeyGenAvatar.js +338 -0
  68. package/dist/lib/avatar/providers/ReplicateAvatar.d.ts +36 -0
  69. package/dist/lib/avatar/providers/ReplicateAvatar.js +268 -0
  70. package/dist/lib/constants/contextWindows.js +101 -0
  71. package/dist/lib/constants/enums.d.ts +273 -2
  72. package/dist/lib/constants/enums.js +290 -1
  73. package/dist/lib/constants/videoErrors.d.ts +4 -0
  74. package/dist/lib/constants/videoErrors.js +4 -0
  75. package/dist/lib/core/baseProvider.d.ts +22 -2
  76. package/dist/lib/core/baseProvider.js +217 -11
  77. package/dist/lib/core/constants.d.ts +12 -0
  78. package/dist/lib/core/constants.js +72 -1
  79. package/dist/lib/evaluation/index.d.ts +2 -0
  80. package/dist/lib/evaluation/index.js +4 -0
  81. package/dist/lib/factories/providerFactory.js +7 -1
  82. package/dist/lib/factories/providerRegistry.js +202 -5
  83. package/dist/lib/features/ppt/contentPlanner.js +42 -14
  84. package/dist/lib/index.d.ts +9 -1
  85. package/dist/lib/index.js +16 -1
  86. package/dist/lib/middleware/builtin/lifecycle.js +39 -9
  87. package/dist/lib/music/index.d.ts +13 -0
  88. package/dist/lib/music/index.js +14 -0
  89. package/dist/lib/music/providers/BeatovenMusic.d.ts +31 -0
  90. package/dist/lib/music/providers/BeatovenMusic.js +334 -0
  91. package/dist/lib/music/providers/ElevenLabsMusic.d.ts +30 -0
  92. package/dist/lib/music/providers/ElevenLabsMusic.js +169 -0
  93. package/dist/lib/music/providers/LyriaMusic.d.ts +29 -0
  94. package/dist/lib/music/providers/LyriaMusic.js +173 -0
  95. package/dist/lib/music/providers/ReplicateMusic.d.ts +31 -0
  96. package/dist/lib/music/providers/ReplicateMusic.js +262 -0
  97. package/dist/lib/neurolink.d.ts +30 -0
  98. package/dist/lib/neurolink.js +323 -77
  99. package/dist/lib/providers/amazonBedrock.d.ts +10 -0
  100. package/dist/lib/providers/amazonBedrock.js +94 -39
  101. package/dist/lib/providers/anthropic.js +55 -7
  102. package/dist/lib/providers/anthropicBaseProvider.js +1 -1
  103. package/dist/lib/providers/azureOpenai.js +66 -17
  104. package/dist/lib/providers/cloudflare.d.ts +35 -0
  105. package/dist/lib/providers/cloudflare.js +174 -0
  106. package/dist/lib/providers/cohere.d.ts +52 -0
  107. package/dist/lib/providers/cohere.js +253 -0
  108. package/dist/lib/providers/deepseek.js +72 -17
  109. package/dist/lib/providers/fireworks.d.ts +33 -0
  110. package/dist/lib/providers/fireworks.js +164 -0
  111. package/dist/lib/providers/googleAiStudio.js +126 -10
  112. package/dist/lib/providers/googleNativeGemini3.d.ts +26 -6
  113. package/dist/lib/providers/googleNativeGemini3.js +276 -29
  114. package/dist/lib/providers/googleVertex.js +639 -181
  115. package/dist/lib/providers/groq.d.ts +33 -0
  116. package/dist/lib/providers/groq.js +181 -0
  117. package/dist/lib/providers/huggingFace.js +9 -8
  118. package/dist/lib/providers/ideogram.d.ts +34 -0
  119. package/dist/lib/providers/ideogram.js +184 -0
  120. package/dist/lib/providers/index.d.ts +13 -0
  121. package/dist/lib/providers/index.js +13 -0
  122. package/dist/lib/providers/jina.d.ts +59 -0
  123. package/dist/lib/providers/jina.js +218 -0
  124. package/dist/lib/providers/llamaCpp.js +14 -46
  125. package/dist/lib/providers/lmStudio.js +14 -47
  126. package/dist/lib/providers/mistral.js +7 -7
  127. package/dist/lib/providers/nvidiaNim.js +160 -19
  128. package/dist/lib/providers/ollama.js +7 -7
  129. package/dist/lib/providers/openAI.d.ts +22 -1
  130. package/dist/lib/providers/openAI.js +181 -0
  131. package/dist/lib/providers/openRouter.js +35 -23
  132. package/dist/lib/providers/openaiCompatible.js +9 -8
  133. package/dist/lib/providers/perplexity.d.ts +33 -0
  134. package/dist/lib/providers/perplexity.js +179 -0
  135. package/dist/lib/providers/recraft.d.ts +34 -0
  136. package/dist/lib/providers/recraft.js +197 -0
  137. package/dist/lib/providers/replicate.d.ts +75 -0
  138. package/dist/lib/providers/replicate.js +403 -0
  139. package/dist/lib/providers/stability.d.ts +37 -0
  140. package/dist/lib/providers/stability.js +191 -0
  141. package/dist/lib/providers/togetherAi.d.ts +33 -0
  142. package/dist/lib/providers/togetherAi.js +176 -0
  143. package/dist/lib/providers/voyage.d.ts +47 -0
  144. package/dist/lib/providers/voyage.js +177 -0
  145. package/dist/lib/providers/xai.d.ts +33 -0
  146. package/dist/lib/providers/xai.js +172 -0
  147. package/dist/lib/telemetry/index.d.ts +1 -1
  148. package/dist/lib/telemetry/index.js +1 -1
  149. package/dist/lib/telemetry/tracers.d.ts +19 -0
  150. package/dist/lib/telemetry/tracers.js +19 -0
  151. package/dist/lib/telemetry/withSpan.d.ts +35 -0
  152. package/dist/lib/telemetry/withSpan.js +103 -0
  153. package/dist/lib/types/avatar.d.ts +143 -0
  154. package/dist/lib/types/avatar.js +20 -0
  155. package/dist/lib/types/cli.d.ts +6 -0
  156. package/dist/lib/types/conversation.d.ts +16 -0
  157. package/dist/lib/types/generate.d.ts +62 -5
  158. package/dist/lib/types/index.d.ts +5 -0
  159. package/dist/lib/types/index.js +7 -0
  160. package/dist/lib/types/middleware.d.ts +27 -0
  161. package/dist/lib/types/multimodal.d.ts +35 -2
  162. package/dist/lib/types/music.d.ts +165 -0
  163. package/dist/lib/types/music.js +21 -0
  164. package/dist/lib/types/providers.d.ts +144 -1
  165. package/dist/lib/types/replicate.d.ts +67 -0
  166. package/dist/lib/types/replicate.js +10 -0
  167. package/dist/lib/types/safeFetch.d.ts +15 -0
  168. package/dist/lib/types/safeFetch.js +7 -0
  169. package/dist/lib/types/stream.d.ts +2 -1
  170. package/dist/lib/types/tools.d.ts +13 -0
  171. package/dist/lib/types/video.d.ts +89 -0
  172. package/dist/lib/types/video.js +15 -0
  173. package/dist/lib/utils/avatarProcessor.d.ts +68 -0
  174. package/dist/lib/utils/avatarProcessor.js +172 -0
  175. package/dist/lib/utils/cloneOptions.d.ts +36 -0
  176. package/dist/lib/utils/cloneOptions.js +62 -0
  177. package/dist/lib/utils/lifecycleCallbacks.d.ts +51 -8
  178. package/dist/lib/utils/lifecycleCallbacks.js +82 -26
  179. package/dist/lib/utils/lifecycleTimeout.d.ts +25 -0
  180. package/dist/lib/utils/lifecycleTimeout.js +39 -0
  181. package/dist/lib/utils/logSanitize.d.ts +49 -0
  182. package/dist/lib/utils/logSanitize.js +170 -0
  183. package/dist/lib/utils/loggingFetch.d.ts +29 -0
  184. package/dist/lib/utils/loggingFetch.js +60 -0
  185. package/dist/lib/utils/messageBuilder.js +43 -25
  186. package/dist/lib/utils/modelChoices.js +236 -3
  187. package/dist/lib/utils/musicProcessor.d.ts +67 -0
  188. package/dist/lib/utils/musicProcessor.js +189 -0
  189. package/dist/lib/utils/optionsConversion.js +3 -2
  190. package/dist/lib/utils/parameterValidation.js +14 -4
  191. package/dist/lib/utils/pricing.js +193 -0
  192. package/dist/lib/utils/providerConfig.d.ts +55 -0
  193. package/dist/lib/utils/providerConfig.js +224 -0
  194. package/dist/lib/utils/safeFetch.d.ts +26 -0
  195. package/dist/lib/utils/safeFetch.js +83 -0
  196. package/dist/lib/utils/sizeGuard.d.ts +34 -0
  197. package/dist/lib/utils/sizeGuard.js +45 -0
  198. package/dist/lib/utils/ssrfGuard.d.ts +52 -0
  199. package/dist/lib/utils/ssrfGuard.js +411 -0
  200. package/dist/lib/utils/videoProcessor.d.ts +60 -0
  201. package/dist/lib/utils/videoProcessor.js +201 -0
  202. package/dist/lib/voice/providers/FishAudioTTS.d.ts +27 -0
  203. package/dist/lib/voice/providers/FishAudioTTS.js +183 -0
  204. package/dist/lib/workflow/core/ensembleExecutor.js +26 -9
  205. package/dist/middleware/builtin/lifecycle.js +39 -9
  206. package/dist/music/index.d.ts +13 -0
  207. package/dist/music/index.js +13 -0
  208. package/dist/music/providers/BeatovenMusic.d.ts +31 -0
  209. package/dist/music/providers/BeatovenMusic.js +333 -0
  210. package/dist/music/providers/ElevenLabsMusic.d.ts +30 -0
  211. package/dist/music/providers/ElevenLabsMusic.js +168 -0
  212. package/dist/music/providers/LyriaMusic.d.ts +29 -0
  213. package/dist/music/providers/LyriaMusic.js +172 -0
  214. package/dist/music/providers/ReplicateMusic.d.ts +31 -0
  215. package/dist/music/providers/ReplicateMusic.js +261 -0
  216. package/dist/neurolink.d.ts +30 -0
  217. package/dist/neurolink.js +323 -77
  218. package/dist/providers/amazonBedrock.d.ts +10 -0
  219. package/dist/providers/amazonBedrock.js +94 -39
  220. package/dist/providers/anthropic.js +55 -7
  221. package/dist/providers/anthropicBaseProvider.js +1 -1
  222. package/dist/providers/azureOpenai.js +66 -17
  223. package/dist/providers/cloudflare.d.ts +35 -0
  224. package/dist/providers/cloudflare.js +173 -0
  225. package/dist/providers/cohere.d.ts +52 -0
  226. package/dist/providers/cohere.js +252 -0
  227. package/dist/providers/deepseek.js +72 -17
  228. package/dist/providers/fireworks.d.ts +33 -0
  229. package/dist/providers/fireworks.js +163 -0
  230. package/dist/providers/googleAiStudio.js +126 -10
  231. package/dist/providers/googleNativeGemini3.d.ts +26 -6
  232. package/dist/providers/googleNativeGemini3.js +276 -29
  233. package/dist/providers/googleVertex.js +639 -181
  234. package/dist/providers/groq.d.ts +33 -0
  235. package/dist/providers/groq.js +180 -0
  236. package/dist/providers/huggingFace.js +9 -8
  237. package/dist/providers/ideogram.d.ts +34 -0
  238. package/dist/providers/ideogram.js +183 -0
  239. package/dist/providers/index.d.ts +13 -0
  240. package/dist/providers/index.js +13 -0
  241. package/dist/providers/jina.d.ts +59 -0
  242. package/dist/providers/jina.js +217 -0
  243. package/dist/providers/llamaCpp.js +14 -46
  244. package/dist/providers/lmStudio.js +14 -47
  245. package/dist/providers/mistral.js +7 -7
  246. package/dist/providers/nvidiaNim.js +160 -19
  247. package/dist/providers/ollama.js +7 -7
  248. package/dist/providers/openAI.d.ts +22 -1
  249. package/dist/providers/openAI.js +181 -0
  250. package/dist/providers/openRouter.js +35 -23
  251. package/dist/providers/openaiCompatible.js +9 -8
  252. package/dist/providers/perplexity.d.ts +33 -0
  253. package/dist/providers/perplexity.js +178 -0
  254. package/dist/providers/recraft.d.ts +34 -0
  255. package/dist/providers/recraft.js +196 -0
  256. package/dist/providers/replicate.d.ts +75 -0
  257. package/dist/providers/replicate.js +402 -0
  258. package/dist/providers/stability.d.ts +37 -0
  259. package/dist/providers/stability.js +190 -0
  260. package/dist/providers/togetherAi.d.ts +33 -0
  261. package/dist/providers/togetherAi.js +175 -0
  262. package/dist/providers/voyage.d.ts +47 -0
  263. package/dist/providers/voyage.js +176 -0
  264. package/dist/providers/xai.d.ts +33 -0
  265. package/dist/providers/xai.js +171 -0
  266. package/dist/telemetry/index.d.ts +1 -1
  267. package/dist/telemetry/index.js +1 -1
  268. package/dist/telemetry/tracers.d.ts +19 -0
  269. package/dist/telemetry/tracers.js +19 -0
  270. package/dist/telemetry/withSpan.d.ts +35 -0
  271. package/dist/telemetry/withSpan.js +103 -0
  272. package/dist/types/avatar.d.ts +143 -0
  273. package/dist/types/avatar.js +19 -0
  274. package/dist/types/cli.d.ts +6 -0
  275. package/dist/types/conversation.d.ts +16 -0
  276. package/dist/types/generate.d.ts +62 -5
  277. package/dist/types/index.d.ts +5 -0
  278. package/dist/types/index.js +7 -0
  279. package/dist/types/middleware.d.ts +27 -0
  280. package/dist/types/multimodal.d.ts +35 -2
  281. package/dist/types/music.d.ts +165 -0
  282. package/dist/types/music.js +20 -0
  283. package/dist/types/providers.d.ts +144 -1
  284. package/dist/types/replicate.d.ts +67 -0
  285. package/dist/types/replicate.js +9 -0
  286. package/dist/types/safeFetch.d.ts +15 -0
  287. package/dist/types/safeFetch.js +6 -0
  288. package/dist/types/stream.d.ts +2 -1
  289. package/dist/types/tools.d.ts +13 -0
  290. package/dist/types/video.d.ts +89 -0
  291. package/dist/types/video.js +14 -0
  292. package/dist/utils/avatarProcessor.d.ts +68 -0
  293. package/dist/utils/avatarProcessor.js +171 -0
  294. package/dist/utils/cloneOptions.d.ts +36 -0
  295. package/dist/utils/cloneOptions.js +61 -0
  296. package/dist/utils/lifecycleCallbacks.d.ts +51 -8
  297. package/dist/utils/lifecycleCallbacks.js +82 -26
  298. package/dist/utils/lifecycleTimeout.d.ts +25 -0
  299. package/dist/utils/lifecycleTimeout.js +38 -0
  300. package/dist/utils/logSanitize.d.ts +49 -0
  301. package/dist/utils/logSanitize.js +169 -0
  302. package/dist/utils/loggingFetch.d.ts +29 -0
  303. package/dist/utils/loggingFetch.js +59 -0
  304. package/dist/utils/messageBuilder.js +43 -25
  305. package/dist/utils/modelChoices.js +236 -3
  306. package/dist/utils/musicProcessor.d.ts +67 -0
  307. package/dist/utils/musicProcessor.js +188 -0
  308. package/dist/utils/optionsConversion.js +3 -2
  309. package/dist/utils/parameterValidation.js +14 -4
  310. package/dist/utils/pricing.js +193 -0
  311. package/dist/utils/providerConfig.d.ts +55 -0
  312. package/dist/utils/providerConfig.js +224 -0
  313. package/dist/utils/safeFetch.d.ts +26 -0
  314. package/dist/utils/safeFetch.js +82 -0
  315. package/dist/utils/sizeGuard.d.ts +34 -0
  316. package/dist/utils/sizeGuard.js +44 -0
  317. package/dist/utils/ssrfGuard.d.ts +52 -0
  318. package/dist/utils/ssrfGuard.js +410 -0
  319. package/dist/utils/videoProcessor.d.ts +60 -0
  320. package/dist/utils/videoProcessor.js +200 -0
  321. package/dist/voice/providers/FishAudioTTS.d.ts +27 -0
  322. package/dist/voice/providers/FishAudioTTS.js +182 -0
  323. package/dist/workflow/core/ensembleExecutor.js +26 -9
  324. package/package.json +32 -5
@@ -22,10 +22,38 @@ export class AmazonBedrockProvider extends BaseProvider {
22
22
  bedrockClient;
23
23
  conversationHistory = [];
24
24
  region;
25
+ /**
26
+ * Parse the region segment from a Bedrock ARN.
27
+ * Returns null when the input is not an ARN.
28
+ *
29
+ * Supports all AWS partitions:
30
+ * - `arn:aws:bedrock:…` (commercial)
31
+ * - `arn:aws-cn:bedrock:…` (China)
32
+ * - `arn:aws-us-gov:bedrock:…` (GovCloud)
33
+ */
34
+ static extractRegionFromArn(modelId) {
35
+ if (!modelId) {
36
+ return null;
37
+ }
38
+ const match = modelId.match(/^arn:aws[a-z0-9-]*:bedrock:([^:]+):/);
39
+ return match?.[1] ?? null;
40
+ }
25
41
  constructor(modelName, neurolink, region, credentials) {
26
42
  super(modelName, "bedrock", neurolink);
43
+ // When the model is given as a Bedrock ARN (e.g. an inference profile
44
+ // like `arn:aws:bedrock:us-east-1:123:inference-profile/foo`), Bedrock
45
+ // requires the runtime client's region to match the region embedded
46
+ // in the ARN — otherwise it returns "The provided model identifier is
47
+ // invalid." Auto-extract so users don't have to keep AWS_REGION in
48
+ // sync with their model ARN.
49
+ const resolvedModel = modelName || process.env.BEDROCK_MODEL || this.modelName;
50
+ const arnRegion = AmazonBedrockProvider.extractRegionFromArn(resolvedModel);
27
51
  this.region =
28
- credentials?.region || region || process.env.AWS_REGION || "us-east-1";
52
+ credentials?.region ||
53
+ region ||
54
+ arnRegion ||
55
+ process.env.AWS_REGION ||
56
+ "us-east-1";
29
57
  logger.debug("[AmazonBedrockProvider] Starting constructor with extensive logging for debugging");
30
58
  // Log environment variables for debugging
31
59
  logger.debug(`[AmazonBedrockProvider] Environment check: AWS_REGION=${process.env.AWS_REGION || "undefined"}, AWS_ACCESS_KEY_ID=${process.env.AWS_ACCESS_KEY_ID ? "SET" : "undefined"}, AWS_SECRET_ACCESS_KEY=${process.env.AWS_SECRET_ACCESS_KEY ? "SET" : "undefined"}`);
@@ -274,7 +302,7 @@ export class AmazonBedrockProvider extends BaseProvider {
274
302
  "gen_ai.operation.name": "chat",
275
303
  },
276
304
  }, async (generateSpan) => {
277
- logger.info(`🚀 [AmazonBedrockProvider] Starting Bedrock API call at ${new Date().toISOString()}`);
305
+ logger.info(`[AmazonBedrockProvider] Starting Bedrock API call at ${new Date().toISOString()}`);
278
306
  try {
279
307
  // Pre-call validation and logging
280
308
  let region = "unknown";
@@ -287,9 +315,9 @@ export class AmazonBedrockProvider extends BaseProvider {
287
315
  catch {
288
316
  // Region lookup failed — not critical, only used for logging
289
317
  }
290
- logger.info(`🔧 [AmazonBedrockProvider] Client region: ${region}`);
291
- logger.info(`🔧 [AmazonBedrockProvider] Model: ${this.modelName || this.getDefaultModel()}`);
292
- logger.info(`🔧 [AmazonBedrockProvider] Conversation history length: ${this.conversationHistory.length}`);
318
+ logger.info(`[AmazonBedrockProvider] Client region: ${region}`);
319
+ logger.info(`[AmazonBedrockProvider] Model: ${this.modelName || this.getDefaultModel()}`);
320
+ logger.info(`[AmazonBedrockProvider] Conversation history length: ${this.conversationHistory.length}`);
293
321
  // Get all available tools
294
322
  const aiTools = await this.getAllTools();
295
323
  const allTools = this.convertAISDKToolsToToolDefinitions(aiTools);
@@ -310,10 +338,10 @@ export class AmazonBedrockProvider extends BaseProvider {
310
338
  };
311
339
  if (toolConfig) {
312
340
  commandInput.toolConfig = toolConfig;
313
- logger.info(`🛠️ [AmazonBedrockProvider] Tools configured: ${toolConfig.tools?.length || 0}`);
341
+ logger.info(`[AmazonBedrockProvider] Tools configured: ${toolConfig.tools?.length || 0}`);
314
342
  }
315
343
  // Log command details for debugging
316
- logger.info(`📋 [AmazonBedrockProvider] Command input summary:`);
344
+ logger.info(`[AmazonBedrockProvider] Command input summary:`);
317
345
  logger.info(` - Model ID: ${commandInput.modelId}`);
318
346
  logger.info(` - Messages count: ${commandInput.messages?.length || 0}`);
319
347
  logger.info(` - System prompts: ${commandInput.system?.length || 0}`);
@@ -821,8 +849,8 @@ export class AmazonBedrockProvider extends BaseProvider {
821
849
  return this.bedrockClient;
822
850
  }
823
851
  async executeStream(options) {
824
- logger.debug("🟢 [TRACE] executeStream ENTRY - starting streaming attempt");
825
- logger.info("🚀 [AmazonBedrockProvider] Attempting real streaming with ConverseStreamCommand");
852
+ logger.debug("[TRACE] executeStream ENTRY - starting streaming attempt");
853
+ logger.info("[AmazonBedrockProvider] Attempting real streaming with ConverseStreamCommand");
826
854
  return bedrockTracer.startActiveSpan("bedrock.stream", {
827
855
  kind: SpanKind.CLIENT,
828
856
  attributes: {
@@ -832,7 +860,7 @@ export class AmazonBedrockProvider extends BaseProvider {
832
860
  },
833
861
  }, async (streamSpan) => {
834
862
  try {
835
- logger.debug("🟢 [TRACE] executeStream TRY block - about to call streamingConversationLoop");
863
+ logger.debug("[TRACE] executeStream TRY block - about to call streamingConversationLoop");
836
864
  // Clear conversation history for new streaming session
837
865
  this.conversationHistory = [];
838
866
  // Check for multimodal input (images, PDFs, CSVs, files)
@@ -871,15 +899,15 @@ export class AmazonBedrockProvider extends BaseProvider {
871
899
  }
872
900
  logger.debug(`[AmazonBedrockProvider] Starting streaming conversation with ${this.conversationHistory.length} message(s)`);
873
901
  // Call the actual streaming implementation that already exists
874
- logger.debug("🟢 [TRACE] executeStream - calling streamingConversationLoop NOW");
902
+ logger.debug("[TRACE] executeStream - calling streamingConversationLoop NOW");
875
903
  const result = await this.streamingConversationLoop(options, streamSpan);
876
- logger.debug("🟢 [TRACE] executeStream - streamingConversationLoop SUCCESS, returning result");
904
+ logger.debug("[TRACE] executeStream - streamingConversationLoop SUCCESS, returning result");
877
905
  streamSpan.setStatus({ code: SpanStatusCode.OK });
878
906
  streamSpan.end();
879
907
  return result;
880
908
  }
881
909
  catch (error) {
882
- logger.debug("🔴 [TRACE] executeStream CATCH - error caught from streamingConversationLoop");
910
+ logger.debug("[TRACE] executeStream CATCH - error caught from streamingConversationLoop");
883
911
  const errorObj = error;
884
912
  // Check if error is related to streaming permissions
885
913
  const isPermissionError = errorObj?.name ===
@@ -889,10 +917,10 @@ export class AmazonBedrockProvider extends BaseProvider {
889
917
  errorObj?.message?.includes("bedrock:InvokeModelWithResponseStream") ||
890
918
  errorObj?.message?.includes("streaming") ||
891
919
  errorObj?.message?.includes("ConverseStream");
892
- logger.debug("🔴 [TRACE] executeStream CATCH - checking if permission error");
893
- logger.debug(`🔴 [TRACE] executeStream CATCH - isPermissionError=${isPermissionError}`);
920
+ logger.debug("[TRACE] executeStream CATCH - checking if permission error");
921
+ logger.debug(`[TRACE] executeStream CATCH - isPermissionError=${isPermissionError}`);
894
922
  if (isPermissionError) {
895
- logger.debug("🟡 [TRACE] executeStream CATCH - PERMISSION ERROR DETECTED, starting fallback");
923
+ logger.debug("[TRACE] executeStream CATCH - PERMISSION ERROR DETECTED, starting fallback");
896
924
  logger.warn(`[AmazonBedrockProvider] Streaming permissions not available, falling back to generate method: ${errorObj.message}`);
897
925
  streamSpan.addEvent("stream.fallback_to_generate", {
898
926
  reason: errorObj.message,
@@ -917,16 +945,28 @@ export class AmazonBedrockProvider extends BaseProvider {
917
945
  streamSpan.setAttribute("gen_ai.response.stop_reason", "fallback_end_turn");
918
946
  streamSpan.setStatus({ code: SpanStatusCode.OK });
919
947
  streamSpan.end();
920
- // Convert generate result to streaming format
948
+ // Convert generate result to streaming format.
949
+ // Use whitespace-preserving split (matches BaseProvider's
950
+ // executeFakeStreaming) so newlines, tabs, indentation, code
951
+ // blocks, and markdown tables aren't collapsed to single spaces.
921
952
  const stream = new ReadableStream({
922
953
  start(controller) {
923
- // Split the response into chunks for pseudo-streaming
924
954
  const responseText = generateResult.content || "";
925
- const chunks = responseText.split(" ");
926
- chunks.forEach((word, _index) => {
927
- controller.enqueue({ content: word + " " });
928
- });
929
- controller.enqueue({ content: "" });
955
+ const tokens = responseText.split(/(\s+)/);
956
+ let buffer = "";
957
+ for (let i = 0; i < tokens.length; i++) {
958
+ buffer += tokens[i];
959
+ const shouldYield = i === tokens.length - 1 ||
960
+ buffer.length > 50 ||
961
+ /[.!?;,]\s*$/.test(buffer);
962
+ if (shouldYield && buffer.length > 0) {
963
+ controller.enqueue({ content: buffer });
964
+ buffer = "";
965
+ }
966
+ }
967
+ if (buffer.length > 0) {
968
+ controller.enqueue({ content: buffer });
969
+ }
930
970
  controller.close();
931
971
  },
932
972
  });
@@ -970,7 +1010,7 @@ export class AmazonBedrockProvider extends BaseProvider {
970
1010
  });
971
1011
  }
972
1012
  async streamingConversationLoop(options, streamSpan) {
973
- logger.debug("🟦 [TRACE] streamingConversationLoop ENTRY");
1013
+ logger.debug("[TRACE] streamingConversationLoop ENTRY");
974
1014
  const startTime = Date.now();
975
1015
  const maxIterations = options.maxSteps || DEFAULT_MAX_STEPS;
976
1016
  let iteration = 0;
@@ -983,7 +1023,7 @@ export class AmazonBedrockProvider extends BaseProvider {
983
1023
  // The REAL issue: ReadableStream errors don't bubble up to the caller
984
1024
  // So we need to make the first streaming call synchronously to test permissions
985
1025
  try {
986
- logger.debug("🟦 [TRACE] streamingConversationLoop - testing first streaming call");
1026
+ logger.debug("[TRACE] streamingConversationLoop - testing first streaming call");
987
1027
  const commandInput = await this.prepareStreamCommand(options);
988
1028
  const command = new ConverseStreamCommand(commandInput);
989
1029
  logger.debug("[Observability] Bedrock streaming API request", {
@@ -1005,7 +1045,7 @@ export class AmazonBedrockProvider extends BaseProvider {
1005
1045
  // Process the first response immediately to avoid waste
1006
1046
  const stream = new ReadableStream({
1007
1047
  start: async (controller) => {
1008
- logger.debug("🟦 [TRACE] streamingConversationLoop - ReadableStream start() called");
1048
+ logger.debug("[TRACE] streamingConversationLoop - ReadableStream start() called");
1009
1049
  try {
1010
1050
  // Process the first response we already have, tracking all event types
1011
1051
  let firstStopReason = "";
@@ -1114,6 +1154,10 @@ export class AmazonBedrockProvider extends BaseProvider {
1114
1154
  const shouldContinue = await this.handleStreamStopReason(firstStopReason, firstAssistantMessage, controller, options);
1115
1155
  if (!shouldContinue) {
1116
1156
  streamSpan.setAttribute("gen_ai.response.stop_reason", firstStopReason);
1157
+ // Close the controller so downstream `for await` exits;
1158
+ // see the close() comment near the bottom of this start()
1159
+ // function for the spec rationale.
1160
+ controller.close();
1117
1161
  return;
1118
1162
  }
1119
1163
  }
@@ -1153,10 +1197,21 @@ export class AmazonBedrockProvider extends BaseProvider {
1153
1197
  if (iteration >= maxIterations) {
1154
1198
  streamSpan.setAttribute("gen_ai.response.stop_reason", "max_iterations");
1155
1199
  controller.error(new Error("Streaming conversation exceeded maximum iterations"));
1200
+ return;
1156
1201
  }
1202
+ // CRITICAL: ReadableStream's start() returning does NOT auto-close
1203
+ // the controller per the WHATWG Streams spec. Without this, the
1204
+ // downstream `for await (const chunk of stream)` in
1205
+ // convertToAsyncIterable never sees `done: true` and the
1206
+ // consumer hangs forever — manifested as a 240s harness
1207
+ // PER_TEST_TIMEOUT_SKIP for `[bedrock] stream tokens`. The first-
1208
+ // iteration `return` path and the while-loop natural `break`
1209
+ // path both reach here, so closing once at the bottom covers
1210
+ // every non-error exit.
1211
+ controller.close();
1157
1212
  }
1158
1213
  catch (error) {
1159
- logger.debug("🔴 [TRACE] streamingConversationLoop - CATCH block hit in ReadableStream");
1214
+ logger.debug("[TRACE] streamingConversationLoop - CATCH block hit in ReadableStream");
1160
1215
  controller.error(error);
1161
1216
  }
1162
1217
  },
@@ -1226,7 +1281,7 @@ export class AmazonBedrockProvider extends BaseProvider {
1226
1281
  };
1227
1282
  }
1228
1283
  catch (error) {
1229
- logger.debug("🔴 [TRACE] streamingConversationLoop - first streaming call FAILED, throwing");
1284
+ logger.debug("[TRACE] streamingConversationLoop - first streaming call FAILED, throwing");
1230
1285
  throw error; // This will be caught by executeStream
1231
1286
  }
1232
1287
  }
@@ -1426,7 +1481,7 @@ export class AmazonBedrockProvider extends BaseProvider {
1426
1481
  return false;
1427
1482
  }
1428
1483
  else if (stopReason === "tool_use") {
1429
- logger.debug(`🛠️ [AmazonBedrockProvider] Tool use detected in streaming - executing tools`);
1484
+ logger.debug(`[AmazonBedrockProvider] Tool use detected in streaming - executing tools`);
1430
1485
  await this.executeStreamTools(assistantMessage.content, options);
1431
1486
  return true; // Continue conversation loop
1432
1487
  }
@@ -1455,10 +1510,10 @@ export class AmazonBedrockProvider extends BaseProvider {
1455
1510
  toolUseCount++;
1456
1511
  }
1457
1512
  }
1458
- logger.debug(`🔍 [AmazonBedrockProvider] Found ${toolUseCount} toolUse blocks in assistant message`);
1513
+ logger.debug(`[AmazonBedrockProvider] Found ${toolUseCount} toolUse blocks in assistant message`);
1459
1514
  for (const contentItem of messageContent) {
1460
1515
  if (contentItem.toolUse) {
1461
- logger.debug(`🔧 [AmazonBedrockProvider] Executing tool: ${contentItem.toolUse.name}`);
1516
+ logger.debug(`[AmazonBedrockProvider] Executing tool: ${contentItem.toolUse.name}`);
1462
1517
  // Track tool call
1463
1518
  toolCalls.push({
1464
1519
  type: "tool-call",
@@ -1468,7 +1523,7 @@ export class AmazonBedrockProvider extends BaseProvider {
1468
1523
  });
1469
1524
  try {
1470
1525
  const toolResult = await this.executeSingleTool(contentItem.toolUse.name, contentItem.toolUse.input || {}, contentItem.toolUse.toolUseId);
1471
- logger.debug(`✅ [AmazonBedrockProvider] Tool execution successful: ${contentItem.toolUse.name}`);
1526
+ logger.debug(`[AmazonBedrockProvider] Tool execution successful: ${contentItem.toolUse.name}`);
1472
1527
  // Track tool result for storage
1473
1528
  toolResultsForStorage.push({
1474
1529
  type: "tool-result",
@@ -1486,7 +1541,7 @@ export class AmazonBedrockProvider extends BaseProvider {
1486
1541
  });
1487
1542
  }
1488
1543
  catch (error) {
1489
- logger.error(`❌ [AmazonBedrockProvider] Tool execution failed: ${contentItem.toolUse.name}`, error);
1544
+ logger.error(`[AmazonBedrockProvider] Tool execution failed: ${contentItem.toolUse.name}`, error);
1490
1545
  const errorMessage = error instanceof Error ? error.message : String(error);
1491
1546
  // Track failed tool result
1492
1547
  toolResultsForStorage.push({
@@ -1509,10 +1564,10 @@ export class AmazonBedrockProvider extends BaseProvider {
1509
1564
  }
1510
1565
  }
1511
1566
  }
1512
- logger.debug(`📊 [AmazonBedrockProvider] Created ${toolResults.length} toolResult blocks for ${toolUseCount} toolUse blocks`);
1567
+ logger.debug(`[AmazonBedrockProvider] Created ${toolResults.length} toolResult blocks for ${toolUseCount} toolUse blocks`);
1513
1568
  // Validate 1:1 mapping before adding to conversation
1514
1569
  if (toolResults.length !== toolUseCount) {
1515
- logger.error(`❌ [AmazonBedrockProvider] Mismatch: ${toolResults.length} toolResults vs ${toolUseCount} toolUse blocks`);
1570
+ logger.error(`[AmazonBedrockProvider] Mismatch: ${toolResults.length} toolResults vs ${toolUseCount} toolUse blocks`);
1516
1571
  throw new Error(`Tool mapping mismatch: ${toolResults.length} toolResults for ${toolUseCount} toolUse blocks`);
1517
1572
  }
1518
1573
  // Add tool results as user message - exact structure like Bedrock-MCP-Connector
@@ -1522,7 +1577,7 @@ export class AmazonBedrockProvider extends BaseProvider {
1522
1577
  content: toolResults,
1523
1578
  };
1524
1579
  this.conversationHistory.push(userMessageWithToolResults);
1525
- logger.debug(`📤 [AmazonBedrockProvider] Added ${toolResults.length} tool results to conversation (1:1 mapping validated)`);
1580
+ logger.debug(`[AmazonBedrockProvider] Added ${toolResults.length} tool results to conversation (1:1 mapping validated)`);
1526
1581
  // Emit tool:end for each completed tool result so Pipeline B
1527
1582
  // captures telemetry for Bedrock-driven tool calls (gap S2).
1528
1583
  emitToolEndFromStepFinish(this.neurolink?.getEventEmitter(), toolResultsForStorage.map((tr) => {
@@ -1558,14 +1613,14 @@ export class AmazonBedrockProvider extends BaseProvider {
1558
1613
  region: process.env.AWS_REGION || "us-east-1",
1559
1614
  });
1560
1615
  try {
1561
- logger.debug("🔍 [AmazonBedrockProvider] Starting health check...");
1616
+ logger.debug("[AmazonBedrockProvider] Starting health check...");
1562
1617
  const command = new ListFoundationModelsCommand({});
1563
1618
  const response = await healthCheckClient.send(command, {
1564
1619
  abortSignal: controller.signal,
1565
1620
  });
1566
1621
  const models = response.modelSummaries || [];
1567
1622
  const activeModels = models.filter((model) => model.modelLifecycle?.status === "ACTIVE");
1568
- logger.debug(`✅ [AmazonBedrockProvider] Health check passed - Found ${activeModels.length} active models out of ${models.length} total models`);
1623
+ logger.debug(`[AmazonBedrockProvider] Health check passed - Found ${activeModels.length} active models out of ${models.length} total models`);
1569
1624
  if (activeModels.length === 0) {
1570
1625
  throw new Error("No active foundation models available in the region");
1571
1626
  }
@@ -1586,7 +1641,7 @@ export class AmazonBedrockProvider extends BaseProvider {
1586
1641
  if (errorObj.code === "ECONNREFUSED" || errorObj.code === "ENOTFOUND") {
1587
1642
  throw new Error("Unable to connect to Bedrock service. Check your network connectivity and AWS region configuration", { cause: error });
1588
1643
  }
1589
- logger.error("[AmazonBedrockProvider] Health check failed:", error);
1644
+ logger.error("[AmazonBedrockProvider] Health check failed:", error);
1590
1645
  throw new Error(`Bedrock health check failed: ${errorMessage || "Unknown error"}`, { cause: error });
1591
1646
  }
1592
1647
  finally {
@@ -323,12 +323,39 @@ export class AnthropicProvider extends BaseProvider {
323
323
  else {
324
324
  // Traditional API key authentication
325
325
  const apiKeyToUse = credentials?.apiKey ?? config?.apiKey ?? getAnthropicApiKey();
326
+ // The Vercel AI SDK builds `${baseURL}/messages` (no version prefix).
327
+ // Anthropic's REST API lives under `/v1/messages`, so a user-supplied
328
+ // bare host like `http://localhost:55669` 404s. Normalize: if the
329
+ // baseURL doesn't already end with `/vN`, append `/v1`.
330
+ //
331
+ // CAVEAT: this heuristic assumes the proxy exposes Anthropic-compatible
332
+ // endpoints rooted at `/vN/...`. Path-rooted proxies like
333
+ // `https://proxy.example.com/anthropic` will be silently rewritten to
334
+ // `…/anthropic/v1` and 404 if the proxy doesn't expose that path. To
335
+ // disable auto-append, set `ANTHROPIC_BASE_URL` explicitly ending in a
336
+ // version segment (e.g. `https://api.anthropic.com/v1`).
337
+ const normalizedBaseURL = (() => {
338
+ const raw = process.env.ANTHROPIC_BASE_URL;
339
+ if (!raw) {
340
+ return undefined;
341
+ }
342
+ const trimmed = raw.replace(/\/+$/, "");
343
+ if (/\/v\d+$/.test(trimmed)) {
344
+ return trimmed;
345
+ }
346
+ logger.warn("[AnthropicProvider] ANTHROPIC_BASE_URL does not end with /vN; " +
347
+ "appending /v1 so the Vercel AI SDK can build /messages correctly. " +
348
+ "To silence this warning, set ANTHROPIC_BASE_URL with an explicit " +
349
+ "version segment (e.g. https://api.anthropic.com/v1). " +
350
+ "If your proxy exposes Anthropic at a custom path that doesn't " +
351
+ "expose /v1/messages, the auto-append will produce a 404 — pass " +
352
+ "the full path including the version explicitly.", { baseURL: raw, rewrittenTo: `${trimmed}/v1` });
353
+ return `${trimmed}/v1`;
354
+ })();
326
355
  anthropic = createAnthropic({
327
356
  apiKey: apiKeyToUse,
328
357
  headers,
329
- ...(process.env.ANTHROPIC_BASE_URL && {
330
- baseURL: process.env.ANTHROPIC_BASE_URL,
331
- }),
358
+ ...(normalizedBaseURL && { baseURL: normalizedBaseURL }),
332
359
  fetch: createProxyFetch(),
333
360
  });
334
361
  logger.debug("Anthropic Provider initialized with API key", {
@@ -379,13 +406,23 @@ export class AnthropicProvider extends BaseProvider {
379
406
  const usingProxy = !!process.env.ANTHROPIC_BASE_URL;
380
407
  if (this.enableBetaFeatures) {
381
408
  if (usingProxy) {
382
- headers["anthropic-beta"] = [
409
+ // The 1M-context beta requires a plan upgrade on most accounts;
410
+ // surfacing it by default forces a "The long context beta is not
411
+ // yet available for this subscription." failure for everyone else.
412
+ // Gate behind ANTHROPIC_ENABLE_LONG_CONTEXT_BETA=1 so default-tier
413
+ // accounts (and CI) can use the proxy without the gated feature.
414
+ const longContextOptIn = process.env.ANTHROPIC_ENABLE_LONG_CONTEXT_BETA === "1" ||
415
+ process.env.ANTHROPIC_ENABLE_LONG_CONTEXT_BETA === "true";
416
+ const betas = [
383
417
  ...CLAUDE_CODE_OAUTH_BETAS,
384
418
  "fine-grained-tool-streaming-2025-05-14",
385
- "context-1m-2025-08-07",
386
419
  "interleaved-thinking-2025-05-14",
387
420
  "redact-thinking-2026-02-12",
388
- ].join(",");
421
+ ];
422
+ if (longContextOptIn) {
423
+ betas.push("context-1m-2025-08-07");
424
+ }
425
+ headers["anthropic-beta"] = betas.join(",");
389
426
  }
390
427
  else {
391
428
  headers["anthropic-beta"] = ANTHROPIC_BETA_HEADERS["anthropic-beta"];
@@ -414,8 +451,11 @@ export class AnthropicProvider extends BaseProvider {
414
451
  * ```
415
452
  */
416
453
  validateModelAccess(model) {
417
- // Proxy mode: bypass tier validation entirely — the proxy handles model access
454
+ // Proxy mode: bypass tier validation entirely — the proxy handles model
455
+ // access. Log at debug level so users can tell why an unknown model name
456
+ // "validated" when their proxy may not actually expose it.
418
457
  if (process.env.ANTHROPIC_BASE_URL) {
458
+ logger.debug("[validateModelAccess] Bypassing tier check (ANTHROPIC_BASE_URL set — proxy enforces access)", { model });
419
459
  return true;
420
460
  }
421
461
  // API tier has access to all models
@@ -539,6 +579,14 @@ export class AnthropicProvider extends BaseProvider {
539
579
  const REFRESH_TIMEOUT_MS = 30_000;
540
580
  const controller = new AbortController();
541
581
  const timeoutId = setTimeout(() => controller.abort(), REFRESH_TIMEOUT_MS);
582
+ // User-Agent is set to CLAUDE_CLI_USER_AGENT so the refresh request
583
+ // matches what the official Claude CLI / CLIProxyAPI sends. Anthropic
584
+ // gates parts of the OAuth flow on this UA (the same `client_id` is
585
+ // rejected by `ANTHROPIC_TOKEN_URL` if the UA looks like a generic
586
+ // SDK), so this is required for OAuth refresh to succeed — not a
587
+ // cosmetic choice. If Anthropic ever publishes a separate UA for
588
+ // third-party OAuth clients, switch to that. See `auth/anthropicOAuth.ts`
589
+ // for the source of `CLAUDE_CLI_USER_AGENT` / `CLAUDE_CODE_CLIENT_ID`.
542
590
  const response = await fetch(ANTHROPIC_TOKEN_URL, {
543
591
  method: "POST",
544
592
  headers: {
@@ -90,7 +90,7 @@ export class AnthropicProviderV2 extends BaseProvider {
90
90
  try {
91
91
  result = streamText({
92
92
  model,
93
- prompt: options.input.text,
93
+ prompt: options.input.text ?? "",
94
94
  system: options.systemPrompt,
95
95
  temperature: options.temperature,
96
96
  maxOutputTokens: options.maxTokens, // No default limit - unlimited unless specified
@@ -5,6 +5,7 @@ import { BaseProvider } from "../core/baseProvider.js";
5
5
  import { DEFAULT_MAX_STEPS } from "../core/constants.js";
6
6
  import { createProxyFetch } from "../proxy/proxyFetch.js";
7
7
  import { emitToolEndFromStepFinish } from "../utils/toolEndEmitter.js";
8
+ import { AuthenticationError, NetworkError, ProviderError, } from "../types/index.js";
8
9
  import { logger } from "../utils/logger.js";
9
10
  import { createAzureAPIKeyConfig, createAzureEndpointConfig, validateApiKey, } from "../utils/providerConfig.js";
10
11
  import { composeAbortSignals, createTimeoutController, TimeoutError, } from "../utils/timeout.js";
@@ -19,12 +20,48 @@ export class AzureOpenAIProvider extends BaseProvider {
19
20
  super(modelName, "azure", sdk);
20
21
  this.apiKey = credentials?.apiKey || process.env.AZURE_OPENAI_API_KEY || "";
21
22
  const endpoint = process.env.AZURE_OPENAI_ENDPOINT || "";
22
- const envResourceName = endpoint
23
- .replace("https://", "")
24
- .replace(/\/+$/, "") // Remove trailing slashes
25
- .replace(".openai.azure.com", "")
26
- .replace(".cognitiveservices.azure.com", "");
23
+ // Use URL parsing instead of string-replace so endpoints that already
24
+ // carry a path segment (e.g. "https://<host>/openai" — a valid Azure AI
25
+ // Foundry shape) don't end up duplicating it as "<host>/openai/openai".
26
+ // Tolerate missing scheme by prefixing https:// before parsing.
27
+ let endpointUrl;
28
+ if (endpoint) {
29
+ try {
30
+ endpointUrl = new URL(endpoint.includes("://") ? endpoint : `https://${endpoint}`);
31
+ }
32
+ catch {
33
+ endpointUrl = undefined;
34
+ }
35
+ }
36
+ const endpointHost = endpointUrl?.hostname ?? "";
37
+ const endpointPath = endpointUrl?.pathname && endpointUrl.pathname !== "/"
38
+ ? endpointUrl.pathname.replace(/\/+$/, "")
39
+ : "";
40
+ // Classic Azure OpenAI ("*.openai.azure.com") and Cognitive Services
41
+ // ("*.cognitiveservices.azure.com") endpoints encode the resource name as
42
+ // a subdomain that @ai-sdk/azure expects to receive verbatim. The newer
43
+ // Azure AI Foundry endpoint format ("*.services.ai.azure.com") does not
44
+ // round-trip through that subdomain rewrite, so passing the resource name
45
+ // would yield e.g. "<host>.services.ai.azure.com.openai.azure.com". For
46
+ // those hosts we hand the full URL back via baseURL instead.
47
+ const isClassicAzureHost = /\.(openai|cognitiveservices)\.azure\.com$/.test(endpointHost);
48
+ const envResourceName = isClassicAzureHost
49
+ ? endpointHost
50
+ .replace(".openai.azure.com", "")
51
+ .replace(".cognitiveservices.azure.com", "")
52
+ : "";
27
53
  this.resourceName = credentials?.resourceName || envResourceName;
54
+ // For Azure AI Foundry the SDK still routes to the OpenAI-compatible API
55
+ // (deployments/{deployment}/chat/completions); the `/openai` path suffix
56
+ // mirrors what the SDK derives in classic mode
57
+ // (`https://${resource}.openai.azure.com/openai`). Reuse the path the
58
+ // operator already supplied if it already terminates in `/openai` *or*
59
+ // a versioned form like `/openai/v1`; otherwise append `/openai`. Never
60
+ // duplicate.
61
+ const hasOpenAIPathSuffix = /\/openai(?:\/v\d+)?$/.test(endpointPath);
62
+ const baseURLForFoundry = !this.resourceName && endpointUrl
63
+ ? `${endpointUrl.origin}${hasOpenAIPathSuffix ? endpointPath : `${endpointPath}/openai`}`
64
+ : undefined;
28
65
  this.deployment =
29
66
  credentials?.deploymentName ||
30
67
  modelName ||
@@ -40,20 +77,32 @@ export class AzureOpenAIProvider extends BaseProvider {
40
77
  if (!this.apiKey) {
41
78
  validateApiKey(createAzureAPIKeyConfig());
42
79
  }
43
- if (!this.resourceName) {
80
+ if (!this.resourceName && !baseURLForFoundry) {
44
81
  validateApiKey(createAzureEndpointConfig());
45
82
  }
46
- // Create the Azure provider instance with proxy support
83
+ // Create the Azure provider instance with proxy support.
84
+ // For classic *.openai.azure.com / *.cognitiveservices.azure.com hosts we
85
+ // pass `resourceName`, which @ai-sdk/azure rewrites into the canonical
86
+ // subdomain. For Azure AI Foundry hosts ("*.services.ai.azure.com") we
87
+ // pass the full URL via `baseURL` so no rewrite happens.
47
88
  // useDeploymentBasedUrls is required because @ai-sdk/azure v3+ defaults to
48
89
  // the /v1/ URL format, but most Azure deployments still require the legacy
49
90
  // /deployments/{deployment}/ URL pattern.
50
- this.azureProvider = createAzure({
51
- resourceName: this.resourceName,
52
- apiKey: this.apiKey,
53
- apiVersion: this.apiVersion,
54
- useDeploymentBasedUrls: true,
55
- fetch: createProxyFetch(),
56
- });
91
+ this.azureProvider = baseURLForFoundry
92
+ ? createAzure({
93
+ baseURL: baseURLForFoundry,
94
+ apiKey: this.apiKey,
95
+ apiVersion: this.apiVersion,
96
+ useDeploymentBasedUrls: true,
97
+ fetch: createProxyFetch(),
98
+ })
99
+ : createAzure({
100
+ resourceName: this.resourceName,
101
+ apiKey: this.apiKey,
102
+ apiVersion: this.apiVersion,
103
+ useDeploymentBasedUrls: true,
104
+ fetch: createProxyFetch(),
105
+ });
57
106
  logger.debug("Azure Vercel Provider initialized", {
58
107
  deployment: this.deployment,
59
108
  resourceName: this.resourceName,
@@ -77,18 +126,18 @@ export class AzureOpenAIProvider extends BaseProvider {
77
126
  }
78
127
  formatProviderError(error) {
79
128
  if (error instanceof TimeoutError) {
80
- return new Error(`Azure OpenAI request timed out: ${error.message}`);
129
+ return new NetworkError(`Request timed out: ${error.message}`, "azure");
81
130
  }
82
131
  const errorObj = error;
83
132
  if (errorObj?.message &&
84
133
  typeof errorObj.message === "string" &&
85
134
  errorObj.message.includes("401")) {
86
- return new Error("Invalid Azure OpenAI API key or endpoint.");
135
+ return new AuthenticationError("Invalid Azure OpenAI API key or endpoint.", "azure");
87
136
  }
88
137
  const message = errorObj?.message && typeof errorObj.message === "string"
89
138
  ? errorObj.message
90
139
  : "Unknown error";
91
- return new Error(`Azure OpenAI error: ${message}`);
140
+ return new ProviderError(`Azure OpenAI error: ${message}`, "azure");
92
141
  }
93
142
  // executeGenerate removed - BaseProvider handles all generation with tools
94
143
  async executeStream(options, _analysisSchema) {
@@ -0,0 +1,35 @@
1
+ import { type LanguageModel } from "ai";
2
+ import type { AIProviderName } from "../constants/enums.js";
3
+ import { BaseProvider } from "../core/baseProvider.js";
4
+ import type { NeurolinkCredentials, StreamOptions, StreamResult, ValidationSchema } from "../types/index.js";
5
+ /**
6
+ * Cloudflare Workers AI Provider
7
+ *
8
+ * Edge-served open models (Llama / Mistral / Qwen / Gemma) at
9
+ * `https://api.cloudflare.com/client/v4/accounts/{accountId}/ai/v1`
10
+ * (OpenAI-compatible). Cheapest tier for high-volume usage.
11
+ *
12
+ * Required env: `CLOUDFLARE_API_KEY` + `CLOUDFLARE_ACCOUNT_ID`.
13
+ *
14
+ * @see https://developers.cloudflare.com/workers-ai/configuration/open-ai-compatibility/
15
+ */
16
+ export declare class CloudflareProvider extends BaseProvider {
17
+ private model;
18
+ private apiKey;
19
+ private baseURL;
20
+ constructor(modelName?: string, sdk?: unknown, _region?: string, credentials?: NeurolinkCredentials["cloudflare"]);
21
+ protected executeStream(options: StreamOptions, _analysisSchema?: ValidationSchema): Promise<StreamResult>;
22
+ private executeStreamInner;
23
+ protected getProviderName(): AIProviderName;
24
+ protected getDefaultModel(): string;
25
+ protected getAISDKModel(): LanguageModel;
26
+ protected formatProviderError(error: unknown): Error;
27
+ validateConfiguration(): Promise<boolean>;
28
+ getConfiguration(): {
29
+ provider: AIProviderName;
30
+ model: string;
31
+ defaultModel: string;
32
+ baseURL: string;
33
+ };
34
+ }
35
+ export default CloudflareProvider;