# backend/src/agent/_llm_params.py # #region AgentChat.LlmParams [C:3] [TYPE Module] [SEMANTICS agent-chat,llm,openai,compatibility] # @defgroup AgentChat Shared LLM parameter compatibility helpers. # @LAYER Service # @BRIEF Build provider-safe ChatOpenAI kwargs and raw OpenAI payloads. # @POST Unsupported sampling parameters are omitted for reasoning/codex models. # @RATIONALE Some OpenAI-compatible gateways reject temperature for reasoning/codex # models. Centralising the guard prevents resume, health-check, and title # generation paths from diverging. from typing import Any _TEMPERATURE_UNSUPPORTED_PREFIXES = ( "codex/", "omni/codex/", "gpt-5", "o1", "o3", "o4", ) def _canonical_model_name(model: str | None) -> str: """Return provider-stripped, lowercase model name for compatibility checks.""" name = (model or "").strip().lower() if name.startswith(("codex/", "omni/codex/")): return name if "/" in name: return name.rsplit("/", 1)[-1] return name def supports_temperature(model: str | None) -> bool: """Return False for model families whose APIs reject temperature.""" name = _canonical_model_name(model) return not any(name.startswith(prefix) for prefix in _TEMPERATURE_UNSUPPORTED_PREFIXES) def chat_openai_kwargs( *, model: str, base_url: str | None, api_key: str, max_tokens: int, temperature: float = 0, ) -> dict[str, Any]: """Build ChatOpenAI kwargs without unsupported temperature for reasoning models.""" kwargs: dict[str, Any] = { "model": model, "base_url": base_url, "api_key": api_key, "max_tokens": max_tokens, } if supports_temperature(model): kwargs["temperature"] = temperature return kwargs def add_temperature_if_supported( payload: dict[str, Any], *, model: str | None, temperature: float = 0, ) -> dict[str, Any]: """Mutate and return payload with temperature only when the model supports it.""" if supports_temperature(model): payload["temperature"] = temperature return payload # #endregion AgentChat.LlmParams