autobyteus 1.0.5__py3-none-any.whl → 1.1.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 (256) hide show
  1. autobyteus/agent/agent.py +97 -222
  2. autobyteus/agent/bootstrap_steps/__init__.py +19 -0
  3. autobyteus/agent/bootstrap_steps/agent_bootstrapper.py +88 -0
  4. autobyteus/agent/bootstrap_steps/agent_runtime_queue_initialization_step.py +57 -0
  5. autobyteus/agent/bootstrap_steps/base_bootstrap_step.py +38 -0
  6. autobyteus/agent/bootstrap_steps/system_prompt_processing_step.py +93 -0
  7. autobyteus/agent/bootstrap_steps/workspace_context_initialization_step.py +47 -0
  8. autobyteus/agent/context/__init__.py +18 -0
  9. autobyteus/agent/context/agent_config.py +80 -0
  10. autobyteus/agent/context/agent_context.py +132 -0
  11. autobyteus/agent/context/agent_phase_manager.py +164 -0
  12. autobyteus/agent/context/agent_runtime_state.py +89 -0
  13. autobyteus/agent/context/phases.py +47 -0
  14. autobyteus/agent/events/__init__.py +63 -0
  15. autobyteus/agent/events/agent_events.py +147 -0
  16. autobyteus/agent/events/agent_input_event_queue_manager.py +174 -0
  17. autobyteus/agent/events/notifiers.py +104 -0
  18. autobyteus/agent/events/worker_event_dispatcher.py +118 -0
  19. autobyteus/agent/factory/__init__.py +9 -0
  20. autobyteus/agent/factory/agent_factory.py +126 -79
  21. autobyteus/agent/group/agent_group.py +155 -0
  22. autobyteus/agent/group/agent_group_context.py +81 -0
  23. autobyteus/agent/handlers/__init__.py +36 -0
  24. autobyteus/agent/handlers/approved_tool_invocation_event_handler.py +134 -0
  25. autobyteus/agent/handlers/base_event_handler.py +36 -0
  26. autobyteus/agent/handlers/event_handler_registry.py +76 -0
  27. autobyteus/agent/handlers/generic_event_handler.py +46 -0
  28. autobyteus/agent/handlers/inter_agent_message_event_handler.py +76 -0
  29. autobyteus/agent/handlers/lifecycle_event_logger.py +64 -0
  30. autobyteus/agent/handlers/llm_complete_response_received_event_handler.py +136 -0
  31. autobyteus/agent/handlers/llm_user_message_ready_event_handler.py +140 -0
  32. autobyteus/agent/handlers/tool_execution_approval_event_handler.py +85 -0
  33. autobyteus/agent/handlers/tool_invocation_request_event_handler.py +186 -0
  34. autobyteus/agent/handlers/tool_result_event_handler.py +96 -0
  35. autobyteus/agent/handlers/user_input_message_event_handler.py +77 -0
  36. autobyteus/agent/hooks/__init__.py +9 -0
  37. autobyteus/agent/hooks/base_phase_hook.py +52 -0
  38. autobyteus/agent/input_processor/__init__.py +18 -0
  39. autobyteus/agent/input_processor/base_user_input_processor.py +51 -0
  40. autobyteus/agent/input_processor/content_prefixing_input_processor.py +41 -0
  41. autobyteus/agent/input_processor/metadata_appending_input_processor.py +34 -0
  42. autobyteus/agent/input_processor/passthrough_input_processor.py +32 -0
  43. autobyteus/agent/input_processor/processor_definition.py +42 -0
  44. autobyteus/agent/input_processor/processor_meta.py +46 -0
  45. autobyteus/agent/input_processor/processor_registry.py +98 -0
  46. autobyteus/agent/llm_response_processor/__init__.py +16 -0
  47. autobyteus/agent/llm_response_processor/base_processor.py +50 -0
  48. autobyteus/agent/llm_response_processor/processor_definition.py +36 -0
  49. autobyteus/agent/llm_response_processor/processor_meta.py +37 -0
  50. autobyteus/agent/llm_response_processor/processor_registry.py +94 -0
  51. autobyteus/agent/llm_response_processor/provider_aware_tool_usage_processor.py +53 -0
  52. autobyteus/agent/message/__init__.py +20 -0
  53. autobyteus/agent/message/agent_input_user_message.py +96 -0
  54. autobyteus/agent/message/context_file.py +82 -0
  55. autobyteus/agent/message/context_file_type.py +64 -0
  56. autobyteus/agent/message/{message.py → inter_agent_message.py} +12 -12
  57. autobyteus/agent/message/{message_types.py → inter_agent_message_type.py} +8 -6
  58. autobyteus/agent/message/send_message_to.py +142 -36
  59. autobyteus/agent/remote_agent.py +240 -5
  60. autobyteus/agent/runtime/__init__.py +15 -0
  61. autobyteus/agent/runtime/agent_runtime.py +139 -0
  62. autobyteus/agent/runtime/agent_thread_pool_manager.py +88 -0
  63. autobyteus/agent/runtime/agent_worker.py +200 -0
  64. autobyteus/agent/streaming/__init__.py +15 -0
  65. autobyteus/agent/streaming/agent_event_stream.py +120 -0
  66. autobyteus/agent/streaming/queue_streamer.py +58 -0
  67. autobyteus/agent/streaming/stream_event_payloads.py +156 -0
  68. autobyteus/agent/streaming/stream_events.py +123 -0
  69. autobyteus/agent/system_prompt_processor/__init__.py +14 -0
  70. autobyteus/agent/system_prompt_processor/base_processor.py +45 -0
  71. autobyteus/agent/system_prompt_processor/processor_definition.py +40 -0
  72. autobyteus/agent/system_prompt_processor/processor_meta.py +47 -0
  73. autobyteus/agent/system_prompt_processor/processor_registry.py +119 -0
  74. autobyteus/agent/system_prompt_processor/tool_manifest_injector_processor.py +65 -0
  75. autobyteus/agent/tool_invocation.py +28 -5
  76. autobyteus/agent/utils/__init__.py +9 -0
  77. autobyteus/agent/utils/wait_for_idle.py +59 -0
  78. autobyteus/agent/workflow/__init__.py +11 -0
  79. autobyteus/agent/workflow/agentic_workflow.py +89 -0
  80. autobyteus/agent/workflow/base_agentic_workflow.py +98 -0
  81. autobyteus/agent/workspace/__init__.py +9 -0
  82. autobyteus/agent/workspace/base_workspace.py +55 -0
  83. autobyteus/cli/__init__.py +10 -0
  84. autobyteus/cli/agent_cli.py +299 -0
  85. autobyteus/events/event_emitter.py +33 -56
  86. autobyteus/events/event_manager.py +133 -66
  87. autobyteus/events/event_types.py +41 -14
  88. autobyteus/llm/api/autobyteus_llm.py +13 -15
  89. autobyteus/llm/api/bedrock_llm.py +9 -3
  90. autobyteus/llm/api/claude_llm.py +10 -5
  91. autobyteus/llm/api/deepseek_llm.py +53 -91
  92. autobyteus/llm/api/gemini_llm.py +10 -4
  93. autobyteus/llm/api/grok_llm.py +53 -77
  94. autobyteus/llm/api/groq_llm.py +10 -5
  95. autobyteus/llm/api/mistral_llm.py +10 -5
  96. autobyteus/llm/api/nvidia_llm.py +9 -4
  97. autobyteus/llm/api/ollama_llm.py +56 -48
  98. autobyteus/llm/api/openai_llm.py +20 -14
  99. autobyteus/llm/base_llm.py +95 -34
  100. autobyteus/llm/extensions/base_extension.py +3 -4
  101. autobyteus/llm/extensions/token_usage_tracking_extension.py +2 -3
  102. autobyteus/llm/llm_factory.py +12 -13
  103. autobyteus/llm/models.py +87 -8
  104. autobyteus/llm/user_message.py +73 -0
  105. autobyteus/llm/utils/llm_config.py +124 -27
  106. autobyteus/llm/utils/response_types.py +3 -2
  107. autobyteus/llm/utils/token_usage.py +7 -4
  108. autobyteus/rpc/__init__.py +73 -0
  109. autobyteus/rpc/client/__init__.py +17 -0
  110. autobyteus/rpc/client/abstract_client_connection.py +124 -0
  111. autobyteus/rpc/client/client_connection_manager.py +153 -0
  112. autobyteus/rpc/client/sse_client_connection.py +306 -0
  113. autobyteus/rpc/client/stdio_client_connection.py +280 -0
  114. autobyteus/rpc/config/__init__.py +13 -0
  115. autobyteus/rpc/config/agent_server_config.py +153 -0
  116. autobyteus/rpc/config/agent_server_registry.py +152 -0
  117. autobyteus/rpc/hosting.py +244 -0
  118. autobyteus/rpc/protocol.py +244 -0
  119. autobyteus/rpc/server/__init__.py +20 -0
  120. autobyteus/rpc/server/agent_server_endpoint.py +181 -0
  121. autobyteus/rpc/server/base_method_handler.py +40 -0
  122. autobyteus/rpc/server/method_handlers.py +259 -0
  123. autobyteus/rpc/server/sse_server_handler.py +182 -0
  124. autobyteus/rpc/server/stdio_server_handler.py +151 -0
  125. autobyteus/rpc/server_main.py +198 -0
  126. autobyteus/rpc/transport_type.py +13 -0
  127. autobyteus/tools/__init__.py +75 -0
  128. autobyteus/tools/ask_user_input.py +34 -77
  129. autobyteus/tools/base_tool.py +66 -37
  130. autobyteus/tools/bash/__init__.py +2 -0
  131. autobyteus/tools/bash/bash_executor.py +42 -79
  132. autobyteus/tools/browser/__init__.py +2 -0
  133. autobyteus/tools/browser/session_aware/browser_session_aware_navigate_to.py +50 -42
  134. autobyteus/tools/browser/session_aware/browser_session_aware_tool.py +7 -4
  135. autobyteus/tools/browser/session_aware/browser_session_aware_web_element_trigger.py +117 -125
  136. autobyteus/tools/browser/session_aware/browser_session_aware_webpage_reader.py +75 -22
  137. autobyteus/tools/browser/session_aware/browser_session_aware_webpage_screenshot_taker.py +94 -28
  138. autobyteus/tools/browser/session_aware/factory/browser_session_aware_web_element_trigger_factory.py +10 -2
  139. autobyteus/tools/browser/session_aware/factory/browser_session_aware_webpage_reader_factory.py +18 -2
  140. autobyteus/tools/browser/session_aware/factory/browser_session_aware_webpage_screenshot_taker_factory.py +10 -2
  141. autobyteus/tools/browser/session_aware/shared_browser_session_manager.py +4 -3
  142. autobyteus/tools/browser/standalone/__init__.py +7 -0
  143. autobyteus/tools/browser/standalone/factory/google_search_factory.py +17 -2
  144. autobyteus/tools/browser/standalone/factory/webpage_reader_factory.py +17 -2
  145. autobyteus/tools/browser/standalone/factory/webpage_screenshot_taker_factory.py +10 -2
  146. autobyteus/tools/browser/standalone/google_search_ui.py +104 -67
  147. autobyteus/tools/browser/standalone/navigate_to.py +52 -28
  148. autobyteus/tools/browser/standalone/web_page_pdf_generator.py +94 -0
  149. autobyteus/tools/browser/standalone/webpage_image_downloader.py +146 -61
  150. autobyteus/tools/browser/standalone/webpage_reader.py +80 -61
  151. autobyteus/tools/browser/standalone/webpage_screenshot_taker.py +91 -45
  152. autobyteus/tools/factory/__init__.py +9 -0
  153. autobyteus/tools/factory/tool_factory.py +25 -4
  154. autobyteus/tools/file/file_reader.py +22 -51
  155. autobyteus/tools/file/file_writer.py +25 -56
  156. autobyteus/tools/functional_tool.py +234 -0
  157. autobyteus/tools/image_downloader.py +49 -71
  158. autobyteus/tools/mcp/__init__.py +47 -0
  159. autobyteus/tools/mcp/call_handlers/__init__.py +18 -0
  160. autobyteus/tools/mcp/call_handlers/base_handler.py +40 -0
  161. autobyteus/tools/mcp/call_handlers/sse_handler.py +22 -0
  162. autobyteus/tools/mcp/call_handlers/stdio_handler.py +62 -0
  163. autobyteus/tools/mcp/call_handlers/streamable_http_handler.py +55 -0
  164. autobyteus/tools/mcp/config_service.py +258 -0
  165. autobyteus/tools/mcp/factory.py +70 -0
  166. autobyteus/tools/mcp/registrar.py +135 -0
  167. autobyteus/tools/mcp/schema_mapper.py +131 -0
  168. autobyteus/tools/mcp/tool.py +101 -0
  169. autobyteus/tools/mcp/types.py +96 -0
  170. autobyteus/tools/parameter_schema.py +268 -0
  171. autobyteus/tools/pdf_downloader.py +78 -79
  172. autobyteus/tools/registry/__init__.py +0 -2
  173. autobyteus/tools/registry/tool_definition.py +106 -34
  174. autobyteus/tools/registry/tool_registry.py +46 -22
  175. autobyteus/tools/timer.py +150 -102
  176. autobyteus/tools/tool_config.py +117 -0
  177. autobyteus/tools/tool_meta.py +48 -26
  178. autobyteus/tools/usage/__init__.py +6 -0
  179. autobyteus/tools/usage/formatters/__init__.py +31 -0
  180. autobyteus/tools/usage/formatters/anthropic_json_example_formatter.py +18 -0
  181. autobyteus/tools/usage/formatters/anthropic_json_schema_formatter.py +25 -0
  182. autobyteus/tools/usage/formatters/base_formatter.py +42 -0
  183. autobyteus/tools/usage/formatters/default_json_example_formatter.py +42 -0
  184. autobyteus/tools/usage/formatters/default_json_schema_formatter.py +28 -0
  185. autobyteus/tools/usage/formatters/default_xml_example_formatter.py +55 -0
  186. autobyteus/tools/usage/formatters/default_xml_schema_formatter.py +46 -0
  187. autobyteus/tools/usage/formatters/gemini_json_example_formatter.py +34 -0
  188. autobyteus/tools/usage/formatters/gemini_json_schema_formatter.py +25 -0
  189. autobyteus/tools/usage/formatters/google_json_example_formatter.py +34 -0
  190. autobyteus/tools/usage/formatters/google_json_schema_formatter.py +25 -0
  191. autobyteus/tools/usage/formatters/openai_json_example_formatter.py +49 -0
  192. autobyteus/tools/usage/formatters/openai_json_schema_formatter.py +28 -0
  193. autobyteus/tools/usage/parsers/__init__.py +22 -0
  194. autobyteus/tools/usage/parsers/anthropic_xml_tool_usage_parser.py +10 -0
  195. autobyteus/tools/usage/parsers/base_parser.py +41 -0
  196. autobyteus/tools/usage/parsers/default_json_tool_usage_parser.py +106 -0
  197. autobyteus/tools/usage/parsers/default_xml_tool_usage_parser.py +135 -0
  198. autobyteus/tools/usage/parsers/exceptions.py +13 -0
  199. autobyteus/tools/usage/parsers/gemini_json_tool_usage_parser.py +68 -0
  200. autobyteus/tools/usage/parsers/openai_json_tool_usage_parser.py +147 -0
  201. autobyteus/tools/usage/parsers/provider_aware_tool_usage_parser.py +67 -0
  202. autobyteus/tools/usage/providers/__init__.py +22 -0
  203. autobyteus/tools/usage/providers/json_example_provider.py +32 -0
  204. autobyteus/tools/usage/providers/json_schema_provider.py +35 -0
  205. autobyteus/tools/usage/providers/json_tool_usage_parser_provider.py +28 -0
  206. autobyteus/tools/usage/providers/tool_manifest_provider.py +68 -0
  207. autobyteus/tools/usage/providers/xml_example_provider.py +28 -0
  208. autobyteus/tools/usage/providers/xml_schema_provider.py +29 -0
  209. autobyteus/tools/usage/providers/xml_tool_usage_parser_provider.py +26 -0
  210. autobyteus/tools/usage/registries/__init__.py +20 -0
  211. autobyteus/tools/usage/registries/json_example_formatter_registry.py +51 -0
  212. autobyteus/tools/usage/registries/json_schema_formatter_registry.py +51 -0
  213. autobyteus/tools/usage/registries/json_tool_usage_parser_registry.py +42 -0
  214. autobyteus/tools/usage/registries/xml_example_formatter_registry.py +30 -0
  215. autobyteus/tools/usage/registries/xml_schema_formatter_registry.py +33 -0
  216. autobyteus/tools/usage/registries/xml_tool_usage_parser_registry.py +30 -0
  217. {autobyteus-1.0.5.dist-info → autobyteus-1.1.0.dist-info}/METADATA +21 -3
  218. autobyteus-1.1.0.dist-info/RECORD +279 -0
  219. {autobyteus-1.0.5.dist-info → autobyteus-1.1.0.dist-info}/WHEEL +1 -1
  220. autobyteus/agent/async_agent.py +0 -175
  221. autobyteus/agent/async_group_aware_agent.py +0 -136
  222. autobyteus/agent/group/async_group_aware_agent.py +0 -122
  223. autobyteus/agent/group/coordinator_agent.py +0 -36
  224. autobyteus/agent/group/group_aware_agent.py +0 -121
  225. autobyteus/agent/orchestrator/__init__.py +0 -0
  226. autobyteus/agent/orchestrator/base_agent_orchestrator.py +0 -82
  227. autobyteus/agent/orchestrator/multi_replica_agent_orchestrator.py +0 -72
  228. autobyteus/agent/orchestrator/single_replica_agent_orchestrator.py +0 -43
  229. autobyteus/agent/registry/__init__.py +0 -11
  230. autobyteus/agent/registry/agent_definition.py +0 -94
  231. autobyteus/agent/registry/agent_registry.py +0 -114
  232. autobyteus/agent/response_parser/__init__.py +0 -0
  233. autobyteus/agent/response_parser/tool_usage_command_parser.py +0 -100
  234. autobyteus/agent/status.py +0 -12
  235. autobyteus/conversation/__init__.py +0 -0
  236. autobyteus/conversation/conversation.py +0 -54
  237. autobyteus/conversation/user_message.py +0 -59
  238. autobyteus/events/decorators.py +0 -29
  239. autobyteus/prompt/prompt_version_manager.py +0 -58
  240. autobyteus/prompt/storage/__init__.py +0 -0
  241. autobyteus/prompt/storage/prompt_version_model.py +0 -29
  242. autobyteus/prompt/storage/prompt_version_repository.py +0 -83
  243. autobyteus/tools/bash/factory/__init__.py +0 -0
  244. autobyteus/tools/bash/factory/bash_executor_factory.py +0 -6
  245. autobyteus/tools/factory/ask_user_input_factory.py +0 -6
  246. autobyteus/tools/factory/image_downloader_factory.py +0 -9
  247. autobyteus/tools/factory/pdf_downloader_factory.py +0 -9
  248. autobyteus/tools/factory/webpage_image_downloader_factory.py +0 -6
  249. autobyteus/tools/file/factory/__init__.py +0 -0
  250. autobyteus/tools/file/factory/file_reader_factory.py +0 -6
  251. autobyteus/tools/file/factory/file_writer_factory.py +0 -6
  252. autobyteus/tools/mcp_remote_tool.py +0 -82
  253. autobyteus/tools/web_page_pdf_generator.py +0 -90
  254. autobyteus-1.0.5.dist-info/RECORD +0 -163
  255. {autobyteus-1.0.5.dist-info → autobyteus-1.1.0.dist-info}/licenses/LICENSE +0 -0
  256. {autobyteus-1.0.5.dist-info → autobyteus-1.1.0.dist-info}/top_level.txt +0 -0
@@ -1,89 +1,88 @@
1
- # File: autobyteus/tools/pdf_downloader.py
2
-
1
+ # This was top-level, keep it there.
3
2
  import os
4
- import requests
5
3
  import logging
4
+ import asyncio
5
+ import requests
6
6
  from datetime import datetime
7
- from autobyteus.tools.base_tool import BaseTool
8
- from autobyteus.utils.file_utils import get_default_download_folder
9
-
10
- class PDFDownloader(BaseTool):
11
- """
12
- A tool that downloads a PDF file from a given URL and saves it locally.
13
- """
14
-
15
- def __init__(self, custom_download_folder=None):
16
- super().__init__()
17
- self.logger = logging.getLogger(__name__)
18
- self.default_download_folder = get_default_download_folder()
19
- self.download_folder = custom_download_folder or self.default_download_folder
20
-
21
- @classmethod
22
- def tool_usage_xml(cls):
23
- """
24
- Return an XML string describing the usage of the PDFDownloader tool.
25
-
26
- Returns:
27
- str: An XML description of how to use the PDFDownloader tool.
28
- """
29
- return '''PDFDownloader: Downloads a PDF file from a given URL. Usage:
30
- <command name="PDFDownloader">
31
- <arg name="url">https://example.com/file.pdf</arg>
32
- </command>
33
- '''
34
-
35
- def _execute(self, **kwargs):
36
- """
37
- Download a PDF file from the given URL and save it locally.
7
+ from typing import TYPE_CHECKING, Optional
38
8
 
39
- Args:
40
- **kwargs: Keyword arguments containing the URL.
41
- 'url': The URL of the PDF file to download.
42
- 'folder' (optional): Custom download folder path.
43
-
44
- Returns:
45
- str: A message indicating the result of the download operation.
46
-
47
- Raises:
48
- ValueError: If the 'url' keyword argument is not specified.
49
- """
50
- url = kwargs.get('url')
51
- custom_folder = kwargs.get('folder')
52
- download_folder = custom_folder or self.download_folder
53
-
54
- if not url:
55
- raise ValueError("The 'url' keyword argument must be specified.")
56
-
57
- self.logger.info(f"Attempting to download PDF from {url}")
9
+ from autobyteus.tools import tool
10
+ from autobyteus.utils.file_utils import get_default_download_folder
58
11
 
59
- try:
60
- response = requests.get(url, stream=True)
61
- response.raise_for_status()
12
+ if TYPE_CHECKING:
13
+ from autobyteus.agent.context import AgentContext
62
14
 
63
- content_type = response.headers.get('Content-Type', '').lower()
64
- if 'application/pdf' not in content_type:
65
- raise ValueError(f"The URL does not point to a PDF file. Content-Type: {content_type}")
15
+ logger = logging.getLogger(__name__)
66
16
 
67
- timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
17
+ @tool(name="PDFDownloader")
18
+ async def pdf_downloader( # function name can be pdf_downloader
19
+ context: 'AgentContext',
20
+ url: str,
21
+ folder: Optional[str] = None
22
+ ) -> str:
23
+ """
24
+ Downloads a PDF file from a given URL and saves it locally.
25
+ 'url' is the URL of the PDF.
26
+ 'folder' (optional) is a custom directory to save the PDF. If not given,
27
+ uses the system's default download folder. Validates Content-Type.
28
+ """
29
+ logger.debug(f"Functional PDFDownloader tool for agent {context.agent_id}, URL: {url}, Folder: {folder}")
30
+
31
+ current_download_folder = folder if folder else get_default_download_folder()
32
+
33
+ try:
34
+ loop = asyncio.get_event_loop()
35
+ response = await loop.run_in_executor(None, lambda: requests.get(url, stream=True, timeout=30))
36
+ response.raise_for_status()
37
+
38
+ content_type = response.headers.get('Content-Type', '').lower()
39
+ if 'application/pdf' not in content_type:
40
+ response.close()
41
+ raise ValueError(f"The URL does not point to a PDF file. Content-Type: {content_type}")
42
+
43
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
44
+ filename_from_header = None
45
+ if 'Content-Disposition' in response.headers:
46
+ import re
47
+ match = re.search(r'filename=[\'"]?([^\'"\s]+)[\'"]?', response.headers['Content-Disposition'])
48
+ if match: filename_from_header = match.group(1)
49
+
50
+ if filename_from_header and filename_from_header.lower().endswith(".pdf"):
51
+ import string
52
+ valid_chars = "-_.() %s%s" % (string.ascii_letters, string.digits)
53
+ filename_from_header = ''.join(c for c in filename_from_header if c in valid_chars)[:200]
54
+ filename = f"{timestamp}_{filename_from_header}"
55
+ else:
68
56
  filename = f"downloaded_pdf_{timestamp}.pdf"
69
- save_path = os.path.join(download_folder, filename)
70
57
 
71
- os.makedirs(download_folder, exist_ok=True)
72
- with open(save_path, 'wb') as file:
58
+ save_path = os.path.join(current_download_folder, filename)
59
+ os.makedirs(current_download_folder, exist_ok=True)
60
+
61
+ def download_and_save_sync():
62
+ with open(save_path, 'wb') as file_handle:
73
63
  for chunk in response.iter_content(chunk_size=8192):
74
- file.write(chunk)
75
-
76
- self.logger.info(f"PDF successfully downloaded and saved to {save_path}")
77
- return f"PDF successfully downloaded and saved to {save_path}"
78
- except requests.exceptions.RequestException as e:
79
- error_message = f"Error downloading PDF: {str(e)}"
80
- self.logger.error(error_message)
81
- return error_message
82
- except ValueError as e:
83
- error_message = str(e)
84
- self.logger.error(error_message)
85
- return error_message
86
- except IOError as e:
87
- error_message = f"Error saving PDF: {str(e)}"
88
- self.logger.error(error_message)
89
- return error_message
64
+ file_handle.write(chunk)
65
+ response.close()
66
+
67
+ await loop.run_in_executor(None, download_and_save_sync)
68
+
69
+ logger.info(f"PDF successfully downloaded and saved to {save_path}")
70
+ return f"PDF successfully downloaded and saved to {save_path}"
71
+ except requests.exceptions.Timeout:
72
+ logger.error(f"Timeout downloading PDF from {url}", exc_info=True)
73
+ return f"Error downloading PDF: Timeout occurred for URL {url}"
74
+ except requests.exceptions.RequestException as e:
75
+ logger.error(f"Error downloading PDF from {url}: {str(e)}", exc_info=True)
76
+ return f"Error downloading PDF: {str(e)}"
77
+ except ValueError as e:
78
+ logger.error(f"Content type error for PDF from {url}: {str(e)}", exc_info=True)
79
+ return str(e)
80
+ except IOError as e:
81
+ logger.error(f"Error saving PDF to {current_download_folder}: {str(e)}", exc_info=True)
82
+ return f"Error saving PDF: {str(e)}"
83
+ except Exception as e:
84
+ logger.error(f"Unexpected error downloading PDF from {url}: {str(e)}", exc_info=True)
85
+ return f"An unexpected error occurred: {str(e)}"
86
+ finally:
87
+ if 'response' in locals() and hasattr(response, 'close') and response.raw and not response.raw.closed:
88
+ response.close()
@@ -1,11 +1,9 @@
1
1
  # file: autobyteus/autobyteus/tools/registry/__init__.py
2
2
  from .tool_definition import ToolDefinition
3
3
  from .tool_registry import ToolRegistry, default_tool_registry
4
- from autobyteus.tools.factory.tool_factory import ToolFactory
5
4
 
6
5
  __all__ = [
7
6
  "ToolDefinition",
8
7
  "ToolRegistry",
9
8
  "default_tool_registry",
10
- "ToolFactory"
11
9
  ]
@@ -1,60 +1,132 @@
1
- # file: autobyteus/tools/registry/tool_definition.py
1
+ # file: autobyteus/autobyteus/tools/registry/tool_definition.py
2
2
  import logging
3
- from typing import Dict, Any
3
+ import json
4
+ from typing import Dict, Any, List as TypingList, Type, TYPE_CHECKING, Optional, Callable
5
+
6
+ from autobyteus.llm.providers import LLMProvider
7
+ from autobyteus.tools.tool_config import ToolConfig
8
+ from autobyteus.tools.parameter_schema import ParameterSchema
9
+ from autobyteus.tools.usage.providers import (
10
+ XmlSchemaProvider,
11
+ JsonSchemaProvider,
12
+ XmlExampleProvider,
13
+ JsonExampleProvider
14
+ )
15
+
16
+ if TYPE_CHECKING:
17
+ from autobyteus.tools.base_tool import BaseTool
4
18
 
5
19
  logger = logging.getLogger(__name__)
6
20
 
7
21
  class ToolDefinition:
8
22
  """
9
- Represents the simplified static definition of a tool, containing
10
- only its name and usage description string.
23
+ Represents the definition of a tool, containing its metadata and the means
24
+ to create an instance. It can generate provider-specific usage information on demand.
11
25
  """
12
26
  def __init__(self,
13
27
  name: str,
14
- description: str):
28
+ description: str,
29
+ argument_schema: Optional['ParameterSchema'],
30
+ config_schema: Optional['ParameterSchema'] = None,
31
+ tool_class: Optional[Type['BaseTool']] = None,
32
+ custom_factory: Optional[Callable[['ToolConfig'], 'BaseTool']] = None):
15
33
  """
16
34
  Initializes the ToolDefinition.
17
-
18
- Args:
19
- name: The unique name/identifier of the tool.
20
- description: The static usage description string for the tool (e.g., XML usage format).
21
-
22
- Raises:
23
- ValueError: If name or description are empty or invalid.
24
35
  """
25
36
  if not name or not isinstance(name, str):
26
37
  raise ValueError("ToolDefinition requires a non-empty string 'name'.")
27
38
  if not description or not isinstance(description, str):
28
39
  raise ValueError(f"ToolDefinition '{name}' requires a non-empty string 'description'.")
29
40
 
41
+ if tool_class is None and custom_factory is None:
42
+ raise ValueError(f"ToolDefinition '{name}' must provide either a 'tool_class' or a 'custom_factory'.")
43
+ if tool_class is not None and custom_factory is not None:
44
+ raise ValueError(f"ToolDefinition '{name}' cannot have both a 'tool_class' and a 'custom_factory'.")
45
+
46
+ if tool_class and not isinstance(tool_class, type):
47
+ raise TypeError(f"ToolDefinition '{name}' requires a valid class for 'tool_class'.")
48
+ if custom_factory and not callable(custom_factory):
49
+ raise TypeError(f"ToolDefinition '{name}' requires a callable for 'custom_factory'.")
50
+
51
+ if argument_schema is not None and not isinstance(argument_schema, ParameterSchema):
52
+ raise TypeError(f"ToolDefinition '{name}' received an invalid 'argument_schema'. Expected ParameterSchema or None.")
53
+ if config_schema is not None and not isinstance(config_schema, ParameterSchema):
54
+ raise TypeError(f"ToolDefinition '{name}' received an invalid 'config_schema'. Expected ParameterSchema or None.")
55
+
30
56
  self._name = name
31
57
  self._description = description
32
-
58
+ self._argument_schema: Optional['ParameterSchema'] = argument_schema
59
+ self._config_schema: Optional['ParameterSchema'] = config_schema
60
+ self._tool_class = tool_class
61
+ self._custom_factory = custom_factory
62
+
33
63
  logger.debug(f"ToolDefinition created for tool '{self.name}'.")
34
64
 
65
+ # --- Properties ---
66
+ @property
67
+ def name(self) -> str: return self._name
68
+ @property
69
+ def description(self) -> str: return self._description
70
+ @property
71
+ def tool_class(self) -> Optional[Type['BaseTool']]: return self._tool_class
35
72
  @property
36
- def name(self) -> str:
37
- """The unique name/identifier of the tool."""
38
- return self._name
73
+ def custom_factory(self) -> Optional[Callable[['ToolConfig'], 'BaseTool']]: return self._custom_factory
74
+ @property
75
+ def argument_schema(self) -> Optional['ParameterSchema']: return self._argument_schema
76
+ @property
77
+ def config_schema(self) -> Optional['ParameterSchema']: return self._config_schema
78
+
79
+ # --- Schema Generation API ---
80
+ def get_usage_xml(self, provider: Optional[LLMProvider] = None) -> str:
81
+ """
82
+ Generates the standardized XML usage schema string for this tool.
83
+ The provider argument is included for API consistency and future-proofing.
84
+ """
85
+ provider_instance = XmlSchemaProvider()
86
+ return provider_instance.provide(self, llm_provider=provider)
39
87
 
88
+ def get_usage_json(self, provider: Optional[LLMProvider] = None) -> Dict[str, Any]:
89
+ """
90
+ Generates the usage schema as a dictionary.
91
+
92
+ Args:
93
+ provider: If provided, generates a provider-specific JSON format.
94
+ If None, generates a default, generic JSON format.
95
+
96
+ Returns:
97
+ A dictionary representing the tool's usage schema.
98
+ """
99
+ provider_instance = JsonSchemaProvider()
100
+ return provider_instance.provide(self, llm_provider=provider)
101
+
102
+ # --- Example Generation API ---
103
+ def get_usage_xml_example(self, provider: Optional[LLMProvider] = None) -> str:
104
+ """
105
+ Generates a standardized XML usage example string for this tool.
106
+ The provider argument is included for API consistency and future-proofing.
107
+ """
108
+ provider_instance = XmlExampleProvider()
109
+ return provider_instance.provide(self, llm_provider=provider)
110
+
111
+ def get_usage_json_example(self, provider: Optional[LLMProvider] = None) -> Any:
112
+ """
113
+ Generates a usage example, either as a dict or a string.
114
+ """
115
+ provider_instance = JsonExampleProvider()
116
+ return provider_instance.provide(self, llm_provider=provider)
117
+
118
+ # --- Other methods ---
40
119
  @property
41
- def description(self) -> str:
42
- """The static usage description string for the tool."""
43
- return self._description
120
+ def has_instantiation_config(self) -> bool:
121
+ return self._config_schema is not None and len(self._config_schema) > 0
44
122
 
45
- def __repr__(self) -> str:
46
- """Provides a developer-friendly string representation."""
47
- desc_repr = self.description
48
- if len(desc_repr) > 70:
49
- desc_repr = desc_repr[:67] + "..."
50
- # Remove newlines/tabs from repr for cleaner logging if description is multiline XML
51
- desc_repr = desc_repr.replace('\n', '\\n').replace('\t', '\\t')
52
- return (f"ToolDefinition(name='{self.name}', description='{desc_repr}')")
53
-
54
- def to_dict(self) -> Dict[str, Any]:
55
- """Returns a dictionary representation of the tool definition."""
56
- return {
57
- "name": self.name,
58
- "description": self.description,
59
- }
123
+ def validate_instantiation_config(self, config_data: Dict[str, Any]) -> tuple[bool, TypingList[str]]:
124
+ if not self._config_schema:
125
+ if config_data:
126
+ return False, [f"Tool '{self.name}' does not accept instantiation configuration parameters"]
127
+ return True, []
128
+ return self._config_schema.validate_config(config_data)
60
129
 
130
+ def __repr__(self) -> str:
131
+ creator_repr = f"class='{self._tool_class.__name__}'" if self._tool_class else "factory=True"
132
+ return (f"ToolDefinition(name='{self.name}', {creator_repr})")
@@ -1,34 +1,32 @@
1
- # file: autobyteus/autobyteus/tools/registry/tool_registry.py
1
+ # file: autobyteus/tools/registry/tool_registry.py
2
2
  import logging
3
- from typing import Dict, List, Optional
3
+ from typing import Dict, List, Optional, Type, TYPE_CHECKING
4
4
 
5
5
  from autobyteus.tools.registry.tool_definition import ToolDefinition
6
6
  from autobyteus.utils.singleton import SingletonMeta
7
- from autobyteus.tools.factory.tool_factory import ToolFactory
7
+ from autobyteus.tools.tool_config import ToolConfig
8
8
 
9
+ if TYPE_CHECKING:
10
+ from autobyteus.tools.base_tool import BaseTool
9
11
 
10
12
  logger = logging.getLogger(__name__)
11
13
 
12
14
  class ToolRegistry(metaclass=SingletonMeta):
13
15
  """
14
- Manages ToolDefinitions (name, description, tool_class), populated exclusively via
15
- programmatic registration. Uses ToolFactory to create tool instances.
16
+ Manages ToolDefinitions and creates tool instances. It can create instances
17
+ from a tool_class or by using a custom_factory provided in the definition.
16
18
  """
17
19
  _definitions: Dict[str, ToolDefinition] = {}
18
20
 
19
- def __init__(self, tool_factory: ToolFactory):
21
+ def __init__(self):
20
22
  """
21
- Initializes the ToolRegistry with a ToolFactory.
22
-
23
- Args:
24
- tool_factory: The ToolFactory instance used to create tool instances.
23
+ Initializes the ToolRegistry.
25
24
  """
26
- self.tool_factory = tool_factory
27
- logger.info("ToolRegistry initialized with ToolFactory.")
25
+ logger.info("ToolRegistry initialized.")
28
26
 
29
27
  def register_tool(self, definition: ToolDefinition):
30
28
  """
31
- Registers a tool definition (name, description, tool_class) programmatically.
29
+ Registers a tool definition.
32
30
 
33
31
  Args:
34
32
  definition: The ToolDefinition object to register.
@@ -53,40 +51,66 @@ class ToolRegistry(metaclass=SingletonMeta):
53
51
  name: The unique name of the tool definition to retrieve.
54
52
 
55
53
  Returns:
56
- The ToolDefinition object (name, description, tool_class) if found, otherwise None.
54
+ The ToolDefinition object if found, otherwise None.
57
55
  """
58
56
  definition = self._definitions.get(name)
59
57
  if not definition:
60
58
  logger.debug(f"Tool definition not found for name: '{name}'")
61
59
  return definition
62
60
 
63
- def create_tool(self, name: str):
61
+ def create_tool(self, name: str, config: Optional[ToolConfig] = None) -> 'BaseTool':
64
62
  """
65
- Creates a tool instance using the ToolFactory based on the tool definition.
63
+ Creates a tool instance using its definition, either from a factory or a class.
66
64
 
67
65
  Args:
68
66
  name: The name of the tool to create.
67
+ config: Optional ToolConfig with constructor parameters for class-based tools
68
+ or to be passed to a custom factory.
69
69
 
70
70
  Returns:
71
- The tool instance if the definition exists, otherwise None.
71
+ The tool instance if the definition exists.
72
72
 
73
73
  Raises:
74
- ValueError: If the tool definition is not found.
74
+ ValueError: If the tool definition is not found or is invalid.
75
+ TypeError: If tool instantiation fails.
75
76
  """
76
77
  definition = self.get_tool_definition(name)
77
78
  if not definition:
78
79
  logger.error(f"Cannot create tool: No definition found for name '{name}'")
79
80
  raise ValueError(f"No tool definition found for name '{name}'")
80
81
 
81
- logger.info(f"Creating tool instance for '{name}' using ToolFactory")
82
- return self.tool_factory.create_tool(name)
82
+ try:
83
+ # Prefer the custom factory if it exists
84
+ if definition.custom_factory:
85
+ logger.info(f"Creating tool instance for '{name}' using its custom factory.")
86
+ # Pass the config to the factory. The factory can choose to use it or not.
87
+ tool_instance = definition.custom_factory(config)
88
+
89
+ # Fall back to instantiating the tool_class
90
+ elif definition.tool_class:
91
+ # For class-based tools, the convention is to pass the ToolConfig object
92
+ # itself to the constructor under the 'config' keyword argument.
93
+ logger.info(f"Creating tool instance for '{name}' using class '{definition.tool_class.__name__}' and passing ToolConfig.")
94
+ tool_instance = definition.tool_class(config=config)
95
+
96
+ else:
97
+ # This case should be prevented by ToolDefinition's validation
98
+ raise ValueError(f"ToolDefinition for '{name}' is invalid: missing both tool_class and custom_factory.")
99
+
100
+ logger.debug(f"Successfully created tool instance for '{name}'")
101
+ return tool_instance
102
+
103
+ except Exception as e:
104
+ creator_type = "factory" if definition.custom_factory else f"class '{definition.tool_class.__name__}'"
105
+ logger.error(f"Failed to create tool instance for '{name}' using {creator_type}: {e}", exc_info=True)
106
+ raise TypeError(f"Failed to create tool '{name}': {e}") from e
83
107
 
84
108
  def list_tools(self) -> List[ToolDefinition]:
85
109
  """
86
110
  Returns a list of all registered tool definitions.
87
111
 
88
112
  Returns:
89
- A list of ToolDefinition objects (name, description, tool_class).
113
+ A list of ToolDefinition objects.
90
114
  """
91
115
  return list(self._definitions.values())
92
116
 
@@ -103,4 +127,4 @@ class ToolRegistry(metaclass=SingletonMeta):
103
127
  """Returns the internal dictionary of definitions."""
104
128
  return dict(ToolRegistry._definitions)
105
129
 
106
- default_tool_registry = ToolRegistry(tool_factory=ToolFactory())
130
+ default_tool_registry = ToolRegistry()