@juspay/neurolink 13.1.0 → 13.2.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 (33) hide show
  1. package/CHANGELOG.md +3 -7
  2. package/README.md +71 -52
  3. package/dist/browser/neurolink.min.js +374 -374
  4. package/dist/cli/commands/decide.d.ts +2 -1
  5. package/dist/cli/commands/decide.js +2 -1
  6. package/dist/cli/commands/setup.d.ts +1 -1
  7. package/dist/cli/commands/setup.js +1 -1
  8. package/dist/cli/factories/commandFactory.js +14 -5
  9. package/dist/constants/contextWindows.d.ts +7 -0
  10. package/dist/constants/contextWindows.js +48 -0
  11. package/dist/constants/enums.d.ts +5 -0
  12. package/dist/constants/enums.js +6 -0
  13. package/dist/factories/providerDescriptors.js +13 -9
  14. package/dist/neurolink.d.ts +4 -0
  15. package/dist/neurolink.js +24 -1
  16. package/dist/providers/catalog/charm-hyper.json +6 -10
  17. package/dist/providers/catalog/index.generated.d.ts +1 -1
  18. package/dist/providers/catalog/index.generated.js +3 -0
  19. package/dist/providers/catalog/openai-decisions.json +1 -1
  20. package/dist/providers/catalog/provider-catalog.schema.json +8 -0
  21. package/dist/providers/catalog/scaledown.json +68 -0
  22. package/dist/providers/catalog/schema.js +2 -0
  23. package/dist/providers/configuredDecision.d.ts +5 -0
  24. package/dist/providers/configuredDecision.js +27 -5
  25. package/dist/types/classifierRouter.d.ts +10 -9
  26. package/dist/types/cli.d.ts +2 -2
  27. package/dist/types/providerCatalog.d.ts +22 -0
  28. package/dist/types/providerCatalog.generated.d.ts +2 -2
  29. package/dist/types/providers.d.ts +4 -0
  30. package/dist/utils/pricing.js +12 -0
  31. package/dist/utils/tokenLimits.js +20 -1
  32. package/docs-site/static/search-index.json +92 -67
  33. package/package.json +4 -2
@@ -24,16 +24,16 @@ export type ClassifierDifficulty = "trivial" | "simple" | "moderate" | "hard" |
24
24
  * - `heuristic` — keyword/length scoring. Deterministic, zero latency.
25
25
  * - `llm` — a cheap classifier model via the injected `generate`.
26
26
  * - `jev` — a decision model, whichever decision provider is configured
27
- * (TypeSafe's Jev, Laya, XOR, SubQ, Perplexity, Cloudflare Clef or OpenAI
28
- * Decisions); one round trip that returns the confidence the provider
27
+ * (TypeSafe's Jev, Laya, XOR, SubQ, Perplexity, Cloudflare Clef, OpenAI
28
+ * Decisions or ScaleDown); one round trip that returns the confidence the provider
29
29
  * reports (*calibrated* on TypeSafe's Jev, ~400ms there) rather than a
30
30
  * self-reported one.
31
31
  * - `auto` — `jev` when a decision provider is configured, in the environment
32
32
  * or in SDK credentials (`TYPESAFE_API_KEY`, `LAYA_API_KEY` with
33
33
  * `LAYA_BASE_URL`, `XOR_API_KEY` with `XOR_BASE_URL`, `SUBQ_API_KEY`, `PERPLEXITY_API_KEY`,
34
- * `CLOUDFLARE_API_KEY` with `CLOUDFLARE_ACCOUNT_ID`, or `OPENAI_API_KEY`,
34
+ * `CLOUDFLARE_API_KEY` with `CLOUDFLARE_ACCOUNT_ID`, `OPENAI_API_KEY`,
35
35
  * which does not count while `OPENAI_BASE_URL`, `OPENAI_API_BASE` or `credentials.openai.baseURL` names a
36
- * host other than `https://api.openai.com`), otherwise `heuristic`.
36
+ * host other than `https://api.openai.com`, or `SCALEDOWN_API_KEY`), otherwise `heuristic`.
37
37
  */
38
38
  export type ClassifierStrategyKind = "heuristic" | "llm" | "jev" | "auto";
39
39
  /**
@@ -195,15 +195,16 @@ export type ClassifierRouterConfig = {
195
195
  * decision provider is configured, in the environment or in SDK credentials
196
196
  * (`TYPESAFE_API_KEY`, `LAYA_API_KEY` with `LAYA_BASE_URL`,
197
197
  * `XOR_API_KEY` with `XOR_BASE_URL`, `SUBQ_API_KEY`, `PERPLEXITY_API_KEY`,
198
- * `CLOUDFLARE_API_KEY` with `CLOUDFLARE_ACCOUNT_ID`, or `OPENAI_API_KEY`) and
199
- * "heuristic" otherwise, so configuring one upgrades routing without any code
198
+ * `CLOUDFLARE_API_KEY` with `CLOUDFLARE_ACCOUNT_ID`, `OPENAI_API_KEY` or
199
+ * `SCALEDOWN_API_KEY`) and "heuristic" otherwise, so configuring one upgrades routing without any code
200
200
  * change. Behaviour for a caller with none of these is unchanged. Three of them
201
201
  * are shared with a text provider and count here too: `PERPLEXITY_API_KEY`
202
202
  * (Perplexity's), the Cloudflare pair (Workers AI's) and `OPENAI_API_KEY` (the
203
203
  * OpenAI chat provider's). A caller whose only key is `OPENAI_API_KEY` now
204
- * resolves to "jev", answered by OpenAI Decisions as the last-resort decision
205
- * provider; each classification is then a paid call that still falls back to
206
- * the heuristic on any failure. The exception is a key that sits beside an
204
+ * resolves to "jev", answered by OpenAI Decisions (the default decision
205
+ * provider whenever none ahead of it in the order is configured); each
206
+ * classification is then a paid call that still falls back to the heuristic
207
+ * on any failure. The exception is a key that sits beside an
207
208
  * `OPENAI_BASE_URL`, `OPENAI_API_BASE` or `credentials.openai.baseURL` naming a host other than
208
209
  * `https://api.openai.com`: that key is a gateway's and does not count.
209
210
  */
@@ -1770,9 +1770,9 @@ export type CliClassifierRouterFlags = {
1770
1770
  * (--classifier-strategy). "auto" resolves to "jev" when a decision
1771
1771
  * provider is configured (TYPESAFE_API_KEY, LAYA_API_KEY with
1772
1772
  * LAYA_BASE_URL, XOR_API_KEY with XOR_BASE_URL, SUBQ_API_KEY, PERPLEXITY_API_KEY,
1773
- * CLOUDFLARE_API_KEY with CLOUDFLARE_ACCOUNT_ID, or OPENAI_API_KEY, which does
1773
+ * CLOUDFLARE_API_KEY with CLOUDFLARE_ACCOUNT_ID, OPENAI_API_KEY, which does
1774
1774
  * not count beside an OPENAI_BASE_URL, OPENAI_API_BASE or credentials.openai.baseURL naming a host other
1775
- * than https://api.openai.com) and "heuristic" otherwise.
1775
+ * than https://api.openai.com, or SCALEDOWN_API_KEY) and "heuristic" otherwise.
1776
1776
  */
1777
1777
  classifierStrategy?: string;
1778
1778
  /**
@@ -25,6 +25,14 @@ export type CatalogTimeouts = {
25
25
  };
26
26
  export type CatalogModelSpec = {
27
27
  contextWindow?: number;
28
+ /**
29
+ * The model's own output-token limit. Enforced at runtime: for a provider
30
+ * with no PROVIDER_MAX_TOKENS row, getSafeMaxTokens clamps a larger
31
+ * requested maxTokens to this value. Record only a number the vendor states
32
+ * as a hard limit; omit it when the source is a roster field that looks
33
+ * derived from the context window, because an omitted value leaves the
34
+ * model uncapped while a wrong one silently truncates callers.
35
+ */
28
36
  maxOutputTokens?: number;
29
37
  pricingPerMTok?: CatalogPricingPerMTok;
30
38
  vision: boolean;
@@ -314,6 +322,20 @@ export type DecideBlock = {
314
322
  /** Request URL; `{baseURL}` is the only placeholder. */
315
323
  endpoint: string;
316
324
  model: DecideModel;
325
+ /**
326
+ * How the API key is sent. `bearer`, the default when omitted, sends
327
+ * `Authorization: Bearer <key>`. `x-api-key` sends `x-api-key: <key>` and no
328
+ * `Authorization` header at all. This is the decide request's own setting;
329
+ * `quirks.authHeaderStyle` belongs to the chat path and is not read here.
330
+ */
331
+ authHeader?: "bearer" | "x-api-key";
332
+ /**
333
+ * `text` makes the state that goes out always a string: a string is sent
334
+ * unchanged (not trimmed), any other value is sent as its JSON text, and a
335
+ * state that is blank after trimming is refused before any request, as an
336
+ * `invalid_request`. Omitted means the state goes out as given.
337
+ */
338
+ stateEncoding?: "text";
317
339
  /** Body keys for images and video, after `model`, `state` and `questions`. Omitted means the body never carries media. */
318
340
  media?: DecideMedia;
319
341
  /**
@@ -1,2 +1,2 @@
1
- export type CatalogProviderName = "a2agent" | "abliteration" | "above-dev" | "ai21" | "aiand" | "aihubmix" | "aionlabs" | "ambient" | "api-route" | "arcee" | "atlas-cloud" | "avian-io" | "baidu-qianfan" | "baseten" | "bee-heossi" | "byteplus-modelark" | "bytez" | "cerebras" | "charm-hyper" | "chutes" | "cloudflare" | "crusoe" | "dashscope" | "deepinfra" | "deepseek" | "empiriolabs" | "featherless-ai" | "fireworks" | "friendli" | "gmicloud" | "gradientai" | "groq" | "hetzner-inference" | "huggingface" | "inception-labs" | "inceptron" | "inco" | "inference-net" | "io-intelligence" | "koscompute" | "laya" | "lemonfox-ai" | "lilac" | "llmtech" | "longcat" | "mancer" | "meta-model-api" | "minimax" | "mistral" | "moark" | "modelscope" | "moonshot-ai" | "morph" | "naver-clova-studio" | "nebius" | "neuralwatt" | "novita" | "openai-decisions" | "ovhcloud" | "parasail" | "pareto-inference" | "perplexity" | "poolside" | "prime-intellect" | "reka" | "runinfra" | "sakana-ai" | "sambanova" | "sarvam" | "scaleway" | "siliconflow" | "stackit" | "stepfun" | "subconscious" | "subq" | "synthetic" | "telnyx" | "thinking-machines" | "tinfoil" | "together-ai" | "umans-ai" | "upstage" | "venice-ai" | "vispark" | "vultr-inference" | "wafer" | "wandb-inference" | "xai" | "xiaomi-mimo" | "xor" | "z-ai";
2
- export type CatalogCredentialKey = "a2agent" | "abliteration" | "aboveDev" | "ai21" | "aiand" | "aihubmix" | "aionlabs" | "ambient" | "apiRoute" | "arcee" | "atlasCloud" | "avianIo" | "baiduQianfan" | "baseten" | "beeHeossi" | "byteplusModelark" | "bytez" | "cerebras" | "charmHyper" | "chutes" | "cloudflare" | "crusoe" | "dashscope" | "deepinfra" | "deepseek" | "empiriolabs" | "featherlessAi" | "fireworks" | "friendli" | "gmicloud" | "gradientai" | "groq" | "hetznerInference" | "huggingFace" | "inceptionLabs" | "inceptron" | "inco" | "inferenceNet" | "ioIntelligence" | "koscompute" | "laya" | "lemonfoxAi" | "lilac" | "llmtech" | "longcat" | "mancer" | "metaModelApi" | "minimax" | "mistral" | "moark" | "modelscope" | "moonshotAi" | "morph" | "naverClovaStudio" | "nebius" | "neuralwatt" | "novita" | "openaiDecisions" | "ovhcloud" | "parasail" | "paretoInference" | "perplexity" | "poolside" | "primeIntellect" | "reka" | "runinfra" | "sakanaAi" | "sambanova" | "sarvam" | "scaleway" | "siliconflow" | "stackit" | "stepfun" | "subconscious" | "subq" | "synthetic" | "telnyx" | "thinkingMachines" | "tinfoil" | "together" | "umansAi" | "upstage" | "veniceAi" | "vispark" | "vultrInference" | "wafer" | "wandbInference" | "xai" | "xiaomiMimo" | "xor" | "zAi";
1
+ export type CatalogProviderName = "a2agent" | "abliteration" | "above-dev" | "ai21" | "aiand" | "aihubmix" | "aionlabs" | "ambient" | "api-route" | "arcee" | "atlas-cloud" | "avian-io" | "baidu-qianfan" | "baseten" | "bee-heossi" | "byteplus-modelark" | "bytez" | "cerebras" | "charm-hyper" | "chutes" | "cloudflare" | "crusoe" | "dashscope" | "deepinfra" | "deepseek" | "empiriolabs" | "featherless-ai" | "fireworks" | "friendli" | "gmicloud" | "gradientai" | "groq" | "hetzner-inference" | "huggingface" | "inception-labs" | "inceptron" | "inco" | "inference-net" | "io-intelligence" | "koscompute" | "laya" | "lemonfox-ai" | "lilac" | "llmtech" | "longcat" | "mancer" | "meta-model-api" | "minimax" | "mistral" | "moark" | "modelscope" | "moonshot-ai" | "morph" | "naver-clova-studio" | "nebius" | "neuralwatt" | "novita" | "openai-decisions" | "ovhcloud" | "parasail" | "pareto-inference" | "perplexity" | "poolside" | "prime-intellect" | "reka" | "runinfra" | "sakana-ai" | "sambanova" | "sarvam" | "scaledown" | "scaleway" | "siliconflow" | "stackit" | "stepfun" | "subconscious" | "subq" | "synthetic" | "telnyx" | "thinking-machines" | "tinfoil" | "together-ai" | "umans-ai" | "upstage" | "venice-ai" | "vispark" | "vultr-inference" | "wafer" | "wandb-inference" | "xai" | "xiaomi-mimo" | "xor" | "z-ai";
2
+ export type CatalogCredentialKey = "a2agent" | "abliteration" | "aboveDev" | "ai21" | "aiand" | "aihubmix" | "aionlabs" | "ambient" | "apiRoute" | "arcee" | "atlasCloud" | "avianIo" | "baiduQianfan" | "baseten" | "beeHeossi" | "byteplusModelark" | "bytez" | "cerebras" | "charmHyper" | "chutes" | "cloudflare" | "crusoe" | "dashscope" | "deepinfra" | "deepseek" | "empiriolabs" | "featherlessAi" | "fireworks" | "friendli" | "gmicloud" | "gradientai" | "groq" | "hetznerInference" | "huggingFace" | "inceptionLabs" | "inceptron" | "inco" | "inferenceNet" | "ioIntelligence" | "koscompute" | "laya" | "lemonfoxAi" | "lilac" | "llmtech" | "longcat" | "mancer" | "metaModelApi" | "minimax" | "mistral" | "moark" | "modelscope" | "moonshotAi" | "morph" | "naverClovaStudio" | "nebius" | "neuralwatt" | "novita" | "openaiDecisions" | "ovhcloud" | "parasail" | "paretoInference" | "perplexity" | "poolside" | "primeIntellect" | "reka" | "runinfra" | "sakanaAi" | "sambanova" | "sarvam" | "scaledown" | "scaleway" | "siliconflow" | "stackit" | "stepfun" | "subconscious" | "subq" | "synthetic" | "telnyx" | "thinkingMachines" | "tinfoil" | "together" | "umansAi" | "upstage" | "veniceAi" | "vispark" | "vultrInference" | "wafer" | "wandbInference" | "xai" | "xiaomiMimo" | "xor" | "zAi";
@@ -469,6 +469,10 @@ export type NeurolinkCredentials = {
469
469
  apiKey?: string;
470
470
  baseURL?: string;
471
471
  };
472
+ scaledown?: {
473
+ apiKey?: string;
474
+ baseURL?: string;
475
+ };
472
476
  scaleway?: {
473
477
  apiKey?: string;
474
478
  baseURL?: string;
@@ -812,6 +812,18 @@ const PRICING = {
812
812
  // name, so a `"gpt-6-luna"` key would price that chat model at this rate.
813
813
  _default: { input: 0.1 / 1_000_000, output: 0 },
814
814
  },
815
+ scaledown: {
816
+ // $0.05 per million input tokens, output free: `output: 0` is a price, not
817
+ // a token count, so do not "correct" it from `usage.output_tokens`. A list
818
+ // price from ScaleDown's launch post and from `usage.cost` in a response,
819
+ // both as reported to this project on 2026-10-15; this project has not
820
+ // priced a call itself. Per ScaleDown's docs each choice option is a
821
+ // separate internal call, so input tokens grow with options times state
822
+ // length (not measured here). Only `_default`: LiteLLM, OpenRouter and
823
+ // openai-compatible search every table by model name, so a `"classify"` key
824
+ // would price an unrelated model at this rate.
825
+ _default: { input: 0.05 / 1_000_000, output: 0 },
826
+ },
815
827
  "cloudflare-clef": {
816
828
  // Workers AI bills Clef per million INPUT tokens ($0.24 for clef, $0.09 for
817
829
  // clef-flash); no output price is listed. `output: 0` is a price, not a token
@@ -2,7 +2,7 @@
2
2
  * Provider-specific token limit utilities
3
3
  * Provides safe maxTokens values based on provider and model capabilities
4
4
  */
5
- import { getRuntimeOutputCeiling } from "../constants/contextWindows.js";
5
+ import { getCatalogOutputCeiling, getRuntimeOutputCeiling, } from "../constants/contextWindows.js";
6
6
  import { bedrockModelIdForLookup, stripBedrockGeoPrefix, } from "../constants/bedrockIdentifiers.js";
7
7
  import { PROVIDER_MAX_TOKENS } from "../core/constants.js";
8
8
  import { logger } from "./logger.js";
@@ -68,6 +68,25 @@ export function getSafeMaxTokens(provider, model, requestedMaxTokens) {
68
68
  // Get provider-specific limits
69
69
  const providerLimits = PROVIDER_MAX_TOKENS[canonicalProvider];
70
70
  if (!providerLimits) {
71
+ // A JSON-catalog provider has no PROVIDER_MAX_TOKENS row, but its catalog
72
+ // entry can declare the model's own output ceiling. Only that per-model
73
+ // value counts: the provider-wide default is a 4096 placeholder for many
74
+ // providers. An over-large request is clamped. Unlike the runtime ceiling
75
+ // above, this ceiling is not injected as a default: an unset request stays
76
+ // unset, because a ceiling-sized default would shrink usable input on
77
+ // total-context backends.
78
+ const catalogCeiling = getCatalogOutputCeiling(provider, model);
79
+ if (catalogCeiling !== undefined) {
80
+ if (requestedMaxTokens === undefined || requestedMaxTokens === null) {
81
+ return undefined;
82
+ }
83
+ if (requestedMaxTokens > catalogCeiling) {
84
+ logger.warn(`Requested maxTokens ${requestedMaxTokens} exceeds the catalog output ceiling ` +
85
+ `of ${catalogCeiling} for ${provider}/${model}. Using ${catalogCeiling} instead.`);
86
+ return catalogCeiling;
87
+ }
88
+ return requestedMaxTokens;
89
+ }
71
90
  logger.warn(`Unknown provider ${provider}, no token limits enforced`);
72
91
  // Explicit undefined/null check so a caller-supplied 0 is preserved
73
92
  // (truthy guard would silently drop 0 here as it does in the restricted branch).