autobyteus 1.2.1__py3-none-any.whl → 1.2.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- autobyteus/agent/agent.py +15 -5
- autobyteus/agent/bootstrap_steps/__init__.py +1 -3
- autobyteus/agent/bootstrap_steps/agent_bootstrapper.py +3 -59
- autobyteus/agent/bootstrap_steps/base_bootstrap_step.py +1 -4
- autobyteus/agent/bootstrap_steps/mcp_server_prewarming_step.py +1 -3
- autobyteus/agent/bootstrap_steps/system_prompt_processing_step.py +16 -13
- autobyteus/agent/bootstrap_steps/workspace_context_initialization_step.py +2 -4
- autobyteus/agent/context/agent_config.py +43 -20
- autobyteus/agent/context/agent_context.py +23 -18
- autobyteus/agent/context/agent_runtime_state.py +19 -19
- autobyteus/agent/events/__init__.py +16 -1
- autobyteus/agent/events/agent_events.py +43 -3
- autobyteus/agent/events/agent_input_event_queue_manager.py +79 -26
- autobyteus/agent/events/event_store.py +57 -0
- autobyteus/agent/events/notifiers.py +69 -59
- autobyteus/agent/events/worker_event_dispatcher.py +21 -64
- autobyteus/agent/factory/agent_factory.py +52 -0
- autobyteus/agent/handlers/__init__.py +2 -0
- autobyteus/agent/handlers/approved_tool_invocation_event_handler.py +51 -34
- autobyteus/agent/handlers/bootstrap_event_handler.py +155 -0
- autobyteus/agent/handlers/inter_agent_message_event_handler.py +10 -0
- autobyteus/agent/handlers/lifecycle_event_logger.py +19 -11
- autobyteus/agent/handlers/llm_complete_response_received_event_handler.py +10 -15
- autobyteus/agent/handlers/llm_user_message_ready_event_handler.py +188 -48
- autobyteus/agent/handlers/tool_execution_approval_event_handler.py +0 -10
- autobyteus/agent/handlers/tool_invocation_request_event_handler.py +53 -48
- autobyteus/agent/handlers/tool_result_event_handler.py +7 -8
- autobyteus/agent/handlers/user_input_message_event_handler.py +10 -3
- autobyteus/agent/input_processor/memory_ingest_input_processor.py +40 -0
- autobyteus/agent/lifecycle/__init__.py +12 -0
- autobyteus/agent/lifecycle/base_processor.py +109 -0
- autobyteus/agent/lifecycle/events.py +35 -0
- autobyteus/agent/lifecycle/processor_definition.py +36 -0
- autobyteus/agent/lifecycle/processor_registry.py +106 -0
- autobyteus/agent/llm_request_assembler.py +98 -0
- autobyteus/agent/llm_response_processor/__init__.py +1 -8
- autobyteus/agent/message/context_file_type.py +1 -1
- autobyteus/agent/runtime/agent_runtime.py +29 -21
- autobyteus/agent/runtime/agent_worker.py +98 -19
- autobyteus/agent/shutdown_steps/__init__.py +2 -0
- autobyteus/agent/shutdown_steps/agent_shutdown_orchestrator.py +2 -0
- autobyteus/agent/shutdown_steps/tool_cleanup_step.py +58 -0
- autobyteus/agent/status/__init__.py +14 -0
- autobyteus/agent/status/manager.py +93 -0
- autobyteus/agent/status/status_deriver.py +96 -0
- autobyteus/agent/{phases/phase_enum.py → status/status_enum.py} +16 -16
- autobyteus/agent/status/status_update_utils.py +73 -0
- autobyteus/agent/streaming/__init__.py +52 -5
- autobyteus/agent/streaming/adapters/__init__.py +18 -0
- autobyteus/agent/streaming/adapters/invocation_adapter.py +184 -0
- autobyteus/agent/streaming/adapters/tool_call_parsing.py +163 -0
- autobyteus/agent/streaming/adapters/tool_syntax_registry.py +67 -0
- autobyteus/agent/streaming/agent_event_stream.py +3 -183
- autobyteus/agent/streaming/api_tool_call/__init__.py +16 -0
- autobyteus/agent/streaming/api_tool_call/file_content_streamer.py +56 -0
- autobyteus/agent/streaming/api_tool_call/json_string_field_extractor.py +175 -0
- autobyteus/agent/streaming/api_tool_call_streaming_response_handler.py +4 -0
- autobyteus/agent/streaming/events/__init__.py +6 -0
- autobyteus/agent/streaming/events/stream_event_payloads.py +284 -0
- autobyteus/agent/streaming/events/stream_events.py +141 -0
- autobyteus/agent/streaming/handlers/__init__.py +15 -0
- autobyteus/agent/streaming/handlers/api_tool_call_streaming_response_handler.py +303 -0
- autobyteus/agent/streaming/handlers/parsing_streaming_response_handler.py +107 -0
- autobyteus/agent/streaming/handlers/pass_through_streaming_response_handler.py +107 -0
- autobyteus/agent/streaming/handlers/streaming_handler_factory.py +177 -0
- autobyteus/agent/streaming/handlers/streaming_response_handler.py +58 -0
- autobyteus/agent/streaming/parser/__init__.py +61 -0
- autobyteus/agent/streaming/parser/event_emitter.py +181 -0
- autobyteus/agent/streaming/parser/events.py +4 -0
- autobyteus/agent/streaming/parser/invocation_adapter.py +4 -0
- autobyteus/agent/streaming/parser/json_parsing_strategies/__init__.py +19 -0
- autobyteus/agent/streaming/parser/json_parsing_strategies/base.py +32 -0
- autobyteus/agent/streaming/parser/json_parsing_strategies/default.py +34 -0
- autobyteus/agent/streaming/parser/json_parsing_strategies/gemini.py +31 -0
- autobyteus/agent/streaming/parser/json_parsing_strategies/openai.py +64 -0
- autobyteus/agent/streaming/parser/json_parsing_strategies/registry.py +75 -0
- autobyteus/agent/streaming/parser/parser_context.py +227 -0
- autobyteus/agent/streaming/parser/parser_factory.py +132 -0
- autobyteus/agent/streaming/parser/sentinel_format.py +7 -0
- autobyteus/agent/streaming/parser/state_factory.py +62 -0
- autobyteus/agent/streaming/parser/states/__init__.py +1 -0
- autobyteus/agent/streaming/parser/states/base_state.py +60 -0
- autobyteus/agent/streaming/parser/states/custom_xml_tag_run_bash_parsing_state.py +38 -0
- autobyteus/agent/streaming/parser/states/custom_xml_tag_write_file_parsing_state.py +55 -0
- autobyteus/agent/streaming/parser/states/delimited_content_state.py +146 -0
- autobyteus/agent/streaming/parser/states/json_initialization_state.py +144 -0
- autobyteus/agent/streaming/parser/states/json_tool_parsing_state.py +137 -0
- autobyteus/agent/streaming/parser/states/sentinel_content_state.py +30 -0
- autobyteus/agent/streaming/parser/states/sentinel_initialization_state.py +117 -0
- autobyteus/agent/streaming/parser/states/text_state.py +78 -0
- autobyteus/agent/streaming/parser/states/xml_patch_file_tool_parsing_state.py +328 -0
- autobyteus/agent/streaming/parser/states/xml_run_bash_tool_parsing_state.py +129 -0
- autobyteus/agent/streaming/parser/states/xml_tag_initialization_state.py +151 -0
- autobyteus/agent/streaming/parser/states/xml_tool_parsing_state.py +63 -0
- autobyteus/agent/streaming/parser/states/xml_write_file_tool_parsing_state.py +343 -0
- autobyteus/agent/streaming/parser/strategies/__init__.py +17 -0
- autobyteus/agent/streaming/parser/strategies/base.py +24 -0
- autobyteus/agent/streaming/parser/strategies/json_tool_strategy.py +26 -0
- autobyteus/agent/streaming/parser/strategies/registry.py +28 -0
- autobyteus/agent/streaming/parser/strategies/sentinel_strategy.py +23 -0
- autobyteus/agent/streaming/parser/strategies/xml_tag_strategy.py +21 -0
- autobyteus/agent/streaming/parser/stream_scanner.py +167 -0
- autobyteus/agent/streaming/parser/streaming_parser.py +212 -0
- autobyteus/agent/streaming/parser/tool_call_parsing.py +4 -0
- autobyteus/agent/streaming/parser/tool_constants.py +7 -0
- autobyteus/agent/streaming/parser/tool_syntax_registry.py +4 -0
- autobyteus/agent/streaming/parser/xml_tool_parsing_state_registry.py +55 -0
- autobyteus/agent/streaming/parsing_streaming_response_handler.py +4 -0
- autobyteus/agent/streaming/pass_through_streaming_response_handler.py +4 -0
- autobyteus/agent/streaming/queue_streamer.py +3 -57
- autobyteus/agent/streaming/segments/__init__.py +5 -0
- autobyteus/agent/streaming/segments/segment_events.py +81 -0
- autobyteus/agent/streaming/stream_event_payloads.py +2 -223
- autobyteus/agent/streaming/stream_events.py +3 -140
- autobyteus/agent/streaming/streaming_handler_factory.py +4 -0
- autobyteus/agent/streaming/streaming_response_handler.py +4 -0
- autobyteus/agent/streaming/streams/__init__.py +5 -0
- autobyteus/agent/streaming/streams/agent_event_stream.py +197 -0
- autobyteus/agent/streaming/utils/__init__.py +5 -0
- autobyteus/agent/streaming/utils/queue_streamer.py +59 -0
- autobyteus/agent/system_prompt_processor/__init__.py +2 -0
- autobyteus/agent/system_prompt_processor/available_skills_processor.py +96 -0
- autobyteus/agent/system_prompt_processor/base_processor.py +1 -1
- autobyteus/agent/system_prompt_processor/processor_meta.py +15 -2
- autobyteus/agent/system_prompt_processor/tool_manifest_injector_processor.py +39 -58
- autobyteus/agent/token_budget.py +56 -0
- autobyteus/agent/tool_execution_result_processor/memory_ingest_tool_result_processor.py +29 -0
- autobyteus/agent/tool_invocation.py +16 -40
- autobyteus/agent/tool_invocation_preprocessor/__init__.py +9 -0
- autobyteus/agent/tool_invocation_preprocessor/base_preprocessor.py +45 -0
- autobyteus/agent/tool_invocation_preprocessor/processor_definition.py +15 -0
- autobyteus/agent/tool_invocation_preprocessor/processor_meta.py +33 -0
- autobyteus/agent/tool_invocation_preprocessor/processor_registry.py +60 -0
- autobyteus/agent/utils/wait_for_idle.py +12 -14
- autobyteus/agent/workspace/base_workspace.py +6 -27
- autobyteus/agent_team/agent_team.py +3 -3
- autobyteus/agent_team/agent_team_builder.py +1 -41
- autobyteus/agent_team/bootstrap_steps/__init__.py +0 -4
- autobyteus/agent_team/bootstrap_steps/agent_configuration_preparation_step.py +8 -18
- autobyteus/agent_team/bootstrap_steps/agent_team_bootstrapper.py +4 -16
- autobyteus/agent_team/bootstrap_steps/base_agent_team_bootstrap_step.py +1 -2
- autobyteus/agent_team/bootstrap_steps/coordinator_initialization_step.py +1 -2
- autobyteus/agent_team/bootstrap_steps/task_notifier_initialization_step.py +1 -2
- autobyteus/agent_team/bootstrap_steps/team_context_initialization_step.py +4 -4
- autobyteus/agent_team/context/agent_team_config.py +6 -3
- autobyteus/agent_team/context/agent_team_context.py +25 -3
- autobyteus/agent_team/context/agent_team_runtime_state.py +9 -6
- autobyteus/agent_team/events/__init__.py +11 -0
- autobyteus/agent_team/events/agent_team_event_dispatcher.py +22 -9
- autobyteus/agent_team/events/agent_team_events.py +16 -0
- autobyteus/agent_team/events/event_store.py +57 -0
- autobyteus/agent_team/factory/agent_team_factory.py +8 -0
- autobyteus/agent_team/handlers/inter_agent_message_request_event_handler.py +18 -2
- autobyteus/agent_team/handlers/lifecycle_agent_team_event_handler.py +21 -5
- autobyteus/agent_team/handlers/process_user_message_event_handler.py +17 -8
- autobyteus/agent_team/handlers/tool_approval_team_event_handler.py +19 -4
- autobyteus/agent_team/runtime/agent_team_runtime.py +41 -10
- autobyteus/agent_team/runtime/agent_team_worker.py +69 -5
- autobyteus/agent_team/status/__init__.py +14 -0
- autobyteus/agent_team/status/agent_team_status.py +18 -0
- autobyteus/agent_team/status/agent_team_status_manager.py +33 -0
- autobyteus/agent_team/status/status_deriver.py +62 -0
- autobyteus/agent_team/status/status_update_utils.py +42 -0
- autobyteus/agent_team/streaming/__init__.py +2 -2
- autobyteus/agent_team/streaming/agent_team_event_notifier.py +6 -6
- autobyteus/agent_team/streaming/agent_team_stream_event_payloads.py +4 -4
- autobyteus/agent_team/streaming/agent_team_stream_events.py +3 -3
- autobyteus/agent_team/system_prompt_processor/__init__.py +6 -0
- autobyteus/agent_team/system_prompt_processor/team_manifest_injector_processor.py +76 -0
- autobyteus/agent_team/task_notification/task_notification_mode.py +19 -0
- autobyteus/agent_team/utils/wait_for_idle.py +4 -4
- autobyteus/cli/agent_cli.py +18 -10
- autobyteus/cli/agent_team_tui/app.py +14 -11
- autobyteus/cli/agent_team_tui/state.py +13 -15
- autobyteus/cli/agent_team_tui/widgets/agent_list_sidebar.py +15 -15
- autobyteus/cli/agent_team_tui/widgets/focus_pane.py +143 -36
- autobyteus/cli/agent_team_tui/widgets/renderables.py +1 -1
- autobyteus/cli/agent_team_tui/widgets/shared.py +25 -25
- autobyteus/cli/cli_display.py +193 -44
- autobyteus/cli/workflow_tui/app.py +9 -10
- autobyteus/cli/workflow_tui/state.py +14 -16
- autobyteus/cli/workflow_tui/widgets/agent_list_sidebar.py +15 -15
- autobyteus/cli/workflow_tui/widgets/focus_pane.py +137 -35
- autobyteus/cli/workflow_tui/widgets/renderables.py +1 -1
- autobyteus/cli/workflow_tui/widgets/shared.py +25 -25
- autobyteus/clients/autobyteus_client.py +94 -1
- autobyteus/events/event_types.py +11 -18
- autobyteus/llm/api/autobyteus_llm.py +33 -29
- autobyteus/llm/api/claude_llm.py +142 -36
- autobyteus/llm/api/gemini_llm.py +163 -59
- autobyteus/llm/api/grok_llm.py +1 -1
- autobyteus/llm/api/minimax_llm.py +26 -0
- autobyteus/llm/api/mistral_llm.py +113 -87
- autobyteus/llm/api/ollama_llm.py +9 -42
- autobyteus/llm/api/openai_compatible_llm.py +127 -91
- autobyteus/llm/api/openai_llm.py +3 -3
- autobyteus/llm/api/openai_responses_llm.py +324 -0
- autobyteus/llm/api/zhipu_llm.py +21 -2
- autobyteus/llm/autobyteus_provider.py +70 -60
- autobyteus/llm/base_llm.py +85 -81
- autobyteus/llm/converters/__init__.py +14 -0
- autobyteus/llm/converters/anthropic_tool_call_converter.py +37 -0
- autobyteus/llm/converters/gemini_tool_call_converter.py +57 -0
- autobyteus/llm/converters/mistral_tool_call_converter.py +37 -0
- autobyteus/llm/converters/openai_tool_call_converter.py +38 -0
- autobyteus/llm/extensions/base_extension.py +6 -12
- autobyteus/llm/extensions/token_usage_tracking_extension.py +45 -18
- autobyteus/llm/llm_factory.py +282 -204
- autobyteus/llm/lmstudio_provider.py +60 -49
- autobyteus/llm/models.py +35 -2
- autobyteus/llm/ollama_provider.py +60 -49
- autobyteus/llm/ollama_provider_resolver.py +0 -1
- autobyteus/llm/prompt_renderers/__init__.py +19 -0
- autobyteus/llm/prompt_renderers/anthropic_prompt_renderer.py +104 -0
- autobyteus/llm/prompt_renderers/autobyteus_prompt_renderer.py +19 -0
- autobyteus/llm/prompt_renderers/base_prompt_renderer.py +10 -0
- autobyteus/llm/prompt_renderers/gemini_prompt_renderer.py +63 -0
- autobyteus/llm/prompt_renderers/mistral_prompt_renderer.py +87 -0
- autobyteus/llm/prompt_renderers/ollama_prompt_renderer.py +51 -0
- autobyteus/llm/prompt_renderers/openai_chat_renderer.py +97 -0
- autobyteus/llm/prompt_renderers/openai_responses_renderer.py +101 -0
- autobyteus/llm/providers.py +1 -3
- autobyteus/llm/token_counter/claude_token_counter.py +56 -25
- autobyteus/llm/token_counter/mistral_token_counter.py +12 -8
- autobyteus/llm/token_counter/openai_token_counter.py +24 -5
- autobyteus/llm/token_counter/token_counter_factory.py +12 -5
- autobyteus/llm/utils/llm_config.py +6 -12
- autobyteus/llm/utils/media_payload_formatter.py +27 -20
- autobyteus/llm/utils/messages.py +55 -3
- autobyteus/llm/utils/response_types.py +3 -0
- autobyteus/llm/utils/tool_call_delta.py +31 -0
- autobyteus/memory/__init__.py +32 -0
- autobyteus/memory/active_transcript.py +69 -0
- autobyteus/memory/compaction/__init__.py +9 -0
- autobyteus/memory/compaction/compaction_result.py +8 -0
- autobyteus/memory/compaction/compactor.py +89 -0
- autobyteus/memory/compaction/summarizer.py +11 -0
- autobyteus/memory/compaction_snapshot_builder.py +84 -0
- autobyteus/memory/memory_manager.py +183 -0
- autobyteus/memory/models/__init__.py +14 -0
- autobyteus/memory/models/episodic_item.py +41 -0
- autobyteus/memory/models/memory_types.py +7 -0
- autobyteus/memory/models/raw_trace_item.py +79 -0
- autobyteus/memory/models/semantic_item.py +41 -0
- autobyteus/memory/models/tool_interaction.py +20 -0
- autobyteus/memory/policies/__init__.py +5 -0
- autobyteus/memory/policies/compaction_policy.py +16 -0
- autobyteus/memory/retrieval/__init__.py +7 -0
- autobyteus/memory/retrieval/memory_bundle.py +11 -0
- autobyteus/memory/retrieval/retriever.py +13 -0
- autobyteus/memory/store/__init__.py +7 -0
- autobyteus/memory/store/base_store.py +14 -0
- autobyteus/memory/store/file_store.py +98 -0
- autobyteus/memory/tool_interaction_builder.py +46 -0
- autobyteus/memory/turn_tracker.py +9 -0
- autobyteus/multimedia/audio/api/autobyteus_audio_client.py +19 -5
- autobyteus/multimedia/audio/api/gemini_audio_client.py +108 -16
- autobyteus/multimedia/audio/audio_client_factory.py +47 -9
- autobyteus/multimedia/audio/audio_model.py +2 -1
- autobyteus/multimedia/image/api/autobyteus_image_client.py +19 -5
- autobyteus/multimedia/image/api/gemini_image_client.py +38 -17
- autobyteus/multimedia/image/api/openai_image_client.py +125 -43
- autobyteus/multimedia/image/autobyteus_image_provider.py +2 -1
- autobyteus/multimedia/image/image_client_factory.py +47 -15
- autobyteus/multimedia/image/image_model.py +5 -2
- autobyteus/multimedia/providers.py +3 -2
- autobyteus/skills/loader.py +71 -0
- autobyteus/skills/model.py +11 -0
- autobyteus/skills/registry.py +70 -0
- autobyteus/task_management/tools/todo_tools/add_todo.py +2 -2
- autobyteus/task_management/tools/todo_tools/create_todo_list.py +2 -2
- autobyteus/task_management/tools/todo_tools/update_todo_status.py +2 -2
- autobyteus/tools/__init__.py +34 -47
- autobyteus/tools/base_tool.py +7 -0
- autobyteus/tools/file/__init__.py +2 -6
- autobyteus/tools/file/patch_file.py +149 -0
- autobyteus/tools/file/read_file.py +36 -5
- autobyteus/tools/file/write_file.py +4 -1
- autobyteus/tools/functional_tool.py +43 -6
- autobyteus/tools/mcp/__init__.py +2 -0
- autobyteus/tools/mcp/config_service.py +5 -1
- autobyteus/tools/mcp/server/__init__.py +2 -0
- autobyteus/tools/mcp/server/http_managed_mcp_server.py +1 -1
- autobyteus/tools/mcp/server/websocket_managed_mcp_server.py +141 -0
- autobyteus/tools/mcp/server_instance_manager.py +8 -1
- autobyteus/tools/mcp/types.py +61 -0
- autobyteus/tools/multimedia/audio_tools.py +70 -17
- autobyteus/tools/multimedia/download_media_tool.py +18 -4
- autobyteus/tools/multimedia/image_tools.py +246 -62
- autobyteus/tools/operation_executor/journal_manager.py +107 -0
- autobyteus/tools/operation_executor/operation_event_buffer.py +57 -0
- autobyteus/tools/operation_executor/operation_event_producer.py +29 -0
- autobyteus/tools/operation_executor/operation_executor.py +58 -0
- autobyteus/tools/registry/tool_definition.py +43 -2
- autobyteus/tools/skill/load_skill.py +50 -0
- autobyteus/tools/terminal/__init__.py +45 -0
- autobyteus/tools/terminal/ansi_utils.py +32 -0
- autobyteus/tools/terminal/background_process_manager.py +233 -0
- autobyteus/tools/terminal/output_buffer.py +105 -0
- autobyteus/tools/terminal/prompt_detector.py +63 -0
- autobyteus/tools/terminal/pty_session.py +241 -0
- autobyteus/tools/terminal/session_factory.py +20 -0
- autobyteus/tools/terminal/terminal_session_manager.py +226 -0
- autobyteus/tools/terminal/tools/__init__.py +13 -0
- autobyteus/tools/terminal/tools/get_process_output.py +81 -0
- autobyteus/tools/terminal/tools/run_bash.py +109 -0
- autobyteus/tools/terminal/tools/start_background_process.py +104 -0
- autobyteus/tools/terminal/tools/stop_background_process.py +67 -0
- autobyteus/tools/terminal/types.py +54 -0
- autobyteus/tools/terminal/wsl_tmux_session.py +221 -0
- autobyteus/tools/terminal/wsl_utils.py +156 -0
- autobyteus/tools/transaction_management/backup_handler.py +48 -0
- autobyteus/tools/transaction_management/operation_lifecycle_manager.py +62 -0
- autobyteus/tools/usage/__init__.py +1 -2
- autobyteus/tools/usage/formatters/__init__.py +17 -1
- autobyteus/tools/usage/formatters/base_formatter.py +8 -0
- autobyteus/tools/usage/formatters/default_xml_schema_formatter.py +2 -2
- autobyteus/tools/usage/formatters/mistral_json_schema_formatter.py +18 -0
- autobyteus/tools/usage/formatters/patch_file_xml_example_formatter.py +64 -0
- autobyteus/tools/usage/formatters/patch_file_xml_schema_formatter.py +31 -0
- autobyteus/tools/usage/formatters/run_bash_xml_example_formatter.py +32 -0
- autobyteus/tools/usage/formatters/run_bash_xml_schema_formatter.py +36 -0
- autobyteus/tools/usage/formatters/write_file_xml_example_formatter.py +53 -0
- autobyteus/tools/usage/formatters/write_file_xml_schema_formatter.py +31 -0
- autobyteus/tools/usage/providers/tool_manifest_provider.py +10 -10
- autobyteus/tools/usage/registries/__init__.py +1 -3
- autobyteus/tools/usage/registries/tool_formatting_registry.py +115 -8
- autobyteus/tools/usage/tool_schema_provider.py +51 -0
- autobyteus/tools/web/__init__.py +4 -0
- autobyteus/tools/web/read_url_tool.py +80 -0
- autobyteus/utils/diff_utils.py +271 -0
- autobyteus/utils/download_utils.py +109 -0
- autobyteus/utils/file_utils.py +57 -2
- autobyteus/utils/gemini_helper.py +56 -0
- autobyteus/utils/gemini_model_mapping.py +71 -0
- autobyteus/utils/llm_output_formatter.py +75 -0
- autobyteus/utils/tool_call_format.py +36 -0
- autobyteus/workflow/agentic_workflow.py +3 -3
- autobyteus/workflow/bootstrap_steps/agent_tool_injection_step.py +2 -2
- autobyteus/workflow/bootstrap_steps/base_workflow_bootstrap_step.py +2 -2
- autobyteus/workflow/bootstrap_steps/coordinator_initialization_step.py +2 -2
- autobyteus/workflow/bootstrap_steps/coordinator_prompt_preparation_step.py +3 -9
- autobyteus/workflow/bootstrap_steps/workflow_bootstrapper.py +6 -6
- autobyteus/workflow/bootstrap_steps/workflow_runtime_queue_initialization_step.py +2 -2
- autobyteus/workflow/context/workflow_context.py +3 -3
- autobyteus/workflow/context/workflow_runtime_state.py +5 -5
- autobyteus/workflow/events/workflow_event_dispatcher.py +5 -5
- autobyteus/workflow/handlers/lifecycle_workflow_event_handler.py +3 -3
- autobyteus/workflow/handlers/process_user_message_event_handler.py +5 -5
- autobyteus/workflow/handlers/tool_approval_workflow_event_handler.py +2 -2
- autobyteus/workflow/runtime/workflow_runtime.py +8 -8
- autobyteus/workflow/runtime/workflow_worker.py +3 -3
- autobyteus/workflow/status/__init__.py +11 -0
- autobyteus/workflow/status/workflow_status.py +19 -0
- autobyteus/workflow/status/workflow_status_manager.py +48 -0
- autobyteus/workflow/streaming/__init__.py +2 -2
- autobyteus/workflow/streaming/workflow_event_notifier.py +7 -7
- autobyteus/workflow/streaming/workflow_stream_event_payloads.py +4 -4
- autobyteus/workflow/streaming/workflow_stream_events.py +3 -3
- autobyteus/workflow/utils/wait_for_idle.py +4 -4
- autobyteus-1.2.3.dist-info/METADATA +293 -0
- autobyteus-1.2.3.dist-info/RECORD +600 -0
- {autobyteus-1.2.1.dist-info → autobyteus-1.2.3.dist-info}/WHEEL +1 -1
- {autobyteus-1.2.1.dist-info → autobyteus-1.2.3.dist-info}/top_level.txt +0 -1
- autobyteus/agent/bootstrap_steps/agent_runtime_queue_initialization_step.py +0 -57
- autobyteus/agent/hooks/__init__.py +0 -16
- autobyteus/agent/hooks/base_phase_hook.py +0 -78
- autobyteus/agent/hooks/hook_definition.py +0 -36
- autobyteus/agent/hooks/hook_meta.py +0 -37
- autobyteus/agent/hooks/hook_registry.py +0 -106
- autobyteus/agent/llm_response_processor/provider_aware_tool_usage_processor.py +0 -103
- autobyteus/agent/phases/__init__.py +0 -18
- autobyteus/agent/phases/discover.py +0 -53
- autobyteus/agent/phases/manager.py +0 -265
- autobyteus/agent/phases/transition_decorator.py +0 -40
- autobyteus/agent/phases/transition_info.py +0 -33
- autobyteus/agent/remote_agent.py +0 -244
- autobyteus/agent/workspace/workspace_definition.py +0 -36
- autobyteus/agent/workspace/workspace_meta.py +0 -37
- autobyteus/agent/workspace/workspace_registry.py +0 -72
- autobyteus/agent_team/bootstrap_steps/agent_team_runtime_queue_initialization_step.py +0 -25
- autobyteus/agent_team/bootstrap_steps/coordinator_prompt_preparation_step.py +0 -85
- autobyteus/agent_team/phases/__init__.py +0 -11
- autobyteus/agent_team/phases/agent_team_operational_phase.py +0 -19
- autobyteus/agent_team/phases/agent_team_phase_manager.py +0 -48
- autobyteus/llm/api/bedrock_llm.py +0 -92
- autobyteus/llm/api/groq_llm.py +0 -94
- autobyteus/llm/api/nvidia_llm.py +0 -108
- autobyteus/llm/utils/token_pricing_config.py +0 -87
- autobyteus/rpc/__init__.py +0 -73
- autobyteus/rpc/client/__init__.py +0 -17
- autobyteus/rpc/client/abstract_client_connection.py +0 -124
- autobyteus/rpc/client/client_connection_manager.py +0 -153
- autobyteus/rpc/client/sse_client_connection.py +0 -306
- autobyteus/rpc/client/stdio_client_connection.py +0 -280
- autobyteus/rpc/config/__init__.py +0 -13
- autobyteus/rpc/config/agent_server_config.py +0 -153
- autobyteus/rpc/config/agent_server_registry.py +0 -152
- autobyteus/rpc/hosting.py +0 -244
- autobyteus/rpc/protocol.py +0 -244
- autobyteus/rpc/server/__init__.py +0 -20
- autobyteus/rpc/server/agent_server_endpoint.py +0 -181
- autobyteus/rpc/server/base_method_handler.py +0 -40
- autobyteus/rpc/server/method_handlers.py +0 -259
- autobyteus/rpc/server/sse_server_handler.py +0 -182
- autobyteus/rpc/server/stdio_server_handler.py +0 -151
- autobyteus/rpc/server_main.py +0 -198
- autobyteus/rpc/transport_type.py +0 -13
- autobyteus/tools/bash/__init__.py +0 -2
- autobyteus/tools/bash/bash_executor.py +0 -100
- autobyteus/tools/browser/__init__.py +0 -2
- autobyteus/tools/browser/session_aware/browser_session_aware_navigate_to.py +0 -75
- autobyteus/tools/browser/session_aware/browser_session_aware_tool.py +0 -30
- autobyteus/tools/browser/session_aware/browser_session_aware_web_element_trigger.py +0 -154
- autobyteus/tools/browser/session_aware/browser_session_aware_webpage_reader.py +0 -89
- autobyteus/tools/browser/session_aware/browser_session_aware_webpage_screenshot_taker.py +0 -107
- autobyteus/tools/browser/session_aware/factory/browser_session_aware_web_element_trigger_factory.py +0 -14
- autobyteus/tools/browser/session_aware/factory/browser_session_aware_webpage_reader_factory.py +0 -26
- autobyteus/tools/browser/session_aware/factory/browser_session_aware_webpage_screenshot_taker_factory.py +0 -14
- autobyteus/tools/browser/session_aware/shared_browser_session.py +0 -11
- autobyteus/tools/browser/session_aware/shared_browser_session_manager.py +0 -25
- autobyteus/tools/browser/session_aware/web_element_action.py +0 -20
- autobyteus/tools/browser/standalone/__init__.py +0 -6
- autobyteus/tools/browser/standalone/factory/__init__.py +0 -0
- autobyteus/tools/browser/standalone/factory/webpage_reader_factory.py +0 -25
- autobyteus/tools/browser/standalone/factory/webpage_screenshot_taker_factory.py +0 -14
- autobyteus/tools/browser/standalone/navigate_to.py +0 -84
- autobyteus/tools/browser/standalone/web_page_pdf_generator.py +0 -101
- autobyteus/tools/browser/standalone/webpage_image_downloader.py +0 -169
- autobyteus/tools/browser/standalone/webpage_reader.py +0 -105
- autobyteus/tools/browser/standalone/webpage_screenshot_taker.py +0 -105
- autobyteus/tools/file/edit_file.py +0 -200
- autobyteus/tools/file/list_directory.py +0 -168
- autobyteus/tools/file/search_files.py +0 -188
- autobyteus/tools/timer.py +0 -175
- autobyteus/tools/usage/parsers/__init__.py +0 -22
- autobyteus/tools/usage/parsers/_json_extractor.py +0 -99
- autobyteus/tools/usage/parsers/_string_decoders.py +0 -18
- autobyteus/tools/usage/parsers/anthropic_xml_tool_usage_parser.py +0 -10
- autobyteus/tools/usage/parsers/base_parser.py +0 -41
- autobyteus/tools/usage/parsers/default_json_tool_usage_parser.py +0 -83
- autobyteus/tools/usage/parsers/default_xml_tool_usage_parser.py +0 -316
- autobyteus/tools/usage/parsers/exceptions.py +0 -13
- autobyteus/tools/usage/parsers/gemini_json_tool_usage_parser.py +0 -77
- autobyteus/tools/usage/parsers/openai_json_tool_usage_parser.py +0 -149
- autobyteus/tools/usage/parsers/provider_aware_tool_usage_parser.py +0 -59
- autobyteus/tools/usage/registries/tool_usage_parser_registry.py +0 -62
- autobyteus/workflow/phases/__init__.py +0 -11
- autobyteus/workflow/phases/workflow_operational_phase.py +0 -19
- autobyteus/workflow/phases/workflow_phase_manager.py +0 -48
- autobyteus-1.2.1.dist-info/METADATA +0 -205
- autobyteus-1.2.1.dist-info/RECORD +0 -511
- examples/__init__.py +0 -1
- examples/agent_team/__init__.py +0 -1
- examples/discover_phase_transitions.py +0 -104
- examples/run_agentic_software_engineer.py +0 -239
- examples/run_browser_agent.py +0 -262
- examples/run_google_slides_agent.py +0 -287
- examples/run_mcp_browser_client.py +0 -174
- examples/run_mcp_google_slides_client.py +0 -270
- examples/run_mcp_list_tools.py +0 -189
- examples/run_poem_writer.py +0 -284
- examples/run_sqlite_agent.py +0 -295
- /autobyteus/{tools/browser/session_aware → skills}/__init__.py +0 -0
- /autobyteus/tools/{browser/session_aware/factory → skill}/__init__.py +0 -0
- {autobyteus-1.2.1.dist-info → autobyteus-1.2.3.dist-info}/licenses/LICENSE +0 -0
|
@@ -155,6 +155,12 @@ def _get_parameter_type_from_hint(py_type: Any, param_name: str) -> Tuple[Parame
|
|
|
155
155
|
logger.warning(f"Unmapped type hint {py_type} (actual_type: {actual_type}) for param '{param_name}'. Defaulting to ParameterType.STRING.")
|
|
156
156
|
return ParameterType.STRING, None
|
|
157
157
|
|
|
158
|
+
|
|
159
|
+
try:
|
|
160
|
+
from pydantic.fields import FieldInfo
|
|
161
|
+
except ImportError:
|
|
162
|
+
FieldInfo = None # type: ignore
|
|
163
|
+
|
|
158
164
|
def _parse_signature(sig: inspect.Signature, tool_name: str) -> Tuple[TypingList[str], bool, bool, ParameterSchema]:
|
|
159
165
|
func_param_names = []
|
|
160
166
|
expects_context = False
|
|
@@ -175,26 +181,51 @@ def _parse_signature(sig: inspect.Signature, tool_name: str) -> Tuple[TypingList
|
|
|
175
181
|
param_type_enum, item_schema = _get_parameter_type_from_hint(param_type_hint, param_name)
|
|
176
182
|
|
|
177
183
|
is_required = param_obj.default == inspect.Parameter.empty
|
|
178
|
-
if
|
|
179
|
-
|
|
180
|
-
|
|
184
|
+
default_val = param_obj.default if param_obj.default != inspect.Parameter.empty else None
|
|
185
|
+
|
|
186
|
+
# --- Pydantic Field Extraction Logic ---
|
|
181
187
|
param_desc = f"Parameter '{param_name}' for tool '{tool_name}'."
|
|
182
188
|
param_name_lower = param_name.lower()
|
|
183
189
|
if "path" in param_name_lower or "file" in param_name_lower or "dir" in param_name_lower or "folder" in param_name_lower:
|
|
184
|
-
|
|
190
|
+
param_desc += " This is expected to be a path."
|
|
191
|
+
|
|
192
|
+
if FieldInfo and isinstance(param_obj.default, FieldInfo):
|
|
193
|
+
field_info = param_obj.default
|
|
194
|
+
|
|
195
|
+
# 1. Description
|
|
196
|
+
if field_info.description:
|
|
197
|
+
param_desc = field_info.description
|
|
185
198
|
|
|
199
|
+
# 2. Default Value & Requiredness
|
|
200
|
+
# If PydanticUndefined (or similar sentinel), it means required.
|
|
201
|
+
# Otherwise, use the default value from Field.
|
|
202
|
+
# Note: Pydantic v1 uses Undefined, v2 uses PydanticUndefined.
|
|
203
|
+
# We check if it is the special undefined value via representation or direct check.
|
|
204
|
+
|
|
205
|
+
# Simple heuristic for "Undefined" without importing the specific sentinel
|
|
206
|
+
if str(field_info.default) == "PydanticUndefined" or field_info.default == ...:
|
|
207
|
+
is_required = True
|
|
208
|
+
default_val = None
|
|
209
|
+
else:
|
|
210
|
+
is_required = False
|
|
211
|
+
default_val = field_info.default
|
|
212
|
+
|
|
213
|
+
if get_origin(param_type_hint) is Union and type(None) in get_args(param_type_hint):
|
|
214
|
+
is_required = False
|
|
215
|
+
|
|
186
216
|
schema_param = ParameterDefinition(
|
|
187
217
|
name=param_name,
|
|
188
218
|
param_type=param_type_enum,
|
|
189
219
|
description=param_desc,
|
|
190
220
|
required=is_required,
|
|
191
|
-
default_value=
|
|
221
|
+
default_value=default_val,
|
|
192
222
|
array_item_schema=item_schema
|
|
193
223
|
)
|
|
194
224
|
generated_arg_schema.add_parameter(schema_param)
|
|
195
225
|
|
|
196
226
|
return func_param_names, expects_context, expects_tool_state, generated_arg_schema
|
|
197
227
|
|
|
228
|
+
|
|
198
229
|
# --- The refactored @tool decorator ---
|
|
199
230
|
|
|
200
231
|
def tool(
|
|
@@ -217,6 +248,11 @@ def tool(
|
|
|
217
248
|
|
|
218
249
|
final_arg_schema = argument_schema if argument_schema is not None else gen_arg_schema
|
|
219
250
|
|
|
251
|
+
def _current_description() -> str:
|
|
252
|
+
"""Recompute the description from the latest docstring/override."""
|
|
253
|
+
latest_doc = inspect.getdoc(func)
|
|
254
|
+
return description or (latest_doc.split('\n\n')[0] if latest_doc else f"Functional tool: {tool_name}")
|
|
255
|
+
|
|
220
256
|
def factory(inst_config: Optional[ToolConfig] = None) -> FunctionalTool:
|
|
221
257
|
return FunctionalTool(
|
|
222
258
|
original_func=func,
|
|
@@ -239,7 +275,8 @@ def tool(
|
|
|
239
275
|
custom_factory=factory,
|
|
240
276
|
tool_class=None,
|
|
241
277
|
origin=ToolOrigin.LOCAL,
|
|
242
|
-
category=category
|
|
278
|
+
category=category,
|
|
279
|
+
description_provider=_current_description
|
|
243
280
|
)
|
|
244
281
|
default_tool_registry.register_tool(tool_def)
|
|
245
282
|
|
autobyteus/tools/mcp/__init__.py
CHANGED
|
@@ -19,6 +19,7 @@ from .types import (
|
|
|
19
19
|
BaseMcpConfig,
|
|
20
20
|
StdioMcpServerConfig,
|
|
21
21
|
StreamableHttpMcpServerConfig,
|
|
22
|
+
WebsocketMcpServerConfig,
|
|
22
23
|
McpTransportType,
|
|
23
24
|
McpServerInstanceKey,
|
|
24
25
|
)
|
|
@@ -37,6 +38,7 @@ __all__ = [
|
|
|
37
38
|
"BaseMcpConfig",
|
|
38
39
|
"StdioMcpServerConfig",
|
|
39
40
|
"StreamableHttpMcpServerConfig",
|
|
41
|
+
"WebsocketMcpServerConfig",
|
|
40
42
|
"McpTransportType",
|
|
41
43
|
"McpServerInstanceKey",
|
|
42
44
|
# Services and Managers
|
|
@@ -9,6 +9,7 @@ from .types import (
|
|
|
9
9
|
BaseMcpConfig,
|
|
10
10
|
StdioMcpServerConfig,
|
|
11
11
|
StreamableHttpMcpServerConfig,
|
|
12
|
+
WebsocketMcpServerConfig,
|
|
12
13
|
McpTransportType
|
|
13
14
|
)
|
|
14
15
|
from autobyteus.utils.singleton import SingletonMeta
|
|
@@ -49,7 +50,8 @@ class McpConfigService(metaclass=SingletonMeta):
|
|
|
49
50
|
|
|
50
51
|
transport_specific_params_key_map = {
|
|
51
52
|
McpTransportType.STDIO: "stdio_params",
|
|
52
|
-
McpTransportType.STREAMABLE_HTTP: "streamable_http_params"
|
|
53
|
+
McpTransportType.STREAMABLE_HTTP: "streamable_http_params",
|
|
54
|
+
McpTransportType.WEBSOCKET: "websocket_params",
|
|
53
55
|
}
|
|
54
56
|
|
|
55
57
|
if transport_type in transport_specific_params_key_map:
|
|
@@ -74,6 +76,8 @@ class McpConfigService(metaclass=SingletonMeta):
|
|
|
74
76
|
return StdioMcpServerConfig(**constructor_params)
|
|
75
77
|
elif transport_type == McpTransportType.STREAMABLE_HTTP:
|
|
76
78
|
return StreamableHttpMcpServerConfig(**constructor_params)
|
|
79
|
+
elif transport_type == McpTransportType.WEBSOCKET:
|
|
80
|
+
return WebsocketMcpServerConfig(**constructor_params)
|
|
77
81
|
else:
|
|
78
82
|
raise ValueError(f"Unsupported McpTransportType '{transport_type}' for server '{server_id}'.")
|
|
79
83
|
except TypeError as e:
|
|
@@ -5,6 +5,7 @@ This package contains the core abstractions for managing connections to remote M
|
|
|
5
5
|
from .base_managed_mcp_server import BaseManagedMcpServer, ServerState
|
|
6
6
|
from .stdio_managed_mcp_server import StdioManagedMcpServer
|
|
7
7
|
from .http_managed_mcp_server import HttpManagedMcpServer
|
|
8
|
+
from .websocket_managed_mcp_server import WebsocketManagedMcpServer
|
|
8
9
|
from .proxy import McpServerProxy
|
|
9
10
|
|
|
10
11
|
__all__ = [
|
|
@@ -12,5 +13,6 @@ __all__ = [
|
|
|
12
13
|
"ServerState",
|
|
13
14
|
"StdioManagedMcpServer",
|
|
14
15
|
"HttpManagedMcpServer",
|
|
16
|
+
"WebsocketManagedMcpServer",
|
|
15
17
|
"McpServerProxy",
|
|
16
18
|
]
|
|
@@ -23,7 +23,7 @@ class HttpManagedMcpServer(BaseManagedMcpServer):
|
|
|
23
23
|
config = cast(StreamableHttpMcpServerConfig, self._config)
|
|
24
24
|
|
|
25
25
|
logger.debug(f"Establishing HTTP connection for server '{self.server_id}' to URL: {config.url}")
|
|
26
|
-
read_stream, write_stream = await self._exit_stack.enter_async_context(
|
|
26
|
+
read_stream, write_stream, _ = await self._exit_stack.enter_async_context(
|
|
27
27
|
streamablehttp_client(config.url, headers=config.headers)
|
|
28
28
|
)
|
|
29
29
|
session = await self._exit_stack.enter_async_context(ClientSession(read_stream, write_stream))
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import logging
|
|
5
|
+
import ssl
|
|
6
|
+
from collections.abc import AsyncGenerator
|
|
7
|
+
from contextlib import asynccontextmanager
|
|
8
|
+
from typing import cast
|
|
9
|
+
|
|
10
|
+
import anyio
|
|
11
|
+
from anyio.streams.memory import MemoryObjectReceiveStream, MemoryObjectSendStream
|
|
12
|
+
from mcp import ClientSession, types as mcp_types
|
|
13
|
+
from mcp.shared.message import SessionMessage
|
|
14
|
+
from pydantic import ValidationError
|
|
15
|
+
from websockets.asyncio.client import connect as ws_connect
|
|
16
|
+
from websockets.typing import Subprotocol
|
|
17
|
+
|
|
18
|
+
from .base_managed_mcp_server import BaseManagedMcpServer
|
|
19
|
+
from ..types import WebsocketMcpServerConfig
|
|
20
|
+
|
|
21
|
+
logger = logging.getLogger(__name__)
|
|
22
|
+
|
|
23
|
+
INITIALIZE_TIMEOUT = 10 # seconds
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _build_ssl_context(config: WebsocketMcpServerConfig) -> ssl.SSLContext | None:
|
|
27
|
+
"""Builds an SSL context when the target URL uses wss://."""
|
|
28
|
+
if not config.url or not config.url.lower().startswith("wss://"):
|
|
29
|
+
return None
|
|
30
|
+
|
|
31
|
+
if config.verify_tls:
|
|
32
|
+
context = ssl.create_default_context()
|
|
33
|
+
if config.ca_file:
|
|
34
|
+
context.load_verify_locations(cafile=config.ca_file)
|
|
35
|
+
else:
|
|
36
|
+
context = ssl._create_unverified_context()
|
|
37
|
+
|
|
38
|
+
if config.client_cert:
|
|
39
|
+
context.load_cert_chain(certfile=config.client_cert, keyfile=config.client_key)
|
|
40
|
+
|
|
41
|
+
return context
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _normalize_subprotocols(config: WebsocketMcpServerConfig) -> list[Subprotocol]:
|
|
45
|
+
"""Ensures the MCP subprotocol is always negotiated."""
|
|
46
|
+
provided = [proto for proto in config.subprotocols if proto]
|
|
47
|
+
lowered = {proto.lower() for proto in provided}
|
|
48
|
+
if "mcp" not in lowered:
|
|
49
|
+
provided.append("mcp")
|
|
50
|
+
return [Subprotocol(proto) for proto in provided]
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@asynccontextmanager
|
|
54
|
+
async def _websocket_transport(
|
|
55
|
+
config: WebsocketMcpServerConfig,
|
|
56
|
+
) -> AsyncGenerator[
|
|
57
|
+
tuple[MemoryObjectReceiveStream[SessionMessage | Exception], MemoryObjectSendStream[SessionMessage]],
|
|
58
|
+
None,
|
|
59
|
+
]:
|
|
60
|
+
read_stream_writer, read_stream = anyio.create_memory_object_stream[SessionMessage | Exception](0)
|
|
61
|
+
write_stream, write_stream_reader = anyio.create_memory_object_stream[SessionMessage](0)
|
|
62
|
+
|
|
63
|
+
headers = dict(config.headers)
|
|
64
|
+
if not headers:
|
|
65
|
+
headers = None
|
|
66
|
+
|
|
67
|
+
connect_kwargs = {
|
|
68
|
+
"origin": config.origin,
|
|
69
|
+
"subprotocols": _normalize_subprotocols(config),
|
|
70
|
+
"additional_headers": headers,
|
|
71
|
+
"open_timeout": config.open_timeout,
|
|
72
|
+
"ping_interval": config.ping_interval,
|
|
73
|
+
"ping_timeout": config.ping_timeout,
|
|
74
|
+
"ssl": _build_ssl_context(config),
|
|
75
|
+
}
|
|
76
|
+
# Remove None values so websockets uses library defaults
|
|
77
|
+
connect_kwargs = {key: value for key, value in connect_kwargs.items() if value is not None}
|
|
78
|
+
|
|
79
|
+
negotiated_protocols = [str(proto) for proto in connect_kwargs.get("subprotocols", [])]
|
|
80
|
+
|
|
81
|
+
logger.debug(
|
|
82
|
+
"Connecting to MCP WebSocket %s (subprotocols=%s, origin=%s)",
|
|
83
|
+
config.url,
|
|
84
|
+
negotiated_protocols,
|
|
85
|
+
config.origin,
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
async with ws_connect(config.url, **connect_kwargs) as websocket:
|
|
89
|
+
|
|
90
|
+
async def ws_reader():
|
|
91
|
+
async with read_stream_writer:
|
|
92
|
+
async for raw_message in websocket:
|
|
93
|
+
payload = raw_message.decode("utf-8") if isinstance(raw_message, bytes) else raw_message
|
|
94
|
+
try:
|
|
95
|
+
message = mcp_types.JSONRPCMessage.model_validate_json(payload)
|
|
96
|
+
await read_stream_writer.send(SessionMessage(message))
|
|
97
|
+
except ValidationError as exc:
|
|
98
|
+
await read_stream_writer.send(exc)
|
|
99
|
+
|
|
100
|
+
async def ws_writer():
|
|
101
|
+
async with write_stream_reader:
|
|
102
|
+
async for session_message in write_stream_reader:
|
|
103
|
+
payload = session_message.message.model_dump_json(by_alias=True, exclude_none=True)
|
|
104
|
+
await websocket.send(payload)
|
|
105
|
+
|
|
106
|
+
async with anyio.create_task_group() as tg:
|
|
107
|
+
tg.start_soon(ws_reader)
|
|
108
|
+
tg.start_soon(ws_writer)
|
|
109
|
+
try:
|
|
110
|
+
yield read_stream, write_stream
|
|
111
|
+
finally:
|
|
112
|
+
tg.cancel_scope.cancel()
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
class WebsocketManagedMcpServer(BaseManagedMcpServer):
|
|
116
|
+
"""Manages the lifecycle of a WebSocket-based MCP server connection."""
|
|
117
|
+
|
|
118
|
+
def __init__(self, config: WebsocketMcpServerConfig):
|
|
119
|
+
super().__init__(config)
|
|
120
|
+
|
|
121
|
+
async def _create_client_session(self) -> ClientSession:
|
|
122
|
+
config = cast(WebsocketMcpServerConfig, self._config)
|
|
123
|
+
|
|
124
|
+
read_stream, write_stream = await self._exit_stack.enter_async_context(
|
|
125
|
+
_websocket_transport(config)
|
|
126
|
+
)
|
|
127
|
+
session = await self._exit_stack.enter_async_context(ClientSession(read_stream, write_stream))
|
|
128
|
+
|
|
129
|
+
try:
|
|
130
|
+
await asyncio.wait_for(session.initialize(), timeout=INITIALIZE_TIMEOUT)
|
|
131
|
+
except asyncio.TimeoutError:
|
|
132
|
+
logger.error(
|
|
133
|
+
"Timeout occurred while initializing WebSocket session for server '%s'.",
|
|
134
|
+
self.server_id,
|
|
135
|
+
)
|
|
136
|
+
raise ConnectionError(
|
|
137
|
+
f"Server '{self.server_id}' failed to initialize within the timeout period."
|
|
138
|
+
) from None
|
|
139
|
+
|
|
140
|
+
logger.debug("ClientSession established and initialized for WebSocket server '%s'.", self.server_id)
|
|
141
|
+
return session
|
|
@@ -7,7 +7,12 @@ from contextlib import asynccontextmanager
|
|
|
7
7
|
from autobyteus.utils.singleton import SingletonMeta
|
|
8
8
|
from autobyteus.agent.context import AgentContextRegistry
|
|
9
9
|
from .config_service import McpConfigService
|
|
10
|
-
from .server import
|
|
10
|
+
from .server import (
|
|
11
|
+
BaseManagedMcpServer,
|
|
12
|
+
StdioManagedMcpServer,
|
|
13
|
+
HttpManagedMcpServer,
|
|
14
|
+
WebsocketManagedMcpServer,
|
|
15
|
+
)
|
|
11
16
|
from .types import McpTransportType, McpServerInstanceKey, BaseMcpConfig, StdioMcpServerConfig
|
|
12
17
|
|
|
13
18
|
logger = logging.getLogger(__name__)
|
|
@@ -29,6 +34,8 @@ class McpServerInstanceManager(metaclass=SingletonMeta):
|
|
|
29
34
|
return StdioManagedMcpServer(server_config)
|
|
30
35
|
elif server_config.transport_type == McpTransportType.STREAMABLE_HTTP:
|
|
31
36
|
return HttpManagedMcpServer(server_config)
|
|
37
|
+
elif server_config.transport_type == McpTransportType.WEBSOCKET:
|
|
38
|
+
return WebsocketManagedMcpServer(server_config)
|
|
32
39
|
else:
|
|
33
40
|
raise NotImplementedError(f"No ManagedMcpServer implementation for transport type '{server_config.transport_type}'.")
|
|
34
41
|
|
autobyteus/tools/mcp/types.py
CHANGED
|
@@ -10,6 +10,7 @@ class McpTransportType(str, Enum):
|
|
|
10
10
|
"""Enumeration of supported MCP transport types."""
|
|
11
11
|
STDIO = "stdio"
|
|
12
12
|
STREAMABLE_HTTP = "streamable_http"
|
|
13
|
+
WEBSOCKET = "websocket"
|
|
13
14
|
|
|
14
15
|
@dataclass(frozen=True)
|
|
15
16
|
class McpServerInstanceKey:
|
|
@@ -85,3 +86,63 @@ class StreamableHttpMcpServerConfig(BaseMcpConfig):
|
|
|
85
86
|
raise ValueError(f"StreamableHttpMcpServerConfig '{self.server_id}' 'token' must be a string if provided.")
|
|
86
87
|
if not isinstance(self.headers, dict) or not all(isinstance(k, str) and isinstance(v, str) for k, v in self.headers.items()):
|
|
87
88
|
raise ValueError(f"StreamableHttpMcpServerConfig '{self.server_id}' 'headers' must be a Dict[str, str].")
|
|
89
|
+
|
|
90
|
+
@dataclass
|
|
91
|
+
class WebsocketMcpServerConfig(BaseMcpConfig):
|
|
92
|
+
"""Configuration parameters for an MCP server using a WebSocket transport."""
|
|
93
|
+
|
|
94
|
+
url: Optional[str] = None
|
|
95
|
+
headers: Dict[str, str] = field(default_factory=dict)
|
|
96
|
+
subprotocols: List[str] = field(default_factory=list)
|
|
97
|
+
origin: Optional[str] = None
|
|
98
|
+
open_timeout: Optional[float] = 10.0
|
|
99
|
+
ping_interval: Optional[float] = None
|
|
100
|
+
ping_timeout: Optional[float] = None
|
|
101
|
+
verify_tls: bool = True
|
|
102
|
+
ca_file: Optional[str] = None
|
|
103
|
+
client_cert: Optional[str] = None
|
|
104
|
+
client_key: Optional[str] = None
|
|
105
|
+
|
|
106
|
+
def __post_init__(self):
|
|
107
|
+
super().__post_init__()
|
|
108
|
+
self.transport_type = McpTransportType.WEBSOCKET
|
|
109
|
+
|
|
110
|
+
if self.url is None or not isinstance(self.url, str) or not self.url.strip():
|
|
111
|
+
raise ValueError(f"WebsocketMcpServerConfig '{self.server_id}' 'url' must be a non-empty string.")
|
|
112
|
+
|
|
113
|
+
normalized_url = self.url.strip().lower()
|
|
114
|
+
if not (normalized_url.startswith("ws://") or normalized_url.startswith("wss://")):
|
|
115
|
+
raise ValueError(
|
|
116
|
+
f"WebsocketMcpServerConfig '{self.server_id}' 'url' must start with ws:// or wss://."
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
if not isinstance(self.headers, dict) or not all(isinstance(k, str) and isinstance(v, str) for k, v in self.headers.items()):
|
|
120
|
+
raise ValueError(f"WebsocketMcpServerConfig '{self.server_id}' 'headers' must be a Dict[str, str].")
|
|
121
|
+
|
|
122
|
+
if not isinstance(self.subprotocols, list) or not all(isinstance(item, str) for item in self.subprotocols):
|
|
123
|
+
raise ValueError(f"WebsocketMcpServerConfig '{self.server_id}' 'subprotocols' must be a list of strings.")
|
|
124
|
+
|
|
125
|
+
if self.origin is not None and not isinstance(self.origin, str):
|
|
126
|
+
raise ValueError(f"WebsocketMcpServerConfig '{self.server_id}' 'origin' must be a string if provided.")
|
|
127
|
+
|
|
128
|
+
for field_name in ("open_timeout", "ping_interval", "ping_timeout"):
|
|
129
|
+
value = getattr(self, field_name)
|
|
130
|
+
if value is not None and (not isinstance(value, (int, float)) or value <= 0):
|
|
131
|
+
raise ValueError(
|
|
132
|
+
f"WebsocketMcpServerConfig '{self.server_id}' '{field_name}' must be a positive number when provided."
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
if not isinstance(self.verify_tls, bool):
|
|
136
|
+
raise ValueError(f"WebsocketMcpServerConfig '{self.server_id}' 'verify_tls' must be a boolean.")
|
|
137
|
+
|
|
138
|
+
for path_field in ("ca_file", "client_cert", "client_key"):
|
|
139
|
+
path_value = getattr(self, path_field)
|
|
140
|
+
if path_value is not None and not isinstance(path_value, str):
|
|
141
|
+
raise ValueError(
|
|
142
|
+
f"WebsocketMcpServerConfig '{self.server_id}' '{path_field}' must be a string path when provided."
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
if self.client_key and not self.client_cert:
|
|
146
|
+
raise ValueError(
|
|
147
|
+
f"WebsocketMcpServerConfig '{self.server_id}' requires 'client_cert' when 'client_key' is provided."
|
|
148
|
+
)
|
|
@@ -1,14 +1,38 @@
|
|
|
1
1
|
import os
|
|
2
2
|
import logging
|
|
3
|
-
from typing import Optional, List
|
|
3
|
+
from typing import Optional, List, Any
|
|
4
|
+
from pathlib import Path
|
|
4
5
|
|
|
5
6
|
from autobyteus.tools.base_tool import BaseTool
|
|
6
7
|
from autobyteus.utils.parameter_schema import ParameterSchema, ParameterDefinition, ParameterType
|
|
7
8
|
from autobyteus.tools.tool_category import ToolCategory
|
|
8
9
|
from autobyteus.multimedia.audio import audio_client_factory, AudioModel, AudioClientFactory
|
|
10
|
+
from autobyteus.multimedia.audio.base_audio_client import BaseAudioClient
|
|
11
|
+
from autobyteus.utils.download_utils import download_file_from_url
|
|
12
|
+
from autobyteus.utils.file_utils import resolve_safe_path
|
|
9
13
|
|
|
10
14
|
logger = logging.getLogger(__name__)
|
|
11
15
|
|
|
16
|
+
def _get_workspace_root(context) -> str:
|
|
17
|
+
if not context.workspace:
|
|
18
|
+
error_msg = (
|
|
19
|
+
f"Relative path provided, but no workspace is configured for agent '{context.agent_id}'. "
|
|
20
|
+
"A workspace is required to resolve relative paths."
|
|
21
|
+
)
|
|
22
|
+
logger.error(error_msg)
|
|
23
|
+
raise ValueError(error_msg)
|
|
24
|
+
|
|
25
|
+
base_path = context.workspace.get_base_path()
|
|
26
|
+
if not base_path or not isinstance(base_path, str):
|
|
27
|
+
error_msg = (
|
|
28
|
+
f"Agent '{context.agent_id}' has a configured workspace, but it provided an invalid base path "
|
|
29
|
+
f"('{base_path}'). Cannot resolve relative paths."
|
|
30
|
+
)
|
|
31
|
+
logger.error(error_msg)
|
|
32
|
+
raise ValueError(error_msg)
|
|
33
|
+
|
|
34
|
+
return base_path
|
|
35
|
+
|
|
12
36
|
|
|
13
37
|
def _get_configured_model_identifier(env_var: str, default_model: Optional[str] = None) -> str:
|
|
14
38
|
"""
|
|
@@ -54,13 +78,16 @@ def _build_dynamic_audio_schema(base_params: List[ParameterDefinition], model_en
|
|
|
54
78
|
|
|
55
79
|
class GenerateSpeechTool(BaseTool):
|
|
56
80
|
"""
|
|
57
|
-
|
|
58
81
|
An agent tool for generating speech from text using a Text-to-Speech (TTS) model.
|
|
59
82
|
"""
|
|
60
83
|
CATEGORY = ToolCategory.MULTIMEDIA
|
|
61
84
|
MODEL_ENV_VAR = "DEFAULT_SPEECH_GENERATION_MODEL"
|
|
62
85
|
DEFAULT_MODEL = "gemini-2.5-flash-tts"
|
|
63
86
|
|
|
87
|
+
def __init__(self, config=None):
|
|
88
|
+
super().__init__(config)
|
|
89
|
+
self._client: Optional[BaseAudioClient] = None
|
|
90
|
+
|
|
64
91
|
@classmethod
|
|
65
92
|
def get_name(cls) -> str:
|
|
66
93
|
return "generate_speech"
|
|
@@ -69,7 +96,7 @@ class GenerateSpeechTool(BaseTool):
|
|
|
69
96
|
def get_description(cls) -> str:
|
|
70
97
|
return (
|
|
71
98
|
"Generates spoken audio from text using the system's default Text-to-Speech (TTS) model. "
|
|
72
|
-
"
|
|
99
|
+
"Saves the generated audio file (.wav or .mp3) to the specified local file path and returns the path."
|
|
73
100
|
)
|
|
74
101
|
|
|
75
102
|
@classmethod
|
|
@@ -82,25 +109,51 @@ class GenerateSpeechTool(BaseTool):
|
|
|
82
109
|
"The text to be converted into spoken audio. For multi-speaker mode, you must format the prompt "
|
|
83
110
|
"with speaker labels that match the speakers defined in 'speaker_mapping'. "
|
|
84
111
|
"CRITICAL: Each speaker's dialogue MUST be on a new line. "
|
|
85
|
-
"Example: 'Joe: Hello Jane
|
|
112
|
+
"Example: 'Joe: Hello Jane.\nJane: Hi Joe, how are you?'"
|
|
113
|
+
),
|
|
114
|
+
required=True
|
|
115
|
+
),
|
|
116
|
+
ParameterDefinition(
|
|
117
|
+
name="output_file_path",
|
|
118
|
+
param_type=ParameterType.STRING,
|
|
119
|
+
description=(
|
|
120
|
+
"Required. The local file path (relative to workspace) where the generated audio should be saved. "
|
|
121
|
+
"Example: 'assets/audio/speech.wav'"
|
|
86
122
|
),
|
|
87
123
|
required=True
|
|
88
124
|
)
|
|
89
125
|
]
|
|
90
126
|
return _build_dynamic_audio_schema(base_params, cls.MODEL_ENV_VAR, cls.DEFAULT_MODEL)
|
|
91
127
|
|
|
92
|
-
async def _execute(
|
|
128
|
+
async def _execute(
|
|
129
|
+
self,
|
|
130
|
+
context,
|
|
131
|
+
prompt: str,
|
|
132
|
+
output_file_path: str,
|
|
133
|
+
generation_config: Optional[dict] = None,
|
|
134
|
+
) -> Any:
|
|
93
135
|
model_identifier = _get_configured_model_identifier(self.MODEL_ENV_VAR, self.DEFAULT_MODEL)
|
|
94
136
|
logger.info(f"generate_speech executing with configured model '{model_identifier}'.")
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
137
|
+
if self._client is None:
|
|
138
|
+
self._client = audio_client_factory.create_audio_client(model_identifier=model_identifier)
|
|
139
|
+
|
|
140
|
+
response = await self._client.generate_speech(prompt=prompt, generation_config=generation_config)
|
|
141
|
+
|
|
142
|
+
if not response.audio_urls:
|
|
143
|
+
raise ValueError("Speech generation failed to return any audio file paths.")
|
|
144
|
+
|
|
145
|
+
first_url = response.audio_urls[0]
|
|
146
|
+
|
|
147
|
+
if not output_file_path:
|
|
148
|
+
raise ValueError("output_file_path is required but was not provided.")
|
|
149
|
+
|
|
150
|
+
# Save to File
|
|
151
|
+
resolved_path = resolve_safe_path(output_file_path, _get_workspace_root(context))
|
|
152
|
+
await download_file_from_url(first_url, resolved_path)
|
|
153
|
+
|
|
154
|
+
return {"file_path": str(resolved_path)}
|
|
155
|
+
|
|
156
|
+
async def cleanup(self) -> None:
|
|
157
|
+
if self._client:
|
|
158
|
+
await self._client.cleanup()
|
|
159
|
+
self._client = None
|
|
@@ -28,9 +28,10 @@ class DownloadMediaTool(BaseTool):
|
|
|
28
28
|
@classmethod
|
|
29
29
|
def get_description(cls) -> str:
|
|
30
30
|
return (
|
|
31
|
-
"
|
|
32
|
-
"
|
|
33
|
-
"
|
|
31
|
+
"Download a media file (image/PDF/audio/etc.) from a direct URL and save it locally. "
|
|
32
|
+
"The tool picks the correct file extension from the HTTP Content-Type header (or falls back to the URL). "
|
|
33
|
+
"Files are saved to the agent workspace if you give a relative folder (preferred), or to your default "
|
|
34
|
+
"Downloads directory when no folder is provided. Returns the absolute path of the saved file."
|
|
34
35
|
)
|
|
35
36
|
|
|
36
37
|
@classmethod
|
|
@@ -63,7 +64,20 @@ class DownloadMediaTool(BaseTool):
|
|
|
63
64
|
# Security: prevent path traversal attacks.
|
|
64
65
|
if ".." in folder:
|
|
65
66
|
raise ValueError("Security error: 'folder' path cannot contain '..'.")
|
|
66
|
-
|
|
67
|
+
if not os.path.isabs(folder):
|
|
68
|
+
workspace = context.workspace
|
|
69
|
+
# Prefer workspace base path when available to keep downloads inside the agent's sandbox.
|
|
70
|
+
if workspace and hasattr(workspace, "get_base_path") and callable(getattr(workspace, "get_base_path")):
|
|
71
|
+
base_path = os.path.abspath(workspace.get_base_path())
|
|
72
|
+
destination_dir = os.path.abspath(os.path.join(base_path, folder))
|
|
73
|
+
# Ensure resolved path stays within workspace
|
|
74
|
+
if os.path.commonpath([base_path]) != os.path.commonpath([base_path, destination_dir]):
|
|
75
|
+
raise ValueError(f"Security error: 'folder' resolves outside workspace: {destination_dir}")
|
|
76
|
+
else:
|
|
77
|
+
# Fallback: resolve relative folder under the default download directory
|
|
78
|
+
destination_dir = os.path.abspath(os.path.join(get_default_download_folder(), folder))
|
|
79
|
+
else:
|
|
80
|
+
destination_dir = os.path.abspath(folder)
|
|
67
81
|
else:
|
|
68
82
|
destination_dir = get_default_download_folder()
|
|
69
83
|
|