autobyteus 1.2.1__py3-none-any.whl → 1.2.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- autobyteus/agent/agent.py +15 -5
- autobyteus/agent/bootstrap_steps/__init__.py +1 -3
- autobyteus/agent/bootstrap_steps/agent_bootstrapper.py +3 -59
- autobyteus/agent/bootstrap_steps/base_bootstrap_step.py +1 -4
- autobyteus/agent/bootstrap_steps/mcp_server_prewarming_step.py +1 -3
- autobyteus/agent/bootstrap_steps/system_prompt_processing_step.py +16 -13
- autobyteus/agent/bootstrap_steps/workspace_context_initialization_step.py +2 -4
- autobyteus/agent/context/agent_config.py +43 -20
- autobyteus/agent/context/agent_context.py +23 -18
- autobyteus/agent/context/agent_runtime_state.py +19 -19
- autobyteus/agent/events/__init__.py +16 -1
- autobyteus/agent/events/agent_events.py +43 -3
- autobyteus/agent/events/agent_input_event_queue_manager.py +79 -26
- autobyteus/agent/events/event_store.py +57 -0
- autobyteus/agent/events/notifiers.py +69 -59
- autobyteus/agent/events/worker_event_dispatcher.py +21 -64
- autobyteus/agent/factory/agent_factory.py +52 -0
- autobyteus/agent/handlers/__init__.py +2 -0
- autobyteus/agent/handlers/approved_tool_invocation_event_handler.py +51 -34
- autobyteus/agent/handlers/bootstrap_event_handler.py +155 -0
- autobyteus/agent/handlers/inter_agent_message_event_handler.py +10 -0
- autobyteus/agent/handlers/lifecycle_event_logger.py +19 -11
- autobyteus/agent/handlers/llm_complete_response_received_event_handler.py +10 -15
- autobyteus/agent/handlers/llm_user_message_ready_event_handler.py +188 -48
- autobyteus/agent/handlers/tool_execution_approval_event_handler.py +0 -10
- autobyteus/agent/handlers/tool_invocation_request_event_handler.py +53 -48
- autobyteus/agent/handlers/tool_result_event_handler.py +7 -8
- autobyteus/agent/handlers/user_input_message_event_handler.py +10 -3
- autobyteus/agent/input_processor/memory_ingest_input_processor.py +40 -0
- autobyteus/agent/lifecycle/__init__.py +12 -0
- autobyteus/agent/lifecycle/base_processor.py +109 -0
- autobyteus/agent/lifecycle/events.py +35 -0
- autobyteus/agent/lifecycle/processor_definition.py +36 -0
- autobyteus/agent/lifecycle/processor_registry.py +106 -0
- autobyteus/agent/llm_request_assembler.py +98 -0
- autobyteus/agent/llm_response_processor/__init__.py +1 -8
- autobyteus/agent/message/context_file_type.py +1 -1
- autobyteus/agent/runtime/agent_runtime.py +29 -21
- autobyteus/agent/runtime/agent_worker.py +98 -19
- autobyteus/agent/shutdown_steps/__init__.py +2 -0
- autobyteus/agent/shutdown_steps/agent_shutdown_orchestrator.py +2 -0
- autobyteus/agent/shutdown_steps/tool_cleanup_step.py +58 -0
- autobyteus/agent/status/__init__.py +14 -0
- autobyteus/agent/status/manager.py +93 -0
- autobyteus/agent/status/status_deriver.py +96 -0
- autobyteus/agent/{phases/phase_enum.py → status/status_enum.py} +16 -16
- autobyteus/agent/status/status_update_utils.py +73 -0
- autobyteus/agent/streaming/__init__.py +52 -5
- autobyteus/agent/streaming/adapters/__init__.py +18 -0
- autobyteus/agent/streaming/adapters/invocation_adapter.py +184 -0
- autobyteus/agent/streaming/adapters/tool_call_parsing.py +163 -0
- autobyteus/agent/streaming/adapters/tool_syntax_registry.py +67 -0
- autobyteus/agent/streaming/agent_event_stream.py +3 -183
- autobyteus/agent/streaming/api_tool_call/__init__.py +16 -0
- autobyteus/agent/streaming/api_tool_call/file_content_streamer.py +56 -0
- autobyteus/agent/streaming/api_tool_call/json_string_field_extractor.py +175 -0
- autobyteus/agent/streaming/api_tool_call_streaming_response_handler.py +4 -0
- autobyteus/agent/streaming/events/__init__.py +6 -0
- autobyteus/agent/streaming/events/stream_event_payloads.py +284 -0
- autobyteus/agent/streaming/events/stream_events.py +141 -0
- autobyteus/agent/streaming/handlers/__init__.py +15 -0
- autobyteus/agent/streaming/handlers/api_tool_call_streaming_response_handler.py +303 -0
- autobyteus/agent/streaming/handlers/parsing_streaming_response_handler.py +107 -0
- autobyteus/agent/streaming/handlers/pass_through_streaming_response_handler.py +107 -0
- autobyteus/agent/streaming/handlers/streaming_handler_factory.py +177 -0
- autobyteus/agent/streaming/handlers/streaming_response_handler.py +58 -0
- autobyteus/agent/streaming/parser/__init__.py +61 -0
- autobyteus/agent/streaming/parser/event_emitter.py +181 -0
- autobyteus/agent/streaming/parser/events.py +4 -0
- autobyteus/agent/streaming/parser/invocation_adapter.py +4 -0
- autobyteus/agent/streaming/parser/json_parsing_strategies/__init__.py +19 -0
- autobyteus/agent/streaming/parser/json_parsing_strategies/base.py +32 -0
- autobyteus/agent/streaming/parser/json_parsing_strategies/default.py +34 -0
- autobyteus/agent/streaming/parser/json_parsing_strategies/gemini.py +31 -0
- autobyteus/agent/streaming/parser/json_parsing_strategies/openai.py +64 -0
- autobyteus/agent/streaming/parser/json_parsing_strategies/registry.py +75 -0
- autobyteus/agent/streaming/parser/parser_context.py +227 -0
- autobyteus/agent/streaming/parser/parser_factory.py +132 -0
- autobyteus/agent/streaming/parser/sentinel_format.py +7 -0
- autobyteus/agent/streaming/parser/state_factory.py +62 -0
- autobyteus/agent/streaming/parser/states/__init__.py +1 -0
- autobyteus/agent/streaming/parser/states/base_state.py +60 -0
- autobyteus/agent/streaming/parser/states/custom_xml_tag_run_bash_parsing_state.py +38 -0
- autobyteus/agent/streaming/parser/states/custom_xml_tag_write_file_parsing_state.py +55 -0
- autobyteus/agent/streaming/parser/states/delimited_content_state.py +146 -0
- autobyteus/agent/streaming/parser/states/json_initialization_state.py +144 -0
- autobyteus/agent/streaming/parser/states/json_tool_parsing_state.py +137 -0
- autobyteus/agent/streaming/parser/states/sentinel_content_state.py +30 -0
- autobyteus/agent/streaming/parser/states/sentinel_initialization_state.py +117 -0
- autobyteus/agent/streaming/parser/states/text_state.py +78 -0
- autobyteus/agent/streaming/parser/states/xml_patch_file_tool_parsing_state.py +328 -0
- autobyteus/agent/streaming/parser/states/xml_run_bash_tool_parsing_state.py +129 -0
- autobyteus/agent/streaming/parser/states/xml_tag_initialization_state.py +151 -0
- autobyteus/agent/streaming/parser/states/xml_tool_parsing_state.py +63 -0
- autobyteus/agent/streaming/parser/states/xml_write_file_tool_parsing_state.py +343 -0
- autobyteus/agent/streaming/parser/strategies/__init__.py +17 -0
- autobyteus/agent/streaming/parser/strategies/base.py +24 -0
- autobyteus/agent/streaming/parser/strategies/json_tool_strategy.py +26 -0
- autobyteus/agent/streaming/parser/strategies/registry.py +28 -0
- autobyteus/agent/streaming/parser/strategies/sentinel_strategy.py +23 -0
- autobyteus/agent/streaming/parser/strategies/xml_tag_strategy.py +21 -0
- autobyteus/agent/streaming/parser/stream_scanner.py +167 -0
- autobyteus/agent/streaming/parser/streaming_parser.py +212 -0
- autobyteus/agent/streaming/parser/tool_call_parsing.py +4 -0
- autobyteus/agent/streaming/parser/tool_constants.py +7 -0
- autobyteus/agent/streaming/parser/tool_syntax_registry.py +4 -0
- autobyteus/agent/streaming/parser/xml_tool_parsing_state_registry.py +55 -0
- autobyteus/agent/streaming/parsing_streaming_response_handler.py +4 -0
- autobyteus/agent/streaming/pass_through_streaming_response_handler.py +4 -0
- autobyteus/agent/streaming/queue_streamer.py +3 -57
- autobyteus/agent/streaming/segments/__init__.py +5 -0
- autobyteus/agent/streaming/segments/segment_events.py +81 -0
- autobyteus/agent/streaming/stream_event_payloads.py +2 -223
- autobyteus/agent/streaming/stream_events.py +3 -140
- autobyteus/agent/streaming/streaming_handler_factory.py +4 -0
- autobyteus/agent/streaming/streaming_response_handler.py +4 -0
- autobyteus/agent/streaming/streams/__init__.py +5 -0
- autobyteus/agent/streaming/streams/agent_event_stream.py +197 -0
- autobyteus/agent/streaming/utils/__init__.py +5 -0
- autobyteus/agent/streaming/utils/queue_streamer.py +59 -0
- autobyteus/agent/system_prompt_processor/__init__.py +2 -0
- autobyteus/agent/system_prompt_processor/available_skills_processor.py +96 -0
- autobyteus/agent/system_prompt_processor/base_processor.py +1 -1
- autobyteus/agent/system_prompt_processor/processor_meta.py +15 -2
- autobyteus/agent/system_prompt_processor/tool_manifest_injector_processor.py +39 -58
- autobyteus/agent/token_budget.py +56 -0
- autobyteus/agent/tool_execution_result_processor/memory_ingest_tool_result_processor.py +29 -0
- autobyteus/agent/tool_invocation.py +16 -40
- autobyteus/agent/tool_invocation_preprocessor/__init__.py +9 -0
- autobyteus/agent/tool_invocation_preprocessor/base_preprocessor.py +45 -0
- autobyteus/agent/tool_invocation_preprocessor/processor_definition.py +15 -0
- autobyteus/agent/tool_invocation_preprocessor/processor_meta.py +33 -0
- autobyteus/agent/tool_invocation_preprocessor/processor_registry.py +60 -0
- autobyteus/agent/utils/wait_for_idle.py +12 -14
- autobyteus/agent/workspace/base_workspace.py +6 -27
- autobyteus/agent_team/agent_team.py +3 -3
- autobyteus/agent_team/agent_team_builder.py +1 -41
- autobyteus/agent_team/bootstrap_steps/__init__.py +0 -4
- autobyteus/agent_team/bootstrap_steps/agent_configuration_preparation_step.py +8 -18
- autobyteus/agent_team/bootstrap_steps/agent_team_bootstrapper.py +4 -16
- autobyteus/agent_team/bootstrap_steps/base_agent_team_bootstrap_step.py +1 -2
- autobyteus/agent_team/bootstrap_steps/coordinator_initialization_step.py +1 -2
- autobyteus/agent_team/bootstrap_steps/task_notifier_initialization_step.py +1 -2
- autobyteus/agent_team/bootstrap_steps/team_context_initialization_step.py +4 -4
- autobyteus/agent_team/context/agent_team_config.py +6 -3
- autobyteus/agent_team/context/agent_team_context.py +25 -3
- autobyteus/agent_team/context/agent_team_runtime_state.py +9 -6
- autobyteus/agent_team/events/__init__.py +11 -0
- autobyteus/agent_team/events/agent_team_event_dispatcher.py +22 -9
- autobyteus/agent_team/events/agent_team_events.py +16 -0
- autobyteus/agent_team/events/event_store.py +57 -0
- autobyteus/agent_team/factory/agent_team_factory.py +8 -0
- autobyteus/agent_team/handlers/inter_agent_message_request_event_handler.py +18 -2
- autobyteus/agent_team/handlers/lifecycle_agent_team_event_handler.py +21 -5
- autobyteus/agent_team/handlers/process_user_message_event_handler.py +17 -8
- autobyteus/agent_team/handlers/tool_approval_team_event_handler.py +19 -4
- autobyteus/agent_team/runtime/agent_team_runtime.py +41 -10
- autobyteus/agent_team/runtime/agent_team_worker.py +69 -5
- autobyteus/agent_team/status/__init__.py +14 -0
- autobyteus/agent_team/status/agent_team_status.py +18 -0
- autobyteus/agent_team/status/agent_team_status_manager.py +33 -0
- autobyteus/agent_team/status/status_deriver.py +62 -0
- autobyteus/agent_team/status/status_update_utils.py +42 -0
- autobyteus/agent_team/streaming/__init__.py +2 -2
- autobyteus/agent_team/streaming/agent_team_event_notifier.py +6 -6
- autobyteus/agent_team/streaming/agent_team_stream_event_payloads.py +4 -4
- autobyteus/agent_team/streaming/agent_team_stream_events.py +3 -3
- autobyteus/agent_team/system_prompt_processor/__init__.py +6 -0
- autobyteus/agent_team/system_prompt_processor/team_manifest_injector_processor.py +76 -0
- autobyteus/agent_team/task_notification/task_notification_mode.py +19 -0
- autobyteus/agent_team/utils/wait_for_idle.py +4 -4
- autobyteus/cli/agent_cli.py +18 -10
- autobyteus/cli/agent_team_tui/app.py +14 -11
- autobyteus/cli/agent_team_tui/state.py +13 -15
- autobyteus/cli/agent_team_tui/widgets/agent_list_sidebar.py +15 -15
- autobyteus/cli/agent_team_tui/widgets/focus_pane.py +143 -36
- autobyteus/cli/agent_team_tui/widgets/renderables.py +1 -1
- autobyteus/cli/agent_team_tui/widgets/shared.py +25 -25
- autobyteus/cli/cli_display.py +193 -44
- autobyteus/cli/workflow_tui/app.py +9 -10
- autobyteus/cli/workflow_tui/state.py +14 -16
- autobyteus/cli/workflow_tui/widgets/agent_list_sidebar.py +15 -15
- autobyteus/cli/workflow_tui/widgets/focus_pane.py +137 -35
- autobyteus/cli/workflow_tui/widgets/renderables.py +1 -1
- autobyteus/cli/workflow_tui/widgets/shared.py +25 -25
- autobyteus/clients/autobyteus_client.py +94 -1
- autobyteus/events/event_types.py +11 -18
- autobyteus/llm/api/autobyteus_llm.py +33 -29
- autobyteus/llm/api/claude_llm.py +142 -36
- autobyteus/llm/api/gemini_llm.py +163 -59
- autobyteus/llm/api/grok_llm.py +1 -1
- autobyteus/llm/api/minimax_llm.py +26 -0
- autobyteus/llm/api/mistral_llm.py +113 -87
- autobyteus/llm/api/ollama_llm.py +9 -42
- autobyteus/llm/api/openai_compatible_llm.py +127 -91
- autobyteus/llm/api/openai_llm.py +3 -3
- autobyteus/llm/api/openai_responses_llm.py +324 -0
- autobyteus/llm/api/zhipu_llm.py +21 -2
- autobyteus/llm/autobyteus_provider.py +70 -60
- autobyteus/llm/base_llm.py +85 -81
- autobyteus/llm/converters/__init__.py +14 -0
- autobyteus/llm/converters/anthropic_tool_call_converter.py +37 -0
- autobyteus/llm/converters/gemini_tool_call_converter.py +57 -0
- autobyteus/llm/converters/mistral_tool_call_converter.py +37 -0
- autobyteus/llm/converters/openai_tool_call_converter.py +38 -0
- autobyteus/llm/extensions/base_extension.py +6 -12
- autobyteus/llm/extensions/token_usage_tracking_extension.py +45 -18
- autobyteus/llm/llm_factory.py +282 -204
- autobyteus/llm/lmstudio_provider.py +60 -49
- autobyteus/llm/models.py +35 -2
- autobyteus/llm/ollama_provider.py +60 -49
- autobyteus/llm/ollama_provider_resolver.py +0 -1
- autobyteus/llm/prompt_renderers/__init__.py +19 -0
- autobyteus/llm/prompt_renderers/anthropic_prompt_renderer.py +104 -0
- autobyteus/llm/prompt_renderers/autobyteus_prompt_renderer.py +19 -0
- autobyteus/llm/prompt_renderers/base_prompt_renderer.py +10 -0
- autobyteus/llm/prompt_renderers/gemini_prompt_renderer.py +63 -0
- autobyteus/llm/prompt_renderers/mistral_prompt_renderer.py +87 -0
- autobyteus/llm/prompt_renderers/ollama_prompt_renderer.py +51 -0
- autobyteus/llm/prompt_renderers/openai_chat_renderer.py +97 -0
- autobyteus/llm/prompt_renderers/openai_responses_renderer.py +101 -0
- autobyteus/llm/providers.py +1 -3
- autobyteus/llm/token_counter/claude_token_counter.py +56 -25
- autobyteus/llm/token_counter/mistral_token_counter.py +12 -8
- autobyteus/llm/token_counter/openai_token_counter.py +24 -5
- autobyteus/llm/token_counter/token_counter_factory.py +12 -5
- autobyteus/llm/utils/llm_config.py +6 -12
- autobyteus/llm/utils/media_payload_formatter.py +27 -20
- autobyteus/llm/utils/messages.py +55 -3
- autobyteus/llm/utils/response_types.py +3 -0
- autobyteus/llm/utils/tool_call_delta.py +31 -0
- autobyteus/memory/__init__.py +32 -0
- autobyteus/memory/active_transcript.py +69 -0
- autobyteus/memory/compaction/__init__.py +9 -0
- autobyteus/memory/compaction/compaction_result.py +8 -0
- autobyteus/memory/compaction/compactor.py +89 -0
- autobyteus/memory/compaction/summarizer.py +11 -0
- autobyteus/memory/compaction_snapshot_builder.py +84 -0
- autobyteus/memory/memory_manager.py +183 -0
- autobyteus/memory/models/__init__.py +14 -0
- autobyteus/memory/models/episodic_item.py +41 -0
- autobyteus/memory/models/memory_types.py +7 -0
- autobyteus/memory/models/raw_trace_item.py +79 -0
- autobyteus/memory/models/semantic_item.py +41 -0
- autobyteus/memory/models/tool_interaction.py +20 -0
- autobyteus/memory/policies/__init__.py +5 -0
- autobyteus/memory/policies/compaction_policy.py +16 -0
- autobyteus/memory/retrieval/__init__.py +7 -0
- autobyteus/memory/retrieval/memory_bundle.py +11 -0
- autobyteus/memory/retrieval/retriever.py +13 -0
- autobyteus/memory/store/__init__.py +7 -0
- autobyteus/memory/store/base_store.py +14 -0
- autobyteus/memory/store/file_store.py +98 -0
- autobyteus/memory/tool_interaction_builder.py +46 -0
- autobyteus/memory/turn_tracker.py +9 -0
- autobyteus/multimedia/audio/api/autobyteus_audio_client.py +19 -5
- autobyteus/multimedia/audio/api/gemini_audio_client.py +108 -16
- autobyteus/multimedia/audio/audio_client_factory.py +47 -9
- autobyteus/multimedia/audio/audio_model.py +2 -1
- autobyteus/multimedia/image/api/autobyteus_image_client.py +19 -5
- autobyteus/multimedia/image/api/gemini_image_client.py +38 -17
- autobyteus/multimedia/image/api/openai_image_client.py +125 -43
- autobyteus/multimedia/image/autobyteus_image_provider.py +2 -1
- autobyteus/multimedia/image/image_client_factory.py +47 -15
- autobyteus/multimedia/image/image_model.py +5 -2
- autobyteus/multimedia/providers.py +3 -2
- autobyteus/skills/loader.py +71 -0
- autobyteus/skills/model.py +11 -0
- autobyteus/skills/registry.py +70 -0
- autobyteus/task_management/tools/todo_tools/add_todo.py +2 -2
- autobyteus/task_management/tools/todo_tools/create_todo_list.py +2 -2
- autobyteus/task_management/tools/todo_tools/update_todo_status.py +2 -2
- autobyteus/tools/__init__.py +34 -47
- autobyteus/tools/base_tool.py +7 -0
- autobyteus/tools/file/__init__.py +2 -6
- autobyteus/tools/file/patch_file.py +149 -0
- autobyteus/tools/file/read_file.py +36 -5
- autobyteus/tools/file/write_file.py +4 -1
- autobyteus/tools/functional_tool.py +43 -6
- autobyteus/tools/mcp/__init__.py +2 -0
- autobyteus/tools/mcp/config_service.py +5 -1
- autobyteus/tools/mcp/server/__init__.py +2 -0
- autobyteus/tools/mcp/server/http_managed_mcp_server.py +1 -1
- autobyteus/tools/mcp/server/websocket_managed_mcp_server.py +141 -0
- autobyteus/tools/mcp/server_instance_manager.py +8 -1
- autobyteus/tools/mcp/types.py +61 -0
- autobyteus/tools/multimedia/audio_tools.py +70 -17
- autobyteus/tools/multimedia/download_media_tool.py +18 -4
- autobyteus/tools/multimedia/image_tools.py +246 -62
- autobyteus/tools/operation_executor/journal_manager.py +107 -0
- autobyteus/tools/operation_executor/operation_event_buffer.py +57 -0
- autobyteus/tools/operation_executor/operation_event_producer.py +29 -0
- autobyteus/tools/operation_executor/operation_executor.py +58 -0
- autobyteus/tools/registry/tool_definition.py +43 -2
- autobyteus/tools/skill/load_skill.py +50 -0
- autobyteus/tools/terminal/__init__.py +45 -0
- autobyteus/tools/terminal/ansi_utils.py +32 -0
- autobyteus/tools/terminal/background_process_manager.py +233 -0
- autobyteus/tools/terminal/output_buffer.py +105 -0
- autobyteus/tools/terminal/prompt_detector.py +63 -0
- autobyteus/tools/terminal/pty_session.py +241 -0
- autobyteus/tools/terminal/session_factory.py +20 -0
- autobyteus/tools/terminal/terminal_session_manager.py +226 -0
- autobyteus/tools/terminal/tools/__init__.py +13 -0
- autobyteus/tools/terminal/tools/get_process_output.py +81 -0
- autobyteus/tools/terminal/tools/run_bash.py +109 -0
- autobyteus/tools/terminal/tools/start_background_process.py +104 -0
- autobyteus/tools/terminal/tools/stop_background_process.py +67 -0
- autobyteus/tools/terminal/types.py +54 -0
- autobyteus/tools/terminal/wsl_tmux_session.py +221 -0
- autobyteus/tools/terminal/wsl_utils.py +156 -0
- autobyteus/tools/transaction_management/backup_handler.py +48 -0
- autobyteus/tools/transaction_management/operation_lifecycle_manager.py +62 -0
- autobyteus/tools/usage/__init__.py +1 -2
- autobyteus/tools/usage/formatters/__init__.py +17 -1
- autobyteus/tools/usage/formatters/base_formatter.py +8 -0
- autobyteus/tools/usage/formatters/default_xml_schema_formatter.py +2 -2
- autobyteus/tools/usage/formatters/mistral_json_schema_formatter.py +18 -0
- autobyteus/tools/usage/formatters/patch_file_xml_example_formatter.py +64 -0
- autobyteus/tools/usage/formatters/patch_file_xml_schema_formatter.py +31 -0
- autobyteus/tools/usage/formatters/run_bash_xml_example_formatter.py +32 -0
- autobyteus/tools/usage/formatters/run_bash_xml_schema_formatter.py +36 -0
- autobyteus/tools/usage/formatters/write_file_xml_example_formatter.py +53 -0
- autobyteus/tools/usage/formatters/write_file_xml_schema_formatter.py +31 -0
- autobyteus/tools/usage/providers/tool_manifest_provider.py +10 -10
- autobyteus/tools/usage/registries/__init__.py +1 -3
- autobyteus/tools/usage/registries/tool_formatting_registry.py +115 -8
- autobyteus/tools/usage/tool_schema_provider.py +51 -0
- autobyteus/tools/web/__init__.py +4 -0
- autobyteus/tools/web/read_url_tool.py +80 -0
- autobyteus/utils/diff_utils.py +271 -0
- autobyteus/utils/download_utils.py +109 -0
- autobyteus/utils/file_utils.py +57 -2
- autobyteus/utils/gemini_helper.py +56 -0
- autobyteus/utils/gemini_model_mapping.py +71 -0
- autobyteus/utils/llm_output_formatter.py +75 -0
- autobyteus/utils/tool_call_format.py +36 -0
- autobyteus/workflow/agentic_workflow.py +3 -3
- autobyteus/workflow/bootstrap_steps/agent_tool_injection_step.py +2 -2
- autobyteus/workflow/bootstrap_steps/base_workflow_bootstrap_step.py +2 -2
- autobyteus/workflow/bootstrap_steps/coordinator_initialization_step.py +2 -2
- autobyteus/workflow/bootstrap_steps/coordinator_prompt_preparation_step.py +3 -9
- autobyteus/workflow/bootstrap_steps/workflow_bootstrapper.py +6 -6
- autobyteus/workflow/bootstrap_steps/workflow_runtime_queue_initialization_step.py +2 -2
- autobyteus/workflow/context/workflow_context.py +3 -3
- autobyteus/workflow/context/workflow_runtime_state.py +5 -5
- autobyteus/workflow/events/workflow_event_dispatcher.py +5 -5
- autobyteus/workflow/handlers/lifecycle_workflow_event_handler.py +3 -3
- autobyteus/workflow/handlers/process_user_message_event_handler.py +5 -5
- autobyteus/workflow/handlers/tool_approval_workflow_event_handler.py +2 -2
- autobyteus/workflow/runtime/workflow_runtime.py +8 -8
- autobyteus/workflow/runtime/workflow_worker.py +3 -3
- autobyteus/workflow/status/__init__.py +11 -0
- autobyteus/workflow/status/workflow_status.py +19 -0
- autobyteus/workflow/status/workflow_status_manager.py +48 -0
- autobyteus/workflow/streaming/__init__.py +2 -2
- autobyteus/workflow/streaming/workflow_event_notifier.py +7 -7
- autobyteus/workflow/streaming/workflow_stream_event_payloads.py +4 -4
- autobyteus/workflow/streaming/workflow_stream_events.py +3 -3
- autobyteus/workflow/utils/wait_for_idle.py +4 -4
- autobyteus-1.2.3.dist-info/METADATA +293 -0
- autobyteus-1.2.3.dist-info/RECORD +600 -0
- {autobyteus-1.2.1.dist-info → autobyteus-1.2.3.dist-info}/WHEEL +1 -1
- {autobyteus-1.2.1.dist-info → autobyteus-1.2.3.dist-info}/top_level.txt +0 -1
- autobyteus/agent/bootstrap_steps/agent_runtime_queue_initialization_step.py +0 -57
- autobyteus/agent/hooks/__init__.py +0 -16
- autobyteus/agent/hooks/base_phase_hook.py +0 -78
- autobyteus/agent/hooks/hook_definition.py +0 -36
- autobyteus/agent/hooks/hook_meta.py +0 -37
- autobyteus/agent/hooks/hook_registry.py +0 -106
- autobyteus/agent/llm_response_processor/provider_aware_tool_usage_processor.py +0 -103
- autobyteus/agent/phases/__init__.py +0 -18
- autobyteus/agent/phases/discover.py +0 -53
- autobyteus/agent/phases/manager.py +0 -265
- autobyteus/agent/phases/transition_decorator.py +0 -40
- autobyteus/agent/phases/transition_info.py +0 -33
- autobyteus/agent/remote_agent.py +0 -244
- autobyteus/agent/workspace/workspace_definition.py +0 -36
- autobyteus/agent/workspace/workspace_meta.py +0 -37
- autobyteus/agent/workspace/workspace_registry.py +0 -72
- autobyteus/agent_team/bootstrap_steps/agent_team_runtime_queue_initialization_step.py +0 -25
- autobyteus/agent_team/bootstrap_steps/coordinator_prompt_preparation_step.py +0 -85
- autobyteus/agent_team/phases/__init__.py +0 -11
- autobyteus/agent_team/phases/agent_team_operational_phase.py +0 -19
- autobyteus/agent_team/phases/agent_team_phase_manager.py +0 -48
- autobyteus/llm/api/bedrock_llm.py +0 -92
- autobyteus/llm/api/groq_llm.py +0 -94
- autobyteus/llm/api/nvidia_llm.py +0 -108
- autobyteus/llm/utils/token_pricing_config.py +0 -87
- autobyteus/rpc/__init__.py +0 -73
- autobyteus/rpc/client/__init__.py +0 -17
- autobyteus/rpc/client/abstract_client_connection.py +0 -124
- autobyteus/rpc/client/client_connection_manager.py +0 -153
- autobyteus/rpc/client/sse_client_connection.py +0 -306
- autobyteus/rpc/client/stdio_client_connection.py +0 -280
- autobyteus/rpc/config/__init__.py +0 -13
- autobyteus/rpc/config/agent_server_config.py +0 -153
- autobyteus/rpc/config/agent_server_registry.py +0 -152
- autobyteus/rpc/hosting.py +0 -244
- autobyteus/rpc/protocol.py +0 -244
- autobyteus/rpc/server/__init__.py +0 -20
- autobyteus/rpc/server/agent_server_endpoint.py +0 -181
- autobyteus/rpc/server/base_method_handler.py +0 -40
- autobyteus/rpc/server/method_handlers.py +0 -259
- autobyteus/rpc/server/sse_server_handler.py +0 -182
- autobyteus/rpc/server/stdio_server_handler.py +0 -151
- autobyteus/rpc/server_main.py +0 -198
- autobyteus/rpc/transport_type.py +0 -13
- autobyteus/tools/bash/__init__.py +0 -2
- autobyteus/tools/bash/bash_executor.py +0 -100
- autobyteus/tools/browser/__init__.py +0 -2
- autobyteus/tools/browser/session_aware/browser_session_aware_navigate_to.py +0 -75
- autobyteus/tools/browser/session_aware/browser_session_aware_tool.py +0 -30
- autobyteus/tools/browser/session_aware/browser_session_aware_web_element_trigger.py +0 -154
- autobyteus/tools/browser/session_aware/browser_session_aware_webpage_reader.py +0 -89
- autobyteus/tools/browser/session_aware/browser_session_aware_webpage_screenshot_taker.py +0 -107
- autobyteus/tools/browser/session_aware/factory/browser_session_aware_web_element_trigger_factory.py +0 -14
- autobyteus/tools/browser/session_aware/factory/browser_session_aware_webpage_reader_factory.py +0 -26
- autobyteus/tools/browser/session_aware/factory/browser_session_aware_webpage_screenshot_taker_factory.py +0 -14
- autobyteus/tools/browser/session_aware/shared_browser_session.py +0 -11
- autobyteus/tools/browser/session_aware/shared_browser_session_manager.py +0 -25
- autobyteus/tools/browser/session_aware/web_element_action.py +0 -20
- autobyteus/tools/browser/standalone/__init__.py +0 -6
- autobyteus/tools/browser/standalone/factory/__init__.py +0 -0
- autobyteus/tools/browser/standalone/factory/webpage_reader_factory.py +0 -25
- autobyteus/tools/browser/standalone/factory/webpage_screenshot_taker_factory.py +0 -14
- autobyteus/tools/browser/standalone/navigate_to.py +0 -84
- autobyteus/tools/browser/standalone/web_page_pdf_generator.py +0 -101
- autobyteus/tools/browser/standalone/webpage_image_downloader.py +0 -169
- autobyteus/tools/browser/standalone/webpage_reader.py +0 -105
- autobyteus/tools/browser/standalone/webpage_screenshot_taker.py +0 -105
- autobyteus/tools/file/edit_file.py +0 -200
- autobyteus/tools/file/list_directory.py +0 -168
- autobyteus/tools/file/search_files.py +0 -188
- autobyteus/tools/timer.py +0 -175
- autobyteus/tools/usage/parsers/__init__.py +0 -22
- autobyteus/tools/usage/parsers/_json_extractor.py +0 -99
- autobyteus/tools/usage/parsers/_string_decoders.py +0 -18
- autobyteus/tools/usage/parsers/anthropic_xml_tool_usage_parser.py +0 -10
- autobyteus/tools/usage/parsers/base_parser.py +0 -41
- autobyteus/tools/usage/parsers/default_json_tool_usage_parser.py +0 -83
- autobyteus/tools/usage/parsers/default_xml_tool_usage_parser.py +0 -316
- autobyteus/tools/usage/parsers/exceptions.py +0 -13
- autobyteus/tools/usage/parsers/gemini_json_tool_usage_parser.py +0 -77
- autobyteus/tools/usage/parsers/openai_json_tool_usage_parser.py +0 -149
- autobyteus/tools/usage/parsers/provider_aware_tool_usage_parser.py +0 -59
- autobyteus/tools/usage/registries/tool_usage_parser_registry.py +0 -62
- autobyteus/workflow/phases/__init__.py +0 -11
- autobyteus/workflow/phases/workflow_operational_phase.py +0 -19
- autobyteus/workflow/phases/workflow_phase_manager.py +0 -48
- autobyteus-1.2.1.dist-info/METADATA +0 -205
- autobyteus-1.2.1.dist-info/RECORD +0 -511
- examples/__init__.py +0 -1
- examples/agent_team/__init__.py +0 -1
- examples/discover_phase_transitions.py +0 -104
- examples/run_agentic_software_engineer.py +0 -239
- examples/run_browser_agent.py +0 -262
- examples/run_google_slides_agent.py +0 -287
- examples/run_mcp_browser_client.py +0 -174
- examples/run_mcp_google_slides_client.py +0 -270
- examples/run_mcp_list_tools.py +0 -189
- examples/run_poem_writer.py +0 -284
- examples/run_sqlite_agent.py +0 -295
- /autobyteus/{tools/browser/session_aware → skills}/__init__.py +0 -0
- /autobyteus/tools/{browser/session_aware/factory → skill}/__init__.py +0 -0
- {autobyteus-1.2.1.dist-info → autobyteus-1.2.3.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
"""
|
|
2
|
+
XmlPatchFileToolParsingState: Streams <tool name="patch_file"> blocks.
|
|
3
|
+
|
|
4
|
+
This state specializes the generic XmlToolParsingState to stream patch/diff content
|
|
5
|
+
and capture the path for display. Argument parsing is handled later by the
|
|
6
|
+
ToolInvocationAdapter.
|
|
7
|
+
"""
|
|
8
|
+
from typing import TYPE_CHECKING, Optional
|
|
9
|
+
|
|
10
|
+
from .xml_tool_parsing_state import XmlToolParsingState
|
|
11
|
+
from ..events import SegmentType
|
|
12
|
+
|
|
13
|
+
if TYPE_CHECKING:
|
|
14
|
+
from ..parser_context import ParserContext
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class XmlPatchFileToolParsingState(XmlToolParsingState):
|
|
18
|
+
"""
|
|
19
|
+
Streams <tool name="patch_file"> tool calls.
|
|
20
|
+
|
|
21
|
+
This state operates similarly to XmlWriteFileToolParsingState but provides
|
|
22
|
+
a distinct type (PATCH_FILE) for unified diff content streaming.
|
|
23
|
+
|
|
24
|
+
Key differences from write_file:
|
|
25
|
+
- Content arg name: 'patch' instead of 'content'
|
|
26
|
+
- Sentinel markers: __START_PATCH__ / __END_PATCH__
|
|
27
|
+
- Segment type: PATCH_FILE
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
SEGMENT_TYPE = SegmentType.PATCH_FILE
|
|
31
|
+
START_CONTENT_MARKER = "__START_PATCH__"
|
|
32
|
+
END_CONTENT_MARKER = "__END_PATCH__"
|
|
33
|
+
CONTENT_ARG_CLOSE_TAG = "</arg>"
|
|
34
|
+
|
|
35
|
+
def __init__(self, context: "ParserContext", opening_tag: str):
|
|
36
|
+
super().__init__(context, opening_tag)
|
|
37
|
+
if self._tool_name != "patch_file":
|
|
38
|
+
pass
|
|
39
|
+
|
|
40
|
+
# Internal state for streaming
|
|
41
|
+
self._found_content_start = False
|
|
42
|
+
self._content_buffering = ""
|
|
43
|
+
self._captured_path: Optional[str] = None
|
|
44
|
+
self._defer_start = True # Defer emission until path is found
|
|
45
|
+
self._swallowing_remaining = False # Swallow closing tags
|
|
46
|
+
self._content_mode = "seek_marker"
|
|
47
|
+
self._content_seek_buffer = ""
|
|
48
|
+
self._marker_tail = ""
|
|
49
|
+
|
|
50
|
+
def run(self) -> None:
|
|
51
|
+
"""
|
|
52
|
+
Custom run loop to stream ONLY the patch argument content.
|
|
53
|
+
"""
|
|
54
|
+
from .text_state import TextState
|
|
55
|
+
|
|
56
|
+
if self._swallowing_remaining:
|
|
57
|
+
self._handle_swallowing()
|
|
58
|
+
return
|
|
59
|
+
|
|
60
|
+
# Note: We do NOT emit start immediately anymore.
|
|
61
|
+
|
|
62
|
+
if not self.context.has_more_chars():
|
|
63
|
+
return
|
|
64
|
+
|
|
65
|
+
chunk = self.context.consume_remaining()
|
|
66
|
+
|
|
67
|
+
if not self._found_content_start:
|
|
68
|
+
self._content_buffering += chunk
|
|
69
|
+
|
|
70
|
+
import re
|
|
71
|
+
|
|
72
|
+
# 1. Try to find path if missing
|
|
73
|
+
if not self._captured_path:
|
|
74
|
+
path_match = re.search(r'<arg\s+name=["\']path["\']>([^<]+)</arg>', self._content_buffering, re.IGNORECASE)
|
|
75
|
+
if path_match:
|
|
76
|
+
self._captured_path = path_match.group(1).strip()
|
|
77
|
+
# Now we have path, we can emit start if we were waiting for it
|
|
78
|
+
if self._defer_start and not self._segment_started:
|
|
79
|
+
# Construct metadata with path
|
|
80
|
+
meta = self._get_start_metadata()
|
|
81
|
+
meta["path"] = self._captured_path
|
|
82
|
+
self.context.emit_segment_start(self.SEGMENT_TYPE, **meta)
|
|
83
|
+
self._segment_started = True
|
|
84
|
+
self._defer_start = False
|
|
85
|
+
|
|
86
|
+
# 2. Look for patch content start (note: arg name is 'patch', not 'content')
|
|
87
|
+
match = re.search(r'<arg\s+name=["\']patch["\']>', self._content_buffering, re.IGNORECASE)
|
|
88
|
+
|
|
89
|
+
if match:
|
|
90
|
+
self._found_content_start = True
|
|
91
|
+
end_of_tag = match.end()
|
|
92
|
+
|
|
93
|
+
# If we still haven't emitted start (e.g. no path found but content started), emit now without path
|
|
94
|
+
if not self._segment_started:
|
|
95
|
+
self.context.emit_segment_start(self.SEGMENT_TYPE, **self._get_start_metadata())
|
|
96
|
+
self._segment_started = True
|
|
97
|
+
|
|
98
|
+
# Update path in metadata if we found it late (redundant but safe)
|
|
99
|
+
if self._captured_path:
|
|
100
|
+
self.context.update_current_segment_metadata(path=self._captured_path)
|
|
101
|
+
|
|
102
|
+
real_content = self._content_buffering[end_of_tag:]
|
|
103
|
+
self._content_buffering = ""
|
|
104
|
+
self._content_mode = "seek_marker"
|
|
105
|
+
self._content_seek_buffer = ""
|
|
106
|
+
self._marker_tail = ""
|
|
107
|
+
self._tail = ""
|
|
108
|
+
self._process_content_chunk(real_content)
|
|
109
|
+
else:
|
|
110
|
+
# If closing tool and still no content
|
|
111
|
+
if "</tool>" in self._content_buffering:
|
|
112
|
+
# If start never happened, force it
|
|
113
|
+
if not self._segment_started:
|
|
114
|
+
self.context.emit_segment_start(self.SEGMENT_TYPE, **self._get_start_metadata())
|
|
115
|
+
self._segment_started = True
|
|
116
|
+
|
|
117
|
+
self._on_segment_complete()
|
|
118
|
+
self.context.emit_segment_end()
|
|
119
|
+
self.context.transition_to(TextState(self.context))
|
|
120
|
+
else:
|
|
121
|
+
self._process_content_chunk(chunk)
|
|
122
|
+
|
|
123
|
+
def _process_content_chunk(self, chunk: str) -> None:
|
|
124
|
+
"""Process content chunk, supporting optional content markers."""
|
|
125
|
+
if not chunk:
|
|
126
|
+
return
|
|
127
|
+
|
|
128
|
+
if self._content_mode == "marker":
|
|
129
|
+
self._process_marker_content(chunk)
|
|
130
|
+
return
|
|
131
|
+
|
|
132
|
+
if self._content_mode == "default":
|
|
133
|
+
self._process_default_content(chunk)
|
|
134
|
+
return
|
|
135
|
+
|
|
136
|
+
self._process_seek_marker_content(chunk)
|
|
137
|
+
|
|
138
|
+
def _process_seek_marker_content(self, chunk: str) -> None:
|
|
139
|
+
"""Seek __START_PATCH__ before committing to default parsing."""
|
|
140
|
+
self._content_seek_buffer += chunk
|
|
141
|
+
|
|
142
|
+
start_idx = self._content_seek_buffer.find(self.START_CONTENT_MARKER)
|
|
143
|
+
if start_idx != -1:
|
|
144
|
+
after_start = self._content_seek_buffer[start_idx + len(self.START_CONTENT_MARKER):]
|
|
145
|
+
# Strip leading newline after marker to avoid empty first line
|
|
146
|
+
if after_start.startswith("\n"):
|
|
147
|
+
after_start = after_start[1:]
|
|
148
|
+
self._content_seek_buffer = ""
|
|
149
|
+
self._content_mode = "marker"
|
|
150
|
+
self._marker_tail = ""
|
|
151
|
+
self._tail = ""
|
|
152
|
+
if after_start:
|
|
153
|
+
self._process_marker_content(after_start)
|
|
154
|
+
return
|
|
155
|
+
|
|
156
|
+
closing_idx = self._content_seek_buffer.find(self.CONTENT_ARG_CLOSE_TAG)
|
|
157
|
+
if closing_idx != -1:
|
|
158
|
+
buffered = self._content_seek_buffer
|
|
159
|
+
self._content_seek_buffer = ""
|
|
160
|
+
self._content_mode = "default"
|
|
161
|
+
self._tail = ""
|
|
162
|
+
self._process_default_content(buffered)
|
|
163
|
+
return
|
|
164
|
+
|
|
165
|
+
stripped = self._content_seek_buffer.lstrip()
|
|
166
|
+
if stripped and not self.START_CONTENT_MARKER.startswith(stripped):
|
|
167
|
+
buffered = self._content_seek_buffer
|
|
168
|
+
self._content_seek_buffer = ""
|
|
169
|
+
self._content_mode = "default"
|
|
170
|
+
self._tail = ""
|
|
171
|
+
self._process_default_content(buffered)
|
|
172
|
+
|
|
173
|
+
def _process_default_content(self, chunk: str) -> None:
|
|
174
|
+
"""Process content chunk, stripping closing tags."""
|
|
175
|
+
closing_tag = self.CONTENT_ARG_CLOSE_TAG
|
|
176
|
+
combined = self._tail + chunk
|
|
177
|
+
|
|
178
|
+
idx = combined.find(closing_tag)
|
|
179
|
+
|
|
180
|
+
if idx != -1:
|
|
181
|
+
actual_content = combined[:idx]
|
|
182
|
+
if actual_content:
|
|
183
|
+
self.context.emit_segment_content(actual_content)
|
|
184
|
+
|
|
185
|
+
# We found the end of the content argument.
|
|
186
|
+
# Switch to swallowing mode to eat </arguments></tool>
|
|
187
|
+
self._tail = ""
|
|
188
|
+
remainder = combined[idx + len(closing_tag):]
|
|
189
|
+
self._content_buffering = remainder
|
|
190
|
+
self._swallowing_remaining = True
|
|
191
|
+
|
|
192
|
+
# Immediately try to finish if we have the closing tags
|
|
193
|
+
self._handle_swallowing()
|
|
194
|
+
return
|
|
195
|
+
|
|
196
|
+
holdback_len = len(closing_tag) - 1
|
|
197
|
+
if len(combined) > holdback_len:
|
|
198
|
+
safe = combined[:-holdback_len]
|
|
199
|
+
if safe:
|
|
200
|
+
self.context.emit_segment_content(safe)
|
|
201
|
+
self._tail = combined[-holdback_len:]
|
|
202
|
+
else:
|
|
203
|
+
self._tail = combined
|
|
204
|
+
|
|
205
|
+
def _process_marker_content(self, chunk: str) -> None:
|
|
206
|
+
"""Process content chunk when inside __START_PATCH__/__END_PATCH__ markers.
|
|
207
|
+
|
|
208
|
+
The __END_PATCH__ sentinel is only valid if followed by optional whitespace
|
|
209
|
+
and then </arg>. This prevents false positives when patch content contains
|
|
210
|
+
the literal __END_PATCH__ string.
|
|
211
|
+
"""
|
|
212
|
+
import re
|
|
213
|
+
|
|
214
|
+
combined = self._marker_tail + chunk
|
|
215
|
+
end_marker = self.END_CONTENT_MARKER
|
|
216
|
+
closing_tag = self.CONTENT_ARG_CLOSE_TAG
|
|
217
|
+
|
|
218
|
+
# Priority 1: Check for the explicit end marker WITH lookahead validation
|
|
219
|
+
search_start = 0
|
|
220
|
+
while True:
|
|
221
|
+
idx = combined.find(end_marker, search_start)
|
|
222
|
+
if idx == -1:
|
|
223
|
+
break
|
|
224
|
+
|
|
225
|
+
remainder_after_marker = combined[idx + len(end_marker):]
|
|
226
|
+
|
|
227
|
+
# Validate: must be followed by whitespace* + </arg>
|
|
228
|
+
if re.match(r'^\s*</arg>', remainder_after_marker):
|
|
229
|
+
# Valid sentinel - emit content and transition
|
|
230
|
+
actual_content = combined[:idx]
|
|
231
|
+
if actual_content:
|
|
232
|
+
self.context.emit_segment_content(actual_content)
|
|
233
|
+
|
|
234
|
+
self._marker_tail = ""
|
|
235
|
+
remainder = combined[idx + len(end_marker):]
|
|
236
|
+
self._content_buffering = remainder
|
|
237
|
+
self._swallowing_remaining = True
|
|
238
|
+
self._handle_swallowing()
|
|
239
|
+
return
|
|
240
|
+
elif remainder_after_marker.strip() == "":
|
|
241
|
+
# Indeterminate - need more data
|
|
242
|
+
# Hold back from idx onwards
|
|
243
|
+
if idx > 0:
|
|
244
|
+
safe_content = combined[:idx]
|
|
245
|
+
if safe_content:
|
|
246
|
+
self.context.emit_segment_content(safe_content)
|
|
247
|
+
self._marker_tail = combined[idx:]
|
|
248
|
+
else:
|
|
249
|
+
self._marker_tail = combined
|
|
250
|
+
return
|
|
251
|
+
else:
|
|
252
|
+
# False positive
|
|
253
|
+
search_start = idx + len(end_marker)
|
|
254
|
+
|
|
255
|
+
# Priority 2: Check for closing arg tag as fallback (missing sentinel case)
|
|
256
|
+
idx_close = combined.find(closing_tag)
|
|
257
|
+
if idx_close != -1:
|
|
258
|
+
remainder_after_close = combined[idx_close + len(closing_tag):]
|
|
259
|
+
|
|
260
|
+
# Check if followed by standard XML closure (ignoring whitespace)
|
|
261
|
+
is_valid_closure = False
|
|
262
|
+
if re.match(r'^\s*(?:</arguments>|</tool>)', remainder_after_close):
|
|
263
|
+
is_valid_closure = True
|
|
264
|
+
|
|
265
|
+
# If we have indeterminate whitespace, we must hold back to be sure
|
|
266
|
+
elif remainder_after_close.strip() == "":
|
|
267
|
+
self._marker_tail = combined
|
|
268
|
+
return
|
|
269
|
+
|
|
270
|
+
if is_valid_closure:
|
|
271
|
+
actual_content = combined[:idx_close]
|
|
272
|
+
|
|
273
|
+
# Remove trailing newline for clean file content
|
|
274
|
+
if re.search(r'\n\s*$', actual_content):
|
|
275
|
+
actual_content = re.sub(r'\n\s*$', '', actual_content)
|
|
276
|
+
|
|
277
|
+
if actual_content:
|
|
278
|
+
self.context.emit_segment_content(actual_content)
|
|
279
|
+
|
|
280
|
+
self._marker_tail = ""
|
|
281
|
+
remainder = combined[idx_close + len(closing_tag):]
|
|
282
|
+
self._content_buffering = remainder
|
|
283
|
+
self._swallowing_remaining = True
|
|
284
|
+
self._handle_swallowing()
|
|
285
|
+
return
|
|
286
|
+
|
|
287
|
+
# Holdback logic - hold back enough to detect EITHER marker OR closing_tag + context
|
|
288
|
+
# __END_PATCH__ is shorter than __END_CONTENT__ but still needs buffer
|
|
289
|
+
max_holdback = 35
|
|
290
|
+
|
|
291
|
+
if len(combined) > max_holdback:
|
|
292
|
+
safe = combined[:-max_holdback]
|
|
293
|
+
if safe:
|
|
294
|
+
self.context.emit_segment_content(safe)
|
|
295
|
+
self._marker_tail = combined[-max_holdback:]
|
|
296
|
+
else:
|
|
297
|
+
self._marker_tail = combined
|
|
298
|
+
|
|
299
|
+
def _handle_swallowing(self) -> None:
|
|
300
|
+
"""Consume stream until </tool> is found."""
|
|
301
|
+
from .text_state import TextState
|
|
302
|
+
|
|
303
|
+
# Add any new data to buffer
|
|
304
|
+
self._content_buffering += self.context.consume_remaining()
|
|
305
|
+
|
|
306
|
+
closing_tag = "</tool>"
|
|
307
|
+
idx = self._content_buffering.find(closing_tag)
|
|
308
|
+
|
|
309
|
+
if idx != -1:
|
|
310
|
+
# We found the end!
|
|
311
|
+
# Anything after </tool> belongs to the next state (TextState)
|
|
312
|
+
remainder = self._content_buffering[idx + len(closing_tag):]
|
|
313
|
+
|
|
314
|
+
self._on_segment_complete()
|
|
315
|
+
self.context.emit_segment_end()
|
|
316
|
+
if remainder:
|
|
317
|
+
# Rewind so the next state can parse the remainder (e.g., another tool tag).
|
|
318
|
+
self.context.rewind_by(len(remainder))
|
|
319
|
+
self.context.transition_to(TextState(self.context))
|
|
320
|
+
else:
|
|
321
|
+
# Nothing yet, keep swallowing
|
|
322
|
+
holdback_len = len(closing_tag) - 1
|
|
323
|
+
if len(self._content_buffering) > holdback_len:
|
|
324
|
+
# Discard safe prefix
|
|
325
|
+
self._content_buffering = self._content_buffering[-holdback_len:]
|
|
326
|
+
|
|
327
|
+
def _on_segment_complete(self) -> None:
|
|
328
|
+
return None
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
"""
|
|
2
|
+
XmlRunBashToolParsingState: Streams <tool name="run_bash"> blocks.
|
|
3
|
+
|
|
4
|
+
This state specializes the generic XmlToolParsingState to stream the command
|
|
5
|
+
content without parsing arguments. Argument parsing is handled later by the
|
|
6
|
+
ToolInvocationAdapter.
|
|
7
|
+
"""
|
|
8
|
+
from typing import TYPE_CHECKING
|
|
9
|
+
|
|
10
|
+
from .xml_tool_parsing_state import XmlToolParsingState
|
|
11
|
+
from ..events import SegmentType
|
|
12
|
+
|
|
13
|
+
if TYPE_CHECKING:
|
|
14
|
+
from ..parser_context import ParserContext
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class XmlRunBashToolParsingState(XmlToolParsingState):
|
|
18
|
+
"""
|
|
19
|
+
Parses <tool name="run_bash"> tool calls.
|
|
20
|
+
|
|
21
|
+
This state operates identically to XmlToolParsingState but provides
|
|
22
|
+
a distinct type (RUN_BASH) and specialized metadata handling if needed.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
SEGMENT_TYPE = SegmentType.RUN_BASH
|
|
26
|
+
|
|
27
|
+
def __init__(self, context: "ParserContext", opening_tag: str):
|
|
28
|
+
super().__init__(context, opening_tag)
|
|
29
|
+
if self._tool_name != "run_bash":
|
|
30
|
+
pass
|
|
31
|
+
|
|
32
|
+
self._found_content_start = False
|
|
33
|
+
self._content_buffering = ""
|
|
34
|
+
self._swallowing_remaining = False
|
|
35
|
+
|
|
36
|
+
def run(self) -> None:
|
|
37
|
+
"""
|
|
38
|
+
Custom run loop to stream ONLY the command argument.
|
|
39
|
+
"""
|
|
40
|
+
from .text_state import TextState
|
|
41
|
+
|
|
42
|
+
if self._swallowing_remaining:
|
|
43
|
+
self._handle_swallowing()
|
|
44
|
+
return
|
|
45
|
+
|
|
46
|
+
if not self._segment_started:
|
|
47
|
+
self.context.emit_segment_start(self.SEGMENT_TYPE, **self._get_start_metadata())
|
|
48
|
+
self._segment_started = True
|
|
49
|
+
|
|
50
|
+
if not self.context.has_more_chars():
|
|
51
|
+
return
|
|
52
|
+
|
|
53
|
+
chunk = self.context.consume_remaining()
|
|
54
|
+
|
|
55
|
+
if not self._found_content_start:
|
|
56
|
+
self._content_buffering += chunk
|
|
57
|
+
|
|
58
|
+
import re
|
|
59
|
+
# Look for command start
|
|
60
|
+
match = re.search(r'<arg\s+name=["\']command["\']>', self._content_buffering, re.IGNORECASE)
|
|
61
|
+
|
|
62
|
+
if match:
|
|
63
|
+
self._found_content_start = True
|
|
64
|
+
end_of_tag = match.end()
|
|
65
|
+
|
|
66
|
+
real_content = self._content_buffering[end_of_tag:]
|
|
67
|
+
self._content_buffering = ""
|
|
68
|
+
self._process_content_chunk(real_content)
|
|
69
|
+
else:
|
|
70
|
+
if "</tool>" in self._content_buffering:
|
|
71
|
+
self._on_segment_complete()
|
|
72
|
+
self.context.emit_segment_end()
|
|
73
|
+
self.context.transition_to(TextState(self.context))
|
|
74
|
+
else:
|
|
75
|
+
self._process_content_chunk(chunk)
|
|
76
|
+
|
|
77
|
+
def _process_content_chunk(self, chunk: str) -> None:
|
|
78
|
+
"""Process content chunk, stripping closing tags."""
|
|
79
|
+
|
|
80
|
+
closing_tag = "</arg>"
|
|
81
|
+
combined = self._tail + chunk
|
|
82
|
+
|
|
83
|
+
idx = combined.find(closing_tag)
|
|
84
|
+
if idx != -1:
|
|
85
|
+
actual_content = combined[:idx]
|
|
86
|
+
if actual_content:
|
|
87
|
+
self.context.emit_segment_content(actual_content)
|
|
88
|
+
|
|
89
|
+
self._tail = ""
|
|
90
|
+
remainder = combined[idx + len(closing_tag):]
|
|
91
|
+
self._content_buffering = remainder
|
|
92
|
+
self._swallowing_remaining = True
|
|
93
|
+
|
|
94
|
+
self._handle_swallowing()
|
|
95
|
+
return
|
|
96
|
+
|
|
97
|
+
holdback_len = len(closing_tag) - 1
|
|
98
|
+
if len(combined) > holdback_len:
|
|
99
|
+
safe = combined[:-holdback_len]
|
|
100
|
+
self.context.emit_segment_content(safe)
|
|
101
|
+
self._tail = combined[-holdback_len:]
|
|
102
|
+
else:
|
|
103
|
+
self._tail = combined
|
|
104
|
+
|
|
105
|
+
def _handle_swallowing(self) -> None:
|
|
106
|
+
"""Consume stream until </tool> is found."""
|
|
107
|
+
from .text_state import TextState
|
|
108
|
+
|
|
109
|
+
self._content_buffering += self.context.consume_remaining()
|
|
110
|
+
|
|
111
|
+
closing_tag = "</tool>"
|
|
112
|
+
idx = self._content_buffering.find(closing_tag)
|
|
113
|
+
|
|
114
|
+
if idx != -1:
|
|
115
|
+
remainder = self._content_buffering[idx + len(closing_tag):]
|
|
116
|
+
|
|
117
|
+
self._on_segment_complete()
|
|
118
|
+
self.context.emit_segment_end()
|
|
119
|
+
if remainder:
|
|
120
|
+
# Rewind so the next state can parse the remainder (e.g., another tool tag).
|
|
121
|
+
self.context.rewind_by(len(remainder))
|
|
122
|
+
self.context.transition_to(TextState(self.context))
|
|
123
|
+
else:
|
|
124
|
+
holdback_len = len(closing_tag) - 1
|
|
125
|
+
if len(self._content_buffering) > holdback_len:
|
|
126
|
+
self._content_buffering = self._content_buffering[-holdback_len:]
|
|
127
|
+
|
|
128
|
+
def _on_segment_complete(self) -> None:
|
|
129
|
+
return None
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
"""
|
|
2
|
+
XmlTagInitializationState: Analyzes potential XML tags after a '<' is detected.
|
|
3
|
+
|
|
4
|
+
This state buffers characters to identify special tags like <tool, <write_file,
|
|
5
|
+
<run_bash and transitions to the appropriate specialized state.
|
|
6
|
+
|
|
7
|
+
UNIFORM HANDOFF: All content states receive the complete opening_tag and handle
|
|
8
|
+
their own initialization consistently.
|
|
9
|
+
"""
|
|
10
|
+
from typing import TYPE_CHECKING
|
|
11
|
+
|
|
12
|
+
from .base_state import BaseState
|
|
13
|
+
from ..events import SegmentType
|
|
14
|
+
|
|
15
|
+
if TYPE_CHECKING:
|
|
16
|
+
from ..parser_context import ParserContext
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class XmlTagInitializationState(BaseState):
|
|
20
|
+
"""
|
|
21
|
+
Analyzes a potential XML tag to determine the correct specialized state.
|
|
22
|
+
|
|
23
|
+
This state is entered when a '<' is detected. It buffers characters
|
|
24
|
+
to identify tags like <tool, <write_file, <run_bash.
|
|
25
|
+
|
|
26
|
+
If no known tag is detected, the buffered content is emitted as text.
|
|
27
|
+
|
|
28
|
+
UNIFORM HANDOFF PATTERN:
|
|
29
|
+
All content-parsing states receive (context, opening_tag) and handle
|
|
30
|
+
their own buffer initialization consistently.
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
# Known tag prefixes (lowercase for case-insensitive matching)
|
|
34
|
+
POSSIBLE_WRITE_FILE = "<write_file"
|
|
35
|
+
POSSIBLE_RUN_BASH = "<run_bash"
|
|
36
|
+
POSSIBLE_TOOL = "<tool"
|
|
37
|
+
|
|
38
|
+
def __init__(self, context: "ParserContext"):
|
|
39
|
+
super().__init__(context)
|
|
40
|
+
# Consume the '<' that triggered this state
|
|
41
|
+
self.context.advance()
|
|
42
|
+
self._tag_buffer = "<"
|
|
43
|
+
|
|
44
|
+
def run(self) -> None:
|
|
45
|
+
"""
|
|
46
|
+
Buffer characters and identify the tag type.
|
|
47
|
+
|
|
48
|
+
Transitions to specialized states or reverts to text if unknown.
|
|
49
|
+
"""
|
|
50
|
+
from .text_state import TextState
|
|
51
|
+
from .custom_xml_tag_write_file_parsing_state import CustomXmlTagWriteFileParsingState
|
|
52
|
+
from .custom_xml_tag_run_bash_parsing_state import CustomXmlTagRunBashParsingState
|
|
53
|
+
from .xml_tool_parsing_state import XmlToolParsingState
|
|
54
|
+
from .xml_write_file_tool_parsing_state import XmlWriteFileToolParsingState
|
|
55
|
+
from .xml_run_bash_tool_parsing_state import XmlRunBashToolParsingState
|
|
56
|
+
|
|
57
|
+
if not self.context.has_more_chars():
|
|
58
|
+
return
|
|
59
|
+
|
|
60
|
+
start_pos = self.context.get_position()
|
|
61
|
+
end_idx = self.context.find(">", start_pos)
|
|
62
|
+
|
|
63
|
+
if end_idx == -1:
|
|
64
|
+
self._tag_buffer += self.context.consume_remaining()
|
|
65
|
+
|
|
66
|
+
lower_buffer = self._tag_buffer.lower()
|
|
67
|
+
could_be_write_file = (
|
|
68
|
+
self.POSSIBLE_WRITE_FILE.startswith(lower_buffer) or
|
|
69
|
+
lower_buffer.startswith(self.POSSIBLE_WRITE_FILE)
|
|
70
|
+
)
|
|
71
|
+
could_be_run_bash = (
|
|
72
|
+
self.POSSIBLE_RUN_BASH.startswith(lower_buffer) or
|
|
73
|
+
lower_buffer.startswith(self.POSSIBLE_RUN_BASH)
|
|
74
|
+
)
|
|
75
|
+
could_be_tool = (
|
|
76
|
+
self.POSSIBLE_TOOL.startswith(lower_buffer) or
|
|
77
|
+
lower_buffer.startswith(self.POSSIBLE_TOOL)
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
if not (could_be_write_file or could_be_run_bash or could_be_tool):
|
|
81
|
+
self.context.append_text_segment(self._tag_buffer)
|
|
82
|
+
self.context.transition_to(TextState(self.context))
|
|
83
|
+
return
|
|
84
|
+
|
|
85
|
+
self._tag_buffer += self.context.consume(end_idx - start_pos + 1)
|
|
86
|
+
lower_buffer = self._tag_buffer.lower()
|
|
87
|
+
|
|
88
|
+
# Handle legacy <write_file> tag
|
|
89
|
+
if lower_buffer.startswith(self.POSSIBLE_WRITE_FILE):
|
|
90
|
+
if self.context.get_current_segment_type() == SegmentType.TEXT:
|
|
91
|
+
self.context.emit_segment_end()
|
|
92
|
+
self.context.transition_to(
|
|
93
|
+
CustomXmlTagWriteFileParsingState(self.context, self._tag_buffer)
|
|
94
|
+
)
|
|
95
|
+
return
|
|
96
|
+
|
|
97
|
+
if lower_buffer.startswith(self.POSSIBLE_RUN_BASH):
|
|
98
|
+
if self.context.get_current_segment_type() == SegmentType.TEXT:
|
|
99
|
+
self.context.emit_segment_end()
|
|
100
|
+
self.context.transition_to(
|
|
101
|
+
CustomXmlTagRunBashParsingState(self.context, self._tag_buffer)
|
|
102
|
+
)
|
|
103
|
+
return
|
|
104
|
+
|
|
105
|
+
if lower_buffer.startswith(self.POSSIBLE_TOOL):
|
|
106
|
+
if self.context.parse_tool_calls:
|
|
107
|
+
if self.context.get_current_segment_type() == SegmentType.TEXT:
|
|
108
|
+
self.context.emit_segment_end()
|
|
109
|
+
|
|
110
|
+
# Extract tool name
|
|
111
|
+
import re
|
|
112
|
+
name_match = re.search(r'name\s*=\s*["\']([^"\']+)["\']', self._tag_buffer, re.IGNORECASE)
|
|
113
|
+
|
|
114
|
+
if name_match:
|
|
115
|
+
tool_name = name_match.group(1).lower()
|
|
116
|
+
|
|
117
|
+
# --- Registry Lookup ---
|
|
118
|
+
from ..xml_tool_parsing_state_registry import XmlToolParsingStateRegistry
|
|
119
|
+
registry = XmlToolParsingStateRegistry()
|
|
120
|
+
|
|
121
|
+
# Dispatch
|
|
122
|
+
state_class = registry.get_state_for_tool(tool_name)
|
|
123
|
+
if state_class:
|
|
124
|
+
self.context.transition_to(state_class(self.context, self._tag_buffer))
|
|
125
|
+
else:
|
|
126
|
+
# Fallback to generic tool state
|
|
127
|
+
self.context.transition_to(XmlToolParsingState(self.context, self._tag_buffer))
|
|
128
|
+
else:
|
|
129
|
+
# No name found, generic fallback
|
|
130
|
+
self.context.transition_to(XmlToolParsingState(self.context, self._tag_buffer))
|
|
131
|
+
else:
|
|
132
|
+
self.context.append_text_segment(self._tag_buffer)
|
|
133
|
+
self.context.transition_to(TextState(self.context))
|
|
134
|
+
return
|
|
135
|
+
|
|
136
|
+
self.context.append_text_segment(self._tag_buffer)
|
|
137
|
+
self.context.transition_to(TextState(self.context))
|
|
138
|
+
|
|
139
|
+
def finalize(self) -> None:
|
|
140
|
+
"""
|
|
141
|
+
Called when stream ends while in this state.
|
|
142
|
+
|
|
143
|
+
Emit any buffered content as text.
|
|
144
|
+
"""
|
|
145
|
+
from .text_state import TextState
|
|
146
|
+
|
|
147
|
+
if self._tag_buffer:
|
|
148
|
+
self.context.append_text_segment(self._tag_buffer)
|
|
149
|
+
self._tag_buffer = ""
|
|
150
|
+
|
|
151
|
+
self.context.transition_to(TextState(self.context))
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"""
|
|
2
|
+
XmlToolParsingState: Streams <tool name="...">...</tool> blocks.
|
|
3
|
+
|
|
4
|
+
This state handles tool call boundaries and content streaming. Tool arguments
|
|
5
|
+
are parsed later by the ToolInvocationAdapter.
|
|
6
|
+
"""
|
|
7
|
+
import re
|
|
8
|
+
from typing import TYPE_CHECKING, Optional
|
|
9
|
+
|
|
10
|
+
from .delimited_content_state import DelimitedContentState
|
|
11
|
+
from ..events import SegmentType
|
|
12
|
+
|
|
13
|
+
if TYPE_CHECKING:
|
|
14
|
+
from ..parser_context import ParserContext
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class XmlToolParsingState(DelimitedContentState):
|
|
18
|
+
"""
|
|
19
|
+
Streams tool call blocks.
|
|
20
|
+
|
|
21
|
+
Expected format: <tool name="..."><arg>value</arg></tool>
|
|
22
|
+
|
|
23
|
+
Supports two argument formats:
|
|
24
|
+
1. Wrapped: <arguments><arg1>value1</arg1></arguments>
|
|
25
|
+
2. Direct: <arg1>value1</arg1><arg2>value2</arg2>
|
|
26
|
+
|
|
27
|
+
The state:
|
|
28
|
+
1. Extracts tool name from the opening tag
|
|
29
|
+
2. Emits SEGMENT_START with tool metadata
|
|
30
|
+
3. Streams raw content for real-time display
|
|
31
|
+
4. Emits SEGMENT_END when </tool> is found
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
# Pattern to extract tool name
|
|
35
|
+
NAME_PATTERN = re.compile(r'name\s*=\s*["\']([^"\']+)["\']', re.IGNORECASE)
|
|
36
|
+
CLOSING_TAG = "</tool>"
|
|
37
|
+
SEGMENT_TYPE = SegmentType.TOOL_CALL
|
|
38
|
+
def __init__(self, context: "ParserContext", opening_tag: str):
|
|
39
|
+
"""
|
|
40
|
+
Initialize the tool parsing state.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
context: The parser context.
|
|
44
|
+
opening_tag: The complete opening tag (e.g., '<tool name="read_file">').
|
|
45
|
+
"""
|
|
46
|
+
super().__init__(context, opening_tag)
|
|
47
|
+
self._tool_name: Optional[str] = None
|
|
48
|
+
|
|
49
|
+
# Extract tool name from opening tag
|
|
50
|
+
match = self.NAME_PATTERN.search(opening_tag)
|
|
51
|
+
if match:
|
|
52
|
+
self._tool_name = match.group(1)
|
|
53
|
+
|
|
54
|
+
def _can_start_segment(self) -> bool:
|
|
55
|
+
return self._tool_name is not None
|
|
56
|
+
|
|
57
|
+
def _get_start_metadata(self) -> dict:
|
|
58
|
+
return {"tool_name": self._tool_name} if self._tool_name else {}
|
|
59
|
+
|
|
60
|
+
def _on_segment_complete(self) -> None:
|
|
61
|
+
return None
|
|
62
|
+
|
|
63
|
+
# finalize inherited from DelimitedContentState
|