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.

@@ -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)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: xgae
3
- Version: 0.1.3
3
+ Version: 0.1.4
4
4
  Summary: Extreme General Agent Engine
5
5
  Requires-Python: >=3.13
6
6
  Requires-Dist: colorlog>=6.9.0
@@ -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
@@ -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