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.
Files changed (307) hide show
  1. {code_puppy-0.0.855 → code_puppy-0.0.856}/PKG-INFO +1 -1
  2. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/gemini_code_assist.py +8 -22
  3. code_puppy-0.0.856/code_puppy/gemini_common.py +328 -0
  4. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/gemini_model.py +2 -202
  5. {code_puppy-0.0.855 → code_puppy-0.0.856}/pyproject.toml +1 -1
  6. code_puppy-0.0.855/code_puppy/gemini_common.py +0 -103
  7. {code_puppy-0.0.855 → code_puppy-0.0.856}/.gitignore +0 -0
  8. {code_puppy-0.0.855 → code_puppy-0.0.856}/LICENSE +0 -0
  9. {code_puppy-0.0.855 → code_puppy-0.0.856}/README.md +0 -0
  10. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/__init__.py +0 -0
  11. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/__main__.py +0 -0
  12. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/_plugin_i18n_lifecycle.py +0 -0
  13. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/_pydantic_tool_helpers.py +0 -0
  14. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/agent_completion_inbox.py +0 -0
  15. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/agent_execution_context.py +0 -0
  16. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/agents/__init__.py +0 -0
  17. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/agents/_builder.py +0 -0
  18. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/agents/_compaction.py +0 -0
  19. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/agents/_diagnostics.py +0 -0
  20. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/agents/_history.py +0 -0
  21. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/agents/_key_listeners.py +0 -0
  22. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/agents/_model_message_transform.py +0 -0
  23. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/agents/_non_streaming_render.py +0 -0
  24. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/agents/_output_limits.py +0 -0
  25. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/agents/_run_signals.py +0 -0
  26. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/agents/_runtime.py +0 -0
  27. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/agents/_steer_processor.py +0 -0
  28. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/agents/_subagent_recursion.py +0 -0
  29. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/agents/_windows_console.py +0 -0
  30. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/agents/agent_code_puppy.py +0 -0
  31. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/agents/agent_creator_agent.py +0 -0
  32. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/agents/agent_helios.py +0 -0
  33. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/agents/agent_manager.py +0 -0
  34. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/agents/agent_model_judge.py +0 -0
  35. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/agents/agent_planning.py +0 -0
  36. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/agents/agent_qa_kitten.py +0 -0
  37. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/agents/agent_web_retriever.py +0 -0
  38. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/agents/base_agent.py +0 -0
  39. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/agents/event_stream_handler.py +0 -0
  40. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/agents/json_agent.py +0 -0
  41. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/agents/retry_checkpoint.py +0 -0
  42. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/agents/retry_profiles.py +0 -0
  43. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/agents/run_stats.py +0 -0
  44. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/agents/smooth_stream.py +0 -0
  45. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/agents/stream_status.py +0 -0
  46. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/agents/subagent_stream_handler.py +0 -0
  47. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/agents/thinking_output.py +0 -0
  48. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/asyncio_cleanup.py +0 -0
  49. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/atomic_io.py +0 -0
  50. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/atomic_json.py +0 -0
  51. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/callbacks.py +0 -0
  52. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/chatgpt_codex_client.py +0 -0
  53. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/claude_cache_client.py +0 -0
  54. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/claude_oauth_transport.py +0 -0
  55. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/cli_runner.py +0 -0
  56. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/__init__.py +0 -0
  57. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/add_model_details.py +0 -0
  58. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/add_model_menu.py +0 -0
  59. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/add_model_menus.py +0 -0
  60. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/add_model_vllm.py +0 -0
  61. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/agent_menu.py +0 -0
  62. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/attachments.py +0 -0
  63. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/autosave_menu.py +0 -0
  64. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/autosave_search.py +0 -0
  65. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/clipboard.py +0 -0
  66. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/colors_menu.py +0 -0
  67. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/command_handler.py +0 -0
  68. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/command_registry.py +0 -0
  69. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/completers.py +0 -0
  70. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/completion_cache.py +0 -0
  71. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/config_apply.py +0 -0
  72. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/config_commands.py +0 -0
  73. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/core_commands.py +0 -0
  74. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/file_index.py +0 -0
  75. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/file_path_completion.py +0 -0
  76. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/help_catalog.py +0 -0
  77. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/help_overlay.py +0 -0
  78. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/image_utils.py +0 -0
  79. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/load_context_completion.py +0 -0
  80. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/mcp/__init__.py +0 -0
  81. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/mcp/base.py +0 -0
  82. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/mcp/catalog_server_installer.py +0 -0
  83. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/mcp/custom_server_form.py +0 -0
  84. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/mcp/custom_server_installer.py +0 -0
  85. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/mcp/edit_command.py +0 -0
  86. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/mcp/handler.py +0 -0
  87. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/mcp/help_command.py +0 -0
  88. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/mcp/install_command.py +0 -0
  89. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/mcp/install_menu.py +0 -0
  90. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/mcp/list_command.py +0 -0
  91. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/mcp/logs_command.py +0 -0
  92. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/mcp/mcp_servers_store.py +0 -0
  93. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/mcp/remove_command.py +0 -0
  94. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/mcp/restart_command.py +0 -0
  95. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/mcp/search_command.py +0 -0
  96. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/mcp/silence_warning_command.py +0 -0
  97. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/mcp/start_all_command.py +0 -0
  98. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/mcp/start_command.py +0 -0
  99. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/mcp/status_command.py +0 -0
  100. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/mcp/stop_all_command.py +0 -0
  101. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/mcp/stop_command.py +0 -0
  102. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/mcp/trust_command.py +0 -0
  103. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/mcp/utils.py +0 -0
  104. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/mcp/wizard_utils.py +0 -0
  105. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/mcp_binding_menu.py +0 -0
  106. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/mcp_completion.py +0 -0
  107. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/menu_session.py +0 -0
  108. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/model_picker_completion.py +0 -0
  109. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/model_settings_defs.py +0 -0
  110. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/model_settings_menu.py +0 -0
  111. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/onboarding_slides.py +0 -0
  112. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/onboarding_wizard.py +0 -0
  113. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/pagination.py +0 -0
  114. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/pin_command_completion.py +0 -0
  115. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/refresh_models.py +0 -0
  116. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/session_browser.py +0 -0
  117. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/session_browser_data.py +0 -0
  118. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/session_commands.py +0 -0
  119. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/set_menu.py +0 -0
  120. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/set_menu_catalog.py +0 -0
  121. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/set_menu_schema.py +0 -0
  122. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/set_menu_settings.py +0 -0
  123. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/set_menu_shims.py +0 -0
  124. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/set_menu_values.py +0 -0
  125. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/shell_passthrough.py +0 -0
  126. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/skills_completion.py +0 -0
  127. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/tui_style.py +0 -0
  128. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/uc_menu.py +0 -0
  129. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/command_line/utils.py +0 -0
  130. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/config.py +0 -0
  131. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/config_file.py +0 -0
  132. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/diagnostics.py +0 -0
  133. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/error_logging.py +0 -0
  134. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/file_completion_io.py +0 -0
  135. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/file_completion_tokens.py +0 -0
  136. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/hook_engine/README.md +0 -0
  137. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/hook_engine/__init__.py +0 -0
  138. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/hook_engine/aliases.py +0 -0
  139. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/hook_engine/engine.py +0 -0
  140. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/hook_engine/executor.py +0 -0
  141. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/hook_engine/matcher.py +0 -0
  142. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/hook_engine/models.py +0 -0
  143. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/hook_engine/registry.py +0 -0
  144. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/hook_engine/validator.py +0 -0
  145. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/http_retry.py +0 -0
  146. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/http_utils.py +0 -0
  147. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/httpx2_utils.py +0 -0
  148. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/i18n/__init__.py +0 -0
  149. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/i18n/audit.py +0 -0
  150. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/i18n/catalog.py +0 -0
  151. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/i18n/formats.py +0 -0
  152. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/i18n/interpolation.py +0 -0
  153. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/i18n/locale.py +0 -0
  154. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/i18n/locales/en-US.json +0 -0
  155. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/i18n/locales/es.json +0 -0
  156. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/i18n/locales/fr-CA.json +0 -0
  157. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/i18n/plugin_catalog.py +0 -0
  158. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/i18n/plurals.py +0 -0
  159. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/i18n/pseudo.py +0 -0
  160. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/i18n/translate.py +0 -0
  161. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/kennel_provider.py +0 -0
  162. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/keymap.py +0 -0
  163. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/list_filtering.py +0 -0
  164. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/main.py +0 -0
  165. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/mcp_/__init__.py +0 -0
  166. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/mcp_/agent_bindings.py +0 -0
  167. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/mcp_/async_lifecycle.py +0 -0
  168. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/mcp_/blocking_startup.py +0 -0
  169. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/mcp_/circuit_breaker.py +0 -0
  170. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/mcp_/config_wizard.py +0 -0
  171. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/mcp_/dashboard.py +0 -0
  172. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/mcp_/error_isolation.py +0 -0
  173. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/mcp_/examples/retry_example.py +0 -0
  174. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/mcp_/health_monitor.py +0 -0
  175. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/mcp_/http_auth.py +0 -0
  176. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/mcp_/managed_server.py +0 -0
  177. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/mcp_/manager.py +0 -0
  178. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/mcp_/manager_lifecycle.py +0 -0
  179. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/mcp_/mcp_logs.py +0 -0
  180. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/mcp_/project_config.py +0 -0
  181. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/mcp_/registry.py +0 -0
  182. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/mcp_/retry_manager.py +0 -0
  183. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/mcp_/server_registry_catalog.py +0 -0
  184. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/mcp_/status_tracker.py +0 -0
  185. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/mcp_/system_tools.py +0 -0
  186. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/mcp_/tool_arg_coercion.py +0 -0
  187. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/mcp_/toolset_utils.py +0 -0
  188. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/mcp_prompts/__init__.py +0 -0
  189. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/mcp_prompts/hook_creator.py +0 -0
  190. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/messaging/__init__.py +0 -0
  191. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/messaging/bar_painters.py +0 -0
  192. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/messaging/bar_rendering.py +0 -0
  193. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/messaging/bottom_bar.py +0 -0
  194. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/messaging/bus.py +0 -0
  195. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/messaging/chords.py +0 -0
  196. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/messaging/commands.py +0 -0
  197. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/messaging/editor_actions.py +0 -0
  198. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/messaging/editor_completion.py +0 -0
  199. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/messaging/editor_display.py +0 -0
  200. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/messaging/editor_history.py +0 -0
  201. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/messaging/editor_keys.py +0 -0
  202. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/messaging/editor_paste.py +0 -0
  203. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/messaging/external_editor.py +0 -0
  204. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/messaging/identity_line.py +0 -0
  205. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/messaging/idle_status.py +0 -0
  206. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/messaging/inline_bar.py +0 -0
  207. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/messaging/line_editor.py +0 -0
  208. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/messaging/markdown_patches.py +0 -0
  209. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/messaging/menu_lifecycle.py +0 -0
  210. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/messaging/message_queue.py +0 -0
  211. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/messaging/messages.py +0 -0
  212. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/messaging/pause_controller.py +0 -0
  213. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/messaging/prompt_prefix_style.py +0 -0
  214. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/messaging/queue_console.py +0 -0
  215. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/messaging/renderers.py +0 -0
  216. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/messaging/rich_renderer.py +0 -0
  217. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/messaging/run_ui.py +0 -0
  218. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/messaging/run_ui_wiring.py +0 -0
  219. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/messaging/spinner/__init__.py +0 -0
  220. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/messaging/status_line.py +0 -0
  221. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/messaging/subagent_console.py +0 -0
  222. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/messaging/theme_accent.py +0 -0
  223. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/messaging/tool_output.py +0 -0
  224. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/messaging/transcript_guard.py +0 -0
  225. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/model_descriptions.py +0 -0
  226. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/model_factory.py +0 -0
  227. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/model_switching.py +0 -0
  228. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/model_utils.py +0 -0
  229. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/models.json +0 -0
  230. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/models_dev_api.json +0 -0
  231. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/models_dev_parser.py +0 -0
  232. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/observability.py +0 -0
  233. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/platform_utils.py +0 -0
  234. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/plugins/__init__.py +0 -0
  235. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/plugins/config.py +0 -0
  236. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/plugins/oauth_pasteback.py +0 -0
  237. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/plugins/oauth_puppy_html.py +0 -0
  238. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/plugins/trust.py +0 -0
  239. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/plugins/trust_notice.py +0 -0
  240. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/private_inference.py +0 -0
  241. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/provider_credentials.py +0 -0
  242. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/provider_identity.py +0 -0
  243. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/pydantic_patches.py +0 -0
  244. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/reopenable_async_client.py +0 -0
  245. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/round_robin_model.py +0 -0
  246. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/secret_store.py +0 -0
  247. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/secret_store_backends.py +0 -0
  248. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/session_format_migration.py +0 -0
  249. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/session_lifecycle.py +0 -0
  250. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/session_migration.py +0 -0
  251. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/session_storage.py +0 -0
  252. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/session_surrogate_unpickler.py +0 -0
  253. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/shared_credentials.py +0 -0
  254. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/skill_provider.py +0 -0
  255. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/splash.py +0 -0
  256. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/status_display.py +0 -0
  257. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/steer_metadata.py +0 -0
  258. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/terminal_utils.py +0 -0
  259. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/token_usage.py +0 -0
  260. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/tools/__init__.py +0 -0
  261. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/tools/_browser_registry.py +0 -0
  262. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/tools/_lazy.py +0 -0
  263. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/tools/agent_tools.py +0 -0
  264. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/tools/ask_user_question/__init__.py +0 -0
  265. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/tools/ask_user_question/constants.py +0 -0
  266. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/tools/ask_user_question/demo_tui.py +0 -0
  267. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/tools/ask_user_question/handler.py +0 -0
  268. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/tools/ask_user_question/models.py +0 -0
  269. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/tools/ask_user_question/registration.py +0 -0
  270. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/tools/ask_user_question/renderers.py +0 -0
  271. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/tools/ask_user_question/terminal_ui.py +0 -0
  272. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/tools/ask_user_question/theme.py +0 -0
  273. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/tools/ask_user_question/tui_loop.py +0 -0
  274. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/tools/browser/__init__.py +0 -0
  275. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/tools/browser/browser_control.py +0 -0
  276. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/tools/browser/browser_interactions.py +0 -0
  277. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/tools/browser/browser_locator_resolver.py +0 -0
  278. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/tools/browser/browser_locators.py +0 -0
  279. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/tools/browser/browser_manager.py +0 -0
  280. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/tools/browser/browser_navigation.py +0 -0
  281. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/tools/browser/browser_page_snapshot.py +0 -0
  282. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/tools/browser/browser_screenshot.py +0 -0
  283. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/tools/browser/browser_scripts.py +0 -0
  284. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/tools/browser/browser_semantic_interactions.py +0 -0
  285. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/tools/browser/browser_workflows.py +0 -0
  286. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/tools/browser/tool_registry.py +0 -0
  287. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/tools/command_runner.py +0 -0
  288. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/tools/common.py +0 -0
  289. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/tools/display.py +0 -0
  290. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/tools/file_modifications.py +0 -0
  291. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/tools/file_operations.py +0 -0
  292. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/tools/file_permission_state.py +0 -0
  293. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/tools/fs_access.py +0 -0
  294. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/tools/image_tools.py +0 -0
  295. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/tools/io_backends.py +0 -0
  296. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/tools/model_tools.py +0 -0
  297. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/tools/shell_backgrounding.py +0 -0
  298. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/tools/skills_tools.py +0 -0
  299. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/tools/subagent_context.py +0 -0
  300. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/tools/subagent_invocation.py +0 -0
  301. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/tools/subagent_usage_metrics.py +0 -0
  302. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/tools/tools_content.py +0 -0
  303. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/tools/universal_constructor.py +0 -0
  304. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/undo_manager.py +0 -0
  305. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/universal_constructor_provider.py +0 -0
  306. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/version_checker.py +0 -0
  307. {code_puppy-0.0.855 → code_puppy-0.0.856}/code_puppy/zai_model.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: code-puppy
3
- Version: 0.0.855
3
+ Version: 0.0.856
4
4
  Summary: Code generation agent
5
5
  Project-URL: repository, https://github.com/mpfaffenberger/code_puppy
6
6
  Project-URL: HomePage, https://github.com/mpfaffenberger/code_puppy
@@ -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 _parse_candidate_parts, _build_generation_config
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
- self._build_tools(model_request_parameters.function_tools)
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"] = self._build_tools(model_request_parameters.function_tools)
421
+ body["tools"] = _build_tools(model_request_parameters.function_tools)
622
422
 
623
423
  if streaming:
624
424
  body["toolConfig"] = {
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "code-puppy"
7
- version = "0.0.855"
7
+ version = "0.0.856"
8
8
  description = "Code generation agent"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11,<3.15"