xgae 0.1.3__py3-none-any.whl → 0.1.4__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/responser/xga_non_stream_responser.py +213 -0
- xgae/engine/responser/xga_responser_base.py +751 -0
- xgae/engine/responser/xga_stream_responser.py +787 -0
- xgae/engine/xga_base.py +21 -9
- xgae/engine/xga_engine.py +206 -27
- xgae/engine/xga_mcp_tool_box.py +6 -0
- xgae/utils/json_helpers.py +174 -0
- xgae/utils/llm_client.py +17 -6
- xgae/utils/setup_env.py +1 -4
- xgae/utils/xml_tool_parser.py +236 -0
- {xgae-0.1.3.dist-info → xgae-0.1.4.dist-info}/METADATA +1 -1
- xgae-0.1.4.dist-info/RECORD +16 -0
- xgae/engine/responser/xga_responser_utils.py +0 -0
- xgae/engine/responser/xga_stream_reponser.py +0 -0
- xgae-0.1.3.dist-info/RECORD +0 -14
- {xgae-0.1.3.dist-info → xgae-0.1.4.dist-info}/WHEEL +0 -0
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
"""
|
|
2
|
+
XML Tool Call Parser Module
|
|
3
|
+
|
|
4
|
+
This module provides a reliable XML tool call parsing system that supports
|
|
5
|
+
the XML format with structured function_calls blocks.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import re
|
|
9
|
+
import xml.etree.ElementTree as ET
|
|
10
|
+
from typing import List, Dict, Any, Optional, Tuple
|
|
11
|
+
from dataclasses import dataclass
|
|
12
|
+
import json
|
|
13
|
+
import logging
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass
|
|
19
|
+
class XMLToolCall:
|
|
20
|
+
"""Represents a parsed XML tool call."""
|
|
21
|
+
function_name: str
|
|
22
|
+
parameters: Dict[str, Any]
|
|
23
|
+
raw_xml: str
|
|
24
|
+
parsing_details: Dict[str, Any]
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class XMLToolParser:
|
|
28
|
+
"""
|
|
29
|
+
Parser for XML tool calls format:
|
|
30
|
+
|
|
31
|
+
<function_calls>
|
|
32
|
+
<invoke name="function_name">
|
|
33
|
+
<parameter name="param_name">param_value</parameter>
|
|
34
|
+
...
|
|
35
|
+
</invoke>
|
|
36
|
+
</function_calls>
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
# Regex patterns for extracting XML blocks
|
|
40
|
+
FUNCTION_CALLS_PATTERN = re.compile(
|
|
41
|
+
r'<function_calls>(.*?)</function_calls>',
|
|
42
|
+
re.DOTALL | re.IGNORECASE
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
INVOKE_PATTERN = re.compile(
|
|
46
|
+
r'<invoke\s+name=["\']([^"\']+)["\']>(.*?)</invoke>',
|
|
47
|
+
re.DOTALL | re.IGNORECASE
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
PARAMETER_PATTERN = re.compile(
|
|
51
|
+
r'<parameter\s+name=["\']([^"\']+)["\']>(.*?)</parameter>',
|
|
52
|
+
re.DOTALL | re.IGNORECASE
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
def __init__(self):
|
|
56
|
+
"""Initialize the XML tool parser."""
|
|
57
|
+
pass
|
|
58
|
+
|
|
59
|
+
def parse_content(self, content: str) -> List[XMLToolCall]:
|
|
60
|
+
"""
|
|
61
|
+
Parse XML tool calls from content.
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
content: The text content potentially containing XML tool calls
|
|
65
|
+
|
|
66
|
+
Returns:
|
|
67
|
+
List of parsed XMLToolCall objects
|
|
68
|
+
"""
|
|
69
|
+
tool_calls = []
|
|
70
|
+
|
|
71
|
+
# Find function_calls blocks
|
|
72
|
+
function_calls_matches = self.FUNCTION_CALLS_PATTERN.findall(content)
|
|
73
|
+
|
|
74
|
+
for fc_content in function_calls_matches:
|
|
75
|
+
# Find all invoke blocks within this function_calls block
|
|
76
|
+
invoke_matches = self.INVOKE_PATTERN.findall(fc_content)
|
|
77
|
+
|
|
78
|
+
for function_name, invoke_content in invoke_matches:
|
|
79
|
+
try:
|
|
80
|
+
tool_call = self._parse_invoke_block(
|
|
81
|
+
function_name,
|
|
82
|
+
invoke_content,
|
|
83
|
+
fc_content
|
|
84
|
+
)
|
|
85
|
+
if tool_call:
|
|
86
|
+
tool_calls.append(tool_call)
|
|
87
|
+
except Exception as e:
|
|
88
|
+
logger.error(f"Error parsing invoke block for {function_name}: {e}")
|
|
89
|
+
|
|
90
|
+
return tool_calls
|
|
91
|
+
|
|
92
|
+
def _parse_invoke_block(
|
|
93
|
+
self,
|
|
94
|
+
function_name: str,
|
|
95
|
+
invoke_content: str,
|
|
96
|
+
full_block: str
|
|
97
|
+
) -> Optional[XMLToolCall]:
|
|
98
|
+
"""Parse a single invoke block into an XMLToolCall."""
|
|
99
|
+
parameters = {}
|
|
100
|
+
parsing_details = {
|
|
101
|
+
"function_name": function_name,
|
|
102
|
+
"raw_parameters": {}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
# Extract all parameters
|
|
106
|
+
param_matches = self.PARAMETER_PATTERN.findall(invoke_content)
|
|
107
|
+
|
|
108
|
+
for param_name, param_value in param_matches:
|
|
109
|
+
# Clean up the parameter value
|
|
110
|
+
param_value = param_value.strip()
|
|
111
|
+
|
|
112
|
+
# Try to parse as JSON if it looks like JSON
|
|
113
|
+
parsed_value = self._parse_parameter_value(param_value)
|
|
114
|
+
|
|
115
|
+
parameters[param_name] = parsed_value
|
|
116
|
+
parsing_details["raw_parameters"][param_name] = param_value
|
|
117
|
+
|
|
118
|
+
# Extract the raw XML for this specific invoke
|
|
119
|
+
invoke_pattern = re.compile(
|
|
120
|
+
rf'<invoke\s+name=["\']{re.escape(function_name)}["\']>.*?</invoke>',
|
|
121
|
+
re.DOTALL | re.IGNORECASE
|
|
122
|
+
)
|
|
123
|
+
raw_xml_match = invoke_pattern.search(full_block)
|
|
124
|
+
raw_xml = raw_xml_match.group(0) if raw_xml_match else f"<invoke name=\"{function_name}\">...</invoke>"
|
|
125
|
+
|
|
126
|
+
return XMLToolCall(
|
|
127
|
+
function_name=function_name,
|
|
128
|
+
parameters=parameters,
|
|
129
|
+
raw_xml=raw_xml,
|
|
130
|
+
parsing_details=parsing_details
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
def _parse_parameter_value(self, value: str) -> Any:
|
|
134
|
+
"""
|
|
135
|
+
Parse a parameter value, attempting to convert to appropriate type.
|
|
136
|
+
|
|
137
|
+
Args:
|
|
138
|
+
value: The string value to parse
|
|
139
|
+
|
|
140
|
+
Returns:
|
|
141
|
+
Parsed value (could be dict, list, bool, int, float, or str)
|
|
142
|
+
"""
|
|
143
|
+
value = value.strip()
|
|
144
|
+
|
|
145
|
+
# Try to parse as JSON first
|
|
146
|
+
if value.startswith(('{', '[')):
|
|
147
|
+
try:
|
|
148
|
+
return json.loads(value)
|
|
149
|
+
except json.JSONDecodeError:
|
|
150
|
+
pass
|
|
151
|
+
|
|
152
|
+
# Try to parse as boolean
|
|
153
|
+
if value.lower() in ('true', 'false'):
|
|
154
|
+
return value.lower() == 'true'
|
|
155
|
+
|
|
156
|
+
# Try to parse as number
|
|
157
|
+
try:
|
|
158
|
+
if '.' in value:
|
|
159
|
+
return float(value)
|
|
160
|
+
else:
|
|
161
|
+
return int(value)
|
|
162
|
+
except ValueError:
|
|
163
|
+
pass
|
|
164
|
+
|
|
165
|
+
# Return as string
|
|
166
|
+
return value
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def format_tool_call(self, function_name: str, parameters: Dict[str, Any]) -> str:
|
|
170
|
+
"""
|
|
171
|
+
Format a tool call in the XML format.
|
|
172
|
+
|
|
173
|
+
Args:
|
|
174
|
+
function_name: Name of the function to call
|
|
175
|
+
parameters: Dictionary of parameters
|
|
176
|
+
|
|
177
|
+
Returns:
|
|
178
|
+
Formatted XML string
|
|
179
|
+
"""
|
|
180
|
+
lines = ['<function_calls>', '<invoke name="{}">'.format(function_name)]
|
|
181
|
+
|
|
182
|
+
for param_name, param_value in parameters.items():
|
|
183
|
+
# Convert value to string representation
|
|
184
|
+
if isinstance(param_value, (dict, list)):
|
|
185
|
+
value_str = json.dumps(param_value)
|
|
186
|
+
elif isinstance(param_value, bool):
|
|
187
|
+
value_str = str(param_value).lower()
|
|
188
|
+
else:
|
|
189
|
+
value_str = str(param_value)
|
|
190
|
+
|
|
191
|
+
lines.append('<parameter name="{}">{}</parameter>'.format(
|
|
192
|
+
param_name, value_str
|
|
193
|
+
))
|
|
194
|
+
|
|
195
|
+
lines.extend(['</invoke>', '</function_calls>'])
|
|
196
|
+
return '\n'.join(lines)
|
|
197
|
+
|
|
198
|
+
def validate_tool_call(self, tool_call: XMLToolCall, expected_params: Optional[Dict[str, type]] = None) -> Tuple[bool, Optional[str]]:
|
|
199
|
+
"""
|
|
200
|
+
Validate a tool call against expected parameters.
|
|
201
|
+
|
|
202
|
+
Args:
|
|
203
|
+
tool_call: The XMLToolCall to validate
|
|
204
|
+
expected_params: Optional dict of parameter names to expected types
|
|
205
|
+
|
|
206
|
+
Returns:
|
|
207
|
+
Tuple of (is_valid, error_message)
|
|
208
|
+
"""
|
|
209
|
+
if not tool_call.function_name:
|
|
210
|
+
return False, "Function name is required"
|
|
211
|
+
|
|
212
|
+
if expected_params:
|
|
213
|
+
for param_name, expected_type in expected_params.items():
|
|
214
|
+
if param_name not in tool_call.parameters:
|
|
215
|
+
return False, f"Missing required parameter: {param_name}"
|
|
216
|
+
|
|
217
|
+
param_value = tool_call.parameters[param_name]
|
|
218
|
+
if not isinstance(param_value, expected_type):
|
|
219
|
+
return False, f"Parameter {param_name} should be of type {expected_type.__name__}"
|
|
220
|
+
|
|
221
|
+
return True, None
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
# Convenience function for quick parsing
|
|
225
|
+
def parse_xml_tool_calls(content: str) -> List[XMLToolCall]:
|
|
226
|
+
"""
|
|
227
|
+
Parse XML tool calls from content.
|
|
228
|
+
|
|
229
|
+
Args:
|
|
230
|
+
content: The text content potentially containing XML tool calls
|
|
231
|
+
|
|
232
|
+
Returns:
|
|
233
|
+
List of parsed XMLToolCall objects
|
|
234
|
+
"""
|
|
235
|
+
parser = XMLToolParser()
|
|
236
|
+
return parser.parse_content(content)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
xgae/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
xgae/engine/xga_base.py,sha256=yM8YdThOVHo_TFrLUiu33hZzU2H9knQLs3JfD6hOx24,1669
|
|
3
|
+
xgae/engine/xga_engine.py,sha256=7XRRC6KKz3a2EvH7P9k8szbCCm2O6MxIL674GzUkwHI,12940
|
|
4
|
+
xgae/engine/xga_mcp_tool_box.py,sha256=dvToorrw8FJq4NrzUz494czI6QhT3bvmJQlt5oFgVBA,9994
|
|
5
|
+
xgae/engine/xga_prompt_builder.py,sha256=RuTvQCNufqxDwVvSOPXR0qxAc42cG7NuIaUy9amu66A,4351
|
|
6
|
+
xgae/engine/responser/xga_non_stream_responser.py,sha256=HlSN025jIsl-JY_n6fEdqJQkqc1UqCrgFr6K23uXF3E,12704
|
|
7
|
+
xgae/engine/responser/xga_responser_base.py,sha256=IBJPQELMxZSpnz8YlSCgvPNSHEEUp8_vglotVnHoSeY,36808
|
|
8
|
+
xgae/engine/responser/xga_stream_responser.py,sha256=FESVliTzHFy8BkTudi_Ftcty6QFpJWx7kYRubSuLqsg,50370
|
|
9
|
+
xgae/utils/json_helpers.py,sha256=K1ja6GJCatrAheW9bEWAYSQbDI42__boBCZgtsv1gtk,4865
|
|
10
|
+
xgae/utils/llm_client.py,sha256=mgzn8heUyRm92HTLEYGdfsGEpFtD-xLFr39P98_JP0s,12402
|
|
11
|
+
xgae/utils/setup_env.py,sha256=-Ehv7_E9udHc8AjP66Y78E4X7_G6gpuNJkioCh5fn4A,2902
|
|
12
|
+
xgae/utils/utils.py,sha256=cCYmWjKFksZ8BRD1YYnaM_jTLVHAg1ibEdjsczEUO6k,1134
|
|
13
|
+
xgae/utils/xml_tool_parser.py,sha256=7Ei7X8zSgVct0fFCSmxDtknCLtdrUIwL9hy_0qSNlvs,7546
|
|
14
|
+
xgae-0.1.4.dist-info/METADATA,sha256=R_nN1j5mESZfEQIiLFBQufsOc_Hbd6190GBLgo7-k-o,309
|
|
15
|
+
xgae-0.1.4.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
16
|
+
xgae-0.1.4.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
xgae-0.1.3.dist-info/RECORD
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
xgae/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
-
xgae/engine/xga_base.py,sha256=b9FnBbCuO4lCAIBBhnedtRk49Dl3M6cBn5h_Wkzw4o0,1345
|
|
3
|
-
xgae/engine/xga_engine.py,sha256=kci7pvF8NwAYJ3Y6DS0JKLkPEQERZwcwRzjcAFQx_ec,4169
|
|
4
|
-
xgae/engine/xga_mcp_tool_box.py,sha256=coyQy7tj2YTBpOc0JKmVBJO8ItO4Z6s1pdbucHd85qg,9762
|
|
5
|
-
xgae/engine/xga_prompt_builder.py,sha256=RuTvQCNufqxDwVvSOPXR0qxAc42cG7NuIaUy9amu66A,4351
|
|
6
|
-
xgae/engine/responser/xga_non_stream_responser.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
|
-
xgae/engine/responser/xga_responser_utils.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
|
-
xgae/engine/responser/xga_stream_reponser.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
|
-
xgae/utils/llm_client.py,sha256=UMMK84psLsx36-Nn6Q8X1hl9wd-OdaS9ZhxbRjwNCr0,12149
|
|
10
|
-
xgae/utils/setup_env.py,sha256=vY2wnq5KMXQ2dx2wAPeTpudp7b-fvgzenkwHpgd8vuI,2951
|
|
11
|
-
xgae/utils/utils.py,sha256=cCYmWjKFksZ8BRD1YYnaM_jTLVHAg1ibEdjsczEUO6k,1134
|
|
12
|
-
xgae-0.1.3.dist-info/METADATA,sha256=t4sr4k9pbVAcicIRjoqt327eWtSC0uAeo2pDB5LPjs0,309
|
|
13
|
-
xgae-0.1.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
14
|
-
xgae-0.1.3.dist-info/RECORD,,
|
|
File without changes
|