/**
 * Google `generateContent` wire format.
 *
 * The Gemini CLI honours `GOOGLE_GEMINI_BASE_URL`, so it can be pointed at this
 * proxy with no vendor cooperation — verified live: with the variable set the
 * CLI reached the proxy and failed with `ModelNotFoundError: 404`, which is the
 * right answer from a proxy that had no route to answer it. This module is the
 * missing half.
 *
 * Three differences from the Claude and OpenAI shapes drive everything here:
 *
 * - Roles are `user` / `model`, not `user` / `assistant`.
 * - The system prompt is a sibling `systemInstruction`, not a turn.
 * - Generation settings nest under `generationConfig`.
 *
 * Token counts also have their own names — `promptTokenCount`,
 * `candidatesTokenCount`, `totalTokenCount` — and the CLI reads them to render
 * its own usage line, so getting them wrong is visible to the user rather than
 * merely wrong in a log.
 */
import type { ParsedGeminiRequest, StreamSerializerAdapter } from "../types/index.js";
/**
 * Parse a `generateContent` body into the shape the translation engine takes.
 *
 * The final user turn becomes `prompt`, with Google's `model` role mapped to
 * `assistant` so downstream providers see a role they understand.
 *
 * `conversationMessages` carries EVERY turn, the final one included. That
 * looks redundant next to `prompt`, and it is the contract the shared engine
 * expects: `buildTranslationOptions` does `conversationMessages.slice(0, -1)`
 * to derive history, because the final turn is already being sent as `prompt`.
 * `claudeFormat` and `openaiFormat` both push unconditionally for that reason.
 * Excluding the last turn here — the intuitive reading of "history" — made the
 * engine's slice eat one real turn instead, so every multi-turn Gemini request
 * silently lost its most recent message.
 */
export declare function parseGeminiRequest(model: string, body: Record<string, unknown>, stream: boolean): ParsedGeminiRequest;
/** Build a complete `generateContent` response body. */
/**
 * Render a tool call as text.
 *
 * The proxy does not forward tool calls in Google's `functionCall` part shape:
 * the CLI drives tools locally, so a `functionCall` it never asked for would be
 * an unresolvable pending call. Text is what it can act on. Both the streaming
 * serializer and the non-streaming builder go through here so the two paths
 * cannot drift.
 */
export declare function renderGeminiToolUse(name: string, input: unknown): string;
export declare function buildGeminiResponse(text: string, finishReason: string, usage: {
    input: number;
    output: number;
    total: number;
}, modelVersion: string, toolCalls?: ReadonlyArray<{
    toolName: string;
    args: Record<string, unknown>;
}>): Record<string, unknown>;
/** Google's error envelope, which the CLI parses to classify failures. */
export declare function buildGeminiErrorResponse(status: number, message: string, statusText?: string): Response;
/**
 * SSE serializer for `streamGenerateContent?alt=sse`.
 *
 * Google streams whole `GenerateContentResponse` objects, one per `data:`
 * frame, rather than the deltas Claude and OpenAI send. Each frame therefore
 * carries a complete `candidates[0].content.parts[0].text` holding only that
 * chunk's text — the CLI concatenates them — and the final frame is the one
 * that carries `finishReason` and `usageMetadata`.
 */
export declare class GeminiStreamSerializer {
    private readonly model;
    constructor(model: string);
    private frame;
    /** Google sends no preamble frame; the first delta is the first frame. */
    start(): string[];
    pushDelta(text: string): string[];
    /**
     * Tool calls are surfaced as text.
     *
     * The translation engine can emit a tool call, but the Gemini CLI drives its
     * own tools locally and does not expect `functionCall` parts from a plain
     * `generateContent`. Emitting one would make the CLI wait for a tool result
     * that is never coming; rendering it as text keeps the turn terminating.
     */
    pushToolUse(_id: string, name: string, input: unknown): string[];
    finish(finishReason: string, usage: {
        input: number;
        output: number;
        total: number;
    }): string[];
    /**
     * Errors ride the stream as a Google error object.
     *
     * Once headers are sent the status code is spent, so the CLI can only learn
     * about a mid-stream failure from the body.
     */
    emitError(message: string): string[];
}
/** Adapter so the translation engine can drive this like the other two. */
export declare function createGeminiSerializerAdapter(model: string): StreamSerializerAdapter;
