code-puppy 0.0.855__tar.gz → 0.0.856__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.855 → code_puppy-0.0.856}/PKG-INFO +1 -1
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/gemini_code_assist.py +8 -22
- code_puppy-0.0.856/code_puppy/gemini_common.py +328 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/gemini_model.py +2 -202
- {code_puppy-0.0.855 → code_puppy-0.0.856}/pyproject.toml +1 -1
- code_puppy-0.0.855/code_puppy/gemini_common.py +0 -103
- {code_puppy-0.0.855 → code_puppy-0.0.856}/.gitignore +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/LICENSE +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/README.md +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/__init__.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/__main__.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/_plugin_i18n_lifecycle.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/_pydantic_tool_helpers.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/agent_completion_inbox.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/agent_execution_context.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/agents/__init__.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/agents/_builder.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/agents/_compaction.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/agents/_diagnostics.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/agents/_history.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/agents/_key_listeners.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/agents/_model_message_transform.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/agents/_non_streaming_render.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/agents/_output_limits.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/agents/_run_signals.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/agents/_runtime.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/agents/_steer_processor.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/agents/_subagent_recursion.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/agents/_windows_console.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/agents/agent_code_puppy.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/agents/agent_creator_agent.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/agents/agent_helios.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/agents/agent_manager.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/agents/agent_model_judge.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/agents/agent_planning.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/agents/agent_qa_kitten.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/agents/agent_web_retriever.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/agents/base_agent.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/agents/event_stream_handler.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/agents/json_agent.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/agents/retry_checkpoint.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/agents/retry_profiles.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/agents/run_stats.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/agents/smooth_stream.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/agents/stream_status.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/agents/subagent_stream_handler.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/agents/thinking_output.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/asyncio_cleanup.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/atomic_io.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/atomic_json.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/callbacks.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/chatgpt_codex_client.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/claude_cache_client.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/claude_oauth_transport.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/cli_runner.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/__init__.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/add_model_details.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/add_model_menu.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/add_model_menus.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/add_model_vllm.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/agent_menu.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/attachments.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/autosave_menu.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/autosave_search.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/clipboard.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/colors_menu.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/command_handler.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/command_registry.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/completers.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/completion_cache.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/config_apply.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/config_commands.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/core_commands.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/file_index.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/file_path_completion.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/help_catalog.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/help_overlay.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/image_utils.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/load_context_completion.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/mcp/__init__.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/mcp/base.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/mcp/catalog_server_installer.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/mcp/custom_server_form.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/mcp/custom_server_installer.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/mcp/edit_command.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/mcp/handler.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/mcp/help_command.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/mcp/install_command.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/mcp/install_menu.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/mcp/list_command.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/mcp/logs_command.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/mcp/mcp_servers_store.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/mcp/remove_command.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/mcp/restart_command.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/mcp/search_command.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/mcp/silence_warning_command.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/mcp/start_all_command.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/mcp/start_command.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/mcp/status_command.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/mcp/stop_all_command.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/mcp/stop_command.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/mcp/trust_command.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/mcp/utils.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/mcp/wizard_utils.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/mcp_binding_menu.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/mcp_completion.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/menu_session.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/model_picker_completion.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/model_settings_defs.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/model_settings_menu.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/onboarding_slides.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/onboarding_wizard.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/pagination.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/pin_command_completion.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/refresh_models.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/session_browser.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/session_browser_data.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/session_commands.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/set_menu.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/set_menu_catalog.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/set_menu_schema.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/set_menu_settings.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/set_menu_shims.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/set_menu_values.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/shell_passthrough.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/skills_completion.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/tui_style.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/uc_menu.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/utils.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/config.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/config_file.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/diagnostics.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/error_logging.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/file_completion_io.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/file_completion_tokens.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/hook_engine/README.md +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/hook_engine/__init__.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/hook_engine/aliases.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/hook_engine/engine.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/hook_engine/executor.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/hook_engine/matcher.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/hook_engine/models.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/hook_engine/registry.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/hook_engine/validator.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/http_retry.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/http_utils.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/httpx2_utils.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/i18n/__init__.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/i18n/audit.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/i18n/catalog.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/i18n/formats.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/i18n/interpolation.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/i18n/locale.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/i18n/locales/en-US.json +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/i18n/locales/es.json +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/i18n/locales/fr-CA.json +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/i18n/plugin_catalog.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/i18n/plurals.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/i18n/pseudo.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/i18n/translate.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/kennel_provider.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/keymap.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/list_filtering.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/main.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/mcp_/__init__.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/mcp_/agent_bindings.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/mcp_/async_lifecycle.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/mcp_/blocking_startup.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/mcp_/circuit_breaker.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/mcp_/config_wizard.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/mcp_/dashboard.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/mcp_/error_isolation.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/mcp_/examples/retry_example.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/mcp_/health_monitor.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/mcp_/http_auth.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/mcp_/managed_server.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/mcp_/manager.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/mcp_/manager_lifecycle.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/mcp_/mcp_logs.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/mcp_/project_config.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/mcp_/registry.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/mcp_/retry_manager.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/mcp_/server_registry_catalog.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/mcp_/status_tracker.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/mcp_/system_tools.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/mcp_/tool_arg_coercion.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/mcp_/toolset_utils.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/mcp_prompts/__init__.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/mcp_prompts/hook_creator.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/messaging/__init__.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/messaging/bar_painters.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/messaging/bar_rendering.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/messaging/bottom_bar.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/messaging/bus.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/messaging/chords.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/messaging/commands.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/messaging/editor_actions.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/messaging/editor_completion.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/messaging/editor_display.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/messaging/editor_history.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/messaging/editor_keys.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/messaging/editor_paste.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/messaging/external_editor.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/messaging/identity_line.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/messaging/idle_status.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/messaging/inline_bar.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/messaging/line_editor.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/messaging/markdown_patches.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/messaging/menu_lifecycle.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/messaging/message_queue.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/messaging/messages.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/messaging/pause_controller.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/messaging/prompt_prefix_style.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/messaging/queue_console.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/messaging/renderers.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/messaging/rich_renderer.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/messaging/run_ui.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/messaging/run_ui_wiring.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/messaging/spinner/__init__.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/messaging/status_line.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/messaging/subagent_console.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/messaging/theme_accent.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/messaging/tool_output.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/messaging/transcript_guard.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/model_descriptions.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/model_factory.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/model_switching.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/model_utils.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/models.json +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/models_dev_api.json +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/models_dev_parser.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/observability.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/platform_utils.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/plugins/__init__.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/plugins/config.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/plugins/oauth_pasteback.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/plugins/oauth_puppy_html.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/plugins/trust.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/plugins/trust_notice.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/private_inference.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/provider_credentials.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/provider_identity.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/pydantic_patches.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/reopenable_async_client.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/round_robin_model.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/secret_store.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/secret_store_backends.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/session_format_migration.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/session_lifecycle.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/session_migration.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/session_storage.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/session_surrogate_unpickler.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/shared_credentials.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/skill_provider.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/splash.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/status_display.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/steer_metadata.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/terminal_utils.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/token_usage.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/tools/__init__.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/tools/_browser_registry.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/tools/_lazy.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/tools/agent_tools.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/tools/ask_user_question/__init__.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/tools/ask_user_question/constants.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/tools/ask_user_question/demo_tui.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/tools/ask_user_question/handler.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/tools/ask_user_question/models.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/tools/ask_user_question/registration.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/tools/ask_user_question/renderers.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/tools/ask_user_question/terminal_ui.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/tools/ask_user_question/theme.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/tools/ask_user_question/tui_loop.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/tools/browser/__init__.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/tools/browser/browser_control.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/tools/browser/browser_interactions.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/tools/browser/browser_locator_resolver.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/tools/browser/browser_locators.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/tools/browser/browser_manager.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/tools/browser/browser_navigation.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/tools/browser/browser_page_snapshot.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/tools/browser/browser_screenshot.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/tools/browser/browser_scripts.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/tools/browser/browser_semantic_interactions.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/tools/browser/browser_workflows.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/tools/browser/tool_registry.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/tools/command_runner.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/tools/common.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/tools/display.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/tools/file_modifications.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/tools/file_operations.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/tools/file_permission_state.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/tools/fs_access.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/tools/image_tools.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/tools/io_backends.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/tools/model_tools.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/tools/shell_backgrounding.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/tools/skills_tools.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/tools/subagent_context.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/tools/subagent_invocation.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/tools/subagent_usage_metrics.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/tools/tools_content.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/tools/universal_constructor.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/undo_manager.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/universal_constructor_provider.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/version_checker.py +0 -0
- {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/zai_model.py +0 -0
|
@@ -30,10 +30,13 @@ from pydantic_ai.messages import (
|
|
|
30
30
|
)
|
|
31
31
|
from pydantic_ai.models import Model, ModelRequestParameters
|
|
32
32
|
from pydantic_ai.settings import ModelSettings
|
|
33
|
-
from pydantic_ai.tools import ToolDefinition
|
|
34
33
|
from pydantic_ai.usage import RequestUsage
|
|
35
34
|
|
|
36
|
-
from code_puppy.gemini_common import
|
|
35
|
+
from code_puppy.gemini_common import (
|
|
36
|
+
_build_tools,
|
|
37
|
+
_parse_candidate_parts,
|
|
38
|
+
_build_generation_config,
|
|
39
|
+
)
|
|
37
40
|
|
|
38
41
|
logger = logging.getLogger(__name__)
|
|
39
42
|
|
|
@@ -227,9 +230,9 @@ class GeminiCodeAssistModel(Model):
|
|
|
227
230
|
|
|
228
231
|
# Add tools if available.
|
|
229
232
|
if model_request_parameters.function_tools:
|
|
230
|
-
inner_request["tools"] =
|
|
231
|
-
|
|
232
|
-
|
|
233
|
+
inner_request["tools"] = _build_tools(
|
|
234
|
+
model_request_parameters.function_tools
|
|
235
|
+
)
|
|
233
236
|
|
|
234
237
|
# Add generation config.
|
|
235
238
|
generation_config = _build_generation_config(model_settings)
|
|
@@ -244,23 +247,6 @@ class GeminiCodeAssistModel(Model):
|
|
|
244
247
|
"request": inner_request,
|
|
245
248
|
}
|
|
246
249
|
|
|
247
|
-
def _build_tools(self, tools: list[ToolDefinition]) -> Dict[str, Any]:
|
|
248
|
-
"""Build tool definitions for the API."""
|
|
249
|
-
function_declarations = []
|
|
250
|
-
|
|
251
|
-
for tool in tools:
|
|
252
|
-
func_decl: Dict[str, Any] = {
|
|
253
|
-
"name": tool.name,
|
|
254
|
-
"description": tool.description or "",
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
if tool.parameters_json_schema:
|
|
258
|
-
func_decl["parametersJsonSchema"] = tool.parameters_json_schema
|
|
259
|
-
|
|
260
|
-
function_declarations.append(func_decl)
|
|
261
|
-
|
|
262
|
-
return {"functionDeclarations": function_declarations}
|
|
263
|
-
|
|
264
250
|
def _parse_response(self, data: Dict[str, Any]) -> ModelResponse:
|
|
265
251
|
"""Parse the Code Assist API response."""
|
|
266
252
|
# Unwrap the Code Assist response format.
|
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
"""Shared Gemini wire-format helpers.
|
|
2
|
+
|
|
3
|
+
Consolidates logic that was previously duplicated and drifted across
|
|
4
|
+
`gemini_model.py::GeminiModel` and `gemini_code_assist.py::GeminiCodeAssistModel`.
|
|
5
|
+
|
|
6
|
+
Wire-format knowledge for the Gemini API should live in exactly one place.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import uuid
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
from pydantic_ai import (
|
|
13
|
+
ModelResponsePart,
|
|
14
|
+
ThinkingPart,
|
|
15
|
+
TextPart,
|
|
16
|
+
ToolCallPart,
|
|
17
|
+
RequestUsage,
|
|
18
|
+
)
|
|
19
|
+
from pydantic_ai.tools import ToolDefinition
|
|
20
|
+
from pydantic_ai.settings import ModelSettings
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def generate_tool_call_id() -> str:
|
|
24
|
+
"""Generate a unique tool call ID."""
|
|
25
|
+
return str(uuid.uuid4())
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _flatten_union_to_object_gemini(union_items: list, defs: dict, resolve_fn) -> dict:
|
|
29
|
+
"""Flatten a union of object types into a single object with all properties.
|
|
30
|
+
|
|
31
|
+
For discriminated unions like EditFilePayload, we merge all object types
|
|
32
|
+
into one with all properties (Gemini doesn't support anyOf/oneOf).
|
|
33
|
+
"""
|
|
34
|
+
import copy as copy_module
|
|
35
|
+
|
|
36
|
+
merged_properties = {}
|
|
37
|
+
has_string_type = False
|
|
38
|
+
|
|
39
|
+
for item in union_items:
|
|
40
|
+
if not isinstance(item, dict):
|
|
41
|
+
continue
|
|
42
|
+
|
|
43
|
+
# Resolve $ref first.
|
|
44
|
+
if "$ref" in item:
|
|
45
|
+
ref_path = item["$ref"]
|
|
46
|
+
ref_name = None
|
|
47
|
+
|
|
48
|
+
if ref_path.startswith("#/$defs/"):
|
|
49
|
+
ref_name = ref_path[8:]
|
|
50
|
+
|
|
51
|
+
elif ref_path.startswith("#/definitions/"):
|
|
52
|
+
ref_name = ref_path[14:]
|
|
53
|
+
|
|
54
|
+
if ref_name and ref_name in defs:
|
|
55
|
+
item = copy_module.deepcopy(defs[ref_name])
|
|
56
|
+
|
|
57
|
+
else:
|
|
58
|
+
continue
|
|
59
|
+
|
|
60
|
+
if item.get("type") == "string":
|
|
61
|
+
has_string_type = True
|
|
62
|
+
continue
|
|
63
|
+
|
|
64
|
+
if item.get("type") == "null":
|
|
65
|
+
continue
|
|
66
|
+
|
|
67
|
+
if item.get("type") == "object" or "properties" in item:
|
|
68
|
+
props = item.get("properties", {})
|
|
69
|
+
for prop_name, prop_schema in props.items():
|
|
70
|
+
if prop_name not in merged_properties:
|
|
71
|
+
merged_properties[prop_name] = resolve_fn(
|
|
72
|
+
copy_module.deepcopy(prop_schema)
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
if not merged_properties:
|
|
76
|
+
return {"type": "string"} if has_string_type else {"type": "object"}
|
|
77
|
+
|
|
78
|
+
return {
|
|
79
|
+
"type": "object",
|
|
80
|
+
"properties": merged_properties,
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def _sanitize_schema_for_gemini(schema: dict) -> dict:
|
|
85
|
+
"""Sanitize JSON schema for Gemini API compatibility.
|
|
86
|
+
|
|
87
|
+
Removes/transforms fields that Gemini doesn't support:
|
|
88
|
+
- $defs, definitions, $schema, $id
|
|
89
|
+
- additionalProperties
|
|
90
|
+
- $ref (inlined)
|
|
91
|
+
- anyOf/oneOf/allOf (flattened - Gemini doesn't support unions!)
|
|
92
|
+
- For unions of objects: merges into single object with all properties
|
|
93
|
+
- For simple unions (string | null): picks first non-null type
|
|
94
|
+
"""
|
|
95
|
+
import copy
|
|
96
|
+
|
|
97
|
+
if not isinstance(schema, dict):
|
|
98
|
+
return schema
|
|
99
|
+
|
|
100
|
+
# Make a deep copy to avoid modifying original.
|
|
101
|
+
schema = copy.deepcopy(schema)
|
|
102
|
+
|
|
103
|
+
# Extract $defs for reference resolution.
|
|
104
|
+
defs = schema.pop("$defs", schema.pop("definitions", {}))
|
|
105
|
+
|
|
106
|
+
def resolve_refs(obj):
|
|
107
|
+
"""Recursively resolve $ref references and clean schema."""
|
|
108
|
+
if isinstance(obj, dict):
|
|
109
|
+
# Handle anyOf/oneOf unions.
|
|
110
|
+
for union_key in ["anyOf", "oneOf"]:
|
|
111
|
+
if union_key in obj:
|
|
112
|
+
union = obj[union_key]
|
|
113
|
+
|
|
114
|
+
if isinstance(union, list):
|
|
115
|
+
# Check if this is a complex union of objects.
|
|
116
|
+
object_count = 0
|
|
117
|
+
has_refs = False
|
|
118
|
+
|
|
119
|
+
for item in union:
|
|
120
|
+
if isinstance(item, dict):
|
|
121
|
+
if "$ref" in item:
|
|
122
|
+
has_refs = True
|
|
123
|
+
object_count += 1
|
|
124
|
+
|
|
125
|
+
elif (
|
|
126
|
+
item.get("type") == "object" or "properties" in item
|
|
127
|
+
):
|
|
128
|
+
object_count += 1
|
|
129
|
+
|
|
130
|
+
# If multiple objects or has refs, flatten to single object.
|
|
131
|
+
if object_count > 1 or has_refs:
|
|
132
|
+
flattened = _flatten_union_to_object_gemini(
|
|
133
|
+
union, defs, resolve_refs
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
if "description" in obj:
|
|
137
|
+
flattened["description"] = obj["description"]
|
|
138
|
+
return flattened
|
|
139
|
+
|
|
140
|
+
# Simple union — pick first non-null type.
|
|
141
|
+
for item in union:
|
|
142
|
+
if isinstance(item, dict) and item.get("type") != "null":
|
|
143
|
+
result = dict(item)
|
|
144
|
+
|
|
145
|
+
if "description" in obj:
|
|
146
|
+
result["description"] = obj["description"]
|
|
147
|
+
return resolve_refs(result)
|
|
148
|
+
|
|
149
|
+
# Handle allOf by merging all schemas.
|
|
150
|
+
if "allOf" in obj:
|
|
151
|
+
all_of = obj["allOf"]
|
|
152
|
+
|
|
153
|
+
if isinstance(all_of, list):
|
|
154
|
+
merged = {}
|
|
155
|
+
merged_properties = {}
|
|
156
|
+
|
|
157
|
+
for item in all_of:
|
|
158
|
+
if isinstance(item, dict):
|
|
159
|
+
resolved_item = resolve_refs(item)
|
|
160
|
+
|
|
161
|
+
if "properties" in resolved_item:
|
|
162
|
+
merged_properties.update(
|
|
163
|
+
resolved_item.pop("properties")
|
|
164
|
+
)
|
|
165
|
+
merged.update(resolved_item)
|
|
166
|
+
|
|
167
|
+
if merged_properties:
|
|
168
|
+
merged["properties"] = merged_properties
|
|
169
|
+
|
|
170
|
+
for key, value in obj.items():
|
|
171
|
+
if key != "allOf":
|
|
172
|
+
merged[key] = value
|
|
173
|
+
|
|
174
|
+
return resolve_refs(merged)
|
|
175
|
+
|
|
176
|
+
# Check for $ref.
|
|
177
|
+
if "$ref" in obj:
|
|
178
|
+
ref_path = obj["$ref"]
|
|
179
|
+
ref_name = None
|
|
180
|
+
|
|
181
|
+
# Parse ref like "#/$defs/SomeType" or "#/definitions/SomeType".
|
|
182
|
+
if ref_path.startswith("#/$defs/"):
|
|
183
|
+
ref_name = ref_path[8:]
|
|
184
|
+
|
|
185
|
+
elif ref_path.startswith("#/definitions/"):
|
|
186
|
+
ref_name = ref_path[14:]
|
|
187
|
+
|
|
188
|
+
if ref_name and ref_name in defs:
|
|
189
|
+
resolved = resolve_refs(copy.deepcopy(defs[ref_name]))
|
|
190
|
+
other_props = {k: v for k, v in obj.items() if k != "$ref"}
|
|
191
|
+
|
|
192
|
+
if other_props:
|
|
193
|
+
resolved.update(resolve_refs(other_props))
|
|
194
|
+
return resolved
|
|
195
|
+
|
|
196
|
+
else:
|
|
197
|
+
return {"type": "object"}
|
|
198
|
+
|
|
199
|
+
# Recursively process and transform.
|
|
200
|
+
result = {}
|
|
201
|
+
|
|
202
|
+
for key, value in obj.items():
|
|
203
|
+
# Skip unsupported fields.
|
|
204
|
+
if key in (
|
|
205
|
+
"$defs",
|
|
206
|
+
"definitions",
|
|
207
|
+
"$schema",
|
|
208
|
+
"$id",
|
|
209
|
+
"additionalProperties",
|
|
210
|
+
"default",
|
|
211
|
+
"examples",
|
|
212
|
+
"const",
|
|
213
|
+
"anyOf", # Skip any remaining union types.
|
|
214
|
+
"oneOf",
|
|
215
|
+
"allOf",
|
|
216
|
+
):
|
|
217
|
+
continue
|
|
218
|
+
|
|
219
|
+
result[key] = resolve_refs(value)
|
|
220
|
+
return result
|
|
221
|
+
|
|
222
|
+
elif isinstance(obj, list):
|
|
223
|
+
return [resolve_refs(item) for item in obj]
|
|
224
|
+
|
|
225
|
+
else:
|
|
226
|
+
return obj
|
|
227
|
+
|
|
228
|
+
return resolve_refs(schema)
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
def _build_tools(tools: list[ToolDefinition]) -> list[dict[str, Any]]:
|
|
232
|
+
"""Build tool definitions for the API."""
|
|
233
|
+
function_declarations = []
|
|
234
|
+
|
|
235
|
+
for tool in tools:
|
|
236
|
+
func_decl: dict[str, Any] = {
|
|
237
|
+
"name": tool.name,
|
|
238
|
+
"description": tool.description or "",
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
if tool.parameters_json_schema:
|
|
242
|
+
# Sanitize schema for Gemini compatibility.
|
|
243
|
+
func_decl["parameters"] = _sanitize_schema_for_gemini(
|
|
244
|
+
tool.parameters_json_schema
|
|
245
|
+
)
|
|
246
|
+
|
|
247
|
+
function_declarations.append(func_decl)
|
|
248
|
+
|
|
249
|
+
return [{"functionDeclarations": function_declarations}]
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
def _parse_candidate_parts(
|
|
253
|
+
data: dict[str, Any], candidates: list[dict[str, Any]]
|
|
254
|
+
) -> tuple[RequestUsage, list[ModelResponsePart]]:
|
|
255
|
+
"""Parse candidate parts from Gemini API response."""
|
|
256
|
+
# Extract usage.
|
|
257
|
+
usage_meta = data.get("usageMetadata", {})
|
|
258
|
+
usage = RequestUsage(
|
|
259
|
+
input_tokens=usage_meta.get("promptTokenCount", 0),
|
|
260
|
+
output_tokens=usage_meta.get("candidatesTokenCount", 0),
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
response_parts: list[ModelResponsePart] = []
|
|
264
|
+
|
|
265
|
+
if not candidates:
|
|
266
|
+
return usage, response_parts
|
|
267
|
+
|
|
268
|
+
candidate = candidates[0]
|
|
269
|
+
content = candidate.get("content", {})
|
|
270
|
+
parts = content.get("parts", [])
|
|
271
|
+
|
|
272
|
+
for part in parts:
|
|
273
|
+
if part.get("thought") and part.get("text") is not None:
|
|
274
|
+
# Thinking part.
|
|
275
|
+
signature = part.get("thoughtSignature")
|
|
276
|
+
response_parts.append(
|
|
277
|
+
ThinkingPart(content=part["text"], signature=signature)
|
|
278
|
+
)
|
|
279
|
+
|
|
280
|
+
elif "text" in part:
|
|
281
|
+
response_parts.append(TextPart(content=part["text"]))
|
|
282
|
+
|
|
283
|
+
elif "functionCall" in part:
|
|
284
|
+
fc = part["functionCall"]
|
|
285
|
+
|
|
286
|
+
response_parts.append(
|
|
287
|
+
ToolCallPart(
|
|
288
|
+
tool_name=fc["name"],
|
|
289
|
+
args=fc.get("args", {}),
|
|
290
|
+
tool_call_id=fc.get("id") or generate_tool_call_id(),
|
|
291
|
+
)
|
|
292
|
+
)
|
|
293
|
+
|
|
294
|
+
return usage, response_parts
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
def _build_generation_config(model_settings: ModelSettings | None) -> dict[str, Any]:
|
|
298
|
+
"""Build generation config from model settings."""
|
|
299
|
+
config: dict[str, Any] = {}
|
|
300
|
+
|
|
301
|
+
if model_settings:
|
|
302
|
+
# ModelSettings is a TypedDict, so use .get() for all access.
|
|
303
|
+
temperature = model_settings.get("temperature")
|
|
304
|
+
if temperature is not None:
|
|
305
|
+
config["temperature"] = temperature
|
|
306
|
+
|
|
307
|
+
top_p = model_settings.get("top_p")
|
|
308
|
+
if top_p is not None:
|
|
309
|
+
config["topP"] = top_p
|
|
310
|
+
|
|
311
|
+
max_tokens = model_settings.get("max_tokens")
|
|
312
|
+
if max_tokens is not None:
|
|
313
|
+
config["maxOutputTokens"] = max_tokens
|
|
314
|
+
|
|
315
|
+
# Handle Gemini 3 Pro thinking settings.
|
|
316
|
+
thinking_enabled = model_settings.get("thinking_enabled")
|
|
317
|
+
thinking_level = model_settings.get("thinking_level")
|
|
318
|
+
|
|
319
|
+
# Build `thinkingConfig` if thinking settings are present.
|
|
320
|
+
if thinking_enabled is not False and thinking_level is not None:
|
|
321
|
+
# Gemini 3 Pro uses thinkingLevel with values "low" or "high".
|
|
322
|
+
# `includeThoughts=True` is required to surface the thinking in response.
|
|
323
|
+
config["thinkingConfig"] = {
|
|
324
|
+
"thinkingLevel": thinking_level,
|
|
325
|
+
"includeThoughts": True,
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
return config
|
|
@@ -34,10 +34,10 @@ from pydantic_ai.messages import (
|
|
|
34
34
|
)
|
|
35
35
|
from pydantic_ai.models import Model, ModelRequestParameters, StreamedResponse
|
|
36
36
|
from pydantic_ai.settings import ModelSettings
|
|
37
|
-
from pydantic_ai.tools import ToolDefinition
|
|
38
37
|
from pydantic_ai.usage import RequestUsage
|
|
39
38
|
|
|
40
39
|
from code_puppy.gemini_common import (
|
|
40
|
+
_build_tools,
|
|
41
41
|
_parse_candidate_parts,
|
|
42
42
|
generate_tool_call_id,
|
|
43
43
|
_build_generation_config,
|
|
@@ -57,188 +57,6 @@ STEER_PREAMBLE = (
|
|
|
57
57
|
)
|
|
58
58
|
|
|
59
59
|
|
|
60
|
-
def _flatten_union_to_object_gemini(union_items: list, defs: dict, resolve_fn) -> dict:
|
|
61
|
-
"""Flatten a union of object types into a single object with all properties.
|
|
62
|
-
|
|
63
|
-
For discriminated unions like EditFilePayload, we merge all object types
|
|
64
|
-
into one with all properties (Gemini doesn't support anyOf/oneOf).
|
|
65
|
-
"""
|
|
66
|
-
import copy as copy_module
|
|
67
|
-
|
|
68
|
-
merged_properties = {}
|
|
69
|
-
has_string_type = False
|
|
70
|
-
|
|
71
|
-
for item in union_items:
|
|
72
|
-
if not isinstance(item, dict):
|
|
73
|
-
continue
|
|
74
|
-
|
|
75
|
-
# Resolve $ref first
|
|
76
|
-
if "$ref" in item:
|
|
77
|
-
ref_path = item["$ref"]
|
|
78
|
-
ref_name = None
|
|
79
|
-
if ref_path.startswith("#/$defs/"):
|
|
80
|
-
ref_name = ref_path[8:]
|
|
81
|
-
elif ref_path.startswith("#/definitions/"):
|
|
82
|
-
ref_name = ref_path[14:]
|
|
83
|
-
if ref_name and ref_name in defs:
|
|
84
|
-
item = copy_module.deepcopy(defs[ref_name])
|
|
85
|
-
else:
|
|
86
|
-
continue
|
|
87
|
-
|
|
88
|
-
if item.get("type") == "string":
|
|
89
|
-
has_string_type = True
|
|
90
|
-
continue
|
|
91
|
-
|
|
92
|
-
if item.get("type") == "null":
|
|
93
|
-
continue
|
|
94
|
-
|
|
95
|
-
if item.get("type") == "object" or "properties" in item:
|
|
96
|
-
props = item.get("properties", {})
|
|
97
|
-
for prop_name, prop_schema in props.items():
|
|
98
|
-
if prop_name not in merged_properties:
|
|
99
|
-
merged_properties[prop_name] = resolve_fn(
|
|
100
|
-
copy_module.deepcopy(prop_schema)
|
|
101
|
-
)
|
|
102
|
-
|
|
103
|
-
if not merged_properties:
|
|
104
|
-
return {"type": "string"} if has_string_type else {"type": "object"}
|
|
105
|
-
|
|
106
|
-
return {
|
|
107
|
-
"type": "object",
|
|
108
|
-
"properties": merged_properties,
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
def _sanitize_schema_for_gemini(schema: dict) -> dict:
|
|
113
|
-
"""Sanitize JSON schema for Gemini API compatibility.
|
|
114
|
-
|
|
115
|
-
Removes/transforms fields that Gemini doesn't support:
|
|
116
|
-
- $defs, definitions, $schema, $id
|
|
117
|
-
- additionalProperties
|
|
118
|
-
- $ref (inlined)
|
|
119
|
-
- anyOf/oneOf/allOf (flattened - Gemini doesn't support unions!)
|
|
120
|
-
- For unions of objects: merges into single object with all properties
|
|
121
|
-
- For simple unions (string | null): picks first non-null type
|
|
122
|
-
"""
|
|
123
|
-
import copy
|
|
124
|
-
|
|
125
|
-
if not isinstance(schema, dict):
|
|
126
|
-
return schema
|
|
127
|
-
|
|
128
|
-
# Make a deep copy to avoid modifying original
|
|
129
|
-
schema = copy.deepcopy(schema)
|
|
130
|
-
|
|
131
|
-
# Extract $defs for reference resolution
|
|
132
|
-
defs = schema.pop("$defs", schema.pop("definitions", {}))
|
|
133
|
-
|
|
134
|
-
def resolve_refs(obj):
|
|
135
|
-
"""Recursively resolve $ref references and clean schema."""
|
|
136
|
-
if isinstance(obj, dict):
|
|
137
|
-
# Handle anyOf/oneOf unions
|
|
138
|
-
for union_key in ["anyOf", "oneOf"]:
|
|
139
|
-
if union_key in obj:
|
|
140
|
-
union = obj[union_key]
|
|
141
|
-
if isinstance(union, list):
|
|
142
|
-
# Check if this is a complex union of objects
|
|
143
|
-
object_count = 0
|
|
144
|
-
has_refs = False
|
|
145
|
-
for item in union:
|
|
146
|
-
if isinstance(item, dict):
|
|
147
|
-
if "$ref" in item:
|
|
148
|
-
has_refs = True
|
|
149
|
-
object_count += 1
|
|
150
|
-
elif (
|
|
151
|
-
item.get("type") == "object" or "properties" in item
|
|
152
|
-
):
|
|
153
|
-
object_count += 1
|
|
154
|
-
|
|
155
|
-
# If multiple objects or has refs, flatten to single object
|
|
156
|
-
if object_count > 1 or has_refs:
|
|
157
|
-
flattened = _flatten_union_to_object_gemini(
|
|
158
|
-
union, defs, resolve_refs
|
|
159
|
-
)
|
|
160
|
-
if "description" in obj:
|
|
161
|
-
flattened["description"] = obj["description"]
|
|
162
|
-
return flattened
|
|
163
|
-
|
|
164
|
-
# Simple union - pick first non-null type
|
|
165
|
-
for item in union:
|
|
166
|
-
if isinstance(item, dict) and item.get("type") != "null":
|
|
167
|
-
result = dict(item)
|
|
168
|
-
if "description" in obj:
|
|
169
|
-
result["description"] = obj["description"]
|
|
170
|
-
return resolve_refs(result)
|
|
171
|
-
|
|
172
|
-
# Handle allOf by merging all schemas
|
|
173
|
-
if "allOf" in obj:
|
|
174
|
-
all_of = obj["allOf"]
|
|
175
|
-
if isinstance(all_of, list):
|
|
176
|
-
merged = {}
|
|
177
|
-
merged_properties = {}
|
|
178
|
-
for item in all_of:
|
|
179
|
-
if isinstance(item, dict):
|
|
180
|
-
resolved_item = resolve_refs(item)
|
|
181
|
-
if "properties" in resolved_item:
|
|
182
|
-
merged_properties.update(
|
|
183
|
-
resolved_item.pop("properties")
|
|
184
|
-
)
|
|
185
|
-
merged.update(resolved_item)
|
|
186
|
-
if merged_properties:
|
|
187
|
-
merged["properties"] = merged_properties
|
|
188
|
-
for k, v in obj.items():
|
|
189
|
-
if k != "allOf":
|
|
190
|
-
merged[k] = v
|
|
191
|
-
return resolve_refs(merged)
|
|
192
|
-
|
|
193
|
-
# Check for $ref
|
|
194
|
-
if "$ref" in obj:
|
|
195
|
-
ref_path = obj["$ref"]
|
|
196
|
-
ref_name = None
|
|
197
|
-
|
|
198
|
-
# Parse ref like "#/$defs/SomeType" or "#/definitions/SomeType"
|
|
199
|
-
if ref_path.startswith("#/$defs/"):
|
|
200
|
-
ref_name = ref_path[8:]
|
|
201
|
-
elif ref_path.startswith("#/definitions/"):
|
|
202
|
-
ref_name = ref_path[14:]
|
|
203
|
-
|
|
204
|
-
if ref_name and ref_name in defs:
|
|
205
|
-
resolved = resolve_refs(copy.deepcopy(defs[ref_name]))
|
|
206
|
-
other_props = {k: v for k, v in obj.items() if k != "$ref"}
|
|
207
|
-
if other_props:
|
|
208
|
-
resolved.update(resolve_refs(other_props))
|
|
209
|
-
return resolved
|
|
210
|
-
else:
|
|
211
|
-
return {"type": "object"}
|
|
212
|
-
|
|
213
|
-
# Recursively process and transform
|
|
214
|
-
result = {}
|
|
215
|
-
for key, value in obj.items():
|
|
216
|
-
# Skip unsupported fields
|
|
217
|
-
if key in (
|
|
218
|
-
"$defs",
|
|
219
|
-
"definitions",
|
|
220
|
-
"$schema",
|
|
221
|
-
"$id",
|
|
222
|
-
"additionalProperties",
|
|
223
|
-
"default",
|
|
224
|
-
"examples",
|
|
225
|
-
"const",
|
|
226
|
-
"anyOf", # Skip any remaining union types
|
|
227
|
-
"oneOf",
|
|
228
|
-
"allOf",
|
|
229
|
-
):
|
|
230
|
-
continue
|
|
231
|
-
|
|
232
|
-
result[key] = resolve_refs(value)
|
|
233
|
-
return result
|
|
234
|
-
elif isinstance(obj, list):
|
|
235
|
-
return [resolve_refs(item) for item in obj]
|
|
236
|
-
else:
|
|
237
|
-
return obj
|
|
238
|
-
|
|
239
|
-
return resolve_refs(schema)
|
|
240
|
-
|
|
241
|
-
|
|
242
60
|
def _split_mixed_user_contents(contents: list[dict[str, Any]]) -> None:
|
|
243
61
|
"""Split user turns carrying both function responses and other parts.
|
|
244
62
|
|
|
@@ -580,24 +398,6 @@ class GeminiModel(Model):
|
|
|
580
398
|
return None
|
|
581
399
|
return {"role": "model", "parts": parts}
|
|
582
400
|
|
|
583
|
-
def _build_tools(self, tools: list[ToolDefinition]) -> list[dict[str, Any]]:
|
|
584
|
-
"""Build tool definitions for the API."""
|
|
585
|
-
function_declarations = []
|
|
586
|
-
|
|
587
|
-
for tool in tools:
|
|
588
|
-
func_decl: dict[str, Any] = {
|
|
589
|
-
"name": tool.name,
|
|
590
|
-
"description": tool.description or "",
|
|
591
|
-
}
|
|
592
|
-
if tool.parameters_json_schema:
|
|
593
|
-
# Sanitize schema for Gemini compatibility
|
|
594
|
-
func_decl["parameters"] = _sanitize_schema_for_gemini(
|
|
595
|
-
tool.parameters_json_schema
|
|
596
|
-
)
|
|
597
|
-
function_declarations.append(func_decl)
|
|
598
|
-
|
|
599
|
-
return [{"functionDeclarations": function_declarations}]
|
|
600
|
-
|
|
601
401
|
def _build_request_body(
|
|
602
402
|
self,
|
|
603
403
|
system_instruction,
|
|
@@ -618,7 +418,7 @@ class GeminiModel(Model):
|
|
|
618
418
|
|
|
619
419
|
# Add tools.
|
|
620
420
|
if model_request_parameters.function_tools:
|
|
621
|
-
body["tools"] =
|
|
421
|
+
body["tools"] = _build_tools(model_request_parameters.function_tools)
|
|
622
422
|
|
|
623
423
|
if streaming:
|
|
624
424
|
body["toolConfig"] = {
|