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
|
@@ -13,13 +13,51 @@ from autobyteus.utils.parameter_schema import ParameterSchema, ParameterDefiniti
|
|
|
13
13
|
|
|
14
14
|
logger = logging.getLogger(__name__)
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
"
|
|
19
|
-
"
|
|
20
|
-
"
|
|
21
|
-
"
|
|
16
|
+
# Enhanced metadata for Google Gemini TTS voices, including gender and description.
|
|
17
|
+
GEMINI_VOICE_DETAILS = {
|
|
18
|
+
"Zephyr": {"gender": "female", "description": "Bright, Higher pitch"},
|
|
19
|
+
"Puck": {"gender": "male", "description": "Upbeat, Middle pitch"},
|
|
20
|
+
"Charon": {"gender": "male", "description": "Informative, Lower pitch"},
|
|
21
|
+
"Kore": {"gender": "female", "description": "Firm, Middle pitch"},
|
|
22
|
+
"Fenrir": {"gender": "male", "description": "Excitable, Lower middle pitch"},
|
|
23
|
+
"Leda": {"gender": "female", "description": "Youthful, Higher pitch"},
|
|
24
|
+
"Orus": {"gender": "male", "description": "Firm, Lower middle pitch"},
|
|
25
|
+
"Aoede": {"gender": "female", "description": "Breezy, Middle pitch"},
|
|
26
|
+
"Callirrhoe": {"gender": "female", "description": "Easy-going, Middle pitch"},
|
|
27
|
+
"Autonoe": {"gender": "female", "description": "Bright, Middle pitch"},
|
|
28
|
+
"Enceladus": {"gender": "male", "description": "Breathy, Lower pitch"},
|
|
29
|
+
"Iapetus": {"gender": "male", "description": "Clear, Lower middle pitch"},
|
|
30
|
+
"Umbriel": {"gender": "male", "description": "Easy-going, Lower middle pitch"},
|
|
31
|
+
"Algieba": {"gender": "male", "description": "Smooth, Lower pitch"},
|
|
32
|
+
"Despina": {"gender": "female", "description": "Smooth, Middle pitch"},
|
|
33
|
+
"Erinome": {"gender": "female", "description": "Clear, Middle pitch"},
|
|
34
|
+
"Algenib": {"gender": "male", "description": "Gravelly, Lower pitch"},
|
|
35
|
+
"Rasalgethi": {"gender": "male", "description": "Informative, Middle pitch"},
|
|
36
|
+
"Laomedeia": {"gender": "female", "description": "Upbeat, Higher pitch"},
|
|
37
|
+
"Achernar": {"gender": "female", "description": "Soft, Higher pitch"},
|
|
38
|
+
"Alnilam": {"gender": "male", "description": "Firm, Lower middle pitch"},
|
|
39
|
+
"Schedar": {"gender": "male", "description": "Even, Lower middle pitch"},
|
|
40
|
+
"Gacrux": {"gender": "female", "description": "Mature, Middle pitch"},
|
|
41
|
+
"Pulcherrima": {"gender": "female", "description": "Forward, Middle pitch"},
|
|
42
|
+
"Achird": {"gender": "male", "description": "Friendly, Lower middle pitch"},
|
|
43
|
+
"Zubenelgenubi": {"gender": "male", "description": "Casual, Lower middle pitch"},
|
|
44
|
+
"Vindemiatrix": {"gender": "female", "description": "Gentle, Middle pitch"},
|
|
45
|
+
"Sadachbia": {"gender": "male", "description": "Lively, Lower pitch"},
|
|
46
|
+
"Sadaltager": {"gender": "male", "description": "Knowledgeable, Middle pitch"},
|
|
47
|
+
"Sulafat": {"gender": "female", "description": "Warm, Middle pitch"},
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
# The list of voice names, derived from the keys of the details dictionary.
|
|
51
|
+
# This is used for the `enum_values` to maintain compatibility.
|
|
52
|
+
GEMINI_TTS_VOICES = list(GEMINI_VOICE_DETAILS.keys())
|
|
53
|
+
|
|
54
|
+
# Generate a formatted string of voice metadata to be appended to parameter descriptions.
|
|
55
|
+
_voice_descriptions_list = [
|
|
56
|
+
f"- {name} ({details['gender']}): {details['description']}"
|
|
57
|
+
for name, details in GEMINI_VOICE_DETAILS.items()
|
|
22
58
|
]
|
|
59
|
+
GEMINI_VOICE_METADATA_DESC = "\n\nDetailed Voice Options:\n" + "\n".join(_voice_descriptions_list)
|
|
60
|
+
|
|
23
61
|
|
|
24
62
|
OPENAI_TTS_VOICES = [
|
|
25
63
|
"alloy", "ash", "ballad", "coral", "echo", "fable", "onyx",
|
|
@@ -64,7 +102,7 @@ class AudioClientFactory(metaclass=SingletonMeta):
|
|
|
64
102
|
ParameterDefinition(
|
|
65
103
|
name="voice",
|
|
66
104
|
param_type=ParameterType.ENUM,
|
|
67
|
-
description="The voice to assign to this speaker.",
|
|
105
|
+
description="The voice to assign to this speaker." + GEMINI_VOICE_METADATA_DESC,
|
|
68
106
|
enum_values=GEMINI_TTS_VOICES,
|
|
69
107
|
required=True
|
|
70
108
|
)
|
|
@@ -84,7 +122,7 @@ class AudioClientFactory(metaclass=SingletonMeta):
|
|
|
84
122
|
param_type=ParameterType.ENUM,
|
|
85
123
|
default_value="Kore",
|
|
86
124
|
enum_values=GEMINI_TTS_VOICES,
|
|
87
|
-
description="The voice to use for single-speaker generation."
|
|
125
|
+
description="The voice to use for single-speaker generation." + GEMINI_VOICE_METADATA_DESC
|
|
88
126
|
),
|
|
89
127
|
ParameterDefinition(
|
|
90
128
|
name="style_instructions",
|
|
@@ -102,7 +140,7 @@ class AudioClientFactory(metaclass=SingletonMeta):
|
|
|
102
140
|
gemini_tts_model = AudioModel(
|
|
103
141
|
name="gemini-2.5-flash-tts",
|
|
104
142
|
value="gemini-2.5-flash-preview-tts",
|
|
105
|
-
provider=MultimediaProvider.
|
|
143
|
+
provider=MultimediaProvider.GEMINI,
|
|
106
144
|
client_class=GeminiAudioClient,
|
|
107
145
|
parameter_schema=gemini_tts_schema
|
|
108
146
|
)
|
|
@@ -79,7 +79,8 @@ class AudioModel(metaclass=AudioModelMeta):
|
|
|
79
79
|
"""Returns the unique identifier for the model."""
|
|
80
80
|
if self.runtime == MultimediaRuntime.AUTOBYTEUS and self.host_url:
|
|
81
81
|
try:
|
|
82
|
-
|
|
82
|
+
parsed = urlparse(self.host_url)
|
|
83
|
+
host = parsed.netloc or parsed.hostname or self.host_url
|
|
83
84
|
return f"{self.name}@{host}"
|
|
84
85
|
except Exception:
|
|
85
86
|
return f"{self.name}@{self.host_url}" # Fallback
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import logging
|
|
2
|
+
import uuid
|
|
2
3
|
from typing import Optional, List, Dict, Any, TYPE_CHECKING
|
|
3
4
|
from autobyteus.clients import AutobyteusClient
|
|
4
5
|
from autobyteus.multimedia.image.base_image_client import BaseImageClient
|
|
@@ -13,6 +14,7 @@ logger = logging.getLogger(__name__)
|
|
|
13
14
|
class AutobyteusImageClient(BaseImageClient):
|
|
14
15
|
"""
|
|
15
16
|
An image client that connects to an Autobyteus LLM server instance for image tasks.
|
|
17
|
+
Maintains a persistent session ID for stateful interactions (e.g. conversational editing).
|
|
16
18
|
"""
|
|
17
19
|
|
|
18
20
|
def __init__(self, model: "ImageModel", config: "MultimediaConfig"):
|
|
@@ -21,7 +23,9 @@ class AutobyteusImageClient(BaseImageClient):
|
|
|
21
23
|
raise ValueError("AutobyteusImageClient requires a host_url in its ImageModel.")
|
|
22
24
|
|
|
23
25
|
self.autobyteus_client = AutobyteusClient(server_url=model.host_url)
|
|
24
|
-
|
|
26
|
+
self.session_id = str(uuid.uuid4())
|
|
27
|
+
logger.info(f"AutobyteusImageClient initialized for model '{self.model.name}' "
|
|
28
|
+
f"on host '{model.host_url}' with session_id '{self.session_id}'.")
|
|
25
29
|
|
|
26
30
|
async def generate_image(
|
|
27
31
|
self,
|
|
@@ -72,7 +76,7 @@ class AutobyteusImageClient(BaseImageClient):
|
|
|
72
76
|
) -> ImageGenerationResponse:
|
|
73
77
|
"""Internal helper to call the remote server."""
|
|
74
78
|
try:
|
|
75
|
-
logger.info(f"Sending image generation request for model '{self.model.name}' to {self.model.host_url}")
|
|
79
|
+
logger.info(f"Sending image generation request for model '{self.model.name}' to {self.model.host_url} (Session: {self.session_id})")
|
|
76
80
|
|
|
77
81
|
# The model name for the remote server is the `value`, not the unique `model_identifier`
|
|
78
82
|
model_name_for_server = self.model.name
|
|
@@ -84,7 +88,8 @@ class AutobyteusImageClient(BaseImageClient):
|
|
|
84
88
|
prompt=prompt,
|
|
85
89
|
input_image_urls=input_image_urls,
|
|
86
90
|
mask_url=mask_url,
|
|
87
|
-
generation_config=generation_config
|
|
91
|
+
generation_config=generation_config,
|
|
92
|
+
session_id=self.session_id
|
|
88
93
|
)
|
|
89
94
|
|
|
90
95
|
image_urls = response_data.get("image_urls", [])
|
|
@@ -98,7 +103,16 @@ class AutobyteusImageClient(BaseImageClient):
|
|
|
98
103
|
raise
|
|
99
104
|
|
|
100
105
|
async def cleanup(self):
|
|
101
|
-
"""
|
|
106
|
+
"""
|
|
107
|
+
Notifies the server to cleanup the session, then closes the underlying HTTP client.
|
|
108
|
+
"""
|
|
102
109
|
if self.autobyteus_client:
|
|
103
|
-
|
|
110
|
+
try:
|
|
111
|
+
logger.info(f"Notifying server to cleanup image session '{self.session_id}'...")
|
|
112
|
+
await self.autobyteus_client.cleanup_image_session(self.session_id)
|
|
113
|
+
except Exception as e:
|
|
114
|
+
logger.error(f"Failed to cleanup remote image session '{self.session_id}': {e}")
|
|
115
|
+
finally:
|
|
116
|
+
await self.autobyteus_client.close()
|
|
117
|
+
|
|
104
118
|
logger.debug("AutobyteusImageClient cleaned up.")
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
import base64
|
|
3
|
-
import os
|
|
4
3
|
from typing import Optional, List, Dict, Any, TYPE_CHECKING
|
|
5
|
-
from google import
|
|
6
|
-
from PIL import Image
|
|
7
|
-
import requests
|
|
4
|
+
from google.genai import types as genai_types
|
|
8
5
|
|
|
9
6
|
from autobyteus.multimedia.image.base_image_client import BaseImageClient
|
|
10
7
|
from autobyteus.multimedia.utils.response_types import ImageGenerationResponse
|
|
11
8
|
from autobyteus.multimedia.utils.api_utils import load_image_from_url
|
|
9
|
+
from autobyteus.utils.gemini_helper import initialize_gemini_client_with_runtime
|
|
10
|
+
from autobyteus.utils.gemini_model_mapping import resolve_model_for_runtime
|
|
12
11
|
|
|
13
12
|
if TYPE_CHECKING:
|
|
14
13
|
from autobyteus.multimedia.image.image_model import ImageModel
|
|
@@ -21,17 +20,15 @@ class GeminiImageClient(BaseImageClient):
|
|
|
21
20
|
An image client that uses Google's Gemini models for image generation tasks.
|
|
22
21
|
|
|
23
22
|
**Setup Requirements:**
|
|
24
|
-
1. **
|
|
23
|
+
1. **AI Studio Mode:** Set `GEMINI_API_KEY`.
|
|
24
|
+
2. **Vertex AI Mode:** Set `VERTEX_AI_PROJECT` and `VERTEX_AI_LOCATION`.
|
|
25
25
|
"""
|
|
26
26
|
|
|
27
27
|
def __init__(self, model: "ImageModel", config: "MultimediaConfig"):
|
|
28
28
|
super().__init__(model, config)
|
|
29
|
-
api_key = os.getenv("GEMINI_API_KEY")
|
|
30
|
-
if not api_key:
|
|
31
|
-
raise ValueError("Please set the GEMINI_API_KEY environment variable.")
|
|
32
29
|
|
|
33
30
|
try:
|
|
34
|
-
self.client =
|
|
31
|
+
self.client, self.runtime_info = initialize_gemini_client_with_runtime()
|
|
35
32
|
self.async_client = self.client.aio
|
|
36
33
|
logger.info(f"GeminiImageClient initialized for model '{self.model.name}'.")
|
|
37
34
|
except Exception as e:
|
|
@@ -60,16 +57,40 @@ class GeminiImageClient(BaseImageClient):
|
|
|
60
57
|
except Exception as e:
|
|
61
58
|
logger.error(f"Skipping image at '{url}' due to loading error: {e}")
|
|
62
59
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
60
|
+
config_dict: Dict[str, Any] = {}
|
|
61
|
+
if self.config and self.config.params:
|
|
62
|
+
config_dict.update(self.config.params)
|
|
63
|
+
if generation_config:
|
|
64
|
+
config_dict.update(generation_config)
|
|
65
|
+
if "response_modalities" not in config_dict:
|
|
66
|
+
if getattr(self, "runtime_info", None) and self.runtime_info.runtime == "vertex":
|
|
67
|
+
config_dict["response_modalities"] = ["TEXT", "IMAGE"]
|
|
68
|
+
else:
|
|
69
|
+
config_dict["response_modalities"] = ["IMAGE"]
|
|
70
|
+
config = genai_types.GenerateContentConfig(**config_dict)
|
|
71
|
+
|
|
72
|
+
# FIX: Removed 'models/' prefix from model_name to support Vertex AI
|
|
73
|
+
runtime_adjusted_model = resolve_model_for_runtime(
|
|
74
|
+
self.model.value,
|
|
75
|
+
modality="image",
|
|
76
|
+
runtime=getattr(self, "runtime_info", None) and self.runtime_info.runtime,
|
|
77
|
+
)
|
|
78
|
+
if runtime_adjusted_model != self.model.value:
|
|
79
|
+
logger.info(
|
|
80
|
+
"Using runtime-adjusted Gemini image model '%s' (requested '%s').",
|
|
81
|
+
runtime_adjusted_model,
|
|
82
|
+
self.model.value,
|
|
83
|
+
)
|
|
84
|
+
response = await self.async_client.models.generate_content(
|
|
85
|
+
model=runtime_adjusted_model,
|
|
86
|
+
contents=content,
|
|
87
|
+
config=config,
|
|
88
|
+
)
|
|
68
89
|
|
|
69
90
|
|
|
70
91
|
image_urls = []
|
|
71
|
-
for part in response.parts:
|
|
72
|
-
if part.inline_data and "image" in part.inline_data.mime_type:
|
|
92
|
+
for part in response.parts or []:
|
|
93
|
+
if part.inline_data and part.inline_data.mime_type and "image" in part.inline_data.mime_type:
|
|
73
94
|
image_bytes = part.inline_data.data
|
|
74
95
|
base64_image = base64.b64encode(image_bytes).decode("utf-8")
|
|
75
96
|
data_uri = f"data:{part.inline_data.mime_type};base64,{base64_image}"
|
|
@@ -77,7 +98,7 @@ class GeminiImageClient(BaseImageClient):
|
|
|
77
98
|
|
|
78
99
|
if not image_urls:
|
|
79
100
|
# Check for a safety-related refusal to generate content
|
|
80
|
-
if response.prompt_feedback.block_reason:
|
|
101
|
+
if response.prompt_feedback and response.prompt_feedback.block_reason:
|
|
81
102
|
reason = response.prompt_feedback.block_reason.name
|
|
82
103
|
logger.error(f"Image generation blocked due to safety settings. Reason: {reason}")
|
|
83
104
|
raise ValueError(f"Image generation failed due to safety settings: {reason}")
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
import os
|
|
3
|
+
import tempfile
|
|
4
|
+
from pathlib import Path
|
|
3
5
|
from typing import Optional, List, Dict, Any, TYPE_CHECKING
|
|
6
|
+
|
|
4
7
|
from openai import OpenAI
|
|
8
|
+
|
|
5
9
|
from autobyteus.multimedia.image.base_image_client import BaseImageClient
|
|
6
10
|
from autobyteus.multimedia.utils.response_types import ImageGenerationResponse
|
|
11
|
+
from autobyteus.utils.download_utils import download_file_from_url
|
|
7
12
|
|
|
8
13
|
if TYPE_CHECKING:
|
|
9
14
|
from autobyteus.multimedia.image.image_model import ImageModel
|
|
@@ -11,9 +16,19 @@ if TYPE_CHECKING:
|
|
|
11
16
|
|
|
12
17
|
logger = logging.getLogger(__name__)
|
|
13
18
|
|
|
19
|
+
|
|
20
|
+
def _mime_type_from_format(output_format: str) -> str:
|
|
21
|
+
fmt = (output_format or "png").lower()
|
|
22
|
+
if fmt in {"jpg", "jpeg"}:
|
|
23
|
+
return "image/jpeg"
|
|
24
|
+
if fmt == "webp":
|
|
25
|
+
return "image/webp"
|
|
26
|
+
return "image/png"
|
|
27
|
+
|
|
28
|
+
|
|
14
29
|
class OpenAIImageClient(BaseImageClient):
|
|
15
30
|
"""
|
|
16
|
-
An image client that uses OpenAI's
|
|
31
|
+
An image client that uses OpenAI's gpt-image series via the images API.
|
|
17
32
|
"""
|
|
18
33
|
|
|
19
34
|
def __init__(self, model: "ImageModel", config: "MultimediaConfig"):
|
|
@@ -34,49 +49,68 @@ class OpenAIImageClient(BaseImageClient):
|
|
|
34
49
|
**kwargs
|
|
35
50
|
) -> ImageGenerationResponse:
|
|
36
51
|
"""
|
|
37
|
-
Generates an image using
|
|
38
|
-
Note: This endpoint does not support image inputs
|
|
52
|
+
Generates an image using OpenAI's images generation endpoint.
|
|
53
|
+
Note: This endpoint does not support image inputs.
|
|
39
54
|
"""
|
|
40
55
|
if input_image_urls:
|
|
41
56
|
logger.warning(
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
57
|
+
"The OpenAI `images.generate` API used by this client does not support input images. "
|
|
58
|
+
"The images provided for model '%s' will be ignored. "
|
|
59
|
+
"To use image inputs, a client based on the Chat Completions API is required.",
|
|
60
|
+
self.model.value,
|
|
45
61
|
)
|
|
46
62
|
|
|
47
63
|
try:
|
|
48
64
|
image_model = self.model.value
|
|
49
|
-
logger.info(
|
|
65
|
+
logger.info("Generating image with OpenAI model '%s' and prompt: '%s...'", image_model, prompt[:50])
|
|
50
66
|
|
|
51
67
|
# Combine default config with any overrides
|
|
52
68
|
final_config = self.config.to_dict().copy()
|
|
53
69
|
if generation_config:
|
|
54
70
|
final_config.update(generation_config)
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
71
|
+
# Always request a single image for simplicity
|
|
72
|
+
final_config["n"] = 1
|
|
73
|
+
|
|
74
|
+
request_kwargs = {
|
|
75
|
+
"model": image_model,
|
|
76
|
+
"prompt": prompt,
|
|
77
|
+
"n": 1,
|
|
78
|
+
"size": final_config.get("size", "1024x1024"),
|
|
79
|
+
"quality": final_config.get("quality", "standard"),
|
|
80
|
+
}
|
|
81
|
+
if "output_format" in final_config:
|
|
82
|
+
request_kwargs["output_format"] = final_config["output_format"]
|
|
83
|
+
if "output_compression" in final_config:
|
|
84
|
+
request_kwargs["output_compression"] = final_config["output_compression"]
|
|
85
|
+
|
|
86
|
+
response = self.client.images.generate(**request_kwargs)
|
|
87
|
+
|
|
88
|
+
output_format = final_config.get("output_format", "png")
|
|
89
|
+
mime_type = _mime_type_from_format(output_format)
|
|
90
|
+
image_urls_list: List[str] = []
|
|
91
|
+
for img in response.data:
|
|
92
|
+
if getattr(img, "url", None):
|
|
93
|
+
image_urls_list.append(img.url)
|
|
94
|
+
elif getattr(img, "b64_json", None):
|
|
95
|
+
image_urls_list.append(f"data:{mime_type};base64,{img.b64_json}")
|
|
96
|
+
|
|
97
|
+
revised_prompt: Optional[str] = (
|
|
98
|
+
response.data[0].revised_prompt
|
|
99
|
+
if response.data and hasattr(response.data[0], "revised_prompt")
|
|
100
|
+
else None
|
|
64
101
|
)
|
|
65
102
|
|
|
66
|
-
image_urls_list: List[str] = [img.url for img in response.data if img.url]
|
|
67
|
-
revised_prompt: Optional[str] = response.data[0].revised_prompt if response.data and hasattr(response.data[0], 'revised_prompt') else None
|
|
68
|
-
|
|
69
103
|
if not image_urls_list:
|
|
70
|
-
raise ValueError("OpenAI API did not return any image
|
|
104
|
+
raise ValueError("OpenAI API did not return any image data.")
|
|
71
105
|
|
|
72
|
-
logger.info(
|
|
106
|
+
logger.info("Successfully generated %s image(s).", len(image_urls_list))
|
|
73
107
|
|
|
74
108
|
return ImageGenerationResponse(
|
|
75
109
|
image_urls=image_urls_list,
|
|
76
110
|
revised_prompt=revised_prompt
|
|
77
111
|
)
|
|
78
112
|
except Exception as e:
|
|
79
|
-
logger.error(
|
|
113
|
+
logger.error("Error during OpenAI image generation: %s", str(e))
|
|
80
114
|
raise ValueError(f"OpenAI image generation failed: {str(e)}")
|
|
81
115
|
|
|
82
116
|
async def edit_image(
|
|
@@ -95,49 +129,97 @@ class OpenAIImageClient(BaseImageClient):
|
|
|
95
129
|
|
|
96
130
|
source_image_url = input_image_urls[0]
|
|
97
131
|
if len(input_image_urls) > 1:
|
|
98
|
-
logger.warning(
|
|
132
|
+
logger.warning(
|
|
133
|
+
"OpenAI edit endpoint only supports one input image. Using '%s' and ignoring the rest.",
|
|
134
|
+
source_image_url,
|
|
135
|
+
)
|
|
99
136
|
|
|
137
|
+
temp_image_path: Optional[Path] = None
|
|
138
|
+
temp_mask_path: Optional[Path] = None
|
|
100
139
|
try:
|
|
101
|
-
logger.info(
|
|
140
|
+
logger.info("Editing image '%s' with prompt: '%s...'", source_image_url, prompt[:50])
|
|
102
141
|
|
|
103
142
|
# Combine default config with any overrides
|
|
104
143
|
final_config = self.config.to_dict().copy()
|
|
105
144
|
if generation_config:
|
|
106
145
|
final_config.update(generation_config)
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
146
|
+
# Always request a single edited image
|
|
147
|
+
final_config["n"] = 1
|
|
148
|
+
|
|
149
|
+
source_path = Path(source_image_url)
|
|
150
|
+
if not source_path.exists():
|
|
151
|
+
temp_image_file = tempfile.NamedTemporaryFile(delete=False, suffix=".png")
|
|
152
|
+
temp_image_file.close()
|
|
153
|
+
temp_image_path = Path(temp_image_file.name)
|
|
154
|
+
await download_file_from_url(source_image_url, temp_image_path)
|
|
155
|
+
source_path = temp_image_path
|
|
156
|
+
|
|
157
|
+
if mask_url:
|
|
158
|
+
mask_path = Path(mask_url)
|
|
159
|
+
if not mask_path.exists():
|
|
160
|
+
temp_mask_file = tempfile.NamedTemporaryFile(delete=False, suffix=".png")
|
|
161
|
+
temp_mask_file.close()
|
|
162
|
+
temp_mask_path = Path(temp_mask_file.name)
|
|
163
|
+
await download_file_from_url(mask_url, temp_mask_path)
|
|
164
|
+
mask_path = temp_mask_path
|
|
165
|
+
else:
|
|
166
|
+
mask_path = None
|
|
167
|
+
|
|
168
|
+
with open(source_path, "rb") as image_file:
|
|
169
|
+
mask_file = open(mask_path, "rb") if mask_path else None
|
|
110
170
|
try:
|
|
111
|
-
|
|
112
|
-
image
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
171
|
+
request_kwargs = {
|
|
172
|
+
"image": image_file,
|
|
173
|
+
"prompt": prompt,
|
|
174
|
+
"model": self.model.value,
|
|
175
|
+
"n": final_config.get("n", 1),
|
|
176
|
+
"size": final_config.get("size", "1024x1024"),
|
|
177
|
+
}
|
|
178
|
+
if mask_file:
|
|
179
|
+
request_kwargs["mask"] = mask_file
|
|
180
|
+
if "output_format" in final_config:
|
|
181
|
+
request_kwargs["output_format"] = final_config["output_format"]
|
|
182
|
+
if "output_compression" in final_config:
|
|
183
|
+
request_kwargs["output_compression"] = final_config["output_compression"]
|
|
184
|
+
response = self.client.images.edit(**request_kwargs)
|
|
120
185
|
finally:
|
|
121
186
|
if mask_file:
|
|
122
187
|
mask_file.close()
|
|
123
188
|
|
|
124
|
-
|
|
189
|
+
output_format = final_config.get("output_format", "png")
|
|
190
|
+
mime_type = _mime_type_from_format(output_format)
|
|
191
|
+
image_urls_list: List[str] = []
|
|
192
|
+
for img in response.data:
|
|
193
|
+
if getattr(img, "url", None):
|
|
194
|
+
image_urls_list.append(img.url)
|
|
195
|
+
elif getattr(img, "b64_json", None):
|
|
196
|
+
image_urls_list.append(f"data:{mime_type};base64,{img.b64_json}")
|
|
197
|
+
|
|
125
198
|
if not image_urls_list:
|
|
126
|
-
raise ValueError("OpenAI API did not return any edited image
|
|
199
|
+
raise ValueError("OpenAI API did not return any edited image data.")
|
|
127
200
|
|
|
128
|
-
logger.info(
|
|
201
|
+
logger.info("Successfully edited image, generated %s version(s).", len(image_urls_list))
|
|
129
202
|
return ImageGenerationResponse(image_urls=image_urls_list)
|
|
130
203
|
|
|
131
204
|
except FileNotFoundError as e:
|
|
132
|
-
logger.error(
|
|
205
|
+
logger.error("Image file not found for editing: %s", e.filename)
|
|
133
206
|
raise
|
|
134
207
|
except Exception as e:
|
|
135
|
-
logger.error(
|
|
136
|
-
# The API might return a 400 Bad Request if the model doesn't support edits
|
|
208
|
+
logger.error("Error during OpenAI image editing: %s", str(e))
|
|
137
209
|
if "does not support image editing" in str(e):
|
|
138
210
|
raise ValueError(f"The model '{self.model.value}' does not support the image editing endpoint.")
|
|
139
211
|
raise ValueError(f"OpenAI image editing failed: {str(e)}")
|
|
140
|
-
|
|
212
|
+
finally:
|
|
213
|
+
if temp_image_path and temp_image_path.exists():
|
|
214
|
+
try:
|
|
215
|
+
temp_image_path.unlink()
|
|
216
|
+
except OSError:
|
|
217
|
+
logger.warning("Failed to clean up temp image file: %s", temp_image_path)
|
|
218
|
+
if temp_mask_path and temp_mask_path.exists():
|
|
219
|
+
try:
|
|
220
|
+
temp_mask_path.unlink()
|
|
221
|
+
except OSError:
|
|
222
|
+
logger.warning("Failed to clean up temp mask file: %s", temp_mask_path)
|
|
141
223
|
|
|
142
224
|
async def cleanup(self):
|
|
143
225
|
# The OpenAI client does not require explicit cleanup of a session.
|
|
@@ -86,7 +86,8 @@ class AutobyteusImageModelProvider:
|
|
|
86
86
|
client_class=AutobyteusImageClient,
|
|
87
87
|
runtime=MultimediaRuntime.AUTOBYTEUS,
|
|
88
88
|
host_url=host_url,
|
|
89
|
-
parameter_schema=model_info.get("parameter_schema")
|
|
89
|
+
parameter_schema=model_info.get("parameter_schema"),
|
|
90
|
+
description=model_info.get("description")
|
|
90
91
|
)
|
|
91
92
|
|
|
92
93
|
ImageClientFactory.register_model(image_model)
|
|
@@ -40,43 +40,75 @@ class ImageClientFactory(metaclass=SingletonMeta):
|
|
|
40
40
|
"""Initializes the registry with built-in image models and discovers remote ones."""
|
|
41
41
|
|
|
42
42
|
# OpenAI Models
|
|
43
|
-
|
|
43
|
+
gpt_image_15_schema = ParameterSchema(parameters=[
|
|
44
44
|
ParameterDefinition(name="n", param_type=ParameterType.INTEGER, default_value=1, enum_values=[1], description="The number of images to generate."),
|
|
45
45
|
ParameterDefinition(name="size", param_type=ParameterType.ENUM, default_value="1024x1024", enum_values=["1024x1024", "1792x1024", "1024x1792"], description="The size of the generated images."),
|
|
46
|
-
ParameterDefinition(name="quality", param_type=ParameterType.ENUM, default_value="
|
|
47
|
-
ParameterDefinition(name="style", param_type=ParameterType.ENUM, default_value="vivid", enum_values=["vivid", "natural"], description="The style of the generated images.")
|
|
46
|
+
ParameterDefinition(name="quality", param_type=ParameterType.ENUM, default_value="auto", enum_values=["auto", "low", "medium", "high"], description="The quality of the image that will be generated.")
|
|
48
47
|
])
|
|
49
48
|
|
|
50
|
-
|
|
51
|
-
name="
|
|
52
|
-
|
|
49
|
+
gemini_image_schema = ParameterSchema(parameters=[
|
|
50
|
+
ParameterDefinition(name="n", param_type=ParameterType.INTEGER, default_value=1, enum_values=[1], description="The number of images to generate."),
|
|
51
|
+
ParameterDefinition(name="size", param_type=ParameterType.ENUM, default_value="1024x1024", enum_values=["1024x1024", "1792x1024", "1024x1792"], description="The size of the generated images."),
|
|
52
|
+
ParameterDefinition(name="quality", param_type=ParameterType.ENUM, default_value="auto", enum_values=["auto", "low", "medium", "high"], description="The quality of the image that will be generated.")
|
|
53
|
+
])
|
|
54
|
+
|
|
55
|
+
gpt_image_15_model = ImageModel(
|
|
56
|
+
name="gpt-image-1.5",
|
|
57
|
+
value="gpt-image-1.5",
|
|
53
58
|
provider=MultimediaProvider.OPENAI,
|
|
54
59
|
client_class=OpenAIImageClient,
|
|
55
|
-
parameter_schema=
|
|
60
|
+
parameter_schema=gpt_image_15_schema,
|
|
61
|
+
description=(
|
|
62
|
+
"OpenAI's latest **stateless (single-turn)** image model with faster renders, improved text rendering, "
|
|
63
|
+
"and higher fidelity edits. Same API surface as gpt-image-1."
|
|
64
|
+
)
|
|
56
65
|
)
|
|
57
66
|
|
|
58
67
|
# Google Imagen Models (via Gemini API)
|
|
59
68
|
imagen_model = ImageModel(
|
|
60
69
|
name="imagen-4",
|
|
61
70
|
value="imagen-4.0-generate-001",
|
|
62
|
-
provider=MultimediaProvider.
|
|
71
|
+
provider=MultimediaProvider.GEMINI,
|
|
63
72
|
client_class=GeminiImageClient,
|
|
64
|
-
parameter_schema=None # The genai library doesn't expose these as simple params
|
|
73
|
+
parameter_schema=None, # The genai library doesn't expose these as simple params
|
|
74
|
+
description=(
|
|
75
|
+
"A high-fidelity **stateless (single-turn)** model. "
|
|
76
|
+
"Does **NOT** support input images (text-to-image only). "
|
|
77
|
+
"Any provided input images will be ignored."
|
|
78
|
+
)
|
|
65
79
|
)
|
|
66
80
|
|
|
67
|
-
# Google Gemini Flash Image
|
|
81
|
+
# Google Gemini 2.5 Flash Image (legacy, still widely available)
|
|
68
82
|
gemini_flash_image_model = ImageModel(
|
|
69
|
-
name="gemini-2.5-flash-image
|
|
70
|
-
value="gemini-2.5-flash-image
|
|
71
|
-
provider=MultimediaProvider.
|
|
83
|
+
name="gemini-2.5-flash-image",
|
|
84
|
+
value="gemini-2.5-flash-image",
|
|
85
|
+
provider=MultimediaProvider.GEMINI,
|
|
86
|
+
client_class=GeminiImageClient,
|
|
87
|
+
parameter_schema=None, # Parameters handled by genai library
|
|
88
|
+
description=(
|
|
89
|
+
"Fast **conversational (multi-turn)** multimodal image model. "
|
|
90
|
+
"Supports context retention and input images for edits/variations."
|
|
91
|
+
)
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
# Google Gemini 3 Pro Image (aka "Nano Banana Pro")
|
|
95
|
+
gemini_pro_image_model = ImageModel(
|
|
96
|
+
name="gemini-3-pro-image-preview",
|
|
97
|
+
value="gemini-3-pro-image-preview",
|
|
98
|
+
provider=MultimediaProvider.GEMINI,
|
|
72
99
|
client_class=GeminiImageClient,
|
|
73
|
-
parameter_schema=None
|
|
100
|
+
parameter_schema=None, # genai library handles options internally
|
|
101
|
+
description=(
|
|
102
|
+
"High-quality **conversational (multi-turn)** image model for complex edits and 4K renders. "
|
|
103
|
+
"Supports up to 14 reference images, advanced text rendering, and thinking mode."
|
|
104
|
+
)
|
|
74
105
|
)
|
|
75
106
|
|
|
76
107
|
models_to_register = [
|
|
77
|
-
|
|
108
|
+
gpt_image_15_model,
|
|
78
109
|
imagen_model,
|
|
79
110
|
gemini_flash_image_model,
|
|
111
|
+
gemini_pro_image_model,
|
|
80
112
|
]
|
|
81
113
|
|
|
82
114
|
for model in models_to_register:
|
|
@@ -50,7 +50,8 @@ class ImageModel(metaclass=ImageModelMeta):
|
|
|
50
50
|
client_class: Type["BaseImageClient"],
|
|
51
51
|
parameter_schema: Optional[Union[Dict[str, Any], ParameterSchema]] = None,
|
|
52
52
|
runtime: MultimediaRuntime = MultimediaRuntime.API,
|
|
53
|
-
host_url: Optional[str] = None
|
|
53
|
+
host_url: Optional[str] = None,
|
|
54
|
+
description: Optional[str] = None
|
|
54
55
|
):
|
|
55
56
|
self.name = name
|
|
56
57
|
self.value = value
|
|
@@ -58,6 +59,7 @@ class ImageModel(metaclass=ImageModelMeta):
|
|
|
58
59
|
self.client_class = client_class
|
|
59
60
|
self.runtime = runtime
|
|
60
61
|
self.host_url = host_url
|
|
62
|
+
self.description = description
|
|
61
63
|
|
|
62
64
|
if isinstance(parameter_schema, dict):
|
|
63
65
|
self.parameter_schema = ParameterSchema.from_dict(parameter_schema)
|
|
@@ -79,7 +81,8 @@ class ImageModel(metaclass=ImageModelMeta):
|
|
|
79
81
|
"""Returns the unique identifier for the model."""
|
|
80
82
|
if self.runtime == MultimediaRuntime.AUTOBYTEUS and self.host_url:
|
|
81
83
|
try:
|
|
82
|
-
|
|
84
|
+
parsed = urlparse(self.host_url)
|
|
85
|
+
host = parsed.netloc or parsed.hostname or self.host_url
|
|
83
86
|
return f"{self.name}@{host}"
|
|
84
87
|
except Exception:
|
|
85
88
|
return f"{self.name}@{self.host_url}" # Fallback
|