quantalogic 0.2.26__py3-none-any.whl → 0.28__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.
@@ -4,6 +4,7 @@ from quantalogic.tools import (
4
4
  DuckDuckGoSearchTool,
5
5
  EditWholeContentTool,
6
6
  ExecuteBashCommandTool,
7
+ GrepAppTool,
7
8
  InputQuestionTool,
8
9
  JinjaTool,
9
10
  ListDirectoryTool,
@@ -75,7 +76,8 @@ def create_coding_agent(
75
76
  InputQuestionTool(),
76
77
  DuckDuckGoSearchTool(),
77
78
  JinjaTool(),
78
- ReadHTMLTool()
79
+ ReadHTMLTool(),
80
+ GrepAppTool()
79
81
  ]
80
82
 
81
83
  if vision_model_name:
@@ -7,6 +7,8 @@ from .duckduckgo_search_tool import DuckDuckGoSearchTool
7
7
  from .edit_whole_content_tool import EditWholeContentTool
8
8
  from .elixir_tool import ElixirTool
9
9
  from .execute_bash_command_tool import ExecuteBashCommandTool
10
+ from .generate_database_report_tool import GenerateDatabaseReportTool
11
+ from .grep_app_tool import GrepAppTool
10
12
  from .input_question_tool import InputQuestionTool
11
13
  from .jinja_tool import JinjaTool
12
14
  from .list_directory_tool import ListDirectoryTool
@@ -22,6 +24,7 @@ from .replace_in_file_tool import ReplaceInFileTool
22
24
  from .ripgrep_tool import RipgrepTool
23
25
  from .search_definition_names import SearchDefinitionNames
24
26
  from .serpapi_search_tool import SerpApiSearchTool
27
+ from .sql_query_tool import SQLQueryTool
25
28
  from .task_complete_tool import TaskCompleteTool
26
29
  from .tool import Tool, ToolArgument
27
30
  from .unified_diff_tool import UnifiedDiffTool
@@ -56,5 +59,8 @@ __all__ = [
56
59
  "EditWholeContentTool",
57
60
  "JinjaTool",
58
61
  "LLMImageGenerationTool",
59
- "ReadHTMLTool"
62
+ "ReadHTMLTool",
63
+ "GrepAppTool",
64
+ "GenerateDatabaseReportTool",
65
+ 'SQLQueryTool'
60
66
  ]
@@ -1,14 +1,18 @@
1
- """Tool for executing bash commands and capturing their output."""
1
+ """Tool for executing bash commands with interactive input support."""
2
2
 
3
3
  import os
4
+ import pty
5
+ import select
6
+ import signal
4
7
  import subprocess
8
+ import sys
5
9
  from typing import Dict, Optional, Union
6
10
 
7
11
  from quantalogic.tools.tool import Tool, ToolArgument
8
12
 
9
13
 
10
14
  class ExecuteBashCommandTool(Tool):
11
- """Tool for executing bash commands and capturing their output."""
15
+ """Tool for executing bash commands with real-time I/O handling."""
12
16
 
13
17
  name: str = "execute_bash_tool"
14
18
  description: str = "Executes a bash command and returns its output."
@@ -35,7 +39,6 @@ class ExecuteBashCommandTool(Tool):
35
39
  required=False,
36
40
  example="60",
37
41
  ),
38
- # Removed the `env` argument from ToolArgument since it doesn't support `dict` type
39
42
  ]
40
43
 
41
44
  def execute(
@@ -45,72 +48,86 @@ class ExecuteBashCommandTool(Tool):
45
48
  timeout: Union[int, str, None] = 60,
46
49
  env: Optional[Dict[str, str]] = None,
47
50
  ) -> str:
48
- """Executes a bash command and returns its output.
49
-
50
- Args:
51
- command (str): The bash command to execute.
52
- working_dir (str, optional): Working directory for command execution. Defaults to the current directory.
53
- timeout (int or str, optional): Maximum execution time in seconds. Defaults to 60 seconds.
54
- env (dict, optional): Environment variables to set for the command execution. Defaults to the current environment.
55
-
56
- Returns:
57
- str: The command output or error message.
58
-
59
- Raises:
60
- subprocess.TimeoutExpired: If the command execution exceeds the timeout.
61
- subprocess.CalledProcessError: If the command returns a non-zero exit status.
62
- ValueError: If the timeout cannot be converted to an integer.
63
- """
64
- # Convert timeout to integer, defaulting to 60 if None or invalid
65
- try:
66
- timeout_seconds = int(timeout) if timeout else 60
67
- except (ValueError, TypeError):
68
- timeout_seconds = 60
69
-
70
- # Use the current working directory if no working directory is specified
71
- cwd = working_dir if working_dir else os.getcwd()
72
-
73
- # Use the current environment if no custom environment is specified
51
+ """Executes a bash command with interactive input handling."""
52
+ timeout_seconds = int(timeout) if timeout else 60
53
+ cwd = working_dir or os.getcwd()
74
54
  env_vars = os.environ.copy()
75
55
  if env:
76
56
  env_vars.update(env)
77
57
 
78
58
  try:
79
- # Execute the command with specified timeout, working directory, and environment
80
- result = subprocess.run(
59
+ master, slave = pty.openpty()
60
+ proc = subprocess.Popen(
81
61
  command,
82
62
  shell=True,
63
+ stdin=slave,
64
+ stdout=slave,
65
+ stderr=subprocess.STDOUT,
83
66
  cwd=cwd,
84
- capture_output=True,
85
- text=True,
86
- timeout=timeout_seconds,
87
67
  env=env_vars,
68
+ preexec_fn=os.setsid,
69
+ close_fds=True,
88
70
  )
89
-
90
- formated_result = (
71
+ os.close(slave)
72
+
73
+ stdout_buffer = []
74
+ break_loop = False
75
+
76
+ try:
77
+ while True:
78
+ rlist, _, _ = select.select([master, sys.stdin], [], [], timeout_seconds)
79
+ if not rlist:
80
+ if proc.poll() is not None:
81
+ break # Process completed but select timed out
82
+ raise subprocess.TimeoutExpired(command, timeout_seconds)
83
+
84
+ for fd in rlist:
85
+ if fd == master:
86
+ data = os.read(master, 1024).decode()
87
+ if not data:
88
+ break_loop = True
89
+ break
90
+ stdout_buffer.append(data)
91
+ sys.stdout.write(data)
92
+ sys.stdout.flush()
93
+ elif fd == sys.stdin:
94
+ user_input = os.read(sys.stdin.fileno(), 1024)
95
+ os.write(master, user_input)
96
+
97
+ # Check if process completed or EOF received
98
+ if break_loop or proc.poll() is not None:
99
+ # Read any remaining output
100
+ while True:
101
+ data = os.read(master, 1024).decode()
102
+ if not data:
103
+ break
104
+ stdout_buffer.append(data)
105
+ sys.stdout.write(data)
106
+ sys.stdout.flush()
107
+ break
108
+
109
+ except subprocess.TimeoutExpired:
110
+ os.killpg(os.getpgid(proc.pid), signal.SIGTERM)
111
+ return f"Command timed out after {timeout_seconds} seconds."
112
+ except EOFError:
113
+ pass # Process exited normally
114
+ finally:
115
+ os.close(master)
116
+ proc.wait()
117
+
118
+ stdout_content = ''.join(stdout_buffer)
119
+ return_code = proc.returncode
120
+ formatted_result = (
91
121
  "<command_output>"
92
- f" <stdout>"
93
- f"{result.stdout.strip()}"
94
- f" </stdout>"
95
- f" <stderr>"
96
- f"{result.stderr.strip()}"
97
- f" </stderr>"
98
- f" <returncode>"
99
- f" {result.returncode}"
100
- f" </returncode>"
122
+ f" <stdout>{stdout_content.strip()}</stdout>"
123
+ f" <returncode>{return_code}</returncode>"
101
124
  f"</command_output>"
102
125
  )
103
-
104
- return formated_result
105
-
106
- except subprocess.TimeoutExpired:
107
- return f"Command timed out after {timeout_seconds} seconds."
108
- except subprocess.CalledProcessError as e:
109
- return f"Command failed with error: {e.stderr.strip()}"
126
+ return formatted_result
110
127
  except Exception as e:
111
128
  return f"Unexpected error executing command: {str(e)}"
112
129
 
113
130
 
114
131
  if __name__ == "__main__":
115
132
  tool = ExecuteBashCommandTool()
116
- print(tool.to_markdown())
133
+ print(tool.to_markdown())
@@ -0,0 +1,52 @@
1
+ """Tool for generating comprehensive database documentation reports."""
2
+
3
+ from pydantic import Field, ValidationError
4
+
5
+ from quantalogic.tools.tool import Tool
6
+ from quantalogic.tools.utils.generate_database_report import generate_database_report
7
+
8
+
9
+ class GenerateDatabaseReportTool(Tool):
10
+ """Tool for generating database documentation reports from a connection string."""
11
+
12
+ name: str = "generate_database_report_tool"
13
+ description: str = (
14
+ "Generates a comprehensive Markdown database documentation report with ER diagram. "
15
+ )
16
+ arguments: list = [] # No execution arguments - connection string is configured during tool setup
17
+ connection_string: str = Field(
18
+ ...,
19
+ description="SQLAlchemy-compatible database connection string (e.g., 'sqlite:///database.db')",
20
+ example="postgresql://user:password@localhost/mydatabase"
21
+ )
22
+
23
+ def execute(self) -> str:
24
+ """Generates a database documentation report using the configured connection string.
25
+
26
+ Returns:
27
+ str: Markdown-formatted database report
28
+
29
+ Raises:
30
+ ValueError: For invalid connection strings or database connection errors
31
+ RuntimeError: For errors during report generation
32
+ """
33
+ try:
34
+ return generate_database_report(self.connection_string)
35
+ except ValidationError as e:
36
+ raise ValueError(f"Invalid connection configuration: {e}") from e
37
+ except Exception as e:
38
+ raise RuntimeError(f"Database report generation failed: {e}") from e
39
+
40
+
41
+ if __name__ == "__main__":
42
+
43
+ from quantalogic.tools.utils.create_sample_database import create_sample_database
44
+
45
+ # Create and document sample database
46
+ create_sample_database("sample.db")
47
+
48
+ # Example usage
49
+ tool = GenerateDatabaseReportTool(
50
+ connection_string="sqlite:///sample.db"
51
+ )
52
+ print(tool.execute())