code-puppy 0.0.335__tar.gz → 0.0.337__tar.gz
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.
- {code_puppy-0.0.335 → code_puppy-0.0.337}/PKG-INFO +1 -1
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/http_utils.py +93 -130
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/messaging/messages.py +3 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/messaging/rich_renderer.py +114 -22
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/plugins/antigravity_oauth/antigravity_model.py +67 -11
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/plugins/antigravity_oauth/transport.py +127 -58
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/tools/command_runner.py +1 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/pyproject.toml +1 -1
- {code_puppy-0.0.335 → code_puppy-0.0.337}/.gitignore +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/LICENSE +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/README.md +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/__init__.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/__main__.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/agents/__init__.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/agents/agent_c_reviewer.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/agents/agent_code_puppy.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/agents/agent_code_reviewer.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/agents/agent_cpp_reviewer.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/agents/agent_creator_agent.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/agents/agent_golang_reviewer.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/agents/agent_javascript_reviewer.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/agents/agent_manager.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/agents/agent_planning.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/agents/agent_python_programmer.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/agents/agent_python_reviewer.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/agents/agent_qa_expert.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/agents/agent_qa_kitten.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/agents/agent_security_auditor.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/agents/agent_typescript_reviewer.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/agents/base_agent.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/agents/json_agent.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/agents/prompt_reviewer.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/callbacks.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/chatgpt_codex_client.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/claude_cache_client.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/cli_runner.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/command_line/__init__.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/command_line/add_model_menu.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/command_line/attachments.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/command_line/autosave_menu.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/command_line/colors_menu.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/command_line/command_handler.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/command_line/command_registry.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/command_line/config_commands.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/command_line/core_commands.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/command_line/diff_menu.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/command_line/file_path_completion.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/command_line/load_context_completion.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/command_line/mcp/__init__.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/command_line/mcp/add_command.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/command_line/mcp/base.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/command_line/mcp/catalog_server_installer.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/command_line/mcp/custom_server_form.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/command_line/mcp/custom_server_installer.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/command_line/mcp/edit_command.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/command_line/mcp/handler.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/command_line/mcp/help_command.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/command_line/mcp/install_command.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/command_line/mcp/install_menu.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/command_line/mcp/list_command.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/command_line/mcp/logs_command.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/command_line/mcp/remove_command.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/command_line/mcp/restart_command.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/command_line/mcp/search_command.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/command_line/mcp/start_all_command.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/command_line/mcp/start_command.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/command_line/mcp/status_command.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/command_line/mcp/stop_all_command.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/command_line/mcp/stop_command.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/command_line/mcp/test_command.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/command_line/mcp/utils.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/command_line/mcp/wizard_utils.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/command_line/mcp_completion.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/command_line/model_picker_completion.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/command_line/model_settings_menu.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/command_line/motd.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/command_line/onboarding_slides.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/command_line/onboarding_wizard.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/command_line/pin_command_completion.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/command_line/prompt_toolkit_completion.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/command_line/session_commands.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/command_line/utils.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/config.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/error_logging.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/gemini_code_assist.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/keymap.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/main.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/mcp_/__init__.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/mcp_/async_lifecycle.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/mcp_/blocking_startup.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/mcp_/captured_stdio_server.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/mcp_/circuit_breaker.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/mcp_/config_wizard.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/mcp_/dashboard.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/mcp_/error_isolation.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/mcp_/examples/retry_example.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/mcp_/health_monitor.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/mcp_/managed_server.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/mcp_/manager.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/mcp_/mcp_logs.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/mcp_/registry.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/mcp_/retry_manager.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/mcp_/server_registry_catalog.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/mcp_/status_tracker.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/mcp_/system_tools.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/messaging/__init__.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/messaging/bus.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/messaging/commands.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/messaging/markdown_patches.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/messaging/message_queue.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/messaging/queue_console.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/messaging/renderers.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/messaging/spinner/__init__.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/messaging/spinner/console_spinner.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/messaging/spinner/spinner_base.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/model_factory.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/model_utils.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/models.json +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/models_dev_api.json +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/models_dev_parser.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/plugins/__init__.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/plugins/antigravity_oauth/__init__.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/plugins/antigravity_oauth/accounts.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/plugins/antigravity_oauth/config.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/plugins/antigravity_oauth/constants.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/plugins/antigravity_oauth/oauth.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/plugins/antigravity_oauth/register_callbacks.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/plugins/antigravity_oauth/storage.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/plugins/antigravity_oauth/test_plugin.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/plugins/antigravity_oauth/token.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/plugins/antigravity_oauth/utils.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/plugins/chatgpt_oauth/__init__.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/plugins/chatgpt_oauth/config.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/plugins/chatgpt_oauth/oauth_flow.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/plugins/chatgpt_oauth/register_callbacks.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/plugins/chatgpt_oauth/test_plugin.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/plugins/chatgpt_oauth/utils.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/plugins/claude_code_oauth/README.md +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/plugins/claude_code_oauth/SETUP.md +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/plugins/claude_code_oauth/__init__.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/plugins/claude_code_oauth/config.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/plugins/claude_code_oauth/register_callbacks.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/plugins/claude_code_oauth/test_plugin.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/plugins/claude_code_oauth/utils.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/plugins/customizable_commands/__init__.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/plugins/customizable_commands/register_callbacks.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/plugins/example_custom_command/README.md +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/plugins/example_custom_command/register_callbacks.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/plugins/file_permission_handler/__init__.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/plugins/file_permission_handler/register_callbacks.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/plugins/oauth_puppy_html.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/plugins/shell_safety/__init__.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/plugins/shell_safety/agent_shell_safety.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/plugins/shell_safety/command_cache.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/plugins/shell_safety/register_callbacks.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/prompts/codex_system_prompt.md +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/pydantic_patches.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/reopenable_async_client.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/round_robin_model.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/session_storage.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/status_display.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/summarization_agent.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/terminal_utils.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/tools/__init__.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/tools/agent_tools.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/tools/browser/__init__.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/tools/browser/browser_control.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/tools/browser/browser_interactions.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/tools/browser/browser_locators.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/tools/browser/browser_navigation.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/tools/browser/browser_screenshot.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/tools/browser/browser_scripts.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/tools/browser/browser_workflows.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/tools/browser/camoufox_manager.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/tools/browser/vqa_agent.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/tools/common.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/tools/file_modifications.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/tools/file_operations.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/tools/tools_content.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/uvx_detection.py +0 -0
- {code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/version_checker.py +0 -0
|
@@ -5,10 +5,10 @@ This module provides functions for creating properly configured HTTP clients.
|
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
7
|
import asyncio
|
|
8
|
-
import logging
|
|
9
8
|
import os
|
|
10
9
|
import socket
|
|
11
10
|
import time
|
|
11
|
+
from dataclasses import dataclass
|
|
12
12
|
from typing import Any, Dict, Optional, Union
|
|
13
13
|
|
|
14
14
|
import httpx
|
|
@@ -16,7 +16,69 @@ import requests
|
|
|
16
16
|
|
|
17
17
|
from code_puppy.config import get_http2
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
|
|
20
|
+
@dataclass
|
|
21
|
+
class ProxyConfig:
|
|
22
|
+
"""Configuration for proxy and SSL settings."""
|
|
23
|
+
|
|
24
|
+
verify: Union[bool, str, None]
|
|
25
|
+
trust_env: bool
|
|
26
|
+
proxy_url: str | None
|
|
27
|
+
disable_retry: bool
|
|
28
|
+
http2_enabled: bool
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _resolve_proxy_config(verify: Union[bool, str, None] = None) -> ProxyConfig:
|
|
32
|
+
"""Resolve proxy, SSL, and retry settings from environment.
|
|
33
|
+
|
|
34
|
+
This centralizes the logic for detecting proxies, determining SSL verification,
|
|
35
|
+
and checking if retry transport should be disabled.
|
|
36
|
+
"""
|
|
37
|
+
if verify is None:
|
|
38
|
+
verify = get_cert_bundle_path()
|
|
39
|
+
|
|
40
|
+
http2_enabled = get_http2()
|
|
41
|
+
|
|
42
|
+
disable_retry = os.environ.get(
|
|
43
|
+
"CODE_PUPPY_DISABLE_RETRY_TRANSPORT", ""
|
|
44
|
+
).lower() in ("1", "true", "yes")
|
|
45
|
+
|
|
46
|
+
has_proxy = bool(
|
|
47
|
+
os.environ.get("HTTP_PROXY")
|
|
48
|
+
or os.environ.get("HTTPS_PROXY")
|
|
49
|
+
or os.environ.get("http_proxy")
|
|
50
|
+
or os.environ.get("https_proxy")
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
# Determine trust_env and verify based on proxy/retry settings
|
|
54
|
+
if disable_retry:
|
|
55
|
+
# Test mode: disable SSL verification for proxy testing
|
|
56
|
+
verify = False
|
|
57
|
+
trust_env = True
|
|
58
|
+
elif has_proxy:
|
|
59
|
+
# Production proxy: keep SSL verification enabled
|
|
60
|
+
trust_env = True
|
|
61
|
+
else:
|
|
62
|
+
trust_env = False
|
|
63
|
+
|
|
64
|
+
# Extract proxy URL
|
|
65
|
+
proxy_url = None
|
|
66
|
+
if has_proxy:
|
|
67
|
+
proxy_url = (
|
|
68
|
+
os.environ.get("HTTPS_PROXY")
|
|
69
|
+
or os.environ.get("https_proxy")
|
|
70
|
+
or os.environ.get("HTTP_PROXY")
|
|
71
|
+
or os.environ.get("http_proxy")
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
return ProxyConfig(
|
|
75
|
+
verify=verify,
|
|
76
|
+
trust_env=trust_env,
|
|
77
|
+
proxy_url=proxy_url,
|
|
78
|
+
disable_retry=disable_retry,
|
|
79
|
+
http2_enabled=http2_enabled,
|
|
80
|
+
)
|
|
81
|
+
|
|
20
82
|
|
|
21
83
|
try:
|
|
22
84
|
from .reopenable_async_client import ReopenableAsyncClient
|
|
@@ -58,14 +120,7 @@ class RetryingAsyncClient(httpx.AsyncClient):
|
|
|
58
120
|
|
|
59
121
|
for attempt in range(self.max_retries + 1):
|
|
60
122
|
try:
|
|
61
|
-
|
|
62
|
-
# But only if it's not the first attempt
|
|
63
|
-
req_to_send = request
|
|
64
|
-
if attempt > 0:
|
|
65
|
-
# httpx requests are reusable, but we need to be careful with streams
|
|
66
|
-
pass
|
|
67
|
-
|
|
68
|
-
response = await super().send(req_to_send, **kwargs)
|
|
123
|
+
response = await super().send(request, **kwargs)
|
|
69
124
|
last_response = response
|
|
70
125
|
|
|
71
126
|
# Check for retryable status
|
|
@@ -128,7 +183,7 @@ class RetryingAsyncClient(httpx.AsyncClient):
|
|
|
128
183
|
return last_response
|
|
129
184
|
|
|
130
185
|
|
|
131
|
-
def get_cert_bundle_path() -> str:
|
|
186
|
+
def get_cert_bundle_path() -> str | None:
|
|
132
187
|
# First check if SSL_CERT_FILE environment variable is set
|
|
133
188
|
ssl_cert_file = os.environ.get("SSL_CERT_FILE")
|
|
134
189
|
if ssl_cert_file and os.path.exists(ssl_cert_file):
|
|
@@ -164,66 +219,26 @@ def create_async_client(
|
|
|
164
219
|
headers: Optional[Dict[str, str]] = None,
|
|
165
220
|
retry_status_codes: tuple = (429, 502, 503, 504),
|
|
166
221
|
) -> httpx.AsyncClient:
|
|
167
|
-
|
|
168
|
-
verify = get_cert_bundle_path()
|
|
169
|
-
|
|
170
|
-
# Check if HTTP/2 is enabled in config
|
|
171
|
-
http2_enabled = get_http2()
|
|
172
|
-
|
|
173
|
-
# Check if custom retry transport should be disabled (e.g., for integration tests with proxies)
|
|
174
|
-
disable_retry_transport = os.environ.get(
|
|
175
|
-
"CODE_PUPPY_DISABLE_RETRY_TRANSPORT", ""
|
|
176
|
-
).lower() in ("1", "true", "yes")
|
|
177
|
-
|
|
178
|
-
# Check if proxy environment variables are set
|
|
179
|
-
has_proxy = bool(
|
|
180
|
-
os.environ.get("HTTP_PROXY")
|
|
181
|
-
or os.environ.get("HTTPS_PROXY")
|
|
182
|
-
or os.environ.get("http_proxy")
|
|
183
|
-
or os.environ.get("https_proxy")
|
|
184
|
-
)
|
|
185
|
-
|
|
186
|
-
# When retry transport is disabled (test mode), disable SSL verification
|
|
187
|
-
# for proxy testing. For production proxies, SSL should still be verified!
|
|
188
|
-
if disable_retry_transport:
|
|
189
|
-
verify = False
|
|
190
|
-
trust_env = True
|
|
191
|
-
elif has_proxy:
|
|
192
|
-
# Production proxy detected - keep SSL verification enabled for security
|
|
193
|
-
trust_env = True
|
|
194
|
-
else:
|
|
195
|
-
trust_env = False
|
|
196
|
-
|
|
197
|
-
# Extract proxy URL if needed
|
|
198
|
-
proxy_url = None
|
|
199
|
-
if has_proxy:
|
|
200
|
-
proxy_url = (
|
|
201
|
-
os.environ.get("HTTPS_PROXY")
|
|
202
|
-
or os.environ.get("https_proxy")
|
|
203
|
-
or os.environ.get("HTTP_PROXY")
|
|
204
|
-
or os.environ.get("http_proxy")
|
|
205
|
-
)
|
|
222
|
+
config = _resolve_proxy_config(verify)
|
|
206
223
|
|
|
207
|
-
|
|
208
|
-
if not disable_retry_transport:
|
|
224
|
+
if not config.disable_retry:
|
|
209
225
|
return RetryingAsyncClient(
|
|
210
226
|
retry_status_codes=retry_status_codes,
|
|
211
|
-
proxy=proxy_url,
|
|
212
|
-
verify=verify,
|
|
227
|
+
proxy=config.proxy_url,
|
|
228
|
+
verify=config.verify,
|
|
213
229
|
headers=headers or {},
|
|
214
230
|
timeout=timeout,
|
|
215
|
-
http2=http2_enabled,
|
|
216
|
-
trust_env=trust_env,
|
|
231
|
+
http2=config.http2_enabled,
|
|
232
|
+
trust_env=config.trust_env,
|
|
217
233
|
)
|
|
218
234
|
else:
|
|
219
|
-
# Regular client for testing
|
|
220
235
|
return httpx.AsyncClient(
|
|
221
|
-
proxy=proxy_url,
|
|
222
|
-
verify=verify,
|
|
236
|
+
proxy=config.proxy_url,
|
|
237
|
+
verify=config.verify,
|
|
223
238
|
headers=headers or {},
|
|
224
239
|
timeout=timeout,
|
|
225
|
-
http2=http2_enabled,
|
|
226
|
-
trust_env=trust_env,
|
|
240
|
+
http2=config.http2_enabled,
|
|
241
|
+
trust_env=config.trust_env,
|
|
227
242
|
)
|
|
228
243
|
|
|
229
244
|
|
|
@@ -273,85 +288,33 @@ def create_reopenable_async_client(
|
|
|
273
288
|
headers: Optional[Dict[str, str]] = None,
|
|
274
289
|
retry_status_codes: tuple = (429, 502, 503, 504),
|
|
275
290
|
) -> Union[ReopenableAsyncClient, httpx.AsyncClient]:
|
|
276
|
-
|
|
277
|
-
verify = get_cert_bundle_path()
|
|
278
|
-
|
|
279
|
-
# Check if HTTP/2 is enabled in config
|
|
280
|
-
http2_enabled = get_http2()
|
|
281
|
-
|
|
282
|
-
# Check if custom retry transport should be disabled (e.g., for integration tests with proxies)
|
|
283
|
-
disable_retry_transport = os.environ.get(
|
|
284
|
-
"CODE_PUPPY_DISABLE_RETRY_TRANSPORT", ""
|
|
285
|
-
).lower() in ("1", "true", "yes")
|
|
286
|
-
|
|
287
|
-
# Check if proxy environment variables are set
|
|
288
|
-
has_proxy = bool(
|
|
289
|
-
os.environ.get("HTTP_PROXY")
|
|
290
|
-
or os.environ.get("HTTPS_PROXY")
|
|
291
|
-
or os.environ.get("http_proxy")
|
|
292
|
-
or os.environ.get("https_proxy")
|
|
293
|
-
)
|
|
294
|
-
|
|
295
|
-
# When retry transport is disabled (test mode), disable SSL verification
|
|
296
|
-
if disable_retry_transport:
|
|
297
|
-
verify = False
|
|
298
|
-
trust_env = True
|
|
299
|
-
elif has_proxy:
|
|
300
|
-
trust_env = True
|
|
301
|
-
else:
|
|
302
|
-
trust_env = False
|
|
291
|
+
config = _resolve_proxy_config(verify)
|
|
303
292
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
)
|
|
293
|
+
base_kwargs = {
|
|
294
|
+
"proxy": config.proxy_url,
|
|
295
|
+
"verify": config.verify,
|
|
296
|
+
"headers": headers or {},
|
|
297
|
+
"timeout": timeout,
|
|
298
|
+
"http2": config.http2_enabled,
|
|
299
|
+
"trust_env": config.trust_env,
|
|
300
|
+
}
|
|
313
301
|
|
|
314
302
|
if ReopenableAsyncClient is not None:
|
|
315
|
-
# Use RetryingAsyncClient if retries are enabled
|
|
316
303
|
client_class = (
|
|
317
|
-
RetryingAsyncClient if not
|
|
304
|
+
RetryingAsyncClient if not config.disable_retry else httpx.AsyncClient
|
|
318
305
|
)
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
kwargs = {
|
|
322
|
-
"proxy": proxy_url,
|
|
323
|
-
"verify": verify,
|
|
324
|
-
"headers": headers or {},
|
|
325
|
-
"timeout": timeout,
|
|
326
|
-
"http2": http2_enabled,
|
|
327
|
-
"trust_env": trust_env,
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
if not disable_retry_transport:
|
|
306
|
+
kwargs = {**base_kwargs, "client_class": client_class}
|
|
307
|
+
if not config.disable_retry:
|
|
331
308
|
kwargs["retry_status_codes"] = retry_status_codes
|
|
332
|
-
|
|
333
|
-
return ReopenableAsyncClient(client_class=client_class, **kwargs)
|
|
309
|
+
return ReopenableAsyncClient(**kwargs)
|
|
334
310
|
else:
|
|
335
|
-
# Fallback to RetryingAsyncClient
|
|
336
|
-
if not
|
|
311
|
+
# Fallback to RetryingAsyncClient or plain AsyncClient
|
|
312
|
+
if not config.disable_retry:
|
|
337
313
|
return RetryingAsyncClient(
|
|
338
|
-
retry_status_codes=retry_status_codes,
|
|
339
|
-
proxy=proxy_url,
|
|
340
|
-
verify=verify,
|
|
341
|
-
headers=headers or {},
|
|
342
|
-
timeout=timeout,
|
|
343
|
-
http2=http2_enabled,
|
|
344
|
-
trust_env=trust_env,
|
|
314
|
+
retry_status_codes=retry_status_codes, **base_kwargs
|
|
345
315
|
)
|
|
346
316
|
else:
|
|
347
|
-
return httpx.AsyncClient(
|
|
348
|
-
proxy=proxy_url,
|
|
349
|
-
verify=verify,
|
|
350
|
-
headers=headers or {},
|
|
351
|
-
timeout=timeout,
|
|
352
|
-
http2=http2_enabled,
|
|
353
|
-
trust_env=trust_env,
|
|
354
|
-
)
|
|
317
|
+
return httpx.AsyncClient(**base_kwargs)
|
|
355
318
|
|
|
356
319
|
|
|
357
320
|
def is_cert_bundle_available() -> bool:
|
|
@@ -209,6 +209,9 @@ class ShellStartMessage(BaseMessage):
|
|
|
209
209
|
default=None, description="Working directory for the command"
|
|
210
210
|
)
|
|
211
211
|
timeout: int = Field(default=60, description="Timeout in seconds")
|
|
212
|
+
background: bool = Field(
|
|
213
|
+
default=False, description="Whether command runs in background mode"
|
|
214
|
+
)
|
|
212
215
|
|
|
213
216
|
|
|
214
217
|
class ShellLineMessage(BaseMessage):
|
|
@@ -348,7 +348,17 @@ class RichConsoleRenderer:
|
|
|
348
348
|
# =========================================================================
|
|
349
349
|
|
|
350
350
|
def _render_file_listing(self, msg: FileListingMessage) -> None:
|
|
351
|
-
"""Render a directory listing
|
|
351
|
+
"""Render a compact directory listing with directory summaries.
|
|
352
|
+
|
|
353
|
+
Instead of listing every file, we group by directory and show:
|
|
354
|
+
- Directory name
|
|
355
|
+
- Number of files
|
|
356
|
+
- Total size
|
|
357
|
+
- Number of subdirectories
|
|
358
|
+
"""
|
|
359
|
+
import os
|
|
360
|
+
from collections import defaultdict
|
|
361
|
+
|
|
352
362
|
# Header on single line
|
|
353
363
|
rec_flag = f"(recursive={msg.recursive})"
|
|
354
364
|
banner = self._format_banner("directory_listing", "DIRECTORY LISTING")
|
|
@@ -357,32 +367,104 @@ class RichConsoleRenderer:
|
|
|
357
367
|
f"📂 [bold cyan]{msg.directory}[/bold cyan] [dim]{rec_flag}[/dim]\n"
|
|
358
368
|
)
|
|
359
369
|
|
|
360
|
-
#
|
|
361
|
-
|
|
362
|
-
|
|
370
|
+
# Build a tree structure: {parent_path: {files: [], dirs: set(), size: int}}
|
|
371
|
+
# Each key is a directory path, value contains direct children stats
|
|
372
|
+
dir_stats: dict = defaultdict(
|
|
373
|
+
lambda: {"files": [], "subdirs": set(), "total_size": 0}
|
|
374
|
+
)
|
|
375
|
+
|
|
376
|
+
# Root directory is represented as ""
|
|
377
|
+
root_key = ""
|
|
363
378
|
|
|
364
|
-
# Build tree structure from flat list
|
|
365
379
|
for entry in msg.files:
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
for d in range(entry.depth + 1):
|
|
369
|
-
if d == entry.depth:
|
|
370
|
-
prefix += "└── "
|
|
371
|
-
else:
|
|
372
|
-
prefix += " "
|
|
380
|
+
path = entry.path
|
|
381
|
+
parent = os.path.dirname(path) if os.path.dirname(path) else root_key
|
|
373
382
|
|
|
374
383
|
if entry.type == "dir":
|
|
375
|
-
|
|
384
|
+
# Register this dir as a subdir of its parent
|
|
385
|
+
dir_stats[parent]["subdirs"].add(path)
|
|
386
|
+
# Ensure the dir itself exists in stats (even if empty)
|
|
387
|
+
_ = dir_stats[path]
|
|
376
388
|
else:
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
389
|
+
# It's a file - add to parent's stats
|
|
390
|
+
dir_stats[parent]["files"].append(entry)
|
|
391
|
+
dir_stats[parent]["total_size"] += entry.size
|
|
392
|
+
|
|
393
|
+
def render_dir_tree(dir_path: str, depth: int = 0) -> None:
|
|
394
|
+
"""Recursively render directory with compact summary."""
|
|
395
|
+
stats = dir_stats.get(
|
|
396
|
+
dir_path, {"files": [], "subdirs": set(), "total_size": 0}
|
|
397
|
+
)
|
|
398
|
+
files = stats["files"]
|
|
399
|
+
subdirs = sorted(stats["subdirs"])
|
|
400
|
+
|
|
401
|
+
# Calculate total size including subdirectories (recursive)
|
|
402
|
+
def get_recursive_size(d: str) -> int:
|
|
403
|
+
s = dir_stats.get(d, {"files": [], "subdirs": set(), "total_size": 0})
|
|
404
|
+
size = s["total_size"]
|
|
405
|
+
for sub in s["subdirs"]:
|
|
406
|
+
size += get_recursive_size(sub)
|
|
407
|
+
return size
|
|
408
|
+
|
|
409
|
+
def get_recursive_file_count(d: str) -> int:
|
|
410
|
+
s = dir_stats.get(d, {"files": [], "subdirs": set(), "total_size": 0})
|
|
411
|
+
count = len(s["files"])
|
|
412
|
+
for sub in s["subdirs"]:
|
|
413
|
+
count += get_recursive_file_count(sub)
|
|
414
|
+
return count
|
|
415
|
+
|
|
416
|
+
indent = " " * depth
|
|
417
|
+
|
|
418
|
+
# For root level, just show contents
|
|
419
|
+
if dir_path == root_key:
|
|
420
|
+
# Show files at root level (depth 0)
|
|
421
|
+
for f in sorted(files, key=lambda x: x.path):
|
|
422
|
+
icon = self._get_file_icon(f.path)
|
|
423
|
+
name = os.path.basename(f.path)
|
|
424
|
+
size_str = (
|
|
425
|
+
f" [dim]({self._format_size(f.size)})[/dim]"
|
|
426
|
+
if f.size > 0
|
|
427
|
+
else ""
|
|
428
|
+
)
|
|
429
|
+
self._console.print(
|
|
430
|
+
f"{indent}{icon} [green]{name}[/green]{size_str}"
|
|
431
|
+
)
|
|
432
|
+
|
|
433
|
+
# Show subdirs at root level
|
|
434
|
+
for subdir in subdirs:
|
|
435
|
+
render_dir_tree(subdir, depth)
|
|
436
|
+
else:
|
|
437
|
+
# Show directory with summary
|
|
438
|
+
dir_name = os.path.basename(dir_path)
|
|
439
|
+
rec_size = get_recursive_size(dir_path)
|
|
440
|
+
rec_file_count = get_recursive_file_count(dir_path)
|
|
441
|
+
subdir_count = len(subdirs)
|
|
442
|
+
|
|
443
|
+
# Build summary parts
|
|
444
|
+
parts = []
|
|
445
|
+
if rec_file_count > 0:
|
|
446
|
+
parts.append(
|
|
447
|
+
f"{rec_file_count} file{'s' if rec_file_count != 1 else ''}"
|
|
448
|
+
)
|
|
449
|
+
if subdir_count > 0:
|
|
450
|
+
parts.append(
|
|
451
|
+
f"{subdir_count} subdir{'s' if subdir_count != 1 else ''}"
|
|
452
|
+
)
|
|
453
|
+
if rec_size > 0:
|
|
454
|
+
parts.append(self._format_size(rec_size))
|
|
455
|
+
|
|
456
|
+
summary = f" [dim]({', '.join(parts)})[/dim]" if parts else ""
|
|
382
457
|
self._console.print(
|
|
383
|
-
f"{
|
|
458
|
+
f"{indent}📁 [bold blue]{dir_name}/[/bold blue]{summary}"
|
|
384
459
|
)
|
|
385
460
|
|
|
461
|
+
# Recursively show subdirectories
|
|
462
|
+
for subdir in subdirs:
|
|
463
|
+
render_dir_tree(subdir, depth + 1)
|
|
464
|
+
|
|
465
|
+
# Render the tree starting from root
|
|
466
|
+
render_dir_tree(root_key, 0)
|
|
467
|
+
|
|
386
468
|
# Summary
|
|
387
469
|
self._console.print("\n[bold cyan]Summary:[/bold cyan]")
|
|
388
470
|
self._console.print(
|
|
@@ -538,15 +620,25 @@ class RichConsoleRenderer:
|
|
|
538
620
|
safe_command = escape_rich_markup(msg.command)
|
|
539
621
|
# Header showing command is starting
|
|
540
622
|
banner = self._format_banner("shell_command", "SHELL COMMAND")
|
|
541
|
-
|
|
623
|
+
|
|
624
|
+
# Add background indicator if running in background mode
|
|
625
|
+
if msg.background:
|
|
626
|
+
self._console.print(
|
|
627
|
+
f"\n{banner} 🚀 [dim]$ {safe_command}[/dim] [bold magenta][BACKGROUND 🌙][/bold magenta]"
|
|
628
|
+
)
|
|
629
|
+
else:
|
|
630
|
+
self._console.print(f"\n{banner} 🚀 [dim]$ {safe_command}[/dim]")
|
|
542
631
|
|
|
543
632
|
# Show working directory if specified
|
|
544
633
|
if msg.cwd:
|
|
545
634
|
safe_cwd = escape_rich_markup(msg.cwd)
|
|
546
635
|
self._console.print(f"[dim]📂 Working directory: {safe_cwd}[/dim]")
|
|
547
636
|
|
|
548
|
-
# Show timeout
|
|
549
|
-
|
|
637
|
+
# Show timeout or background status
|
|
638
|
+
if msg.background:
|
|
639
|
+
self._console.print("[dim]⏱ Runs detached (no timeout)[/dim]")
|
|
640
|
+
else:
|
|
641
|
+
self._console.print(f"[dim]⏱ Timeout: {msg.timeout}s[/dim]")
|
|
550
642
|
|
|
551
643
|
def _render_shell_line(self, msg: ShellLineMessage) -> None:
|
|
552
644
|
"""Render shell output line preserving ANSI codes."""
|
{code_puppy-0.0.335 → code_puppy-0.0.337}/code_puppy/plugins/antigravity_oauth/antigravity_model.py
RENAMED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import base64
|
|
3
4
|
import json
|
|
4
5
|
import logging
|
|
5
6
|
from collections.abc import AsyncIterator
|
|
@@ -54,7 +55,15 @@ class AntigravityModel(GoogleModel):
|
|
|
54
55
|
messages: list[ModelMessage],
|
|
55
56
|
model_request_parameters: ModelRequestParameters,
|
|
56
57
|
) -> tuple[ContentDict | None, list[dict]]:
|
|
57
|
-
"""Map messages to Google GenAI format, preserving thinking signatures.
|
|
58
|
+
"""Map messages to Google GenAI format, preserving thinking signatures.
|
|
59
|
+
|
|
60
|
+
IMPORTANT: For Gemini with parallel function calls, the API expects:
|
|
61
|
+
- Model message: [FC1 + signature, FC2, ...] (all function calls together)
|
|
62
|
+
- User message: [FR1, FR2, ...] (all function responses together)
|
|
63
|
+
|
|
64
|
+
If messages are interleaved (FC1, FR1, FC2, FR2), the API returns 400.
|
|
65
|
+
This method merges consecutive same-role messages to fix this.
|
|
66
|
+
"""
|
|
58
67
|
contents: list[dict] = []
|
|
59
68
|
system_parts: list[PartDict] = []
|
|
60
69
|
|
|
@@ -67,7 +76,16 @@ class AntigravityModel(GoogleModel):
|
|
|
67
76
|
system_parts.append({"text": part.content})
|
|
68
77
|
elif isinstance(part, UserPromptPart):
|
|
69
78
|
# Use parent's _map_user_prompt
|
|
70
|
-
|
|
79
|
+
mapped_parts = await self._map_user_prompt(part)
|
|
80
|
+
# Sanitize bytes to base64 for JSON serialization
|
|
81
|
+
for mp in mapped_parts:
|
|
82
|
+
if "inline_data" in mp and "data" in mp["inline_data"]:
|
|
83
|
+
data = mp["inline_data"]["data"]
|
|
84
|
+
if isinstance(data, bytes):
|
|
85
|
+
mp["inline_data"]["data"] = base64.b64encode(
|
|
86
|
+
data
|
|
87
|
+
).decode("utf-8")
|
|
88
|
+
message_parts.extend(mapped_parts)
|
|
71
89
|
elif isinstance(part, ToolReturnPart):
|
|
72
90
|
message_parts.append(
|
|
73
91
|
{
|
|
@@ -95,7 +113,12 @@ class AntigravityModel(GoogleModel):
|
|
|
95
113
|
assert_never(part)
|
|
96
114
|
|
|
97
115
|
if message_parts:
|
|
98
|
-
|
|
116
|
+
# Merge with previous user message if exists (for parallel function responses)
|
|
117
|
+
if contents and contents[-1].get("role") == "user":
|
|
118
|
+
contents[-1]["parts"].extend(message_parts)
|
|
119
|
+
else:
|
|
120
|
+
contents.append({"role": "user", "parts": message_parts})
|
|
121
|
+
|
|
99
122
|
elif isinstance(m, ModelResponse):
|
|
100
123
|
# USE CUSTOM HELPER HERE
|
|
101
124
|
# Pass model name so we can handle Claude vs Gemini signature placement
|
|
@@ -103,7 +126,11 @@ class AntigravityModel(GoogleModel):
|
|
|
103
126
|
m, self.system, self._model_name
|
|
104
127
|
)
|
|
105
128
|
if maybe_content:
|
|
106
|
-
|
|
129
|
+
# Merge with previous model message if exists (for parallel function calls)
|
|
130
|
+
if contents and contents[-1].get("role") == "model":
|
|
131
|
+
contents[-1]["parts"].extend(maybe_content["parts"])
|
|
132
|
+
else:
|
|
133
|
+
contents.append(maybe_content)
|
|
107
134
|
else:
|
|
108
135
|
assert_never(m)
|
|
109
136
|
|
|
@@ -435,6 +462,13 @@ class AntigravityStreamingResponse(StreamedResponse):
|
|
|
435
462
|
return self._timestamp_val
|
|
436
463
|
|
|
437
464
|
|
|
465
|
+
# Bypass signature for when no real thought signature is available.
|
|
466
|
+
# Gemini API requires EVERY function call to have a thoughtSignature field.
|
|
467
|
+
# When there's no thinking block or no signature was captured, we use this bypass.
|
|
468
|
+
# This specific key is the official bypass token for Gemini 3 Pro.
|
|
469
|
+
BYPASS_THOUGHT_SIGNATURE = "context_engineering_is_the_way_to_go"
|
|
470
|
+
|
|
471
|
+
|
|
438
472
|
def _antigravity_content_model_response(
|
|
439
473
|
m: ModelResponse, provider_name: str, model_name: str = ""
|
|
440
474
|
) -> ContentDict | None:
|
|
@@ -443,6 +477,10 @@ def _antigravity_content_model_response(
|
|
|
443
477
|
Handles different signature protocols:
|
|
444
478
|
- Claude models: signature goes ON the thinking block itself
|
|
445
479
|
- Gemini models: signature goes on the NEXT part (function_call or text) after thinking
|
|
480
|
+
|
|
481
|
+
IMPORTANT: For Gemini, EVERY function call MUST have a thoughtSignature field.
|
|
482
|
+
If no real signature is available (no preceding ThinkingPart, or ThinkingPart
|
|
483
|
+
had no signature), we use BYPASS_THOUGHT_SIGNATURE as a fallback.
|
|
446
484
|
"""
|
|
447
485
|
parts: list[PartDict] = []
|
|
448
486
|
|
|
@@ -451,6 +489,7 @@ def _antigravity_content_model_response(
|
|
|
451
489
|
is_gemini = "gemini" in model_name.lower()
|
|
452
490
|
|
|
453
491
|
# For Gemini: save signature from ThinkingPart to attach to next part
|
|
492
|
+
# Initialize to None - we'll use BYPASS_THOUGHT_SIGNATURE if still None when needed
|
|
454
493
|
pending_signature: str | None = None
|
|
455
494
|
|
|
456
495
|
for item in m.parts:
|
|
@@ -462,16 +501,24 @@ def _antigravity_content_model_response(
|
|
|
462
501
|
)
|
|
463
502
|
part["function_call"] = function_call
|
|
464
503
|
|
|
465
|
-
# For Gemini: attach
|
|
466
|
-
if
|
|
467
|
-
|
|
468
|
-
|
|
504
|
+
# For Gemini: ALWAYS attach a thoughtSignature to function calls.
|
|
505
|
+
# Use the real signature if available, otherwise use bypass.
|
|
506
|
+
# NOTE: Do NOT clear pending_signature here! Multiple tool calls
|
|
507
|
+
# in a row (e.g., parallel function calls) all need the same
|
|
508
|
+
# signature from the preceding ThinkingPart.
|
|
509
|
+
if is_gemini:
|
|
510
|
+
part["thoughtSignature"] = (
|
|
511
|
+
pending_signature
|
|
512
|
+
if pending_signature is not None
|
|
513
|
+
else BYPASS_THOUGHT_SIGNATURE
|
|
514
|
+
)
|
|
469
515
|
|
|
470
516
|
elif isinstance(item, TextPart):
|
|
471
517
|
part["text"] = item.content
|
|
472
518
|
|
|
473
|
-
# For Gemini: attach pending signature to text part
|
|
474
|
-
|
|
519
|
+
# For Gemini: attach pending signature to text part if available
|
|
520
|
+
# Clear signature after text since text typically ends a response
|
|
521
|
+
if is_gemini and pending_signature is not None:
|
|
475
522
|
part["thoughtSignature"] = pending_signature
|
|
476
523
|
pending_signature = None
|
|
477
524
|
|
|
@@ -490,6 +537,10 @@ def _antigravity_content_model_response(
|
|
|
490
537
|
else:
|
|
491
538
|
# Default: try both (put on thinking block)
|
|
492
539
|
part["thoughtSignature"] = item.signature
|
|
540
|
+
elif is_gemini:
|
|
541
|
+
# ThinkingPart exists but has no signature - use bypass
|
|
542
|
+
# This ensures subsequent tool calls still get a signature
|
|
543
|
+
pending_signature = BYPASS_THOUGHT_SIGNATURE
|
|
493
544
|
|
|
494
545
|
elif isinstance(item, BuiltinToolCallPart):
|
|
495
546
|
# Skip code execution for now
|
|
@@ -501,8 +552,13 @@ def _antigravity_content_model_response(
|
|
|
501
552
|
|
|
502
553
|
elif isinstance(item, FilePart):
|
|
503
554
|
content = item.content
|
|
555
|
+
# Ensure data is base64 string, not bytes
|
|
556
|
+
data_val = content.data
|
|
557
|
+
if isinstance(data_val, bytes):
|
|
558
|
+
data_val = base64.b64encode(data_val).decode("utf-8")
|
|
559
|
+
|
|
504
560
|
inline_data_dict: BlobDict = {
|
|
505
|
-
"data":
|
|
561
|
+
"data": data_val,
|
|
506
562
|
"mime_type": content.media_type,
|
|
507
563
|
}
|
|
508
564
|
part["inline_data"] = inline_data_dict
|