autobyteus 1.1.4__py3-none-any.whl → 1.1.5__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 (167) hide show
  1. autobyteus/agent/context/__init__.py +4 -2
  2. autobyteus/agent/context/agent_config.py +0 -4
  3. autobyteus/agent/context/agent_context_registry.py +73 -0
  4. autobyteus/agent/events/notifiers.py +4 -0
  5. autobyteus/agent/handlers/inter_agent_message_event_handler.py +7 -2
  6. autobyteus/agent/handlers/llm_complete_response_received_event_handler.py +19 -19
  7. autobyteus/agent/handlers/user_input_message_event_handler.py +15 -0
  8. autobyteus/agent/message/send_message_to.py +29 -23
  9. autobyteus/agent/runtime/agent_runtime.py +10 -2
  10. autobyteus/agent/sender_type.py +15 -0
  11. autobyteus/agent/streaming/agent_event_stream.py +6 -0
  12. autobyteus/agent/streaming/stream_event_payloads.py +12 -0
  13. autobyteus/agent/streaming/stream_events.py +3 -0
  14. autobyteus/agent/system_prompt_processor/tool_manifest_injector_processor.py +7 -4
  15. autobyteus/agent_team/__init__.py +1 -0
  16. autobyteus/agent_team/agent_team.py +93 -0
  17. autobyteus/agent_team/agent_team_builder.py +184 -0
  18. autobyteus/agent_team/base_agent_team.py +86 -0
  19. autobyteus/agent_team/bootstrap_steps/__init__.py +24 -0
  20. autobyteus/agent_team/bootstrap_steps/agent_configuration_preparation_step.py +73 -0
  21. autobyteus/agent_team/bootstrap_steps/agent_team_bootstrapper.py +54 -0
  22. autobyteus/agent_team/bootstrap_steps/agent_team_runtime_queue_initialization_step.py +25 -0
  23. autobyteus/agent_team/bootstrap_steps/base_agent_team_bootstrap_step.py +23 -0
  24. autobyteus/agent_team/bootstrap_steps/coordinator_initialization_step.py +41 -0
  25. autobyteus/agent_team/bootstrap_steps/coordinator_prompt_preparation_step.py +85 -0
  26. autobyteus/agent_team/bootstrap_steps/task_notifier_initialization_step.py +51 -0
  27. autobyteus/agent_team/bootstrap_steps/team_context_initialization_step.py +45 -0
  28. autobyteus/agent_team/context/__init__.py +17 -0
  29. autobyteus/agent_team/context/agent_team_config.py +33 -0
  30. autobyteus/agent_team/context/agent_team_context.py +61 -0
  31. autobyteus/agent_team/context/agent_team_runtime_state.py +56 -0
  32. autobyteus/agent_team/context/team_manager.py +147 -0
  33. autobyteus/agent_team/context/team_node_config.py +76 -0
  34. autobyteus/agent_team/events/__init__.py +29 -0
  35. autobyteus/agent_team/events/agent_team_event_dispatcher.py +39 -0
  36. autobyteus/agent_team/events/agent_team_events.py +53 -0
  37. autobyteus/agent_team/events/agent_team_input_event_queue_manager.py +21 -0
  38. autobyteus/agent_team/exceptions.py +8 -0
  39. autobyteus/agent_team/factory/__init__.py +9 -0
  40. autobyteus/agent_team/factory/agent_team_factory.py +99 -0
  41. autobyteus/agent_team/handlers/__init__.py +19 -0
  42. autobyteus/agent_team/handlers/agent_team_event_handler_registry.py +23 -0
  43. autobyteus/agent_team/handlers/base_agent_team_event_handler.py +16 -0
  44. autobyteus/agent_team/handlers/inter_agent_message_request_event_handler.py +61 -0
  45. autobyteus/agent_team/handlers/lifecycle_agent_team_event_handler.py +27 -0
  46. autobyteus/agent_team/handlers/process_user_message_event_handler.py +46 -0
  47. autobyteus/agent_team/handlers/tool_approval_team_event_handler.py +48 -0
  48. autobyteus/agent_team/phases/__init__.py +11 -0
  49. autobyteus/agent_team/phases/agent_team_operational_phase.py +19 -0
  50. autobyteus/agent_team/phases/agent_team_phase_manager.py +48 -0
  51. autobyteus/agent_team/runtime/__init__.py +13 -0
  52. autobyteus/agent_team/runtime/agent_team_runtime.py +82 -0
  53. autobyteus/agent_team/runtime/agent_team_worker.py +117 -0
  54. autobyteus/agent_team/shutdown_steps/__init__.py +17 -0
  55. autobyteus/agent_team/shutdown_steps/agent_team_shutdown_orchestrator.py +35 -0
  56. autobyteus/agent_team/shutdown_steps/agent_team_shutdown_step.py +42 -0
  57. autobyteus/agent_team/shutdown_steps/base_agent_team_shutdown_step.py +16 -0
  58. autobyteus/agent_team/shutdown_steps/bridge_cleanup_step.py +28 -0
  59. autobyteus/agent_team/shutdown_steps/sub_team_shutdown_step.py +41 -0
  60. autobyteus/agent_team/streaming/__init__.py +26 -0
  61. autobyteus/agent_team/streaming/agent_event_bridge.py +48 -0
  62. autobyteus/agent_team/streaming/agent_event_multiplexer.py +70 -0
  63. autobyteus/agent_team/streaming/agent_team_event_notifier.py +64 -0
  64. autobyteus/agent_team/streaming/agent_team_event_stream.py +33 -0
  65. autobyteus/agent_team/streaming/agent_team_stream_event_payloads.py +32 -0
  66. autobyteus/agent_team/streaming/agent_team_stream_events.py +56 -0
  67. autobyteus/agent_team/streaming/team_event_bridge.py +50 -0
  68. autobyteus/agent_team/task_notification/__init__.py +11 -0
  69. autobyteus/agent_team/task_notification/system_event_driven_agent_task_notifier.py +164 -0
  70. autobyteus/agent_team/task_notification/task_notification_mode.py +24 -0
  71. autobyteus/agent_team/utils/__init__.py +9 -0
  72. autobyteus/agent_team/utils/wait_for_idle.py +46 -0
  73. autobyteus/cli/agent_team_tui/__init__.py +4 -0
  74. autobyteus/cli/agent_team_tui/app.py +210 -0
  75. autobyteus/cli/agent_team_tui/state.py +180 -0
  76. autobyteus/cli/agent_team_tui/widgets/__init__.py +6 -0
  77. autobyteus/cli/agent_team_tui/widgets/agent_list_sidebar.py +149 -0
  78. autobyteus/cli/agent_team_tui/widgets/focus_pane.py +320 -0
  79. autobyteus/cli/agent_team_tui/widgets/logo.py +20 -0
  80. autobyteus/cli/agent_team_tui/widgets/renderables.py +77 -0
  81. autobyteus/cli/agent_team_tui/widgets/shared.py +60 -0
  82. autobyteus/cli/agent_team_tui/widgets/status_bar.py +14 -0
  83. autobyteus/cli/agent_team_tui/widgets/task_board_panel.py +82 -0
  84. autobyteus/events/event_types.py +7 -2
  85. autobyteus/llm/api/autobyteus_llm.py +11 -12
  86. autobyteus/llm/api/lmstudio_llm.py +10 -13
  87. autobyteus/llm/api/ollama_llm.py +8 -13
  88. autobyteus/llm/autobyteus_provider.py +73 -46
  89. autobyteus/llm/llm_factory.py +102 -140
  90. autobyteus/llm/lmstudio_provider.py +63 -48
  91. autobyteus/llm/models.py +83 -53
  92. autobyteus/llm/ollama_provider.py +69 -61
  93. autobyteus/llm/ollama_provider_resolver.py +1 -0
  94. autobyteus/llm/providers.py +13 -13
  95. autobyteus/llm/runtimes.py +11 -0
  96. autobyteus/task_management/__init__.py +43 -0
  97. autobyteus/task_management/base_task_board.py +68 -0
  98. autobyteus/task_management/converters/__init__.py +11 -0
  99. autobyteus/task_management/converters/task_board_converter.py +64 -0
  100. autobyteus/task_management/converters/task_plan_converter.py +48 -0
  101. autobyteus/task_management/deliverable.py +16 -0
  102. autobyteus/task_management/deliverables/__init__.py +8 -0
  103. autobyteus/task_management/deliverables/file_deliverable.py +15 -0
  104. autobyteus/task_management/events.py +27 -0
  105. autobyteus/task_management/in_memory_task_board.py +126 -0
  106. autobyteus/task_management/schemas/__init__.py +15 -0
  107. autobyteus/task_management/schemas/deliverable_schema.py +13 -0
  108. autobyteus/task_management/schemas/plan_definition.py +35 -0
  109. autobyteus/task_management/schemas/task_status_report.py +27 -0
  110. autobyteus/task_management/task_plan.py +110 -0
  111. autobyteus/task_management/tools/__init__.py +14 -0
  112. autobyteus/task_management/tools/get_task_board_status.py +68 -0
  113. autobyteus/task_management/tools/publish_task_plan.py +113 -0
  114. autobyteus/task_management/tools/update_task_status.py +135 -0
  115. autobyteus/tools/bash/bash_executor.py +59 -14
  116. autobyteus/tools/mcp/config_service.py +63 -58
  117. autobyteus/tools/mcp/server/http_managed_mcp_server.py +14 -2
  118. autobyteus/tools/mcp/server/stdio_managed_mcp_server.py +14 -2
  119. autobyteus/tools/mcp/server_instance_manager.py +30 -4
  120. autobyteus/tools/mcp/tool_registrar.py +103 -50
  121. autobyteus/tools/parameter_schema.py +17 -11
  122. autobyteus/tools/registry/tool_definition.py +24 -29
  123. autobyteus/tools/tool_category.py +1 -0
  124. autobyteus/tools/usage/formatters/default_json_example_formatter.py +78 -3
  125. autobyteus/tools/usage/formatters/default_xml_example_formatter.py +23 -3
  126. autobyteus/tools/usage/formatters/gemini_json_example_formatter.py +6 -0
  127. autobyteus/tools/usage/formatters/google_json_example_formatter.py +7 -0
  128. autobyteus/tools/usage/formatters/openai_json_example_formatter.py +6 -4
  129. autobyteus/tools/usage/parsers/gemini_json_tool_usage_parser.py +23 -7
  130. autobyteus/tools/usage/parsers/provider_aware_tool_usage_parser.py +14 -25
  131. autobyteus/tools/usage/providers/__init__.py +2 -12
  132. autobyteus/tools/usage/providers/tool_manifest_provider.py +36 -29
  133. autobyteus/tools/usage/registries/__init__.py +7 -12
  134. autobyteus/tools/usage/registries/tool_formatter_pair.py +15 -0
  135. autobyteus/tools/usage/registries/tool_formatting_registry.py +58 -0
  136. autobyteus/tools/usage/registries/tool_usage_parser_registry.py +55 -0
  137. {autobyteus-1.1.4.dist-info → autobyteus-1.1.5.dist-info}/METADATA +3 -3
  138. {autobyteus-1.1.4.dist-info → autobyteus-1.1.5.dist-info}/RECORD +146 -72
  139. examples/agent_team/__init__.py +1 -0
  140. examples/run_browser_agent.py +17 -15
  141. examples/run_google_slides_agent.py +17 -16
  142. examples/run_poem_writer.py +22 -12
  143. examples/run_sqlite_agent.py +17 -15
  144. autobyteus/tools/mcp/call_handlers/__init__.py +0 -16
  145. autobyteus/tools/mcp/call_handlers/base_handler.py +0 -40
  146. autobyteus/tools/mcp/call_handlers/stdio_handler.py +0 -76
  147. autobyteus/tools/mcp/call_handlers/streamable_http_handler.py +0 -55
  148. autobyteus/tools/usage/providers/json_example_provider.py +0 -32
  149. autobyteus/tools/usage/providers/json_schema_provider.py +0 -35
  150. autobyteus/tools/usage/providers/json_tool_usage_parser_provider.py +0 -28
  151. autobyteus/tools/usage/providers/xml_example_provider.py +0 -28
  152. autobyteus/tools/usage/providers/xml_schema_provider.py +0 -29
  153. autobyteus/tools/usage/providers/xml_tool_usage_parser_provider.py +0 -26
  154. autobyteus/tools/usage/registries/json_example_formatter_registry.py +0 -51
  155. autobyteus/tools/usage/registries/json_schema_formatter_registry.py +0 -51
  156. autobyteus/tools/usage/registries/json_tool_usage_parser_registry.py +0 -42
  157. autobyteus/tools/usage/registries/xml_example_formatter_registry.py +0 -30
  158. autobyteus/tools/usage/registries/xml_schema_formatter_registry.py +0 -33
  159. autobyteus/tools/usage/registries/xml_tool_usage_parser_registry.py +0 -30
  160. examples/workflow/__init__.py +0 -1
  161. examples/workflow/run_basic_research_workflow.py +0 -189
  162. examples/workflow/run_code_review_workflow.py +0 -269
  163. examples/workflow/run_debate_workflow.py +0 -212
  164. examples/workflow/run_workflow_with_tui.py +0 -153
  165. {autobyteus-1.1.4.dist-info → autobyteus-1.1.5.dist-info}/WHEEL +0 -0
  166. {autobyteus-1.1.4.dist-info → autobyteus-1.1.5.dist-info}/licenses/LICENSE +0 -0
  167. {autobyteus-1.1.4.dist-info → autobyteus-1.1.5.dist-info}/top_level.txt +0 -0
@@ -79,67 +79,120 @@ class McpToolRegistrar(metaclass=SingletonMeta):
79
79
  tool_class=None
80
80
  )
81
81
 
82
- async def discover_and_register_tools(self, mcp_config: Optional[Union[BaseMcpConfig, Dict[str, Any]]] = None) -> List[ToolDefinition]:
82
+ async def _discover_and_register_from_config(self, server_config: BaseMcpConfig, schema_mapper: McpSchemaMapper) -> List[ToolDefinition]:
83
83
  """
84
- Discovers tools from MCP servers and registers them.
84
+ Performs the core discovery and registration logic for a single server configuration.
85
+ This method does NOT handle un-registration of existing tools.
85
86
  """
86
- configs_to_process: List[BaseMcpConfig]
87
+ registered_tool_definitions: List[ToolDefinition] = []
88
+ if not server_config.enabled:
89
+ logger.info(f"MCP server '{server_config.server_id}' is disabled. Skipping.")
90
+ return registered_tool_definitions
91
+
92
+ logger.info(f"Discovering tools from MCP server: '{server_config.server_id}'")
87
93
 
88
- if mcp_config:
89
- validated_config: BaseMcpConfig
90
- if isinstance(mcp_config, dict):
94
+ try:
95
+ remote_tools = await self._fetch_tools_from_server(server_config)
96
+ logger.info(f"Discovered {len(remote_tools)} tools from server '{server_config.server_id}'.")
97
+
98
+ for remote_tool in remote_tools:
91
99
  try:
92
- validated_config = self._config_service.load_config(mcp_config)
93
- except ValueError as e:
94
- logger.error(f"Failed to parse provided MCP config dictionary: {e}")
95
- raise
96
- elif isinstance(mcp_config, BaseMcpConfig):
97
- validated_config = self._config_service.add_config(mcp_config)
98
- else:
99
- raise TypeError(f"mcp_config must be a BaseMcpConfig object or a dictionary, not {type(mcp_config)}.")
100
-
101
- logger.info(f"Starting targeted MCP tool discovery for server: {validated_config.server_id}")
102
- self.unregister_tools_from_server(validated_config.server_id)
103
- configs_to_process = [validated_config]
104
- else:
105
- logger.info("Starting full MCP tool discovery. Unregistering all existing MCP tools first.")
106
- all_server_ids = list(self._registered_tools_by_server.keys())
107
- for server_id in all_server_ids:
108
- self.unregister_tools_from_server(server_id)
109
- self._registered_tools_by_server.clear()
110
- configs_to_process = self._config_service.get_all_configs()
100
+ tool_def = self._create_tool_definition_from_remote(remote_tool, server_config, schema_mapper)
101
+ self._tool_registry.register_tool(tool_def)
102
+ self._registered_tools_by_server.setdefault(server_config.server_id, []).append(tool_def)
103
+ registered_tool_definitions.append(tool_def)
104
+ except Exception as e_tool:
105
+ logger.error(f"Failed to process or register remote tool '{remote_tool.name}': {e_tool}", exc_info=True)
106
+
107
+ except Exception as e_server:
108
+ logger.error(f"Failed to discover tools from MCP server '{server_config.server_id}': {e_server}", exc_info=True)
109
+ # Re-raise to signal failure to the caller
110
+ raise
111
+
112
+ return registered_tool_definitions
113
+
114
+ async def register_server(self, config_object: BaseMcpConfig) -> List[ToolDefinition]:
115
+ """
116
+ Discovers and registers tools from a single MCP server using a validated
117
+ config object. This will overwrite any existing tools from that server.
118
+
119
+ Args:
120
+ config_object: A pre-instantiated and validated BaseMcpConfig object.
121
+
122
+ Returns:
123
+ A list of the successfully registered ToolDefinition objects from this server.
124
+ """
125
+ if not isinstance(config_object, BaseMcpConfig):
126
+ raise TypeError(f"config_object must be a BaseMcpConfig object, not {type(config_object)}.")
127
+
128
+ # Add/update the config in the service
129
+ self._config_service.add_config(config_object)
130
+
131
+ logger.info(f"Starting targeted MCP tool registration for server: {config_object.server_id}")
132
+
133
+ # Unregister existing tools for this specific server before re-registering
134
+ self.unregister_tools_from_server(config_object.server_id)
135
+
136
+ schema_mapper = McpSchemaMapper()
137
+
138
+ return await self._discover_and_register_from_config(config_object, schema_mapper)
139
+
140
+ async def load_and_register_server(self, config_dict: Dict[str, Any]) -> List[ToolDefinition]:
141
+ """
142
+ Loads a server configuration from a dictionary, then discovers and registers its tools.
143
+ This is a convenience method that wraps the parsing and registration process.
144
+
145
+ Args:
146
+ config_dict: The raw dictionary configuration for a single MCP server.
147
+
148
+ Returns:
149
+ A list of the successfully registered ToolDefinition objects from this server.
150
+ """
151
+ logger.debug(f"Attempting to load and register server from dictionary: {config_dict.get(next(iter(config_dict), 'unknown'))}")
152
+ try:
153
+ validated_config = self._config_service.load_config_from_dict(config_dict)
154
+ except ValueError as e:
155
+ logger.error(f"Failed to parse provided MCP config dictionary: {e}")
156
+ raise
157
+
158
+ return await self.register_server(validated_config)
159
+
160
+ async def reload_all_mcp_tools(self) -> List[ToolDefinition]:
161
+ """
162
+ Performs a full refresh of tools from ALL MCP servers currently configured
163
+ in the McpConfigService. This first unregisters all previously registered
164
+ MCP tools, then re-discovers and re-registers them. This process is resilient
165
+ to failures from individual servers.
111
166
 
167
+ Returns:
168
+ A list of all successfully registered ToolDefinition objects.
169
+ """
170
+ logger.info("Reloading all MCP tools. Unregistering existing MCP tools first.")
171
+
172
+ # Unregister all previously known MCP tools
173
+ all_server_ids = list(self._registered_tools_by_server.keys())
174
+ for server_id in all_server_ids:
175
+ self.unregister_tools_from_server(server_id)
176
+
177
+ configs_to_process = self._config_service.get_all_configs()
112
178
  if not configs_to_process:
113
- logger.info("No MCP server configurations to process. Skipping discovery.")
179
+ logger.info("No MCP server configurations to process. Skipping reload.")
114
180
  return []
115
181
 
116
182
  schema_mapper = McpSchemaMapper()
117
- registered_tool_definitions: List[ToolDefinition] = []
183
+ all_registered_definitions: List[ToolDefinition] = []
184
+
118
185
  for server_config in configs_to_process:
119
- if not server_config.enabled:
120
- logger.info(f"MCP server '{server_config.server_id}' is disabled. Skipping.")
121
- continue
122
-
123
- logger.info(f"Discovering tools from MCP server: '{server_config.server_id}'")
124
-
125
186
  try:
126
- remote_tools = await self._fetch_tools_from_server(server_config)
127
- logger.info(f"Discovered {len(remote_tools)} tools from server '{server_config.server_id}'.")
128
-
129
- for remote_tool in remote_tools:
130
- try:
131
- tool_def = self._create_tool_definition_from_remote(remote_tool, server_config, schema_mapper)
132
- self._tool_registry.register_tool(tool_def)
133
- self._registered_tools_by_server.setdefault(server_config.server_id, []).append(tool_def)
134
- registered_tool_definitions.append(tool_def)
135
- except Exception as e_tool:
136
- logger.error(f"Failed to process or register remote tool '{remote_tool.name}': {e_tool}", exc_info=True)
137
-
138
- except Exception as e_server:
139
- logger.error(f"Failed to discover tools from MCP server '{server_config.server_id}': {e_server}", exc_info=True)
140
-
141
- logger.info(f"MCP tool discovery and registration process completed. Total tools registered: {len(registered_tool_definitions)}.")
142
- return registered_tool_definitions
187
+ newly_registered = await self._discover_and_register_from_config(server_config, schema_mapper)
188
+ all_registered_definitions.extend(newly_registered)
189
+ except Exception as e:
190
+ # Log the error but continue to the next server. This makes the process resilient.
191
+ # exc_info is False because the inner method already logged the full stack trace.
192
+ logger.error(f"Failed to complete discovery for server '{server_config.server_id}', it will be skipped. Error: {e}", exc_info=False)
193
+
194
+ logger.info(f"Finished reloading all MCP tools. Total tools registered: {len(all_registered_definitions)}.")
195
+ return all_registered_definitions
143
196
 
144
197
  async def list_remote_tools(self, mcp_config: Union[BaseMcpConfig, Dict[str, Any]]) -> List[ToolDefinition]:
145
198
  validated_config: BaseMcpConfig
@@ -14,8 +14,6 @@ class ParameterType(str, Enum):
14
14
  FLOAT = "float"
15
15
  BOOLEAN = "boolean"
16
16
  ENUM = "enum"
17
- # FILE_PATH = "file_path" # REMOVED
18
- # DIRECTORY_PATH = "directory_path" # REMOVED
19
17
  OBJECT = "object"
20
18
  ARRAY = "array"
21
19
 
@@ -23,11 +21,8 @@ class ParameterType(str, Enum):
23
21
  """Maps parameter type to JSON schema type."""
24
22
  if self == ParameterType.FLOAT:
25
23
  return "number"
26
- # REMOVED: FILE_PATH and DIRECTORY_PATH handling, ENUM still maps to string
27
24
  if self == ParameterType.ENUM:
28
25
  return "string"
29
- # For OBJECT and ARRAY, their value is already the correct JSON schema type
30
- # STRING, INTEGER, BOOLEAN also map directly to their values.
31
26
  if self in [ParameterType.OBJECT, ParameterType.ARRAY, ParameterType.STRING, ParameterType.INTEGER, ParameterType.BOOLEAN]:
32
27
  return self.value
33
28
  return self.value # Fallback, should be covered by above
@@ -46,8 +41,9 @@ class ParameterDefinition:
46
41
  min_value: Optional[Union[int, float]] = None
47
42
  max_value: Optional[Union[int, float]] = None
48
43
  pattern: Optional[str] = None
49
- array_item_schema: Optional[Dict[str, Any]] = None
50
-
44
+ array_item_schema: Optional[Dict[str, Any]] = None
45
+ object_schema: Optional[Dict[str, Any]] = None
46
+
51
47
  def __post_init__(self):
52
48
  if not self.name or not isinstance(self.name, str):
53
49
  raise ValueError("ParameterDefinition name must be a non-empty string")
@@ -64,6 +60,9 @@ class ParameterDefinition:
64
60
  if self.param_type != ParameterType.ARRAY and self.array_item_schema is not None:
65
61
  raise ValueError(f"ParameterDefinition '{self.name}': array_item_schema should only be provided if param_type is ARRAY.")
66
62
 
63
+ if self.param_type != ParameterType.OBJECT and self.object_schema is not None:
64
+ raise ValueError(f"ParameterDefinition '{self.name}': object_schema should only be provided if param_type is OBJECT.")
65
+
67
66
  if self.required and self.default_value is not None:
68
67
  logger.debug(f"ParameterDefinition '{self.name}' is marked as required but has a default value. This is acceptable.")
69
68
 
@@ -103,8 +102,6 @@ class ParameterDefinition:
103
102
  if not isinstance(value, str) or value not in (self.enum_values or []):
104
103
  return False
105
104
 
106
- # REMOVED: Specific validation for FILE_PATH, DIRECTORY_PATH (they are now STRING)
107
-
108
105
  elif self.param_type == ParameterType.OBJECT:
109
106
  if not isinstance(value, dict):
110
107
  return False
@@ -129,9 +126,18 @@ class ParameterDefinition:
129
126
  }
130
127
  if self.param_type == ParameterType.ARRAY and self.array_item_schema is not None:
131
128
  data["array_item_schema"] = self.array_item_schema
129
+ if self.param_type == ParameterType.OBJECT and self.object_schema is not None:
130
+ data["object_schema"] = self.object_schema
132
131
  return data
133
132
 
134
133
  def to_json_schema_property_dict(self) -> Dict[str, Any]:
134
+ if self.param_type == ParameterType.OBJECT and self.object_schema:
135
+ # If a detailed object schema is provided, use it directly.
136
+ # We add the description at the top level for clarity.
137
+ schema = self.object_schema.copy()
138
+ schema["description"] = self.description
139
+ return schema
140
+
135
141
  prop_dict: Dict[str, Any] = {
136
142
  "type": self.param_type.to_json_schema_type(),
137
143
  "description": self.description,
@@ -150,7 +156,6 @@ class ParameterDefinition:
150
156
  if self.param_type in [ParameterType.INTEGER, ParameterType.FLOAT]:
151
157
  prop_dict["maximum"] = self.max_value
152
158
 
153
- # REMOVED: Pattern check specific to FILE_PATH/DIRECTORY_PATH as they are now STRING
154
159
  if self.pattern and self.param_type == ParameterType.STRING:
155
160
  prop_dict["pattern"] = self.pattern
156
161
 
@@ -255,7 +260,8 @@ class ParameterSchema:
255
260
  min_value=param_data.get("min_value"),
256
261
  max_value=param_data.get("max_value"),
257
262
  pattern=param_data.get("pattern"),
258
- array_item_schema=param_data.get("array_item_schema")
263
+ array_item_schema=param_data.get("array_item_schema"),
264
+ object_schema=param_data.get("object_schema")
259
265
  )
260
266
  schema.add_parameter(param)
261
267
 
@@ -7,11 +7,12 @@ from autobyteus.llm.providers import LLMProvider
7
7
  from autobyteus.tools.tool_config import ToolConfig
8
8
  from autobyteus.tools.parameter_schema import ParameterSchema
9
9
  from autobyteus.tools.tool_origin import ToolOrigin
10
- from autobyteus.tools.usage.providers import (
11
- XmlSchemaProvider,
12
- JsonSchemaProvider,
13
- XmlExampleProvider,
14
- JsonExampleProvider
10
+ # Import default formatters directly to provide convenience methods
11
+ from autobyteus.tools.usage.formatters import (
12
+ DefaultXmlSchemaFormatter,
13
+ DefaultJsonSchemaFormatter,
14
+ DefaultXmlExampleFormatter,
15
+ DefaultJsonExampleFormatter
15
16
  )
16
17
 
17
18
  if TYPE_CHECKING:
@@ -22,7 +23,7 @@ logger = logging.getLogger(__name__)
22
23
  class ToolDefinition:
23
24
  """
24
25
  Represents the definition of a tool, containing its metadata and the means
25
- to create an instance. It can generate provider-specific usage information on demand.
26
+ to create an instance. It can generate provider-agnostic usage information on demand.
26
27
  """
27
28
  def __init__(self,
28
29
  name: str,
@@ -95,44 +96,38 @@ class ToolDefinition:
95
96
  @property
96
97
  def metadata(self) -> Dict[str, Any]: return self._metadata
97
98
 
98
- # --- Schema Generation API ---
99
+ # --- Convenience Schema/Example Generation API (using default formatters) ---
99
100
  def get_usage_xml(self, provider: Optional[LLMProvider] = None) -> str:
100
101
  """
101
- Generates the standardized XML usage schema string for this tool.
102
- The provider argument is included for API consistency and future-proofing.
102
+ Generates the default XML usage schema string for this tool.
103
+ The provider argument is ignored, kept for API consistency.
103
104
  """
104
- provider_instance = XmlSchemaProvider()
105
- return provider_instance.provide(self, llm_provider=provider)
105
+ formatter = DefaultXmlSchemaFormatter()
106
+ return formatter.provide(self)
106
107
 
107
108
  def get_usage_json(self, provider: Optional[LLMProvider] = None) -> Dict[str, Any]:
108
109
  """
109
- Generates the usage schema as a dictionary.
110
-
111
- Args:
112
- provider: If provided, generates a provider-specific JSON format.
113
- If None, generates a default, generic JSON format.
114
-
115
- Returns:
116
- A dictionary representing the tool's usage schema.
110
+ Generates the default JSON usage schema as a dictionary.
111
+ The provider argument is ignored, kept for API consistency.
117
112
  """
118
- provider_instance = JsonSchemaProvider()
119
- return provider_instance.provide(self, llm_provider=provider)
113
+ formatter = DefaultJsonSchemaFormatter()
114
+ return formatter.provide(self)
120
115
 
121
- # --- Example Generation API ---
122
116
  def get_usage_xml_example(self, provider: Optional[LLMProvider] = None) -> str:
123
117
  """
124
- Generates a standardized XML usage example string for this tool.
125
- The provider argument is included for API consistency and future-proofing.
118
+ Generates a default XML usage example string for this tool.
119
+ The provider argument is ignored, kept for API consistency.
126
120
  """
127
- provider_instance = XmlExampleProvider()
128
- return provider_instance.provide(self, llm_provider=provider)
121
+ formatter = DefaultXmlExampleFormatter()
122
+ return formatter.provide(self)
129
123
 
130
124
  def get_usage_json_example(self, provider: Optional[LLMProvider] = None) -> Any:
131
125
  """
132
- Generates a usage example, either as a dict or a string.
126
+ Generates a default JSON usage example as a dictionary.
127
+ The provider argument is ignored, kept for API consistency.
133
128
  """
134
- provider_instance = JsonExampleProvider()
135
- return provider_instance.provide(self, llm_provider=provider)
129
+ formatter = DefaultJsonExampleFormatter()
130
+ return formatter.provide(self)
136
131
 
137
132
  # --- Other methods ---
138
133
  @property
@@ -14,6 +14,7 @@ class ToolCategory(str, Enum):
14
14
  UTILITY = "Utility"
15
15
  AGENT_COMMUNICATION = "Agent Communication"
16
16
  PROMPT_MANAGEMENT = "Prompt Management"
17
+ TASK_MANAGEMENT = "Task Management" # NEW CATEGORY ADDED
17
18
  GENERAL = "General"
18
19
  MCP = "MCP"
19
20
 
@@ -1,5 +1,5 @@
1
1
  # file: autobyteus/autobyteus/tools/usage/formatters/default_json_example_formatter.py
2
- from typing import Dict, Any, TYPE_CHECKING
2
+ from typing import Dict, Any, TYPE_CHECKING, List, Optional
3
3
 
4
4
  from autobyteus.tools.parameter_schema import ParameterType, ParameterDefinition
5
5
  from .base_formatter import BaseExampleFormatter
@@ -9,8 +9,8 @@ if TYPE_CHECKING:
9
9
 
10
10
  class DefaultJsonExampleFormatter(BaseExampleFormatter):
11
11
  """
12
- Formats a tool usage example into a generic JSON format, inspired by
13
- the default XML format.
12
+ Formats a tool usage example into a generic JSON format.
13
+ It intelligently generates detailed examples for complex object schemas.
14
14
  """
15
15
 
16
16
  def provide(self, tool_definition: 'ToolDefinition') -> Dict:
@@ -20,6 +20,8 @@ class DefaultJsonExampleFormatter(BaseExampleFormatter):
20
20
 
21
21
  if arg_schema and arg_schema.parameters:
22
22
  for param_def in arg_schema.parameters:
23
+ # Always include required parameters in the example.
24
+ # Also include optional parameters that have a default value to show common usage.
23
25
  if param_def.required or param_def.default_value is not None:
24
26
  arguments[param_def.name] = self._generate_placeholder_value(param_def)
25
27
 
@@ -31,6 +33,12 @@ class DefaultJsonExampleFormatter(BaseExampleFormatter):
31
33
  }
32
34
 
33
35
  def _generate_placeholder_value(self, param_def: ParameterDefinition) -> Any:
36
+ # If an object parameter has a detailed schema, generate a structured example from it.
37
+ if param_def.param_type == ParameterType.OBJECT and param_def.object_schema:
38
+ # We pass the full schema document to allow for resolving $refs
39
+ return DefaultJsonExampleFormatter._generate_example_from_schema(param_def.object_schema, param_def.object_schema)
40
+
41
+ # Fallback to simple placeholder generation for primitives or objects without schemas.
34
42
  if param_def.default_value is not None: return param_def.default_value
35
43
  if param_def.param_type == ParameterType.STRING: return f"example_{param_def.name}"
36
44
  if param_def.param_type == ParameterType.INTEGER: return 123
@@ -40,3 +48,70 @@ class DefaultJsonExampleFormatter(BaseExampleFormatter):
40
48
  if param_def.param_type == ParameterType.OBJECT: return {"key": "value"}
41
49
  if param_def.param_type == ParameterType.ARRAY: return ["item1", "item2"]
42
50
  return "placeholder"
51
+
52
+ @staticmethod
53
+ def _generate_example_from_schema(sub_schema: Dict[str, Any], full_schema: Dict[str, Any]) -> Any:
54
+ """
55
+ Recursively generates an example value from a JSON schema dictionary.
56
+ This is a static method so it can be reused by other formatters.
57
+ """
58
+ if "$ref" in sub_schema:
59
+ ref_path = sub_schema["$ref"]
60
+ try:
61
+ # Resolve the reference, e.g., "#/$defs/MySchema"
62
+ parts = ref_path.lstrip("#/").split("/")
63
+ resolved_schema = full_schema
64
+ for part in parts:
65
+ resolved_schema = resolved_schema[part]
66
+ return DefaultJsonExampleFormatter._generate_example_from_schema(resolved_schema, full_schema)
67
+ except (KeyError, IndexError):
68
+ return {"error": f"Could not resolve schema reference: {ref_path}"}
69
+
70
+ schema_type = sub_schema.get("type")
71
+
72
+ if "default" in sub_schema:
73
+ return sub_schema["default"]
74
+
75
+ if "enum" in sub_schema and sub_schema["enum"]:
76
+ return sub_schema["enum"][0]
77
+
78
+ if schema_type == "object":
79
+ example_obj = {}
80
+ properties = sub_schema.get("properties", {})
81
+ required_fields = sub_schema.get("required", [])
82
+ for prop_name, prop_schema in properties.items():
83
+ # Include required fields and a subset of optional fields for a concise example.
84
+ if prop_name in required_fields:
85
+ example_obj[prop_name] = DefaultJsonExampleFormatter._generate_example_from_schema(prop_schema, full_schema)
86
+ return example_obj
87
+
88
+ elif schema_type == "array":
89
+ items_schema = sub_schema.get("items")
90
+ if isinstance(items_schema, dict):
91
+ # Generate one example item for the array to keep it concise
92
+ return [DefaultJsonExampleFormatter._generate_example_from_schema(items_schema, full_schema)]
93
+ else:
94
+ return ["example_item_1"]
95
+
96
+ elif schema_type == "string":
97
+ description = sub_schema.get("description", "")
98
+ if "e.g." in description.lower():
99
+ try:
100
+ return description.split("e.g.,")[1].split(')')[0].strip().strip("'\"")
101
+ except IndexError:
102
+ pass
103
+ return "example_string"
104
+
105
+ elif schema_type == "integer":
106
+ return 1
107
+
108
+ elif schema_type == "number":
109
+ return 1.1
110
+
111
+ elif schema_type == "boolean":
112
+ return True
113
+
114
+ elif schema_type == "null":
115
+ return None
116
+
117
+ return "unknown_type"
@@ -1,9 +1,11 @@
1
1
  # file: autobyteus/autobyteus/tools/usage/formatters/default_xml_example_formatter.py
2
2
  import xml.sax.saxutils
3
+ import json # Import json for embedding complex objects
3
4
  from typing import Any, TYPE_CHECKING
4
5
 
5
6
  from autobyteus.tools.parameter_schema import ParameterType, ParameterDefinition
6
7
  from .base_formatter import BaseExampleFormatter
8
+ from .default_json_example_formatter import DefaultJsonExampleFormatter # Import for reuse
7
9
 
8
10
  if TYPE_CHECKING:
9
11
  from autobyteus.tools.registry import ToolDefinition
@@ -22,8 +24,21 @@ class DefaultXmlExampleFormatter(BaseExampleFormatter):
22
24
  for param_def in arg_schema.parameters:
23
25
  if param_def.required or param_def.default_value is not None:
24
26
  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
+ # For complex objects/arrays, we now get a dict/list.
28
+ # Convert it to a JSON string for embedding in XML.
29
+ if isinstance(placeholder_value, (dict, list)):
30
+ value_str = json.dumps(placeholder_value, indent=2)
31
+ else:
32
+ value_str = str(placeholder_value)
33
+
34
+ escaped_value = xml.sax.saxutils.escape(value_str)
35
+
36
+ # Add newlines for readability if the value is a multiline JSON string
37
+ if '\n' in escaped_value:
38
+ arguments_part.append(f' <arg name="{param_def.name}">\n{escaped_value}\n </arg>')
39
+ else:
40
+ arguments_part.append(f' <arg name="{param_def.name}">{escaped_value}</arg>')
41
+
27
42
 
28
43
  if arguments_part:
29
44
  example_xml_parts.append(" <arguments>")
@@ -36,6 +51,11 @@ class DefaultXmlExampleFormatter(BaseExampleFormatter):
36
51
  return "\n".join(example_xml_parts)
37
52
 
38
53
  def _generate_placeholder_value(self, param_def: ParameterDefinition) -> Any:
54
+ # REUSE a more intelligent generator for complex objects
55
+ if param_def.param_type == ParameterType.OBJECT and param_def.object_schema:
56
+ return DefaultJsonExampleFormatter._generate_example_from_schema(param_def.object_schema, param_def.object_schema)
57
+
58
+ # Fallback for primitives
39
59
  if param_def.default_value is not None:
40
60
  return param_def.default_value
41
61
  if param_def.param_type == ParameterType.STRING:
@@ -49,7 +69,7 @@ class DefaultXmlExampleFormatter(BaseExampleFormatter):
49
69
  if param_def.param_type == ParameterType.ENUM:
50
70
  return param_def.enum_values[0] if param_def.enum_values else "enum_val"
51
71
  if param_def.param_type == ParameterType.OBJECT:
52
- return {"key": "value"}
72
+ return {"key": "value"} # Fallback if no schema
53
73
  if param_def.param_type == ParameterType.ARRAY:
54
74
  return ["item1", "item2"]
55
75
  return "placeholder"
@@ -3,6 +3,7 @@ from typing import Dict, Any, TYPE_CHECKING
3
3
 
4
4
  from autobyteus.tools.parameter_schema import ParameterType, ParameterDefinition
5
5
  from .base_formatter import BaseExampleFormatter
6
+ from .default_json_example_formatter import DefaultJsonExampleFormatter # Import for reuse
6
7
 
7
8
  if TYPE_CHECKING:
8
9
  from autobyteus.tools.registry import ToolDefinition
@@ -23,6 +24,11 @@ class GeminiJsonExampleFormatter(BaseExampleFormatter):
23
24
  return {"name": tool_name, "args": arguments}
24
25
 
25
26
  def _generate_placeholder_value(self, param_def: ParameterDefinition) -> Any:
27
+ # REUSE a more intelligent generator for complex objects
28
+ if param_def.param_type == ParameterType.OBJECT and param_def.object_schema:
29
+ return DefaultJsonExampleFormatter._generate_example_from_schema(param_def.object_schema, param_def.object_schema)
30
+
31
+ # Fallback for primitives
26
32
  if param_def.default_value is not None: return param_def.default_value
27
33
  if param_def.param_type == ParameterType.STRING: return f"example_{param_def.name}"
28
34
  if param_def.param_type == ParameterType.INTEGER: return 123
@@ -3,6 +3,8 @@ from typing import Dict, Any, TYPE_CHECKING
3
3
 
4
4
  from autobyteus.tools.parameter_schema import ParameterType, ParameterDefinition
5
5
  from .base_formatter import BaseExampleFormatter
6
+ # Import for reuse of the intelligent example generation logic
7
+ from .default_json_example_formatter import DefaultJsonExampleFormatter
6
8
 
7
9
  if TYPE_CHECKING:
8
10
  from autobyteus.tools.registry import ToolDefinition
@@ -23,6 +25,11 @@ class GoogleJsonExampleFormatter(BaseExampleFormatter):
23
25
  return {"name": tool_name, "args": arguments}
24
26
 
25
27
  def _generate_placeholder_value(self, param_def: ParameterDefinition) -> Any:
28
+ # REUSE the intelligent generator for complex objects
29
+ if param_def.param_type == ParameterType.OBJECT and param_def.object_schema:
30
+ return DefaultJsonExampleFormatter._generate_example_from_schema(param_def.object_schema, param_def.object_schema)
31
+
32
+ # Fallback for primitives
26
33
  if param_def.default_value is not None: return param_def.default_value
27
34
  if param_def.param_type == ParameterType.STRING: return f"example_{param_def.name}"
28
35
  if param_def.param_type == ParameterType.INTEGER: return 123
@@ -4,6 +4,7 @@ from typing import Dict, Any, TYPE_CHECKING
4
4
 
5
5
  from autobyteus.tools.parameter_schema import ParameterType, ParameterDefinition
6
6
  from .base_formatter import BaseExampleFormatter
7
+ from .default_json_example_formatter import DefaultJsonExampleFormatter # Import for reuse
7
8
 
8
9
  if TYPE_CHECKING:
9
10
  from autobyteus.tools.registry import ToolDefinition
@@ -12,7 +13,6 @@ class OpenAiJsonExampleFormatter(BaseExampleFormatter):
12
13
  """
13
14
  Formats a tool usage example into a format resembling an entry in the
14
15
  OpenAI JSON 'tool_calls' array, intended for prompting a model.
15
- The output is wrapped in a 'tool' key for consistency in prompts.
16
16
  """
17
17
 
18
18
  def provide(self, tool_definition: 'ToolDefinition') -> Dict:
@@ -25,8 +25,6 @@ class OpenAiJsonExampleFormatter(BaseExampleFormatter):
25
25
  if param_def.required or param_def.default_value is not None:
26
26
  arguments[param_def.name] = self._generate_placeholder_value(param_def)
27
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
28
  function_call = {
31
29
  "function": {
32
30
  "name": tool_name,
@@ -34,10 +32,14 @@ class OpenAiJsonExampleFormatter(BaseExampleFormatter):
34
32
  },
35
33
  }
36
34
 
37
- # Wrap in a 'tool' key for consistency in prompt generation.
38
35
  return {"tool": function_call}
39
36
 
40
37
  def _generate_placeholder_value(self, param_def: ParameterDefinition) -> Any:
38
+ # REUSE a more intelligent generator for complex objects
39
+ if param_def.param_type == ParameterType.OBJECT and param_def.object_schema:
40
+ return DefaultJsonExampleFormatter._generate_example_from_schema(param_def.object_schema, param_def.object_schema)
41
+
42
+ # Fallback for primitives
41
43
  if param_def.default_value is not None: return param_def.default_value
42
44
  if param_def.param_type == ParameterType.STRING: return f"example_{param_def.name}"
43
45
  if param_def.param_type == ParameterType.INTEGER: return 123