deepagent-bot
ci: sync dashboard from 6cb6620
e1dc207
Raw
History Blame Contribute Delete
4.97 kB
"""LLM factory β€” resolves a DEEPAGENT_MODEL string into a LangChain BaseChatModel.
Every model returned is wired with the ``CostCallback`` so token/cost metrics
flow through the observability stack automatically.
Supported model specs:
ollama:<model> β†’ ChatOllama, OLLAMA_BASE_URL
openrouter:<model> β†’ ChatOpenAI with base_url=openrouter.ai
anthropic:<model> β†’ langchain init_chat_model
openai:<model> β†’ langchain init_chat_model
google_genai:<model> β†’ langchain init_chat_model
groq:<model> β†’ langchain init_chat_model
<anything else with ":"> β†’ forwarded to init_chat_model verbatim
"""
from __future__ import annotations
import os
from typing import Any
from langchain.chat_models import init_chat_model
from .config import get_settings
OPENROUTER_BASE_URL = "https://openrouter.ai/api/v1"
def _attach_callbacks(model):
"""Attach the cost callback to a chat model. Idempotent."""
try:
from .observability.cost import CostCallback
except Exception: # pragma: no cover
return model
cb = CostCallback()
existing = list(getattr(model, "callbacks", None) or [])
if any(isinstance(c, CostCallback) for c in existing):
return model
existing.append(cb)
try:
model.callbacks = existing
return model
except Exception:
return model.with_config({"callbacks": existing})
def _build_openrouter(model_name: str, **overrides: Any):
"""OpenRouter speaks the OpenAI HTTP API. Use ChatOpenAI with a custom base_url.
Recommended HTTP headers (`HTTP-Referer` + `X-Title`) help OpenRouter rank
your traffic; we set them from env vars when present.
"""
try:
from langchain_openai import ChatOpenAI
except ImportError as e:
raise RuntimeError(
"Provider 'openrouter' selected but the langchain-openai package "
"isn't installed. Install with: `pip install langchain-openai`."
) from e
api_key = os.getenv("OPENROUTER_API_KEY", "")
if not api_key:
raise RuntimeError(
"OPENROUTER_API_KEY is not set. Configure it via the dashboard's "
"βš™οΈ LLM Settings page or in the Space's Settings β†’ Secrets."
)
extra_headers = {}
if site := os.getenv("OPENROUTER_HTTP_REFERER"):
extra_headers["HTTP-Referer"] = site
if title := os.getenv("OPENROUTER_X_TITLE", "gh-deepagent"):
extra_headers["X-Title"] = title
params: dict[str, Any] = dict(
model=model_name,
base_url=OPENROUTER_BASE_URL,
api_key=api_key,
temperature=0.0,
)
if extra_headers:
params["default_headers"] = extra_headers
params.update(overrides)
return ChatOpenAI(**params)
def build_model(model_spec: str | None = None, **overrides: Any):
"""Return a LangChain chat model with observability callbacks attached."""
settings = get_settings()
spec = model_spec or settings.model
if spec.startswith("ollama:"):
try:
from langchain_ollama import ChatOllama
except ImportError as e:
raise RuntimeError(
"Provider 'ollama' selected (DEEPAGENT_MODEL=" + spec + ") but "
"the langchain-ollama package isn't installed. Install with: "
"`pip install langchain-ollama` (or `pip install -e .[ollama]` "
"from the project root). On Hugging Face Spaces, redeploy after "
"rebasing on the latest Dockerfile which includes all providers."
) from e
_, model_name = spec.split("ollama:", 1)
params = dict(
model=model_name,
base_url=settings.ollama_base_url,
temperature=0.0,
num_ctx=32768,
num_predict=4096,
)
params.update(overrides)
return _attach_callbacks(ChatOllama(**params))
if spec.startswith("openrouter:"):
_, model_name = spec.split("openrouter:", 1)
return _attach_callbacks(_build_openrouter(model_name, **overrides))
# Everything else β†’ LangChain's init_chat_model (handles anthropic, openai, …).
try:
return _attach_callbacks(init_chat_model(spec, temperature=0.0, **overrides))
except ImportError as e:
provider = spec.split(":", 1)[0] if ":" in spec else spec
hint_pkg = {
"anthropic": "langchain-anthropic",
"openai": "langchain-openai",
"google_genai": "langchain-google-genai",
"groq": "langchain-groq",
}.get(provider, f"the package for provider '{provider}'")
raise RuntimeError(
f"Couldn't load provider '{provider}' (DEEPAGENT_MODEL={spec}). "
f"Install with: `pip install {hint_pkg}`. On Hugging Face Spaces, "
"redeploy after rebasing on the latest Dockerfile."
) from e