@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
62 lines (61 loc) • 3.43 kB
TypeScript
import type Anthropic from "@anthropic-ai/sdk";
/**
* Additive structured output for the native Anthropic Messages API.
*
* Anthropic has no `response_format`, so a schema has to be expressed as a
* tool. The provider's pre-existing `responseFormat` path does that by
* REPLACING the tools array with a single json tool and pinning `tool_choice`
* to it — correct for a schema-only call, but mutually exclusive with real
* tools, so agent/MCP turns that pass both silently lost the schema.
*
* The additive pattern here APPENDS a `final_result` tool to the caller's
* tools and leaves `tool_choice` on auto: the model keeps calling real tools
* for as long as it needs, then emits its answer as `final_result` arguments
* that already conform to the schema. This mirrors the native
* Claude-on-Vertex loop in `googleVertex/client.ts`, which has used the same
* tool name, description, and instruction wording since it shipped.
*/
/** Internal tool name — filtered out of every returned tool call / execution. */
export declare const FINAL_RESULT_TOOL_NAME = "final_result";
/** The system-prompt instruction to answer through the named tool. */
export declare function toolAnswerInstruction(toolName: string): string;
/** Appended to the system prompt whenever the final_result tool is in play. */
export declare const FINAL_RESULT_INSTRUCTION: string;
/**
* Build the `final_result` tool definition from a JSON Schema.
*
* `$ref`s are inlined and `$schema` dropped — Anthropic's `input_schema` must
* be a self-contained object schema. Schemas that are not object-rooted (a
* bare array/string schema) are wrapped so `input_schema.type` is always
* "object", which the Messages API requires.
*/
export declare function buildFinalResultTool(jsonSchema: Record<string, unknown>): Anthropic.Messages.Tool;
/**
* Append `final_result` to an Anthropic tool list.
*
* Returns a NEW array so the caller's tool list is never mutated, and reports
* `applied: false` (with the list unchanged) when the pattern must not run:
* there are no real tools to preserve, or the caller already exposes a tool of
* that name — shadowing a caller's tool would break their turn.
*/
export declare function appendFinalResultTool(tools: Anthropic.Messages.Tool[] | undefined, jsonSchema: Record<string, unknown>): {
tools: Anthropic.Messages.Tool[] | undefined;
applied: boolean;
};
/**
* Append the final_result instruction to an Anthropic `system` value.
*
* The block-array form gets a NEW trailing block rather than an edit to the
* existing one: rewriting a block that carries a `cache_control` marker would
* change the cached prefix and invalidate the prompt cache on every turn.
*/
export declare function appendToolAnswerInstruction(system: string | Anthropic.Messages.TextBlockParam[] | undefined, toolName: string): string | Anthropic.Messages.TextBlockParam[];
export declare function appendFinalResultInstruction(system: string | Anthropic.Messages.TextBlockParam[] | undefined): string | Anthropic.Messages.TextBlockParam[];
/**
* Canonical JSON text for a `final_result` payload.
*
* Accepts the raw accumulated `input_json` from a stream so a payload
* truncated by the token cap is still returned verbatim — the caller's
* coercion layer can repair it, whereas dropping it loses the whole answer.
*/
export declare function stringifyFinalResultInput(inputJson: string): string;