@juspay/neurolink
Version:
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
179 lines (178 loc) • 8.49 kB
JavaScript
/**
* Converts the JSON authoring format into the runtime
* OpenAICompatCatalogEntry shape the registry loop consumes. All
* knowledge about deriving env vars, template interpolation and error
* rule matching lives HERE — the JSON stays declarative.
*/
import { CATALOG_JSON_ENTRIES } from "./index.generated.js";
import { DEFAULT_ERROR_RULES } from "../../utils/errorClassifier.js";
import { AuthenticationError, RateLimitError, InvalidModelError, NetworkError, ProviderError, } from "../../types/index.js";
const ERROR_CLASS_MAP = {
authentication: AuthenticationError,
"rate-limit": RateLimitError,
"invalid-model": InvalidModelError,
network: NetworkError,
provider: ProviderError,
};
// Duplicated from tools/codegen-catalog.ts intentionally: src/ must not
// import from tools/ (build-time tooling depends on runtime source, not the
// reverse).
function toCamelCase(id) {
return id.replace(/-([a-z])/g, (_m, c) => c.toUpperCase());
}
// Resolves the NeurolinkCredentials key for an entry — respects a
// credentialsKey override (required where the derived camelCase would
// rename a pre-existing public credential field; see together-ai). Single
// source of truth shared with codegen's own credentialsKeyFor() so the
// generated NeurolinkCredentials keys and any runtime consumer (Task 7's
// descriptors) can never drift from each other.
export function catalogCredentialsKey(entry) {
return entry.credentialsKey ?? toCamelCase(entry.id);
}
export function catalogEnvVar(entry, kind) {
const override = entry.wire.envOverrides?.[kind];
if (override) {
return override;
}
const base = entry.id.toUpperCase().replace(/-/g, "_");
return kind === "apiKey"
? `${base}_API_KEY`
: kind === "baseURL"
? `${base}_BASE_URL`
: `${base}_MODEL`;
}
function interpolate(template, entry, modelName, rawMessage) {
return template
.replace(/\{apiKeyEnvVar\}/g, catalogEnvVar(entry, "apiKey"))
.replace(/\{setupUrl\}/g, entry.setup.url)
.replace(/\{model\}/g, modelName ?? "")
.replace(/\{message\}/g, rawMessage ?? "");
}
export function buildCatalogConfigOptions(entry) {
return {
providerName: entry.displayName,
envVarName: catalogEnvVar(entry, "apiKey"),
setupUrl: entry.setup.url,
description: entry.setup.description ?? "API key",
instructions: entry.setup.instructions.map((line) => interpolate(line, entry)),
...(entry.wire.apiKeyFallbackEnvVars
? { fallbackEnvVars: entry.wire.apiKeyFallbackEnvVars }
: {}),
};
}
function buildErrorRules(entry) {
const bespoke = entry.errorRules.map((rule) => {
const regex = rule.pattern ? new RegExp(rule.pattern, "i") : undefined;
const needsContext = rule.message.includes("{model}") || rule.message.includes("{message}");
return {
match: (ctx) => (rule.status !== undefined && ctx.statusCode === rule.status) ||
(regex !== undefined &&
(rule.patternStatuses === undefined ||
(ctx.statusCode !== undefined &&
rule.patternStatuses.includes(ctx.statusCode))) &&
regex.test(ctx.message)),
errorClass: ERROR_CLASS_MAP[rule.class],
message: needsContext
? (ctx) => interpolate(rule.message, entry, ctx.modelName, ctx.message)
: interpolate(rule.message, entry),
};
});
return [...bespoke, ...DEFAULT_ERROR_RULES];
}
// Recursively freezes a value in place and returns it. Used only on a
// structuredClone()d graph below — never on CATALOG_JSON_ENTRIES itself —
// so this never touches the module-level singleton.
function deepFreeze(value) {
if (Array.isArray(value)) {
for (const item of value) {
deepFreeze(item);
}
return Object.freeze(value);
}
if (value !== null && typeof value === "object") {
for (const key of Object.keys(value)) {
deepFreeze(value[key]);
}
return Object.freeze(value);
}
return value;
}
// Returns a deep clone of the catalog graph, frozen at every level, never
// the module-level singleton by reference or anything sharing its nested
// objects/arrays — CATALOG_JSON_ENTRIES is reused on every call, so handing
// out (a shallow copy of) the live array would let any one caller's
// mutation corrupt catalog state for the rest of the process, and freezing
// only the outer array left `entry.aliases`, `entry.models.fallbacks`,
// `entry.models.catalog` etc. shared and mutable despite the `readonly`
// return type. structuredClone() first (so the frozen graph never aliases
// CATALOG_JSON_ENTRIES's own objects), then deepFreeze() the clone. All
// current callers only .find/.map/.flatMap/.filter/iterate, so this is
// behavior-preserving for them.
export function getCatalogJsonEntries() {
return deepFreeze(structuredClone(CATALOG_JSON_ENTRIES));
}
export function buildCatalogEntries() {
return CATALOG_JSON_ENTRIES.map((entry) => {
const base = {
providerName: entry.id,
aliases: [entry.id, ...entry.aliases],
apiKeyEnvVar: catalogEnvVar(entry, "apiKey"),
configOptions: buildCatalogConfigOptions(entry),
modelEnvVar: catalogEnvVar(entry, "model"),
defaultModel: entry.models.default,
registryDefaultModel: entry.models.registryDefaultModel ?? entry.models.default,
registryDefaultModelChecksEnvVar: entry.quirks?.registryDefaultIgnoresModelEnvVar !== true,
fallbackModelName: entry.models.fallbackModelName ??
entry.models.fallbacks[1] ??
entry.models.fallbacks[0],
fallbackModels: [...entry.models.fallbacks],
errorRules: buildErrorRules(entry),
// "model-dependent" leaves this field unset entirely (not the string
// itself, which OpenAICompatCatalogEntry.supportsTools types as
// boolean|undefined) so ConfiguredOpenAICompatProvider.supportsTools()
// falls through to the model-registry default, same as an entry that
// never set this field at all.
...(typeof entry.capabilities.tools === "boolean"
? { supportsTools: entry.capabilities.tools }
: {}),
// Unlike `tools`, `capabilities.structuredOutputWithTools` is a plain
// required boolean in the schema (no "model-dependent" member), so this
// is a direct passthrough — `false` and `true` both carry meaning to
// ConfiguredOpenAICompatProvider.suppressResponseFormatWithTools().
supportsStructuredOutputWithTools: entry.capabilities.structuredOutputWithTools,
};
const { baseURLTemplate } = entry.wire;
if (baseURLTemplate) {
// Schema constrains extraCredentials to exactly one entry (Task 1) —
// [0] is the whole list by construction, mirroring the deliberately
// accountId-shaped runtime computedBaseURL type.
const extra = entry.wire.extraCredentials?.[0] ?? "accountId";
base.computedBaseURL = {
envVar: `${entry.id.toUpperCase().replace(/-/g, "_")}_${extra.replace(/([A-Z])/g, "_$1").toUpperCase()}`,
missingValueMessage: entry.wire.missingCredentialMessage ??
`Missing ${extra} for ${entry.displayName}`,
build: (value) => baseURLTemplate.replaceAll(`{${extra}}`, value),
};
}
else {
base.baseURLEnvVar = catalogEnvVar(entry, "baseURL");
base.defaultBaseURL = entry.wire.baseURL;
}
if (entry.quirks?.messageContentFormat) {
base.messageContentFormat = entry.quirks.messageContentFormat;
}
if (entry.quirks?.timeoutErrorClass === "provider") {
base.timeoutErrorClass = ProviderError;
}
if (entry.quirks?.responseFormatDowngrade) {
base.responseFormatDowngrade = entry.quirks.responseFormatDowngrade;
}
if (entry.quirks?.replayReasoningContent) {
base.replayReasoningContent = true;
}
if (entry.quirks?.authHeaderStyle) {
base.authHeaderStyle = entry.quirks.authHeaderStyle;
}
return base;
});
}