2026-04-28 15:03:21 -07:00
|
|
|
from typing import Literal, TypedDict, Unpack
|
|
|
|
|
|
2026-03-02 12:13:24 -08:00
|
|
|
from langchain.chat_models import init_chat_model
|
|
|
|
|
|
|
|
|
|
OPENAI_RESPONSES_WS_BASE_URL = "wss://api.openai.com/v1"
|
|
|
|
|
|
2026-05-08 15:35:13 -07:00
|
|
|
# Anthropic SDK default is 2; a 529 burst can outlive that. Bump to give the
|
|
|
|
|
# primary provider a fair chance before the fallback middleware kicks in.
|
|
|
|
|
DEFAULT_MAX_RETRIES = 6
|
|
|
|
|
|
2026-03-02 12:13:24 -08:00
|
|
|
|
2026-04-28 15:03:21 -07:00
|
|
|
OpenAIReasoningEffort = Literal["none", "low", "medium", "high", "xhigh"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class OpenAIReasoning(TypedDict, total=False):
|
|
|
|
|
effort: OpenAIReasoningEffort
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ModelKwargs(TypedDict, total=False):
|
|
|
|
|
max_tokens: int | None
|
|
|
|
|
reasoning: OpenAIReasoning | None
|
|
|
|
|
temperature: float | None
|
2026-05-08 15:35:13 -07:00
|
|
|
max_retries: int | None
|
2026-04-28 15:03:21 -07:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def make_model(model_id: str, **kwargs: Unpack[ModelKwargs]):
|
|
|
|
|
model_kwargs: dict[str, object] = kwargs.copy()
|
2026-05-08 15:35:13 -07:00
|
|
|
model_kwargs.setdefault("max_retries", DEFAULT_MAX_RETRIES)
|
2026-03-02 12:13:24 -08:00
|
|
|
|
|
|
|
|
if model_id.startswith("openai:"):
|
|
|
|
|
model_kwargs["base_url"] = OPENAI_RESPONSES_WS_BASE_URL
|
|
|
|
|
model_kwargs["use_responses_api"] = True
|
|
|
|
|
|
|
|
|
|
return init_chat_model(model=model_id, **model_kwargs)
|
2026-05-08 15:35:13 -07:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def fallback_model_id_for(primary_model_id: str) -> str | None:
|
|
|
|
|
"""Return the cross-provider fallback model id for a given primary, if any.
|
|
|
|
|
|
|
|
|
|
Anthropic primaries fall back to OpenAI and vice versa. Returns ``None``
|
|
|
|
|
when the provider has no configured cross-provider fallback (e.g. local
|
|
|
|
|
or self-hosted providers we don't want to silently route off-host).
|
|
|
|
|
"""
|
|
|
|
|
if primary_model_id.startswith("anthropic:"):
|
|
|
|
|
return "openai:gpt-5.5"
|
|
|
|
|
if primary_model_id.startswith("openai:"):
|
|
|
|
|
return "anthropic:claude-opus-4-5"
|
|
|
|
|
return None
|