@juspay/neurolink
Version:
Universal AI Development Platform with working MCP integration, multi-provider support, voice (TTS/STT/realtime), and professional CLI. 58+ external MCP servers discoverable, multimodal file processing, RAG pipelines. Build, test, and deploy AI applicatio
145 lines (144 loc) • 5.24 kB
TypeScript
/**
* Claude Messages API format conversion layer.
*
* Provides a request parser (Claude -> NeuroLink), a response serializer
* (NeuroLink -> Claude), a streaming SSE state machine, and an error
* envelope helper. Together they allow NeuroLink to act as a
* drop-in Claude API proxy.
*
* Reference: https://docs.anthropic.com/en/api/messages
*/
import type { ClaudeErrorResponse, ClaudeRequest, ClaudeResponse, ContentBlockType, InternalResult, ParsedClaudeRequest, StreamLifecycleState } from "../types/index.js";
/** Generate a unique message id in the Claude format. */
export declare function generateMessageId(): string;
/** Generate a Claude-format tool use ID (`toolu_` + 24 random chars). */
export declare function generateToolUseId(): string;
/**
* Reset the internal id counter (useful in tests).
* @internal
*/
export declare function _resetIdCounter(): void;
/**
* Parse an incoming Claude Messages API request into an intermediate
* representation consumable by NeuroLink's generate/stream pipeline.
*
* Handles:
* - System prompt extraction (string or content-block array)
* - Message flattening (text + image blocks)
* - Tool definition conversion
* - tool_choice mapping
* - top_p pass-through
* - thinking configuration
*/
export declare function parseClaudeRequest(body: ClaudeRequest): ParsedClaudeRequest;
/**
* Serialize a NeuroLink GenerateResult into a Claude Messages API response.
*/
export declare function serializeClaudeResponse(result: InternalResult, requestModel: string): ClaudeResponse;
/**
* Build a Claude-compatible error envelope.
*/
export declare function buildClaudeError(status: number, message: string, errorType?: string): ClaudeErrorResponse;
/**
* Format a single SSE frame (one `event:` + `data:` pair).
*/
export declare function formatSSE(eventType: string, data: unknown): string;
/**
* Stateful SSE serializer that emits a well-formed Claude SSE stream.
*
* Tracks both lifecycle state (`idle` -> `streaming` -> `done`) and the
* current content block type (`text`, `thinking`, `tool_use`). Each
* content block gets a unique, monotonically increasing `blockIndex`.
*
* Usage:
* ```ts
* const sse = new ClaudeStreamSerializer(requestModel, inputTokens);
*
* // Thinking deltas
* for await (const thought of thinkingStream) {
* yield* sse.pushThinkingDelta(thought);
* }
*
* // Text deltas
* for await (const chunk of textStream) {
* yield* sse.pushDelta(chunk);
* }
*
* // Tool use
* yield* sse.pushToolUse(toolId, toolName, toolInput);
*
* // Finalize
* yield* sse.finish(outputTokens, finishReason);
* ```
*/
export declare class ClaudeStreamSerializer {
private state;
private currentBlockType;
private sawToolUseBlock;
private blockIndex;
private hasOpenedBlock;
private outputTokens;
private messageStarted;
private readonly messageId;
private readonly model;
private readonly inputTokens;
constructor(model: string, inputTokens?: number);
/** Current lifecycle state (exposed for testing). */
getState(): StreamLifecycleState;
/** Current content block type (exposed for testing). */
getCurrentBlockType(): ContentBlockType;
/** Current block index (exposed for testing). */
getBlockIndex(): number;
/**
* Emit a ping event frame. The actual periodic timer is wired in
* the route handler; this method just formats the SSE frame.
*/
static pingEvent(): string;
/**
* Emit `message_start` and initial ping if not already done.
*/
private ensureMessageStarted;
/**
* Close the current content block if one is open.
*/
private closeCurrentBlock;
/**
* Open a new content block of the given type.
* Increments blockIndex for each new block.
*/
private openBlock;
/**
* Emit the opening frames: message_start and ping.
* The first actual content decides which content block opens next.
*/
start(): Generator<string>;
/**
* Push a text delta. Returns zero or more SSE frames.
* If currently in a thinking block, the thinking block is closed first.
*/
pushDelta(text: string): Generator<string>;
/**
* Push a thinking delta. Returns zero or more SSE frames.
* If currently in a text or tool_use block, that block is closed first
* and a new thinking block is opened.
*/
pushThinkingDelta(text: string): Generator<string>;
/**
* Push a complete tool use block.
*
* 1. Closes any open content block
* 2. Emits `content_block_start` with `{type: "tool_use", id, name}`
* 3. JSON-stringifies the input, chunks it into ~100 char segments
* 4. Emits `content_block_delta` with `{type: "input_json_delta", partial_json}` per chunk
* 5. Emits `content_block_stop`
*/
pushToolUse(id: string, name: string, input: unknown): Generator<string>;
/**
* Finalize the stream: content_block_stop, message_delta, message_stop.
*/
finish(outputTokens?: number, finishReason?: string): Generator<string>;
/**
* Emit an error event. Transitions to terminal ERROR state.
*/
emitError(status: number, message: string): Generator<string>;
}