Skip to content

Commit 32a9b70

Browse files
committed
Refactor model selection logic: streamline default model handling in ChatService and WorkbenchService, and enhance LLM selection tests
Signed-off-by: Andre Bossard <anbossar@microsoft.com>
1 parent 6e547a9 commit 32a9b70

6 files changed

Lines changed: 75 additions & 12 deletions

File tree

backend/agent_builder/chat_service.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,14 @@ def _env_int(name: str, default: int) -> int:
3232
return default
3333

3434

35+
def _default_model(explicit_model: str = "") -> str:
36+
if explicit_model:
37+
return explicit_model
38+
if os.getenv("AGENT_BACKEND", "").strip().lower() == "openai":
39+
return os.getenv("OPENAI_MODEL", "gpt-4o-mini")
40+
return os.getenv("LITELLM_MODEL", "github_copilot/gpt-4o")
41+
42+
3543
class ChatService:
3644
"""
3745
Runs one-shot chat agent interactions.
@@ -45,12 +53,12 @@ def __init__(
4553
self,
4654
tool_registry: ToolRegistry,
4755
openai_api_key: str = "",
48-
openai_model: str = "gpt-4o-mini",
56+
openai_model: str = "",
4957
openai_base_url: str = "",
5058
) -> None:
5159
self._registry = tool_registry
5260
self._api_key = openai_api_key or os.getenv("OPENAI_API_KEY", "")
53-
self._model = openai_model or os.getenv("OPENAI_MODEL", "gpt-4o-mini")
61+
self._model = _default_model(openai_model)
5462
self._base_url = openai_base_url or os.getenv("OPENAI_BASE_URL", "")
5563
self._llm: Any = None
5664
self._recursion_limit = max(3, _env_int("REACT_AGENT_RECURSION_LIMIT", 8))

backend/agent_builder/engine/react_runner.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@
1818
logger = logging.getLogger(__name__)
1919

2020

21+
def _force_openai_backend() -> bool:
22+
"""Use OpenAI only when explicitly requested, matching backend/agents.py."""
23+
return os.getenv("AGENT_BACKEND", "").strip().lower() == "openai"
24+
25+
2126
@dataclass
2227
class RunResult:
2328
"""Immutable result of a ReAct agent invocation."""
@@ -35,8 +40,8 @@ def build_llm(
3540
max_tokens: int = 0,
3641
reasoning_effort: str = "low",
3742
) -> Any:
38-
"""Construct an LLM instance — ChatOpenAI when api_key is provided, ChatLiteLLM otherwise."""
39-
if api_key:
43+
"""Construct an LLM instance — LiteLLM by default, OpenAI only when forced."""
44+
if _force_openai_backend() and api_key:
4045
from langchain_openai import ChatOpenAI
4146
kwargs: dict[str, Any] = {
4247
"model": model,
@@ -51,7 +56,7 @@ def build_llm(
5156
return ChatOpenAI(**kwargs)
5257
else:
5358
from langchain_litellm import ChatLiteLLM
54-
litellm_model = os.getenv("LITELLM_MODEL", "github_copilot/gpt-4o")
59+
litellm_model = model or os.getenv("LITELLM_MODEL", "github_copilot/gpt-4o")
5560
return ChatLiteLLM(model=litellm_model, temperature=temperature)
5661

5762

backend/agent_builder/service.py

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,15 @@
1717
from time import perf_counter
1818
from typing import Any, Optional
1919

20-
from .engine.prompt_builder import append_output_instructions, resolve_output_schema
21-
from .engine.react_runner import build_llm, build_react_agent, extract_tools_used, make_tool_logging_callback
2220
from .engine.callbacks import make_streaming_callback
2321
from .engine.event_bus import AgentEvent, agent_event_bus
22+
from .engine.prompt_builder import append_output_instructions, resolve_output_schema
23+
from .engine.react_runner import (
24+
build_llm,
25+
build_react_agent,
26+
extract_tools_used,
27+
make_tool_logging_callback,
28+
)
2429
from .evaluator import compute_score
2530
from .evaluator import evaluate_run as _evaluate_criteria
2631
from .models import (
@@ -41,6 +46,14 @@
4146
logger = logging.getLogger(__name__)
4247

4348

49+
def _default_model(explicit_model: str = "") -> str:
50+
if explicit_model:
51+
return explicit_model
52+
if os.getenv("AGENT_BACKEND", "").strip().lower() == "openai":
53+
return os.getenv("OPENAI_MODEL", "gpt-4o-mini")
54+
return os.getenv("LITELLM_MODEL", "github_copilot/gpt-4o")
55+
56+
4457
# ============================================================================
4558
# CALCULATIONS (pure — no side effects, no I/O)
4659
# ============================================================================
@@ -182,13 +195,13 @@ def __init__(
182195
tool_registry: ToolRegistry,
183196
db_path: Optional[Path] = None,
184197
openai_api_key: str = "",
185-
openai_model: str = "gpt-4o-mini",
198+
openai_model: str = "",
186199
openai_base_url: str = "",
187200
recursion_limit: int = 10,
188201
) -> None:
189202
self._registry = tool_registry
190203
self._api_key = openai_api_key or os.getenv("OPENAI_API_KEY", "")
191-
self._model = openai_model or os.getenv("OPENAI_MODEL", "gpt-4o-mini")
204+
self._model = _default_model(openai_model)
192205
self._base_url = openai_base_url or os.getenv("OPENAI_BASE_URL", "")
193206
self._recursion_limit = recursion_limit
194207
self._db_path = db_path or (

backend/agent_builder/tests/test_engine.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
"""Tests for engine helpers — extract_tools_used and ReactRunner."""
22

3+
from unittest.mock import patch
4+
35
from agent_builder.engine.react_runner import extract_tools_used
46

57

@@ -70,3 +72,27 @@ def test_mixed_sources(self):
7072
result = extract_tools_used(messages)
7173
assert "tool_a" in result
7274
assert "tool_b" in result
75+
76+
77+
class TestBuildLlmSelection:
78+
def test_defaults_to_litellm_even_with_api_key(self, monkeypatch):
79+
monkeypatch.delenv("AGENT_BACKEND", raising=False)
80+
81+
with patch("langchain_litellm.ChatLiteLLM") as mock_litellm:
82+
from agent_builder.engine.react_runner import build_llm
83+
84+
build_llm("openai/nvidia/nemotron-3-nano-4b", api_key="test-key")
85+
86+
mock_litellm.assert_called_once()
87+
assert mock_litellm.call_args.kwargs["model"] == "openai/nvidia/nemotron-3-nano-4b"
88+
89+
def test_uses_openai_when_explicitly_forced(self, monkeypatch):
90+
monkeypatch.setenv("AGENT_BACKEND", "openai")
91+
92+
with patch("langchain_openai.ChatOpenAI") as mock_openai:
93+
from agent_builder.engine.react_runner import build_llm
94+
95+
build_llm("gpt-4o-mini", api_key="test-key", base_url="http://localhost:1234/v1")
96+
97+
mock_openai.assert_called_once()
98+
assert mock_openai.call_args.kwargs["model"] == "gpt-4o-mini"

backend/agent_builder/tests/test_service.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,20 @@ def _make_service(tmp_path: Path) -> WorkbenchService:
3535

3636

3737
class TestWorkbenchServiceCRUD:
38+
def test_defaults_to_litellm_model_when_not_forced(self, monkeypatch):
39+
monkeypatch.delenv("AGENT_BACKEND", raising=False)
40+
monkeypatch.setenv("LITELLM_MODEL", "openai/nvidia/nemotron-3-nano-4b")
41+
monkeypatch.setenv("OPENAI_MODEL", "gpt-4o-mini")
42+
43+
with TemporaryDirectory() as tmp:
44+
registry = ToolRegistry()
45+
svc = WorkbenchService(
46+
tool_registry=registry,
47+
db_path=Path(tmp) / "test.db",
48+
openai_api_key="test-key",
49+
)
50+
assert svc._model == "openai/nvidia/nemotron-3-nano-4b"
51+
3852
def test_create_and_get_agent(self):
3953
with TemporaryDirectory() as tmp:
4054
svc = _make_service(Path(tmp))

backend/workbench_integration.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414

1515
# Ensure operations are loaded so @operation decorators run
1616
import operations # noqa: F401
17-
1817
from agent_builder import ChatService, ToolRegistry, WorkbenchService
1918
from api_decorators import get_langchain_tools
2019

@@ -56,14 +55,12 @@ def _build_registry() -> ToolRegistry:
5655
tool_registry=_tool_registry,
5756
db_path=Path(__file__).parent / "data" / "workbench.db",
5857
openai_api_key=os.getenv("OPENAI_API_KEY", ""),
59-
openai_model=os.getenv("OPENAI_MODEL", "gpt-4o-mini"),
6058
openai_base_url=os.getenv("OPENAI_BASE_URL", ""),
6159
)
6260

6361
chat_service = ChatService(
6462
tool_registry=_tool_registry,
6563
openai_api_key=os.getenv("OPENAI_API_KEY", ""),
66-
openai_model=os.getenv("OPENAI_MODEL", "gpt-4o-mini"),
6764
openai_base_url=os.getenv("OPENAI_BASE_URL", ""),
6865
)
6966

0 commit comments

Comments
 (0)