@juspay/neurolink 1.9.0 → 1.10.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 CHANGED
@@ -1,3 +1,10 @@
1
+ # [1.10.0](https://github.com/juspay/neurolink/compare/v1.9.0...v1.10.0) (2025-06-21)
2
+
3
+
4
+ ### Features
5
+
6
+ * **cli:** improve provider status accuracy and error handling ([523e845](https://github.com/juspay/neurolink/commit/523e84566fee5d9afa3638186f90c628e20e4894))
7
+
1
8
  # 1.9.0 (2025-06-20)
2
9
 
3
10
 
package/README.md CHANGED
@@ -201,7 +201,7 @@ cd neurolink-demo && node server.js
201
201
  ### 🖥️ CLI Demonstrations
202
202
 
203
203
  - **[CLI Help & Commands](./docs/visual-content/cli-videos/cli-01-cli-help.mp4)** - Complete command reference
204
- - **[Provider Status Check](./docs/visual-content/cli-videos/cli-02-provider-status.mp4)** - Connectivity verification
204
+ - **[Provider Status Check](./docs/visual-content/cli-videos/cli-02-provider-status.mp4)** - Connectivity verification (now with authentication and model availability checks)
205
205
  - **[Text Generation](./docs/visual-content/cli-videos/cli-03-text-generation.mp4)** - Real AI content creation
206
206
 
207
207
  ### 🌐 Web Interface Videos
package/dist/cli/index.js CHANGED
@@ -595,22 +595,102 @@ const cli = yargs(args)
595
595
  "ollama",
596
596
  "mistral",
597
597
  ];
598
+ // Import hasProviderEnvVars to check environment variables
599
+ const { hasProviderEnvVars } = await import("../lib/utils/providerUtils.js");
598
600
  const results = [];
599
601
  for (const p of providers) {
600
602
  if (spinner) {
601
603
  spinner.text = `Testing ${p}...`;
602
604
  }
605
+ // First check if provider has env vars configured
606
+ const hasEnvVars = hasProviderEnvVars(p);
607
+ if (!hasEnvVars && p !== "ollama") {
608
+ // No env vars, don't even try to test
609
+ results.push({
610
+ provider: p,
611
+ status: "not-configured",
612
+ configured: false,
613
+ error: "Missing required environment variables",
614
+ });
615
+ if (spinner) {
616
+ spinner.fail(`${p}: ${chalk.gray("⚪ Not configured")} - Missing environment variables`);
617
+ }
618
+ else if (!argv.quiet) {
619
+ console.log(`${p}: ${chalk.gray("⚪ Not configured")} - Missing environment variables`);
620
+ }
621
+ continue;
622
+ }
623
+ // Special handling for Ollama
624
+ if (p === "ollama") {
625
+ try {
626
+ // First, check if the service is running
627
+ const serviceResponse = await fetch('http://localhost:11434/api/tags', {
628
+ method: 'GET',
629
+ signal: AbortSignal.timeout(2000)
630
+ });
631
+ if (!serviceResponse.ok) {
632
+ throw new Error("Ollama service not responding");
633
+ }
634
+ // Service is running, now check if the default model is available
635
+ const { models } = await serviceResponse.json();
636
+ const defaultOllamaModel = "llama3.2:latest";
637
+ const modelIsAvailable = models.some((m) => m.name === defaultOllamaModel);
638
+ if (modelIsAvailable) {
639
+ results.push({
640
+ provider: p,
641
+ status: "working",
642
+ configured: true,
643
+ authenticated: true,
644
+ responseTime: 0,
645
+ });
646
+ if (spinner) {
647
+ spinner.succeed(`${p}: ${chalk.green("✅ Working")} - Service running and model '${defaultOllamaModel}' is available.`);
648
+ }
649
+ }
650
+ else {
651
+ results.push({
652
+ provider: p,
653
+ status: "failed",
654
+ configured: true,
655
+ authenticated: false,
656
+ error: `Ollama service is running, but model '${defaultOllamaModel}' is not found. Please run 'ollama pull ${defaultOllamaModel}'.`,
657
+ });
658
+ if (spinner) {
659
+ spinner.fail(`${p}: ${chalk.red("❌ Model Not Found")} - Run 'ollama pull ${defaultOllamaModel}'`);
660
+ }
661
+ }
662
+ }
663
+ catch (error) {
664
+ results.push({
665
+ provider: p,
666
+ status: "failed",
667
+ configured: false,
668
+ authenticated: false,
669
+ error: "Ollama is not running. Please start with: ollama serve",
670
+ });
671
+ if (spinner) {
672
+ spinner.fail(`${p}: ${chalk.red("❌ Failed")} - Service not running`);
673
+ }
674
+ }
675
+ continue;
676
+ }
677
+ // Provider has env vars, now test authentication
603
678
  try {
604
679
  const start = Date.now();
605
- await sdk.generateText({
680
+ // Import AIProviderFactory to test providers directly without fallback
681
+ const { AIProviderFactory } = await import("../lib/core/factory.js");
682
+ // Create and test provider directly to avoid automatic fallback
683
+ const provider = await AIProviderFactory.createProvider(p);
684
+ await provider.generateText({
606
685
  prompt: "test",
607
- provider: p,
608
686
  maxTokens: 1,
609
687
  });
610
688
  const duration = Date.now() - start;
611
689
  results.push({
612
690
  provider: p,
613
691
  status: "working",
692
+ configured: true,
693
+ authenticated: true,
614
694
  responseTime: duration,
615
695
  });
616
696
  if (spinner) {
@@ -621,25 +701,41 @@ const cli = yargs(args)
621
701
  }
622
702
  }
623
703
  catch (error) {
704
+ const errorMsg = error.message;
705
+ let authStatus = false;
706
+ let statusText = "Failed";
707
+ // Check if it's an authentication error
708
+ if (errorMsg.includes('401') || errorMsg.includes('Unauthorized') ||
709
+ errorMsg.includes('Invalid API') || errorMsg.includes('Authentication') ||
710
+ errorMsg.includes('API key') || errorMsg.includes('not authorized') ||
711
+ errorMsg.includes('ExpiredToken') || errorMsg.includes('expired')) {
712
+ statusText = "Invalid credentials";
713
+ }
714
+ else if (errorMsg.includes('fetch') || errorMsg.includes('blob')) {
715
+ statusText = "Service error";
716
+ }
624
717
  results.push({
625
718
  provider: p,
626
719
  status: "failed",
627
- error: error.message,
720
+ configured: true,
721
+ authenticated: authStatus,
722
+ error: errorMsg,
628
723
  });
629
724
  if (spinner) {
630
- spinner.fail(`${p}: ${chalk.red("❌ Failed")} - ${error.message.split("\n")[0]}`);
725
+ spinner.fail(`${p}: ${chalk.red("❌ " + statusText)} - ${errorMsg.split("\n")[0]}`);
631
726
  }
632
727
  else if (!argv.quiet) {
633
- console.error(`${p}: ${chalk.red("❌ Failed")} - ${error.message.split("\n")[0]}`);
728
+ console.error(`${p}: ${chalk.red("❌ " + statusText)} - ${errorMsg.split("\n")[0]}`);
634
729
  }
635
730
  }
636
731
  }
637
732
  const working = results.filter((r) => r.status === "working").length;
733
+ const configured = results.filter((r) => r.configured).length;
638
734
  if (spinner) {
639
- spinner.info(chalk.blue(`\n📊 Summary: ${working}/${results.length} providers working`));
735
+ spinner.info(chalk.blue(`\n📊 Summary: ${working}/${results.length} providers working, ${configured}/${results.length} configured`));
640
736
  }
641
737
  else if (!argv.quiet) {
642
- console.log(chalk.blue(`\n📊 Summary: ${working}/${results.length} providers working`));
738
+ console.log(chalk.blue(`\n📊 Summary: ${working}/${results.length} providers working, ${configured}/${results.length} configured`));
643
739
  }
644
740
  if (argv.verbose && !argv.quiet) {
645
741
  console.log(chalk.blue("\n📋 Detailed Results:"));
@@ -1,5 +1,5 @@
1
1
  import { GoogleVertexAI, AmazonBedrock, OpenAI, AnthropicProvider, AzureOpenAIProvider, GoogleAIStudio, HuggingFace, Ollama, MistralAI, } from "../providers/index.js";
2
- import { getBestProvider } from "../utils/providerUtils.js";
2
+ import { getBestProviderSync } from "../utils/providerUtils.js";
3
3
  import { logger } from "../utils/logger.js";
4
4
  import { dynamicModelProvider } from "./dynamic-models.js";
5
5
  const componentIdentifier = "aiProviderFactory";
@@ -233,7 +233,7 @@ export class AIProviderFactory {
233
233
  static async createBestProvider(requestedProvider, modelName, enableMCP = true) {
234
234
  const functionTag = "AIProviderFactory.createBestProvider";
235
235
  try {
236
- const bestProvider = getBestProvider(requestedProvider);
236
+ const bestProvider = getBestProviderSync(requestedProvider);
237
237
  logger.debug(`[${functionTag}] Best provider selected`, {
238
238
  requestedProvider: requestedProvider || "auto",
239
239
  selectedProvider: bestProvider,
package/dist/index.d.ts CHANGED
@@ -13,7 +13,7 @@ export { BedrockModels, OpenAIModels, VertexModels, DEFAULT_PROVIDER_CONFIGS, }
13
13
  export { GoogleVertexAI, AmazonBedrock, OpenAI, AnthropicProvider, AzureOpenAIProvider, } from "./providers/index.js";
14
14
  export type { ProviderName } from "./providers/index.js";
15
15
  export { PROVIDERS, AVAILABLE_PROVIDERS } from "./providers/index.js";
16
- export { getBestProvider, getAvailableProviders, isValidProvider, } from "./utils/providerUtils.js";
16
+ export { getBestProviderSync as getBestProvider, getAvailableProviders, isValidProvider, } from "./utils/providerUtils.js";
17
17
  export { NeuroLink } from "./neurolink.js";
18
18
  export type { TextGenerationOptions, StreamTextOptions, TextGenerationResult, } from "./neurolink.js";
19
19
  export declare const VERSION = "1.0.0";
package/dist/index.js CHANGED
@@ -15,7 +15,7 @@ export { BedrockModels, OpenAIModels, VertexModels, DEFAULT_PROVIDER_CONFIGS, }
15
15
  export { GoogleVertexAI, AmazonBedrock, OpenAI, AnthropicProvider, AzureOpenAIProvider, } from "./providers/index.js";
16
16
  export { PROVIDERS, AVAILABLE_PROVIDERS } from "./providers/index.js";
17
17
  // Utility exports
18
- export { getBestProvider, getAvailableProviders, isValidProvider, } from "./utils/providerUtils.js";
18
+ export { getBestProviderSync as getBestProvider, getAvailableProviders, isValidProvider, } from "./utils/providerUtils.js";
19
19
  // Main NeuroLink wrapper class
20
20
  export { NeuroLink } from "./neurolink.js";
21
21
  // Version
@@ -1,5 +1,5 @@
1
1
  import { GoogleVertexAI, AmazonBedrock, OpenAI, AnthropicProvider, AzureOpenAIProvider, GoogleAIStudio, HuggingFace, Ollama, MistralAI, } from "../providers/index.js";
2
- import { getBestProvider } from "../utils/providerUtils.js";
2
+ import { getBestProviderSync } from "../utils/providerUtils.js";
3
3
  import { logger } from "../utils/logger.js";
4
4
  import { dynamicModelProvider } from "./dynamic-models.js";
5
5
  const componentIdentifier = "aiProviderFactory";
@@ -233,7 +233,7 @@ export class AIProviderFactory {
233
233
  static async createBestProvider(requestedProvider, modelName, enableMCP = true) {
234
234
  const functionTag = "AIProviderFactory.createBestProvider";
235
235
  try {
236
- const bestProvider = getBestProvider(requestedProvider);
236
+ const bestProvider = getBestProviderSync(requestedProvider);
237
237
  logger.debug(`[${functionTag}] Best provider selected`, {
238
238
  requestedProvider: requestedProvider || "auto",
239
239
  selectedProvider: bestProvider,
@@ -13,7 +13,7 @@ export { BedrockModels, OpenAIModels, VertexModels, DEFAULT_PROVIDER_CONFIGS, }
13
13
  export { GoogleVertexAI, AmazonBedrock, OpenAI, AnthropicProvider, AzureOpenAIProvider, } from "./providers/index.js";
14
14
  export type { ProviderName } from "./providers/index.js";
15
15
  export { PROVIDERS, AVAILABLE_PROVIDERS } from "./providers/index.js";
16
- export { getBestProvider, getAvailableProviders, isValidProvider, } from "./utils/providerUtils.js";
16
+ export { getBestProviderSync as getBestProvider, getAvailableProviders, isValidProvider, } from "./utils/providerUtils.js";
17
17
  export { NeuroLink } from "./neurolink.js";
18
18
  export type { TextGenerationOptions, StreamTextOptions, TextGenerationResult, } from "./neurolink.js";
19
19
  export declare const VERSION = "1.0.0";
package/dist/lib/index.js CHANGED
@@ -15,7 +15,7 @@ export { BedrockModels, OpenAIModels, VertexModels, DEFAULT_PROVIDER_CONFIGS, }
15
15
  export { GoogleVertexAI, AmazonBedrock, OpenAI, AnthropicProvider, AzureOpenAIProvider, } from "./providers/index.js";
16
16
  export { PROVIDERS, AVAILABLE_PROVIDERS } from "./providers/index.js";
17
17
  // Utility exports
18
- export { getBestProvider, getAvailableProviders, isValidProvider, } from "./utils/providerUtils.js";
18
+ export { getBestProviderSync as getBestProvider, getAvailableProviders, isValidProvider, } from "./utils/providerUtils.js";
19
19
  // Main NeuroLink wrapper class
20
20
  export { NeuroLink } from "./neurolink.js";
21
21
  // Version
@@ -149,8 +149,16 @@ export declare class MCPToolRegistry {
149
149
  private updateExecutionMetrics;
150
150
  }
151
151
  /**
152
- * Default registry instance
153
- * Can be used across the application for consistent tool management
152
+ * Get the default singleton instance of the MCPToolRegistry.
153
+ * This function ensures that the registry is only instantiated once.
154
+ * Using a function getter helps prevent circular dependency issues during module initialization.
155
+ *
156
+ * @returns The singleton MCPToolRegistry instance.
157
+ */
158
+ export declare function getDefaultToolRegistry(): MCPToolRegistry;
159
+ /**
160
+ * @deprecated The direct export `defaultToolRegistry` is deprecated and will be removed.
161
+ * Please use `getDefaultToolRegistry()` instead to avoid circular dependency issues.
154
162
  */
155
163
  export declare const defaultToolRegistry: MCPToolRegistry;
156
164
  /**
@@ -350,14 +350,32 @@ export class MCPToolRegistry {
350
350
  * Default registry instance
351
351
  * Can be used across the application for consistent tool management
352
352
  */
353
- export const defaultToolRegistry = new MCPToolRegistry();
353
+ let defaultRegistryInstance = null;
354
+ /**
355
+ * Get the default singleton instance of the MCPToolRegistry.
356
+ * This function ensures that the registry is only instantiated once.
357
+ * Using a function getter helps prevent circular dependency issues during module initialization.
358
+ *
359
+ * @returns The singleton MCPToolRegistry instance.
360
+ */
361
+ export function getDefaultToolRegistry() {
362
+ if (!defaultRegistryInstance) {
363
+ defaultRegistryInstance = new MCPToolRegistry();
364
+ }
365
+ return defaultRegistryInstance;
366
+ }
367
+ /**
368
+ * @deprecated The direct export `defaultToolRegistry` is deprecated and will be removed.
369
+ * Please use `getDefaultToolRegistry()` instead to avoid circular dependency issues.
370
+ */
371
+ export const defaultToolRegistry = getDefaultToolRegistry();
354
372
  /**
355
373
  * Utility function to register server with default registry
356
374
  *
357
375
  * @param server MCP server to register
358
376
  */
359
377
  export async function registerServer(server) {
360
- return defaultToolRegistry.registerServer(server);
378
+ return getDefaultToolRegistry().registerServer(server);
361
379
  }
362
380
  /**
363
381
  * Utility function to execute tool with default registry
@@ -369,7 +387,7 @@ export async function registerServer(server) {
369
387
  * @returns Tool execution result
370
388
  */
371
389
  export async function executeTool(toolName, params, context, options) {
372
- return defaultToolRegistry.executeTool(toolName, params, context, options);
390
+ return getDefaultToolRegistry().executeTool(toolName, params, context, options);
373
391
  }
374
392
  /**
375
393
  * Utility function to list tools with default registry
@@ -378,5 +396,5 @@ export async function executeTool(toolName, params, context, options) {
378
396
  * @returns Array of tool information
379
397
  */
380
398
  export function listTools(criteria) {
381
- return defaultToolRegistry.listTools(criteria);
399
+ return getDefaultToolRegistry().listTools(criteria);
382
400
  }
@@ -5,7 +5,7 @@
5
5
  */
6
6
  import { z } from "zod";
7
7
  import { AIProviderFactory } from "../../../core/factory.js";
8
- import { getBestProvider, getAvailableProviders, } from "../../../utils/providerUtils.js";
8
+ import { getBestProviderSync as getBestProvider, getAvailableProviders, } from "../../../utils/providerUtils.js";
9
9
  /**
10
10
  * Input Schemas for AI Analysis Tools
11
11
  */
@@ -6,7 +6,7 @@
6
6
  import { z } from "zod";
7
7
  import { createMCPServer } from "../../factory.js";
8
8
  import { AIProviderFactory } from "../../../core/factory.js";
9
- import { getBestProvider, getAvailableProviders, } from "../../../utils/providerUtils.js";
9
+ import { getBestProviderSync as getBestProvider, getAvailableProviders, } from "../../../utils/providerUtils.js";
10
10
  import { logger } from "../../../utils/logger.js";
11
11
  import { analyzeAIUsageTool, benchmarkProviderPerformanceTool, optimizePromptParametersTool, } from "./ai-analysis-tools.js";
12
12
  import { generateTestCasesTool, refactorCodeTool, generateDocumentationTool, debugAIOutputTool, } from "./ai-workflow-tools.js";
@@ -4,7 +4,7 @@
4
4
  */
5
5
  import { z } from "zod";
6
6
  import { AIProviderFactory } from "../../../core/factory.js";
7
- import { getBestProvider } from "../../../utils/providerUtils.js";
7
+ import { getBestProviderSync as getBestProvider } from "../../../utils/providerUtils.js";
8
8
  // Tool-specific schemas with comprehensive validation
9
9
  const generateTestCasesSchema = z.object({
10
10
  codeFunction: z
@@ -8,10 +8,10 @@
8
8
  import { AIProviderFactory } from "./index.js";
9
9
  import { ContextManager } from "./mcp/context-manager.js";
10
10
  import { mcpLogger } from "./mcp/logging.js";
11
- import { defaultToolRegistry } from "./mcp/registry.js";
11
+ import { getDefaultToolRegistry } from "./mcp/registry.js";
12
12
  import { defaultUnifiedRegistry } from "./mcp/unified-registry.js";
13
13
  import { logger } from "./utils/logger.js";
14
- import { getBestProvider } from "./utils/providerUtils.js";
14
+ import { getBestProvider } from "./utils/providerUtils-fixed.js";
15
15
  export class NeuroLink {
16
16
  mcpInitialized = false;
17
17
  contextManager;
@@ -108,8 +108,8 @@ export class NeuroLink {
108
108
  // Get available tools from default registry (simplified approach)
109
109
  let availableTools = [];
110
110
  try {
111
- // Use defaultToolRegistry directly instead of unified registry to avoid hanging
112
- const allTools = defaultToolRegistry.listTools();
111
+ // Use getDefaultToolRegistry() to avoid circular dependencies
112
+ const allTools = getDefaultToolRegistry().listTools();
113
113
  availableTools = allTools.map((tool) => ({
114
114
  name: tool.name,
115
115
  description: tool.description || "No description available",
@@ -119,6 +119,12 @@ class OllamaLanguageModel {
119
119
  });
120
120
  clearTimeout(timeoutId);
121
121
  if (!response.ok) {
122
+ if (response.status === 404) {
123
+ const errorData = await response.json();
124
+ if (errorData.error && errorData.error.includes("not found")) {
125
+ throw new Error(`Model '${this.modelId}' not found. Please run 'ollama pull ${this.modelId}'`);
126
+ }
127
+ }
122
128
  throw new Error(`Ollama API error: ${response.status} ${response.statusText}`);
123
129
  }
124
130
  const data = (await response.json());
@@ -183,6 +189,12 @@ class OllamaLanguageModel {
183
189
  });
184
190
  clearTimeout(timeoutId);
185
191
  if (!response.ok) {
192
+ if (response.status === 404) {
193
+ const errorData = await response.json();
194
+ if (errorData.error && errorData.error.includes("not found")) {
195
+ throw new Error(`Model '${this.modelId}' not found. Please run 'ollama pull ${this.modelId}'`);
196
+ }
197
+ }
186
198
  throw new Error(`Ollama API error: ${response.status} ${response.statusText}`);
187
199
  }
188
200
  if (!response.body) {
@@ -400,6 +412,9 @@ export class Ollama {
400
412
  });
401
413
  }
402
414
  const result = await generateText(generateOptions);
415
+ if (result.text.includes("model not found")) {
416
+ throw new Error(`Model '${this.modelName}' not found. Please run 'ollama pull ${this.modelName}'`);
417
+ }
403
418
  logger.debug(`[${functionTag}] Generate text completed`, {
404
419
  provider,
405
420
  modelName: this.modelName,
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Asynchronously get the best available provider based on real-time checks.
3
+ * This function performs actual authentication and availability tests.
4
+ *
5
+ * @param requestedProvider - Optional preferred provider name
6
+ * @returns The best provider name to use
7
+ */
8
+ export declare function getBestProvider(requestedProvider?: string): Promise<string>;
@@ -0,0 +1,75 @@
1
+ import { AIProviderFactory } from '../core/factory.js';
2
+ import { logger } from './logger.js';
3
+ import { hasProviderEnvVars } from './providerUtils.js';
4
+ /**
5
+ * Asynchronously get the best available provider based on real-time checks.
6
+ * This function performs actual authentication and availability tests.
7
+ *
8
+ * @param requestedProvider - Optional preferred provider name
9
+ * @returns The best provider name to use
10
+ */
11
+ export async function getBestProvider(requestedProvider) {
12
+ const providers = [
13
+ "google-ai",
14
+ "anthropic",
15
+ "openai",
16
+ "mistral",
17
+ "vertex",
18
+ "azure",
19
+ "huggingface",
20
+ "bedrock",
21
+ "ollama",
22
+ ];
23
+ if (requestedProvider && requestedProvider !== 'auto') {
24
+ if (await isProviderAvailable(requestedProvider)) {
25
+ logger.debug(`[getBestProvider] Using requested provider: ${requestedProvider}`);
26
+ return requestedProvider;
27
+ }
28
+ else {
29
+ logger.warn(`[getBestProvider] Requested provider '${requestedProvider}' is not available. Falling back to auto-selection.`);
30
+ }
31
+ }
32
+ for (const provider of providers) {
33
+ if (await isProviderAvailable(provider)) {
34
+ logger.debug(`[getBestProvider] Selected provider: ${provider}`);
35
+ return provider;
36
+ }
37
+ }
38
+ throw new Error("No available AI providers. Please check your configurations.");
39
+ }
40
+ /**
41
+ * Check if a provider is truly available by performing a quick authentication test.
42
+ *
43
+ * @param providerName - The name of the provider to check.
44
+ * @returns True if the provider is available and authenticated.
45
+ */
46
+ async function isProviderAvailable(providerName) {
47
+ if (!hasProviderEnvVars(providerName) && providerName !== 'ollama') {
48
+ return false;
49
+ }
50
+ if (providerName === 'ollama') {
51
+ try {
52
+ const response = await fetch('http://localhost:11434/api/tags', {
53
+ method: 'GET',
54
+ signal: AbortSignal.timeout(2000)
55
+ });
56
+ if (response.ok) {
57
+ const { models } = await response.json();
58
+ const defaultOllamaModel = "llama3.2:latest";
59
+ return models.some((m) => m.name === defaultOllamaModel);
60
+ }
61
+ return false;
62
+ }
63
+ catch (error) {
64
+ return false;
65
+ }
66
+ }
67
+ try {
68
+ const provider = await AIProviderFactory.createProvider(providerName);
69
+ await provider.generateText({ prompt: "test", maxTokens: 1 });
70
+ return true;
71
+ }
72
+ catch (error) {
73
+ return false;
74
+ }
75
+ }
@@ -3,7 +3,14 @@
3
3
  * @param requestedProvider - Optional preferred provider name
4
4
  * @returns The best provider name to use
5
5
  */
6
- export declare function getBestProvider(requestedProvider?: string): string;
6
+ export declare function getBestProviderSync(requestedProvider?: string): string;
7
+ /**
8
+ * Check if a provider has the minimum required environment variables
9
+ * NOTE: This only checks if variables exist, not if they're valid
10
+ * @param provider - Provider name to check
11
+ * @returns True if the provider has required environment variables
12
+ */
13
+ export declare function hasProviderEnvVars(provider: string): boolean;
7
14
  /**
8
15
  * Get available provider names
9
16
  * @returns Array of available provider names
@@ -7,7 +7,7 @@ import { logger } from "./logger.js";
7
7
  * @param requestedProvider - Optional preferred provider name
8
8
  * @returns The best provider name to use
9
9
  */
10
- export function getBestProvider(requestedProvider) {
10
+ export function getBestProviderSync(requestedProvider) {
11
11
  // If a specific provider is requested, return it
12
12
  if (requestedProvider) {
13
13
  return requestedProvider;
@@ -42,6 +42,15 @@ export function getBestProvider(requestedProvider) {
42
42
  * @returns True if the provider appears to be configured
43
43
  */
44
44
  function isProviderConfigured(provider) {
45
+ return hasProviderEnvVars(provider);
46
+ }
47
+ /**
48
+ * Check if a provider has the minimum required environment variables
49
+ * NOTE: This only checks if variables exist, not if they're valid
50
+ * @param provider - Provider name to check
51
+ * @returns True if the provider has required environment variables
52
+ */
53
+ export function hasProviderEnvVars(provider) {
45
54
  switch (provider.toLowerCase()) {
46
55
  case "bedrock":
47
56
  case "amazon":
@@ -149,8 +149,16 @@ export declare class MCPToolRegistry {
149
149
  private updateExecutionMetrics;
150
150
  }
151
151
  /**
152
- * Default registry instance
153
- * Can be used across the application for consistent tool management
152
+ * Get the default singleton instance of the MCPToolRegistry.
153
+ * This function ensures that the registry is only instantiated once.
154
+ * Using a function getter helps prevent circular dependency issues during module initialization.
155
+ *
156
+ * @returns The singleton MCPToolRegistry instance.
157
+ */
158
+ export declare function getDefaultToolRegistry(): MCPToolRegistry;
159
+ /**
160
+ * @deprecated The direct export `defaultToolRegistry` is deprecated and will be removed.
161
+ * Please use `getDefaultToolRegistry()` instead to avoid circular dependency issues.
154
162
  */
155
163
  export declare const defaultToolRegistry: MCPToolRegistry;
156
164
  /**
@@ -350,14 +350,32 @@ export class MCPToolRegistry {
350
350
  * Default registry instance
351
351
  * Can be used across the application for consistent tool management
352
352
  */
353
- export const defaultToolRegistry = new MCPToolRegistry();
353
+ let defaultRegistryInstance = null;
354
+ /**
355
+ * Get the default singleton instance of the MCPToolRegistry.
356
+ * This function ensures that the registry is only instantiated once.
357
+ * Using a function getter helps prevent circular dependency issues during module initialization.
358
+ *
359
+ * @returns The singleton MCPToolRegistry instance.
360
+ */
361
+ export function getDefaultToolRegistry() {
362
+ if (!defaultRegistryInstance) {
363
+ defaultRegistryInstance = new MCPToolRegistry();
364
+ }
365
+ return defaultRegistryInstance;
366
+ }
367
+ /**
368
+ * @deprecated The direct export `defaultToolRegistry` is deprecated and will be removed.
369
+ * Please use `getDefaultToolRegistry()` instead to avoid circular dependency issues.
370
+ */
371
+ export const defaultToolRegistry = getDefaultToolRegistry();
354
372
  /**
355
373
  * Utility function to register server with default registry
356
374
  *
357
375
  * @param server MCP server to register
358
376
  */
359
377
  export async function registerServer(server) {
360
- return defaultToolRegistry.registerServer(server);
378
+ return getDefaultToolRegistry().registerServer(server);
361
379
  }
362
380
  /**
363
381
  * Utility function to execute tool with default registry
@@ -369,7 +387,7 @@ export async function registerServer(server) {
369
387
  * @returns Tool execution result
370
388
  */
371
389
  export async function executeTool(toolName, params, context, options) {
372
- return defaultToolRegistry.executeTool(toolName, params, context, options);
390
+ return getDefaultToolRegistry().executeTool(toolName, params, context, options);
373
391
  }
374
392
  /**
375
393
  * Utility function to list tools with default registry
@@ -378,5 +396,5 @@ export async function executeTool(toolName, params, context, options) {
378
396
  * @returns Array of tool information
379
397
  */
380
398
  export function listTools(criteria) {
381
- return defaultToolRegistry.listTools(criteria);
399
+ return getDefaultToolRegistry().listTools(criteria);
382
400
  }
@@ -5,7 +5,7 @@
5
5
  */
6
6
  import { z } from "zod";
7
7
  import { AIProviderFactory } from "../../../core/factory.js";
8
- import { getBestProvider, getAvailableProviders, } from "../../../utils/providerUtils.js";
8
+ import { getBestProviderSync as getBestProvider, getAvailableProviders, } from "../../../utils/providerUtils.js";
9
9
  /**
10
10
  * Input Schemas for AI Analysis Tools
11
11
  */
@@ -6,7 +6,7 @@
6
6
  import { z } from "zod";
7
7
  import { createMCPServer } from "../../factory.js";
8
8
  import { AIProviderFactory } from "../../../core/factory.js";
9
- import { getBestProvider, getAvailableProviders, } from "../../../utils/providerUtils.js";
9
+ import { getBestProviderSync as getBestProvider, getAvailableProviders, } from "../../../utils/providerUtils.js";
10
10
  import { logger } from "../../../utils/logger.js";
11
11
  import { analyzeAIUsageTool, benchmarkProviderPerformanceTool, optimizePromptParametersTool, } from "./ai-analysis-tools.js";
12
12
  import { generateTestCasesTool, refactorCodeTool, generateDocumentationTool, debugAIOutputTool, } from "./ai-workflow-tools.js";
@@ -4,7 +4,7 @@
4
4
  */
5
5
  import { z } from "zod";
6
6
  import { AIProviderFactory } from "../../../core/factory.js";
7
- import { getBestProvider } from "../../../utils/providerUtils.js";
7
+ import { getBestProviderSync as getBestProvider } from "../../../utils/providerUtils.js";
8
8
  // Tool-specific schemas with comprehensive validation
9
9
  const generateTestCasesSchema = z.object({
10
10
  codeFunction: z
package/dist/neurolink.js CHANGED
@@ -8,10 +8,10 @@
8
8
  import { AIProviderFactory } from "./index.js";
9
9
  import { ContextManager } from "./mcp/context-manager.js";
10
10
  import { mcpLogger } from "./mcp/logging.js";
11
- import { defaultToolRegistry } from "./mcp/registry.js";
11
+ import { getDefaultToolRegistry } from "./mcp/registry.js";
12
12
  import { defaultUnifiedRegistry } from "./mcp/unified-registry.js";
13
13
  import { logger } from "./utils/logger.js";
14
- import { getBestProvider } from "./utils/providerUtils.js";
14
+ import { getBestProvider } from "./utils/providerUtils-fixed.js";
15
15
  export class NeuroLink {
16
16
  mcpInitialized = false;
17
17
  contextManager;
@@ -108,8 +108,8 @@ export class NeuroLink {
108
108
  // Get available tools from default registry (simplified approach)
109
109
  let availableTools = [];
110
110
  try {
111
- // Use defaultToolRegistry directly instead of unified registry to avoid hanging
112
- const allTools = defaultToolRegistry.listTools();
111
+ // Use getDefaultToolRegistry() to avoid circular dependencies
112
+ const allTools = getDefaultToolRegistry().listTools();
113
113
  availableTools = allTools.map((tool) => ({
114
114
  name: tool.name,
115
115
  description: tool.description || "No description available",
@@ -119,6 +119,12 @@ class OllamaLanguageModel {
119
119
  });
120
120
  clearTimeout(timeoutId);
121
121
  if (!response.ok) {
122
+ if (response.status === 404) {
123
+ const errorData = await response.json();
124
+ if (errorData.error && errorData.error.includes("not found")) {
125
+ throw new Error(`Model '${this.modelId}' not found. Please run 'ollama pull ${this.modelId}'`);
126
+ }
127
+ }
122
128
  throw new Error(`Ollama API error: ${response.status} ${response.statusText}`);
123
129
  }
124
130
  const data = (await response.json());
@@ -183,6 +189,12 @@ class OllamaLanguageModel {
183
189
  });
184
190
  clearTimeout(timeoutId);
185
191
  if (!response.ok) {
192
+ if (response.status === 404) {
193
+ const errorData = await response.json();
194
+ if (errorData.error && errorData.error.includes("not found")) {
195
+ throw new Error(`Model '${this.modelId}' not found. Please run 'ollama pull ${this.modelId}'`);
196
+ }
197
+ }
186
198
  throw new Error(`Ollama API error: ${response.status} ${response.statusText}`);
187
199
  }
188
200
  if (!response.body) {
@@ -400,6 +412,9 @@ export class Ollama {
400
412
  });
401
413
  }
402
414
  const result = await generateText(generateOptions);
415
+ if (result.text.includes("model not found")) {
416
+ throw new Error(`Model '${this.modelName}' not found. Please run 'ollama pull ${this.modelName}'`);
417
+ }
403
418
  logger.debug(`[${functionTag}] Generate text completed`, {
404
419
  provider,
405
420
  modelName: this.modelName,
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Asynchronously get the best available provider based on real-time checks.
3
+ * This function performs actual authentication and availability tests.
4
+ *
5
+ * @param requestedProvider - Optional preferred provider name
6
+ * @returns The best provider name to use
7
+ */
8
+ export declare function getBestProvider(requestedProvider?: string): Promise<string>;
@@ -0,0 +1,75 @@
1
+ import { AIProviderFactory } from '../core/factory.js';
2
+ import { logger } from './logger.js';
3
+ import { hasProviderEnvVars } from './providerUtils.js';
4
+ /**
5
+ * Asynchronously get the best available provider based on real-time checks.
6
+ * This function performs actual authentication and availability tests.
7
+ *
8
+ * @param requestedProvider - Optional preferred provider name
9
+ * @returns The best provider name to use
10
+ */
11
+ export async function getBestProvider(requestedProvider) {
12
+ const providers = [
13
+ "google-ai",
14
+ "anthropic",
15
+ "openai",
16
+ "mistral",
17
+ "vertex",
18
+ "azure",
19
+ "huggingface",
20
+ "bedrock",
21
+ "ollama",
22
+ ];
23
+ if (requestedProvider && requestedProvider !== 'auto') {
24
+ if (await isProviderAvailable(requestedProvider)) {
25
+ logger.debug(`[getBestProvider] Using requested provider: ${requestedProvider}`);
26
+ return requestedProvider;
27
+ }
28
+ else {
29
+ logger.warn(`[getBestProvider] Requested provider '${requestedProvider}' is not available. Falling back to auto-selection.`);
30
+ }
31
+ }
32
+ for (const provider of providers) {
33
+ if (await isProviderAvailable(provider)) {
34
+ logger.debug(`[getBestProvider] Selected provider: ${provider}`);
35
+ return provider;
36
+ }
37
+ }
38
+ throw new Error("No available AI providers. Please check your configurations.");
39
+ }
40
+ /**
41
+ * Check if a provider is truly available by performing a quick authentication test.
42
+ *
43
+ * @param providerName - The name of the provider to check.
44
+ * @returns True if the provider is available and authenticated.
45
+ */
46
+ async function isProviderAvailable(providerName) {
47
+ if (!hasProviderEnvVars(providerName) && providerName !== 'ollama') {
48
+ return false;
49
+ }
50
+ if (providerName === 'ollama') {
51
+ try {
52
+ const response = await fetch('http://localhost:11434/api/tags', {
53
+ method: 'GET',
54
+ signal: AbortSignal.timeout(2000)
55
+ });
56
+ if (response.ok) {
57
+ const { models } = await response.json();
58
+ const defaultOllamaModel = "llama3.2:latest";
59
+ return models.some((m) => m.name === defaultOllamaModel);
60
+ }
61
+ return false;
62
+ }
63
+ catch (error) {
64
+ return false;
65
+ }
66
+ }
67
+ try {
68
+ const provider = await AIProviderFactory.createProvider(providerName);
69
+ await provider.generateText({ prompt: "test", maxTokens: 1 });
70
+ return true;
71
+ }
72
+ catch (error) {
73
+ return false;
74
+ }
75
+ }
@@ -3,7 +3,14 @@
3
3
  * @param requestedProvider - Optional preferred provider name
4
4
  * @returns The best provider name to use
5
5
  */
6
- export declare function getBestProvider(requestedProvider?: string): string;
6
+ export declare function getBestProviderSync(requestedProvider?: string): string;
7
+ /**
8
+ * Check if a provider has the minimum required environment variables
9
+ * NOTE: This only checks if variables exist, not if they're valid
10
+ * @param provider - Provider name to check
11
+ * @returns True if the provider has required environment variables
12
+ */
13
+ export declare function hasProviderEnvVars(provider: string): boolean;
7
14
  /**
8
15
  * Get available provider names
9
16
  * @returns Array of available provider names
@@ -7,7 +7,7 @@ import { logger } from "./logger.js";
7
7
  * @param requestedProvider - Optional preferred provider name
8
8
  * @returns The best provider name to use
9
9
  */
10
- export function getBestProvider(requestedProvider) {
10
+ export function getBestProviderSync(requestedProvider) {
11
11
  // If a specific provider is requested, return it
12
12
  if (requestedProvider) {
13
13
  return requestedProvider;
@@ -42,6 +42,15 @@ export function getBestProvider(requestedProvider) {
42
42
  * @returns True if the provider appears to be configured
43
43
  */
44
44
  function isProviderConfigured(provider) {
45
+ return hasProviderEnvVars(provider);
46
+ }
47
+ /**
48
+ * Check if a provider has the minimum required environment variables
49
+ * NOTE: This only checks if variables exist, not if they're valid
50
+ * @param provider - Provider name to check
51
+ * @returns True if the provider has required environment variables
52
+ */
53
+ export function hasProviderEnvVars(provider) {
45
54
  switch (provider.toLowerCase()) {
46
55
  case "bedrock":
47
56
  case "amazon":
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@juspay/neurolink",
3
- "version": "1.9.0",
3
+ "version": "1.10.0",
4
4
  "description": "Universal AI Development Platform with working MCP integration, multi-provider support, and professional CLI. Built-in tools operational, 58+ external MCP servers discoverable. Connect to filesystem, GitHub, database operations, and more. Build, test, and deploy AI applications with 9 major providers: OpenAI, Anthropic, Google AI, AWS Bedrock, Azure, Hugging Face, Ollama, and Mistral AI.",
5
5
  "author": {
6
6
  "name": "Juspay Technologies",
@@ -25,6 +25,25 @@
25
25
  "npm": ">=8.0.0",
26
26
  "pnpm": ">=8.0.0"
27
27
  },
28
+ "scripts": {
29
+ "dev": "vite dev",
30
+ "build": "vite build && pnpm run prepack",
31
+ "build:cli": "echo 'Building CLI...' && tsc --project tsconfig.cli.json",
32
+ "cli": "node dist/cli/index.js",
33
+ "preview": "vite preview",
34
+ "prepare": "svelte-kit sync || echo ''",
35
+ "prepack": "svelte-kit sync && svelte-package && pnpm run build:cli && publint",
36
+ "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
37
+ "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
38
+ "test": "vitest",
39
+ "test:run": "vitest run",
40
+ "test:dynamic-models": "node test-dynamic-models.js",
41
+ "model-server": "node scripts/model-server.js",
42
+ "lint": "prettier --check . && eslint .",
43
+ "format": "prettier --write .",
44
+ "changeset": "changeset",
45
+ "changeset:version": "changeset version && git add --all"
46
+ },
28
47
  "files": [
29
48
  "dist",
30
49
  "!dist/**/*.test.*",
@@ -151,26 +170,15 @@
151
170
  "ai-development",
152
171
  "llm-integration"
153
172
  ],
173
+ "pnpm": {
174
+ "onlyBuiltDependencies": [
175
+ "esbuild",
176
+ "puppeteer"
177
+ ]
178
+ },
154
179
  "os": [
155
180
  "darwin",
156
181
  "linux",
157
182
  "win32"
158
- ],
159
- "scripts": {
160
- "dev": "vite dev",
161
- "build": "vite build && pnpm run prepack",
162
- "build:cli": "echo 'Building CLI...' && tsc --project tsconfig.cli.json",
163
- "cli": "node dist/cli/index.js",
164
- "preview": "vite preview",
165
- "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
166
- "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
167
- "test": "vitest",
168
- "test:run": "vitest run",
169
- "test:dynamic-models": "node test-dynamic-models.js",
170
- "model-server": "node scripts/model-server.js",
171
- "lint": "prettier --check . && eslint .",
172
- "format": "prettier --write .",
173
- "changeset": "changeset",
174
- "changeset:version": "changeset version && git add --all"
175
- }
176
- }
183
+ ]
184
+ }