quantalogic 0.32.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 CHANGED
@@ -52,11 +52,7 @@ class ObserveResponseResult(BaseModel):
52
52
  class Agent(BaseModel):
53
53
  """Enhanced QuantaLogic agent implementing ReAct framework."""
54
54
 
55
- model_config = ConfigDict(
56
- arbitrary_types_allowed=True,
57
- validate_assignment=True,
58
- extra="forbid"
59
- )
55
+ model_config = ConfigDict(arbitrary_types_allowed=True, validate_assignment=True, extra="forbid")
60
56
 
61
57
  specific_expertise: str
62
58
  model: GenerativeModel
@@ -95,7 +91,7 @@ class Agent(BaseModel):
95
91
  """Initialize the agent with model, memory, tools, and configurations."""
96
92
  try:
97
93
  logger.debug("Initializing agent...")
98
-
94
+
99
95
  # Create event emitter
100
96
  event_emitter = EventEmitter()
101
97
 
@@ -142,9 +138,9 @@ class Agent(BaseModel):
142
138
  compact_every_n_iterations=compact_every_n_iterations or 30,
143
139
  max_tokens_working_memory=max_tokens_working_memory,
144
140
  )
145
-
141
+
146
142
  self._model_name = model_name
147
-
143
+
148
144
  logger.debug(f"Memory will be compacted every {self.compact_every_n_iterations} iterations")
149
145
  logger.debug(f"Max tokens for working memory set to: {self.max_tokens_working_memory}")
150
146
  logger.debug("Agent initialized successfully.")
@@ -168,7 +164,9 @@ class Agent(BaseModel):
168
164
  """Clear the memory and reset the session."""
169
165
  self._reset_session(clear_memory=True)
170
166
 
171
- def solve_task(self, task: str, max_iterations: int = 30, streaming: bool = False, clear_memory: bool = True) -> str:
167
+ def solve_task(
168
+ self, task: str, max_iterations: int = 30, streaming: bool = False, clear_memory: bool = True
169
+ ) -> str:
172
170
  """Solve the given task using the ReAct framework.
173
171
 
174
172
  Args:
@@ -182,7 +180,7 @@ class Agent(BaseModel):
182
180
  str: The final response after task completion.
183
181
  """
184
182
  logger.debug(f"Solving task... {task}")
185
- self._reset_session(task_to_solve=task, max_iterations=max_iterations,clear_memory=clear_memory)
183
+ self._reset_session(task_to_solve=task, max_iterations=max_iterations, clear_memory=clear_memory)
186
184
 
187
185
  # Generate task summary
188
186
  self.task_to_solve_summary = self._generate_task_summary(task)
@@ -228,7 +226,9 @@ class Agent(BaseModel):
228
226
  # For streaming, collect the response chunks
229
227
  content = ""
230
228
  for chunk in self.model.generate_with_history(
231
- messages_history=self.memory.memory, prompt=current_prompt, streaming=True
229
+ messages_history=self.memory.memory,
230
+ prompt=current_prompt,
231
+ streaming=True,
232
232
  ):
233
233
  content += chunk
234
234
 
@@ -245,7 +245,8 @@ class Agent(BaseModel):
245
245
  )
246
246
  else:
247
247
  result = self.model.generate_with_history(
248
- messages_history=self.memory.memory, prompt=current_prompt, streaming=False
248
+ messages_history=self.memory.memory, prompt=current_prompt, streaming=False,
249
+ stop_words=["thinking"]
249
250
  )
250
251
 
251
252
  content = result.response
@@ -296,7 +297,7 @@ class Agent(BaseModel):
296
297
 
297
298
  return answer
298
299
 
299
- def _reset_session(self, task_to_solve: str = "", max_iterations: int = 30,clear_memory: bool = True):
300
+ def _reset_session(self, task_to_solve: str = "", max_iterations: int = 30, clear_memory: bool = True):
300
301
  """Reset the agent's session."""
301
302
  logger.debug("Resetting session...")
302
303
  self.task_to_solve = task_to_solve
@@ -316,29 +317,30 @@ class Agent(BaseModel):
316
317
  def _compact_memory_if_needed(self, current_prompt: str = ""):
317
318
  """Compacts the memory if it exceeds the maximum occupancy or token limit."""
318
319
  ratio_occupied = self._calculate_context_occupancy()
319
-
320
+
320
321
  # Compact memory if any of these conditions are met:
321
322
  # 1. Memory occupancy exceeds MAX_OCCUPANCY, or
322
323
  # 2. Current iteration is a multiple of compact_every_n_iterations, or
323
324
  # 3. Working memory exceeds max_tokens_working_memory (if set)
324
325
  should_compact_by_occupancy = ratio_occupied >= MAX_OCCUPANCY
325
326
  should_compact_by_iteration = (
326
- self.compact_every_n_iterations is not None and
327
- self.current_iteration > 0 and
328
- self.current_iteration % self.compact_every_n_iterations == 0
327
+ self.compact_every_n_iterations is not None
328
+ and self.current_iteration > 0
329
+ and self.current_iteration % self.compact_every_n_iterations == 0
329
330
  )
330
331
  should_compact_by_token_limit = (
331
- self.max_tokens_working_memory is not None and
332
- self.total_tokens > self.max_tokens_working_memory
332
+ self.max_tokens_working_memory is not None and self.total_tokens > self.max_tokens_working_memory
333
333
  )
334
-
334
+
335
335
  if should_compact_by_occupancy or should_compact_by_iteration or should_compact_by_token_limit:
336
336
  if should_compact_by_occupancy:
337
337
  logger.debug(f"Memory compaction triggered: Occupancy {ratio_occupied}% exceeds {MAX_OCCUPANCY}%")
338
-
338
+
339
339
  if should_compact_by_iteration:
340
- logger.debug(f"Memory compaction triggered: Iteration {self.current_iteration} is a multiple of {self.compact_every_n_iterations}")
341
-
340
+ logger.debug(
341
+ f"Memory compaction triggered: Iteration {self.current_iteration} is a multiple of {self.compact_every_n_iterations}"
342
+ )
343
+
342
344
  self._emit_event("memory_full")
343
345
  self.memory.compact()
344
346
  self.total_tokens = self.model.token_counter_with_history(self.memory.memory, current_prompt)
@@ -399,7 +401,7 @@ class Agent(BaseModel):
399
401
  return self._handle_tool_execution_failure(response)
400
402
 
401
403
  variable_name = self.variable_store.add(response)
402
- new_prompt = self._format_observation_response(response, variable_name, iteration)
404
+ new_prompt = self._format_observation_response(response, executed_tool, variable_name, iteration)
403
405
 
404
406
  return ObserveResponseResult(
405
407
  next_prompt=new_prompt,
@@ -414,10 +416,19 @@ class Agent(BaseModel):
414
416
  """Extract tool usage from the response content."""
415
417
  if not content or not isinstance(content, str):
416
418
  return {}
417
-
419
+
420
+ # Extract action
418
421
  xml_parser = ToleranceXMLParser()
422
+ action = xml_parser.extract_elements(text=content, element_names=["action"])
423
+
419
424
  tool_names = self.tools.tool_names()
420
- return xml_parser.extract_elements(text=content, element_names=tool_names)
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
+
421
432
 
422
433
  def _parse_tool_arguments(self, tool, tool_input: str) -> dict:
423
434
  """Parse the tool arguments from the tool input."""
@@ -461,7 +472,7 @@ class Agent(BaseModel):
461
472
  answer=None,
462
473
  )
463
474
 
464
- def _handle_repeated_tool_call(self, tool_name: str, arguments_with_values: dict) -> (str,str):
475
+ def _handle_repeated_tool_call(self, tool_name: str, arguments_with_values: dict) -> (str, str):
465
476
  """Handle the case where a tool call is repeated."""
466
477
  repeat_count = self.last_tool_call.get("count", 0)
467
478
  error_message = (
@@ -494,7 +505,9 @@ class Agent(BaseModel):
494
505
  answer=None,
495
506
  )
496
507
 
497
- def _format_observation_response(self, response: str, variable_name: str, iteration: int) -> str:
508
+ def _format_observation_response(
509
+ self, response: str, last_exectured_tool: str, variable_name: str, iteration: int
510
+ ) -> str:
498
511
  """Format the observation response with the given response, variable name, and iteration."""
499
512
  response_display = response
500
513
  if len(response) > MAX_RESPONSE_LENGTH:
@@ -504,28 +517,46 @@ class Agent(BaseModel):
504
517
  )
505
518
 
506
519
  # Format the response message
507
- formatted_response = (
508
- f"Your next step: you Must now plan the next tool call to complete the based on this new observation\n"
509
- f"\n--- Observations for iteration {iteration} / max {self.max_iterations} ---\n"
510
- f"\n--- Tool execution result in ${variable_name}$ ---\n"
511
- f"<{variable_name}>\n{response_display}\n</{variable_name}>\n\n"
512
- f"--- Tools ---\n{self._get_tools_names_prompt()}\n"
513
- f"--- Variables ---\n{self._get_variable_prompt()}\n"
514
- "Analyze this response to determine the next steps. If the step failed, reconsider your approach.\n"
515
- f"--- Task to solve summary ---\n{self.task_to_solve_summary}\n"
516
- "--- Format ---\n"
517
- "Respond only with two XML blocks in markdown as specified in system prompt.\n"
518
- "No extra comments must be added.\n"
520
+ formatted_response = formatted_response = (
521
+ "# Analysis and Next Action Decision Point\n\n"
522
+ f"📊 Progress: Iteration {iteration}/{self.max_iterations}\n\n"
523
+ "## Global Task summary:\n"
524
+ f"```\n\n{self.task_to_solve_summary}```\n\n"
525
+ "## Available Resources\n"
526
+ f"🛠️ Tools:\n{self._get_tools_names_prompt()}\n\n"
527
+ f"📦 Variables:\n{self._get_variable_prompt()}\n\n"
528
+ "## Your Task\n"
529
+ "1. Analyze the execution result and progress, formalize if the current step is solved according to the task.\n"
530
+ "2. Determine the most effective next step\n"
531
+ "3. Select exactly ONE tool from the available list\n"
532
+ "4. Utilize variable interpolation where needed\n"
533
+ "## Response Requirements\n"
534
+ "Provide TWO markdown-formatted XML blocks:\n"
535
+ "1. Your analysis of the progression resulting from the execution of the tool in <thinking> tags, don't include <context_analysis/>\n"
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"
540
+ "## Response Format\n"
519
541
  "```xml\n"
520
542
  "<thinking>\n"
521
- "...\n"
543
+ "[Detailed analysis of progress, and reasoning for next step]\n"
522
544
  "</thinking>\n"
523
545
  "```\n"
524
546
  "```xml\n"
525
- "< ...tool_name... >\n"
526
- "...\n"
527
- "</ ...tool_name... >\n"
528
- "```"
547
+ "<action>\n"
548
+ "<selected_tool_name>\n"
549
+ "[Precise instruction for tool execution]\n"
550
+ "</selected_tool_name>\n"
551
+ "</action>\n"
552
+ "```\n\n"
553
+ "⚠️ Important:\n"
554
+ "- Respond ONLY with the two XML blocks\n"
555
+ "- No additional commentary\n"
556
+ "- If previous step failed, revise approach\n"
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."
529
560
  )
530
561
 
531
562
  return formatted_response
@@ -589,10 +620,10 @@ class Agent(BaseModel):
589
620
  arguments_with_values_interpolated = {
590
621
  key: self._interpolate_variables(value) for key, value in arguments_with_values.items()
591
622
  }
592
-
623
+
593
624
  arguments_with_values_interpolated = arguments_with_values_interpolated
594
625
 
595
- # test if tool need variables in context
626
+ # test if tool need variables in context
596
627
  if tool.need_variables:
597
628
  # Inject variables into the tool if needed
598
629
  arguments_with_values_interpolated["variables"] = self.variable_store
@@ -603,8 +634,7 @@ class Agent(BaseModel):
603
634
  try:
604
635
  # Convert arguments to proper types
605
636
  converted_args = self.tools.validate_and_convert_arguments(
606
- tool_name,
607
- arguments_with_values_interpolated
637
+ tool_name, arguments_with_values_interpolated
608
638
  )
609
639
  except ValueError as e:
610
640
  return "", f"Argument Error: {str(e)}"
@@ -637,9 +667,10 @@ class Agent(BaseModel):
637
667
  """Interpolate variables using $var$ syntax in the given text."""
638
668
  try:
639
669
  import re
670
+
640
671
  for var in self.variable_store.keys():
641
672
  # Escape the variable name for regex, but use raw value for replacement
642
- pattern = rf'\${re.escape(var)}\$'
673
+ pattern = rf"\${re.escape(var)}\$"
643
674
  replacement = self.variable_store[var]
644
675
  text = re.sub(pattern, replacement, text)
645
676
  return text
@@ -729,9 +760,7 @@ class Agent(BaseModel):
729
760
  # Remove the last assistant / user message
730
761
  user_message = memory_copy.pop()
731
762
  assistant_message = memory_copy.pop()
732
- summary = self.model.generate_with_history(
733
- messages_history=memory_copy, prompt=prompt_summary
734
- )
763
+ summary = self.model.generate_with_history(messages_history=memory_copy, prompt=prompt_summary)
735
764
  # Remove user message
736
765
  memory_copy.pop()
737
766
  # Replace by summary
@@ -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 rich formatting.
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
- # Define panel title with enhanced styling
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
- "[italic yellow]No additional event data available.[/italic yellow]",
26
- title=panel_title,
27
- border_style="dim",
28
- expand=True,
29
- padding=(1, 2),
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
- # Function to render nested dictionaries as a tree
35
- def render_tree(data: dict[str, any], tree: Tree):
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(f"[bold magenta]{key}[/bold magenta]")
32
+ branch = tree.add(key_text)
39
33
  render_tree(value, branch)
40
34
  elif isinstance(value, list):
41
- branch = tree.add(f"[bold magenta]{key}[/bold magenta]")
42
- for index, item in enumerate(value, start=1):
35
+ branch = tree.add(key_text)
36
+ for item in value:
43
37
  if isinstance(item, dict):
44
- sub_branch = branch.add(f"[cyan]Item {index}[/cyan]")
38
+ sub_branch = branch.add(Text("○", style="cyan"))
45
39
  render_tree(item, sub_branch)
46
40
  else:
47
- branch.add(f"[green]{item}[/green]")
41
+ branch.add(Text(f"{item}", style="dim green"))
48
42
  else:
49
- tree.add(f"[bold yellow]{key}[/bold yellow]: [white]{value}[/white]")
50
-
51
- # Create a Tree to represent nested data
52
- tree = Tree(f"[bold blue]{event} Details[/bold blue]", guide_style="bold bright_blue")
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
- # Create a panel to display the tree
57
- panel = Panel(
58
- tree,
59
- title=panel_title,
60
- border_style="bright_blue",
61
- padding=(1, 2),
62
- box=box.ROUNDED,
63
- expand=True,
64
- )
65
-
66
- console.print(panel)
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
+ )
@@ -123,7 +123,8 @@ class GenerativeModel:
123
123
 
124
124
  # Generate a response with conversation history and optional streaming
125
125
  def generate_with_history(
126
- self, messages_history: list[Message], prompt: str, image_url: str | None = None, streaming: bool = False
126
+ self, messages_history: list[Message], prompt: str, image_url: str | None = None, streaming: bool = False,
127
+ stop_words: list[str] | None = None
127
128
  ) -> ResponseStats:
128
129
  """Generate a response with conversation history and optional image.
129
130
 
@@ -132,6 +133,7 @@ class GenerativeModel:
132
133
  prompt: Current user prompt.
133
134
  image_url: Optional image URL for visual queries.
134
135
  streaming: Whether to stream the response.
136
+ stop_words: Optional list of stop words for streaming
135
137
 
136
138
  Returns:
137
139
  Detailed response statistics or a generator in streaming mode.
@@ -163,6 +165,7 @@ class GenerativeModel:
163
165
  model=self.model,
164
166
  messages=messages,
165
167
  num_retries=MIN_RETRIES,
168
+ stop=stop_words,
166
169
  )
167
170
 
168
171
  token_usage = TokenUsage(
@@ -181,7 +184,7 @@ class GenerativeModel:
181
184
  except Exception as e:
182
185
  self._handle_generation_exception(e)
183
186
 
184
- def _stream_response(self, messages):
187
+ def _stream_response(self, messages, stop_words: list[str] | None = None):
185
188
  """Private method to handle streaming responses."""
186
189
  try:
187
190
  for chunk in generate_completion(
@@ -189,7 +192,8 @@ class GenerativeModel:
189
192
  model=self.model,
190
193
  messages=messages,
191
194
  num_retries=MIN_RETRIES,
192
- stream=True, # Enable streaming
195
+ stream=True, # Enable streaming,
196
+ stop=stop_words,
193
197
  ):
194
198
  if chunk.choices[0].delta.content is not None:
195
199
  self.event_emitter.emit("stream_chunk", chunk.choices[0].delta.content)
quantalogic/llm.py CHANGED
@@ -60,6 +60,12 @@ PROVIDERS = {
60
60
  provider="openai",
61
61
  base_url="https://integrate.api.nvidia.com/v1",
62
62
  env_var="NVIDIA_API_KEY"
63
+ ),
64
+ "ovh": ModelProviderConfig(
65
+ prefix="ovh/",
66
+ provider="openai",
67
+ base_url="https://deepseek-r1-distill-llama-70b.endpoints.kepler.ai.cloud.ovh.net/api/openai_compat/v1",
68
+ env_var="OVH_API_KEY"
63
69
  )
64
70
  }
65
71
 
@@ -52,6 +52,9 @@ model_info = {
52
52
  max_output_tokens=8 * 1024,
53
53
  max_input_tokens=1024 * 64,
54
54
  ),
55
-
56
-
55
+ "ovh/DeepSeek-R1-Distill-Llama-70B": ModelInfo(
56
+ model_name="ovh/DeepSeek-R1-Distill-Llama-70B",
57
+ max_output_tokens=8 * 1024,
58
+ max_input_tokens=1024 * 64,
59
+ ),
57
60
  }
quantalogic/prompts.py CHANGED
@@ -1,119 +1,116 @@
1
+ from quantalogic.version import get_version
2
+
1
3
 
2
4
  def system_prompt(tools: str, environment: str, expertise: str = ""):
3
- """System prompt for the ReAct chatbot."""
5
+ """System prompt for the ReAct chatbot with enhanced cognitive architecture."""
4
6
  return f"""
5
- ### Core Identity
6
- You are QuantaLogic, an advanced ReAct AI Agent specializing in systematic problem-solving.
7
+ ### Agent Identity: QuantaLogic {get_version()}
8
+ Expert ReAct AI Agent implementing OODA (Observe-Orient-Decide-Act) loop with advanced problem-solving capabilities.
7
9
 
8
- ### Specific Expertise
10
+ ### Domain Expertise
9
11
  {expertise}
10
12
 
11
- ### Task Format
12
- Tasks will be presented within XML tags:
13
- <task>task_description</task>
13
+ ### Input Protocol
14
+ Task Format: <task>task_description</task>
15
+
16
+ ### Cognitive Framework
17
+ 1. 🔍 OBSERVE: Gather and process information
18
+ 2. 🧭 ORIENT: Analyze context and evaluate options
19
+ 3. 🎯 DECIDE: Select optimal action path
20
+ 4. ⚡ ACT: Execute precise tool operations
14
21
 
15
- ### Response Protocol
16
- Every response must contain exactly two XML blocks:
22
+ ### Response Schema [MANDATORY TWO-BLOCK FORMAT]
17
23
 
18
- 1. **Analysis Block**:
24
+ 1. 🧠 Analysis Block:
19
25
  ```xml
20
26
  <thinking>
21
- <!-- Follow this precise format. Be concise, dense, and use abbreviations, emojis, and Unicode characters to maximize density. -->
22
- <task_analysis_if_no_history>
23
- <!-- Only include if no conversation history exists: -->
24
- * Rewrite the <task> and its context in your own words, ensuring clarity and specificity.
25
- * Define detailed criteria for task completion if not already provided.
26
- * Identify key components, constraints, and potential challenges.
27
- * Break the <task> into smaller, manageable sub-tasks if necessary.
28
- - Each sub-task should have a clear objective, specific deliverables, and a logical sequence for progress tracking.
29
- </task_analysis_if_no_history>
30
- <success_criteria_if_no_history>
31
- <!-- Only include if no conversation history exists: -->
32
- * Specify measurable outcomes for task completion.
33
- * Define explicit quality benchmarks and performance indicators.
34
- * Note any constraints or limitations affecting the task.
35
- </success_criteria_if_no_history>
36
- <strategic_approach_if_no_history>
37
- <!-- Only include if no conversation history exists: -->
38
- * Outline a high-level strategy for solving the task.
39
- * Identify required resources, tools, or information.
40
- * Anticipate potential roadblocks and propose contingency plans.
41
- </strategic_approach_if_no_history>
42
- <last_observation>
43
- <!-- Include if conversation history exists: -->
44
- <variable>
45
- <name>...variable name...</name>
46
- <description>...concise description...</description>
47
- </variable>
48
- <result>
49
- ...concise description of the result...
50
- How does this result contribute to task progress?
51
- </result>
52
- </last_observation>
53
- <progress_analysis>
54
- <!-- Include if conversation history exists: -->
55
- * Summarize completed and failed steps concisely.
56
- * Identify and evaluate blockers or challenges.
57
- * Highlight repetitions and suggest reevaluating the approach if necessary.
58
- * Propose potential solutions or alternative strategies.
59
- </progress_analysis>
60
- <variables>
61
- <!-- Include if conversation history exists: -->
62
- * List all variable names and their current values concisely.
63
- </variables>
64
- <next_steps>
65
- * Outline immediate actions required.
66
- * Justify tool selection and parameter choices.
67
- * Use variable interpolation (e.g., `$var1$`) to minimize token generation.
68
- * Consider alternatives or reevaluate the plan if previous attempts failed.
69
- * Use the `task_complete` tool to confirm task completion.
70
- </next_steps>
71
- <taskpad>
72
- <!-- Optional: Use for notes about intermediate steps. -->
73
- <note>...</note>
74
- </taskpad>
27
+ <!-- COGNITIVE PROCESSING MATRIX -->
28
+
29
+ <!-- INITIAL TASK ANALYSIS - INCLUDE ONLY IF NO MESSAGE HISTORY EXISTS -->
30
+ <context_analysis when="no_history">
31
+ 📋 Task Decomposition based on task and history: Steps, Dependencies, Constraints
32
+ 🎯 Success Metrics: Quantifiable Outcomes
33
+ 🛠️ Resource Requirements: Tools, Data, Variables
34
+ ⚠️ Risk Assessment: Potential Failures, Mitigations
35
+ </context_analysis>
36
+
37
+ <!-- ALWAYS INCLUDE FOR ONGOING OPERATIONS -->
38
+ <execution_analysis>
39
+ <!-- ONGOING OPERATIONS -->
40
+ 🔄 Analyze Last Operation Results: Result, Impact, Effectiveness
41
+ • 📊 Progress Map: Completed%, Remaining%, Blockers
42
+ • 💾 Variable State: $var: short description of the content of each variable.
43
+ 📈 Performance Metrics: Speed, Quality, Resource Usage
44
+ </execution_analysis>
45
+
46
+ <decision_matrix>
47
+ <!-- ACTION PLANNING -->
48
+ • 🎯 Next Action: Tool Selection + Rationale
49
+ 📥 Input Parameters: Values + Variable Interpolation
50
+ • 🔄 Fallback Strategy: Alternative Approaches
51
+ ✅ Exit Criteria: Completion Conditions
52
+ </decision_matrix>
53
+
54
+ <memory_pad>
55
+ <!-- OPERATIONAL NOTES -->
56
+ 📝 Key Observations
57
+ • ⚡ Quick Access Data
58
+ </memory_pad>
75
59
  </thinking>
76
60
  ```
77
61
 
78
- 2. **Action Block**:
62
+ 2. Action Block:
79
63
  ```xml
64
+ <action>
80
65
  <tool_name>
81
- <!-- Replace `tool_name` with the name of the tool from the available tools. -->
82
- <parameter1>
83
- <!-- Use variable interpolation (e.g., `$var1$`) to pass context and minimize token generation. -->
84
- value1
85
- </parameter1>
86
- <parameter2>value2</parameter2>
66
+ <!-- PRECISE TOOL EXECUTION -->
67
+ <param1>value1</param1> <!-- Use $var$ for variable interpolation -->
68
+ <param2>value2</param2> <!-- Keep parameters minimal but sufficient -->
87
69
  </tool_name>
70
+ </action>
88
71
  ```
89
72
 
90
- ### Examples of Action Blocks
91
- - **New Task Example**:
73
+ ### Action Patterns
74
+ 1. 🆕 New Task:
92
75
  ```xml
93
- <data_analyzer>
94
- <file_path>$input_file$</file_path>
95
- <operation>validate_structure</operation>
96
- </data_analyzer>
76
+ <action>
77
+ <analyzer>
78
+ <input>$data$</input>
79
+ <mode>initialize</mode>
80
+ </analyzer>
81
+ </action>
97
82
  ```
98
83
 
99
- - **Continuing Task Example**:
84
+ 2. 🔄 Continuation:
100
85
  ```xml
101
- <memory_optimizer>
102
- <process_id>$current_process$</process_id>
103
- <target_utilization>75%</target_utilization>
104
- </memory_optimizer>
86
+ <action>
87
+ <processor>
88
+ <state>$current$</state>
89
+ <action>optimize</action>
90
+ </processor>
91
+ </action>
105
92
  ```
106
93
 
107
- - **Task Completion Example / When a task is completed**:
94
+ 3. Completion:
108
95
  ```xml
96
+ <action>
109
97
  <task_complete>
110
- <answer>Task completed successfully</answer>
98
+ <result>$final_output$</result>
111
99
  </task_complete>
100
+ </action>
112
101
  ```
113
102
 
114
- ### Available Tools
115
- {tools}
103
+ ### Operational Parameters
104
+ 🛠️ Tools: {tools}
105
+ 🌐 Environment: {environment}
116
106
 
117
- ### Environment Details
118
- {environment}
107
+ ### Execution Guidelines
108
+ 1. 🎯 Maintain laser focus on task objectives
109
+ 2. 📊 Use data-driven decision making
110
+ 3. 🔄 Implement feedback loops for continuous optimization
111
+ 4. ⚡ Maximize efficiency through variable interpolation
112
+ 5. 🔍 Monitor and validate each action's impact
113
+ 6. 🛑 Fail fast and adapt when encountering blockers
114
+ 7. ✅ Verify completion criteria rigorously
119
115
  """
116
+
@@ -62,6 +62,7 @@ class ReplaceInFileTool(Tool):
62
62
  "Updates sections of content in an existing file using SEARCH/REPLACE blocks. "
63
63
  "If exact matches are not found, the tool attempts to find similar sections based on similarity. "
64
64
  "Returns the updated content or an error."
65
+ "⚠️ THIS TOOL MUST BE USED IN PRIORITY TO UPDATE AN EXISTING FILE."
65
66
  )
66
67
  need_validation: bool = True
67
68
 
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,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: quantalogic
3
- Version: 0.32.0
3
+ Version: 0.33.1
4
4
  Summary: QuantaLogic ReAct Agents
5
5
  Author: Raphaël MANSUY
6
6
  Author-email: raphael.mansuy@gmail.com
@@ -1,24 +1,24 @@
1
1
  quantalogic/__init__.py,sha256=Su8CnOEdqKu4zTytjiP9P5olg-oIDuUA3fMWM1WUdRY,925
2
- quantalogic/agent.py,sha256=VqxM_VizDpwH3SLAnoSFMjH4a1ijBVOHaI6rXahHP7M,33289
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=KB-DGi52As8M96eUs1N_vgNqKIFtqv_H8NTOd3TLTgQ,2163
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
11
- quantalogic/generative_model.py,sha256=ut_BFy4BqDxNqUXVbM8e_C_CzwNuJkvGWRsbpbKaees,13423
11
+ quantalogic/generative_model.py,sha256=K0lbtZuuQrI5McPC4pUZ_FdjL32dAy1vM18egmPGaa8,13638
12
12
  quantalogic/get_model_info.py,sha256=_9Nb9JQ09HZzT-_gZUSvl4Er7uCXs5ys36sIBa-8DXA,3005
13
13
  quantalogic/interactive_text_editor.py,sha256=1vW4poJl7SItRGEeGQgtCFcmRDXmfCM8PE-uBtDBJuE,16658
14
- quantalogic/llm.py,sha256=Nk2Dn1lJh1-323Fs7ADfR9ov_eAoJOEEnzyGswZSbJI,5460
14
+ quantalogic/llm.py,sha256=98osu6LmjuYw4g-CDIp1SJkYlfoHNwdvq6yJqI-K5NA,5692
15
15
  quantalogic/main.py,sha256=__-4pX2pgoSFvt-aLdp6Qlrq55_SrwP_l8u2uTaQbjg,9262
16
16
  quantalogic/memory.py,sha256=zbtRuM05jaS2lJll-92dt5JfYVLERnF_m_9xqp2x-k0,6304
17
17
  quantalogic/model_info.py,sha256=j7QqvjEFQDGpDOgQs8uTkVyI3a50Oa_nrsQjyxizTLc,272
18
- quantalogic/model_info_list.py,sha256=bJu2ohQFgZOwmcqydfh5oi5R1G8ZY7jtQlERsR1Z47s,1816
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=M-7rCaQoylnwxedhvy7VmQdgBG6TT1vmcf8_UzPTyY0,4035
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
@@ -62,7 +62,7 @@ quantalogic/tools/python_tool.py,sha256=70HLbfU2clOBgj4axDOtIKzXwEBMNGEAX1nGSf-K
62
62
  quantalogic/tools/read_file_block_tool.py,sha256=FTcDAUOOPQOvWRjnRI6nMI1Upus90klR4PC0pbPP_S8,5266
63
63
  quantalogic/tools/read_file_tool.py,sha256=l6k-SOIV9krpXAmUTkxzua51S-KHgzGqkcDlD5AD8K0,2710
64
64
  quantalogic/tools/read_html_tool.py,sha256=Vq2rHY8a36z1-4rN6c_kYjPUTQ4I2UT154PMpaoWSkA,11139
65
- quantalogic/tools/replace_in_file_tool.py,sha256=wC7OlV8UpR5JGCD3VGUWlBEE6fMdGEDRcwE22cDSG2E,13764
65
+ quantalogic/tools/replace_in_file_tool.py,sha256=95GqcOC0sZzbWHWzYDVwq7rEwChlOgKxb5cxKk-Y7W8,13844
66
66
  quantalogic/tools/ripgrep_tool.py,sha256=sRzHaWac9fa0cCGhECJN04jw_Ko0O3u45KDWzMIYcvY,14291
67
67
  quantalogic/tools/search_definition_names.py,sha256=ui0304UgUke6Ca-H3-S4JP9TsdHhRCR5uk9kPuobIDA,18769
68
68
  quantalogic/tools/serpapi_search_tool.py,sha256=sX-Noch77kGP2XiwislPNFyy3_4TH6TwMK6C81L3q9Y,5316
@@ -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=8yDxvKzAEnefNwUAR-wjerMDOj5T5cxak4WPIA83SBw,11516
93
+ quantalogic/xml_parser.py,sha256=iUQZC4a8uqBsP7RAZaY0Ed9k8TCd-ImuF41eF7MR2WM,11615
94
94
  quantalogic/xml_tool_parser.py,sha256=Vz4LEgDbelJynD1siLOVkJ3gLlfHsUk65_gCwbYJyGc,3784
95
- quantalogic-0.32.0.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
96
- quantalogic-0.32.0.dist-info/METADATA,sha256=w5kJUS98ou0kVeY9fy-WImrW-W9wWZFOXmIQx4xK-_M,23461
97
- quantalogic-0.32.0.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
98
- quantalogic-0.32.0.dist-info/entry_points.txt,sha256=h74O_Q3qBRCrDR99qvwB4BpBGzASPUIjCfxHq6Qnups,183
99
- quantalogic-0.32.0.dist-info/RECORD,,
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,,