@juspay/neurolink 10.5.1 → 10.5.3

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.
@@ -43,9 +43,14 @@ export declare abstract class BaseProvider implements AIProvider {
43
43
  /**
44
44
  * Check if this provider supports tool/function calling
45
45
  * Override in subclasses to disable tools for specific providers or models
46
- * @returns true by default, providers can override to return false
46
+ * @returns the current model's registered capability, or true when unknown
47
47
  */
48
48
  supportsTools(): boolean;
49
+ /**
50
+ * Apply the shared tool gate and optionally report registry-backed
51
+ * suppression at the request entry point.
52
+ */
53
+ private shouldUseTools;
49
54
  /**
50
55
  * Primary streaming method - implements AIProvider interface
51
56
  * When tools are involved, falls back to generate() with synthetic streaming
@@ -2,6 +2,7 @@ import { context, SpanKind, SpanStatusCode, trace } from "@opentelemetry/api";
2
2
  import { directAgentTools } from "../agent/directTools.js";
3
3
  import { IMAGE_GENERATION_MODELS } from "../core/constants.js";
4
4
  import { MiddlewareFactory } from "../middleware/factory.js";
5
+ import { modelSupports } from "../models/modelRegistry.js";
5
6
  import { ATTR, tracers } from "../telemetry/index.js";
6
7
  import { isAbortError } from "../utils/errorHandling.js";
7
8
  import { hasLifecycleErrorFired, markLifecycleErrorFired, } from "../utils/lifecycleCallbacks.js";
@@ -105,10 +106,27 @@ export class BaseProvider {
105
106
  /**
106
107
  * Check if this provider supports tool/function calling
107
108
  * Override in subclasses to disable tools for specific providers or models
108
- * @returns true by default, providers can override to return false
109
+ * @returns the current model's registered capability, or true when unknown
109
110
  */
110
111
  supportsTools() {
111
- return true;
112
+ return modelSupports("functionCalling", this.providerName, this.modelName);
113
+ }
114
+ /**
115
+ * Apply the shared tool gate and optionally report registry-backed
116
+ * suppression at the request entry point.
117
+ */
118
+ shouldUseTools(options, warnWhenUnsupported = false) {
119
+ if (options.disableTools) {
120
+ return false;
121
+ }
122
+ const supportsTools = this.supportsTools();
123
+ if (!supportsTools && warnWhenUnsupported) {
124
+ logger.warn(`Tools disabled for ${this.providerName}/${this.modelName} because the model does not support function calling`, {
125
+ provider: this.providerName,
126
+ model: this.modelName,
127
+ });
128
+ }
129
+ return supportsTools;
112
130
  }
113
131
  // ===================
114
132
  // PUBLIC API METHODS
@@ -178,7 +196,7 @@ export class BaseProvider {
178
196
  // tools (e.g. RAG tools) into options.tools. This way, every provider's
179
197
  // executeStream() can simply use options.tools (or getAllTools() + options.tools)
180
198
  // and get the complete tool set without needing per-provider merge logic.
181
- if (!options.disableTools && this.supportsTools()) {
199
+ if (this.shouldUseTools(options, true)) {
182
200
  const mergedTools = await this.getToolsForStream(options);
183
201
  options = { ...options, tools: mergedTools };
184
202
  }
@@ -731,7 +749,7 @@ export class BaseProvider {
731
749
  * Prepare generation context including tools and model
732
750
  */
733
751
  async prepareGenerationContext(options) {
734
- const shouldUseTools = !options.disableTools && this.supportsTools();
752
+ const shouldUseTools = this.shouldUseTools(options, true);
735
753
  const baseTools = shouldUseTools ? await this.getAllTools() : {};
736
754
  let tools = shouldUseTools
737
755
  ? {
@@ -765,7 +783,7 @@ export class BaseProvider {
765
783
  * All providers should call this instead of getAllTools() directly.
766
784
  */
767
785
  async getToolsForStream(options) {
768
- const shouldUseTools = !options.disableTools && this.supportsTools();
786
+ const shouldUseTools = this.shouldUseTools(options);
769
787
  if (!shouldUseTools) {
770
788
  return {};
771
789
  }
@@ -43,9 +43,14 @@ export declare abstract class BaseProvider implements AIProvider {
43
43
  /**
44
44
  * Check if this provider supports tool/function calling
45
45
  * Override in subclasses to disable tools for specific providers or models
46
- * @returns true by default, providers can override to return false
46
+ * @returns the current model's registered capability, or true when unknown
47
47
  */
48
48
  supportsTools(): boolean;
49
+ /**
50
+ * Apply the shared tool gate and optionally report registry-backed
51
+ * suppression at the request entry point.
52
+ */
53
+ private shouldUseTools;
49
54
  /**
50
55
  * Primary streaming method - implements AIProvider interface
51
56
  * When tools are involved, falls back to generate() with synthetic streaming
@@ -2,6 +2,7 @@ import { context, SpanKind, SpanStatusCode, trace } from "@opentelemetry/api";
2
2
  import { directAgentTools } from "../agent/directTools.js";
3
3
  import { IMAGE_GENERATION_MODELS } from "../core/constants.js";
4
4
  import { MiddlewareFactory } from "../middleware/factory.js";
5
+ import { modelSupports } from "../models/modelRegistry.js";
5
6
  import { ATTR, tracers } from "../telemetry/index.js";
6
7
  import { isAbortError } from "../utils/errorHandling.js";
7
8
  import { hasLifecycleErrorFired, markLifecycleErrorFired, } from "../utils/lifecycleCallbacks.js";
@@ -105,10 +106,27 @@ export class BaseProvider {
105
106
  /**
106
107
  * Check if this provider supports tool/function calling
107
108
  * Override in subclasses to disable tools for specific providers or models
108
- * @returns true by default, providers can override to return false
109
+ * @returns the current model's registered capability, or true when unknown
109
110
  */
110
111
  supportsTools() {
111
- return true;
112
+ return modelSupports("functionCalling", this.providerName, this.modelName);
113
+ }
114
+ /**
115
+ * Apply the shared tool gate and optionally report registry-backed
116
+ * suppression at the request entry point.
117
+ */
118
+ shouldUseTools(options, warnWhenUnsupported = false) {
119
+ if (options.disableTools) {
120
+ return false;
121
+ }
122
+ const supportsTools = this.supportsTools();
123
+ if (!supportsTools && warnWhenUnsupported) {
124
+ logger.warn(`Tools disabled for ${this.providerName}/${this.modelName} because the model does not support function calling`, {
125
+ provider: this.providerName,
126
+ model: this.modelName,
127
+ });
128
+ }
129
+ return supportsTools;
112
130
  }
113
131
  // ===================
114
132
  // PUBLIC API METHODS
@@ -178,7 +196,7 @@ export class BaseProvider {
178
196
  // tools (e.g. RAG tools) into options.tools. This way, every provider's
179
197
  // executeStream() can simply use options.tools (or getAllTools() + options.tools)
180
198
  // and get the complete tool set without needing per-provider merge logic.
181
- if (!options.disableTools && this.supportsTools()) {
199
+ if (this.shouldUseTools(options, true)) {
182
200
  const mergedTools = await this.getToolsForStream(options);
183
201
  options = { ...options, tools: mergedTools };
184
202
  }
@@ -731,7 +749,7 @@ export class BaseProvider {
731
749
  * Prepare generation context including tools and model
732
750
  */
733
751
  async prepareGenerationContext(options) {
734
- const shouldUseTools = !options.disableTools && this.supportsTools();
752
+ const shouldUseTools = this.shouldUseTools(options, true);
735
753
  const baseTools = shouldUseTools ? await this.getAllTools() : {};
736
754
  let tools = shouldUseTools
737
755
  ? {
@@ -765,7 +783,7 @@ export class BaseProvider {
765
783
  * All providers should call this instead of getAllTools() directly.
766
784
  */
767
785
  async getToolsForStream(options) {
768
- const shouldUseTools = !options.disableTools && this.supportsTools();
786
+ const shouldUseTools = this.shouldUseTools(options);
769
787
  if (!shouldUseTools) {
770
788
  return {};
771
789
  }
@@ -4,7 +4,7 @@
4
4
  * Part of Phase 4.1 - Models Command System
5
5
  */
6
6
  import { AIProviderName } from "../constants/enums.js";
7
- import type { JsonValue, ModelInfo } from "../types/index.js";
7
+ import type { JsonValue, ModelCapabilities, ModelInfo } from "../types/index.js";
8
8
  /**
9
9
  * Comprehensive model registry
10
10
  */
@@ -25,6 +25,14 @@ export declare function getAllModels(): ModelInfo[];
25
25
  * Get model by ID
26
26
  */
27
27
  export declare function getModelById(id: string): ModelInfo | undefined;
28
+ /**
29
+ * Check whether a model supports a registered capability.
30
+ *
31
+ * Aliases resolve to their canonical model ID before the provider is
32
+ * cross-checked. Unknown provider/model pairs default to supported so new or
33
+ * custom models retain the existing behavior until capability data is added.
34
+ */
35
+ export declare function modelSupports(capability: keyof ModelCapabilities, provider: string, model: string): boolean;
28
36
  /**
29
37
  * Get models by provider
30
38
  */
@@ -726,7 +726,7 @@ export const MODEL_REGISTRY = {
726
726
  description: "Preview version of O1 reasoning model",
727
727
  capabilities: {
728
728
  vision: false,
729
- functionCalling: true,
729
+ functionCalling: false,
730
730
  codeGeneration: true,
731
731
  reasoning: true,
732
732
  multimodal: false,
@@ -770,7 +770,7 @@ export const MODEL_REGISTRY = {
770
770
  description: "Cost-effective O1 variant with strong reasoning capabilities",
771
771
  capabilities: {
772
772
  vision: false,
773
- functionCalling: true,
773
+ functionCalling: false,
774
774
  codeGeneration: true,
775
775
  reasoning: true,
776
776
  multimodal: false,
@@ -2342,6 +2342,22 @@ export function getAllModels() {
2342
2342
  export function getModelById(id) {
2343
2343
  return MODEL_REGISTRY[id];
2344
2344
  }
2345
+ /**
2346
+ * Check whether a model supports a registered capability.
2347
+ *
2348
+ * Aliases resolve to their canonical model ID before the provider is
2349
+ * cross-checked. Unknown provider/model pairs default to supported so new or
2350
+ * custom models retain the existing behavior until capability data is added.
2351
+ */
2352
+ export function modelSupports(capability, provider, model) {
2353
+ const normalizedModel = model.toLowerCase();
2354
+ const modelId = MODEL_ALIASES[normalizedModel] ?? normalizedModel;
2355
+ const modelInfo = MODEL_REGISTRY[modelId];
2356
+ if (!modelInfo || modelInfo.provider !== provider.toLowerCase()) {
2357
+ return true;
2358
+ }
2359
+ return modelInfo.capabilities[capability];
2360
+ }
2345
2361
  /**
2346
2362
  * Get models by provider
2347
2363
  */
@@ -299,8 +299,9 @@ export const NEUROLINK_BRAND = Symbol.for("@juspay/neurolink/sdk-brand");
299
299
  * exists yet to ask directly; every other provider gets tool definitions
300
300
  * natively via its `tools` parameter, so repeating them in the prompt was
301
301
  * pure token duplication. The stream path asks the provider instance
302
- * (`provider.supportsTools()`) instead of this list. Keep in sync with
303
- * `supportsTools()` overrides when adding providers.
302
+ * (`provider.supportsTools()`) instead of this list. BaseProvider resolves its
303
+ * default through MODEL_REGISTRY's `modelSupports()` facade; keep this list in
304
+ * sync with provider-specific `supportsTools()` overrides when adding providers.
304
305
  */
305
306
  const PROMPT_ONLY_TOOL_PROVIDERS = new Set([
306
307
  "ollama",
@@ -159,7 +159,6 @@ export declare abstract class OpenAIChatCompletionsProvider extends BaseProvider
159
159
  * uses an `api-key` header instead of `Authorization: Bearer`).
160
160
  */
161
161
  protected getAuthHeaders(): Record<string, string>;
162
- supportsTools(): boolean;
163
162
  /**
164
163
  * Health-check hook — part of the documented public provider contract
165
164
  * (`docs/provider-integration/00-architecture.md`). Default returns true
@@ -261,9 +261,6 @@ export class OpenAIChatCompletionsProvider extends BaseProvider {
261
261
  // ===========================================================================
262
262
  // Public/protected concrete methods (shared by all subclasses)
263
263
  // ===========================================================================
264
- supportsTools() {
265
- return true;
266
- }
267
264
  /**
268
265
  * Health-check hook — part of the documented public provider contract
269
266
  * (`docs/provider-integration/00-architecture.md`). Default returns true
@@ -4,7 +4,7 @@
4
4
  * Part of Phase 4.1 - Models Command System
5
5
  */
6
6
  import { AIProviderName } from "../constants/enums.js";
7
- import type { JsonValue, ModelInfo } from "../types/index.js";
7
+ import type { JsonValue, ModelCapabilities, ModelInfo } from "../types/index.js";
8
8
  /**
9
9
  * Comprehensive model registry
10
10
  */
@@ -25,6 +25,14 @@ export declare function getAllModels(): ModelInfo[];
25
25
  * Get model by ID
26
26
  */
27
27
  export declare function getModelById(id: string): ModelInfo | undefined;
28
+ /**
29
+ * Check whether a model supports a registered capability.
30
+ *
31
+ * Aliases resolve to their canonical model ID before the provider is
32
+ * cross-checked. Unknown provider/model pairs default to supported so new or
33
+ * custom models retain the existing behavior until capability data is added.
34
+ */
35
+ export declare function modelSupports(capability: keyof ModelCapabilities, provider: string, model: string): boolean;
28
36
  /**
29
37
  * Get models by provider
30
38
  */
@@ -726,7 +726,7 @@ export const MODEL_REGISTRY = {
726
726
  description: "Preview version of O1 reasoning model",
727
727
  capabilities: {
728
728
  vision: false,
729
- functionCalling: true,
729
+ functionCalling: false,
730
730
  codeGeneration: true,
731
731
  reasoning: true,
732
732
  multimodal: false,
@@ -770,7 +770,7 @@ export const MODEL_REGISTRY = {
770
770
  description: "Cost-effective O1 variant with strong reasoning capabilities",
771
771
  capabilities: {
772
772
  vision: false,
773
- functionCalling: true,
773
+ functionCalling: false,
774
774
  codeGeneration: true,
775
775
  reasoning: true,
776
776
  multimodal: false,
@@ -2342,6 +2342,22 @@ export function getAllModels() {
2342
2342
  export function getModelById(id) {
2343
2343
  return MODEL_REGISTRY[id];
2344
2344
  }
2345
+ /**
2346
+ * Check whether a model supports a registered capability.
2347
+ *
2348
+ * Aliases resolve to their canonical model ID before the provider is
2349
+ * cross-checked. Unknown provider/model pairs default to supported so new or
2350
+ * custom models retain the existing behavior until capability data is added.
2351
+ */
2352
+ export function modelSupports(capability, provider, model) {
2353
+ const normalizedModel = model.toLowerCase();
2354
+ const modelId = MODEL_ALIASES[normalizedModel] ?? normalizedModel;
2355
+ const modelInfo = MODEL_REGISTRY[modelId];
2356
+ if (!modelInfo || modelInfo.provider !== provider.toLowerCase()) {
2357
+ return true;
2358
+ }
2359
+ return modelInfo.capabilities[capability];
2360
+ }
2345
2361
  /**
2346
2362
  * Get models by provider
2347
2363
  */
package/dist/neurolink.js CHANGED
@@ -299,8 +299,9 @@ export const NEUROLINK_BRAND = Symbol.for("@juspay/neurolink/sdk-brand");
299
299
  * exists yet to ask directly; every other provider gets tool definitions
300
300
  * natively via its `tools` parameter, so repeating them in the prompt was
301
301
  * pure token duplication. The stream path asks the provider instance
302
- * (`provider.supportsTools()`) instead of this list. Keep in sync with
303
- * `supportsTools()` overrides when adding providers.
302
+ * (`provider.supportsTools()`) instead of this list. BaseProvider resolves its
303
+ * default through MODEL_REGISTRY's `modelSupports()` facade; keep this list in
304
+ * sync with provider-specific `supportsTools()` overrides when adding providers.
304
305
  */
305
306
  const PROMPT_ONLY_TOOL_PROVIDERS = new Set([
306
307
  "ollama",
@@ -159,7 +159,6 @@ export declare abstract class OpenAIChatCompletionsProvider extends BaseProvider
159
159
  * uses an `api-key` header instead of `Authorization: Bearer`).
160
160
  */
161
161
  protected getAuthHeaders(): Record<string, string>;
162
- supportsTools(): boolean;
163
162
  /**
164
163
  * Health-check hook — part of the documented public provider contract
165
164
  * (`docs/provider-integration/00-architecture.md`). Default returns true
@@ -261,9 +261,6 @@ export class OpenAIChatCompletionsProvider extends BaseProvider {
261
261
  // ===========================================================================
262
262
  // Public/protected concrete methods (shared by all subclasses)
263
263
  // ===========================================================================
264
- supportsTools() {
265
- return true;
266
- }
267
264
  /**
268
265
  * Health-check hook — part of the documented public provider contract
269
266
  * (`docs/provider-integration/00-architecture.md`). Default returns true
@@ -153,7 +153,7 @@ export declare class SageMakerLanguageModel implements SageMakerAsLanguageModel
153
153
  provider: string;
154
154
  specificationVersion: "v2";
155
155
  endpointName: string;
156
- modelType: "huggingface" | "mistral" | "custom" | "claude" | "llama" | "jumpstart" | undefined;
156
+ modelType: "huggingface" | "mistral" | "custom" | "llama" | "claude" | "jumpstart" | undefined;
157
157
  region: string;
158
158
  };
159
159
  /**
@@ -200,7 +200,7 @@ export declare class SageMakerLanguageModel implements SageMakerAsLanguageModel
200
200
  provider: string;
201
201
  specificationVersion: "v2";
202
202
  endpointName: string;
203
- modelType: "huggingface" | "mistral" | "custom" | "claude" | "llama" | "jumpstart" | undefined;
203
+ modelType: "huggingface" | "mistral" | "custom" | "llama" | "claude" | "jumpstart" | undefined;
204
204
  region: string;
205
205
  };
206
206
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@juspay/neurolink",
3
- "version": "10.5.1",
3
+ "version": "10.5.3",
4
4
  "packageManager": "pnpm@10.15.1",
5
5
  "description": "TypeScript AI SDK with 24+ LLM providers behind one consistent API. MCP-native (connect any MCP server), voice TTS/STT/realtime, RAG, agents, memory, context compaction. OpenAI · Anthropic · Gemini · Bedrock · Azure · Ollama · DeepSeek · NVIDIA NIM and more.",
6
6
  "author": {
@@ -141,6 +141,7 @@
141
141
  "test:tool-dedup": "pnpm run test:tool-dedup:vitest && npx tsx test/continuous-test-suite-tool-dedup.ts",
142
142
  "test:model-pool:vitest": "pnpm exec vitest run test/modelPool.test.ts",
143
143
  "test:model-pool": "pnpm run test:model-pool:vitest && npx tsx test/continuous-test-suite-model-pool.ts",
144
+ "test:model-capabilities:vitest": "pnpm exec vitest run test/modelCapabilities.test.ts",
144
145
  "test:ci": "pnpm run test && pnpm run test:client && pnpm run test:hitl",
145
146
  "// CI tier — fast, no live AI calls, safe for every commit": "",
146
147
  "test:unit:vitest": "pnpm exec vitest run test/toolRouting.test.ts",
@@ -149,7 +150,7 @@
149
150
  "test:system-messages:vitest": "pnpm exec vitest run test/systemMessages.test.ts",
150
151
  "test:tool-routing-semantic:vitest": "pnpm exec vitest run test/toolRoutingSemantic.test.ts",
151
152
  "test:tool-routing-semantic": "pnpm run test:tool-routing-semantic:vitest && npx tsx test/continuous-test-suite-tool-routing-semantic.ts",
152
- "test:unit": "pnpm run test:envguard && pnpm run test:bugfixes && pnpm run test:file-detector-extension && pnpm run test:file-detector-magic-bytes && pnpm run test:mcp:infra && pnpm run test:mcp:bash && pnpm run test:mcp:limits && pnpm run test:mcp:spans && pnpm run test:autoresearch:redis && pnpm run test:unit:vitest && pnpm run test:tool-routing-cli:vitest && pnpm run test:tool-dedup:vitest && pnpm run test:model-pool:vitest && pnpm run test:litellm-context:vitest && pnpm run test:step-budget-guard:vitest && pnpm run test:system-messages:vitest && pnpm run test:tool-routing-semantic:vitest && pnpm run test:anthropic-tools-policy && pnpm run test:sagemaker-tools && pnpm run test:anthropic-multimodal && pnpm run test:excel-interop",
153
+ "test:unit": "pnpm run test:envguard && pnpm run test:bugfixes && pnpm run test:file-detector-extension && pnpm run test:file-detector-magic-bytes && pnpm run test:mcp:infra && pnpm run test:mcp:bash && pnpm run test:mcp:limits && pnpm run test:mcp:spans && pnpm run test:autoresearch:redis && pnpm run test:unit:vitest && pnpm run test:tool-routing-cli:vitest && pnpm run test:tool-dedup:vitest && pnpm run test:model-pool:vitest && pnpm run test:litellm-context:vitest && pnpm run test:step-budget-guard:vitest && pnpm run test:system-messages:vitest && pnpm run test:tool-routing-semantic:vitest && pnpm run test:anthropic-tools-policy && pnpm run test:sagemaker-tools && pnpm run test:anthropic-multimodal && pnpm run test:excel-interop && pnpm run test:model-capabilities:vitest",
153
154
  "// CI tier — live providers, runs only when API keys are present (test:credentials and test:dynamic make real provider calls when keys are set, so they live here, not in test:unit)": "",
154
155
  "test:live": "pnpm run test:providers && pnpm run test:mcp:http && pnpm run test:mcp:sdk && pnpm run test:mcp:cli && pnpm run test:observability && pnpm run test:context && pnpm run test:memory && pnpm run test:tool-reliability && pnpm run test:evaluation && pnpm run test:autoresearch && pnpm run test:credentials && pnpm run test:dynamic",
155
156
  "// CI tier — product output (image/video/TTS/PPT) — costs $$ per run": "",