quantalogic 0.35.0__py3-none-any.whl → 0.40.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- quantalogic/__init__.py +0 -4
- quantalogic/agent.py +603 -363
- quantalogic/agent_config.py +233 -46
- quantalogic/agent_factory.py +34 -22
- quantalogic/coding_agent.py +16 -14
- quantalogic/config.py +2 -1
- quantalogic/console_print_events.py +4 -8
- quantalogic/console_print_token.py +2 -2
- quantalogic/docs_cli.py +15 -10
- quantalogic/event_emitter.py +258 -83
- quantalogic/flow/__init__.py +23 -0
- quantalogic/flow/flow.py +595 -0
- quantalogic/flow/flow_extractor.py +672 -0
- quantalogic/flow/flow_generator.py +89 -0
- quantalogic/flow/flow_manager.py +407 -0
- quantalogic/flow/flow_manager_schema.py +169 -0
- quantalogic/flow/flow_yaml.md +419 -0
- quantalogic/generative_model.py +109 -77
- quantalogic/get_model_info.py +5 -5
- quantalogic/interactive_text_editor.py +100 -73
- quantalogic/main.py +17 -21
- quantalogic/model_info_list.py +3 -3
- quantalogic/model_info_litellm.py +14 -14
- quantalogic/prompts.py +2 -1
- quantalogic/{llm.py → quantlitellm.py} +29 -39
- quantalogic/search_agent.py +4 -4
- quantalogic/server/models.py +4 -1
- quantalogic/task_file_reader.py +5 -5
- quantalogic/task_runner.py +20 -20
- quantalogic/tool_manager.py +10 -21
- quantalogic/tools/__init__.py +98 -68
- quantalogic/tools/composio/composio.py +416 -0
- quantalogic/tools/{generate_database_report_tool.py → database/generate_database_report_tool.py} +4 -9
- quantalogic/tools/database/sql_query_tool_advanced.py +261 -0
- quantalogic/tools/document_tools/markdown_to_docx_tool.py +620 -0
- quantalogic/tools/document_tools/markdown_to_epub_tool.py +438 -0
- quantalogic/tools/document_tools/markdown_to_html_tool.py +362 -0
- quantalogic/tools/document_tools/markdown_to_ipynb_tool.py +319 -0
- quantalogic/tools/document_tools/markdown_to_latex_tool.py +420 -0
- quantalogic/tools/document_tools/markdown_to_pdf_tool.py +623 -0
- quantalogic/tools/document_tools/markdown_to_pptx_tool.py +319 -0
- quantalogic/tools/duckduckgo_search_tool.py +2 -4
- quantalogic/tools/finance/alpha_vantage_tool.py +440 -0
- quantalogic/tools/finance/ccxt_tool.py +373 -0
- quantalogic/tools/finance/finance_llm_tool.py +387 -0
- quantalogic/tools/finance/google_finance.py +192 -0
- quantalogic/tools/finance/market_intelligence_tool.py +520 -0
- quantalogic/tools/finance/technical_analysis_tool.py +491 -0
- quantalogic/tools/finance/tradingview_tool.py +336 -0
- quantalogic/tools/finance/yahoo_finance.py +236 -0
- quantalogic/tools/git/bitbucket_clone_repo_tool.py +181 -0
- quantalogic/tools/git/bitbucket_operations_tool.py +326 -0
- quantalogic/tools/git/clone_repo_tool.py +189 -0
- quantalogic/tools/git/git_operations_tool.py +532 -0
- quantalogic/tools/google_packages/google_news_tool.py +480 -0
- quantalogic/tools/grep_app_tool.py +123 -186
- quantalogic/tools/{dalle_e.py → image_generation/dalle_e.py} +37 -27
- quantalogic/tools/jinja_tool.py +6 -10
- quantalogic/tools/language_handlers/__init__.py +22 -9
- quantalogic/tools/list_directory_tool.py +131 -42
- quantalogic/tools/llm_tool.py +45 -15
- quantalogic/tools/llm_vision_tool.py +59 -7
- quantalogic/tools/markitdown_tool.py +17 -5
- quantalogic/tools/nasa_packages/models.py +47 -0
- quantalogic/tools/nasa_packages/nasa_apod_tool.py +232 -0
- quantalogic/tools/nasa_packages/nasa_neows_tool.py +147 -0
- quantalogic/tools/nasa_packages/services.py +82 -0
- quantalogic/tools/presentation_tools/presentation_llm_tool.py +396 -0
- quantalogic/tools/product_hunt/product_hunt_tool.py +258 -0
- quantalogic/tools/product_hunt/services.py +63 -0
- quantalogic/tools/rag_tool/__init__.py +48 -0
- quantalogic/tools/rag_tool/document_metadata.py +15 -0
- quantalogic/tools/rag_tool/query_response.py +20 -0
- quantalogic/tools/rag_tool/rag_tool.py +566 -0
- quantalogic/tools/rag_tool/rag_tool_beta.py +264 -0
- quantalogic/tools/read_html_tool.py +24 -38
- quantalogic/tools/replace_in_file_tool.py +10 -10
- quantalogic/tools/safe_python_interpreter_tool.py +10 -24
- quantalogic/tools/search_definition_names.py +2 -2
- quantalogic/tools/sequence_tool.py +14 -23
- quantalogic/tools/sql_query_tool.py +17 -19
- quantalogic/tools/tool.py +39 -15
- quantalogic/tools/unified_diff_tool.py +1 -1
- quantalogic/tools/utilities/csv_processor_tool.py +234 -0
- quantalogic/tools/utilities/download_file_tool.py +179 -0
- quantalogic/tools/utilities/mermaid_validator_tool.py +661 -0
- quantalogic/tools/utils/__init__.py +1 -4
- quantalogic/tools/utils/create_sample_database.py +24 -38
- quantalogic/tools/utils/generate_database_report.py +74 -82
- quantalogic/tools/wikipedia_search_tool.py +17 -21
- quantalogic/utils/ask_user_validation.py +1 -1
- quantalogic/utils/async_utils.py +35 -0
- quantalogic/utils/check_version.py +3 -5
- quantalogic/utils/get_all_models.py +2 -1
- quantalogic/utils/git_ls.py +21 -7
- quantalogic/utils/lm_studio_model_info.py +9 -7
- quantalogic/utils/python_interpreter.py +113 -43
- quantalogic/utils/xml_utility.py +178 -0
- quantalogic/version_check.py +1 -1
- quantalogic/welcome_message.py +7 -7
- quantalogic/xml_parser.py +0 -1
- {quantalogic-0.35.0.dist-info → quantalogic-0.40.0.dist-info}/METADATA +41 -1
- quantalogic-0.40.0.dist-info/RECORD +148 -0
- quantalogic-0.35.0.dist-info/RECORD +0 -102
- {quantalogic-0.35.0.dist-info → quantalogic-0.40.0.dist-info}/LICENSE +0 -0
- {quantalogic-0.35.0.dist-info → quantalogic-0.40.0.dist-info}/WHEEL +0 -0
- {quantalogic-0.35.0.dist-info → quantalogic-0.40.0.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,261 @@
|
|
1
|
+
"""Tool for executing SQL queries and performing database operations with safety checks."""
|
2
|
+
|
3
|
+
import json
|
4
|
+
from enum import Enum
|
5
|
+
from typing import Any, Dict, List, Optional
|
6
|
+
|
7
|
+
from loguru import logger
|
8
|
+
from pydantic import Field
|
9
|
+
from sqlalchemy import create_engine, inspect, text
|
10
|
+
from sqlalchemy.engine import Engine
|
11
|
+
|
12
|
+
from quantalogic.tools.tool import Tool, ToolArgument
|
13
|
+
|
14
|
+
|
15
|
+
class QueryType(Enum):
|
16
|
+
"""Supported SQL query types"""
|
17
|
+
SELECT = "SELECT"
|
18
|
+
INSERT = "INSERT"
|
19
|
+
UPDATE = "UPDATE"
|
20
|
+
DELETE = "DELETE"
|
21
|
+
CREATE = "CREATE"
|
22
|
+
ALTER = "ALTER"
|
23
|
+
DROP = "DROP"
|
24
|
+
|
25
|
+
|
26
|
+
class SQLQueryToolAdvanced(Tool):
|
27
|
+
"""Tool for executing SQL queries and performing database operations safely."""
|
28
|
+
|
29
|
+
name: str = "sql_query_tool"
|
30
|
+
description: str = (
|
31
|
+
"Executes SQL operations including queries, inserts, updates, and schema modifications "
|
32
|
+
"with built-in safety checks and pagination support for queries."
|
33
|
+
)
|
34
|
+
arguments: list = [
|
35
|
+
ToolArgument(
|
36
|
+
name="query",
|
37
|
+
arg_type="string",
|
38
|
+
description="The SQL query/statement to execute",
|
39
|
+
required=True,
|
40
|
+
example="SELECT * FROM customers WHERE country = :country"
|
41
|
+
),
|
42
|
+
ToolArgument(
|
43
|
+
name="params",
|
44
|
+
arg_type="string",
|
45
|
+
description="JSON string containing named parameters for SQL query binding (e.g., '{\"country\": \"France\"}')",
|
46
|
+
required=False,
|
47
|
+
example='{"country": "France"}'
|
48
|
+
),
|
49
|
+
ToolArgument(
|
50
|
+
name="start_row",
|
51
|
+
arg_type="int",
|
52
|
+
description="1-based starting row number for SELECT results",
|
53
|
+
required=False,
|
54
|
+
example="1",
|
55
|
+
default="1"
|
56
|
+
),
|
57
|
+
ToolArgument(
|
58
|
+
name="end_row",
|
59
|
+
arg_type="int",
|
60
|
+
description="1-based ending row number for SELECT results",
|
61
|
+
required=False,
|
62
|
+
example="100",
|
63
|
+
default="100"
|
64
|
+
),
|
65
|
+
]
|
66
|
+
connection_string: str = Field(
|
67
|
+
...,
|
68
|
+
description="SQLAlchemy-compatible database connection string",
|
69
|
+
example="postgresql://user:password@localhost/mydb"
|
70
|
+
)
|
71
|
+
_engine: Optional[Engine] = None
|
72
|
+
|
73
|
+
def __init__(self, **data):
|
74
|
+
super().__init__(**data)
|
75
|
+
self._engine = create_engine(self.connection_string)
|
76
|
+
logger.info(f"Initialized SQL tool with engine for {self.connection_string}")
|
77
|
+
|
78
|
+
def execute(
|
79
|
+
self,
|
80
|
+
query: str,
|
81
|
+
params: Optional[str] = None,
|
82
|
+
start_row: Optional[Any] = 1,
|
83
|
+
end_row: Optional[Any] = 100
|
84
|
+
) -> str:
|
85
|
+
"""
|
86
|
+
Executes a SQL operation with parameter binding and appropriate handling based on query type.
|
87
|
+
|
88
|
+
Args:
|
89
|
+
query: SQL query/statement to execute
|
90
|
+
params: JSON string containing named parameters for query binding
|
91
|
+
start_row: Starting row for SELECT pagination
|
92
|
+
end_row: Ending row for SELECT pagination
|
93
|
+
|
94
|
+
Returns:
|
95
|
+
str: Operation results in markdown format
|
96
|
+
|
97
|
+
Raises:
|
98
|
+
ValueError: For invalid parameters or query errors
|
99
|
+
RuntimeError: For database connection issues
|
100
|
+
"""
|
101
|
+
try:
|
102
|
+
# Parse params JSON if provided
|
103
|
+
params_dict = json.loads(params) if params else {}
|
104
|
+
|
105
|
+
query_type = self._detect_query_type(query)
|
106
|
+
logger.debug(f"Executing {query_type} operation")
|
107
|
+
|
108
|
+
if query_type == QueryType.SELECT:
|
109
|
+
return self._execute_select(query, params_dict, start_row, end_row)
|
110
|
+
else:
|
111
|
+
return self._execute_modification(query, params_dict, query_type)
|
112
|
+
|
113
|
+
except json.JSONDecodeError as e:
|
114
|
+
logger.error(f"Invalid params JSON: {str(e)}")
|
115
|
+
raise ValueError(f"Invalid params JSON: {str(e)}")
|
116
|
+
except Exception as e:
|
117
|
+
logger.error(f"SQL operation failed: {str(e)}")
|
118
|
+
raise
|
119
|
+
|
120
|
+
def _detect_query_type(self, query: str) -> QueryType:
|
121
|
+
"""Detect the type of SQL operation from the query string."""
|
122
|
+
first_word = query.strip().split()[0].upper()
|
123
|
+
try:
|
124
|
+
return QueryType(first_word)
|
125
|
+
except ValueError:
|
126
|
+
raise ValueError(f"Unsupported SQL operation: {first_word}")
|
127
|
+
|
128
|
+
def _execute_select(
|
129
|
+
self,
|
130
|
+
query: str,
|
131
|
+
params: Dict[str, Any],
|
132
|
+
start_row: Any,
|
133
|
+
end_row: Any
|
134
|
+
) -> str:
|
135
|
+
"""Execute a SELECT query with pagination."""
|
136
|
+
start = self._convert_row_number(start_row, "start_row")
|
137
|
+
end = self._convert_row_number(end_row, "end_row")
|
138
|
+
|
139
|
+
if start > end:
|
140
|
+
raise ValueError(f"start_row ({start}) must be <= end_row ({end})")
|
141
|
+
|
142
|
+
with self._engine.connect() as conn:
|
143
|
+
result = conn.execute(text(query), params)
|
144
|
+
columns: List[str] = result.keys()
|
145
|
+
all_rows: List[Dict] = [dict(row._mapping) for row in result]
|
146
|
+
|
147
|
+
total_rows = len(all_rows)
|
148
|
+
actual_start = max(1, start)
|
149
|
+
actual_end = min(end, total_rows)
|
150
|
+
|
151
|
+
if actual_start > total_rows:
|
152
|
+
return f"No results found (total rows: {total_rows})"
|
153
|
+
|
154
|
+
displayed_rows = all_rows[actual_start-1:actual_end]
|
155
|
+
|
156
|
+
markdown = [
|
157
|
+
f"**SELECT Results:** `{actual_start}-{actual_end}` of `{total_rows}` rows",
|
158
|
+
self._format_table(columns, displayed_rows)
|
159
|
+
]
|
160
|
+
|
161
|
+
if actual_end < total_rows:
|
162
|
+
remaining = total_rows - actual_end
|
163
|
+
markdown.append(f"\n*Showing first {actual_end} rows - {remaining} more row{'s' if remaining > 1 else ''} available*")
|
164
|
+
|
165
|
+
return "\n".join(markdown)
|
166
|
+
|
167
|
+
def _execute_modification(
|
168
|
+
self,
|
169
|
+
query: str,
|
170
|
+
params: Dict[str, Any],
|
171
|
+
query_type: QueryType
|
172
|
+
) -> str:
|
173
|
+
"""Execute a database modification operation within a transaction."""
|
174
|
+
with self._engine.begin() as conn:
|
175
|
+
try:
|
176
|
+
if query_type in [QueryType.CREATE, QueryType.ALTER, QueryType.DROP]:
|
177
|
+
self._validate_schema_operation(query)
|
178
|
+
|
179
|
+
result = conn.execute(text(query), params)
|
180
|
+
row_count = result.rowcount
|
181
|
+
|
182
|
+
operation = query_type.value.capitalize()
|
183
|
+
message = f"**{operation} Operation Successful**\n"
|
184
|
+
|
185
|
+
if row_count >= 0: # Not all operations return a row count
|
186
|
+
message += f"Affected rows: `{row_count}`"
|
187
|
+
|
188
|
+
logger.info(f"{operation} operation completed successfully")
|
189
|
+
return message
|
190
|
+
|
191
|
+
except Exception as e:
|
192
|
+
logger.error(f"Modification operation failed: {str(e)}")
|
193
|
+
raise
|
194
|
+
|
195
|
+
def _validate_schema_operation(self, query: str):
|
196
|
+
"""Validate schema modification operations for safety."""
|
197
|
+
# Basic validation - could be extended based on requirements
|
198
|
+
query_lower = query.lower()
|
199
|
+
if "drop database" in query_lower:
|
200
|
+
raise ValueError("DROP DATABASE operations are not allowed")
|
201
|
+
|
202
|
+
inspector = inspect(self._engine)
|
203
|
+
existing_tables = inspector.get_table_names()
|
204
|
+
|
205
|
+
# Additional safety checks could be added here
|
206
|
+
logger.debug(f"Schema validation passed for operation")
|
207
|
+
|
208
|
+
def _convert_row_number(self, value: Any, field_name: str) -> int:
|
209
|
+
"""Convert and validate row number input."""
|
210
|
+
try:
|
211
|
+
# Handle numeric strings and floats
|
212
|
+
if isinstance(value, str):
|
213
|
+
if "." in value:
|
214
|
+
num = float(value)
|
215
|
+
else:
|
216
|
+
num = int(value)
|
217
|
+
else:
|
218
|
+
num = value
|
219
|
+
|
220
|
+
converted = int(num)
|
221
|
+
if converted != num: # Check if float had decimal part
|
222
|
+
raise ValueError("Decimal values are not allowed for row numbers")
|
223
|
+
|
224
|
+
if converted <= 0:
|
225
|
+
raise ValueError(f"{field_name} must be a positive integer")
|
226
|
+
|
227
|
+
return converted
|
228
|
+
except (ValueError, TypeError) as e:
|
229
|
+
raise ValueError(f"Invalid value for {field_name}: {repr(value)}") from e
|
230
|
+
|
231
|
+
def _format_table(self, columns: List[str], rows: List[Dict]) -> str:
|
232
|
+
"""Format results as markdown table with truncation."""
|
233
|
+
if not rows:
|
234
|
+
return "No results found"
|
235
|
+
|
236
|
+
# Create header
|
237
|
+
header = "| " + " | ".join(columns) + " |"
|
238
|
+
separator = "| " + " | ".join(["---"] * len(columns)) + " |"
|
239
|
+
|
240
|
+
# Create rows with truncation
|
241
|
+
body = []
|
242
|
+
for row in rows:
|
243
|
+
values = []
|
244
|
+
for col in columns:
|
245
|
+
val = str(row.get(col, ""))
|
246
|
+
# Truncate long values
|
247
|
+
values.append(val[:50] + "..." if len(val) > 50 else val)
|
248
|
+
body.append("| " + " | ".join(values) + " |")
|
249
|
+
|
250
|
+
return "\n".join([header, separator] + body)
|
251
|
+
|
252
|
+
|
253
|
+
|
254
|
+
if __name__ == "__main__":
|
255
|
+
from quantalogic.tools.utils.create_sample_database import create_sample_database
|
256
|
+
|
257
|
+
# Create and document sample database
|
258
|
+
create_sample_database("sample.db")
|
259
|
+
tool = SQLQueryToolAdvanced(connection_string="sqlite:///sample.db")
|
260
|
+
print(tool.execute("select * from customers", 1, 10))
|
261
|
+
print(tool.execute("select * from customers", 11, 20))
|