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 CHANGED
@@ -2,7 +2,8 @@
2
2
 
3
3
  import asyncio
4
4
  import os
5
- from collections.abc import Callable
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
- # Extract the name from the class name if not provided
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: {kwargs.get('name')}")
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.llm_tool", "LLMVisionTool"),
76
- "llm_image_generation": lambda: _import_tool("quantalogic.tools.llm_tool", "LLMImageGenerationTool"),
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.download_file_tool", "PrepareDownloadTool"),
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.file_tools", "EditWholeContentTool"),
82
- "read_file_block": lambda: _import_tool("quantalogic.tools.file_tools", "ReadFileBlockTool"),
83
- "read_file": lambda: _import_tool("quantalogic.tools.file_tools", "ReadFileTool"),
84
- "replace_in_file": lambda: _import_tool("quantalogic.tools.file_tools", "ReplaceInFileTool"),
85
- "list_directory": lambda: _import_tool("quantalogic.tools.file_tools", "ListDirectoryTool"),
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.execution_tools", "ExecuteBashCommandTool"),
96
- "nodejs": lambda: _import_tool("quantalogic.tools.execution_tools", "NodeJsTool"),
97
- "python": lambda: _import_tool("quantalogic.tools.execution_tools", "PythonTool"),
98
- "safe_python_interpreter": lambda: _import_tool("quantalogic.tools.execution_tools", "SafePythonInterpreterTool"),
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.database", "SQLQueryTool"),
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.download_file_tool", "PrepareDownloadTool"),
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
- # Define tool configurations using create_tool_instance for proper instantiation
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
- "llm": lambda params: create_tool_instance(TOOL_IMPORTS["llm"](), **params),
181
- "llm_vision": lambda params: create_tool_instance(TOOL_IMPORTS["llm_vision"](), **params) if vision_model_name else None,
182
- "llm_image_generation": lambda params: create_tool_instance(TOOL_IMPORTS["llm_image_generation"](), **params),
183
- "download_http_file": lambda params: create_tool_instance(TOOL_IMPORTS["download_http_file"](), **params),
184
- "duck_duck_go_search": lambda params: create_tool_instance(TOOL_IMPORTS["duck_duck_go_search"](), **params),
185
- "write_file": lambda params: create_tool_instance(TOOL_IMPORTS["write_file"](), **params),
186
- "task_complete": lambda params: create_tool_instance(TOOL_IMPORTS["task_complete"](), **params),
187
- "edit_whole_content": lambda params: create_tool_instance(TOOL_IMPORTS["edit_whole_content"](), name="edit_whole_content", **params),
188
- "execute_bash_command": lambda params: create_tool_instance(TOOL_IMPORTS["execute_bash_command"](), name="execute_bash_command", **params),
189
- "input_question": lambda params: create_tool_instance(TOOL_IMPORTS["input_question"](), name="input_question", **params),
190
- "list_directory": lambda params: create_tool_instance(TOOL_IMPORTS["list_directory"](), name="list_directory", **params),
191
- "markitdown": lambda params: create_tool_instance(TOOL_IMPORTS["markitdown"](), name="markitdown", **params),
192
- "nodejs": lambda params: create_tool_instance(TOOL_IMPORTS["nodejs"](), name="nodejs", **params),
193
- "python": lambda params: create_tool_instance(TOOL_IMPORTS["python"](), name="python", **params),
194
- "read_file_block": lambda params: create_tool_instance(TOOL_IMPORTS["read_file_block"](), name="read_file_block", **params),
195
- "read_file": lambda params: create_tool_instance(TOOL_IMPORTS["read_file"](), name="read_file", **params),
196
- "read_html": lambda params: create_tool_instance(TOOL_IMPORTS["read_html"](), name="read_html", **params),
197
- "replace_in_file": lambda params: create_tool_instance(TOOL_IMPORTS["replace_in_file"](), name="replace_in_file", **params),
198
- "ripgrep": lambda params: create_tool_instance(TOOL_IMPORTS["ripgrep"](), name="ripgrep", **params),
199
- "safe_python_interpreter": lambda params: create_tool_instance(TOOL_IMPORTS["safe_python_interpreter"](), name="safe_python_interpreter", **params),
200
- "search_definition_names": lambda params: create_tool_instance(TOOL_IMPORTS["search_definition_names"](), name="search_definition_names", **params),
201
- "wikipedia_search": lambda params: create_tool_instance(TOOL_IMPORTS["wikipedia_search"](), name="wikipedia_search", **params),
202
- "google_news": lambda params: create_tool_instance(TOOL_IMPORTS["google_news"](), name="google_news", **params),
203
- "presentation_llm": lambda params: create_tool_instance(TOOL_IMPORTS["presentation_llm"](), name="presentation_llm", **params),
204
- "sequence": lambda params: create_tool_instance(TOOL_IMPORTS["sequence"](), name="sequence", **params),
205
- "sql_query": lambda params: create_tool_instance(TOOL_IMPORTS["sql_query"](), name="sql_query", **params),
206
- "sql_query_advanced": lambda params: create_tool_instance(TOOL_IMPORTS["sql_query_advanced"](), name="sql_query_advanced", **params),
207
- "clone_repo_tool": lambda params: create_tool_instance(TOOL_IMPORTS["clone_repo_tool"](), name="clone_repo_tool", **params),
208
- "bitbucket_clone_repo_tool": lambda params: create_tool_instance(TOOL_IMPORTS["bitbucket_clone_repo_tool"](), name="bitbucket_clone_repo_tool", **params),
209
- "bitbucket_operations_tool": lambda params: create_tool_instance(TOOL_IMPORTS["bitbucket_operations_tool"](), name="bitbucket_operations_tool", **params),
210
- "git_operations_tool": lambda params: create_tool_instance(TOOL_IMPORTS["git_operations_tool"](), name="git_operations_tool", **params),
211
- "markdown_to_pdf": lambda params: create_tool_instance(TOOL_IMPORTS["markdown_to_pdf"](), name="markdown_to_pdf", **params),
212
- "markdown_to_pptx": lambda params: create_tool_instance(TOOL_IMPORTS["markdown_to_pptx"](), name="markdown_to_pptx", **params),
213
- "markdown_to_html": lambda params: create_tool_instance(TOOL_IMPORTS["markdown_to_html"](), name="markdown_to_html", **params),
214
- "markdown_to_epub": lambda params: create_tool_instance(TOOL_IMPORTS["markdown_to_epub"](), name="markdown_to_epub", **params),
215
- "markdown_to_ipynb": lambda params: create_tool_instance(TOOL_IMPORTS["markdown_to_ipynb"](), name="markdown_to_ipynb", **params),
216
- "markdown_to_latex": lambda params: create_tool_instance(TOOL_IMPORTS["markdown_to_latex"](), name="markdown_to_latex", **params),
217
- "markdown_to_docx": lambda params: create_tool_instance(TOOL_IMPORTS["markdown_to_docx"](), name="markdown_to_docx", **params),
218
- "csv_processor": lambda params: create_tool_instance(TOOL_IMPORTS["csv_processor"](), name="csv_processor", **params),
219
- "mermaid_validator_tool": lambda params: create_tool_instance(TOOL_IMPORTS["mermaid_validator_tool"](), name="mermaid_validator_tool", **params),
220
- "download_file_tool": lambda params: create_tool_instance(TOOL_IMPORTS["download_file_tool"](), name="download_file_tool", **params),
221
- "email_tool": lambda params: create_tool_instance(TOOL_IMPORTS["email_tool"](), name="email_tool", **params),
222
- "callendar_tool": lambda params: create_tool_instance(TOOL_IMPORTS["callendar_tool"](), name="callendar_tool", **params),
223
- "weather_tool": lambda params: create_tool_instance(TOOL_IMPORTS["weather_tool"](), name="weather_tool", **params),
224
- "nasa_neows_tool": lambda params: create_tool_instance(TOOL_IMPORTS["nasa_neows_tool"](), name="nasa_neows_tool", **params),
225
- "nasa_apod_tool": lambda params: create_tool_instance(TOOL_IMPORTS["nasa_apod_tool"](), name="nasa_apod_tool", **params),
226
- "product_hunt_tool": lambda params: create_tool_instance(TOOL_IMPORTS["product_hunt_tool"](), name="product_hunt_tool", **params),
227
- "rag_tool": lambda params: create_tool_instance(TOOL_IMPORTS["rag_tool"](), name="rag_tool", **params),
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
- # Special handling for Composio tools
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: str = "Do you want to continue?") -> bool:
286
- """SSE-based user validation method."""
287
- validation_id = str(uuid.uuid4())
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
- model_name (str | None): Name of the LLM model to use for summarization
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
- self.model_name = model_name
173
- self.on_token = on_token
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
- css_styles = """
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 with summarization and HTML formatting.
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: HTML formatted news results with summaries
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 summarize each article
283
+ # Process and return full article data
446
284
  processed_articles = []
447
285
  for article in articles:
448
- article_copy = {
286
+ processed_article = {
449
287
  'title': article.title,
450
- 'link': article.url,
451
- 'source': {'title': article.source},
452
- 'published_date': article.date,
453
- 'description': article.full_text if hasattr(article, 'full_text') else '',
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
- article_copy['summary'] = self._summarize_article(article_copy)
456
- processed_articles.append(article_copy)
296
+ processed_articles.append(processed_article)
457
297
 
458
- # Format results as HTML
459
- html_output = self._format_html_output(processed_articles, query)
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
- def console_ask_for_user_validation(question: str = "Do you want to continue?") -> bool:
2
- """Prompt the user for validation using Rich.
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: User's confirmation.
14
+ bool: True if the user validates, False otherwise.
9
15
  """
10
- from rich.prompt import Confirm
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,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: quantalogic
3
- Version: 0.57.0
3
+ Version: 0.58.0
4
4
  Summary: QuantaLogic ReAct Agents
5
5
  Author: Raphaël MANSUY
6
6
  Author-email: raphael.mansuy@gmail.com
@@ -1,12 +1,12 @@
1
1
  quantalogic/__init__.py,sha256=qFbvfHOd_chAu536pH816E3uo6CdyAgXCpQOwMXXVnY,1076
2
- quantalogic/agent.py,sha256=8Zz0kzFcOVdZSQ2FraoWirk8dWrBCKILNGbF9oPXMpY,41163
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=1ZMsbpQGHFueJJpfJIuYCWvR3LUsEtDYqDbr6OcwlWw,20850
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=VXaaWqReUSZOCX7CaKS14jria8yZn1kLEc52E2hV7ZA,22510
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=BdrSlBxCZ1PfWIKzl7_aqvkoV2wVkBH3T2g3X2VBvVQ,17443
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=M54fNYmXlTzG-nTnBVQamxSCuu8X4WzRM4bQQ-NvIC4,606
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=hw_tO6RD0tA_LH46Tathf-LzSxRl7kgEiiP76rTGdds,6616
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=gG6iv5996KRJK16JUQiEsY5JgSnLoZ7vmi6f5DXI7Ho,339
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.57.0.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
170
- quantalogic-0.57.0.dist-info/METADATA,sha256=A0PkCeUlVQdwjuQw8t3Sl3x7iFS0UxRWrQpP2NR1YKs,28783
171
- quantalogic-0.57.0.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
172
- quantalogic-0.57.0.dist-info/entry_points.txt,sha256=h74O_Q3qBRCrDR99qvwB4BpBGzASPUIjCfxHq6Qnups,183
173
- quantalogic-0.57.0.dist-info/RECORD,,
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,,