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.
Files changed (35) hide show
  1. autobyteus/agent/agent.py +1 -1
  2. autobyteus/agent/message/send_message_to.py +0 -3
  3. autobyteus/llm/autobyteus_provider.py +2 -1
  4. autobyteus/llm/llm_factory.py +33 -13
  5. autobyteus/llm/models.py +12 -0
  6. autobyteus/llm/ollama_provider.py +1 -0
  7. autobyteus/tools/ask_user_input.py +8 -19
  8. autobyteus/tools/base_tool.py +52 -24
  9. autobyteus/tools/bash/bash_executor.py +3 -15
  10. autobyteus/tools/browser/session_aware/browser_session_aware_navigate_to.py +8 -4
  11. autobyteus/tools/browser/session_aware/browser_session_aware_web_element_trigger.py +12 -55
  12. autobyteus/tools/browser/session_aware/browser_session_aware_webpage_reader.py +9 -5
  13. autobyteus/tools/browser/session_aware/browser_session_aware_webpage_screenshot_taker.py +8 -4
  14. autobyteus/tools/browser/standalone/google_search_ui.py +6 -32
  15. autobyteus/tools/browser/standalone/navigate_to.py +8 -16
  16. autobyteus/tools/browser/standalone/webpage_image_downloader.py +9 -60
  17. autobyteus/tools/browser/standalone/webpage_reader.py +8 -6
  18. autobyteus/tools/browser/standalone/webpage_screenshot_taker.py +9 -6
  19. autobyteus/tools/factory/tool_factory.py +7 -5
  20. autobyteus/tools/file/file_reader.py +6 -8
  21. autobyteus/tools/file/file_writer.py +6 -9
  22. autobyteus/tools/image_downloader.py +18 -41
  23. autobyteus/tools/pdf_downloader.py +8 -13
  24. autobyteus/tools/registry/__init__.py +11 -0
  25. autobyteus/tools/registry/tool_definition.py +60 -0
  26. autobyteus/tools/registry/tool_registry.py +106 -0
  27. autobyteus/tools/timer.py +16 -18
  28. autobyteus/tools/tool_meta.py +52 -0
  29. autobyteus/tools/utils.py +1 -1
  30. autobyteus/tools/web_page_pdf_generator.py +8 -4
  31. {autobyteus-1.0.4.dist-info → autobyteus-1.0.6.dist-info}/METADATA +2 -2
  32. {autobyteus-1.0.4.dist-info → autobyteus-1.0.6.dist-info}/RECORD +35 -31
  33. {autobyteus-1.0.4.dist-info → autobyteus-1.0.6.dist-info}/WHEEL +1 -1
  34. {autobyteus-1.0.4.dist-info → autobyteus-1.0.6.dist-info}/licenses/LICENSE +0 -0
  35. {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
- def tool_usage(self):
15
- return "NavigateTo: Navigates to a specified website. Usage: <<<NavigateTo(url='https://example.com')>>>, where 'https://example.com' is the URL of the website to navigate to."
14
+ @classmethod
15
+ def tool_usage_xml(cls):
16
+ """
17
+ Return an XML string describing the usage of the NavigateTo tool.
16
18
 
17
- def tool_usage_xml(self):
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
- def tool_usage(self):
15
- return "WebPageImageDownloader: Downloads images (excluding SVGs) from a given webpage and saves them to the specified directory. Usage: <<<WebPageImageDownloader(url='webpage_url', save_dir='path/to/save/directory')>>>, where 'webpage_url' is a string containing the URL of the webpage to download images from, and 'path/to/save/directory' is the directory where the images will be saved."
14
+ @classmethod
15
+ def tool_usage_xml(cls):
16
+ """
17
+ Return an XML string describing the usage of the WebPageImageDownloader tool.
16
18
 
17
- def tool_usage_xml(self):
18
- return '''
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
- def tool_usage(self):
43
- return 'WebPageReader: Reads and cleans the HTML content from a given webpage. Usage: <<<WebPageReader(url="webpage_url")>>>, where "webpage_url" is a string containing the URL of the webpage to read the content from.'
39
+ @classmethod
40
+ def tool_usage_xml(cls):
41
+ """
42
+ Return an XML string describing the usage of the WebPageReader tool.
44
43
 
45
- def tool_usage_xml(self):
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
- def tool_usage(self):
14
- return "WebPageScreenshotTaker: Takes a screenshot of a given webpage and saves it to the specified file path. Usage: <<<WebPageScreenshotTaker(url='webpage_url', file_path='screenshot_file_path')>>>, where 'webpage_url' is a string containing the URL of the webpage to take a screenshot of, and 'screenshot_file_path' is the path where the screenshot will be saved."
12
+ @classmethod
13
+ def tool_usage_xml(cls):
14
+ """
15
+ Return an XML string describing the usage of the WebPageScreenshotTaker tool.
15
16
 
16
- def tool_usage_xml(self):
17
- return '''WebPageScreenshotTaker: Takes a screenshot of a given webpage and saves it to the specified file path. Usage:
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 autobyteus.tools.base_tool import BaseTool
3
+ from typing import TYPE_CHECKING
4
4
 
5
- class ToolFactory(ABC):
6
- @abstractmethod
7
- def create_tool(self) -> BaseTool:
8
- pass
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
- def tool_usage(self):
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
- def tool_usage(self):
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 = ['.jpeg', '.jpg', '.gif', '.png', '.webp']
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
- def tool_usage(self):
25
- return f'''ImageDownloader: Downloads an image from a given URL.
26
-
27
- Usage: <<<ImageDownloader(url="image_url")>>>
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
- def tool_usage_xml(self):
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(self.supported_formats)})
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 self.supported_formats)}
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
- # Check if the URL ends with a supported format
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)}. \n"
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() # Verify the image integrity
109
- format = img.format
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
- def tool_usage(self):
21
+ @classmethod
22
+ def tool_usage_xml(cls):
22
23
  """
23
- Return a string describing the usage of the PDFDownloader tool.
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
- def tool_usage_xml(self):
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() # Raises an HTTPError for bad responses
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())