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.
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.40.0.dist-info}/METADATA +41 -1
  103. quantalogic-0.40.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.40.0.dist-info}/LICENSE +0 -0
  106. {quantalogic-0.35.0.dist-info → quantalogic-0.40.0.dist-info}/WHEEL +0 -0
  107. {quantalogic-0.35.0.dist-info → quantalogic-0.40.0.dist-info}/entry_points.txt +0 -0
@@ -1,21 +1,8 @@
1
- """LLM wrapper module for handling LiteLLM operations."""
2
-
3
- __all__ = [
4
- "generate_completion",
5
- "generate_image",
6
- "count_tokens",
7
- "get_model_max_input_tokens",
8
- "get_model_max_output_tokens",
9
- ]
10
-
1
+ # quantlitellm.py
11
2
  import os
12
- from typing import Any, Dict, List
3
+ from typing import Any, Dict
13
4
 
14
- from litellm import (
15
- completion,
16
- image_generation,
17
- token_counter,
18
- )
5
+ from litellm import aimage_generation, exceptions, token_counter
19
6
  from loguru import logger
20
7
 
21
8
  from quantalogic.get_model_info import (
@@ -25,11 +12,6 @@ from quantalogic.get_model_info import (
25
12
  )
26
13
 
27
14
 
28
- def get_model_info(model_name: str) -> dict | None:
29
- """Get model information for a given model name."""
30
- return model_info.get(model_name, None)
31
-
32
-
33
15
  class ModelProviderConfig:
34
16
  def __init__(self, prefix: str, provider: str, base_url: str, env_var: str):
35
17
  self.prefix = prefix
@@ -53,44 +35,47 @@ PROVIDERS = {
53
35
  prefix="dashscope/",
54
36
  provider="openai",
55
37
  base_url="https://dashscope-intl.aliyuncs.com/compatible-mode/v1",
56
- env_var="DASHSCOPE_API_KEY"
38
+ env_var="DASHSCOPE_API_KEY",
57
39
  ),
58
40
  "nvidia": ModelProviderConfig(
59
- prefix="nvidia/",
60
- provider="openai",
61
- base_url="https://integrate.api.nvidia.com/v1",
62
- env_var="NVIDIA_API_KEY"
41
+ prefix="nvidia/", provider="openai", base_url="https://integrate.api.nvidia.com/v1", env_var="NVIDIA_API_KEY"
63
42
  ),
64
43
  "ovh": ModelProviderConfig(
65
44
  prefix="ovh/",
66
45
  provider="openai",
67
46
  base_url="https://deepseek-r1-distill-llama-70b.endpoints.kepler.ai.cloud.ovh.net/api/openai_compat/v1",
68
- env_var="OVH_API_KEY"
69
- )
47
+ env_var="OVH_API_KEY",
48
+ ),
70
49
  }
71
50
 
72
51
 
73
- def generate_completion(**kwargs: Dict[str, Any]) -> Any:
52
+ async def acompletion(**kwargs: Dict[str, Any]) -> Any:
74
53
  """Wraps litellm completion with proper type hints."""
75
54
  model = kwargs.get("model", "")
76
-
55
+
77
56
  # Find matching provider
78
57
  for provider_name, provider_config in PROVIDERS.items():
79
58
  if model.startswith(provider_config.prefix):
80
59
  provider_config.configure(model, kwargs)
81
60
  break
82
-
83
- return completion(**kwargs)
84
61
 
62
+ from litellm import acompletion
85
63
 
86
- def generate_image(**kwargs: Dict[str, Any]) -> Any:
87
- """Wraps litellm image_generation with proper type hints."""
88
- return image_generation(**kwargs)
64
+ return await acompletion(**kwargs)
89
65
 
90
66
 
91
- def count_tokens(model: str, messages: List[Dict[str, Any]]) -> int:
92
- """Wraps litellm token_counter with proper type hints."""
93
- return token_counter(model=model, messages=messages)
67
+ # Expose the imported litellm components directly
68
+ __all__ = ["acompletion", "aimage_generation", "exceptions", "token_counter"]
69
+
70
+
71
+ def suppress_lite_llm_debug_logging():
72
+ """Suppress debug logging from LiteLLM library."""
73
+ from litellm import litellm
74
+
75
+ litellm.suppress_debug_info = True # Very important to suppress prints don't remove
76
+
77
+
78
+ suppress_lite_llm_debug_logging()
94
79
 
95
80
 
96
81
  def _get_model_info_impl(model_name: str) -> dict:
@@ -170,4 +155,9 @@ def get_model_max_output_tokens(model_name: str) -> int | None:
170
155
 
171
156
  except Exception as e:
172
157
  logger.error(f"Error getting max output tokens for {model_name}: {e}")
173
- return None
158
+ return None
159
+
160
+
161
+ def get_model_info(model_name: str) -> dict | None:
162
+ """Get model information for a given model name."""
163
+ return model_info.get(model_name, None)
@@ -13,11 +13,11 @@ from quantalogic.tools import (
13
13
 
14
14
 
15
15
  def create_search_agent(
16
- model_name: str,
17
- mode_full: bool = False,
18
- no_stream: bool = False,
16
+ model_name: str,
17
+ mode_full: bool = False,
18
+ no_stream: bool = False,
19
19
  compact_every_n_iteration: int | None = None,
20
- max_tokens_working_memory: int | None = None
20
+ max_tokens_working_memory: int | None = None,
21
21
  ) -> Agent:
22
22
  """Creates and configures a search agent with web, knowledge, and privacy-focused search tools.
23
23
 
@@ -1,6 +1,6 @@
1
1
  """Pydantic models for the QuantaLogic API."""
2
2
 
3
- from typing import Any, Dict, Optional
3
+ from typing import Any, Dict, List, Optional
4
4
 
5
5
  from pydantic import BaseModel
6
6
 
@@ -42,6 +42,9 @@ class TaskSubmission(BaseModel):
42
42
  task: str
43
43
  model_name: Optional[str] = MODEL_NAME
44
44
  max_iterations: Optional[int] = 30
45
+ mode: Optional[str] = "minimal"
46
+ expertise: Optional[str] = ""
47
+ tools: Optional[List[Dict[str, Any]]] = None
45
48
 
46
49
  model_config = {"extra": "forbid"}
47
50
 
@@ -5,13 +5,13 @@ import requests
5
5
 
6
6
  def get_task_from_file(source: str) -> str:
7
7
  """Get task content from specified file path or URL.
8
-
8
+
9
9
  Args:
10
10
  source (str): File path or URL to read task content from
11
-
11
+
12
12
  Returns:
13
13
  str: Stripped task content from the file or URL
14
-
14
+
15
15
  Raises:
16
16
  FileNotFoundError: If the local file does not exist
17
17
  PermissionError: If there are permission issues reading the file
@@ -20,11 +20,11 @@ def get_task_from_file(source: str) -> str:
20
20
  """
21
21
  try:
22
22
  # Check if source is a URL
23
- if source.startswith(('http://', 'https://')):
23
+ if source.startswith(("http://", "https://")):
24
24
  response = requests.get(source, timeout=10)
25
25
  response.raise_for_status() # Raise an exception for bad status codes
26
26
  return response.text.strip()
27
-
27
+
28
28
  # If not a URL, treat as a local file path
29
29
  with open(source, encoding="utf-8") as f:
30
30
  return f.read().strip()
@@ -84,16 +84,16 @@ def interactive_task_runner(
84
84
  while True:
85
85
  logger.debug("Waiting for user input...")
86
86
  task_content = get_multiline_input(console).strip()
87
-
87
+
88
88
  if not task_content:
89
89
  logger.info("No task provided. Exiting...")
90
90
  console.print("[yellow]No task provided. Exiting...[/yellow]")
91
91
  break
92
92
 
93
93
  # Handle commands with single return
94
- if task_content.startswith('/'):
94
+ if task_content.startswith("/"):
95
95
  command = task_content.lower()
96
- if command == '/clear':
96
+ if command == "/clear":
97
97
  logger.info("Clearing agent memory...")
98
98
  console.print("[yellow]Clearing agent memory...[/yellow]")
99
99
  agent.clear_memory()
@@ -111,7 +111,7 @@ def interactive_task_runner(
111
111
  border_style="blue",
112
112
  )
113
113
  )
114
-
114
+
115
115
  if not Confirm.ask("[bold]Are you sure you want to submit this task?[/bold]"):
116
116
  console.print("[yellow]Task submission cancelled.[/yellow]")
117
117
  if not Confirm.ask("[bold]Would you like to ask another question?[/bold]"):
@@ -127,14 +127,14 @@ def interactive_task_runner(
127
127
  )
128
128
 
129
129
  logger.debug(f"Solving task with agent: {task_content}")
130
- result = agent.solve_task(task=task_content, max_iterations=max_iterations, streaming=not no_stream,clear_memory=False)
130
+ result = agent.solve_task(
131
+ task=task_content, max_iterations=max_iterations, streaming=not no_stream, clear_memory=False
132
+ )
131
133
  logger.debug(f"Task solved with result: {result} using {max_iterations} iterations")
132
134
 
133
135
  console.print(
134
136
  Panel.fit(
135
- f"[bold]Task Result:[/bold]\n{result}",
136
- title="[bold]Execution Output[/bold]",
137
- border_style="green"
137
+ f"[bold]Task Result:[/bold]\n{result}", title="[bold]Execution Output[/bold]", border_style="green"
138
138
  )
139
139
  )
140
140
 
@@ -165,7 +165,7 @@ def task_runner(
165
165
  vision_model_name=config.vision_model_name,
166
166
  thinking_model_name=config.thinking_model_name,
167
167
  compact_every_n_iteration=config.compact_every_n_iteration,
168
- max_tokens_working_memory=config.max_tokens_working_memory
168
+ max_tokens_working_memory=config.max_tokens_working_memory,
169
169
  )
170
170
 
171
171
  AgentRegistry.register_agent("main_agent", agent)
@@ -176,14 +176,14 @@ def task_runner(
176
176
  logger.debug(f"Solving task with agent: {task_content}")
177
177
  if config.max_iterations < 1:
178
178
  raise ValueError("max_iterations must be greater than 0")
179
- result = agent.solve_task(task=task_content, max_iterations=config.max_iterations, streaming=not config.no_stream)
179
+ result = agent.solve_task(
180
+ task=task_content, max_iterations=config.max_iterations, streaming=not config.no_stream
181
+ )
180
182
  logger.debug(f"Task solved with result: {result} using {config.max_iterations} iterations")
181
183
 
182
184
  console.print(
183
185
  Panel.fit(
184
- f"[bold]Task Result:[/bold]\n{result}",
185
- title="[bold]Execution Output[/bold]",
186
- border_style="green"
186
+ f"[bold]Task Result:[/bold]\n{result}", title="[bold]Execution Output[/bold]", border_style="green"
187
187
  )
188
188
  )
189
189
  else:
@@ -194,14 +194,14 @@ def task_runner(
194
194
  logger.debug(f"Solving task with agent: {task_content}")
195
195
  if config.max_iterations < 1:
196
196
  raise ValueError("max_iterations must be greater than 0")
197
- result = agent.solve_task(task=task_content, max_iterations=config.max_iterations, streaming=not config.no_stream)
197
+ result = agent.solve_task(
198
+ task=task_content, max_iterations=config.max_iterations, streaming=not config.no_stream
199
+ )
198
200
  logger.debug(f"Task solved with result: {result} using {config.max_iterations} iterations")
199
201
 
200
202
  console.print(
201
203
  Panel.fit(
202
- f"[bold]Task Result:[/bold]\n{result}",
203
- title="[bold]Execution Output[/bold]",
204
- border_style="green"
204
+ f"[bold]Task Result:[/bold]\n{result}", title="[bold]Execution Output[/bold]", border_style="green"
205
205
  )
206
206
  )
207
207
  else:
@@ -234,7 +234,7 @@ def task_runner(
234
234
  "memory_summary",
235
235
  ]
236
236
 
237
- #def ask_continue(event: str, data: any) -> None:
237
+ # def ask_continue(event: str, data: any) -> None:
238
238
  # ## Ask for ctrl+return
239
239
  # if event == "task_think_end":
240
240
  # ## Wait return on the keyboard
@@ -253,10 +253,10 @@ def task_runner(
253
253
  if data is not None:
254
254
  console.print(data, end="", markup=False)
255
255
 
256
- #agent.event_emitter.on(
256
+ # agent.event_emitter.on(
257
257
  # event="task_think_end",
258
258
  # listener=ask_continue,
259
- #)
259
+ # )
260
260
 
261
261
  agent.event_emitter.on(
262
262
  event=events,
@@ -67,18 +67,16 @@ class ToolManager(BaseModel):
67
67
  index += 1
68
68
  return markdown
69
69
 
70
-
71
-
72
70
  def validate_and_convert_arguments(self, tool_name: str, provided_args: dict) -> dict:
73
71
  """Validates and converts arguments based on tool definition.
74
-
72
+
75
73
  Args:
76
74
  tool_name: Name of the tool to validate against
77
75
  provided_args: Dictionary of arguments to validate
78
-
76
+
79
77
  Returns:
80
78
  Dictionary of converted arguments with proper types
81
-
79
+
82
80
  Raises:
83
81
  ValueError: For missing/invalid arguments or conversion errors
84
82
  """
@@ -88,7 +86,7 @@ class ToolManager(BaseModel):
88
86
  "string": lambda x: str(x),
89
87
  "int": lambda x: int(x),
90
88
  "float": lambda x: float(x),
91
- "bool": lambda x: str(x).lower() in ['true', '1', 'yes']
89
+ "bool": lambda x: str(x).lower() in ["true", "1", "yes"],
92
90
  }
93
91
 
94
92
  for arg_def in tool.arguments:
@@ -108,12 +106,7 @@ class ToolManager(BaseModel):
108
106
  value = provided_args[arg_name]
109
107
 
110
108
  # Handle empty string for non-string types by replacing with default if available
111
- if (
112
- arg_type != "string"
113
- and isinstance(value, str)
114
- and value.strip() == ""
115
- and default is not None
116
- ):
109
+ if arg_type != "string" and isinstance(value, str) and value.strip() == "" and default is not None:
117
110
  logger.debug(f"Replaced empty string for argument {arg_name} with default value {default}")
118
111
  value = default
119
112
  provided_args[arg_name] = value # Update to ensure validation uses the default
@@ -123,9 +116,7 @@ class ToolManager(BaseModel):
123
116
  try:
124
117
  converted = type_conversion[arg_type](value)
125
118
  except (ValueError, TypeError) as e:
126
- raise ValueError(
127
- f"Invalid value '{value}' for {arg_name} ({arg_type}): {str(e)}"
128
- )
119
+ raise ValueError(f"Invalid value '{value}' for {arg_name} ({arg_type}): {str(e)}")
129
120
  converted_args[arg_name] = converted
130
121
  else:
131
122
  converted_args[arg_name] = value # Unknown type, pass through
@@ -154,14 +145,14 @@ class ToolManager(BaseModel):
154
145
  "string": lambda x: str(x),
155
146
  "int": lambda x: int(x),
156
147
  "float": lambda x: float(x),
157
- "bool": lambda x: str(x).lower() in ['true', '1', 'yes']
148
+ "bool": lambda x: str(x).lower() in ["true", "1", "yes"],
158
149
  }
159
150
 
160
151
  for arg_def in tool.arguments:
161
152
  arg_name = arg_def.name
162
153
  arg_type = arg_def.arg_type
163
154
  required = arg_def.required
164
- default = getattr(arg_def, 'default', None)
155
+ default = getattr(arg_def, "default", None)
165
156
 
166
157
  # Handle missing arguments
167
158
  if arg_name not in provided_args:
@@ -177,9 +168,7 @@ class ToolManager(BaseModel):
177
168
  try:
178
169
  converted = type_conversion[arg_type](value)
179
170
  except (ValueError, TypeError) as e:
180
- raise ValueError(
181
- f"Invalid value '{value}' for {arg_name} ({arg_type}): {str(e)}"
182
- )
171
+ raise ValueError(f"Invalid value '{value}' for {arg_name} ({arg_type}): {str(e)}")
183
172
  converted_args[arg_name] = converted
184
173
  else:
185
174
  converted_args[arg_name] = value # Unknown type, pass through
@@ -189,4 +178,4 @@ class ToolManager(BaseModel):
189
178
  if extra_args:
190
179
  raise ValueError(f"Unexpected arguments: {', '.join(extra_args)}")
191
180
 
192
- return converted_args
181
+ return converted_args
@@ -1,71 +1,101 @@
1
1
  """Tools for the QuantaLogic agent."""
2
2
 
3
- from .agent_tool import AgentTool
4
- from .dalle_e import LLMImageGenerationTool
5
- from .download_http_file_tool import DownloadHttpFileTool
6
- from .duckduckgo_search_tool import DuckDuckGoSearchTool
7
- from .edit_whole_content_tool import EditWholeContentTool
8
- from .elixir_tool import ElixirTool
9
- from .execute_bash_command_tool import ExecuteBashCommandTool
10
- from .generate_database_report_tool import GenerateDatabaseReportTool
11
- from .grep_app_tool import GrepAppTool
12
- from .input_question_tool import InputQuestionTool
13
- from .jinja_tool import JinjaTool
14
- from .list_directory_tool import ListDirectoryTool
15
- from .llm_tool import LLMTool
16
- from .llm_vision_tool import LLMVisionTool
17
- from .markitdown_tool import MarkitdownTool
18
- from .nodejs_tool import NodeJsTool
19
- from .python_tool import PythonTool
20
- from .read_file_block_tool import ReadFileBlockTool
21
- from .read_file_tool import ReadFileTool
22
- from .read_html_tool import ReadHTMLTool
23
- from .replace_in_file_tool import ReplaceInFileTool
24
- from .ripgrep_tool import RipgrepTool
25
- from .safe_python_interpreter_tool import SafePythonInterpreterTool
26
- from .search_definition_names import SearchDefinitionNames
27
- from .sequence_tool import SequenceTool
28
- from .serpapi_search_tool import SerpApiSearchTool
29
- from .sql_query_tool import SQLQueryTool
30
- from .task_complete_tool import TaskCompleteTool
31
- from .tool import Tool, ToolArgument
32
- from .unified_diff_tool import UnifiedDiffTool
33
- from .wikipedia_search_tool import WikipediaSearchTool
34
- from .write_file_tool import WriteFileTool
3
+ import importlib
4
+ import sys
5
+ from typing import Any, Dict
35
6
 
36
- __all__ = [
37
- "WikipediaSearchTool",
38
- "SerpApiSearchTool",
39
- "DuckDuckGoSearchTool",
40
- "Tool",
41
- "ToolArgument",
42
- "TaskCompleteTool",
43
- "ReadFileTool",
44
- "WriteFileTool",
45
- "InputQuestionTool",
46
- "ListDirectoryTool",
47
- "LLMTool",
48
- "LLMVisionTool",
49
- "ExecuteBashCommandTool",
50
- "PythonTool",
51
- "ElixirTool",
52
- "NodeJsTool",
53
- "UnifiedDiffTool",
54
- "ReplaceInFileTool",
55
- "AgentTool",
56
- "ReadFileBlockTool",
57
- "RipgrepTool",
58
- "SearchDefinitionNames",
59
- "MarkitdownTool",
60
- "DownloadHttpFileTool",
61
- "EditWholeContentTool",
62
- "JinjaTool",
63
- "LLMImageGenerationTool",
64
- "ReadHTMLTool",
65
- "GrepAppTool",
66
- "GenerateDatabaseReportTool",
67
- 'SQLQueryTool',
68
- 'SafePythonInterpreterTool'
69
- 'LLMGenerationTool',
70
- 'SequenceTool'
71
- ]
7
+
8
+ class LazyLoader:
9
+ """
10
+ Lazily import a module only when its attributes are accessed.
11
+ This helps reduce startup time by deferring imports until needed.
12
+ """
13
+ def __init__(self, module_path: str):
14
+ self.module_path = module_path
15
+ self._module = None
16
+
17
+ def __getattr__(self, name: str) -> Any:
18
+ if self._module is None:
19
+ self._module = importlib.import_module(self.module_path)
20
+ return getattr(self._module, name)
21
+
22
+
23
+ # Map of tool names to their import paths
24
+ _TOOL_IMPORTS = {
25
+ "AgentTool": ".agent_tool",
26
+ "ComposioTool": ".composio.composio",
27
+ "GenerateDatabaseReportTool": ".database.generate_database_report_tool",
28
+ "SQLQueryToolAdvanced": ".database.sql_query_tool_advanced",
29
+ "MarkdownToDocxTool": ".document_tools.markdown_to_docx_tool",
30
+ "MarkdownToEpubTool": ".document_tools.markdown_to_epub_tool",
31
+ "MarkdownToHtmlTool": ".document_tools.markdown_to_html_tool",
32
+ "MarkdownToIpynbTool": ".document_tools.markdown_to_ipynb_tool",
33
+ "MarkdownToLatexTool": ".document_tools.markdown_to_latex_tool",
34
+ "MarkdownToPdfTool": ".document_tools.markdown_to_pdf_tool",
35
+ "MarkdownToPptxTool": ".document_tools.markdown_to_pptx_tool",
36
+ "DownloadHttpFileTool": ".download_http_file_tool",
37
+ "DuckDuckGoSearchTool": ".duckduckgo_search_tool",
38
+ "EditWholeContentTool": ".edit_whole_content_tool",
39
+ "ElixirTool": ".elixir_tool",
40
+ "ExecuteBashCommandTool": ".execute_bash_command_tool",
41
+ "BitbucketCloneTool": ".git.bitbucket_clone_repo_tool",
42
+ "BitbucketOperationsTool": ".git.bitbucket_operations_tool",
43
+ "CloneRepoTool": ".git.clone_repo_tool",
44
+ "GitOperationsTool": ".git.git_operations_tool",
45
+ "GoogleNewsTool": ".google_packages.google_news_tool",
46
+ "GrepAppTool": ".grep_app_tool",
47
+ "LLMImageGenerationTool": ".image_generation.dalle_e",
48
+ "InputQuestionTool": ".input_question_tool",
49
+ "JinjaTool": ".jinja_tool",
50
+ "ListDirectoryTool": ".list_directory_tool",
51
+ "LLMTool": ".llm_tool",
52
+ "LLMVisionTool": ".llm_vision_tool",
53
+ "MarkitdownTool": ".markitdown_tool",
54
+ "NasaApodTool": ".nasa_packages.nasa_apod_tool",
55
+ "NasaNeoWsTool": ".nasa_packages.nasa_neows_tool",
56
+ "NodeJsTool": ".nodejs_tool",
57
+ "PresentationLLMTool": ".presentation_tools.presentation_llm_tool",
58
+ "ProductHuntTool": ".product_hunt.product_hunt_tool",
59
+ "PythonTool": ".python_tool",
60
+ "RagTool": ".rag_tool.rag_tool",
61
+ "ReadFileBlockTool": ".read_file_block_tool",
62
+ "ReadFileTool": ".read_file_tool",
63
+ "ReadHTMLTool": ".read_html_tool",
64
+ "ReplaceInFileTool": ".replace_in_file_tool",
65
+ "RipgrepTool": ".ripgrep_tool",
66
+ "SafePythonInterpreterTool": ".safe_python_interpreter_tool",
67
+ "SearchDefinitionNames": ".search_definition_names",
68
+ "SequenceTool": ".sequence_tool",
69
+ "SerpApiSearchTool": ".serpapi_search_tool",
70
+ "SQLQueryTool": ".sql_query_tool",
71
+ "TaskCompleteTool": ".task_complete_tool",
72
+ "Tool": ".tool",
73
+ "ToolArgument": ".tool",
74
+ "UnifiedDiffTool": ".unified_diff_tool",
75
+ "CSVProcessorTool": ".utilities.csv_processor_tool",
76
+ "PrepareDownloadTool": ".utilities.download_file_tool",
77
+ "MermaidValidatorTool": ".utilities.mermaid_validator_tool",
78
+ "WikipediaSearchTool": ".wikipedia_search_tool",
79
+ "WriteFileTool": ".write_file_tool",
80
+ }
81
+
82
+ # Create lazy loaders for each module path
83
+ _lazy_modules: Dict[str, LazyLoader] = {}
84
+ for tool, path in _TOOL_IMPORTS.items():
85
+ full_path = f"{__package__}{path}"
86
+ if full_path not in _lazy_modules:
87
+ _lazy_modules[full_path] = LazyLoader(full_path)
88
+
89
+ # Set up attributes for lazy loading
90
+ _tools_to_lazy_modules = {}
91
+ for tool, path in _TOOL_IMPORTS.items():
92
+ full_path = f"{__package__}{path}"
93
+ _tools_to_lazy_modules[tool] = _lazy_modules[full_path]
94
+
95
+ # Define __all__ so that import * works properly
96
+ __all__ = list(_TOOL_IMPORTS.keys())
97
+
98
+ # Set up lazy loading for each tool
99
+ for tool, lazy_module in _tools_to_lazy_modules.items():
100
+ # This will create properties that lazily load the requested tool
101
+ setattr(sys.modules[__name__], tool, getattr(lazy_module, tool))