import type { ServiceAccount } from './gcp-auth.js'; import type { Locale } from './i18n/types.js'; import type { LlmProvider, OptimizationResult, TokenCounter } from './types.js'; /** * Optional LLM layer. * * The deterministic core already does the work at zero cost. This pass adds * the semantic compression rules cannot do — rewriting a whole sentence, * merging two instructions that say the same thing in different words — and * that is why it costs one call. * * The provider is pluggable on purpose: your own hosted model, an * OpenAI-compatible endpoint, the Claude API or anything else behind * `customProvider`. */ export declare const REFINER_SYSTEM_PROMPT = "You rewrite prompts so they cost fewer tokens without changing what they ask for.\n\nRules:\n- Preserve exactly the same task, constraints, output format and success criteria.\n- Copy verbatim, without changing a single character: code blocks, URLs, template placeholders ({{x}}, ${x}, {x}) and XML/HTML tags.\n- Do not summarise and do not drop requirements. When in doubt about whether something is a requirement, keep it.\n- Remove redundancy, padding and repetition. Merge duplicated instructions.\n- Keep the original language of the prompt.\n- Return ONLY the rewritten prompt. No explanations, no commentary, no code fences wrapping the answer."; export interface RefineOptions { /** * Minimum fraction of tokens the result must keep (0-1). Below this * threshold the model is assumed to have summarised rather than compressed, * and the candidate is rejected. Defaults to 0.25. */ minRetainRatio?: number; tokenCounter?: TokenCounter; /** Language of the rejection reason. Defaults to the result's locale. */ locale?: Locale; } /** * Runs the already-optimised prompt through the LLM and accepts the result * only if it passes the safety checks. * * It never returns a prompt worse than the deterministic one: if the candidate * loses protected content, grows in tokens or shrinks suspiciously, it is * discarded and the previous result stands. */ export declare function refineWithLlm(result: OptimizationResult, provider: LlmProvider, options?: RefineOptions): Promise; export interface OpenAiCompatibleOptions { /** Base URL, without `/chat/completions`. E.g. `https://llm.example.com/v1` */ baseUrl: string; apiKey?: string; model: string; /** Extra headers, in case your gateway requires its own. */ headers?: Record; /** Name shown in the report. */ name?: string; maxTokens?: number; fetchImpl?: typeof fetch; /** * Allow http and private hosts — localhost, the RFC1918 ranges, the cloud * metadata address. * * **Only when the operator chose the URL.** That is the whole distinction. An * endpoint from `TRAZUM_LLM_BASE_URL` is somebody configuring their own * machine, and pointing it at `http://localhost:11434` for Ollama is the * documented normal case. An endpoint arriving in an HTTP request body is a * stranger naming a host for this server to fetch, which is server-side * request forgery whatever else it is called. */ allowInsecure?: boolean; } /** * The endpoint check lives in `net.ts`, beside the validator and beside the * `fetch` options every server-side call here carries. * * At construction rather than at call time, so a provider that can never work * does not exist to be handed around. * * The web route already validates a body-supplied URL before it gets here, and * that stays — it turns the reason code into a sentence in the reader's * language. This is the second lock, at the boundary. `openAiCompatible` is an * exported library function, so "the caller checks" is a promise about every * future caller, including ones outside this repository. */ /** * Any endpoint speaking OpenAI's `/chat/completions` format. Covers vLLM, * Ollama, OpenRouter, LM Studio, Together and most internal gateways. */ export declare function openAiCompatible(options: OpenAiCompatibleOptions): LlmProvider; export interface AnthropicProviderOptions { apiKey: string; model?: string; baseUrl?: string; maxTokens?: number; fetchImpl?: typeof fetch; /** See `OpenAiCompatibleOptions.allowInsecure`: only when you chose the URL. */ allowInsecure?: boolean; } /** The Claude API directly, via `/v1/messages`. */ export declare function anthropicProvider(options: AnthropicProviderOptions): LlmProvider; export interface GeminiProviderOptions { apiKey: string; /** Default: `gemini-2.5-pro`. */ model?: string; /** Default: Google's public endpoint. */ baseUrl?: string; maxTokens?: number; fetchImpl?: typeof fetch; /** See `OpenAiCompatibleOptions.allowInsecure`: only when you chose the URL. */ allowInsecure?: boolean; } /** * Gemini directly, via `generateContent`. * * The one provider on the list that needs its own function rather than the * OpenAI-compatible path. Everything else — Groq, Together, Fireworks, * DeepInfra, Cerebras, SiliconFlow, OpenRouter, LiteLLM — speaks the OpenAI * shape, so `openAiCompatibleProvider` with a base URL is the whole * integration. Google's is a different document: the system prompt is * `systemInstruction` rather than a message, turns are `contents` with `parts`, * and the answer is the first candidate's parts joined. * * Three failure modes that are not HTTP errors, and each has bitten somebody: * * - **A safety block returns 200.** `promptFeedback.blockReason` arrives with no * candidates at all, so reading `candidates[0]` gives `undefined` and the * caller sees "no text" for what is actually a refusal. * - **`finishReason: MAX_TOKENS` also returns 200**, with a truncated answer. * For a rewrite pass that is worse than an error: the text looks like a * result and is half a result. * - **Parts can be empty.** A candidate with no text part is a valid document * and not a valid answer. * * The key goes in a header, not the query string. Google's own examples put it * in `?key=`, which puts a credential in every proxy log and referrer between * here and there. */ export declare function geminiProvider(options: GeminiProviderOptions): LlmProvider; export interface BedrockProviderOptions { /** e.g. `anthropic.claude-sonnet-4-5-20250929-v1:0`. */ model: string; region: string; accessKeyId: string; secretAccessKey: string; /** For temporary credentials from STS or an instance role. */ sessionToken?: string; maxTokens?: number; /** Override the host. Defaults to the regional Bedrock runtime endpoint. */ baseUrl?: string; fetchImpl?: typeof fetch; allowInsecure?: boolean; /** Injectable for tests; the signature is a function of the clock. */ now?: () => Date; } /** * Amazon Bedrock, through **Converse** rather than `InvokeModel`. * * That choice is the whole reason this is one provider instead of six. * `InvokeModel` takes a body in each model family's own shape — Anthropic's * `messages` with `anthropic_version`, Meta's `prompt`, Amazon's * `inputText` — so supporting "Bedrock" through it means supporting each vendor * separately and getting a 400 for every model nobody thought about. `Converse` * is Bedrock's unified surface: one request shape, one response shape, every * model that supports it. * * Signed with SigV4 by hand — see `aws-sigv4.ts` for why there is no SDK here * and what the tests do and do not prove. * * `stopReason: 'max_tokens'` throws, for the same reason it does on Gemini: a * truncated rewrite reads exactly like a finished one, and that is the failure * this package exists to refuse. */ export declare function bedrockProvider(options: BedrockProviderOptions): LlmProvider; export interface VertexProviderOptions { /** The parsed contents of a service-account JSON key. */ serviceAccount: ServiceAccount; project: string; /** e.g. `us-central1`. `global` is also valid for some models. */ location: string; /** Default: `gemini-2.5-pro`. */ model?: string; /** Default: `google`. `anthropic` for Claude on Vertex. */ publisher?: string; maxTokens?: number; baseUrl?: string; fetchImpl?: typeof fetch; allowInsecure?: boolean; now?: () => Date; } /** * Gemini through Vertex AI, with a service account instead of an API key. * * Vertex will not take an API key, which is the whole difference from * `geminiProvider`: the credential is a signed assertion traded for an access * token that lasts an hour. `gcp-auth.ts` does that, caches the token, and * explains why there is no SDK. * * The response shape is Gemini's, so the same three HTTP-200 failures apply and * are refused the same way — a blocked prompt, a truncated answer, an empty * candidate. The parsing is shared with `geminiProvider` rather than copied, * because two copies of "is this answer complete" is one copy too many. */ export declare function vertexProvider(options: VertexProviderOptions): LlmProvider; export interface CustomProviderOptions { name: string; model: string; /** Builds the HTTP request from the system and user prompts. */ request(input: { system: string; user: string; }): { url: string; init: RequestInit; }; /** Extracts the text from the already-parsed response body. */ extract(body: unknown): string; fetchImpl?: typeof fetch; } /** * Escape hatch: if your endpoint speaks none of the formats above, you define * how the request is built and how the response is read, and everything else * keeps working the same. */ export declare function customProvider(options: CustomProviderOptions): LlmProvider; /** * Builds a provider from environment variables. * * TRAZUM_LLM_PROVIDER openai | anthropic (default: openai) * TRAZUM_LLM_BASE_URL base URL of the endpoint * TRAZUM_LLM_API_KEY key, when one is needed * TRAZUM_LLM_MODEL model identifier * * Returns `null` when the configuration is incomplete, so the tool keeps * working in deterministic mode instead of failing. */ export declare function providerFromEnv(env?: Record): LlmProvider | null; //# sourceMappingURL=llm.d.ts.map