@juspay/neurolink 9.63.1 → 9.65.0
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.
- package/CHANGELOG.md +12 -0
- package/README.md +18 -17
- package/dist/adapters/providerImageAdapter.js +29 -1
- package/dist/adapters/replicate/auth.d.ts +19 -0
- package/dist/adapters/replicate/auth.js +32 -0
- package/dist/adapters/replicate/predictionLifecycle.d.ts +46 -0
- package/dist/adapters/replicate/predictionLifecycle.js +283 -0
- package/dist/adapters/video/klingVideoHandler.d.ts +37 -0
- package/dist/adapters/video/klingVideoHandler.js +305 -0
- package/dist/adapters/video/replicateVideoHandler.d.ts +29 -0
- package/dist/adapters/video/replicateVideoHandler.js +157 -0
- package/dist/adapters/video/runwayVideoHandler.d.ts +32 -0
- package/dist/adapters/video/runwayVideoHandler.js +316 -0
- package/dist/adapters/video/vertexVideoHandler.d.ts +19 -1
- package/dist/adapters/video/vertexVideoHandler.js +42 -11
- package/dist/autoresearch/runner.js +8 -2
- package/dist/avatar/index.d.ts +13 -0
- package/dist/avatar/index.js +13 -0
- package/dist/avatar/providers/DIDAvatar.d.ts +49 -0
- package/dist/avatar/providers/DIDAvatar.js +501 -0
- package/dist/avatar/providers/HeyGenAvatar.d.ts +30 -0
- package/dist/avatar/providers/HeyGenAvatar.js +337 -0
- package/dist/avatar/providers/ReplicateAvatar.d.ts +36 -0
- package/dist/avatar/providers/ReplicateAvatar.js +267 -0
- package/dist/browser/neurolink.min.js +573 -554
- package/dist/cli/commands/mcp.js +29 -0
- package/dist/cli/commands/proxy.js +24 -5
- package/dist/cli/factories/commandFactory.d.ts +25 -1
- package/dist/cli/factories/commandFactory.js +341 -63
- package/dist/cli/loop/optionsSchema.d.ts +1 -1
- package/dist/cli/loop/optionsSchema.js +12 -0
- package/dist/constants/contextWindows.js +101 -0
- package/dist/constants/enums.d.ts +273 -2
- package/dist/constants/enums.js +290 -1
- package/dist/constants/videoErrors.d.ts +4 -0
- package/dist/constants/videoErrors.js +4 -0
- package/dist/core/baseProvider.d.ts +23 -3
- package/dist/core/baseProvider.js +217 -11
- package/dist/core/constants.d.ts +11 -0
- package/dist/core/constants.js +69 -1
- package/dist/core/modules/MessageBuilder.js +20 -0
- package/dist/core/redisConversationMemoryManager.js +6 -0
- package/dist/evaluation/index.d.ts +2 -0
- package/dist/evaluation/index.js +4 -0
- package/dist/factories/providerFactory.js +7 -1
- package/dist/factories/providerRegistry.js +203 -2
- package/dist/features/ppt/contentPlanner.js +42 -14
- package/dist/index.d.ts +9 -1
- package/dist/index.js +16 -1
- package/dist/lib/adapters/providerImageAdapter.js +29 -1
- package/dist/lib/adapters/replicate/auth.d.ts +19 -0
- package/dist/lib/adapters/replicate/auth.js +33 -0
- package/dist/lib/adapters/replicate/predictionLifecycle.d.ts +46 -0
- package/dist/lib/adapters/replicate/predictionLifecycle.js +284 -0
- package/dist/lib/adapters/video/klingVideoHandler.d.ts +37 -0
- package/dist/lib/adapters/video/klingVideoHandler.js +306 -0
- package/dist/lib/adapters/video/replicateVideoHandler.d.ts +29 -0
- package/dist/lib/adapters/video/replicateVideoHandler.js +158 -0
- package/dist/lib/adapters/video/runwayVideoHandler.d.ts +32 -0
- package/dist/lib/adapters/video/runwayVideoHandler.js +317 -0
- package/dist/lib/adapters/video/vertexVideoHandler.d.ts +19 -1
- package/dist/lib/adapters/video/vertexVideoHandler.js +42 -11
- package/dist/lib/autoresearch/runner.js +8 -2
- package/dist/lib/avatar/index.d.ts +13 -0
- package/dist/lib/avatar/index.js +14 -0
- package/dist/lib/avatar/providers/DIDAvatar.d.ts +49 -0
- package/dist/lib/avatar/providers/DIDAvatar.js +502 -0
- package/dist/lib/avatar/providers/HeyGenAvatar.d.ts +30 -0
- package/dist/lib/avatar/providers/HeyGenAvatar.js +338 -0
- package/dist/lib/avatar/providers/ReplicateAvatar.d.ts +36 -0
- package/dist/lib/avatar/providers/ReplicateAvatar.js +268 -0
- package/dist/lib/constants/contextWindows.js +101 -0
- package/dist/lib/constants/enums.d.ts +273 -2
- package/dist/lib/constants/enums.js +290 -1
- package/dist/lib/constants/videoErrors.d.ts +4 -0
- package/dist/lib/constants/videoErrors.js +4 -0
- package/dist/lib/core/baseProvider.d.ts +23 -3
- package/dist/lib/core/baseProvider.js +217 -11
- package/dist/lib/core/constants.d.ts +11 -0
- package/dist/lib/core/constants.js +69 -1
- package/dist/lib/core/modules/MessageBuilder.js +20 -0
- package/dist/lib/core/redisConversationMemoryManager.js +6 -0
- package/dist/lib/evaluation/index.d.ts +2 -0
- package/dist/lib/evaluation/index.js +4 -0
- package/dist/lib/factories/providerFactory.js +7 -1
- package/dist/lib/factories/providerRegistry.js +203 -2
- package/dist/lib/features/ppt/contentPlanner.js +42 -14
- package/dist/lib/index.d.ts +9 -1
- package/dist/lib/index.js +16 -1
- package/dist/lib/memory/hippocampusInitializer.d.ts +2 -2
- package/dist/lib/memory/hippocampusInitializer.js +32 -2
- package/dist/lib/middleware/builtin/lifecycle.js +52 -51
- package/dist/lib/music/index.d.ts +13 -0
- package/dist/lib/music/index.js +14 -0
- package/dist/lib/music/providers/BeatovenMusic.d.ts +31 -0
- package/dist/lib/music/providers/BeatovenMusic.js +334 -0
- package/dist/lib/music/providers/ElevenLabsMusic.d.ts +30 -0
- package/dist/lib/music/providers/ElevenLabsMusic.js +169 -0
- package/dist/lib/music/providers/LyriaMusic.d.ts +29 -0
- package/dist/lib/music/providers/LyriaMusic.js +173 -0
- package/dist/lib/music/providers/ReplicateMusic.d.ts +31 -0
- package/dist/lib/music/providers/ReplicateMusic.js +262 -0
- package/dist/lib/neurolink.d.ts +30 -0
- package/dist/lib/neurolink.js +342 -49
- package/dist/lib/providers/amazonBedrock.d.ts +10 -0
- package/dist/lib/providers/amazonBedrock.js +94 -39
- package/dist/lib/providers/anthropic.js +55 -7
- package/dist/lib/providers/anthropicBaseProvider.js +1 -1
- package/dist/lib/providers/azureOpenai.js +66 -17
- package/dist/lib/providers/cloudflare.d.ts +35 -0
- package/dist/lib/providers/cloudflare.js +174 -0
- package/dist/lib/providers/cohere.d.ts +52 -0
- package/dist/lib/providers/cohere.js +253 -0
- package/dist/lib/providers/deepseek.js +72 -17
- package/dist/lib/providers/fireworks.d.ts +33 -0
- package/dist/lib/providers/fireworks.js +164 -0
- package/dist/lib/providers/googleAiStudio.d.ts +11 -3
- package/dist/lib/providers/googleAiStudio.js +336 -344
- package/dist/lib/providers/googleNativeGemini3.d.ts +107 -2
- package/dist/lib/providers/googleNativeGemini3.js +381 -25
- package/dist/lib/providers/googleVertex.d.ts +116 -129
- package/dist/lib/providers/googleVertex.js +3002 -1988
- package/dist/lib/providers/groq.d.ts +33 -0
- package/dist/lib/providers/groq.js +181 -0
- package/dist/lib/providers/huggingFace.js +9 -8
- package/dist/lib/providers/ideogram.d.ts +34 -0
- package/dist/lib/providers/ideogram.js +184 -0
- package/dist/lib/providers/index.d.ts +13 -0
- package/dist/lib/providers/index.js +13 -0
- package/dist/lib/providers/jina.d.ts +59 -0
- package/dist/lib/providers/jina.js +218 -0
- package/dist/lib/providers/llamaCpp.js +14 -46
- package/dist/lib/providers/lmStudio.js +14 -47
- package/dist/lib/providers/mistral.js +7 -7
- package/dist/lib/providers/nvidiaNim.js +160 -19
- package/dist/lib/providers/ollama.js +7 -7
- package/dist/lib/providers/openAI.d.ts +22 -1
- package/dist/lib/providers/openAI.js +181 -0
- package/dist/lib/providers/openRouter.js +38 -22
- package/dist/lib/providers/openaiCompatible.js +9 -8
- package/dist/lib/providers/perplexity.d.ts +33 -0
- package/dist/lib/providers/perplexity.js +179 -0
- package/dist/lib/providers/recraft.d.ts +34 -0
- package/dist/lib/providers/recraft.js +197 -0
- package/dist/lib/providers/replicate.d.ts +75 -0
- package/dist/lib/providers/replicate.js +403 -0
- package/dist/lib/providers/stability.d.ts +37 -0
- package/dist/lib/providers/stability.js +191 -0
- package/dist/lib/providers/togetherAi.d.ts +33 -0
- package/dist/lib/providers/togetherAi.js +176 -0
- package/dist/lib/providers/voyage.d.ts +47 -0
- package/dist/lib/providers/voyage.js +177 -0
- package/dist/lib/providers/xai.d.ts +33 -0
- package/dist/lib/providers/xai.js +172 -0
- package/dist/lib/telemetry/index.d.ts +1 -1
- package/dist/lib/telemetry/index.js +1 -1
- package/dist/lib/telemetry/tracers.d.ts +19 -0
- package/dist/lib/telemetry/tracers.js +19 -0
- package/dist/lib/telemetry/withSpan.d.ts +35 -0
- package/dist/lib/telemetry/withSpan.js +103 -0
- package/dist/lib/types/aliases.d.ts +14 -0
- package/dist/lib/types/avatar.d.ts +143 -0
- package/dist/lib/types/avatar.js +20 -0
- package/dist/lib/types/cli.d.ts +6 -0
- package/dist/lib/types/common.d.ts +0 -3
- package/dist/lib/types/conversation.d.ts +10 -3
- package/dist/lib/types/generate.d.ts +76 -5
- package/dist/lib/types/index.d.ts +6 -0
- package/dist/lib/types/index.js +8 -0
- package/dist/lib/types/memory.d.ts +96 -0
- package/dist/lib/types/memory.js +23 -0
- package/dist/lib/types/middleware.d.ts +27 -0
- package/dist/lib/types/multimodal.d.ts +35 -2
- package/dist/lib/types/music.d.ts +165 -0
- package/dist/lib/types/music.js +21 -0
- package/dist/lib/types/providers.d.ts +284 -3
- package/dist/lib/types/replicate.d.ts +67 -0
- package/dist/lib/types/replicate.js +10 -0
- package/dist/lib/types/safeFetch.d.ts +15 -0
- package/dist/lib/types/safeFetch.js +7 -0
- package/dist/lib/types/stream.d.ts +8 -1
- package/dist/lib/types/tools.d.ts +13 -0
- package/dist/lib/types/video.d.ts +89 -0
- package/dist/lib/types/video.js +15 -0
- package/dist/lib/utils/avatarProcessor.d.ts +68 -0
- package/dist/lib/utils/avatarProcessor.js +172 -0
- package/dist/lib/utils/cloneOptions.d.ts +36 -0
- package/dist/lib/utils/cloneOptions.js +62 -0
- package/dist/lib/utils/lifecycleCallbacks.d.ts +56 -0
- package/dist/lib/utils/lifecycleCallbacks.js +100 -0
- package/dist/lib/utils/lifecycleTimeout.d.ts +25 -0
- package/dist/lib/utils/lifecycleTimeout.js +39 -0
- package/dist/lib/utils/logSanitize.d.ts +49 -0
- package/dist/lib/utils/logSanitize.js +170 -0
- package/dist/lib/utils/loggingFetch.d.ts +29 -0
- package/dist/lib/utils/loggingFetch.js +60 -0
- package/dist/lib/utils/messageBuilder.d.ts +10 -0
- package/dist/lib/utils/messageBuilder.js +83 -30
- package/dist/lib/utils/modelChoices.js +236 -3
- package/dist/lib/utils/modelDetection.d.ts +11 -0
- package/dist/lib/utils/modelDetection.js +27 -0
- package/dist/lib/utils/musicProcessor.d.ts +67 -0
- package/dist/lib/utils/musicProcessor.js +189 -0
- package/dist/lib/utils/optionsConversion.js +3 -2
- package/dist/lib/utils/parameterValidation.js +14 -4
- package/dist/lib/utils/pricing.js +193 -0
- package/dist/lib/utils/providerConfig.d.ts +55 -0
- package/dist/lib/utils/providerConfig.js +224 -0
- package/dist/lib/utils/providerHealth.js +7 -7
- package/dist/lib/utils/safeFetch.d.ts +26 -0
- package/dist/lib/utils/safeFetch.js +83 -0
- package/dist/lib/utils/schemaConversion.d.ts +1 -1
- package/dist/lib/utils/schemaConversion.js +59 -4
- package/dist/lib/utils/sizeGuard.d.ts +34 -0
- package/dist/lib/utils/sizeGuard.js +45 -0
- package/dist/lib/utils/ssrfGuard.d.ts +52 -0
- package/dist/lib/utils/ssrfGuard.js +411 -0
- package/dist/lib/utils/tokenLimits.js +23 -32
- package/dist/lib/utils/videoProcessor.d.ts +60 -0
- package/dist/lib/utils/videoProcessor.js +201 -0
- package/dist/lib/voice/providers/FishAudioTTS.d.ts +27 -0
- package/dist/lib/voice/providers/FishAudioTTS.js +183 -0
- package/dist/lib/workflow/core/ensembleExecutor.js +26 -9
- package/dist/memory/hippocampusInitializer.d.ts +2 -2
- package/dist/memory/hippocampusInitializer.js +32 -2
- package/dist/middleware/builtin/lifecycle.js +52 -51
- package/dist/music/index.d.ts +13 -0
- package/dist/music/index.js +13 -0
- package/dist/music/providers/BeatovenMusic.d.ts +31 -0
- package/dist/music/providers/BeatovenMusic.js +333 -0
- package/dist/music/providers/ElevenLabsMusic.d.ts +30 -0
- package/dist/music/providers/ElevenLabsMusic.js +168 -0
- package/dist/music/providers/LyriaMusic.d.ts +29 -0
- package/dist/music/providers/LyriaMusic.js +172 -0
- package/dist/music/providers/ReplicateMusic.d.ts +31 -0
- package/dist/music/providers/ReplicateMusic.js +261 -0
- package/dist/neurolink.d.ts +30 -0
- package/dist/neurolink.js +342 -49
- package/dist/providers/amazonBedrock.d.ts +10 -0
- package/dist/providers/amazonBedrock.js +94 -39
- package/dist/providers/anthropic.js +55 -7
- package/dist/providers/anthropicBaseProvider.js +1 -1
- package/dist/providers/azureOpenai.js +66 -17
- package/dist/providers/cloudflare.d.ts +35 -0
- package/dist/providers/cloudflare.js +173 -0
- package/dist/providers/cohere.d.ts +52 -0
- package/dist/providers/cohere.js +252 -0
- package/dist/providers/deepseek.js +72 -17
- package/dist/providers/fireworks.d.ts +33 -0
- package/dist/providers/fireworks.js +163 -0
- package/dist/providers/googleAiStudio.d.ts +11 -3
- package/dist/providers/googleAiStudio.js +335 -344
- package/dist/providers/googleNativeGemini3.d.ts +107 -2
- package/dist/providers/googleNativeGemini3.js +381 -25
- package/dist/providers/googleVertex.d.ts +116 -129
- package/dist/providers/googleVertex.js +3000 -1987
- package/dist/providers/groq.d.ts +33 -0
- package/dist/providers/groq.js +180 -0
- package/dist/providers/huggingFace.js +9 -8
- package/dist/providers/ideogram.d.ts +34 -0
- package/dist/providers/ideogram.js +183 -0
- package/dist/providers/index.d.ts +13 -0
- package/dist/providers/index.js +13 -0
- package/dist/providers/jina.d.ts +59 -0
- package/dist/providers/jina.js +217 -0
- package/dist/providers/llamaCpp.js +14 -46
- package/dist/providers/lmStudio.js +14 -47
- package/dist/providers/mistral.js +7 -7
- package/dist/providers/nvidiaNim.js +160 -19
- package/dist/providers/ollama.js +7 -7
- package/dist/providers/openAI.d.ts +22 -1
- package/dist/providers/openAI.js +181 -0
- package/dist/providers/openRouter.js +38 -22
- package/dist/providers/openaiCompatible.js +9 -8
- package/dist/providers/perplexity.d.ts +33 -0
- package/dist/providers/perplexity.js +178 -0
- package/dist/providers/recraft.d.ts +34 -0
- package/dist/providers/recraft.js +196 -0
- package/dist/providers/replicate.d.ts +75 -0
- package/dist/providers/replicate.js +402 -0
- package/dist/providers/stability.d.ts +37 -0
- package/dist/providers/stability.js +190 -0
- package/dist/providers/togetherAi.d.ts +33 -0
- package/dist/providers/togetherAi.js +175 -0
- package/dist/providers/voyage.d.ts +47 -0
- package/dist/providers/voyage.js +176 -0
- package/dist/providers/xai.d.ts +33 -0
- package/dist/providers/xai.js +171 -0
- package/dist/telemetry/index.d.ts +1 -1
- package/dist/telemetry/index.js +1 -1
- package/dist/telemetry/tracers.d.ts +19 -0
- package/dist/telemetry/tracers.js +19 -0
- package/dist/telemetry/withSpan.d.ts +35 -0
- package/dist/telemetry/withSpan.js +103 -0
- package/dist/types/aliases.d.ts +14 -0
- package/dist/types/avatar.d.ts +143 -0
- package/dist/types/avatar.js +19 -0
- package/dist/types/cli.d.ts +6 -0
- package/dist/types/common.d.ts +0 -3
- package/dist/types/conversation.d.ts +10 -3
- package/dist/types/generate.d.ts +76 -5
- package/dist/types/index.d.ts +6 -0
- package/dist/types/index.js +8 -0
- package/dist/types/memory.d.ts +96 -0
- package/dist/types/memory.js +22 -0
- package/dist/types/middleware.d.ts +27 -0
- package/dist/types/multimodal.d.ts +35 -2
- package/dist/types/music.d.ts +165 -0
- package/dist/types/music.js +20 -0
- package/dist/types/providers.d.ts +284 -3
- package/dist/types/replicate.d.ts +67 -0
- package/dist/types/replicate.js +9 -0
- package/dist/types/safeFetch.d.ts +15 -0
- package/dist/types/safeFetch.js +6 -0
- package/dist/types/stream.d.ts +8 -1
- package/dist/types/tools.d.ts +13 -0
- package/dist/types/video.d.ts +89 -0
- package/dist/types/video.js +14 -0
- package/dist/utils/avatarProcessor.d.ts +68 -0
- package/dist/utils/avatarProcessor.js +171 -0
- package/dist/utils/cloneOptions.d.ts +36 -0
- package/dist/utils/cloneOptions.js +61 -0
- package/dist/utils/lifecycleCallbacks.d.ts +56 -0
- package/dist/utils/lifecycleCallbacks.js +99 -0
- package/dist/utils/lifecycleTimeout.d.ts +25 -0
- package/dist/utils/lifecycleTimeout.js +38 -0
- package/dist/utils/logSanitize.d.ts +49 -0
- package/dist/utils/logSanitize.js +169 -0
- package/dist/utils/loggingFetch.d.ts +29 -0
- package/dist/utils/loggingFetch.js +59 -0
- package/dist/utils/messageBuilder.d.ts +10 -0
- package/dist/utils/messageBuilder.js +83 -30
- package/dist/utils/modelChoices.js +236 -3
- package/dist/utils/modelDetection.d.ts +11 -0
- package/dist/utils/modelDetection.js +27 -0
- package/dist/utils/musicProcessor.d.ts +67 -0
- package/dist/utils/musicProcessor.js +188 -0
- package/dist/utils/optionsConversion.js +3 -2
- package/dist/utils/parameterValidation.js +14 -4
- package/dist/utils/pricing.js +193 -0
- package/dist/utils/providerConfig.d.ts +55 -0
- package/dist/utils/providerConfig.js +224 -0
- package/dist/utils/providerHealth.js +7 -7
- package/dist/utils/safeFetch.d.ts +26 -0
- package/dist/utils/safeFetch.js +82 -0
- package/dist/utils/schemaConversion.d.ts +1 -1
- package/dist/utils/schemaConversion.js +59 -4
- package/dist/utils/sizeGuard.d.ts +34 -0
- package/dist/utils/sizeGuard.js +44 -0
- package/dist/utils/ssrfGuard.d.ts +52 -0
- package/dist/utils/ssrfGuard.js +410 -0
- package/dist/utils/tokenLimits.js +23 -32
- package/dist/utils/videoProcessor.d.ts +60 -0
- package/dist/utils/videoProcessor.js +200 -0
- package/dist/voice/providers/FishAudioTTS.d.ts +27 -0
- package/dist/voice/providers/FishAudioTTS.js +182 -0
- package/dist/workflow/core/ensembleExecutor.js +26 -9
- package/package.json +42 -8
|
@@ -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";
|
|
@@ -32,15 +33,23 @@ const getOpenRouterConfig = () => {
|
|
|
32
33
|
*
|
|
33
34
|
* OpenRouter uses a 'provider/model' format for model names.
|
|
34
35
|
* For example:
|
|
35
|
-
* - 'anthropic/claude-
|
|
36
|
+
* - 'anthropic/claude-sonnet-4.5'
|
|
36
37
|
* - 'openai/gpt-4o'
|
|
37
|
-
* - 'google/gemini-2.
|
|
38
|
+
* - 'google/gemini-2.5-flash'
|
|
38
39
|
* - 'meta-llama/llama-3-70b-instruct'
|
|
39
40
|
*
|
|
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`.
|
|
48
|
+
*
|
|
40
49
|
* You can override the default by setting the OPENROUTER_MODEL environment variable.
|
|
41
50
|
*/
|
|
42
51
|
const getDefaultOpenRouterModel = () => {
|
|
43
|
-
return getProviderModel("OPENROUTER_MODEL", "anthropic/claude-
|
|
52
|
+
return getProviderModel("OPENROUTER_MODEL", "anthropic/claude-sonnet-4.5");
|
|
44
53
|
};
|
|
45
54
|
/**
|
|
46
55
|
* OpenRouter Provider - BaseProvider Implementation
|
|
@@ -107,62 +116,62 @@ export class OpenRouterProvider extends BaseProvider {
|
|
|
107
116
|
}
|
|
108
117
|
formatProviderError(error) {
|
|
109
118
|
if (error instanceof TimeoutError) {
|
|
110
|
-
return new
|
|
119
|
+
return new NetworkError(`Request timed out: ${error.message}`, "openrouter");
|
|
111
120
|
}
|
|
112
121
|
// Check for timeout by error name and message as fallback
|
|
113
122
|
const errorRecord = error;
|
|
114
123
|
if (errorRecord?.name === "TimeoutError" ||
|
|
115
124
|
(typeof errorRecord?.message === "string" &&
|
|
116
125
|
errorRecord.message.includes("Timeout"))) {
|
|
117
|
-
return new
|
|
126
|
+
return new NetworkError(`Request timed out: ${errorRecord?.message || "Unknown timeout"}`, "openrouter");
|
|
118
127
|
}
|
|
119
128
|
if (typeof errorRecord?.message === "string") {
|
|
120
129
|
if (errorRecord.message.includes("ECONNREFUSED") ||
|
|
121
130
|
errorRecord.message.includes("Failed to fetch")) {
|
|
122
|
-
return new
|
|
131
|
+
return new NetworkError("OpenRouter API not available. Please check your network connection and try again.", "openrouter");
|
|
123
132
|
}
|
|
124
133
|
if (errorRecord.message.includes("API_KEY_INVALID") ||
|
|
125
134
|
errorRecord.message.includes("Invalid API key") ||
|
|
126
135
|
errorRecord.message.includes("invalid_api_key") ||
|
|
127
136
|
errorRecord.message.includes("Unauthorized")) {
|
|
128
|
-
return new
|
|
129
|
-
"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");
|
|
130
139
|
}
|
|
131
140
|
if (errorRecord.message.includes("rate limit")) {
|
|
132
|
-
return new
|
|
141
|
+
return new RateLimitError("OpenRouter rate limit exceeded. Please try again later or upgrade your account at https://openrouter.ai/credits", "openrouter");
|
|
133
142
|
}
|
|
134
143
|
if (errorRecord.message.includes("model") &&
|
|
135
144
|
errorRecord.message.includes("not found")) {
|
|
136
|
-
return new
|
|
137
|
-
"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");
|
|
138
147
|
}
|
|
139
148
|
if (errorRecord.message.includes("insufficient_credits")) {
|
|
140
|
-
return new
|
|
149
|
+
return new ProviderError("Insufficient OpenRouter credits. Add credits at https://openrouter.ai/credits", "openrouter");
|
|
141
150
|
}
|
|
142
151
|
// "No endpoints found" — model temporarily unavailable or unsupported parameters
|
|
143
152
|
// This is distinct from tool errors: it can happen on any request when the
|
|
144
153
|
// model has no available providers on OpenRouter (e.g., free-tier model down).
|
|
145
154
|
if (errorRecord.message.includes("No endpoints found")) {
|
|
146
|
-
return new
|
|
155
|
+
return new InvalidModelError(`No endpoints found for model '${this.modelName}' on OpenRouter. ` +
|
|
147
156
|
"The model may be temporarily unavailable or does not support the requested parameters. " +
|
|
148
|
-
"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");
|
|
149
158
|
}
|
|
150
159
|
// Tool/function calling errors
|
|
151
160
|
if (errorRecord.message.includes("tool use") ||
|
|
152
161
|
errorRecord.message.includes("tool_use") ||
|
|
153
162
|
errorRecord.message.includes("function_call") ||
|
|
154
163
|
errorRecord.message.includes("tools are not supported")) {
|
|
155
|
-
return new
|
|
164
|
+
return new ProviderError(`Model '${this.modelName}' does not support tool calling. ` +
|
|
156
165
|
"Use a tool-capable model like:\n" +
|
|
157
166
|
" • google/gemini-2.0-flash-exp:free (free)\n" +
|
|
158
167
|
" • meta-llama/llama-3.3-70b-instruct:free (free)\n" +
|
|
159
|
-
" • anthropic/claude-3-
|
|
168
|
+
" • anthropic/claude-3.7-sonnet (paid)\n" +
|
|
160
169
|
" • openai/gpt-4o (paid)\n" +
|
|
161
170
|
"Or use --disableTools flag. " +
|
|
162
|
-
"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");
|
|
163
172
|
}
|
|
164
173
|
}
|
|
165
|
-
return new
|
|
174
|
+
return new ProviderError(`OpenRouter error: ${errorRecord?.message || "Unknown error"}`, "openrouter");
|
|
166
175
|
}
|
|
167
176
|
/**
|
|
168
177
|
* OpenRouter supports tools for compatible models
|
|
@@ -207,7 +216,7 @@ export class OpenRouterProvider extends BaseProvider {
|
|
|
207
216
|
// For unknown models, warn and disable tools (safe default)
|
|
208
217
|
logger.warn("OpenRouter: Unknown model tool capability, disabling tools", {
|
|
209
218
|
model: modelName,
|
|
210
|
-
suggestion: "Use a known tool-capable model like anthropic/claude-3-
|
|
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",
|
|
211
220
|
});
|
|
212
221
|
return false;
|
|
213
222
|
}
|
|
@@ -251,7 +260,12 @@ export class OpenRouterProvider extends BaseProvider {
|
|
|
251
260
|
messages: messages,
|
|
252
261
|
temperature: options.temperature,
|
|
253
262
|
maxRetries: 0, // Disable SDK retries - let caller handle rate limit retries with delays
|
|
254
|
-
|
|
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 }),
|
|
255
269
|
...(shouldUseTools &&
|
|
256
270
|
Object.keys(tools).length > 0 && {
|
|
257
271
|
tools,
|
|
@@ -452,10 +466,12 @@ export class OpenRouterProvider extends BaseProvider {
|
|
|
452
466
|
error: error instanceof Error ? error.message : String(error),
|
|
453
467
|
});
|
|
454
468
|
}
|
|
455
|
-
// 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.
|
|
456
472
|
const fallbackModels = [
|
|
457
473
|
// Anthropic Claude models
|
|
458
|
-
"anthropic/claude-3-
|
|
474
|
+
"anthropic/claude-3.7-sonnet",
|
|
459
475
|
"anthropic/claude-3-5-haiku",
|
|
460
476
|
"anthropic/claude-3-opus",
|
|
461
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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,178 @@
|
|
|
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;
|
|
@@ -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,196 @@
|
|
|
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;
|