quantalogic 0.33.4__py3-none-any.whl → 0.40.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 +0 -4
- quantalogic/agent.py +603 -362
- quantalogic/agent_config.py +260 -28
- quantalogic/agent_factory.py +43 -17
- quantalogic/coding_agent.py +20 -12
- quantalogic/config.py +7 -4
- quantalogic/console_print_events.py +4 -8
- quantalogic/console_print_token.py +2 -2
- quantalogic/docs_cli.py +15 -10
- quantalogic/event_emitter.py +258 -83
- quantalogic/flow/__init__.py +23 -0
- quantalogic/flow/flow.py +595 -0
- quantalogic/flow/flow_extractor.py +672 -0
- quantalogic/flow/flow_generator.py +89 -0
- quantalogic/flow/flow_manager.py +407 -0
- quantalogic/flow/flow_manager_schema.py +169 -0
- quantalogic/flow/flow_yaml.md +419 -0
- quantalogic/generative_model.py +109 -77
- quantalogic/get_model_info.py +6 -6
- quantalogic/interactive_text_editor.py +100 -73
- quantalogic/main.py +36 -23
- quantalogic/model_info_list.py +12 -0
- quantalogic/model_info_litellm.py +14 -14
- quantalogic/prompts.py +2 -1
- quantalogic/{llm.py → quantlitellm.py} +29 -39
- quantalogic/search_agent.py +4 -4
- quantalogic/server/models.py +4 -1
- quantalogic/task_file_reader.py +5 -5
- quantalogic/task_runner.py +21 -20
- quantalogic/tool_manager.py +10 -21
- quantalogic/tools/__init__.py +98 -68
- quantalogic/tools/composio/composio.py +416 -0
- quantalogic/tools/{generate_database_report_tool.py → database/generate_database_report_tool.py} +4 -9
- quantalogic/tools/database/sql_query_tool_advanced.py +261 -0
- quantalogic/tools/document_tools/markdown_to_docx_tool.py +620 -0
- quantalogic/tools/document_tools/markdown_to_epub_tool.py +438 -0
- quantalogic/tools/document_tools/markdown_to_html_tool.py +362 -0
- quantalogic/tools/document_tools/markdown_to_ipynb_tool.py +319 -0
- quantalogic/tools/document_tools/markdown_to_latex_tool.py +420 -0
- quantalogic/tools/document_tools/markdown_to_pdf_tool.py +623 -0
- quantalogic/tools/document_tools/markdown_to_pptx_tool.py +319 -0
- quantalogic/tools/duckduckgo_search_tool.py +2 -4
- quantalogic/tools/finance/alpha_vantage_tool.py +440 -0
- quantalogic/tools/finance/ccxt_tool.py +373 -0
- quantalogic/tools/finance/finance_llm_tool.py +387 -0
- quantalogic/tools/finance/google_finance.py +192 -0
- quantalogic/tools/finance/market_intelligence_tool.py +520 -0
- quantalogic/tools/finance/technical_analysis_tool.py +491 -0
- quantalogic/tools/finance/tradingview_tool.py +336 -0
- quantalogic/tools/finance/yahoo_finance.py +236 -0
- quantalogic/tools/git/bitbucket_clone_repo_tool.py +181 -0
- quantalogic/tools/git/bitbucket_operations_tool.py +326 -0
- quantalogic/tools/git/clone_repo_tool.py +189 -0
- quantalogic/tools/git/git_operations_tool.py +532 -0
- quantalogic/tools/google_packages/google_news_tool.py +480 -0
- quantalogic/tools/grep_app_tool.py +123 -186
- quantalogic/tools/{dalle_e.py → image_generation/dalle_e.py} +37 -27
- quantalogic/tools/jinja_tool.py +6 -10
- quantalogic/tools/language_handlers/__init__.py +22 -9
- quantalogic/tools/list_directory_tool.py +131 -42
- quantalogic/tools/llm_tool.py +45 -15
- quantalogic/tools/llm_vision_tool.py +59 -7
- quantalogic/tools/markitdown_tool.py +17 -5
- quantalogic/tools/nasa_packages/models.py +47 -0
- quantalogic/tools/nasa_packages/nasa_apod_tool.py +232 -0
- quantalogic/tools/nasa_packages/nasa_neows_tool.py +147 -0
- quantalogic/tools/nasa_packages/services.py +82 -0
- quantalogic/tools/presentation_tools/presentation_llm_tool.py +396 -0
- quantalogic/tools/product_hunt/product_hunt_tool.py +258 -0
- quantalogic/tools/product_hunt/services.py +63 -0
- quantalogic/tools/rag_tool/__init__.py +48 -0
- quantalogic/tools/rag_tool/document_metadata.py +15 -0
- quantalogic/tools/rag_tool/query_response.py +20 -0
- quantalogic/tools/rag_tool/rag_tool.py +566 -0
- quantalogic/tools/rag_tool/rag_tool_beta.py +264 -0
- quantalogic/tools/read_html_tool.py +24 -38
- quantalogic/tools/replace_in_file_tool.py +10 -10
- quantalogic/tools/safe_python_interpreter_tool.py +10 -24
- quantalogic/tools/search_definition_names.py +2 -2
- quantalogic/tools/sequence_tool.py +14 -23
- quantalogic/tools/sql_query_tool.py +17 -19
- quantalogic/tools/tool.py +39 -15
- quantalogic/tools/unified_diff_tool.py +1 -1
- quantalogic/tools/utilities/csv_processor_tool.py +234 -0
- quantalogic/tools/utilities/download_file_tool.py +179 -0
- quantalogic/tools/utilities/mermaid_validator_tool.py +661 -0
- quantalogic/tools/utils/__init__.py +1 -4
- quantalogic/tools/utils/create_sample_database.py +24 -38
- quantalogic/tools/utils/generate_database_report.py +74 -82
- quantalogic/tools/wikipedia_search_tool.py +17 -21
- quantalogic/utils/ask_user_validation.py +1 -1
- quantalogic/utils/async_utils.py +35 -0
- quantalogic/utils/check_version.py +3 -5
- quantalogic/utils/get_all_models.py +2 -1
- quantalogic/utils/git_ls.py +21 -7
- quantalogic/utils/lm_studio_model_info.py +9 -7
- quantalogic/utils/python_interpreter.py +113 -43
- quantalogic/utils/xml_utility.py +178 -0
- quantalogic/version_check.py +1 -1
- quantalogic/welcome_message.py +7 -7
- quantalogic/xml_parser.py +0 -1
- {quantalogic-0.33.4.dist-info → quantalogic-0.40.0.dist-info}/METADATA +44 -1
- quantalogic-0.40.0.dist-info/RECORD +148 -0
- quantalogic-0.33.4.dist-info/RECORD +0 -102
- {quantalogic-0.33.4.dist-info → quantalogic-0.40.0.dist-info}/LICENSE +0 -0
- {quantalogic-0.33.4.dist-info → quantalogic-0.40.0.dist-info}/WHEEL +0 -0
- {quantalogic-0.33.4.dist-info → quantalogic-0.40.0.dist-info}/entry_points.txt +0 -0
@@ -24,54 +24,65 @@ class InputHistoryManager:
|
|
24
24
|
return True
|
25
25
|
return False
|
26
26
|
|
27
|
+
|
27
28
|
class CommandRegistry:
|
28
29
|
def __init__(self):
|
29
30
|
self.commands = {}
|
30
|
-
|
31
|
+
|
31
32
|
def register(self, name: str, help_text: str = "") -> callable:
|
32
33
|
def decorator(func: callable) -> callable:
|
33
|
-
self.commands[name] = {
|
34
|
-
"handler": func,
|
35
|
-
"help": func.__doc__ or help_text
|
36
|
-
}
|
34
|
+
self.commands[name] = {"handler": func, "help": func.__doc__ or help_text}
|
37
35
|
return func
|
36
|
+
|
38
37
|
return decorator
|
39
38
|
|
39
|
+
|
40
40
|
registry = CommandRegistry()
|
41
41
|
|
42
|
+
|
42
43
|
@registry.register("/help", "Show available commands")
|
43
|
-
def handle_help_command(
|
44
|
-
|
44
|
+
def handle_help_command(
|
45
|
+
lines: List[str], args: List[str], console: Console, session: PromptSession, history_manager: InputHistoryManager
|
46
|
+
) -> None:
|
45
47
|
"""Display auto-generated help from registered commands."""
|
46
48
|
help_content = "\n".join([f" {name}: {cmd['help']}" for name, cmd in registry.commands.items()])
|
47
49
|
console.print(Panel(f"Available commands:\n{help_content}", title="Help Menu", border_style="green"))
|
48
50
|
|
51
|
+
|
49
52
|
@registry.register("/date", "Show current date/time")
|
50
|
-
def handle_date_command(
|
51
|
-
|
53
|
+
def handle_date_command(
|
54
|
+
lines: List[str], args: List[str], console: Console, session: PromptSession, history_manager: InputHistoryManager
|
55
|
+
) -> None:
|
52
56
|
"""Display current date and time."""
|
53
57
|
from datetime import datetime
|
58
|
+
|
54
59
|
console.print(f"[bold #ffaa00]Current datetime: {datetime.now().isoformat()}[/bold #ffaa00]")
|
55
60
|
|
61
|
+
|
56
62
|
@registry.register("/edit", "Edit specific line: /edit <line_number>")
|
57
|
-
def handle_edit_command(
|
58
|
-
|
63
|
+
def handle_edit_command(
|
64
|
+
lines: List[str], args: List[str], console: Console, session: PromptSession, history_manager: InputHistoryManager
|
65
|
+
) -> None:
|
59
66
|
"""Edit a specific line in the input buffer."""
|
60
67
|
try:
|
61
68
|
edit_line_num = int(args[0]) - 1
|
62
69
|
if 0 <= edit_line_num < len(lines):
|
63
|
-
console.print(
|
70
|
+
console.print(
|
71
|
+
f"[bold #1d3557]Editing Line {edit_line_num + 1}:[/bold #1d3557] {lines[edit_line_num]}"
|
72
|
+
) # Dark blue
|
64
73
|
new_line = session.prompt("New content: ")
|
65
74
|
history_manager.push_state(lines)
|
66
75
|
lines[edit_line_num] = new_line
|
67
76
|
else:
|
68
77
|
console.print("[bold #ff4444]Invalid line number.[/bold #ff4444]")
|
69
78
|
except (ValueError, IndexError):
|
70
|
-
|
79
|
+
console.print("[bold #ff4444]Invalid edit command. Usage: /edit <line_number>[/bold #ff4444]")
|
80
|
+
|
71
81
|
|
72
82
|
@registry.register("/delete", "Delete specific line: /delete <line_number>")
|
73
|
-
def handle_delete_command(
|
74
|
-
|
83
|
+
def handle_delete_command(
|
84
|
+
lines: List[str], args: List[str], console: Console, session: PromptSession, history_manager: InputHistoryManager
|
85
|
+
) -> None:
|
75
86
|
"""Delete a specific line from the input buffer."""
|
76
87
|
try:
|
77
88
|
delete_line_num = int(args[0]) - 1
|
@@ -82,11 +93,13 @@ def handle_delete_command(lines: List[str], args: List[str], console: Console,
|
|
82
93
|
else:
|
83
94
|
console.print("[red]Invalid line number.[/red]")
|
84
95
|
except (ValueError, IndexError):
|
85
|
-
|
96
|
+
console.print("[bold #ff4444]Invalid delete command. Usage: /delete <line_number>[/bold #ff4444]")
|
97
|
+
|
86
98
|
|
87
99
|
@registry.register("/replace", "Search and replace: /replace <search> <replace>")
|
88
|
-
def handle_replace_command(
|
89
|
-
session: PromptSession, history_manager: InputHistoryManager
|
100
|
+
def handle_replace_command(
|
101
|
+
lines: List[str], args: List[str], console: Console, session: PromptSession, history_manager: InputHistoryManager
|
102
|
+
) -> None:
|
90
103
|
try:
|
91
104
|
search_str = args[0]
|
92
105
|
replace_str = args[1]
|
@@ -95,12 +108,17 @@ def handle_replace_command(lines: List[str], args: List[str], console: Console,
|
|
95
108
|
lines[i] = lines[i].replace(search_str, replace_str)
|
96
109
|
console.print("[bold #00cc66]Search and replace completed.[/bold #00cc66]")
|
97
110
|
except (ValueError, IndexError):
|
98
|
-
|
111
|
+
console.print(
|
112
|
+
"[bold #ff4444]Invalid replace command. Usage: /replace <search_str> <replace_str>[/bold #ff4444]"
|
113
|
+
)
|
114
|
+
|
99
115
|
|
100
|
-
@registry.register("/model", "Show current AI model")
|
101
|
-
def handle_model_command(
|
102
|
-
session: PromptSession, history_manager: InputHistoryManager
|
116
|
+
@registry.register("/model", "Show current AI model")
|
117
|
+
def handle_model_command(
|
118
|
+
lines: List[str], args: List[str], console: Console, session: PromptSession, history_manager: InputHistoryManager
|
119
|
+
) -> None:
|
103
120
|
from quantalogic.agent_factory import AgentRegistry
|
121
|
+
|
104
122
|
try:
|
105
123
|
current_agent = AgentRegistry.get_agent("main_agent")
|
106
124
|
if current_agent:
|
@@ -108,17 +126,20 @@ def handle_model_command(lines: List[str], args: List[str], console: Console,
|
|
108
126
|
else:
|
109
127
|
console.print("[bold #ffaa00]No active agent found.[/bold #ffaa00]")
|
110
128
|
except ValueError as e:
|
111
|
-
|
129
|
+
console.print(f"[bold #ff4444]Error: {str(e)}[/bold #ff4444]")
|
130
|
+
|
112
131
|
|
113
132
|
@registry.register("/setmodel", "Set AI model name: /setmodel <name>")
|
114
|
-
def handle_set_model_command(
|
115
|
-
session: PromptSession, history_manager: InputHistoryManager
|
133
|
+
def handle_set_model_command(
|
134
|
+
lines: List[str], args: List[str], console: Console, session: PromptSession, history_manager: InputHistoryManager
|
135
|
+
) -> None:
|
116
136
|
from quantalogic.agent_factory import AgentRegistry
|
137
|
+
|
117
138
|
try:
|
118
139
|
if len(args) < 1:
|
119
140
|
console.print("[bold #ff4444]Error: Model name required. Usage: /setmodel <name>[/bold #ff4444]")
|
120
141
|
return
|
121
|
-
|
142
|
+
|
122
143
|
model_name = args[0]
|
123
144
|
current_agent = AgentRegistry.get_agent("main_agent")
|
124
145
|
if current_agent:
|
@@ -129,29 +150,32 @@ def handle_set_model_command(lines: List[str], args: List[str], console: Console
|
|
129
150
|
except ValueError as e:
|
130
151
|
console.print(f"[red]Error: {str(e)}[/red]")
|
131
152
|
|
153
|
+
|
132
154
|
@registry.register("/models", "List all available AI models")
|
133
|
-
def handle_models_command(
|
134
|
-
session: PromptSession, history_manager: InputHistoryManager
|
155
|
+
def handle_models_command(
|
156
|
+
lines: List[str], args: List[str], console: Console, session: PromptSession, history_manager: InputHistoryManager
|
157
|
+
) -> None:
|
135
158
|
"""Display all available AI models supported by the system."""
|
136
159
|
from quantalogic.utils.get_all_models import get_all_models
|
160
|
+
|
137
161
|
try:
|
138
162
|
models = get_all_models()
|
139
163
|
if models:
|
140
164
|
# Group models by provider
|
141
165
|
provider_groups = {}
|
142
166
|
for model in models:
|
143
|
-
provider = model.split(
|
167
|
+
provider = model.split("/")[0] if "/" in model else "default"
|
144
168
|
if provider not in provider_groups:
|
145
169
|
provider_groups[provider] = []
|
146
170
|
provider_groups[provider].append(model)
|
147
|
-
|
171
|
+
|
148
172
|
# Create formatted output
|
149
173
|
output = "[bold #00cc66]Available AI Models:[/bold #00cc66]\n"
|
150
174
|
for provider, model_list in provider_groups.items():
|
151
175
|
output += f"\n[bold #ffaa00]{provider.upper()}[/bold #ffaa00]\n"
|
152
176
|
for model in sorted(model_list):
|
153
177
|
output += f" • {model}\n"
|
154
|
-
|
178
|
+
|
155
179
|
console.print(Panel(output, border_style="green"))
|
156
180
|
else:
|
157
181
|
console.print("[yellow]No models available.[/yellow]")
|
@@ -163,8 +187,7 @@ def get_multiline_input(console: Console) -> str:
|
|
163
187
|
"""Get multiline input with slash command support."""
|
164
188
|
console.print(
|
165
189
|
Panel(
|
166
|
-
"Enter your task. Press [bold]Enter[/bold] twice to submit.\n"
|
167
|
-
"Type /help for available commands",
|
190
|
+
"Enter your task. Press [bold]Enter[/bold] twice to submit.\n" "Type /help for available commands",
|
168
191
|
title="Multi-line Input",
|
169
192
|
border_style="blue",
|
170
193
|
)
|
@@ -182,16 +205,15 @@ def get_multiline_input(console: Console) -> str:
|
|
182
205
|
if history_manager.undo(lines):
|
183
206
|
console.print("[bold #00cc66]Undo successful.[/bold #00cc66]")
|
184
207
|
|
185
|
-
|
186
208
|
from prompt_toolkit.completion import Completer, Completion
|
187
209
|
from prompt_toolkit.styles import Style
|
188
|
-
|
210
|
+
|
189
211
|
class CommandCompleter(Completer):
|
190
212
|
def get_completions(self, document, complete_event):
|
191
213
|
text = document.text_before_cursor
|
192
214
|
line_parts = text.split()
|
193
|
-
|
194
|
-
if len(line_parts) == 0 or (len(line_parts) == 1 and text.endswith(
|
215
|
+
|
216
|
+
if len(line_parts) == 0 or (len(line_parts) == 1 and text.endswith(" ")):
|
195
217
|
for cmd, details in registry.commands.items():
|
196
218
|
yield Completion(
|
197
219
|
cmd,
|
@@ -203,9 +225,8 @@ def get_multiline_input(console: Console) -> str:
|
|
203
225
|
cmd = registry.commands[line_parts[0]]
|
204
226
|
arg_index = len(line_parts) - 1
|
205
227
|
if arg_index == 1:
|
206
|
-
doc = cmd[
|
207
|
-
args_hint = next((line.split(
|
208
|
-
if 'Args:' in line), "")
|
228
|
+
doc = cmd["handler"].__doc__ or ""
|
229
|
+
args_hint = next((line.split(":")[1].strip() for line in doc.split("\n") if "Args:" in line), "")
|
209
230
|
if args_hint:
|
210
231
|
yield Completion(
|
211
232
|
f"<{args_hint}>",
|
@@ -214,42 +235,43 @@ def get_multiline_input(console: Console) -> str:
|
|
214
235
|
display=f"Expected argument: {args_hint}",
|
215
236
|
)
|
216
237
|
else:
|
217
|
-
if text.startswith(
|
218
|
-
partial = text[1:].lstrip(
|
238
|
+
if text.startswith("/"):
|
239
|
+
partial = text[1:].lstrip("/")
|
219
240
|
exact_matches = []
|
220
241
|
prefix_matches = []
|
221
|
-
|
242
|
+
|
222
243
|
for cmd in registry.commands:
|
223
244
|
cmd_without_slash = cmd[1:]
|
224
245
|
if cmd_without_slash.lower() == partial.lower():
|
225
246
|
exact_matches.append(cmd)
|
226
247
|
elif cmd_without_slash.lower().startswith(partial.lower()):
|
227
248
|
prefix_matches.append(cmd)
|
228
|
-
|
249
|
+
|
229
250
|
# Prioritize exact matches first
|
230
251
|
for match in exact_matches:
|
231
|
-
remaining = match[len(
|
252
|
+
remaining = match[len("/" + partial) :]
|
232
253
|
yield Completion(
|
233
254
|
remaining,
|
234
255
|
start_position=0, # Corrected from -len(partial)
|
235
256
|
display=f"{match} - {registry.commands[match]['help']}",
|
236
257
|
style="fg:ansiyellow bold",
|
237
258
|
)
|
238
|
-
|
259
|
+
|
239
260
|
# Then prefix matches
|
240
261
|
for match in prefix_matches:
|
241
|
-
remaining = match[len(
|
262
|
+
remaining = match[len("/" + partial) :]
|
242
263
|
yield Completion(
|
243
264
|
remaining,
|
244
265
|
start_position=0, # Corrected from -len(partial)
|
245
266
|
display=f"{match} - {registry.commands[match]['help']}",
|
246
267
|
style="fg:ansiyellow bold",
|
247
268
|
)
|
269
|
+
|
248
270
|
def get_completions(self, document, complete_event):
|
249
271
|
text = document.text_before_cursor
|
250
272
|
line_parts = text.split()
|
251
|
-
|
252
|
-
if len(line_parts) == 0 or (len(line_parts) == 1 and text.endswith(
|
273
|
+
|
274
|
+
if len(line_parts) == 0 or (len(line_parts) == 1 and text.endswith(" ")):
|
253
275
|
for cmd, details in registry.commands.items():
|
254
276
|
yield Completion(
|
255
277
|
cmd,
|
@@ -261,9 +283,10 @@ def get_multiline_input(console: Console) -> str:
|
|
261
283
|
cmd = registry.commands[line_parts[0]]
|
262
284
|
arg_index = len(line_parts) - 1
|
263
285
|
if arg_index == 1:
|
264
|
-
doc = cmd[
|
265
|
-
args_hint = next(
|
266
|
-
|
286
|
+
doc = cmd["handler"].__doc__ or ""
|
287
|
+
args_hint = next(
|
288
|
+
(line.split(":")[1].strip() for line in doc.split("\n") if "Args:" in line), ""
|
289
|
+
)
|
267
290
|
if args_hint:
|
268
291
|
yield Completion(
|
269
292
|
f"<{args_hint}>",
|
@@ -272,29 +295,29 @@ def get_multiline_input(console: Console) -> str:
|
|
272
295
|
display=f"Expected argument: {args_hint}",
|
273
296
|
)
|
274
297
|
else:
|
275
|
-
if text.startswith(
|
276
|
-
partial = text[1:].lstrip(
|
298
|
+
if text.startswith("/"):
|
299
|
+
partial = text[1:].lstrip("/")
|
277
300
|
exact_matches = []
|
278
301
|
prefix_matches = []
|
279
|
-
|
302
|
+
|
280
303
|
for cmd in registry.commands:
|
281
304
|
cmd_without_slash = cmd[1:]
|
282
305
|
if cmd_without_slash.lower() == partial.lower():
|
283
306
|
exact_matches.append(cmd)
|
284
307
|
elif cmd_without_slash.lower().startswith(partial.lower()):
|
285
308
|
prefix_matches.append(cmd)
|
286
|
-
|
309
|
+
|
287
310
|
for match in exact_matches:
|
288
|
-
remaining = match[len(
|
311
|
+
remaining = match[len("/" + partial) :]
|
289
312
|
yield Completion(
|
290
313
|
remaining,
|
291
314
|
start_position=-len(partial),
|
292
315
|
display=f"{match} - {registry.commands[match]['help']}",
|
293
316
|
style="fg:ansiyellow bold",
|
294
317
|
)
|
295
|
-
|
318
|
+
|
296
319
|
for match in prefix_matches:
|
297
|
-
remaining = match[len(
|
320
|
+
remaining = match[len("/" + partial) :]
|
298
321
|
yield Completion(
|
299
322
|
remaining,
|
300
323
|
start_position=-len(partial),
|
@@ -303,20 +326,22 @@ def get_multiline_input(console: Console) -> str:
|
|
303
326
|
)
|
304
327
|
|
305
328
|
command_completer = CommandCompleter()
|
306
|
-
|
329
|
+
|
307
330
|
def get_command_help(cmd_name: str) -> str:
|
308
331
|
"""Get formatted help text for a command"""
|
309
332
|
if cmd := registry.commands.get(cmd_name):
|
310
333
|
return f"[bold]{cmd_name}[/bold]: {cmd['help']}\n{cmd['handler'].__doc__ or ''}"
|
311
334
|
return ""
|
312
335
|
|
313
|
-
custom_style = Style.from_dict(
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
336
|
+
custom_style = Style.from_dict(
|
337
|
+
{
|
338
|
+
"completion-menu.completion": "bg:#005577 #ffffff",
|
339
|
+
"completion-menu.completion.current": "bg:#007799 #ffffff bold",
|
340
|
+
"scrollbar.background": "bg:#6699aa",
|
341
|
+
"scrollbar.button": "bg:#444444",
|
342
|
+
"documentation": "bg:#003366 #ffffff",
|
343
|
+
}
|
344
|
+
)
|
320
345
|
|
321
346
|
session = PromptSession(
|
322
347
|
history=InMemoryHistory(),
|
@@ -325,9 +350,11 @@ def get_multiline_input(console: Console) -> str:
|
|
325
350
|
completer=command_completer,
|
326
351
|
complete_while_typing=True,
|
327
352
|
style=custom_style,
|
328
|
-
bottom_toolbar=lambda: get_command_help(
|
329
|
-
|
330
|
-
|
353
|
+
bottom_toolbar=lambda: get_command_help(
|
354
|
+
session.default_buffer.document.text.split()[0][1:]
|
355
|
+
if session.default_buffer.document.text.startswith("/")
|
356
|
+
else ""
|
357
|
+
),
|
331
358
|
)
|
332
359
|
|
333
360
|
try:
|
@@ -335,11 +362,11 @@ def get_multiline_input(console: Console) -> str:
|
|
335
362
|
prompt_text = f"{line_number:>3}: "
|
336
363
|
line = session.prompt(prompt_text, rprompt="Press Enter twice to submit")
|
337
364
|
|
338
|
-
if line.strip().startswith(
|
365
|
+
if line.strip().startswith("/"):
|
339
366
|
cmd_parts = line.strip().split()
|
340
367
|
cmd_name = cmd_parts[0].lower()
|
341
368
|
args = cmd_parts[1:]
|
342
|
-
|
369
|
+
|
343
370
|
if cmd_handler := registry.commands.get(cmd_name):
|
344
371
|
try:
|
345
372
|
cmd_handler["handler"](lines, args, console, session, history_manager)
|
@@ -365,4 +392,4 @@ def get_multiline_input(console: Console) -> str:
|
|
365
392
|
console.print("\n[bold]Input cancelled by user.[/bold]")
|
366
393
|
return ""
|
367
394
|
|
368
|
-
return "\n".join(lines)
|
395
|
+
return "\n".join(lines)
|
quantalogic/main.py
CHANGED
@@ -33,7 +33,7 @@ from quantalogic.utils.get_all_models import get_all_models # noqa: E402
|
|
33
33
|
|
34
34
|
# Platform-specific imports
|
35
35
|
try:
|
36
|
-
if sys.platform ==
|
36
|
+
if sys.platform == "win32":
|
37
37
|
import msvcrt # Built-in Windows module
|
38
38
|
else:
|
39
39
|
import termios
|
@@ -50,7 +50,7 @@ AGENT_MODES = ["code", "basic", "interpreter", "full", "code-basic", "search", "
|
|
50
50
|
|
51
51
|
def setup_terminal():
|
52
52
|
"""Configure terminal settings based on platform."""
|
53
|
-
if sys.platform ==
|
53
|
+
if sys.platform == "win32":
|
54
54
|
if msvcrt:
|
55
55
|
return None # Windows terminal is already configured
|
56
56
|
logger.warning("msvcrt module not available on Windows")
|
@@ -70,7 +70,7 @@ def setup_terminal():
|
|
70
70
|
|
71
71
|
def restore_terminal(old_settings):
|
72
72
|
"""Restore terminal settings based on platform."""
|
73
|
-
if sys.platform !=
|
73
|
+
if sys.platform != "win32" and termios and old_settings:
|
74
74
|
try:
|
75
75
|
termios.tcsetattr(sys.stdin.fileno(), termios.TCSADRAIN, old_settings)
|
76
76
|
except (termios.error, AttributeError) as e:
|
@@ -88,14 +88,14 @@ def restore_terminal(old_settings):
|
|
88
88
|
@click.option(
|
89
89
|
"--model-name",
|
90
90
|
default=MODEL_NAME,
|
91
|
-
help=
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
91
|
+
help="Specify the model to use (litellm format). Examples:\n"
|
92
|
+
" - openai/gpt-4o-mini\n"
|
93
|
+
" - openai/gpt-4o\n"
|
94
|
+
" - anthropic/claude-3.5-sonnet\n"
|
95
|
+
" - deepseek/deepseek-chat\n"
|
96
|
+
" - deepseek/deepseek-reasoner\n"
|
97
|
+
" - openrouter/deepseek/deepseek-r1\n"
|
98
|
+
" - openrouter/openai/gpt-4o",
|
99
99
|
)
|
100
100
|
@click.option(
|
101
101
|
"--log",
|
@@ -122,6 +122,12 @@ def restore_terminal(old_settings):
|
|
122
122
|
default=None,
|
123
123
|
help="Set the maximum number of tokens allowed in the working memory.",
|
124
124
|
)
|
125
|
+
@click.option(
|
126
|
+
"--thinking-model",
|
127
|
+
type=str,
|
128
|
+
default="default",
|
129
|
+
help="The thinking model to use",
|
130
|
+
)
|
125
131
|
@click.pass_context
|
126
132
|
def cli(
|
127
133
|
ctx: click.Context,
|
@@ -134,6 +140,7 @@ def cli(
|
|
134
140
|
max_iterations: int,
|
135
141
|
compact_every_n_iteration: int | None,
|
136
142
|
max_tokens_working_memory: int | None,
|
143
|
+
thinking_model: str,
|
137
144
|
) -> None:
|
138
145
|
"""QuantaLogic AI Assistant - A powerful AI tool for various tasks.
|
139
146
|
|
@@ -161,7 +168,8 @@ def cli(
|
|
161
168
|
max_iterations=max_iterations,
|
162
169
|
compact_every_n_iteration=compact_every_n_iteration,
|
163
170
|
max_tokens_working_memory=max_tokens_working_memory,
|
164
|
-
no_stream=False # Default value for backward compatibility
|
171
|
+
no_stream=False, # Default value for backward compatibility
|
172
|
+
thinking_model_name=thinking_model,
|
165
173
|
)
|
166
174
|
ctx.invoke(
|
167
175
|
task,
|
@@ -173,6 +181,7 @@ def cli(
|
|
173
181
|
max_iterations=config.max_iterations,
|
174
182
|
compact_every_n_iteration=config.compact_every_n_iteration,
|
175
183
|
max_tokens_working_memory=config.max_tokens_working_memory,
|
184
|
+
thinking_model=thinking_model,
|
176
185
|
)
|
177
186
|
|
178
187
|
|
@@ -219,6 +228,12 @@ def cli(
|
|
219
228
|
is_flag=True,
|
220
229
|
help="Disable streaming output (default: streaming enabled).",
|
221
230
|
)
|
231
|
+
@click.option(
|
232
|
+
"--thinking-model",
|
233
|
+
type=str,
|
234
|
+
default="default",
|
235
|
+
help="The thinking model to use",
|
236
|
+
)
|
222
237
|
@click.argument("task", required=False)
|
223
238
|
def task(
|
224
239
|
file: Optional[str],
|
@@ -232,6 +247,7 @@ def task(
|
|
232
247
|
compact_every_n_iteration: int | None,
|
233
248
|
max_tokens_working_memory: int | None,
|
234
249
|
no_stream: bool,
|
250
|
+
thinking_model: str,
|
235
251
|
) -> None:
|
236
252
|
console = Console()
|
237
253
|
|
@@ -246,8 +262,9 @@ def task(
|
|
246
262
|
compact_every_n_iteration=compact_every_n_iteration,
|
247
263
|
max_tokens_working_memory=max_tokens_working_memory,
|
248
264
|
no_stream=no_stream,
|
265
|
+
thinking_model_name=thinking_model,
|
249
266
|
)
|
250
|
-
|
267
|
+
|
251
268
|
task_runner(
|
252
269
|
console,
|
253
270
|
file,
|
@@ -264,29 +281,25 @@ def task(
|
|
264
281
|
@click.option("--search", type=str, help="Fuzzy search for models containing the given string.")
|
265
282
|
def list_models(search: Optional[str] = None):
|
266
283
|
"""List supported LiteLLM models with optional fuzzy search.
|
267
|
-
|
284
|
+
|
268
285
|
If a search term is provided, it will return models that closely match the term.
|
269
286
|
"""
|
270
287
|
console = Console()
|
271
288
|
all_models = get_all_models()
|
272
|
-
|
289
|
+
|
273
290
|
if search:
|
274
291
|
# Perform fuzzy matching
|
275
292
|
matched_models = process.extractBests(search, all_models, limit=None, score_cutoff=70)
|
276
293
|
models = [model for model, score in matched_models]
|
277
294
|
else:
|
278
295
|
models = all_models
|
279
|
-
|
280
|
-
console.print(Panel(
|
281
|
-
|
282
|
-
f"({len(all_models)} total)",
|
283
|
-
title="Supported LiteLLM Models"
|
284
|
-
))
|
285
|
-
|
296
|
+
|
297
|
+
console.print(Panel(f"Total Models: {len(models)} " f"({len(all_models)} total)", title="Supported LiteLLM Models"))
|
298
|
+
|
286
299
|
if not models:
|
287
300
|
console.print(f"[yellow]No models found matching '[bold]{search}[/bold]'[/yellow]")
|
288
301
|
return
|
289
|
-
|
302
|
+
|
290
303
|
for model in sorted(models):
|
291
304
|
console.print(f"- {model}")
|
292
305
|
|
quantalogic/model_info_list.py
CHANGED
@@ -57,4 +57,16 @@ model_info = {
|
|
57
57
|
max_output_tokens=8 * 1024,
|
58
58
|
max_input_tokens=1024 * 64,
|
59
59
|
),
|
60
|
+
"gemini/gemini-2.0-flash": ModelInfo(
|
61
|
+
model_name="gemini/gemini-2.0-flash",
|
62
|
+
max_input_tokens=1000000,
|
63
|
+
max_output_tokens=8 * 1024,
|
64
|
+
input_cost_per_token=0.0000001,
|
65
|
+
),
|
66
|
+
"openrouter/google/gemini-2.0-flash-001": ModelInfo(
|
67
|
+
model_name="openrouter/google/gemini-2.0-flash-001",
|
68
|
+
max_input_tokens=1000000,
|
69
|
+
max_output_tokens=8 * 1024,
|
70
|
+
input_cost_per_token=0.0000001,
|
71
|
+
),
|
60
72
|
}
|
@@ -6,18 +6,18 @@ import litellm
|
|
6
6
|
@functools.lru_cache(maxsize=32)
|
7
7
|
def litellm_get_model_info(model_name: str) -> dict | None:
|
8
8
|
"""Get model information with prefix fallback logic using only litellm.
|
9
|
-
|
9
|
+
|
10
10
|
Args:
|
11
11
|
model_name: The model identifier to get information for
|
12
|
-
|
12
|
+
|
13
13
|
Returns:
|
14
14
|
Dictionary containing model information
|
15
|
-
|
15
|
+
|
16
16
|
Raises:
|
17
17
|
ValueError: If model info cannot be found after prefix fallbacks
|
18
18
|
"""
|
19
19
|
tried_models = [model_name]
|
20
|
-
|
20
|
+
|
21
21
|
while True:
|
22
22
|
try:
|
23
23
|
# Attempt to get model info through litellm
|
@@ -26,45 +26,45 @@ def litellm_get_model_info(model_name: str) -> dict | None:
|
|
26
26
|
return info
|
27
27
|
except Exception:
|
28
28
|
pass
|
29
|
-
|
29
|
+
|
30
30
|
# Try removing one prefix level
|
31
31
|
parts = model_name.split("/")
|
32
32
|
if len(parts) <= 1:
|
33
33
|
break
|
34
|
-
|
34
|
+
|
35
35
|
model_name = "/".join(parts[1:])
|
36
36
|
tried_models.append(model_name)
|
37
37
|
|
38
|
-
return None
|
38
|
+
return None
|
39
39
|
|
40
40
|
|
41
41
|
def litellm_get_model_max_input_tokens(model_name: str) -> int | None:
|
42
42
|
"""Get maximum input tokens for a model using litellm.
|
43
|
-
|
43
|
+
|
44
44
|
Args:
|
45
45
|
model_name: The model identifier
|
46
|
-
|
46
|
+
|
47
47
|
Returns:
|
48
48
|
Maximum input tokens or None if not found
|
49
49
|
"""
|
50
50
|
try:
|
51
51
|
info = litellm_get_model_info(model_name)
|
52
52
|
return info.get("max_input_tokens", 8192)
|
53
|
-
except Exception
|
53
|
+
except Exception:
|
54
54
|
return 8192 # Default for many modern models
|
55
55
|
|
56
56
|
|
57
57
|
def litellm_get_model_max_output_tokens(model_name: str) -> int | None:
|
58
58
|
"""Get maximum output tokens for a model using litellm.
|
59
|
-
|
59
|
+
|
60
60
|
Args:
|
61
61
|
model_name: The model identifier
|
62
|
-
|
62
|
+
|
63
63
|
Returns:
|
64
64
|
Maximum output tokens or None if not found
|
65
65
|
"""
|
66
66
|
try:
|
67
67
|
info = litellm_get_model_info(model_name)
|
68
68
|
return info.get("max_output_tokens", 4096)
|
69
|
-
except Exception
|
70
|
-
return 4096 # Conservative default
|
69
|
+
except Exception:
|
70
|
+
return 4096 # Conservative default
|
quantalogic/prompts.py
CHANGED