diff --git a/packages/core/src/agent/providers/registry.ts b/packages/core/src/agent/providers/registry.ts index 4ca7ec54..105a4190 100644 --- a/packages/core/src/agent/providers/registry.ts +++ b/packages/core/src/agent/providers/registry.ts @@ -114,8 +114,8 @@ const PROVIDER_REGISTRY: Record = { id: "openrouter", name: "OpenRouter", authMethod: "api-key", - defaultModel: "anthropic/claude-sonnet-4-5", - models: ["anthropic/claude-sonnet-4-5", "anthropic/claude-opus-4-5", "openai/gpt-4o"], + defaultModel: "anthropic/claude-sonnet-4.5", + models: ["anthropic/claude-sonnet-4.5", "anthropic/claude-opus-4.5", "openai/gpt-4o"], loginUrl: "https://openrouter.ai/keys", }, }; diff --git a/packages/core/src/agent/providers/resolver.ts b/packages/core/src/agent/providers/resolver.ts index 7a18ef88..f9db831f 100644 --- a/packages/core/src/agent/providers/resolver.ts +++ b/packages/core/src/agent/providers/resolver.ts @@ -203,6 +203,25 @@ export function resolveApiKeyForProvider( // Model Resolution // ============================================================ +/** + * Create a fallback OpenRouter model config for models not in pi-ai's registry. + * OpenRouter supports thousands of models; we can't have all of them pre-registered. + */ +function createOpenRouterFallbackModel(modelId: string) { + return { + id: modelId, + name: modelId, + api: "openai-completions" as const, + provider: "openrouter" as const, + baseUrl: "https://openrouter.ai/api/v1", + reasoning: false, + input: ["text" as const], + cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, + contextWindow: 128000, + maxTokens: 16384, + }; +} + /** * Resolve model for pi-ai based on provider and options. */ @@ -212,10 +231,17 @@ export function resolveModel(options: AgentOptions) { const actualProvider = PROVIDER_ALIAS[options.provider] ?? options.provider; // Type assertion needed because provider/model come from dynamic user config - return (getModel as (p: string, m: string) => ReturnType)( + const model = (getModel as (p: string, m: string) => ReturnType | undefined)( actualProvider, options.model, ); + + // Fallback for OpenRouter: allow any model ID even if not in pi-ai registry + if (!model && actualProvider === "openrouter") { + return createOpenRouterFallbackModel(options.model); + } + + return model; } // If only provider specified, use default model for that provider @@ -223,10 +249,16 @@ export function resolveModel(options: AgentOptions) { const actualProvider = PROVIDER_ALIAS[options.provider] ?? options.provider; const defaultModel = getDefaultModel(options.provider) ?? getDefaultModel(actualProvider); if (defaultModel) { - return (getModel as (p: string, m: string) => ReturnType)( + const model = (getModel as (p: string, m: string) => ReturnType | undefined)( actualProvider, defaultModel, ); + + if (!model && actualProvider === "openrouter") { + return createOpenRouterFallbackModel(defaultModel); + } + + return model; } }