@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
@@ -5,6 +5,7 @@ import { BaseProvider } from "../core/baseProvider.js";
5
5
  import { DEFAULT_MAX_STEPS } from "../core/constants.js";
6
6
  import { streamAnalyticsCollector } from "../core/streamAnalytics.js";
7
7
  import { createProxyFetch } from "../proxy/proxyFetch.js";
8
+ import { AuthenticationError, InvalidModelError, NetworkError, ProviderError, RateLimitError, } from "../types/index.js";
8
9
  import { isAbortError } from "../utils/errorHandling.js";
9
10
  import { emitToolEndFromStepFinish } from "../utils/toolEndEmitter.js";
10
11
  import { logger } from "../utils/logger.js";
@@ -37,11 +38,15 @@ const getOpenRouterConfig = () => {
37
38
  * - 'google/gemini-2.5-flash'
38
39
  * - 'meta-llama/llama-3-70b-instruct'
39
40
  *
40
- * You can override the default by setting the OPENROUTER_MODEL environment variable.
41
+ * The previous default `anthropic/claude-3-5-sonnet` was retired by OpenRouter
42
+ * in late 2025 and now returns "No endpoints found for model" for every
43
+ * caller. Default bumped to the current Anthropic mainline (Claude Sonnet
44
+ * 4.5) so callers without an `OPENROUTER_MODEL` env var don't hit a dead
45
+ * model. Must stay aligned with the registry default in
46
+ * `src/lib/factories/providerRegistry.ts` and `PROVIDER_DEFAULTS` in
47
+ * `src/lib/utils/modelChoices.ts`.
41
48
  *
42
- * Default updated from `anthropic/claude-3-5-sonnet` to `anthropic/claude-sonnet-4.5`
43
- * because OpenRouter sunset the Claude 3.5 Sonnet endpoint upstream — every
44
- * call against the old default returned `No endpoints found` 404s.
49
+ * You can override the default by setting the OPENROUTER_MODEL environment variable.
45
50
  */
46
51
  const getDefaultOpenRouterModel = () => {
47
52
  return getProviderModel("OPENROUTER_MODEL", "anthropic/claude-sonnet-4.5");
@@ -111,62 +116,62 @@ export class OpenRouterProvider extends BaseProvider {
111
116
  }
112
117
  formatProviderError(error) {
113
118
  if (error instanceof TimeoutError) {
114
- return new Error(`OpenRouter request timed out: ${error.message}`);
119
+ return new NetworkError(`Request timed out: ${error.message}`, "openrouter");
115
120
  }
116
121
  // Check for timeout by error name and message as fallback
117
122
  const errorRecord = error;
118
123
  if (errorRecord?.name === "TimeoutError" ||
119
124
  (typeof errorRecord?.message === "string" &&
120
125
  errorRecord.message.includes("Timeout"))) {
121
- return new Error(`OpenRouter request timed out: ${errorRecord?.message || "Unknown timeout"}`);
126
+ return new NetworkError(`Request timed out: ${errorRecord?.message || "Unknown timeout"}`, "openrouter");
122
127
  }
123
128
  if (typeof errorRecord?.message === "string") {
124
129
  if (errorRecord.message.includes("ECONNREFUSED") ||
125
130
  errorRecord.message.includes("Failed to fetch")) {
126
- return new Error("OpenRouter API not available. Please check your network connection and try again.");
131
+ return new NetworkError("OpenRouter API not available. Please check your network connection and try again.", "openrouter");
127
132
  }
128
133
  if (errorRecord.message.includes("API_KEY_INVALID") ||
129
134
  errorRecord.message.includes("Invalid API key") ||
130
135
  errorRecord.message.includes("invalid_api_key") ||
131
136
  errorRecord.message.includes("Unauthorized")) {
132
- return new Error("Invalid OpenRouter API key. Please check your OPENROUTER_API_KEY environment variable. " +
133
- "Get your key at https://openrouter.ai/keys");
137
+ return new AuthenticationError("Invalid OpenRouter API key. Please check your OPENROUTER_API_KEY environment variable. " +
138
+ "Get your key at https://openrouter.ai/keys", "openrouter");
134
139
  }
135
140
  if (errorRecord.message.includes("rate limit")) {
136
- return new Error("OpenRouter rate limit exceeded. Please try again later or upgrade your account at https://openrouter.ai/credits");
141
+ return new RateLimitError("OpenRouter rate limit exceeded. Please try again later or upgrade your account at https://openrouter.ai/credits", "openrouter");
137
142
  }
138
143
  if (errorRecord.message.includes("model") &&
139
144
  errorRecord.message.includes("not found")) {
140
- return new Error(`Model '${this.modelName}' not available on OpenRouter. ` +
141
- "Browse available models at https://openrouter.ai/models");
145
+ return new InvalidModelError(`Model '${this.modelName}' not available on OpenRouter. ` +
146
+ "Browse available models at https://openrouter.ai/models", "openrouter");
142
147
  }
143
148
  if (errorRecord.message.includes("insufficient_credits")) {
144
- return new Error("Insufficient OpenRouter credits. Add credits at https://openrouter.ai/credits");
149
+ return new ProviderError("Insufficient OpenRouter credits. Add credits at https://openrouter.ai/credits", "openrouter");
145
150
  }
146
151
  // "No endpoints found" — model temporarily unavailable or unsupported parameters
147
152
  // This is distinct from tool errors: it can happen on any request when the
148
153
  // model has no available providers on OpenRouter (e.g., free-tier model down).
149
154
  if (errorRecord.message.includes("No endpoints found")) {
150
- return new Error(`No endpoints found for model '${this.modelName}' on OpenRouter. ` +
155
+ return new InvalidModelError(`No endpoints found for model '${this.modelName}' on OpenRouter. ` +
151
156
  "The model may be temporarily unavailable or does not support the requested parameters. " +
152
- "Try a different model or check availability at https://openrouter.ai/models");
157
+ "Try a different model or check availability at https://openrouter.ai/models", "openrouter");
153
158
  }
154
159
  // Tool/function calling errors
155
160
  if (errorRecord.message.includes("tool use") ||
156
161
  errorRecord.message.includes("tool_use") ||
157
162
  errorRecord.message.includes("function_call") ||
158
163
  errorRecord.message.includes("tools are not supported")) {
159
- return new Error(`Model '${this.modelName}' does not support tool calling. ` +
164
+ return new ProviderError(`Model '${this.modelName}' does not support tool calling. ` +
160
165
  "Use a tool-capable model like:\n" +
161
166
  " • google/gemini-2.0-flash-exp:free (free)\n" +
162
167
  " • meta-llama/llama-3.3-70b-instruct:free (free)\n" +
163
- " • anthropic/claude-3-5-sonnet (paid)\n" +
168
+ " • anthropic/claude-3.7-sonnet (paid)\n" +
164
169
  " • openai/gpt-4o (paid)\n" +
165
170
  "Or use --disableTools flag. " +
166
- "See all tool-capable models at https://openrouter.ai/models?supported_parameters=tools");
171
+ "See all tool-capable models at https://openrouter.ai/models?supported_parameters=tools", "openrouter");
167
172
  }
168
173
  }
169
- return new Error(`OpenRouter error: ${errorRecord?.message || "Unknown error"}`);
174
+ return new ProviderError(`OpenRouter error: ${errorRecord?.message || "Unknown error"}`, "openrouter");
170
175
  }
171
176
  /**
172
177
  * OpenRouter supports tools for compatible models
@@ -211,7 +216,7 @@ export class OpenRouterProvider extends BaseProvider {
211
216
  // For unknown models, warn and disable tools (safe default)
212
217
  logger.warn("OpenRouter: Unknown model tool capability, disabling tools", {
213
218
  model: modelName,
214
- suggestion: "Use a known tool-capable model like anthropic/claude-3-5-sonnet, openai/gpt-4o, or google/gemini-2.0-flash-exp:free",
219
+ suggestion: "Use a known tool-capable model like anthropic/claude-3.7-sonnet, openai/gpt-4o, or google/gemini-2.0-flash-exp:free",
215
220
  });
216
221
  return false;
217
222
  }
@@ -255,7 +260,12 @@ export class OpenRouterProvider extends BaseProvider {
255
260
  messages: messages,
256
261
  temperature: options.temperature,
257
262
  maxRetries: 0, // Disable SDK retries - let caller handle rate limit retries with delays
258
- ...(options.maxTokens && { maxTokens: options.maxTokens }),
263
+ // AI SDK v6 renamed `maxTokens` to `maxOutputTokens` — using the old
264
+ // name here is a silent no-op, so OpenRouter sees no cap and applies
265
+ // the model's full output max (typically 64K+ tokens) to its pre-bill
266
+ // affordability check. That trips "This request requires more credits"
267
+ // even on cheap models when the account balance is low.
268
+ ...(options.maxTokens && { maxOutputTokens: options.maxTokens }),
259
269
  ...(shouldUseTools &&
260
270
  Object.keys(tools).length > 0 && {
261
271
  tools,
@@ -456,10 +466,12 @@ export class OpenRouterProvider extends BaseProvider {
456
466
  error: error instanceof Error ? error.message : String(error),
457
467
  });
458
468
  }
459
- // Fallback to hardcoded list if API fetch fails
469
+ // Fallback to hardcoded list if API fetch fails. Aligned with
470
+ // `getDefaultOpenRouterModel()` — `anthropic/claude-3-5-sonnet` was
471
+ // retired by OpenRouter late 2025 and would return a dead model here.
460
472
  const fallbackModels = [
461
473
  // Anthropic Claude models
462
- "anthropic/claude-3-5-sonnet",
474
+ "anthropic/claude-3.7-sonnet",
463
475
  "anthropic/claude-3-5-haiku",
464
476
  "anthropic/claude-3-opus",
465
477
  // OpenAI models
@@ -4,6 +4,7 @@ import { BaseProvider } from "../core/baseProvider.js";
4
4
  import { DEFAULT_MAX_STEPS } from "../core/constants.js";
5
5
  import { streamAnalyticsCollector } from "../core/streamAnalytics.js";
6
6
  import { createProxyFetch } from "../proxy/proxyFetch.js";
7
+ import { AuthenticationError, InvalidModelError, NetworkError, ProviderError, RateLimitError, } from "../types/index.js";
7
8
  import { emitToolEndFromStepFinish } from "../utils/toolEndEmitter.js";
8
9
  import { logger } from "../utils/logger.js";
9
10
  import { buildNoOutputSentinel, detectPostStreamNoOutput, stampNoOutputSpan, } from "../utils/noOutputSentinel.js";
@@ -127,36 +128,36 @@ export class OpenAICompatibleProvider extends BaseProvider {
127
128
  }
128
129
  formatProviderError(error) {
129
130
  if (error instanceof TimeoutError) {
130
- return new Error(`OpenAI Compatible request timed out: ${error.message}`);
131
+ return new NetworkError(`Request timed out: ${error.message}`, "openai-compatible");
131
132
  }
132
133
  // Check for timeout by error name and message as fallback
133
134
  const errorRecord = error;
134
135
  if (errorRecord?.name === "TimeoutError" ||
135
136
  (typeof errorRecord?.message === "string" &&
136
137
  errorRecord.message.includes("Timeout"))) {
137
- return new Error(`OpenAI Compatible request timed out: ${errorRecord?.message || "Unknown timeout"}`);
138
+ return new NetworkError(`Request timed out: ${errorRecord?.message || "Unknown timeout"}`, "openai-compatible");
138
139
  }
139
140
  if (typeof errorRecord?.message === "string") {
140
141
  if (errorRecord.message.includes("ECONNREFUSED") ||
141
142
  errorRecord.message.includes("Failed to fetch")) {
142
- return new Error(`OpenAI Compatible endpoint not available. Please check your OPENAI_COMPATIBLE_BASE_URL: ${this.config.baseURL}`);
143
+ return new NetworkError(`OpenAI Compatible endpoint not available. Please check your OPENAI_COMPATIBLE_BASE_URL: ${this.config.baseURL}`, "openai-compatible");
143
144
  }
144
145
  if (errorRecord.message.includes("API_KEY_INVALID") ||
145
146
  errorRecord.message.includes("Invalid API key") ||
146
147
  errorRecord.message.includes("Unauthorized")) {
147
- return new Error("Invalid OpenAI Compatible API key. Please check your OPENAI_COMPATIBLE_API_KEY environment variable.");
148
+ return new AuthenticationError("Invalid OpenAI Compatible API key. Please check your OPENAI_COMPATIBLE_API_KEY environment variable.", "openai-compatible");
148
149
  }
149
150
  if (errorRecord.message.includes("rate limit")) {
150
- return new Error("OpenAI Compatible rate limit exceeded. Please try again later.");
151
+ return new RateLimitError("OpenAI Compatible rate limit exceeded. Please try again later.", "openai-compatible");
151
152
  }
152
153
  if (errorRecord.message.includes("model") &&
153
154
  (errorRecord.message.includes("not found") ||
154
155
  errorRecord.message.includes("does not exist"))) {
155
- return new Error(`Model '${this.modelName}' not available on OpenAI Compatible endpoint. ` +
156
- "Please check available models or use getAvailableModels() to see supported models.");
156
+ return new InvalidModelError(`Model '${this.modelName}' not available on OpenAI Compatible endpoint. ` +
157
+ "Please check available models or use getAvailableModels() to see supported models.", "openai-compatible");
157
158
  }
158
159
  }
159
- return new Error(`OpenAI Compatible error: ${errorRecord?.message || "Unknown error"}`);
160
+ return new ProviderError(`OpenAI Compatible error: ${errorRecord?.message || "Unknown error"}`, "openai-compatible");
160
161
  }
161
162
  /**
162
163
  * OpenAI Compatible endpoints support tools for compatible models
@@ -0,0 +1,33 @@
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
+ * Perplexity Provider
7
+ *
8
+ * Sonar models with built-in web grounding. OpenAI-compatible chat
9
+ * completions at api.perplexity.ai. Best for queries that need fresh
10
+ * web context (search-augmented answers + citations).
11
+ *
12
+ * @see https://docs.perplexity.ai/api-reference/chat-completions
13
+ */
14
+ export declare class PerplexityProvider extends BaseProvider {
15
+ private model;
16
+ private apiKey;
17
+ private baseURL;
18
+ constructor(modelName?: string, sdk?: unknown, _region?: string, credentials?: NeurolinkCredentials["perplexity"]);
19
+ protected executeStream(options: StreamOptions, _analysisSchema?: ValidationSchema): Promise<StreamResult>;
20
+ private executeStreamInner;
21
+ protected getProviderName(): AIProviderName;
22
+ protected getDefaultModel(): string;
23
+ protected getAISDKModel(): LanguageModel;
24
+ protected formatProviderError(error: unknown): Error;
25
+ validateConfiguration(): Promise<boolean>;
26
+ getConfiguration(): {
27
+ provider: AIProviderName;
28
+ model: string;
29
+ defaultModel: string;
30
+ baseURL: string;
31
+ };
32
+ }
33
+ export default PerplexityProvider;
@@ -0,0 +1,179 @@
1
+ import { createOpenAI } from "@ai-sdk/openai";
2
+ import { stepCountIs, streamText } from "ai";
3
+ import { PerplexityModels } from "../constants/enums.js";
4
+ import { BaseProvider } from "../core/baseProvider.js";
5
+ import { DEFAULT_MAX_STEPS } from "../core/constants.js";
6
+ import { streamAnalyticsCollector } from "../core/streamAnalytics.js";
7
+ import { isNeuroLink } from "../neurolink.js";
8
+ import { createLoggingFetch } from "../utils/loggingFetch.js";
9
+ import { tracers, ATTR, withClientStreamSpan } from "../telemetry/index.js";
10
+ import { AuthenticationError, InvalidModelError, NetworkError, ProviderError, RateLimitError, } from "../types/index.js";
11
+ import { logger } from "../utils/logger.js";
12
+ import { createPerplexityConfig, getProviderModel, validateApiKey, } from "../utils/providerConfig.js";
13
+ import { composeAbortSignals, createTimeoutController, TimeoutError, } from "../utils/timeout.js";
14
+ import { emitToolEndFromStepFinish } from "../utils/toolEndEmitter.js";
15
+ import { resolveToolChoice } from "../utils/toolChoice.js";
16
+ import { toAnalyticsStreamResult } from "./providerTypeUtils.js";
17
+ const PERPLEXITY_DEFAULT_BASE_URL = "https://api.perplexity.ai";
18
+ const getPerplexityApiKey = () => validateApiKey(createPerplexityConfig());
19
+ const getDefaultPerplexityModel = () => getProviderModel("PERPLEXITY_MODEL", PerplexityModels.SONAR);
20
+ /**
21
+ * Perplexity Provider
22
+ *
23
+ * Sonar models with built-in web grounding. OpenAI-compatible chat
24
+ * completions at api.perplexity.ai. Best for queries that need fresh
25
+ * web context (search-augmented answers + citations).
26
+ *
27
+ * @see https://docs.perplexity.ai/api-reference/chat-completions
28
+ */
29
+ export class PerplexityProvider extends BaseProvider {
30
+ model;
31
+ apiKey;
32
+ baseURL;
33
+ constructor(modelName, sdk, _region, credentials) {
34
+ const validatedNeurolink = isNeuroLink(sdk) ? sdk : undefined;
35
+ super(modelName, "perplexity", validatedNeurolink);
36
+ const overrideApiKey = credentials?.apiKey?.trim();
37
+ this.apiKey =
38
+ overrideApiKey && overrideApiKey.length > 0
39
+ ? overrideApiKey
40
+ : getPerplexityApiKey();
41
+ this.baseURL =
42
+ credentials?.baseURL ??
43
+ process.env.PERPLEXITY_BASE_URL ??
44
+ PERPLEXITY_DEFAULT_BASE_URL;
45
+ const perplexity = createOpenAI({
46
+ apiKey: this.apiKey,
47
+ baseURL: this.baseURL,
48
+ fetch: createLoggingFetch("perplexity"),
49
+ });
50
+ this.model = perplexity.chat(this.modelName);
51
+ logger.debug("Perplexity Provider initialized", {
52
+ modelName: this.modelName,
53
+ providerName: this.providerName,
54
+ baseURL: this.baseURL,
55
+ });
56
+ }
57
+ async executeStream(options, _analysisSchema) {
58
+ return withClientStreamSpan({
59
+ name: "neurolink.provider.stream",
60
+ tracer: tracers.provider,
61
+ attributes: {
62
+ [ATTR.GEN_AI_SYSTEM]: "perplexity",
63
+ [ATTR.GEN_AI_MODEL]: this.modelName,
64
+ [ATTR.GEN_AI_OPERATION]: "stream",
65
+ [ATTR.NL_STREAM_MODE]: true,
66
+ },
67
+ }, async () => this.executeStreamInner(options), (r) => r.stream, (r, wrapped) => ({ ...r, stream: wrapped }));
68
+ }
69
+ async executeStreamInner(options) {
70
+ this.validateStreamOptions(options);
71
+ // Resolve per-call credentials first, then fall back to instance-level.
72
+ const perCallCreds = options.credentials?.perplexity;
73
+ const effectiveApiKey = perCallCreds?.apiKey?.trim() || this.apiKey;
74
+ const effectiveBaseURL = perCallCreds?.baseURL || this.baseURL;
75
+ const startTime = Date.now();
76
+ const timeout = this.getTimeout(options);
77
+ const timeoutController = createTimeoutController(timeout, this.providerName, "stream");
78
+ try {
79
+ // Perplexity Sonar's tool support is limited; default to disabled when
80
+ // not explicitly requested by the caller. The web-grounding signal is
81
+ // baked into the model itself, not exposed as tool calls.
82
+ const shouldUseTools = !options.disableTools && this.supportsTools();
83
+ const tools = shouldUseTools
84
+ ? options.tools || (await this.getAllTools())
85
+ : {};
86
+ const messages = await this.buildMessagesForStream(options);
87
+ // When per-call credentials differ from instance, build a fresh client.
88
+ const hasDifferentCreds = effectiveApiKey !== this.apiKey || effectiveBaseURL !== this.baseURL;
89
+ const model = hasDifferentCreds
90
+ ? createOpenAI({
91
+ apiKey: effectiveApiKey,
92
+ baseURL: effectiveBaseURL,
93
+ fetch: createLoggingFetch("perplexity"),
94
+ }).chat(this.modelName)
95
+ : await this.getAISDKModelWithMiddleware(options);
96
+ const result = await streamText({
97
+ model,
98
+ messages,
99
+ temperature: options.temperature,
100
+ maxOutputTokens: options.maxTokens,
101
+ tools,
102
+ stopWhen: stepCountIs(options.maxSteps || DEFAULT_MAX_STEPS),
103
+ toolChoice: resolveToolChoice(options, tools, shouldUseTools),
104
+ abortSignal: composeAbortSignals(options.abortSignal, timeoutController?.controller.signal),
105
+ experimental_telemetry: this.telemetryHandler.getTelemetryConfig(options),
106
+ experimental_repairToolCall: this.getToolCallRepairFn(options),
107
+ onStepFinish: ({ toolCalls, toolResults }) => {
108
+ emitToolEndFromStepFinish(this.neurolink?.getEventEmitter(), toolResults);
109
+ this.handleToolExecutionStorage(toolCalls, toolResults, options, new Date()).catch((error) => {
110
+ logger.warn("[PerplexityProvider] Failed to store tool executions", {
111
+ provider: this.providerName,
112
+ error: error instanceof Error ? error.message : String(error),
113
+ });
114
+ });
115
+ },
116
+ });
117
+ timeoutController?.cleanup();
118
+ const transformedStream = this.createTextStream(result);
119
+ const analyticsPromise = streamAnalyticsCollector.createAnalytics(this.providerName, this.modelName, toAnalyticsStreamResult(result), Date.now() - startTime, {
120
+ requestId: `perplexity-stream-${Date.now()}`,
121
+ streamingMode: true,
122
+ });
123
+ return {
124
+ stream: transformedStream,
125
+ provider: this.providerName,
126
+ model: this.modelName,
127
+ analytics: analyticsPromise,
128
+ metadata: { startTime, streamId: `perplexity-${Date.now()}` },
129
+ };
130
+ }
131
+ catch (error) {
132
+ timeoutController?.cleanup();
133
+ throw this.handleProviderError(error);
134
+ }
135
+ }
136
+ getProviderName() {
137
+ return this.providerName;
138
+ }
139
+ getDefaultModel() {
140
+ return getDefaultPerplexityModel();
141
+ }
142
+ getAISDKModel() {
143
+ return this.model;
144
+ }
145
+ formatProviderError(error) {
146
+ if (error instanceof TimeoutError) {
147
+ return new NetworkError(`Request timed out: ${error.message}`, "perplexity");
148
+ }
149
+ const errorRecord = error;
150
+ const message = typeof errorRecord?.message === "string"
151
+ ? errorRecord.message
152
+ : "Unknown error";
153
+ if (message.includes("Invalid API key") ||
154
+ message.includes("Authentication") ||
155
+ message.includes("401")) {
156
+ return new AuthenticationError("Invalid Perplexity API key. Get one at https://www.perplexity.ai/settings/api", "perplexity");
157
+ }
158
+ if (message.includes("rate limit") || message.includes("429")) {
159
+ return new RateLimitError("Perplexity rate limit exceeded. Back off and retry.", "perplexity");
160
+ }
161
+ if (message.includes("model_not_found") || message.includes("404")) {
162
+ return new InvalidModelError(`Perplexity model '${this.modelName}' not found. Use sonar, sonar-pro, sonar-reasoning, sonar-reasoning-pro, or sonar-deep-research.`, "perplexity");
163
+ }
164
+ return new ProviderError(`Perplexity error: ${message}`, "perplexity");
165
+ }
166
+ async validateConfiguration() {
167
+ return typeof this.apiKey === "string" && this.apiKey.trim().length > 0;
168
+ }
169
+ getConfiguration() {
170
+ return {
171
+ provider: this.providerName,
172
+ model: this.modelName,
173
+ defaultModel: getDefaultPerplexityModel(),
174
+ baseURL: this.baseURL,
175
+ };
176
+ }
177
+ }
178
+ export default PerplexityProvider;
179
+ //# sourceMappingURL=perplexity.js.map
@@ -0,0 +1,34 @@
1
+ import type { LanguageModel } from "ai";
2
+ import type { AIProviderName } from "../constants/enums.js";
3
+ import { BaseProvider } from "../core/baseProvider.js";
4
+ import type { EnhancedGenerateResult, NeurolinkCredentials, StreamOptions, StreamResult, TextGenerationOptions, ValidationSchema } from "../types/index.js";
5
+ /**
6
+ * Recraft Provider — image generation with vector / illustration focus.
7
+ *
8
+ * Hits external.api.recraft.ai/v1/images/generations (OpenAI-compat
9
+ * shape). Returns either url or b64_json; we convert URL → base64 for
10
+ * the uniform imageOutput contract.
11
+ *
12
+ * @see https://www.recraft.ai/docs
13
+ */
14
+ export declare class RecraftProvider extends BaseProvider {
15
+ private readonly apiKey;
16
+ private readonly baseURL;
17
+ private readonly proxyFetch;
18
+ constructor(modelName?: string, sdk?: unknown, _region?: string, credentials?: NeurolinkCredentials["recraft"]);
19
+ protected getProviderName(): AIProviderName;
20
+ protected getDefaultModel(): string;
21
+ supportsTools(): boolean;
22
+ protected getAISDKModel(): LanguageModel;
23
+ protected executeStream(_options: StreamOptions, _analysisSchema?: ValidationSchema): Promise<StreamResult>;
24
+ protected formatProviderError(error: unknown): Error;
25
+ protected executeImageGeneration(options: TextGenerationOptions): Promise<EnhancedGenerateResult>;
26
+ validateConfiguration(): Promise<boolean>;
27
+ getConfiguration(): {
28
+ provider: AIProviderName;
29
+ model: string;
30
+ defaultModel: string;
31
+ baseURL: string;
32
+ };
33
+ }
34
+ export default RecraftProvider;
@@ -0,0 +1,197 @@
1
+ import { RecraftModels } from "../constants/enums.js";
2
+ import { BaseProvider } from "../core/baseProvider.js";
3
+ import { isNeuroLink } from "../neurolink.js";
4
+ import { createProxyFetch } from "../proxy/proxyFetch.js";
5
+ import { AuthenticationError, InvalidModelError, ProviderError, RateLimitError, } from "../types/index.js";
6
+ import { logger } from "../utils/logger.js";
7
+ import { createRecraftConfig, getProviderModel, validateApiKey, } from "../utils/providerConfig.js";
8
+ import { MAX_IMAGE_BYTES, readBoundedBuffer } from "../utils/sizeGuard.js";
9
+ import { assertSafeUrl } from "../utils/ssrfGuard.js";
10
+ const RECRAFT_DEFAULT_BASE_URL = "https://external.api.recraft.ai/v1";
11
+ const REQUEST_TIMEOUT_MS = 120_000;
12
+ const getRecraftApiKey = () => validateApiKey(createRecraftConfig());
13
+ const getDefaultRecraftModel = () => getProviderModel("RECRAFT_MODEL", RecraftModels.RECRAFT_V3);
14
+ /**
15
+ * Recraft Provider — image generation with vector / illustration focus.
16
+ *
17
+ * Hits external.api.recraft.ai/v1/images/generations (OpenAI-compat
18
+ * shape). Returns either url or b64_json; we convert URL → base64 for
19
+ * the uniform imageOutput contract.
20
+ *
21
+ * @see https://www.recraft.ai/docs
22
+ */
23
+ export class RecraftProvider extends BaseProvider {
24
+ apiKey;
25
+ baseURL;
26
+ proxyFetch;
27
+ constructor(modelName, sdk, _region, credentials) {
28
+ const validatedNeurolink = isNeuroLink(sdk) ? sdk : undefined;
29
+ super(modelName, "recraft", validatedNeurolink);
30
+ const overrideKey = credentials?.apiKey?.trim();
31
+ this.apiKey =
32
+ overrideKey && overrideKey.length > 0 ? overrideKey : getRecraftApiKey();
33
+ this.baseURL =
34
+ credentials?.baseURL ??
35
+ process.env.RECRAFT_BASE_URL ??
36
+ RECRAFT_DEFAULT_BASE_URL;
37
+ this.proxyFetch = createProxyFetch();
38
+ logger.debug("Recraft Provider initialized (image-gen only)", {
39
+ modelName: this.modelName,
40
+ baseURL: this.baseURL,
41
+ });
42
+ }
43
+ getProviderName() {
44
+ return this.providerName;
45
+ }
46
+ getDefaultModel() {
47
+ return getDefaultRecraftModel();
48
+ }
49
+ supportsTools() {
50
+ return false;
51
+ }
52
+ getAISDKModel() {
53
+ throw new Error("Recraft is an image-generation-only provider; chat completions are not available.");
54
+ }
55
+ async executeStream(_options, _analysisSchema) {
56
+ throw new Error("Recraft is an image-generation-only provider; streaming chat is not available.");
57
+ }
58
+ formatProviderError(error) {
59
+ const message = error instanceof Error
60
+ ? error.message
61
+ : typeof error === "string"
62
+ ? error
63
+ : "Unknown error";
64
+ if (message.includes("401") ||
65
+ message.toLowerCase().includes("unauthorized")) {
66
+ return new AuthenticationError("Invalid Recraft API key. Get one at https://www.recraft.ai/api", "recraft");
67
+ }
68
+ if (message.includes("429") ||
69
+ message.toLowerCase().includes("rate limit")) {
70
+ return new RateLimitError("Recraft rate limit exceeded. Back off and retry.", "recraft");
71
+ }
72
+ if (message.includes("404") || message.includes("model_not_found")) {
73
+ return new InvalidModelError(`Recraft model '${this.modelName}' not found. Use recraftv3, recraftv3-svg, or recraftv2.`, "recraft");
74
+ }
75
+ return new ProviderError(`Recraft error: ${message}`, "recraft");
76
+ }
77
+ async executeImageGeneration(options) {
78
+ const startTime = Date.now();
79
+ // Resolve per-call credentials first, then fall back to instance-level.
80
+ const perCallCreds = options.credentials?.recraft;
81
+ const effectiveApiKey = perCallCreds?.apiKey?.trim() || this.apiKey;
82
+ const effectiveBaseURL = perCallCreds?.baseURL || this.baseURL;
83
+ const prompt = options.prompt ?? options.input?.text ?? "";
84
+ if (!prompt.trim()) {
85
+ throw new Error("Recraft image generation requires a prompt (input.text or prompt)");
86
+ }
87
+ const extras = options;
88
+ const body = {
89
+ model: options.model ?? this.modelName,
90
+ prompt,
91
+ n: 1,
92
+ response_format: "b64_json",
93
+ };
94
+ if (extras.negativePrompt) {
95
+ body.negative_prompt = extras.negativePrompt;
96
+ }
97
+ if (extras.style) {
98
+ body.style = extras.style;
99
+ }
100
+ if (extras.styleId) {
101
+ body.style_id = extras.styleId;
102
+ }
103
+ if (extras.size) {
104
+ body.size = extras.size;
105
+ }
106
+ const controller = new AbortController();
107
+ const timeoutId = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
108
+ let response;
109
+ try {
110
+ response = await this.proxyFetch(`${effectiveBaseURL}/images/generations`, {
111
+ method: "POST",
112
+ headers: {
113
+ Authorization: `Bearer ${effectiveApiKey}`,
114
+ "Content-Type": "application/json",
115
+ },
116
+ body: JSON.stringify(body),
117
+ signal: controller.signal,
118
+ });
119
+ }
120
+ catch (err) {
121
+ if (err instanceof Error && err.name === "AbortError") {
122
+ throw this.formatProviderError(new Error(`Recraft image-gen request timed out after ${REQUEST_TIMEOUT_MS / 1000}s`));
123
+ }
124
+ throw this.formatProviderError(err);
125
+ }
126
+ finally {
127
+ clearTimeout(timeoutId);
128
+ }
129
+ if (!response.ok) {
130
+ const text = await response.text();
131
+ throw this.formatProviderError(new Error(`Recraft image-gen failed: ${response.status} — ${text}`));
132
+ }
133
+ const data = (await response.json());
134
+ const entry = data.data?.[0];
135
+ if (!entry) {
136
+ throw new Error("Recraft returned no image data");
137
+ }
138
+ let base64;
139
+ if (entry.b64_json) {
140
+ base64 = entry.b64_json;
141
+ }
142
+ else if (entry.url) {
143
+ // Guard the API-returned URL before fetching (provider-returned URLs
144
+ // carry the same SSRF risk as caller-supplied ones).
145
+ await assertSafeUrl(entry.url);
146
+ // Fallback URL download — apply a 60s timeout so it cannot hang indefinitely.
147
+ const dlController = new AbortController();
148
+ const dlTimeoutId = setTimeout(() => dlController.abort(), 60_000);
149
+ let dl;
150
+ try {
151
+ dl = await this.proxyFetch(entry.url, { signal: dlController.signal });
152
+ }
153
+ catch (err) {
154
+ if (err instanceof Error && err.name === "AbortError") {
155
+ throw new Error("Recraft image download timed out after 60s", {
156
+ cause: err,
157
+ });
158
+ }
159
+ throw err;
160
+ }
161
+ finally {
162
+ clearTimeout(dlTimeoutId);
163
+ }
164
+ if (!dl.ok) {
165
+ throw new Error(`Failed to download Recraft image: ${dl.status}`);
166
+ }
167
+ const dlBuf = await readBoundedBuffer(dl, MAX_IMAGE_BYTES, "Recraft image");
168
+ base64 = dlBuf.toString("base64");
169
+ }
170
+ else {
171
+ throw new Error("Recraft response missing both b64_json and url");
172
+ }
173
+ const generationTimeMs = Date.now() - startTime;
174
+ logger.info(`[RecraftProvider] Generated image (${base64.length} base64 chars) in ${generationTimeMs}ms — model ${this.modelName}`);
175
+ return {
176
+ content: prompt,
177
+ provider: this.providerName,
178
+ model: this.modelName,
179
+ // output: 1000 = sentinel for per-image pricing (see pricing.ts)
180
+ usage: { input: 0, output: 1000, total: 1000 },
181
+ imageOutput: { base64 },
182
+ };
183
+ }
184
+ async validateConfiguration() {
185
+ return typeof this.apiKey === "string" && this.apiKey.trim().length > 0;
186
+ }
187
+ getConfiguration() {
188
+ return {
189
+ provider: this.providerName,
190
+ model: this.modelName,
191
+ defaultModel: getDefaultRecraftModel(),
192
+ baseURL: this.baseURL,
193
+ };
194
+ }
195
+ }
196
+ export default RecraftProvider;
197
+ //# sourceMappingURL=recraft.js.map