quantalogic 0.33.1__py3-none-any.whl → 0.33.3__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.
@@ -0,0 +1,285 @@
1
+ """
2
+ Module for SequenceTool.
3
+
4
+ The SequenceTool groups multiple tool calls into one action for use within the ReAct framework.
5
+ It allows an LLM to invoke several authorized tools in one step by using XML syntax.
6
+ The XML input must follow one of these formats:
7
+
8
+ Option A (wrapped in a <sequence> element):
9
+ <sequence>
10
+ <tool_name1>
11
+ <param1>value1</param1>
12
+ <param2>value2</param2>
13
+ ...
14
+ </tool_name1>
15
+ <tool_name2>
16
+ <param1>value1</param1>
17
+ </tool_name2>
18
+ ...
19
+ </sequence>
20
+
21
+ Option B (raw tool calls without a wrapping <sequence> element):
22
+ <tool_name1>
23
+ <param1>value1</param1>
24
+ <param2>value2</param2>
25
+ ...
26
+ </tool_name1>
27
+ <tool_name2>
28
+ <param1>value1</param1>
29
+ </tool_name2>
30
+ ...
31
+
32
+ Where:
33
+ - In Option A the root tag is <sequence>.
34
+ - Each tool call (child element of <sequence>) must have a tag that exactly matches one of the authorized tools.
35
+ - In Option B the entire input is treated as the sequence content.
36
+ - The sub-elements inside each tool call represent parameter names and values.
37
+
38
+ Objective:
39
+ Execute each tool call in order—even if some calls fail—and return a comprehensive XML report.
40
+ The report includes the execution order, the provided parameters, execution status (success/failure),
41
+ and either the output or error message for each call.
42
+
43
+ Authorized Tools:
44
+ The list of authorized tool names is derived from the tools provided in the constructor.
45
+ """
46
+
47
+ import xml.etree.ElementTree as ET
48
+ from typing import Any, List
49
+
50
+ from quantalogic.tools.tool import Tool, ToolArgument
51
+ from quantalogic.xml_parser import ToleranceXMLParser
52
+ from quantalogic.xml_tool_parser import ToolParser # XML-based argument parser
53
+
54
+
55
+ class SequenceTool(Tool):
56
+ """
57
+ A tool to execute a sequence of authorized tool calls specified in XML.
58
+
59
+ Description for the LLM:
60
+ Use this tool to chain multiple operations into one action. Provide an XML string that either
61
+ wraps the tool calls in a single <sequence> element or is the raw collection of tool calls.
62
+ The tag for each tool call must be one of the authorized tool names.
63
+ Authorized tools: {authorized_list}
64
+
65
+ For each tool call, include the required parameters as sub-elements. The syntax is as follows:
66
+
67
+ <sequence>
68
+ <tool_name>
69
+ <param1>value1</param1>
70
+ <param2>value2</param2>
71
+ ...
72
+ </tool_name>
73
+ ...
74
+ </sequence>
75
+
76
+ Objective:
77
+ Execute each tool call in order—even if some calls fail—and return an XML report that includes
78
+ the order of execution, provided parameters, execution status (success/failure), and the output or
79
+ error message for each call.
80
+ """
81
+
82
+ def __init__(self, tools: List[Tool], **data: Any):
83
+ authorized_names = sorted(tool.name for tool in tools)
84
+ desc = (
85
+ "Executes a sequence of authorized tool calls defined in XML, then returns an XML report "
86
+ "detailing the outcome of each call. The XML input may either be wrapped in a <sequence> "
87
+ "element or provided as raw tool calls. Example formats:\n\n"
88
+ "Option A:\n"
89
+ "<sequence>\n"
90
+ " <tool_name>\n"
91
+ " <param1>value1</param1>\n"
92
+ " <param2>value2</param2>\n"
93
+ " </tool_name>\n"
94
+ " ...\n"
95
+ "</sequence>\n\n"
96
+ "Option B:\n"
97
+ "<tool_name>\n"
98
+ " <param1>value1</param1>\n"
99
+ " <param2>value2</param2>\n"
100
+ "</tool_name>\n"
101
+ "...\n\n"
102
+ f"Authorized tools provided: {', '.join(authorized_names)}."
103
+ )
104
+ data.setdefault("name", "sequence_tool")
105
+ data.setdefault("description", desc)
106
+ default_argument = ToolArgument(
107
+ name="sequence",
108
+ arg_type="string",
109
+ required=True,
110
+ description="XML formatted sequence of tool calls. See syntax above.",
111
+ )
112
+ data.setdefault("arguments", [default_argument])
113
+ super().__init__(**data)
114
+
115
+ # Build a dictionary mapping tool names to tool instances.
116
+ self.available_tools = {tool.name: tool for tool in tools}
117
+
118
+ def execute(self, **kwargs) -> str:
119
+ """
120
+ Execute a sequence of tool calls provided in XML format and return an XML summary report.
121
+
122
+ Expected input formats:
123
+ Option A (wrapped):
124
+ <sequence>...</sequence>
125
+ Option B (raw):
126
+ <tool_name>...</tool_name>
127
+ <tool_name2>...</tool_name2>
128
+ ...
129
+
130
+ This implementation uses ToleranceXMLParser to extract the potential <sequence> element.
131
+ If exactly one <sequence> element is found, its content is used; otherwise, the entire input is treated
132
+ as the sequence content. Then, for each tool call, ToolParser (from quantalogic/xml_tool_parser.py)
133
+ is used to parse and validate arguments.
134
+
135
+ For each tool call, a <tool_call> element is created with:
136
+ - order: the position in the sequence.
137
+ - name: the tool's name.
138
+ - parameters: the parsed parameter names and values.
139
+ - status: "success" or "failure".
140
+ - output: tool output (if successful) or error_message (if an error occurred).
141
+
142
+ All tool calls are executed regardless of failures.
143
+ """
144
+ if "sequence" not in kwargs:
145
+ raise ValueError("Missing 'sequence' parameter.")
146
+
147
+ sequence_xml = kwargs["sequence"]
148
+ xml_parser = ToleranceXMLParser()
149
+
150
+ # Try to locate <sequence> elements in the input.
151
+ all_elements = xml_parser._find_all_elements(sequence_xml)
152
+ sequence_elems = [
153
+ (tag, content)
154
+ for tag, content in all_elements
155
+ if tag.strip().lower() == "sequence"
156
+ ]
157
+ if len(sequence_elems) > 1:
158
+ raise ValueError("Input XML must contain exactly one <sequence> element.")
159
+ elif len(sequence_elems) == 1:
160
+ # Use the inner content of the found <sequence> element.
161
+ sequence_content = sequence_elems[0][1]
162
+ else:
163
+ # No <sequence> element found; assume the entire input is the sequence content.
164
+ sequence_content = sequence_xml
165
+
166
+ # Extract tool call elements from the sequence content.
167
+ # Each tool call is represented as a tuple: (tool_name, inner_xml)
168
+ tool_calls = xml_parser._find_all_elements(sequence_content)
169
+ results_root = ET.Element("sequence_results")
170
+
171
+ for index, (tool_name, inner_xml) in enumerate(tool_calls, start=1):
172
+ tool_call_elem = ET.Element("tool_call", attrib={"order": str(index), "name": tool_name})
173
+ parameters_elem = ET.SubElement(tool_call_elem, "parameters")
174
+
175
+ # Validate that this is an authorized tool.
176
+ if tool_name not in self.available_tools:
177
+ status_elem = ET.SubElement(tool_call_elem, "status")
178
+ status_elem.text = "failure"
179
+ error_elem = ET.SubElement(tool_call_elem, "error_message")
180
+ error_elem.text = f"Tool '{tool_name}' not found among authorized tools."
181
+ results_root.append(tool_call_elem)
182
+ continue
183
+
184
+ tool = self.available_tools[tool_name]
185
+ # Use ToolParser to extract and validate arguments from the inner XML.
186
+ tool_parser = ToolParser(tool)
187
+ try:
188
+ parsed_params = tool_parser.parse(inner_xml)
189
+ # Record parsed parameters in the XML report.
190
+ for arg_name, arg_value in parsed_params.items():
191
+ param_record = ET.SubElement(parameters_elem, "param", attrib={"name": arg_name})
192
+ param_record.text = arg_value
193
+ except Exception as e:
194
+ status_elem = ET.SubElement(tool_call_elem, "status")
195
+ status_elem.text = "failure"
196
+ error_elem = ET.SubElement(tool_call_elem, "error_message")
197
+ error_elem.text = f"Argument parsing error: {str(e)}"
198
+ results_root.append(tool_call_elem)
199
+ continue
200
+
201
+ # Execute the authorized tool with the validated parameters.
202
+ try:
203
+ output = tool.execute(**parsed_params)
204
+ status_elem = ET.SubElement(tool_call_elem, "status")
205
+ status_elem.text = "success"
206
+ output_elem = ET.SubElement(tool_call_elem, "output")
207
+ output_elem.text = output
208
+ except Exception as e:
209
+ status_elem = ET.SubElement(tool_call_elem, "status")
210
+ status_elem.text = "failure"
211
+ error_elem = ET.SubElement(tool_call_elem, "error_message")
212
+ error_elem.text = str(e)
213
+ results_root.append(tool_call_elem)
214
+
215
+ return ET.tostring(results_root, encoding="unicode")
216
+
217
+
218
+ # ----------------- Example Usage and Testing -----------------
219
+
220
+ if __name__ == "__main__":
221
+ from typing import Any
222
+
223
+ # Dummy tool: WriteFileTool simulates writing content to a file.
224
+ class WriteFileTool(Tool):
225
+ def __init__(self, **data: Any):
226
+ data.setdefault("name", "write_file_tool")
227
+ data.setdefault("description", "Writes content to a file at the specified path.")
228
+ data.setdefault("arguments", [
229
+ ToolArgument(
230
+ name="file_path",
231
+ arg_type="string",
232
+ required=True,
233
+ description="Path to the file"
234
+ ),
235
+ ToolArgument(
236
+ name="content",
237
+ arg_type="string",
238
+ required=True,
239
+ description="Content to write to the file"
240
+ )
241
+ ])
242
+ super().__init__(**data)
243
+
244
+ def execute(self, **kwargs) -> str:
245
+ file_path = kwargs.get("file_path", "")
246
+ content = kwargs.get("content", "")
247
+ # For demonstration, simulate writing to a file.
248
+ return f"File '{file_path}' written with content length {len(content)}."
249
+
250
+ # Instantiate the WriteFileTool.
251
+ write_file = WriteFileTool()
252
+
253
+ # Instantiate the SequenceTool with WriteFileTool as the authorized tool.
254
+ sequence_tool = SequenceTool(tools=[write_file])
255
+
256
+ # Example XML sequence using CDATA blocks for multiline content.
257
+ # This example uses Option A (wrapped in a <sequence> element).
258
+ xml_sequence = """
259
+ <sequence>
260
+ <write_file_tool>
261
+ <file_path>poem1.txt</file_path>
262
+ <content><![CDATA[
263
+ Under the moon's silver glow,
264
+ Where shadows dance and winds do flow,
265
+ There lies a heart so pure and bright,
266
+ In the quiet of the night.
267
+ ]]></content>
268
+ </write_file_tool>
269
+ <write_file_tool>
270
+ <file_path>poem2.txt</file_path>
271
+ <content><![CDATA[
272
+ In the morning's golden light,
273
+ When dewdrops kiss the grass so bright,
274
+ The world awakens anew each day,
275
+ And hope finds its own sweet way.
276
+ ]]></content>
277
+ </write_file_tool>
278
+ </sequence>
279
+ """
280
+
281
+ try:
282
+ result_xml = sequence_tool.execute(sequence=xml_sequence)
283
+ print("SequenceTool result:\n", result_xml)
284
+ except Exception as e:
285
+ print("SequenceTool error:\n", str(e))
@@ -6,6 +6,7 @@ from .get_environment import get_environment
6
6
  from .get_coding_environment import get_coding_environment
7
7
  from .get_quantalogic_rules_content import get_quantalogic_rules_file_content
8
8
  from .lm_studio_model_info import get_model_list
9
+ from .python_interpreter import interpret_ast
9
10
 
10
11
  __all__ = [
11
12
  "download_http_file",