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

74 lines (73 loc) • 2.85 kB
/** * Classification of a tool result that the loop received as a normal return * but that reports a failure in-band. * * `neurolink.executeTool` converts a thrown error and a circuit-breaker * refusal into `{ isError: true, content: [...] }`, MCP servers return the * same shape for a failed call, and the documented custom-tool pattern is * `return { error: "User not found" }`. Every consumer that decides "did this * tool succeed" — the `tool:end` event, the stored `tool_result` row, the * replayed marker, and `toolExecutions[].isError` — has to read those shapes * the same way, so the rule lives in ONE predicate, `isErrorShapedToolResult`, * and this module only extracts the text to report for it. */ import { isErrorShapedToolResult, isMeaningfulError, } from "../core/toolExecutionRecorder.js"; const ISERROR_FALLBACK_TEXT = "Tool returned isError: true"; const ERROR_SHAPE_FALLBACK_TEXT = "Tool reported an error"; /** * The error text carried by an error-shaped result (`isError: true`, an * `error` field, or a failing `status`), or undefined when the value is not * one. Prefers an `error` string, then a serialized non-string `error`, then * the joined text parts of an MCP `content` array, then a fixed fallback. */ export function extractIsErrorText(output) { if (!isErrorShapedToolResult(output)) { return undefined; } const record = output; if (typeof record.error === "string" && record.error.length > 0) { return record.error; } if (record.error !== true && isMeaningfulError(record.error)) { try { return JSON.stringify(record.error) ?? String(record.error); } catch { return String(record.error); } } if (Array.isArray(record.content)) { const texts = record.content .filter((c) => c && c.type === "text" && typeof c.text === "string") .map((c) => c.text); if (texts.length > 0) { return texts.join(" "); } } return record.isError === true ? ISERROR_FALLBACK_TEXT : ERROR_SHAPE_FALLBACK_TEXT; } /** * Same classification for a serialized result: the memory stores keep * `tool_result.content` as a JSON string, so a row written before the store * learned to flag error shapes can still be recognised at replay. */ export function extractIsErrorTextFromContent(content) { if (typeof content !== "string") { return extractIsErrorText(content); } const trimmed = content.trim(); if (!trimmed.startsWith("{") || !(trimmed.includes('"isError"') || trimmed.includes('"error"') || trimmed.includes('"status"'))) { return undefined; } try { return extractIsErrorText(JSON.parse(trimmed)); } catch { return undefined; } }