import type { LLMProvider, ProviderRuntimeMetadata } from './interface.js'; import { type ProviderCapability, type RequestProfile, type RouteExplanation } from './capabilities.js'; import type { DiscoveredServer } from '../discovery/scanner.js'; import type { RuntimeEventBus } from '../runtime/events/index.js'; import { type CatalogModel, type CatalogModelPricing, type MinimalModelDefinition } from './model-catalog.js'; import { type ResolvedModelPricing } from './model-pricing.js'; import type { CanonicalModel } from './synthetic.js'; import type { LiveModelDiscoveryResult } from './live-model-discovery.js'; import type { ModelDefinition, ProviderRegistryOptions, RuntimeProviderRegistration, TokenLimits } from './registry-types.js'; export type { ContextWindowProvenance, ModelDefinition, ModelTier, ProviderRegistryOptions, RuntimeProviderRegistration, TokenLimits, } from './registry-types.js'; /** * ProviderRegistry, manages LLM provider instances and model selection. * Lazily instantiates providers on first use. */ export declare class ProviderRegistry { private providers; private currentModelRegistryKey; private readonly configuredModel; private discoveredProviderNames; /** The servers the last registerDiscoveredProviders call was given, verbatim. */ private discoveredServers; private runtimeProviderNames; private customModels; private runtimeModels; private discoveredModels; /** Provider-sourced models, see registry-live-model-discovery.ts. */ private providerNativeModels; private readonly runtimeCatalogSuppressedRegistryKeys; private _watcher; private _readyPromise; private readonly configManager; private readonly subscriptionManager; private readonly capabilityRegistry; private readonly cacheHitTracker; private readonly featureFlags; private readonly favoritesStore; private readonly benchmarkStore; private readonly modelLimitsService; private readonly gatewayPricing; private readonly runtimeMetadataDeps; private readonly runtimeBus; private readonly localContextIngestionService; private catalogModels; private pricingCatalog; private syntheticCanonicalModels; private _cachedModelRegistry; private _modelRegistryRevision; /** Persisted per-model context-window overrides; lazy-constructed (needs persistence root). */ private _contextWindowOverrideStore; constructor(options: ProviderRegistryOptions); private registerBuiltins; /** * Live credential refresh: re-resolves every builtin key (env -> secrets * store) and force re-registers builtins, so a key written to the secrets * store is usable in the SAME process. Runs at boot and on secrets changes. */ refreshProviderCredentials(): Promise; private getPersistenceRoot; private getCustomProvidersDir; private getCatalogCachePaths; private getCatalogBuiltins; private getSyntheticBuiltins; private updateCatalogState; private getSuppressedCatalogModelRegistryKeys; private _invalidateModelRegistry; private getModelRegistry; private contextWindowOverrideStore; /** Register a provider. Overwrites any existing entry with the same name. Fails closed on a dead model source (model-source-contract.ts). */ register(provider: LLMProvider): void; /** The picker-open re-check hook (see registry-live-model-discovery.ts): force bypasses each provider's TTL cache. */ refreshLiveModelDiscovery(providerId?: string, options?: { force?: boolean; }): Promise>; /** Register a runtime/plugin-owned provider + optional models; returns an unregister callback. */ registerRuntimeProvider(registration: RuntimeProviderRegistration): () => void; /** * The locally discovered servers this registry currently holds. * * Exposed because a process may build a SECOND registry that has to see the * same local models, the daemon composes one per hosted-session workspace, * and without this the machine's own Ollama or LM Studio would be routable * for the daemon's agents and invisible to a hosted session on the same box. * Returns what was registered, so a caller re-registers the same servers * rather than reconstructing them from provider objects. */ listDiscoveredServers(): readonly DiscoveredServer[]; listProviders(): readonly LLMProvider[]; /** * Register providers discovered by the local LLM scanner. * Clears previously discovered providers before re-registering. * Does not overwrite built-in or custom-loaded providers/models. */ registerDiscoveredProviders(servers: DiscoveredServer[]): void; /** * Returns `true` if a provider with the given name (or its catalog alias) is registered. * * @param id - Provider name as registered (e.g. `'anthropic'`, `'openai'`). */ has(id: string): boolean; /** * Retrieve a provider by name, applying subscription route aliasing. * Throws a {@link ProviderNotFoundError} if no provider with that name is registered. * * The error message includes the list of all currently registered provider IDs * to aid discoverability. * * @param id - Provider name as registered (e.g. `'anthropic'`, `'openai'`). * @throws {ProviderNotFoundError} When no matching provider is registered. */ require(id: string): LLMProvider; /** * Retrieve a provider by name, applying subscription route aliasing. * Returns `undefined` if no provider with that name (or its catalog alias) is registered. * * Prefer {@link require} when you know the provider must exist and want a clear * error when it does not. * * @param name - Provider name as registered (e.g. `'anthropic'`, `'openai'`). */ tryGet(name: string): LLMProvider | undefined; get(name: string): LLMProvider | undefined; /** * Retrieve the directly-registered provider without subscription route aliasing. * Throws if no provider with that name (or its catalog alias) is registered. * * Note: Unlike {@link require}, this method does NOT apply subscription route aliasing * (e.g. the openai-subscriber redirect). Prefer {@link require} for general use. */ getRegistered(name: string): LLMProvider; describeRuntime(name: string): Promise; /** Return the provider responsible for a model, a registryKey, or a bare id resolved via the shared resolver. */ getForModel(modelId: string, provider?: string): LLMProvider; /** All registered model definitions. */ listModels(): ModelDefinition[]; /** Legacy string-keyed catalog lookup, null when unpriced. Prefer resolveModelPricing. */ getCostFromCatalog(modelId: string): CatalogModelPricing | null; /** * ONE pricing resolution per (provider, model): manual config price ('user') * -> registration price ('user') -> provider-served ('provider', dated) -> * catalog ('catalog', dated) -> subscription -> honest unknown (never $0). * Manual prices are read live on every call, no restart. */ resolveModelPricing(modelRef: string, providerId?: string): ResolvedModelPricing; getContextWindowForModel(modelDef: ModelDefinition): number; getTokenLimitsForModel(modelDef: ModelDefinition): Required; getPricingForModel(modelId: string, provider: string): { prompt: number; completion: number; } | null; getToolResultMaxCharsForModel(model: ModelDefinition | null | undefined): number; initModelLimits(): void; refreshModelLimits(): Promise; getRawCatalogModels(): readonly CatalogModel[]; getCatalogModelDefinitions(): readonly MinimalModelDefinition[]; getSyntheticModelDefinitions(): readonly MinimalModelDefinition[]; getSyntheticCanonicalModels(): readonly CanonicalModel[]; getSyntheticBackendModelIds(): Set; getSyntheticModelInfoFromCatalog(modelId: string): import("./model-catalog-synthetic.js").SyntheticModelInfo | null; getConfiguredProviderIds(): string[]; /** Only the models the user can switch to. */ getSelectableModels(): ModelDefinition[]; /** Currently active model definition. Follows a `provider.model` write made after startup. */ getCurrentModel(): ModelDefinition; /** * Synthesize a minimal model definition for the configured registryKey when * the catalog-backed registry hasn't materialized it yet, e.g. a fresh * daemon home before the models.dev catalog fetch has completed (or while * offline, where it never will). `buildModelRegistry()` only draws from * custom/runtime/synthetic/catalog/discovered models, none of which are * populated synchronously at construction time, so a stock default like * 'openrouter:openrouter/free' can otherwise be unresolvable for the entire * lifetime of a catalog-less boot. * * Deliberately narrow: only resolves when `providerId` is an actually * registered provider AND that provider's own static `models` list already * declares `resolvedModelId` (e.g. the builtin openrouter provider declares * 'openrouter/free' in builtin-registry.ts). A genuinely unknown or * misconfigured registryKey still falls through to the "not in registry" * error below so callers keep an honest signal instead of a guess. */ private buildConfiguredModelFallback; /** * Set a user-configured context window (tokens) for any model, custom, * discovered, or catalog. Applied as a 'configured_cap' overlay and * persisted to the control-plane config dir, so it survives restarts and * reaches every consumer of the same home. Keys for models not yet * registered are allowed, discovered models pick the override up when * they materialize. */ setModelContextCap(registryKey: string, cap: number): void; /** * Clear the user-configured context window AND any learned provider limit, * returning the model to fully automatic resolution (catalog / provider * API / family fallback). Returns true when anything was cleared. */ clearModelContextCap(registryKey: string): boolean; /** The user-configured context window for a model, or null when automatic. */ getModelContextCap(registryKey: string): number | null; /** The learned provider context ceiling for a model, or null when none observed. */ getObservedContextWindow(registryKey: string): number | null; /** * A provider rejected a request of ~`rejectedAtTokens` as too long, learn * that ceiling so window math, compaction thresholds, and meters use the * endpoint's REAL limit instead of an over-stated catalog value. */ recordContextWindowRejection(registryKey: string, rejectedAtTokens: number): void; /** A request with real billed input succeeded, raise a too-pessimistic learned ceiling. */ reconcileObservedContextWindow(registryKey: string, successfulInputTokens: number): void; /** Switch to a different model. Accepts a registryKey or a bare model id (resolved via the shared resolver). */ setCurrentModel(modelReference: string): void; /** * Load custom providers from the configured providers directory and merge them * into the live model registry. Returns any warnings collected during loading. * Call this after construction to populate custom providers. */ loadCustomProviders(): Promise<{ warnings: string[]; added: string[]; removed: string[]; updated: string[]; }>; /** * Start watching the configured providers directory for file changes. * On change, reloads custom providers and emits typed provider runtime events. * Safe to call multiple times, stops the previous watcher first. */ startWatching(runtimeBus?: RuntimeEventBus | null): void; /** Stop the file watcher started by startWatching(). */ stopWatching(): void; /** * Returns a promise that resolves when the initial custom provider load * completes. Callers can await this before looking up a custom registryKey. */ ready(): Promise; /** * Find an alternative model when the current provider fails non-transiently. * Prefers a synthetic failover wrapper; falls back to same-tier model on a different provider. */ findAlternativeModel(currentRegistryKey: string): ModelDefinition | null; /** * Resolve the full capability record for a model. * * @param modelReference - A provider-qualified registryKey (`provider:modelId`) or a bare model id (resolved via the shared resolver). * @returns A fully-resolved, immutable `ProviderCapability`. */ getCapabilityForModel(modelReference: string): ProviderCapability; /** * Check whether a model can handle a request described by `profile`. * Fails early with a typed explanation when unsupported, avoids mid-stream errors. * * @param modelReference - A provider-qualified registryKey or a bare model id (resolved via the shared resolver). * @param profile - The capability requirements for this request. * @returns A `RouteExplanation` with `accepted` flag, rejections, and capability. */ explainRoute(modelReference: string, profile: RequestProfile): RouteExplanation; /** * Resolve the provider identity and instance for a registryKey. * Shared by `getCapabilityForModel` and `explainRoute` to avoid duplication. * * @param registryKey - Provider-qualified registryKey (`provider:modelId`). */ private _resolveModelContext; /** Kick off async custom provider loading. Called once from bootstrap-owned construction. */ initCustomProviders(): void; initCatalog(): void; /** Background, TTL-respecting live model discovery sweep (never blocks/throws); separate from initCatalog() so tests calling that without mocking a provider API aren't surprised by a live network call. Real callers invoke this once at startup too. */ initProviderModelDiscovery(): void; refreshCatalog(): Promise; private _catalogLifecycleCtx; } //# sourceMappingURL=registry.d.ts.map