Skip to content

Commit 9896901

Browse files
committed
turn run_stream_sync into normal method
1 parent 00246b5 commit 9896901

File tree

3 files changed

+197
-185
lines changed

3 files changed

+197
-185
lines changed

pydantic_ai_slim/pydantic_ai/agent/abstract.py

Lines changed: 36 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import inspect
55
from abc import ABC, abstractmethod
66
from collections.abc import AsyncIterable, AsyncIterator, Awaitable, Callable, Iterator, Mapping, Sequence
7-
from contextlib import AbstractAsyncContextManager, AbstractContextManager, asynccontextmanager, contextmanager
7+
from contextlib import AbstractAsyncContextManager, asynccontextmanager, contextmanager
88
from types import FrameType
99
from typing import TYPE_CHECKING, Any, Generic, TypeAlias, cast, overload
1010

@@ -598,7 +598,7 @@ def run_stream_sync(
598598
toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None,
599599
builtin_tools: Sequence[AbstractBuiltinTool] | None = None,
600600
event_stream_handler: EventStreamHandler[AgentDepsT] | None = None,
601-
) -> AbstractContextManager[result.StreamedRunResult[AgentDepsT, OutputDataT]]: ...
601+
) -> result.StreamedRunResult[AgentDepsT, OutputDataT]: ...
602602

603603
@overload
604604
def run_stream_sync(
@@ -617,9 +617,8 @@ def run_stream_sync(
617617
toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None,
618618
builtin_tools: Sequence[AbstractBuiltinTool] | None = None,
619619
event_stream_handler: EventStreamHandler[AgentDepsT] | None = None,
620-
) -> AbstractContextManager[result.StreamedRunResult[AgentDepsT, RunOutputDataT]]: ...
620+
) -> result.StreamedRunResult[AgentDepsT, RunOutputDataT]: ...
621621

622-
@contextmanager
623622
def run_stream_sync(
624623
self,
625624
user_prompt: str | Sequence[_messages.UserContent] | None = None,
@@ -636,7 +635,7 @@ def run_stream_sync(
636635
toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None,
637636
builtin_tools: Sequence[AbstractBuiltinTool] | None = None,
638637
event_stream_handler: EventStreamHandler[AgentDepsT] | None = None,
639-
) -> Iterator[result.StreamedRunResult[AgentDepsT, Any]]:
638+
) -> result.StreamedRunResult[AgentDepsT, Any]:
640639
"""Run the agent with a user prompt in sync streaming mode.
641640
642641
This is a convenience method that wraps [`self.run_stream`][pydantic_ai.agent.AbstractAgent.run_stream] with `loop.run_until_complete(...)`.
@@ -652,6 +651,18 @@ def run_stream_sync(
652651
If you want to always run the agent graph to completion and stream events and output at the same time,
653652
use [`agent.run()`][pydantic_ai.agent.AbstractAgent.run] with an `event_stream_handler` or [`agent.iter()`][pydantic_ai.agent.AbstractAgent.iter] instead.
654653
654+
Example:
655+
```python
656+
from pydantic_ai import Agent
657+
658+
agent = Agent('openai:gpt-4o')
659+
660+
def main():
661+
response = agent.run_stream_sync('What is the capital of the UK?')
662+
print(response.get_output_sync())
663+
#> The capital of the UK is London.
664+
```
665+
655666
Args:
656667
user_prompt: User input to start/continue the conversation.
657668
output_type: Custom output type to use for this run, `output_type` may only be used if the agent has no
@@ -673,22 +684,26 @@ def run_stream_sync(
673684
Returns:
674685
The result of the run.
675686
"""
676-
async_cm = self.run_stream(
677-
user_prompt,
678-
output_type=output_type,
679-
message_history=message_history,
680-
deferred_tool_results=deferred_tool_results,
681-
model=model,
682-
deps=deps,
683-
model_settings=model_settings,
684-
usage_limits=usage_limits,
685-
usage=usage,
686-
infer_name=infer_name,
687-
toolsets=toolsets,
688-
builtin_tools=builtin_tools,
689-
event_stream_handler=event_stream_handler,
690-
)
691-
yield get_event_loop().run_until_complete(async_cm.__aenter__())
687+
688+
async def _consume_stream():
689+
async with self.run_stream(
690+
user_prompt,
691+
output_type=output_type,
692+
message_history=message_history,
693+
deferred_tool_results=deferred_tool_results,
694+
model=model,
695+
deps=deps,
696+
model_settings=model_settings,
697+
usage_limits=usage_limits,
698+
usage=usage,
699+
infer_name=infer_name,
700+
toolsets=toolsets,
701+
builtin_tools=builtin_tools,
702+
event_stream_handler=event_stream_handler,
703+
) as stream_result:
704+
yield stream_result
705+
706+
return get_event_loop().run_until_complete(_consume_stream().__anext__())
692707

693708
@overload
694709
def run_stream_events(

pydantic_ai_slim/pydantic_ai/result.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -425,7 +425,7 @@ async def stream_output(self, *, debounce_by: float | None = 0.1) -> AsyncIterat
425425
def stream_output_sync(self, *, debounce_by: float | None = 0.1) -> Iterator[OutputDataT]:
426426
"""Stream the output as an iterable.
427427
428-
This is a convenience method that wraps [`self.stream_output`][pydantic_ai.result.StreamedRunResult.stream_output] with `loop.run_until_complete(...)`.
428+
This is a convenience method that wraps [`stream_output()`][pydantic_ai.result.StreamedRunResult.stream_output] with `loop.run_until_complete(...)`.
429429
You therefore can't use this method inside async code or if there's an active event loop.
430430
431431
The pydantic validator for structured data will be called in
@@ -530,7 +530,7 @@ def stream_responses_sync(
530530
) -> Iterator[tuple[_messages.ModelResponse, bool]]:
531531
"""Stream the response as an iterable of Structured LLM Messages.
532532
533-
This is a convenience method that wraps [`self.stream_responses`][pydantic_ai.result.StreamedRunResult.stream_responses] with `loop.run_until_complete(...)`.
533+
This is a convenience method that wraps [`stream_responses()`][pydantic_ai.result.StreamedRunResult.stream_responses] with `loop.run_until_complete(...)`.
534534
You therefore can't use this method inside async code or if there's an active event loop.
535535
536536
Args:
@@ -560,7 +560,7 @@ async def get_output(self) -> OutputDataT:
560560
def get_output_sync(self) -> OutputDataT:
561561
"""Stream the whole response, validate and return it.
562562
563-
This is a convenience method that wraps [`self.get_output`][pydantic_ai.result.StreamedRunResult.get_output] with `loop.run_until_complete(...)`.
563+
This is a convenience method that wraps [`get_output()`][pydantic_ai.result.StreamedRunResult.get_output] with `loop.run_until_complete(...)`.
564564
You therefore can't use this method inside async code or if there's an active event loop.
565565
"""
566566
return get_event_loop().run_until_complete(self.get_output())
@@ -642,11 +642,9 @@ class FinalResult(Generic[OutputDataT]):
642642

643643
def _blocking_async_iterator(async_iter: AsyncIterator[T]) -> Iterator[T]:
644644
loop = get_event_loop()
645-
646645
while True:
647646
try:
648-
item = loop.run_until_complete(async_iter.__anext__())
649-
yield item
647+
yield loop.run_until_complete(async_iter.__anext__())
650648
except StopAsyncIteration:
651649
break
652650

0 commit comments

Comments
 (0)