UNPKG

@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

84 lines (83 loc) • 3.28 kB
import { redactCredentials } from "../systemOneDecision.js"; import { DECIDE_FORMATTERS } from "./formatters.js"; import { readPath } from "./paths.js"; const RETRYABLE_BY_DEFAULT = new Set([ "rate_limit", "overloaded", "server", ]); const isRetryableByDefault = (kind) => RETRYABLE_BY_DEFAULT.has(kind); // The window a messagePattern is searched in: an error body can be arbitrarily // long, and a pattern only needs the start of it. const MESSAGE_MATCH_LIMIT = 2000; const COMPILED_PATTERNS = new Map(); function compilePattern(source) { const cached = COMPILED_PATTERNS.get(source); if (cached) { return cached; } const compiled = new RegExp(source, "i"); COMPILED_PATTERNS.set(source, compiled); return compiled; } function ruleMatches(rule, status, message) { const statuses = rule.status === undefined ? [] : typeof rule.status === "number" ? [rule.status] : rule.status; return ((statuses.includes(status) || (rule.statusMin !== undefined && status >= rule.statusMin)) && (rule.messagePattern === undefined || compilePattern(rule.messagePattern).test(message.slice(0, MESSAGE_MATCH_LIMIT)))); } function messageFromShape(shape, value) { if (shape.isArray) { return Array.isArray(value) && shape.formatter ? DECIDE_FORMATTERS[shape.formatter](value) : undefined; } if (typeof value !== "string" || value.length === 0) { return undefined; } return shape.formatter ? DECIDE_FORMATTERS[shape.formatter](value) : value; } function firstShapeText(shapes, payload) { for (const shape of shapes) { const text = messageFromShape(shape, readPath(payload, shape.path)); if (text !== undefined) { return { shape, text }; } } return undefined; } export function classifyDecisionError(spec, input) { const { status, payload, requestId, label, apiKey } = input; // Redacted once, here: it stands in for a shape's text that redaction // empties, and the label is catalog data a key can match. Redaction is not // idempotent for a key such as "e" (it matches inside "[redacted]"), so the // fallback is never passed through it a second time. const fallback = redactCredentials(`${label} request failed with HTTP ${status}`, apiKey); const found = firstShapeText(spec.bodyShapes, payload); // The message comes first because a rule may match on it: the key is gone // from the text a pattern sees. const message = (found?.text && redactCredentials(found.text, apiKey)) || fallback; const rule = spec.rules.find((candidate) => ruleMatches(candidate, status, message)); const ruleKind = rule?.kind ?? spec.default; const ruleRetryable = rule?.retryable ?? isRetryableByDefault(ruleKind); const build = (kind, retryable) => ({ kind, message, status, requestId, retryable, }); if (found) { const { shape } = found; return build(shape.kind ?? ruleKind, shape.retryable ?? (shape.kind === undefined ? ruleRetryable : isRetryableByDefault(shape.kind))); } return build(ruleKind, ruleRetryable); }