quantalogic 0.57.0__py3-none-any.whl → 0.58.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/agent.py +28 -4
- quantalogic/create_custom_agent.py +146 -71
- quantalogic/server/agent_server.py +19 -4
- quantalogic/tools/google_packages/google_news_tool.py +26 -187
- quantalogic/tools/utilities/__init__.py +2 -0
- quantalogic/tools/utilities/download_file_tool.py +4 -2
- quantalogic/tools/utilities/vscode_tool.py +123 -0
- quantalogic/utils/ask_user_validation.py +26 -6
- {quantalogic-0.57.0.dist-info → quantalogic-0.58.0.dist-info}/METADATA +1 -1
- {quantalogic-0.57.0.dist-info → quantalogic-0.58.0.dist-info}/RECORD +13 -12
- {quantalogic-0.57.0.dist-info → quantalogic-0.58.0.dist-info}/LICENSE +0 -0
- {quantalogic-0.57.0.dist-info → quantalogic-0.58.0.dist-info}/WHEEL +0 -0
- {quantalogic-0.57.0.dist-info → quantalogic-0.58.0.dist-info}/entry_points.txt +0 -0
quantalogic/agent.py
CHANGED
@@ -2,7 +2,8 @@
|
|
2
2
|
|
3
3
|
import asyncio
|
4
4
|
import os
|
5
|
-
|
5
|
+
import uuid
|
6
|
+
from collections.abc import Awaitable, Callable
|
6
7
|
from datetime import datetime
|
7
8
|
from pathlib import Path
|
8
9
|
from typing import Any
|
@@ -75,7 +76,7 @@ class Agent(BaseModel):
|
|
75
76
|
config: AgentConfig
|
76
77
|
task_to_solve: str
|
77
78
|
task_to_solve_summary: str = ""
|
78
|
-
ask_for_user_validation: Callable[[str], bool] = console_ask_for_user_validation
|
79
|
+
ask_for_user_validation: Callable[[str, str], Awaitable[bool]] = console_ask_for_user_validation
|
79
80
|
last_tool_call: dict[str, Any] = {} # Stores the last tool call information
|
80
81
|
total_tokens: int = 0 # Total tokens in the conversation
|
81
82
|
current_iteration: int = 0
|
@@ -93,7 +94,7 @@ class Agent(BaseModel):
|
|
93
94
|
memory: AgentMemory = AgentMemory(),
|
94
95
|
variable_store: VariableMemory = VariableMemory(),
|
95
96
|
tools: list[Tool] = [TaskCompleteTool()],
|
96
|
-
ask_for_user_validation: Callable[[str], bool] = console_ask_for_user_validation,
|
97
|
+
ask_for_user_validation: Callable[[str, str], Awaitable[bool]] = console_ask_for_user_validation,
|
97
98
|
task_to_solve: str = "",
|
98
99
|
specific_expertise: str = "General AI assistant with coding and problem-solving capabilities",
|
99
100
|
get_environment: Callable[[], str] = get_environment,
|
@@ -399,6 +400,18 @@ class Agent(BaseModel):
|
|
399
400
|
Tuple of (executed_tool_name, response)
|
400
401
|
"""
|
401
402
|
if tool.need_validation:
|
403
|
+
logger.info(f"Tool '{tool_name}' requires validation.")
|
404
|
+
validation_id = str(uuid.uuid4())
|
405
|
+
logger.info(f"Validation ID: {validation_id}")
|
406
|
+
|
407
|
+
self._emit_event(
|
408
|
+
"tool_execute_validation_start",
|
409
|
+
{
|
410
|
+
"validation_id": validation_id,
|
411
|
+
"tool_name": tool_name,
|
412
|
+
"arguments": arguments_with_values
|
413
|
+
},
|
414
|
+
)
|
402
415
|
question_validation = (
|
403
416
|
"Do you permit the execution of this tool?\n"
|
404
417
|
f"Tool: {tool_name}\nArguments:\n"
|
@@ -406,7 +419,18 @@ class Agent(BaseModel):
|
|
406
419
|
+ "\n".join([f" <{key}>{value}</{key}>" for key, value in arguments_with_values.items()])
|
407
420
|
+ "\n</arguments>\nYes or No"
|
408
421
|
)
|
409
|
-
permission_granted = self.ask_for_user_validation(question_validation)
|
422
|
+
permission_granted = await self.ask_for_user_validation(validation_id=validation_id, question=question_validation)
|
423
|
+
|
424
|
+
self._emit_event(
|
425
|
+
"tool_execute_validation_end",
|
426
|
+
{
|
427
|
+
"validation_id": validation_id,
|
428
|
+
"tool_name": tool_name,
|
429
|
+
"arguments": arguments_with_values,
|
430
|
+
"granted": permission_granted
|
431
|
+
},
|
432
|
+
)
|
433
|
+
|
410
434
|
if not permission_granted:
|
411
435
|
return "", f"Error: execution of tool '{tool_name}' was denied by the user."
|
412
436
|
|
@@ -55,14 +55,14 @@ def create_tool_instance(tool_class, **kwargs):
|
|
55
55
|
return None
|
56
56
|
|
57
57
|
try:
|
58
|
-
#
|
59
|
-
if 'name' not in kwargs:
|
58
|
+
# Only set name if the class doesn't already define it
|
59
|
+
if 'name' not in kwargs and not hasattr(tool_class, 'name'):
|
60
60
|
class_name = tool_class.__name__
|
61
61
|
kwargs['name'] = class_name.lower().replace('tool', '')
|
62
62
|
|
63
63
|
# Create and return the tool instance
|
64
64
|
instance = tool_class(**kwargs)
|
65
|
-
logger.debug(f"Successfully created tool instance: {
|
65
|
+
logger.debug(f"Successfully created tool instance: {tool_class.__name__}")
|
66
66
|
return instance
|
67
67
|
except Exception as e:
|
68
68
|
logger.error(f"Failed to instantiate {tool_class.__name__}: {str(e)}")
|
@@ -72,33 +72,33 @@ def create_tool_instance(tool_class, **kwargs):
|
|
72
72
|
TOOL_IMPORTS = {
|
73
73
|
# LLM Tools
|
74
74
|
"llm": lambda: _import_tool("quantalogic.tools.llm_tool", "LLMTool"),
|
75
|
-
"llm_vision": lambda: _import_tool("quantalogic.tools.
|
76
|
-
"llm_image_generation": lambda: _import_tool("quantalogic.tools.
|
75
|
+
"llm_vision": lambda: _import_tool("quantalogic.tools.llm_vision_tool", "LLMVisionTool"),
|
76
|
+
"llm_image_generation": lambda: _import_tool("quantalogic.tools.image_generation.dalle_e", "LLMImageGenerationTool"),
|
77
77
|
|
78
78
|
# File Tools
|
79
|
-
"download_http_file": lambda: _import_tool("quantalogic.tools.
|
79
|
+
"download_http_file": lambda: _import_tool("quantalogic.tools.utilities", "PrepareDownloadTool"),
|
80
80
|
"write_file": lambda: _import_tool("quantalogic.tools.write_file_tool", "WriteFileTool"),
|
81
|
-
"edit_whole_content": lambda: _import_tool("quantalogic.tools
|
82
|
-
"read_file_block": lambda: _import_tool("quantalogic.tools
|
83
|
-
"read_file": lambda: _import_tool("quantalogic.tools
|
84
|
-
"replace_in_file": lambda: _import_tool("quantalogic.tools
|
85
|
-
"list_directory": lambda: _import_tool("quantalogic.tools
|
81
|
+
"edit_whole_content": lambda: _import_tool("quantalogic.tools", "EditWholeContentTool"),
|
82
|
+
"read_file_block": lambda: _import_tool("quantalogic.tools", "ReadFileBlockTool"),
|
83
|
+
"read_file": lambda: _import_tool("quantalogic.tools", "ReadFileTool"),
|
84
|
+
"replace_in_file": lambda: _import_tool("quantalogic.tools", "ReplaceInFileTool"),
|
85
|
+
"list_directory": lambda: _import_tool("quantalogic.tools", "ListDirectoryTool"),
|
86
86
|
|
87
87
|
# Search Tools
|
88
88
|
"duck_duck_go_search": lambda: _import_tool("quantalogic.tools", "DuckDuckGoSearchTool"),
|
89
89
|
"wikipedia_search": lambda: _import_tool("quantalogic.tools", "WikipediaSearchTool"),
|
90
|
-
"google_news": lambda: _import_tool("quantalogic.tools", "GoogleNewsTool"),
|
90
|
+
"google_news": lambda: _import_tool("quantalogic.tools.google_packages", "GoogleNewsTool"),
|
91
91
|
"search_definition_names": lambda: _import_tool("quantalogic.tools", "SearchDefinitionNames"),
|
92
92
|
"ripgrep": lambda: _import_tool("quantalogic.tools", "RipgrepTool"),
|
93
93
|
|
94
94
|
# Execution Tools
|
95
|
-
"execute_bash_command": lambda: _import_tool("quantalogic.tools
|
96
|
-
"nodejs": lambda: _import_tool("quantalogic.tools
|
97
|
-
"python": lambda: _import_tool("quantalogic.tools
|
98
|
-
"safe_python_interpreter": lambda: _import_tool("quantalogic.tools
|
95
|
+
"execute_bash_command": lambda: _import_tool("quantalogic.tools", "ExecuteBashCommandTool"),
|
96
|
+
"nodejs": lambda: _import_tool("quantalogic.tools", "NodeJsTool"),
|
97
|
+
"python": lambda: _import_tool("quantalogic.tools", "PythonTool"),
|
98
|
+
"safe_python_interpreter": lambda: _import_tool("quantalogic.tools", "SafePythonInterpreterTool"),
|
99
99
|
|
100
100
|
# Database Tools
|
101
|
-
"sql_query": lambda: _import_tool("quantalogic.tools
|
101
|
+
"sql_query": lambda: _import_tool("quantalogic.tools", "SQLQueryTool"),
|
102
102
|
"sql_query_advanced": lambda: _import_tool("quantalogic.tools.database", "SQLQueryToolAdvanced"),
|
103
103
|
|
104
104
|
# Document Tools
|
@@ -140,7 +140,8 @@ TOOL_IMPORTS = {
|
|
140
140
|
"sequence": lambda: _import_tool("quantalogic.tools.utilities", "SequenceTool"),
|
141
141
|
"csv_processor": lambda: _import_tool("quantalogic.tools.utilities", "CSVProcessorTool"),
|
142
142
|
"mermaid_validator_tool": lambda: _import_tool("quantalogic.tools.utilities", "MermaidValidatorTool"),
|
143
|
-
"download_file_tool": lambda: _import_tool("quantalogic.tools.
|
143
|
+
"download_file_tool": lambda: _import_tool("quantalogic.tools.utilities", "PrepareDownloadTool"),
|
144
|
+
"vscode_server_tool": lambda: _import_tool("quantalogic.tools.utilities.vscode_tool", "VSCodeServerTool"),
|
144
145
|
}
|
145
146
|
|
146
147
|
def create_custom_agent(
|
@@ -175,61 +176,135 @@ def create_custom_agent(
|
|
175
176
|
# Create event emitter
|
176
177
|
event_emitter = EventEmitter()
|
177
178
|
|
178
|
-
|
179
|
+
def get_llm_params(params: dict) -> dict:
|
180
|
+
"""Get common parameters for LLM-based tools."""
|
181
|
+
return {
|
182
|
+
"model_name": params.get("model_name", model_name),
|
183
|
+
"on_token": console_print_token if not no_stream else None,
|
184
|
+
"event_emitter": event_emitter
|
185
|
+
}
|
186
|
+
|
187
|
+
# Define tool configurations with default parameters
|
179
188
|
tool_configs = {
|
180
|
-
|
181
|
-
"
|
182
|
-
"
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
"
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
"
|
195
|
-
"
|
196
|
-
"
|
197
|
-
"
|
198
|
-
"
|
199
|
-
"
|
200
|
-
"
|
201
|
-
"
|
202
|
-
"
|
203
|
-
"
|
204
|
-
"
|
205
|
-
"
|
206
|
-
"
|
207
|
-
"
|
208
|
-
"
|
209
|
-
"
|
210
|
-
"
|
211
|
-
"
|
212
|
-
"
|
213
|
-
|
214
|
-
|
215
|
-
"
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
"
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
"
|
225
|
-
|
226
|
-
|
227
|
-
|
189
|
+
# LLM Tools with shared parameters
|
190
|
+
"llm": lambda params: create_tool_instance(TOOL_IMPORTS["llm"](), **get_llm_params(params)),
|
191
|
+
"llm_vision": lambda params: create_tool_instance(TOOL_IMPORTS["llm_vision"](),
|
192
|
+
model_name=params.get("vision_model_name") or vision_model_name,
|
193
|
+
on_token=console_print_token if not no_stream else None,
|
194
|
+
event_emitter=event_emitter
|
195
|
+
) if vision_model_name else None,
|
196
|
+
"llm_image_generation": lambda params: create_tool_instance(TOOL_IMPORTS["llm_image_generation"](),
|
197
|
+
provider="dall-e",
|
198
|
+
model_name="openai/dall-e-3",
|
199
|
+
on_token=console_print_token if not no_stream else None
|
200
|
+
),
|
201
|
+
|
202
|
+
# Simple tools without parameters
|
203
|
+
"download_http_file": lambda _: create_tool_instance(TOOL_IMPORTS["download_http_file"]()),
|
204
|
+
"duck_duck_go_search": lambda _: create_tool_instance(TOOL_IMPORTS["duck_duck_go_search"]()),
|
205
|
+
"write_file": lambda _: create_tool_instance(TOOL_IMPORTS["write_file"]()),
|
206
|
+
"task_complete": lambda _: create_tool_instance(TOOL_IMPORTS["task_complete"]()),
|
207
|
+
"edit_whole_content": lambda _: create_tool_instance(TOOL_IMPORTS["edit_whole_content"]()),
|
208
|
+
"execute_bash_command": lambda _: create_tool_instance(TOOL_IMPORTS["execute_bash_command"]()),
|
209
|
+
"input_question": lambda _: create_tool_instance(TOOL_IMPORTS["input_question"]()),
|
210
|
+
"list_directory": lambda _: create_tool_instance(TOOL_IMPORTS["list_directory"]()),
|
211
|
+
"markitdown": lambda _: create_tool_instance(TOOL_IMPORTS["markitdown"]()),
|
212
|
+
"nodejs": lambda _: create_tool_instance(TOOL_IMPORTS["nodejs"]()),
|
213
|
+
"python": lambda _: create_tool_instance(TOOL_IMPORTS["python"]()),
|
214
|
+
"read_file_block": lambda _: create_tool_instance(TOOL_IMPORTS["read_file_block"]()),
|
215
|
+
"read_file": lambda _: create_tool_instance(TOOL_IMPORTS["read_file"]()),
|
216
|
+
"read_html": lambda _: create_tool_instance(TOOL_IMPORTS["read_html"]()),
|
217
|
+
"replace_in_file": lambda _: create_tool_instance(TOOL_IMPORTS["replace_in_file"]()),
|
218
|
+
"ripgrep": lambda _: create_tool_instance(TOOL_IMPORTS["ripgrep"]()),
|
219
|
+
"search_definition_names": lambda _: create_tool_instance(TOOL_IMPORTS["search_definition_names"]()),
|
220
|
+
"wikipedia_search": lambda _: create_tool_instance(TOOL_IMPORTS["wikipedia_search"]()),
|
221
|
+
"google_news": lambda _: create_tool_instance(TOOL_IMPORTS["google_news"]()),
|
222
|
+
|
223
|
+
# Tools with specific configurations
|
224
|
+
"safe_python_interpreter": lambda _: create_tool_instance(TOOL_IMPORTS["safe_python_interpreter"](),
|
225
|
+
allowed_modules=["math", "numpy", "pandas", "datetime", "random", "statistics", "decimal"]
|
226
|
+
),
|
227
|
+
|
228
|
+
# LLM-based tools with additional parameters
|
229
|
+
"presentation_llm": lambda params: create_tool_instance(TOOL_IMPORTS["presentation_llm"](),
|
230
|
+
**get_llm_params(params),
|
231
|
+
additional_info=params.get("additional_info", "")
|
232
|
+
),
|
233
|
+
"sequence": lambda params: create_tool_instance(TOOL_IMPORTS["sequence"](),
|
234
|
+
**get_llm_params(params)
|
235
|
+
),
|
236
|
+
|
237
|
+
# Database tools
|
238
|
+
"sql_query": lambda params: create_tool_instance(TOOL_IMPORTS["sql_query"](),
|
239
|
+
**get_llm_params(params),
|
240
|
+
connection_string=params.get("connection_string", "")
|
241
|
+
),
|
242
|
+
"sql_query_advanced": lambda params: create_tool_instance(TOOL_IMPORTS["sql_query_advanced"](),
|
243
|
+
**get_llm_params(params),
|
244
|
+
connection_string=params.get("connection_string", "")
|
245
|
+
),
|
246
|
+
|
247
|
+
# Git tools
|
248
|
+
"clone_repo_tool": lambda params: create_tool_instance(TOOL_IMPORTS["clone_repo_tool"](),
|
249
|
+
auth_token=params.get("auth_token", "")
|
250
|
+
),
|
251
|
+
"bitbucket_clone_repo_tool": lambda params: create_tool_instance(TOOL_IMPORTS["bitbucket_clone_repo_tool"](),
|
252
|
+
access_token=params.get("access_token", "")
|
253
|
+
),
|
254
|
+
"bitbucket_operations_tool": lambda params: create_tool_instance(TOOL_IMPORTS["bitbucket_operations_tool"](),
|
255
|
+
access_token=params.get("access_token", "")
|
256
|
+
),
|
257
|
+
"git_operations_tool": lambda params: create_tool_instance(TOOL_IMPORTS["git_operations_tool"](),
|
258
|
+
auth_token=params.get("auth_token", "")
|
259
|
+
),
|
260
|
+
|
261
|
+
# Document conversion tools
|
262
|
+
"markdown_to_pdf": lambda _: create_tool_instance(TOOL_IMPORTS["markdown_to_pdf"]()),
|
263
|
+
"markdown_to_pptx": lambda _: create_tool_instance(TOOL_IMPORTS["markdown_to_pptx"]()),
|
264
|
+
"markdown_to_html": lambda _: create_tool_instance(TOOL_IMPORTS["markdown_to_html"]()),
|
265
|
+
"markdown_to_epub": lambda _: create_tool_instance(TOOL_IMPORTS["markdown_to_epub"]()),
|
266
|
+
"markdown_to_ipynb": lambda _: create_tool_instance(TOOL_IMPORTS["markdown_to_ipynb"]()),
|
267
|
+
"markdown_to_latex": lambda _: create_tool_instance(TOOL_IMPORTS["markdown_to_latex"]()),
|
268
|
+
"markdown_to_docx": lambda _: create_tool_instance(TOOL_IMPORTS["markdown_to_docx"]()),
|
269
|
+
|
270
|
+
# Utility tools
|
271
|
+
"csv_processor": lambda _: create_tool_instance(TOOL_IMPORTS["csv_processor"]()),
|
272
|
+
"mermaid_validator_tool": lambda _: create_tool_instance(TOOL_IMPORTS["mermaid_validator_tool"]()),
|
273
|
+
"download_file_tool": lambda _: create_tool_instance(TOOL_IMPORTS["download_file_tool"]()),
|
274
|
+
|
275
|
+
# Composio tools
|
276
|
+
"email_tool": lambda _: create_tool_instance(TOOL_IMPORTS["email_tool"](),
|
277
|
+
action="GMAIL_SEND_EMAIL",
|
278
|
+
name="email_tool",
|
279
|
+
description="Send emails via Gmail",
|
280
|
+
need_validation=False
|
281
|
+
),
|
282
|
+
"callendar_tool": lambda _: create_tool_instance(TOOL_IMPORTS["callendar_tool"](),
|
283
|
+
action="GOOGLECALENDAR_CREATE_EVENT",
|
284
|
+
name="callendar_tool",
|
285
|
+
description="Create events in Google Calendar",
|
286
|
+
need_validation=False
|
287
|
+
),
|
288
|
+
"weather_tool": lambda _: create_tool_instance(TOOL_IMPORTS["weather_tool"](),
|
289
|
+
action="WEATHERMAP_WEATHER",
|
290
|
+
name="weather_tool",
|
291
|
+
description="Get weather information for a location"
|
292
|
+
),
|
293
|
+
|
294
|
+
# NASA tools
|
295
|
+
"nasa_neows_tool": lambda _: create_tool_instance(TOOL_IMPORTS["nasa_neows_tool"]()),
|
296
|
+
"nasa_apod_tool": lambda _: create_tool_instance(TOOL_IMPORTS["nasa_apod_tool"]()),
|
297
|
+
"product_hunt_tool": lambda _: create_tool_instance(TOOL_IMPORTS["product_hunt_tool"]()),
|
298
|
+
|
299
|
+
# RAG tool
|
300
|
+
"rag_tool": lambda params: create_tool_instance(TOOL_IMPORTS["rag_tool"](),
|
301
|
+
vector_store=params.get("vector_store", "chroma"),
|
302
|
+
embedding_model=params.get("embedding_model", "openai"),
|
303
|
+
persist_dir=storage_dir,
|
304
|
+
document_paths=params.get("document_paths", [])
|
305
|
+
),
|
228
306
|
|
229
|
-
|
230
|
-
"email_tool": lambda params: create_tool_instance(TOOL_IMPORTS["email_tool"](), action="EMAIL", name="email_tool", **params),
|
231
|
-
"callendar_tool": lambda params: create_tool_instance(TOOL_IMPORTS["callendar_tool"](), action="CALLENDAR", name="callendar_tool", **params),
|
232
|
-
"weather_tool": lambda params: create_tool_instance(TOOL_IMPORTS["weather_tool"](), action="WEATHER", name="weather_tool", **params),
|
307
|
+
"vscode_server_tool": lambda _: create_tool_instance(TOOL_IMPORTS["vscode_server_tool"]())
|
233
308
|
}
|
234
309
|
|
235
310
|
# Log available tool types before processing
|
@@ -1,6 +1,7 @@
|
|
1
1
|
#!/usr/bin/env python
|
2
2
|
"""FastAPI server for the QuantaLogic agent."""
|
3
3
|
|
4
|
+
# Standard library imports
|
4
5
|
import asyncio
|
5
6
|
import functools
|
6
7
|
import json
|
@@ -14,6 +15,7 @@ from queue import Empty, Queue
|
|
14
15
|
from threading import Lock
|
15
16
|
from typing import Any, AsyncGenerator, Dict, List, Optional
|
16
17
|
|
18
|
+
# Third-party imports
|
17
19
|
import uvicorn
|
18
20
|
from fastapi import FastAPI, HTTPException, Request
|
19
21
|
from fastapi.middleware.cors import CORSMiddleware
|
@@ -24,11 +26,12 @@ from loguru import logger
|
|
24
26
|
from pydantic import BaseModel
|
25
27
|
from rich.console import Console
|
26
28
|
|
29
|
+
# Local imports
|
27
30
|
from quantalogic.agent_config import (
|
28
31
|
MODEL_NAME,
|
29
32
|
create_agent,
|
30
|
-
create_coding_agent, # noqa: F401
|
31
33
|
create_basic_agent, # noqa: F401
|
34
|
+
create_coding_agent, # noqa: F401
|
32
35
|
)
|
33
36
|
from quantalogic.console_print_events import console_print_events
|
34
37
|
|
@@ -282,9 +285,21 @@ class AgentState:
|
|
282
285
|
logger.error(f"Failed to initialize agent: {e}", exc_info=True)
|
283
286
|
raise
|
284
287
|
|
285
|
-
async def sse_ask_for_user_validation(self, question
|
286
|
-
"""
|
287
|
-
|
288
|
+
async def sse_ask_for_user_validation(self, question="Do you want to continue?", validation_id=None) -> bool:
|
289
|
+
"""
|
290
|
+
SSE-based user validation method.
|
291
|
+
|
292
|
+
Args:
|
293
|
+
question: The validation question to ask
|
294
|
+
validation_id: Optional ID for tracking validation requests
|
295
|
+
|
296
|
+
Returns:
|
297
|
+
bool: True if the user validates, False otherwise.
|
298
|
+
"""
|
299
|
+
# Ensure we have a validation_id
|
300
|
+
if validation_id is None:
|
301
|
+
validation_id = str(uuid.uuid4())
|
302
|
+
|
288
303
|
response_queue = asyncio.Queue()
|
289
304
|
|
290
305
|
# Store validation request and response queue
|
@@ -4,18 +4,20 @@ This tool provides a sophisticated interface to fetch, analyze, and format news
|
|
4
4
|
from Google News using multiple sources and advanced filtering capabilities.
|
5
5
|
"""
|
6
6
|
|
7
|
+
from datetime import datetime, timedelta
|
8
|
+
from typing import List, Optional, Dict, Any
|
9
|
+
import json
|
10
|
+
from urllib.parse import quote_plus
|
7
11
|
import asyncio
|
8
|
-
from typing import Any, Dict, List
|
9
|
-
|
10
12
|
import aiohttp
|
11
|
-
import html2text
|
12
13
|
from bs4 import BeautifulSoup
|
13
14
|
from gnews import GNews
|
14
15
|
from loguru import logger
|
16
|
+
from pydantic import Field, validator, ConfigDict
|
17
|
+
import html2text
|
15
18
|
|
19
|
+
from quantalogic.tools.tool import Tool, ToolArgument
|
16
20
|
from quantalogic.event_emitter import EventEmitter
|
17
|
-
from quantalogic.tools.llm_tool import LLMTool
|
18
|
-
from quantalogic.tools.tool import Tool, ToolArgument
|
19
21
|
|
20
22
|
|
21
23
|
class NewsArticle:
|
@@ -155,151 +157,19 @@ class GoogleNewsTool(Tool):
|
|
155
157
|
),
|
156
158
|
]
|
157
159
|
|
158
|
-
def __init__(
|
159
|
-
self,
|
160
|
-
model_name: str | None = None,
|
161
|
-
on_token: Any | None = None,
|
162
|
-
event_emitter: EventEmitter | None = None,
|
163
|
-
):
|
160
|
+
def __init__(self, name: str | None = None):
|
164
161
|
"""Initialize the GoogleNewsTool.
|
165
162
|
|
166
163
|
Args:
|
167
|
-
|
168
|
-
on_token (Any | None): Token callback for streaming
|
169
|
-
event_emitter (EventEmitter | None): Event emitter for the tool
|
164
|
+
name (str | None): Optional name override for the tool
|
170
165
|
"""
|
171
166
|
super().__init__()
|
172
|
-
|
173
|
-
|
174
|
-
self.event_emitter = event_emitter
|
175
|
-
if model_name:
|
176
|
-
self.llm_tool = LLMTool(
|
177
|
-
model_name=model_name,
|
178
|
-
on_token=on_token,
|
179
|
-
event_emitter=event_emitter,
|
180
|
-
)
|
181
|
-
|
182
|
-
def _summarize_article(self, article: Dict[str, Any]) -> str:
|
183
|
-
"""Summarize a news article using LLM.
|
184
|
-
|
185
|
-
Args:
|
186
|
-
article (Dict[str, Any]): Article data including title and description
|
187
|
-
|
188
|
-
Returns:
|
189
|
-
str: Summarized article content
|
190
|
-
"""
|
191
|
-
if not hasattr(self, 'llm_tool'):
|
192
|
-
return article.get('description', '')
|
193
|
-
|
194
|
-
prompt = f"""
|
195
|
-
Summarize this news article concisely and professionally:
|
196
|
-
|
197
|
-
Title: {article.get('title', '')}
|
198
|
-
Description: {article.get('description', '')}
|
199
|
-
|
200
|
-
Provide a 2-3 sentence summary that captures the key points.
|
201
|
-
"""
|
202
|
-
|
203
|
-
try:
|
204
|
-
summary = self.llm_tool.execute(
|
205
|
-
system_prompt="You are a professional news summarizer. Create clear, accurate, and concise summaries.",
|
206
|
-
prompt=prompt,
|
207
|
-
temperature="0.3"
|
208
|
-
)
|
209
|
-
return summary
|
210
|
-
except Exception as e:
|
211
|
-
logger.error(f"Error summarizing article: {e}")
|
212
|
-
return article.get('description', '')
|
167
|
+
if name:
|
168
|
+
self.name = name
|
213
169
|
|
214
170
|
def _format_html_output(self, articles: List[Dict[str, Any]], query: str) -> str:
|
215
171
|
"""Format articles as HTML with a modern, clean design."""
|
216
|
-
|
217
|
-
body {
|
218
|
-
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
219
|
-
line-height: 1.6;
|
220
|
-
max-width: 1200px;
|
221
|
-
margin: 0 auto;
|
222
|
-
padding: 20px;
|
223
|
-
background-color: #f5f5f5;
|
224
|
-
}
|
225
|
-
.header {
|
226
|
-
background-color: #2c3e50;
|
227
|
-
color: white;
|
228
|
-
padding: 20px;
|
229
|
-
border-radius: 8px;
|
230
|
-
margin-bottom: 20px;
|
231
|
-
}
|
232
|
-
.article {
|
233
|
-
background-color: white;
|
234
|
-
padding: 20px;
|
235
|
-
margin-bottom: 20px;
|
236
|
-
border-radius: 8px;
|
237
|
-
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
238
|
-
}
|
239
|
-
.article h2 {
|
240
|
-
color: #2c3e50;
|
241
|
-
margin-top: 0;
|
242
|
-
}
|
243
|
-
.article-meta {
|
244
|
-
color: #666;
|
245
|
-
font-size: 0.9em;
|
246
|
-
margin-bottom: 10px;
|
247
|
-
}
|
248
|
-
.summary {
|
249
|
-
border-left: 4px solid #2c3e50;
|
250
|
-
padding-left: 15px;
|
251
|
-
margin: 15px 0;
|
252
|
-
}
|
253
|
-
.source-link {
|
254
|
-
display: inline-block;
|
255
|
-
margin-top: 10px;
|
256
|
-
color: #3498db;
|
257
|
-
text-decoration: none;
|
258
|
-
}
|
259
|
-
.source-link:hover {
|
260
|
-
text-decoration: underline;
|
261
|
-
}
|
262
|
-
"""
|
263
|
-
|
264
|
-
articles_html = []
|
265
|
-
for article in articles:
|
266
|
-
article_html = f"""
|
267
|
-
<div class="article">
|
268
|
-
<h2>{article.get('title', 'No Title')}</h2>
|
269
|
-
<div class="article-meta">
|
270
|
-
<span>Source: {article.get('source', {}).get('title', 'Unknown')}</span>
|
271
|
-
<span> • </span>
|
272
|
-
<span>Published: {article.get('published_date', 'Unknown date')}</span>
|
273
|
-
</div>
|
274
|
-
<div class="summary">
|
275
|
-
{article.get('summary', 'No summary available')}
|
276
|
-
</div>
|
277
|
-
<a href="{article.get('link', '#')}" class="source-link" target="_blank">Read full article →</a>
|
278
|
-
</div>
|
279
|
-
"""
|
280
|
-
articles_html.append(article_html)
|
281
|
-
|
282
|
-
html_content = f"""
|
283
|
-
<!DOCTYPE html>
|
284
|
-
<html>
|
285
|
-
<head>
|
286
|
-
<meta charset="UTF-8">
|
287
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
288
|
-
<style>
|
289
|
-
{css_styles}
|
290
|
-
</style>
|
291
|
-
</head>
|
292
|
-
<body>
|
293
|
-
<div class="header">
|
294
|
-
<h1>News Results for: {query}</h1>
|
295
|
-
<p>Found {len(articles)} articles</p>
|
296
|
-
</div>
|
297
|
-
{''.join(articles_html)}
|
298
|
-
</body>
|
299
|
-
</html>
|
300
|
-
"""
|
301
|
-
|
302
|
-
return html_content.strip()
|
172
|
+
return json.dumps(articles, indent=2)
|
303
173
|
|
304
174
|
def _fetch_article_data(self, articles: List[NewsArticle]) -> List[NewsArticle]:
|
305
175
|
"""Fetch detailed data for multiple articles."""
|
@@ -319,38 +189,6 @@ class GoogleNewsTool(Tool):
|
|
319
189
|
|
320
190
|
return loop.run_until_complete(fetch_all())
|
321
191
|
|
322
|
-
def _format_results(self, articles: List[NewsArticle], analyze: bool) -> str:
|
323
|
-
"""Format news results with optional analysis data."""
|
324
|
-
results = ["=== Advanced Google News Results ===\n"]
|
325
|
-
|
326
|
-
for i, article in enumerate(articles, 1):
|
327
|
-
results.extend([
|
328
|
-
f"{i}. {article.title}",
|
329
|
-
f" Source: {article.source} | Date: {article.date}",
|
330
|
-
f" URL: {article.url}",
|
331
|
-
""
|
332
|
-
])
|
333
|
-
|
334
|
-
if analyze and article.summary:
|
335
|
-
results.extend([
|
336
|
-
" Summary:",
|
337
|
-
f" {article.summary}",
|
338
|
-
"",
|
339
|
-
" Key Topics:",
|
340
|
-
f" {', '.join(article.keywords[:5])}",
|
341
|
-
"",
|
342
|
-
" Sentiment Analysis:",
|
343
|
-
" Overall tone: " + self._interpret_sentiment(article.sentiment),
|
344
|
-
f" - Positive: {article.sentiment.get('pos', 0)*100:.1f}% ({self._get_sentiment_level(article.sentiment.get('pos', 0))})",
|
345
|
-
f" - Negative: {article.sentiment.get('neg', 0)*100:.1f}% ({self._get_sentiment_level(article.sentiment.get('neg', 0))})",
|
346
|
-
f" - Neutral: {article.sentiment.get('neu', 0)*100:.1f}% ({self._get_sentiment_level(article.sentiment.get('neu', 0))})",
|
347
|
-
""
|
348
|
-
])
|
349
|
-
|
350
|
-
results.append("")
|
351
|
-
|
352
|
-
return "\n".join(results)
|
353
|
-
|
354
192
|
def _get_sentiment_level(self, score: float) -> str:
|
355
193
|
"""Convert sentiment score to descriptive level."""
|
356
194
|
if score >= 0.6:
|
@@ -388,7 +226,7 @@ class GoogleNewsTool(Tool):
|
|
388
226
|
sort_by: str = "relevance",
|
389
227
|
analyze: bool = True,
|
390
228
|
) -> str:
|
391
|
-
"""Execute the Google News search
|
229
|
+
"""Execute the Google News search and return full articles in JSON format.
|
392
230
|
|
393
231
|
Args:
|
394
232
|
query (str): Search query
|
@@ -400,7 +238,7 @@ class GoogleNewsTool(Tool):
|
|
400
238
|
analyze (bool, optional): Whether to analyze results. Defaults to True.
|
401
239
|
|
402
240
|
Returns:
|
403
|
-
str:
|
241
|
+
str: JSON-formatted string containing list of article dictionaries with full content
|
404
242
|
"""
|
405
243
|
try:
|
406
244
|
# Input validation
|
@@ -442,22 +280,23 @@ class GoogleNewsTool(Tool):
|
|
442
280
|
if sort_by == "date":
|
443
281
|
articles.sort(key=lambda x: x.date if x.date else "", reverse=True)
|
444
282
|
|
445
|
-
# Process and
|
283
|
+
# Process and return full article data
|
446
284
|
processed_articles = []
|
447
285
|
for article in articles:
|
448
|
-
|
286
|
+
processed_article = {
|
449
287
|
'title': article.title,
|
450
|
-
'
|
451
|
-
'source':
|
452
|
-
'
|
453
|
-
'
|
288
|
+
'url': article.url,
|
289
|
+
'source': article.source,
|
290
|
+
'date': article.date,
|
291
|
+
'full_text': article.full_text if hasattr(article, 'full_text') else '',
|
292
|
+
'keywords': article.keywords if hasattr(article, 'keywords') else [],
|
293
|
+
'sentiment': article.sentiment if hasattr(article, 'sentiment') else {},
|
294
|
+
'summary': article.summary if hasattr(article, 'summary') else ''
|
454
295
|
}
|
455
|
-
|
456
|
-
processed_articles.append(article_copy)
|
296
|
+
processed_articles.append(processed_article)
|
457
297
|
|
458
|
-
#
|
459
|
-
|
460
|
-
return html_output
|
298
|
+
# Return pretty-printed JSON string, matching DuckDuckGo tool format
|
299
|
+
return json.dumps(processed_articles, indent=4, ensure_ascii=False)
|
461
300
|
|
462
301
|
except Exception as e:
|
463
302
|
logger.error(f"Error in GoogleNewsTool: {e}")
|
@@ -10,12 +10,14 @@ from loguru import logger
|
|
10
10
|
from .csv_processor_tool import CSVProcessorTool
|
11
11
|
from .download_file_tool import PrepareDownloadTool
|
12
12
|
from .mermaid_validator_tool import MermaidValidatorTool
|
13
|
+
from .vscode_tool import VSCodeServerTool
|
13
14
|
|
14
15
|
# Define __all__ to control what is imported with `from ... import *`
|
15
16
|
__all__ = [
|
16
17
|
'CSVProcessorTool',
|
17
18
|
'PrepareDownloadTool',
|
18
19
|
'MermaidValidatorTool',
|
20
|
+
'VSCodeServerTool',
|
19
21
|
]
|
20
22
|
|
21
23
|
# Optional: Add logging for import confirmation
|
@@ -45,14 +45,16 @@ class PrepareDownloadTool(Tool):
|
|
45
45
|
),
|
46
46
|
]
|
47
47
|
|
48
|
-
def __init__(self, upload_dir: str = "/tmp/data", base_url: str = "http://localhost:8082"):
|
48
|
+
def __init__(self, upload_dir: str = "/tmp/data", base_url: str = "http://localhost:8082", name: str = None):
|
49
49
|
"""Initialize the tool with upload directory path.
|
50
50
|
|
51
51
|
Args:
|
52
52
|
upload_dir: Directory where files are served from. Defaults to /tmp/data.
|
53
53
|
base_url: Base URL of the server. Defaults to http://localhost:8082.
|
54
54
|
"""
|
55
|
-
super().__init__()
|
55
|
+
super().__init__()
|
56
|
+
if name:
|
57
|
+
self.name = name
|
56
58
|
self.upload_dir = upload_dir
|
57
59
|
self.base_url = base_url.rstrip('/')
|
58
60
|
# Ensure upload directory exists
|
@@ -0,0 +1,123 @@
|
|
1
|
+
"""Tool for launching VS Code Server instances."""
|
2
|
+
|
3
|
+
import os
|
4
|
+
import subprocess
|
5
|
+
import urllib.parse
|
6
|
+
from typing import Dict, Optional, Union
|
7
|
+
|
8
|
+
from loguru import logger
|
9
|
+
|
10
|
+
from quantalogic.tools.tool import Tool, ToolArgument
|
11
|
+
|
12
|
+
|
13
|
+
class VSCodeServerTool(Tool):
|
14
|
+
"""Tool for launching VS Code Server instances with configurable settings."""
|
15
|
+
|
16
|
+
name: str = "vscode_tool"
|
17
|
+
description: str = "Launches a VS Code Server instance for remote development."
|
18
|
+
need_validation: bool = True
|
19
|
+
arguments: list = [
|
20
|
+
ToolArgument(
|
21
|
+
name="workspace_path",
|
22
|
+
arg_type="string",
|
23
|
+
description="The path to the workspace directory to open in VS Code.",
|
24
|
+
required=True,
|
25
|
+
example="/path/to/workspace",
|
26
|
+
),
|
27
|
+
ToolArgument(
|
28
|
+
name="auth",
|
29
|
+
arg_type="string",
|
30
|
+
description="Authentication mode for VS Code Server ('none' or 'password').",
|
31
|
+
required=False,
|
32
|
+
example="none",
|
33
|
+
default="none",
|
34
|
+
),
|
35
|
+
ToolArgument(
|
36
|
+
name="port",
|
37
|
+
arg_type="int",
|
38
|
+
description="Port number for the VS Code Server.",
|
39
|
+
required=False,
|
40
|
+
example="8080",
|
41
|
+
default="8080",
|
42
|
+
),
|
43
|
+
]
|
44
|
+
|
45
|
+
def execute(
|
46
|
+
self,
|
47
|
+
workspace_path: str,
|
48
|
+
auth: str = "none",
|
49
|
+
port: Union[int, str] = 8080,
|
50
|
+
) -> str:
|
51
|
+
"""Launch a VS Code Server instance with the specified configuration.
|
52
|
+
|
53
|
+
Args:
|
54
|
+
workspace_path: Directory to open in VS Code
|
55
|
+
auth: Authentication mode ('none' or 'password')
|
56
|
+
port: Port number for the server
|
57
|
+
|
58
|
+
Returns:
|
59
|
+
Formatted string containing command output and status
|
60
|
+
"""
|
61
|
+
try:
|
62
|
+
# Validate workspace path
|
63
|
+
workspace_path = os.path.abspath(workspace_path)
|
64
|
+
if not os.path.exists(workspace_path):
|
65
|
+
return f"Error: Workspace path does not exist: {workspace_path}"
|
66
|
+
|
67
|
+
# Build the command
|
68
|
+
command = [
|
69
|
+
"code-server",
|
70
|
+
f"--auth={auth}",
|
71
|
+
f"--port={port}",
|
72
|
+
workspace_path
|
73
|
+
]
|
74
|
+
|
75
|
+
# Launch the server
|
76
|
+
process = subprocess.Popen(
|
77
|
+
command,
|
78
|
+
stdout=subprocess.PIPE,
|
79
|
+
stderr=subprocess.PIPE,
|
80
|
+
text=True,
|
81
|
+
encoding="utf-8"
|
82
|
+
)
|
83
|
+
|
84
|
+
# Wait briefly to check for immediate startup errors
|
85
|
+
try:
|
86
|
+
stdout, stderr = process.communicate(timeout=2)
|
87
|
+
if process.returncode is not None and process.returncode != 0:
|
88
|
+
return f"Failed to start VS Code Server: {stderr}"
|
89
|
+
except subprocess.TimeoutExpired:
|
90
|
+
# Process is still running (expected behavior)
|
91
|
+
pass
|
92
|
+
|
93
|
+
# Create URL with folder parameter
|
94
|
+
encoded_path = urllib.parse.quote(workspace_path)
|
95
|
+
url = f"http://localhost:{port}/?folder={encoded_path}"
|
96
|
+
|
97
|
+
# Return success message with connection details
|
98
|
+
return (
|
99
|
+
"<command_output>"
|
100
|
+
"<div style='background: #1e1e1e; border-radius: 8px; padding: 20px; margin: 10px 0; font-family: Arial, sans-serif;'>"
|
101
|
+
"<div style='color: #3fb950; margin-bottom: 15px;'>"
|
102
|
+
"✓ VS Code Server started successfully"
|
103
|
+
"</div>"
|
104
|
+
"<div style='background: #2d2d2d; padding: 15px; border-radius: 6px; border-left: 4px solid #58a6ff;'>"
|
105
|
+
"<div style='color: #8b949e; margin-bottom: 8px;'>Server URL:</div>"
|
106
|
+
f"<a href='{url}' style='color: #58a6ff; text-decoration: none; display: block; word-break: break-all;'>"
|
107
|
+
f"Display App builder"
|
108
|
+
"</a>"
|
109
|
+
"</div>"
|
110
|
+
"<div style='color: #8b949e; font-size: 0.9em; margin-top: 15px;'>"
|
111
|
+
"Click the link above to open VS Code in your browser"
|
112
|
+
"</div>"
|
113
|
+
"</div>"
|
114
|
+
"</command_output>"
|
115
|
+
)
|
116
|
+
|
117
|
+
except Exception as e:
|
118
|
+
logger.error(f"Error launching VS Code Server: {str(e)}")
|
119
|
+
return f"Unexpected error: {str(e)}"
|
120
|
+
|
121
|
+
|
122
|
+
if __name__ == "__main__":
|
123
|
+
tool = VSCodeServerTool()
|
@@ -1,12 +1,32 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
import asyncio
|
2
|
+
|
3
|
+
from rich.prompt import Confirm
|
3
4
|
|
4
|
-
Args:
|
5
|
-
question (str): The validation question.
|
6
5
|
|
6
|
+
async def console_ask_for_user_validation(question="Do you want to continue?", validation_id=None) -> bool:
|
7
|
+
"""Prompt the user for validation using Rich (async version).
|
8
|
+
|
9
|
+
Args:
|
10
|
+
question: The validation question to ask
|
11
|
+
validation_id: Optional ID for tracking validation requests (not used in this implementation)
|
12
|
+
|
7
13
|
Returns:
|
8
|
-
bool:
|
14
|
+
bool: True if the user validates, False otherwise.
|
9
15
|
"""
|
10
|
-
|
16
|
+
# Run the synchronous Rich prompt in a thread pool to avoid blocking
|
17
|
+
return await asyncio.to_thread(Confirm.ask, question, default=True)
|
18
|
+
|
11
19
|
|
20
|
+
def sync_console_ask_for_user_validation(question="Do you want to continue?", validation_id=None) -> bool:
|
21
|
+
"""Synchronous wrapper for console_ask_for_user_validation.
|
22
|
+
|
23
|
+
This function allows for backward compatibility with code that isn't using async/await.
|
24
|
+
|
25
|
+
Args:
|
26
|
+
question: The validation question to ask
|
27
|
+
validation_id: Optional ID for tracking validation requests (not used in this implementation)
|
28
|
+
|
29
|
+
Returns:
|
30
|
+
bool: User's confirmation
|
31
|
+
"""
|
12
32
|
return Confirm.ask(question, default=True)
|
@@ -1,12 +1,12 @@
|
|
1
1
|
quantalogic/__init__.py,sha256=qFbvfHOd_chAu536pH816E3uo6CdyAgXCpQOwMXXVnY,1076
|
2
|
-
quantalogic/agent.py,sha256=
|
2
|
+
quantalogic/agent.py,sha256=Jpi4sK9Lj0miMo51zoocCh0OW2bd_IzHgY-bWW5Ka5w,42062
|
3
3
|
quantalogic/agent_config.py,sha256=fPyMfm77BzimyfyFkTzb2ZdyZtGlr2fq5aTRETu77Vs,8202
|
4
4
|
quantalogic/agent_factory.py,sha256=LO0qsFtHxU7lcEi8dn-nDHhkh6Dl8Is6sP_3f1ap_Vg,6251
|
5
5
|
quantalogic/coding_agent.py,sha256=A-firiPWQjMC56B329Ne606_v2GsoF5-nxcuz3rVbYM,5496
|
6
6
|
quantalogic/config.py,sha256=lsJxfWFWEMqS2Asus8z4A3W7X8JWoMi3-VHxfltvSfA,423
|
7
7
|
quantalogic/console_print_events.py,sha256=yDtfOr7s5r_gLTgwkl_XoKSkUqNRZhqqq4hwR_oJsUw,2050
|
8
8
|
quantalogic/console_print_token.py,sha256=5IRVoPhwWZtSc4LpNoAsCQhCB_RnAW9chycGgyD3_5U,437
|
9
|
-
quantalogic/create_custom_agent.py,sha256=
|
9
|
+
quantalogic/create_custom_agent.py,sha256=e-InULqXFFUOfgZ9TqgsVkDTZTpVCGOPS-5wx5VQedY,21474
|
10
10
|
quantalogic/docs_cli.py,sha256=Ie6NwKQuxLKwVQ-cjhFMCttXeiHDjGhNY4hSmMtc0Qg,1664
|
11
11
|
quantalogic/event_emitter.py,sha256=e_1r6hvx5GmW84iuRkoqcjpjRiYHBk4hzujd5ZoUC6U,16777
|
12
12
|
quantalogic/flow/__init__.py,sha256=MD5FAdD6jnVnTPMIOmToKjFxHBQvLmOCT0VeaWhASBc,1295
|
@@ -42,7 +42,7 @@ quantalogic/prompts.py,sha256=bSaODtMUhPeIMt4BsOdjA6-9Efadu8LH6msdGnbLvxE,1112
|
|
42
42
|
quantalogic/quantlitellm.py,sha256=nf-awyOxP0ANoAPGHNvHfdLu8gNn65L39gl7x4saIQc,5550
|
43
43
|
quantalogic/search_agent.py,sha256=tr0cwscJ4wu_G1aumjFyvGHQ0eQv5OL5sxj17s6Ocls,2470
|
44
44
|
quantalogic/server/__init__.py,sha256=8sz_PYAUCrkM6JM5EAUeIzNM4NPW6j6UT72JVkc21WQ,91
|
45
|
-
quantalogic/server/agent_server.py,sha256=
|
45
|
+
quantalogic/server/agent_server.py,sha256=1u5rpiCNAssi7_9KMBlBlyHz7abHodcyt2seGTtyNDo,22949
|
46
46
|
quantalogic/server/models.py,sha256=_j6dAx3L_w0kiP55vcC5uykJJRfChV2K3uL_xAPnsms,1447
|
47
47
|
quantalogic/server/routes.py,sha256=00nFe6s0T4Gv8vCp0wQFjWGo1tC8FViH8h0koAJdWs4,4216
|
48
48
|
quantalogic/server/state.py,sha256=TwtL0BTp_LT-fynF1IR4k8WVXuxXWtSv3NgWG9fuUME,7369
|
@@ -87,7 +87,7 @@ quantalogic/tools/git/bitbucket_operations_tool.py,sha256=6Vdelau1VSTYREtuQgHlV-
|
|
87
87
|
quantalogic/tools/git/clone_repo_tool.py,sha256=FA_29pmEsy_71YSrt94M0rAUNt_rEo4TDvq2Y7-uinU,7565
|
88
88
|
quantalogic/tools/git/git_operations_tool.py,sha256=tZqY7fXXfiLkArV_18pEmqreqF6n6BALo0jFy4Hjzfs,20610
|
89
89
|
quantalogic/tools/google_packages/__init__.py,sha256=BIf2t1oJQTCfbh7qZZAzQGIHVQyWiZQ559OwmYrG1A0,452
|
90
|
-
quantalogic/tools/google_packages/google_news_tool.py,sha256=
|
90
|
+
quantalogic/tools/google_packages/google_news_tool.py,sha256=q9ztwUdF2phcSMAceoIIBJXMjVxwCvbFdxNF3xjkAxM,11730
|
91
91
|
quantalogic/tools/grep_app_tool.py,sha256=qAKgqMTtoH82bEZkiNlIk5oDSgVckFgxVXhU7ieTJwc,18672
|
92
92
|
quantalogic/tools/image_generation/__init__.py,sha256=7_ckDTy0ZHW34S9ZIQJqeRCZisdBbAtH-1CLO-8_yUI,455
|
93
93
|
quantalogic/tools/image_generation/dalle_e.py,sha256=SYvKZ1VbdslIKUKBy3nC0jD680-ujabBQr6iK5IFAXY,10968
|
@@ -137,17 +137,18 @@ quantalogic/tools/sql_query_tool.py,sha256=jEDZLlxOsB2bzsWlEqsqvTKiyovnRuk0Xvgtw
|
|
137
137
|
quantalogic/tools/task_complete_tool.py,sha256=L8tuyVoN07Q2hOsxx17JTW0C5Jd_N-C0i_0PtCUQUKU,929
|
138
138
|
quantalogic/tools/tool.py,sha256=FLF4Y1nqjArVUYakD0w3WcDxkRJkbSBQzTG6Sp72Z4Q,15592
|
139
139
|
quantalogic/tools/unified_diff_tool.py,sha256=o7OiYnCM5MjbPlQTpB2OmkMQRI9zjdToQmgVkhiTvOI,14148
|
140
|
-
quantalogic/tools/utilities/__init__.py,sha256=
|
140
|
+
quantalogic/tools/utilities/__init__.py,sha256=Ra61ITv4SNmMFcj5CLEkAZW9Vl9uv8Mi2qI_kr2psfo,672
|
141
141
|
quantalogic/tools/utilities/csv_processor_tool.py,sha256=Mu_EPVj6iYAclNaVX_vbkekxcNwPYwy7dW1SCY22EwY,9023
|
142
|
-
quantalogic/tools/utilities/download_file_tool.py,sha256=
|
142
|
+
quantalogic/tools/utilities/download_file_tool.py,sha256=x60wpFeMMHmcipgrMnS04l7OnQd3C8MPNyB9Oar7h8Y,6681
|
143
143
|
quantalogic/tools/utilities/mermaid_validator_tool.py,sha256=Brd6pt8OxpUgf8dm6kHqJJs_IU8V4Kc-8VDP-n1eCUM,25886
|
144
|
+
quantalogic/tools/utilities/vscode_tool.py,sha256=RNDUtWxutlJ_cATgFM-oMSsGKipwhQ-PJDluJ1yxggs,4362
|
144
145
|
quantalogic/tools/utils/__init__.py,sha256=-NtMSwxRt_G79Oo_DcDaCdLU2vLvuXIoCd34TOYEUmI,334
|
145
146
|
quantalogic/tools/utils/create_sample_database.py,sha256=h5c_uxv3eztQvHlloTZxzWt5gEzai8zfnR8-_QrUqxU,3724
|
146
147
|
quantalogic/tools/utils/generate_database_report.py,sha256=3PT34ClMvZ2O62-24cp_5lOyZHY_pBjVObMHpfyVi-s,10140
|
147
148
|
quantalogic/tools/wikipedia_search_tool.py,sha256=LXQSPH8961Efw2QNxKe-cD5ZiIYD3ufEgrxH4y5uB74,5180
|
148
149
|
quantalogic/tools/write_file_tool.py,sha256=_mx9_Zjg2oMAAVzlcHEKjZVZUxQVgbRfcoMKgWnoZcg,3764
|
149
150
|
quantalogic/utils/__init__.py,sha256=hsS3hXH5lsBQcZh2QBANY1Af2Zs1jtrgxA7kXJEWi58,680
|
150
|
-
quantalogic/utils/ask_user_validation.py,sha256=
|
151
|
+
quantalogic/utils/ask_user_validation.py,sha256=kSr7TXPTpsLR9zgwpGWgvffx8-cKAC_rdFRdLqwC22A,1176
|
151
152
|
quantalogic/utils/async_utils.py,sha256=FOizWRbHdsZwoD36dNErzunfwPlE7zDprS6RXcWuWSo,963
|
152
153
|
quantalogic/utils/check_version.py,sha256=aDTEvIn5XNNBIQ0tVOqcY3hcoznRmpsnNuwES6je1MQ,1133
|
153
154
|
quantalogic/utils/download_http_file.py,sha256=FTN3brq9WvCFvuBX-lYAhjsdYTzQT4m9m2vqlcyjkNk,3472
|
@@ -166,8 +167,8 @@ quantalogic/version_check.py,sha256=JyQFTNMDWtpHCLnN-BiakzB2cyXf6kUFsTjvmSruZi4,
|
|
166
167
|
quantalogic/welcome_message.py,sha256=o4tHdgabNuIV9kbIDPgS3_2yzJhayK30oKad2UouYDc,3020
|
167
168
|
quantalogic/xml_parser.py,sha256=bLLwIwO-VEHWF3heNS7nuPC8wgdYw9F_fVZZNW1figY,11728
|
168
169
|
quantalogic/xml_tool_parser.py,sha256=hGHA1q20JUoTNTbZYmi4FTdA5I25-AGEIP8DwZgQCNA,3897
|
169
|
-
quantalogic-0.
|
170
|
-
quantalogic-0.
|
171
|
-
quantalogic-0.
|
172
|
-
quantalogic-0.
|
173
|
-
quantalogic-0.
|
170
|
+
quantalogic-0.58.0.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
171
|
+
quantalogic-0.58.0.dist-info/METADATA,sha256=A4AY49m3p5CSFQEBxmV57Spp1EsTsyno9zTQOL5Yqd8,28783
|
172
|
+
quantalogic-0.58.0.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
|
173
|
+
quantalogic-0.58.0.dist-info/entry_points.txt,sha256=h74O_Q3qBRCrDR99qvwB4BpBGzASPUIjCfxHq6Qnups,183
|
174
|
+
quantalogic-0.58.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|