@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
98 lines (97 loc) • 4.73 kB
TypeScript
/**
* ToolExecutionRecorder — real per-call tool execution capture.
*
* One recorder is created per generate()/stream() call and attached to the
* request options (surviving option spreads). Tools are wrapped exactly once
* at the central tool-assembly points (BaseProvider.prepareGenerationContext /
* getToolsForStream), so every loop — the AI-SDK loop and the native
* Gemini/Anthropic loops alike — records through the same wrapper with real
* params, timing, and error status. Memory is bounded: serialized results are
* capped at `maxResultChars` and the record list at `maxRecords`.
*/
import type { Tool, ToolExecutionCaptureOptions, ToolExecutionRecord, StandardRecord, ToolExecutionSummaryInternal } from "../types/index.js";
/** Default cap on serialized result characters kept per record (~8KB). */
export declare const DEFAULT_TOOL_RESULT_CAPTURE_CHARS = 8192;
/** Default cap on records kept per turn. */
export declare const DEFAULT_TOOL_EXECUTION_MAX_RECORDS = 500;
/**
* Serialize a tool result to bounded text. JSON when serializable, else
* String(); truncation is explicit so consumers know the text is partial.
*/
export declare function serializeToolResult(result: unknown, maxChars: number): string;
/**
* Error-shaped tool results (MCP `isError`, `{error}` payloads) count as
* failures even though the loop received them as normal returns.
*/
/**
* True for an `error` value that actually carries something — `true`, a
* non-empty string, or a non-empty object/array — never a falsy placeholder
* a tool sets on every call (`error: false`, `error: 0`, `error: {}`).
*/
export declare function isMeaningfulError(error: unknown): boolean;
export declare function isErrorShapedToolResult(result: unknown): boolean;
/**
* Per-call recorder for tool executions. See module doc.
*/
export declare class ToolExecutionRecorder {
#private;
constructor(capture?: ToolExecutionCaptureOptions);
/** Record one completed (or failed) execution. Never throws. */
record(entry: {
toolName: string;
params: unknown;
result?: unknown;
error?: unknown;
startedAt: number;
durationMs: number;
}): void;
/** All records captured so far (oldest first, bounded by maxRecords). */
getRecords(): ToolExecutionRecord[];
/** True when at least one execution was recorded. */
hasRecords(): boolean;
/** Number of records evicted by the maxRecords cap. */
getDroppedCount(): number;
/**
* Wrap every executable tool so its invocations are recorded. Wrapping is
* idempotent — already-wrapped executes are left untouched, so a generate
* that falls back to another path never double-records.
*/
wrapTools(tools: Record<string, Tool>): Record<string, Tool>;
/**
* Attach this recorder to a request options object. The property is
* enumerable on purpose: downstream stages spread options (`{...options}`)
* and the recorder must survive into the provider loops.
*/
attachTo(options: object): void;
/** Retrieve the recorder a request is carrying, if any. */
static from(options: unknown): ToolExecutionRecorder | undefined;
}
/**
* Convert legacy loop-local `{name, input, output}` entries (plus optional
* timing/error fields) into `ToolExecutionRecord`s. Used as the fallback when
* a path produced executions the recorder did not see (e.g. results captured
* before the recorder existed, or breaker/not-found entries that never
* reached a real execute()).
*/
export declare function toToolExecutionRecords(legacyExecutions: unknown[] | undefined, capture?: ToolExecutionCaptureOptions): ToolExecutionRecord[];
/**
* Resolve the toolExecutions for a result: recorder records when the request
* carried a recorder that saw executions, else a conversion of the loop's
* legacy accumulator entries.
*/
/**
* The public `toolCalls` view of a native turn's execution summaries.
*
* The native generate loops record every executed call in
* `ToolExecutionSummaryInternal[]` and surface it as `toolExecutions`, but
* never mapped it onto `EnhancedGenerateResult.toolCalls` — the field the type
* has always declared and the ai-package formatter used to fill. A caller
* reading `result.toolCalls` after a tool ran saw nothing. This is the one
* place that mapping lives, so the three native paths cannot drift apart.
*/
export declare function toolCallsFromSummaries(summaries: ReadonlyArray<ToolExecutionSummaryInternal>): Array<{
toolCallId: string;
toolName: string;
args: StandardRecord;
}>;
export declare function resolveToolExecutionRecords(options: unknown, legacyExecutions?: unknown[]): ToolExecutionRecord[];