xgae 0.1.13__py3-none-any.whl → 0.1.15__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.
- xgae/engine/mcp_tool_box.py +8 -7
- xgae/engine/prompt_builder.py +9 -8
- xgae/engine/responser/non_stream_responser.py +13 -13
- xgae/engine/responser/responser_base.py +174 -262
- xgae/engine/responser/stream_responser.py +165 -45
- xgae/engine/task_engine.py +93 -93
- xgae/engine/task_langfuse.py +11 -11
- xgae/utils/json_helpers.py +0 -29
- xgae/utils/llm_client.py +23 -23
- xgae/utils/xml_tool_parser.py +7 -7
- {xgae-0.1.13.dist-info → xgae-0.1.15.dist-info}/METADATA +1 -1
- xgae-0.1.15.dist-info/RECORD +21 -0
- {xgae-0.1.13.dist-info → xgae-0.1.15.dist-info}/entry_points.txt +1 -1
- xgae-0.1.13.dist-info/RECORD +0 -21
- {xgae-0.1.13.dist-info → xgae-0.1.15.dist-info}/WHEEL +0 -0
xgae/engine/task_engine.py
CHANGED
|
@@ -7,7 +7,7 @@ from uuid import uuid4
|
|
|
7
7
|
|
|
8
8
|
from xgae.utils import log_trace, to_bool
|
|
9
9
|
from xgae.utils.llm_client import LLMClient, LLMConfig
|
|
10
|
-
|
|
10
|
+
|
|
11
11
|
|
|
12
12
|
from xgae.engine.engine_base import XGAResponseMsgType, XGAResponseMessage, XGAToolBox, XGATaskResult
|
|
13
13
|
from xgae.engine.task_langfuse import XGATaskLangFuse
|
|
@@ -28,32 +28,32 @@ class XGATaskEngine:
|
|
|
28
28
|
llm_config: Optional[LLMConfig] = None,
|
|
29
29
|
prompt_builder: Optional[XGAPromptBuilder] = None,
|
|
30
30
|
tool_box: Optional[XGAToolBox] = None):
|
|
31
|
-
self.task_id
|
|
32
|
-
self.agent_id
|
|
31
|
+
self.task_id = task_id if task_id else f"xga_task_{uuid4()}"
|
|
32
|
+
self.agent_id = agent_id
|
|
33
33
|
self.session_id = session_id
|
|
34
34
|
|
|
35
35
|
self.llm_client = LLMClient(llm_config)
|
|
36
36
|
self.model_name = self.llm_client.model_name
|
|
37
|
-
self.is_stream
|
|
37
|
+
self.is_stream = self.llm_client.is_stream
|
|
38
38
|
|
|
39
|
-
self.prompt_builder
|
|
40
|
-
self.tool_box: XGAToolBox
|
|
41
|
-
self.task_langfuse: XGATaskLangFuse = None
|
|
39
|
+
self.prompt_builder = prompt_builder or XGAPromptBuilder(system_prompt)
|
|
40
|
+
self.tool_box: XGAToolBox = tool_box or XGAMcpToolBox()
|
|
42
41
|
|
|
43
42
|
self.general_tools:List[str] = general_tools
|
|
44
|
-
self.custom_tools:List[str]
|
|
45
|
-
self.task_response_msgs: List[XGAResponseMessage] = []
|
|
43
|
+
self.custom_tools:List[str] = custom_tools
|
|
46
44
|
|
|
47
|
-
max_auto_run = max_auto_run if max_auto_run else int(os.getenv(
|
|
45
|
+
max_auto_run = max_auto_run if max_auto_run else int(os.getenv('MAX_AUTO_RUN', 15))
|
|
48
46
|
self.max_auto_run: int = 1 if max_auto_run <= 1 else max_auto_run
|
|
49
47
|
|
|
50
|
-
self.use_assistant_chunk_msg = to_bool(os.getenv(
|
|
48
|
+
self.use_assistant_chunk_msg = to_bool(os.getenv('USE_ASSISTANT_CHUNK_MSG', False))
|
|
51
49
|
self.tool_exec_parallel = True if tool_exec_parallel is None else tool_exec_parallel
|
|
52
50
|
|
|
53
51
|
self.task_no = -1
|
|
54
52
|
self.task_run_id :str = None
|
|
55
53
|
self.task_prompt :str = None
|
|
54
|
+
self.task_langfuse: XGATaskLangFuse = None
|
|
56
55
|
|
|
56
|
+
self.task_response_msgs: List[XGAResponseMessage] = []
|
|
57
57
|
|
|
58
58
|
async def run_task_with_final_answer(self,
|
|
59
59
|
task_message: Dict[str, Any],
|
|
@@ -126,14 +126,15 @@ class XGATaskEngine:
|
|
|
126
126
|
|
|
127
127
|
async def _run_task_auto(self) -> AsyncGenerator[Dict[str, Any], None]:
|
|
128
128
|
continuous_state: TaskRunContinuousState = {
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
129
|
+
'accumulated_content' : "",
|
|
130
|
+
'auto_continue_count' : 0,
|
|
131
|
+
'auto_continue' : False if self.max_auto_run <= 1 else True,
|
|
132
|
+
'assistant_msg_sequence': 0
|
|
132
133
|
}
|
|
133
134
|
|
|
134
135
|
def update_continuous_state(_auto_continue_count, _auto_continue):
|
|
135
|
-
continuous_state[
|
|
136
|
-
continuous_state[
|
|
136
|
+
continuous_state['auto_continue_count'] = _auto_continue_count
|
|
137
|
+
continuous_state['auto_continue'] = _auto_continue
|
|
137
138
|
|
|
138
139
|
auto_continue_count = 0
|
|
139
140
|
auto_continue = True
|
|
@@ -144,28 +145,28 @@ class XGATaskEngine:
|
|
|
144
145
|
async for chunk in self._run_task_once(continuous_state):
|
|
145
146
|
yield chunk
|
|
146
147
|
try:
|
|
147
|
-
if chunk
|
|
148
|
-
|
|
149
|
-
status_type =
|
|
148
|
+
if chunk['type'] == "status":
|
|
149
|
+
status_content = chunk['content']
|
|
150
|
+
status_type = status_content['status_type']
|
|
150
151
|
if status_type == "error":
|
|
151
|
-
logging.error(f"TaskEngine run_task_auto: task_response error: {chunk.get('message'
|
|
152
|
+
logging.error(f"TaskEngine run_task_auto: task_response error: {chunk.get('message')}")
|
|
152
153
|
auto_continue = False
|
|
153
154
|
break
|
|
154
155
|
elif status_type == 'finish':
|
|
155
|
-
finish_reason =
|
|
156
|
-
if finish_reason ==
|
|
156
|
+
finish_reason = status_content['finish_reason']
|
|
157
|
+
if finish_reason == "completed":
|
|
157
158
|
logging.info(f"TaskEngine run_task_auto: Detected finish_reason='completed', TASK_COMPLETE Success !")
|
|
158
159
|
auto_continue = False
|
|
159
160
|
break
|
|
160
|
-
elif finish_reason ==
|
|
161
|
+
elif finish_reason == "xml_tool_limit_reached":
|
|
161
162
|
logging.warning(f"TaskEngine run_task_auto: Detected finish_reason='xml_tool_limit_reached', stop auto-continue")
|
|
162
163
|
auto_continue = False
|
|
163
164
|
break
|
|
164
|
-
elif finish_reason ==
|
|
165
|
+
elif finish_reason == "non_tool_call":
|
|
165
166
|
logging.warning(f"TaskEngine run_task_auto: Detected finish_reason='non_tool_call', stop auto-continue")
|
|
166
167
|
auto_continue = False
|
|
167
168
|
break
|
|
168
|
-
elif finish_reason
|
|
169
|
+
elif finish_reason in ["stop", "length"]: # 'length' occur on some LLM
|
|
169
170
|
auto_continue = True
|
|
170
171
|
auto_continue_count += 1
|
|
171
172
|
update_continuous_state(auto_continue_count, auto_continue)
|
|
@@ -176,18 +177,18 @@ class XGATaskEngine:
|
|
|
176
177
|
status_message=f"Task Engine parse chunk error: {parse_error}",
|
|
177
178
|
metadata={"content": chunk, "trace": trace})
|
|
178
179
|
|
|
179
|
-
|
|
180
|
-
error_msg = self.add_response_message(type="status", content=
|
|
181
|
-
yield
|
|
180
|
+
status_content = {"status_type": "error", "role": "system", "message": "Parse response chunk Error"}
|
|
181
|
+
error_msg = self.add_response_message(type="status", content=status_content, is_llm_message=False)
|
|
182
|
+
yield error_msg
|
|
182
183
|
except Exception as run_error:
|
|
183
184
|
trace = log_trace(run_error, "TaskEngine run_task_auto: Call task_run_once")
|
|
184
185
|
self.task_langfuse.root_span.event(name="engine_task_run_once_error", level="ERROR",
|
|
185
186
|
status_message=f"Call task_run_once error: {run_error}",
|
|
186
187
|
metadata={"trace": trace})
|
|
187
188
|
|
|
188
|
-
|
|
189
|
-
error_msg = self.add_response_message(type="status", content=
|
|
190
|
-
yield
|
|
189
|
+
status_content = {"status_type": "error", "role": "system", "message": "Call run_task_once error"}
|
|
190
|
+
error_msg = self.add_response_message(type="status", content=status_content, is_llm_message=False)
|
|
191
|
+
yield error_msg
|
|
191
192
|
|
|
192
193
|
|
|
193
194
|
async def _run_task_once(self, continuous_state: TaskRunContinuousState) -> AsyncGenerator[Dict[str, Any], None]:
|
|
@@ -206,8 +207,6 @@ class XGATaskEngine:
|
|
|
206
207
|
auto_count = continuous_state.get("auto_continue_count")
|
|
207
208
|
langfuse_metadata = self.task_langfuse.create_llm_langfuse_meta(auto_count)
|
|
208
209
|
|
|
209
|
-
self.task_langfuse.root_span.event(name="engine_start_create_completion", level="DEFAULT",
|
|
210
|
-
status_message=(f"Task Engine start create_completion llm_messages len={len(llm_messages)}"))
|
|
211
210
|
llm_response = await self.llm_client.create_completion(llm_messages, langfuse_metadata)
|
|
212
211
|
response_processor = self._create_response_processer()
|
|
213
212
|
|
|
@@ -222,44 +221,45 @@ class XGATaskEngine:
|
|
|
222
221
|
try:
|
|
223
222
|
finish_reason = ''
|
|
224
223
|
for chunk in reverse_chunks:
|
|
225
|
-
chunk_type = chunk
|
|
224
|
+
chunk_type = chunk['type']
|
|
226
225
|
if chunk_type == "status":
|
|
227
|
-
status_content =
|
|
228
|
-
status_type = status_content
|
|
226
|
+
status_content = chunk['content']
|
|
227
|
+
status_type = status_content['status_type']
|
|
229
228
|
if status_type == "error":
|
|
230
|
-
error = status_content
|
|
229
|
+
error = status_content['message']
|
|
231
230
|
final_result = XGATaskResult(type="error", content=error)
|
|
232
231
|
elif status_type == "finish":
|
|
233
|
-
finish_reason = status_content
|
|
234
|
-
elif chunk_type == "tool" and finish_reason in ['completed', 'stop', 'xml_tool_limit_reached']:
|
|
235
|
-
tool_content
|
|
232
|
+
finish_reason = status_content['finish_reason']
|
|
233
|
+
elif chunk_type == "tool" and finish_reason in ['completed', 'stop', 'xml_tool_limit_reached', 'length']:
|
|
234
|
+
tool_content= chunk['content']
|
|
236
235
|
tool_execution = tool_content.get('tool_execution')
|
|
237
236
|
tool_name = tool_execution.get('function_name')
|
|
238
237
|
if tool_name == "complete":
|
|
239
|
-
result_content = tool_execution[
|
|
240
|
-
attachments = tool_execution[
|
|
238
|
+
result_content = tool_execution['arguments'].get('text', "Task completed with no answer")
|
|
239
|
+
attachments = tool_execution['arguments'].get('attachments', None)
|
|
241
240
|
final_result = XGATaskResult(type="answer", content=result_content, attachments=attachments)
|
|
242
241
|
elif tool_name == "ask":
|
|
243
242
|
result_content = tool_execution["arguments"].get("text", "Task ask for more info")
|
|
244
243
|
attachments = tool_execution["arguments"].get("attachments", None)
|
|
245
244
|
final_result = XGATaskResult(type="ask", content=result_content, attachments=attachments)
|
|
246
245
|
else:
|
|
246
|
+
# finish reason 1) 'stop': auto run reach max_auto_run limit 2) 'xml_tool_limit_reached' 3) 'length': occur on some LLM
|
|
247
247
|
tool_result = tool_execution.get("result", None)
|
|
248
248
|
if tool_result is not None:
|
|
249
|
-
success = tool_result.get(
|
|
250
|
-
output = tool_result.get(
|
|
249
|
+
success = tool_result.get('success')
|
|
250
|
+
output = tool_result.get('output', '')
|
|
251
251
|
result_type = "answer" if success else "error"
|
|
252
252
|
result_content = f"Task execute '{tool_name}' {result_type}: {output}"
|
|
253
253
|
final_result = XGATaskResult(type=result_type, content=result_content)
|
|
254
|
-
elif chunk_type == "assistant" and finish_reason ==
|
|
255
|
-
assis_content = chunk
|
|
256
|
-
result_content = assis_content.get(
|
|
254
|
+
elif chunk_type == "assistant" and finish_reason == "non_tool_call":
|
|
255
|
+
assis_content = chunk['content']
|
|
256
|
+
result_content = assis_content.get('content', "LLM output is empty")
|
|
257
257
|
final_result = XGATaskResult(type="answer", content=result_content)
|
|
258
258
|
|
|
259
259
|
if final_result:
|
|
260
260
|
break
|
|
261
261
|
|
|
262
|
-
if final_result and finish_reason == "completed":
|
|
262
|
+
if final_result and (finish_reason == "completed" or finish_reason == "non_tool_call"):
|
|
263
263
|
logging.info(f"✅ FINAL_RESULT: finish_reason={finish_reason}, final_result={final_result}")
|
|
264
264
|
elif final_result is not None:
|
|
265
265
|
logging.warning(f"⚠️ FINAL_RESULT: finish_reason={finish_reason}, final_result={final_result}")
|
|
@@ -281,18 +281,18 @@ class XGATaskEngine:
|
|
|
281
281
|
is_llm_message: bool,
|
|
282
282
|
metadata: Optional[Dict[str, Any]]=None)-> XGAResponseMessage:
|
|
283
283
|
metadata = metadata or {}
|
|
284
|
-
metadata["task_id"]
|
|
284
|
+
metadata["task_id"] = self.task_id
|
|
285
285
|
metadata["task_run_id"] = self.task_run_id
|
|
286
|
-
metadata["trace_id"]
|
|
287
|
-
metadata["session_id"]
|
|
288
|
-
metadata["agent_id"]
|
|
286
|
+
metadata["trace_id"] = self.task_langfuse.trace_id
|
|
287
|
+
metadata["session_id"] = self.session_id
|
|
288
|
+
metadata["agent_id"] = self.agent_id
|
|
289
289
|
|
|
290
290
|
message = XGAResponseMessage(
|
|
291
|
-
message_id
|
|
292
|
-
type
|
|
293
|
-
is_llm_message=is_llm_message,
|
|
294
|
-
content
|
|
295
|
-
metadata
|
|
291
|
+
message_id = f"xga_msg_{uuid4()}",
|
|
292
|
+
type = type,
|
|
293
|
+
is_llm_message = is_llm_message,
|
|
294
|
+
content = content,
|
|
295
|
+
metadata = metadata
|
|
296
296
|
)
|
|
297
297
|
|
|
298
298
|
return message
|
|
@@ -308,12 +308,12 @@ class XGATaskEngine:
|
|
|
308
308
|
def get_history_llm_messages (self) -> List[Dict[str, Any]]:
|
|
309
309
|
llm_messages = []
|
|
310
310
|
for message in self.task_response_msgs:
|
|
311
|
-
if message[
|
|
311
|
+
if message['is_llm_message']:
|
|
312
312
|
llm_messages.append(message)
|
|
313
313
|
|
|
314
314
|
response_llm_contents = []
|
|
315
315
|
for llm_message in llm_messages:
|
|
316
|
-
content = llm_message[
|
|
316
|
+
content = llm_message['content']
|
|
317
317
|
if isinstance(content, str):
|
|
318
318
|
try:
|
|
319
319
|
_content = json.loads(content)
|
|
@@ -327,7 +327,7 @@ class XGATaskEngine:
|
|
|
327
327
|
|
|
328
328
|
def _create_response_processer(self) -> TaskResponseProcessor:
|
|
329
329
|
response_context = self._create_response_context()
|
|
330
|
-
is_stream = response_context
|
|
330
|
+
is_stream = response_context['is_stream']
|
|
331
331
|
if is_stream:
|
|
332
332
|
from xgae.engine.responser.stream_responser import StreamTaskResponser
|
|
333
333
|
return StreamTaskResponser(response_context)
|
|
@@ -337,19 +337,20 @@ class XGATaskEngine:
|
|
|
337
337
|
|
|
338
338
|
def _create_response_context(self) -> TaskResponserContext:
|
|
339
339
|
response_context: TaskResponserContext = {
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
"
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
340
|
+
'is_stream' : self.is_stream,
|
|
341
|
+
'task_id' : self.task_id,
|
|
342
|
+
'task_run_id' : self.task_run_id,
|
|
343
|
+
'task_no' : self.task_no,
|
|
344
|
+
'model_name' : self.model_name,
|
|
345
|
+
'max_xml_tool_calls' : 0,
|
|
346
|
+
'use_assistant_chunk_msg' : self.use_assistant_chunk_msg,
|
|
347
|
+
'tool_exec_strategy' : "parallel" if self.tool_exec_parallel else "sequential",
|
|
348
|
+
'tool_exec_on_stream' : True,
|
|
349
|
+
'xml_adding_strategy' : "assistant_message", # user_message
|
|
350
|
+
'add_response_msg_func' : self.add_response_message,
|
|
351
|
+
'create_response_msg_func' : self.create_response_message,
|
|
352
|
+
'tool_box' : self.tool_box,
|
|
353
|
+
'task_langfuse' : self.task_langfuse,
|
|
353
354
|
}
|
|
354
355
|
return response_context
|
|
355
356
|
|
|
@@ -360,24 +361,24 @@ class XGATaskEngine:
|
|
|
360
361
|
|
|
361
362
|
def _logging_reponse_chunk(self, chunk, auto_count: int)-> None:
|
|
362
363
|
try:
|
|
363
|
-
chunk_type = chunk
|
|
364
|
+
chunk_type = chunk['type']
|
|
364
365
|
prefix = ""
|
|
365
|
-
if chunk_type ==
|
|
366
|
-
|
|
367
|
-
status_type =
|
|
366
|
+
if chunk_type == "status":
|
|
367
|
+
status_content = chunk['content']
|
|
368
|
+
status_type = status_content.get('status_type', "empty")
|
|
368
369
|
if status_type in ["tool_started", "tool_completed"]:
|
|
369
370
|
return
|
|
370
371
|
prefix = "-" + status_type
|
|
371
|
-
elif chunk_type ==
|
|
372
|
-
tool_content =
|
|
372
|
+
elif chunk_type == "tool":
|
|
373
|
+
tool_content = chunk['content']
|
|
373
374
|
tool_execution = tool_content.get('tool_execution')
|
|
374
375
|
tool_name = tool_execution.get('function_name')
|
|
375
376
|
prefix = "-" + tool_name
|
|
376
377
|
|
|
377
|
-
|
|
378
|
-
pretty_content =
|
|
379
|
-
if isinstance(
|
|
380
|
-
pretty_content = json.dumps(
|
|
378
|
+
status_content = chunk['content']
|
|
379
|
+
pretty_content = status_content
|
|
380
|
+
if isinstance(status_content, dict):
|
|
381
|
+
pretty_content = json.dumps(status_content, ensure_ascii=False, indent=2)
|
|
381
382
|
|
|
382
383
|
if chunk_type == "assistant_chunk":
|
|
383
384
|
logging.debug(f"TASK_RESP_CHUNK[{auto_count}]<{chunk_type}{prefix}> content: {pretty_content}")
|
|
@@ -396,19 +397,18 @@ if __name__ == "__main__":
|
|
|
396
397
|
setup_logging()
|
|
397
398
|
|
|
398
399
|
async def main():
|
|
399
|
-
# Before Run Exec: uv run
|
|
400
|
+
# Before Run Exec: uv run example-fault-tools
|
|
400
401
|
tool_box = XGAMcpToolBox(custom_mcp_server_file="mcpservers/custom_servers.json")
|
|
401
402
|
system_prompt = read_file("templates/example/fault_user_prompt.txt")
|
|
402
403
|
engine = XGATaskEngine(tool_box=tool_box,
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
print("FINAL RESULT:", final_result)
|
|
404
|
+
custom_tools=["*"],
|
|
405
|
+
system_prompt=system_prompt,
|
|
406
|
+
session_id="session_1",
|
|
407
|
+
agent_id="agent_1",)
|
|
408
|
+
|
|
409
|
+
final_result = await engine.run_task_with_final_answer(task_message={'role': "user",
|
|
410
|
+
'content': "locate 10.0.0.1 fault and solution"})
|
|
411
|
+
print(f"FINAL RESULT:{final_result}")
|
|
412
412
|
|
|
413
413
|
|
|
414
414
|
asyncio.run(main())
|
xgae/engine/task_langfuse.py
CHANGED
|
@@ -18,14 +18,14 @@ class XGATaskLangFuse:
|
|
|
18
18
|
if XGATaskLangFuse.langfuse is None:
|
|
19
19
|
XGATaskLangFuse.langfuse = setup_langfuse()
|
|
20
20
|
|
|
21
|
-
self.session_id
|
|
22
|
-
self.task_id
|
|
23
|
-
self.task_run_id
|
|
24
|
-
self.task_no
|
|
25
|
-
self.agent_id
|
|
21
|
+
self.session_id = session_id
|
|
22
|
+
self.task_id = task_id
|
|
23
|
+
self.task_run_id = task_run_id
|
|
24
|
+
self.task_no = task_no
|
|
25
|
+
self.agent_id = agent_id
|
|
26
26
|
|
|
27
|
-
self.trace_id
|
|
28
|
-
self.root_span
|
|
27
|
+
self.trace_id = None
|
|
28
|
+
self.root_span = None
|
|
29
29
|
self.root_span_name = None
|
|
30
30
|
|
|
31
31
|
|
|
@@ -58,8 +58,8 @@ class XGATaskLangFuse:
|
|
|
58
58
|
generation_name = f"xga_task_engine_llm_completion[{self.task_no}]({llm_count})"
|
|
59
59
|
generation_id = f"{self.task_run_id}({llm_count})"
|
|
60
60
|
return LangfuseMetadata(
|
|
61
|
-
generation_name=generation_name,
|
|
62
|
-
generation_id=generation_id,
|
|
63
|
-
existing_trace_id=self.trace_id,
|
|
64
|
-
session_id=self.session_id,
|
|
61
|
+
generation_name = generation_name,
|
|
62
|
+
generation_id = generation_id,
|
|
63
|
+
existing_trace_id = self.trace_id,
|
|
64
|
+
session_id = self.session_id,
|
|
65
65
|
)
|
xgae/utils/json_helpers.py
CHANGED
|
@@ -137,32 +137,3 @@ def to_json_string(value: Any) -> str:
|
|
|
137
137
|
# For all other types, convert to JSON
|
|
138
138
|
return json.dumps(value)
|
|
139
139
|
|
|
140
|
-
|
|
141
|
-
def format_for_yield(message_object: Dict[str, Any]) -> Dict[str, Any]:
|
|
142
|
-
"""
|
|
143
|
-
Format a message object for yielding, ensuring content and metadata are JSON strings.
|
|
144
|
-
|
|
145
|
-
This maintains backward compatibility with clients expecting JSON strings
|
|
146
|
-
while the database now stores proper objects.
|
|
147
|
-
|
|
148
|
-
Args:
|
|
149
|
-
message_object: The message object from the database
|
|
150
|
-
|
|
151
|
-
Returns:
|
|
152
|
-
Message object with content and metadata as JSON strings
|
|
153
|
-
"""
|
|
154
|
-
if not message_object:
|
|
155
|
-
return message_object
|
|
156
|
-
|
|
157
|
-
# Create a copy to avoid modifying the original
|
|
158
|
-
formatted = message_object.copy()
|
|
159
|
-
|
|
160
|
-
# Ensure content is a JSON string
|
|
161
|
-
if 'content' in formatted and not isinstance(formatted['content'], str):
|
|
162
|
-
formatted['content'] = json.dumps(formatted['content'], ensure_ascii=False, indent=2)
|
|
163
|
-
|
|
164
|
-
# Ensure metadata is a JSON string
|
|
165
|
-
if 'metadata' in formatted and not isinstance(formatted['metadata'], str):
|
|
166
|
-
formatted['metadata'] = json.dumps(formatted['metadata'], ensure_ascii=False, indent=2)
|
|
167
|
-
|
|
168
|
-
return formatted
|
xgae/utils/llm_client.py
CHANGED
|
@@ -51,34 +51,34 @@ class LLMClient:
|
|
|
51
51
|
self._init_langfuse()
|
|
52
52
|
|
|
53
53
|
llm_config = llm_config or LLMConfig()
|
|
54
|
-
self.max_retries = int(os.getenv(
|
|
54
|
+
self.max_retries = int(os.getenv('LLM_MAX_RETRIES', 1))
|
|
55
55
|
|
|
56
|
-
env_llm_model
|
|
57
|
-
env_llm_api_key
|
|
58
|
-
env_llm_api_base
|
|
59
|
-
env_llm_max_tokens
|
|
60
|
-
env_llm_temperature
|
|
61
|
-
env_llm_stream
|
|
62
|
-
env_llm_enable_thinking = to_bool(os.getenv(
|
|
56
|
+
env_llm_model = os.getenv('LLM_MODEL', "openai/qwen3-235b-a22b")
|
|
57
|
+
env_llm_api_key = os.getenv('LLM_API_KEY')
|
|
58
|
+
env_llm_api_base = os.getenv('LLM_API_BASE', "https://dashscope.aliyuncs.com/compatible-mode/v1")
|
|
59
|
+
env_llm_max_tokens = int(os.getenv('LLM_MAX_TOKENS', 16384))
|
|
60
|
+
env_llm_temperature = float(os.getenv('LLM_TEMPERATURE', 0.7))
|
|
61
|
+
env_llm_stream = to_bool(os.getenv('LLM_STREAM', False))
|
|
62
|
+
env_llm_enable_thinking = to_bool(os.getenv('LLM_ENABLE_THINKING', False))
|
|
63
63
|
|
|
64
64
|
llm_config_params = {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
65
|
+
'model': llm_config.get('model', env_llm_model),
|
|
66
|
+
'model_name': llm_config.get('model_name', env_llm_model),
|
|
67
|
+
'model_id': llm_config.get('model_id', None),
|
|
68
|
+
'api_key': llm_config.get('api_key', env_llm_api_key),
|
|
69
|
+
'api_base': llm_config.get('api_base', env_llm_api_base),
|
|
70
|
+
'temperature': llm_config.get('temperature', env_llm_temperature),
|
|
71
|
+
'max_tokens': llm_config.get('max_tokens', env_llm_max_tokens),
|
|
72
|
+
'stream': llm_config.get('stream', env_llm_stream),
|
|
73
|
+
'enable_thinking': llm_config.get('enable_thinking', env_llm_enable_thinking),
|
|
74
|
+
'reasoning_effort': llm_config.get('reasoning_effort', "low"),
|
|
75
|
+
'response_format': llm_config.get('response_format', None),
|
|
76
|
+
'top_p': llm_config.get('top_p', None),
|
|
77
|
+
'tools': None,
|
|
78
|
+
'tool_choice': 'none',
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
-
self.model_name = llm_config_params[
|
|
81
|
+
self.model_name = llm_config_params['model_name']
|
|
82
82
|
self.is_stream = llm_config_params['stream']
|
|
83
83
|
|
|
84
84
|
self.lite_llm_params = self._prepare_llm_params(llm_config_params)
|
xgae/utils/xml_tool_parser.py
CHANGED
|
@@ -95,8 +95,8 @@ class XMLToolParser:
|
|
|
95
95
|
"""Parse a single invoke block into an XMLToolCall."""
|
|
96
96
|
parameters = {}
|
|
97
97
|
parsing_details = {
|
|
98
|
-
|
|
99
|
-
|
|
98
|
+
'function_name': function_name,
|
|
99
|
+
'raw_parameters': {}
|
|
100
100
|
}
|
|
101
101
|
|
|
102
102
|
param_matches = self.PARAMETER_PATTERN.findall(invoke_content)
|
|
@@ -104,7 +104,7 @@ class XMLToolParser:
|
|
|
104
104
|
param_value = param_value.strip()
|
|
105
105
|
parsed_value = self._parse_parameter_value(param_value)
|
|
106
106
|
parameters[param_name] = parsed_value
|
|
107
|
-
parsing_details[
|
|
107
|
+
parsing_details['raw_parameters'][param_name] = param_value
|
|
108
108
|
|
|
109
109
|
# Extract the raw XML for this specific invoke
|
|
110
110
|
invoke_pattern = re.compile(
|
|
@@ -115,10 +115,10 @@ class XMLToolParser:
|
|
|
115
115
|
raw_xml = raw_xml_match.group(0) if raw_xml_match else f"<invoke name=\"{function_name}\">...</invoke>"
|
|
116
116
|
|
|
117
117
|
return XMLToolCall(
|
|
118
|
-
function_name=function_name,
|
|
119
|
-
parameters=parameters,
|
|
120
|
-
raw_xml=raw_xml,
|
|
121
|
-
parsing_details=parsing_details
|
|
118
|
+
function_name = function_name,
|
|
119
|
+
parameters = parameters,
|
|
120
|
+
raw_xml = raw_xml,
|
|
121
|
+
parsing_details = parsing_details
|
|
122
122
|
)
|
|
123
123
|
|
|
124
124
|
def _parse_parameter_value(self, value: str) -> Any:
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
xgae/__init__.py,sha256=OEUd9y9AoGBd3xYerdTTpz9xl4NWkmXeq1a2eil7Qro,72
|
|
2
|
+
xgae/cli_app.py,sha256=vKuCIJw0gwXdtkT-QNCZKt2dE53thvTFwQr7nTgvaPY,3000
|
|
3
|
+
xgae/engine/engine_base.py,sha256=-QZqLRbQdwRUfbY4l3i7dFfMB-BL267a-wGZR9bMPLc,1662
|
|
4
|
+
xgae/engine/mcp_tool_box.py,sha256=ohlSN8NLoZR-tQyldoE3gX3cPoAUhmp5-ih0vSk4haM,10415
|
|
5
|
+
xgae/engine/prompt_builder.py,sha256=nw9BEROI6IbmNMLgggUTegfqN8FjbSO2lixTbpho3I4,4365
|
|
6
|
+
xgae/engine/task_engine.py,sha256=6YsibsPkZI526YzqGzRkAT0NoRZBU_NzUXjKZFAm-Ww,20999
|
|
7
|
+
xgae/engine/task_langfuse.py,sha256=86KRHpLs1cVh2b9PfcHzYGGmfxLZ37URI1cMZxKiGis,2416
|
|
8
|
+
xgae/engine/responser/non_stream_responser.py,sha256=5K1FwhlX4-21Z-BJKTqvZo-RdHaG0zqcpgC7t_5_ibI,5442
|
|
9
|
+
xgae/engine/responser/responser_base.py,sha256=UPXFOCT1HWBlyGeRMRE0yBwQgmd67OwIJYMUPZB7lBg,24782
|
|
10
|
+
xgae/engine/responser/stream_responser.py,sha256=1IpOkGWYKSb-RU_tJlxE_RkzOcH_M41qK_WwyuDubdw,15367
|
|
11
|
+
xgae/tools/without_general_tools_app.py,sha256=FGMV6njcOKwwfitc0j_nUov0RC-eWlhO1IP8_KHz1tQ,3788
|
|
12
|
+
xgae/utils/__init__.py,sha256=ElaGS-zdeZeu6is41u3Ny7lkvhg7BDSK-jMNg9j6K5A,499
|
|
13
|
+
xgae/utils/json_helpers.py,sha256=WD4G5U9Dh8N6J9O0L5wGyqj-NHi09kcXHGdLD_26nlc,3607
|
|
14
|
+
xgae/utils/llm_client.py,sha256=mWRtvtSMk_8NuzFReT9x52ayHlCNVZMZAltD6TQ-xZ8,14404
|
|
15
|
+
xgae/utils/misc.py,sha256=aMWOvJ9VW52q-L9Lkjl1hvXqLwpJAmyxA-Z8jzqFG0U,907
|
|
16
|
+
xgae/utils/setup_env.py,sha256=MqNG0c2QQBDFU1kI8frxr9kB5d08Mmi3QZ1OoorgIa0,2662
|
|
17
|
+
xgae/utils/xml_tool_parser.py,sha256=wWtmir64xPq39oGF_fe9JRJuhFocSJ0xyG-gMuflKOc,4805
|
|
18
|
+
xgae-0.1.15.dist-info/METADATA,sha256=KiVg_6tDTUNlymMa-ZgyqE6XjADFm8MbwOU_cLCepxE,310
|
|
19
|
+
xgae-0.1.15.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
20
|
+
xgae-0.1.15.dist-info/entry_points.txt,sha256=SWN01JNAncV0oApEvFzpH0wsXfnFlB1adCH4IrAJxGc,163
|
|
21
|
+
xgae-0.1.15.dist-info/RECORD,,
|
xgae-0.1.13.dist-info/RECORD
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
xgae/__init__.py,sha256=OEUd9y9AoGBd3xYerdTTpz9xl4NWkmXeq1a2eil7Qro,72
|
|
2
|
-
xgae/cli_app.py,sha256=vKuCIJw0gwXdtkT-QNCZKt2dE53thvTFwQr7nTgvaPY,3000
|
|
3
|
-
xgae/engine/engine_base.py,sha256=-QZqLRbQdwRUfbY4l3i7dFfMB-BL267a-wGZR9bMPLc,1662
|
|
4
|
-
xgae/engine/mcp_tool_box.py,sha256=eE4qGxTHaSMMNDDWWafNXFT-vj_YYof4AjVSsxKoq68,10413
|
|
5
|
-
xgae/engine/prompt_builder.py,sha256=X9bS7YIms6LYplCpNHeUmi74xFP5MwFXmXNqOt1Xz-Q,4356
|
|
6
|
-
xgae/engine/task_engine.py,sha256=ZWxi292fceWvZuv501lwUGgb_PStktmfLFDzhDlsFfY,21011
|
|
7
|
-
xgae/engine/task_langfuse.py,sha256=b0aJ_Di-WDcYzi0TFCvcKWxkBz7PYP2jx3N52OptQMs,2349
|
|
8
|
-
xgae/engine/responser/non_stream_responser.py,sha256=RS2fIP_XCWjZEVtFRSNDJ9wM1N66MuzA66wXm3Nz1Jg,5583
|
|
9
|
-
xgae/engine/responser/responser_base.py,sha256=WsUMUfEE2cexAg5LzXA1yUECOkbs1ekh8HbJS5-R7f8,30813
|
|
10
|
-
xgae/engine/responser/stream_responser.py,sha256=O6_wSwdbqjYO-XowiLvHZKuw-F6fvxyjWULhfkkF6ow,7830
|
|
11
|
-
xgae/tools/without_general_tools_app.py,sha256=FGMV6njcOKwwfitc0j_nUov0RC-eWlhO1IP8_KHz1tQ,3788
|
|
12
|
-
xgae/utils/__init__.py,sha256=ElaGS-zdeZeu6is41u3Ny7lkvhg7BDSK-jMNg9j6K5A,499
|
|
13
|
-
xgae/utils/json_helpers.py,sha256=ubp-dOCeROnZv7JHARRdmDIO5Npdwzrt8AWo3SMv0kI,4705
|
|
14
|
-
xgae/utils/llm_client.py,sha256=6e3kzx73QN6z2SYMQQFmrmODj2Rk-GPJYIxBcFZhMQE,14361
|
|
15
|
-
xgae/utils/misc.py,sha256=aMWOvJ9VW52q-L9Lkjl1hvXqLwpJAmyxA-Z8jzqFG0U,907
|
|
16
|
-
xgae/utils/setup_env.py,sha256=MqNG0c2QQBDFU1kI8frxr9kB5d08Mmi3QZ1OoorgIa0,2662
|
|
17
|
-
xgae/utils/xml_tool_parser.py,sha256=I9xAZC_ElwBY19PNUq-WLXe9FSIJMeAv2Xs-VlajI7Y,4782
|
|
18
|
-
xgae-0.1.13.dist-info/METADATA,sha256=8y0v909gMEo6oyUYTrY5ZjRP8ACB8U-BPHZTDd5uq4M,310
|
|
19
|
-
xgae-0.1.13.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
20
|
-
xgae-0.1.13.dist-info/entry_points.txt,sha256=vClvL_WBJyF2x3wJCz5CNJ_BJG-dWUh7h2YbAoskHsc,162
|
|
21
|
-
xgae-0.1.13.dist-info/RECORD,,
|
|
File without changes
|