autobyteus 1.0.4__py3-none-any.whl → 1.0.6__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.
- autobyteus/agent/agent.py +1 -1
- autobyteus/agent/message/send_message_to.py +0 -3
- autobyteus/llm/autobyteus_provider.py +2 -1
- autobyteus/llm/llm_factory.py +33 -13
- autobyteus/llm/models.py +12 -0
- autobyteus/llm/ollama_provider.py +1 -0
- autobyteus/tools/ask_user_input.py +8 -19
- autobyteus/tools/base_tool.py +52 -24
- autobyteus/tools/bash/bash_executor.py +3 -15
- autobyteus/tools/browser/session_aware/browser_session_aware_navigate_to.py +8 -4
- autobyteus/tools/browser/session_aware/browser_session_aware_web_element_trigger.py +12 -55
- autobyteus/tools/browser/session_aware/browser_session_aware_webpage_reader.py +9 -5
- autobyteus/tools/browser/session_aware/browser_session_aware_webpage_screenshot_taker.py +8 -4
- autobyteus/tools/browser/standalone/google_search_ui.py +6 -32
- autobyteus/tools/browser/standalone/navigate_to.py +8 -16
- autobyteus/tools/browser/standalone/webpage_image_downloader.py +9 -60
- autobyteus/tools/browser/standalone/webpage_reader.py +8 -6
- autobyteus/tools/browser/standalone/webpage_screenshot_taker.py +9 -6
- autobyteus/tools/factory/tool_factory.py +7 -5
- autobyteus/tools/file/file_reader.py +6 -8
- autobyteus/tools/file/file_writer.py +6 -9
- autobyteus/tools/image_downloader.py +18 -41
- autobyteus/tools/pdf_downloader.py +8 -13
- autobyteus/tools/registry/__init__.py +11 -0
- autobyteus/tools/registry/tool_definition.py +60 -0
- autobyteus/tools/registry/tool_registry.py +106 -0
- autobyteus/tools/timer.py +16 -18
- autobyteus/tools/tool_meta.py +52 -0
- autobyteus/tools/utils.py +1 -1
- autobyteus/tools/web_page_pdf_generator.py +8 -4
- {autobyteus-1.0.4.dist-info → autobyteus-1.0.6.dist-info}/METADATA +2 -2
- {autobyteus-1.0.4.dist-info → autobyteus-1.0.6.dist-info}/RECORD +35 -31
- {autobyteus-1.0.4.dist-info → autobyteus-1.0.6.dist-info}/WHEEL +1 -1
- {autobyteus-1.0.4.dist-info → autobyteus-1.0.6.dist-info}/licenses/LICENSE +0 -0
- {autobyteus-1.0.4.dist-info → autobyteus-1.0.6.dist-info}/top_level.txt +0 -0
|
@@ -11,10 +11,14 @@ class NavigateTo(BaseTool, UIIntegrator):
|
|
|
11
11
|
BaseTool.__init__(self)
|
|
12
12
|
UIIntegrator.__init__(self)
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
@classmethod
|
|
15
|
+
def tool_usage_xml(cls):
|
|
16
|
+
"""
|
|
17
|
+
Return an XML string describing the usage of the NavigateTo tool.
|
|
16
18
|
|
|
17
|
-
|
|
19
|
+
Returns:
|
|
20
|
+
str: An XML description of how to use the NavigateTo tool.
|
|
21
|
+
"""
|
|
18
22
|
return '''
|
|
19
23
|
NavigateTo: Navigates to a specified website. Usage:
|
|
20
24
|
<command name="NavigateTo">
|
|
@@ -24,18 +28,6 @@ class NavigateTo(BaseTool, UIIntegrator):
|
|
|
24
28
|
'''
|
|
25
29
|
|
|
26
30
|
async def _execute(self, **kwargs):
|
|
27
|
-
"""
|
|
28
|
-
Navigate to the specified URL using Playwright.
|
|
29
|
-
|
|
30
|
-
Args:
|
|
31
|
-
**kwargs: Keyword arguments containing the URL. The URL should be specified as 'url'.
|
|
32
|
-
|
|
33
|
-
Returns:
|
|
34
|
-
str: A message indicating successful navigation or an error message.
|
|
35
|
-
|
|
36
|
-
Raises:
|
|
37
|
-
ValueError: If the 'url' keyword argument is not specified or is invalid.
|
|
38
|
-
"""
|
|
39
31
|
url = kwargs.get('url')
|
|
40
32
|
if not url:
|
|
41
33
|
raise ValueError("The 'url' keyword argument must be specified.")
|
|
@@ -59,4 +51,4 @@ class NavigateTo(BaseTool, UIIntegrator):
|
|
|
59
51
|
result = urlparse(url)
|
|
60
52
|
return all([result.scheme, result.netloc])
|
|
61
53
|
except ValueError:
|
|
62
|
-
return False
|
|
54
|
+
return False
|
|
@@ -11,11 +11,15 @@ class WebPageImageDownloader(BaseTool, UIIntegrator):
|
|
|
11
11
|
BaseTool.__init__(self)
|
|
12
12
|
UIIntegrator.__init__(self)
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
@classmethod
|
|
15
|
+
def tool_usage_xml(cls):
|
|
16
|
+
"""
|
|
17
|
+
Return an XML string describing the usage of the WebPageImageDownloader tool.
|
|
16
18
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
+
Returns:
|
|
20
|
+
str: An XML description of how to use the WebPageImageDownloader tool.
|
|
21
|
+
"""
|
|
22
|
+
return '''
|
|
19
23
|
WebPageImageDownloader: Downloads images (excluding SVGs) from a given webpage and saves them to the specified directory. Usage:
|
|
20
24
|
<command name="WebPageImageDownloader">
|
|
21
25
|
<arg name="url">webpage_url</arg>
|
|
@@ -25,18 +29,6 @@ class WebPageImageDownloader(BaseTool, UIIntegrator):
|
|
|
25
29
|
'''
|
|
26
30
|
|
|
27
31
|
async def _execute(self, **kwargs):
|
|
28
|
-
"""
|
|
29
|
-
Download images (excluding SVGs) from the webpage at the given URL using Playwright and save them to the specified directory.
|
|
30
|
-
|
|
31
|
-
Args:
|
|
32
|
-
**kwargs: Keyword arguments containing the URL and save directory. The URL should be specified as 'url', and the directory as 'save_dir'.
|
|
33
|
-
|
|
34
|
-
Returns:
|
|
35
|
-
list: The file paths of the saved images.
|
|
36
|
-
|
|
37
|
-
Raises:
|
|
38
|
-
ValueError: If the 'url' or 'save_dir' keyword arguments are not specified.
|
|
39
|
-
"""
|
|
40
32
|
url = kwargs.get('url')
|
|
41
33
|
save_dir = kwargs.get('save_dir')
|
|
42
34
|
if not url:
|
|
@@ -62,67 +54,24 @@ class WebPageImageDownloader(BaseTool, UIIntegrator):
|
|
|
62
54
|
return saved_paths
|
|
63
55
|
|
|
64
56
|
async def _get_image_urls(self):
|
|
65
|
-
"""
|
|
66
|
-
Get the URLs of all images on the current page.
|
|
67
|
-
|
|
68
|
-
Returns:
|
|
69
|
-
list: A list of image URLs.
|
|
70
|
-
"""
|
|
71
57
|
image_urls = await self.page.evaluate("""() => {
|
|
72
58
|
return Array.from(document.images).map(i => i.src);
|
|
73
59
|
}""")
|
|
74
60
|
return image_urls
|
|
75
61
|
|
|
76
62
|
def _resolve_url(self, base_url, url):
|
|
77
|
-
"""
|
|
78
|
-
Resolve a URL against a base URL to get the absolute URL.
|
|
79
|
-
|
|
80
|
-
Args:
|
|
81
|
-
base_url (str): The base URL.
|
|
82
|
-
url (str): The URL to resolve.
|
|
83
|
-
|
|
84
|
-
Returns:
|
|
85
|
-
str: The absolute URL.
|
|
86
|
-
"""
|
|
87
63
|
return urljoin(base_url, url)
|
|
88
64
|
|
|
89
65
|
def _is_svg(self, url):
|
|
90
|
-
"""
|
|
91
|
-
Check if a URL points to an SVG image.
|
|
92
|
-
|
|
93
|
-
Args:
|
|
94
|
-
url (str): The URL to check.
|
|
95
|
-
|
|
96
|
-
Returns:
|
|
97
|
-
bool: True if the URL points to an SVG, False otherwise.
|
|
98
|
-
"""
|
|
99
66
|
return url.lower().endswith('.svg')
|
|
100
67
|
|
|
101
68
|
def _generate_file_path(self, directory, index, url):
|
|
102
|
-
"""
|
|
103
|
-
Generate a unique file path for an image.
|
|
104
|
-
|
|
105
|
-
Args:
|
|
106
|
-
directory (str): The directory to save the image in.
|
|
107
|
-
index (int): A unique index for this image.
|
|
108
|
-
url (str): The URL of the image (used to get the file extension).
|
|
109
|
-
|
|
110
|
-
Returns:
|
|
111
|
-
str: The generated file path.
|
|
112
|
-
"""
|
|
113
69
|
ext = os.path.splitext(url)[1]
|
|
114
70
|
filename = f"image_{index}{ext}"
|
|
115
71
|
return os.path.join(directory, filename)
|
|
116
72
|
|
|
117
73
|
async def _download_and_save_image(self, url, file_path):
|
|
118
|
-
"""
|
|
119
|
-
Download an image from a URL and save it to a file.
|
|
120
|
-
|
|
121
|
-
Args:
|
|
122
|
-
url (str): The URL of the image to download.
|
|
123
|
-
file_path (str): The path to save the image to.
|
|
124
|
-
"""
|
|
125
74
|
await self.page.goto(url)
|
|
126
75
|
image_buffer = await self.page.screenshot(full_page=True)
|
|
127
76
|
with open(file_path, "wb") as f:
|
|
128
|
-
f.write(image_buffer)
|
|
77
|
+
f.write(image_buffer)
|
|
@@ -6,9 +6,6 @@ This module provides a WebPageReader tool for reading and cleaning HTML content
|
|
|
6
6
|
The WebPageReader class allows users to retrieve and clean the HTML content of a specified webpage
|
|
7
7
|
using Playwright. It inherits from BaseTool and UIIntegrator, providing a seamless integration
|
|
8
8
|
with web browsers.
|
|
9
|
-
|
|
10
|
-
Classes:
|
|
11
|
-
WebPageReader: A tool for reading and cleaning HTML content from webpages.
|
|
12
9
|
"""
|
|
13
10
|
|
|
14
11
|
from autobyteus.tools.base_tool import BaseTool
|
|
@@ -39,16 +36,21 @@ class WebPageReader(BaseTool, UIIntegrator):
|
|
|
39
36
|
UIIntegrator.__init__(self)
|
|
40
37
|
self.cleaning_mode = cleaning_mode
|
|
41
38
|
|
|
42
|
-
|
|
43
|
-
|
|
39
|
+
@classmethod
|
|
40
|
+
def tool_usage_xml(cls):
|
|
41
|
+
"""
|
|
42
|
+
Return an XML string describing the usage of the WebPageReader tool.
|
|
44
43
|
|
|
45
|
-
|
|
44
|
+
Returns:
|
|
45
|
+
str: An XML description of how to use the WebPageReader tool.
|
|
46
|
+
"""
|
|
46
47
|
return '''WebPageReader: Reads the HTML content from a given webpage. Usage:
|
|
47
48
|
<command name="WebPageReader">
|
|
48
49
|
<arg name="url">webpage_url</arg>
|
|
49
50
|
</command>
|
|
50
51
|
where "webpage_url" is a string containing the URL of the webpage to read the content from.
|
|
51
52
|
'''
|
|
53
|
+
|
|
52
54
|
async def _execute(self, **kwargs):
|
|
53
55
|
"""
|
|
54
56
|
Read and clean the HTML content from the webpage at the given URL using Playwright.
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
from autobyteus.tools.base_tool import BaseTool
|
|
2
2
|
from brui_core.ui_integrator import UIIntegrator
|
|
3
3
|
|
|
4
|
-
|
|
5
4
|
class WebPageScreenshotTaker(BaseTool, UIIntegrator):
|
|
6
5
|
"""
|
|
7
6
|
A class that takes a screenshot of a given webpage using Playwright.
|
|
@@ -10,11 +9,15 @@ class WebPageScreenshotTaker(BaseTool, UIIntegrator):
|
|
|
10
9
|
BaseTool.__init__(self)
|
|
11
10
|
UIIntegrator.__init__(self)
|
|
12
11
|
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
@classmethod
|
|
13
|
+
def tool_usage_xml(cls):
|
|
14
|
+
"""
|
|
15
|
+
Return an XML string describing the usage of the WebPageScreenshotTaker tool.
|
|
15
16
|
|
|
16
|
-
|
|
17
|
-
|
|
17
|
+
Returns:
|
|
18
|
+
str: An XML description of how to use the WebPageScreenshotTaker tool.
|
|
19
|
+
"""
|
|
20
|
+
return '''WebPageScreenshotTaker: Takes a screenshot of a given webpage and saves it to the specified file path. Usage:
|
|
18
21
|
<command name="WebPageScreenshotTaker">
|
|
19
22
|
<arg name="url">webpage_url</arg>
|
|
20
23
|
<arg name="file_path">screenshot_file_path</arg>
|
|
@@ -46,4 +49,4 @@ class WebPageScreenshotTaker(BaseTool, UIIntegrator):
|
|
|
46
49
|
await self.page.goto(url)
|
|
47
50
|
await self.page.screenshot(path=file_path, full_page=True)
|
|
48
51
|
await self.close()
|
|
49
|
-
return file_path
|
|
52
|
+
return file_path
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
# file: autobyteus/tools/factory/tool_factory.py
|
|
2
2
|
from abc import ABC, abstractmethod
|
|
3
|
-
from
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
if TYPE_CHECKING:
|
|
6
|
+
from autobyteus.tools.base_tool import BaseTool
|
|
7
|
+
|
|
8
|
+
class ToolFactory():
|
|
9
|
+
def create_tool(self) -> "BaseTool":
|
|
10
|
+
pass
|
|
@@ -15,15 +15,13 @@ class FileReader(BaseTool):
|
|
|
15
15
|
it will raise a FileNotFoundError.
|
|
16
16
|
"""
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
Return a string describing the usage of the FileReader tool.
|
|
21
|
-
"""
|
|
22
|
-
return 'FileReader: Reads content from a specified file. Usage: <<<FileReader(path="file_path")>>>, where "file_path" is the path to the file to be read.'
|
|
23
|
-
|
|
24
|
-
def tool_usage_xml(self):
|
|
18
|
+
@classmethod
|
|
19
|
+
def tool_usage_xml(cls):
|
|
25
20
|
"""
|
|
26
21
|
Return an XML string describing the usage of the FileReader tool.
|
|
22
|
+
|
|
23
|
+
Returns:
|
|
24
|
+
str: An XML description of how to use the FileReader tool.
|
|
27
25
|
"""
|
|
28
26
|
return '''FileReader: Reads content from a specified file. Usage:
|
|
29
27
|
<command name="FileReader">
|
|
@@ -57,4 +55,4 @@ class FileReader(BaseTool):
|
|
|
57
55
|
|
|
58
56
|
with open(path, 'r') as file:
|
|
59
57
|
content = file.read()
|
|
60
|
-
return content
|
|
58
|
+
return content
|
|
@@ -14,15 +14,13 @@ class FileWriter(BaseTool):
|
|
|
14
14
|
it will create the necessary directories.
|
|
15
15
|
"""
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
Return a string describing the usage of the FileWriter tool.
|
|
20
|
-
"""
|
|
21
|
-
return 'FileWriter: Creates a file with specified content. Usage: <<<FileWriter(path="file_path", content="file_content")>>>, where "file_path" is the path to create the file and "file_content" is the content to write to the file.'
|
|
22
|
-
|
|
23
|
-
def tool_usage_xml(self):
|
|
17
|
+
@classmethod
|
|
18
|
+
def tool_usage_xml(cls):
|
|
24
19
|
"""
|
|
25
20
|
Return an XML string describing the usage of the FileWriter tool.
|
|
21
|
+
|
|
22
|
+
Returns:
|
|
23
|
+
str: An XML description of how to use the FileWriter tool.
|
|
26
24
|
"""
|
|
27
25
|
return '''FileWriter: Creates a file with specified content. Usage:
|
|
28
26
|
<command name="FileWriter">
|
|
@@ -56,10 +54,9 @@ class FileWriter(BaseTool):
|
|
|
56
54
|
if content is None:
|
|
57
55
|
raise ValueError("The 'content' keyword argument must be specified.")
|
|
58
56
|
|
|
59
|
-
# Ensure the directory exists
|
|
60
57
|
os.makedirs(os.path.dirname(path), exist_ok=True)
|
|
61
58
|
|
|
62
59
|
with open(path, 'w') as file:
|
|
63
60
|
file.write(content)
|
|
64
61
|
|
|
65
|
-
return f"File created at {path}"
|
|
62
|
+
return f"File created at {path}"
|
|
@@ -14,37 +14,24 @@ from autobyteus.events.decorators import event_listener
|
|
|
14
14
|
logger = logging.getLogger(__name__)
|
|
15
15
|
|
|
16
16
|
class ImageDownloader(BaseTool):
|
|
17
|
+
# Define supported_formats as a class variable so it can be accessed in class methods
|
|
18
|
+
supported_formats = ['.jpeg', '.jpg', '.gif', '.png', '.webp']
|
|
19
|
+
|
|
17
20
|
def __init__(self, custom_download_folder=None):
|
|
18
21
|
super().__init__()
|
|
19
22
|
self.default_download_folder = get_default_download_folder()
|
|
20
23
|
self.download_folder = custom_download_folder or self.default_download_folder
|
|
21
|
-
self.supported_formats =
|
|
24
|
+
self.supported_formats = self.__class__.supported_formats # Set instance attribute from class variable for backward compatibility
|
|
22
25
|
self.last_downloaded_image = None
|
|
23
26
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
Parameters:
|
|
30
|
-
- "image_url": A string containing a direct URL to an image file (must end with {', '.join(self.supported_formats)})
|
|
31
|
-
|
|
32
|
-
Supported image formats: {', '.join(format.upper()[1:] for format in self.supported_formats)}
|
|
33
|
-
|
|
34
|
-
Positive examples:
|
|
35
|
-
- https://example.com/image.jpg
|
|
36
|
-
- https://example.com/photo.png
|
|
37
|
-
- https://example.com/animation.gif
|
|
38
|
-
- https://example.com/picture.webp
|
|
39
|
-
|
|
40
|
-
Negative examples (These will not work):
|
|
41
|
-
- https://example.com/page_containing_image.html
|
|
42
|
-
- https://example.com/image_without_extension
|
|
43
|
-
- https://example.com/document.pdf
|
|
44
|
-
|
|
45
|
-
Note: The URL must be a direct link to the image file, not a webpage containing the image.'''
|
|
27
|
+
@classmethod
|
|
28
|
+
def tool_usage_xml(cls):
|
|
29
|
+
"""
|
|
30
|
+
Return an XML string describing the usage of the ImageDownloader tool.
|
|
46
31
|
|
|
47
|
-
|
|
32
|
+
Returns:
|
|
33
|
+
str: An XML description of how to use the ImageDownloader tool.
|
|
34
|
+
"""
|
|
48
35
|
return f'''ImageDownloader: Downloads an image from a given URL.
|
|
49
36
|
|
|
50
37
|
Usage:
|
|
@@ -53,9 +40,9 @@ Usage:
|
|
|
53
40
|
</command>
|
|
54
41
|
|
|
55
42
|
Parameters:
|
|
56
|
-
- "image_url": A string containing a direct URL to an image file (must end with {', '.join(
|
|
43
|
+
- "image_url": A string containing a direct URL to an image file (must end with {', '.join(cls.supported_formats)})
|
|
57
44
|
|
|
58
|
-
Supported image formats: {', '.join(format.upper()[1:] for format in
|
|
45
|
+
Supported image formats: {', '.join(format.upper()[1:] for format in cls.supported_formats)}
|
|
59
46
|
|
|
60
47
|
Positive examples:
|
|
61
48
|
<command name="ImageDownloader">
|
|
@@ -76,11 +63,7 @@ Negative examples (These will not work):
|
|
|
76
63
|
</command>
|
|
77
64
|
|
|
78
65
|
Note: The URL must be a direct link to the image file, not a webpage containing the image.'''
|
|
79
|
-
|
|
80
66
|
async def _execute(self, **kwargs):
|
|
81
|
-
"""
|
|
82
|
-
Download the image from the given URL.
|
|
83
|
-
"""
|
|
84
67
|
url = kwargs.get('url')
|
|
85
68
|
if not url:
|
|
86
69
|
raise ValueError("The 'url' keyword argument must be specified.")
|
|
@@ -88,13 +71,10 @@ Note: The URL must be a direct link to the image file, not a webpage containing
|
|
|
88
71
|
custom_folder = kwargs.get('folder')
|
|
89
72
|
download_folder = custom_folder or self.download_folder
|
|
90
73
|
|
|
91
|
-
|
|
92
|
-
if not any(url.lower().endswith(format) for format in self.supported_formats):
|
|
74
|
+
if not any(url.lower().endswith(fmt) for fmt in self.supported_formats):
|
|
93
75
|
raise ValueError(
|
|
94
76
|
f"Unsupported image format. The URL must end with one of the following extensions: "
|
|
95
|
-
f"{', '.join(self.supported_formats)}.
|
|
96
|
-
f"Provided URL: {url}\n"
|
|
97
|
-
f"Please ensure you're providing a direct link to an image file, not a webpage containing the image."
|
|
77
|
+
f"{', '.join(self.supported_formats)}. Provided URL: {url}"
|
|
98
78
|
)
|
|
99
79
|
|
|
100
80
|
try:
|
|
@@ -103,18 +83,15 @@ Note: The URL must be a direct link to the image file, not a webpage containing
|
|
|
103
83
|
response.raise_for_status()
|
|
104
84
|
image_bytes = await response.read()
|
|
105
85
|
|
|
106
|
-
# Open the image for verification
|
|
107
86
|
with Image.open(BytesIO(image_bytes)) as img:
|
|
108
|
-
img.verify()
|
|
109
|
-
|
|
87
|
+
img.verify()
|
|
88
|
+
fmt = img.format
|
|
110
89
|
|
|
111
|
-
# Generate a unique filename based on the current timestamp
|
|
112
90
|
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
113
91
|
extension = os.path.splitext(url)[1].lower()
|
|
114
92
|
filename = f"downloaded_image_{timestamp}{extension}"
|
|
115
93
|
filepath = os.path.join(download_folder, filename)
|
|
116
94
|
|
|
117
|
-
# Save the image to a file
|
|
118
95
|
os.makedirs(download_folder, exist_ok=True)
|
|
119
96
|
with open(filepath, 'wb') as f:
|
|
120
97
|
f.write(image_bytes)
|
|
@@ -139,4 +116,4 @@ Note: The URL must be a direct link to the image file, not a webpage containing
|
|
|
139
116
|
logger.error(f"Failed to remove downloaded image: {self.last_downloaded_image}. Error: {str(e)}")
|
|
140
117
|
else:
|
|
141
118
|
logger.warning("No image to remove or image file not found.")
|
|
142
|
-
self.last_downloaded_image = None
|
|
119
|
+
self.last_downloaded_image = None
|
|
@@ -18,13 +18,14 @@ class PDFDownloader(BaseTool):
|
|
|
18
18
|
self.default_download_folder = get_default_download_folder()
|
|
19
19
|
self.download_folder = custom_download_folder or self.default_download_folder
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
@classmethod
|
|
22
|
+
def tool_usage_xml(cls):
|
|
22
23
|
"""
|
|
23
|
-
Return
|
|
24
|
-
"""
|
|
25
|
-
return 'PDFDownloader: Downloads a PDF file from a given URL. Usage: <<<PDFDownloader(url="https://example.com/file.pdf")>>>'
|
|
24
|
+
Return an XML string describing the usage of the PDFDownloader tool.
|
|
26
25
|
|
|
27
|
-
|
|
26
|
+
Returns:
|
|
27
|
+
str: An XML description of how to use the PDFDownloader tool.
|
|
28
|
+
"""
|
|
28
29
|
return '''PDFDownloader: Downloads a PDF file from a given URL. Usage:
|
|
29
30
|
<command name="PDFDownloader">
|
|
30
31
|
<arg name="url">https://example.com/file.pdf</arg>
|
|
@@ -57,29 +58,23 @@ class PDFDownloader(BaseTool):
|
|
|
57
58
|
|
|
58
59
|
try:
|
|
59
60
|
response = requests.get(url, stream=True)
|
|
60
|
-
response.raise_for_status()
|
|
61
|
+
response.raise_for_status()
|
|
61
62
|
|
|
62
|
-
# Check if the content type is PDF
|
|
63
63
|
content_type = response.headers.get('Content-Type', '').lower()
|
|
64
64
|
if 'application/pdf' not in content_type:
|
|
65
65
|
raise ValueError(f"The URL does not point to a PDF file. Content-Type: {content_type}")
|
|
66
66
|
|
|
67
|
-
# Generate a unique filename based on the current timestamp
|
|
68
67
|
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
69
68
|
filename = f"downloaded_pdf_{timestamp}.pdf"
|
|
70
69
|
save_path = os.path.join(download_folder, filename)
|
|
71
70
|
|
|
72
|
-
# Ensure the directory exists
|
|
73
71
|
os.makedirs(download_folder, exist_ok=True)
|
|
74
|
-
|
|
75
|
-
# Save the PDF file
|
|
76
72
|
with open(save_path, 'wb') as file:
|
|
77
73
|
for chunk in response.iter_content(chunk_size=8192):
|
|
78
74
|
file.write(chunk)
|
|
79
75
|
|
|
80
76
|
self.logger.info(f"PDF successfully downloaded and saved to {save_path}")
|
|
81
77
|
return f"PDF successfully downloaded and saved to {save_path}"
|
|
82
|
-
|
|
83
78
|
except requests.exceptions.RequestException as e:
|
|
84
79
|
error_message = f"Error downloading PDF: {str(e)}"
|
|
85
80
|
self.logger.error(error_message)
|
|
@@ -91,4 +86,4 @@ class PDFDownloader(BaseTool):
|
|
|
91
86
|
except IOError as e:
|
|
92
87
|
error_message = f"Error saving PDF: {str(e)}"
|
|
93
88
|
self.logger.error(error_message)
|
|
94
|
-
return error_message
|
|
89
|
+
return error_message
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# file: autobyteus/autobyteus/tools/registry/__init__.py
|
|
2
|
+
from .tool_definition import ToolDefinition
|
|
3
|
+
from .tool_registry import ToolRegistry, default_tool_registry
|
|
4
|
+
from autobyteus.tools.factory.tool_factory import ToolFactory
|
|
5
|
+
|
|
6
|
+
__all__ = [
|
|
7
|
+
"ToolDefinition",
|
|
8
|
+
"ToolRegistry",
|
|
9
|
+
"default_tool_registry",
|
|
10
|
+
"ToolFactory"
|
|
11
|
+
]
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# file: autobyteus/tools/registry/tool_definition.py
|
|
2
|
+
import logging
|
|
3
|
+
from typing import Dict, Any
|
|
4
|
+
|
|
5
|
+
logger = logging.getLogger(__name__)
|
|
6
|
+
|
|
7
|
+
class ToolDefinition:
|
|
8
|
+
"""
|
|
9
|
+
Represents the simplified static definition of a tool, containing
|
|
10
|
+
only its name and usage description string.
|
|
11
|
+
"""
|
|
12
|
+
def __init__(self,
|
|
13
|
+
name: str,
|
|
14
|
+
description: str):
|
|
15
|
+
"""
|
|
16
|
+
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
|
+
"""
|
|
25
|
+
if not name or not isinstance(name, str):
|
|
26
|
+
raise ValueError("ToolDefinition requires a non-empty string 'name'.")
|
|
27
|
+
if not description or not isinstance(description, str):
|
|
28
|
+
raise ValueError(f"ToolDefinition '{name}' requires a non-empty string 'description'.")
|
|
29
|
+
|
|
30
|
+
self._name = name
|
|
31
|
+
self._description = description
|
|
32
|
+
|
|
33
|
+
logger.debug(f"ToolDefinition created for tool '{self.name}'.")
|
|
34
|
+
|
|
35
|
+
@property
|
|
36
|
+
def name(self) -> str:
|
|
37
|
+
"""The unique name/identifier of the tool."""
|
|
38
|
+
return self._name
|
|
39
|
+
|
|
40
|
+
@property
|
|
41
|
+
def description(self) -> str:
|
|
42
|
+
"""The static usage description string for the tool."""
|
|
43
|
+
return self._description
|
|
44
|
+
|
|
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
|
+
}
|
|
60
|
+
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# file: autobyteus/autobyteus/tools/registry/tool_registry.py
|
|
2
|
+
import logging
|
|
3
|
+
from typing import Dict, List, Optional
|
|
4
|
+
|
|
5
|
+
from autobyteus.tools.registry.tool_definition import ToolDefinition
|
|
6
|
+
from autobyteus.utils.singleton import SingletonMeta
|
|
7
|
+
from autobyteus.tools.factory.tool_factory import ToolFactory
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
logger = logging.getLogger(__name__)
|
|
11
|
+
|
|
12
|
+
class ToolRegistry(metaclass=SingletonMeta):
|
|
13
|
+
"""
|
|
14
|
+
Manages ToolDefinitions (name, description, tool_class), populated exclusively via
|
|
15
|
+
programmatic registration. Uses ToolFactory to create tool instances.
|
|
16
|
+
"""
|
|
17
|
+
_definitions: Dict[str, ToolDefinition] = {}
|
|
18
|
+
|
|
19
|
+
def __init__(self, tool_factory: ToolFactory):
|
|
20
|
+
"""
|
|
21
|
+
Initializes the ToolRegistry with a ToolFactory.
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
tool_factory: The ToolFactory instance used to create tool instances.
|
|
25
|
+
"""
|
|
26
|
+
self.tool_factory = tool_factory
|
|
27
|
+
logger.info("ToolRegistry initialized with ToolFactory.")
|
|
28
|
+
|
|
29
|
+
def register_tool(self, definition: ToolDefinition):
|
|
30
|
+
"""
|
|
31
|
+
Registers a tool definition (name, description, tool_class) programmatically.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
definition: The ToolDefinition object to register.
|
|
35
|
+
|
|
36
|
+
Raises:
|
|
37
|
+
ValueError: If the definition is invalid. Overwrites existing definitions with the same name.
|
|
38
|
+
"""
|
|
39
|
+
if not isinstance(definition, ToolDefinition):
|
|
40
|
+
raise ValueError("Attempted to register an object that is not a ToolDefinition.")
|
|
41
|
+
|
|
42
|
+
tool_name = definition.name
|
|
43
|
+
if tool_name in self._definitions:
|
|
44
|
+
logger.warning(f"Overwriting existing tool definition for name: '{tool_name}'")
|
|
45
|
+
ToolRegistry._definitions[tool_name] = definition
|
|
46
|
+
logger.info(f"Successfully registered tool definition: '{tool_name}'")
|
|
47
|
+
|
|
48
|
+
def get_tool_definition(self, name: str) -> Optional[ToolDefinition]:
|
|
49
|
+
"""
|
|
50
|
+
Retrieves the definition for a specific tool name.
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
name: The unique name of the tool definition to retrieve.
|
|
54
|
+
|
|
55
|
+
Returns:
|
|
56
|
+
The ToolDefinition object (name, description, tool_class) if found, otherwise None.
|
|
57
|
+
"""
|
|
58
|
+
definition = self._definitions.get(name)
|
|
59
|
+
if not definition:
|
|
60
|
+
logger.debug(f"Tool definition not found for name: '{name}'")
|
|
61
|
+
return definition
|
|
62
|
+
|
|
63
|
+
def create_tool(self, name: str):
|
|
64
|
+
"""
|
|
65
|
+
Creates a tool instance using the ToolFactory based on the tool definition.
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
name: The name of the tool to create.
|
|
69
|
+
|
|
70
|
+
Returns:
|
|
71
|
+
The tool instance if the definition exists, otherwise None.
|
|
72
|
+
|
|
73
|
+
Raises:
|
|
74
|
+
ValueError: If the tool definition is not found.
|
|
75
|
+
"""
|
|
76
|
+
definition = self.get_tool_definition(name)
|
|
77
|
+
if not definition:
|
|
78
|
+
logger.error(f"Cannot create tool: No definition found for name '{name}'")
|
|
79
|
+
raise ValueError(f"No tool definition found for name '{name}'")
|
|
80
|
+
|
|
81
|
+
logger.info(f"Creating tool instance for '{name}' using ToolFactory")
|
|
82
|
+
return self.tool_factory.create_tool(name)
|
|
83
|
+
|
|
84
|
+
def list_tools(self) -> List[ToolDefinition]:
|
|
85
|
+
"""
|
|
86
|
+
Returns a list of all registered tool definitions.
|
|
87
|
+
|
|
88
|
+
Returns:
|
|
89
|
+
A list of ToolDefinition objects (name, description, tool_class).
|
|
90
|
+
"""
|
|
91
|
+
return list(self._definitions.values())
|
|
92
|
+
|
|
93
|
+
def list_tool_names(self) -> List[str]:
|
|
94
|
+
"""
|
|
95
|
+
Returns a list of the names of all registered tools.
|
|
96
|
+
|
|
97
|
+
Returns:
|
|
98
|
+
A list of tool name strings.
|
|
99
|
+
"""
|
|
100
|
+
return list(self._definitions.keys())
|
|
101
|
+
|
|
102
|
+
def get_all_definitions(self) -> Dict[str, ToolDefinition]:
|
|
103
|
+
"""Returns the internal dictionary of definitions."""
|
|
104
|
+
return dict(ToolRegistry._definitions)
|
|
105
|
+
|
|
106
|
+
default_tool_registry = ToolRegistry(tool_factory=ToolFactory())
|