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
|
@@ -20,21 +20,26 @@ if TYPE_CHECKING:
|
|
|
20
20
|
|
|
21
21
|
logger = logging.getLogger(__name__)
|
|
22
22
|
|
|
23
|
+
# A sentinel object to differentiate between a cached value of None and an unset cache.
|
|
24
|
+
_CACHE_NOT_SET = object()
|
|
25
|
+
|
|
23
26
|
class ToolDefinition:
|
|
24
27
|
"""
|
|
25
28
|
Represents the definition of a tool, containing its metadata and the means
|
|
26
29
|
to create an instance. It can generate provider-agnostic usage information on demand.
|
|
30
|
+
This class now supports dynamic schema generation with caching.
|
|
27
31
|
"""
|
|
28
32
|
def __init__(self,
|
|
29
33
|
name: str,
|
|
30
34
|
description: str,
|
|
31
|
-
argument_schema: Optional['ParameterSchema'],
|
|
32
35
|
origin: ToolOrigin,
|
|
33
36
|
category: str,
|
|
34
|
-
|
|
37
|
+
argument_schema_provider: Callable[[], Optional['ParameterSchema']],
|
|
38
|
+
config_schema_provider: Callable[[], Optional['ParameterSchema']],
|
|
35
39
|
tool_class: Optional[Type['BaseTool']] = None,
|
|
36
40
|
custom_factory: Optional[Callable[['ToolConfig'], 'BaseTool']] = None,
|
|
37
|
-
metadata: Optional[Dict[str, Any]] = None
|
|
41
|
+
metadata: Optional[Dict[str, Any]] = None,
|
|
42
|
+
description_provider: Optional[Callable[[], str]] = None):
|
|
38
43
|
"""
|
|
39
44
|
Initializes the ToolDefinition.
|
|
40
45
|
"""
|
|
@@ -53,10 +58,10 @@ class ToolDefinition:
|
|
|
53
58
|
if custom_factory and not callable(custom_factory):
|
|
54
59
|
raise TypeError(f"ToolDefinition '{name}' requires a callable for 'custom_factory'.")
|
|
55
60
|
|
|
56
|
-
if
|
|
57
|
-
raise TypeError(f"ToolDefinition '{name}'
|
|
58
|
-
if
|
|
59
|
-
raise TypeError(f"ToolDefinition '{name}'
|
|
61
|
+
if not callable(argument_schema_provider):
|
|
62
|
+
raise TypeError(f"ToolDefinition '{name}' requires a callable for 'argument_schema_provider'.")
|
|
63
|
+
if not callable(config_schema_provider):
|
|
64
|
+
raise TypeError(f"ToolDefinition '{name}' requires a callable for 'config_schema_provider'.")
|
|
60
65
|
if not isinstance(origin, ToolOrigin):
|
|
61
66
|
raise TypeError(f"ToolDefinition '{name}' requires a ToolOrigin for 'origin'. Got {type(origin)}")
|
|
62
67
|
|
|
@@ -65,14 +70,31 @@ class ToolDefinition:
|
|
|
65
70
|
raise ValueError(f"ToolDefinition '{name}' with origin MCP must provide a 'mcp_server_id' in its metadata.")
|
|
66
71
|
|
|
67
72
|
self._name = name
|
|
73
|
+
# Prefer an explicit description provider, otherwise derive one from the tool class when available.
|
|
74
|
+
if description_provider is not None and not callable(description_provider):
|
|
75
|
+
raise TypeError(f"ToolDefinition '{name}' requires a callable for 'description_provider' if provided.")
|
|
76
|
+
|
|
77
|
+
if description_provider:
|
|
78
|
+
self._description_provider = description_provider
|
|
79
|
+
elif tool_class is not None and hasattr(tool_class, "get_description") and callable(getattr(tool_class, "get_description")):
|
|
80
|
+
# Use the tool class' get_description as a dynamic provider by default.
|
|
81
|
+
self._description_provider = tool_class.get_description
|
|
82
|
+
else:
|
|
83
|
+
# Fall back to a static description provider.
|
|
84
|
+
self._description_provider = lambda: description
|
|
85
|
+
|
|
68
86
|
self._description = description
|
|
69
|
-
self._argument_schema: Optional['ParameterSchema'] = argument_schema
|
|
70
|
-
self._config_schema: Optional['ParameterSchema'] = config_schema
|
|
71
87
|
self._tool_class = tool_class
|
|
72
88
|
self._custom_factory = custom_factory
|
|
73
89
|
self._origin = origin
|
|
74
90
|
self._category = category
|
|
75
91
|
self._metadata = metadata or {}
|
|
92
|
+
|
|
93
|
+
# Store schema providers and initialize caches
|
|
94
|
+
self._argument_schema_provider = argument_schema_provider
|
|
95
|
+
self._config_schema_provider = config_schema_provider
|
|
96
|
+
self._cached_argument_schema: Any = _CACHE_NOT_SET
|
|
97
|
+
self._cached_config_schema: Any = _CACHE_NOT_SET
|
|
76
98
|
|
|
77
99
|
logger.debug(f"ToolDefinition created for tool '{self.name}'.")
|
|
78
100
|
|
|
@@ -85,16 +107,85 @@ class ToolDefinition:
|
|
|
85
107
|
def tool_class(self) -> Optional[Type['BaseTool']]: return self._tool_class
|
|
86
108
|
@property
|
|
87
109
|
def custom_factory(self) -> Optional[Callable[['ToolConfig'], 'BaseTool']]: return self._custom_factory
|
|
110
|
+
|
|
88
111
|
@property
|
|
89
|
-
def argument_schema(self) -> Optional['ParameterSchema']:
|
|
112
|
+
def argument_schema(self) -> Optional['ParameterSchema']:
|
|
113
|
+
"""On-demand schema generation and caching for argument_schema."""
|
|
114
|
+
if self._cached_argument_schema is _CACHE_NOT_SET:
|
|
115
|
+
logger.debug(f"Cache miss for argument_schema of tool '{self.name}'. Generating...")
|
|
116
|
+
try:
|
|
117
|
+
self._cached_argument_schema = self._argument_schema_provider()
|
|
118
|
+
except Exception as e:
|
|
119
|
+
logger.warning(
|
|
120
|
+
f"Failed to generate argument schema for tool '{self.name}' due to an error. "
|
|
121
|
+
f"The tool will have no arguments. Error: {e}",
|
|
122
|
+
exc_info=True
|
|
123
|
+
)
|
|
124
|
+
self._cached_argument_schema = None
|
|
125
|
+
return self._cached_argument_schema
|
|
126
|
+
|
|
90
127
|
@property
|
|
91
|
-
def config_schema(self) -> Optional['ParameterSchema']:
|
|
128
|
+
def config_schema(self) -> Optional['ParameterSchema']:
|
|
129
|
+
"""On-demand schema generation and caching for config_schema."""
|
|
130
|
+
if self._cached_config_schema is _CACHE_NOT_SET:
|
|
131
|
+
logger.debug(f"Cache miss for config_schema of tool '{self.name}'. Generating...")
|
|
132
|
+
try:
|
|
133
|
+
self._cached_config_schema = self._config_schema_provider()
|
|
134
|
+
except Exception as e:
|
|
135
|
+
logger.warning(
|
|
136
|
+
f"Failed to generate config schema for tool '{self.name}' due to an error. "
|
|
137
|
+
f"The tool will have no config. Error: {e}",
|
|
138
|
+
exc_info=True
|
|
139
|
+
)
|
|
140
|
+
self._cached_config_schema = None
|
|
141
|
+
return self._cached_config_schema
|
|
142
|
+
|
|
92
143
|
@property
|
|
93
144
|
def origin(self) -> ToolOrigin: return self._origin
|
|
94
145
|
@property
|
|
95
146
|
def category(self) -> str: return self._category
|
|
96
147
|
@property
|
|
97
148
|
def metadata(self) -> Dict[str, Any]: return self._metadata
|
|
149
|
+
|
|
150
|
+
def reload_cached_schema(self) -> None:
|
|
151
|
+
"""
|
|
152
|
+
Actively re-generates the schemas from their providers and updates the cache.
|
|
153
|
+
Also refreshes the description if a provider is available. This is an eager operation.
|
|
154
|
+
"""
|
|
155
|
+
logger.info(f"Eagerly reloading schema cache for tool '{self.name}'.")
|
|
156
|
+
self._reload_description()
|
|
157
|
+
self._cached_argument_schema = _CACHE_NOT_SET
|
|
158
|
+
self._cached_config_schema = _CACHE_NOT_SET
|
|
159
|
+
# The schemas will be regenerated on the next property access.
|
|
160
|
+
# To make it fully eager, we can trigger the access here.
|
|
161
|
+
_ = self.argument_schema
|
|
162
|
+
_ = self.config_schema
|
|
163
|
+
|
|
164
|
+
def _reload_description(self) -> None:
|
|
165
|
+
"""
|
|
166
|
+
Refreshes the cached description using the provider if available.
|
|
167
|
+
"""
|
|
168
|
+
if not self._description_provider:
|
|
169
|
+
return
|
|
170
|
+
try:
|
|
171
|
+
new_description = self._description_provider()
|
|
172
|
+
if isinstance(new_description, str) and new_description:
|
|
173
|
+
if new_description != self._description:
|
|
174
|
+
logger.info(
|
|
175
|
+
f"Description for tool '{self.name}' updated during reload."
|
|
176
|
+
)
|
|
177
|
+
self._description = new_description
|
|
178
|
+
else:
|
|
179
|
+
logger.warning(
|
|
180
|
+
f"Description provider for tool '{self.name}' returned an invalid value. "
|
|
181
|
+
"Keeping existing description."
|
|
182
|
+
)
|
|
183
|
+
except Exception as exc:
|
|
184
|
+
logger.warning(
|
|
185
|
+
f"Failed to refresh description for tool '{self.name}' during reload: {exc}. "
|
|
186
|
+
"Keeping existing description.",
|
|
187
|
+
exc_info=True
|
|
188
|
+
)
|
|
98
189
|
|
|
99
190
|
# --- Convenience Schema/Example Generation API (using default formatters) ---
|
|
100
191
|
def get_usage_xml(self, provider: Optional[LLMProvider] = None) -> str:
|
|
@@ -132,14 +223,17 @@ class ToolDefinition:
|
|
|
132
223
|
# --- Other methods ---
|
|
133
224
|
@property
|
|
134
225
|
def has_instantiation_config(self) -> bool:
|
|
135
|
-
|
|
226
|
+
# Use the property to access the schema
|
|
227
|
+
return self.config_schema is not None and len(self.config_schema) > 0
|
|
136
228
|
|
|
137
229
|
def validate_instantiation_config(self, config_data: Dict[str, Any]) -> tuple[bool, TypingList[str]]:
|
|
138
|
-
|
|
230
|
+
# Use the property to access the schema
|
|
231
|
+
schema = self.config_schema
|
|
232
|
+
if not schema:
|
|
139
233
|
if config_data:
|
|
140
234
|
return False, [f"Tool '{self.name}' does not accept instantiation configuration parameters"]
|
|
141
235
|
return True, []
|
|
142
|
-
return
|
|
236
|
+
return schema.validate_config(config_data)
|
|
143
237
|
|
|
144
238
|
def __repr__(self) -> str:
|
|
145
239
|
creator_repr = f"class='{self._tool_class.__name__}'" if self._tool_class else "factory=True"
|
|
@@ -63,6 +63,35 @@ class ToolRegistry(metaclass=SingletonMeta):
|
|
|
63
63
|
logger.warning(f"Attempted to unregister tool '{name}', but it was not found in the registry.")
|
|
64
64
|
return False
|
|
65
65
|
|
|
66
|
+
def reload_tool_schema(self, name: str) -> bool:
|
|
67
|
+
"""
|
|
68
|
+
Actively reloads the schema for a specific tool by calling its schema provider.
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
name: The unique name of the tool to reload the schema for.
|
|
72
|
+
|
|
73
|
+
Returns:
|
|
74
|
+
True if the tool was found and its schema was reloaded, False otherwise.
|
|
75
|
+
"""
|
|
76
|
+
definition = self.get_tool_definition(name)
|
|
77
|
+
if definition:
|
|
78
|
+
definition.reload_cached_schema()
|
|
79
|
+
return True
|
|
80
|
+
else:
|
|
81
|
+
logger.warning(f"Attempted to reload schema for tool '{name}', but it was not found in the registry.")
|
|
82
|
+
return False
|
|
83
|
+
|
|
84
|
+
def reload_all_tool_schemas(self) -> None:
|
|
85
|
+
"""
|
|
86
|
+
Actively reloads the schemas for all registered tools.
|
|
87
|
+
"""
|
|
88
|
+
logger.info("Eagerly reloading schemas for all registered tools...")
|
|
89
|
+
count = 0
|
|
90
|
+
for definition in self._definitions.values():
|
|
91
|
+
definition.reload_cached_schema()
|
|
92
|
+
count += 1
|
|
93
|
+
logger.info(f"Schemas for {count} tool(s) have been reloaded.")
|
|
94
|
+
|
|
66
95
|
def get_tool_definition(self, name: str) -> Optional[ToolDefinition]:
|
|
67
96
|
"""
|
|
68
97
|
Retrieves the definition for a specific tool name.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from .providers import SearchProvider
|
|
2
|
+
from .base_strategy import SearchStrategy
|
|
3
|
+
from .serper_strategy import SerperSearchStrategy
|
|
4
|
+
from .serpapi_strategy import SerpApiSearchStrategy
|
|
5
|
+
from .google_cse_strategy import GoogleCSESearchStrategy
|
|
6
|
+
from .client import SearchClient
|
|
7
|
+
from .factory import SearchClientFactory
|
|
8
|
+
|
|
9
|
+
__all__ = [
|
|
10
|
+
"SearchProvider",
|
|
11
|
+
"SearchStrategy",
|
|
12
|
+
"SerperSearchStrategy",
|
|
13
|
+
"SerpApiSearchStrategy",
|
|
14
|
+
"GoogleCSESearchStrategy",
|
|
15
|
+
"SearchClient",
|
|
16
|
+
"SearchClientFactory",
|
|
17
|
+
]
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
from typing import Dict, Any
|
|
3
|
+
|
|
4
|
+
class SearchStrategy(ABC):
|
|
5
|
+
"""
|
|
6
|
+
Abstract base class for a search provider strategy.
|
|
7
|
+
Defines the common interface for performing a search and formatting results.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
@abstractmethod
|
|
11
|
+
async def search(self, query: str, num_results: int) -> str:
|
|
12
|
+
"""
|
|
13
|
+
Executes a search query against a specific provider.
|
|
14
|
+
|
|
15
|
+
Args:
|
|
16
|
+
query: The search query string.
|
|
17
|
+
num_results: The desired number of organic search results.
|
|
18
|
+
|
|
19
|
+
Returns:
|
|
20
|
+
A formatted string summarizing the search results, suitable for an LLM.
|
|
21
|
+
"""
|
|
22
|
+
pass
|
|
23
|
+
|
|
24
|
+
@abstractmethod
|
|
25
|
+
def _format_results(self, data: Dict[str, Any]) -> str:
|
|
26
|
+
"""
|
|
27
|
+
Formats the raw JSON response from the search API into a clean string.
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
data: The JSON data from the API response.
|
|
31
|
+
|
|
32
|
+
Returns:
|
|
33
|
+
A formatted summary string.
|
|
34
|
+
"""
|
|
35
|
+
pass
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from typing import TYPE_CHECKING
|
|
3
|
+
|
|
4
|
+
if TYPE_CHECKING:
|
|
5
|
+
from .base_strategy import SearchStrategy
|
|
6
|
+
|
|
7
|
+
logger = logging.getLogger(__name__)
|
|
8
|
+
|
|
9
|
+
class SearchClient:
|
|
10
|
+
"""
|
|
11
|
+
A client that uses a configured search strategy to perform searches.
|
|
12
|
+
This acts as the 'Context' in the Strategy pattern.
|
|
13
|
+
"""
|
|
14
|
+
def __init__(self, strategy: 'SearchStrategy'):
|
|
15
|
+
if not strategy:
|
|
16
|
+
raise ValueError("SearchClient must be initialized with a valid SearchStrategy.")
|
|
17
|
+
self._strategy = strategy
|
|
18
|
+
logger.debug(f"SearchClient initialized with strategy: {type(strategy).__name__}")
|
|
19
|
+
|
|
20
|
+
async def search(self, query: str, num_results: int) -> str:
|
|
21
|
+
"""
|
|
22
|
+
Delegates the search operation to the configured strategy.
|
|
23
|
+
"""
|
|
24
|
+
return await self._strategy.search(query, num_results)
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import logging
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
5
|
+
from autobyteus.utils.singleton import SingletonMeta
|
|
6
|
+
from .providers import SearchProvider
|
|
7
|
+
from .client import SearchClient
|
|
8
|
+
from .serper_strategy import SerperSearchStrategy
|
|
9
|
+
from .serpapi_strategy import SerpApiSearchStrategy
|
|
10
|
+
from .google_cse_strategy import GoogleCSESearchStrategy
|
|
11
|
+
|
|
12
|
+
logger = logging.getLogger(__name__)
|
|
13
|
+
|
|
14
|
+
class SearchClientFactory(metaclass=SingletonMeta):
|
|
15
|
+
"""
|
|
16
|
+
Factory for creating a SearchClient with the appropriate strategy
|
|
17
|
+
based on environment variable configuration.
|
|
18
|
+
"""
|
|
19
|
+
_instance: Optional[SearchClient] = None
|
|
20
|
+
|
|
21
|
+
def create_search_client(self) -> SearchClient:
|
|
22
|
+
"""
|
|
23
|
+
Creates and returns a singleton instance of the SearchClient, configured
|
|
24
|
+
with the appropriate search strategy.
|
|
25
|
+
"""
|
|
26
|
+
if self._instance:
|
|
27
|
+
return self._instance
|
|
28
|
+
|
|
29
|
+
provider_name = os.getenv("DEFAULT_SEARCH_PROVIDER", "").lower()
|
|
30
|
+
|
|
31
|
+
serper_key = os.getenv("SERPER_API_KEY")
|
|
32
|
+
serpapi_key = os.getenv("SERPAPI_API_KEY")
|
|
33
|
+
google_api_key = os.getenv("GOOGLE_CSE_API_KEY")
|
|
34
|
+
google_cse_id = os.getenv("GOOGLE_CSE_ID")
|
|
35
|
+
|
|
36
|
+
is_serper_configured = bool(serper_key)
|
|
37
|
+
is_serpapi_configured = bool(serpapi_key)
|
|
38
|
+
is_google_cse_configured = bool(google_api_key and google_cse_id)
|
|
39
|
+
|
|
40
|
+
strategy = None
|
|
41
|
+
|
|
42
|
+
if provider_name == SearchProvider.GOOGLE_CSE:
|
|
43
|
+
if is_google_cse_configured:
|
|
44
|
+
logger.info("DEFAULT_SEARCH_PROVIDER is 'google_cse', using Google CSE strategy.")
|
|
45
|
+
strategy = GoogleCSESearchStrategy()
|
|
46
|
+
else:
|
|
47
|
+
raise ValueError("DEFAULT_SEARCH_PROVIDER is 'google_cse', but Google CSE is not configured. "
|
|
48
|
+
"Set GOOGLE_CSE_API_KEY and GOOGLE_CSE_ID.")
|
|
49
|
+
|
|
50
|
+
elif provider_name == SearchProvider.SERPAPI:
|
|
51
|
+
if is_serpapi_configured:
|
|
52
|
+
logger.info("DEFAULT_SEARCH_PROVIDER is 'serpapi', using SerpApi strategy.")
|
|
53
|
+
strategy = SerpApiSearchStrategy()
|
|
54
|
+
else:
|
|
55
|
+
raise ValueError("DEFAULT_SEARCH_PROVIDER is 'serpapi', but SerpApi is not configured. "
|
|
56
|
+
"Set SERPAPI_API_KEY.")
|
|
57
|
+
|
|
58
|
+
# Default to Serper if explicitly set, or if not set and Serper is available.
|
|
59
|
+
# This handles the case where multiple providers are configured but no provider is specified.
|
|
60
|
+
elif provider_name == SearchProvider.SERPER or is_serper_configured:
|
|
61
|
+
if is_serper_configured:
|
|
62
|
+
logger.info("Using Serper search strategy (either as default or as first fallback).")
|
|
63
|
+
strategy = SerperSearchStrategy()
|
|
64
|
+
else:
|
|
65
|
+
# This branch is only taken if provider_name is 'serper' but it's not configured.
|
|
66
|
+
raise ValueError("DEFAULT_SEARCH_PROVIDER is 'serper', but Serper is not configured. Set SERPER_API_KEY.")
|
|
67
|
+
|
|
68
|
+
elif is_serpapi_configured:
|
|
69
|
+
logger.info("Serper not configured, falling back to available SerpApi strategy.")
|
|
70
|
+
strategy = SerpApiSearchStrategy()
|
|
71
|
+
|
|
72
|
+
elif is_google_cse_configured:
|
|
73
|
+
logger.info("Neither Serper nor SerpApi are configured, falling back to available Google CSE strategy.")
|
|
74
|
+
strategy = GoogleCSESearchStrategy()
|
|
75
|
+
|
|
76
|
+
else:
|
|
77
|
+
raise ValueError("No search provider is configured. Please set either SERPER_API_KEY, SERPAPI_API_KEY, "
|
|
78
|
+
"or both GOOGLE_CSE_API_KEY and GOOGLE_CSE_ID environment variables.")
|
|
79
|
+
|
|
80
|
+
self._instance = SearchClient(strategy=strategy)
|
|
81
|
+
return self._instance
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import logging
|
|
3
|
+
import aiohttp
|
|
4
|
+
from typing import Dict, Any, Optional
|
|
5
|
+
|
|
6
|
+
from .base_strategy import SearchStrategy
|
|
7
|
+
|
|
8
|
+
logger = logging.getLogger(__name__)
|
|
9
|
+
|
|
10
|
+
class GoogleCSESearchStrategy(SearchStrategy):
|
|
11
|
+
"""
|
|
12
|
+
A search strategy that uses the official Google Custom Search Engine (CSE) API.
|
|
13
|
+
"""
|
|
14
|
+
API_URL = "https://www.googleapis.com/customsearch/v1"
|
|
15
|
+
|
|
16
|
+
def __init__(self):
|
|
17
|
+
self.api_key: Optional[str] = os.getenv("GOOGLE_CSE_API_KEY")
|
|
18
|
+
self.cse_id: Optional[str] = os.getenv("GOOGLE_CSE_ID")
|
|
19
|
+
if not self.api_key or not self.cse_id:
|
|
20
|
+
raise ValueError(
|
|
21
|
+
"GoogleCSESearchStrategy requires both 'GOOGLE_CSE_API_KEY' and 'GOOGLE_CSE_ID' environment variables to be set."
|
|
22
|
+
)
|
|
23
|
+
logger.debug("GoogleCSESearchStrategy initialized.")
|
|
24
|
+
|
|
25
|
+
def _format_results(self, data: Dict[str, Any]) -> str:
|
|
26
|
+
"""Formats the JSON response from Google CSE API into a clean string for an LLM."""
|
|
27
|
+
if "items" not in data or not data["items"]:
|
|
28
|
+
return "No relevant information found for the query via Google CSE."
|
|
29
|
+
|
|
30
|
+
results = data["items"]
|
|
31
|
+
results_str = "\n".join(
|
|
32
|
+
f"{i+1}. {result.get('title', 'No Title')}\n"
|
|
33
|
+
f" Link: {result.get('link', 'No Link')}\n"
|
|
34
|
+
f" Snippet: {result.get('snippet', 'No Snippet')}"
|
|
35
|
+
for i, result in enumerate(results)
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
return f"Search Results:\n{results_str}"
|
|
39
|
+
|
|
40
|
+
async def search(self, query: str, num_results: int) -> str:
|
|
41
|
+
logger.info(f"Executing search with Google CSE strategy for query: '{query}'")
|
|
42
|
+
|
|
43
|
+
params = {
|
|
44
|
+
'key': self.api_key,
|
|
45
|
+
'cx': self.cse_id,
|
|
46
|
+
'q': query,
|
|
47
|
+
'num': num_results
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
try:
|
|
51
|
+
async with aiohttp.ClientSession() as session:
|
|
52
|
+
async with session.get(self.API_URL, params=params) as response:
|
|
53
|
+
if response.status == 200:
|
|
54
|
+
data = await response.json()
|
|
55
|
+
return self._format_results(data)
|
|
56
|
+
else:
|
|
57
|
+
error_text = await response.text()
|
|
58
|
+
logger.error(
|
|
59
|
+
f"Google CSE API returned a non-200 status code: {response.status}. "
|
|
60
|
+
f"Response: {error_text}"
|
|
61
|
+
)
|
|
62
|
+
raise RuntimeError(f"Google CSE API request failed with status {response.status}: {error_text}")
|
|
63
|
+
except aiohttp.ClientError as e:
|
|
64
|
+
logger.error(f"Network error during Google CSE API call: {e}", exc_info=True)
|
|
65
|
+
raise RuntimeError(f"A network error occurred during Google CSE search: {e}")
|
|
66
|
+
except Exception as e:
|
|
67
|
+
logger.error(f"An unexpected error occurred in Google CSE strategy: {e}", exc_info=True)
|
|
68
|
+
raise
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import logging
|
|
3
|
+
import aiohttp
|
|
4
|
+
from typing import Dict, Any, Optional
|
|
5
|
+
|
|
6
|
+
from .base_strategy import SearchStrategy
|
|
7
|
+
|
|
8
|
+
logger = logging.getLogger(__name__)
|
|
9
|
+
|
|
10
|
+
class SerpApiSearchStrategy(SearchStrategy):
|
|
11
|
+
"""
|
|
12
|
+
A search strategy that uses the SerpApi.com API.
|
|
13
|
+
"""
|
|
14
|
+
API_URL = "https://serpapi.com/search.json"
|
|
15
|
+
|
|
16
|
+
def __init__(self):
|
|
17
|
+
self.api_key: Optional[str] = os.getenv("SERPAPI_API_KEY")
|
|
18
|
+
if not self.api_key:
|
|
19
|
+
raise ValueError("SerpApiSearchStrategy requires the 'SERPAPI_API_KEY' environment variable to be set.")
|
|
20
|
+
logger.debug("SerpApiSearchStrategy initialized.")
|
|
21
|
+
|
|
22
|
+
def _format_results(self, data: Dict[str, Any]) -> str:
|
|
23
|
+
"""Formats the JSON response from SerpApi into a clean string for an LLM."""
|
|
24
|
+
if "organic_results" not in data or not data["organic_results"]:
|
|
25
|
+
return "No relevant information found for the query via SerpApi."
|
|
26
|
+
|
|
27
|
+
results = data["organic_results"]
|
|
28
|
+
results_str = "\n".join(
|
|
29
|
+
f"{i+1}. {result.get('title', 'No Title')}\n"
|
|
30
|
+
f" Link: {result.get('link', 'No Link')}\n"
|
|
31
|
+
f" Snippet: {result.get('snippet', 'No Snippet')}"
|
|
32
|
+
for i, result in enumerate(results)
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
return f"Search Results:\n{results_str}"
|
|
36
|
+
|
|
37
|
+
async def search(self, query: str, num_results: int) -> str:
|
|
38
|
+
logger.info(f"Executing search with SerpApi strategy for query: '{query}'")
|
|
39
|
+
|
|
40
|
+
params = {
|
|
41
|
+
'api_key': self.api_key,
|
|
42
|
+
'engine': 'google',
|
|
43
|
+
'q': query,
|
|
44
|
+
'num': num_results
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
try:
|
|
48
|
+
async with aiohttp.ClientSession() as session:
|
|
49
|
+
async with session.get(self.API_URL, params=params) as response:
|
|
50
|
+
if response.status == 200:
|
|
51
|
+
data = await response.json()
|
|
52
|
+
return self._format_results(data)
|
|
53
|
+
else:
|
|
54
|
+
error_text = await response.text()
|
|
55
|
+
logger.error(
|
|
56
|
+
f"SerpApi API returned a non-200 status code: {response.status}. "
|
|
57
|
+
f"Response: {error_text}"
|
|
58
|
+
)
|
|
59
|
+
raise RuntimeError(f"SerpApi API request failed with status {response.status}: {error_text}")
|
|
60
|
+
except aiohttp.ClientError as e:
|
|
61
|
+
logger.error(f"Network error during SerpApi API call: {e}", exc_info=True)
|
|
62
|
+
raise RuntimeError(f"A network error occurred during SerpApi search: {e}")
|
|
63
|
+
except Exception as e:
|
|
64
|
+
logger.error(f"An unexpected error occurred in SerpApi strategy: {e}", exc_info=True)
|
|
65
|
+
raise
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import json
|
|
3
|
+
import logging
|
|
4
|
+
import aiohttp
|
|
5
|
+
from typing import Dict, Any, Optional
|
|
6
|
+
|
|
7
|
+
from .base_strategy import SearchStrategy
|
|
8
|
+
|
|
9
|
+
logger = logging.getLogger(__name__)
|
|
10
|
+
|
|
11
|
+
class SerperSearchStrategy(SearchStrategy):
|
|
12
|
+
"""
|
|
13
|
+
A search strategy that uses the Serper.dev API.
|
|
14
|
+
"""
|
|
15
|
+
API_URL = "https://google.serper.dev/search"
|
|
16
|
+
|
|
17
|
+
def __init__(self):
|
|
18
|
+
self.api_key: Optional[str] = os.getenv("SERPER_API_KEY")
|
|
19
|
+
if not self.api_key:
|
|
20
|
+
raise ValueError("SerperSearchStrategy requires the 'SERPER_API_KEY' environment variable to be set.")
|
|
21
|
+
logger.debug("SerperSearchStrategy initialized.")
|
|
22
|
+
|
|
23
|
+
def _format_results(self, data: Dict[str, Any]) -> str:
|
|
24
|
+
"""Formats the JSON response from Serper into a clean string for an LLM."""
|
|
25
|
+
summary_parts = []
|
|
26
|
+
|
|
27
|
+
# 1. Answer Box (most important for direct questions)
|
|
28
|
+
if "answerBox" in data:
|
|
29
|
+
answer_box = data["answerBox"]
|
|
30
|
+
title = answer_box.get("title", "")
|
|
31
|
+
snippet = answer_box.get("snippet") or answer_box.get("answer")
|
|
32
|
+
summary_parts.append(f"Direct Answer for '{title}':\n{snippet}")
|
|
33
|
+
|
|
34
|
+
# 2. Knowledge Graph (for entity information)
|
|
35
|
+
if "knowledgeGraph" in data:
|
|
36
|
+
kg = data["knowledgeGraph"]
|
|
37
|
+
title = kg.get("title", "")
|
|
38
|
+
description = kg.get("description")
|
|
39
|
+
summary_parts.append(f"Summary for '{title}':\n{description}")
|
|
40
|
+
|
|
41
|
+
# 3. Organic Results (the main search links)
|
|
42
|
+
if "organic" in data and data["organic"]:
|
|
43
|
+
organic_results = data["organic"]
|
|
44
|
+
results_str = "\n".join(
|
|
45
|
+
f"{i+1}. {result.get('title', 'No Title')}\n"
|
|
46
|
+
f" Link: {result.get('link', 'No Link')}\n"
|
|
47
|
+
f" Snippet: {result.get('snippet', 'No Snippet')}"
|
|
48
|
+
for i, result in enumerate(organic_results)
|
|
49
|
+
)
|
|
50
|
+
summary_parts.append(f"Search Results:\n{results_str}")
|
|
51
|
+
|
|
52
|
+
if not summary_parts:
|
|
53
|
+
return "No relevant information found for the query via Serper."
|
|
54
|
+
|
|
55
|
+
return "\n\n---\n\n".join(summary_parts)
|
|
56
|
+
|
|
57
|
+
async def search(self, query: str, num_results: int) -> str:
|
|
58
|
+
logger.info(f"Executing search with Serper strategy for query: '{query}'")
|
|
59
|
+
|
|
60
|
+
headers = {
|
|
61
|
+
'X-API-KEY': self.api_key,
|
|
62
|
+
'Content-Type': 'application/json'
|
|
63
|
+
}
|
|
64
|
+
payload = json.dumps({
|
|
65
|
+
"q": query,
|
|
66
|
+
"num": num_results
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
try:
|
|
70
|
+
async with aiohttp.ClientSession() as session:
|
|
71
|
+
async with session.post(self.API_URL, headers=headers, data=payload) as response:
|
|
72
|
+
if response.status == 200:
|
|
73
|
+
data = await response.json()
|
|
74
|
+
return self._format_results(data)
|
|
75
|
+
else:
|
|
76
|
+
error_text = await response.text()
|
|
77
|
+
logger.error(
|
|
78
|
+
f"Serper API returned a non-200 status code: {response.status}. "
|
|
79
|
+
f"Response: {error_text}"
|
|
80
|
+
)
|
|
81
|
+
raise RuntimeError(f"Serper API request failed with status {response.status}: {error_text}")
|
|
82
|
+
except aiohttp.ClientError as e:
|
|
83
|
+
logger.error(f"Network error during Serper API call: {e}", exc_info=True)
|
|
84
|
+
raise RuntimeError(f"A network error occurred during Serper search: {e}")
|
|
85
|
+
except Exception as e:
|
|
86
|
+
logger.error(f"An unexpected error occurred in Serper strategy: {e}", exc_info=True)
|
|
87
|
+
raise
|