autobyteus 1.2.0__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 +23 -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 +74 -60
- 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/message/send_message_to.py +5 -4
- 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 -178
- 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 -198
- autobyteus/agent/streaming/stream_events.py +3 -128
- 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 +5 -6
- autobyteus/agent_team/bootstrap_steps/team_context_initialization_step.py +15 -15
- 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 +11 -8
- 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 +10 -10
- autobyteus/agent_team/streaming/agent_team_stream_event_payloads.py +7 -7
- autobyteus/agent_team/streaming/agent_team_stream_events.py +11 -11
- 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/activation_policy.py +1 -1
- autobyteus/agent_team/task_notification/system_event_driven_agent_task_notifier.py +22 -22
- autobyteus/agent_team/task_notification/task_notification_mode.py +20 -1
- autobyteus/agent_team/utils/wait_for_idle.py +4 -4
- autobyteus/cli/agent_cli.py +18 -10
- autobyteus/cli/agent_team_tui/app.py +18 -15
- autobyteus/cli/agent_team_tui/state.py +21 -23
- autobyteus/cli/agent_team_tui/widgets/agent_list_sidebar.py +15 -15
- autobyteus/cli/agent_team_tui/widgets/focus_pane.py +146 -39
- autobyteus/cli/agent_team_tui/widgets/renderables.py +1 -1
- autobyteus/cli/agent_team_tui/widgets/shared.py +26 -26
- autobyteus/cli/agent_team_tui/widgets/{task_board_panel.py → task_plan_panel.py} +5 -5
- 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 +15 -21
- 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/__init__.py +3 -2
- autobyteus/multimedia/audio/api/autobyteus_audio_client.py +19 -5
- autobyteus/multimedia/audio/api/gemini_audio_client.py +108 -16
- autobyteus/multimedia/audio/api/openai_audio_client.py +112 -0
- autobyteus/multimedia/audio/audio_client_factory.py +84 -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/__init__.py +43 -20
- autobyteus/task_management/{base_task_board.py → base_task_plan.py} +16 -13
- autobyteus/task_management/converters/__init__.py +2 -2
- autobyteus/task_management/converters/{task_board_converter.py → task_plan_converter.py} +13 -13
- autobyteus/task_management/events.py +7 -7
- autobyteus/task_management/{in_memory_task_board.py → in_memory_task_plan.py} +34 -22
- autobyteus/task_management/schemas/__init__.py +3 -0
- autobyteus/task_management/schemas/task_status_report.py +2 -2
- autobyteus/task_management/schemas/todo_definition.py +15 -0
- autobyteus/task_management/todo.py +29 -0
- autobyteus/task_management/todo_list.py +75 -0
- autobyteus/task_management/tools/__init__.py +24 -8
- autobyteus/task_management/tools/task_tools/__init__.py +19 -0
- autobyteus/task_management/tools/{assign_task_to.py → task_tools/assign_task_to.py} +18 -18
- autobyteus/task_management/tools/{publish_task.py → task_tools/create_task.py} +16 -18
- autobyteus/task_management/tools/{publish_tasks.py → task_tools/create_tasks.py} +19 -19
- autobyteus/task_management/tools/{get_my_tasks.py → task_tools/get_my_tasks.py} +15 -15
- autobyteus/task_management/tools/{get_task_board_status.py → task_tools/get_task_plan_status.py} +16 -16
- autobyteus/task_management/tools/{update_task_status.py → task_tools/update_task_status.py} +16 -16
- autobyteus/task_management/tools/todo_tools/__init__.py +18 -0
- autobyteus/task_management/tools/todo_tools/add_todo.py +78 -0
- autobyteus/task_management/tools/todo_tools/create_todo_list.py +79 -0
- autobyteus/task_management/tools/todo_tools/get_todo_list.py +55 -0
- autobyteus/task_management/tools/todo_tools/update_todo_status.py +85 -0
- autobyteus/tools/__init__.py +43 -52
- autobyteus/tools/base_tool.py +7 -0
- autobyteus/tools/file/__init__.py +9 -0
- autobyteus/tools/file/patch_file.py +149 -0
- autobyteus/tools/file/{file_reader.py → read_file.py} +38 -7
- autobyteus/tools/file/{file_writer.py → write_file.py} +7 -4
- autobyteus/tools/functional_tool.py +53 -14
- 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/tool.py +3 -3
- autobyteus/tools/mcp/tool_registrar.py +5 -2
- autobyteus/tools/mcp/types.py +61 -0
- autobyteus/tools/multimedia/__init__.py +2 -1
- autobyteus/tools/multimedia/audio_tools.py +72 -19
- autobyteus/tools/{download_media_tool.py → multimedia/download_media_tool.py} +21 -7
- autobyteus/tools/multimedia/image_tools.py +248 -64
- autobyteus/tools/multimedia/media_reader_tool.py +1 -1
- 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 +108 -14
- autobyteus/tools/registry/tool_registry.py +29 -0
- autobyteus/tools/search/__init__.py +17 -0
- autobyteus/tools/search/base_strategy.py +35 -0
- autobyteus/tools/search/client.py +24 -0
- autobyteus/tools/search/factory.py +81 -0
- autobyteus/tools/search/google_cse_strategy.py +68 -0
- autobyteus/tools/search/providers.py +10 -0
- autobyteus/tools/search/serpapi_strategy.py +65 -0
- autobyteus/tools/search/serper_strategy.py +87 -0
- autobyteus/tools/search_tool.py +83 -0
- 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/tool_meta.py +4 -24
- 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 +4 -11
- 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.0.dist-info → autobyteus-1.2.3.dist-info}/WHEEL +1 -1
- {autobyteus-1.2.0.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/person/examples/sample_persons.py +0 -14
- autobyteus/person/examples/sample_roles.py +0 -14
- autobyteus/person/person.py +0 -29
- autobyteus/person/role.py +0 -14
- 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/__init__.py +0 -0
- 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/__init__.py +0 -0
- 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 -80
- autobyteus/tools/browser/standalone/web_page_pdf_generator.py +0 -97
- autobyteus/tools/browser/standalone/webpage_image_downloader.py +0 -165
- autobyteus/tools/browser/standalone/webpage_reader.py +0 -101
- autobyteus/tools/browser/standalone/webpage_screenshot_taker.py +0 -101
- autobyteus/tools/file/file_editor.py +0 -200
- autobyteus/tools/google_search.py +0 -149
- autobyteus/tools/timer.py +0 -171
- 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.0.dist-info/METADATA +0 -205
- autobyteus-1.2.0.dist-info/RECORD +0 -496
- examples/__init__.py +0 -1
- examples/agent_team/__init__.py +0 -1
- examples/discover_phase_transitions.py +0 -104
- 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/{person → skills}/__init__.py +0 -0
- /autobyteus/{person/examples → tools/skill}/__init__.py +0 -0
- {autobyteus-1.2.0.dist-info → autobyteus-1.2.3.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,259 +0,0 @@
|
|
|
1
|
-
# file: autobyteus/autobyteus/rpc/server/method_handlers.py
|
|
2
|
-
import logging
|
|
3
|
-
from typing import Optional, Dict, Any, List, Callable, Awaitable, Tuple
|
|
4
|
-
|
|
5
|
-
from autobyteus.rpc.protocol import ProtocolMessage, MessageType, RequestType, ResponseType, ErrorCode
|
|
6
|
-
from autobyteus.rpc.server.base_method_handler import BaseMethodHandler
|
|
7
|
-
from autobyteus.agent.agent import Agent
|
|
8
|
-
from autobyteus.agent.message.agent_input_user_message import AgentInputUserMessage
|
|
9
|
-
from autobyteus.agent.message.inter_agent_message import InterAgentMessage
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
logger = logging.getLogger(__name__)
|
|
13
|
-
|
|
14
|
-
class DiscoverCapabilitiesHandler(BaseMethodHandler):
|
|
15
|
-
"""
|
|
16
|
-
Handles the 'discover_capabilities' RPC method.
|
|
17
|
-
Responds with the agent's ID, definition details, supported methods, and status,
|
|
18
|
-
obtained via the Agent's public interface/context.
|
|
19
|
-
"""
|
|
20
|
-
async def handle(self,
|
|
21
|
-
request_id: Optional[str],
|
|
22
|
-
params: Optional[Dict[str, Any]],
|
|
23
|
-
agent: Agent) -> ProtocolMessage:
|
|
24
|
-
logger.debug(f"DiscoverCapabilitiesHandler: Handling request_id '{request_id}' for agent '{agent.agent_id}'.")
|
|
25
|
-
try:
|
|
26
|
-
# Define capabilities, including the new stream download initiation method
|
|
27
|
-
capabilities = {
|
|
28
|
-
"post_user_message": {
|
|
29
|
-
"description": "Posts a message from an external user to the agent.",
|
|
30
|
-
"params": {"agent_input_user_message": "dict (serialized AgentInputUserMessage)"}
|
|
31
|
-
},
|
|
32
|
-
"post_inter_agent_message": {
|
|
33
|
-
"description": "Posts a message from another agent.",
|
|
34
|
-
"params": {"inter_agent_message": "dict (serialized InterAgentMessage)"}
|
|
35
|
-
},
|
|
36
|
-
"post_tool_execution_approval": {
|
|
37
|
-
"description": "Provides approval or denial for a pending tool execution.",
|
|
38
|
-
"params": {"tool_invocation_id": "str", "is_approved": "bool", "reason": "Optional[str]"}
|
|
39
|
-
},
|
|
40
|
-
"get_status": {
|
|
41
|
-
"description": "Gets the current operational status of the agent.",
|
|
42
|
-
"params": None
|
|
43
|
-
},
|
|
44
|
-
# No specific invoke method for REQUEST_STREAM_DOWNLOAD, it's a top-level RequestType handled by its own handler.
|
|
45
|
-
# However, if it were invoked via InvokeMethod, it would be listed here.
|
|
46
|
-
# For clarity, methods invokable via InvokeMethodHandler are listed.
|
|
47
|
-
# REQUEST_STREAM_DOWNLOAD will be handled by InitiateStreamDownloadHandler directly.
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
if not agent.context:
|
|
51
|
-
logger.error(f"Agent '{agent.agent_id}' context is None. Cannot discover capabilities.")
|
|
52
|
-
return ProtocolMessage.create_error_response(
|
|
53
|
-
id=request_id,
|
|
54
|
-
code=ErrorCode.SERVER_ERROR_CAPABILITY_DISCOVERY_FAILED,
|
|
55
|
-
message="Agent context not available."
|
|
56
|
-
)
|
|
57
|
-
|
|
58
|
-
# Explicitly list RequestTypes that this server (potentially) handles
|
|
59
|
-
# This part of discover_capabilities might need to be more dynamic or list top-level RPC methods
|
|
60
|
-
# rather than just methods invokable via InvokeMethodHandler.
|
|
61
|
-
# For now, assume InvokeMethodHandler is the primary way to call agent functions.
|
|
62
|
-
# The fact that REQUEST_STREAM_DOWNLOAD exists is a server capability.
|
|
63
|
-
supported_rpc_request_types = [rt.value for rt in RequestType]
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
result_data = {
|
|
67
|
-
"agent_id": agent.agent_id,
|
|
68
|
-
"agent_name": agent.context.definition.name,
|
|
69
|
-
"agent_role": agent.context.definition.role,
|
|
70
|
-
"agent_description": agent.context.definition.description,
|
|
71
|
-
"supported_rpc_request_types": supported_rpc_request_types, # Top-level RPC methods
|
|
72
|
-
"invokable_methods_details": capabilities, # Methods callable via 'invoke_method'
|
|
73
|
-
"status": agent.get_status().value,
|
|
74
|
-
"system_prompt_summary": agent.context.definition.system_prompt[:200] + "..." if len(agent.context.definition.system_prompt) > 200 else agent.context.definition.system_prompt,
|
|
75
|
-
"tool_names": agent.context.definition.tool_names,
|
|
76
|
-
}
|
|
77
|
-
return ProtocolMessage.create_response(
|
|
78
|
-
id=request_id,
|
|
79
|
-
result=result_data,
|
|
80
|
-
response_type=ResponseType.CAPABILITIES_RESPONSE
|
|
81
|
-
)
|
|
82
|
-
except Exception as e:
|
|
83
|
-
logger.error(f"Error in DiscoverCapabilitiesHandler for agent '{agent.agent_id}': {e}", exc_info=True)
|
|
84
|
-
return ProtocolMessage.create_error_response(
|
|
85
|
-
id=request_id,
|
|
86
|
-
code=ErrorCode.SERVER_ERROR_CAPABILITY_DISCOVERY_FAILED,
|
|
87
|
-
message=f"Failed to discover capabilities: {e}"
|
|
88
|
-
)
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
class InvokeMethodHandler(BaseMethodHandler):
|
|
92
|
-
"""
|
|
93
|
-
Handles the 'invoke_method' RPC method.
|
|
94
|
-
It dispatches to public methods of the Agent instance based on 'method_name'.
|
|
95
|
-
"""
|
|
96
|
-
|
|
97
|
-
def __init__(self):
|
|
98
|
-
# Map method names to Agent's public API methods (or wrappers around them)
|
|
99
|
-
self._method_map: Dict[str, Callable[[Optional[str], Dict[str, Any], Agent], Awaitable[ProtocolMessage]]] = {
|
|
100
|
-
"post_user_message": self._handle_post_user_message,
|
|
101
|
-
"post_inter_agent_message": self._handle_post_inter_agent_message,
|
|
102
|
-
"post_tool_execution_approval": self._handle_post_tool_execution_approval,
|
|
103
|
-
"get_status": self._handle_get_status,
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
async def handle(self,
|
|
107
|
-
request_id: Optional[str],
|
|
108
|
-
params: Optional[Dict[str, Any]],
|
|
109
|
-
agent: Agent) -> ProtocolMessage:
|
|
110
|
-
if not params or "method_name" not in params:
|
|
111
|
-
return ProtocolMessage.create_error_response(
|
|
112
|
-
id=request_id,
|
|
113
|
-
code=ErrorCode.INVALID_PARAMS,
|
|
114
|
-
message="'method_name' is required in params for invoke_method."
|
|
115
|
-
)
|
|
116
|
-
|
|
117
|
-
method_name = params["method_name"]
|
|
118
|
-
method_params = params.get("method_params", {})
|
|
119
|
-
|
|
120
|
-
logger.debug(f"InvokeMethodHandler: Handling method '{method_name}' for agent '{agent.agent_id}' with request_id '{request_id}'.")
|
|
121
|
-
|
|
122
|
-
handler_func = self._method_map.get(method_name)
|
|
123
|
-
if not handler_func:
|
|
124
|
-
return ProtocolMessage.create_error_response(
|
|
125
|
-
id=request_id,
|
|
126
|
-
code=ErrorCode.METHOD_NOT_FOUND,
|
|
127
|
-
message=f"Method '{method_name}' is not supported by this agent server for invocation via InvokeMethodHandler."
|
|
128
|
-
)
|
|
129
|
-
|
|
130
|
-
try:
|
|
131
|
-
return await handler_func(request_id, method_params, agent)
|
|
132
|
-
except Exception as e:
|
|
133
|
-
logger.error(f"Error invoking method '{method_name}' on agent '{agent.agent_id}': {e}", exc_info=True)
|
|
134
|
-
return ProtocolMessage.create_error_response(
|
|
135
|
-
id=request_id,
|
|
136
|
-
code=ErrorCode.SERVER_ERROR_AGENT_PROCESSING_FAILED,
|
|
137
|
-
message=f"Error processing method '{method_name}': {e}"
|
|
138
|
-
)
|
|
139
|
-
|
|
140
|
-
async def _handle_post_user_message(self, request_id: Optional[str], params: Dict[str, Any], agent: Agent) -> ProtocolMessage:
|
|
141
|
-
serialized_msg = params.get("agent_input_user_message")
|
|
142
|
-
if not isinstance(serialized_msg, dict):
|
|
143
|
-
return ProtocolMessage.create_error_response(request_id, ErrorCode.INVALID_PARAMS, "'agent_input_user_message' (dict) parameter is required.")
|
|
144
|
-
|
|
145
|
-
try:
|
|
146
|
-
user_message = AgentInputUserMessage.from_dict(serialized_msg)
|
|
147
|
-
except (ValueError, TypeError) as e:
|
|
148
|
-
return ProtocolMessage.create_error_response(request_id, ErrorCode.INVALID_PARAMS, f"Invalid 'agent_input_user_message' structure: {e}")
|
|
149
|
-
|
|
150
|
-
await agent.post_user_message(user_message)
|
|
151
|
-
logger.info(f"Agent '{agent.agent_id}' (via RPC): Called agent.post_user_message().")
|
|
152
|
-
return ProtocolMessage.create_response(request_id, {"status": "User message posted to agent"}, ResponseType.ACKNOWLEDGEMENT)
|
|
153
|
-
|
|
154
|
-
async def _handle_post_inter_agent_message(self, request_id: Optional[str], params: Dict[str, Any], agent: Agent) -> ProtocolMessage:
|
|
155
|
-
serialized_msg = params.get("inter_agent_message")
|
|
156
|
-
if not isinstance(serialized_msg, dict):
|
|
157
|
-
return ProtocolMessage.create_error_response(request_id, ErrorCode.INVALID_PARAMS, "'inter_agent_message' (dict) parameter is required.")
|
|
158
|
-
|
|
159
|
-
try:
|
|
160
|
-
msg_type_str = serialized_msg.get("message_type")
|
|
161
|
-
if not msg_type_str: raise ValueError("message_type missing")
|
|
162
|
-
|
|
163
|
-
inter_agent_msg = InterAgentMessage.create_with_dynamic_message_type(
|
|
164
|
-
recipient_role_name=serialized_msg.get("recipient_role_name"),
|
|
165
|
-
recipient_agent_id=serialized_msg.get("recipient_agent_id"),
|
|
166
|
-
content=serialized_msg.get("content"),
|
|
167
|
-
message_type=msg_type_str,
|
|
168
|
-
sender_agent_id=serialized_msg.get("sender_agent_id")
|
|
169
|
-
)
|
|
170
|
-
except (ValueError, TypeError) as e:
|
|
171
|
-
return ProtocolMessage.create_error_response(request_id, ErrorCode.INVALID_PARAMS, f"Invalid 'inter_agent_message' structure: {e}")
|
|
172
|
-
|
|
173
|
-
await agent.post_inter_agent_message(inter_agent_msg)
|
|
174
|
-
logger.info(f"Agent '{agent.agent_id}' (via RPC): Called agent.post_inter_agent_message() from '{inter_agent_msg.sender_agent_id}'.")
|
|
175
|
-
return ProtocolMessage.create_response(request_id, {"status": "Inter-agent message posted to agent"}, ResponseType.ACKNOWLEDGEMENT)
|
|
176
|
-
|
|
177
|
-
async def _handle_post_tool_execution_approval(self, request_id: Optional[str], params: Dict[str, Any], agent: Agent) -> ProtocolMessage:
|
|
178
|
-
tool_invocation_id = params.get("tool_invocation_id")
|
|
179
|
-
is_approved = params.get("is_approved")
|
|
180
|
-
reason = params.get("reason")
|
|
181
|
-
|
|
182
|
-
if not isinstance(tool_invocation_id, str) or not tool_invocation_id:
|
|
183
|
-
return ProtocolMessage.create_error_response(request_id, ErrorCode.INVALID_PARAMS, "'tool_invocation_id' (str) is required.")
|
|
184
|
-
if not isinstance(is_approved, bool):
|
|
185
|
-
return ProtocolMessage.create_error_response(request_id, ErrorCode.INVALID_PARAMS, "'is_approved' (bool) is required.")
|
|
186
|
-
if reason is not None and not isinstance(reason, str):
|
|
187
|
-
return ProtocolMessage.create_error_response(request_id, ErrorCode.INVALID_PARAMS, "'reason' must be a string if provided.")
|
|
188
|
-
|
|
189
|
-
await agent.post_tool_execution_approval(tool_invocation_id, is_approved, reason)
|
|
190
|
-
status_str = "approved" if is_approved else "denied"
|
|
191
|
-
logger.info(f"Agent '{agent.agent_id}' (via RPC): Called agent.post_tool_execution_approval() for id '{tool_invocation_id}' ({status_str}).")
|
|
192
|
-
return ProtocolMessage.create_response(request_id, {"status": f"Tool approval ({status_str}) posted to agent"}, ResponseType.ACKNOWLEDGEMENT)
|
|
193
|
-
|
|
194
|
-
async def _handle_get_status(self, request_id: Optional[str], params: Dict[str, Any], agent: Agent) -> ProtocolMessage:
|
|
195
|
-
status_value = agent.get_status().value
|
|
196
|
-
logger.debug(f"Agent '{agent.agent_id}' (via RPC): Current status is '{status_value}'.")
|
|
197
|
-
return ProtocolMessage.create_response(request_id, {"status": status_value, "agent_id": agent.agent_id})
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
class InitiateStreamDownloadHandler(BaseMethodHandler):
|
|
201
|
-
"""
|
|
202
|
-
Handles the 'request_stream_download' RPC method.
|
|
203
|
-
Interacts with the agent to prepare a streamable resource and returns
|
|
204
|
-
information required by the client to download it, including a stream_id.
|
|
205
|
-
The SseServerHandler will augment this response with the full download URL.
|
|
206
|
-
"""
|
|
207
|
-
async def handle(self,
|
|
208
|
-
request_id: Optional[str],
|
|
209
|
-
params: Optional[Dict[str, Any]],
|
|
210
|
-
agent: Agent) -> ProtocolMessage:
|
|
211
|
-
logger.debug(f"InitiateStreamDownloadHandler: Handling request_id '{request_id}' for agent '{agent.agent_id}' with params: {params}.")
|
|
212
|
-
if not params:
|
|
213
|
-
return ProtocolMessage.create_error_response(
|
|
214
|
-
id=request_id,
|
|
215
|
-
code=ErrorCode.INVALID_PARAMS,
|
|
216
|
-
message="Parameters are required for requesting a stream download (e.g., resource identifier)."
|
|
217
|
-
)
|
|
218
|
-
|
|
219
|
-
try:
|
|
220
|
-
# Agent must implement `prepare_resource_for_streaming`
|
|
221
|
-
# This method is conceptual; actual signature might vary.
|
|
222
|
-
# It should return (stream_id: str, metadata: Dict[str, Any])
|
|
223
|
-
# The agent becomes responsible for managing the lifecycle of this stream_id
|
|
224
|
-
# and providing data when get_stream_data(stream_id) is called by SseServerHandler.
|
|
225
|
-
if not hasattr(agent, "prepare_resource_for_streaming"):
|
|
226
|
-
logger.error(f"Agent '{agent.agent_id}' does not support 'prepare_resource_for_streaming'.")
|
|
227
|
-
return ProtocolMessage.create_error_response(
|
|
228
|
-
id=request_id,
|
|
229
|
-
code=ErrorCode.METHOD_NOT_FOUND,
|
|
230
|
-
message=f"Agent '{agent.agent_id}' cannot prepare streamable resources."
|
|
231
|
-
)
|
|
232
|
-
|
|
233
|
-
# Type hint for clarity if agent method is known
|
|
234
|
-
# stream_id, metadata = await agent.prepare_resource_for_streaming(params)
|
|
235
|
-
stream_preparation_result: Tuple[str, Dict[str, Any]] = await agent.prepare_resource_for_streaming(params)
|
|
236
|
-
stream_id, metadata = stream_preparation_result
|
|
237
|
-
|
|
238
|
-
logger.info(f"Agent '{agent.agent_id}' prepared stream_id '{stream_id}' for request_id '{request_id}'.")
|
|
239
|
-
|
|
240
|
-
# The SseServerHandler will add the 'download_url' to this result later
|
|
241
|
-
# agent_id_on_server is also added by SseServerHandler for constructing the URL
|
|
242
|
-
result_payload = {
|
|
243
|
-
"stream_id": stream_id,
|
|
244
|
-
"metadata": metadata,
|
|
245
|
-
# "agent_id_on_server": agent.agent_id # Or the server_key if different. SseServerHandler knows this.
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
return ProtocolMessage.create_response(
|
|
249
|
-
id=request_id,
|
|
250
|
-
result=result_payload,
|
|
251
|
-
response_type=ResponseType.STREAM_DOWNLOAD_READY
|
|
252
|
-
)
|
|
253
|
-
except Exception as e:
|
|
254
|
-
logger.error(f"Error in InitiateStreamDownloadHandler for agent '{agent.agent_id}': {e}", exc_info=True)
|
|
255
|
-
return ProtocolMessage.create_error_response(
|
|
256
|
-
id=request_id,
|
|
257
|
-
code=ErrorCode.SERVER_ERROR_STREAM_PREPARATION_FAILED,
|
|
258
|
-
message=f"Agent failed to prepare stream: {e}"
|
|
259
|
-
)
|
|
@@ -1,182 +0,0 @@
|
|
|
1
|
-
# file: autobyteus/autobyteus/rpc/server/sse_server_handler.py
|
|
2
|
-
import asyncio
|
|
3
|
-
import logging
|
|
4
|
-
import json
|
|
5
|
-
from typing import Dict, Optional, Union, Set, cast, AsyncIterator
|
|
6
|
-
from weakref import WeakSet
|
|
7
|
-
|
|
8
|
-
from aiohttp import web
|
|
9
|
-
from aiohttp_sse import sse_response
|
|
10
|
-
|
|
11
|
-
from autobyteus.agent.agent import Agent
|
|
12
|
-
# MODIFIED: Import AgentEventStream (remains the same, but it's now the sole class)
|
|
13
|
-
from autobyteus.agent.streaming import AgentEventStream
|
|
14
|
-
from autobyteus.agent.streaming.stream_events import StreamEvent, StreamEventType
|
|
15
|
-
from autobyteus.rpc.protocol import ProtocolMessage, MessageType, ErrorCode, RequestType, ResponseType, EventType as RPCEventType
|
|
16
|
-
from autobyteus.rpc.server.base_method_handler import BaseMethodHandler
|
|
17
|
-
from autobyteus.rpc.config import AgentServerConfig
|
|
18
|
-
|
|
19
|
-
logger = logging.getLogger(__name__)
|
|
20
|
-
|
|
21
|
-
DEFAULT_STREAM_CHUNK_SIZE = 8192
|
|
22
|
-
|
|
23
|
-
class SseServerHandler:
|
|
24
|
-
def __init__(self, agents: Dict[str, Agent], method_handlers: Dict[Union[RequestType, str], BaseMethodHandler]):
|
|
25
|
-
self._agents: Dict[str, Agent] = agents
|
|
26
|
-
self._method_handlers = method_handlers
|
|
27
|
-
self._app = web.Application()
|
|
28
|
-
self._runner: Optional[web.AppRunner] = None
|
|
29
|
-
self._site: Optional[web.TCPSite] = None
|
|
30
|
-
self._active_sse_forwarding_tasks: Dict[web.StreamResponse, asyncio.Task] = {}
|
|
31
|
-
logger.info(f"SseServerHandler initialized to serve agents: {list(self._agents.keys())}.")
|
|
32
|
-
|
|
33
|
-
def _setup_routes(self, config: AgentServerConfig):
|
|
34
|
-
# ... (method body remains the same) ...
|
|
35
|
-
rpc_request_path = config.sse_request_endpoint
|
|
36
|
-
base_events_path = config.sse_events_endpoint.rstrip('/')
|
|
37
|
-
full_events_path = f"{base_events_path}/{{agent_id_on_server}}"
|
|
38
|
-
stream_download_prefix = config.sse_stream_download_path_prefix.rstrip('/')
|
|
39
|
-
full_stream_download_path = f"{stream_download_prefix}/{{agent_id_on_server}}/{{stream_id}}"
|
|
40
|
-
self._app.router.add_post(rpc_request_path, self.handle_rpc_request)
|
|
41
|
-
self._app.router.add_get(full_events_path, self.handle_sse_events_subscription)
|
|
42
|
-
self._app.router.add_get(full_stream_download_path, self.handle_http_stream_download)
|
|
43
|
-
logger.info(f"SseServerHandler routes: POST {rpc_request_path} (RPC), GET {full_events_path} (SSE per agent), GET {full_stream_download_path} (HTTP Stream Download).")
|
|
44
|
-
|
|
45
|
-
async def handle_rpc_request(self, http_request: web.Request) -> web.Response:
|
|
46
|
-
# ... (method body remains the same) ...
|
|
47
|
-
request_id: Optional[str] = None; raw_body_str: str = ""; target_agent_id: Optional[str] = None
|
|
48
|
-
try:
|
|
49
|
-
raw_body = await http_request.read(); raw_body_str = raw_body.decode()
|
|
50
|
-
try: parsed_json_early = json.loads(raw_body_str); request_id = parsed_json_early.get("id")
|
|
51
|
-
except json.JSONDecodeError: pass
|
|
52
|
-
request_message = ProtocolMessage.from_json_str(raw_body_str); request_id = request_message.id
|
|
53
|
-
if request_message.type != MessageType.REQUEST or not request_message.method:
|
|
54
|
-
return self._create_json_error_response(request_id, ErrorCode.INVALID_REQUEST, "Must be REQUEST with method.", 400)
|
|
55
|
-
if request_message.params and "target_agent_id" in request_message.params:
|
|
56
|
-
target_agent_id = str(request_message.params["target_agent_id"])
|
|
57
|
-
if not target_agent_id: return self._create_json_error_response(request_id, ErrorCode.INVALID_PARAMS, "'target_agent_id' missing in request params.", 400)
|
|
58
|
-
target_agent = self._agents.get(target_agent_id)
|
|
59
|
-
if not target_agent: return self._create_json_error_response(request_id, ErrorCode.METHOD_NOT_FOUND, f"Agent with id '{target_agent_id}' not found.", 404)
|
|
60
|
-
actual_handler_params = request_message.params
|
|
61
|
-
handler = self._method_handlers.get(request_message.method)
|
|
62
|
-
if not handler: return self._create_json_error_response(request_id, ErrorCode.METHOD_NOT_FOUND, f"RPC Method '{request_message.method}' not found.", 404)
|
|
63
|
-
logger.debug(f"SseServerHandler dispatching RPC '{request_message.method}' (ReqID: {request_id}) to '{handler.__class__.__name__}' for agent '{target_agent_id}'.")
|
|
64
|
-
response_proto = await handler.handle(request_id, actual_handler_params, target_agent)
|
|
65
|
-
if response_proto.response_type == ResponseType.STREAM_DOWNLOAD_READY and response_proto.result and "stream_id" in response_proto.result and target_agent_id:
|
|
66
|
-
server_config: AgentServerConfig = http_request.app['agent_server_config']
|
|
67
|
-
full_url_prefix = server_config.get_sse_full_stream_download_url_prefix_for_agent(target_agent_id)
|
|
68
|
-
if full_url_prefix:
|
|
69
|
-
stream_id = response_proto.result["stream_id"]; download_url = f"{full_url_prefix.rstrip('/')}/{stream_id}"
|
|
70
|
-
response_proto.result["download_url"] = download_url; logger.info(f"Constructed download URL for stream_id '{stream_id}': {download_url}")
|
|
71
|
-
else: logger.error(f"Could not construct download_url for stream_id '{response_proto.result['stream_id']}'.")
|
|
72
|
-
http_status = 200
|
|
73
|
-
if response_proto.type == MessageType.ERROR and response_proto.error:
|
|
74
|
-
if response_proto.error.code == ErrorCode.METHOD_NOT_FOUND.value: http_status = 404
|
|
75
|
-
elif response_proto.error.code in [ErrorCode.INVALID_REQUEST.value, ErrorCode.INVALID_PARAMS.value, ErrorCode.PARSE_ERROR.value]: http_status = 400
|
|
76
|
-
else: http_status = 500
|
|
77
|
-
return web.json_response(response_proto.model_dump(exclude_none=True), status=http_status)
|
|
78
|
-
except json.JSONDecodeError as e: logger.error(f"Sse JSONDecodeError: {e}. Body: '{raw_body_str[:200]}'"); return self._create_json_error_response(request_id, ErrorCode.PARSE_ERROR, f"JSON parse error: {e}", 400)
|
|
79
|
-
except ValueError as e: logger.error(f"Sse Protocol validation error: {e}. Body: '{raw_body_str[:200]}'"); return self._create_json_error_response(request_id, ErrorCode.INVALID_REQUEST, f"Invalid request: {e}", 400)
|
|
80
|
-
except Exception as e: logger.error(f"Sse unexpected error: {e}", exc_info=True); return self._create_json_error_response(request_id, ErrorCode.INTERNAL_ERROR, f"Server error: {e}", 500)
|
|
81
|
-
|
|
82
|
-
def _create_json_error_response(self, req_id: Optional[str], code: ErrorCode, msg: str, http_status: int) -> web.Response:
|
|
83
|
-
# ... (method body remains the same) ...
|
|
84
|
-
err_proto = ProtocolMessage.create_error_response(req_id, code, msg)
|
|
85
|
-
return web.json_response(err_proto.model_dump(exclude_none=True), status=http_status)
|
|
86
|
-
|
|
87
|
-
async def handle_sse_events_subscription(self, http_request: web.Request) -> web.StreamResponse:
|
|
88
|
-
# ... (method body remains the same, it instantiates AgentEventStream and calls all_events()) ...
|
|
89
|
-
agent_id_on_server = http_request.match_info.get("agent_id_on_server")
|
|
90
|
-
if not agent_id_on_server: raise web.HTTPBadRequest(text="'agent_id_on_server' path param required.")
|
|
91
|
-
target_agent = self._agents.get(agent_id_on_server)
|
|
92
|
-
if not target_agent: raise web.HTTPNotFound(text=f"Agent '{agent_id_on_server}' not found.")
|
|
93
|
-
client_addr = http_request.remote
|
|
94
|
-
logger.info(f"SSE client {client_addr} subscribing to events for agent '{target_agent.agent_id}' (server key: '{agent_id_on_server}').")
|
|
95
|
-
sse_resp = web.StreamResponse(status=200, reason='OK', headers={'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache', 'Connection': 'keep-alive'})
|
|
96
|
-
await sse_resp.prepare(http_request)
|
|
97
|
-
forwarding_task = asyncio.create_task(self._stream_agent_events_to_client(sse_resp, target_agent, agent_id_on_server), name=f"sse_fwd_{target_agent.agent_id}_{client_addr}")
|
|
98
|
-
self._active_sse_forwarding_tasks[sse_resp] = forwarding_task
|
|
99
|
-
try: await forwarding_task
|
|
100
|
-
except asyncio.CancelledError: logger.info(f"SSE task for agent '{target_agent.agent_id}' to {client_addr} cancelled.")
|
|
101
|
-
except Exception as e: logger.error(f"Error in SSE streaming for agent '{target_agent.agent_id}' to {client_addr}: {e}", exc_info=True)
|
|
102
|
-
finally: logger.info(f"SSE client {client_addr} for agent '{target_agent.agent_id}' disconnected."); self._active_sse_forwarding_tasks.pop(sse_resp, None)
|
|
103
|
-
return sse_resp
|
|
104
|
-
|
|
105
|
-
async def _stream_agent_events_to_client(self, sse_client_resp: web.StreamResponse, agent: Agent, agent_id_on_server: str):
|
|
106
|
-
# Instantiates AgentEventStream
|
|
107
|
-
event_stream_provider = AgentEventStream(agent)
|
|
108
|
-
logger.debug(f"SseServerHandler: Streaming events from agent '{agent.agent_id}' (key: {agent_id_on_server}) via AgentEventStream.all_events().")
|
|
109
|
-
try:
|
|
110
|
-
# Calls .all_events() method
|
|
111
|
-
async for agent_event_obj in event_stream_provider.all_events():
|
|
112
|
-
if sse_client_resp.closed: break
|
|
113
|
-
rpc_event_type: RPCEventType
|
|
114
|
-
if agent_event_obj.event_type == StreamEventType.ASSISTANT_CHUNK: rpc_event_type = RPCEventType.AGENT_OUTPUT_CHUNK
|
|
115
|
-
elif agent_event_obj.event_type == StreamEventType.ASSISTANT_FINAL_MESSAGE: rpc_event_type = RPCEventType.AGENT_FINAL_MESSAGE
|
|
116
|
-
elif agent_event_obj.event_type == StreamEventType.TOOL_INTERACTION_LOG_ENTRY: rpc_event_type = RPCEventType.TOOL_LOG_ENTRY
|
|
117
|
-
elif agent_event_obj.event_type == StreamEventType.AGENT_STATUS_CHANGE: rpc_event_type = RPCEventType.AGENT_STATUS_UPDATE
|
|
118
|
-
elif agent_event_obj.event_type == StreamEventType.ERROR_EVENT: rpc_event_type = RPCEventType.AGENT_STATUS_UPDATE; logger.error(f"Unified stream error for agent '{agent.agent_id}': {agent_event_obj.data}")
|
|
119
|
-
else: rpc_event_type = RPCEventType.AGENT_STATUS_UPDATE; logger.warning(f"Unhandled StreamEventType '{agent_event_obj.event_type}' from agent '{agent.agent_id}'.")
|
|
120
|
-
payload_data = {"agent_id_on_server": agent_id_on_server, **agent_event_obj.data}
|
|
121
|
-
protocol_event_msg = ProtocolMessage.create_event(event_type=rpc_event_type, payload=payload_data)
|
|
122
|
-
try:
|
|
123
|
-
sse_event_str = f"event: {str(protocol_event_msg.event_type.value)}\ndata: {protocol_event_msg.to_json_str()}\n\n"
|
|
124
|
-
await sse_client_resp.write(sse_event_str.encode('utf-8'))
|
|
125
|
-
except ConnectionResetError: logger.info(f"SSE client reset for agent '{agent.agent_id}'."); break
|
|
126
|
-
except Exception as send_e: logger.error(f"Error sending SSE event for agent '{agent.agent_id}': {send_e}"); break
|
|
127
|
-
if not sse_client_resp.closed: await sse_client_resp.write_eof()
|
|
128
|
-
except asyncio.CancelledError: logger.info(f"Event streaming for agent '{agent.agent_id}' (key: {agent_id_on_server}) cancelled.")
|
|
129
|
-
except Exception as e: logger.error(f"Error in event streaming for agent '{agent.agent_id}' (key: {agent_id_on_server}): {e}", exc_info=True)
|
|
130
|
-
finally: logger.debug(f"Finished event streaming from agent '{agent.agent_id}' (key: {agent_id_on_server}).")
|
|
131
|
-
|
|
132
|
-
async def handle_http_stream_download(self, http_request: web.Request) -> web.StreamResponse:
|
|
133
|
-
# ... (method body remains the same) ...
|
|
134
|
-
agent_id_on_server = http_request.match_info.get("agent_id_on_server"); stream_id = http_request.match_info.get("stream_id")
|
|
135
|
-
if not agent_id_on_server or not stream_id: raise web.HTTPBadRequest(text="'agent_id_on_server' and 'stream_id' path params required.")
|
|
136
|
-
target_agent = self._agents.get(agent_id_on_server)
|
|
137
|
-
if not target_agent: raise web.HTTPNotFound(text=f"Agent '{agent_id_on_server}' not found.")
|
|
138
|
-
logger.info(f"HTTP stream download for agent '{target_agent.agent_id}' (key: '{agent_id_on_server}'), stream_id '{stream_id}'.")
|
|
139
|
-
if not hasattr(target_agent, "get_stream_data") or not hasattr(target_agent, "cleanup_stream_resource"):
|
|
140
|
-
raise web.HTTPNotImplemented(text=f"Agent '{target_agent.agent_id}' does not support stream retrieval.")
|
|
141
|
-
try:
|
|
142
|
-
get_stream_data_method = getattr(target_agent, "get_stream_data")
|
|
143
|
-
data_iterator: AsyncIterator[bytes] = await get_stream_data_method(stream_id)
|
|
144
|
-
response = web.StreamResponse(status=200, reason="OK", headers={"Content-Type": "application/octet-stream"})
|
|
145
|
-
await response.prepare(http_request)
|
|
146
|
-
async for chunk in data_iterator:
|
|
147
|
-
if not isinstance(chunk, bytes): logger.error(f"Agent '{target_agent.agent_id}' stream '{stream_id}' yielded non-bytes: {type(chunk)}."); break
|
|
148
|
-
await response.write(chunk); await asyncio.sleep(0)
|
|
149
|
-
await response.write_eof(); logger.info(f"Streamed data for agent '{target_agent.agent_id}', stream_id '{stream_id}'."); return response
|
|
150
|
-
except FileNotFoundError: logger.warning(f"Stream '{stream_id}' not found for agent '{target_agent.agent_id}'."); raise web.HTTPNotFound(text=f"Stream resource '{stream_id}' not found.")
|
|
151
|
-
except asyncio.CancelledError: logger.info(f"HTTP stream download for '{stream_id}' (agent '{target_agent.agent_id}') cancelled."); raise
|
|
152
|
-
except Exception as e: logger.error(f"Error during HTTP stream download for '{stream_id}' (agent '{target_agent.agent_id}'): {e}", exc_info=True); raise web.HTTPInternalServerError(text="Error serving stream data.")
|
|
153
|
-
finally:
|
|
154
|
-
try: cleanup_method = getattr(target_agent, "cleanup_stream_resource"); await cleanup_method(stream_id); logger.debug(f"Agent '{target_agent.agent_id}' cleaned up stream '{stream_id}'.")
|
|
155
|
-
except Exception as cleanup_e: logger.error(f"Error cleaning up stream '{stream_id}': {cleanup_e}", exc_info=True)
|
|
156
|
-
|
|
157
|
-
async def start_server(self, config: AgentServerConfig):
|
|
158
|
-
# ... (method body remains the same) ...
|
|
159
|
-
if not config.sse_base_url: raise ValueError("SSE base URL required.")
|
|
160
|
-
self._app['agent_server_config'] = config; self._setup_routes(config)
|
|
161
|
-
host = config.sse_base_url.host or "0.0.0.0"; port = config.sse_base_url.port or 80
|
|
162
|
-
self._runner = web.AppRunner(self._app); await self._runner.setup()
|
|
163
|
-
self._site = web.TCPSite(self._runner, host, port)
|
|
164
|
-
try: await self._site.start(); logger.info(f"SseServerHandler started for agents {list(self._agents.keys())} on http://{host}:{port}")
|
|
165
|
-
except OSError as e: logger.error(f"Failed to start SseServer on http://{host}:{port}: {e}", exc_info=True); await self.stop_server(); raise
|
|
166
|
-
except Exception as e: logger.error(f"Unexpected error starting SseServer: {e}", exc_info=True); await self.stop_server(); raise
|
|
167
|
-
|
|
168
|
-
async def stop_server(self):
|
|
169
|
-
# ... (method body remains the same) ...
|
|
170
|
-
logger.info(f"SseServerHandler for agents {list(self._agents.keys())} stopping...")
|
|
171
|
-
for task in list(self._active_sse_forwarding_tasks.values()):
|
|
172
|
-
if task and not task.done(): task.cancel()
|
|
173
|
-
if self._active_sse_forwarding_tasks: await asyncio.gather(*self._active_sse_forwarding_tasks.values(), return_exceptions=True)
|
|
174
|
-
self._active_sse_forwarding_tasks.clear()
|
|
175
|
-
if self._site: try: await self._site.stop()
|
|
176
|
-
except Exception as e: logger.error(f"Error stopping TCPSite: {e}", exc_info=True)
|
|
177
|
-
self._site = None
|
|
178
|
-
if self._runner: try: await self._runner.cleanup()
|
|
179
|
-
except Exception as e: logger.error(f"Error cleaning AppRunner: {e}", exc_info=True)
|
|
180
|
-
self._runner = None
|
|
181
|
-
logger.info(f"SseServerHandler for agents {list(self._agents.keys())} stopped.")
|
|
182
|
-
|
|
@@ -1,151 +0,0 @@
|
|
|
1
|
-
# file: autobyteus/autobyteus/rpc/server/stdio_server_handler.py
|
|
2
|
-
import asyncio
|
|
3
|
-
import sys
|
|
4
|
-
import json
|
|
5
|
-
import logging
|
|
6
|
-
from typing import Dict, Optional, Union
|
|
7
|
-
|
|
8
|
-
from autobyteus.rpc.protocol import ProtocolMessage, MessageType, ErrorCode, RequestType
|
|
9
|
-
from autobyteus.rpc.server.base_method_handler import BaseMethodHandler
|
|
10
|
-
from autobyteus.agent.agent import Agent # Changed from AgentRuntime
|
|
11
|
-
|
|
12
|
-
logger = logging.getLogger(__name__)
|
|
13
|
-
|
|
14
|
-
class StdioServerHandler:
|
|
15
|
-
"""
|
|
16
|
-
Handles RPC communication over stdio for an Agent Server.
|
|
17
|
-
Reads newline-delimited JSON ProtocolMessages from stdin, dispatches them
|
|
18
|
-
to appropriate method handlers (which operate on an Agent instance),
|
|
19
|
-
and writes responses to stdout.
|
|
20
|
-
"""
|
|
21
|
-
|
|
22
|
-
def __init__(self, agent: Agent, method_handlers: Dict[Union[RequestType, str], BaseMethodHandler]): # Changed runtime to agent
|
|
23
|
-
"""
|
|
24
|
-
Initializes the StdioServerHandler.
|
|
25
|
-
|
|
26
|
-
Args:
|
|
27
|
-
agent: The Agent instance this server handler is serving.
|
|
28
|
-
method_handlers: A dictionary mapping method names (RequestType or str)
|
|
29
|
-
to their handler instances.
|
|
30
|
-
"""
|
|
31
|
-
self._agent = agent # Changed from _runtime to _agent
|
|
32
|
-
self._method_handlers = method_handlers
|
|
33
|
-
self._running = False
|
|
34
|
-
logger.info(f"StdioServerHandler initialized for agent '{self._agent.agent_id}'.") # Use agent.agent_id
|
|
35
|
-
|
|
36
|
-
async def listen_and_dispatch(self) -> None:
|
|
37
|
-
"""
|
|
38
|
-
Starts listening on stdin for requests and dispatches them.
|
|
39
|
-
This method runs indefinitely until stdin is closed or an error occurs.
|
|
40
|
-
"""
|
|
41
|
-
self._running = True
|
|
42
|
-
logger.info(f"StdioServerHandler for agent '{self._agent.agent_id}' now listening on stdin.")
|
|
43
|
-
|
|
44
|
-
loop = asyncio.get_event_loop()
|
|
45
|
-
reader = asyncio.StreamReader(loop=loop)
|
|
46
|
-
protocol = asyncio.StreamReaderProtocol(reader, loop=loop)
|
|
47
|
-
await loop.connect_read_pipe(lambda: protocol, sys.stdin)
|
|
48
|
-
|
|
49
|
-
try:
|
|
50
|
-
while self._running:
|
|
51
|
-
line_bytes = await reader.readline()
|
|
52
|
-
if not line_bytes:
|
|
53
|
-
logger.info(f"StdioServerHandler for agent '{self._agent.agent_id}': stdin EOF reached. Shutting down.")
|
|
54
|
-
self._running = False
|
|
55
|
-
break
|
|
56
|
-
|
|
57
|
-
line_str = line_bytes.decode().strip()
|
|
58
|
-
if not line_str:
|
|
59
|
-
continue
|
|
60
|
-
|
|
61
|
-
request_id: Optional[str] = None
|
|
62
|
-
try:
|
|
63
|
-
try:
|
|
64
|
-
parsed_json = json.loads(line_str)
|
|
65
|
-
request_id = parsed_json.get("id")
|
|
66
|
-
except json.JSONDecodeError:
|
|
67
|
-
pass
|
|
68
|
-
|
|
69
|
-
message = ProtocolMessage.from_json_str(line_str)
|
|
70
|
-
request_id = message.id
|
|
71
|
-
|
|
72
|
-
if message.type == MessageType.REQUEST:
|
|
73
|
-
response_message = await self._process_request(message)
|
|
74
|
-
else:
|
|
75
|
-
logger.warning(f"StdioServerHandler received non-REQUEST message type: {message.type}. Ignoring.")
|
|
76
|
-
response_message = ProtocolMessage.create_error_response(
|
|
77
|
-
id=request_id,
|
|
78
|
-
code=ErrorCode.INVALID_REQUEST,
|
|
79
|
-
message="Server only accepts REQUEST messages via stdio."
|
|
80
|
-
)
|
|
81
|
-
|
|
82
|
-
await self._send_response(response_message)
|
|
83
|
-
|
|
84
|
-
except json.JSONDecodeError as e:
|
|
85
|
-
logger.error(f"StdioServerHandler: JSONDecodeError: {e}. Raw line: '{line_str[:200]}'")
|
|
86
|
-
err_response = ProtocolMessage.create_error_response(
|
|
87
|
-
id=request_id,
|
|
88
|
-
code=ErrorCode.PARSE_ERROR,
|
|
89
|
-
message=f"Failed to parse JSON request: {e}"
|
|
90
|
-
)
|
|
91
|
-
await self._send_response(err_response)
|
|
92
|
-
except ValueError as e:
|
|
93
|
-
logger.error(f"StdioServerHandler: ProtocolMessage validation error: {e}. Raw line: '{line_str[:200]}'")
|
|
94
|
-
err_response = ProtocolMessage.create_error_response(
|
|
95
|
-
id=request_id,
|
|
96
|
-
code=ErrorCode.INVALID_REQUEST,
|
|
97
|
-
message=f"Invalid request structure: {e}"
|
|
98
|
-
)
|
|
99
|
-
await self._send_response(err_response)
|
|
100
|
-
except Exception as e:
|
|
101
|
-
logger.error(f"StdioServerHandler: Unexpected error processing line: {e}. Raw line: '{line_str[:200]}'", exc_info=True)
|
|
102
|
-
err_response = ProtocolMessage.create_error_response(
|
|
103
|
-
id=request_id,
|
|
104
|
-
code=ErrorCode.INTERNAL_ERROR,
|
|
105
|
-
message=f"Internal server error: {e}"
|
|
106
|
-
)
|
|
107
|
-
await self._send_response(err_response)
|
|
108
|
-
|
|
109
|
-
except asyncio.CancelledError:
|
|
110
|
-
logger.info(f"StdioServerHandler for agent '{self._agent.agent_id}' listen_and_dispatch task cancelled.")
|
|
111
|
-
except Exception as e:
|
|
112
|
-
logger.error(f"StdioServerHandler for agent '{self._agent.agent_id}' fatal error in listen_and_dispatch: {e}", exc_info=True)
|
|
113
|
-
finally:
|
|
114
|
-
self._running = False
|
|
115
|
-
logger.info(f"StdioServerHandler for agent '{self._agent.agent_id}' stopped listening.")
|
|
116
|
-
|
|
117
|
-
async def _process_request(self, request_message: ProtocolMessage) -> ProtocolMessage:
|
|
118
|
-
if not request_message.method:
|
|
119
|
-
logger.warning(f"StdioServerHandler: Request message missing 'method'. ID: {request_message.id}")
|
|
120
|
-
return ProtocolMessage.create_error_response(
|
|
121
|
-
id=request_message.id,
|
|
122
|
-
code=ErrorCode.INVALID_REQUEST,
|
|
123
|
-
message="Request message must include a 'method'."
|
|
124
|
-
)
|
|
125
|
-
|
|
126
|
-
handler = self._method_handlers.get(request_message.method)
|
|
127
|
-
if not handler:
|
|
128
|
-
logger.warning(f"StdioServerHandler: No handler found for method '{request_message.method}'. ID: {request_message.id}")
|
|
129
|
-
return ProtocolMessage.create_error_response(
|
|
130
|
-
id=request_message.id,
|
|
131
|
-
code=ErrorCode.METHOD_NOT_FOUND,
|
|
132
|
-
message=f"Method '{request_message.method}' not found."
|
|
133
|
-
)
|
|
134
|
-
|
|
135
|
-
logger.debug(f"StdioServerHandler dispatching method '{request_message.method}' (ID: {request_message.id}) to {handler.__class__.__name__}.")
|
|
136
|
-
return await handler.handle(request_message.id, request_message.params, self._agent) # Pass self._agent
|
|
137
|
-
|
|
138
|
-
async def _send_response(self, response_message: ProtocolMessage) -> None:
|
|
139
|
-
try:
|
|
140
|
-
json_response = response_message.to_json_str()
|
|
141
|
-
loop = asyncio.get_event_loop()
|
|
142
|
-
await loop.run_in_executor(None, lambda: sys.stdout.write(json_response + '\n'))
|
|
143
|
-
await loop.run_in_executor(None, sys.stdout.flush)
|
|
144
|
-
logger.debug(f"StdioServerHandler sent response (ID: {response_message.id}, Type: {response_message.type}).")
|
|
145
|
-
except Exception as e:
|
|
146
|
-
logger.error(f"StdioServerHandler failed to send response: {e}", exc_info=True)
|
|
147
|
-
|
|
148
|
-
def stop(self):
|
|
149
|
-
logger.info(f"StdioServerHandler for agent '{self._agent.agent_id}' stop requested.")
|
|
150
|
-
self._running = False
|
|
151
|
-
|