import { LlmProvider, type ChatResult, type ChatStreamChunk, type LlmMessage, type NormalizedOptions, type ProviderConfigBase } from './provider-base.js';
export interface OpenAICompatProviderConfig extends Omit<ProviderConfigBase, 'providerType'> {
    /** Full base URL up to (but not including) `/chat/completions`. Each
     *  vendor publishes its own; we default to OpenAI's. Examples:
     *    - OpenAI:       https://api.openai.com/v1
     *    - DeepSeek:     https://api.deepseek.com/v1
     *    - Moonshot:     https://api.moonshot.cn/v1
     *    - Qwen compat:  https://dashscope.aliyuncs.com/compatible-mode/v1
     *    - Ollama:       http://localhost:11434/v1
     *    - vLLM:         http://localhost:8000/v1
     *    - OpenRouter:   https://openrouter.ai/api/v1
     */
    baseUrl?: string;
    /** Headers merged into every request. Useful for OpenRouter's
     *  `HTTP-Referer` / `X-Title`, Azure's `api-key`, etc. The Authorization
     *  header is set automatically from `apiKey` and cannot be overridden
     *  here — that would defeat the secrets layer. */
    extraHeaders?: Record<string, string>;
    /** Number of retries on transient (429 / 5xx) failures. Default 1.
     *  Caller-side retry budgets (audit / cost) make >2 wasteful. */
    retryOnTransient?: number;
    /** Display label for logs / web UI. Defaults to "openai-compat" but
     *  individual deployments may want "deepseek" / "kimi" / "qwen" so the
     *  cost dashboard groups correctly. */
    providerLabel?: string;
    /** Operator opt-in: this provider's configured model understands the
     *  OpenAI vision content shape ({type:'image_url'} blocks). When true,
     *  `LlmMessage.media` is encoded into the request; otherwise media is
     *  silently ignored and only the text path travels.
     *  Default heuristic: true if the model id contains 'vision', 'gpt-4o',
     *  'gpt-4.1', 'qwen-vl', or 'glm-4v'. Override explicitly when needed. */
    vision?: boolean;
}
/** Parse an HTTP `Retry-After` header per RFC 9110: either delta-seconds
 *  (e.g. "30") or an HTTP-date. Returns ms-from-now, clamped to ≥0.
 *  Returns null on null / empty / unparsable. Exported for unit tests. */
export declare function parseRetryAfter(header: string | null | undefined): number | null;
/** Compute backoff for retry `attempt` (1-based). Honors any `Retry-After`
 *  the server sent — we wait at least that long (server knows when it's
 *  next available). Exponential 250ms × 2^(attempt-1) with full jitter
 *  on top to spread thundering herd. Capped at 30s — anything longer and
 *  the worker is better off surrendering to the caller's fallback path.
 *  `nowJitter` is injectable for deterministic tests. */
export declare function computeBackoffMs(attempt: number, retryAfterMs: number | null, opts?: {
    base?: number;
    cap?: number;
    jitter?: () => number;
}): number;
/**
 * Concrete provider for any OpenAI-compatible `/v1/chat/completions`
 * endpoint. Single-shot chat is the only Stage-1 path we care about —
 * the streaming method is wired up for Stage 2 / future "show thinking
 * in web UI" features and is fully functional.
 */
export declare class OpenAICompatProvider extends LlmProvider {
    private readonly baseUrl;
    private readonly extraHeaders;
    private readonly retryOnTransient;
    private readonly vision;
    constructor(cfg: OpenAICompatProviderConfig);
    supportsVision(): boolean;
    protected _callApi(messages: LlmMessage[], opts: NormalizedOptions, signal: AbortSignal): Promise<ChatResult>;
    protected _callApiStream(messages: LlmMessage[], opts: NormalizedOptions, signal: AbortSignal): AsyncIterable<ChatStreamChunk>;
    /**
     * Compose the JSON body. Kept tiny on purpose — the messages array
     * goes through as-is and the rest of the OpenAI surface (logprobs,
     * top_p, stop, …) lands when callers ask for it via providerOptions.
     */
    private _buildBody;
    private _postJson;
    /**
     * fetch + transient retry with Retry-After + exponential backoff + jitter.
     * Earlier versions used a tiny fixed delay and ignored `Retry-After`,
     * which made DeepSeek / Moonshot 429s flap straight to the fallback
     * provider (cold start + model-semantics swap) instead of waiting the
     * few seconds the upstream actually asked for. We now honor the header
     * as a hard floor; exponential backoff with jitter rides on top to
     * avoid thundering-herd against the upstream's recovery window.
     *
     * Defaults: 3 retries (= 4 attempts), 250ms base, doubling per attempt,
     * capped at 30s. Override via `IMHUB_LLM_RETRY_MAX` env (clamped [0,10])
     * or the `retryOnTransient` ctor option. The cap exists because beyond
     * ~30s the worker is better off surrendering so the agent loop can pick
     * a fallback provider.
     */
    private _fetchWithRetry;
}
//# sourceMappingURL=openai-compat-provider.d.ts.map