@juspay/neurolink 10.12.9 → 11.1.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 +32 -0
- package/dist/browser/neurolink.min.js +504 -506
- package/dist/cli/commands/setup.d.ts +20 -1
- package/dist/cli/commands/setup.js +160 -36
- package/dist/cli/factories/commandFactory.d.ts +1157 -1
- package/dist/cli/factories/commandFactory.js +20 -53
- package/dist/core/baseProvider.js +3 -3
- package/dist/core/dynamicModels.d.ts +1 -1
- package/dist/core/dynamicModels.js +1 -23
- package/dist/core/modelConfiguration.d.ts +1 -70
- package/dist/core/modelConfiguration.js +0 -303
- package/dist/core/modules/GenerationHandler.js +2 -2
- package/dist/core/modules/structuredOutputPolicy.d.ts +2 -2
- package/dist/core/modules/structuredOutputPolicy.js +2 -2
- package/dist/factories/providerDescriptors.d.ts +18 -0
- package/dist/factories/providerDescriptors.js +536 -0
- package/dist/factories/providerFactory.d.ts +28 -2
- package/dist/factories/providerFactory.js +53 -42
- package/dist/factories/providerRegistry.js +33 -32
- package/dist/index.d.ts +10 -1
- package/dist/index.js +13 -3
- package/dist/lib/core/baseProvider.js +3 -3
- package/dist/lib/core/dynamicModels.d.ts +1 -1
- package/dist/lib/core/dynamicModels.js +1 -23
- package/dist/lib/core/modelConfiguration.d.ts +1 -70
- package/dist/lib/core/modelConfiguration.js +0 -303
- package/dist/lib/core/modules/GenerationHandler.js +2 -2
- package/dist/lib/core/modules/structuredOutputPolicy.d.ts +2 -2
- package/dist/lib/core/modules/structuredOutputPolicy.js +2 -2
- package/dist/lib/factories/providerDescriptors.d.ts +18 -0
- package/dist/lib/factories/providerDescriptors.js +537 -0
- package/dist/lib/factories/providerFactory.d.ts +28 -2
- package/dist/lib/factories/providerFactory.js +53 -42
- package/dist/lib/factories/providerRegistry.js +33 -32
- package/dist/lib/index.d.ts +10 -1
- package/dist/lib/index.js +13 -3
- package/dist/lib/models/anthropicModels.d.ts +0 -7
- package/dist/lib/models/anthropicModels.js +0 -9
- package/dist/lib/neurolink.js +25 -37
- package/dist/lib/providers/amazonBedrock/client.js +34 -16
- package/dist/lib/providers/anthropic/client.js +39 -34
- package/dist/lib/providers/anthropic/constants.d.ts +0 -1
- package/dist/lib/providers/anthropic/constants.js +0 -2
- package/dist/lib/providers/anthropic/index.d.ts +0 -1
- package/dist/lib/providers/anthropic/index.js +0 -1
- package/dist/lib/providers/azureOpenai.js +17 -15
- package/dist/lib/providers/cloudflare.js +12 -21
- package/dist/lib/providers/cohere.js +31 -25
- package/dist/lib/providers/deepseek.js +23 -26
- package/dist/lib/providers/fireworks.js +12 -21
- package/dist/lib/providers/googleAiStudio/client.d.ts +2 -0
- package/dist/lib/providers/googleAiStudio/client.js +39 -17
- package/dist/lib/providers/googleAiStudio/index.d.ts +0 -1
- package/dist/lib/providers/googleAiStudio/index.js +0 -1
- package/dist/lib/providers/googleNativeGemini3/index.d.ts +0 -1
- package/dist/lib/providers/googleNativeGemini3/index.js +0 -1
- package/dist/lib/providers/googleVertex/client.d.ts +0 -51
- package/dist/lib/providers/googleVertex/client.js +107 -777
- package/dist/lib/providers/groq.js +19 -22
- package/dist/lib/providers/huggingFace/client.d.ts +1 -1
- package/dist/lib/providers/huggingFace/client.js +27 -25
- package/dist/lib/providers/huggingFace/index.d.ts +0 -1
- package/dist/lib/providers/huggingFace/index.js +0 -1
- package/dist/lib/providers/jina.d.ts +0 -1
- package/dist/lib/providers/jina.js +0 -1
- package/dist/lib/providers/litellm/client.js +54 -39
- package/dist/lib/providers/litellm/index.d.ts +0 -2
- package/dist/lib/providers/litellm/index.js +0 -2
- package/dist/lib/providers/llamaCpp.d.ts +1 -0
- package/dist/lib/providers/llamaCpp.js +24 -18
- package/dist/lib/providers/lmStudio.js +25 -42
- package/dist/lib/providers/mistral.js +12 -24
- package/dist/lib/providers/nvidiaNim/client.js +37 -34
- package/dist/lib/providers/nvidiaNim/index.d.ts +0 -1
- package/dist/lib/providers/nvidiaNim/index.js +0 -1
- package/dist/lib/providers/ollama/client.js +51 -58
- package/dist/lib/providers/ollama/index.d.ts +0 -1
- package/dist/lib/providers/ollama/index.js +0 -1
- package/dist/lib/providers/openAI/client.js +37 -40
- package/dist/lib/providers/openAI/index.d.ts +0 -2
- package/dist/lib/providers/openAI/index.js +0 -2
- package/dist/lib/providers/openRouter/client.js +53 -47
- package/dist/lib/providers/openRouter/utils.d.ts +0 -2
- package/dist/lib/providers/openRouter/utils.js +0 -12
- package/dist/lib/providers/openaiChatCompletionsBase.d.ts +9 -0
- package/dist/lib/providers/openaiChatCompletionsBase.js +73 -16
- package/dist/lib/providers/openaiCompatible/client.js +36 -32
- package/dist/lib/providers/perplexity.d.ts +2 -1
- package/dist/lib/providers/perplexity.js +14 -22
- package/dist/lib/providers/replicate.d.ts +0 -1
- package/dist/lib/providers/replicate.js +22 -9
- package/dist/lib/providers/togetherAi.js +12 -21
- package/dist/lib/providers/voyage.d.ts +0 -1
- package/dist/lib/providers/voyage.js +0 -1
- package/dist/lib/providers/xai.js +17 -26
- package/dist/lib/server/errors.d.ts +1 -1
- package/dist/lib/server/errors.js +2 -2
- package/dist/lib/server/index.d.ts +2 -2
- package/dist/lib/server/index.js +5 -3
- package/dist/lib/server/middleware/rateLimit.d.ts +0 -4
- package/dist/lib/server/middleware/rateLimit.js +0 -4
- package/dist/lib/types/errors.d.ts +35 -0
- package/dist/lib/types/index.d.ts +0 -1
- package/dist/lib/types/index.js +0 -1
- package/dist/lib/types/providers.d.ts +66 -11
- package/dist/lib/utils/errorClassifier.d.ts +30 -0
- package/dist/lib/utils/errorClassifier.js +94 -0
- package/dist/lib/utils/fileDetector.js +6 -43
- package/dist/lib/utils/providerConfig.d.ts +0 -8
- package/dist/lib/utils/providerConfig.js +0 -37
- package/dist/lib/utils/providerHealth.d.ts +42 -7
- package/dist/lib/utils/providerHealth.js +115 -122
- package/dist/lib/utils/providerUtils.js +21 -87
- package/dist/models/anthropicModels.d.ts +0 -7
- package/dist/models/anthropicModels.js +0 -9
- package/dist/neurolink.js +25 -37
- package/dist/providers/amazonBedrock/client.js +34 -16
- package/dist/providers/amazonSagemaker.d.ts +1 -1
- package/dist/providers/anthropic/client.js +39 -34
- package/dist/providers/anthropic/constants.d.ts +0 -1
- package/dist/providers/anthropic/constants.js +0 -2
- package/dist/providers/anthropic/index.d.ts +0 -1
- package/dist/providers/anthropic/index.js +0 -1
- package/dist/providers/azureOpenai.js +17 -15
- package/dist/providers/cloudflare.js +12 -21
- package/dist/providers/cohere.js +31 -25
- package/dist/providers/deepseek.js +23 -26
- package/dist/providers/fireworks.js +12 -21
- package/dist/providers/googleAiStudio/client.d.ts +2 -0
- package/dist/providers/googleAiStudio/client.js +39 -17
- package/dist/providers/googleAiStudio/index.d.ts +0 -1
- package/dist/providers/googleAiStudio/index.js +0 -1
- package/dist/providers/googleNativeGemini3/index.d.ts +0 -1
- package/dist/providers/googleNativeGemini3/index.js +0 -1
- package/dist/providers/googleVertex/client.d.ts +0 -51
- package/dist/providers/googleVertex/client.js +107 -777
- package/dist/providers/groq.js +19 -22
- package/dist/providers/huggingFace/client.d.ts +1 -1
- package/dist/providers/huggingFace/client.js +27 -25
- package/dist/providers/huggingFace/index.d.ts +0 -1
- package/dist/providers/huggingFace/index.js +0 -1
- package/dist/providers/jina.d.ts +0 -1
- package/dist/providers/jina.js +0 -1
- package/dist/providers/litellm/client.js +54 -39
- package/dist/providers/litellm/index.d.ts +0 -2
- package/dist/providers/litellm/index.js +0 -2
- package/dist/providers/llamaCpp.d.ts +1 -0
- package/dist/providers/llamaCpp.js +24 -18
- package/dist/providers/lmStudio.js +25 -42
- package/dist/providers/mistral.js +12 -24
- package/dist/providers/nvidiaNim/client.js +37 -34
- package/dist/providers/nvidiaNim/index.d.ts +0 -1
- package/dist/providers/nvidiaNim/index.js +0 -1
- package/dist/providers/ollama/client.js +51 -58
- package/dist/providers/ollama/index.d.ts +0 -1
- package/dist/providers/ollama/index.js +0 -1
- package/dist/providers/openAI/client.js +37 -40
- package/dist/providers/openAI/index.d.ts +0 -2
- package/dist/providers/openAI/index.js +0 -2
- package/dist/providers/openRouter/client.js +53 -47
- package/dist/providers/openRouter/utils.d.ts +0 -2
- package/dist/providers/openRouter/utils.js +0 -12
- package/dist/providers/openaiChatCompletionsBase.d.ts +9 -0
- package/dist/providers/openaiChatCompletionsBase.js +73 -16
- package/dist/providers/openaiCompatible/client.js +36 -32
- package/dist/providers/perplexity.d.ts +2 -1
- package/dist/providers/perplexity.js +14 -22
- package/dist/providers/replicate.d.ts +0 -1
- package/dist/providers/replicate.js +22 -9
- package/dist/providers/sagemaker/language-model.d.ts +2 -2
- package/dist/providers/togetherAi.js +12 -21
- package/dist/providers/voyage.d.ts +0 -1
- package/dist/providers/voyage.js +0 -1
- package/dist/providers/xai.js +17 -26
- package/dist/server/errors.d.ts +1 -1
- package/dist/server/errors.js +2 -2
- package/dist/server/index.d.ts +2 -2
- package/dist/server/index.js +5 -3
- package/dist/server/middleware/rateLimit.d.ts +0 -4
- package/dist/server/middleware/rateLimit.js +0 -4
- package/dist/types/errors.d.ts +35 -0
- package/dist/types/index.d.ts +0 -1
- package/dist/types/index.js +0 -1
- package/dist/types/providers.d.ts +66 -11
- package/dist/utils/errorClassifier.d.ts +30 -0
- package/dist/utils/errorClassifier.js +93 -0
- package/dist/utils/fileDetector.js +6 -43
- package/dist/utils/providerConfig.d.ts +0 -8
- package/dist/utils/providerConfig.js +0 -37
- package/dist/utils/providerHealth.d.ts +42 -7
- package/dist/utils/providerHealth.js +115 -122
- package/dist/utils/providerUtils.js +21 -87
- package/package.json +21 -75
- package/dist/lib/providers/anthropic/utils.d.ts +0 -7
- package/dist/lib/providers/anthropic/utils.js +0 -150
- package/dist/lib/providers/googleAiStudio/utils.d.ts +0 -2
- package/dist/lib/providers/googleAiStudio/utils.js +0 -19
- package/dist/lib/providers/googleNativeGemini3/constants.d.ts +0 -1
- package/dist/lib/providers/googleNativeGemini3/constants.js +0 -2
- package/dist/lib/providers/huggingFace/utils.d.ts +0 -2
- package/dist/lib/providers/huggingFace/utils.js +0 -8
- package/dist/lib/providers/index.d.ts +0 -33
- package/dist/lib/providers/index.js +0 -34
- package/dist/lib/providers/litellm/constants.d.ts +0 -1
- package/dist/lib/providers/litellm/constants.js +0 -3
- package/dist/lib/providers/litellm/utils.d.ts +0 -5
- package/dist/lib/providers/litellm/utils.js +0 -11
- package/dist/lib/providers/nvidiaNim/utils.d.ts +0 -9
- package/dist/lib/providers/nvidiaNim/utils.js +0 -101
- package/dist/lib/providers/ollama/utils.d.ts +0 -8
- package/dist/lib/providers/ollama/utils.js +0 -43
- package/dist/lib/providers/openAI/constants.d.ts +0 -1
- package/dist/lib/providers/openAI/constants.js +0 -3
- package/dist/lib/providers/openAI/utils.d.ts +0 -2
- package/dist/lib/providers/openAI/utils.js +0 -8
- package/dist/lib/types/universalProviderOptions.d.ts +0 -89
- package/dist/lib/types/universalProviderOptions.js +0 -55
- package/dist/providers/anthropic/utils.d.ts +0 -7
- package/dist/providers/anthropic/utils.js +0 -149
- package/dist/providers/googleAiStudio/utils.d.ts +0 -2
- package/dist/providers/googleAiStudio/utils.js +0 -18
- package/dist/providers/googleNativeGemini3/constants.d.ts +0 -1
- package/dist/providers/googleNativeGemini3/constants.js +0 -1
- package/dist/providers/huggingFace/utils.d.ts +0 -2
- package/dist/providers/huggingFace/utils.js +0 -7
- package/dist/providers/index.d.ts +0 -33
- package/dist/providers/index.js +0 -33
- package/dist/providers/litellm/constants.d.ts +0 -1
- package/dist/providers/litellm/constants.js +0 -2
- package/dist/providers/litellm/utils.d.ts +0 -5
- package/dist/providers/litellm/utils.js +0 -10
- package/dist/providers/nvidiaNim/utils.d.ts +0 -9
- package/dist/providers/nvidiaNim/utils.js +0 -100
- package/dist/providers/ollama/utils.d.ts +0 -8
- package/dist/providers/ollama/utils.js +0 -42
- package/dist/providers/openAI/constants.d.ts +0 -1
- package/dist/providers/openAI/constants.js +0 -2
- package/dist/providers/openAI/utils.d.ts +0 -2
- package/dist/providers/openAI/utils.js +0 -7
- package/dist/types/universalProviderOptions.d.ts +0 -89
- package/dist/types/universalProviderOptions.js +0 -54
package/dist/providers/groq.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { GroqModels } from "../constants/enums.js";
|
|
2
|
-
import { AuthenticationError, InvalidModelError, ProviderError,
|
|
2
|
+
import { AuthenticationError, InvalidModelError, ProviderError, } from "../types/index.js";
|
|
3
3
|
import { logger } from "../utils/logger.js";
|
|
4
4
|
import { redactUrlCredentials } from "../utils/logSanitize.js";
|
|
5
|
+
import { classifyProviderError, DEFAULT_ERROR_RULES, } from "../utils/errorClassifier.js";
|
|
5
6
|
import { createGroqConfig, getProviderModel, validateApiKey, } from "../utils/providerConfig.js";
|
|
6
7
|
import { TimeoutError } from "../utils/timeout.js";
|
|
7
8
|
import { OpenAIChatCompletionsProvider } from "./openaiChatCompletionsBase.js";
|
|
@@ -57,29 +58,25 @@ export class GroqProvider extends OpenAIChatCompletionsProvider {
|
|
|
57
58
|
];
|
|
58
59
|
}
|
|
59
60
|
formatProviderError(error) {
|
|
61
|
+
// Groq's TimeoutError maps to plain ProviderError (not NetworkError, the
|
|
62
|
+
// classifier's built-in default) — intercept before delegating.
|
|
60
63
|
if (error instanceof TimeoutError) {
|
|
61
64
|
return new ProviderError(`Groq request timed out: ${error.message}`, "groq");
|
|
62
65
|
}
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
message.includes("model_decommissioned")) {
|
|
79
|
-
return new InvalidModelError(message.includes("model_decommissioned")
|
|
80
|
-
? `Groq model '${this.modelName}' was decommissioned. Pick a current model from https://console.groq.com/docs/models.`
|
|
81
|
-
: `Groq model '${this.modelName}' not found. See https://console.groq.com/docs/models for the current catalog.`, "groq");
|
|
82
|
-
}
|
|
83
|
-
return new ProviderError(`Groq error: ${message}`, "groq");
|
|
66
|
+
const rules = [
|
|
67
|
+
{
|
|
68
|
+
match: (ctx) => ctx.statusCode === 401 ||
|
|
69
|
+
/Invalid API key|Authentication|invalid_api_key/i.test(ctx.message),
|
|
70
|
+
errorClass: AuthenticationError,
|
|
71
|
+
message: "Invalid Groq API key. Check GROQ_API_KEY. Get one at https://console.groq.com/keys",
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
match: (ctx) => /model_decommissioned/i.test(ctx.message),
|
|
75
|
+
errorClass: InvalidModelError,
|
|
76
|
+
message: (ctx) => `Groq model '${ctx.modelName}' was decommissioned. Pick a current model from https://console.groq.com/docs/models.`,
|
|
77
|
+
},
|
|
78
|
+
...DEFAULT_ERROR_RULES,
|
|
79
|
+
];
|
|
80
|
+
return classifyProviderError(error, rules, "groq", this.modelName);
|
|
84
81
|
}
|
|
85
82
|
}
|
|
@@ -14,7 +14,7 @@ import { OpenAIChatCompletionsProvider } from "../openaiChatCompletionsBase.js";
|
|
|
14
14
|
* @see https://huggingface.co/docs/api-inference/index
|
|
15
15
|
*/
|
|
16
16
|
export declare class HuggingFaceProvider extends OpenAIChatCompletionsProvider {
|
|
17
|
-
constructor(modelName?: string, sdk?: unknown, credentials?: NeurolinkCredentials["huggingFace"]);
|
|
17
|
+
constructor(modelName?: string, sdk?: unknown, _region?: string, credentials?: NeurolinkCredentials["huggingFace"]);
|
|
18
18
|
protected getProviderName(): AIProviderName;
|
|
19
19
|
protected getDefaultModel(): string;
|
|
20
20
|
protected getFallbackModelName(): string;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { AuthenticationError, InvalidModelError,
|
|
1
|
+
import { AuthenticationError, InvalidModelError, ProviderError, RateLimitError, } from "../../types/index.js";
|
|
2
|
+
import { classifyProviderError, DEFAULT_ERROR_RULES, } from "../../utils/errorClassifier.js";
|
|
2
3
|
import { logger } from "../../utils/logger.js";
|
|
3
4
|
import { redactUrlCredentials } from "../../utils/logSanitize.js";
|
|
4
5
|
import { createHuggingFaceConfig, getProviderModel, validateApiKey, } from "../../utils/providerConfig.js";
|
|
5
|
-
import { TimeoutError } from "../../utils/timeout.js";
|
|
6
6
|
import { OpenAIChatCompletionsProvider } from "../openaiChatCompletionsBase.js";
|
|
7
7
|
const HUGGINGFACE_DEFAULT_BASE_URL = "https://router.huggingface.co/v1";
|
|
8
8
|
const getHuggingFaceApiKey = () => validateApiKey(createHuggingFaceConfig());
|
|
@@ -20,7 +20,7 @@ const getDefaultHuggingFaceModel = () => getProviderModel("HUGGINGFACE_MODEL", "
|
|
|
20
20
|
* @see https://huggingface.co/docs/api-inference/index
|
|
21
21
|
*/
|
|
22
22
|
export class HuggingFaceProvider extends OpenAIChatCompletionsProvider {
|
|
23
|
-
constructor(modelName, sdk, credentials) {
|
|
23
|
+
constructor(modelName, sdk, _region, credentials) {
|
|
24
24
|
const apiKey = credentials?.apiKey?.trim()
|
|
25
25
|
? credentials.apiKey.trim()
|
|
26
26
|
: getHuggingFaceApiKey();
|
|
@@ -74,27 +74,29 @@ export class HuggingFaceProvider extends OpenAIChatCompletionsProvider {
|
|
|
74
74
|
return toolCapableModels.some((capable) => modelName.includes(capable));
|
|
75
75
|
}
|
|
76
76
|
formatProviderError(error) {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
77
|
+
const rules = [
|
|
78
|
+
{
|
|
79
|
+
match: (ctx) => /API_TOKEN_INVALID|Invalid token/.test(ctx.message),
|
|
80
|
+
errorClass: AuthenticationError,
|
|
81
|
+
message: "Invalid HuggingFace API token. Please check your HUGGINGFACE_API_KEY environment variable.",
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
match: (ctx) => /rate limit/.test(ctx.message),
|
|
85
|
+
errorClass: RateLimitError,
|
|
86
|
+
message: "HuggingFace rate limit exceeded. Consider using a paid plan or try again later.",
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
match: (ctx) => /model/.test(ctx.message) && /not found/.test(ctx.message),
|
|
90
|
+
errorClass: InvalidModelError,
|
|
91
|
+
message: () => `HuggingFace model '${this.modelName}' not found.\n\nSuggestions:\n1. Check model name spelling\n2. Ensure model exists on HuggingFace Hub\n3. For tool calling, use: Llama-3.1-8B-Instruct, Hermes-3-Llama-3.2-3B, or CodeLlama-34b-Instruct-hf`,
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
match: (ctx) => /function|tool/.test(ctx.message),
|
|
95
|
+
errorClass: ProviderError,
|
|
96
|
+
message: (ctx) => `HuggingFace tool calling error: ${ctx.message}\n\nNotes:\n1. Ensure you're using a tool-capable model (Llama-3.1+, Hermes-3+, CodeLlama)\n2. Check that your model supports function calling\n3. Verify tool schema format is correct`,
|
|
97
|
+
},
|
|
98
|
+
...DEFAULT_ERROR_RULES,
|
|
99
|
+
];
|
|
100
|
+
return classifyProviderError(error, rules, "huggingface", this.modelName);
|
|
99
101
|
}
|
|
100
102
|
}
|
package/dist/providers/jina.d.ts
CHANGED
package/dist/providers/jina.js
CHANGED
|
@@ -2,13 +2,14 @@ import { SpanKind, SpanStatusCode, trace } from "@opentelemetry/api";
|
|
|
2
2
|
import { registerRuntimeContextWindow, registerRuntimeOutputCeiling, } from "../../constants/contextWindows.js";
|
|
3
3
|
import { createProxyFetch } from "../../proxy/proxyFetch.js";
|
|
4
4
|
import { AuthenticationError, InvalidModelError, ModelAccessDeniedError, NetworkError, ProviderError, RateLimitError, isModelAccessDeniedMessage, parseAllowedModels, } from "../../types/index.js";
|
|
5
|
+
import { classifyProviderError, DEFAULT_ERROR_RULES, } from "../../utils/errorClassifier.js";
|
|
5
6
|
import { isAbortError } from "../../utils/errorHandling.js";
|
|
6
7
|
import { logger } from "../../utils/logger.js";
|
|
7
8
|
import { redactUrlCredentials } from "../../utils/logSanitize.js";
|
|
8
9
|
import { isGemini25Model as isCanonicalGemini25Model } from "../../utils/modelDetection.js";
|
|
9
10
|
import { calculateCost } from "../../utils/pricing.js";
|
|
10
11
|
import { getProviderModel } from "../../utils/providerConfig.js";
|
|
11
|
-
import { createTimeoutController
|
|
12
|
+
import { createTimeoutController } from "../../utils/timeout.js";
|
|
12
13
|
import { stripTrailingSlash } from "../openaiChatCompletionsClient.js";
|
|
13
14
|
import { OpenAIChatCompletionsProvider } from "../openaiChatCompletionsBase.js";
|
|
14
15
|
const streamTracer = trace.getTracer("neurolink.provider.litellm");
|
|
@@ -320,46 +321,60 @@ export class LiteLLMProvider extends OpenAIChatCompletionsProvider {
|
|
|
320
321
|
};
|
|
321
322
|
}
|
|
322
323
|
formatProviderError(error) {
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
324
|
+
// Curator P1-1: detect "team not allowed to access model" responses and
|
|
325
|
+
// surface as ModelAccessDeniedError with the allowed_models array parsed
|
|
326
|
+
// from the body. Must run before classification (not just before the
|
|
327
|
+
// "API key" rule) because ModelAccessDeniedError's constructor takes an
|
|
328
|
+
// `{ provider, requestedModel, allowedModels }` options object rather
|
|
329
|
+
// than the `(message, provider?)` shape ProviderErrorRule expects, so it
|
|
330
|
+
// can't be expressed as a declarative rule. No realistic overlap with the
|
|
331
|
+
// timeout/ECONNREFUSED checks below (disjoint wording), so running this
|
|
332
|
+
// first is behaviorally identical to the original nesting order.
|
|
326
333
|
const errorRecord = error;
|
|
327
|
-
if (errorRecord?.
|
|
328
|
-
(
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
errorRecord.message.includes("Failed to fetch")) {
|
|
335
|
-
return new NetworkError("LiteLLM proxy server not available. Please start the LiteLLM proxy server at " +
|
|
336
|
-
`${process.env.LITELLM_BASE_URL || "http://localhost:4000"}`, this.providerName);
|
|
337
|
-
}
|
|
338
|
-
// Curator P1-1: detect "team not allowed to access model" responses and
|
|
339
|
-
// surface as ModelAccessDeniedError with the allowed_models array parsed
|
|
340
|
-
// from the body. Must run before the generic "API key" check because
|
|
341
|
-
// LiteLLM phrases this as a 403 distinct from auth.
|
|
342
|
-
if (isModelAccessDeniedMessage(errorRecord.message)) {
|
|
343
|
-
return new ModelAccessDeniedError(errorRecord.message, {
|
|
344
|
-
provider: this.providerName,
|
|
345
|
-
requestedModel: this.modelName,
|
|
346
|
-
allowedModels: parseAllowedModels(errorRecord.message),
|
|
347
|
-
});
|
|
348
|
-
}
|
|
349
|
-
if (errorRecord.message.includes("API_KEY_INVALID") ||
|
|
350
|
-
errorRecord.message.includes("Invalid API key")) {
|
|
351
|
-
return new AuthenticationError("Invalid LiteLLM configuration. Please check your LITELLM_API_KEY environment variable.", this.providerName);
|
|
352
|
-
}
|
|
353
|
-
if (errorRecord.message.toLowerCase().includes("rate limit")) {
|
|
354
|
-
return new RateLimitError("LiteLLM rate limit exceeded. Please try again later.", this.providerName);
|
|
355
|
-
}
|
|
356
|
-
if (errorRecord.message.toLowerCase().includes("model") &&
|
|
357
|
-
errorRecord.message.toLowerCase().includes("not found")) {
|
|
358
|
-
return new InvalidModelError(`Model '${this.modelName}' not available in LiteLLM proxy. ` +
|
|
359
|
-
"Please check your LiteLLM configuration and ensure the model is configured.", this.providerName);
|
|
360
|
-
}
|
|
334
|
+
if (typeof errorRecord?.message === "string" &&
|
|
335
|
+
isModelAccessDeniedMessage(errorRecord.message)) {
|
|
336
|
+
return new ModelAccessDeniedError(errorRecord.message, {
|
|
337
|
+
provider: this.providerName,
|
|
338
|
+
requestedModel: this.modelName,
|
|
339
|
+
allowedModels: parseAllowedModels(errorRecord.message),
|
|
340
|
+
});
|
|
361
341
|
}
|
|
362
|
-
|
|
342
|
+
const rules = [
|
|
343
|
+
// Duck-typed timeout detection (name === "TimeoutError" OR message
|
|
344
|
+
// contains "timeout") distinct from the `instanceof TimeoutError` check
|
|
345
|
+
// classifyProviderError already performs first — preserved because
|
|
346
|
+
// some rejection paths produce a plain object/Error with that shape
|
|
347
|
+
// rather than a real TimeoutError instance.
|
|
348
|
+
{
|
|
349
|
+
match: (ctx) => ctx.errorName === "TimeoutError" || /timeout/i.test(ctx.message),
|
|
350
|
+
errorClass: NetworkError,
|
|
351
|
+
message: (ctx) => `Request timed out: ${ctx.message}`,
|
|
352
|
+
},
|
|
353
|
+
{
|
|
354
|
+
match: (ctx) => /ECONNREFUSED|Failed to fetch/.test(ctx.message),
|
|
355
|
+
errorClass: NetworkError,
|
|
356
|
+
message: () => "LiteLLM proxy server not available. Please start the LiteLLM proxy server at " +
|
|
357
|
+
`${process.env.LITELLM_BASE_URL || "http://localhost:4000"}`,
|
|
358
|
+
},
|
|
359
|
+
{
|
|
360
|
+
match: (ctx) => /API_KEY_INVALID|Invalid API key/.test(ctx.message),
|
|
361
|
+
errorClass: AuthenticationError,
|
|
362
|
+
message: "Invalid LiteLLM configuration. Please check your LITELLM_API_KEY environment variable.",
|
|
363
|
+
},
|
|
364
|
+
{
|
|
365
|
+
match: (ctx) => /rate limit/i.test(ctx.message),
|
|
366
|
+
errorClass: RateLimitError,
|
|
367
|
+
message: "LiteLLM rate limit exceeded. Please try again later.",
|
|
368
|
+
},
|
|
369
|
+
{
|
|
370
|
+
match: (ctx) => /model/i.test(ctx.message) && /not found/i.test(ctx.message),
|
|
371
|
+
errorClass: InvalidModelError,
|
|
372
|
+
message: () => `Model '${this.modelName}' not available in LiteLLM proxy. ` +
|
|
373
|
+
"Please check your LiteLLM configuration and ensure the model is configured.",
|
|
374
|
+
},
|
|
375
|
+
...DEFAULT_ERROR_RULES,
|
|
376
|
+
];
|
|
377
|
+
return classifyProviderError(error, rules, this.providerName, this.modelName);
|
|
363
378
|
}
|
|
364
379
|
/**
|
|
365
380
|
* Get available models from LiteLLM proxy `/v1/models` endpoint.
|
|
@@ -15,6 +15,7 @@ import { OpenAIChatCompletionsProvider } from "./openaiChatCompletionsBase.js";
|
|
|
15
15
|
*/
|
|
16
16
|
export declare class LlamaCppProvider extends OpenAIChatCompletionsProvider {
|
|
17
17
|
constructor(modelName?: string, sdk?: unknown, _region?: string, credentials?: NeurolinkCredentials["llamacpp"]);
|
|
18
|
+
validateConfiguration(): Promise<boolean>;
|
|
18
19
|
protected getProviderName(): AIProviderName;
|
|
19
20
|
protected getDefaultModel(): string;
|
|
20
21
|
protected getFallbackModelName(): string;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { NetworkError, ProviderError } from "../types/index.js";
|
|
2
|
+
import { classifyProviderError, DEFAULT_ERROR_RULES, } from "../utils/errorClassifier.js";
|
|
2
3
|
import { logger } from "../utils/logger.js";
|
|
3
4
|
import { redactUrlCredentials } from "../utils/logSanitize.js";
|
|
4
|
-
import { TimeoutError } from "../utils/timeout.js";
|
|
5
5
|
import { OpenAIChatCompletionsProvider } from "./openaiChatCompletionsBase.js";
|
|
6
6
|
const LLAMACPP_DEFAULT_BASE_URL = "http://localhost:8080/v1";
|
|
7
7
|
const LLAMACPP_PLACEHOLDER_KEY = "llamacpp";
|
|
@@ -36,6 +36,9 @@ export class LlamaCppProvider extends OpenAIChatCompletionsProvider {
|
|
|
36
36
|
baseURL: redactUrlCredentials(this.config.baseURL),
|
|
37
37
|
});
|
|
38
38
|
}
|
|
39
|
+
async validateConfiguration() {
|
|
40
|
+
return this.probeModelsEndpoint(this.getAuthHeaders());
|
|
41
|
+
}
|
|
39
42
|
getProviderName() {
|
|
40
43
|
return "llamacpp";
|
|
41
44
|
}
|
|
@@ -49,25 +52,28 @@ export class LlamaCppProvider extends OpenAIChatCompletionsProvider {
|
|
|
49
52
|
return ["loaded-model"];
|
|
50
53
|
}
|
|
51
54
|
formatProviderError(error) {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
+
// `code`/`cause.code` aren't part of ProviderErrorContext, so they're read
|
|
56
|
+
// off the raw error here (mirrors ollama's `responseBody` extraction) for
|
|
57
|
+
// the ECONNREFUSED rule below, which the pre-migration code also checked
|
|
58
|
+
// via a duck-typed error code in addition to the message text.
|
|
55
59
|
const errorRecord = error;
|
|
56
|
-
const message = typeof errorRecord?.message === "string"
|
|
57
|
-
? errorRecord.message
|
|
58
|
-
: "Unknown error";
|
|
59
60
|
const cause = errorRecord?.cause ?? {};
|
|
60
61
|
const code = (errorRecord?.code ?? cause?.code);
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
62
|
+
const rules = [
|
|
63
|
+
{
|
|
64
|
+
match: (ctx) => code === "ECONNREFUSED" ||
|
|
65
|
+
/ECONNREFUSED|Failed to fetch|fetch failed/.test(ctx.message),
|
|
66
|
+
errorClass: NetworkError,
|
|
67
|
+
message: () => `llama.cpp server not reachable at ${redactUrlCredentials(this.config.baseURL)}. ` +
|
|
68
|
+
"Start it with: ./llama-server -m model.gguf --port 8080",
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
match: (ctx) => /400/.test(ctx.message),
|
|
72
|
+
errorClass: ProviderError,
|
|
73
|
+
message: "llama.cpp rejected the request. Common cause: model doesn't support tools (start llama-server with --jinja for tool support).",
|
|
74
|
+
},
|
|
75
|
+
...DEFAULT_ERROR_RULES,
|
|
76
|
+
];
|
|
77
|
+
return classifyProviderError(error, rules, "llamacpp", this.modelName);
|
|
72
78
|
}
|
|
73
79
|
}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { InvalidModelError, NetworkError } from "../types/index.js";
|
|
2
|
+
import { classifyProviderError, DEFAULT_ERROR_RULES, } from "../utils/errorClassifier.js";
|
|
3
3
|
import { logger } from "../utils/logger.js";
|
|
4
4
|
import { redactUrlCredentials } from "../utils/logSanitize.js";
|
|
5
|
-
import { TimeoutError } from "../utils/timeout.js";
|
|
6
5
|
import { OpenAIChatCompletionsProvider } from "./openaiChatCompletionsBase.js";
|
|
7
6
|
const LM_STUDIO_DEFAULT_BASE_URL = "http://localhost:1234/v1";
|
|
8
7
|
const LM_STUDIO_PLACEHOLDER_KEY = "lm-studio";
|
|
@@ -48,50 +47,34 @@ export class LMStudioProvider extends OpenAIChatCompletionsProvider {
|
|
|
48
47
|
return FALLBACK_MODEL;
|
|
49
48
|
}
|
|
50
49
|
formatProviderError(error) {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
50
|
+
// `code`/`cause.code` aren't part of ProviderErrorContext, so they're read
|
|
51
|
+
// off the raw error here (mirrors ollama's `responseBody` extraction) for
|
|
52
|
+
// the ECONNREFUSED rule below, which the pre-migration code also checked
|
|
53
|
+
// via a duck-typed error code in addition to the message text.
|
|
54
54
|
const errorRecord = error;
|
|
55
|
-
const message = typeof errorRecord?.message === "string"
|
|
56
|
-
? errorRecord.message
|
|
57
|
-
: "Unknown error";
|
|
58
55
|
const cause = errorRecord?.cause ?? {};
|
|
59
56
|
const code = (errorRecord?.code ?? cause?.code);
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
57
|
+
const rules = [
|
|
58
|
+
{
|
|
59
|
+
match: (ctx) => code === "ECONNREFUSED" ||
|
|
60
|
+
/ECONNREFUSED|Failed to fetch|fetch failed/.test(ctx.message),
|
|
61
|
+
errorClass: NetworkError,
|
|
62
|
+
message: () => `LM Studio server not reachable at ${redactUrlCredentials(this.config.baseURL)}. ` +
|
|
63
|
+
`Open the LM Studio app, load a model, and click "Start Server".`,
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
match: (ctx) => /model_not_found|404/.test(ctx.message),
|
|
67
|
+
errorClass: InvalidModelError,
|
|
68
|
+
message: () => `LM Studio model '${this.modelName}' is not loaded. Load it in the LM Studio app first.`,
|
|
69
|
+
},
|
|
70
|
+
...DEFAULT_ERROR_RULES,
|
|
71
|
+
];
|
|
72
|
+
return classifyProviderError(error, rules, "lm-studio", this.modelName);
|
|
71
73
|
}
|
|
72
74
|
async validateConfiguration() {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
const r = await proxyFetch(url, {
|
|
77
|
-
headers: this.config.apiKey && this.config.apiKey !== LM_STUDIO_PLACEHOLDER_KEY
|
|
78
|
-
? { Authorization: `Bearer ${this.config.apiKey}` }
|
|
79
|
-
: undefined,
|
|
80
|
-
signal: AbortSignal.timeout(5000),
|
|
81
|
-
});
|
|
82
|
-
if (!r.ok) {
|
|
83
|
-
return false;
|
|
84
|
-
}
|
|
85
|
-
// A 200 with an empty data array means LM Studio is up but no model is
|
|
86
|
-
// loaded — `resolveModelName()` will fall back to FALLBACK_MODEL and the
|
|
87
|
-
// first real request will fail. Require at least one loaded model so
|
|
88
|
-
// health checks honestly reflect whether the provider is usable.
|
|
89
|
-
const data = (await r.json().catch(() => null));
|
|
90
|
-
return Boolean(data?.data?.some((m) => typeof m?.id === "string" && m.id.trim().length > 0));
|
|
91
|
-
}
|
|
92
|
-
catch {
|
|
93
|
-
return false;
|
|
94
|
-
}
|
|
75
|
+
return this.probeModelsEndpoint(this.config.apiKey && this.config.apiKey !== LM_STUDIO_PLACEHOLDER_KEY
|
|
76
|
+
? { Authorization: `Bearer ${this.config.apiKey}` }
|
|
77
|
+
: {});
|
|
95
78
|
}
|
|
96
79
|
getConfiguration() {
|
|
97
80
|
return {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { MistralModels } from "../constants/enums.js";
|
|
2
|
-
import { AuthenticationError
|
|
2
|
+
import { AuthenticationError } from "../types/index.js";
|
|
3
3
|
import { logger } from "../utils/logger.js";
|
|
4
4
|
import { redactUrlCredentials } from "../utils/logSanitize.js";
|
|
5
|
+
import { classifyProviderError, DEFAULT_ERROR_RULES, } from "../utils/errorClassifier.js";
|
|
5
6
|
import { createMistralConfig, getProviderModel, validateApiKey, } from "../utils/providerConfig.js";
|
|
6
|
-
import { TimeoutError } from "../utils/timeout.js";
|
|
7
7
|
import { OpenAIChatCompletionsProvider } from "./openaiChatCompletionsBase.js";
|
|
8
8
|
const MISTRAL_DEFAULT_BASE_URL = "https://api.mistral.ai/v1";
|
|
9
9
|
const getMistralApiKey = () => {
|
|
@@ -60,28 +60,16 @@ export class MistralProvider extends OpenAIChatCompletionsProvider {
|
|
|
60
60
|
return getDefaultMistralModel();
|
|
61
61
|
}
|
|
62
62
|
formatProviderError(error) {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
message.includes("401")) {
|
|
74
|
-
return new AuthenticationError("Invalid Mistral API key. Please check your MISTRAL_API_KEY environment variable.", "mistral");
|
|
75
|
-
}
|
|
76
|
-
if (message.includes("rate limit") ||
|
|
77
|
-
message.includes("Rate limit") ||
|
|
78
|
-
message.includes("429")) {
|
|
79
|
-
return new RateLimitError("Mistral rate limit exceeded", "mistral");
|
|
80
|
-
}
|
|
81
|
-
if (message.includes("model_not_found") || message.includes("404")) {
|
|
82
|
-
return new InvalidModelError(`Mistral model '${this.modelName}' not found.`, "mistral");
|
|
83
|
-
}
|
|
84
|
-
return new ProviderError(`Mistral error: ${message}`, "mistral");
|
|
63
|
+
const rules = [
|
|
64
|
+
{
|
|
65
|
+
match: (ctx) => ctx.statusCode === 401 ||
|
|
66
|
+
/API_KEY_INVALID|Invalid API key|Unauthorized/i.test(ctx.message),
|
|
67
|
+
errorClass: AuthenticationError,
|
|
68
|
+
message: "Invalid Mistral API key. Please check your MISTRAL_API_KEY environment variable.",
|
|
69
|
+
},
|
|
70
|
+
...DEFAULT_ERROR_RULES,
|
|
71
|
+
];
|
|
72
|
+
return classifyProviderError(error, rules, "mistral", this.modelName);
|
|
85
73
|
}
|
|
86
74
|
// ===========================================================================
|
|
87
75
|
// Optional hooks
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { NvidiaNimModels } from "../../constants/enums.js";
|
|
2
|
-
import { AuthenticationError, InvalidModelError,
|
|
2
|
+
import { AuthenticationError, InvalidModelError, ProviderError, RateLimitError, } from "../../types/index.js";
|
|
3
|
+
import { classifyProviderError, DEFAULT_ERROR_RULES, } from "../../utils/errorClassifier.js";
|
|
3
4
|
import { logger } from "../../utils/logger.js";
|
|
4
5
|
import { redactUrlCredentials } from "../../utils/logSanitize.js";
|
|
5
6
|
import { createNvidiaNimConfig, getProviderModel, validateApiKey, } from "../../utils/providerConfig.js";
|
|
6
|
-
import { TimeoutError } from "../../utils/timeout.js";
|
|
7
7
|
import { OpenAIChatCompletionsProvider } from "../openaiChatCompletionsBase.js";
|
|
8
8
|
/**
|
|
9
9
|
* Decide whether a NIM 400 response body is a rejection of the named
|
|
@@ -237,38 +237,41 @@ export class NvidiaNimProvider extends OpenAIChatCompletionsProvider {
|
|
|
237
237
|
return JSON.parse(serialized);
|
|
238
238
|
}
|
|
239
239
|
formatProviderError(error) {
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
240
|
+
const rules = [
|
|
241
|
+
// NIM canonically returns HTTP 401/Unauthorized for invalid API keys,
|
|
242
|
+
// but its OpenAI-compatible gateway sometimes surfaces a bare 400 +
|
|
243
|
+
// "Bad Request" with no body details for both malformed-credentials
|
|
244
|
+
// and bad-parameter cases. Because the two are indistinguishable from
|
|
245
|
+
// the message alone, bare 400/"Bad Request" is deliberately NOT
|
|
246
|
+
// promoted to "invalid key" here — that would mis-classify legitimate
|
|
247
|
+
// parameter errors (e.g. unsupported `reasoning_budget`, unsupported
|
|
248
|
+
// `chat_template`) as auth failures. Tests that probe the auth path
|
|
249
|
+
// (K1) detect "bad request" / "400" themselves; tests that probe
|
|
250
|
+
// parameter retry (K5) need the original "Bad Request" message to
|
|
251
|
+
// surface.
|
|
252
|
+
{
|
|
253
|
+
match: (ctx) => /Invalid API key|401|Unauthorized/.test(ctx.message),
|
|
254
|
+
errorClass: AuthenticationError,
|
|
255
|
+
message: "Invalid NVIDIA NIM API key. Get one at https://build.nvidia.com/settings/api-keys",
|
|
256
|
+
},
|
|
257
|
+
{
|
|
258
|
+
match: (ctx) => /rate limit|429/.test(ctx.message),
|
|
259
|
+
errorClass: RateLimitError,
|
|
260
|
+
message: "NVIDIA NIM rate limit exceeded",
|
|
261
|
+
},
|
|
262
|
+
{
|
|
263
|
+
match: (ctx) => /404|model_not_found/.test(ctx.message),
|
|
264
|
+
errorClass: InvalidModelError,
|
|
265
|
+
message: () => `NVIDIA NIM model '${this.modelName}' not available. Browse the catalog at https://build.nvidia.com/models`,
|
|
266
|
+
},
|
|
267
|
+
{
|
|
268
|
+
match: (ctx) => /quota|403/.test(ctx.message),
|
|
269
|
+
errorClass: ProviderError,
|
|
270
|
+
message: "NVIDIA NIM quota exceeded for your account",
|
|
271
|
+
},
|
|
272
|
+
...DEFAULT_ERROR_RULES,
|
|
273
|
+
];
|
|
274
|
+
return classifyProviderError(error, rules, "nvidia-nim", this.modelName);
|
|
272
275
|
}
|
|
273
276
|
async validateConfiguration() {
|
|
274
277
|
return (typeof this.config.apiKey === "string" &&
|