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.
- autobyteus/agent/context/__init__.py +4 -2
- autobyteus/agent/context/agent_config.py +0 -4
- autobyteus/agent/context/agent_context_registry.py +73 -0
- autobyteus/agent/events/notifiers.py +4 -0
- autobyteus/agent/handlers/inter_agent_message_event_handler.py +7 -2
- autobyteus/agent/handlers/llm_complete_response_received_event_handler.py +19 -19
- autobyteus/agent/handlers/user_input_message_event_handler.py +15 -0
- autobyteus/agent/message/send_message_to.py +29 -23
- autobyteus/agent/runtime/agent_runtime.py +10 -2
- autobyteus/agent/sender_type.py +15 -0
- autobyteus/agent/streaming/agent_event_stream.py +6 -0
- autobyteus/agent/streaming/stream_event_payloads.py +12 -0
- autobyteus/agent/streaming/stream_events.py +3 -0
- autobyteus/agent/system_prompt_processor/tool_manifest_injector_processor.py +7 -4
- autobyteus/agent_team/__init__.py +1 -0
- autobyteus/agent_team/agent_team.py +93 -0
- autobyteus/agent_team/agent_team_builder.py +184 -0
- autobyteus/agent_team/base_agent_team.py +86 -0
- autobyteus/agent_team/bootstrap_steps/__init__.py +24 -0
- autobyteus/agent_team/bootstrap_steps/agent_configuration_preparation_step.py +73 -0
- autobyteus/agent_team/bootstrap_steps/agent_team_bootstrapper.py +54 -0
- autobyteus/agent_team/bootstrap_steps/agent_team_runtime_queue_initialization_step.py +25 -0
- autobyteus/agent_team/bootstrap_steps/base_agent_team_bootstrap_step.py +23 -0
- autobyteus/agent_team/bootstrap_steps/coordinator_initialization_step.py +41 -0
- autobyteus/agent_team/bootstrap_steps/coordinator_prompt_preparation_step.py +85 -0
- autobyteus/agent_team/bootstrap_steps/task_notifier_initialization_step.py +51 -0
- autobyteus/agent_team/bootstrap_steps/team_context_initialization_step.py +45 -0
- autobyteus/agent_team/context/__init__.py +17 -0
- autobyteus/agent_team/context/agent_team_config.py +33 -0
- autobyteus/agent_team/context/agent_team_context.py +61 -0
- autobyteus/agent_team/context/agent_team_runtime_state.py +56 -0
- autobyteus/agent_team/context/team_manager.py +147 -0
- autobyteus/agent_team/context/team_node_config.py +76 -0
- autobyteus/agent_team/events/__init__.py +29 -0
- autobyteus/agent_team/events/agent_team_event_dispatcher.py +39 -0
- autobyteus/agent_team/events/agent_team_events.py +53 -0
- autobyteus/agent_team/events/agent_team_input_event_queue_manager.py +21 -0
- autobyteus/agent_team/exceptions.py +8 -0
- autobyteus/agent_team/factory/__init__.py +9 -0
- autobyteus/agent_team/factory/agent_team_factory.py +99 -0
- autobyteus/agent_team/handlers/__init__.py +19 -0
- autobyteus/agent_team/handlers/agent_team_event_handler_registry.py +23 -0
- autobyteus/agent_team/handlers/base_agent_team_event_handler.py +16 -0
- autobyteus/agent_team/handlers/inter_agent_message_request_event_handler.py +61 -0
- autobyteus/agent_team/handlers/lifecycle_agent_team_event_handler.py +27 -0
- autobyteus/agent_team/handlers/process_user_message_event_handler.py +46 -0
- autobyteus/agent_team/handlers/tool_approval_team_event_handler.py +48 -0
- autobyteus/agent_team/phases/__init__.py +11 -0
- autobyteus/agent_team/phases/agent_team_operational_phase.py +19 -0
- autobyteus/agent_team/phases/agent_team_phase_manager.py +48 -0
- autobyteus/agent_team/runtime/__init__.py +13 -0
- autobyteus/agent_team/runtime/agent_team_runtime.py +82 -0
- autobyteus/agent_team/runtime/agent_team_worker.py +117 -0
- autobyteus/agent_team/shutdown_steps/__init__.py +17 -0
- autobyteus/agent_team/shutdown_steps/agent_team_shutdown_orchestrator.py +35 -0
- autobyteus/agent_team/shutdown_steps/agent_team_shutdown_step.py +42 -0
- autobyteus/agent_team/shutdown_steps/base_agent_team_shutdown_step.py +16 -0
- autobyteus/agent_team/shutdown_steps/bridge_cleanup_step.py +28 -0
- autobyteus/agent_team/shutdown_steps/sub_team_shutdown_step.py +41 -0
- autobyteus/agent_team/streaming/__init__.py +26 -0
- autobyteus/agent_team/streaming/agent_event_bridge.py +48 -0
- autobyteus/agent_team/streaming/agent_event_multiplexer.py +70 -0
- autobyteus/agent_team/streaming/agent_team_event_notifier.py +64 -0
- autobyteus/agent_team/streaming/agent_team_event_stream.py +33 -0
- autobyteus/agent_team/streaming/agent_team_stream_event_payloads.py +32 -0
- autobyteus/agent_team/streaming/agent_team_stream_events.py +56 -0
- autobyteus/agent_team/streaming/team_event_bridge.py +50 -0
- autobyteus/agent_team/task_notification/__init__.py +11 -0
- autobyteus/agent_team/task_notification/system_event_driven_agent_task_notifier.py +164 -0
- autobyteus/agent_team/task_notification/task_notification_mode.py +24 -0
- autobyteus/agent_team/utils/__init__.py +9 -0
- autobyteus/agent_team/utils/wait_for_idle.py +46 -0
- autobyteus/cli/agent_team_tui/__init__.py +4 -0
- autobyteus/cli/agent_team_tui/app.py +210 -0
- autobyteus/cli/agent_team_tui/state.py +180 -0
- autobyteus/cli/agent_team_tui/widgets/__init__.py +6 -0
- autobyteus/cli/agent_team_tui/widgets/agent_list_sidebar.py +149 -0
- autobyteus/cli/agent_team_tui/widgets/focus_pane.py +320 -0
- autobyteus/cli/agent_team_tui/widgets/logo.py +20 -0
- autobyteus/cli/agent_team_tui/widgets/renderables.py +77 -0
- autobyteus/cli/agent_team_tui/widgets/shared.py +60 -0
- autobyteus/cli/agent_team_tui/widgets/status_bar.py +14 -0
- autobyteus/cli/agent_team_tui/widgets/task_board_panel.py +82 -0
- autobyteus/events/event_types.py +7 -2
- autobyteus/llm/api/autobyteus_llm.py +11 -12
- autobyteus/llm/api/lmstudio_llm.py +10 -13
- autobyteus/llm/api/ollama_llm.py +8 -13
- autobyteus/llm/autobyteus_provider.py +73 -46
- autobyteus/llm/llm_factory.py +102 -140
- autobyteus/llm/lmstudio_provider.py +63 -48
- autobyteus/llm/models.py +83 -53
- autobyteus/llm/ollama_provider.py +69 -61
- autobyteus/llm/ollama_provider_resolver.py +1 -0
- autobyteus/llm/providers.py +13 -13
- autobyteus/llm/runtimes.py +11 -0
- autobyteus/task_management/__init__.py +43 -0
- autobyteus/task_management/base_task_board.py +68 -0
- autobyteus/task_management/converters/__init__.py +11 -0
- autobyteus/task_management/converters/task_board_converter.py +64 -0
- autobyteus/task_management/converters/task_plan_converter.py +48 -0
- autobyteus/task_management/deliverable.py +16 -0
- autobyteus/task_management/deliverables/__init__.py +8 -0
- autobyteus/task_management/deliverables/file_deliverable.py +15 -0
- autobyteus/task_management/events.py +27 -0
- autobyteus/task_management/in_memory_task_board.py +126 -0
- autobyteus/task_management/schemas/__init__.py +15 -0
- autobyteus/task_management/schemas/deliverable_schema.py +13 -0
- autobyteus/task_management/schemas/plan_definition.py +35 -0
- autobyteus/task_management/schemas/task_status_report.py +27 -0
- autobyteus/task_management/task_plan.py +110 -0
- autobyteus/task_management/tools/__init__.py +14 -0
- autobyteus/task_management/tools/get_task_board_status.py +68 -0
- autobyteus/task_management/tools/publish_task_plan.py +113 -0
- autobyteus/task_management/tools/update_task_status.py +135 -0
- autobyteus/tools/bash/bash_executor.py +59 -14
- autobyteus/tools/mcp/config_service.py +63 -58
- autobyteus/tools/mcp/server/http_managed_mcp_server.py +14 -2
- autobyteus/tools/mcp/server/stdio_managed_mcp_server.py +14 -2
- autobyteus/tools/mcp/server_instance_manager.py +30 -4
- autobyteus/tools/mcp/tool_registrar.py +103 -50
- autobyteus/tools/parameter_schema.py +17 -11
- autobyteus/tools/registry/tool_definition.py +24 -29
- autobyteus/tools/tool_category.py +1 -0
- autobyteus/tools/usage/formatters/default_json_example_formatter.py +78 -3
- autobyteus/tools/usage/formatters/default_xml_example_formatter.py +23 -3
- autobyteus/tools/usage/formatters/gemini_json_example_formatter.py +6 -0
- autobyteus/tools/usage/formatters/google_json_example_formatter.py +7 -0
- autobyteus/tools/usage/formatters/openai_json_example_formatter.py +6 -4
- autobyteus/tools/usage/parsers/gemini_json_tool_usage_parser.py +23 -7
- autobyteus/tools/usage/parsers/provider_aware_tool_usage_parser.py +14 -25
- autobyteus/tools/usage/providers/__init__.py +2 -12
- autobyteus/tools/usage/providers/tool_manifest_provider.py +36 -29
- autobyteus/tools/usage/registries/__init__.py +7 -12
- autobyteus/tools/usage/registries/tool_formatter_pair.py +15 -0
- autobyteus/tools/usage/registries/tool_formatting_registry.py +58 -0
- autobyteus/tools/usage/registries/tool_usage_parser_registry.py +55 -0
- {autobyteus-1.1.4.dist-info → autobyteus-1.1.5.dist-info}/METADATA +3 -3
- {autobyteus-1.1.4.dist-info → autobyteus-1.1.5.dist-info}/RECORD +146 -72
- examples/agent_team/__init__.py +1 -0
- examples/run_browser_agent.py +17 -15
- examples/run_google_slides_agent.py +17 -16
- examples/run_poem_writer.py +22 -12
- examples/run_sqlite_agent.py +17 -15
- autobyteus/tools/mcp/call_handlers/__init__.py +0 -16
- autobyteus/tools/mcp/call_handlers/base_handler.py +0 -40
- autobyteus/tools/mcp/call_handlers/stdio_handler.py +0 -76
- autobyteus/tools/mcp/call_handlers/streamable_http_handler.py +0 -55
- autobyteus/tools/usage/providers/json_example_provider.py +0 -32
- autobyteus/tools/usage/providers/json_schema_provider.py +0 -35
- autobyteus/tools/usage/providers/json_tool_usage_parser_provider.py +0 -28
- autobyteus/tools/usage/providers/xml_example_provider.py +0 -28
- autobyteus/tools/usage/providers/xml_schema_provider.py +0 -29
- autobyteus/tools/usage/providers/xml_tool_usage_parser_provider.py +0 -26
- autobyteus/tools/usage/registries/json_example_formatter_registry.py +0 -51
- autobyteus/tools/usage/registries/json_schema_formatter_registry.py +0 -51
- autobyteus/tools/usage/registries/json_tool_usage_parser_registry.py +0 -42
- autobyteus/tools/usage/registries/xml_example_formatter_registry.py +0 -30
- autobyteus/tools/usage/registries/xml_schema_formatter_registry.py +0 -33
- autobyteus/tools/usage/registries/xml_tool_usage_parser_registry.py +0 -30
- examples/workflow/__init__.py +0 -1
- examples/workflow/run_basic_research_workflow.py +0 -189
- examples/workflow/run_code_review_workflow.py +0 -269
- examples/workflow/run_debate_workflow.py +0 -212
- examples/workflow/run_workflow_with_tui.py +0 -153
- {autobyteus-1.1.4.dist-info → autobyteus-1.1.5.dist-info}/WHEEL +0 -0
- {autobyteus-1.1.4.dist-info → autobyteus-1.1.5.dist-info}/licenses/LICENSE +0 -0
- {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
|
|
82
|
+
async def _discover_and_register_from_config(self, server_config: BaseMcpConfig, schema_mapper: McpSchemaMapper) -> List[ToolDefinition]:
|
|
83
83
|
"""
|
|
84
|
-
|
|
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
|
-
|
|
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
|
-
|
|
89
|
-
|
|
90
|
-
|
|
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
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
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
|
|
179
|
+
logger.info("No MCP server configurations to process. Skipping reload.")
|
|
114
180
|
return []
|
|
115
181
|
|
|
116
182
|
schema_mapper = McpSchemaMapper()
|
|
117
|
-
|
|
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
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
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
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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-
|
|
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
|
|
102
|
-
The provider argument is
|
|
102
|
+
Generates the default XML usage schema string for this tool.
|
|
103
|
+
The provider argument is ignored, kept for API consistency.
|
|
103
104
|
"""
|
|
104
|
-
|
|
105
|
-
return
|
|
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
|
-
|
|
119
|
-
return
|
|
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
|
|
125
|
-
The provider argument is
|
|
118
|
+
Generates a default XML usage example string for this tool.
|
|
119
|
+
The provider argument is ignored, kept for API consistency.
|
|
126
120
|
"""
|
|
127
|
-
|
|
128
|
-
return
|
|
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
|
|
126
|
+
Generates a default JSON usage example as a dictionary.
|
|
127
|
+
The provider argument is ignored, kept for API consistency.
|
|
133
128
|
"""
|
|
134
|
-
|
|
135
|
-
return
|
|
129
|
+
formatter = DefaultJsonExampleFormatter()
|
|
130
|
+
return formatter.provide(self)
|
|
136
131
|
|
|
137
132
|
# --- Other methods ---
|
|
138
133
|
@property
|
|
@@ -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
|
|
13
|
-
|
|
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
|
-
|
|
26
|
-
|
|
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
|