@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
140 lines (139 loc) • 5.41 kB
TypeScript
/**
* OpenAI Chat Completions API format conversion layer.
*
* Provides a request parser (OpenAI -> NeuroLink), a response serializer
* (NeuroLink -> OpenAI), a streaming SSE state machine, and an error
* envelope helper. Together they allow NeuroLink to act as a
* drop-in OpenAI API proxy.
*
* Reference: https://platform.openai.com/docs/api-reference/chat/create
*/
import type { ClaudeRequest, ClaudeResponse, InternalResult, OpenAICompletionRequest, OpenAICompletionResponse, OpenAIErrorResponse, ParsedOpenAIRequest, StreamLifecycleState } from "../types/index.js";
/** Generate a unique chat completion ID in the OpenAI format. */
export declare function generateChatCompletionId(): string;
/** Generate an OpenAI-format tool call ID (`call_` + random chars). */
export declare function generateOpenAIToolCallId(): string;
/**
* Parse an incoming OpenAI Chat Completions request into an intermediate
* representation consumable by NeuroLink's generate/stream pipeline.
*
* Handles:
* - System prompt extraction from system messages
* - Message flattening (text + image parts)
* - Tool definition conversion
* - tool_choice mapping
* - top_p, temperature, max_tokens pass-through
*/
export declare function parseOpenAIRequest(body: OpenAICompletionRequest): ParsedOpenAIRequest;
/**
* Serialize a NeuroLink GenerateResult into an OpenAI Chat Completions response.
*/
export declare function serializeOpenAIResponse(result: InternalResult, requestModel: string): OpenAICompletionResponse;
/**
* Build an OpenAI-compatible error envelope.
*/
export declare function buildOpenAIError(status: number, message: string): OpenAIErrorResponse;
/**
* Format a single OpenAI SSE frame.
* OpenAI uses only `data:` lines (no `event:` prefix unlike Claude).
*/
export declare function formatOpenAISSE(data: unknown): string;
/**
* Stateful SSE serializer that emits a well-formed OpenAI streaming response.
*
* Tracks lifecycle state (`idle` -> `streaming` -> `done`) and the current
* tool call index for multi-tool streaming.
*
* Usage:
* ```ts
* const sse = new OpenAIStreamSerializer(requestModel);
*
* // Start the stream
* yield* sse.start();
*
* // Text deltas
* for await (const chunk of textStream) {
* yield* sse.pushDelta(chunk);
* }
*
* // Tool use
* yield* sse.pushToolUse(toolId, toolName, toolInput);
*
* // Finalize
* yield* sse.finish("stop", usage);
* ```
*/
export declare class OpenAIStreamSerializer {
private state;
private readonly id;
private readonly model;
private started;
private toolCallIndex;
constructor(model: string);
/** Current lifecycle state (exposed for testing). */
getState(): StreamLifecycleState;
private makeChunk;
/**
* Emit the opening frame with `role: "assistant"`.
*/
start(): Generator<string>;
/**
* Push a text content delta.
*/
pushDelta(text: string): Generator<string>;
/**
* Push the start of a tool call (id, name, empty arguments).
*/
pushToolCallStart(id: string, name: string): Generator<string>;
/**
* Push an arguments delta for the current tool call.
*/
pushToolCallArgDelta(index: number, argsChunk: string): Generator<string>;
/**
* Push a complete tool use: emits tool call start followed by chunked
* argument deltas (~100 chars per chunk).
*/
pushToolUse(id: string, name: string, input: unknown): Generator<string>;
/**
* Finalize the stream: emit finish_reason chunk, then `data: [DONE]`.
*/
finish(finishReason?: string, usage?: {
input: number;
output: number;
total: number;
}): Generator<string>;
/**
* Emit an error event. Transitions to terminal ERROR state.
*/
emitError(message: string): Generator<string>;
}
/**
* Convert an OpenAI Chat Completions request to a Claude Messages API request.
*
* Used by the OpenAI proxy endpoint to internally loopback requests targeting
* Claude models through the proxy's native /v1/messages passthrough path,
* so they benefit from OAuth account rotation, retry, SSE interception, etc.
*/
export declare function convertOpenAIToClaudeRequest(openai: OpenAICompletionRequest): ClaudeRequest;
/**
* Convert a non-streaming Claude Messages response to an OpenAI Chat
* Completions response by bridging through {@link InternalResult}.
*/
export declare function convertClaudeToOpenAIResponse(claude: ClaudeResponse, requestModel: string): OpenAICompletionResponse;
/**
* Create a TransformStream that parses Claude Messages API SSE events from
* the upstream response and re-emits them as OpenAI Chat Completions SSE
* frames.
*
* Handles the canonical Claude SSE event types:
* - message_start -> emits the opening `role: "assistant"` chunk
* - content_block_start -> text block: no-op; tool_use block: emit tool call start
* - content_block_delta -> text_delta: emit content delta;
* input_json_delta: emit tool call argument delta
* - content_block_stop -> no-op
* - message_delta -> captures stop_reason and output token usage
* - message_stop -> emits the final `finish_reason` chunk + `[DONE]`
*/
export declare function createClaudeToOpenAIStreamTransform(requestModel: string, options?: {
onError?: (message: string) => void;
}): TransformStream<Uint8Array, Uint8Array>;