xgae 0.1.14__py3-none-any.whl → 0.1.16__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of xgae might be problematic. Click here for more details.

@@ -3,7 +3,7 @@ import asyncio
3
3
  from typing import List, Dict, Any, Optional, AsyncGenerator, override
4
4
 
5
5
  from xgae.utils import log_trace
6
- from xgae.utils.json_helpers import format_for_yield
6
+
7
7
  from xgae.engine.responser.responser_base import TaskResponseProcessor, TaskResponserContext, TaskRunContinuousState
8
8
 
9
9
 
@@ -17,27 +17,23 @@ class StreamTaskResponser(TaskResponseProcessor):
17
17
  prompt_messages: List[Dict[str, Any]],
18
18
  continuous_state: TaskRunContinuousState
19
19
  ) -> AsyncGenerator[Dict[str, Any], None]:
20
- accumulated_content = continuous_state.get('accumulated_content', "")
21
- auto_continue_count = continuous_state.get('auto_continue_count', 0)
22
- can_auto_continue = continuous_state.get("auto_continue", False)
23
- use_assistant_chunk_msg = self.response_context.get("use_assistant_chunk_msg")
20
+ accumulated_content = continuous_state['accumulated_content']
21
+ auto_continue_count = continuous_state['auto_continue_count']
22
+ can_auto_continue = continuous_state['auto_continue']
23
+ msg_sequence = continuous_state['assistant_msg_sequence']
24
+
25
+ use_assistant_chunk_msg = self.response_context["use_assistant_chunk_msg"]
24
26
 
25
27
  finish_reason = None
26
28
  should_auto_continue = False
27
- sequence = continuous_state.get('assistant_msg_sequence', 0)
28
29
 
29
- pending_tool_executions = []
30
- yielded_tool_indices = set() # Track which tool statuses have been yielded
31
- tool_results_buffer = [] # Store (tool_call, result, tool_index, context)
32
- tool_index = 0
33
- current_xml_content = accumulated_content # Track XML content for streaming detection
34
-
35
- logging.info(f"=== StreamResp:tool_execute_on_stream={self.tool_execute_on_stream}, auto_continue_count={auto_continue_count}, accumulated_content_len={len(accumulated_content)}")
30
+ logging.info(f"=== StreamResp:Start Process Response, assistant_msg_sequence={msg_sequence}, "
31
+ f"accumulated_content_len={len(accumulated_content)}")
36
32
  try:
37
33
  async for llm_chunk in llm_response:
38
34
  if hasattr(llm_chunk, 'choices') and llm_chunk.choices and hasattr(llm_chunk.choices[0],'finish_reason'):
39
35
  if llm_chunk.choices[0].finish_reason:
40
- finish_reason = llm_chunk.choices[0].finish_reason
36
+ finish_reason = llm_chunk.choices[0].finish_reason # LLM finish reason: ‘stop' , 'length'
41
37
  logging.info(f"StreamResp:LLM chunk response finish_reason={finish_reason}")
42
38
 
43
39
  if hasattr(llm_chunk, 'choices') and llm_chunk.choices:
@@ -46,71 +42,24 @@ class StreamTaskResponser(TaskResponseProcessor):
46
42
  if llm_chunk_msg and hasattr(llm_chunk_msg, 'content') and llm_chunk_msg.content:
47
43
  chunk_content = llm_chunk_msg.content
48
44
  accumulated_content += chunk_content
49
- current_xml_content += chunk_content #Track streaming XML content
50
45
 
51
46
  xml_tool_call_count = len(self._extract_xml_chunks(accumulated_content))
52
- if self.max_xml_tool_calls <= 0 or xml_tool_call_count < self.max_xml_tool_calls:
47
+ if self.max_xml_tool_calls <= 0 or xml_tool_call_count <= self.max_xml_tool_calls:
53
48
  if use_assistant_chunk_msg:
54
49
  message_data = {"role": "assistant", "content": chunk_content}
55
- metadata = {"sequence": sequence}
56
- assistant_chunk_msg = self.create_response_message(type="assistant_chunk",content=message_data,
57
- is_llm_message=True,metadata=metadata)
50
+ metadata = {"sequence": msg_sequence}
51
+ assistant_chunk_msg = self.create_response_message(type="assistant_chunk",
52
+ content=message_data,
53
+ is_llm_message=True,
54
+ metadata=metadata)
58
55
  yield assistant_chunk_msg
59
-
60
- #Process XML tool calls during streaming
61
- if self.tool_execute_on_stream:
62
- xml_chunks = self._extract_xml_chunks(current_xml_content)
63
- for xml_chunk in xml_chunks:
64
- current_xml_content = current_xml_content.replace(xml_chunk, "", 1)
65
- result = self._parse_xml_tool_call(xml_chunk)
66
- if result:
67
- tool_call, parsing_details = result
68
-
69
- # Create tool context for streaming execution
70
- tool_context = self._create_tool_context(tool_call, tool_index, None, parsing_details)
71
-
72
- # Yield tool start status immediately
73
- tool_start_msg = self._add_tool_start_message(tool_context)
74
- if tool_start_msg:
75
- yield format_for_yield(tool_start_msg)
76
- yielded_tool_indices.add(tool_index)
77
-
78
- # Create async execution task
79
- execution_task = asyncio.create_task(self._execute_tool(tool_call))
80
- pending_tool_executions.append({"task": execution_task,"tool_call": tool_call,"tool_index": tool_index,
81
- "context": tool_context,"parsing_details": parsing_details})
82
- tool_index += 1
83
-
84
- sequence += 1
56
+ msg_sequence += 1
85
57
  else:
86
58
  finish_reason = "xml_tool_limit_reached"
59
+ logging.warning(f"StreamResp: Over XML Tool Limit, finish_reason='xml_tool_limit_reached', "
60
+ f"xml_tool_call_count={xml_tool_call_count}")
87
61
  break
88
62
 
89
- if len(accumulated_content) == 0:
90
- logging.warning(f"StreamResp: LLM response_message content is empty")
91
-
92
- # Wait for pending tool executions from streaming phase
93
- if pending_tool_executions:
94
- logging.info(f"Waiting for {len(pending_tool_executions)} pending streamed tool executions")
95
-
96
- pending_tasks = [execution["task"] for execution in pending_tool_executions]
97
- done, _ = await asyncio.wait(pending_tasks)
98
-
99
- for execution in pending_tool_executions:
100
- tool_idx = execution.get("tool_index", -1)
101
- context = execution["context"]
102
-
103
- try:
104
- if execution["task"].done():
105
- result = execution["task"].result()
106
- context.result = result
107
- tool_results_buffer.append((execution["tool_call"],result,tool_idx,context))
108
- else:
109
- logging.warning(f"Task for tool index {tool_idx} not done after wait.")
110
- except Exception as e:
111
- logging.error(f"Error getting result for pending tool execution {tool_idx}: {str(e)}")
112
- context.error = e
113
-
114
63
  if finish_reason == "xml_tool_limit_reached":
115
64
  xml_chunks = self._extract_xml_chunks(accumulated_content)
116
65
  if len(xml_chunks) > self.max_xml_tool_calls:
@@ -124,9 +73,9 @@ class StreamTaskResponser(TaskResponseProcessor):
124
73
  should_auto_continue = (can_auto_continue and finish_reason == 'length')
125
74
 
126
75
  self.root_span.event(name=f"stream_processor_start[{self.task_no}]({auto_continue_count})", level="DEFAULT",
127
- status_message=f"finish_reason={finish_reason}, tool_exec_strategy={self.tool_execution_strategy}, "
128
- f"parsed_xml_data_len={len(parsed_xml_data)}, accumulated_content={len(accumulated_content)}, "
129
- f"should_auto_continue={should_auto_continue}, pending_executions={len(pending_tool_executions)}")
76
+ status_message=f"finish_reason={finish_reason}, tool_exec_strategy={self.tool_exec_strategy}, "
77
+ f"parsed_xml_data_len={len(parsed_xml_data)}, accumulated_content_len={len(accumulated_content)}, "
78
+ f"should_auto_continue={should_auto_continue}")
130
79
 
131
80
  assistant_msg = None
132
81
  if accumulated_content and not should_auto_continue:
@@ -134,116 +83,55 @@ class StreamTaskResponser(TaskResponseProcessor):
134
83
  assistant_msg = self.add_response_message(type="assistant", content=message_data, is_llm_message=True)
135
84
  yield assistant_msg
136
85
 
137
- # Process results from both streaming and non-streaming executions
138
- tool_calls_to_execute = [item['tool_call'] for item in parsed_xml_data]
139
-
140
- # Update assistant_message_id for streaming tool contexts
141
86
  assistant_msg_id = assistant_msg['message_id'] if assistant_msg else None
142
- for execution in pending_tool_executions:
143
- if not execution["context"].assistant_message_id:
144
- execution["context"].assistant_message_id = assistant_msg_id
145
-
146
- if len(tool_calls_to_execute) > 0:
147
- if self.tool_execute_on_stream:
148
- # Handle results from streaming executions + any remaining tools
149
- remaining_tools = []
150
- streamed_tool_indices = set()
151
-
152
- # Identify which tools were already executed during streaming by index
153
- for execution in pending_tool_executions:
154
- streamed_tool_indices.add(execution["tool_index"])
155
-
156
- # Find remaining tools that weren't executed during streaming
157
- for i, parsed_item in enumerate(parsed_xml_data):
158
- tool_call = parsed_item['tool_call']
159
- tool_identifier = (tool_call.get('function_name', ''), str(tool_call.get('arguments', {})))
160
-
161
- # Check if this tool was already executed during streaming
162
- already_executed = False
163
- for execution in pending_tool_executions:
164
- exec_tool_call = execution["tool_call"]
165
- exec_identifier = (exec_tool_call.get('function_name', ''),str(exec_tool_call.get('arguments', {})))
166
- if tool_identifier == exec_identifier:
167
- already_executed = True
168
- break
169
-
170
- if not already_executed:
171
- remaining_tools.append((parsed_item['tool_call'], parsed_item['parsing_details'], tool_index))
172
- tool_index += 1
173
-
174
- # Execute remaining tools if any
175
- if remaining_tools:
176
- for tool_call, parsing_details, t_idx in remaining_tools:
177
- tool_context = self._create_tool_context(tool_call, t_idx, assistant_msg_id,parsing_details)
178
-
179
- tool_start_msg = self._add_tool_start_message(tool_context)
180
- yield format_for_yield(tool_start_msg)
181
-
182
- result = await self._execute_tool(tool_call)
183
- tool_context.result = result
184
- tool_results_buffer.append((tool_call, result, t_idx, tool_context))
185
-
186
- # Process all tool results
187
- for tool_call, result, t_idx, context in tool_results_buffer:
188
- tool_message = self._add_tool_messsage(tool_call, result, self.xml_adding_strategy,assistant_msg_id,
189
- getattr(context, 'parsing_details', None))
190
-
191
- tool_completed_msg = self._add_tool_completed_message(context,tool_message['message_id'] if tool_message else None)
192
- yield format_for_yield(tool_completed_msg)
193
-
194
- if tool_message:
195
- yield format_for_yield(tool_message)
196
-
197
- if tool_completed_msg["metadata"].get("agent_should_terminate") == "true":
198
- finish_reason = "completed"
199
- break
200
- else: # non-streaming execution
201
- tool_results = await self._execute_tools(tool_calls_to_execute, self.tool_execution_strategy)
202
- tool_index = 0
203
- for i, (returned_tool_call, tool_result) in enumerate(tool_results):
204
- parsed_xml_item = parsed_xml_data[i]
205
- tool_call = parsed_xml_item['tool_call']
206
- parsing_details = parsed_xml_item['parsing_details']
207
87
 
208
- tool_context = self._create_tool_context(tool_call, tool_index, assistant_msg_id,parsing_details, tool_result)
88
+ tool_calls_to_execute = [item['tool_call'] for item in parsed_xml_data]
89
+ if len(tool_calls_to_execute) > 0 and not should_auto_continue:
90
+ tool_results = await self._execute_tools(tool_calls_to_execute, self.tool_exec_strategy)
91
+ tool_index = 0
92
+ for i, (returned_tool_call, tool_result) in enumerate(tool_results):
93
+ parsed_xml_item = parsed_xml_data[i]
94
+ tool_call = parsed_xml_item['tool_call']
95
+ parsing_details = parsed_xml_item['parsing_details']
96
+
97
+ tool_context = self._create_tool_context(tool_call, tool_index, assistant_msg_id,parsing_details, tool_result)
209
98
 
210
- tool_start_msg = self._add_tool_start_message(tool_context)
211
- yield format_for_yield(tool_start_msg)
99
+ tool_start_msg = self._add_tool_start_message(tool_context)
100
+ yield tool_start_msg
212
101
 
213
- tool_message = self._add_tool_messsage(tool_call, tool_result, self.xml_adding_strategy,assistant_msg_id, parsing_details)
102
+ tool_message = self._add_tool_messsage(tool_call, tool_result, self.xml_adding_strategy,assistant_msg_id, parsing_details)
214
103
 
215
- tool_completed_msg = self._add_tool_completed_message(tool_context, tool_message['message_id'])
216
- yield format_for_yield(tool_completed_msg)
104
+ tool_completed_msg = self._add_tool_completed_message(tool_context, tool_message['message_id'])
105
+ yield tool_completed_msg
217
106
 
218
- yield format_for_yield(tool_message)
107
+ yield tool_message
219
108
 
220
- if tool_completed_msg["metadata"].get("agent_should_terminate") == "true":
221
- finish_reason = "completed"
222
- break
109
+ if tool_context.function_name in ['ask', 'complete']:
110
+ finish_reason = "completed"
111
+ break
223
112
 
224
- tool_index += 1
113
+ tool_index += 1
225
114
  else:
226
115
  finish_reason = "non_tool_call"
227
- logging.warning(f"StreamResp: tool_calls is empty, No Tool need to call !")
116
+ logging.warning(f"StreamResp: finish_reason='non_tool_call', No Tool need to call !")
228
117
 
229
118
  if finish_reason:
230
- finish_content = {"status_type": "finish", "finish_reason": finish_reason}
119
+ finish_content = {'status_type': "finish", 'finish_reason': finish_reason}
231
120
  finish_msg = self.add_response_message(type="status", content=finish_content, is_llm_message=False)
232
- yield format_for_yield(finish_msg)
121
+ yield finish_msg
233
122
  except Exception as e:
234
123
  trace = log_trace(e, f"StreamResp: Process response accumulated_content:\n {accumulated_content}")
235
124
  self.root_span.event(name="stream_response_process_error", level="ERROR",
236
125
  status_message=f"Process streaming response error: {e}",
237
126
  metadata={"content": accumulated_content, "trace": trace})
238
127
 
239
- content = {"role": "system", "status_type": "error", "message": f"Process streaming response error: {e}"}
128
+ content = {'role': "system", 'status_type': "error", 'message': f"Process streaming response error: {e}"}
240
129
  error_msg = self.add_response_message(type="status", content=content, is_llm_message=False)
241
- yield format_for_yield(error_msg)
130
+ yield error_msg
242
131
 
243
132
  raise # Use bare 'raise' to preserve the original exception with its traceback
244
133
  finally:
245
134
  if should_auto_continue:
246
135
  continuous_state['accumulated_content'] = accumulated_content
247
- continuous_state['assistant_msg_sequence'] = sequence
248
- logging.warning(
249
- f"StreamResp: Updated continuous state for auto-continue with {len(accumulated_content)} chars")
136
+ continuous_state['assistant_msg_sequence'] = msg_sequence
137
+ logging.warning(f"StreamResp: Updated continuous state for auto-continue with {len(accumulated_content)} chars")