@juspay/neurolink 7.6.1 → 7.7.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 (138) hide show
  1. package/CHANGELOG.md +15 -4
  2. package/README.md +78 -3
  3. package/dist/cli/commands/config.d.ts +275 -3
  4. package/dist/cli/commands/config.js +121 -0
  5. package/dist/cli/commands/mcp.js +77 -28
  6. package/dist/cli/factories/commandFactory.js +359 -6
  7. package/dist/core/analytics.js +7 -27
  8. package/dist/core/baseProvider.js +43 -4
  9. package/dist/core/constants.d.ts +46 -0
  10. package/dist/core/constants.js +47 -0
  11. package/dist/core/dynamicModels.d.ts +16 -4
  12. package/dist/core/dynamicModels.js +130 -26
  13. package/dist/core/evaluation.js +5 -1
  14. package/dist/core/evaluationProviders.d.ts +6 -2
  15. package/dist/core/evaluationProviders.js +41 -125
  16. package/dist/core/factory.d.ts +5 -0
  17. package/dist/core/factory.js +62 -50
  18. package/dist/core/modelConfiguration.d.ts +246 -0
  19. package/dist/core/modelConfiguration.js +775 -0
  20. package/dist/core/types.d.ts +22 -3
  21. package/dist/core/types.js +5 -1
  22. package/dist/factories/providerRegistry.js +3 -3
  23. package/dist/index.d.ts +1 -1
  24. package/dist/index.js +1 -1
  25. package/dist/lib/core/analytics.js +7 -27
  26. package/dist/lib/core/baseProvider.js +43 -4
  27. package/dist/lib/core/constants.d.ts +46 -0
  28. package/dist/lib/core/constants.js +47 -0
  29. package/dist/lib/core/dynamicModels.d.ts +16 -4
  30. package/dist/lib/core/dynamicModels.js +130 -26
  31. package/dist/lib/core/evaluation.js +5 -1
  32. package/dist/lib/core/evaluationProviders.d.ts +6 -2
  33. package/dist/lib/core/evaluationProviders.js +41 -125
  34. package/dist/lib/core/factory.d.ts +5 -0
  35. package/dist/lib/core/factory.js +63 -50
  36. package/dist/lib/core/modelConfiguration.d.ts +246 -0
  37. package/dist/lib/core/modelConfiguration.js +775 -0
  38. package/dist/lib/core/types.d.ts +22 -3
  39. package/dist/lib/core/types.js +5 -1
  40. package/dist/lib/factories/providerRegistry.js +3 -3
  41. package/dist/lib/index.d.ts +1 -1
  42. package/dist/lib/index.js +1 -1
  43. package/dist/lib/mcp/factory.d.ts +5 -5
  44. package/dist/lib/mcp/factory.js +2 -2
  45. package/dist/lib/mcp/servers/utilities/utilityServer.d.ts +1 -1
  46. package/dist/lib/mcp/servers/utilities/utilityServer.js +1 -1
  47. package/dist/lib/mcp/toolRegistry.js +2 -2
  48. package/dist/lib/neurolink.d.ts +168 -12
  49. package/dist/lib/neurolink.js +685 -123
  50. package/dist/lib/providers/anthropic.js +52 -2
  51. package/dist/lib/providers/googleAiStudio.js +4 -0
  52. package/dist/lib/providers/googleVertex.d.ts +75 -9
  53. package/dist/lib/providers/googleVertex.js +365 -46
  54. package/dist/lib/providers/huggingFace.d.ts +52 -11
  55. package/dist/lib/providers/huggingFace.js +180 -42
  56. package/dist/lib/providers/litellm.d.ts +9 -9
  57. package/dist/lib/providers/litellm.js +103 -16
  58. package/dist/lib/providers/ollama.d.ts +52 -17
  59. package/dist/lib/providers/ollama.js +276 -68
  60. package/dist/lib/sdk/toolRegistration.d.ts +42 -0
  61. package/dist/lib/sdk/toolRegistration.js +269 -27
  62. package/dist/lib/telemetry/telemetryService.d.ts +6 -0
  63. package/dist/lib/telemetry/telemetryService.js +38 -3
  64. package/dist/lib/types/contextTypes.d.ts +75 -11
  65. package/dist/lib/types/contextTypes.js +227 -1
  66. package/dist/lib/types/domainTypes.d.ts +62 -0
  67. package/dist/lib/types/domainTypes.js +5 -0
  68. package/dist/lib/types/generateTypes.d.ts +52 -0
  69. package/dist/lib/types/index.d.ts +1 -0
  70. package/dist/lib/types/mcpTypes.d.ts +1 -1
  71. package/dist/lib/types/mcpTypes.js +1 -1
  72. package/dist/lib/types/streamTypes.d.ts +14 -0
  73. package/dist/lib/types/universalProviderOptions.d.ts +1 -1
  74. package/dist/lib/utils/errorHandling.d.ts +142 -0
  75. package/dist/lib/utils/errorHandling.js +316 -0
  76. package/dist/lib/utils/factoryProcessing.d.ts +74 -0
  77. package/dist/lib/utils/factoryProcessing.js +588 -0
  78. package/dist/lib/utils/optionsConversion.d.ts +54 -0
  79. package/dist/lib/utils/optionsConversion.js +126 -0
  80. package/dist/lib/utils/optionsUtils.d.ts +246 -0
  81. package/dist/lib/utils/optionsUtils.js +960 -0
  82. package/dist/lib/utils/providerConfig.js +6 -2
  83. package/dist/lib/utils/providerHealth.d.ts +107 -0
  84. package/dist/lib/utils/providerHealth.js +543 -0
  85. package/dist/lib/utils/providerUtils.d.ts +17 -0
  86. package/dist/lib/utils/providerUtils.js +271 -16
  87. package/dist/lib/utils/timeout.js +1 -1
  88. package/dist/lib/utils/tokenLimits.d.ts +33 -0
  89. package/dist/lib/utils/tokenLimits.js +118 -0
  90. package/dist/mcp/factory.d.ts +5 -5
  91. package/dist/mcp/factory.js +2 -2
  92. package/dist/mcp/servers/utilities/utilityServer.d.ts +1 -1
  93. package/dist/mcp/servers/utilities/utilityServer.js +1 -1
  94. package/dist/mcp/toolRegistry.js +2 -2
  95. package/dist/neurolink.d.ts +168 -12
  96. package/dist/neurolink.js +685 -123
  97. package/dist/providers/anthropic.js +52 -2
  98. package/dist/providers/googleAiStudio.js +4 -0
  99. package/dist/providers/googleVertex.d.ts +75 -9
  100. package/dist/providers/googleVertex.js +365 -46
  101. package/dist/providers/huggingFace.d.ts +52 -11
  102. package/dist/providers/huggingFace.js +181 -43
  103. package/dist/providers/litellm.d.ts +9 -9
  104. package/dist/providers/litellm.js +103 -16
  105. package/dist/providers/ollama.d.ts +52 -17
  106. package/dist/providers/ollama.js +276 -68
  107. package/dist/sdk/toolRegistration.d.ts +42 -0
  108. package/dist/sdk/toolRegistration.js +269 -27
  109. package/dist/telemetry/telemetryService.d.ts +6 -0
  110. package/dist/telemetry/telemetryService.js +38 -3
  111. package/dist/types/contextTypes.d.ts +75 -11
  112. package/dist/types/contextTypes.js +227 -2
  113. package/dist/types/domainTypes.d.ts +62 -0
  114. package/dist/types/domainTypes.js +5 -0
  115. package/dist/types/generateTypes.d.ts +52 -0
  116. package/dist/types/index.d.ts +1 -0
  117. package/dist/types/mcpTypes.d.ts +1 -1
  118. package/dist/types/mcpTypes.js +1 -1
  119. package/dist/types/streamTypes.d.ts +14 -0
  120. package/dist/types/universalProviderOptions.d.ts +1 -1
  121. package/dist/types/universalProviderOptions.js +0 -1
  122. package/dist/utils/errorHandling.d.ts +142 -0
  123. package/dist/utils/errorHandling.js +316 -0
  124. package/dist/utils/factoryProcessing.d.ts +74 -0
  125. package/dist/utils/factoryProcessing.js +588 -0
  126. package/dist/utils/optionsConversion.d.ts +54 -0
  127. package/dist/utils/optionsConversion.js +126 -0
  128. package/dist/utils/optionsUtils.d.ts +246 -0
  129. package/dist/utils/optionsUtils.js +960 -0
  130. package/dist/utils/providerConfig.js +6 -2
  131. package/dist/utils/providerHealth.d.ts +107 -0
  132. package/dist/utils/providerHealth.js +543 -0
  133. package/dist/utils/providerUtils.d.ts +17 -0
  134. package/dist/utils/providerUtils.js +271 -16
  135. package/dist/utils/timeout.js +1 -1
  136. package/dist/utils/tokenLimits.d.ts +33 -0
  137. package/dist/utils/tokenLimits.js +118 -0
  138. package/package.json +2 -2
@@ -0,0 +1,775 @@
1
+ /**
2
+ * Model Configuration System
3
+ *
4
+ * Replaces hardcoded model-specific logic with configurable, runtime-updateable configurations.
5
+ * This addresses GitHub Copilot review comment about making model-specific logic configuration-based.
6
+ */
7
+ import { logger } from "../utils/logger.js";
8
+ import fs from "fs";
9
+ import path from "path";
10
+ /**
11
+ * Model name constants - extracted from hardcoded values for better maintainability
12
+ * These constants can be overridden by environment variables
13
+ */
14
+ export const MODEL_NAMES = {
15
+ // Google AI Models
16
+ GOOGLE_AI: {
17
+ FAST: "gemini-2.5-flash",
18
+ BALANCED: "gemini-2.5-pro",
19
+ QUALITY: "gemini-2.5-pro",
20
+ },
21
+ // Google Vertex Models
22
+ GOOGLE_VERTEX: {
23
+ FAST: "gemini-2.5-flash",
24
+ BALANCED: "gemini-2.5-pro",
25
+ QUALITY: "gemini-2.5-pro",
26
+ },
27
+ // OpenAI Models
28
+ OPENAI: {
29
+ FAST: "gpt-4o-mini",
30
+ BALANCED: "gpt-4o",
31
+ QUALITY: "gpt-4o",
32
+ },
33
+ // Anthropic Models
34
+ ANTHROPIC: {
35
+ FAST: "claude-3-haiku-20240307",
36
+ BALANCED: "claude-3-sonnet-20240229",
37
+ QUALITY: "claude-3-5-sonnet-20241022",
38
+ },
39
+ // Vertex AI Models (legacy alias)
40
+ VERTEX: {
41
+ FAST: "gemini-2.5-flash",
42
+ BALANCED: "gemini-2.5-pro",
43
+ QUALITY: "gemini-2.5-pro",
44
+ },
45
+ // AWS Bedrock Models
46
+ BEDROCK: {
47
+ FAST: "anthropic.claude-3-haiku-20240307-v1:0",
48
+ BALANCED: "anthropic.claude-3-sonnet-20240229-v1:0",
49
+ QUALITY: "anthropic.claude-3-opus-20240229-v1:0",
50
+ },
51
+ // Azure OpenAI Models
52
+ AZURE: {
53
+ FAST: "gpt-4o-mini",
54
+ BALANCED: "gpt-4o",
55
+ QUALITY: "gpt-4o",
56
+ },
57
+ // Ollama Models
58
+ OLLAMA: {
59
+ FAST: "llama3.2:latest",
60
+ BALANCED: "llama3.1:8b",
61
+ QUALITY: "llama3.1:70b",
62
+ },
63
+ // HuggingFace Models
64
+ HUGGINGFACE: {
65
+ FAST: "microsoft/DialoGPT-medium",
66
+ BALANCED: "microsoft/DialoGPT-large",
67
+ QUALITY: "meta-llama/Llama-2-7b-chat-hf",
68
+ },
69
+ // Mistral Models
70
+ MISTRAL: {
71
+ FAST: "mistral-small-latest",
72
+ BALANCED: "mistral-medium-latest",
73
+ QUALITY: "mistral-large-latest",
74
+ },
75
+ };
76
+ /**
77
+ * Model configuration manager
78
+ */
79
+ export class ModelConfigurationManager {
80
+ static instance;
81
+ configurations = new Map();
82
+ configSource = "default";
83
+ lastUpdated = Date.now();
84
+ constructor() {
85
+ this.loadDefaultConfigurations();
86
+ }
87
+ static getInstance() {
88
+ if (!ModelConfigurationManager.instance) {
89
+ ModelConfigurationManager.instance = new ModelConfigurationManager();
90
+ }
91
+ return ModelConfigurationManager.instance;
92
+ }
93
+ /**
94
+ * Load default configurations (replaces hardcoded values)
95
+ */
96
+ loadDefaultConfigurations() {
97
+ // Default provider configurations - these can be overridden
98
+ const defaultConfigs = {
99
+ "google-ai": {
100
+ provider: "google-ai",
101
+ models: {
102
+ fast: this.getConfigValue("GOOGLE_AI_FAST_MODEL", MODEL_NAMES.GOOGLE_AI.FAST),
103
+ balanced: this.getConfigValue("GOOGLE_AI_BALANCED_MODEL", MODEL_NAMES.GOOGLE_AI.BALANCED),
104
+ quality: this.getConfigValue("GOOGLE_AI_QUALITY_MODEL", MODEL_NAMES.GOOGLE_AI.QUALITY),
105
+ },
106
+ defaultCost: {
107
+ input: this.parseFloat(process.env.GOOGLE_AI_DEFAULT_INPUT_COST, 0.000075),
108
+ output: this.parseFloat(process.env.GOOGLE_AI_DEFAULT_OUTPUT_COST, 0.0003),
109
+ },
110
+ requiredEnvVars: ["GOOGLE_AI_API_KEY"],
111
+ performance: {
112
+ speed: this.parseInt(process.env.GOOGLE_AI_SPEED_RATING, 3),
113
+ quality: this.parseInt(process.env.GOOGLE_AI_QUALITY_RATING, 3),
114
+ cost: this.parseInt(process.env.GOOGLE_AI_COST_RATING, 3),
115
+ },
116
+ modelBehavior: {
117
+ maxTokensIssues: this.getConfigArray("GOOGLE_AI_MAX_TOKENS_ISSUES", [
118
+ MODEL_NAMES.GOOGLE_AI.FAST,
119
+ MODEL_NAMES.GOOGLE_AI.BALANCED,
120
+ ]),
121
+ },
122
+ },
123
+ "google-vertex": {
124
+ provider: "google-vertex",
125
+ models: {
126
+ fast: this.getConfigValue("GOOGLE_VERTEX_FAST_MODEL", MODEL_NAMES.GOOGLE_VERTEX.FAST),
127
+ balanced: this.getConfigValue("GOOGLE_VERTEX_BALANCED_MODEL", MODEL_NAMES.GOOGLE_VERTEX.BALANCED),
128
+ quality: this.getConfigValue("GOOGLE_VERTEX_QUALITY_MODEL", MODEL_NAMES.GOOGLE_VERTEX.QUALITY),
129
+ },
130
+ defaultCost: {
131
+ input: this.parseFloat(process.env.GOOGLE_VERTEX_DEFAULT_INPUT_COST, 0.000075),
132
+ output: this.parseFloat(process.env.GOOGLE_VERTEX_DEFAULT_OUTPUT_COST, 0.0003),
133
+ },
134
+ requiredEnvVars: ["GOOGLE_VERTEX_PROJECT_ID", "GOOGLE_VERTEX_LOCATION"],
135
+ performance: {
136
+ speed: this.parseInt(process.env.GOOGLE_VERTEX_SPEED_RATING, 3),
137
+ quality: this.parseInt(process.env.GOOGLE_VERTEX_QUALITY_RATING, 3),
138
+ cost: this.parseInt(process.env.GOOGLE_VERTEX_COST_RATING, 3),
139
+ },
140
+ modelBehavior: {
141
+ maxTokensIssues: this.getConfigArray("GOOGLE_VERTEX_MAX_TOKENS_ISSUES", [
142
+ MODEL_NAMES.GOOGLE_VERTEX.FAST,
143
+ MODEL_NAMES.GOOGLE_VERTEX.BALANCED,
144
+ ]),
145
+ },
146
+ },
147
+ openai: {
148
+ provider: "openai",
149
+ models: {
150
+ fast: this.getConfigValue("OPENAI_FAST_MODEL", MODEL_NAMES.OPENAI.FAST),
151
+ balanced: this.getConfigValue("OPENAI_BALANCED_MODEL", MODEL_NAMES.OPENAI.BALANCED),
152
+ quality: this.getConfigValue("OPENAI_QUALITY_MODEL", MODEL_NAMES.OPENAI.QUALITY),
153
+ },
154
+ defaultCost: {
155
+ input: this.parseFloat(process.env.OPENAI_DEFAULT_INPUT_COST, 0.00015),
156
+ output: this.parseFloat(process.env.OPENAI_DEFAULT_OUTPUT_COST, 0.0006),
157
+ },
158
+ requiredEnvVars: ["OPENAI_API_KEY"],
159
+ performance: {
160
+ speed: this.parseInt(process.env.OPENAI_SPEED_RATING, 2),
161
+ quality: this.parseInt(process.env.OPENAI_QUALITY_RATING, 3),
162
+ cost: this.parseInt(process.env.OPENAI_COST_RATING, 2),
163
+ },
164
+ modelBehavior: {
165
+ maxTokensIssues: this.getConfigArray("OPENAI_MAX_TOKENS_ISSUES", []),
166
+ specialHandling: this.getConfigObject("OPENAI_SPECIAL_HANDLING", {}),
167
+ },
168
+ },
169
+ anthropic: {
170
+ provider: "anthropic",
171
+ models: {
172
+ fast: this.getConfigValue("ANTHROPIC_FAST_MODEL", MODEL_NAMES.ANTHROPIC.FAST),
173
+ balanced: this.getConfigValue("ANTHROPIC_BALANCED_MODEL", MODEL_NAMES.ANTHROPIC.BALANCED),
174
+ quality: this.getConfigValue("ANTHROPIC_QUALITY_MODEL", MODEL_NAMES.ANTHROPIC.QUALITY),
175
+ },
176
+ defaultCost: {
177
+ input: this.parseFloat(process.env.ANTHROPIC_DEFAULT_INPUT_COST, 0.00025),
178
+ output: this.parseFloat(process.env.ANTHROPIC_DEFAULT_OUTPUT_COST, 0.00125),
179
+ },
180
+ requiredEnvVars: ["ANTHROPIC_API_KEY"],
181
+ performance: {
182
+ speed: this.parseInt(process.env.ANTHROPIC_SPEED_RATING, 2),
183
+ quality: this.parseInt(process.env.ANTHROPIC_QUALITY_RATING, 3),
184
+ cost: this.parseInt(process.env.ANTHROPIC_COST_RATING, 2),
185
+ },
186
+ modelBehavior: {
187
+ maxTokensIssues: this.getConfigArray("ANTHROPIC_MAX_TOKENS_ISSUES", []),
188
+ specialHandling: this.getConfigObject("ANTHROPIC_SPECIAL_HANDLING", {}),
189
+ },
190
+ },
191
+ vertex: {
192
+ provider: "vertex",
193
+ models: {
194
+ fast: this.getConfigValue("VERTEX_FAST_MODEL", MODEL_NAMES.VERTEX.FAST),
195
+ balanced: this.getConfigValue("VERTEX_BALANCED_MODEL", MODEL_NAMES.VERTEX.BALANCED),
196
+ quality: this.getConfigValue("VERTEX_QUALITY_MODEL", MODEL_NAMES.VERTEX.QUALITY),
197
+ },
198
+ defaultCost: {
199
+ input: this.parseFloat(process.env.VERTEX_DEFAULT_INPUT_COST, 0.000075),
200
+ output: this.parseFloat(process.env.VERTEX_DEFAULT_OUTPUT_COST, 0.0003),
201
+ },
202
+ requiredEnvVars: [
203
+ "GOOGLE_VERTEX_PROJECT",
204
+ "GOOGLE_APPLICATION_CREDENTIALS",
205
+ ],
206
+ performance: {
207
+ speed: this.parseInt(process.env.VERTEX_SPEED_RATING, 2),
208
+ quality: this.parseInt(process.env.VERTEX_QUALITY_RATING, 3),
209
+ cost: this.parseInt(process.env.VERTEX_COST_RATING, 3),
210
+ },
211
+ modelBehavior: {
212
+ maxTokensIssues: this.getConfigArray("VERTEX_MAX_TOKENS_ISSUES", [
213
+ MODEL_NAMES.VERTEX.FAST,
214
+ MODEL_NAMES.VERTEX.BALANCED,
215
+ ]),
216
+ specialHandling: this.getConfigObject("VERTEX_SPECIAL_HANDLING", {}),
217
+ },
218
+ },
219
+ bedrock: {
220
+ provider: "bedrock",
221
+ models: {
222
+ fast: this.getConfigValue("BEDROCK_FAST_MODEL", MODEL_NAMES.BEDROCK.FAST),
223
+ balanced: this.getConfigValue("BEDROCK_BALANCED_MODEL", MODEL_NAMES.BEDROCK.BALANCED),
224
+ quality: this.getConfigValue("BEDROCK_QUALITY_MODEL", MODEL_NAMES.BEDROCK.QUALITY),
225
+ },
226
+ defaultCost: {
227
+ input: this.parseFloat(process.env.BEDROCK_DEFAULT_INPUT_COST, 0.00025),
228
+ output: this.parseFloat(process.env.BEDROCK_DEFAULT_OUTPUT_COST, 0.00125),
229
+ },
230
+ requiredEnvVars: ["AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY"],
231
+ performance: {
232
+ speed: this.parseInt(process.env.BEDROCK_SPEED_RATING, 2),
233
+ quality: this.parseInt(process.env.BEDROCK_QUALITY_RATING, 3),
234
+ cost: this.parseInt(process.env.BEDROCK_COST_RATING, 2),
235
+ },
236
+ modelBehavior: {
237
+ maxTokensIssues: this.getConfigArray("BEDROCK_MAX_TOKENS_ISSUES", []),
238
+ specialHandling: this.getConfigObject("BEDROCK_SPECIAL_HANDLING", {}),
239
+ },
240
+ },
241
+ azure: {
242
+ provider: "azure",
243
+ models: {
244
+ fast: this.getConfigValue("AZURE_FAST_MODEL", MODEL_NAMES.AZURE.FAST),
245
+ balanced: this.getConfigValue("AZURE_BALANCED_MODEL", MODEL_NAMES.AZURE.BALANCED),
246
+ quality: this.getConfigValue("AZURE_QUALITY_MODEL", MODEL_NAMES.AZURE.QUALITY),
247
+ },
248
+ defaultCost: {
249
+ input: this.parseFloat(process.env.AZURE_DEFAULT_INPUT_COST, 0.00015),
250
+ output: this.parseFloat(process.env.AZURE_DEFAULT_OUTPUT_COST, 0.0006),
251
+ },
252
+ requiredEnvVars: ["AZURE_OPENAI_API_KEY", "AZURE_OPENAI_ENDPOINT"],
253
+ performance: {
254
+ speed: this.parseInt(process.env.AZURE_SPEED_RATING, 2),
255
+ quality: this.parseInt(process.env.AZURE_QUALITY_RATING, 3),
256
+ cost: this.parseInt(process.env.AZURE_COST_RATING, 2),
257
+ },
258
+ modelBehavior: {
259
+ maxTokensIssues: this.getConfigArray("AZURE_MAX_TOKENS_ISSUES", []),
260
+ specialHandling: this.getConfigObject("AZURE_SPECIAL_HANDLING", {}),
261
+ },
262
+ },
263
+ ollama: {
264
+ provider: "ollama",
265
+ models: {
266
+ fast: this.getConfigValue("OLLAMA_FAST_MODEL", MODEL_NAMES.OLLAMA.FAST),
267
+ balanced: this.getConfigValue("OLLAMA_BALANCED_MODEL", MODEL_NAMES.OLLAMA.BALANCED),
268
+ quality: this.getConfigValue("OLLAMA_QUALITY_MODEL", MODEL_NAMES.OLLAMA.QUALITY),
269
+ },
270
+ defaultCost: {
271
+ input: 0, // Local models are free
272
+ output: 0,
273
+ },
274
+ requiredEnvVars: [], // No API key needed
275
+ performance: {
276
+ speed: this.parseInt(process.env.OLLAMA_SPEED_RATING, 1),
277
+ quality: this.parseInt(process.env.OLLAMA_QUALITY_RATING, 2),
278
+ cost: this.parseInt(process.env.OLLAMA_COST_RATING, 3),
279
+ },
280
+ modelBehavior: {
281
+ maxTokensIssues: this.getConfigArray("OLLAMA_MAX_TOKENS_ISSUES", []),
282
+ specialHandling: this.getConfigObject("OLLAMA_SPECIAL_HANDLING", {}),
283
+ // Tool-capable models configuration (replaces hardcoded list in ollama.ts)
284
+ toolCapableModels: this.getConfigArray("OLLAMA_TOOL_CAPABLE_MODELS", [
285
+ // Llama 3.1 series (excellent tool calling)
286
+ "llama3.1:8b-instruct",
287
+ "llama3.1:70b-instruct",
288
+ "llama3.1",
289
+ // Mistral series (reliable function calling)
290
+ "mistral:7b-instruct",
291
+ "mistral-nemo:12b",
292
+ "mistral",
293
+ // Hermes series (specialized for tools)
294
+ "hermes3:8b",
295
+ "hermes2-pro",
296
+ // Function-calling specialized models
297
+ "firefunction-v2",
298
+ "firefunction",
299
+ // Code Llama (code-focused tools)
300
+ "codellama:34b-instruct",
301
+ "codellama:13b-instruct",
302
+ // Other capable models
303
+ "qwen2.5:14b-instruct",
304
+ "gemma2:27b-instruct",
305
+ ]),
306
+ },
307
+ },
308
+ huggingface: {
309
+ provider: "huggingface",
310
+ models: {
311
+ fast: this.getConfigValue("HUGGINGFACE_FAST_MODEL", MODEL_NAMES.HUGGINGFACE.FAST),
312
+ balanced: this.getConfigValue("HUGGINGFACE_BALANCED_MODEL", MODEL_NAMES.HUGGINGFACE.BALANCED),
313
+ quality: this.getConfigValue("HUGGINGFACE_QUALITY_MODEL", MODEL_NAMES.HUGGINGFACE.QUALITY),
314
+ },
315
+ defaultCost: {
316
+ input: this.parseFloat(process.env.HUGGINGFACE_DEFAULT_INPUT_COST, 0.0002),
317
+ output: this.parseFloat(process.env.HUGGINGFACE_DEFAULT_OUTPUT_COST, 0.0006),
318
+ },
319
+ requiredEnvVars: ["HUGGINGFACE_API_KEY"],
320
+ performance: {
321
+ speed: this.parseInt(process.env.HUGGINGFACE_SPEED_RATING, 1),
322
+ quality: this.parseInt(process.env.HUGGINGFACE_QUALITY_RATING, 2),
323
+ cost: this.parseInt(process.env.HUGGINGFACE_COST_RATING, 2),
324
+ },
325
+ modelBehavior: {
326
+ maxTokensIssues: this.getConfigArray("HUGGINGFACE_MAX_TOKENS_ISSUES", []),
327
+ specialHandling: this.getConfigObject("HUGGINGFACE_SPECIAL_HANDLING", {}),
328
+ },
329
+ },
330
+ mistral: {
331
+ provider: "mistral",
332
+ models: {
333
+ fast: this.getConfigValue("MISTRAL_FAST_MODEL", MODEL_NAMES.MISTRAL.FAST),
334
+ balanced: this.getConfigValue("MISTRAL_BALANCED_MODEL", MODEL_NAMES.MISTRAL.BALANCED),
335
+ quality: this.getConfigValue("MISTRAL_QUALITY_MODEL", MODEL_NAMES.MISTRAL.QUALITY),
336
+ },
337
+ defaultCost: {
338
+ input: this.parseFloat(process.env.MISTRAL_DEFAULT_INPUT_COST, 0.0002),
339
+ output: this.parseFloat(process.env.MISTRAL_DEFAULT_OUTPUT_COST, 0.0006),
340
+ },
341
+ requiredEnvVars: ["MISTRAL_API_KEY"],
342
+ performance: {
343
+ speed: this.parseInt(process.env.MISTRAL_SPEED_RATING, 2),
344
+ quality: this.parseInt(process.env.MISTRAL_QUALITY_RATING, 2),
345
+ cost: this.parseInt(process.env.MISTRAL_COST_RATING, 2),
346
+ },
347
+ modelBehavior: {
348
+ maxTokensIssues: this.getConfigArray("MISTRAL_MAX_TOKENS_ISSUES", []),
349
+ specialHandling: this.getConfigObject("MISTRAL_SPECIAL_HANDLING", {}),
350
+ },
351
+ },
352
+ };
353
+ // Load configurations
354
+ for (const [provider, config] of Object.entries(defaultConfigs)) {
355
+ this.configurations.set(provider, config);
356
+ }
357
+ logger.debug(`Loaded ${this.configurations.size} provider configurations from ${this.configSource}`);
358
+ }
359
+ /**
360
+ * Helper method to get configuration value with fallback and validation
361
+ */
362
+ getConfigValue(envVar, defaultValue) {
363
+ const value = process.env[envVar];
364
+ if (value && !this.isValidConfigValue(value)) {
365
+ logger.warn(`Environment variable ${envVar} has an invalid value: "${value}". Falling back to default value.`);
366
+ return defaultValue;
367
+ }
368
+ return value || defaultValue;
369
+ }
370
+ /**
371
+ * Validate configuration values for security and correctness
372
+ */
373
+ isValidConfigValue(value) {
374
+ // Basic validation rules for security-sensitive configuration values
375
+ // Check for potentially dangerous characters (script injection prevention)
376
+ const dangerousChars = /[<>;"'`${}]/;
377
+ if (dangerousChars.test(value)) {
378
+ return false;
379
+ }
380
+ // Check for excessively long values (DoS prevention)
381
+ if (value.length > 500) {
382
+ return false;
383
+ }
384
+ // Check for null bytes (security)
385
+ if (value.includes("\0")) {
386
+ return false;
387
+ }
388
+ // Check for control characters except newlines and tabs
389
+ // Using String.fromCharCode to avoid ESLint control character error
390
+ const controlChars = new RegExp(`[${String.fromCharCode(0x00)}-${String.fromCharCode(0x08)}${String.fromCharCode(0x0b)}${String.fromCharCode(0x0c)}${String.fromCharCode(0x0e)}-${String.fromCharCode(0x1f)}${String.fromCharCode(0x7f)}]`);
391
+ if (controlChars.test(value)) {
392
+ return false;
393
+ }
394
+ return true;
395
+ }
396
+ /**
397
+ * Helper method to get configuration array with fallback
398
+ * Parses comma-separated environment variable values
399
+ */
400
+ getConfigArray(envVar, defaultValue) {
401
+ const envValue = process.env[envVar];
402
+ if (!envValue) {
403
+ return defaultValue;
404
+ }
405
+ return envValue
406
+ .split(",")
407
+ .map((item) => item.trim())
408
+ .filter(Boolean);
409
+ }
410
+ /**
411
+ * Helper method to parse float with fallback
412
+ */
413
+ parseFloat(value, defaultValue) {
414
+ if (!value) {
415
+ return defaultValue;
416
+ }
417
+ const parsed = Number.parseFloat(value);
418
+ return Number.isNaN(parsed) ? defaultValue : parsed;
419
+ }
420
+ /**
421
+ * Helper method to parse int with fallback
422
+ */
423
+ parseInt(value, defaultValue) {
424
+ if (!value) {
425
+ return defaultValue;
426
+ }
427
+ const parsed = Number.parseInt(value, 10);
428
+ return Number.isNaN(parsed) ? defaultValue : parsed;
429
+ }
430
+ /**
431
+ * Helper method to get configuration object with fallback
432
+ * Parses JSON environment variable values
433
+ */
434
+ getConfigObject(envVar, defaultValue) {
435
+ const envValue = process.env[envVar];
436
+ if (!envValue) {
437
+ return defaultValue;
438
+ }
439
+ try {
440
+ const parsed = JSON.parse(envValue);
441
+ return typeof parsed === "object" && parsed !== null
442
+ ? parsed
443
+ : defaultValue;
444
+ }
445
+ catch {
446
+ logger.warn(`Invalid JSON in environment variable ${envVar}, using default`);
447
+ return defaultValue;
448
+ }
449
+ }
450
+ /**
451
+ * Get provider configuration
452
+ */
453
+ getProviderConfig(provider) {
454
+ return this.configurations.get(provider) || null;
455
+ }
456
+ /**
457
+ * Get all provider configurations
458
+ */
459
+ getAllConfigurations() {
460
+ return new Map(this.configurations);
461
+ }
462
+ /**
463
+ * Update provider configuration (runtime updates)
464
+ */
465
+ updateProviderConfig(provider, config) {
466
+ this.configurations.set(provider, config);
467
+ this.lastUpdated = Date.now();
468
+ this.configSource = "dynamic";
469
+ logger.debug(`Updated configuration for provider: ${provider}`);
470
+ }
471
+ /**
472
+ * Load configurations from external source
473
+ */
474
+ loadConfigurationsFromFile(configPath) {
475
+ try {
476
+ // Check if file exists
477
+ if (!fs.existsSync(configPath)) {
478
+ throw new Error(`Configuration file not found: ${configPath}`);
479
+ }
480
+ // Read and parse configuration file
481
+ const configContent = fs.readFileSync(configPath, "utf8");
482
+ const fileExtension = path.extname(configPath).toLowerCase();
483
+ let configData;
484
+ if (fileExtension === ".json") {
485
+ configData = JSON.parse(configContent);
486
+ }
487
+ else if (fileExtension === ".yaml" || fileExtension === ".yml") {
488
+ // Basic YAML parsing for simple configurations
489
+ // For full YAML support, would need a proper YAML library
490
+ try {
491
+ configData = JSON.parse(configContent);
492
+ }
493
+ catch {
494
+ // Simple YAML-to-JSON conversion for basic cases
495
+ const yamlLines = configContent.split("\n");
496
+ const jsonObj = {};
497
+ for (const line of yamlLines) {
498
+ const trimmedLine = line.trim();
499
+ if (trimmedLine && !trimmedLine.startsWith("#")) {
500
+ const colonIndex = trimmedLine.indexOf(":");
501
+ if (colonIndex > 0) {
502
+ const key = trimmedLine.substring(0, colonIndex).trim();
503
+ const value = trimmedLine.substring(colonIndex + 1).trim();
504
+ // Basic type conversion
505
+ if (value === "true") {
506
+ jsonObj[key] = true;
507
+ }
508
+ else if (value === "false") {
509
+ jsonObj[key] = false;
510
+ }
511
+ else if (!isNaN(Number(value))) {
512
+ jsonObj[key] = Number(value);
513
+ }
514
+ else {
515
+ jsonObj[key] = value.replace(/['"]/g, "");
516
+ }
517
+ }
518
+ }
519
+ }
520
+ configData = jsonObj;
521
+ }
522
+ }
523
+ else {
524
+ throw new Error(`Unsupported configuration file format: ${fileExtension}. Supported formats: .json, .yaml, .yml`);
525
+ }
526
+ // Validate configuration structure
527
+ if (!configData || typeof configData !== "object") {
528
+ throw new Error("Invalid configuration format: must be an object");
529
+ }
530
+ const config = configData;
531
+ // Load provider configurations from file
532
+ if (config.providers && typeof config.providers === "object") {
533
+ const providers = config.providers;
534
+ for (const [providerName, providerConfig] of Object.entries(providers)) {
535
+ if (this.isValidProviderConfig(providerConfig)) {
536
+ this.configurations.set(providerName, providerConfig);
537
+ logger.debug(`Loaded configuration for provider: ${providerName}`);
538
+ }
539
+ else {
540
+ logger.warn(`Invalid configuration for provider: ${providerName}, skipping`);
541
+ }
542
+ }
543
+ }
544
+ // Load global model name overrides
545
+ if (config.modelNames && typeof config.modelNames === "object") {
546
+ const modelNames = config.modelNames;
547
+ this.applyModelNameOverrides(modelNames);
548
+ }
549
+ // Load global defaults
550
+ if (config.defaults && typeof config.defaults === "object") {
551
+ const defaults = config.defaults;
552
+ this.applyGlobalDefaults(defaults);
553
+ }
554
+ this.configSource = "file";
555
+ this.lastUpdated = Date.now();
556
+ logger.info(`Successfully loaded configurations from file: ${configPath}`);
557
+ logger.debug(`Loaded ${this.configurations.size} provider configurations`);
558
+ }
559
+ catch (error) {
560
+ const errorMessage = error instanceof Error ? error.message : String(error);
561
+ logger.error(`Failed to load configurations from file: ${errorMessage}`);
562
+ throw new Error(`Configuration loading failed: ${errorMessage}`);
563
+ }
564
+ }
565
+ /**
566
+ * Validate provider configuration structure
567
+ */
568
+ isValidProviderConfig(config) {
569
+ if (!config || typeof config !== "object") {
570
+ return false;
571
+ }
572
+ const providerConfig = config;
573
+ // Check required fields
574
+ const requiredFields = [
575
+ "provider",
576
+ "models",
577
+ "defaultCost",
578
+ "requiredEnvVars",
579
+ "performance",
580
+ ];
581
+ for (const field of requiredFields) {
582
+ if (!(field in providerConfig)) {
583
+ return false;
584
+ }
585
+ }
586
+ // Validate models structure
587
+ if (typeof providerConfig.models !== "object" || !providerConfig.models) {
588
+ return false;
589
+ }
590
+ const models = providerConfig.models;
591
+ const requiredTiers = ["fast", "balanced", "quality"];
592
+ for (const tier of requiredTiers) {
593
+ if (typeof models[tier] !== "string") {
594
+ return false;
595
+ }
596
+ }
597
+ // Validate defaultCost structure
598
+ if (typeof providerConfig.defaultCost !== "object" ||
599
+ !providerConfig.defaultCost) {
600
+ return false;
601
+ }
602
+ const defaultCost = providerConfig.defaultCost;
603
+ if (typeof defaultCost.input !== "number" ||
604
+ typeof defaultCost.output !== "number") {
605
+ return false;
606
+ }
607
+ // Validate requiredEnvVars
608
+ if (!Array.isArray(providerConfig.requiredEnvVars)) {
609
+ return false;
610
+ }
611
+ // Validate performance structure
612
+ if (typeof providerConfig.performance !== "object" ||
613
+ !providerConfig.performance) {
614
+ return false;
615
+ }
616
+ const performance = providerConfig.performance;
617
+ const perfFields = ["speed", "quality", "cost"];
618
+ for (const field of perfFields) {
619
+ if (typeof performance[field] !== "number") {
620
+ return false;
621
+ }
622
+ }
623
+ return true;
624
+ }
625
+ /**
626
+ * Apply model name overrides from configuration file
627
+ */
628
+ applyModelNameOverrides(modelNames) {
629
+ try {
630
+ // Apply overrides to existing configurations
631
+ for (const [providerKey, providerModels] of Object.entries(modelNames)) {
632
+ if (typeof providerModels === "object" && providerModels) {
633
+ const models = providerModels;
634
+ const existingConfig = this.configurations.get(providerKey);
635
+ if (existingConfig) {
636
+ // Update existing configuration
637
+ for (const [tier, model] of Object.entries(models)) {
638
+ if (typeof model === "string" &&
639
+ ["fast", "balanced", "quality"].includes(tier)) {
640
+ existingConfig.models[tier] = model;
641
+ }
642
+ }
643
+ this.configurations.set(providerKey, existingConfig);
644
+ logger.debug(`Applied model name overrides for provider: ${providerKey}`);
645
+ }
646
+ }
647
+ }
648
+ }
649
+ catch (error) {
650
+ logger.warn(`Failed to apply model name overrides: ${error instanceof Error ? error.message : String(error)}`);
651
+ }
652
+ }
653
+ /**
654
+ * Apply global default configurations
655
+ */
656
+ applyGlobalDefaults(defaults) {
657
+ try {
658
+ // Apply global defaults to all existing configurations
659
+ for (const [providerName, config] of this.configurations.entries()) {
660
+ let updated = false;
661
+ // Apply default cost overrides
662
+ if (defaults.defaultCost && typeof defaults.defaultCost === "object") {
663
+ const defaultCost = defaults.defaultCost;
664
+ if (typeof defaultCost.input === "number") {
665
+ config.defaultCost.input = defaultCost.input;
666
+ updated = true;
667
+ }
668
+ if (typeof defaultCost.output === "number") {
669
+ config.defaultCost.output = defaultCost.output;
670
+ updated = true;
671
+ }
672
+ }
673
+ // Apply performance overrides
674
+ if (defaults.performance && typeof defaults.performance === "object") {
675
+ const performance = defaults.performance;
676
+ ["speed", "quality", "cost"].forEach((field) => {
677
+ if (typeof performance[field] === "number") {
678
+ config.performance[field] =
679
+ performance[field];
680
+ updated = true;
681
+ }
682
+ });
683
+ }
684
+ if (updated) {
685
+ this.configurations.set(providerName, config);
686
+ logger.debug(`Applied global defaults to provider: ${providerName}`);
687
+ }
688
+ }
689
+ }
690
+ catch (error) {
691
+ logger.warn(`Failed to apply global defaults: ${error instanceof Error ? error.message : String(error)}`);
692
+ }
693
+ }
694
+ /**
695
+ * Get configuration metadata
696
+ */
697
+ getConfigurationMeta() {
698
+ return {
699
+ source: this.configSource,
700
+ lastUpdated: this.lastUpdated,
701
+ providerCount: this.configurations.size,
702
+ };
703
+ }
704
+ /**
705
+ * Get model for specific tier and provider
706
+ */
707
+ getModelForTier(provider, tier) {
708
+ const config = this.getProviderConfig(provider);
709
+ return config?.models[tier] || null;
710
+ }
711
+ /**
712
+ * Get cost information for provider and model
713
+ */
714
+ getCostInfo(provider, model) {
715
+ const config = this.getProviderConfig(provider);
716
+ if (!config) {
717
+ return null;
718
+ }
719
+ // If specific model config exists, use it; otherwise use default
720
+ if (model && config.modelConfigs?.[model]) {
721
+ return config.modelConfigs[model].cost;
722
+ }
723
+ return config.defaultCost;
724
+ }
725
+ /**
726
+ * Check if provider is available (has required environment variables)
727
+ */
728
+ isProviderAvailable(provider) {
729
+ const config = this.getProviderConfig(provider);
730
+ if (!config) {
731
+ return false;
732
+ }
733
+ if (config.requiredEnvVars.length === 0) {
734
+ return true; // No requirements (e.g., Ollama)
735
+ }
736
+ return config.requiredEnvVars.some((envVar) => Boolean(process.env[envVar]));
737
+ }
738
+ /**
739
+ * Get available providers
740
+ */
741
+ getAvailableProviders() {
742
+ return Array.from(this.configurations.values()).filter((config) => this.isProviderAvailable(config.provider));
743
+ }
744
+ }
745
+ /**
746
+ * Global instance accessor
747
+ */
748
+ export const modelConfig = ModelConfigurationManager.getInstance();
749
+ /**
750
+ * Convenience functions for common operations
751
+ */
752
+ /**
753
+ * Get provider configuration (backwards compatible)
754
+ */
755
+ export function getProviderConfig(provider) {
756
+ return modelConfig.getProviderConfig(provider);
757
+ }
758
+ /**
759
+ * Get model for tier (backwards compatible)
760
+ */
761
+ export function getModelForTier(provider, tier) {
762
+ return modelConfig.getModelForTier(provider, tier);
763
+ }
764
+ /**
765
+ * Get cost information (backwards compatible)
766
+ */
767
+ export function getCostInfo(provider, model) {
768
+ return modelConfig.getCostInfo(provider, model);
769
+ }
770
+ /**
771
+ * Check provider availability (backwards compatible)
772
+ */
773
+ export function isProviderAvailable(provider) {
774
+ return modelConfig.isProviderAvailable(provider);
775
+ }