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
@@ -0,0 +1,28 @@
1
+ # file: autobyteus/autobyteus/tools/usage/formatters/default_json_schema_formatter.py
2
+ from typing import Dict, TYPE_CHECKING
3
+
4
+ from .base_formatter import BaseSchemaFormatter
5
+
6
+ if TYPE_CHECKING:
7
+ from autobyteus.tools.registry import ToolDefinition
8
+
9
+ class DefaultJsonSchemaFormatter(BaseSchemaFormatter):
10
+ """
11
+ Formats a tool's schema into a generic, provider-agnostic JSON format.
12
+ This serves as the default for JSON-based schema representation.
13
+ """
14
+
15
+ def provide(self, tool_definition: 'ToolDefinition') -> Dict:
16
+ name = tool_definition.name
17
+ description = tool_definition.description
18
+ arg_schema = tool_definition.argument_schema
19
+
20
+ input_schema = arg_schema.to_json_schema_dict() if arg_schema else {
21
+ "type": "object", "properties": {}, "required": []
22
+ }
23
+
24
+ return {
25
+ "name": name,
26
+ "description": description,
27
+ "inputSchema": input_schema,
28
+ }
@@ -0,0 +1,55 @@
1
+ # file: autobyteus/autobyteus/tools/usage/formatters/default_xml_example_formatter.py
2
+ import xml.sax.saxutils
3
+ from typing import Any, TYPE_CHECKING
4
+
5
+ from autobyteus.tools.parameter_schema import ParameterType, ParameterDefinition
6
+ from .base_formatter import BaseExampleFormatter
7
+
8
+ if TYPE_CHECKING:
9
+ from autobyteus.tools.registry import ToolDefinition
10
+
11
+ class DefaultXmlExampleFormatter(BaseExampleFormatter):
12
+ """Formats a tool usage example into a standardized XML <tool> string."""
13
+
14
+ def provide(self, tool_definition: 'ToolDefinition') -> str:
15
+ tool_name = tool_definition.name
16
+ arg_schema = tool_definition.argument_schema
17
+
18
+ example_xml_parts = [f'<tool name="{tool_name}">']
19
+ arguments_part = []
20
+
21
+ if arg_schema and arg_schema.parameters:
22
+ for param_def in arg_schema.parameters:
23
+ if param_def.required or param_def.default_value is not None:
24
+ placeholder_value = self._generate_placeholder_value(param_def)
25
+ escaped_value = xml.sax.saxutils.escape(str(placeholder_value))
26
+ arguments_part.append(f' <arg name="{param_def.name}">{escaped_value}</arg>')
27
+
28
+ if arguments_part:
29
+ example_xml_parts.append(" <arguments>")
30
+ example_xml_parts.extend(arguments_part)
31
+ example_xml_parts.append(" </arguments>")
32
+ else:
33
+ example_xml_parts.append(" <!-- This tool takes no arguments -->")
34
+
35
+ example_xml_parts.append("</tool>")
36
+ return "\n".join(example_xml_parts)
37
+
38
+ def _generate_placeholder_value(self, param_def: ParameterDefinition) -> Any:
39
+ if param_def.default_value is not None:
40
+ return param_def.default_value
41
+ if param_def.param_type == ParameterType.STRING:
42
+ return f"example_{param_def.name}"
43
+ if param_def.param_type == ParameterType.INTEGER:
44
+ return 123
45
+ if param_def.param_type == ParameterType.FLOAT:
46
+ return 123.45
47
+ if param_def.param_type == ParameterType.BOOLEAN:
48
+ return True
49
+ if param_def.param_type == ParameterType.ENUM:
50
+ return param_def.enum_values[0] if param_def.enum_values else "enum_val"
51
+ if param_def.param_type == ParameterType.OBJECT:
52
+ return {"key": "value"}
53
+ if param_def.param_type == ParameterType.ARRAY:
54
+ return ["item1", "item2"]
55
+ return "placeholder"
@@ -0,0 +1,46 @@
1
+ # file: autobyteus/autobyteus/tools/usage/formatters/default_xml_schema_formatter.py
2
+ import xml.sax.saxutils
3
+ from typing import TYPE_CHECKING
4
+
5
+ from autobyteus.tools.parameter_schema import ParameterType
6
+ from .base_formatter import BaseSchemaFormatter
7
+
8
+ if TYPE_CHECKING:
9
+ from autobyteus.tools.registry import ToolDefinition
10
+
11
+ class DefaultXmlSchemaFormatter(BaseSchemaFormatter):
12
+ """Formats a tool's schema into a standardized XML string."""
13
+
14
+ def provide(self, tool_definition: 'ToolDefinition') -> str:
15
+ arg_schema = tool_definition.argument_schema
16
+ tool_name = tool_definition.name
17
+ description = tool_definition.description
18
+
19
+ escaped_description = xml.sax.saxutils.escape(description) if description else ""
20
+ tool_tag = f'<tool name="{tool_name}" description="{escaped_description}">'
21
+ xml_parts = [tool_tag]
22
+
23
+ if arg_schema and arg_schema.parameters:
24
+ xml_parts.append(" <arguments>")
25
+ for param in arg_schema.parameters:
26
+ arg_tag = f' <arg name="{param.name}"'
27
+ arg_tag += f' type="{param.param_type.value}"'
28
+ if param.description:
29
+ escaped_param_desc = xml.sax.saxutils.escape(param.description)
30
+ arg_tag += f' description="{escaped_param_desc}"'
31
+ arg_tag += f" required=\"{'true' if param.required else 'false'}\""
32
+
33
+ if param.default_value is not None:
34
+ arg_tag += f' default="{xml.sax.saxutils.escape(str(param.default_value))}"'
35
+ if param.param_type == ParameterType.ENUM and param.enum_values:
36
+ escaped_enum_values = [xml.sax.saxutils.escape(ev) for ev in param.enum_values]
37
+ arg_tag += f' enum_values="{",".join(escaped_enum_values)}"'
38
+
39
+ arg_tag += " />"
40
+ xml_parts.append(arg_tag)
41
+ xml_parts.append(" </arguments>")
42
+ else:
43
+ xml_parts.append(" <!-- This tool takes no arguments -->")
44
+
45
+ xml_parts.append("</tool>")
46
+ return "\n".join(xml_parts)
@@ -0,0 +1,34 @@
1
+ # file: autobyteus/autobyteus/tools/usage/formatters/gemini_json_example_formatter.py
2
+ from typing import Dict, Any, TYPE_CHECKING
3
+
4
+ from autobyteus.tools.parameter_schema import ParameterType, ParameterDefinition
5
+ from .base_formatter import BaseExampleFormatter
6
+
7
+ if TYPE_CHECKING:
8
+ from autobyteus.tools.registry import ToolDefinition
9
+
10
+ class GeminiJsonExampleFormatter(BaseExampleFormatter):
11
+ """Formats a tool usage example into the Google Gemini tool_calls format."""
12
+
13
+ def provide(self, tool_definition: 'ToolDefinition') -> Dict:
14
+ tool_name = tool_definition.name
15
+ arg_schema = tool_definition.argument_schema
16
+ arguments = {}
17
+
18
+ if arg_schema and arg_schema.parameters:
19
+ for param_def in arg_schema.parameters:
20
+ if param_def.required or param_def.default_value is not None:
21
+ arguments[param_def.name] = self._generate_placeholder_value(param_def)
22
+
23
+ return {"name": tool_name, "args": arguments}
24
+
25
+ def _generate_placeholder_value(self, param_def: ParameterDefinition) -> Any:
26
+ if param_def.default_value is not None: return param_def.default_value
27
+ if param_def.param_type == ParameterType.STRING: return f"example_{param_def.name}"
28
+ if param_def.param_type == ParameterType.INTEGER: return 123
29
+ if param_def.param_type == ParameterType.FLOAT: return 123.45
30
+ if param_def.param_type == ParameterType.BOOLEAN: return True
31
+ if param_def.param_type == ParameterType.ENUM: return param_def.enum_values[0] if param_def.enum_values else "enum_val"
32
+ if param_def.param_type == ParameterType.OBJECT: return {"key": "value"}
33
+ if param_def.param_type == ParameterType.ARRAY: return ["item1", "item2"]
34
+ return "placeholder"
@@ -0,0 +1,25 @@
1
+ # file: autobyteus/autobyteus/tools/usage/formatters/gemini_json_schema_formatter.py
2
+ from typing import Dict, TYPE_CHECKING
3
+
4
+ from .base_formatter import BaseSchemaFormatter
5
+
6
+ if TYPE_CHECKING:
7
+ from autobyteus.tools.registry import ToolDefinition
8
+
9
+ class GeminiJsonSchemaFormatter(BaseSchemaFormatter):
10
+ """Formats a tool's schema into a Google Gemini function declaration format."""
11
+
12
+ def provide(self, tool_definition: 'ToolDefinition') -> Dict:
13
+ name = tool_definition.name
14
+ description = tool_definition.description
15
+ arg_schema = tool_definition.argument_schema
16
+
17
+ parameters = arg_schema.to_json_schema_dict() if arg_schema else {
18
+ "type": "object", "properties": {}, "required": []
19
+ }
20
+
21
+ return {
22
+ "name": name,
23
+ "description": description,
24
+ "parameters": parameters,
25
+ }
@@ -0,0 +1,34 @@
1
+ # file: autobyteus/autobyteus/tools/usage/formatters/google_json_example_formatter.py
2
+ from typing import Dict, Any, TYPE_CHECKING
3
+
4
+ from autobyteus.tools.parameter_schema import ParameterType, ParameterDefinition
5
+ from .base_formatter import BaseExampleFormatter
6
+
7
+ if TYPE_CHECKING:
8
+ from autobyteus.tools.registry import ToolDefinition
9
+
10
+ class GoogleJsonExampleFormatter(BaseExampleFormatter):
11
+ """Formats a tool usage example into the Google JSON tool_calls format."""
12
+
13
+ def provide(self, tool_definition: 'ToolDefinition') -> Dict:
14
+ tool_name = tool_definition.name
15
+ arg_schema = tool_definition.argument_schema
16
+ arguments = {}
17
+
18
+ if arg_schema and arg_schema.parameters:
19
+ for param_def in arg_schema.parameters:
20
+ if param_def.required or param_def.default_value is not None:
21
+ arguments[param_def.name] = self._generate_placeholder_value(param_def)
22
+
23
+ return {"name": tool_name, "args": arguments}
24
+
25
+ def _generate_placeholder_value(self, param_def: ParameterDefinition) -> Any:
26
+ if param_def.default_value is not None: return param_def.default_value
27
+ if param_def.param_type == ParameterType.STRING: return f"example_{param_def.name}"
28
+ if param_def.param_type == ParameterType.INTEGER: return 123
29
+ if param_def.param_type == ParameterType.FLOAT: return 123.45
30
+ if param_def.param_type == ParameterType.BOOLEAN: return True
31
+ if param_def.param_type == ParameterType.ENUM: return param_def.enum_values[0] if param_def.enum_values else "enum_val"
32
+ if param_def.param_type == ParameterType.OBJECT: return {"key": "value"}
33
+ if param_def.param_type == ParameterType.ARRAY: return ["item1", "item2"]
34
+ return "placeholder"
@@ -0,0 +1,25 @@
1
+ # file: autobyteus/autobyteus/tools/usage/formatters/google_json_schema_formatter.py
2
+ from typing import Dict, TYPE_CHECKING
3
+
4
+ from .base_formatter import BaseSchemaFormatter
5
+
6
+ if TYPE_CHECKING:
7
+ from autobyteus.tools.registry import ToolDefinition
8
+
9
+ class GoogleJsonSchemaFormatter(BaseSchemaFormatter):
10
+ """Formats a tool's schema into a Google function declaration format."""
11
+
12
+ def provide(self, tool_definition: 'ToolDefinition') -> Dict:
13
+ name = tool_definition.name
14
+ description = tool_definition.description
15
+ arg_schema = tool_definition.argument_schema
16
+
17
+ parameters = arg_schema.to_json_schema_dict() if arg_schema else {
18
+ "type": "object", "properties": {}, "required": []
19
+ }
20
+
21
+ return {
22
+ "name": name,
23
+ "description": description,
24
+ "parameters": parameters,
25
+ }
@@ -0,0 +1,49 @@
1
+ # file: autobyteus/autobyteus/tools/usage/formatters/openai_json_example_formatter.py
2
+ import json
3
+ from typing import Dict, Any, TYPE_CHECKING
4
+
5
+ from autobyteus.tools.parameter_schema import ParameterType, ParameterDefinition
6
+ from .base_formatter import BaseExampleFormatter
7
+
8
+ if TYPE_CHECKING:
9
+ from autobyteus.tools.registry import ToolDefinition
10
+
11
+ class OpenAiJsonExampleFormatter(BaseExampleFormatter):
12
+ """
13
+ Formats a tool usage example into a format resembling an entry in the
14
+ OpenAI JSON 'tool_calls' array, intended for prompting a model.
15
+ The output is wrapped in a 'tool' key for consistency in prompts.
16
+ """
17
+
18
+ def provide(self, tool_definition: 'ToolDefinition') -> Dict:
19
+ tool_name = tool_definition.name
20
+ arg_schema = tool_definition.argument_schema
21
+ arguments = {}
22
+
23
+ if arg_schema and arg_schema.parameters:
24
+ for param_def in arg_schema.parameters:
25
+ if param_def.required or param_def.default_value is not None:
26
+ arguments[param_def.name] = self._generate_placeholder_value(param_def)
27
+
28
+ # This format contains the 'function' wrapper with a stringified 'arguments' field.
29
+ # This aligns with the structure often seen inside an OpenAI API tool_calls object.
30
+ function_call = {
31
+ "function": {
32
+ "name": tool_name,
33
+ "arguments": json.dumps(arguments),
34
+ },
35
+ }
36
+
37
+ # Wrap in a 'tool' key for consistency in prompt generation.
38
+ return {"tool": function_call}
39
+
40
+ def _generate_placeholder_value(self, param_def: ParameterDefinition) -> Any:
41
+ if param_def.default_value is not None: return param_def.default_value
42
+ if param_def.param_type == ParameterType.STRING: return f"example_{param_def.name}"
43
+ if param_def.param_type == ParameterType.INTEGER: return 123
44
+ if param_def.param_type == ParameterType.FLOAT: return 123.45
45
+ if param_def.param_type == ParameterType.BOOLEAN: return True
46
+ if param_def.param_type == ParameterType.ENUM: return param_def.enum_values[0] if param_def.enum_values else "enum_val"
47
+ if param_def.param_type == ParameterType.OBJECT: return {"key": "value"}
48
+ if param_def.param_type == ParameterType.ARRAY: return ["item1", "item2"]
49
+ return "placeholder"
@@ -0,0 +1,28 @@
1
+ # file: autobyteus/autobyteus/tools/usage/formatters/openai_json_schema_formatter.py
2
+ from typing import Dict, TYPE_CHECKING
3
+
4
+ from .base_formatter import BaseSchemaFormatter
5
+
6
+ if TYPE_CHECKING:
7
+ from autobyteus.tools.registry import ToolDefinition
8
+
9
+ class OpenAiJsonSchemaFormatter(BaseSchemaFormatter):
10
+ """Formats a tool's schema into the OpenAI JSON function format."""
11
+
12
+ def provide(self, tool_definition: 'ToolDefinition') -> Dict:
13
+ name = tool_definition.name
14
+ description = tool_definition.description
15
+ arg_schema = tool_definition.argument_schema
16
+
17
+ parameters = arg_schema.to_json_schema_dict() if arg_schema else {
18
+ "type": "object", "properties": {}, "required": []
19
+ }
20
+
21
+ return {
22
+ "type": "function",
23
+ "function": {
24
+ "name": name,
25
+ "description": description,
26
+ "parameters": parameters,
27
+ },
28
+ }
@@ -0,0 +1,22 @@
1
+ # file: autobyteus/autobyteus/tools/usage/parsers/__init__.py
2
+ """
3
+ This package contains concrete parser classes that translate an LLM's raw response
4
+ text into structured ToolInvocation objects.
5
+ """
6
+ from .base_parser import BaseToolUsageParser
7
+ from .provider_aware_tool_usage_parser import ProviderAwareToolUsageParser
8
+ from .default_xml_tool_usage_parser import DefaultXmlToolUsageParser
9
+ from .anthropic_xml_tool_usage_parser import AnthropicXmlToolUsageParser
10
+ from .default_json_tool_usage_parser import DefaultJsonToolUsageParser
11
+ from .openai_json_tool_usage_parser import OpenAiJsonToolUsageParser
12
+ from .gemini_json_tool_usage_parser import GeminiJsonToolUsageParser
13
+
14
+ __all__ = [
15
+ "BaseToolUsageParser",
16
+ "ProviderAwareToolUsageParser",
17
+ "DefaultXmlToolUsageParser",
18
+ "AnthropicXmlToolUsageParser",
19
+ "DefaultJsonToolUsageParser",
20
+ "OpenAiJsonToolUsageParser",
21
+ "GeminiJsonToolUsageParser",
22
+ ]
@@ -0,0 +1,10 @@
1
+ # file: autobyteus/autobyteus/tools/usage/parsers/anthropic_xml_tool_usage_parser.py
2
+ from .default_xml_tool_usage_parser import DefaultXmlToolUsageParser
3
+
4
+ class AnthropicXmlToolUsageParser(DefaultXmlToolUsageParser):
5
+ """
6
+ Parser for Anthropic models. Anthropic uses XML for tool calls,
7
+ so this is an alias for the default XML parser.
8
+ """
9
+ def get_name(self) -> str:
10
+ return "anthropic_xml_tool_usage_parser"
@@ -0,0 +1,41 @@
1
+ # file: autobyteus/autobyteus/tools/usage/parsers/base_parser.py
2
+ import logging
3
+ from abc import ABC, abstractmethod
4
+ from typing import List, TYPE_CHECKING
5
+
6
+ if TYPE_CHECKING:
7
+ from autobyteus.agent.tool_invocation import ToolInvocation
8
+ from autobyteus.llm.utils.response_types import CompleteResponse
9
+
10
+ logger = logging.getLogger(__name__)
11
+
12
+ class BaseToolUsageParser(ABC):
13
+ """
14
+ Abstract base class for parsing tool usage from an LLM's response text.
15
+ These parsers are responsible for extracting structured tool call information.
16
+ """
17
+
18
+ def get_name(self) -> str:
19
+ """
20
+ Returns the unique name for this parser.
21
+ Defaults to the class name.
22
+ """
23
+ return self.__class__.__name__
24
+
25
+ @abstractmethod
26
+ def parse(self, response: 'CompleteResponse') -> List['ToolInvocation']:
27
+ """
28
+ Parses the LLM's response. If actionable tool calls are found,
29
+ this method should return a list of ToolInvocation objects.
30
+
31
+ Args:
32
+ response: The CompleteResponse object from the LLM.
33
+
34
+ Returns:
35
+ A list of ToolInvocation objects. Returns an empty list if no
36
+ valid tool calls are found.
37
+ """
38
+ raise NotImplementedError("Subclasses must implement the 'parse' method.")
39
+
40
+ def __repr__(self) -> str:
41
+ return f"<{self.__class__.__name__}>"
@@ -0,0 +1,106 @@
1
+ # file: autobyteus/autobyteus/tools/usage/parsers/default_json_tool_usage_parser.py
2
+ import json
3
+ import re
4
+ import logging
5
+ from typing import Optional, Dict, Any, TYPE_CHECKING, List
6
+ import uuid
7
+
8
+ from autobyteus.agent.tool_invocation import ToolInvocation
9
+ from .base_parser import BaseToolUsageParser
10
+
11
+ if TYPE_CHECKING:
12
+ from autobyteus.llm.utils.response_types import CompleteResponse
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+ class DefaultJsonToolUsageParser(BaseToolUsageParser):
17
+ """
18
+ A default parser for tool usage commands formatted as custom JSON.
19
+ It expects a 'tool' object with 'function' and 'parameters' keys.
20
+ """
21
+ def get_name(self) -> str:
22
+ return "default_json_tool_usage_parser"
23
+
24
+ def parse(self, response: 'CompleteResponse') -> List[ToolInvocation]:
25
+ response_text = self._extract_json_from_response(response.content)
26
+ if not response_text:
27
+ return []
28
+
29
+ try:
30
+ data = json.loads(response_text)
31
+ except json.JSONDecodeError:
32
+ logger.debug(f"Could not parse extracted text as JSON. Text: {response_text[:200]}")
33
+ return []
34
+
35
+ tool_calls_data = []
36
+ if isinstance(data, list):
37
+ tool_calls_data = data
38
+ elif isinstance(data, dict):
39
+ if "tools" in data and isinstance(data.get("tools"), list):
40
+ tool_calls_data = data["tools"]
41
+ else:
42
+ tool_calls_data = [data]
43
+ else:
44
+ return []
45
+
46
+ invocations: List[ToolInvocation] = []
47
+ for call_data in tool_calls_data:
48
+ if not isinstance(call_data, dict):
49
+ continue
50
+
51
+ tool_block = call_data.get("tool")
52
+ if not isinstance(tool_block, dict):
53
+ continue
54
+
55
+ tool_name = tool_block.get("function")
56
+ arguments = tool_block.get("parameters")
57
+
58
+ if not tool_name or not isinstance(tool_name, str):
59
+ logger.debug(f"Skipping malformed tool block (missing or invalid 'function'): {tool_block}")
60
+ continue
61
+
62
+ if arguments is None:
63
+ arguments = {}
64
+
65
+ if not isinstance(arguments, dict):
66
+ logger.debug(f"Skipping tool block with invalid 'parameters' type ({type(arguments)}): {tool_block}")
67
+ continue
68
+
69
+ # The custom format does not have a tool ID, so we generate one.
70
+ tool_id = str(uuid.uuid4())
71
+ try:
72
+ tool_invocation = ToolInvocation(name=tool_name, arguments=arguments, id=tool_id)
73
+ invocations.append(tool_invocation)
74
+ except Exception as e:
75
+ logger.error(f"Unexpected error creating ToolInvocation for tool '{tool_name}': {e}", exc_info=True)
76
+
77
+ return invocations
78
+
79
+ def _extract_json_from_response(self, text: str) -> Optional[str]:
80
+ match = re.search(r"```(?:json)?\s*([\s\S]+?)\s*```", text)
81
+ if match:
82
+ return match.group(1).strip()
83
+
84
+ # Try to find a JSON object or array in the text
85
+ first_bracket = text.find('[')
86
+ first_brace = text.find('{')
87
+
88
+ if first_brace == -1 and first_bracket == -1:
89
+ return None
90
+
91
+ start_index = -1
92
+ if first_bracket != -1 and first_brace != -1:
93
+ start_index = min(first_bracket, first_brace)
94
+ elif first_bracket != -1:
95
+ start_index = first_bracket
96
+ else: # first_brace != -1
97
+ start_index = first_brace
98
+
99
+ json_substring = text[start_index:]
100
+ try:
101
+ # Check if the substring is valid JSON
102
+ json.loads(json_substring)
103
+ return json_substring
104
+ except json.JSONDecodeError:
105
+ logger.debug(f"Found potential start of JSON, but substring was not valid: {json_substring[:100]}")
106
+ return None
@@ -0,0 +1,135 @@
1
+ # file: autobyteus/autobyteus/tools/usage/parsers/default_xml_tool_usage_parser.py
2
+ import xml.etree.ElementTree as ET
3
+ import re
4
+ import uuid
5
+ from xml.sax.saxutils import escape, unescape
6
+ import xml.parsers.expat
7
+ import logging
8
+ from typing import TYPE_CHECKING, Dict, Any, List
9
+
10
+ from autobyteus.agent.tool_invocation import ToolInvocation
11
+ from .base_parser import BaseToolUsageParser
12
+ from .exceptions import ToolUsageParseException
13
+
14
+ if TYPE_CHECKING:
15
+ from autobyteus.llm.utils.response_types import CompleteResponse
16
+
17
+ logger = logging.getLogger(__name__)
18
+
19
+ class DefaultXmlToolUsageParser(BaseToolUsageParser):
20
+ """
21
+ Parses LLM responses for tool usage commands formatted as XML.
22
+ It looks for either a <tools> block (for multiple calls) or a
23
+ single <tool> block.
24
+ """
25
+ def get_name(self) -> str:
26
+ return "default_xml_tool_usage_parser"
27
+
28
+ def parse(self, response: 'CompleteResponse') -> List[ToolInvocation]:
29
+ response_text = response.content
30
+ logger.debug(f"{self.get_name()} attempting to parse response (first 500 chars): {response_text[:500]}...")
31
+
32
+ invocations: List[ToolInvocation] = []
33
+ match = re.search(r"<tools\b[^>]*>.*?</tools\s*>|<tool\b[^>]*>.*?</tool\s*>", response_text, re.DOTALL | re.IGNORECASE)
34
+ if not match:
35
+ logger.debug(f"No <tools> or <tool> block found by {self.get_name()}.")
36
+ return invocations
37
+
38
+ xml_content = match.group(0)
39
+ processed_xml = self._preprocess_xml_for_parsing(xml_content)
40
+
41
+ try:
42
+ root = ET.fromstring(processed_xml)
43
+ tool_elements = []
44
+
45
+ if root.tag.lower() == "tools":
46
+ tool_elements = root.findall('tool')
47
+ if not tool_elements:
48
+ logger.debug("Found <tools> but no <tool> children.")
49
+ return invocations
50
+ elif root.tag.lower() == "tool":
51
+ tool_elements = [root]
52
+ else:
53
+ logger.warning(f"Root XML tag is '{root.tag}', not 'tools' or 'tool'. Skipping parsing.")
54
+ return invocations
55
+
56
+ for tool_elem in tool_elements:
57
+ tool_name = tool_elem.attrib.get("name")
58
+ tool_id = tool_elem.attrib.get("id") or str(uuid.uuid4())
59
+ arguments = self._parse_arguments_from_xml(tool_elem)
60
+
61
+ if tool_name:
62
+ tool_invocation = ToolInvocation(name=tool_name, arguments=arguments, id=tool_id)
63
+ invocations.append(tool_invocation)
64
+ else:
65
+ logger.warning(f"Parsed a <tool> element but its 'name' attribute is missing or empty.")
66
+
67
+ except (ET.ParseError, xml.parsers.expat.ExpatError) as e:
68
+ error_msg = f"XML parsing error in '{self.get_name()}': {e}. Content: '{processed_xml[:200]}'"
69
+ logger.debug(error_msg)
70
+ # Raise a specific exception to be caught upstream.
71
+ raise ToolUsageParseException(error_msg, original_exception=e)
72
+
73
+ except Exception as e:
74
+ logger.error(f"Unexpected error in {self.get_name()} processing XML: {e}. XML Content: {xml_content[:200]}", exc_info=True)
75
+ # Also wrap unexpected errors for consistent handling.
76
+ raise ToolUsageParseException(f"Unexpected error during XML parsing: {e}", original_exception=e)
77
+
78
+ return invocations
79
+
80
+ def _preprocess_xml_for_parsing(self, xml_content: str) -> str:
81
+ """
82
+ Preprocesses raw XML string from an LLM to fix common errors before parsing.
83
+ """
84
+ processed_content = re.sub(
85
+ r'(<arg\s+name\s*=\s*")([^"]+?)>',
86
+ r'\1\2">',
87
+ xml_content,
88
+ flags=re.IGNORECASE
89
+ )
90
+ if processed_content != xml_content:
91
+ logger.debug("Preprocessor fixed a missing quote in an <arg> tag.")
92
+
93
+ cdata_sections: Dict[str, str] = {}
94
+ def cdata_replacer(match_obj: re.Match) -> str:
95
+ placeholder = f"__CDATA_PLACEHOLDER_{len(cdata_sections)}__"
96
+ cdata_sections[placeholder] = match_obj.group(0)
97
+ return placeholder
98
+
99
+ xml_no_cdata = re.sub(r'<!\[CDATA\[.*?\]\]>', cdata_replacer, processed_content, flags=re.DOTALL)
100
+
101
+ def escape_arg_value(match_obj: re.Match) -> str:
102
+ open_tag = match_obj.group(1)
103
+ content = match_obj.group(2)
104
+ close_tag = match_obj.group(3)
105
+ if re.search(r'<\s*/?[a-zA-Z]', content.strip()):
106
+ return f"{open_tag}{content}{close_tag}"
107
+ escaped_content = escape(content) if not content.startswith("__CDATA_PLACEHOLDER_") else content
108
+ return f"{open_tag}{escaped_content}{close_tag}"
109
+
110
+ processed_content = re.sub(
111
+ r'(<arg\s+name\s*=\s*"[^"]*"\s*>\s*)(.*?)(\s*</arg\s*>)',
112
+ escape_arg_value,
113
+ xml_no_cdata,
114
+ flags=re.DOTALL | re.IGNORECASE
115
+ )
116
+
117
+ for placeholder, original_cdata_tag in cdata_sections.items():
118
+ processed_content = processed_content.replace(placeholder, original_cdata_tag)
119
+
120
+ return processed_content
121
+
122
+ def _parse_arguments_from_xml(self, command_element: ET.Element) -> Dict[str, Any]:
123
+ arguments: Dict[str, Any] = {}
124
+ arguments_container = command_element.find('arguments')
125
+ if arguments_container is None:
126
+ logger.debug(f"No <arguments> tag found in <tool name='{command_element.attrib.get('name')}'>. No arguments will be parsed.")
127
+ return arguments
128
+
129
+ for arg_element in arguments_container.findall('arg'):
130
+ arg_name = arg_element.attrib.get('name')
131
+ if arg_name:
132
+ raw_text = "".join(arg_element.itertext())
133
+ unescaped_value = unescape(raw_text)
134
+ arguments[arg_name] = unescaped_value
135
+ return arguments
@@ -0,0 +1,13 @@
1
+ # file: autobyteus/autobyteus/tools/usage/parsers/exceptions.py
2
+ """
3
+ Custom exceptions for the tool usage parsing module.
4
+ """
5
+
6
+ class ToolUsageParseException(Exception):
7
+ """
8
+ Raised when a tool usage parser fails to parse an LLM response due to
9
+ malformed content or other parsing errors.
10
+ """
11
+ def __init__(self, message: str, original_exception: Exception = None):
12
+ super().__init__(message)
13
+ self.original_exception = original_exception