import { LlmProvider, type ChatResult, type ChatStreamChunk, type LlmMessage, type NormalizedOptions, type ProviderConfigBase } from './provider-base.js'; export interface OpenAICompatProviderConfig extends Omit { /** 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 * - Kimi/Moonshot: https://api.moonshot.cn/v1 (intl: api.moonshot.ai/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; /** 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; /** * Which max-tokens field to emit. Some OpenAI-compat gateways (Kimi K3, * MiMo, custom proxies) only accept `max_completion_tokens`. * Default: `max_tokens`. */ maxTokensField?: 'max_tokens' | 'max_completion_tokens'; /** 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; private readonly maxTokensField; constructor(cfg: OpenAICompatProviderConfig); supportsVision(): boolean; protected _callApi(messages: LlmMessage[], opts: NormalizedOptions, signal: AbortSignal): Promise; protected _callApiStream(messages: LlmMessage[], opts: NormalizedOptions, signal: AbortSignal): AsyncIterable; /** * 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 `AGIM_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