@juspay/neurolink 12.23.2 → 12.24.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.
Files changed (50) hide show
  1. package/CHANGELOG.md +3 -3
  2. package/dist/browser/neurolink.min.js +367 -367
  3. package/dist/constants/enums.d.ts +61 -69
  4. package/dist/constants/enums.js +63 -96
  5. package/dist/core/nativeGenerateLoop.js +11 -1
  6. package/dist/factories/providerDescriptors.d.ts +1 -1
  7. package/dist/factories/providerDescriptors.js +50 -83
  8. package/dist/factories/providerRegistry.js +9 -16
  9. package/dist/models/manifestRegistry.js +11 -2
  10. package/dist/providers/anthropic/client.js +7 -1
  11. package/dist/providers/catalog/deepseek.json +84 -0
  12. package/dist/providers/catalog/huggingface.json +405 -0
  13. package/dist/providers/catalog/index.generated.d.ts +1 -1
  14. package/dist/providers/catalog/index.generated.js +6 -0
  15. package/dist/providers/catalog/loader.js +20 -5
  16. package/dist/providers/catalog/mistral.json +8 -3
  17. package/dist/providers/catalog/provider-catalog.schema.json +31 -1
  18. package/dist/providers/catalog/schema.d.ts +8 -1
  19. package/dist/providers/catalog/schema.js +9 -1
  20. package/dist/providers/configuredOpenAICompat.d.ts +14 -5
  21. package/dist/providers/configuredOpenAICompat.js +19 -4
  22. package/dist/providers/embeddingResponseParsing.d.ts +1 -0
  23. package/dist/providers/embeddingResponseParsing.js +47 -0
  24. package/dist/providers/ideogram.js +7 -1
  25. package/dist/providers/jina.js +6 -15
  26. package/dist/providers/llamaCpp.js +4 -15
  27. package/dist/providers/lmStudio.js +4 -15
  28. package/dist/providers/localRuntimeOpenAICompat.d.ts +16 -0
  29. package/dist/providers/localRuntimeOpenAICompat.js +26 -0
  30. package/dist/providers/ollama/client.js +3 -9
  31. package/dist/providers/openaiChatCompletionsBase.d.ts +3 -3
  32. package/dist/providers/openaiChatCompletionsBase.js +20 -5
  33. package/dist/providers/openaiCompatCatalog.d.ts +16 -12
  34. package/dist/providers/openaiCompatCatalog.js +16 -12
  35. package/dist/providers/voyage.js +5 -18
  36. package/dist/types/providerCatalog.d.ts +46 -1
  37. package/dist/types/providerCatalog.generated.d.ts +2 -2
  38. package/dist/types/providers.d.ts +12 -8
  39. package/dist/utils/modelChoices.d.ts +2 -2
  40. package/dist/utils/modelChoices.js +10 -38
  41. package/dist/utils/sizeGuard.d.ts +8 -5
  42. package/dist/utils/sizeGuard.js +32 -9
  43. package/docs-site/static/search-index.json +1 -1
  44. package/package.json +2 -1
  45. package/dist/providers/deepseek.d.ts +0 -47
  46. package/dist/providers/deepseek.js +0 -120
  47. package/dist/providers/huggingFace/client.d.ts +0 -54
  48. package/dist/providers/huggingFace/client.js +0 -120
  49. package/dist/providers/huggingFace/index.d.ts +0 -1
  50. package/dist/providers/huggingFace/index.js +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@juspay/neurolink",
3
- "version": "12.23.2",
3
+ "version": "12.24.0",
4
4
  "packageManager": "pnpm@10.15.1",
5
5
  "description": "The pipe layer of an AI nervous system: one interface connecting provider neurons to your application, across three inference types — generate, stream and decide. `decide` returns typed, calibrated judgements from a non-generative model (~400ms, ~$0.00002/call) for routing, tool selection and context budgeting. MCP-native (4 transports), voice TTS/STT/realtime, RAG, agents, memory, compaction, 9 observability exporters. OpenAI · Anthropic · Gemini · Bedrock · Azure · Ollama · TypeSafe Jev and more.",
6
6
  "author": {
@@ -99,6 +99,7 @@
99
99
  "test:openai-compat-streaming-retry": "pnpm exec tsx test/continuous-test-suite-openai-compat-streaming-retry.ts",
100
100
  "test:anthropic-streaming-retry": "pnpm exec tsx test/continuous-test-suite-anthropic-streaming-retry.ts",
101
101
  "test:anthropic-silent-truncation": "pnpm exec tsx test/continuous-test-suite-anthropic-silent-truncation.ts",
102
+ "test:cache-cost-accounting": "pnpm exec tsx test/continuous-test-suite-cache-cost-accounting.ts",
102
103
  "test:adjust-body-after-400": "pnpm exec tsx test/continuous-test-suite-adjust-body-after-400.ts",
103
104
  "test:zod3-schema-native-path": "pnpm exec tsx test/continuous-test-suite-zod3-schema-native-path.ts",
104
105
  "test:error-classification-e2e": "pnpm exec tsx test/continuous-test-suite-error-classification-e2e.ts",
@@ -1,47 +0,0 @@
1
- import type { AIProviderName } from "../constants/enums.js";
2
- import type { NeurolinkCredentials, OpenAICompatResponseFormat } from "../types/index.js";
3
- import { OpenAIChatCompletionsProvider } from "./openaiChatCompletionsBase.js";
4
- /**
5
- * DeepSeek Provider — direct HTTP, no AI SDK.
6
- *
7
- * OpenAI-compatible chat completions at api.deepseek.com (deepseek-chat /
8
- * deepseek-reasoner). All request/stream/tool-loop orchestration lives in
9
- * `OpenAIChatCompletionsProvider`; this class declares configuration and
10
- * provider-specific quirks:
11
- *
12
- * 1. Structured-output downgrade — DeepSeek rejects `response_format:
13
- * { type: "json_schema" }` ("This response_format type is unavailable
14
- * now"), so `adjustResponseFormat` downgrades it to `json_object` —
15
- * matching the `supportsStructuredOutputs: false` behaviour of the
16
- * `@ai-sdk/openai-compatible` path this migration replaced. The base
17
- * client injects the literal "json" word the API requires for that mode.
18
- *
19
- * 2. Reasoning support — `reasoning_content` (deepseek-reasoner / R1) is
20
- * surfaced automatically by the native base client: streamed deltas
21
- * arrive as `{ content: "", reasoning }` chunks and the non-streaming
22
- * result carries `result.reasoning`. The opt-in `thinking` request
23
- * param (non-reasoner chat models) still needs thinking-signal plumbing
24
- * and is tracked as a follow-up. All other behavior is preserved.
25
- *
26
- * @see https://api-docs.deepseek.com
27
- */
28
- export declare class DeepSeekProvider extends OpenAIChatCompletionsProvider {
29
- constructor(modelName?: string, sdk?: unknown, _region?: string, credentials?: NeurolinkCredentials["deepseek"]);
30
- protected getProviderName(): AIProviderName;
31
- protected getDefaultModel(): string;
32
- protected formatProviderError(error: unknown): Error;
33
- protected getFallbackModelName(): string;
34
- protected getFallbackModels(): string[];
35
- /**
36
- * DeepSeek's /chat/completions rejects `response_format: { type:
37
- * "json_schema" }` outright ("This response_format type is unavailable
38
- * now"). The `@ai-sdk/openai-compatible` provider this migration replaced
39
- * ran with `supportsStructuredOutputs: false`, which downgraded structured-
40
- * output requests to `{ type: "json_object" }`. Replicate that downgrade so
41
- * `generate({ schema })` keeps working. (DeepSeek's json_object mode also
42
- * requires the word "json" somewhere in the messages; the base client's
43
- * `ensureJsonWordInBody` injects a minimal instruction when the prompt
44
- * lacks it.)
45
- */
46
- protected adjustResponseFormat(rf: OpenAICompatResponseFormat | undefined, _modelId: string): OpenAICompatResponseFormat | undefined;
47
- }
@@ -1,120 +0,0 @@
1
- import { DeepSeekModels } from "../constants/enums.js";
2
- import { AuthenticationError, InvalidModelError, ProviderError, } from "../types/index.js";
3
- import { logger } from "../utils/logger.js";
4
- import { redactUrlCredentials } from "../utils/logSanitize.js";
5
- import { classifyProviderError, DEFAULT_ERROR_RULES, } from "../utils/errorClassifier.js";
6
- import { createDeepSeekConfig, getProviderModel, validateApiKey, } from "../utils/providerConfig.js";
7
- import { OpenAIChatCompletionsProvider } from "./openaiChatCompletionsBase.js";
8
- const DEEPSEEK_BASE_URL = "https://api.deepseek.com";
9
- const getDeepSeekApiKey = () => {
10
- return validateApiKey(createDeepSeekConfig());
11
- };
12
- const getDefaultDeepSeekModel = () => {
13
- return getProviderModel("DEEPSEEK_MODEL", DeepSeekModels.DEEPSEEK_CHAT);
14
- };
15
- /**
16
- * DeepSeek Provider — direct HTTP, no AI SDK.
17
- *
18
- * OpenAI-compatible chat completions at api.deepseek.com (deepseek-chat /
19
- * deepseek-reasoner). All request/stream/tool-loop orchestration lives in
20
- * `OpenAIChatCompletionsProvider`; this class declares configuration and
21
- * provider-specific quirks:
22
- *
23
- * 1. Structured-output downgrade — DeepSeek rejects `response_format:
24
- * { type: "json_schema" }` ("This response_format type is unavailable
25
- * now"), so `adjustResponseFormat` downgrades it to `json_object` —
26
- * matching the `supportsStructuredOutputs: false` behaviour of the
27
- * `@ai-sdk/openai-compatible` path this migration replaced. The base
28
- * client injects the literal "json" word the API requires for that mode.
29
- *
30
- * 2. Reasoning support — `reasoning_content` (deepseek-reasoner / R1) is
31
- * surfaced automatically by the native base client: streamed deltas
32
- * arrive as `{ content: "", reasoning }` chunks and the non-streaming
33
- * result carries `result.reasoning`. The opt-in `thinking` request
34
- * param (non-reasoner chat models) still needs thinking-signal plumbing
35
- * and is tracked as a follow-up. All other behavior is preserved.
36
- *
37
- * @see https://api-docs.deepseek.com
38
- */
39
- export class DeepSeekProvider extends OpenAIChatCompletionsProvider {
40
- constructor(modelName, sdk, _region, credentials) {
41
- // Trim the override before applying precedence. A blank/whitespace
42
- // `credentials.apiKey` must NOT bypass `getDeepSeekApiKey()` — that
43
- // would build a client with an unusable bearer token and fail at
44
- // request time with a confusing 401 instead of at construction time.
45
- const overrideApiKey = credentials?.apiKey?.trim();
46
- const apiKey = overrideApiKey && overrideApiKey.length > 0
47
- ? overrideApiKey
48
- : getDeepSeekApiKey();
49
- // Treat blank/whitespace overrides as unset so an empty
50
- // `credentials.baseURL` or `DEEPSEEK_BASE_URL=` cannot silently override
51
- // the default with "" (mirrors the apiKey precedence above).
52
- const baseURL = credentials?.baseURL?.trim() ||
53
- process.env.DEEPSEEK_BASE_URL?.trim() ||
54
- DEEPSEEK_BASE_URL;
55
- super("deepseek", modelName, sdk, { baseURL, apiKey });
56
- logger.debug("DeepSeek Provider initialized", {
57
- modelName: this.modelName,
58
- providerName: this.providerName,
59
- baseURL: redactUrlCredentials(this.config.baseURL),
60
- });
61
- }
62
- // ===========================================================================
63
- // Abstract hooks (required)
64
- // ===========================================================================
65
- getProviderName() {
66
- return "deepseek";
67
- }
68
- getDefaultModel() {
69
- return getDefaultDeepSeekModel();
70
- }
71
- formatProviderError(error) {
72
- const rules = [
73
- {
74
- match: (ctx) => ctx.statusCode === 401 ||
75
- /Invalid API key|Authentication/i.test(ctx.message),
76
- errorClass: AuthenticationError,
77
- message: "Invalid DeepSeek API key. Please check your DEEPSEEK_API_KEY environment variable.",
78
- },
79
- {
80
- match: (ctx) => ctx.statusCode === 402 ||
81
- /Insufficient Balance|insufficient_balance/i.test(ctx.message),
82
- errorClass: ProviderError,
83
- message: "DeepSeek account has insufficient balance. Top up at https://platform.deepseek.com/usage",
84
- },
85
- {
86
- match: (ctx) => /model_not_found/i.test(ctx.message),
87
- errorClass: InvalidModelError,
88
- message: (ctx) => `DeepSeek model '${ctx.modelName}' not found. Use 'deepseek-chat' or 'deepseek-reasoner'.`,
89
- },
90
- ...DEFAULT_ERROR_RULES,
91
- ];
92
- return classifyProviderError(error, rules, this.providerName, this.modelName);
93
- }
94
- // ===========================================================================
95
- // Optional hooks — provider-specific quirks
96
- // ===========================================================================
97
- getFallbackModelName() {
98
- return DeepSeekModels.DEEPSEEK_CHAT;
99
- }
100
- getFallbackModels() {
101
- return [DeepSeekModels.DEEPSEEK_CHAT, DeepSeekModels.DEEPSEEK_REASONER];
102
- }
103
- /**
104
- * DeepSeek's /chat/completions rejects `response_format: { type:
105
- * "json_schema" }` outright ("This response_format type is unavailable
106
- * now"). The `@ai-sdk/openai-compatible` provider this migration replaced
107
- * ran with `supportsStructuredOutputs: false`, which downgraded structured-
108
- * output requests to `{ type: "json_object" }`. Replicate that downgrade so
109
- * `generate({ schema })` keeps working. (DeepSeek's json_object mode also
110
- * requires the word "json" somewhere in the messages; the base client's
111
- * `ensureJsonWordInBody` injects a minimal instruction when the prompt
112
- * lacks it.)
113
- */
114
- adjustResponseFormat(rf, _modelId) {
115
- if (rf?.type === "json_schema") {
116
- return { type: "json_object" };
117
- }
118
- return rf;
119
- }
120
- }
@@ -1,54 +0,0 @@
1
- import type { AIProviderName } from "../../constants/enums.js";
2
- import type { NeurolinkCredentials } from "../../types/index.js";
3
- import { OpenAIChatCompletionsProvider } from "../openaiChatCompletionsBase.js";
4
- /**
5
- * HuggingFace Provider — direct HTTP, no AI SDK.
6
- *
7
- * OpenAI-compatible chat completions at router.huggingface.co/v1 (unified
8
- * router endpoint, 2025). Supports the full HuggingFace model hub including
9
- * Llama 3.x, Qwen 2.5, Mistral, DeepSeek, and tool-calling capable variants.
10
- * All request/stream/tool-loop orchestration lives in
11
- * `OpenAIChatCompletionsProvider`; this class only declares configuration
12
- * and provider-specific error mapping.
13
- *
14
- * @see https://huggingface.co/docs/api-inference/index
15
- */
16
- export declare class HuggingFaceProvider extends OpenAIChatCompletionsProvider {
17
- constructor(modelName?: string, sdk?: unknown, _region?: string, credentials?: NeurolinkCredentials["huggingFace"]);
18
- protected getProviderName(): AIProviderName;
19
- protected getDefaultModel(): string;
20
- protected getFallbackModelName(): string;
21
- /**
22
- * Tool support is resolved by the base through MODEL_REGISTRY's
23
- * `modelSupports()` facade, which defaults unknown models to supported.
24
- *
25
- * This class used to override that with a 13-entry allowlist of model-name
26
- * substrings, written for the old per-model Inference API where many
27
- * endpoints rejected the OpenAI `tools` field. The router replaced that, and
28
- * the list did not keep up. Measured against the 142 models
29
- * `GET router.huggingface.co/v1/models` served on 2026-09-13 (the
30
- * catalogue drifts, so treat the totals as a snapshot; the ratio is the
31
- * point):
32
- *
33
- * allowlist entries matching ANY served model 1 of 13
34
- * served models the allowlist ADMITS 1 of 142
35
- * served models the allowlist BLOCKS 141
36
- *
37
- * So it suppressed tool calling for 99% of the catalogue, including
38
- * Qwen/Qwen2.5-72B-Instruct, which the router demonstrably tool-calls. A
39
- * spread of the blocked models — GLM-5.3-Flash, gemma-4-31B-it,
40
- * DeepSeek-V4-Flash — all accept `tools` with HTTP 200 and return
41
- * `tool_calls`; none rejected the field.
42
- *
43
- * The allowlist's own comment named `microsoft/DialoGPT-medium` — which
44
- * `getDefaultModel()`, not `getFallbackModelName()`, used to return — as a
45
- * model that rejects `tools`. The router does not serve it under any
46
- * request shape, so that path was never reachable; the default now points
47
- * at a served model (see `getDefaultHuggingFaceModel`).
48
- *
49
- * A model that genuinely cannot use tools simply does not emit `tool_calls`,
50
- * which the loop already handles, so the optimistic default costs nothing
51
- * that the allowlist was protecting.
52
- */
53
- protected formatProviderError(error: unknown): Error;
54
- }
@@ -1,120 +0,0 @@
1
- import { HuggingFaceModels } from "../../constants/enums.js";
2
- import { AuthenticationError, InvalidModelError, ProviderError, RateLimitError, } from "../../types/index.js";
3
- import { classifyProviderError, DEFAULT_ERROR_RULES, } from "../../utils/errorClassifier.js";
4
- import { logger } from "../../utils/logger.js";
5
- import { redactUrlCredentials } from "../../utils/logSanitize.js";
6
- import { createHuggingFaceConfig, getProviderModel, validateApiKey, } from "../../utils/providerConfig.js";
7
- import { OpenAIChatCompletionsProvider } from "../openaiChatCompletionsBase.js";
8
- const HUGGINGFACE_DEFAULT_BASE_URL = "https://router.huggingface.co/v1";
9
- const getHuggingFaceApiKey = () => validateApiKey(createHuggingFaceConfig());
10
- /**
11
- * Must stay the same id `providerRegistry` passes as the registration default
12
- * (`process.env.HUGGINGFACE_MODEL || HuggingFaceModels.QWEN_2_5_72B_INSTRUCT`),
13
- * hence the shared constant rather than a second literal.
14
- *
15
- * The registry supplies `modelName` on every factory-created provider, so this
16
- * only decides a directly-constructed `new HuggingFaceProvider()`. It still
17
- * matters: it previously read `microsoft/DialoGPT-medium`, which the router
18
- * does not serve under any request shape — it answers 400 "not supported by
19
- * any provider you have enabled" with or without `tools` — and that stale id
20
- * was what the docs and setup guidance advertised as the default.
21
- */
22
- const getDefaultHuggingFaceModel = () => getProviderModel("HUGGINGFACE_MODEL", HuggingFaceModels.QWEN_2_5_72B_INSTRUCT);
23
- /**
24
- * HuggingFace Provider — direct HTTP, no AI SDK.
25
- *
26
- * OpenAI-compatible chat completions at router.huggingface.co/v1 (unified
27
- * router endpoint, 2025). Supports the full HuggingFace model hub including
28
- * Llama 3.x, Qwen 2.5, Mistral, DeepSeek, and tool-calling capable variants.
29
- * All request/stream/tool-loop orchestration lives in
30
- * `OpenAIChatCompletionsProvider`; this class only declares configuration
31
- * and provider-specific error mapping.
32
- *
33
- * @see https://huggingface.co/docs/api-inference/index
34
- */
35
- export class HuggingFaceProvider extends OpenAIChatCompletionsProvider {
36
- constructor(modelName, sdk, _region, credentials) {
37
- const apiKey = credentials?.apiKey?.trim()
38
- ? credentials.apiKey.trim()
39
- : getHuggingFaceApiKey();
40
- // Treat blank/whitespace overrides as unset so an empty
41
- // `credentials.baseURL` or `HUGGINGFACE_BASE_URL=` cannot override the
42
- // default with "" (mirrors the apiKey precedence above).
43
- const baseURL = credentials?.baseURL?.trim() ||
44
- process.env.HUGGINGFACE_BASE_URL?.trim() ||
45
- HUGGINGFACE_DEFAULT_BASE_URL;
46
- super("huggingface", modelName, sdk, { baseURL, apiKey });
47
- logger.debug("HuggingFaceProvider initialized", {
48
- modelName: this.modelName,
49
- providerName: this.providerName,
50
- baseURL: redactUrlCredentials(this.config.baseURL),
51
- });
52
- }
53
- getProviderName() {
54
- return "huggingface";
55
- }
56
- getDefaultModel() {
57
- return getDefaultHuggingFaceModel();
58
- }
59
- getFallbackModelName() {
60
- return "meta-llama/Llama-3.1-8B-Instruct";
61
- }
62
- /**
63
- * Tool support is resolved by the base through MODEL_REGISTRY's
64
- * `modelSupports()` facade, which defaults unknown models to supported.
65
- *
66
- * This class used to override that with a 13-entry allowlist of model-name
67
- * substrings, written for the old per-model Inference API where many
68
- * endpoints rejected the OpenAI `tools` field. The router replaced that, and
69
- * the list did not keep up. Measured against the 142 models
70
- * `GET router.huggingface.co/v1/models` served on 2026-09-13 (the
71
- * catalogue drifts, so treat the totals as a snapshot; the ratio is the
72
- * point):
73
- *
74
- * allowlist entries matching ANY served model 1 of 13
75
- * served models the allowlist ADMITS 1 of 142
76
- * served models the allowlist BLOCKS 141
77
- *
78
- * So it suppressed tool calling for 99% of the catalogue, including
79
- * Qwen/Qwen2.5-72B-Instruct, which the router demonstrably tool-calls. A
80
- * spread of the blocked models — GLM-5.3-Flash, gemma-4-31B-it,
81
- * DeepSeek-V4-Flash — all accept `tools` with HTTP 200 and return
82
- * `tool_calls`; none rejected the field.
83
- *
84
- * The allowlist's own comment named `microsoft/DialoGPT-medium` — which
85
- * `getDefaultModel()`, not `getFallbackModelName()`, used to return — as a
86
- * model that rejects `tools`. The router does not serve it under any
87
- * request shape, so that path was never reachable; the default now points
88
- * at a served model (see `getDefaultHuggingFaceModel`).
89
- *
90
- * A model that genuinely cannot use tools simply does not emit `tool_calls`,
91
- * which the loop already handles, so the optimistic default costs nothing
92
- * that the allowlist was protecting.
93
- */
94
- formatProviderError(error) {
95
- const rules = [
96
- {
97
- match: (ctx) => /API_TOKEN_INVALID|Invalid token/.test(ctx.message),
98
- errorClass: AuthenticationError,
99
- message: "Invalid HuggingFace API token. Please check your HUGGINGFACE_API_KEY environment variable.",
100
- },
101
- {
102
- match: (ctx) => /rate limit/.test(ctx.message),
103
- errorClass: RateLimitError,
104
- message: "HuggingFace rate limit exceeded. Consider using a paid plan or try again later.",
105
- },
106
- {
107
- match: (ctx) => /model/.test(ctx.message) && /not found/.test(ctx.message),
108
- errorClass: InvalidModelError,
109
- message: () => `HuggingFace model '${this.modelName}' not found.\n\nSuggestions:\n1. Check model name spelling\n2. Ensure model exists on HuggingFace Hub\n3. For tool calling, use: Llama-3.1-8B-Instruct, Hermes-3-Llama-3.2-3B, or CodeLlama-34b-Instruct-hf`,
110
- },
111
- {
112
- match: (ctx) => /function|tool/.test(ctx.message),
113
- errorClass: ProviderError,
114
- message: (ctx) => `HuggingFace tool calling error: ${ctx.message}\n\nNotes:\n1. Ensure you're using a tool-capable model (Llama-3.1+, Hermes-3+, CodeLlama)\n2. Check that your model supports function calling\n3. Verify tool schema format is correct`,
115
- },
116
- ...DEFAULT_ERROR_RULES,
117
- ];
118
- return classifyProviderError(error, rules, this.providerName, this.modelName);
119
- }
120
- }
@@ -1 +0,0 @@
1
- export * from "./client.js";
@@ -1 +0,0 @@
1
- export * from "./client.js";