quantalogic 0.2.0__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.
Files changed (68) hide show
  1. quantalogic/__init__.py +20 -0
  2. quantalogic/agent.py +638 -0
  3. quantalogic/agent_config.py +138 -0
  4. quantalogic/coding_agent.py +83 -0
  5. quantalogic/event_emitter.py +223 -0
  6. quantalogic/generative_model.py +226 -0
  7. quantalogic/interactive_text_editor.py +190 -0
  8. quantalogic/main.py +185 -0
  9. quantalogic/memory.py +217 -0
  10. quantalogic/model_names.py +19 -0
  11. quantalogic/print_event.py +66 -0
  12. quantalogic/prompts.py +99 -0
  13. quantalogic/server/__init__.py +3 -0
  14. quantalogic/server/agent_server.py +633 -0
  15. quantalogic/server/models.py +60 -0
  16. quantalogic/server/routes.py +117 -0
  17. quantalogic/server/state.py +199 -0
  18. quantalogic/server/static/js/event_visualizer.js +430 -0
  19. quantalogic/server/static/js/quantalogic.js +571 -0
  20. quantalogic/server/templates/index.html +134 -0
  21. quantalogic/tool_manager.py +68 -0
  22. quantalogic/tools/__init__.py +46 -0
  23. quantalogic/tools/agent_tool.py +88 -0
  24. quantalogic/tools/download_http_file_tool.py +64 -0
  25. quantalogic/tools/edit_whole_content_tool.py +70 -0
  26. quantalogic/tools/elixir_tool.py +240 -0
  27. quantalogic/tools/execute_bash_command_tool.py +116 -0
  28. quantalogic/tools/input_question_tool.py +57 -0
  29. quantalogic/tools/language_handlers/__init__.py +21 -0
  30. quantalogic/tools/language_handlers/c_handler.py +33 -0
  31. quantalogic/tools/language_handlers/cpp_handler.py +33 -0
  32. quantalogic/tools/language_handlers/go_handler.py +33 -0
  33. quantalogic/tools/language_handlers/java_handler.py +37 -0
  34. quantalogic/tools/language_handlers/javascript_handler.py +42 -0
  35. quantalogic/tools/language_handlers/python_handler.py +29 -0
  36. quantalogic/tools/language_handlers/rust_handler.py +33 -0
  37. quantalogic/tools/language_handlers/scala_handler.py +33 -0
  38. quantalogic/tools/language_handlers/typescript_handler.py +42 -0
  39. quantalogic/tools/list_directory_tool.py +123 -0
  40. quantalogic/tools/llm_tool.py +119 -0
  41. quantalogic/tools/markitdown_tool.py +105 -0
  42. quantalogic/tools/nodejs_tool.py +515 -0
  43. quantalogic/tools/python_tool.py +469 -0
  44. quantalogic/tools/read_file_block_tool.py +140 -0
  45. quantalogic/tools/read_file_tool.py +79 -0
  46. quantalogic/tools/replace_in_file_tool.py +300 -0
  47. quantalogic/tools/ripgrep_tool.py +353 -0
  48. quantalogic/tools/search_definition_names.py +419 -0
  49. quantalogic/tools/task_complete_tool.py +35 -0
  50. quantalogic/tools/tool.py +146 -0
  51. quantalogic/tools/unified_diff_tool.py +387 -0
  52. quantalogic/tools/write_file_tool.py +97 -0
  53. quantalogic/utils/__init__.py +17 -0
  54. quantalogic/utils/ask_user_validation.py +12 -0
  55. quantalogic/utils/download_http_file.py +77 -0
  56. quantalogic/utils/get_coding_environment.py +15 -0
  57. quantalogic/utils/get_environment.py +26 -0
  58. quantalogic/utils/get_quantalogic_rules_content.py +19 -0
  59. quantalogic/utils/git_ls.py +121 -0
  60. quantalogic/utils/read_file.py +54 -0
  61. quantalogic/utils/read_http_text_content.py +101 -0
  62. quantalogic/xml_parser.py +242 -0
  63. quantalogic/xml_tool_parser.py +99 -0
  64. quantalogic-0.2.0.dist-info/LICENSE +201 -0
  65. quantalogic-0.2.0.dist-info/METADATA +1034 -0
  66. quantalogic-0.2.0.dist-info/RECORD +68 -0
  67. quantalogic-0.2.0.dist-info/WHEEL +4 -0
  68. quantalogic-0.2.0.dist-info/entry_points.txt +3 -0
@@ -0,0 +1,190 @@
1
+ """Utility functions for the QuantaLogic AI Assistant."""
2
+
3
+ from prompt_toolkit import PromptSession
4
+ from prompt_toolkit.auto_suggest import AutoSuggestFromHistory
5
+ from prompt_toolkit.history import InMemoryHistory
6
+ from prompt_toolkit.key_binding import KeyBindings
7
+ from rich.console import Console
8
+ from rich.panel import Panel
9
+
10
+
11
+ class InputHistoryManager:
12
+ """Manages the history of input states for undo functionality."""
13
+
14
+ def __init__(self):
15
+ """Initialize the InputHistoryManager with an empty history stack and current state."""
16
+ self.history_stack = []
17
+ self.current_state = []
18
+
19
+ def push_state(self, lines):
20
+ """Push the current state to the history stack and update the current state.
21
+
22
+ Args:
23
+ lines (list): The current lines of input to be saved in the history.
24
+ """
25
+ self.history_stack.append(self.current_state.copy())
26
+ self.current_state = lines.copy()
27
+
28
+ def undo(self, lines):
29
+ """Revert to the previous state from the history stack.
30
+
31
+ Args:
32
+ lines (list): The current lines of input to be updated to the previous state.
33
+
34
+ Returns:
35
+ bool: True if undo was successful, False otherwise.
36
+ """
37
+ if self.history_stack:
38
+ self.current_state = self.history_stack.pop()
39
+ lines[:] = self.current_state
40
+ return True
41
+ return False
42
+
43
+
44
+ def handle_edit_command(lines, args, console, session, history_manager):
45
+ """Handle the 'edit' command to modify a specific line.
46
+
47
+ Args:
48
+ lines (list): The current lines of input.
49
+ args (list): The arguments provided with the command.
50
+ console (Console): The console object for output.
51
+ session (PromptSession): The prompt session for user input.
52
+ history_manager (InputHistoryManager): The history manager for state tracking.
53
+ """
54
+ try:
55
+ edit_line_num = int(args[0]) - 1
56
+ if 0 <= edit_line_num < len(lines):
57
+ console.print(f"[bold]Editing Line {edit_line_num + 1}:[/bold] {lines[edit_line_num]}")
58
+ new_line = session.prompt("New content: ")
59
+ history_manager.push_state(lines)
60
+ lines[edit_line_num] = new_line
61
+ else:
62
+ console.print("[red]Invalid line number.[/red]")
63
+ except (ValueError, IndexError):
64
+ console.print("[red]Invalid edit command. Usage: edit <line_number>[/red]")
65
+
66
+
67
+ def handle_delete_command(lines, args, console, history_manager):
68
+ """Handle the 'delete' command to remove a specific line.
69
+
70
+ Args:
71
+ lines (list): The current lines of input.
72
+ args (list): The arguments provided with the command.
73
+ console (Console): The console object for output.
74
+ history_manager (InputHistoryManager): The history manager for state tracking.
75
+ """
76
+ try:
77
+ delete_line_num = int(args[0]) - 1
78
+ if 0 <= delete_line_num < len(lines):
79
+ history_manager.push_state(lines)
80
+ lines.pop(delete_line_num)
81
+ console.print(f"[bold]Deleted Line {delete_line_num + 1}[/bold]")
82
+ else:
83
+ console.print("[red]Invalid line number.[/red]")
84
+ except (ValueError, IndexError):
85
+ console.print("[red]Invalid delete command. Usage: delete <line_number>[/red]")
86
+
87
+
88
+ def handle_replace_command(lines, args, console, history_manager):
89
+ """Handle the 'replace' command to search and replace text in all lines.
90
+
91
+ Args:
92
+ lines (list): The current lines of input.
93
+ args (list): The arguments provided with the command.
94
+ console (Console): The console object for output.
95
+ history_manager (InputHistoryManager): The history manager for state tracking.
96
+ """
97
+ try:
98
+ search_str, replace_str = args
99
+ history_manager.push_state(lines)
100
+ for i in range(len(lines)):
101
+ lines[i] = lines[i].replace(search_str, replace_str)
102
+ console.print("[bold]Search and replace completed.[/bold]")
103
+ except ValueError:
104
+ console.print("[red]Invalid replace command. Usage: replace <search_str> <replace_str>[/red]")
105
+
106
+
107
+ commands = {"edit": handle_edit_command, "delete": handle_delete_command, "replace": handle_replace_command}
108
+
109
+
110
+ def handle_command(line, lines, console, session, history_manager):
111
+ """Handle a command entered by the user.
112
+
113
+ Args:
114
+ line (str): The command line entered by the user.
115
+ lines (list): The current lines of input.
116
+ console (Console): The console object for output.
117
+ session (PromptSession): The prompt session for user input.
118
+ history_manager (InputHistoryManager): The history manager for state tracking.
119
+
120
+ Returns:
121
+ bool: True if the command was handled, False otherwise.
122
+ """
123
+ parts = line.split()
124
+ if not parts:
125
+ return False
126
+ command = parts[0]
127
+ args = parts[1:]
128
+ if command in commands:
129
+ commands[command](lines, args, console, session, history_manager)
130
+ return True
131
+ return False
132
+
133
+
134
+ def get_multiline_input(console: Console) -> str:
135
+ """Get multiline input from the user with enhanced UX.
136
+
137
+ Args:
138
+ console (Console): The console object for output.
139
+
140
+ Returns:
141
+ str: The multiline input provided by the user.
142
+ """
143
+ console.print(
144
+ Panel(
145
+ "Enter your task. Press [bold]Enter[/bold] twice to submit.\n"
146
+ "Available commands:\n"
147
+ " edit <line_number> - Edit a specific line\n"
148
+ " delete <line_number> - Delete a specific line\n"
149
+ " replace <search_str> <replace_str> - Replace text in all lines",
150
+ title="Multi-line Input",
151
+ border_style="blue",
152
+ )
153
+ )
154
+
155
+ lines = []
156
+ history_manager = InputHistoryManager()
157
+ blank_lines = 0
158
+ line_number = 1
159
+
160
+ bindings = KeyBindings()
161
+
162
+ @bindings.add("c-z")
163
+ def _(event):
164
+ if history_manager.undo(lines):
165
+ console.print("[bold]Undo successful.[/bold]")
166
+
167
+ session = PromptSession(history=InMemoryHistory(), auto_suggest=AutoSuggestFromHistory(), key_bindings=bindings)
168
+
169
+ try:
170
+ while True:
171
+ prompt_text = f"{line_number:>3}: "
172
+ line = session.prompt(prompt_text, rprompt="Press Enter twice to submit")
173
+
174
+ if line.strip() == "":
175
+ blank_lines += 1
176
+ if blank_lines == 2:
177
+ break
178
+ else:
179
+ blank_lines = 0
180
+ if not handle_command(line, lines, console, session, history_manager):
181
+ history_manager.push_state(lines)
182
+ lines.append(line)
183
+ line_number += 1
184
+ except EOFError:
185
+ console.print("\n[bold]Input terminated.[/bold]")
186
+ except KeyboardInterrupt:
187
+ console.print("\n[bold]Input cancelled by user.[/bold]")
188
+ return ""
189
+
190
+ return "\n".join(lines)
quantalogic/main.py ADDED
@@ -0,0 +1,185 @@
1
+ #!/usr/bin/env python
2
+ """Main module for the QuantaLogic agent."""
3
+
4
+ # Standard library imports
5
+ import sys
6
+ from typing import Optional
7
+
8
+ # Third-party imports
9
+ import click
10
+ from rich.console import Console
11
+ from rich.panel import Panel
12
+ from rich.prompt import Confirm
13
+
14
+ from quantalogic.agent import Agent
15
+
16
+ # Local application imports
17
+ from quantalogic.agent_config import (
18
+ MODEL_NAME,
19
+ create_coding_agent,
20
+ create_full_agent,
21
+ create_interpreter_agent,
22
+ create_orchestrator_agent,
23
+ )
24
+ from quantalogic.interactive_text_editor import get_multiline_input
25
+ from quantalogic.print_event import console_print_events
26
+
27
+ AGENT_MODES = ["code", "basic", "interpreter", "full", "code-basic"]
28
+
29
+
30
+ def create_agent_for_mode(mode: str, model_name: str) -> Agent:
31
+ """Create an agent based on the specified mode."""
32
+ if mode == "code":
33
+ return create_coding_agent(model_name, basic=False)
34
+ if mode == "code-basic":
35
+ return create_coding_agent(model_name, basic=True)
36
+ elif mode == "basic":
37
+ return create_orchestrator_agent(model_name)
38
+ elif mode == "full":
39
+ return create_full_agent(model_name)
40
+ elif mode == "interpreter":
41
+ return create_interpreter_agent(model_name)
42
+ else:
43
+ raise ValueError(f"Unknown agent mode: {mode}")
44
+
45
+
46
+ def switch_verbose(verbose_mode: bool) -> None:
47
+ pass
48
+
49
+
50
+ def get_task_from_file(file_path: str) -> str:
51
+ """Get task content from specified file."""
52
+ try:
53
+ with open(file_path, encoding="utf-8") as f:
54
+ return f.read().strip()
55
+ except FileNotFoundError:
56
+ raise FileNotFoundError(f"Error: File '{file_path}' not found.")
57
+ except PermissionError:
58
+ raise PermissionError(f"Error: Permission denied when reading '{file_path}'.")
59
+ except Exception as e:
60
+ raise Exception(f"Unexpected error reading file: {e}")
61
+
62
+
63
+ def display_welcome_message(console: Console, model_name: str) -> None:
64
+ """Display the welcome message and instructions."""
65
+ version = get_version()
66
+ console.print(
67
+ Panel.fit(
68
+ f"[bold cyan]🌟 Welcome to QuantaLogic AI Assistant v{version} ! 🌟[/bold cyan]\n\n"
69
+ "[green]🎯 How to Use:[/green]\n\n"
70
+ "1. [bold]Describe your task[/bold]: Tell the AI what you need help with.\n"
71
+ ' - Example: "Write a Python function to calculate Fibonacci numbers."\n'
72
+ ' - Example: "Explain quantum computing in simple terms."\n'
73
+ ' - Example: "Generate a list of 10 creative project ideas."\n'
74
+ ' - Example: "Create a project plan for a new AI startup.\n'
75
+ ' - Example: "Help me debug this Python code."\n\n'
76
+ "2. [bold]Submit your task[/bold]: Press [bold]Enter[/bold] twice to send your request.\n\n"
77
+ "3. [bold]Exit the app[/bold]: Leave the input blank and press [bold]Enter[/bold] twice to close the assistant.\n\n"
78
+ f"[yellow]ℹ️ System Info:[/yellow]\n\n"
79
+ f"- Version: {get_version()}\n"
80
+ f"- Model: {model_name}\n\n"
81
+ "[bold magenta]💡 Pro Tips:[/bold magenta]\n\n"
82
+ "- Be as specific as possible in your task description to get the best results!\n"
83
+ "- Use clear and concise language when describing your task\n"
84
+ "- For coding tasks, include relevant context and requirements\n"
85
+ "- The AI can handle complex tasks - don't hesitate to ask challenging questions!",
86
+ title="[bold]Instructions[/bold]",
87
+ border_style="blue",
88
+ )
89
+ )
90
+
91
+
92
+ def get_version() -> str:
93
+ """Get the current version of the package."""
94
+ return "QuantaLogic version: 1.0.0"
95
+
96
+
97
+ @click.group(invoke_without_command=True)
98
+ @click.option("--version", is_flag=True, help="Show version information.")
99
+ @click.pass_context
100
+ def cli(ctx: click.Context, version: bool) -> None:
101
+ """QuantaLogic AI Assistant - A powerful AI tool for various tasks."""
102
+ if version:
103
+ console = Console()
104
+ console.print(f"QuantaLogic version: {get_version()}")
105
+ sys.exit(0)
106
+ if ctx.invoked_subcommand is None:
107
+ ctx.invoke(task)
108
+
109
+
110
+ @cli.command()
111
+ @click.option("--file", type=click.Path(exists=True), help="Path to task file.")
112
+ @click.option(
113
+ "--model-name",
114
+ default=MODEL_NAME,
115
+ help='Specify the model to use (litellm format, e.g. "openrouter/deepseek-chat").',
116
+ )
117
+ @click.option("--verbose", is_flag=True, help="Enable verbose output.")
118
+ @click.option("--mode", type=click.Choice(AGENT_MODES), default="code", help="Agent mode (code/search/full).")
119
+ @click.argument("task", required=False)
120
+ def task(file: Optional[str], model_name: str, verbose: bool, mode: str, task: Optional[str]) -> None:
121
+ """Execute a task with the QuantaLogic AI Assistant."""
122
+ console = Console()
123
+ switch_verbose(verbose)
124
+
125
+ try:
126
+ if file:
127
+ task_content = get_task_from_file(file)
128
+ else:
129
+ if task:
130
+ task_content = task
131
+ else:
132
+ display_welcome_message(console, model_name)
133
+ task_content = get_multiline_input(console).strip()
134
+ if not task_content:
135
+ console.print("[yellow]No task provided. Exiting...[/yellow]")
136
+ sys.exit(2)
137
+
138
+ if model_name != MODEL_NAME:
139
+ console.print(
140
+ Panel.fit(
141
+ f"[bold]Task to be submitted:[/bold]\n{task_content}",
142
+ title="[bold]Task Preview[/bold]",
143
+ border_style="blue",
144
+ )
145
+ )
146
+ if not Confirm.ask("[bold]Are you sure you want to submit this task?[/bold]"):
147
+ console.print("[yellow]Task submission cancelled. Exiting...[/yellow]")
148
+ sys.exit(0)
149
+
150
+ agent = create_agent_for_mode(mode, model_name)
151
+ agent.event_emitter.on(
152
+ [
153
+ "task_complete",
154
+ "task_think_start",
155
+ "task_think_end",
156
+ "tool_execution_start",
157
+ "tool_execution_end",
158
+ "error_max_iterations_reached",
159
+ "memory_full",
160
+ "memory_compacted",
161
+ "memory_summary",
162
+ ],
163
+ console_print_events,
164
+ )
165
+
166
+ result = agent.solve_task(task=task_content, max_iterations=300)
167
+
168
+ console.print(
169
+ Panel.fit(
170
+ f"[bold]Task Result:[/bold]\n{result}", title="[bold]Execution Output[/bold]", border_style="green"
171
+ )
172
+ )
173
+
174
+ except Exception as e:
175
+ console.print(f"[red]{str(e)}[/red]")
176
+ sys.exit(1)
177
+
178
+
179
+ def main():
180
+ """Entry point for the quantalogic CLI."""
181
+ cli()
182
+
183
+
184
+ if __name__ == "__main__":
185
+ main()
quantalogic/memory.py ADDED
@@ -0,0 +1,217 @@
1
+ """Memory for the agent."""
2
+
3
+ from pydantic import BaseModel
4
+
5
+
6
+ class Message(BaseModel):
7
+ """Represents a message in the agent's memory."""
8
+
9
+ role: str
10
+ content: str
11
+
12
+
13
+ class AgentMemory:
14
+ """Memory for the agent."""
15
+
16
+ def __init__(self):
17
+ """Initialize the agent memory."""
18
+ self.memory: list[Message] = []
19
+
20
+ def add(self, message: Message):
21
+ """Add a message to the agent memory.
22
+
23
+ Args:
24
+ message (Message): The message to add to memory.
25
+ """
26
+ self.memory.append(message)
27
+
28
+ def reset(self):
29
+ """Reset the agent memory."""
30
+ self.memory.clear()
31
+
32
+ def compact(self, n: int = 2):
33
+ """Compact the memory to keep only essential messages.
34
+
35
+ This method keeps:
36
+ - The system message (if present)
37
+ - First two pairs of user-assistant messages
38
+ - Last n pairs of user-assistant messages (default: 2)
39
+
40
+ Args:
41
+ n (int): Number of last message pairs to keep. Defaults to 2.
42
+ """
43
+ if not self.memory:
44
+ return
45
+
46
+ # Keep system message if present
47
+ compacted_memory = []
48
+ if self.memory and self.memory[0].role == "system":
49
+ compacted_memory.append(self.memory[0])
50
+ messages = self.memory[1:]
51
+ else:
52
+ messages = self.memory[:]
53
+
54
+ # Extract user-assistant pairs
55
+ pairs = []
56
+ i = 0
57
+ while i < len(messages) - 1:
58
+ if messages[i].role == "user" and messages[i + 1].role == "assistant":
59
+ pairs.append((messages[i], messages[i + 1]))
60
+ i += 2
61
+
62
+ # Keep first two and last n pairs
63
+ total_pairs_to_keep = 2 + n
64
+ if len(pairs) <= total_pairs_to_keep:
65
+ for user_msg, assistant_msg in pairs:
66
+ compacted_memory.extend([user_msg, assistant_msg])
67
+ else:
68
+ # Add first two pairs
69
+ for pair in pairs[:2]:
70
+ compacted_memory.extend(pair)
71
+ # Add last n pairs
72
+ for pair in pairs[-n:]:
73
+ compacted_memory.extend(pair)
74
+
75
+ self.memory = compacted_memory
76
+
77
+
78
+ class VariableMemory:
79
+ """Memory for a variable."""
80
+
81
+ def __init__(self):
82
+ """Initialize the variable memory."""
83
+ self.memory: dict[str, tuple[str, str]] = {}
84
+ self.counter: int = 0
85
+
86
+ def add(self, value: str) -> str:
87
+ """Add a value to the variable memory.
88
+
89
+ Args:
90
+ value (str): The value to add to memory.
91
+
92
+ Returns:
93
+ str: The key associated with the added value.
94
+ """
95
+ self.counter += 1
96
+ key = f"var{self.counter}"
97
+ self.memory[key] = (key, value)
98
+ return key
99
+
100
+ def reset(self):
101
+ """Reset the variable memory."""
102
+ self.memory.clear()
103
+ self.counter = 0
104
+
105
+ def get(self, key: str, default: str | None = None) -> str | None:
106
+ """Get a value from the variable memory.
107
+
108
+ Args:
109
+ key (str): The key of the value to retrieve.
110
+ default (str, optional): Default value if key is not found. Defaults to None.
111
+
112
+ Returns:
113
+ str | None: The value associated with the key, or default if not found.
114
+ """
115
+ return self.memory.get(key, default)[1] if key in self.memory else default
116
+
117
+ def __getitem__(self, key: str) -> str:
118
+ """Get a value using dictionary-style access.
119
+
120
+ Args:
121
+ key (str): The key of the value to retrieve.
122
+
123
+ Returns:
124
+ str: The value associated with the key.
125
+
126
+ Raises:
127
+ KeyError: If the key is not found.
128
+ """
129
+ return self.memory[key][1]
130
+
131
+ def __setitem__(self, key: str, value: str):
132
+ """Set a value using dictionary-style assignment.
133
+
134
+ Args:
135
+ key (str): The key to set.
136
+ value (str): The value to associate with the key.
137
+ """
138
+ self.memory[key] = (key, value)
139
+
140
+ def __delitem__(self, key: str):
141
+ """Delete a key-value pair using dictionary-style deletion.
142
+
143
+ Args:
144
+ key (str): The key to delete.
145
+
146
+ Raises:
147
+ KeyError: If the key is not found.
148
+ """
149
+ del self.memory[key]
150
+
151
+ def __contains__(self, key: str) -> bool:
152
+ """Check if a key exists in the memory.
153
+
154
+ Args:
155
+ key (str): The key to check.
156
+
157
+ Returns:
158
+ bool: True if the key exists, False otherwise.
159
+ """
160
+ return key in self.memory
161
+
162
+ def __len__(self) -> int:
163
+ """Get the number of items in the memory.
164
+
165
+ Returns:
166
+ int: Number of items in the memory.
167
+ """
168
+ return len(self.memory)
169
+
170
+ def keys(self):
171
+ """Return a view of the memory's keys.
172
+
173
+ Returns:
174
+ dict_keys: A view of the memory's keys.
175
+ """
176
+ return self.memory.keys()
177
+
178
+ def values(self):
179
+ """Return a view of the memory's values.
180
+
181
+ Returns:
182
+ dict_values: A view of the memory's values.
183
+ """
184
+ return (value[1] for value in self.memory.values())
185
+
186
+ def items(self):
187
+ """Return a view of the memory's items.
188
+
189
+ Returns:
190
+ dict_items: A view of the memory's items.
191
+ """
192
+ return ((key, value[1]) for key, value in self.memory.items())
193
+
194
+ def pop(self, key: str, default: str | None = None) -> str | None:
195
+ """Remove and return a value for a key.
196
+
197
+ Args:
198
+ key (str): The key to remove.
199
+ default (str, optional): Default value if key is not found. Defaults to None.
200
+
201
+ Returns:
202
+ str | None: The value associated with the key, or default if not found.
203
+ """
204
+ return self.memory.pop(key, (None, default))[1] if default is not None else self.memory.pop(key)[1]
205
+
206
+ def update(self, other: dict[str, str] | None = None, **kwargs):
207
+ """Update the memory with key-value pairs from another dictionary.
208
+
209
+ Args:
210
+ other (dict, optional): Dictionary to update from. Defaults to None.
211
+ **kwargs: Additional key-value pairs to update.
212
+ """
213
+ if other is not None:
214
+ for key, value in other.items():
215
+ self.memory[key] = (key, value)
216
+ for key, value in kwargs.items():
217
+ self.memory[key] = (key, value)
@@ -0,0 +1,19 @@
1
+ # Model names for reference
2
+ # MODEL_NAME = "ollama/qwen2.5-coder:7b"
3
+ # MODEL_NAME = "lm_studio/qwen2.5-coder-14b-instruct-mlx@8bit"
4
+ # MODEL_NAME = "lm_studio/qwen2.5-coder-3b-instruct-mlx"
5
+ # MODEL_NAME = "lm_studio/qwen2-7b-instruct"
6
+ # MODEL_NAME = "gpt-4o-mini"
7
+ # MODEL_NAME = "gpt-4o"
8
+ # MODEL_NAME = "lm_studio/qwen2-7b-instruct"
9
+ # MODEL_NAME = "lm_studio/llama-3.2-3b-instruct"
10
+ # MODEL_NAME = "lm_studio/llama-3.3-70b-instruct"
11
+ # MODEL_NAME = "bedrock/amazon.nova-pro-v1:0"
12
+ # MODEL_NAME = "bedrock/amazon.nova-micro-v1:0"
13
+ # MODEL_NAME = "bedrock/amazon.nova-lite-v1:0"
14
+ # MODEL_NAME = "mistral/mistral-large-2411"
15
+ # MODEL_NAME = "openrouter/meta-llama/llama-3.2-90b-vision-instruct:free"
16
+ # MODEL_NAME = "openrouter/google/gemini-2.0-flash-thinking-exp:free"
17
+ # MODEL_NAME = "openrouter/google/gemini-2.0-flash-exp:free"
18
+ # MODEL_NAME = "openrouter/qwen/qwen-2.5-coder-32b-instruct"
19
+ # MODEL_NAME = "openrouter/anthropic/claude-3.5-sonnet"
@@ -0,0 +1,66 @@
1
+ """Print events with rich formatting."""
2
+
3
+ from rich import box
4
+ from rich.console import Console
5
+ from rich.panel import Panel
6
+ from rich.tree import Tree
7
+
8
+
9
+ def console_print_events(event: str, data: dict[str, any] = 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
+ """
16
+ console = Console()
17
+
18
+ # Define panel title with enhanced styling
19
+ panel_title = f"[bold cyan]Event: {event}[/bold cyan]"
20
+
21
+ if not data:
22
+ # Display a friendly message when no data is available
23
+ 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),
30
+ )
31
+ )
32
+ return
33
+
34
+ # Function to render nested dictionaries as a tree
35
+ def render_tree(data: dict[str, any], tree: Tree):
36
+ for key, value in data.items():
37
+ if isinstance(value, dict):
38
+ branch = tree.add(f"[bold magenta]{key}[/bold magenta]")
39
+ render_tree(value, branch)
40
+ elif isinstance(value, list):
41
+ branch = tree.add(f"[bold magenta]{key}[/bold magenta]")
42
+ for index, item in enumerate(value, start=1):
43
+ if isinstance(item, dict):
44
+ sub_branch = branch.add(f"[cyan]Item {index}[/cyan]")
45
+ render_tree(item, sub_branch)
46
+ else:
47
+ branch.add(f"[green]{item}[/green]")
48
+ 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")
53
+
54
+ render_tree(data, tree)
55
+
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)