11import json
2- from typing import Literal , cast
2+ from typing import Literal
33
44from agentex import AsyncAgentex
55from agentex .lib .core .adapters .streams .port import StreamRepository
1616 DataDelta ,
1717 ToolRequestDelta ,
1818 ToolResponseDelta ,
19+ ReasoningSummaryDelta ,
20+ ReasoningContentDelta ,
1921)
2022from agentex .lib .utils .logging import make_logger
2123from agentex .types .data_content import DataContent
2628from agentex .types .text_content import TextContent
2729from agentex .types .tool_request_content import ToolRequestContent
2830from agentex .types .tool_response_content import ToolResponseContent
31+ from agentex .types .reasoning_content import ReasoningContent
2932
3033logger = make_logger (__name__ )
3134
@@ -37,7 +40,10 @@ def _get_stream_topic(task_id: str) -> str:
3740class DeltaAccumulator :
3841 def __init__ (self ):
3942 self ._accumulated_deltas : list [TaskMessageDelta ] = []
40- self ._delta_type : Literal ["text" , "data" , "tool_request" , "tool_response" ] | None = None
43+ self ._delta_type : Literal ["text" , "data" , "tool_request" , "tool_response" , "reasoning" ] | None = None
44+ # For reasoning, we need to track both summary and content deltas
45+ self ._reasoning_summaries : dict [int , str ] = {}
46+ self ._reasoning_contents : dict [int , str ] = {}
4147
4248 def add_delta (self , delta : TaskMessageDelta ):
4349 if self ._delta_type is None :
@@ -49,15 +55,35 @@ def add_delta(self, delta: TaskMessageDelta):
4955 self ._delta_type = "tool_request"
5056 elif delta .type == "tool_response" :
5157 self ._delta_type = "tool_response"
58+ elif delta .type in ["reasoning_summary" , "reasoning_content" ]:
59+ self ._delta_type = "reasoning"
5260 else :
5361 raise ValueError (f"Unknown delta type: { delta .type } " )
5462 else :
55- if self ._delta_type != delta .type :
63+ # For reasoning, we allow both summary and content deltas
64+ if self ._delta_type == "reasoning" :
65+ if delta .type not in ["reasoning_summary" , "reasoning_content" ]:
66+ raise ValueError (
67+ f"Expected reasoning delta but got: { delta .type } "
68+ )
69+ elif self ._delta_type != delta .type :
5670 raise ValueError (
5771 f"Delta type mismatch: { self ._delta_type } != { delta .type } "
5872 )
5973
60- self ._accumulated_deltas .append (delta )
74+ # Handle reasoning deltas specially
75+ if delta .type == "reasoning_summary" :
76+ if isinstance (delta , ReasoningSummaryDelta ):
77+ if delta .summary_index not in self ._reasoning_summaries :
78+ self ._reasoning_summaries [delta .summary_index ] = ""
79+ self ._reasoning_summaries [delta .summary_index ] += delta .summary_delta or ""
80+ elif delta .type == "reasoning_content" :
81+ if isinstance (delta , ReasoningContentDelta ):
82+ if delta .content_index not in self ._reasoning_contents :
83+ self ._reasoning_contents [delta .content_index ] = ""
84+ self ._reasoning_contents [delta .content_index ] += delta .content_delta or ""
85+ else :
86+ self ._accumulated_deltas .append (delta )
6187
6288 def convert_to_content (self ) -> TaskMessageContent :
6389 if self ._delta_type == "text" :
@@ -66,6 +92,7 @@ def convert_to_content(self) -> TaskMessageContent:
6692 text_content_str = "" .join (
6793 [delta .text_delta or "" for delta in text_deltas ]
6894 )
95+ logger .info (f"Converting text deltas: { len (text_deltas )} deltas, total length: { len (text_content_str )} " )
6996 return TextContent (
7097 author = "agent" ,
7198 content = text_content_str ,
@@ -108,14 +135,34 @@ def convert_to_content(self) -> TaskMessageContent:
108135 # Type assertion: we know all deltas are ToolResponseDelta when _delta_type is TOOL_RESPONSE
109136 tool_response_deltas = [delta for delta in self ._accumulated_deltas if isinstance (delta , ToolResponseDelta )]
110137 tool_response_content_str = "" .join (
111- [delta .tool_response_delta or "" for delta in tool_response_deltas ]
138+ [delta .content_delta or "" for delta in tool_response_deltas ]
112139 )
113140 return ToolResponseContent (
114141 author = "agent" ,
115142 tool_call_id = tool_response_deltas [0 ].tool_call_id ,
116143 name = tool_response_deltas [0 ].name ,
117144 content = tool_response_content_str ,
118145 )
146+ elif self ._delta_type == "reasoning" :
147+ # Convert accumulated reasoning deltas to ReasoningContent
148+ # Sort by index to maintain order
149+ summary_list = [self ._reasoning_summaries [i ] for i in sorted (self ._reasoning_summaries .keys ()) if self ._reasoning_summaries [i ]]
150+ content_list = [self ._reasoning_contents [i ] for i in sorted (self ._reasoning_contents .keys ()) if self ._reasoning_contents [i ]]
151+
152+ # Only return reasoning content if we have non-empty summaries or content
153+ if summary_list or content_list :
154+ return ReasoningContent (
155+ author = "agent" ,
156+ summary = summary_list ,
157+ content = content_list if content_list else None ,
158+ type = "reasoning" ,
159+ )
160+ else :
161+ # Return empty text content instead of empty reasoning
162+ return TextContent (
163+ author = "agent" ,
164+ content = "" ,
165+ )
119166 else :
120167 raise ValueError (f"Unknown delta type: { self ._delta_type } " )
121168
@@ -155,6 +202,7 @@ async def open(self) -> "StreamingTaskMessageContext":
155202 start_event = StreamTaskMessageStart (
156203 parent_task_message = self .task_message ,
157204 content = self .initial_content ,
205+ type = "start" ,
158206 )
159207 await self ._streaming_service .stream_update (start_event )
160208
@@ -169,11 +217,19 @@ async def close(self) -> TaskMessage:
169217 return self .task_message # Already done
170218
171219 # Send the DONE event
172- done_event = StreamTaskMessageDone (parent_task_message = self .task_message )
220+ done_event = StreamTaskMessageDone (
221+ parent_task_message = self .task_message ,
222+ type = "done" ,
223+ )
173224 await self ._streaming_service .stream_update (done_event )
174225
175226 # Update the task message with the final content
176- if self ._delta_accumulator ._accumulated_deltas :
227+ has_deltas = (
228+ self ._delta_accumulator ._accumulated_deltas or
229+ self ._delta_accumulator ._reasoning_summaries or
230+ self ._delta_accumulator ._reasoning_contents
231+ )
232+ if has_deltas :
177233 self .task_message .content = self ._delta_accumulator .convert_to_content ()
178234
179235 await self ._agentex_client .messages .update (
0 commit comments