quantalogic 0.35.0__py3-none-any.whl → 0.50.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.
Files changed (107) hide show
  1. quantalogic/__init__.py +0 -4
  2. quantalogic/agent.py +603 -363
  3. quantalogic/agent_config.py +233 -46
  4. quantalogic/agent_factory.py +34 -22
  5. quantalogic/coding_agent.py +16 -14
  6. quantalogic/config.py +2 -1
  7. quantalogic/console_print_events.py +4 -8
  8. quantalogic/console_print_token.py +2 -2
  9. quantalogic/docs_cli.py +15 -10
  10. quantalogic/event_emitter.py +258 -83
  11. quantalogic/flow/__init__.py +23 -0
  12. quantalogic/flow/flow.py +595 -0
  13. quantalogic/flow/flow_extractor.py +672 -0
  14. quantalogic/flow/flow_generator.py +89 -0
  15. quantalogic/flow/flow_manager.py +407 -0
  16. quantalogic/flow/flow_manager_schema.py +169 -0
  17. quantalogic/flow/flow_yaml.md +419 -0
  18. quantalogic/generative_model.py +109 -77
  19. quantalogic/get_model_info.py +5 -5
  20. quantalogic/interactive_text_editor.py +100 -73
  21. quantalogic/main.py +17 -21
  22. quantalogic/model_info_list.py +3 -3
  23. quantalogic/model_info_litellm.py +14 -14
  24. quantalogic/prompts.py +2 -1
  25. quantalogic/{llm.py → quantlitellm.py} +29 -39
  26. quantalogic/search_agent.py +4 -4
  27. quantalogic/server/models.py +4 -1
  28. quantalogic/task_file_reader.py +5 -5
  29. quantalogic/task_runner.py +20 -20
  30. quantalogic/tool_manager.py +10 -21
  31. quantalogic/tools/__init__.py +98 -68
  32. quantalogic/tools/composio/composio.py +416 -0
  33. quantalogic/tools/{generate_database_report_tool.py → database/generate_database_report_tool.py} +4 -9
  34. quantalogic/tools/database/sql_query_tool_advanced.py +261 -0
  35. quantalogic/tools/document_tools/markdown_to_docx_tool.py +620 -0
  36. quantalogic/tools/document_tools/markdown_to_epub_tool.py +438 -0
  37. quantalogic/tools/document_tools/markdown_to_html_tool.py +362 -0
  38. quantalogic/tools/document_tools/markdown_to_ipynb_tool.py +319 -0
  39. quantalogic/tools/document_tools/markdown_to_latex_tool.py +420 -0
  40. quantalogic/tools/document_tools/markdown_to_pdf_tool.py +623 -0
  41. quantalogic/tools/document_tools/markdown_to_pptx_tool.py +319 -0
  42. quantalogic/tools/duckduckgo_search_tool.py +2 -4
  43. quantalogic/tools/finance/alpha_vantage_tool.py +440 -0
  44. quantalogic/tools/finance/ccxt_tool.py +373 -0
  45. quantalogic/tools/finance/finance_llm_tool.py +387 -0
  46. quantalogic/tools/finance/google_finance.py +192 -0
  47. quantalogic/tools/finance/market_intelligence_tool.py +520 -0
  48. quantalogic/tools/finance/technical_analysis_tool.py +491 -0
  49. quantalogic/tools/finance/tradingview_tool.py +336 -0
  50. quantalogic/tools/finance/yahoo_finance.py +236 -0
  51. quantalogic/tools/git/bitbucket_clone_repo_tool.py +181 -0
  52. quantalogic/tools/git/bitbucket_operations_tool.py +326 -0
  53. quantalogic/tools/git/clone_repo_tool.py +189 -0
  54. quantalogic/tools/git/git_operations_tool.py +532 -0
  55. quantalogic/tools/google_packages/google_news_tool.py +480 -0
  56. quantalogic/tools/grep_app_tool.py +123 -186
  57. quantalogic/tools/{dalle_e.py → image_generation/dalle_e.py} +37 -27
  58. quantalogic/tools/jinja_tool.py +6 -10
  59. quantalogic/tools/language_handlers/__init__.py +22 -9
  60. quantalogic/tools/list_directory_tool.py +131 -42
  61. quantalogic/tools/llm_tool.py +45 -15
  62. quantalogic/tools/llm_vision_tool.py +59 -7
  63. quantalogic/tools/markitdown_tool.py +17 -5
  64. quantalogic/tools/nasa_packages/models.py +47 -0
  65. quantalogic/tools/nasa_packages/nasa_apod_tool.py +232 -0
  66. quantalogic/tools/nasa_packages/nasa_neows_tool.py +147 -0
  67. quantalogic/tools/nasa_packages/services.py +82 -0
  68. quantalogic/tools/presentation_tools/presentation_llm_tool.py +396 -0
  69. quantalogic/tools/product_hunt/product_hunt_tool.py +258 -0
  70. quantalogic/tools/product_hunt/services.py +63 -0
  71. quantalogic/tools/rag_tool/__init__.py +48 -0
  72. quantalogic/tools/rag_tool/document_metadata.py +15 -0
  73. quantalogic/tools/rag_tool/query_response.py +20 -0
  74. quantalogic/tools/rag_tool/rag_tool.py +566 -0
  75. quantalogic/tools/rag_tool/rag_tool_beta.py +264 -0
  76. quantalogic/tools/read_html_tool.py +24 -38
  77. quantalogic/tools/replace_in_file_tool.py +10 -10
  78. quantalogic/tools/safe_python_interpreter_tool.py +10 -24
  79. quantalogic/tools/search_definition_names.py +2 -2
  80. quantalogic/tools/sequence_tool.py +14 -23
  81. quantalogic/tools/sql_query_tool.py +17 -19
  82. quantalogic/tools/tool.py +39 -15
  83. quantalogic/tools/unified_diff_tool.py +1 -1
  84. quantalogic/tools/utilities/csv_processor_tool.py +234 -0
  85. quantalogic/tools/utilities/download_file_tool.py +179 -0
  86. quantalogic/tools/utilities/mermaid_validator_tool.py +661 -0
  87. quantalogic/tools/utils/__init__.py +1 -4
  88. quantalogic/tools/utils/create_sample_database.py +24 -38
  89. quantalogic/tools/utils/generate_database_report.py +74 -82
  90. quantalogic/tools/wikipedia_search_tool.py +17 -21
  91. quantalogic/utils/ask_user_validation.py +1 -1
  92. quantalogic/utils/async_utils.py +35 -0
  93. quantalogic/utils/check_version.py +3 -5
  94. quantalogic/utils/get_all_models.py +2 -1
  95. quantalogic/utils/git_ls.py +21 -7
  96. quantalogic/utils/lm_studio_model_info.py +9 -7
  97. quantalogic/utils/python_interpreter.py +113 -43
  98. quantalogic/utils/xml_utility.py +178 -0
  99. quantalogic/version_check.py +1 -1
  100. quantalogic/welcome_message.py +7 -7
  101. quantalogic/xml_parser.py +0 -1
  102. {quantalogic-0.35.0.dist-info → quantalogic-0.50.0.dist-info}/METADATA +40 -1
  103. quantalogic-0.50.0.dist-info/RECORD +148 -0
  104. quantalogic-0.35.0.dist-info/RECORD +0 -102
  105. {quantalogic-0.35.0.dist-info → quantalogic-0.50.0.dist-info}/LICENSE +0 -0
  106. {quantalogic-0.35.0.dist-info → quantalogic-0.50.0.dist-info}/WHEEL +0 -0
  107. {quantalogic-0.35.0.dist-info → quantalogic-0.50.0.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,416 @@
1
+ """Tool for interacting with Composio API services."""
2
+
3
+ import json
4
+ import os
5
+ import traceback
6
+ from datetime import datetime
7
+ from typing import Any, Dict, Optional
8
+
9
+ from composio import Action, ComposioToolSet
10
+ from loguru import logger
11
+ from pydantic import BaseModel, ConfigDict, Field
12
+
13
+ from quantalogic.tools.tool import Tool, ToolArgument
14
+
15
+
16
+ def setup_logger():
17
+ """Configure Loguru logger with custom format and levels."""
18
+ config = {
19
+ "handlers": [
20
+ {
21
+ "sink": os.path.join(os.path.dirname(__file__), "composio_tool.log"),
22
+ "format": "{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {name}:{function}:{line} | {message}",
23
+ "level": "DEBUG",
24
+ "rotation": "1 day",
25
+ "retention": "7 days",
26
+ "compression": "zip",
27
+ },
28
+ {
29
+ "sink": lambda msg: print(msg),
30
+ "format": "<blue>{time:HH:mm:ss}</blue> | <level>{level: <8}</level> | <cyan>{name}</cyan>:<cyan>{function}</cyan>:<cyan>{line}</cyan> | <level>{message}</level>",
31
+ "level": "INFO",
32
+ }
33
+ ],
34
+ }
35
+ logger.configure(**config)
36
+
37
+
38
+ setup_logger()
39
+
40
+
41
+ class ActionSchema(BaseModel):
42
+ """Schema for Composio actions with validation."""
43
+ name: str
44
+ description: str
45
+ parameters: Dict[str, Any]
46
+ response: Dict[str, Any]
47
+ version: str
48
+ enabled: bool = True
49
+
50
+ model_config = ConfigDict(extra="allow")
51
+
52
+ def __str__(self) -> str:
53
+ """String representation for logging purposes."""
54
+ return f"ActionSchema(name={self.name}, version={self.version}, enabled={self.enabled})"
55
+
56
+
57
+ class ComposioTool(Tool):
58
+ """Tool for executing Composio actions with proper validation and error handling."""
59
+
60
+ model_config = ConfigDict(arbitrary_types_allowed=True, extra="allow")
61
+
62
+ # Tool configuration
63
+ name: str = Field(default="composio_tool")
64
+ description: str = Field(default="")
65
+ need_validation: bool = False
66
+ arguments: list = Field(default_factory=list)
67
+
68
+ # Composio-specific fields
69
+ api_key: Optional[str] = Field(
70
+ default_factory=lambda: os.getenv("COMPOSIO_API_KEY"),
71
+ description="Composio API key for authentication",
72
+ )
73
+ action: str = Field(default="") # Single action per tool instance
74
+ action_schema: Optional[ActionSchema] = None
75
+ toolset: Optional[ComposioToolSet] = None
76
+
77
+ # Performance tracking
78
+ _last_execution_time: Optional[datetime] = None
79
+ _execution_count: int = 0
80
+ _error_count: int = 0
81
+
82
+ def __init__(
83
+ self,
84
+ action: str,
85
+ name: Optional[str] = None,
86
+ description: Optional[str] = None,
87
+ need_validation: Optional[bool] = None,
88
+ **data: Any
89
+ ):
90
+ """Initialize a Composio tool for a specific action.
91
+
92
+ Args:
93
+ action: Name of the Composio action to handle
94
+ name: Custom name for this tool instance
95
+ description: Custom description for this tool instance
96
+ need_validation: Whether this specific instance needs validation
97
+ **data: Additional data for tool initialization
98
+ """
99
+ logger.info(f"Initializing ComposioTool for action: {action}")
100
+ start_time = datetime.now()
101
+
102
+ try:
103
+ # Set instance-specific attributes
104
+ super().__init__(**data)
105
+ self.action = action.upper()
106
+
107
+ if name:
108
+ self.name = name
109
+ else:
110
+ self.name = f"composio_{self.action.lower()}"
111
+
112
+ # Validate API key
113
+ if not self.api_key:
114
+ logger.error("COMPOSIO_API_KEY environment variable is missing")
115
+ raise ValueError("COMPOSIO_API_KEY environment variable is required")
116
+
117
+ # Initialize toolset and get action schema
118
+ logger.debug(f"Setting up toolset with API key: {'*' * len(self.api_key)}")
119
+ self.toolset = ComposioToolSet(api_key=self.api_key)
120
+ self._setup_action()
121
+
122
+ # Set description if provided
123
+ if description:
124
+ self.description = description
125
+
126
+ if need_validation is not None:
127
+ self.need_validation = need_validation
128
+
129
+ init_time = datetime.now() - start_time
130
+ logger.info(f"ComposioTool initialization completed in {init_time.total_seconds():.2f}s")
131
+ logger.debug(f"Tool configuration: {self._get_tool_info()}")
132
+
133
+ except Exception as e:
134
+ logger.error(f"Failed to initialize ComposioTool: {str(e)}")
135
+ logger.debug(f"Stack trace:\n{traceback.format_exc()}")
136
+ raise
137
+
138
+ def _get_tool_info(self) -> Dict[str, Any]:
139
+ """Get tool configuration for logging purposes."""
140
+ return {
141
+ "name": self.name,
142
+ "action": self.action,
143
+ "need_validation": self.need_validation,
144
+ "schema_loaded": self.action_schema is not None,
145
+ "toolset_initialized": self.toolset is not None,
146
+ "execution_count": self._execution_count,
147
+ "error_count": self._error_count,
148
+ "last_execution": self._last_execution_time.isoformat() if self._last_execution_time else None
149
+ }
150
+
151
+ def _setup_action(self) -> None:
152
+ """Set up the Composio action with its schema and parameters."""
153
+ logger.debug(f"Setting up action: {self.action}")
154
+ start_time = datetime.now()
155
+
156
+ try:
157
+ # Get action schema
158
+ logger.debug("Fetching action schema from Composio")
159
+ (schema,) = self.toolset.get_action_schemas(actions=[self.action])
160
+ schema_dict = schema.model_dump()
161
+
162
+ # For GOOGLECALENDAR_CREATE_EVENT, ensure summary is a required parameter
163
+ if self.action == "GOOGLECALENDAR_CREATE_EVENT":
164
+ parameters = schema_dict.get("parameters", {})
165
+ properties = parameters.get("properties", {})
166
+ required_params = parameters.get("required", [])
167
+
168
+ # Add summary to properties if not present
169
+ if "summary" not in properties:
170
+ properties["summary"] = {
171
+ "type": "string",
172
+ "description": "Title of the calendar event"
173
+ }
174
+
175
+ # Add summary to required parameters if not present
176
+ if "summary" not in required_params:
177
+ required_params.append("summary")
178
+
179
+ # Update the schema
180
+ parameters["properties"] = properties
181
+ parameters["required"] = required_params
182
+ schema_dict["parameters"] = parameters
183
+
184
+ # Store schema
185
+ self.action_schema = ActionSchema(**schema_dict)
186
+ logger.debug(f"Loaded schema: {self.action_schema}")
187
+
188
+ # Log schema version and metadata
189
+ logger.debug(f"Schema version: {schema_dict.get('version', 'unknown')}")
190
+ logger.debug(f"Schema description: {schema_dict.get('description', 'No description')}")
191
+
192
+ # Set up tool description and arguments
193
+ self._update_tool_info()
194
+
195
+ setup_time = datetime.now() - start_time
196
+ logger.info(f"Action setup completed in {setup_time.total_seconds():.2f}s")
197
+
198
+ except Exception as e:
199
+ logger.error(f"Error setting up action {self.action}: {str(e)}")
200
+ logger.debug(f"Stack trace:\n{traceback.format_exc()}")
201
+ raise RuntimeError(f"Failed to set up action: {str(e)}")
202
+
203
+ def _update_tool_info(self) -> None:
204
+ """Update tool description and arguments based on the action schema."""
205
+ logger.debug("Updating tool information from schema")
206
+
207
+ if not self.action_schema:
208
+ logger.warning("No schema available for tool info update")
209
+ return
210
+
211
+ try:
212
+ # Get parameters info
213
+ schema_dict = self.action_schema.model_dump()
214
+ parameters = schema_dict.get("parameters", {})
215
+ required_params = parameters.get("required", [])
216
+ properties = parameters.get("properties", {})
217
+
218
+ logger.debug(f"Found {len(properties)} parameters in schema")
219
+ logger.debug(f"Required parameters: {required_params}")
220
+
221
+ # Build parameter details
222
+ param_details = []
223
+ total_required = 0
224
+ total_optional = 0
225
+
226
+ logger.info(f"Processing parameters for action {self.action}")
227
+ for param, param_info in properties.items():
228
+ is_required = param in required_params
229
+ param_type = param_info.get("type", "any")
230
+ param_desc = param_info.get("description", "").split(".")[0]
231
+ default_value = param_info.get("default", "Not specified")
232
+ examples = param_info.get("examples", [])
233
+
234
+ # Track parameter counts
235
+ if is_required:
236
+ total_required += 1
237
+ # Create detailed parameter description
238
+ param_detail = (
239
+ f"- `{param}`: ({param_type}*) {param_desc}\n"
240
+ f" - Default: {default_value}\n"
241
+ )
242
+ if examples:
243
+ param_detail += f" - Examples: {examples}\n"
244
+ param_details.append(param_detail)
245
+ else:
246
+ total_optional += 1
247
+
248
+ # Log detailed parameter information
249
+ logger.debug(
250
+ f"Parameter '{param}':\n"
251
+ f" - Type: {param_type}\n"
252
+ f" - Required: {is_required}\n"
253
+ f" - Description: {param_desc}\n"
254
+ f" - Default: {default_value}\n"
255
+ f" - Examples: {examples}"
256
+ )
257
+
258
+ logger.info(f"Parameter summary: {total_required} required, {total_optional} optional")
259
+
260
+ # Update description if not explicitly set
261
+ if not self.description:
262
+ # Create example parameters object with all required parameters
263
+ example_params = {}
264
+ for param, param_info in properties.items():
265
+ if param in required_params:
266
+ if param_info.get("examples"):
267
+ example_params[param] = param_info["examples"][0]
268
+ else:
269
+ # Generate a sensible example based on type
270
+ param_type = param_info.get("type", "string")
271
+ example_params[param] = self._get_example_value(param_type)
272
+
273
+ new_description = (
274
+ f"Execute Composio action {self.action}:\n"
275
+ f"Description: {self.action_schema.description}\n\n"
276
+ f"Required Parameters:\n" +
277
+ "".join(param_details) + "\n"
278
+ f"Example usage:\n"
279
+ f"```json\n"
280
+ f"{json.dumps(example_params, indent=2)}\n"
281
+ f"```"
282
+ )
283
+ self.description = new_description
284
+ logger.debug(f"Updated tool description:\n{new_description}")
285
+ else:
286
+ logger.debug("Tool description already set, skipping update")
287
+
288
+ # Update arguments with better examples
289
+ example_params = {}
290
+ for param, param_info in properties.items():
291
+ if param in required_params:
292
+ if param_info.get("examples"):
293
+ example_params[param] = param_info["examples"][0]
294
+ else:
295
+ param_type = param_info.get("type", "string")
296
+ example_params[param] = self._get_example_value(param_type)
297
+
298
+ self.arguments = [
299
+ ToolArgument(
300
+ name="action_name",
301
+ arg_type="string",
302
+ description=f"Name of the action to execute (must be {self.action})",
303
+ required=True,
304
+ example=self.action,
305
+ ),
306
+ ToolArgument(
307
+ name="parameters",
308
+ arg_type="string",
309
+ description="JSON string of parameters for the action. Required parameters:\n" +
310
+ "\n".join([f"- {p}: {properties[p].get('description', '')}" for p in required_params]),
311
+ required=True,
312
+ example=json.dumps(example_params),
313
+ ),
314
+ ]
315
+
316
+ logger.debug(f"Updated tool description and {len(self.arguments)} arguments")
317
+
318
+ # Validate parameters if needed
319
+ if self.need_validation and self.action_schema:
320
+ logger.debug("Validating parameters against schema")
321
+ schema_dict = self.action_schema.model_dump()
322
+ parameters_data = schema_dict.get("parameters", {})
323
+ required_params = parameters_data.get("required", [])
324
+ missing_params = [p for p in required_params if p not in example_params]
325
+ if missing_params:
326
+ logger.error(f"Missing required parameters: {missing_params}")
327
+ raise ValueError(f"Missing required parameters: {', '.join(missing_params)}")
328
+
329
+ except Exception as e:
330
+ logger.error(f"Error updating tool info: {str(e)}")
331
+ logger.debug(f"Stack trace:\n{traceback.format_exc()}")
332
+ raise
333
+
334
+ def _get_example_value(self, param_type: str) -> Any:
335
+ """Generate a sensible example value based on parameter type."""
336
+ type_examples = {
337
+ "string": "example_value",
338
+ "integer": 42,
339
+ "number": 3.14,
340
+ "boolean": True,
341
+ "array": [],
342
+ "object": {},
343
+ }
344
+ return type_examples.get(param_type, "example_value")
345
+
346
+ def execute(self, **kwargs: Any) -> str:
347
+ """Execute the Composio action with the given parameters."""
348
+ start_time = datetime.now()
349
+ self._execution_count += 1
350
+ self._last_execution_time = start_time
351
+
352
+ logger.info(f"Executing Composio action {self.action}")
353
+ logger.debug(f"Execution parameters: {json.dumps(kwargs, indent=2)}")
354
+
355
+ try:
356
+ action_name = kwargs.get("action_name", "").upper()
357
+ parameters = kwargs.get("parameters")
358
+
359
+ if not action_name or not parameters:
360
+ raise ValueError("Both action_name and parameters are required")
361
+
362
+ # Validate action name
363
+ if action_name != self.action:
364
+ raise ValueError(f"This tool only handles the {self.action} action")
365
+
366
+ # Convert parameters to dict if string
367
+ try:
368
+ parameters_dict = json.loads(parameters) if isinstance(parameters, str) else parameters
369
+ logger.debug(f"Parsed parameters: {json.dumps(parameters_dict, indent=2)}")
370
+ except json.JSONDecodeError as e:
371
+ logger.error(f"Invalid JSON parameters: {str(e)}")
372
+ raise ValueError(f"Invalid JSON parameters: {str(e)}")
373
+
374
+ # Validate parameters if needed
375
+ if self.need_validation and self.action_schema:
376
+ logger.debug("Validating parameters against schema")
377
+ schema_dict = self.action_schema.model_dump()
378
+ parameters_data = schema_dict.get("parameters", {})
379
+ required_params = parameters_data.get("required", [])
380
+ missing_params = [p for p in required_params if p not in parameters_dict]
381
+ if missing_params:
382
+ logger.error(f"Missing required parameters: {missing_params}")
383
+ raise ValueError(f"Missing required parameters: {', '.join(missing_params)}")
384
+
385
+ # Execute action
386
+ logger.info(f"Executing {self.action} with validated parameters")
387
+ result = self.toolset.execute_action(
388
+ action=Action(self.action),
389
+ params=parameters_dict
390
+ )
391
+
392
+ execution_time = datetime.now() - start_time
393
+ logger.info(f"Action executed successfully in {execution_time.total_seconds():.2f}s")
394
+ logger.debug(f"Action result: {json.dumps(result, indent=2)}")
395
+
396
+ return json.dumps(result, indent=2)
397
+
398
+ except Exception as e:
399
+ self._error_count += 1
400
+ logger.error(f"Error executing action: {str(e)}")
401
+ logger.debug(f"Stack trace:\n{traceback.format_exc()}")
402
+ logger.debug(f"Tool state at error: {self._get_tool_info()}")
403
+ raise RuntimeError(f"Failed to execute action: {str(e)}")
404
+
405
+
406
+ if __name__ == "__main__":
407
+ # Example usage
408
+ try:
409
+ tool = ComposioTool(action="EXAMPLE_ACTION")
410
+ result = tool.execute(
411
+ action_name="EXAMPLE_ACTION",
412
+ parameters=json.dumps({"param1": "value1"})
413
+ )
414
+ print(f"Result: {result}")
415
+ except Exception as e:
416
+ logger.error(f"Example failed: {str(e)}")
@@ -10,14 +10,12 @@ class GenerateDatabaseReportTool(Tool):
10
10
  """Tool for generating database documentation reports from a connection string."""
11
11
 
12
12
  name: str = "generate_database_report_tool"
13
- description: str = (
14
- "Generates a comprehensive Markdown database documentation report with ER diagram. "
15
- )
13
+ description: str = "Generates a comprehensive Markdown database documentation report with ER diagram. "
16
14
  arguments: list = [] # No execution arguments - connection string is configured during tool setup
17
15
  connection_string: str = Field(
18
16
  ...,
19
17
  description="SQLAlchemy-compatible database connection string (e.g., 'sqlite:///database.db')",
20
- example="postgresql://user:password@localhost/mydatabase"
18
+ example="postgresql://user:password@localhost/mydatabase",
21
19
  )
22
20
 
23
21
  def execute(self) -> str:
@@ -39,14 +37,11 @@ class GenerateDatabaseReportTool(Tool):
39
37
 
40
38
 
41
39
  if __name__ == "__main__":
42
-
43
40
  from quantalogic.tools.utils.create_sample_database import create_sample_database
44
41
 
45
42
  # Create and document sample database
46
43
  create_sample_database("sample.db")
47
44
 
48
45
  # Example usage
49
- tool = GenerateDatabaseReportTool(
50
- connection_string="sqlite:///sample.db"
51
- )
52
- print(tool.execute())
46
+ tool = GenerateDatabaseReportTool(connection_string="sqlite:///sample.db")
47
+ print(tool.execute())