Skip to content

Commit 10d32fe

Browse files
committed
remove get agent utility and make supported builtin tools a classmethod
1 parent 897e4e4 commit 10d32fe

File tree

10 files changed

+43
-49
lines changed

10 files changed

+43
-49
lines changed

pydantic_ai_slim/pydantic_ai/models/__init__.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -509,11 +509,9 @@ def label(self) -> str:
509509
"""Human-friendly display label for the model."""
510510
return _format_model_label(self.model_name)
511511

512-
def supported_builtin_tools(self, profile: ModelProfile) -> frozenset[type[AbstractBuiltinTool]]:
513-
"""Return the set of builtin tool types this model can handle.
514-
515-
Args:
516-
profile: The resolved model profile (passed to avoid circular dependency with self.profile)
512+
@classmethod
513+
def supported_builtin_tools(cls) -> frozenset[type[AbstractBuiltinTool]]:
514+
"""Return the set of builtin tool types this model class can handle.
517515
518516
Subclasses should override this to reflect their actual capabilities.
519517
Default is empty set - subclasses must explicitly declare support.
@@ -536,7 +534,7 @@ def profile(self) -> ModelProfile:
536534
_profile = DEFAULT_PROFILE
537535

538536
# Compute intersection: profile's allowed tools & model's implemented tools
539-
model_supported = self.supported_builtin_tools(_profile)
537+
model_supported = self.__class__.supported_builtin_tools()
540538
profile_supported = _profile.supported_builtin_tools
541539
effective_tools = profile_supported & model_supported
542540

pydantic_ai_slim/pydantic_ai/models/anthropic.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444
ToolReturnPart,
4545
UserPromptPart,
4646
)
47-
from ..profiles import ModelProfile, ModelProfileSpec
47+
from ..profiles import ModelProfileSpec
4848
from ..providers import Provider, infer_provider
4949
from ..providers.anthropic import AsyncAnthropicClient
5050
from ..settings import ModelSettings, merge_model_settings
@@ -261,7 +261,8 @@ def system(self) -> str:
261261
"""The model provider."""
262262
return self._provider.name
263263

264-
def supported_builtin_tools(self, profile: ModelProfile) -> frozenset[type[AbstractBuiltinTool]]:
264+
@classmethod
265+
def supported_builtin_tools(cls) -> frozenset[type[AbstractBuiltinTool]]:
265266
"""Return the set of builtin tool types this model can handle."""
266267
return frozenset({WebSearchTool, CodeExecutionTool, WebFetchTool, MemoryTool, MCPServerTool})
267268

pydantic_ai_slim/pydantic_ai/models/function.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,8 @@ def system(self) -> str:
201201
"""The system / model provider."""
202202
return self._system
203203

204-
def supported_builtin_tools(self, profile: ModelProfile) -> frozenset[type[AbstractBuiltinTool]]:
204+
@classmethod
205+
def supported_builtin_tools(cls) -> frozenset[type[AbstractBuiltinTool]]:
205206
"""FunctionModel supports all builtin tools for testing flexibility."""
206207
from ..builtin_tools import get_builtin_tool_types
207208

pydantic_ai_slim/pydantic_ai/models/google.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
UserPromptPart,
3838
VideoUrl,
3939
)
40-
from ..profiles import ModelProfile, ModelProfileSpec
40+
from ..profiles import ModelProfileSpec
4141
from ..profiles.google import GoogleModelProfile
4242
from ..providers import Provider, infer_provider
4343
from ..settings import ModelSettings
@@ -229,7 +229,8 @@ def system(self) -> str:
229229
"""The model provider."""
230230
return self._provider.name
231231

232-
def supported_builtin_tools(self, profile: ModelProfile) -> frozenset[type[AbstractBuiltinTool]]:
232+
@classmethod
233+
def supported_builtin_tools(cls) -> frozenset[type[AbstractBuiltinTool]]:
233234
"""Return the set of builtin tool types this model can handle."""
234235
return frozenset({WebSearchTool, CodeExecutionTool, WebFetchTool, ImageGenerationTool})
235236

pydantic_ai_slim/pydantic_ai/models/groq.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,8 @@ def system(self) -> str:
179179
"""The model provider."""
180180
return self._provider.name
181181

182-
def supported_builtin_tools(self, profile: ModelProfile) -> frozenset[type[AbstractBuiltinTool]]:
182+
@classmethod
183+
def supported_builtin_tools(cls) -> frozenset[type[AbstractBuiltinTool]]:
183184
"""Return the set of builtin tool types this model can handle."""
184185
return frozenset({WebSearchTool})
185186

pydantic_ai_slim/pydantic_ai/models/openai.py

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from contextlib import asynccontextmanager
99
from dataclasses import dataclass, field, replace
1010
from datetime import datetime
11+
from functools import cached_property
1112
from typing import TYPE_CHECKING, Any, Literal, cast, overload
1213

1314
from pydantic import ValidationError
@@ -424,15 +425,23 @@ def system(self) -> str:
424425
"""The model provider."""
425426
return self._provider.name
426427

427-
def supported_builtin_tools(self, profile: ModelProfile) -> frozenset[type[AbstractBuiltinTool]]:
428-
"""Return the set of builtin tool types this model can handle.
428+
@classmethod
429+
def supported_builtin_tools(cls) -> frozenset[type[AbstractBuiltinTool]]:
430+
"""Return the set of builtin tool types this model can handle."""
431+
return frozenset({WebSearchTool})
432+
433+
@cached_property
434+
def profile(self) -> ModelProfile:
435+
"""The model profile.
429436
430437
WebSearchTool is only supported if openai_chat_supports_web_search is True.
431438
"""
432-
openai_profile = OpenAIModelProfile.from_profile(profile)
433-
if openai_profile.openai_chat_supports_web_search:
434-
return frozenset({WebSearchTool})
435-
return frozenset()
439+
_profile = super().profile
440+
openai_profile = OpenAIModelProfile.from_profile(_profile)
441+
if not openai_profile.openai_chat_supports_web_search:
442+
new_tools = _profile.supported_builtin_tools - {WebSearchTool}
443+
_profile = replace(_profile, supported_builtin_tools=new_tools)
444+
return _profile
436445

437446
@property
438447
@deprecated('Set the `system_prompt_role` in the `OpenAIModelProfile` instead.')
@@ -1092,7 +1101,8 @@ def system(self) -> str:
10921101
"""The model provider."""
10931102
return self._provider.name
10941103

1095-
def supported_builtin_tools(self, profile: ModelProfile) -> frozenset[type[AbstractBuiltinTool]]:
1104+
@classmethod
1105+
def supported_builtin_tools(cls) -> frozenset[type[AbstractBuiltinTool]]:
10961106
"""Return the set of builtin tool types this model can handle."""
10971107
return frozenset({WebSearchTool, CodeExecutionTool, MCPServerTool, ImageGenerationTool})
10981108

pydantic_ai_slim/pydantic_ai/models/test.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
ToolCallPart,
3131
ToolReturnPart,
3232
)
33-
from ..profiles import ModelProfile, ModelProfileSpec
33+
from ..profiles import ModelProfileSpec
3434
from ..settings import ModelSettings
3535
from ..tools import ToolDefinition
3636
from ..usage import RequestUsage
@@ -158,7 +158,8 @@ def system(self) -> str:
158158
"""The model provider."""
159159
return self._system
160160

161-
def supported_builtin_tools(self, profile: ModelProfile) -> frozenset[type[AbstractBuiltinTool]]:
161+
@classmethod
162+
def supported_builtin_tools(cls) -> frozenset[type[AbstractBuiltinTool]]:
162163
"""TestModel supports all builtin tools for testing flexibility."""
163164
from ..builtin_tools import get_builtin_tool_types
164165

pydantic_ai_slim/pydantic_ai/ui/_web/api.py

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,6 @@ class ModelInfo(BaseModel, alias_generator=to_camel, populate_by_name=True):
2323
builtin_tools: list[str]
2424

2525

26-
def _get_agent(request: Request) -> Agent:
27-
"""Get the agent from app state."""
28-
agent = getattr(request.app.state, 'agent', None)
29-
if agent is None:
30-
raise RuntimeError('No agent configured. Server must be started with a valid agent.')
31-
return agent
32-
33-
3426
class BuiltinToolInfo(BaseModel, alias_generator=to_camel, populate_by_name=True):
3527
"""Serializable info about a builtin tool for frontend config."""
3628

@@ -56,6 +48,7 @@ class _ChatRequestExtra(BaseModel, extra='ignore', alias_generator=to_camel):
5648

5749
def add_api_routes(
5850
app: Starlette,
51+
agent: Agent,
5952
models: list[ModelInfo] | None = None,
6053
builtin_tools: list[AbstractBuiltinTool] | None = None,
6154
toolsets: Sequence[AbstractToolset[Any]] | None = None,
@@ -64,6 +57,7 @@ def add_api_routes(
6457
6558
Args:
6659
app: The Starlette app to add routes to.
60+
agent: Agent instance.
6761
models: Optional list of AI models. If not provided, the UI will have no model options.
6862
builtin_tools: Optional list of builtin tools. If not provided, no tools will be available.
6963
toolsets: Optional list of toolsets (e.g., MCP servers). These provide additional tools.
@@ -92,7 +86,6 @@ async def health(request: Request) -> Response:
9286

9387
async def post_chat(request: Request) -> Response:
9488
"""Handle chat requests via Vercel AI Adapter."""
95-
agent = _get_agent(request)
9689
adapter = await VercelAIAdapter.from_request(request, agent=agent)
9790
extra_data = _ChatRequestExtra.model_validate(adapter.run_input.__pydantic_extra__)
9891

pydantic_ai_slim/pydantic_ai/ui/_web/app.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -127,9 +127,13 @@ def create_web_app(
127127

128128
app = Starlette()
129129

130-
app.state.agent = agent
131-
132-
add_api_routes(app, models=resolved_models, builtin_tools=builtin_tools, toolsets=toolsets)
130+
add_api_routes(
131+
app,
132+
agent=agent, # pyright: ignore[reportArgumentType]
133+
models=resolved_models,
134+
builtin_tools=builtin_tools,
135+
toolsets=toolsets,
136+
)
133137

134138
async def index(request: Request) -> Response:
135139
"""Serve the chat UI from filesystem cache or CDN."""

tests/test_ui_web.py

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -169,22 +169,6 @@ def test_chat_app_index_caching():
169169
assert response2.status_code == 200
170170

171171

172-
def test_get_agent_missing():
173-
"""Test that _get_agent raises RuntimeError when agent is not configured."""
174-
from pydantic_ai.ui._web.api import _get_agent # pyright: ignore[reportPrivateUsage]
175-
176-
app = Starlette()
177-
178-
class FakeRequest:
179-
def __init__(self, app: Starlette):
180-
self.app = app
181-
182-
request = FakeRequest(app)
183-
184-
with pytest.raises(RuntimeError, match='No agent configured'):
185-
_get_agent(request) # pyright: ignore[reportArgumentType]
186-
187-
188172
@pytest.mark.anyio
189173
async def test_post_chat_endpoint():
190174
"""Test the POST /api/chat endpoint."""

0 commit comments

Comments
 (0)