Skip to content

Commit 831bdf3

Browse files
committed
move to toolsets
1 parent 943c7a8 commit 831bdf3

File tree

7 files changed

+235
-203
lines changed

7 files changed

+235
-203
lines changed

pydantic_ai_slim/pydantic_ai/_cli/_web.py

Lines changed: 14 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,20 @@
44

55
import sys
66
from pathlib import Path
7+
from typing import Any
78

8-
from pydantic import BaseModel, ImportString, ValidationError
9+
from pydantic import ImportString, TypeAdapter, ValidationError
910
from rich.console import Console
1011

1112
from pydantic_ai import Agent
1213
from pydantic_ai.builtin_tools import AbstractBuiltinTool, get_builtin_tool_cls
14+
from pydantic_ai.mcp import MCPServerSSE, MCPServerStdio, MCPServerStreamableHTTP, load_mcp_servers
1315
from pydantic_ai.models import infer_model
14-
from pydantic_ai.ui._web import create_web_app, load_mcp_server_tools
16+
from pydantic_ai.ui._web import create_web_app
1517

1618
__all__ = ['_run_web_command']
1719

18-
19-
class _AgentLoader(BaseModel):
20-
"""Helper model for loading agents using Pydantic ImportString."""
21-
22-
agent: ImportString # type: ignore[valid-type]
20+
_import_string_adapter: TypeAdapter[Any] = TypeAdapter(ImportString)
2321

2422

2523
def _load_agent(agent_path: str) -> Agent | None:
@@ -33,11 +31,10 @@ def _load_agent(agent_path: str) -> Agent | None:
3331
"""
3432
sys.path.insert(0, str(Path.cwd()))
3533
try:
36-
loader = _AgentLoader(agent=agent_path)
37-
agent = loader.agent # pyright: ignore[reportUnknownVariableType,reportUnknownMemberType]
38-
if not isinstance(agent, Agent):
34+
obj = _import_string_adapter.validate_python(agent_path)
35+
if not isinstance(obj, Agent):
3936
return None
40-
return agent # pyright: ignore[reportUnknownVariableType]
37+
return obj # pyright: ignore[reportUnknownVariableType]
4138
except ValidationError:
4239
return None
4340

@@ -70,13 +67,7 @@ def _run_web_command( # noqa: C901
7067
console.print(f'[red]Error: Could not load agent from {agent_path}[/red]')
7168
return 1
7269
else:
73-
agent = Agent()
74-
75-
if instructions:
76-
77-
@agent.system_prompt
78-
def system_prompt() -> str: # pyright: ignore[reportUnusedFunction]
79-
return instructions # pragma: no cover
70+
agent = Agent(instructions=instructions)
8071

8172
if agent.model is None and not models:
8273
console.print('[red]Error: At least one model (-m) is required when agent has no model[/red]')
@@ -105,12 +96,12 @@ def system_prompt() -> str: # pyright: ignore[reportUnusedFunction]
10596
continue
10697
all_tool_instances.append(tool_cls())
10798

108-
# Load MCP server tools if specified
99+
# Load MCP servers as toolsets if specified
100+
mcp_servers: list[MCPServerStdio | MCPServerStreamableHTTP | MCPServerSSE] = []
109101
if mcp:
110102
try:
111-
mcp_tools = load_mcp_server_tools(mcp)
112-
all_tool_instances.extend(mcp_tools)
113-
console.print(f'[dim]Loaded {len(mcp_tools)} MCP server(s) from {mcp}[/dim]')
103+
mcp_servers = load_mcp_servers(mcp)
104+
console.print(f'[dim]Loaded {len(mcp_servers)} MCP server(s) from {mcp}[/dim]')
114105
except FileNotFoundError as e:
115106
console.print(f'[red]Error: {e}[/red]')
116107
return 1
@@ -125,6 +116,7 @@ def system_prompt() -> str: # pyright: ignore[reportUnusedFunction]
125116
agent,
126117
models=models,
127118
builtin_tools=all_tool_instances if all_tool_instances else None,
119+
toolsets=mcp_servers if mcp_servers else None,
128120
)
129121

130122
agent_desc = agent_path if agent_path else 'generic agent'

pydantic_ai_slim/pydantic_ai/builtin_tools.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -417,7 +417,7 @@ def _tool_discriminator(tool_data: dict[str, Any] | AbstractBuiltinTool) -> str:
417417
return tool_data.kind
418418

419419

420-
def get_builtin_tool_ids() -> frozenset[str]:
420+
def get_builtin_tool_ids() -> frozenset[str]: # pragma: no cover
421421
"""Get the set of all builtin tool IDs (excluding deprecated tools like url_context)."""
422422
return frozenset(_BUILTIN_TOOL_TYPES.keys() - {'url_context'})
423423

pydantic_ai_slim/pydantic_ai/models/test.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ def __init__(
9696
custom_output_text: str | None = None,
9797
custom_output_args: Any | None = None,
9898
seed: int = 0,
99+
model_name: str = 'test',
99100
profile: ModelProfileSpec | None = None,
100101
settings: ModelSettings | None = None,
101102
):
@@ -105,7 +106,7 @@ def __init__(
105106
self.custom_output_args = custom_output_args
106107
self.seed = seed
107108
self.last_model_request_parameters = None
108-
self._model_name = 'test'
109+
self._model_name = model_name
109110
self._system = 'test'
110111
super().__init__(settings=settings, profile=profile)
111112

pydantic_ai_slim/pydantic_ai/ui/_web/api.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
"""API routes for the web chat UI."""
22

3+
from collections.abc import Sequence
4+
from typing import Any
5+
36
from pydantic import BaseModel
47
from pydantic.alias_generators import to_camel
58
from starlette.applications import Starlette
@@ -8,6 +11,7 @@
811

912
from pydantic_ai import Agent
1013
from pydantic_ai.builtin_tools import AbstractBuiltinTool
14+
from pydantic_ai.toolsets import AbstractToolset
1115
from pydantic_ai.ui.vercel_ai import VercelAIAdapter
1216

1317

@@ -52,17 +56,20 @@ def add_api_routes(
5256
app: Starlette,
5357
models: list[ModelInfo] | None = None,
5458
builtin_tools: list[AbstractBuiltinTool] | None = None,
59+
toolsets: Sequence[AbstractToolset[Any]] | None = None,
5560
) -> None:
5661
"""Add API routes to a Starlette app.
5762
5863
Args:
5964
app: The Starlette app to add routes to.
6065
models: Optional list of AI models. If not provided, the UI will have no model options.
6166
builtin_tools: Optional list of builtin tools. If not provided, no tools will be available.
67+
toolsets: Optional list of toolsets (e.g., MCP servers). These provide additional tools.
6268
"""
6369
_models = models or []
6470
_model_ids = {m.id for m in _models}
6571
_builtin_tools = builtin_tools or []
72+
_toolsets = list(toolsets) if toolsets else None
6673
_tools_by_id: dict[str, AbstractBuiltinTool] = {tool.unique_id: tool for tool in _builtin_tools}
6774

6875
async def options_chat(request: Request) -> Response:
@@ -102,6 +109,7 @@ async def post_chat(request: Request) -> Response:
102109
agent=agent,
103110
model=extra_data.model,
104111
builtin_tools=request_builtin_tools,
112+
toolsets=_toolsets,
105113
)
106114
return streaming_response
107115

pydantic_ai_slim/pydantic_ai/ui/_web/app.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
"""Factory function for creating a web chat app for a Pydantic AI agent."""
2-
31
from __future__ import annotations
42

53
import os

0 commit comments

Comments
 (0)