Skip to content

Commit 025f1b5

Browse files
committed
take agent's own model into account
1 parent 125c059 commit 025f1b5

File tree

2 files changed

+52
-8
lines changed

2 files changed

+52
-8
lines changed

pydantic_ai_slim/pydantic_ai/_cli/_web.py

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,10 @@ def _run_web_command( # noqa: C901
5050
) -> int:
5151
"""Run the web command to serve an agent via web UI.
5252
53+
If an agent is provided, its model and builtin tools are used as defaults.
54+
CLI-specified models and tools are added on top. Duplicates are removed.
55+
MCP servers are loaded as toolsets if specified.
56+
5357
Args:
5458
agent_path: Agent path in 'module:variable' format. If None, creates generic agent.
5559
host: Host to bind the server to.
@@ -73,18 +77,19 @@ def _run_web_command( # noqa: C901
7377
console.print('[red]Error: At least one model (-m) is required when agent has no model[/red]')
7478
return 1
7579

76-
# If no CLI models provided but agent has a model, use agent's model
77-
if not models and agent.model is not None:
78-
resolved_model = infer_model(agent.model)
79-
models = [f'{resolved_model.system}:{resolved_model.model_name}']
80+
# build models list
81+
if agent.model is not None:
82+
resolved = infer_model(agent.model)
83+
agent_model = f'{resolved.system}:{resolved.model_name}'
84+
models = [agent_model] + [m for m in (models or []) if m != agent_model]
8085

81-
# Collect builtin tools: agent's own + CLI-provided
86+
# collect builtin tools
8287
all_tool_instances: list[AbstractBuiltinTool] = []
8388

84-
# Add agent's own builtin tools first (these are always enabled)
89+
# agent's own builtin tools first
8590
all_tool_instances.extend(agent._builtin_tools) # pyright: ignore[reportPrivateUsage]
8691

87-
# Parse and add CLI tools
92+
# then CLI tools
8893
if tools:
8994
for tool_id in tools:
9095
tool_cls = get_builtin_tool_cls(tool_id)
@@ -128,7 +133,7 @@ def _run_web_command( # noqa: C901
128133
import uvicorn
129134

130135
uvicorn.run(app, host=host, port=port)
131-
return 0 # pragma: no cover
136+
return 0
132137
except KeyboardInterrupt: # pragma: no cover
133138
console.print('\n[dim]Server stopped.[/dim]')
134139
return 0

tests/test_cli.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -660,3 +660,42 @@ def test_run_web_command_mcp_success(tmp_path: Path, mocker: MockerFixture, capf
660660
mock_uvicorn_run.assert_called_once()
661661
output = capfd.readouterr().out
662662
assert 'Loaded 1 MCP server(s)' in output
663+
664+
665+
def test_run_web_command_agent_model_merged_with_cli_models(
666+
mocker: MockerFixture, create_test_module: Callable[..., None]
667+
):
668+
"""Test that agent's model is included first, followed by CLI models."""
669+
mock_uvicorn_run = mocker.patch('uvicorn.run')
670+
mock_create_app = mocker.patch('pydantic_ai._cli._web.create_web_app')
671+
672+
test_agent = Agent(TestModel(custom_output_text='test'))
673+
create_test_module(custom_agent=test_agent)
674+
675+
result = _run_web_command(
676+
agent_path='test_module:custom_agent', models=['openai:gpt-5', 'anthropic:claude-sonnet-4-5']
677+
)
678+
679+
assert result == 0
680+
mock_uvicorn_run.assert_called_once()
681+
682+
call_kwargs = mock_create_app.call_args.kwargs
683+
assert call_kwargs.get('models') == snapshot(['test:test', 'openai:gpt-5', 'anthropic:claude-sonnet-4-5'])
684+
685+
686+
def test_run_web_command_agent_model_deduplicated(mocker: MockerFixture, create_test_module: Callable[..., None]):
687+
"""Test that duplicate models are removed when CLI passes the same model as agent."""
688+
mock_uvicorn_run = mocker.patch('uvicorn.run')
689+
mock_create_app = mocker.patch('pydantic_ai._cli._web.create_web_app')
690+
691+
test_agent = Agent(TestModel(custom_output_text='test'))
692+
create_test_module(custom_agent=test_agent)
693+
694+
# Pass the same model that the agent has, plus another
695+
result = _run_web_command(agent_path='test_module:custom_agent', models=['test:test', 'openai:gpt-5'])
696+
697+
assert result == 0
698+
mock_uvicorn_run.assert_called_once()
699+
700+
call_kwargs = mock_create_app.call_args.kwargs
701+
assert call_kwargs.get('models') == snapshot(['test:test', 'openai:gpt-5'])

0 commit comments

Comments
 (0)