fix(llm): pin LLMTR endpoint for a dedicated llmtr/ dedupe model

When the dedupe model uses llmtr/ but the main model is a different
provider, the global litellm.api_base (derived from the main model) is
not the LLMTR gateway, so the dedupe request would resolve to LiteLLM's
default openai/ endpoint and fail. Mirror the existing per-call dedupe
credential pattern: inject the LLMTR gateway base URL into the dedupe
request's extra_args when no explicit DEDUPE_LLM_API_BASE is set. An
explicit DEDUPE_LLM_API_BASE still wins.

Addresses Greptile review feedback on the mixed main/dedupe routing case.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ag2XdAsvrR7XKMmQsAjqSN
This commit is contained in:
knowhycodata 2026-08-22 15:46:40 +03:00
parent 60f54a5363
commit bcfa922cf5
2 changed files with 38 additions and 1 deletions

View file

@ -13,8 +13,10 @@ from openai.types.responses import ResponseOutputMessage
from strix.config import load_settings
from strix.config.models import (
LLMTR_API_BASE,
StrixProvider,
configure_sdk_model_defaults,
is_llmtr_model,
)
from strix.core.inputs import make_model_settings
from strix.report.state import get_global_report_state
@ -45,6 +47,12 @@ def _dedupe_extra_args(dedupe: DedupeSettings) -> dict[str, str]:
extra["api_key"] = dedupe.api_key.strip()
if dedupe.api_base and dedupe.api_base.strip():
extra["api_base"] = dedupe.api_base.strip()
elif is_llmtr_model(dedupe.model):
# A dedicated llmtr/ dedupe model reaches the LLMTR gateway only through
# its endpoint. The global default is derived from the main model, which
# may be a different provider, so pin the gateway per call here rather
# than relying on the shared litellm.api_base.
extra["api_base"] = LLMTR_API_BASE
return extra

View file

@ -7,7 +7,7 @@ from typing import TYPE_CHECKING
from strix.config import loader
from strix.config.settings import DedupeSettings
from strix.report.dedupe import _dedupe_model_settings
from strix.report.dedupe import _dedupe_extra_args, _dedupe_model_settings
if TYPE_CHECKING:
@ -44,6 +44,35 @@ def test_dedupe_endpoint_sent_per_call() -> None:
assert (settings.extra_args or {})["api_key"] == "dedupe-key"
def test_llmtr_dedupe_model_pins_gateway_endpoint_per_call() -> None:
# A dedicated llmtr/ dedupe model reaches the gateway only via its endpoint.
# The global default is keyed to the main model, which may be a different
# provider, so the endpoint must ride on the dedupe request itself.
dedupe = DedupeSettings(
STRIX_DEDUPE_MODEL="llmtr/openai/gpt-5.5",
DEDUPE_LLM_API_KEY="llmtr-key",
)
extra = _dedupe_extra_args(dedupe)
assert extra["api_base"] == "https://llmtr.com/v1"
assert extra["api_key"] == "llmtr-key"
def test_llmtr_dedupe_model_respects_explicit_endpoint() -> None:
# An explicit DEDUPE_LLM_API_BASE (e.g. a proxy in front of LLMTR) wins over
# the gateway default.
dedupe = DedupeSettings(
STRIX_DEDUPE_MODEL="llmtr/openai/gpt-5.5",
DEDUPE_LLM_API_KEY="llmtr-key",
DEDUPE_LLM_API_BASE="https://proxy.example/v1",
)
assert _dedupe_extra_args(dedupe)["api_base"] == "https://proxy.example/v1"
def test_non_llmtr_dedupe_model_gets_no_injected_endpoint() -> None:
dedupe = DedupeSettings(STRIX_DEDUPE_MODEL="openai/gpt-5.5", DEDUPE_LLM_API_KEY="k")
assert "api_base" not in _dedupe_extra_args(dedupe)
def test_dedicated_dedupe_model_uses_own_headers_not_main() -> None:
dedupe = DedupeSettings(
STRIX_DEDUPE_MODEL="deepseek/cheap",