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

98 lines (97 loc) • 4.98 kB
/** * The multi-step tool loop that the ai package's `generateText` used to supply. * * It deliberately loops over a provider's own `doGenerate` rather than over any * streaming machinery. That is the whole lesson of the reverted first attempt: * `doGenerate` is where the JSON-versus-SSE wire choice lives, along with the * 400 retry, the context-overflow refit and the provider's own structured-output * handling. Looping around the streaming path instead silently changed * generate() to send `stream: true` and broke ten providers against a * non-streaming body. * * Every provider whose delegating model exposes a v3-shaped `doGenerate` can * share this: the v3 result shape (`content` parts, `finishReason`, `usage`) is * the same across Anthropic, the OpenAI-compatible family and SageMaker. */ import type { NativeGenerateLoopArgs, NativeGenerateLoopResult, StepResult, StepToolChoiceInput, Tool, ToolExecutionSummaryInternal } from "../types/index.js"; /** * Decide the tool choice for ONE step of a multi-step loop. * * A forced choice held on every step compels a tool call on every step, so * the loop could only end when `maxSteps` ran out — which is exactly what * happened before this existed. The rule: a forced choice applies while * `step < toolChoiceSteps` (default 1), after which the model chooses * (`"auto"`). `"auto"` and `"none"` pass through unchanged on every step. * * A `prepareStep` hook, when supplied, runs first and its `toolChoice` wins * for the step — including a forced choice past `toolChoiceSteps`, which is * how a caller forces a tool on step 3 only. A hook that returns no * `toolChoice` defers to the rule. Nothing else on the hook's result is read. * * A forced single-tool choice, from the hook or from the turn, that names a * tool outside `declaredTools` is warned about and not sent. A hook's choice * is then treated as "no override" and the rule decides; the turn's own * choice becomes `"auto"`. Without `declaredTools` nothing is checked. * * A hook that THROWS is logged and treated as "no override": the steps * already billed are kept and the rule decides. An abort of the turn while * the hook is pending is not a hook failure and is re-thrown. The two are * told apart by the LOOP's signal, never by the error's name: a hook that * rejects with its own `AbortError` (its policy service timed out) has * failed, and must not end a turn the caller never cancelled. */ export declare function resolveStepToolChoice(input: StepToolChoiceInput): Promise<unknown>; /** * Translate a NeuroLink-shape tool choice (`"auto"` / `"none"` / * `"required"` / `{ type: "tool", toolName }`) into the v3 call-option * OBJECT every `doGenerate` converter switches on. The bare strings were * being handed straight to `doGenerate`; Anthropic's converter normalises * them, but the OpenAI-compatible one reads `.type` off the value and * silently dropped every string form. An already-object value passes * through untouched. */ export declare const toV3ToolChoice: (choice: unknown) => Record<string, unknown> | undefined; /** * The record of one completed step that `prepareStep` receives in `steps`. * Built the same way by every native loop so a hook sees one shape. */ export declare const toPrepareStepRecord: (step: { stepNumber: number; content: Array<Record<string, unknown>>; text: string; toolCalls: Array<{ toolName: string; toolCallId: string; input: unknown; }>; toolResults: Array<{ toolName: string; toolCallId: string; output: unknown; }>; finishReason: string; inputTokens: number; outputTokens: number; /** Prompt-cache tokens for the step, when the provider reports them. */ cacheReadTokens?: number; cacheWriteTokens?: number; }) => StepResult<Record<string, Tool>>; /** * Narrow a model handle to the delegating shape this loop drives. * `LanguageModel` is a union that includes a bare string id, and a double * assertion through unknown is banned by Critical Rule 14. */ export declare const hasNativeDoGenerate: (value: unknown) => value is { doGenerate: (options: Record<string, unknown>) => Promise<Record<string, unknown>>; }; /** * Spell a JSON Schema into the conversation's system turn. * * The structured-output fallback for vendors that reject or ignore * `response_format`. Merged into an existing trailing system message rather * than appended as a second one: several self-hosted OpenAI-compatible stacks * honour only the first system message, so a second would be dropped and the * fallback would silently do nothing. */ export declare const appendJsonSchemaInstruction: (conversation: Array<Record<string, unknown>>, schema: unknown) => Array<Record<string, unknown>>; export declare function runNativeGenerateLoop(args: NativeGenerateLoopArgs, toolExecutionSummaries: ToolExecutionSummaryInternal[]): Promise<NativeGenerateLoopResult>;