diff --git a/src/backend/base/langflow/components/models/__init__.py b/src/backend/base/langflow/components/models/__init__.py
index e15ce28fd..b757bd2c6 100644
--- a/src/backend/base/langflow/components/models/__init__.py
+++ b/src/backend/base/langflow/components/models/__init__.py
@@ -13,6 +13,7 @@ from .mistral import MistralAIModelComponent
from .nvidia import NVIDIAModelComponent
from .ollama import ChatOllamaComponent
from .openai import OpenAIModelComponent
+from .openrouter import OpenRouterComponent
from .perplexity import PerplexityComponent
from .sambanova import SambaNovaComponent
from .vertexai import ChatVertexAIComponent
@@ -33,6 +34,7 @@ __all__ = [
"MistralAIModelComponent",
"NVIDIAModelComponent",
"OpenAIModelComponent",
+ "OpenRouterComponent",
"PerplexityComponent",
"QianfanChatEndpointComponent",
"SambaNovaComponent",
diff --git a/src/backend/base/langflow/components/models/openrouter.py b/src/backend/base/langflow/components/models/openrouter.py
new file mode 100644
index 000000000..45d945b92
--- /dev/null
+++ b/src/backend/base/langflow/components/models/openrouter.py
@@ -0,0 +1,197 @@
+from collections import defaultdict
+from typing import Any
+
+import httpx
+from langchain_openai import ChatOpenAI
+from pydantic.v1 import SecretStr
+
+from langflow.base.models.model import LCModelComponent
+from langflow.field_typing import LanguageModel
+from langflow.field_typing.range_spec import RangeSpec
+from langflow.inputs import (
+ DropdownInput,
+ IntInput,
+ SecretStrInput,
+ SliderInput,
+ StrInput,
+)
+
+
+class OpenRouterComponent(LCModelComponent):
+ """OpenRouter API component for language models."""
+
+ display_name = "OpenRouter"
+ description = (
+ "OpenRouter provides unified access to multiple AI models " "from different providers through a single API."
+ )
+ icon = "OpenRouter"
+
+ inputs = [
+ *LCModelComponent._base_inputs,
+ SecretStrInput(
+ name="api_key", display_name="OpenRouter API Key", required=True, info="Your OpenRouter API key"
+ ),
+ StrInput(
+ name="site_url",
+ display_name="Site URL",
+ info="Your site URL for OpenRouter rankings",
+ advanced=True,
+ ),
+ StrInput(
+ name="app_name",
+ display_name="App Name",
+ info="Your app name for OpenRouter rankings",
+ advanced=True,
+ ),
+ DropdownInput(
+ name="provider",
+ display_name="Provider",
+ info="The AI model provider",
+ options=["Loading providers..."],
+ value="Loading providers...",
+ real_time_refresh=True,
+ ),
+ DropdownInput(
+ name="model_name",
+ display_name="Model",
+ info="The model to use for chat completion",
+ options=["Select a provider first"],
+ value="Select a provider first",
+ real_time_refresh=True,
+ ),
+ SliderInput(
+ name="temperature", display_name="Temperature", value=0.7, range_spec=RangeSpec(min=0, max=2, step=0.01)
+ ),
+ IntInput(
+ name="max_tokens",
+ display_name="Max Tokens",
+ info="Maximum number of tokens to generate",
+ advanced=True,
+ ),
+ ]
+
+ def fetch_models(self) -> dict[str, list]:
+ """Fetch available models from OpenRouter API and organize them by provider."""
+ url = "https://openrouter.ai/api/v1/models"
+
+ try:
+ with httpx.Client() as client:
+ response = client.get(url)
+ response.raise_for_status()
+
+ models_data = response.json().get("data", [])
+ provider_models = defaultdict(list)
+
+ for model in models_data:
+ model_id = model.get("id", "")
+ if "/" in model_id:
+ provider = model_id.split("/")[0].title()
+ provider_models[provider].append(
+ {
+ "id": model_id,
+ "name": model.get("name", ""),
+ "description": model.get("description", ""),
+ "context_length": model.get("context_length", 0),
+ }
+ )
+
+ return dict(provider_models)
+
+ except httpx.HTTPError as e:
+ self.log(f"Error fetching models: {e!s}")
+ return {"Error": [{"id": "error", "name": f"Error fetching models: {e!s}"}]}
+
+ def build_model(self) -> LanguageModel:
+ """Build and return the OpenRouter language model."""
+ model_not_selected = "Please select a model"
+ api_key_required = "API key is required"
+
+ if not self.model_name or self.model_name == "Select a provider first":
+ raise ValueError(model_not_selected)
+
+ if not self.api_key:
+ raise ValueError(api_key_required)
+
+ api_key = SecretStr(self.api_key).get_secret_value()
+
+ # Build base configuration
+ kwargs: dict[str, Any] = {
+ "model": self.model_name,
+ "openai_api_key": api_key,
+ "openai_api_base": "https://openrouter.ai/api/v1",
+ "temperature": self.temperature if self.temperature is not None else 0.7,
+ }
+
+ # Add optional parameters
+ if self.max_tokens:
+ kwargs["max_tokens"] = self.max_tokens
+
+ headers = {}
+ if self.site_url:
+ headers["HTTP-Referer"] = self.site_url
+ if self.app_name:
+ headers["X-Title"] = self.app_name
+
+ if headers:
+ kwargs["default_headers"] = headers
+
+ try:
+ return ChatOpenAI(**kwargs)
+ except (ValueError, httpx.HTTPError) as err:
+ error_msg = f"Failed to build model: {err!s}"
+ self.log(error_msg)
+ raise ValueError(error_msg) from err
+
+ def _get_exception_message(self, e: Exception) -> str | None:
+ """Get a message from an OpenRouter exception.
+
+ Args:
+ e (Exception): The exception to get the message from.
+
+ Returns:
+ str | None: The message from the exception, or None if no specific message can be extracted.
+ """
+ try:
+ from openai import BadRequestError
+
+ if isinstance(e, BadRequestError):
+ message = e.body.get("message")
+ if message:
+ return message
+ except ImportError:
+ pass
+ return None
+
+ def update_build_config(self, build_config: dict, field_value: str, field_name: str | None = None) -> dict:
+ """Update build configuration based on field updates."""
+ try:
+ if field_name is None or field_name == "provider":
+ provider_models = self.fetch_models()
+ build_config["provider"]["options"] = sorted(provider_models.keys())
+ if build_config["provider"]["value"] not in provider_models:
+ build_config["provider"]["value"] = build_config["provider"]["options"][0]
+
+ if field_name == "provider" and field_value in self.fetch_models():
+ provider_models = self.fetch_models()
+ models = provider_models[field_value]
+
+ build_config["model_name"]["options"] = [model["id"] for model in models]
+ if models:
+ build_config["model_name"]["value"] = models[0]["id"]
+
+ tooltips = {
+ model["id"]: (
+ f"{model['name']}\n" f"Context Length: {model['context_length']}\n" f"{model['description']}"
+ )
+ for model in models
+ }
+ build_config["model_name"]["tooltips"] = tooltips
+
+ except httpx.HTTPError as e:
+ self.log(f"Error updating build config: {e!s}")
+ build_config["provider"]["options"] = ["Error loading providers"]
+ build_config["provider"]["value"] = "Error loading providers"
+ build_config["model_name"]["options"] = ["Error loading models"]
+ build_config["model_name"]["value"] = "Error loading models"
+
+ return build_config
diff --git a/src/frontend/src/icons/OpenRouter/OpenRouterIcon.jsx b/src/frontend/src/icons/OpenRouter/OpenRouterIcon.jsx
new file mode 100644
index 000000000..e16339149
--- /dev/null
+++ b/src/frontend/src/icons/OpenRouter/OpenRouterIcon.jsx
@@ -0,0 +1,23 @@
+const SvgOpenRouter = (props) => (
+
+);
+export default SvgOpenRouter;
diff --git a/src/frontend/src/icons/OpenRouter/index.tsx b/src/frontend/src/icons/OpenRouter/index.tsx
new file mode 100644
index 000000000..fc79d74a5
--- /dev/null
+++ b/src/frontend/src/icons/OpenRouter/index.tsx
@@ -0,0 +1,9 @@
+import React, { forwardRef } from "react";
+import SvgOpenRouter from "./OpenRouterIcon";
+
+export const OpenRouterIcon = forwardRef<
+ SVGSVGElement,
+ React.PropsWithChildren<{}>
+>((props, ref) => {
+ return ;
+});
diff --git a/src/frontend/src/icons/OpenRouter/openrouter.svg b/src/frontend/src/icons/OpenRouter/openrouter.svg
new file mode 100644
index 000000000..1f4b1dad0
--- /dev/null
+++ b/src/frontend/src/icons/OpenRouter/openrouter.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/frontend/src/utils/styleUtils.ts b/src/frontend/src/utils/styleUtils.ts
index 88216eedc..d8d11b344 100644
--- a/src/frontend/src/utils/styleUtils.ts
+++ b/src/frontend/src/utils/styleUtils.ts
@@ -282,6 +282,7 @@ import { NotionIcon } from "../icons/Notion";
import { NvidiaIcon } from "../icons/Nvidia";
import { OllamaIcon } from "../icons/Ollama";
import { OpenAiIcon } from "../icons/OpenAi";
+import { OpenRouterIcon } from "../icons/OpenRouter";
import { OpenSearch } from "../icons/OpenSearch";
import { PineconeIcon } from "../icons/Pinecone";
import { PostgresIcon } from "../icons/Postgres";
@@ -666,6 +667,7 @@ export const nodeIconsLucide: iconsType = {
ChatOpenAI: OpenAiIcon,
AzureChatOpenAI: OpenAiIcon,
OpenAI: OpenAiIcon,
+ OpenRouter: OpenRouterIcon,
OpenAIEmbeddings: OpenAiIcon,
Pinecone: PineconeIcon,
Qdrant: QDrantIcon,