Added Pydantic AI sync, async, temporal integration#359
Open
michael-chou359 wants to merge 3 commits into
Open
Added Pydantic AI sync, async, temporal integration#359michael-chou359 wants to merge 3 commits into
michael-chou359 wants to merge 3 commits into
Conversation
86e30bb to
739f1e5
Compare
739f1e5 to
bee0223
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Greptile Summary
This PR adds Pydantic AI integration for Agentex across three deployment modes: synchronous HTTP-yield (sync ACP), asynchronous Redis-publish (async ACP), and Temporal-durable async. It also ships a new
CoalescingBuffer-backedStreamingTaskMessageContextfor time-and-size-windowed delta merging, and three end-to-end tutorial examples._pydantic_ai_sync.pyconverts Pydantic AIAgentStreamEvents intoStreamTaskMessage*events for direct HTTP streaming; correctly handles text, tool-call argument deltas, and tool results — butThinkingPartstart events emitTextContentinstead ofReasoningContent(flagged in a prior review), and the test suite does not assert the start-content type so the gap is not caught._pydantic_ai_async.pypushes events to Redis viaadk.streamingcontexts andadk.messages.create; serializes non-string tool-result content with barestr()rather than the richer_tool_return_contenthelper already present in the sync module, producing Python repr strings instead of structured data.streaming.pyintroducesCoalescingBuffer,DeltaAccumulator, andStreamingTaskMessageContextwith correct buffer-drain-before-done sequencing andfinally-block cleanup.Confidence Score: 4/5
The sync converter contains a type mismatch in the ThinkingPart start event that was flagged in the prior review and remains unresolved; all other new logic is sound.
The sync converter emits
TextContentin theStreamTaskMessageStartforThinkingPart, then immediately follows withReasoningContentDeltadeltas. Any consumer that uses the declared content type to select a renderer will treat thinking output as a plain-text bubble. The async module and Temporal integration are clean; the async tests are thorough; the streaming service buffer mechanics are correct.src/agentex/lib/adk/_modules/_pydantic_ai_sync.py— theThinkingPartbranch ofPartStartEvent(around line 159) needs the content type changed fromTextContenttoReasoningContent, mirroring the async helper.tests/lib/adk/test_pydantic_ai_sync.py—TestThinkingStreamingshould add anisinstance(..., ReasoningContent)assertion on the start event to lock this in.Important Files Changed
ThinkingPartstart events emitTextContentinstead ofReasoningContent, causing a type mismatch with the subsequentReasoningContentDeltadeltas (flagged in prior review).ReasoningContentfor ThinkingPart and handles cleanup infinally. Non-string tool result content is serialized withstr()rather than the richer_tool_return_contentlogic from the sync helper.CoalescingBuffer(50ms/128-char window),DeltaAccumulator, andStreamingTaskMessageContext. The buffer correctly drains before sending DONE. Thestream_updatepath forStreamTaskMessageDonewould double-publish the done event, but this path is not exercised by any current caller.TestThinkingStreamingis missing an assertion on the start event's content type, leaving the existingTextContent/ReasoningContentmismatch undetected by the test suite.temporal_agent.run()and closes viacomplete_task_signal.wait_condition(timeout=None)is intentional for a long-running conversational workflow.Sequence Diagram
sequenceDiagram participant PA as Pydantic AI participant SC as Sync Converter participant AC as Async Helper participant ACP as FastACP (HTTP yield) participant Redis as Redis Stream participant Agentex as Agentex Server note over SC,AC: Two parallel paths for the same Pydantic AI events rect rgb(220, 240, 255) note over PA,ACP: Sync ACP path PA->>SC: PartStartEvent(TextPart) SC->>ACP: StreamTaskMessageStart(TextContent) PA->>SC: PartDeltaEvent(TextPartDelta) SC->>ACP: StreamTaskMessageDelta(TextDelta) PA->>SC: PartStartEvent(ToolCallPart) SC->>ACP: StreamTaskMessageStart(ToolRequestContent) PA->>SC: PartDeltaEvent(ToolCallPartDelta) SC->>ACP: StreamTaskMessageDelta(ToolRequestDelta) PA->>SC: FunctionToolResultEvent SC->>ACP: StreamTaskMessageFull(ToolResponseContent) PA->>SC: PartEndEvent SC->>ACP: StreamTaskMessageDone end rect rgb(240, 255, 220) note over PA,Agentex: Async ACP path (Redis + CoalescingBuffer) PA->>AC: PartStartEvent(TextPart) AC->>Redis: StreamingContext open → START PA->>AC: PartDeltaEvent(TextPartDelta) AC->>Redis: CoalescingBuffer.add(TextDelta) PA->>AC: PartEndEvent(TextPart) AC->>Redis: buffer.close() → flush → DONE PA->>AC: PartStartEvent(ToolCallPart) PA->>AC: PartEndEvent(ToolCallPart) AC->>Agentex: adk.messages.create(ToolRequestContent) PA->>AC: FunctionToolResultEvent AC->>Agentex: adk.messages.create(ToolResponseContent) endComments Outside Diff (1)
src/agentex/lib/adk/_modules/_pydantic_ai_sync.py, line 431-450 (link)TextContentinstead ofReasoningContentThe sync converter emits
StreamTaskMessageStart(content=TextContent(...))forThinkingPartevents (line 434–440), but immediately follows withStreamTaskMessageDelta(delta=ReasoningContentDelta(...))deltas. The async counterpart (_pydantic_ai_async.py) consistently usesReasoningContentas the initial content type for the same event. The type mismatch — aTextContentstart receivingReasoningContentDeltaupdates — means any server-side logic that requires the accumulated message type to match the delta type will either fail silently or render thinking content as a plain text bubble rather than a collapsible reasoning block.ReasoningContentis not imported in this file at all, confirming the type was never set intentionally.Prompt To Fix With AI
Prompt To Fix All With AI
Reviews (3): Last reviewed commit: "fix lint errors" | Re-trigger Greptile