diff --git a/docs/changelog.md b/docs/changelog.md index 8becb31459..88eddb94a3 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -14,8 +14,14 @@ Pydantic AI is still pre-version 1, so breaking changes will occur, however: ### v0.6.0 (2025-08-06) +This release was meant to clean some old deprecated code, so we can get a step closer to V1. + See [#2440](https://github.com/pydantic/pydantic-ai/pull/2440) - The `next` method was removed from the `Graph` class. Use `async with graph.iter(...) as run: run.next()` instead. +See [#2441](https://github.com/pydantic/pydantic-ai/pull/2441) - The `result_type`, `result_tool_name` and `result_tool_description` arguments were removed from the `Agent` class. Use `output_type` instead. + +See [#2441](https://github.com/pydantic/pydantic-ai/pull/2441) - The `result_retries` argument was also removed from the `Agent` class. Use `output_retries` instead. + See [#2443](https://github.com/pydantic/pydantic-ai/pull/2443) - The `data` property was removed from the `FinalResult` class. Use `output` instead. See [#2445](https://github.com/pydantic/pydantic-ai/pull/2445) - The `get_data` and `validate_structured_result` methods were removed from the diff --git a/examples/pydantic_ai_examples/flight_booking.py b/examples/pydantic_ai_examples/flight_booking.py index 5029c2038d..0b180db3f2 100644 --- a/examples/pydantic_ai_examples/flight_booking.py +++ b/examples/pydantic_ai_examples/flight_booking.py @@ -108,7 +108,7 @@ class Failed(BaseModel): # This agent is responsible for extracting the user's seat selection seat_preference_agent = Agent[None, SeatPreference | Failed]( 'openai:gpt-4o', - output_type=SeatPreference | Failed, # type: ignore + output_type=SeatPreference | Failed, system_prompt=( "Extract the user's seat preference. " 'Seats A and F are window seats. ' diff --git a/pydantic_ai_slim/pydantic_ai/agent.py b/pydantic_ai_slim/pydantic_ai/agent.py index 271d0ebc7f..c352e4dfbf 100644 --- a/pydantic_ai_slim/pydantic_ai/agent.py +++ b/pydantic_ai_slim/pydantic_ai/agent.py @@ -151,8 +151,6 @@ class Agent(Generic[AgentDepsT, OutputDataT]): _instrument_default: ClassVar[InstrumentationSettings | bool] = False _deps_type: type[AgentDepsT] = dataclasses.field(repr=False) - _deprecated_result_tool_name: str | None = dataclasses.field(repr=False) - _deprecated_result_tool_description: str | None = dataclasses.field(repr=False) _output_schema: _output.BaseOutputSchema[OutputDataT] = dataclasses.field(repr=False) _output_validators: list[_output.OutputValidator[AgentDepsT, OutputDataT]] = dataclasses.field(repr=False) _instructions: str | None = dataclasses.field(repr=False) @@ -199,44 +197,13 @@ def __init__( history_processors: Sequence[HistoryProcessor[AgentDepsT]] | None = None, ) -> None: ... - @overload - @deprecated( - '`result_type`, `result_tool_name` & `result_tool_description` are deprecated, use `output_type` instead. `result_retries` is deprecated, use `output_retries` instead.' - ) - def __init__( - self, - model: models.Model | models.KnownModelName | str | None = None, - *, - result_type: type[OutputDataT] = str, - instructions: str - | _system_prompt.SystemPromptFunc[AgentDepsT] - | Sequence[str | _system_prompt.SystemPromptFunc[AgentDepsT]] - | None = None, - system_prompt: str | Sequence[str] = (), - deps_type: type[AgentDepsT] = NoneType, - name: str | None = None, - model_settings: ModelSettings | None = None, - retries: int = 1, - result_tool_name: str = _output.DEFAULT_OUTPUT_TOOL_NAME, - result_tool_description: str | None = None, - result_retries: int | None = None, - tools: Sequence[Tool[AgentDepsT] | ToolFuncEither[AgentDepsT, ...]] = (), - prepare_tools: ToolsPrepareFunc[AgentDepsT] | None = None, - prepare_output_tools: ToolsPrepareFunc[AgentDepsT] | None = None, - toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None, - defer_model_check: bool = False, - end_strategy: EndStrategy = 'early', - instrument: InstrumentationSettings | bool | None = None, - history_processors: Sequence[HistoryProcessor[AgentDepsT]] | None = None, - ) -> None: ... - @overload @deprecated('`mcp_servers` is deprecated, use `toolsets` instead.') def __init__( self, model: models.Model | models.KnownModelName | str | None = None, *, - result_type: type[OutputDataT] = str, + output_type: OutputSpec[OutputDataT] = str, instructions: str | _system_prompt.SystemPromptFunc[AgentDepsT] | Sequence[str | _system_prompt.SystemPromptFunc[AgentDepsT]] @@ -246,9 +213,7 @@ def __init__( name: str | None = None, model_settings: ModelSettings | None = None, retries: int = 1, - result_tool_name: str = _output.DEFAULT_OUTPUT_TOOL_NAME, - result_tool_description: str | None = None, - result_retries: int | None = None, + output_retries: int | None = None, tools: Sequence[Tool[AgentDepsT] | ToolFuncEither[AgentDepsT, ...]] = (), prepare_tools: ToolsPrepareFunc[AgentDepsT] | None = None, prepare_output_tools: ToolsPrepareFunc[AgentDepsT] | None = None, @@ -263,8 +228,7 @@ def __init__( self, model: models.Model | models.KnownModelName | str | None = None, *, - # TODO change this back to `output_type: _output.OutputType[OutputDataT] = str,` when we remove the overloads - output_type: Any = str, + output_type: OutputSpec[OutputDataT] = str, instructions: str | _system_prompt.SystemPromptFunc[AgentDepsT] | Sequence[str | _system_prompt.SystemPromptFunc[AgentDepsT]] @@ -341,42 +305,10 @@ def __init__( self.name = name self.model_settings = model_settings - if 'result_type' in _deprecated_kwargs: - if output_type is not str: # pragma: no cover - raise TypeError('`result_type` and `output_type` cannot be set at the same time.') - warnings.warn('`result_type` is deprecated, use `output_type` instead', DeprecationWarning, stacklevel=2) - output_type = _deprecated_kwargs.pop('result_type') - self.output_type = output_type - self.instrument = instrument - self._deps_type = deps_type - self._deprecated_result_tool_name = _deprecated_kwargs.pop('result_tool_name', None) - if self._deprecated_result_tool_name is not None: - warnings.warn( - '`result_tool_name` is deprecated, use `output_type` with `ToolOutput` instead', - DeprecationWarning, - stacklevel=2, - ) - - self._deprecated_result_tool_description = _deprecated_kwargs.pop('result_tool_description', None) - if self._deprecated_result_tool_description is not None: - warnings.warn( - '`result_tool_description` is deprecated, use `output_type` with `ToolOutput` instead', - DeprecationWarning, - stacklevel=2, - ) - result_retries = _deprecated_kwargs.pop('result_retries', None) - if result_retries is not None: - if output_retries is not None: # pragma: no cover - raise TypeError('`output_retries` and `result_retries` cannot be set at the same time.') - warnings.warn( - '`result_retries` is deprecated, use `max_result_retries` instead', DeprecationWarning, stacklevel=2 - ) - output_retries = result_retries - if mcp_servers := _deprecated_kwargs.pop('mcp_servers', None): if toolsets is not None: # pragma: no cover raise TypeError('`mcp_servers` and `toolsets` cannot be set at the same time.') @@ -389,12 +321,7 @@ def __init__( self.model.profile.default_structured_output_mode if isinstance(self.model, models.Model) else None ) - self._output_schema = _output.OutputSchema[OutputDataT].build( - output_type, - default_mode=default_output_mode, - name=self._deprecated_result_tool_name, - description=self._deprecated_result_tool_description, - ) + self._output_schema = _output.OutputSchema[OutputDataT].build(output_type, default_mode=default_output_mode) self._output_validators = [] self._instructions = '' @@ -472,23 +399,6 @@ async def run( toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None, ) -> AgentRunResult[RunOutputDataT]: ... - @overload - @deprecated('`result_type` is deprecated, use `output_type` instead.') - async def run( - self, - user_prompt: str | Sequence[_messages.UserContent] | None = None, - *, - result_type: type[RunOutputDataT], - message_history: list[_messages.ModelMessage] | None = None, - model: models.Model | models.KnownModelName | str | None = None, - deps: AgentDepsT = None, - model_settings: ModelSettings | None = None, - usage_limits: _usage.UsageLimits | None = None, - usage: _usage.Usage | None = None, - infer_name: bool = True, - toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None, - ) -> AgentRunResult[RunOutputDataT]: ... - async def run( self, user_prompt: str | Sequence[_messages.UserContent] | None = None, @@ -540,12 +450,6 @@ async def main(): if infer_name and self.name is None: self._infer_name(inspect.currentframe()) - if 'result_type' in _deprecated_kwargs: # pragma: no cover - if output_type is not str: - raise TypeError('`result_type` and `output_type` cannot be set at the same time.') - warnings.warn('`result_type` is deprecated, use `output_type` instead.', DeprecationWarning, stacklevel=2) - output_type = _deprecated_kwargs.pop('result_type') - _utils.validate_empty_kwargs(_deprecated_kwargs) async with self.iter( @@ -599,23 +503,6 @@ def iter( **_deprecated_kwargs: Never, ) -> AbstractAsyncContextManager[AgentRun[AgentDepsT, RunOutputDataT]]: ... - @overload - @deprecated('`result_type` is deprecated, use `output_type` instead.') - def iter( - self, - user_prompt: str | Sequence[_messages.UserContent] | None = None, - *, - result_type: type[RunOutputDataT], - message_history: list[_messages.ModelMessage] | None = None, - model: models.Model | models.KnownModelName | str | None = None, - deps: AgentDepsT = None, - model_settings: ModelSettings | None = None, - usage_limits: _usage.UsageLimits | None = None, - usage: _usage.Usage | None = None, - infer_name: bool = True, - toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None, - ) -> AbstractAsyncContextManager[AgentRun[AgentDepsT, Any]]: ... - @asynccontextmanager async def iter( self, @@ -714,12 +601,6 @@ async def main(): model_used = self._get_model(model) del model - if 'result_type' in _deprecated_kwargs: # pragma: no cover - if output_type is not str: - raise TypeError('`result_type` and `output_type` cannot be set at the same time.') - warnings.warn('`result_type` is deprecated, use `output_type` instead.', DeprecationWarning, stacklevel=2) - output_type = _deprecated_kwargs.pop('result_type') - _utils.validate_empty_kwargs(_deprecated_kwargs) deps = self._get_deps(deps) @@ -910,23 +791,6 @@ def run_sync( toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None, ) -> AgentRunResult[RunOutputDataT]: ... - @overload - @deprecated('`result_type` is deprecated, use `output_type` instead.') - def run_sync( - self, - user_prompt: str | Sequence[_messages.UserContent] | None = None, - *, - result_type: type[RunOutputDataT], - message_history: list[_messages.ModelMessage] | None = None, - model: models.Model | models.KnownModelName | str | None = None, - deps: AgentDepsT = None, - model_settings: ModelSettings | None = None, - usage_limits: _usage.UsageLimits | None = None, - usage: _usage.Usage | None = None, - infer_name: bool = True, - toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None, - ) -> AgentRunResult[RunOutputDataT]: ... - def run_sync( self, user_prompt: str | Sequence[_messages.UserContent] | None = None, @@ -977,12 +841,6 @@ def run_sync( if infer_name and self.name is None: self._infer_name(inspect.currentframe()) - if 'result_type' in _deprecated_kwargs: # pragma: no cover - if output_type is not str: - raise TypeError('`result_type` and `output_type` cannot be set at the same time.') - warnings.warn('`result_type` is deprecated, use `output_type` instead.', DeprecationWarning, stacklevel=2) - output_type = _deprecated_kwargs.pop('result_type') - _utils.validate_empty_kwargs(_deprecated_kwargs) return get_event_loop().run_until_complete( @@ -1031,25 +889,8 @@ def run_stream( toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None, ) -> AbstractAsyncContextManager[result.StreamedRunResult[AgentDepsT, RunOutputDataT]]: ... - @overload - @deprecated('`result_type` is deprecated, use `output_type` instead.') - def run_stream( - self, - user_prompt: str | Sequence[_messages.UserContent] | None = None, - *, - result_type: type[RunOutputDataT], - message_history: list[_messages.ModelMessage] | None = None, - model: models.Model | models.KnownModelName | str | None = None, - deps: AgentDepsT = None, - model_settings: ModelSettings | None = None, - usage_limits: _usage.UsageLimits | None = None, - usage: _usage.Usage | None = None, - infer_name: bool = True, - toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None, - ) -> AbstractAsyncContextManager[result.StreamedRunResult[AgentDepsT, RunOutputDataT]]: ... - @asynccontextmanager - async def run_stream( # noqa C901 + async def run_stream( self, user_prompt: str | Sequence[_messages.UserContent] | None = None, *, @@ -1101,12 +942,6 @@ async def main(): if frame := inspect.currentframe(): # pragma: no branch self._infer_name(frame.f_back) - if 'result_type' in _deprecated_kwargs: # pragma: no cover - if output_type is not str: - raise TypeError('`result_type` and `output_type` cannot be set at the same time.') - warnings.warn('`result_type` is deprecated, use `output_type` instead.', DeprecationWarning, stacklevel=2) - output_type = _deprecated_kwargs.pop('result_type') - _utils.validate_empty_kwargs(_deprecated_kwargs) yielded = False @@ -1732,10 +1567,7 @@ def _prepare_output_schema( if self._output_validators: raise exceptions.UserError('Cannot set a custom run `output_type` when the agent has output validators') schema = _output.OutputSchema[RunOutputDataT].build( - output_type, - name=self._deprecated_result_tool_name, - description=self._deprecated_result_tool_description, - default_mode=model_profile.default_structured_output_mode, + output_type, default_mode=model_profile.default_structured_output_mode ) else: schema = self._output_schema.with_default_mode(model_profile.default_structured_output_mode) diff --git a/tests/test_agent.py b/tests/test_agent.py index 796fc45160..2778204f77 100644 --- a/tests/test_agent.py +++ b/tests/test_agent.py @@ -3638,46 +3638,13 @@ def test_deprecated_kwargs_still_work(): """Test that valid deprecated kwargs still work with warnings.""" import warnings - with warnings.catch_warnings(record=True) as w: - warnings.simplefilter('always') - - agent = Agent('test', result_type=str) # type: ignore[call-arg] - assert len(w) == 1 - assert issubclass(w[0].category, DeprecationWarning) - assert '`result_type` is deprecated' in str(w[0].message) - assert agent.output_type is str - - with warnings.catch_warnings(record=True) as w: - warnings.simplefilter('always') - - agent = Agent('test', result_tool_name='test_tool') # type: ignore[call-arg] - assert len(w) == 1 - assert issubclass(w[0].category, DeprecationWarning) - assert '`result_tool_name` is deprecated' in str(w[0].message) - - with warnings.catch_warnings(record=True) as w: - warnings.simplefilter('always') - - agent = Agent('test', result_tool_description='test description') # type: ignore[call-arg] - assert len(w) == 1 - assert issubclass(w[0].category, DeprecationWarning) - assert '`result_tool_description` is deprecated' in str(w[0].message) - - with warnings.catch_warnings(record=True) as w: - warnings.simplefilter('always') - - agent = Agent('test', result_retries=3) # type: ignore[call-arg] - assert len(w) == 1 - assert issubclass(w[0].category, DeprecationWarning) - assert '`result_retries` is deprecated' in str(w[0].message) - try: from pydantic_ai.mcp import MCPServerStdio with warnings.catch_warnings(record=True) as w: warnings.simplefilter('always') - agent = Agent('test', mcp_servers=[MCPServerStdio('python', ['-m', 'tests.mcp_server'])]) # type: ignore[call-arg] + Agent('test', mcp_servers=[MCPServerStdio('python', ['-m', 'tests.mcp_server'])]) # type: ignore[call-arg] assert len(w) == 1 assert issubclass(w[0].category, DeprecationWarning) assert '`mcp_servers` is deprecated' in str(w[0].message) @@ -3685,21 +3652,6 @@ def test_deprecated_kwargs_still_work(): pass -def test_deprecated_kwargs_mixed_valid_invalid(): - """Test that mix of valid deprecated and invalid kwargs raises error for invalid ones.""" - import warnings - - with pytest.raises(UserError, match='Unknown keyword arguments: `usage_limits`'): - with warnings.catch_warnings(): - warnings.simplefilter('ignore', DeprecationWarning) # Ignore the deprecation warning for result_type - Agent('test', result_type=str, usage_limits='invalid') # type: ignore[call-arg] - - with pytest.raises(UserError, match='Unknown keyword arguments: `foo`, `bar`'): - with warnings.catch_warnings(): - warnings.simplefilter('ignore', DeprecationWarning) # Ignore the deprecation warning for result_tool_name - Agent('test', result_tool_name='test', foo='value1', bar='value2') # type: ignore[call-arg] - - def test_override_toolsets(): foo_toolset = FunctionToolset() diff --git a/tests/test_streaming.py b/tests/test_streaming.py index e8861a0e01..965d12c6a9 100644 --- a/tests/test_streaming.py +++ b/tests/test_streaming.py @@ -926,7 +926,7 @@ async def test_stream_iter_structured_validator() -> None: class NotOutputType(BaseModel): not_value: str - agent = Agent[None, Union[OutputType, NotOutputType]]('test', output_type=Union[OutputType, NotOutputType]) # pyright: ignore[reportArgumentType] + agent = Agent[None, Union[OutputType, NotOutputType]]('test', output_type=Union[OutputType, NotOutputType]) @agent.output_validator def output_validator(data: OutputType | NotOutputType) -> OutputType | NotOutputType: diff --git a/tests/typed_agent.py b/tests/typed_agent.py index 3e1171c076..681d6a8bbb 100644 --- a/tests/typed_agent.py +++ b/tests/typed_agent.py @@ -1,4 +1,5 @@ """This file is used to test static typing, it's analyzed with pyright and mypy.""" +# pyright: reportUnnecessaryTypeIgnoreComment=false import re from collections.abc import Awaitable