/** * Model registry - manages built-in and custom models, provides API key resolution. */ import { type OAuthProviderInterface } from "@caupulican/pi-ai/oauth"; import type { Api, AssistantMessageEventStream, Context, Model, SimpleStreamOptions } from "@caupulican/pi-ai/types"; import { type Static, Type } from "typebox"; import type { AuthStatus, AuthStorage } from "./auth-storage.ts"; import { clearConfigValueCache } from "./resolve-config-value.ts"; declare const ModelOverrideSchema: Type.TObject<{ name: Type.TOptional; reasoning: Type.TOptional; defaultThinkingLevel: Type.TOptional, Type.TLiteral<"low">, Type.TLiteral<"medium">, Type.TLiteral<"high">, Type.TLiteral<"xhigh">, Type.TLiteral<"max">, Type.TLiteral<"ultra">]>>; thinkingLevelMap: Type.TOptional>; minimal: Type.TOptional>; low: Type.TOptional>; medium: Type.TOptional>; high: Type.TOptional>; xhigh: Type.TOptional>; max: Type.TOptional>; ultra: Type.TOptional>; }>>; input: Type.TOptional, Type.TLiteral<"image">]>>>; contextWindow: Type.TOptional; textToolCallProtocol: Type.TOptional; maxTokens: Type.TOptional; headers: Type.TOptional>; compat: Type.TOptional, Type.TLiteral<"openai-nosession">, Type.TLiteral<"openrouter">]>>; supportsLongCacheRetention: Type.TOptional; supportsStore: Type.TOptional; supportsDeveloperRole: Type.TOptional; supportsReasoningEffort: Type.TOptional; supportsUsageInStreaming: Type.TOptional; maxTokensField: Type.TOptional, Type.TLiteral<"max_tokens">]>>; requiresToolResultName: Type.TOptional; requiresAssistantAfterToolResult: Type.TOptional; requiresThinkingAsText: Type.TOptional; requiresReasoningContentOnAssistantMessages: Type.TOptional; thinkingFormat: Type.TOptional, Type.TLiteral<"openrouter">, Type.TLiteral<"together">, Type.TLiteral<"deepseek">, Type.TLiteral<"zai">, Type.TLiteral<"qwen">, Type.TLiteral<"qwen-chat-template">]>>; cacheControlFormat: Type.TOptional>; openRouterRouting: Type.TOptional; require_parameters: Type.TOptional; data_collection: Type.TOptional, Type.TLiteral<"allow">]>>; zdr: Type.TOptional; enforce_distillable_text: Type.TOptional; order: Type.TOptional>; only: Type.TOptional>; ignore: Type.TOptional>; quantizations: Type.TOptional>; sort: Type.TOptional; partition: Type.TOptional>; }>]>>; max_price: Type.TOptional>; completion: Type.TOptional>; image: Type.TOptional>; audio: Type.TOptional>; request: Type.TOptional>; }>>; preferred_min_throughput: Type.TOptional; p75: Type.TOptional; p90: Type.TOptional; p99: Type.TOptional; }>]>>; preferred_max_latency: Type.TOptional; p75: Type.TOptional; p90: Type.TOptional; p99: Type.TOptional; }>]>>; }>>; vercelGatewayRouting: Type.TOptional>; order: Type.TOptional>; }>>; supportsStrictMode: Type.TOptional; sendSessionAffinityHeaders: Type.TOptional; }>, Type.TObject<{ sessionAffinityFormat: Type.TOptional, Type.TLiteral<"openai-nosession">, Type.TLiteral<"openrouter">]>>; supportsLongCacheRetention: Type.TOptional; }>, Type.TObject<{ supportsEagerToolInputStreaming: Type.TOptional; supportsLongCacheRetention: Type.TOptional; sendSessionAffinityHeaders: Type.TOptional; supportsCacheControlOnTools: Type.TOptional; supportsTemperature: Type.TOptional; forceAdaptiveThinking: Type.TOptional; }>]>>; cost: Type.TOptional; output: Type.TOptional; cacheRead: Type.TOptional; cacheWrite: Type.TOptional; }>>; }>; type ModelOverride = Static; interface ProviderRequestConfig { apiKey?: string; headers?: Record; authHeader?: boolean; } export type ResolvedRequestAuth = { ok: true; apiKey?: string; headers?: Record; } | { ok: false; error: string; }; /** Clear the config value command cache. Exported for testing. */ export declare const clearApiKeyCache: typeof clearConfigValueCache; /** * Model registry - loads and manages models, resolves API keys via AuthStorage. */ export declare class ModelRegistry { private models; private providerRequestConfigs; private modelRequestHeaders; private registeredProviders; private providerModelScopes; private loadError; readonly authStorage: AuthStorage; private modelsJsonPath; private constructor(); static create(authStorage: AuthStorage, modelsJsonPath?: string): ModelRegistry; static inMemory(authStorage: AuthStorage): ModelRegistry; /** * Reload models from disk (built-in + custom from models.json). */ refresh(): void; /** * Get any error from loading models.json (undefined if no error). */ getError(): string | undefined; private loadModels; /** Load built-in models and apply provider/model overrides */ private loadBuiltInModels; /** Merge custom models into built-in list by provider+id (custom wins on conflicts). */ private mergeCustomModels; private loadCustomModels; private validateConfig; private parseModels; /** * Get all models (built-in + custom). * If models.json had errors, returns only built-in models. */ getAll(): Model[]; /** * Get only models that have auth configured. * This is a fast check that doesn't refresh OAuth tokens. */ getAvailable(): Model[]; /** Restrict one provider to an exact, externally verified set of model IDs. */ setProviderModelScope(provider: string, modelIds: Iterable | undefined): void; getProviderModelScope(provider: string): string[] | undefined; /** * Find a model by provider and ID. */ find(provider: string, modelId: string): Model | undefined; /** * Get API key for a model. */ hasConfiguredAuth(model: Model): boolean; canUseResolvedRequestAuth(model: Model, auth: ResolvedRequestAuth): auth is Extract; private getModelRequestKey; private storeProviderRequestConfig; private storeModelHeaders; /** * Get API key and request headers for a model. */ getApiKeyAndHeaders(model: Model): Promise; /** Recover a locally stored OAuth credential rejected by the provider. */ recoverRejectedOAuthApiKey(providerId: string, rejectedApiKey: string): Promise; /** * Return auth status for a provider, including request auth configured in models.json. * This intentionally does not execute command-backed config values. */ getProviderAuthStatus(provider: string): AuthStatus; /** * Get display name for a provider. */ getProviderDisplayName(provider: string): string; /** * Get API key for a provider. */ getApiKeyForProvider(provider: string): Promise; /** * Check if a model is using OAuth credentials (subscription). */ isUsingOAuth(model: Model): boolean; /** * Check if a model is using OAuth credentials backed by an actual provider subscription * (as opposed to OAuth that merely mints a permanent API key, e.g. OpenRouter). */ isUsingSubscription(model: Model): boolean; /** * Register a provider dynamically (from extensions). * * If provider has models: replaces all existing models for this provider. * If provider has only baseUrl/headers: overrides existing models' URLs. * If provider has oauth: registers OAuth provider for /login support. */ registerProvider(providerName: string, config: ProviderConfigInput): void; /** Capture all mutable registry state before a runtime generation is rebuilt. */ createReloadSnapshot(): ModelRegistryReloadSnapshot; /** Restore a captured generation, including global API/OAuth registrations. */ restoreReloadSnapshot(snapshot: ModelRegistryReloadSnapshot): void; /** * Unregister a previously registered provider. * * Removes the provider from the registry and reloads models from disk so that * built-in models overridden by this provider are restored to their original state. * Also resets dynamic OAuth and API stream registrations before reapplying * remaining dynamic providers. * Has no effect if the provider was never registered. */ unregisterProvider(providerName: string): void; /** Remove a provider generation in one refresh so surviving dynamic streams remain registered. */ unregisterProviders(providerNames: Iterable): void; /** * Upsert a provider config into registeredProviders. * If the provider is already registered, defined values in the incoming config * override existing ones; undefined values are preserved from the stored config. * If the provider is not registered, the incoming config is stored as-is. */ private upsertRegisteredProvider; private validateProviderConfig; private registerProviderIntegrations; private applyProviderConfig; } /** * Input type for registerProvider API. */ export interface ProviderConfigInput { name?: string; baseUrl?: string; apiKey?: string; api?: Api; streamSimple?: (model: Model, context: Context, options?: SimpleStreamOptions) => AssistantMessageEventStream; headers?: Record; authHeader?: boolean; /** OAuth provider for /login support */ oauth?: Omit; models?: Array<{ id: string; name: string; api?: Api; baseUrl?: string; reasoning: boolean; defaultThinkingLevel?: Model["defaultThinkingLevel"]; thinkingLevelMap?: Model["thinkingLevelMap"]; input: ("text" | "image")[]; cost: { input: number; output: number; cacheRead: number; cacheWrite: number; }; contextWindow?: number; textToolCallProtocol?: boolean; maxTokens?: number; headers?: Record; compat?: Model["compat"]; }>; modelOverrides?: Record; } export interface ModelRegistryReloadSnapshot { models: Model[]; providerRequestConfigs: Map; modelRequestHeaders: Map>; registeredProviders: Map; providerModelScopes: Map>; loadError: string | undefined; } export {}; //# sourceMappingURL=model-registry.d.ts.map