import type { TypedEventEmitter as TypedEmitter } from '@livekit/typed-emitter';
import type { LLMMetrics } from '../metrics/base.js';
import { type APIConnectOptions } from '../types.js';
import { AsyncIterableQueue } from '../utils.js';
import { type ChatContext, type ChatRole, type FunctionCall } from './chat_context.js';
import { type ToolChoice, type ToolContext, type ToolContextLike } from './tool_context.js';
export interface ChoiceDelta {
    role: ChatRole;
    content?: string;
    toolCalls?: FunctionCall[];
    extra?: Record<string, unknown>;
}
export interface CompletionUsage {
    completionTokens: number;
    promptTokens: number;
    promptCachedTokens: number;
    /** Tokens used to write to the prompt cache. */
    cacheCreationTokens?: number;
    totalTokens: number;
    /** The service tier used for processing (e.g. 'default', 'priority', 'flex'). */
    serviceTier?: string;
}
export interface ChatChunk {
    id: string;
    delta?: ChoiceDelta;
    usage?: CompletionUsage;
}
/**
 * Whether this chunk delivered generation the caller can see.
 *
 * Token counts and provider metadata (a gateway deployment stamp, a thought
 * signature) reach the caller without being output: they neither start the clock
 * on time-to-first-token nor give a retry anything to duplicate.
 */
export declare function hasResponse(chunk: ChatChunk): boolean;
export interface CollectedResponse {
    text: string;
    toolCalls: FunctionCall[];
    usage?: CompletionUsage;
    /**
     * Provider-specific extra data accumulated across chunks
     * (e.g., xAI encrypted reasoning, Google thought signatures).
     */
    extra: Record<string, unknown>;
}
export interface LLMError {
    type: 'llm_error';
    timestamp: number;
    label: string;
    error: Error;
    recoverable: boolean;
}
export type LLMCallbacks = {
    ['metrics_collected']: (metrics: LLMMetrics) => void;
    ['error']: (error: LLMError) => void;
};
declare const LLM_base: new () => TypedEmitter<LLMCallbacks>;
export declare abstract class LLM extends LLM_base {
    #private;
    constructor();
    abstract label(): string;
    /**
     * Get the model name/identifier for this LLM instance.
     *
     * @returns The model name if available, "unknown" otherwise.
     *
     * @remarks
     * Plugins should override this property to provide their model information.
     */
    get model(): string;
    /**
     * Get the provider name for this LLM instance.
     *
     * @returns The provider name if available, "unknown" otherwise.
     *
     * @remarks
     * Plugins should override this property to provide their provider information.
     */
    get provider(): string;
    /**
     * Returns a {@link LLMStream} that can be used to push text and receive LLM responses.
     */
    abstract chat({ chatCtx, toolCtx, connOptions, parallelToolCalls, toolChoice, extraKwargs, }: {
        chatCtx: ChatContext;
        /**
         * Tools to advertise to the LLM. Accepts either a `ToolContext` instance or a raw
         * `(FunctionTool | ProviderTool)[]` array — the array form is normalized into a
         * `ToolContext` internally so callers don't have to construct one themselves.
         */
        toolCtx?: ToolContextLike;
        connOptions?: APIConnectOptions;
        parallelToolCalls?: boolean;
        toolChoice?: ToolChoice;
        extraKwargs?: Record<string, unknown>;
    }): LLMStream;
    /**
     * Pre-warm connection to the LLM service.
     *
     * Establishes DNS resolution and the TLS connection to the provider before the first inference
     * request, reducing time-to-first-token on the initial reply. Non-blocking (fire-and-forget) and
     * idempotent; calls made after {@link aclose} are ignored. Providers enable it by overriding
     * {@link _prewarmImpl}.
     */
    prewarm(): void;
    /**
     * Performs a provider-specific, token-free request that initializes DNS, TLS, authentication,
     * and keep-alive state.
     *
     * @remarks Exceptions are swallowed by {@link prewarm}. Implementations must honor `signal` so
     * {@link aclose} can cancel and await in-flight work.
     */
    protected _prewarmImpl(_signal: AbortSignal): Promise<void>;
    aclose(): Promise<void>;
}
export declare abstract class LLMStream implements AsyncIterableIterator<ChatChunk> {
    #private;
    protected output: AsyncIterableQueue<ChatChunk>;
    protected queue: AsyncIterableQueue<ChatChunk>;
    protected closed: boolean;
    protected abortController: AbortController;
    protected _connOptions: APIConnectOptions;
    protected logger: import("pino").Logger;
    constructor(llm: LLM, { chatCtx, toolCtx, connOptions, }: {
        chatCtx: ChatContext;
        toolCtx?: ToolContextLike;
        connOptions: APIConnectOptions;
    });
    /** The GenAI inference span's request side, per the OTel GenAI conventions. */
    private recordGenAIRequest;
    private _mainTaskImpl;
    private mainTask;
    private emitError;
    protected monitorMetrics(): Promise<void>;
    protected abstract run(): Promise<void>;
    /** The function context of this stream. */
    get toolCtx(): ToolContext | undefined;
    /** The initial chat context of this stream. */
    get chatCtx(): ChatContext;
    /** The connection options for this stream. */
    get connOptions(): APIConnectOptions;
    next(): Promise<IteratorResult<ChatChunk>>;
    close(): void;
    /**
     * Collect the entire stream into a single response.
     *
     * @example
     * ```ts
     * const response = await myLlm.chat({ chatCtx, toolCtx }).collect();
     *
     * for (const tc of response.toolCalls) {
     *   // execute the tool call...
     * }
     * ```
     */
    collect(): Promise<CollectedResponse>;
    [Symbol.asyncIterator](): LLMStream;
}
export {};
//# sourceMappingURL=llm.d.ts.map