import type { LLMProvider, ProviderRequest, ProviderResult, ProviderUsage } from './types'; /** * Error thrown on a TERMINAL OpenAI-compatible call failure. Mirrors * {@link AgentProviderError}: it carries the failure `subtype` (e.g. * `openai_parse_failed`, `openai_bad_response`, `openai_http_500`, * `openai_network_error`, `aborted`) and the `costUsd` accrued before the * failure, so a caller can still account for spend on a failed call. A local, * key-less call simply carries `costUsd: 0`. */ export declare class OpenAiProviderError extends Error { /** A short, stable marker for the kind of terminal failure (see class doc). */ readonly subtype: string; /** Cost in USD accrued before the terminal failure, for cost accounting. */ readonly costUsd: number; constructor(message: string, subtype: string, costUsd: number); } /** Construction options for {@link OpenAiCompatibleProvider}. */ export interface OpenAiCompatibleProviderOptions { /** * Base URL of the OpenAI-compatible server. A bare host * (`http://10.0.230.76:1234`) or a versioned base * (`https://api.openai.com/v1`) are both accepted — see * {@link resolveChatCompletionsUrl} for the exact normalization to the * `/chat/completions` endpoint. */ baseUrl: string; /** Default model id sent in the request body when a call does not override it. */ model: string; /** * API key. REQUIRED for real OpenAI (the factory passes * `process.env.OPENAI_API_KEY`); OMITTED for a local server. When present it * is sent as `Authorization: Bearer `; when absent NO auth header is * sent (local servers are typically key-less). */ apiKey?: string; /** * Native structured-output mode. DEFAULT `true`: send the * `response_format: { type: 'json_schema', … }` envelope. `false` falls back * to the portable prompt-mode directive (schema embedded in the user prompt). * EITHER way the response is still parsed + validated locally. */ jsonMode?: boolean; /** * Optional metered pricing in dollars per MILLION tokens. When supplied, * {@link ProviderResult.costUsd} is computed from the response's token usage; * when ABSENT (the default — typical for a local server) every call reports * `costUsd: 0`. There is NO built-in price table. */ pricing?: { inputPerMTok: number; outputPerMTok: number; }; /** * Which config provider name this instance is serving. Becomes the prefix of * {@link LLMProvider.name}. DEFAULT `'openai-compatible'`. */ providerLabel?: 'openai' | 'openai-compatible'; /** * The `fetch` implementation. DEFAULT `globalThis.fetch`. INJECTED so offline * tests pass a fake that returns canned `Response`s and never opens a socket. */ fetchImpl?: typeof fetch; /** Per-request timeout in milliseconds. DEFAULT {@link DEFAULT_TIMEOUT_MS}. */ timeoutMs?: number; /** * Number of TRANSPORT retries (429 / 5xx / network error) before surfacing a * terminal error. DEFAULT {@link DEFAULT_MAX_RETRIES}. This is independent of * the ONE parse/validate re-request {@link OpenAiCompatibleProvider.generate} * performs on a schema miss. */ maxRetries?: number; /** * Sampling temperature sent in the request body. DEFAULT `0` for the most * reproducible output the server will give us. */ temperature?: number; } /** * Normalize a configured base URL to the concrete Chat Completions endpoint. * PURE and OFFLINE so every normalization case is unit-testable: * - trailing slashes are stripped; * - a base with NO path (`http://h:1234` or `http://h:1234/`) gets the default * version + endpoint appended → `…/v1/chat/completions`; * - a base that ALREADY ends with `/chat/completions` is returned unchanged * (idempotent), so re-normalizing a resolved URL is a no-op; * - otherwise `/chat/completions` is appended to the base AS-IS (the base is * assumed to already carry its own version segment, e.g. `…/v1`). * * Invariants that MUST hold (and are pinned by tests): * `http://h:1234` → `http://h:1234/v1/chat/completions` * `http://h:1234/v1` → `http://h:1234/v1/chat/completions` * `https://api.openai.com/v1` → `https://api.openai.com/v1/chat/completions` * `http://h/v1/chat/completions` → unchanged */ export declare function resolveChatCompletionsUrl(baseUrl: string): string; /** * Assemble the Chat Completions request body. PURE and OFFLINE so the json-mode * vs prompt-mode envelope, the optional system message, and the temperature are * unit-testable without a socket. * * Messages: an optional `{ role: 'system', content: request.system }` when a * system prompt is present, then a single `{ role: 'user', … }` whose content is * the raw `request.prompt` in json mode, or {@link buildPromptModePrompt}'s * schema-embedding directive in prompt mode. In json mode the * `response_format: { type: 'json_schema', json_schema: { name, strict, schema } }` * envelope is added; in prompt mode it is omitted entirely. */ export declare function buildChatRequestBody(request: ProviderRequest, opts: { model: string; jsonMode: boolean; temperature: number; }): Record; /** * Pull `choices[0].message.content` (a STRING) out of a parsed Chat Completions * response. PURE and OFFLINE. Throws {@link OpenAiProviderError} * (`openai_bad_response`) when the content is missing or not a string — a * well-formed-looking response that carries no usable text. */ export declare function extractMessageContent(responseJson: unknown): string; /** * Map an OpenAI-style `usage` object onto the project's {@link ProviderUsage} * shape. PURE, OFFLINE, and TOLERANT of partial usage: `prompt_tokens` → * `inputTokens`, `completion_tokens` → `outputTokens`, and the cache counters * (`prompt_tokens_details.cached_tokens`, or the Anthropic-style * `cache_read_input_tokens` / `cache_creation_input_tokens`) are included ONLY * when the server actually reports them. Returns `undefined` when there is no * usage object at all. */ export declare function mapUsage(usageJson: unknown): ProviderUsage | undefined; /** * Compute the dollar cost of a call from token usage and OPTIONAL config-supplied * pricing. PURE and OFFLINE. With pricing absent (the default, and the only * possibility for a local server) the cost is always `0`. With pricing present * the cost is `(inputTokens / 1e6) * inputPerMTok + (outputTokens / 1e6) * * outputPerMTok`, treating a missing token count as `0`. */ export declare function computeCostUsd(usage: ProviderUsage | undefined, pricing?: { inputPerMTok: number; outputPerMTok: number; }): number; /** * The OpenAI-compatible one-shot provider. Implements the single * {@link LLMProvider} operation by POSTing a Chat Completions request and parsing * + validating the returned text locally against the request schema. */ export declare class OpenAiCompatibleProvider implements LLMProvider { #private; readonly name: string; constructor(options: OpenAiCompatibleProviderOptions); /** * Generate a schema-validated object via the OpenAI-compatible Chat * Completions endpoint. * * Aborts up front if `request.signal` is already aborted. Builds the request * body once (json or prompt mode), then runs the SAME parse/validate loop as * the Anthropic prompt path: POST, read `choices[0].message.content`, * {@link extractJsonObject} + {@link validateAgainstSchema} it; on a * parse/validate MISS re-request ONCE (one initial attempt + one retry); on a * second miss throw {@link OpenAiProviderError} (`openai_parse_failed`). The * provider NEVER resolves with an unvalidated value. Transport-level retries * (429 / 5xx / network) happen INSIDE {@link OpenAiCompatibleProvider#post} * and are independent of this one parse retry; an HTTP/auth/abort failure * propagates straight out. */ generate(request: ProviderRequest): Promise>; }