quantalogic 0.33.0__py3-none-any.whl → 0.33.1__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.
- quantalogic/agent.py +19 -8
- quantalogic/console_print_events.py +37 -38
- quantalogic/prompts.py +1 -1
- quantalogic/xml_parser.py +38 -36
- {quantalogic-0.33.0.dist-info → quantalogic-0.33.1.dist-info}/METADATA +1 -1
- {quantalogic-0.33.0.dist-info → quantalogic-0.33.1.dist-info}/RECORD +9 -9
- {quantalogic-0.33.0.dist-info → quantalogic-0.33.1.dist-info}/LICENSE +0 -0
- {quantalogic-0.33.0.dist-info → quantalogic-0.33.1.dist-info}/WHEEL +0 -0
- {quantalogic-0.33.0.dist-info → quantalogic-0.33.1.dist-info}/entry_points.txt +0 -0
quantalogic/agent.py
CHANGED
@@ -417,9 +417,18 @@ class Agent(BaseModel):
|
|
417
417
|
if not content or not isinstance(content, str):
|
418
418
|
return {}
|
419
419
|
|
420
|
+
# Extract action
|
420
421
|
xml_parser = ToleranceXMLParser()
|
422
|
+
action = xml_parser.extract_elements(text=content, element_names=["action"])
|
423
|
+
|
421
424
|
tool_names = self.tools.tool_names()
|
422
|
-
|
425
|
+
|
426
|
+
if action:
|
427
|
+
return xml_parser.extract_elements(text=action["action"], element_names=tool_names)
|
428
|
+
else:
|
429
|
+
# Fallback to extracting tool usage directly
|
430
|
+
return xml_parser.extract_elements(text=content, element_names=tool_names)
|
431
|
+
|
423
432
|
|
424
433
|
def _parse_tool_arguments(self, tool, tool_input: str) -> dict:
|
425
434
|
"""Parse the tool arguments from the tool input."""
|
@@ -511,11 +520,8 @@ class Agent(BaseModel):
|
|
511
520
|
formatted_response = formatted_response = (
|
512
521
|
"# Analysis and Next Action Decision Point\n\n"
|
513
522
|
f"📊 Progress: Iteration {iteration}/{self.max_iterations}\n\n"
|
514
|
-
"##
|
515
|
-
f"```\n{self.task_to_solve_summary}```\n\n"
|
516
|
-
f"## Latest Tool {last_exectured_tool} Execution Result:\n"
|
517
|
-
f"Variable: ${variable_name}$\n"
|
518
|
-
f"```\n{response_display}```\n\n"
|
523
|
+
"## Global Task summary:\n"
|
524
|
+
f"```\n\n{self.task_to_solve_summary}```\n\n"
|
519
525
|
"## Available Resources\n"
|
520
526
|
f"🛠️ Tools:\n{self._get_tools_names_prompt()}\n\n"
|
521
527
|
f"📦 Variables:\n{self._get_variable_prompt()}\n\n"
|
@@ -528,6 +534,9 @@ class Agent(BaseModel):
|
|
528
534
|
"Provide TWO markdown-formatted XML blocks:\n"
|
529
535
|
"1. Your analysis of the progression resulting from the execution of the tool in <thinking> tags, don't include <context_analysis/>\n"
|
530
536
|
"2. Your tool execution plan in <tool_name> tags\n\n"
|
537
|
+
"## Last executed action result\n"
|
538
|
+
f"Last executed tool {last_exectured_tool} Execution Result:\n"
|
539
|
+
f"\n<{variable_name}>\n{response_display}\n</{variable_name}>\n"
|
531
540
|
"## Response Format\n"
|
532
541
|
"```xml\n"
|
533
542
|
"<thinking>\n"
|
@@ -545,8 +554,10 @@ class Agent(BaseModel):
|
|
545
554
|
"- Respond ONLY with the two XML blocks\n"
|
546
555
|
"- No additional commentary\n"
|
547
556
|
"- If previous step failed, revise approach\n"
|
548
|
-
"-
|
549
|
-
"-
|
557
|
+
"- Use interpolated variables ($variable_name$) where required in tool calls, to minimize token usage, if possible\n"
|
558
|
+
"- strictly follow the required arguments for each tool as defined in system prompt\n"
|
559
|
+
"- Utilize <action><task_complete><answer>...</answer></task_complete><action> to indicate task completion, display the result or if the task is deemed unfeasible."
|
560
|
+
)
|
550
561
|
|
551
562
|
return formatted_response
|
552
563
|
|
@@ -3,64 +3,63 @@ from typing import Any
|
|
3
3
|
from rich import box
|
4
4
|
from rich.console import Console
|
5
5
|
from rich.panel import Panel
|
6
|
+
from rich.text import Text
|
6
7
|
from rich.tree import Tree
|
7
8
|
|
8
9
|
|
9
10
|
def console_print_events(event: str, data: dict[str, Any] | None = None):
|
10
|
-
"""Print events with
|
11
|
-
|
12
|
-
Args:
|
13
|
-
event (str): Name of the event.
|
14
|
-
data (Dict[str, Any], optional): Additional event data. Defaults to None.
|
15
|
-
"""
|
11
|
+
"""Print events with elegant compact formatting."""
|
16
12
|
console = Console()
|
17
13
|
|
18
|
-
#
|
19
|
-
panel_title = f"[bold cyan]Event: {event}[/bold cyan]"
|
20
|
-
|
14
|
+
# Stylish no-data presentation
|
21
15
|
if not data:
|
22
|
-
# Display a friendly message when no data is available
|
23
16
|
console.print(
|
24
|
-
Panel(
|
25
|
-
"
|
26
|
-
title=
|
27
|
-
border_style="
|
28
|
-
|
29
|
-
padding=(
|
17
|
+
Panel.fit(
|
18
|
+
Text(f"ⓘ No event data", justify="center", style="italic cyan"),
|
19
|
+
title=f"✨ {event}",
|
20
|
+
border_style="cyan",
|
21
|
+
box=box.ROUNDED,
|
22
|
+
padding=(0, 2),
|
30
23
|
)
|
31
24
|
)
|
32
25
|
return
|
33
26
|
|
34
|
-
#
|
35
|
-
def render_tree(data: dict[str,
|
27
|
+
# Enhanced tree rendering with subtle decorations
|
28
|
+
def render_tree(data: dict[str, Any], tree: Tree) -> None:
|
36
29
|
for key, value in data.items():
|
30
|
+
key_text = Text(f"◈ {key}", style="bright_magenta")
|
37
31
|
if isinstance(value, dict):
|
38
|
-
branch = tree.add(
|
32
|
+
branch = tree.add(key_text)
|
39
33
|
render_tree(value, branch)
|
40
34
|
elif isinstance(value, list):
|
41
|
-
branch = tree.add(
|
42
|
-
for
|
35
|
+
branch = tree.add(key_text)
|
36
|
+
for item in value:
|
43
37
|
if isinstance(item, dict):
|
44
|
-
sub_branch = branch.add(
|
38
|
+
sub_branch = branch.add(Text("○", style="cyan"))
|
45
39
|
render_tree(item, sub_branch)
|
46
40
|
else:
|
47
|
-
branch.add(f"
|
41
|
+
branch.add(Text(f"• {item}", style="dim green"))
|
48
42
|
else:
|
49
|
-
tree.add(
|
50
|
-
|
51
|
-
|
52
|
-
|
43
|
+
tree.add(Text.assemble(
|
44
|
+
key_text,
|
45
|
+
(" → ", "dim"),
|
46
|
+
str(value), style="bright_white"
|
47
|
+
))
|
53
48
|
|
49
|
+
# Create a compact tree with subtle styling
|
50
|
+
tree = Tree("", guide_style="dim cyan", hide_root=True)
|
54
51
|
render_tree(data, tree)
|
55
52
|
|
56
|
-
#
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
53
|
+
# Elegant panel design
|
54
|
+
console.print(
|
55
|
+
Panel(
|
56
|
+
tree,
|
57
|
+
title=f"🎯 [bold bright_cyan]{event}[/]",
|
58
|
+
border_style="bright_blue",
|
59
|
+
box=box.DOUBLE_EDGE,
|
60
|
+
padding=(0, 1),
|
61
|
+
subtitle=f"[dim]Items: {len(data)}[/dim]",
|
62
|
+
subtitle_align="right",
|
63
|
+
),
|
64
|
+
no_wrap=True
|
65
|
+
)
|
quantalogic/prompts.py
CHANGED
@@ -39,7 +39,7 @@ Task Format: <task>task_description</task>
|
|
39
39
|
<!-- ONGOING OPERATIONS -->
|
40
40
|
• 🔄 Analyze Last Operation Results: Result, Impact, Effectiveness
|
41
41
|
• 📊 Progress Map: Completed%, Remaining%, Blockers
|
42
|
-
• 💾 Variable State: $var:
|
42
|
+
• 💾 Variable State: $var: short description of the content of each variable.
|
43
43
|
• 📈 Performance Metrics: Speed, Quality, Resource Usage
|
44
44
|
</execution_analysis>
|
45
45
|
|
quantalogic/xml_parser.py
CHANGED
@@ -7,7 +7,6 @@ with support for handling malformed XML and CDATA sections.
|
|
7
7
|
import html
|
8
8
|
import re
|
9
9
|
from collections import defaultdict
|
10
|
-
from functools import lru_cache
|
11
10
|
from typing import Self
|
12
11
|
|
13
12
|
from loguru import logger
|
@@ -53,29 +52,22 @@ class ToleranceXMLParser:
|
|
53
52
|
"""
|
54
53
|
|
55
54
|
# Default mappings for element name normalization
|
56
|
-
DEFAULT_NAME_MAP = {
|
57
|
-
"o": "output",
|
58
|
-
"i": "input",
|
59
|
-
"opt": "optional"
|
60
|
-
}
|
55
|
+
DEFAULT_NAME_MAP = {"o": "output", "i": "input", "opt": "optional"}
|
61
56
|
|
62
57
|
def __init__(self: Self) -> None:
|
63
58
|
"""Initialize the parser with regex patterns for matching XML-like elements."""
|
64
59
|
# Pattern for matching individual XML elements with better whitespace handling
|
65
|
-
self.element_pattern = re.compile(
|
66
|
-
r"<\s*([^/>]+?)\s*>(.*?)(?:</\s*\1\s*>|<\s*\1\s*>)",
|
67
|
-
re.DOTALL
|
68
|
-
)
|
60
|
+
self.element_pattern = re.compile(r"<\s*([^/>]+?)\s*>(.*?)(?:</\s*\1\s*>|<\s*\1\s*>)", re.DOTALL)
|
69
61
|
# Pattern for matching CDATA sections
|
70
62
|
self.cdata_pattern = re.compile(r"<!\[CDATA\[(.*?)]]>", re.DOTALL)
|
71
63
|
logger.debug("Initialized ToleranceXMLParser with regex patterns")
|
72
64
|
|
73
65
|
def _validate_input(self, text: str) -> None:
|
74
66
|
"""Validate input text before processing.
|
75
|
-
|
67
|
+
|
76
68
|
Args:
|
77
69
|
text: Input text to validate.
|
78
|
-
|
70
|
+
|
79
71
|
Raises:
|
80
72
|
ValueError: If input text is invalid.
|
81
73
|
"""
|
@@ -120,7 +112,6 @@ class ToleranceXMLParser:
|
|
120
112
|
# Only unescape HTML entities, preserve everything else exactly as is
|
121
113
|
return html.unescape(content)
|
122
114
|
|
123
|
-
@lru_cache(maxsize=128)
|
124
115
|
def _map_element_name(self: Self, name: str) -> str:
|
125
116
|
"""Map element names to their canonical form.
|
126
117
|
|
@@ -134,10 +125,10 @@ class ToleranceXMLParser:
|
|
134
125
|
|
135
126
|
def _build_element_pattern(self, element_name: str) -> re.Pattern[str]:
|
136
127
|
"""Build regex pattern for finding specific XML elements.
|
137
|
-
|
128
|
+
|
138
129
|
Args:
|
139
130
|
element_name: Name of the element to match.
|
140
|
-
|
131
|
+
|
141
132
|
Returns:
|
142
133
|
Compiled regex pattern for matching the element.
|
143
134
|
"""
|
@@ -145,53 +136,45 @@ class ToleranceXMLParser:
|
|
145
136
|
cdata_section = r"(?:<!\[CDATA\[.*?]]>)?"
|
146
137
|
content_pattern = f"({non_cdata}{cdata_section}{non_cdata})"
|
147
138
|
closing_pattern = "(?:</\1>|<\1>)"
|
148
|
-
|
149
|
-
return re.compile(
|
150
|
-
f"<{element_name}>{content_pattern}{closing_pattern}",
|
151
|
-
re.DOTALL
|
152
|
-
)
|
139
|
+
|
140
|
+
return re.compile(f"<{element_name}>{content_pattern}{closing_pattern}", re.DOTALL)
|
153
141
|
|
154
142
|
def _find_all_elements(self, text: str) -> list[tuple[str, str]]:
|
155
143
|
"""Find all XML elements in text.
|
156
|
-
|
144
|
+
|
157
145
|
Args:
|
158
146
|
text: Input text to search.
|
159
|
-
|
147
|
+
|
160
148
|
Returns:
|
161
149
|
List of tuples containing element names and their content.
|
162
150
|
"""
|
163
|
-
return [(match.group(1), match.group(2) or "")
|
164
|
-
for match in self.element_pattern.finditer(text)]
|
151
|
+
return [(match.group(1), match.group(2) or "") for match in self.element_pattern.finditer(text)]
|
165
152
|
|
166
153
|
def _process_element_content(self, content: str, preserve_cdata: bool) -> str:
|
167
154
|
"""Process content of a single element.
|
168
|
-
|
155
|
+
|
169
156
|
Args:
|
170
157
|
content: Raw element content.
|
171
158
|
preserve_cdata: Whether to preserve CDATA sections.
|
172
|
-
|
159
|
+
|
173
160
|
Returns:
|
174
161
|
Processed content string.
|
175
162
|
"""
|
176
163
|
content, cdata_sections = self._extract_and_remove_cdata(content, preserve_cdata)
|
177
164
|
content = self._clean_content(content)
|
178
|
-
|
165
|
+
|
179
166
|
# If content is empty but we have CDATA sections and we're not preserving them
|
180
167
|
if not content.strip() and cdata_sections and not preserve_cdata:
|
181
168
|
return cdata_sections[0]
|
182
169
|
return content
|
183
170
|
|
184
|
-
def _process_elements(
|
185
|
-
self,
|
186
|
-
elements: list[tuple[str, str]],
|
187
|
-
preserve_cdata: bool
|
188
|
-
) -> dict[str, str]:
|
171
|
+
def _process_elements(self, elements: list[tuple[str, str]], preserve_cdata: bool) -> dict[str, str]:
|
189
172
|
"""Process found elements and handle CDATA sections.
|
190
|
-
|
173
|
+
|
191
174
|
Args:
|
192
175
|
elements: List of element name and content tuples.
|
193
176
|
preserve_cdata: Whether to preserve CDATA sections.
|
194
|
-
|
177
|
+
|
195
178
|
Returns:
|
196
179
|
Dictionary mapping element names to their processed content.
|
197
180
|
"""
|
@@ -199,12 +182,12 @@ class ToleranceXMLParser:
|
|
199
182
|
for name, content in elements:
|
200
183
|
name = self._map_element_name(name)
|
201
184
|
result[name] = self._process_element_content(content, preserve_cdata)
|
202
|
-
|
185
|
+
|
203
186
|
# Handle nested elements
|
204
187
|
nested_elements = self._find_all_elements(content)
|
205
188
|
nested_results = self._process_elements(nested_elements, preserve_cdata)
|
206
189
|
result.update(nested_results)
|
207
|
-
|
190
|
+
|
208
191
|
return dict(result)
|
209
192
|
|
210
193
|
def _extract_element_content(self: Self, text: str, preserve_cdata: bool = False) -> dict[str, str]:
|
@@ -301,3 +284,22 @@ class ToleranceXMLParser:
|
|
301
284
|
error_msg = error_msg + f"\n{text}\n"
|
302
285
|
logger.error(error_msg)
|
303
286
|
raise ValueError(error_msg)
|
287
|
+
|
288
|
+
|
289
|
+
if __name__ == "__main__":
|
290
|
+
xml_content = """
|
291
|
+
<action>
|
292
|
+
<task_complete>
|
293
|
+
<answer>Hello</answer>
|
294
|
+
</task_complete>
|
295
|
+
</action>
|
296
|
+
"""
|
297
|
+
|
298
|
+
parser = ToleranceXMLParser()
|
299
|
+
parsed_values = parser.extract_elements(xml_content)
|
300
|
+
print(parsed_values)
|
301
|
+
if "action" in parsed_values:
|
302
|
+
print(parsed_values["action"])
|
303
|
+
action = parser.extract_elements(text=xml_content, element_names=["action"])
|
304
|
+
print(action["action"])
|
305
|
+
|
@@ -1,10 +1,10 @@
|
|
1
1
|
quantalogic/__init__.py,sha256=Su8CnOEdqKu4zTytjiP9P5olg-oIDuUA3fMWM1WUdRY,925
|
2
|
-
quantalogic/agent.py,sha256=
|
2
|
+
quantalogic/agent.py,sha256=x-VDSRkC98Rtw-4kvptE68sbtnZo72Y1jDUyaGoXgvs,34835
|
3
3
|
quantalogic/agent_config.py,sha256=SIRVSF0kkrYfvtyHiMCJhnm_nYqJCD2p1pN-reMIy24,7868
|
4
4
|
quantalogic/agent_factory.py,sha256=HWKwN_DN57EPmME-hoCD2uJE0DqsPCzGU_V7nq54XzI,5284
|
5
5
|
quantalogic/coding_agent.py,sha256=Z7ik6LUvLKDnaW9Ax1iZGC7p1WMnlYEUIlE5lkBP414,4975
|
6
6
|
quantalogic/config.py,sha256=S_SAdsCoTa1jS8GIJW2TjlCtE5vjGDbMBg-6E0j8K1o,355
|
7
|
-
quantalogic/console_print_events.py,sha256=
|
7
|
+
quantalogic/console_print_events.py,sha256=md1CwNBoi58nW7KRVESna3QfkWDJo8PvugXSKjL-c-k,2129
|
8
8
|
quantalogic/console_print_token.py,sha256=qSU-3kmoZk4T5-1ybrEBi8tIXDPcz7eyWKhGh3E8uIg,395
|
9
9
|
quantalogic/docs_cli.py,sha256=3giVbUpespB9ZdTSJ955A3BhcOaBl5Lwsn1AVy9XAeY,1663
|
10
10
|
quantalogic/event_emitter.py,sha256=jqot2g4JRXc88K6PW837Oqxbf7shZfO-xdPaUWmzupk,7901
|
@@ -18,7 +18,7 @@ quantalogic/model_info.py,sha256=j7QqvjEFQDGpDOgQs8uTkVyI3a50Oa_nrsQjyxizTLc,272
|
|
18
18
|
quantalogic/model_info_list.py,sha256=qGDKI5oBbu5oyJSH4o4jGYGqyqYEUMXFRi_qMDAQ2nU,1997
|
19
19
|
quantalogic/model_info_litellm.py,sha256=m1Yt4SIiOBRWLx7S8f8k4fcTiKJZKtOvcPN_QvQ_Oxk,1880
|
20
20
|
quantalogic/model_names.py,sha256=UZlz25zG9B2dpfwdw_e1Gw5qFsKQ7iME9FJh9Ts4u6s,938
|
21
|
-
quantalogic/prompts.py,sha256=
|
21
|
+
quantalogic/prompts.py,sha256=60BzWY31zRS89bIISiIcCLK5764n8Ry-aXFHda4SDq4,3197
|
22
22
|
quantalogic/search_agent.py,sha256=EA_FAPP0dVuUbJ_lAGKfYq1FIJ6oLYzGMgKLMvBL4ZQ,2472
|
23
23
|
quantalogic/server/__init__.py,sha256=8sz_PYAUCrkM6JM5EAUeIzNM4NPW6j6UT72JVkc21WQ,91
|
24
24
|
quantalogic/server/agent_server.py,sha256=VXaaWqReUSZOCX7CaKS14jria8yZn1kLEc52E2hV7ZA,22510
|
@@ -90,10 +90,10 @@ quantalogic/utils/read_http_text_content.py,sha256=n3IayT5KcqctIVVF2gOQQAMf3Ow6e
|
|
90
90
|
quantalogic/version.py,sha256=ea_cRutaQk5_lwlLbUUvPFuOT7Of7-gAsDl7wdveS-g,107
|
91
91
|
quantalogic/version_check.py,sha256=cttR1lR3OienGLl7NrK1Te1fhDkqSjCci7HC1vFUTSY,1627
|
92
92
|
quantalogic/welcome_message.py,sha256=IXMhem8h7srzNUwvw8G_lmEkHU8PFfote021E_BXmVk,3039
|
93
|
-
quantalogic/xml_parser.py,sha256=
|
93
|
+
quantalogic/xml_parser.py,sha256=iUQZC4a8uqBsP7RAZaY0Ed9k8TCd-ImuF41eF7MR2WM,11615
|
94
94
|
quantalogic/xml_tool_parser.py,sha256=Vz4LEgDbelJynD1siLOVkJ3gLlfHsUk65_gCwbYJyGc,3784
|
95
|
-
quantalogic-0.33.
|
96
|
-
quantalogic-0.33.
|
97
|
-
quantalogic-0.33.
|
98
|
-
quantalogic-0.33.
|
99
|
-
quantalogic-0.33.
|
95
|
+
quantalogic-0.33.1.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
96
|
+
quantalogic-0.33.1.dist-info/METADATA,sha256=Y9LVZWkWkL1D0a04gld5M2yJRF1-dEdzTLTaTa3urrM,23461
|
97
|
+
quantalogic-0.33.1.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
|
98
|
+
quantalogic-0.33.1.dist-info/entry_points.txt,sha256=h74O_Q3qBRCrDR99qvwB4BpBGzASPUIjCfxHq6Qnups,183
|
99
|
+
quantalogic-0.33.1.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|