@dataclassclassStreamedRunResult(Generic[AgentDepsT,ResultDataT]):"""Result of a streamed run that returns structured data via a tool call."""_all_messages:list[_messages.ModelMessage]_new_message_index:int_usage_limits:UsageLimits|None_stream_response:models.StreamedResponse_result_schema:_result.ResultSchema[ResultDataT]|None_run_ctx:RunContext[AgentDepsT]_result_validators:list[_result.ResultValidator[AgentDepsT,ResultDataT]]_result_tool_name:str|None_on_complete:Callable[[],Awaitable[None]]_initial_run_ctx_usage:Usage=field(init=False)is_complete:bool=field(default=False,init=False)"""Whether the stream has all been received. This is set to `True` when one of [`stream`][pydantic_ai.result.StreamedRunResult.stream], [`stream_text`][pydantic_ai.result.StreamedRunResult.stream_text], [`stream_structured`][pydantic_ai.result.StreamedRunResult.stream_structured] or [`get_data`][pydantic_ai.result.StreamedRunResult.get_data] completes. """def__post_init__(self):self._initial_run_ctx_usage=copy(self._run_ctx.usage)defall_messages(self,*,result_tool_return_content:str|None=None)->list[_messages.ModelMessage]:"""Return the history of _messages. Args: result_tool_return_content: The return content of the tool call to set in the last message. This provides a convenient way to modify the content of the result tool call if you want to continue the conversation and want to set the response to the result tool call. If `None`, the last message will not be modified. Returns: List of messages. """# this is a method to be consistent with the other methodsifresult_tool_return_contentisnotNone:raiseNotImplementedError('Setting result tool return content is not supported for this result type.')returnself._all_messagesdefall_messages_json(self,*,result_tool_return_content:str|None=None)->bytes:"""Return all messages from [`all_messages`][pydantic_ai.result.StreamedRunResult.all_messages] as JSON bytes. Args: result_tool_return_content: The return content of the tool call to set in the last message. This provides a convenient way to modify the content of the result tool call if you want to continue the conversation and want to set the response to the result tool call. If `None`, the last message will not be modified. Returns: JSON bytes representing the messages. """return_messages.ModelMessagesTypeAdapter.dump_json(self.all_messages(result_tool_return_content=result_tool_return_content))defnew_messages(self,*,result_tool_return_content:str|None=None)->list[_messages.ModelMessage]:"""Return new messages associated with this run. Messages from older runs are excluded. Args: result_tool_return_content: The return content of the tool call to set in the last message. This provides a convenient way to modify the content of the result tool call if you want to continue the conversation and want to set the response to the result tool call. If `None`, the last message will not be modified. Returns: List of new messages. """returnself.all_messages(result_tool_return_content=result_tool_return_content)[self._new_message_index:]defnew_messages_json(self,*,result_tool_return_content:str|None=None)->bytes:"""Return new messages from [`new_messages`][pydantic_ai.result.StreamedRunResult.new_messages] as JSON bytes. Args: result_tool_return_content: The return content of the tool call to set in the last message. This provides a convenient way to modify the content of the result tool call if you want to continue the conversation and want to set the response to the result tool call. If `None`, the last message will not be modified. Returns: JSON bytes representing the new messages. """return_messages.ModelMessagesTypeAdapter.dump_json(self.new_messages(result_tool_return_content=result_tool_return_content))asyncdefstream(self,*,debounce_by:float|None=0.1)->AsyncIterator[ResultDataT]:"""Stream the response as an async iterable. The pydantic validator for structured data will be called in [partial mode](https://docs.pydantic.dev/dev/concepts/experimental/#partial-validation) on each iteration. Args: debounce_by: by how much (if at all) to debounce/group the response chunks by. `None` means no debouncing. Debouncing is particularly important for long structured responses to reduce the overhead of performing validation as each token is received. Returns: An async iterable of the response data. """asyncforstructured_message,is_lastinself.stream_structured(debounce_by=debounce_by):result=awaitself.validate_structured_result(structured_message,allow_partial=notis_last)yieldresultasyncdefstream_text(self,*,delta:bool=False,debounce_by:float|None=0.1)->AsyncIterator[str]:"""Stream the text result as an async iterable. !!! note Result validators will NOT be called on the text result if `delta=True`. Args: delta: if `True`, yield each chunk of text as it is received, if `False` (default), yield the full text up to the current point. debounce_by: by how much (if at all) to debounce/group the response chunks by. `None` means no debouncing. Debouncing is particularly important for long structured responses to reduce the overhead of performing validation as each token is received. """ifself._result_schemaandnotself._result_schema.allow_text_result:raiseexceptions.UserError('stream_text() can only be used with text responses')ifdelta:asyncfortextinself._stream_response_text(delta=delta,debounce_by=debounce_by):yieldtextelse:asyncfortextinself._stream_response_text(delta=delta,debounce_by=debounce_by):combined_validated_text=awaitself._validate_text_result(text)yieldcombined_validated_textawaitself._marked_completed(self._stream_response.get())asyncdefstream_structured(self,*,debounce_by:float|None=0.1)->AsyncIterator[tuple[_messages.ModelResponse,bool]]:"""Stream the response as an async iterable of Structured LLM Messages. Args: debounce_by: by how much (if at all) to debounce/group the response chunks by. `None` means no debouncing. Debouncing is particularly important for long structured responses to reduce the overhead of performing validation as each token is received. Returns: An async iterable of the structured response message and whether that is the last message. """# if the message currently has any parts with content, yield before streamingmsg=self._stream_response.get()forpartinmsg.parts:ifpart.has_content():yieldmsg,Falsebreakasyncformsginself._stream_response_structured(debounce_by=debounce_by):yieldmsg,Falsemsg=self._stream_response.get()yieldmsg,Trueawaitself._marked_completed(msg)asyncdefget_data(self)->ResultDataT:"""Stream the whole response, validate and return it."""usage_checking_stream=_get_usage_checking_stream_response(self._stream_response,self._usage_limits,self.usage)asyncfor_inusage_checking_stream:passmessage=self._stream_response.get()awaitself._marked_completed(message)returnawaitself.validate_structured_result(message)defusage(self)->Usage:"""Return the usage of the whole run. !!! note This won't return the full usage until the stream is finished. """returnself._initial_run_ctx_usage+self._stream_response.usage()deftimestamp(self)->datetime:"""Get the timestamp of the response."""returnself._stream_response.timestampasyncdefvalidate_structured_result(self,message:_messages.ModelResponse,*,allow_partial:bool=False)->ResultDataT:"""Validate a structured result message."""ifself._result_schemaisnotNoneandself._result_tool_nameisnotNone:match=self._result_schema.find_named_tool(message.parts,self._result_tool_name)ifmatchisNone:raiseexceptions.UnexpectedModelBehavior(f'Invalid response, unable to find tool: {self._result_schema.tool_names()}')call,result_tool=matchresult_data=result_tool.validate(call,allow_partial=allow_partial,wrap_validation_errors=False)forvalidatorinself._result_validators:result_data=awaitvalidator.validate(result_data,call,self._run_ctx)returnresult_dataelse:text='\n\n'.join(x.contentforxinmessage.partsifisinstance(x,_messages.TextPart))forvalidatorinself._result_validators:text=awaitvalidator.validate(text,None,self._run_ctx,)# Since there is no result tool, we can assume that str is compatible with ResultDataTreturncast(ResultDataT,text)asyncdef_validate_text_result(self,text:str)->str:forvalidatorinself._result_validators:text=awaitvalidator.validate(text,None,self._run_ctx,)returntextasyncdef_marked_completed(self,message:_messages.ModelResponse)->None:self.is_complete=Trueself._all_messages.append(message)awaitself._on_complete()asyncdef_stream_response_structured(self,*,debounce_by:float|None=0.1)->AsyncIterator[_messages.ModelResponse]:asyncwith_utils.group_by_temporal(self._stream_response,debounce_by)asgroup_iter:asyncfor_itemsingroup_iter:yieldself._stream_response.get()asyncdef_stream_response_text(self,*,delta:bool=False,debounce_by:float|None=0.1)->AsyncIterator[str]:"""Stream the response as an async iterable of text."""# Define a "merged" version of the iterator that will yield items that have already been retrieved# and items that we receive while streaming. We define a dedicated async iterator for this so we can# pass the combined stream to the group_by_temporal function within `_stream_text_deltas` below.asyncdef_stream_text_deltas_ungrouped()->AsyncIterator[tuple[str,int]]:# yields tuples of (text_content, part_index)# we don't currently make use of the part_index, but in principle this may be useful# so we retain it here for now to make possible future refactors simplermsg=self._stream_response.get()fori,partinenumerate(msg.parts):ifisinstance(part,_messages.TextPart)andpart.content:yieldpart.content,iasyncforeventinself._stream_response:if(isinstance(event,_messages.PartStartEvent)andisinstance(event.part,_messages.TextPart)andevent.part.content):yieldevent.part.content,event.indexelif(isinstance(event,_messages.PartDeltaEvent)andisinstance(event.delta,_messages.TextPartDelta)andevent.delta.content_delta):yieldevent.delta.content_delta,event.indexasyncdef_stream_text_deltas()->AsyncIterator[str]:asyncwith_utils.group_by_temporal(_stream_text_deltas_ungrouped(),debounce_by)asgroup_iter:asyncforitemsingroup_iter:# Note: we are currently just dropping the part index on the group hereyield''.join([contentforcontent,_initems])ifdelta:asyncfortextin_stream_text_deltas():yieldtextelse:# a quick benchmark shows it's faster to build up a string with concat when we're# yielding at each stepdeltas:list[str]=[]asyncfortextin_stream_text_deltas():deltas.append(text)yield''.join(deltas)
The return content of the tool call to set in the last message.
This provides a convenient way to modify the content of the result tool call if you want to continue
the conversation and want to set the response to the result tool call. If None, the last message will
not be modified.
Source code in pydantic_ai_slim/pydantic_ai/result.py
202203204205206207208209210211212213214215216217
defall_messages(self,*,result_tool_return_content:str|None=None)->list[_messages.ModelMessage]:"""Return the history of _messages. Args: result_tool_return_content: The return content of the tool call to set in the last message. This provides a convenient way to modify the content of the result tool call if you want to continue the conversation and want to set the response to the result tool call. If `None`, the last message will not be modified. Returns: List of messages. """# this is a method to be consistent with the other methodsifresult_tool_return_contentisnotNone:raiseNotImplementedError('Setting result tool return content is not supported for this result type.')returnself._all_messages
The return content of the tool call to set in the last message.
This provides a convenient way to modify the content of the result tool call if you want to continue
the conversation and want to set the response to the result tool call. If None, the last message will
not be modified.
Source code in pydantic_ai_slim/pydantic_ai/result.py
219220221222223224225226227228229230231232233
defall_messages_json(self,*,result_tool_return_content:str|None=None)->bytes:"""Return all messages from [`all_messages`][pydantic_ai.result.StreamedRunResult.all_messages] as JSON bytes. Args: result_tool_return_content: The return content of the tool call to set in the last message. This provides a convenient way to modify the content of the result tool call if you want to continue the conversation and want to set the response to the result tool call. If `None`, the last message will not be modified. Returns: JSON bytes representing the messages. """return_messages.ModelMessagesTypeAdapter.dump_json(self.all_messages(result_tool_return_content=result_tool_return_content))
The return content of the tool call to set in the last message.
This provides a convenient way to modify the content of the result tool call if you want to continue
the conversation and want to set the response to the result tool call. If None, the last message will
not be modified.
Source code in pydantic_ai_slim/pydantic_ai/result.py
235236237238239240241242243244245246247248249
defnew_messages(self,*,result_tool_return_content:str|None=None)->list[_messages.ModelMessage]:"""Return new messages associated with this run. Messages from older runs are excluded. Args: result_tool_return_content: The return content of the tool call to set in the last message. This provides a convenient way to modify the content of the result tool call if you want to continue the conversation and want to set the response to the result tool call. If `None`, the last message will not be modified. Returns: List of new messages. """returnself.all_messages(result_tool_return_content=result_tool_return_content)[self._new_message_index:]
The return content of the tool call to set in the last message.
This provides a convenient way to modify the content of the result tool call if you want to continue
the conversation and want to set the response to the result tool call. If None, the last message will
not be modified.
Source code in pydantic_ai_slim/pydantic_ai/result.py
251252253254255256257258259260261262263264265
defnew_messages_json(self,*,result_tool_return_content:str|None=None)->bytes:"""Return new messages from [`new_messages`][pydantic_ai.result.StreamedRunResult.new_messages] as JSON bytes. Args: result_tool_return_content: The return content of the tool call to set in the last message. This provides a convenient way to modify the content of the result tool call if you want to continue the conversation and want to set the response to the result tool call. If `None`, the last message will not be modified. Returns: JSON bytes representing the new messages. """return_messages.ModelMessagesTypeAdapter.dump_json(self.new_messages(result_tool_return_content=result_tool_return_content))
by how much (if at all) to debounce/group the response chunks by. None means no debouncing.
Debouncing is particularly important for long structured responses to reduce the overhead of
performing validation as each token is received.
asyncdefstream(self,*,debounce_by:float|None=0.1)->AsyncIterator[ResultDataT]:"""Stream the response as an async iterable. The pydantic validator for structured data will be called in [partial mode](https://docs.pydantic.dev/dev/concepts/experimental/#partial-validation) on each iteration. Args: debounce_by: by how much (if at all) to debounce/group the response chunks by. `None` means no debouncing. Debouncing is particularly important for long structured responses to reduce the overhead of performing validation as each token is received. Returns: An async iterable of the response data. """asyncforstructured_message,is_lastinself.stream_structured(debounce_by=debounce_by):result=awaitself.validate_structured_result(structured_message,allow_partial=notis_last)yieldresult
by how much (if at all) to debounce/group the response chunks by. None means no debouncing.
Debouncing is particularly important for long structured responses to reduce the overhead of
performing validation as each token is received.
0.1
Source code in pydantic_ai_slim/pydantic_ai/result.py
asyncdefstream_text(self,*,delta:bool=False,debounce_by:float|None=0.1)->AsyncIterator[str]:"""Stream the text result as an async iterable. !!! note Result validators will NOT be called on the text result if `delta=True`. Args: delta: if `True`, yield each chunk of text as it is received, if `False` (default), yield the full text up to the current point. debounce_by: by how much (if at all) to debounce/group the response chunks by. `None` means no debouncing. Debouncing is particularly important for long structured responses to reduce the overhead of performing validation as each token is received. """ifself._result_schemaandnotself._result_schema.allow_text_result:raiseexceptions.UserError('stream_text() can only be used with text responses')ifdelta:asyncfortextinself._stream_response_text(delta=delta,debounce_by=debounce_by):yieldtextelse:asyncfortextinself._stream_response_text(delta=delta,debounce_by=debounce_by):combined_validated_text=awaitself._validate_text_result(text)yieldcombined_validated_textawaitself._marked_completed(self._stream_response.get())
by how much (if at all) to debounce/group the response chunks by. None means no debouncing.
Debouncing is particularly important for long structured responses to reduce the overhead of
performing validation as each token is received.
asyncdefstream_structured(self,*,debounce_by:float|None=0.1)->AsyncIterator[tuple[_messages.ModelResponse,bool]]:"""Stream the response as an async iterable of Structured LLM Messages. Args: debounce_by: by how much (if at all) to debounce/group the response chunks by. `None` means no debouncing. Debouncing is particularly important for long structured responses to reduce the overhead of performing validation as each token is received. Returns: An async iterable of the structured response message and whether that is the last message. """# if the message currently has any parts with content, yield before streamingmsg=self._stream_response.get()forpartinmsg.parts:ifpart.has_content():yieldmsg,Falsebreakasyncformsginself._stream_response_structured(debounce_by=debounce_by):yieldmsg,Falsemsg=self._stream_response.get()yieldmsg,Trueawaitself._marked_completed(msg)
Stream the whole response, validate and return it.
Source code in pydantic_ai_slim/pydantic_ai/result.py
339340341342343344345346347348349
asyncdefget_data(self)->ResultDataT:"""Stream the whole response, validate and return it."""usage_checking_stream=_get_usage_checking_stream_response(self._stream_response,self._usage_limits,self.usage)asyncfor_inusage_checking_stream:passmessage=self._stream_response.get()awaitself._marked_completed(message)returnawaitself.validate_structured_result(message)
This won't return the full usage until the stream is finished.
Source code in pydantic_ai_slim/pydantic_ai/result.py
351352353354355356357
defusage(self)->Usage:"""Return the usage of the whole run. !!! note This won't return the full usage until the stream is finished. """returnself._initial_run_ctx_usage+self._stream_response.usage()
asyncdefvalidate_structured_result(self,message:_messages.ModelResponse,*,allow_partial:bool=False)->ResultDataT:"""Validate a structured result message."""ifself._result_schemaisnotNoneandself._result_tool_nameisnotNone:match=self._result_schema.find_named_tool(message.parts,self._result_tool_name)ifmatchisNone:raiseexceptions.UnexpectedModelBehavior(f'Invalid response, unable to find tool: {self._result_schema.tool_names()}')call,result_tool=matchresult_data=result_tool.validate(call,allow_partial=allow_partial,wrap_validation_errors=False)forvalidatorinself._result_validators:result_data=awaitvalidator.validate(result_data,call,self._run_ctx)returnresult_dataelse:text='\n\n'.join(x.contentforxinmessage.partsifisinstance(x,_messages.TextPart))forvalidatorinself._result_validators:text=awaitvalidator.validate(text,None,self._run_ctx,)# Since there is no result tool, we can assume that str is compatible with ResultDataTreturncast(ResultDataT,text)