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.
- quantalogic/__init__.py +20 -0
- quantalogic/agent.py +638 -0
- quantalogic/agent_config.py +138 -0
- quantalogic/coding_agent.py +83 -0
- quantalogic/event_emitter.py +223 -0
- quantalogic/generative_model.py +226 -0
- quantalogic/interactive_text_editor.py +190 -0
- quantalogic/main.py +185 -0
- quantalogic/memory.py +217 -0
- quantalogic/model_names.py +19 -0
- quantalogic/print_event.py +66 -0
- quantalogic/prompts.py +99 -0
- quantalogic/server/__init__.py +3 -0
- quantalogic/server/agent_server.py +633 -0
- quantalogic/server/models.py +60 -0
- quantalogic/server/routes.py +117 -0
- quantalogic/server/state.py +199 -0
- quantalogic/server/static/js/event_visualizer.js +430 -0
- quantalogic/server/static/js/quantalogic.js +571 -0
- quantalogic/server/templates/index.html +134 -0
- quantalogic/tool_manager.py +68 -0
- quantalogic/tools/__init__.py +46 -0
- quantalogic/tools/agent_tool.py +88 -0
- quantalogic/tools/download_http_file_tool.py +64 -0
- quantalogic/tools/edit_whole_content_tool.py +70 -0
- quantalogic/tools/elixir_tool.py +240 -0
- quantalogic/tools/execute_bash_command_tool.py +116 -0
- quantalogic/tools/input_question_tool.py +57 -0
- quantalogic/tools/language_handlers/__init__.py +21 -0
- quantalogic/tools/language_handlers/c_handler.py +33 -0
- quantalogic/tools/language_handlers/cpp_handler.py +33 -0
- quantalogic/tools/language_handlers/go_handler.py +33 -0
- quantalogic/tools/language_handlers/java_handler.py +37 -0
- quantalogic/tools/language_handlers/javascript_handler.py +42 -0
- quantalogic/tools/language_handlers/python_handler.py +29 -0
- quantalogic/tools/language_handlers/rust_handler.py +33 -0
- quantalogic/tools/language_handlers/scala_handler.py +33 -0
- quantalogic/tools/language_handlers/typescript_handler.py +42 -0
- quantalogic/tools/list_directory_tool.py +123 -0
- quantalogic/tools/llm_tool.py +119 -0
- quantalogic/tools/markitdown_tool.py +105 -0
- quantalogic/tools/nodejs_tool.py +515 -0
- quantalogic/tools/python_tool.py +469 -0
- quantalogic/tools/read_file_block_tool.py +140 -0
- quantalogic/tools/read_file_tool.py +79 -0
- quantalogic/tools/replace_in_file_tool.py +300 -0
- quantalogic/tools/ripgrep_tool.py +353 -0
- quantalogic/tools/search_definition_names.py +419 -0
- quantalogic/tools/task_complete_tool.py +35 -0
- quantalogic/tools/tool.py +146 -0
- quantalogic/tools/unified_diff_tool.py +387 -0
- quantalogic/tools/write_file_tool.py +97 -0
- quantalogic/utils/__init__.py +17 -0
- quantalogic/utils/ask_user_validation.py +12 -0
- quantalogic/utils/download_http_file.py +77 -0
- quantalogic/utils/get_coding_environment.py +15 -0
- quantalogic/utils/get_environment.py +26 -0
- quantalogic/utils/get_quantalogic_rules_content.py +19 -0
- quantalogic/utils/git_ls.py +121 -0
- quantalogic/utils/read_file.py +54 -0
- quantalogic/utils/read_http_text_content.py +101 -0
- quantalogic/xml_parser.py +242 -0
- quantalogic/xml_tool_parser.py +99 -0
- quantalogic-0.2.0.dist-info/LICENSE +201 -0
- quantalogic-0.2.0.dist-info/METADATA +1034 -0
- quantalogic-0.2.0.dist-info/RECORD +68 -0
- quantalogic-0.2.0.dist-info/WHEEL +4 -0
- 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)
|