import type { EngineAnswer, EngineId, EngineKind } from '../types.js'; import { type HttpPost, type RetryOptions } from './http.js'; export type AskMode = EngineKind; export interface AskOptions { mode?: AskMode; maxTokens?: number; } export interface EngineAdapter { id: EngineId; kind: EngineKind; /** Resolved model id this adapter answers with (used for cost/judge). */ model: string; /** * Whether this engine can actually search when asked for grounded mode. * Mirrors {@link ProviderSpec.supportsGrounded} so cost reservation can tell * a real search from a request for one - an engine that cannot ground is * neither billed nor reserved for searches it will never run. */ supportsGrounded?: boolean; available(): boolean; ask(prompt: string, opts?: AskOptions): Promise; } export interface ParsedResponse { text: string; citations?: string[]; /** The internal search queries the engine ran, where the API exposes them. */ fanoutQueries?: string[]; usage?: { input: number; output: number; }; model?: string; } export interface BuildRequestArgs { prompt: string; model: string; mode: AskMode; apiKey: string; maxTokens?: number; } export interface ProviderSpec { id: EngineId; kind: EngineKind; defaultModel: string; supportsGrounded?: boolean; endpoint: (mode: AskMode, model: string) => string; buildRequest: (args: BuildRequestArgs) => { headers: Record; body: unknown; }; parse: (json: unknown, mode: AskMode) => ParsedResponse; } export interface AdapterDeps { httpPost: HttpPost; /** Resolved API key; undefined means the adapter is unavailable. */ apiKey?: string; /** Override the spec's default model. */ model?: string; /** Clock, injected for deterministic tests. */ now?: () => string; retry?: RetryOptions; } export declare function createAdapter(spec: ProviderSpec, deps: AdapterDeps): EngineAdapter;