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
autobyteus/tools/__init__.py
CHANGED
|
@@ -15,23 +15,35 @@ from .tool_config import ToolConfig # Configuration data object, primarily for c
|
|
|
15
15
|
from .tool_origin import ToolOrigin
|
|
16
16
|
from .tool_category import ToolCategory
|
|
17
17
|
|
|
18
|
+
# Tool Formatting Registration Support
|
|
19
|
+
# Tool Formatting Registration Support
|
|
20
|
+
from autobyteus.tools.usage.registries.tool_formatting_registry import ToolFormattingRegistry, register_tool_formatter
|
|
21
|
+
from autobyteus.tools.usage.registries.tool_formatter_pair import ToolFormatterPair
|
|
22
|
+
from autobyteus.tools.usage.formatters.base_formatter import BaseSchemaFormatter, BaseExampleFormatter
|
|
23
|
+
|
|
24
|
+
|
|
18
25
|
logger = logging.getLogger(__name__)
|
|
19
26
|
|
|
20
27
|
# --- Re-export specific tools for easier access ---
|
|
21
28
|
|
|
22
29
|
# Functional tools (decorated functions are now instances)
|
|
23
|
-
from .
|
|
24
|
-
from .file.
|
|
25
|
-
from .file.
|
|
26
|
-
from .
|
|
30
|
+
from .file.read_file import read_file
|
|
31
|
+
from .file.write_file import write_file
|
|
32
|
+
from .file.patch_file import patch_file
|
|
33
|
+
from .skill.load_skill import load_skill
|
|
34
|
+
|
|
35
|
+
# Terminal tools (PTY-based stateful terminal)
|
|
36
|
+
from .terminal.tools.run_bash import run_bash
|
|
37
|
+
from .terminal.tools.start_background_process import start_background_process
|
|
38
|
+
from .terminal.tools.get_process_output import get_process_output
|
|
39
|
+
from .terminal.tools.stop_background_process import stop_background_process
|
|
27
40
|
|
|
28
41
|
# General Class-based tools
|
|
29
42
|
try:
|
|
30
|
-
from .
|
|
43
|
+
from .search_tool import Search
|
|
31
44
|
except ModuleNotFoundError as import_err:
|
|
32
|
-
logger.warning("
|
|
33
|
-
|
|
34
|
-
from .timer import Timer
|
|
45
|
+
logger.warning("Search tool not available: %s", import_err)
|
|
46
|
+
Search = None
|
|
35
47
|
try:
|
|
36
48
|
from .multimedia.image_tools import GenerateImageTool, EditImageTool
|
|
37
49
|
except ModuleNotFoundError as import_err:
|
|
@@ -44,38 +56,19 @@ except ModuleNotFoundError as import_err:
|
|
|
44
56
|
logger.warning("Media reader tool not available: %s", import_err)
|
|
45
57
|
ReadMediaFile = None
|
|
46
58
|
try:
|
|
47
|
-
from .download_media_tool import DownloadMediaTool
|
|
59
|
+
from autobyteus.multimedia.download_media_tool import DownloadMediaTool
|
|
48
60
|
except ModuleNotFoundError as import_err:
|
|
49
61
|
logger.warning("Download media tool not available: %s", import_err)
|
|
50
62
|
DownloadMediaTool = None
|
|
51
63
|
|
|
52
|
-
#
|
|
53
|
-
try:
|
|
54
|
-
from .browser.standalone.navigate_to import NavigateTo as StandaloneNavigateTo # Alias to avoid name clash
|
|
55
|
-
from .browser.standalone.webpage_reader import WebPageReader as StandaloneWebPageReader # Alias
|
|
56
|
-
from .browser.standalone.webpage_screenshot_taker import WebPageScreenshotTaker as StandaloneWebPageScreenshotTaker # Alias
|
|
57
|
-
from .browser.standalone.webpage_image_downloader import WebPageImageDownloader
|
|
58
|
-
from .browser.standalone.web_page_pdf_generator import WebPagePDFGenerator
|
|
59
|
-
except ModuleNotFoundError as import_err:
|
|
60
|
-
logger.warning('Standalone browser tools not available: %s', import_err)
|
|
61
|
-
StandaloneNavigateTo = None
|
|
62
|
-
StandaloneWebPageReader = None
|
|
63
|
-
StandaloneWebPageScreenshotTaker = None
|
|
64
|
-
WebPageImageDownloader = None
|
|
65
|
-
WebPagePDFGenerator = None
|
|
66
|
-
|
|
67
|
-
# Session-Aware Browser tools
|
|
64
|
+
# Web tools
|
|
68
65
|
try:
|
|
69
|
-
from .
|
|
70
|
-
from .browser.session_aware.browser_session_aware_web_element_trigger import BrowserSessionAwareWebElementTrigger
|
|
71
|
-
from .browser.session_aware.browser_session_aware_webpage_reader import BrowserSessionAwareWebPageReader
|
|
72
|
-
from .browser.session_aware.browser_session_aware_webpage_screenshot_taker import BrowserSessionAwareWebPageScreenshotTaker
|
|
66
|
+
from .web.read_url_tool import ReadUrl
|
|
73
67
|
except ModuleNotFoundError as import_err:
|
|
74
|
-
logger.warning(
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
BrowserSessionAwareWebPageScreenshotTaker = None
|
|
68
|
+
logger.warning("ReadUrl tool not available: %s", import_err)
|
|
69
|
+
ReadUrl = None
|
|
70
|
+
|
|
71
|
+
|
|
79
72
|
|
|
80
73
|
|
|
81
74
|
__all__ = [
|
|
@@ -90,29 +83,27 @@ __all__ = [
|
|
|
90
83
|
"ToolCategory",
|
|
91
84
|
|
|
92
85
|
# Re-exported functional tool instances
|
|
93
|
-
"
|
|
94
|
-
"
|
|
95
|
-
"
|
|
96
|
-
"
|
|
86
|
+
"run_bash",
|
|
87
|
+
"start_background_process",
|
|
88
|
+
"get_process_output",
|
|
89
|
+
"stop_background_process",
|
|
90
|
+
"read_file",
|
|
91
|
+
"write_file",
|
|
92
|
+
"patch_file",
|
|
93
|
+
"load_skill",
|
|
97
94
|
|
|
98
95
|
# Re-exported general class-based tools
|
|
99
|
-
"
|
|
100
|
-
"Timer",
|
|
96
|
+
"Search",
|
|
101
97
|
"GenerateImageTool",
|
|
102
98
|
"EditImageTool",
|
|
103
99
|
"ReadMediaFile",
|
|
104
100
|
"DownloadMediaTool",
|
|
105
101
|
|
|
106
|
-
# Re-exported
|
|
107
|
-
"
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
"
|
|
111
|
-
"
|
|
112
|
-
|
|
113
|
-
# Re-exported Session-Aware Browser tools
|
|
114
|
-
"BrowserSessionAwareNavigateTo",
|
|
115
|
-
"BrowserSessionAwareWebElementTrigger",
|
|
116
|
-
"BrowserSessionAwareWebPageReader",
|
|
117
|
-
"BrowserSessionAwareWebPageScreenshotTaker",
|
|
102
|
+
# Re-exported Web tools
|
|
103
|
+
"ReadUrl",
|
|
104
|
+
|
|
105
|
+
# Tool Formatting
|
|
106
|
+
"register_tool_formatter",
|
|
107
|
+
"BaseSchemaFormatter",
|
|
108
|
+
"BaseExampleFormatter",
|
|
118
109
|
]
|
autobyteus/tools/base_tool.py
CHANGED
|
@@ -174,6 +174,13 @@ class BaseTool(ABC, EventEmitter, metaclass=ToolMeta):
|
|
|
174
174
|
async def _execute(self, context: 'AgentContext', **kwargs) -> Any:
|
|
175
175
|
raise NotImplementedError("Subclasses must implement the '_execute' method.")
|
|
176
176
|
|
|
177
|
+
async def cleanup(self) -> None:
|
|
178
|
+
"""
|
|
179
|
+
Lifecycle hook invoked during agent shutdown to release resources held by the tool.
|
|
180
|
+
Default implementation is a no-op.
|
|
181
|
+
"""
|
|
182
|
+
return None
|
|
183
|
+
|
|
177
184
|
@classmethod
|
|
178
185
|
def tool_usage(cls) -> str:
|
|
179
186
|
logger.warning("BaseTool.tool_usage() is deprecated. Tool usage is now generated by formatters.")
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import logging
|
|
3
|
+
from typing import TYPE_CHECKING, List
|
|
4
|
+
|
|
5
|
+
from pydantic import Field
|
|
6
|
+
|
|
7
|
+
from autobyteus.tools.functional_tool import tool
|
|
8
|
+
from autobyteus.tools.tool_category import ToolCategory
|
|
9
|
+
from autobyteus.utils.diff_utils import apply_unified_diff, PatchApplicationError
|
|
10
|
+
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
from autobyteus.agent.context import AgentContext
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _resolve_file_path(context: 'AgentContext', path: str) -> str:
|
|
18
|
+
"""Resolves an absolute path for the given input, using the agent workspace when needed."""
|
|
19
|
+
if os.path.isabs(path):
|
|
20
|
+
final_path = path
|
|
21
|
+
logger.debug("patch_file: provided path '%s' is absolute.", path)
|
|
22
|
+
else:
|
|
23
|
+
if not context.workspace:
|
|
24
|
+
error_msg = ("Relative path '%s' provided, but no workspace is configured for agent '%s'. "
|
|
25
|
+
"A workspace is required to resolve relative paths.")
|
|
26
|
+
logger.error(error_msg, path, context.agent_id)
|
|
27
|
+
raise ValueError(error_msg % (path, context.agent_id))
|
|
28
|
+
base_path = context.workspace.get_base_path()
|
|
29
|
+
if not base_path or not isinstance(base_path, str):
|
|
30
|
+
error_msg = ("Agent '%s' has a configured workspace, but it provided an invalid base path ('%s'). "
|
|
31
|
+
"Cannot resolve relative path '%s'.")
|
|
32
|
+
logger.error(error_msg, context.agent_id, base_path, path)
|
|
33
|
+
raise ValueError(error_msg % (context.agent_id, base_path, path))
|
|
34
|
+
final_path = os.path.join(base_path, path)
|
|
35
|
+
logger.debug("patch_file: resolved relative path '%s' against workspace base '%s' to '%s'.", path, base_path, final_path)
|
|
36
|
+
|
|
37
|
+
normalized_path = os.path.normpath(final_path)
|
|
38
|
+
logger.debug("patch_file: normalized path to '%s'.", normalized_path)
|
|
39
|
+
return normalized_path
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@tool(name="patch_file", category=ToolCategory.FILE_SYSTEM)
|
|
43
|
+
async def patch_file(
|
|
44
|
+
context: 'AgentContext',
|
|
45
|
+
path: str = Field(..., description="Path to the target file."),
|
|
46
|
+
patch: str = Field(
|
|
47
|
+
...,
|
|
48
|
+
description=(
|
|
49
|
+
"Unified diff hunks describing edits to apply. "
|
|
50
|
+
"Example:\n"
|
|
51
|
+
"--- a/sample.txt\n"
|
|
52
|
+
"+++ b/sample.txt\n"
|
|
53
|
+
"@@ -1,2 +1,2 @@\n"
|
|
54
|
+
"-old line\n"
|
|
55
|
+
"+new line\n"
|
|
56
|
+
" unchanged line"
|
|
57
|
+
),
|
|
58
|
+
),
|
|
59
|
+
) -> str:
|
|
60
|
+
"""Applies a unified diff patch to update a text file without overwriting unrelated content.
|
|
61
|
+
|
|
62
|
+
Args:
|
|
63
|
+
path: Path to the target file. Relative paths are resolved against the agent workspace when available.
|
|
64
|
+
patch: Unified diff patch describing the edits to apply.
|
|
65
|
+
|
|
66
|
+
Raises:
|
|
67
|
+
FileNotFoundError: If the file does not exist.
|
|
68
|
+
PatchApplicationError: If the patch content cannot be applied cleanly.
|
|
69
|
+
IOError: If file reading or writing fails.
|
|
70
|
+
"""
|
|
71
|
+
logger.debug("patch_file: requested patch for agent '%s' on path '%s'.", context.agent_id, path)
|
|
72
|
+
return_path = os.path.normpath(path)
|
|
73
|
+
|
|
74
|
+
# Detailed logging for debugging patch content
|
|
75
|
+
logger.info("patch_file: ===== PATCH ARGUMENT DEBUG START =====")
|
|
76
|
+
logger.info("patch_file: raw patch repr: %r", patch)
|
|
77
|
+
logger.info("patch_file: patch length: %d chars", len(patch) if patch else 0)
|
|
78
|
+
patch_lines = patch.splitlines(keepends=True) if patch else []
|
|
79
|
+
for i, line in enumerate(patch_lines, 1):
|
|
80
|
+
prefix = line[0] if line else '<empty>'
|
|
81
|
+
logger.info("patch_file: line %d: prefix=%r content=%r", i, prefix, line)
|
|
82
|
+
logger.info("patch_file: ===== PATCH ARGUMENT DEBUG END =====")
|
|
83
|
+
|
|
84
|
+
final_path = _resolve_file_path(context, path)
|
|
85
|
+
|
|
86
|
+
file_exists = os.path.exists(final_path)
|
|
87
|
+
if not file_exists:
|
|
88
|
+
raise FileNotFoundError(f"The file at resolved path {final_path} does not exist.")
|
|
89
|
+
|
|
90
|
+
try:
|
|
91
|
+
original_lines: List[str]
|
|
92
|
+
if file_exists:
|
|
93
|
+
with open(final_path, 'r', encoding='utf-8') as source:
|
|
94
|
+
original_lines = source.read().splitlines(keepends=True)
|
|
95
|
+
else:
|
|
96
|
+
original_lines = []
|
|
97
|
+
|
|
98
|
+
# Log original file content for comparison
|
|
99
|
+
logger.info("patch_file: ===== ORIGINAL FILE DEBUG START =====")
|
|
100
|
+
for i, line in enumerate(original_lines, 1):
|
|
101
|
+
logger.info("patch_file: original line %d: %r", i, line)
|
|
102
|
+
logger.info("patch_file: ===== ORIGINAL FILE DEBUG END =====")
|
|
103
|
+
|
|
104
|
+
patched_lines = None
|
|
105
|
+
patch_error = None
|
|
106
|
+
retry_strategies = [
|
|
107
|
+
(0, False),
|
|
108
|
+
(1, False),
|
|
109
|
+
(1, True),
|
|
110
|
+
(2, True),
|
|
111
|
+
]
|
|
112
|
+
for fuzz_factor, ignore_whitespace in retry_strategies:
|
|
113
|
+
try:
|
|
114
|
+
patched_lines = apply_unified_diff(
|
|
115
|
+
original_lines,
|
|
116
|
+
patch,
|
|
117
|
+
fuzz_factor=fuzz_factor,
|
|
118
|
+
ignore_whitespace=ignore_whitespace,
|
|
119
|
+
)
|
|
120
|
+
if (fuzz_factor, ignore_whitespace) != (0, False):
|
|
121
|
+
logger.info(
|
|
122
|
+
"patch_file: applied with fuzz=%d ignore_whitespace=%s.",
|
|
123
|
+
fuzz_factor,
|
|
124
|
+
ignore_whitespace,
|
|
125
|
+
)
|
|
126
|
+
break
|
|
127
|
+
except PatchApplicationError as patch_err:
|
|
128
|
+
patch_error = patch_err
|
|
129
|
+
logger.warning(
|
|
130
|
+
"patch_file: patch failed with fuzz=%d ignore_whitespace=%s: %s",
|
|
131
|
+
fuzz_factor,
|
|
132
|
+
ignore_whitespace,
|
|
133
|
+
patch_err,
|
|
134
|
+
)
|
|
135
|
+
continue
|
|
136
|
+
if patched_lines is None:
|
|
137
|
+
raise patch_error or PatchApplicationError("Patch could not be applied.")
|
|
138
|
+
|
|
139
|
+
with open(final_path, 'w', encoding='utf-8') as destination:
|
|
140
|
+
destination.writelines(patched_lines)
|
|
141
|
+
|
|
142
|
+
logger.info("patch_file: successfully applied patch to '%s'.", final_path)
|
|
143
|
+
return f"File patched successfully at {return_path}"
|
|
144
|
+
except PatchApplicationError as patch_err:
|
|
145
|
+
logger.error("patch_file: failed to apply patch to '%s': %s", final_path, patch_err, exc_info=True)
|
|
146
|
+
raise patch_err
|
|
147
|
+
except Exception as exc: # pragma: no cover - general safeguard
|
|
148
|
+
logger.error("patch_file: unexpected error while patching '%s': %s", final_path, exc, exc_info=True)
|
|
149
|
+
raise IOError(f"Could not patch file at '{final_path}': {exc}")
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import os
|
|
2
2
|
import logging
|
|
3
|
-
from typing import TYPE_CHECKING
|
|
3
|
+
from typing import TYPE_CHECKING, Optional
|
|
4
|
+
|
|
5
|
+
from pydantic import Field
|
|
4
6
|
|
|
5
7
|
from autobyteus.tools import tool
|
|
6
8
|
from autobyteus.tools.tool_category import ToolCategory
|
|
@@ -10,16 +12,33 @@ if TYPE_CHECKING:
|
|
|
10
12
|
|
|
11
13
|
logger = logging.getLogger(__name__)
|
|
12
14
|
|
|
13
|
-
@tool(name="
|
|
14
|
-
async def
|
|
15
|
+
@tool(name="read_file", category=ToolCategory.FILE_SYSTEM)
|
|
16
|
+
async def read_file(
|
|
17
|
+
context: 'AgentContext',
|
|
18
|
+
path: str,
|
|
19
|
+
start_line: Optional[int] = None,
|
|
20
|
+
end_line: Optional[int] = None,
|
|
21
|
+
include_line_numbers: bool = Field(
|
|
22
|
+
True,
|
|
23
|
+
description="If true, prefix each returned line with its line number (default).",
|
|
24
|
+
),
|
|
25
|
+
) -> str:
|
|
15
26
|
"""
|
|
16
|
-
Reads content from a specified file.
|
|
27
|
+
Reads content from a specified file. Supports optional 1-based inclusive line ranges via start_line/end_line.
|
|
28
|
+
Each returned line is prefixed with its line number when include_line_numbers is true.
|
|
17
29
|
'path' is the path to the file. If relative, it must be resolved against a configured agent workspace.
|
|
18
|
-
Raises ValueError if a relative path is given without a valid workspace.
|
|
30
|
+
Raises ValueError if a relative path is given without a valid workspace or if line range arguments are invalid.
|
|
19
31
|
Raises FileNotFoundError if the file does not exist.
|
|
20
32
|
Raises IOError if file reading fails for other reasons.
|
|
21
33
|
"""
|
|
22
|
-
logger.debug(f"Functional
|
|
34
|
+
logger.debug(f"Functional read_file tool for agent {context.agent_id}, initial path: {path}")
|
|
35
|
+
|
|
36
|
+
if start_line is not None and start_line < 1:
|
|
37
|
+
raise ValueError(f"start_line must be >= 1 when provided; got {start_line}.")
|
|
38
|
+
if end_line is not None and end_line < 1:
|
|
39
|
+
raise ValueError(f"end_line must be >= 1 when provided; got {end_line}.")
|
|
40
|
+
if start_line is not None and end_line is not None and end_line < start_line:
|
|
41
|
+
raise ValueError(f"end_line ({end_line}) must be >= start_line ({start_line}).")
|
|
23
42
|
|
|
24
43
|
final_path: str
|
|
25
44
|
if os.path.isabs(path):
|
|
@@ -48,7 +67,19 @@ async def file_reader(context: 'AgentContext', path: str) -> str:
|
|
|
48
67
|
|
|
49
68
|
try:
|
|
50
69
|
with open(final_path, 'r', encoding='utf-8') as file:
|
|
51
|
-
|
|
70
|
+
selected_lines = []
|
|
71
|
+
for line_no, line in enumerate(file, start=1):
|
|
72
|
+
if start_line is not None and line_no < start_line:
|
|
73
|
+
continue
|
|
74
|
+
if end_line is not None and line_no > end_line:
|
|
75
|
+
break
|
|
76
|
+
if include_line_numbers:
|
|
77
|
+
line_text = line.rstrip('\n')
|
|
78
|
+
line_suffix = '\n' if line.endswith('\n') else ''
|
|
79
|
+
selected_lines.append(f"{line_no}: {line_text}{line_suffix}")
|
|
80
|
+
else:
|
|
81
|
+
selected_lines.append(line)
|
|
82
|
+
content = ''.join(selected_lines)
|
|
52
83
|
logger.info(f"File successfully read from '{final_path}' for agent '{context.agent_id}'.")
|
|
53
84
|
return content
|
|
54
85
|
except Exception as e:
|
|
@@ -10,8 +10,8 @@ if TYPE_CHECKING:
|
|
|
10
10
|
|
|
11
11
|
logger = logging.getLogger(__name__)
|
|
12
12
|
|
|
13
|
-
@tool(name="
|
|
14
|
-
async def
|
|
13
|
+
@tool(name="write_file", category=ToolCategory.FILE_SYSTEM)
|
|
14
|
+
async def write_file(context: 'AgentContext', path: str, content: str) -> str:
|
|
15
15
|
"""
|
|
16
16
|
Creates or overwrites a file with specified content.
|
|
17
17
|
'path' is the path where the file will be written. If relative, it must be resolved against a configured agent workspace.
|
|
@@ -20,11 +20,13 @@ async def file_writer(context: 'AgentContext', path: str, content: str) -> str:
|
|
|
20
20
|
Raises ValueError if a relative path is given without a valid workspace.
|
|
21
21
|
Raises IOError if file writing fails.
|
|
22
22
|
"""
|
|
23
|
-
logger.debug(f"Functional
|
|
23
|
+
logger.debug(f"Functional write_file tool for agent {context.agent_id}, initial path: {path}")
|
|
24
24
|
|
|
25
25
|
final_path: str
|
|
26
|
+
return_path: str
|
|
26
27
|
if os.path.isabs(path):
|
|
27
28
|
final_path = path
|
|
29
|
+
return_path = final_path
|
|
28
30
|
logger.debug(f"Path '{path}' is absolute. Using it directly.")
|
|
29
31
|
else:
|
|
30
32
|
if not context.workspace:
|
|
@@ -39,6 +41,7 @@ async def file_writer(context: 'AgentContext', path: str, content: str) -> str:
|
|
|
39
41
|
raise ValueError(error_msg)
|
|
40
42
|
|
|
41
43
|
final_path = os.path.join(base_path, path)
|
|
44
|
+
return_path = os.path.normpath(path)
|
|
42
45
|
logger.debug(f"Path '{path}' is relative. Resolved to '{final_path}' using workspace base path '{base_path}'.")
|
|
43
46
|
|
|
44
47
|
try:
|
|
@@ -53,7 +56,7 @@ async def file_writer(context: 'AgentContext', path: str, content: str) -> str:
|
|
|
53
56
|
file.write(content)
|
|
54
57
|
|
|
55
58
|
logger.info(f"File successfully written to '{final_path}' for agent '{context.agent_id}'.")
|
|
56
|
-
return f"File created/updated at {
|
|
59
|
+
return f"File created/updated at {return_path}"
|
|
57
60
|
except Exception as e:
|
|
58
61
|
logger.error(f"Error writing file to final path '{final_path}' for agent {context.agent_id}: {e}", exc_info=True)
|
|
59
62
|
raise IOError(f"Could not write file at '{final_path}': {str(e)}")
|
|
@@ -99,7 +99,7 @@ def _python_type_to_json_schema(py_type: Any) -> Optional[Dict[str, Any]]:
|
|
|
99
99
|
if py_type is float: return {"type": "number"}
|
|
100
100
|
if py_type is bool: return {"type": "boolean"}
|
|
101
101
|
if py_type is dict: return {"type": "object"}
|
|
102
|
-
if py_type is list: return {"type": "array", "items":
|
|
102
|
+
if py_type is list: return {"type": "array", "items": {}} # Use empty dict for 'any'
|
|
103
103
|
|
|
104
104
|
origin_type = get_origin(py_type)
|
|
105
105
|
if origin_type is Union:
|
|
@@ -111,8 +111,8 @@ def _python_type_to_json_schema(py_type: Any) -> Optional[Dict[str, Any]]:
|
|
|
111
111
|
list_args = get_args(py_type)
|
|
112
112
|
if list_args and len(list_args) == 1:
|
|
113
113
|
item_schema = _python_type_to_json_schema(list_args[0])
|
|
114
|
-
return {"type": "array", "items": item_schema if item_schema else
|
|
115
|
-
return {"type": "array", "items":
|
|
114
|
+
return {"type": "array", "items": item_schema if item_schema else {}}
|
|
115
|
+
return {"type": "array", "items": {}} # Use empty dict for 'any'
|
|
116
116
|
if origin_type is Dict or origin_type is dict: return {"type": "object"}
|
|
117
117
|
logger.debug(f"Could not map Python type {py_type} to a simple JSON schema for array items.")
|
|
118
118
|
return None
|
|
@@ -141,18 +141,26 @@ def _get_parameter_type_from_hint(py_type: Any, param_name: str) -> Tuple[Parame
|
|
|
141
141
|
list_args = get_args(actual_type)
|
|
142
142
|
if list_args and len(list_args) == 1:
|
|
143
143
|
array_item_js_schema = _python_type_to_json_schema(list_args[0])
|
|
144
|
-
|
|
145
|
-
|
|
144
|
+
# FIX: For an untyped list, the item schema should be None, not True.
|
|
145
|
+
# An empty dict `{}` is a valid JSON schema for 'any'.
|
|
146
|
+
if array_item_js_schema is None:
|
|
147
|
+
array_item_js_schema = {}
|
|
146
148
|
return param_type_enum, array_item_js_schema
|
|
147
149
|
|
|
148
150
|
mapped_type = _TYPE_MAPPING.get(actual_type)
|
|
149
151
|
if mapped_type:
|
|
150
|
-
item_schema_for_array =
|
|
152
|
+
item_schema_for_array = {} if mapped_type == ParameterType.ARRAY else None
|
|
151
153
|
return mapped_type, item_schema_for_array
|
|
152
154
|
|
|
153
155
|
logger.warning(f"Unmapped type hint {py_type} (actual_type: {actual_type}) for param '{param_name}'. Defaulting to ParameterType.STRING.")
|
|
154
156
|
return ParameterType.STRING, None
|
|
155
157
|
|
|
158
|
+
|
|
159
|
+
try:
|
|
160
|
+
from pydantic.fields import FieldInfo
|
|
161
|
+
except ImportError:
|
|
162
|
+
FieldInfo = None # type: ignore
|
|
163
|
+
|
|
156
164
|
def _parse_signature(sig: inspect.Signature, tool_name: str) -> Tuple[TypingList[str], bool, bool, ParameterSchema]:
|
|
157
165
|
func_param_names = []
|
|
158
166
|
expects_context = False
|
|
@@ -173,26 +181,51 @@ def _parse_signature(sig: inspect.Signature, tool_name: str) -> Tuple[TypingList
|
|
|
173
181
|
param_type_enum, item_schema = _get_parameter_type_from_hint(param_type_hint, param_name)
|
|
174
182
|
|
|
175
183
|
is_required = param_obj.default == inspect.Parameter.empty
|
|
176
|
-
if
|
|
177
|
-
|
|
178
|
-
|
|
184
|
+
default_val = param_obj.default if param_obj.default != inspect.Parameter.empty else None
|
|
185
|
+
|
|
186
|
+
# --- Pydantic Field Extraction Logic ---
|
|
179
187
|
param_desc = f"Parameter '{param_name}' for tool '{tool_name}'."
|
|
180
188
|
param_name_lower = param_name.lower()
|
|
181
189
|
if "path" in param_name_lower or "file" in param_name_lower or "dir" in param_name_lower or "folder" in param_name_lower:
|
|
182
|
-
|
|
190
|
+
param_desc += " This is expected to be a path."
|
|
191
|
+
|
|
192
|
+
if FieldInfo and isinstance(param_obj.default, FieldInfo):
|
|
193
|
+
field_info = param_obj.default
|
|
194
|
+
|
|
195
|
+
# 1. Description
|
|
196
|
+
if field_info.description:
|
|
197
|
+
param_desc = field_info.description
|
|
183
198
|
|
|
199
|
+
# 2. Default Value & Requiredness
|
|
200
|
+
# If PydanticUndefined (or similar sentinel), it means required.
|
|
201
|
+
# Otherwise, use the default value from Field.
|
|
202
|
+
# Note: Pydantic v1 uses Undefined, v2 uses PydanticUndefined.
|
|
203
|
+
# We check if it is the special undefined value via representation or direct check.
|
|
204
|
+
|
|
205
|
+
# Simple heuristic for "Undefined" without importing the specific sentinel
|
|
206
|
+
if str(field_info.default) == "PydanticUndefined" or field_info.default == ...:
|
|
207
|
+
is_required = True
|
|
208
|
+
default_val = None
|
|
209
|
+
else:
|
|
210
|
+
is_required = False
|
|
211
|
+
default_val = field_info.default
|
|
212
|
+
|
|
213
|
+
if get_origin(param_type_hint) is Union and type(None) in get_args(param_type_hint):
|
|
214
|
+
is_required = False
|
|
215
|
+
|
|
184
216
|
schema_param = ParameterDefinition(
|
|
185
217
|
name=param_name,
|
|
186
218
|
param_type=param_type_enum,
|
|
187
219
|
description=param_desc,
|
|
188
220
|
required=is_required,
|
|
189
|
-
default_value=
|
|
221
|
+
default_value=default_val,
|
|
190
222
|
array_item_schema=item_schema
|
|
191
223
|
)
|
|
192
224
|
generated_arg_schema.add_parameter(schema_param)
|
|
193
225
|
|
|
194
226
|
return func_param_names, expects_context, expects_tool_state, generated_arg_schema
|
|
195
227
|
|
|
228
|
+
|
|
196
229
|
# --- The refactored @tool decorator ---
|
|
197
230
|
|
|
198
231
|
def tool(
|
|
@@ -215,6 +248,11 @@ def tool(
|
|
|
215
248
|
|
|
216
249
|
final_arg_schema = argument_schema if argument_schema is not None else gen_arg_schema
|
|
217
250
|
|
|
251
|
+
def _current_description() -> str:
|
|
252
|
+
"""Recompute the description from the latest docstring/override."""
|
|
253
|
+
latest_doc = inspect.getdoc(func)
|
|
254
|
+
return description or (latest_doc.split('\n\n')[0] if latest_doc else f"Functional tool: {tool_name}")
|
|
255
|
+
|
|
218
256
|
def factory(inst_config: Optional[ToolConfig] = None) -> FunctionalTool:
|
|
219
257
|
return FunctionalTool(
|
|
220
258
|
original_func=func,
|
|
@@ -232,12 +270,13 @@ def tool(
|
|
|
232
270
|
tool_def = ToolDefinition(
|
|
233
271
|
name=tool_name,
|
|
234
272
|
description=tool_desc,
|
|
235
|
-
|
|
236
|
-
|
|
273
|
+
argument_schema_provider=lambda: final_arg_schema,
|
|
274
|
+
config_schema_provider=lambda: config_schema,
|
|
237
275
|
custom_factory=factory,
|
|
238
276
|
tool_class=None,
|
|
239
277
|
origin=ToolOrigin.LOCAL,
|
|
240
|
-
category=category
|
|
278
|
+
category=category,
|
|
279
|
+
description_provider=_current_description
|
|
241
280
|
)
|
|
242
281
|
default_tool_registry.register_tool(tool_def)
|
|
243
282
|
|
autobyteus/tools/mcp/__init__.py
CHANGED
|
@@ -19,6 +19,7 @@ from .types import (
|
|
|
19
19
|
BaseMcpConfig,
|
|
20
20
|
StdioMcpServerConfig,
|
|
21
21
|
StreamableHttpMcpServerConfig,
|
|
22
|
+
WebsocketMcpServerConfig,
|
|
22
23
|
McpTransportType,
|
|
23
24
|
McpServerInstanceKey,
|
|
24
25
|
)
|
|
@@ -37,6 +38,7 @@ __all__ = [
|
|
|
37
38
|
"BaseMcpConfig",
|
|
38
39
|
"StdioMcpServerConfig",
|
|
39
40
|
"StreamableHttpMcpServerConfig",
|
|
41
|
+
"WebsocketMcpServerConfig",
|
|
40
42
|
"McpTransportType",
|
|
41
43
|
"McpServerInstanceKey",
|
|
42
44
|
# Services and Managers
|
|
@@ -9,6 +9,7 @@ from .types import (
|
|
|
9
9
|
BaseMcpConfig,
|
|
10
10
|
StdioMcpServerConfig,
|
|
11
11
|
StreamableHttpMcpServerConfig,
|
|
12
|
+
WebsocketMcpServerConfig,
|
|
12
13
|
McpTransportType
|
|
13
14
|
)
|
|
14
15
|
from autobyteus.utils.singleton import SingletonMeta
|
|
@@ -49,7 +50,8 @@ class McpConfigService(metaclass=SingletonMeta):
|
|
|
49
50
|
|
|
50
51
|
transport_specific_params_key_map = {
|
|
51
52
|
McpTransportType.STDIO: "stdio_params",
|
|
52
|
-
McpTransportType.STREAMABLE_HTTP: "streamable_http_params"
|
|
53
|
+
McpTransportType.STREAMABLE_HTTP: "streamable_http_params",
|
|
54
|
+
McpTransportType.WEBSOCKET: "websocket_params",
|
|
53
55
|
}
|
|
54
56
|
|
|
55
57
|
if transport_type in transport_specific_params_key_map:
|
|
@@ -74,6 +76,8 @@ class McpConfigService(metaclass=SingletonMeta):
|
|
|
74
76
|
return StdioMcpServerConfig(**constructor_params)
|
|
75
77
|
elif transport_type == McpTransportType.STREAMABLE_HTTP:
|
|
76
78
|
return StreamableHttpMcpServerConfig(**constructor_params)
|
|
79
|
+
elif transport_type == McpTransportType.WEBSOCKET:
|
|
80
|
+
return WebsocketMcpServerConfig(**constructor_params)
|
|
77
81
|
else:
|
|
78
82
|
raise ValueError(f"Unsupported McpTransportType '{transport_type}' for server '{server_id}'.")
|
|
79
83
|
except TypeError as e:
|
|
@@ -5,6 +5,7 @@ This package contains the core abstractions for managing connections to remote M
|
|
|
5
5
|
from .base_managed_mcp_server import BaseManagedMcpServer, ServerState
|
|
6
6
|
from .stdio_managed_mcp_server import StdioManagedMcpServer
|
|
7
7
|
from .http_managed_mcp_server import HttpManagedMcpServer
|
|
8
|
+
from .websocket_managed_mcp_server import WebsocketManagedMcpServer
|
|
8
9
|
from .proxy import McpServerProxy
|
|
9
10
|
|
|
10
11
|
__all__ = [
|
|
@@ -12,5 +13,6 @@ __all__ = [
|
|
|
12
13
|
"ServerState",
|
|
13
14
|
"StdioManagedMcpServer",
|
|
14
15
|
"HttpManagedMcpServer",
|
|
16
|
+
"WebsocketManagedMcpServer",
|
|
15
17
|
"McpServerProxy",
|
|
16
18
|
]
|
|
@@ -23,7 +23,7 @@ class HttpManagedMcpServer(BaseManagedMcpServer):
|
|
|
23
23
|
config = cast(StreamableHttpMcpServerConfig, self._config)
|
|
24
24
|
|
|
25
25
|
logger.debug(f"Establishing HTTP connection for server '{self.server_id}' to URL: {config.url}")
|
|
26
|
-
read_stream, write_stream = await self._exit_stack.enter_async_context(
|
|
26
|
+
read_stream, write_stream, _ = await self._exit_stack.enter_async_context(
|
|
27
27
|
streamablehttp_client(config.url, headers=config.headers)
|
|
28
28
|
)
|
|
29
29
|
session = await self._exit_stack.enter_async_context(ClientSession(read_stream, write_stream))
|