From a798cc90c4bc665319ba0dc767306fa6dc9daaf2 Mon Sep 17 00:00:00 2001 From: Ari Webb Date: Wed, 28 Jan 2026 15:04:53 -0800 Subject: [PATCH] fix: openrouter provider (#9166) * fix: openrouter provider * just stage publish api * web openapi --- fern/openapi.json | 90 +++++++++++++++++++++++++++++ letta/schemas/llm_config.py | 12 +++- letta/schemas/model.py | 19 ++++++ letta/services/streaming_service.py | 2 + 4 files changed, 120 insertions(+), 3 deletions(-) diff --git a/fern/openapi.json b/fern/openapi.json index 44fbf5ef..42154985 100644 --- a/fern/openapi.json +++ b/fern/openapi.json @@ -25650,6 +25650,9 @@ { "$ref": "#/components/schemas/BedrockModelSettings" }, + { + "$ref": "#/components/schemas/OpenRouterModelSettings" + }, { "$ref": "#/components/schemas/ChatGPTOAuthModelSettings" } @@ -25666,6 +25669,7 @@ "google_vertex": "#/components/schemas/GoogleVertexModelSettings", "groq": "#/components/schemas/GroqModelSettings", "openai": "#/components/schemas/OpenAIModelSettings", + "openrouter": "#/components/schemas/OpenRouterModelSettings", "together": "#/components/schemas/TogetherModelSettings", "xai": "#/components/schemas/XAIModelSettings", "zai": "#/components/schemas/ZAIModelSettings" @@ -29639,6 +29643,9 @@ { "$ref": "#/components/schemas/BedrockModelSettings" }, + { + "$ref": "#/components/schemas/OpenRouterModelSettings" + }, { "$ref": "#/components/schemas/ChatGPTOAuthModelSettings" } @@ -29655,6 +29662,7 @@ "google_vertex": "#/components/schemas/GoogleVertexModelSettings", "groq": "#/components/schemas/GroqModelSettings", "openai": "#/components/schemas/OpenAIModelSettings", + "openrouter": "#/components/schemas/OpenRouterModelSettings", "together": "#/components/schemas/TogetherModelSettings", "xai": "#/components/schemas/XAIModelSettings", "zai": "#/components/schemas/ZAIModelSettings" @@ -29755,6 +29763,9 @@ { "$ref": "#/components/schemas/BedrockModelSettings" }, + { + "$ref": "#/components/schemas/OpenRouterModelSettings" + }, { "$ref": "#/components/schemas/ChatGPTOAuthModelSettings" } @@ -29771,6 +29782,7 @@ "google_vertex": "#/components/schemas/GoogleVertexModelSettings", "groq": "#/components/schemas/GroqModelSettings", "openai": "#/components/schemas/OpenAIModelSettings", + "openrouter": "#/components/schemas/OpenRouterModelSettings", "together": "#/components/schemas/TogetherModelSettings", "xai": "#/components/schemas/XAIModelSettings", "zai": "#/components/schemas/ZAIModelSettings" @@ -30887,6 +30899,9 @@ { "$ref": "#/components/schemas/BedrockModelSettings" }, + { + "$ref": "#/components/schemas/OpenRouterModelSettings" + }, { "$ref": "#/components/schemas/ChatGPTOAuthModelSettings" } @@ -30903,6 +30918,7 @@ "google_vertex": "#/components/schemas/GoogleVertexModelSettings", "groq": "#/components/schemas/GroqModelSettings", "openai": "#/components/schemas/OpenAIModelSettings", + "openrouter": "#/components/schemas/OpenRouterModelSettings", "together": "#/components/schemas/TogetherModelSettings", "xai": "#/components/schemas/XAIModelSettings", "zai": "#/components/schemas/ZAIModelSettings" @@ -35262,6 +35278,9 @@ { "$ref": "#/components/schemas/BedrockModelSettings" }, + { + "$ref": "#/components/schemas/OpenRouterModelSettings" + }, { "$ref": "#/components/schemas/ChatGPTOAuthModelSettings" } @@ -35278,6 +35297,7 @@ "google_vertex": "#/components/schemas/GoogleVertexModelSettings", "groq": "#/components/schemas/GroqModelSettings", "openai": "#/components/schemas/OpenAIModelSettings", + "openrouter": "#/components/schemas/OpenRouterModelSettings", "together": "#/components/schemas/TogetherModelSettings", "xai": "#/components/schemas/XAIModelSettings", "zai": "#/components/schemas/ZAIModelSettings" @@ -39128,6 +39148,68 @@ "type": "object", "title": "OpenAIReasoning" }, + "OpenRouterModelSettings": { + "properties": { + "max_output_tokens": { + "type": "integer", + "title": "Max Output Tokens", + "description": "The maximum number of tokens the model can generate.", + "default": 4096 + }, + "parallel_tool_calls": { + "type": "boolean", + "title": "Parallel Tool Calls", + "description": "Whether to enable parallel tool calling.", + "default": false + }, + "provider_type": { + "type": "string", + "const": "openrouter", + "title": "Provider Type", + "description": "The type of the provider.", + "default": "openrouter" + }, + "temperature": { + "type": "number", + "title": "Temperature", + "description": "The temperature of the model.", + "default": 0.7 + }, + "response_format": { + "anyOf": [ + { + "oneOf": [ + { + "$ref": "#/components/schemas/TextResponseFormat" + }, + { + "$ref": "#/components/schemas/JsonSchemaResponseFormat" + }, + { + "$ref": "#/components/schemas/JsonObjectResponseFormat" + } + ], + "discriminator": { + "propertyName": "type", + "mapping": { + "json_object": "#/components/schemas/JsonObjectResponseFormat", + "json_schema": "#/components/schemas/JsonSchemaResponseFormat", + "text": "#/components/schemas/TextResponseFormat" + } + } + }, + { + "type": "null" + } + ], + "title": "Response Format", + "description": "The response format for the model." + } + }, + "type": "object", + "title": "OpenRouterModelSettings", + "description": "OpenRouter model configuration (OpenAI-compatible)." + }, "Organization": { "properties": { "id": { @@ -45125,6 +45207,9 @@ { "$ref": "#/components/schemas/BedrockModelSettings" }, + { + "$ref": "#/components/schemas/OpenRouterModelSettings" + }, { "$ref": "#/components/schemas/ChatGPTOAuthModelSettings" } @@ -45141,6 +45226,7 @@ "google_vertex": "#/components/schemas/GoogleVertexModelSettings", "groq": "#/components/schemas/GroqModelSettings", "openai": "#/components/schemas/OpenAIModelSettings", + "openrouter": "#/components/schemas/OpenRouterModelSettings", "together": "#/components/schemas/TogetherModelSettings", "xai": "#/components/schemas/XAIModelSettings", "zai": "#/components/schemas/ZAIModelSettings" @@ -46513,6 +46599,9 @@ { "$ref": "#/components/schemas/BedrockModelSettings" }, + { + "$ref": "#/components/schemas/OpenRouterModelSettings" + }, { "$ref": "#/components/schemas/ChatGPTOAuthModelSettings" } @@ -46529,6 +46618,7 @@ "google_vertex": "#/components/schemas/GoogleVertexModelSettings", "groq": "#/components/schemas/GroqModelSettings", "openai": "#/components/schemas/OpenAIModelSettings", + "openrouter": "#/components/schemas/OpenRouterModelSettings", "together": "#/components/schemas/TogetherModelSettings", "xai": "#/components/schemas/XAIModelSettings", "zai": "#/components/schemas/ZAIModelSettings" diff --git a/letta/schemas/llm_config.py b/letta/schemas/llm_config.py index 4dbc96b0..4a62b2c1 100644 --- a/letta/schemas/llm_config.py +++ b/letta/schemas/llm_config.py @@ -322,9 +322,10 @@ class LLMConfig(BaseModel): GoogleAIModelSettings, GoogleVertexModelSettings, GroqModelSettings, - Model, + ModelSettings, OpenAIModelSettings, OpenAIReasoning, + OpenRouterModelSettings, TogetherModelSettings, XAIModelSettings, ZAIModelSettings, @@ -397,6 +398,11 @@ class LLMConfig(BaseModel): max_output_tokens=self.max_tokens or 4096, temperature=self.temperature, ) + elif self.model_endpoint_type == "openrouter": + return OpenRouterModelSettings( + max_output_tokens=self.max_tokens or 4096, + temperature=self.temperature, + ) elif self.model_endpoint_type == "chatgpt_oauth": return ChatGPTOAuthModelSettings( max_output_tokens=self.max_tokens or 4096, @@ -414,8 +420,8 @@ class LLMConfig(BaseModel): strict=self.strict, ) else: - # If we don't know the model type, use the default Model schema - return Model(max_output_tokens=self.max_tokens or 4096) + # If we don't know the model type, use the base ModelSettings schema + return ModelSettings(max_output_tokens=self.max_tokens or 4096) @classmethod def is_openai_reasoning_model(cls, config: "LLMConfig") -> bool: diff --git a/letta/schemas/model.py b/letta/schemas/model.py index fa657e5e..f5d5fdac 100644 --- a/letta/schemas/model.py +++ b/letta/schemas/model.py @@ -140,6 +140,7 @@ class Model(LLMConfig, ModelBase): ProviderType.deepseek: DeepseekModelSettings, ProviderType.together: TogetherModelSettings, ProviderType.bedrock: BedrockModelSettings, + ProviderType.openrouter: OpenRouterModelSettings, } settings_class = PROVIDER_SETTINGS_MAP.get(self.provider_type) @@ -458,6 +459,23 @@ class BedrockModelSettings(ModelSettings): } +class OpenRouterModelSettings(ModelSettings): + """OpenRouter model configuration (OpenAI-compatible).""" + + provider_type: Literal[ProviderType.openrouter] = Field(ProviderType.openrouter, description="The type of the provider.") + temperature: float = Field(0.7, description="The temperature of the model.") + response_format: Optional[ResponseFormatUnion] = Field(None, description="The response format for the model.") + + def _to_legacy_config_params(self) -> dict: + return { + "temperature": self.temperature, + "max_tokens": self.max_output_tokens, + "response_format": self.response_format, + "parallel_tool_calls": self.parallel_tool_calls, + "strict": False, # OpenRouter does not support strict mode + } + + class ChatGPTOAuthReasoning(BaseModel): """Reasoning configuration for ChatGPT OAuth models (GPT-5.x, o-series).""" @@ -497,6 +515,7 @@ ModelSettingsUnion = Annotated[ DeepseekModelSettings, TogetherModelSettings, BedrockModelSettings, + OpenRouterModelSettings, ChatGPTOAuthModelSettings, ], Field(discriminator="provider_type"), diff --git a/letta/services/streaming_service.py b/letta/services/streaming_service.py index b5fe0d87..82057622 100644 --- a/letta/services/streaming_service.py +++ b/letta/services/streaming_service.py @@ -517,6 +517,7 @@ class StreamingService: "deepseek", "chatgpt_oauth", "minimax", + "openrouter", ] def _is_token_streaming_compatible(self, agent: AgentState) -> bool: @@ -529,6 +530,7 @@ class StreamingService: "zai", "chatgpt_oauth", "minimax", + "openrouter", ] google_letta_v1 = agent.agent_type == AgentType.letta_v1_agent and agent.llm_config.model_endpoint_type in [ "google_ai",