-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Add docs and tests for RunContext.partial_output in output tools
#3726
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Add docs and tests for RunContext.partial_output in output tools
#3726
Conversation
|
When I was working on tests, I discovered that It seems this is what was discussed in #3393 (comment), so it does not deserve a new issue? Here are the tests that demonstrate bugged behavior: link I think I would like to include the failing streaming test with |
|
About tests: I created a new class designed to test Here are the changes: test_output_validator_partial_sync before: pydantic-ai/tests/test_agent.py Lines 363 to 377 in ccefddc
after: test_agent.py::TestPartialOutput::test_output_validator_texthttps://github.com/Danipulok/pydantic-ai/blob/d08191ee0e84ea2ae54fd4c28e8d5076986c281e/tests/test_agent.py#L370-L387 test_output_validator_partial_stream_text pydantic-ai/tests/test_agent.py Lines 380 to 409 in ccefddc
after: test_streaming.py::TestPartialOutput::test_output_validator_texthttps://github.com/Danipulok/pydantic-ai/blob/d08191ee0e84ea2ae54fd4c28e8d5076986c281e/tests/test_streaming.py#L762-L789 test_output_validator_partial_stream_output before: pydantic-ai/tests/test_agent.py Lines 412 to 439 in ccefddc
after: test_streaming.py::TestPartialOutput::test_output_validator_structuredhttps://github.com/Danipulok/pydantic-ai/blob/d08191ee0e84ea2ae54fd4c28e8d5076986c281e/tests/test_streaming.py#L791-L818 |
RunContext.partial_output in output toolsRunContext.partial_output in output tools
|
|
||
| _(This example is complete, it can be run "as is")_ | ||
|
|
||
| #### Handling partial output in output functions |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's rename this Handling partial output and also drop in output validators from the other section as it's already under "Output validators"
|
|
||
| #### Handling partial output in output functions | ||
|
|
||
| !!! warning "Output functions are called multiple times during streaming" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd prefer for this to be top-level text rather than the entire docs section to be an indented warning block
| #### Handling partial output in output functions | ||
|
|
||
| !!! warning "Output functions are called multiple times during streaming" | ||
| When using streaming mode (`run_stream()`), output functions are called **multiple times** — once for each partial output received from the model, and once for the final complete output. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should say this in the "Handling partial output in output validators" section as well. Maybe the 2 sections can be merged as output validators and functions are very similar?
|
|
||
| **How `partial_output` works:** | ||
|
|
||
| - **In sync mode** (`run_sync()`): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As I mentioned on the other PR, it's just specific to sync mode or run_sync: run (async), iter and run_stream_events have the same behavior. It's really just "run_stream vs the rest"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd rather this be a paragraph than a list btw
|
|
||
| _(This example is complete, it can be run "as is")_ | ||
|
|
||
| **Example without side effects (transformation only):** |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't need this example as it's the "base case" already covered above
|
|
||
| class DatabaseRecord(BaseModel): | ||
| name: str | ||
| value: int |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is actually not a great example because both of these fields are required, meaning the partial data wouldn't validate anyway until the output is complete, so there would not be any partial output in this case.
|
|
||
| agent = Agent('openai:gpt-5', output_type=save_to_database) | ||
|
|
||
| result = agent.run_sync('Create a record with name "test" and value 42') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We're not using run_stream here 😄
This example needs some work to actually show the problem + use case
|
|
||
| For output functions with **side effects** (e.g., sending notifications, logging, database updates), you should check the [`RunContext.partial_output`][pydantic_ai.tools.RunContext.partial_output] flag to avoid executing side effects on partial data. | ||
|
|
||
| **How `partial_output` works:** |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No "fake" subheadings please, I'd rather have paragraphs
| **Best practices:** | ||
|
|
||
| - If your output function **has** side effects (database writes, API calls, notifications) → use `ctx.partial_output` to guard them | ||
| - If your output function only **transforms** data (formatting, validation, normalization) → no need to check the flag |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is duplicative with For output functions with **side effects** (e.g., sending notifications, logging, database updates), you should check the [RunContext.partial_output][pydantic_ai.tools.RunContext.partial_output] flag to avoid executing side effects on partial data. above. I'd rather have one authoritative paragraph
| ] | ||
| ) | ||
|
|
||
| async def test_output_function_structured(self): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's add that test_output_function_text test with TextOutput(func), create a new issue for that bug, and then skip the test
Closes #3624