xgae 0.1.8__py3-none-any.whl → 0.1.9__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/engine_base.py +1 -1
- xgae/engine/mcp_tool_box.py +6 -1
- xgae/engine/prompt_builder.py +1 -1
- xgae/engine/responser/non_stream_responser.py +4 -3
- xgae/engine/responser/responser_base.py +9 -9
- xgae/engine/task_engine.py +52 -79
- xgae/engine/task_langfuse.py +63 -0
- xgae/tools/without_general_tools_app.py +1 -1
- xgae/utils/__init__.py +6 -0
- xgae/utils/json_helpers.py +7 -13
- xgae/utils/llm_client.py +19 -8
- xgae/utils/misc.py +3 -1
- xgae/utils/setup_env.py +30 -27
- xgae/utils/xml_tool_parser.py +4 -80
- {xgae-0.1.8.dist-info → xgae-0.1.9.dist-info}/METADATA +1 -1
- xgae-0.1.9.dist-info/RECORD +20 -0
- {xgae-0.1.8.dist-info → xgae-0.1.9.dist-info}/entry_points.txt +1 -0
- xgae-0.1.8.dist-info/RECORD +0 -19
- {xgae-0.1.8.dist-info → xgae-0.1.9.dist-info}/WHEEL +0 -0
xgae/engine/engine_base.py
CHANGED
|
@@ -7,7 +7,7 @@ class XGAError(Exception):
|
|
|
7
7
|
pass
|
|
8
8
|
|
|
9
9
|
XGAMsgStatusType = Literal["error", "finish", "tool_error", "tool_started", "tool_completed", "tool_failed", "thread_run_start", "thread_run_end", "assistant_response_start", "assistant_response_end"]
|
|
10
|
-
XGAResponseMsgType = Literal["user", "status", "tool", "assistant"]
|
|
10
|
+
XGAResponseMsgType = Literal["user", "status", "tool", "assistant", "assistant_complete"]
|
|
11
11
|
|
|
12
12
|
class XGAResponseMessage(TypedDict, total=False):
|
|
13
13
|
message_id: str
|
xgae/engine/mcp_tool_box.py
CHANGED
|
@@ -190,11 +190,16 @@ class XGAMcpToolBox(XGAToolBox):
|
|
|
190
190
|
if __name__ == "__main__":
|
|
191
191
|
import asyncio
|
|
192
192
|
from dataclasses import asdict
|
|
193
|
+
from xgae.utils.setup_env import setup_logging
|
|
194
|
+
|
|
195
|
+
setup_logging()
|
|
193
196
|
|
|
194
197
|
async def main():
|
|
195
|
-
|
|
198
|
+
## Before Run Exec: uv run custom_fault_tools
|
|
196
199
|
mcp_tool_box = XGAMcpToolBox(custom_mcp_server_file="mcpservers/custom_servers.json")
|
|
197
200
|
#mcp_tool_box = XGAMcpToolBox()
|
|
201
|
+
|
|
202
|
+
task_id = "task1"
|
|
198
203
|
await mcp_tool_box.load_mcp_tools_schema()
|
|
199
204
|
await mcp_tool_box.creat_task_tool_box(task_id=task_id, general_tools=["*"], custom_tools=["bomc_fault.*"])
|
|
200
205
|
tool_schemas = mcp_tool_box.get_task_tool_schemas(task_id, "general_tool")
|
xgae/engine/prompt_builder.py
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
|
|
3
3
|
from typing import List, Dict, Any, AsyncGenerator, override,Optional
|
|
4
|
+
from xgae.utils.json_helpers import format_for_yield
|
|
4
5
|
|
|
5
6
|
from xgae.engine.responser.responser_base import TaskResponseProcessor, TaskResponserContext, TaskRunContinuousState
|
|
6
|
-
|
|
7
|
+
|
|
7
8
|
|
|
8
9
|
class NonStreamTaskResponser(TaskResponseProcessor):
|
|
9
10
|
def __init__(self, response_context: TaskResponserContext):
|
|
@@ -47,8 +48,8 @@ class NonStreamTaskResponser(TaskResponseProcessor):
|
|
|
47
48
|
else:
|
|
48
49
|
logging.warning(f"NonStreamTask:LLM response_message is empty")
|
|
49
50
|
|
|
50
|
-
message_data = {"role": "assistant", "content": llm_content
|
|
51
|
-
assistant_msg = self.add_response_message(type="
|
|
51
|
+
message_data = {"role": "assistant", "content": llm_content} # index=-1, full llm_content
|
|
52
|
+
assistant_msg = self.add_response_message(type="assistant_complete", content=message_data, is_llm_message=True)
|
|
52
53
|
yield assistant_msg
|
|
53
54
|
|
|
54
55
|
tool_calls_to_execute = [item['tool_call'] for item in parsed_xml_data]
|
|
@@ -6,11 +6,12 @@ from abc import ABC, abstractmethod
|
|
|
6
6
|
from dataclasses import dataclass
|
|
7
7
|
from typing import List, Dict, Any, Optional, Tuple, Union, Literal, Callable, TypedDict, AsyncGenerator
|
|
8
8
|
|
|
9
|
+
from xgae.utils.json_helpers import safe_json_parse
|
|
10
|
+
from xgae.utils.xml_tool_parser import XMLToolParser
|
|
11
|
+
|
|
9
12
|
from xgae.engine.engine_base import XGAToolResult, XGAToolBox
|
|
10
|
-
from xgae.
|
|
13
|
+
from xgae.engine.task_langfuse import XGATaskLangFuse
|
|
11
14
|
|
|
12
|
-
from xgae.utils.json_helpers import safe_json_parse, format_for_yield
|
|
13
|
-
from xgae.utils.xml_tool_parser import XMLToolParser
|
|
14
15
|
|
|
15
16
|
# Type alias for XML result adding strategy
|
|
16
17
|
XmlAddingStrategy = Literal["user_message", "assistant_message", "inline_edit"]
|
|
@@ -23,14 +24,13 @@ class TaskResponserContext(TypedDict, total=False):
|
|
|
23
24
|
task_id: str
|
|
24
25
|
task_run_id: str
|
|
25
26
|
task_no: int
|
|
26
|
-
trace_id: str
|
|
27
|
-
root_span_id: str
|
|
28
27
|
model_name: str
|
|
29
28
|
max_xml_tool_calls: int # LLM generate max_xml_tool limit, 0 is no limit
|
|
30
|
-
add_response_msg_func: Callable
|
|
31
|
-
tool_box: XGAToolBox
|
|
32
29
|
tool_execution_strategy: ToolExecutionStrategy
|
|
33
30
|
xml_adding_strategy: XmlAddingStrategy
|
|
31
|
+
add_response_msg_func: Callable
|
|
32
|
+
tool_box: XGAToolBox
|
|
33
|
+
task_langfuse: XGATaskLangFuse
|
|
34
34
|
|
|
35
35
|
|
|
36
36
|
class TaskRunContinuousState(TypedDict, total=False):
|
|
@@ -63,14 +63,14 @@ class TaskResponseProcessor(ABC):
|
|
|
63
63
|
self.xml_adding_strategy = self.response_context.get("xml_adding_strategy", "user_message")
|
|
64
64
|
self.max_xml_tool_calls = self.response_context.get("max_xml_tool_calls", 0)
|
|
65
65
|
|
|
66
|
-
|
|
66
|
+
task_langfuse = response_context.get("task_langfuse")
|
|
67
|
+
self.root_span = task_langfuse.root_span
|
|
67
68
|
self.add_response_message = response_context.get("add_response_msg_func")
|
|
68
69
|
|
|
69
70
|
self.tool_box = response_context.get("tool_box")
|
|
70
71
|
self.xml_parser = XMLToolParser()
|
|
71
72
|
|
|
72
73
|
|
|
73
|
-
|
|
74
74
|
@abstractmethod
|
|
75
75
|
async def process_response(self,
|
|
76
76
|
llm_response: AsyncGenerator,
|
xgae/engine/task_engine.py
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
|
|
2
1
|
import logging
|
|
3
2
|
import json
|
|
4
3
|
import os
|
|
@@ -6,18 +5,15 @@ import os
|
|
|
6
5
|
from typing import List, Any, Dict, Optional, AsyncGenerator, Union, Literal
|
|
7
6
|
from uuid import uuid4
|
|
8
7
|
|
|
9
|
-
from xgae.engine.responser.responser_base import TaskResponserContext, TaskResponseProcessor, TaskRunContinuousState
|
|
10
|
-
from xgae.engine.engine_base import XGAResponseMsgType, XGAResponseMessage, XGAToolBox, XGATaskResult
|
|
11
|
-
|
|
12
8
|
from xgae.utils import handle_error
|
|
13
|
-
from xgae.utils.
|
|
14
|
-
|
|
15
|
-
from xgae.utils.llm_client import LLMClient, LLMConfig, LangfuseMetadata
|
|
16
|
-
|
|
9
|
+
from xgae.utils.llm_client import LLMClient, LLMConfig
|
|
17
10
|
from xgae.utils.json_helpers import format_for_yield
|
|
11
|
+
|
|
12
|
+
from xgae.engine.engine_base import XGAResponseMsgType, XGAResponseMessage, XGAToolBox, XGATaskResult
|
|
13
|
+
from xgae.engine.task_langfuse import XGATaskLangFuse
|
|
18
14
|
from xgae.engine.prompt_builder import XGAPromptBuilder
|
|
19
15
|
from xgae.engine.mcp_tool_box import XGAMcpToolBox
|
|
20
|
-
|
|
16
|
+
from xgae.engine.responser.responser_base import TaskResponserContext, TaskResponseProcessor, TaskRunContinuousState
|
|
21
17
|
|
|
22
18
|
class XGATaskEngine:
|
|
23
19
|
def __init__(self,
|
|
@@ -42,6 +38,7 @@ class XGATaskEngine:
|
|
|
42
38
|
|
|
43
39
|
self.prompt_builder = prompt_builder or XGAPromptBuilder(system_prompt)
|
|
44
40
|
self.tool_box: XGAToolBox = tool_box or XGAMcpToolBox()
|
|
41
|
+
self.task_langfuse: XGATaskLangFuse = None
|
|
45
42
|
|
|
46
43
|
self.general_tools:List[str] = general_tools
|
|
47
44
|
self.custom_tools:List[str] = custom_tools
|
|
@@ -53,18 +50,18 @@ class XGATaskEngine:
|
|
|
53
50
|
|
|
54
51
|
self.task_no = -1
|
|
55
52
|
self.task_run_id :str = None
|
|
56
|
-
|
|
57
53
|
self.task_prompt :str = None
|
|
58
|
-
|
|
59
|
-
self.root_span_id :str = None
|
|
60
|
-
self.root_span_name :str = None
|
|
54
|
+
|
|
61
55
|
|
|
62
56
|
async def run_task_with_final_answer(self,
|
|
63
57
|
task_message: Dict[str, Any],
|
|
64
58
|
trace_id: Optional[str] = None) -> XGATaskResult:
|
|
65
59
|
final_result:XGATaskResult = None
|
|
66
60
|
try:
|
|
67
|
-
self.
|
|
61
|
+
await self._init_task()
|
|
62
|
+
|
|
63
|
+
self.task_langfuse.start_root_span("run_task_with_final_answer", task_message, trace_id)
|
|
64
|
+
|
|
68
65
|
chunks = []
|
|
69
66
|
async for chunk in self.run_task(task_message=task_message, trace_id=trace_id):
|
|
70
67
|
chunks.append(chunk)
|
|
@@ -76,7 +73,7 @@ class XGATaskEngine:
|
|
|
76
73
|
|
|
77
74
|
return final_result
|
|
78
75
|
finally:
|
|
79
|
-
self.
|
|
76
|
+
self.task_langfuse.end_root_span("run_task_with_final_answer", final_result)
|
|
80
77
|
|
|
81
78
|
|
|
82
79
|
async def run_task(self,
|
|
@@ -84,7 +81,8 @@ class XGATaskEngine:
|
|
|
84
81
|
trace_id: Optional[str] = None) -> AsyncGenerator[Dict[str, Any], None]:
|
|
85
82
|
try:
|
|
86
83
|
await self._init_task()
|
|
87
|
-
|
|
84
|
+
|
|
85
|
+
self.task_langfuse.start_root_span("run_task", task_message, trace_id)
|
|
88
86
|
|
|
89
87
|
self.add_response_message(type="user", content=task_message, is_llm_message=True)
|
|
90
88
|
|
|
@@ -92,33 +90,36 @@ class XGATaskEngine:
|
|
|
92
90
|
yield chunk
|
|
93
91
|
finally:
|
|
94
92
|
await self.tool_box.destroy_task_tool_box(self.task_id)
|
|
95
|
-
self.
|
|
96
|
-
|
|
93
|
+
self.task_langfuse.end_root_span("run_task")
|
|
94
|
+
self.task_run_id = None
|
|
97
95
|
|
|
98
96
|
async def _init_task(self) -> None:
|
|
99
|
-
|
|
100
|
-
|
|
97
|
+
if self.task_run_id is None:
|
|
98
|
+
self.task_no = self.task_no + 1
|
|
99
|
+
self.task_run_id = f"{self.task_id}[{self.task_no}]"
|
|
100
|
+
|
|
101
|
+
self.task_langfuse =self._create_task_langfuse()
|
|
101
102
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
103
|
+
general_tools = self.general_tools or ["complete", "ask"]
|
|
104
|
+
if "*" not in general_tools:
|
|
105
|
+
if "complete" not in general_tools:
|
|
106
|
+
general_tools.append("complete")
|
|
107
|
+
elif "ask" not in general_tools:
|
|
108
|
+
general_tools.append("ask")
|
|
108
109
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
110
|
+
custom_tools = self.custom_tools or []
|
|
111
|
+
if isinstance(self.tool_box, XGAMcpToolBox):
|
|
112
|
+
await self.tool_box.load_mcp_tools_schema()
|
|
112
113
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
114
|
+
await self.tool_box.creat_task_tool_box(self.task_id, general_tools, custom_tools)
|
|
115
|
+
general_tool_schemas = self.tool_box.get_task_tool_schemas(self.task_id, "general_tool")
|
|
116
|
+
custom_tool_schemas = self.tool_box.get_task_tool_schemas(self.task_id, "custom_tool")
|
|
116
117
|
|
|
117
|
-
|
|
118
|
+
self.task_prompt = self.prompt_builder.build_task_prompt(self.model_name, general_tool_schemas, custom_tool_schemas)
|
|
118
119
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
120
|
+
logging.info("*" * 30 + f" XGATaskEngine Task'{self.task_id}' Initialized " + "*" * 30)
|
|
121
|
+
logging.info(f"model_name={self.model_name}, is_stream={self.is_stream}")
|
|
122
|
+
logging.info(f"general_tools={general_tools}, custom_tools={custom_tools}")
|
|
122
123
|
|
|
123
124
|
|
|
124
125
|
async def _run_task_auto(self) -> AsyncGenerator[Dict[str, Any], None]:
|
|
@@ -191,7 +192,8 @@ class XGATaskEngine:
|
|
|
191
192
|
llm_messages.append(temp_assistant_message)
|
|
192
193
|
|
|
193
194
|
llm_count = continuous_state.get("auto_continue_count")
|
|
194
|
-
langfuse_metadata = self.
|
|
195
|
+
langfuse_metadata = self.task_langfuse.create_llm_langfuse_meta(llm_count)
|
|
196
|
+
|
|
195
197
|
llm_response = await self.llm_client.create_completion(llm_messages, langfuse_metadata)
|
|
196
198
|
response_processor = self._create_response_processer()
|
|
197
199
|
|
|
@@ -236,7 +238,7 @@ class XGATaskEngine:
|
|
|
236
238
|
result_type = "answer" if success else "error"
|
|
237
239
|
result_content = f"Task execute '{tool_name}' {result_type}: {output}"
|
|
238
240
|
final_result = XGATaskResult(type=result_type, content=result_content)
|
|
239
|
-
elif chunk_type == "
|
|
241
|
+
elif chunk_type == "assistant_complete" and finish_reason == 'stop':
|
|
240
242
|
assis_content = chunk.get('content', {})
|
|
241
243
|
result_content = assis_content.get("content", "LLM output is empty")
|
|
242
244
|
final_result = XGATaskResult(type="answer", content=result_content)
|
|
@@ -258,7 +260,7 @@ class XGATaskEngine:
|
|
|
258
260
|
metadata = metadata or {}
|
|
259
261
|
metadata["task_id"] = self.task_id
|
|
260
262
|
metadata["task_run_id"] = self.task_run_id
|
|
261
|
-
metadata["trace_id"] = self.trace_id
|
|
263
|
+
metadata["trace_id"] = self.task_langfuse.trace_id
|
|
262
264
|
metadata["session_id"] = self.session_id
|
|
263
265
|
metadata["agent_id"] = self.agent_id
|
|
264
266
|
|
|
@@ -295,41 +297,6 @@ class XGATaskEngine:
|
|
|
295
297
|
|
|
296
298
|
return response_llm_contents
|
|
297
299
|
|
|
298
|
-
|
|
299
|
-
def _create_llm_langfuse_meta(self, llm_count:int)-> LangfuseMetadata:
|
|
300
|
-
generation_name = f"xga_task_engine_llm_completion[{self.task_no}]({llm_count})"
|
|
301
|
-
generation_id = f"{self.task_run_id}({llm_count})"
|
|
302
|
-
return LangfuseMetadata(
|
|
303
|
-
generation_name=generation_name,
|
|
304
|
-
generation_id=generation_id,
|
|
305
|
-
existing_trace_id=self.trace_id,
|
|
306
|
-
session_id=self.session_id,
|
|
307
|
-
)
|
|
308
|
-
|
|
309
|
-
def _init_langfuse(self,
|
|
310
|
-
root_span_name: str,
|
|
311
|
-
task_message: Dict[str, Any],
|
|
312
|
-
trace_id: Optional[str] = None):
|
|
313
|
-
|
|
314
|
-
if self.root_span_id is None:
|
|
315
|
-
trace = None
|
|
316
|
-
if trace_id:
|
|
317
|
-
self.trace_id = trace_id
|
|
318
|
-
trace = langfuse.trace(id=trace_id)
|
|
319
|
-
else:
|
|
320
|
-
trace = langfuse.trace(name="xga_task_engine")
|
|
321
|
-
self.trace_id = trace.id
|
|
322
|
-
|
|
323
|
-
span = trace.span(name=root_span_name, input=task_message,metadata={"task_id": self.task_id})
|
|
324
|
-
self.root_span_id = span.id
|
|
325
|
-
self.root_span_name = root_span_name
|
|
326
|
-
|
|
327
|
-
def _end_langfuse(self, root_span_name:str, output: Optional[XGATaskResult]=None):
|
|
328
|
-
if self.root_span_id and self.root_span_name == root_span_name:
|
|
329
|
-
langfuse.span(trace_id=self.trace_id, id=self.root_span_id).end(output=output)
|
|
330
|
-
self.root_span_id = None
|
|
331
|
-
self.root_span_name = None
|
|
332
|
-
|
|
333
300
|
def _create_response_processer(self) -> TaskResponseProcessor:
|
|
334
301
|
response_context = self._create_response_context()
|
|
335
302
|
is_stream = response_context.get("is_stream", False)
|
|
@@ -346,18 +313,21 @@ class XGATaskEngine:
|
|
|
346
313
|
"task_id": self.task_id,
|
|
347
314
|
"task_run_id": self.task_run_id,
|
|
348
315
|
"task_no": self.task_no,
|
|
349
|
-
"trace_id": self.trace_id,
|
|
350
|
-
"root_span_id": self.root_span_id,
|
|
351
316
|
"model_name": self.model_name,
|
|
352
317
|
"max_xml_tool_calls": 0,
|
|
318
|
+
"tool_execution_strategy": "parallel" if self.tool_exec_parallel else "sequential", # ,
|
|
319
|
+
"xml_adding_strategy": "user_message",
|
|
353
320
|
"add_response_msg_func": self.add_response_message,
|
|
354
321
|
"tool_box": self.tool_box,
|
|
355
|
-
"
|
|
356
|
-
"xml_adding_strategy": "user_message",
|
|
322
|
+
"task_langfuse": self.task_langfuse,
|
|
357
323
|
}
|
|
358
324
|
return response_context
|
|
359
325
|
|
|
360
326
|
|
|
327
|
+
def _create_task_langfuse(self)-> XGATaskLangFuse:
|
|
328
|
+
return XGATaskLangFuse(self.session_id, self.task_id, self.task_run_id, self.task_no, self.agent_id)
|
|
329
|
+
|
|
330
|
+
|
|
361
331
|
def _logging_reponse_chunk(self, chunk):
|
|
362
332
|
chunk_type = chunk.get('type')
|
|
363
333
|
prefix = ""
|
|
@@ -380,6 +350,7 @@ if __name__ == "__main__":
|
|
|
380
350
|
from xgae.utils.misc import read_file
|
|
381
351
|
|
|
382
352
|
async def main():
|
|
353
|
+
# Before Run Exec: uv run custom_fault_tools
|
|
383
354
|
tool_box = XGAMcpToolBox(custom_mcp_server_file="mcpservers/custom_servers.json")
|
|
384
355
|
system_prompt = read_file("templates/example_user_prompt.txt")
|
|
385
356
|
engine = XGATaskEngine(tool_box=tool_box,
|
|
@@ -387,7 +358,9 @@ if __name__ == "__main__":
|
|
|
387
358
|
custom_tools=["*"],
|
|
388
359
|
llm_config=LLMConfig(stream=False),
|
|
389
360
|
system_prompt=system_prompt,
|
|
390
|
-
max_auto_run=8
|
|
361
|
+
max_auto_run=8,
|
|
362
|
+
session_id="session_1",
|
|
363
|
+
agent_id="agent_1",)
|
|
391
364
|
|
|
392
365
|
final_result = await engine.run_task_with_final_answer(task_message={"role": "user",
|
|
393
366
|
"content": "locate 10.0.0.1 fault and solution"})
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
|
|
2
|
+
from typing import Any, Dict, Optional
|
|
3
|
+
from langfuse import Langfuse
|
|
4
|
+
|
|
5
|
+
from xgae.utils.setup_env import setup_langfuse, setup_env_logging
|
|
6
|
+
from xgae.utils.llm_client import LangfuseMetadata
|
|
7
|
+
from xgae.engine.engine_base import XGATaskResult
|
|
8
|
+
|
|
9
|
+
setup_env_logging()
|
|
10
|
+
langfuse:Langfuse = setup_langfuse()
|
|
11
|
+
|
|
12
|
+
class XGATaskLangFuse:
|
|
13
|
+
def __init__(self,
|
|
14
|
+
session_id: str,
|
|
15
|
+
task_id:str,
|
|
16
|
+
task_run_id: str,
|
|
17
|
+
task_no: int,
|
|
18
|
+
agent_id: str) -> None:
|
|
19
|
+
self.session_id = session_id
|
|
20
|
+
self.task_id = task_id
|
|
21
|
+
self.task_run_id = task_run_id
|
|
22
|
+
self.task_no = task_no
|
|
23
|
+
self.agent_id = agent_id
|
|
24
|
+
|
|
25
|
+
self.trace_id = None
|
|
26
|
+
self.root_span = None
|
|
27
|
+
self.root_span_name = None
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def start_root_span(self,
|
|
31
|
+
root_span_name: str,
|
|
32
|
+
task_message: Dict[str, Any],
|
|
33
|
+
trace_id: Optional[str] = None):
|
|
34
|
+
if self.root_span is None:
|
|
35
|
+
trace = None
|
|
36
|
+
if trace_id:
|
|
37
|
+
self.trace_id = trace_id
|
|
38
|
+
trace = langfuse.trace(id=trace_id)
|
|
39
|
+
else:
|
|
40
|
+
trace = langfuse.trace(name="xga_task_engine")
|
|
41
|
+
self.trace_id = trace.id
|
|
42
|
+
|
|
43
|
+
metadata = {"task_id": self.task_id, "session_id": self.session_id, "agent_id": self.agent_id}
|
|
44
|
+
self.root_span = trace.span(id=self.task_run_id, name=root_span_name, input=task_message,metadata=metadata)
|
|
45
|
+
self.root_span_name = root_span_name
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def end_root_span(self, root_span_name:str, output: Optional[XGATaskResult]=None):
|
|
49
|
+
if self.root_span and self.root_span_name == root_span_name:
|
|
50
|
+
self.root_span.end(output=output)
|
|
51
|
+
self.root_span = None
|
|
52
|
+
self.root_span_name = None
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def create_llm_langfuse_meta(self, llm_count:int)-> LangfuseMetadata:
|
|
56
|
+
generation_name = f"xga_task_engine_llm_completion[{self.task_no}]({llm_count})"
|
|
57
|
+
generation_id = f"{self.task_run_id}({llm_count})"
|
|
58
|
+
return LangfuseMetadata(
|
|
59
|
+
generation_name=generation_name,
|
|
60
|
+
generation_id=generation_id,
|
|
61
|
+
existing_trace_id=self.trace_id,
|
|
62
|
+
session_id=self.session_id,
|
|
63
|
+
)
|
|
@@ -41,7 +41,7 @@ async def end_task(task_id: str) :
|
|
|
41
41
|
|
|
42
42
|
|
|
43
43
|
def main():
|
|
44
|
-
|
|
44
|
+
print("="*20 + " XGAE Message Tools Sever Started in Stdio mode " + "="*20)
|
|
45
45
|
mcp.run(transport="stdio")
|
|
46
46
|
|
|
47
47
|
if __name__ == "__main__":
|
xgae/utils/__init__.py
CHANGED
xgae/utils/json_helpers.py
CHANGED
|
@@ -26,8 +26,7 @@ def ensure_dict(value: Union[str, Dict[str, Any], None], default: Dict[str, Any]
|
|
|
26
26
|
Returns:
|
|
27
27
|
A dictionary
|
|
28
28
|
"""
|
|
29
|
-
|
|
30
|
-
default = {}
|
|
29
|
+
default = default or {}
|
|
31
30
|
|
|
32
31
|
if value is None:
|
|
33
32
|
return default
|
|
@@ -64,8 +63,7 @@ def ensure_list(value: Union[str, List[Any], None], default: List[Any] = None) -
|
|
|
64
63
|
Returns:
|
|
65
64
|
A list
|
|
66
65
|
"""
|
|
67
|
-
|
|
68
|
-
default = []
|
|
66
|
+
default = default or []
|
|
69
67
|
|
|
70
68
|
if value is None:
|
|
71
69
|
return default
|
|
@@ -84,7 +82,7 @@ def ensure_list(value: Union[str, List[Any], None], default: List[Any] = None) -
|
|
|
84
82
|
|
|
85
83
|
return default
|
|
86
84
|
|
|
87
|
-
|
|
85
|
+
# @todo if all call value is str, delete useless code
|
|
88
86
|
def safe_json_parse(value: Union[str, Dict, List, Any], default: Any = None) -> Any:
|
|
89
87
|
"""
|
|
90
88
|
Safely parse a value that might be JSON string or already parsed.
|
|
@@ -105,16 +103,13 @@ def safe_json_parse(value: Union[str, Dict, List, Any], default: Any = None) ->
|
|
|
105
103
|
# If it's already a dict or list, return as-is
|
|
106
104
|
if isinstance(value, (dict, list)):
|
|
107
105
|
return value
|
|
108
|
-
|
|
109
|
-
# If it's a string, try to parse it
|
|
106
|
+
|
|
110
107
|
if isinstance(value, str):
|
|
111
108
|
try:
|
|
112
109
|
return json.loads(value)
|
|
113
110
|
except (json.JSONDecodeError, TypeError):
|
|
114
|
-
# If it's not valid JSON, return the string itself
|
|
115
111
|
return value
|
|
116
|
-
|
|
117
|
-
# For any other type, return as-is
|
|
112
|
+
|
|
118
113
|
return value
|
|
119
114
|
|
|
120
115
|
|
|
@@ -137,9 +132,8 @@ def to_json_string(value: Any) -> str:
|
|
|
137
132
|
json.loads(value)
|
|
138
133
|
return value # It's already a JSON string
|
|
139
134
|
except (json.JSONDecodeError, TypeError):
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
135
|
+
pass
|
|
136
|
+
|
|
143
137
|
# For all other types, convert to JSON
|
|
144
138
|
return json.dumps(value)
|
|
145
139
|
|
xgae/utils/llm_client.py
CHANGED
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import json
|
|
3
|
-
import logging
|
|
4
3
|
import os
|
|
4
|
+
import logging
|
|
5
5
|
import litellm
|
|
6
6
|
|
|
7
7
|
from typing import Union, Dict, Any, Optional, List, TypedDict
|
|
8
|
-
|
|
9
|
-
from litellm.utils import ModelResponse, CustomStreamWrapper
|
|
10
8
|
from openai import OpenAIError
|
|
9
|
+
from litellm.utils import ModelResponse, CustomStreamWrapper
|
|
11
10
|
|
|
12
|
-
from xgae.utils.setup_env import
|
|
11
|
+
from xgae.utils.setup_env import setup_langfuse
|
|
13
12
|
|
|
14
13
|
class LLMConfig(TypedDict, total=False):
|
|
15
14
|
model: str # Optional Name of the model to use , Override .env LLM_MODEL
|
|
@@ -85,7 +84,8 @@ class LLMClient:
|
|
|
85
84
|
def _init_langfuse():
|
|
86
85
|
if not LLMClient.langfuse_inited:
|
|
87
86
|
LLMClient.langfuse_inited =True
|
|
88
|
-
|
|
87
|
+
env_langfuse = setup_langfuse()
|
|
88
|
+
if env_langfuse and env_langfuse.enabled:
|
|
89
89
|
litellm.success_callback = ["langfuse"]
|
|
90
90
|
litellm.failure_callback = ["langfuse"]
|
|
91
91
|
LLMClient.langfuse_enabled = True
|
|
@@ -95,7 +95,6 @@ class LLMClient:
|
|
|
95
95
|
logging.warning("*** LiteLLM Langfuse is disable !")
|
|
96
96
|
|
|
97
97
|
|
|
98
|
-
|
|
99
98
|
def _prepare_llm_params(self, llm_config_params: Dict[str, Any]) -> Dict[str, Any]:
|
|
100
99
|
prepared_llm_params = llm_config_params.copy()
|
|
101
100
|
|
|
@@ -240,12 +239,24 @@ class LLMClient:
|
|
|
240
239
|
raise LLMError(f"LLM completion failed after {self.max_retries} attempts !")
|
|
241
240
|
|
|
242
241
|
if __name__ == "__main__":
|
|
242
|
+
from xgae.utils.setup_env import setup_logging
|
|
243
|
+
|
|
244
|
+
setup_logging()
|
|
245
|
+
langfuse = setup_langfuse()
|
|
246
|
+
|
|
243
247
|
async def llm_completion():
|
|
244
248
|
llm_client = LLMClient(LLMConfig(stream=False))
|
|
249
|
+
|
|
245
250
|
messages = [{"role": "user", "content": "1+1="}]
|
|
246
251
|
trace_id = langfuse.trace(name = "xgae_litellm_test").trace_id
|
|
247
|
-
|
|
248
|
-
|
|
252
|
+
meta = LangfuseMetadata(
|
|
253
|
+
generation_name="llm_completion_test",
|
|
254
|
+
generation_id="generation_id",
|
|
255
|
+
existing_trace_id=trace_id,
|
|
256
|
+
session_id="session_0",
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
response = await llm_client.create_completion(messages, meta)
|
|
249
260
|
if llm_client.is_stream:
|
|
250
261
|
async for chunk in response:
|
|
251
262
|
choices = chunk.get("choices", [{}])
|
xgae/utils/misc.py
CHANGED
|
@@ -4,6 +4,8 @@ import sys
|
|
|
4
4
|
|
|
5
5
|
from typing import Any, Dict
|
|
6
6
|
|
|
7
|
+
from xgae.utils import handle_error
|
|
8
|
+
|
|
7
9
|
def read_file(file_path: str) -> str:
|
|
8
10
|
if not os.path.exists(file_path):
|
|
9
11
|
logging.error(f"File '{file_path}' not found")
|
|
@@ -31,4 +33,4 @@ def format_file_with_args(file_content:str, args: Dict[str, Any])-> str:
|
|
|
31
33
|
finally:
|
|
32
34
|
sys.stdout = original_stdout
|
|
33
35
|
|
|
34
|
-
return formated
|
|
36
|
+
return formated
|
xgae/utils/setup_env.py
CHANGED
|
@@ -4,24 +4,14 @@ import os
|
|
|
4
4
|
from dotenv import load_dotenv
|
|
5
5
|
from langfuse import Langfuse
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
from xgae.utils import to_bool
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
log_enable = bool(os.getenv("LOG_ENABLE", True))
|
|
11
|
-
if not log_enable :
|
|
12
|
-
return
|
|
9
|
+
load_dotenv()
|
|
13
10
|
|
|
11
|
+
def setup_logging(log_file: str=None, log_level: str="INFO") :
|
|
14
12
|
import colorlog
|
|
15
13
|
|
|
16
|
-
|
|
17
|
-
log_file = os.getenv("LOG_FILE", "log/xga.log")
|
|
18
|
-
log_level = getattr(logging, env_log_level.upper(), logging.INFO)
|
|
19
|
-
|
|
20
|
-
log_dir = os.path.dirname(log_file)
|
|
21
|
-
if log_dir and not os.path.exists(log_dir):
|
|
22
|
-
os.makedirs(log_dir, exist_ok=True)
|
|
23
|
-
else:
|
|
24
|
-
os.remove(log_file)
|
|
14
|
+
logging_level = getattr(logging, log_level.upper(), logging.INFO)
|
|
25
15
|
|
|
26
16
|
logger = logging.getLogger()
|
|
27
17
|
for handler in logger.handlers[:]:
|
|
@@ -40,25 +30,36 @@ def setup_logging() :
|
|
|
40
30
|
datefmt='%Y-%m-%d %H:%M:%S'
|
|
41
31
|
)
|
|
42
32
|
|
|
43
|
-
file_formatter = logging.Formatter(
|
|
44
|
-
'%(asctime)s -%(levelname)-8s %(message)s',
|
|
45
|
-
datefmt='%Y-%m-%d %H:%M:%S'
|
|
46
|
-
)
|
|
47
|
-
|
|
48
33
|
console_handler = logging.StreamHandler()
|
|
49
34
|
console_handler.setFormatter(console_formatter)
|
|
35
|
+
logger.addHandler(console_handler)
|
|
50
36
|
|
|
51
|
-
|
|
52
|
-
|
|
37
|
+
if log_file:
|
|
38
|
+
log_dir = os.path.dirname(log_file)
|
|
39
|
+
if log_dir and not os.path.exists(log_dir):
|
|
40
|
+
os.makedirs(log_dir, exist_ok=True)
|
|
41
|
+
else:
|
|
42
|
+
os.remove(log_file)
|
|
53
43
|
|
|
54
|
-
|
|
55
|
-
|
|
44
|
+
file_formatter = logging.Formatter(
|
|
45
|
+
'%(asctime)s -%(levelname)-8s %(message)s',
|
|
46
|
+
datefmt='%Y-%m-%d %H:%M:%S'
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
file_handler = logging.FileHandler(log_file, encoding='utf-8')
|
|
50
|
+
file_handler.setFormatter(file_formatter)
|
|
51
|
+
logger.addHandler(file_handler)
|
|
56
52
|
|
|
57
|
-
logger.setLevel(
|
|
53
|
+
logger.setLevel(logging_level)
|
|
58
54
|
|
|
59
|
-
logging.info(f"📡 XGAE_LOGGING is initialized, log_level={
|
|
55
|
+
logging.info(f"📡 XGAE_LOGGING is initialized, log_level={log_level}, log_file={log_file}")
|
|
60
56
|
|
|
61
|
-
|
|
57
|
+
def setup_env_logging():
|
|
58
|
+
log_enable = to_bool(os.getenv("LOG_ENABLE", True))
|
|
59
|
+
log_level = os.getenv("LOG_LEVEL", "INFO")
|
|
60
|
+
log_file = os.getenv("LOG_FILE", "log/xga.log")
|
|
61
|
+
if log_enable :
|
|
62
|
+
setup_logging(log_file, log_level)
|
|
62
63
|
|
|
63
64
|
def setup_langfuse() -> Langfuse:
|
|
64
65
|
env_public_key = os.getenv("LANGFUSE_PUBLIC_KEY")
|
|
@@ -77,4 +78,6 @@ def setup_langfuse() -> Langfuse:
|
|
|
77
78
|
|
|
78
79
|
return _langfuse
|
|
79
80
|
|
|
80
|
-
|
|
81
|
+
if __name__ == "__main__":
|
|
82
|
+
langfuse = setup_langfuse()
|
|
83
|
+
logging.warning(f"langfuse is enable={langfuse.enabled}")
|
xgae/utils/xml_tool_parser.py
CHANGED
|
@@ -68,16 +68,16 @@ class XMLToolParser:
|
|
|
68
68
|
# Find function_calls blocks
|
|
69
69
|
function_calls_matches = self.FUNCTION_CALLS_PATTERN.findall(content)
|
|
70
70
|
|
|
71
|
-
for
|
|
71
|
+
for func_content in function_calls_matches:
|
|
72
72
|
# Find all invoke blocks within this function_calls block
|
|
73
|
-
invoke_matches = self.INVOKE_PATTERN.findall(
|
|
73
|
+
invoke_matches = self.INVOKE_PATTERN.findall(func_content)
|
|
74
74
|
|
|
75
75
|
for function_name, invoke_content in invoke_matches:
|
|
76
76
|
try:
|
|
77
77
|
tool_call = self._parse_invoke_block(
|
|
78
78
|
function_name,
|
|
79
79
|
invoke_content,
|
|
80
|
-
|
|
80
|
+
func_content
|
|
81
81
|
)
|
|
82
82
|
if tool_call:
|
|
83
83
|
tool_calls.append(tool_call)
|
|
@@ -98,17 +98,11 @@ class XMLToolParser:
|
|
|
98
98
|
"function_name": function_name,
|
|
99
99
|
"raw_parameters": {}
|
|
100
100
|
}
|
|
101
|
-
|
|
102
|
-
# Extract all parameters
|
|
101
|
+
|
|
103
102
|
param_matches = self.PARAMETER_PATTERN.findall(invoke_content)
|
|
104
|
-
|
|
105
103
|
for param_name, param_value in param_matches:
|
|
106
|
-
# Clean up the parameter value
|
|
107
104
|
param_value = param_value.strip()
|
|
108
|
-
|
|
109
|
-
# Try to parse as JSON if it looks like JSON
|
|
110
105
|
parsed_value = self._parse_parameter_value(param_value)
|
|
111
|
-
|
|
112
106
|
parameters[param_name] = parsed_value
|
|
113
107
|
parsing_details["raw_parameters"][param_name] = param_value
|
|
114
108
|
|
|
@@ -161,73 +155,3 @@ class XMLToolParser:
|
|
|
161
155
|
|
|
162
156
|
# Return as string
|
|
163
157
|
return value
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
def format_tool_call(self, function_name: str, parameters: Dict[str, Any]) -> str:
|
|
167
|
-
"""
|
|
168
|
-
Format a tool call in the XML format.
|
|
169
|
-
|
|
170
|
-
Args:
|
|
171
|
-
function_name: Name of the function to call
|
|
172
|
-
parameters: Dictionary of parameters
|
|
173
|
-
|
|
174
|
-
Returns:
|
|
175
|
-
Formatted XML string
|
|
176
|
-
"""
|
|
177
|
-
lines = ['<function_calls>', '<invoke name="{}">'.format(function_name)]
|
|
178
|
-
|
|
179
|
-
for param_name, param_value in parameters.items():
|
|
180
|
-
# Convert value to string representation
|
|
181
|
-
if isinstance(param_value, (dict, list)):
|
|
182
|
-
value_str = json.dumps(param_value)
|
|
183
|
-
elif isinstance(param_value, bool):
|
|
184
|
-
value_str = str(param_value).lower()
|
|
185
|
-
else:
|
|
186
|
-
value_str = str(param_value)
|
|
187
|
-
|
|
188
|
-
lines.append('<parameter name="{}">{}</parameter>'.format(
|
|
189
|
-
param_name, value_str
|
|
190
|
-
))
|
|
191
|
-
|
|
192
|
-
lines.extend(['</invoke>', '</function_calls>'])
|
|
193
|
-
return '\n'.join(lines)
|
|
194
|
-
|
|
195
|
-
def validate_tool_call(self, tool_call: XMLToolCall, expected_params: Optional[Dict[str, type]] = None) -> Tuple[bool, Optional[str]]:
|
|
196
|
-
"""
|
|
197
|
-
Validate a tool call against expected parameters.
|
|
198
|
-
|
|
199
|
-
Args:
|
|
200
|
-
tool_call: The XMLToolCall to validate
|
|
201
|
-
expected_params: Optional dict of parameter names to expected types
|
|
202
|
-
|
|
203
|
-
Returns:
|
|
204
|
-
Tuple of (is_valid, error_message)
|
|
205
|
-
"""
|
|
206
|
-
if not tool_call.function_name:
|
|
207
|
-
return False, "Function name is required"
|
|
208
|
-
|
|
209
|
-
if expected_params:
|
|
210
|
-
for param_name, expected_type in expected_params.items():
|
|
211
|
-
if param_name not in tool_call.parameters:
|
|
212
|
-
return False, f"Missing required parameter: {param_name}"
|
|
213
|
-
|
|
214
|
-
param_value = tool_call.parameters[param_name]
|
|
215
|
-
if not isinstance(param_value, expected_type):
|
|
216
|
-
return False, f"Parameter {param_name} should be of type {expected_type.__name__}"
|
|
217
|
-
|
|
218
|
-
return True, None
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
# Convenience function for quick parsing
|
|
222
|
-
def parse_xml_tool_calls(content: str) -> List[XMLToolCall]:
|
|
223
|
-
"""
|
|
224
|
-
Parse XML tool calls from content.
|
|
225
|
-
|
|
226
|
-
Args:
|
|
227
|
-
content: The text content potentially containing XML tool calls
|
|
228
|
-
|
|
229
|
-
Returns:
|
|
230
|
-
List of parsed XMLToolCall objects
|
|
231
|
-
"""
|
|
232
|
-
parser = XMLToolParser()
|
|
233
|
-
return parser.parse_content(content)
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
xgae/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
xgae/engine/engine_base.py,sha256=ioywuTpDMHEmyVcd6BInoU-vR70PhQStE2MVRWoEiJg,1768
|
|
3
|
+
xgae/engine/mcp_tool_box.py,sha256=ZSCBSXRWhISwyZ1uEIbt3esjesM46g-ktv6CxvyPVDU,10030
|
|
4
|
+
xgae/engine/prompt_builder.py,sha256=X9bS7YIms6LYplCpNHeUmi74xFP5MwFXmXNqOt1Xz-Q,4356
|
|
5
|
+
xgae/engine/task_engine.py,sha256=BLy32wJUbBFGkjAPfHcbSpCUPQm2uZAmLHtZVqvXwx0,18026
|
|
6
|
+
xgae/engine/task_langfuse.py,sha256=-WMnlMcBGfDuJpV0ZYKnVzlO0HizGOfFVGyzVkP6Rag,2260
|
|
7
|
+
xgae/engine/responser/non_stream_responser.py,sha256=80ZFcitjXpQJANxbYpQ0LlhVzIxmURNJi9Fbo953Uak,6349
|
|
8
|
+
xgae/engine/responser/responser_base.py,sha256=8PcsvQHP68FEhu6v3dT9hDCc_rLKs38i4txWLcJD4ck,29851
|
|
9
|
+
xgae/engine/responser/stream_responser.py,sha256=oPGtrT1nedGMjiBAwPzUlu6Z_rPWeVSODC1xQ6D8cTY,52055
|
|
10
|
+
xgae/tools/without_general_tools_app.py,sha256=FGMV6njcOKwwfitc0j_nUov0RC-eWlhO1IP8_KHz1tQ,3788
|
|
11
|
+
xgae/utils/__init__.py,sha256=tMSYguPrubusr7IyKZn6iPYE6qbN7sOsEVDeJPRdPa8,351
|
|
12
|
+
xgae/utils/json_helpers.py,sha256=6BkqiyEF3jV3Irb4Z6-wGY2_FNaLlxE1WKlMJHHT6E0,4645
|
|
13
|
+
xgae/utils/llm_client.py,sha256=nOhPlllUj4mASdj2dU5HkSmDqYZ8ZMpDa7AKQwUWJnQ,13600
|
|
14
|
+
xgae/utils/misc.py,sha256=M8lMXYp1pHiY6Ee8ZTUG88GpOAsE5fbYoRO_hcBFUCE,953
|
|
15
|
+
xgae/utils/setup_env.py,sha256=XD6WmGNyMFDKVzNU_ufYtqJNd2Otq4hzLs9YZ2aSz6g,2613
|
|
16
|
+
xgae/utils/xml_tool_parser.py,sha256=I9xAZC_ElwBY19PNUq-WLXe9FSIJMeAv2Xs-VlajI7Y,4782
|
|
17
|
+
xgae-0.1.9.dist-info/METADATA,sha256=q4_B-_Kt2hswi0kGX9B_H3uI_7C7SWiJOo6HnzoMF8g,309
|
|
18
|
+
xgae-0.1.9.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
19
|
+
xgae-0.1.9.dist-info/entry_points.txt,sha256=bYzQ-1iabCduosArMzq1hktJrCjp8jq4_E8Sg8-gEic,137
|
|
20
|
+
xgae-0.1.9.dist-info/RECORD,,
|
xgae-0.1.8.dist-info/RECORD
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
xgae/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
-
xgae/engine/engine_base.py,sha256=MExJxXHkufv_a-n9tm3FZAbJyxVAwEkRC3C_WAGdu4Q,1746
|
|
3
|
-
xgae/engine/mcp_tool_box.py,sha256=6bsORGK2HE-NS8vWNnbbgUQ9uGr_MI7JGO9j_FYKUKA,9903
|
|
4
|
-
xgae/engine/prompt_builder.py,sha256=8_rNRJksn2QLV_K98S0x0qNeHcmxhU0kB_53IZJTGOU,4366
|
|
5
|
-
xgae/engine/task_engine.py,sha256=kOntdzVtTjjakIhcBNK5vBMiBuAXPqB4R5ustY-BNfU,18931
|
|
6
|
-
xgae/engine/responser/non_stream_responser.py,sha256=tXvkGHFGm2oO0IE8Bz9DFDwqemBkO92Nb6MbX1ukHeE,6352
|
|
7
|
-
xgae/engine/responser/responser_base.py,sha256=FdR0yX7VfU5kW-4JmTXBfnn9AwyX1EbOAnslkC7Vcyg,29874
|
|
8
|
-
xgae/engine/responser/stream_responser.py,sha256=oPGtrT1nedGMjiBAwPzUlu6Z_rPWeVSODC1xQ6D8cTY,52055
|
|
9
|
-
xgae/tools/without_general_tools_app.py,sha256=QknIF4OW9xvOad8gx-F_sCBwQYXqMalnNFvYvZXkQ_I,3789
|
|
10
|
-
xgae/utils/__init__.py,sha256=GPubcIs2XFPiWKnuCpevAtYEmVWKJuXlmGkmsH9qoXA,219
|
|
11
|
-
xgae/utils/json_helpers.py,sha256=K1ja6GJCatrAheW9bEWAYSQbDI42__boBCZgtsv1gtk,4865
|
|
12
|
-
xgae/utils/llm_client.py,sha256=Y-o26VW1MOhJYsWJ0zR4u_YXsHSEbvVPY6r90zLQJXU,13213
|
|
13
|
-
xgae/utils/misc.py,sha256=EK94YesZp8AmRUqWfN-CjTxyEHPWdIIWpFNO17dzm9g,915
|
|
14
|
-
xgae/utils/setup_env.py,sha256=P_p74q3nroBdTkAElfGr4QLm4fu7ZRP0R9BMGNUL010,2352
|
|
15
|
-
xgae/utils/xml_tool_parser.py,sha256=EJ6BjpD4CSdmS_LqViUJ6P8H9GY2R1e4Dh8rLCR6nSE,7474
|
|
16
|
-
xgae-0.1.8.dist-info/METADATA,sha256=mk0D1208wUJnbIZxyteCAjE2tlKtQixumHo0mcqD3RI,309
|
|
17
|
-
xgae-0.1.8.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
18
|
-
xgae-0.1.8.dist-info/entry_points.txt,sha256=rhQ9Vksnu8nA78lPTjiJxOCZ5k6sH6s5YNMR68y7C-A,73
|
|
19
|
-
xgae-0.1.8.dist-info/RECORD,,
|
|
File without changes
|