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

101 lines (100 loc) • 6.22 kB
/** * Bridges Bedrock's native `BedrockMessage[]` conversation shape and * `AgenticLoopChunk` stream to the AI SDK V3 model-middleware protocol * (`LanguageModelV3`), so `BaseProvider.applyMiddlewareToModel()` can wrap a * turn built entirely out of `runAgenticLoop` — the same engine Bedrock's * `generate()` and `executeStream()` already run on. * * Scope: only the conversation `generate()`/`executeStream()` hand to the * loop as its STARTING point ever crosses this boundary. Both reset * `conversationHistory` to a single fresh message before calling the loop * (see `client.ts`), so that starting point is always `system + one user * message` — never prior assistant/tool-result turns, which are constructed * entirely inside the loop's own steps and stay in native * `BedrockMessage[]`/`ConverseCommand` shape. A general, lossless * `BedrockMessage` <-> `ModelMessage` converter is not attempted here, and * would not be possible anyway: `BedrockToolResult` carries no `toolName`, * which a faithful `ToolModelMessage` requires. * * Further scoped to TEXT-ONLY turns. A multimodal starting message (image, * document, or — pathologically — a tool-use/tool-result block) skips * wrapping entirely; the caller checks `isTextOnlyBedrockConversation` and * falls back to calling the loop directly, logging that the turn was * excluded. This mirrors the plan's AI Studio precedent: an excluded shape * must be explicit in code, not implied by no test ever sending one. * * Who owns the outcome. When the wrapped `doGenerate`/`doStream` ran the loop, * the loop's own result is authoritative for `finishReason`, `usage`, * `toolExecutions` and the final conversation: a middleware may replace the * content or the stream parts a caller sees, but it cannot rewrite the * metadata of a turn that already reached the wire. When a middleware never * calls through (a cache hit, a refusal, a synthetic answer) no loop ran, and * its own V3 result is what gets reported. */ import type { ToolInputSchema } from "@aws-sdk/client-bedrock-runtime"; import type { AgenticLoopChunk, BedrockMessage, LanguageModelV3CallOptions, LanguageModelV3StreamPart, ModelMessage } from "../../types/index.js"; /** Names of the unsent options `params` carries; an empty list counts as unset. */ export declare function unsentBedrockMiddlewareOptions(params: LanguageModelV3CallOptions): string[]; /** * Unwrap Bedrock's `ToolSpecification.inputSchema` — always `{ json: * DocumentType }` on the wire (see `ToolInputSchema.JsonMember` in the AWS * SDK), never the bare schema — to the plain JSON schema object the V3 tool * shape's `inputSchema` field expects. `client.ts`'s own tool-formatting path * (`formatToolsForBedrock`) always sends `{ json: schema }` to AWS for this * exact reason; handing a middleware the wrapper itself instead of the * schema breaks any schema-inspecting middleware (e.g. a guardrail * validating tool arguments against the declared shape). */ export declare function unwrapBedrockToolInputSchema(inputSchema: ToolInputSchema | undefined): unknown; /** True when every content block of every message is plain text. */ export declare function isTextOnlyBedrockConversation(conversation: BedrockMessage[]): boolean; /** * `system` + the (already text-only) starting conversation, as a V3 prompt. * The system prompt is always sent as its own leading message — Bedrock * carries it out-of-band (`ConverseCommandInput.system`), but the V3 prompt * shape has no separate slot, so `bedrockConversationFromV3Prompt` is the * inverse that pulls it back out. */ export declare function buildV3PromptFromBedrock(systemPrompt: string, conversation: BedrockMessage[]): ModelMessage[]; /** * Inverse of `buildV3PromptFromBedrock`, read AFTER `transformParams` has * run — this is what makes a middleware's prompt rewrite (correction 1) * reach the wire: the caller passes the ORIGINAL prompt into `doGenerate`/ * `doStream`, and by AI SDK contract `transformParams` has already produced * the version handed to the closure by the time this runs. * * A `tool` role message has no faithful Bedrock shape at this boundary (no * toolUseId/toolName pairing survives `ToolModelMessage`), so one is * dropped with a WARN rather than guessed at. The starting turn Bedrock * ever hands across this boundary never contains one; seeing one here means * middleware injected it. */ export declare function bedrockConversationFromV3Prompt(prompt: ModelMessage[]): { system: string; conversation: BedrockMessage[]; }; /** * Wrap a native `AgenticLoopChunk` source into the V3 stream contract, one * chunk at a time, forwarding cancellation to both the source iterator and * `cancel` (which the caller wires to the turn's own `AbortController`, so * breaking out of a wrapped stream tears down the underlying AWS SDK * request — correction 4). `finish` resolves the terminal `finish` part * once the loop settles; middleware inspecting the returned stream sees a * genuine usage-bearing finish part even when nothing downstream reads it * back out (a guardrail that blocks before calling `doStream` never * produces this stream at all, which is the `!loopPromise` case its own * caller handles). */ export declare function bedrockChunksToV3Stream(source: AsyncIterable<AgenticLoopChunk>, finish: Promise<LanguageModelV3StreamPart>, cancel: () => void): ReadableStream<LanguageModelV3StreamPart>; /** * Inverse of `bedrockChunksToV3Stream`: drains a (possibly * middleware-wrapped) V3 stream back into Bedrock's native chunk shape. * `finish`/`error` parts are consumed here but not surfaced as chunks — * `streamingConversationLoop` reads the turn's outcome from the loop's own * `resultPromise`, which already carries richer usage/finishReason/ * toolExecutions than a V3 finish part does; this function exists only to * satisfy the wrapped stream's own contract (a middleware may expect its * returned stream to be drained to completion) and to `throw` a middleware * `error` part into the consumer. */ export declare function bedrockV3StreamToChunks(stream: ReadableStream<LanguageModelV3StreamPart>): AsyncIterable<AgenticLoopChunk>;