autobyteus 1.2.1__py3-none-any.whl → 1.3.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- autobyteus/agent/agent.py +15 -5
- autobyteus/agent/bootstrap_steps/__init__.py +3 -3
- autobyteus/agent/bootstrap_steps/agent_bootstrapper.py +5 -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/working_context_snapshot_restore_step.py +38 -0
- autobyteus/agent/bootstrap_steps/workspace_context_initialization_step.py +2 -4
- autobyteus/agent/context/agent_config.py +47 -20
- autobyteus/agent/context/agent_context.py +23 -18
- autobyteus/agent/context/agent_runtime_state.py +21 -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 +83 -6
- 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 +44 -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 +82 -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 +35 -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 +205 -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/path_resolver.py +27 -0
- autobyteus/memory/policies/__init__.py +5 -0
- autobyteus/memory/policies/compaction_policy.py +16 -0
- autobyteus/memory/restore/__init__.py +1 -0
- autobyteus/memory/restore/working_context_snapshot_bootstrapper.py +61 -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 +9 -0
- autobyteus/memory/store/base_store.py +14 -0
- autobyteus/memory/store/file_store.py +98 -0
- autobyteus/memory/store/working_context_snapshot_store.py +28 -0
- autobyteus/memory/tool_interaction_builder.py +46 -0
- autobyteus/memory/turn_tracker.py +9 -0
- autobyteus/memory/working_context_snapshot.py +69 -0
- autobyteus/memory/working_context_snapshot_serializer.py +135 -0
- autobyteus/multimedia/audio/api/autobyteus_audio_client.py +19 -5
- autobyteus/multimedia/audio/api/gemini_audio_client.py +109 -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 +39 -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 +64 -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.3.0.dist-info/METADATA +293 -0
- autobyteus-1.3.0.dist-info/RECORD +606 -0
- {autobyteus-1.2.1.dist-info → autobyteus-1.3.0.dist-info}/WHEEL +1 -1
- {autobyteus-1.2.1.dist-info → autobyteus-1.3.0.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.3.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -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
|
|