eggpool 0.7.2__tar.gz → 0.7.3__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 (419) hide show
  1. {eggpool-0.7.2 → eggpool-0.7.3}/AGENTS.md +3 -0
  2. {eggpool-0.7.2 → eggpool-0.7.3}/CHANGELOG.md +36 -0
  3. {eggpool-0.7.2 → eggpool-0.7.3}/PKG-INFO +1 -1
  4. {eggpool-0.7.2 → eggpool-0.7.3}/architecture/deep-dive-health.md +19 -0
  5. {eggpool-0.7.2 → eggpool-0.7.3}/architecture/deep-dive-providers.md +10 -0
  6. {eggpool-0.7.2 → eggpool-0.7.3}/architecture/deep-dive-request-lifecycle.md +50 -0
  7. {eggpool-0.7.2 → eggpool-0.7.3}/architecture/deep-dive-retry.md +5 -1
  8. {eggpool-0.7.2 → eggpool-0.7.3}/architecture/deep-dive-routing.md +12 -0
  9. {eggpool-0.7.2 → eggpool-0.7.3}/pyproject.toml +1 -1
  10. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/failure/applier.py +25 -26
  11. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/request/coordinator.py +97 -32
  12. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/request/provider_bound_request.py +26 -1
  13. {eggpool-0.7.2 → eggpool-0.7.3}/uv.lock +1 -1
  14. {eggpool-0.7.2 → eggpool-0.7.3}/.env.example +0 -0
  15. {eggpool-0.7.2 → eggpool-0.7.3}/.github/workflows/ci.yml +0 -0
  16. {eggpool-0.7.2 → eggpool-0.7.3}/.gitignore +0 -0
  17. {eggpool-0.7.2 → eggpool-0.7.3}/LICENSE +0 -0
  18. {eggpool-0.7.2 → eggpool-0.7.3}/README.md +0 -0
  19. {eggpool-0.7.2 → eggpool-0.7.3}/architecture/README.md +0 -0
  20. {eggpool-0.7.2 → eggpool-0.7.3}/architecture/deep-dive-background.md +0 -0
  21. {eggpool-0.7.2 → eggpool-0.7.3}/architecture/deep-dive-catalog.md +0 -0
  22. {eggpool-0.7.2 → eggpool-0.7.3}/architecture/deep-dive-control.md +0 -0
  23. {eggpool-0.7.2 → eggpool-0.7.3}/architecture/deep-dive-core.md +0 -0
  24. {eggpool-0.7.2 → eggpool-0.7.3}/architecture/deep-dive-dashboard.md +0 -0
  25. {eggpool-0.7.2 → eggpool-0.7.3}/architecture/deep-dive-database.md +0 -0
  26. {eggpool-0.7.2 → eggpool-0.7.3}/architecture/deep-dive-deployment.md +0 -0
  27. {eggpool-0.7.2 → eggpool-0.7.3}/architecture/deep-dive-integrations.md +0 -0
  28. {eggpool-0.7.2 → eggpool-0.7.3}/architecture/deep-dive-lifecycle.md +0 -0
  29. {eggpool-0.7.2 → eggpool-0.7.3}/architecture/deep-dive-metrics.md +0 -0
  30. {eggpool-0.7.2 → eggpool-0.7.3}/architecture/deep-dive-model-info.md +0 -0
  31. {eggpool-0.7.2 → eggpool-0.7.3}/architecture/deep-dive-models.md +0 -0
  32. {eggpool-0.7.2 → eggpool-0.7.3}/architecture/deep-dive-observability.md +0 -0
  33. {eggpool-0.7.2 → eggpool-0.7.3}/architecture/deep-dive-runtime.md +0 -0
  34. {eggpool-0.7.2 → eggpool-0.7.3}/architecture/deep-dive-security.md +0 -0
  35. {eggpool-0.7.2 → eggpool-0.7.3}/architecture/deep-dive-transcoder.md +0 -0
  36. {eggpool-0.7.2 → eggpool-0.7.3}/architecture/overview.md +0 -0
  37. {eggpool-0.7.2 → eggpool-0.7.3}/artifacts/plan-019-evidence.md +0 -0
  38. {eggpool-0.7.2 → eggpool-0.7.3}/artifacts/plan-020-evidence.md +0 -0
  39. {eggpool-0.7.2 → eggpool-0.7.3}/artifacts/plan-021-evidence.md +0 -0
  40. {eggpool-0.7.2 → eggpool-0.7.3}/artifacts/plan-023-baseline.md +0 -0
  41. {eggpool-0.7.2 → eggpool-0.7.3}/artifacts/plan-030-exact-head-evidence.md +0 -0
  42. {eggpool-0.7.2 → eggpool-0.7.3}/config-examples/claude-code.env +0 -0
  43. {eggpool-0.7.2 → eggpool-0.7.3}/config-examples/opencode.jsonc +0 -0
  44. {eggpool-0.7.2 → eggpool-0.7.3}/config.example.toml +0 -0
  45. {eggpool-0.7.2 → eggpool-0.7.3}/config.sbc.example.toml +0 -0
  46. {eggpool-0.7.2 → eggpool-0.7.3}/deploy/eggpool-logrotate.conf +0 -0
  47. {eggpool-0.7.2 → eggpool-0.7.3}/deploy/eggpool.service +0 -0
  48. {eggpool-0.7.2 → eggpool-0.7.3}/deploy/env.example +0 -0
  49. {eggpool-0.7.2 → eggpool-0.7.3}/docs/agent-configuration.md +0 -0
  50. {eggpool-0.7.2 → eggpool-0.7.3}/docs/api-reference.md +0 -0
  51. {eggpool-0.7.2 → eggpool-0.7.3}/docs/backup-restore.md +0 -0
  52. {eggpool-0.7.2 → eggpool-0.7.3}/docs/config-profiles.md +0 -0
  53. {eggpool-0.7.2 → eggpool-0.7.3}/docs/deployment.md +0 -0
  54. {eggpool-0.7.2 → eggpool-0.7.3}/docs/filesystem-layout.md +0 -0
  55. {eggpool-0.7.2 → eggpool-0.7.3}/docs/firewall.md +0 -0
  56. {eggpool-0.7.2 → eggpool-0.7.3}/docs/live-config-rehash.md +0 -0
  57. {eggpool-0.7.2 → eggpool-0.7.3}/docs/model-info-openrouter-debug.md +0 -0
  58. {eggpool-0.7.2 → eggpool-0.7.3}/docs/model-limits.md +0 -0
  59. {eggpool-0.7.2 → eggpool-0.7.3}/docs/network-diagnostics.md +0 -0
  60. {eggpool-0.7.2 → eggpool-0.7.3}/docs/opencode-stream-stability.md +0 -0
  61. {eggpool-0.7.2 → eggpool-0.7.3}/docs/operations/dispatch-stability.md +0 -0
  62. {eggpool-0.7.2 → eggpool-0.7.3}/docs/providers.md +0 -0
  63. {eggpool-0.7.2 → eggpool-0.7.3}/docs/proxy.md +0 -0
  64. {eggpool-0.7.2 → eggpool-0.7.3}/docs/raspberry-pi.md +0 -0
  65. {eggpool-0.7.2 → eggpool-0.7.3}/docs/releasing.md +0 -0
  66. {eggpool-0.7.2 → eggpool-0.7.3}/docs/runbooks/database-recovery.md +0 -0
  67. {eggpool-0.7.2 → eggpool-0.7.3}/docs/stateless-responses.md +0 -0
  68. {eggpool-0.7.2 → eggpool-0.7.3}/docs/thinking.md +0 -0
  69. {eggpool-0.7.2 → eggpool-0.7.3}/docs/transcoding.md +0 -0
  70. {eggpool-0.7.2 → eggpool-0.7.3}/scripts/__init__.py +0 -0
  71. {eggpool-0.7.2 → eggpool-0.7.3}/scripts/admission_race_stress.py +0 -0
  72. {eggpool-0.7.2 → eggpool-0.7.3}/scripts/bench_sqlite_writepath.py +0 -0
  73. {eggpool-0.7.2 → eggpool-0.7.3}/scripts/check_database.py +0 -0
  74. {eggpool-0.7.2 → eggpool-0.7.3}/scripts/debug_model_info_openrouter.sh +0 -0
  75. {eggpool-0.7.2 → eggpool-0.7.3}/scripts/install.sh +0 -0
  76. {eggpool-0.7.2 → eggpool-0.7.3}/scripts/install_prompt.py +0 -0
  77. {eggpool-0.7.2 → eggpool-0.7.3}/scripts/repro_high_concurrency_streams.py +0 -0
  78. {eggpool-0.7.2 → eggpool-0.7.3}/scripts/run_tests_with_timeout.py +0 -0
  79. {eggpool-0.7.2 → eggpool-0.7.3}/scripts/smoke_test.py +0 -0
  80. {eggpool-0.7.2 → eggpool-0.7.3}/scripts/test_model_info_identity.sh +0 -0
  81. {eggpool-0.7.2 → eggpool-0.7.3}/scripts/validate_routing.py +0 -0
  82. {eggpool-0.7.2 → eggpool-0.7.3}/scripts/verify_upstream_auth.py +0 -0
  83. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/__init__.py +0 -0
  84. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/__main__.py +0 -0
  85. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/_share/.env.example +0 -0
  86. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/_share/config.example.toml +0 -0
  87. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/_share/config.sbc.example.toml +0 -0
  88. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/accounts/__init__.py +0 -0
  89. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/accounts/registry.py +0 -0
  90. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/accounts/state.py +0 -0
  91. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/api/__init__.py +0 -0
  92. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/api/backoff.py +0 -0
  93. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/api/chat_completions.py +0 -0
  94. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/api/errors.py +0 -0
  95. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/api/messages.py +0 -0
  96. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/api/model_info.py +0 -0
  97. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/api/models.py +0 -0
  98. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/api/network.py +0 -0
  99. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/api/proxy_request.py +0 -0
  100. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/api/responses.py +0 -0
  101. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/api/runtime.py +0 -0
  102. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/api/stats.py +0 -0
  103. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/api/update.py +0 -0
  104. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/app.py +0 -0
  105. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/auth.py +0 -0
  106. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/background/__init__.py +0 -0
  107. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/background/backup.py +0 -0
  108. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/background/cleanup.py +0 -0
  109. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/background/maintenance.py +0 -0
  110. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/catalog/__init__.py +0 -0
  111. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/catalog/_time.py +0 -0
  112. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/catalog/cache.py +0 -0
  113. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/catalog/capabilities.py +0 -0
  114. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/catalog/catalog_resolvers.py +0 -0
  115. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/catalog/fetcher.py +0 -0
  116. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/catalog/limits.py +0 -0
  117. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/catalog/models_dev.py +0 -0
  118. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/catalog/normalizer.py +0 -0
  119. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/catalog/pricing.py +0 -0
  120. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/catalog/pricing_aliases.py +0 -0
  121. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/catalog/pricing_resolver.py +0 -0
  122. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/catalog/protocols.py +0 -0
  123. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/catalog/service.py +0 -0
  124. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/cli.py +0 -0
  125. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/cli_exit_codes.py +0 -0
  126. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/cli_full.py +0 -0
  127. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/cli_rehash_format.py +0 -0
  128. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/cli_rehash_helper.py +0 -0
  129. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/config.py +0 -0
  130. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/config_reload_policy.py +0 -0
  131. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/config_utils.py +0 -0
  132. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/config_validation.py +0 -0
  133. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/constants.py +0 -0
  134. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/control/__init__.py +0 -0
  135. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/control/accepted_finalization.py +0 -0
  136. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/control/client.py +0 -0
  137. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/control/reload_manager.py +0 -0
  138. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/control/server.py +0 -0
  139. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/cost_recompute.py +0 -0
  140. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/cost_repair.py +0 -0
  141. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/dashboard/__init__.py +0 -0
  142. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/dashboard/_resources.py +0 -0
  143. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/dashboard/escape.py +0 -0
  144. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/dashboard/render.py +0 -0
  145. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/dashboard/routes.py +0 -0
  146. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/dashboard/static/chart.umd.min.js +0 -0
  147. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/dashboard/static/dashboard.css +0 -0
  148. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/dashboard/static/dashboard.js +0 -0
  149. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/dashboard/static/favicon.svg +0 -0
  150. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/dashboard/telemetry.py +0 -0
  151. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/dashboard/theme.py +0 -0
  152. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/dashboard/themes/Booberry.toml +0 -0
  153. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/dashboard/themes/Catppuccin Latte.toml +0 -0
  154. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/dashboard/themes/Catppuccin Macchiato.toml +0 -0
  155. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/dashboard/themes/Catppuccin Mocha.toml +0 -0
  156. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/dashboard/themes/Cyber Red.toml +0 -0
  157. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/dashboard/themes/Cyberpunk.toml +0 -0
  158. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/dashboard/themes/Dark Green.toml +0 -0
  159. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/dashboard/themes/Discord (80_ Saturation).toml +0 -0
  160. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/dashboard/themes/Discord.toml +0 -0
  161. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/dashboard/themes/Dracula.toml +0 -0
  162. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/dashboard/themes/Ferra Light.toml +0 -0
  163. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/dashboard/themes/Flexor Dark.toml +0 -0
  164. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/dashboard/themes/Gruvbox.toml +0 -0
  165. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/dashboard/themes/Halcyon Dark.toml +0 -0
  166. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/dashboard/themes/IntelliJ Light.toml +0 -0
  167. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/dashboard/themes/Kanagawa.toml +0 -0
  168. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/dashboard/themes/Macaw Dark.toml +0 -0
  169. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/dashboard/themes/Macaw Light.toml +0 -0
  170. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/dashboard/themes/Matrix.toml +0 -0
  171. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/dashboard/themes/Noctis Lilac.toml +0 -0
  172. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/dashboard/themes/Nord.toml +0 -0
  173. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/dashboard/themes/Nostromo Terminal.toml +0 -0
  174. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/dashboard/themes/One Dark.toml +0 -0
  175. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/dashboard/themes/Oxocarbon.toml +0 -0
  176. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/dashboard/themes/Rose Pine Dawn.toml +0 -0
  177. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/dashboard/themes/Rose Pine Moon.toml +0 -0
  178. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/dashboard/themes/Rose Pine.toml +0 -0
  179. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/dashboard/themes/Solarized Dark.toml +0 -0
  180. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/dashboard/themes/Sonokai.toml +0 -0
  181. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/dashboard/themes/Tokyo Night Storm.toml +0 -0
  182. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/dashboard/themes/VESPER.toml +0 -0
  183. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/dashboard/themes/Zenburn.toml +0 -0
  184. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/dashboard/themes/acton.toml +0 -0
  185. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/dashboard/themes/bam.toml +0 -0
  186. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/dashboard/themes/base16-atelier-forest-light.toml +0 -0
  187. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/dashboard/themes/berlin.toml +0 -0
  188. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/dashboard/themes/black but with important highlights.toml +0 -0
  189. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/dashboard/themes/broc.toml +0 -0
  190. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/dashboard/themes/cork.toml +0 -0
  191. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/dashboard/themes/ferra.toml +0 -0
  192. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/dashboard/themes/forest.toml +0 -0
  193. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/dashboard/themes/lisbon.toml +0 -0
  194. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/dashboard/themes/midnight.toml +0 -0
  195. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/dashboard/themes/oslo.toml +0 -0
  196. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/dashboard/themes/plum.toml +0 -0
  197. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/dashboard/themes/portland.toml +0 -0
  198. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/dashboard/themes/sunset.toml +0 -0
  199. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/dashboard/themes/tofino.toml +0 -0
  200. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/dashboard/themes/vanimo.toml +0 -0
  201. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/dashboard/themes/vik.toml +0 -0
  202. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/dashboard/timeseries_buckets.py +0 -0
  203. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/db/__init__.py +0 -0
  204. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/db/connection.py +0 -0
  205. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/db/migrations.py +0 -0
  206. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/db/repositories.py +0 -0
  207. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/db/rollup_repository.py +0 -0
  208. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/db/schema/0001_initial.sql +0 -0
  209. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/db/schema/0002_indexes.sql +0 -0
  210. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/db/schema/0003_request_attempts.sql +0 -0
  211. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/db/schema/0004_integration_hardening.sql +0 -0
  212. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/db/schema/0005_price_microdollars.sql +0 -0
  213. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/db/schema/0006_correct_price_microdollars.sql +0 -0
  214. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/db/schema/0007_price_cache_rates.sql +0 -0
  215. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/db/schema/0008_proxy_request_identity.sql +0 -0
  216. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/db/schema/0009_model_protocol_source.sql +0 -0
  217. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/db/schema/0010_health_probe.sql +0 -0
  218. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/db/schema/0011_model_resolution_status.sql +0 -0
  219. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/db/schema/0012_drop_reservations_estimated_microdollars.sql +0 -0
  220. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/db/schema/0013_request_attempts_account_id_index.sql +0 -0
  221. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/db/schema/0014_bandwidth_tracking.sql +0 -0
  222. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/db/schema/0015_multi_provider.sql +0 -0
  223. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/db/schema/0016_requests_provider_id.sql +0 -0
  224. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/db/schema/0017_price_snapshots_provider_id.sql +0 -0
  225. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/db/schema/0018_provider_pings.sql +0 -0
  226. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/db/schema/0019_client_ip.sql +0 -0
  227. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/db/schema/0020_performance_indexes.sql +0 -0
  228. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/db/schema/0021_provider_model_metadata.sql +0 -0
  229. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/db/schema/0022_dashboard_indexes.sql +0 -0
  230. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/db/schema/0023_deprecated_model_placeholder.sql +0 -0
  231. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/db/schema/0024_account_backoffs.sql +0 -0
  232. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/db/schema/0025_stale_request_index.sql +0 -0
  233. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/db/schema/0026_attempt_observability.sql +0 -0
  234. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/db/schema/0027_routing_decisions.sql +0 -0
  235. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/db/schema/0028_operational_events.sql +0 -0
  236. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/db/schema/0029_latency_phases.sql +0 -0
  237. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/db/schema/0030_model_pricing_aliases.sql +0 -0
  238. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/db/schema/0031_price_snapshot_provenance.sql +0 -0
  239. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/db/schema/0032_usage_rollups.sql +0 -0
  240. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/db/schema/0033_request_provider_local_cost.sql +0 -0
  241. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/db/schema/0034_transcoding_daily.sql +0 -0
  242. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/db/schema/0035_routing_decision_score_components.sql +0 -0
  243. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/db/schema/0036_model_info.sql +0 -0
  244. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/db/schema/0037_model_info_failure_count.sql +0 -0
  245. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/db/schema/0038_model_info_phase5.sql +0 -0
  246. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/db/schema/0039_thinking_observability.sql +0 -0
  247. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/db/schema/0040_cache_token_observability.sql +0 -0
  248. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/db/schema/0041_canonical_request_segmentation.sql +0 -0
  249. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/db/schema/0042_observe_mode_compression_accounting.sql +0 -0
  250. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/db/schema/0043_safe_suffix_compression.sql +0 -0
  251. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/db/schema/0044_compression_policy_resolver.sql +0 -0
  252. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/db/schema/0045_synthetic_cache_controls.sql +0 -0
  253. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/db/schema/0046_closed_loop_threshold_tuning.sql +0 -0
  254. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/db/schema/0047_normalize_rollup_bucket_start.sql +0 -0
  255. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/db/schema/0048_request_cost_repairs.sql +0 -0
  256. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/db/schema/0049_model_info_match_evidence.sql +0 -0
  257. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/db/schema/0050_routing_decisions_retention_index.sql +0 -0
  258. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/db/schema/0051_model_quarantine.sql +0 -0
  259. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/db/schema/0052_catalog_refresh_state.sql +0 -0
  260. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/db/schema/0053_remove_attempt_status_analytics_index.sql +0 -0
  261. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/db/schema/0054_model_quarantine_null_identity.sql +0 -0
  262. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/db/schema/checksums.json +0 -0
  263. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/deploy/__init__.py +0 -0
  264. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/deploy_user.py +0 -0
  265. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/errors.py +0 -0
  266. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/event_loop_lag.py +0 -0
  267. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/failure/__init__.py +0 -0
  268. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/failure/classifier.py +0 -0
  269. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/failure/effects.py +0 -0
  270. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/failure/observation.py +0 -0
  271. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/failure/quarantine.py +0 -0
  272. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/failure/signal.py +0 -0
  273. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/failure/signal_extract.py +0 -0
  274. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/fastcli.py +0 -0
  275. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/generation_factory.py +0 -0
  276. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/health/__init__.py +0 -0
  277. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/health/backoff.py +0 -0
  278. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/health/circuit_breaker.py +0 -0
  279. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/health/health_manager.py +0 -0
  280. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/health/writable_probe.py +0 -0
  281. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/integrations/__init__.py +0 -0
  282. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/integrations/aider.py +0 -0
  283. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/integrations/cline.py +0 -0
  284. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/integrations/codex.py +0 -0
  285. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/integrations/common.py +0 -0
  286. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/integrations/continue_dev.py +0 -0
  287. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/integrations/goose.py +0 -0
  288. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/integrations/kilo.py +0 -0
  289. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/integrations/opencode.py +0 -0
  290. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/integrations/openhands.py +0 -0
  291. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/integrations/qwen_code.py +0 -0
  292. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/integrations/roo_code.py +0 -0
  293. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/jsonx.py +0 -0
  294. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/lifecycle/__init__.py +0 -0
  295. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/lifecycle/backup.py +0 -0
  296. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/lifecycle/uninstall.py +0 -0
  297. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/logging.py +0 -0
  298. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/metrics/__init__.py +0 -0
  299. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/metrics/buffer.py +0 -0
  300. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/metrics/failure_effects.py +0 -0
  301. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/metrics/thinking.py +0 -0
  302. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/model_info/__init__.py +0 -0
  303. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/model_info/dedup.py +0 -0
  304. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/model_info/identity.py +0 -0
  305. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/model_info/matching.py +0 -0
  306. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/model_info/normalization.py +0 -0
  307. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/model_info/presentation.py +0 -0
  308. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/model_info/repository.py +0 -0
  309. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/model_info/scheduler.py +0 -0
  310. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/model_info/service.py +0 -0
  311. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/model_info/sources/__init__.py +0 -0
  312. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/model_info/sources/artificial_analysis.py +0 -0
  313. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/model_info/sources/base.py +0 -0
  314. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/model_info/sources/huggingface.py +0 -0
  315. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/model_info/sources/openrouter.py +0 -0
  316. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/model_info/sources/provider_catalog.py +0 -0
  317. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/model_info/types.py +0 -0
  318. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/models/__init__.py +0 -0
  319. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/models/api.py +0 -0
  320. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/models/config.py +0 -0
  321. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/models/database.py +0 -0
  322. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/models/domain.py +0 -0
  323. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/observability/__init__.py +0 -0
  324. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/observability/routing_trace_writer.py +0 -0
  325. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/onboard.py +0 -0
  326. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/providers/__init__.py +0 -0
  327. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/providers/_templates.toml +0 -0
  328. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/providers/auth.py +0 -0
  329. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/providers/client_pool.py +0 -0
  330. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/providers/connect.py +0 -0
  331. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/providers/contract.py +0 -0
  332. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/providers/outbound.py +0 -0
  333. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/providers/pproxy_transport.py +0 -0
  334. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/proxy/__init__.py +0 -0
  335. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/proxy/client.py +0 -0
  336. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/proxy/cost_reporting.py +0 -0
  337. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/proxy/normalized_usage.py +0 -0
  338. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/proxy/sse.py +0 -0
  339. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/proxy/sse_observer.py +0 -0
  340. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/proxy/usage.py +0 -0
  341. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/py.typed +0 -0
  342. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/quota/__init__.py +0 -0
  343. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/quota/audit.py +0 -0
  344. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/quota/estimation.py +0 -0
  345. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/quota/reservation.py +0 -0
  346. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/quota/scorer.py +0 -0
  347. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/reload_diagnostics.py +0 -0
  348. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/reload_transaction.py +0 -0
  349. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/request/__init__.py +0 -0
  350. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/request/attempt_finalizer.py +0 -0
  351. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/request/backoff_persistence.py +0 -0
  352. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/request/body.py +0 -0
  353. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/request/claim_lifecycle.py +0 -0
  354. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/request/failure_helpers.py +0 -0
  355. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/request/finalization_job.py +0 -0
  356. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/request/finalizer.py +0 -0
  357. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/request/limits.py +0 -0
  358. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/request/parsed_payload.py +0 -0
  359. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/request/parsed_upstream_response.py +0 -0
  360. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/request/payload_utils.py +0 -0
  361. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/request/response_handoff.py +0 -0
  362. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/request/routing_helpers.py +0 -0
  363. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/request/routing_trace_guard.py +0 -0
  364. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/request/selection_claim_diagnostics.py +0 -0
  365. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/request/static_helpers.py +0 -0
  366. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/request/stream_completion.py +0 -0
  367. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/request/stream_diagnostics.py +0 -0
  368. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/request/terminal_status.py +0 -0
  369. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/request/thinking_adaptation.py +0 -0
  370. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/request/transform_pipeline.py +0 -0
  371. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/request/upstream_execution.py +0 -0
  372. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/request/upstream_helpers.py +0 -0
  373. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/request/usage_helpers.py +0 -0
  374. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/retry/__init__.py +0 -0
  375. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/retry/classification.py +0 -0
  376. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/routing/__init__.py +0 -0
  377. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/routing/config.py +0 -0
  378. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/routing/eligibility.py +0 -0
  379. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/routing/fairness.py +0 -0
  380. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/routing/provider.py +0 -0
  381. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/routing/router.py +0 -0
  382. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/runtime.py +0 -0
  383. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/runtime_dispatch.py +0 -0
  384. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/runtime_manager.py +0 -0
  385. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/runtime_metrics.py +0 -0
  386. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/runtime_paths.py +0 -0
  387. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/runtime_task_inventory.py +0 -0
  388. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/runtime_tasks.py +0 -0
  389. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/security/__init__.py +0 -0
  390. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/security/redaction.py +0 -0
  391. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/stats/__init__.py +0 -0
  392. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/stats/cache_metrics.py +0 -0
  393. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/stats/dashboard_explain.py +0 -0
  394. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/stats/grouped_timeseries.py +0 -0
  395. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/stats/queries.py +0 -0
  396. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/stats/segmentation.py +0 -0
  397. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/stats/service.py +0 -0
  398. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/stats/transcoding.py +0 -0
  399. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/toml_edit.py +0 -0
  400. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/transcoder/__init__.py +0 -0
  401. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/transcoder/anthropic_to_openai.py +0 -0
  402. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/transcoder/budget_resolver.py +0 -0
  403. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/transcoder/builtin_contracts.py +0 -0
  404. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/transcoder/cache_stability.py +0 -0
  405. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/transcoder/cache_translation.py +0 -0
  406. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/transcoder/context.py +0 -0
  407. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/transcoder/errors.py +0 -0
  408. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/transcoder/ids.py +0 -0
  409. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/transcoder/json_helpers.py +0 -0
  410. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/transcoder/openai_to_anthropic.py +0 -0
  411. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/transcoder/policy.py +0 -0
  412. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/transcoder/prepared.py +0 -0
  413. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/transcoder/protocol.py +0 -0
  414. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/transcoder/provider_adaptation.py +0 -0
  415. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/transcoder/sensitive_media.py +0 -0
  416. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/transcoder/static_headers.py +0 -0
  417. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/transcoder/streaming.py +0 -0
  418. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/transcoder/usage.py +0 -0
  419. {eggpool-0.7.2 → eggpool-0.7.3}/src/eggpool/update_checker.py +0 -0
@@ -147,6 +147,9 @@ Non-obvious wiring:
147
147
  - **`static_models` is source of truth for provider-specific protocol**: providers serving non-default protocols must ship `[[providers.<id>.static_models]]` rows
148
148
  - **No pre-commit hooks configured**: CI runs ruff, pyright, and pytest via GitHub Actions
149
149
  - **When constructing `RequestCoordinator` in tests**: pass an explicit `transcoder_policy` or assert the desired default
150
+ - **`ProviderBoundRequest` dispatch-freeze**: `serialize_provider_payload()` freezes the body; `replace_provider_payload()` and `set_provider_payload(increment_generation=False)` reject when frozen. Only generation-incrementing methods (`set_provider_payload(increment_generation=True)`, `adopt_provider_payload(increment_generation=True)`) clear the freeze — the post-selection transcoder relies on this to replace a previously dispatched body on retry
151
+ - **Thinking rejection error class**: `CapabilityError` (400) only when the aggregated thinking status is genuinely `unknown` or `unsupported`. When all supporting accounts are quarantined but the provider entry reports `supported`/`mixed`, a transient 503/502 is raised instead. Aggregation iterates `cache.get_provider_model_entries()` (which applies overrides), not `cache.get_model()` (which does not). See `RequestCoordinator._determine_thinking_rejection_status` and `architecture/deep-dive-request-lifecycle.md`
152
+ - **Per-model quarantine suppresses account-wide circuit breaker**: when `effects.model_effect != "none"` (quarantine), `EffectsApplier._apply_account_effect` skips `HealthManager.record_failure()` for that account. The account-wide breaker advances only when the classifier sets `source="transport"` (genuine account-wide failure: DNS, TLS, persistent transport failure with no per-model cause). A per-model 5xx must quarantine the `(account, model)` pair, not the whole account. See `architecture/deep-dive-health.md`
150
153
 
151
154
  ## Error Handling
152
155
 
@@ -36,6 +36,42 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
36
36
  no-account-disable path for keyword false-positives in client-error bodies,
37
37
  5xx cooldown non-extension to siblings, and applier resilience under partial
38
38
  side-effect failure.
39
+ - **muse-spark-1.2-contributor routing fix.** Three coordinated changes
40
+ restore the canonical ``OpenCode Go`` Muse Spark request path and stop
41
+ one model's failure from black-holing sibling models on the same
42
+ subscription:
43
+
44
+ 1. `ProviderBoundRequest.set_provider_payload` and
45
+ `adopt_provider_payload` now clear the dispatch-freeze flag when a new
46
+ generation begins. Pre-fix, the post-selection transcoder's
47
+ `adopt_provider_payload` raised `RuntimeError: provider payload is
48
+ frozen` after the first attempt's serialization, breaking every retry
49
+ against a different selected provider.
50
+
51
+ 2. `RequestCoordinator._determine_thinking_rejection_status` now
52
+ aggregates per-provider capability overrides when the collapsed
53
+ entry reports `unknown`. Built-in overrides (e.g. the canonical
54
+ OpenCode Go host capabilities for `muse-spark-1.2-contributor`) live
55
+ in the provider-scoped cache row, so the previous bare-model lookup
56
+ missed them and surfaced a misleading client-validation 400. The
57
+ capability-error path now requires `rejected_status` to be truly
58
+ `unknown`/`unsupported` before surfacing the 400; otherwise the
59
+ router falls through to a transient `No accounts available` (503).
60
+
61
+ 3. `EffectsApplier._apply_account_effect` now skips
62
+ `HealthManager.record_failure` when the classifier set
63
+ `model_effect="quarantine"` — the per-model disable is the only
64
+ shared-state penalty in that case. Pre-fix, a single muse-spark 5xx
65
+ per account advanced the account-wide circuit breaker so five
66
+ per-model 5xxes tripped the breaker for ALL models on the same
67
+ subscription. Post-fix, sibling models on the same opencode-go
68
+ account keep routing while only muse-spark-1.2-contributor is
69
+ quarantined for the bounded TTL. Tests:
70
+ `tests/integration/test_muse_spark_live_e2e.py` (3 tests covering the
71
+ end-to-end live routing path with thinking controls and sibling-model
72
+ isolation), `tests/integration/test_muse_spark_capability_status_attribution.py`
73
+ (catalog + status attribution), and `tests/unit/test_provider_bound_request.py`
74
+ + `tests/unit/test_effects_idempotency.py` (per-fix unit coverage).
39
75
 
40
76
  ### Added
41
77
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: eggpool
3
- Version: 0.7.2
3
+ Version: 0.7.3
4
4
  Summary: A lightweight proxy that aggregates multiple LLM provider accounts behind an OpenAI Chat Completions-compatible endpoint
5
5
  Project-URL: Homepage, https://github.com/eggstack/eggpool
6
6
  Project-URL: Repository, https://github.com/eggstack/eggpool
@@ -126,6 +126,24 @@ records the circuit failure; the effects applier never records that same
126
126
  transition a second time. Request-local errors and cancellation release a
127
127
  half-open probe without provider penalties.
128
128
 
129
+ ### Per-model quarantine suppresses account-wide circuit advance
130
+
131
+ When the classifier sets `model_effect = "quarantine"` on a 5xx, the
132
+ effects applier (`EffectsApplier._apply_account_effect`) skips the
133
+ `HealthManager.record_failure()` call for that account. The per-model
134
+ disable (`disable_model`) is the **sole** shared-state penalty in that
135
+ case; the account-wide circuit breaker stays closed. The breaker
136
+ advances only when the classifier sets `source = "transport"`
137
+ (genuine account-wide failure: DNS failure, TLS error, persistent 503
138
+ with no model-specific cause) **and** `model_effect = "none"`.
139
+
140
+ This isolation prevents N per-model 5xx failures from tripping the
141
+ breaker for all models on the same subscription. A single model's
142
+ upstream anomaly must not black-hole sibling models on the same
143
+ account. The `effects.account_effect` is still `"failure"` in the
144
+ quarantined case; the `model_effect` flag gates whether the breaker
145
+ call fires.
146
+
129
147
  ## Key Invariants
130
148
 
131
149
  - Health driven solely by upstream-observed failures, operator disablement, and catalog/protocol incompatibility
@@ -138,3 +156,4 @@ half-open probe without provider penalties.
138
156
  - Half-open probe acquisition always converges through success, failure, cancellation, or local release
139
157
  - Cooldown timers are monotonic
140
158
  - `AccountHealth` fields: `consecutive_failures`, `disabled_models`, `disabled_until`, `disabled_reason`, `cooldown_until`
159
+ - Per-model quarantine (`model_effect != "none"`) is the SOLE shared-state penalty; the account-wide circuit breaker advances only when `source="transport"` AND `model_effect="none"` (genuine account-wide failure). A single model's 5xx must not black-hole sibling models on the same account
@@ -50,3 +50,13 @@ truth when a provider serves a non-default protocol or capability contract.
50
50
  provider's capability row. Speculative universal ceilings (such as a
51
51
  universal 5 MiB local-runtime ceiling) are not encoded in bundled templates;
52
52
  only verified provider-defined limits are advertised.
53
+ - Per-provider capability overrides (e.g. `thinking.status`) live in the
54
+ provider-scoped cache row and are applied by
55
+ `ModelCatalogCache.get_provider_model_entry()` /
56
+ `ModelCatalogCache.get_provider_model_entries()`. The collapsed `models`
57
+ row (`get_model()`) deliberately does **not** apply overrides — it
58
+ represents the raw catalog state. Capability rejection attribution must
59
+ consult provider-scoped entries, not just the collapsed row, to avoid
60
+ false `"unknown"` readings when overrides are present. Quarantine does
61
+ **not** erase a provider's capability — quarantined entries still
62
+ contribute to the aggregated status.
@@ -192,6 +192,19 @@ re-raising.
192
192
  - `recompute_thinking_budget_for_provider()` — re-resolves budget against selected provider's capability
193
193
  - `adapt_provider_thinking_controls()` — validates and adapts controls against the provider contract
194
194
 
195
+ `_determine_thinking_rejection_status()` (coordinator) — attributes a
196
+ thinking rejection to an aggregated capability status when all
197
+ eligible accounts are filtered out. Consults the collapsed
198
+ `models` row first, then falls back to the provider-scoped row when
199
+ `context.provider_id` is known, and otherwise iterates every
200
+ provider entry returned by `cache.get_provider_model_entries()` —
201
+ including quarantined accounts — and aggregates the most permissive
202
+ status. Most-permissive order: `supported` > `mixed` >
203
+ `unsupported` > `unknown`. Returns `"unknown"` or `"unsupported"`
204
+ only when the aggregated status is genuinely authoritative;
205
+ otherwise returns `None` so the caller falls through to the
206
+ generic transient `no_eligible_providers` reason.
207
+
195
208
  ### `request/upstream_helpers.py` — Upstream URL and endpoint validation
196
209
 
197
210
  Extracted from `RequestCoordinator` in Plan 136 Phase 5. Resolves the absolute
@@ -299,6 +312,42 @@ graph afterward. `replace_provider_payload()` remains as a conservative
299
312
  conditional ownership primitive for compatibility callers; the former
300
313
  `provider_payload_copy()` transcode helper was removed in Plan 124.
301
314
 
315
+ ### Dispatch-freeze lifecycle
316
+
317
+ `serialize_provider_payload()` serializes the current generation and
318
+ sets a dispatch-freeze flag (`_frozen`). Once frozen, mutations that
319
+ do **not** begin a new generation — `replace_provider_payload()` and
320
+ `set_provider_payload(increment_generation=False)` — raise
321
+ `RuntimeError("provider payload is frozen")` because they cannot
322
+ safely overwrite a body that downstream dispatch already consumed.
323
+
324
+ Methods that **begin a new generation** — `set_provider_payload(increment_generation=True)` and `adopt_provider_payload(increment_generation=True)` — reset `_frozen` alongside the cached serialized-bytes. The new generation is the canonical provider payload, and the next `serialize_provider_payload()` call re-encodes from scratch. This is what allows the post-selection cross-protocol transcoder (`_apply_selected_provider_transcode`) to replace a body that the previous attempt already serialized and dispatched — a retry that selects a different provider must rebuild from the original client payload, but the transcoder may also need to replace the previously adopted transcode result when account failover picks a different upstream. `adopt_provider_payload(increment_generation=False)` still raises when
325
+ frozen: it does not begin a new generation.
326
+
327
+ ### Thinking rejection attribution
328
+
329
+ When all eligible accounts are filtered out (including quarantine) and
330
+ the request carries thinking controls,
331
+ `_determine_thinking_rejection_status()` attributes the rejection to a
332
+ capability status. Per-provider thinking overrides (e.g. the bundled
333
+ OpenCode Go host capabilities for `muse-spark-1.2-contributor`)
334
+ live in the provider-scoped cache row, **not** the collapsed
335
+ `models` row. `cache.get_model()` deliberately does **not** apply
336
+ overrides; `cache.get_provider_model_entry()` and
337
+ `cache.get_provider_model_entries()` do.
338
+
339
+ When `context.provider_id` is unset, the method iterates every
340
+ provider entry for the model and aggregates the most permissive
341
+ status. Quarantine does **not** erase a provider's capability —
342
+ quarantined entries still contribute to the aggregated status. A
343
+ 400 (`CapabilityError`) is surfaced only when the aggregated status
344
+ is genuinely `unknown` or `unsupported`; when the status is
345
+ `supported` or `mixed` but every supporting account is currently
346
+ quarantined, the caller falls through to `ModelUnavailableError`
347
+ (503) / `UpstreamExhaustedError` so a transient retry is possible.
348
+ This prevents a misleading `thinking capability status: unknown`
349
+ 400 from masking a recoverable quarantine-state failure.
350
+
302
351
  Prepared transcode results retain one request-local translated JSON generation
303
352
  without recursively freezing or rematerializing it. Valid unchanged reuse
304
353
  adopts that generation through `adopt_provider_payload()` and attaches the
@@ -463,3 +512,4 @@ Streaming usage accumulation across chunks.
463
512
  - Same URL composition rules for catalog fetch and chat dispatch
464
513
  - Selection-claim lock splits DB I/O from runtime publication (Milestone B)
465
514
  - Dispatch persistence is one direct per-request transaction
515
+ - `ProviderBoundRequest` dispatch-freeze flag resets together with `payload_generation`; mutations that do not start a new generation (`replace_provider_payload`, `set_provider_payload(increment_generation=False)`, `adopt_provider_payload(increment_generation=False)`) reject when frozen. The post-selection transcoder relies on the generation-incrementing methods to replace a previously dispatched body on retry
@@ -76,7 +76,11 @@ Upstream HTTP response
76
76
  → RetryClassifier().classify(status_code, headers, body)
77
77
  → RetryableError(category, retry_after)
78
78
  → RequestCoordinator._should_retry()
79
- → HealthManager.record_failure() / record_success()
79
+ → EffectsApplier.apply_once()
80
+ - model_effect="quarantine": disable_model only;
81
+ record_failure() skipped (per-model failure isolation)
82
+ - source="transport" + model_effect="none":
83
+ record_failure() advances account-wide breaker
80
84
  → Retry with excluded accounts
81
85
  ```
82
86
 
@@ -185,6 +185,18 @@ Authentication failure and authoritative catalog withdrawal are separate
185
185
  terminal gates; they recover only through corrected credentials/operator action
186
186
  or authoritative model reappearance.
187
187
 
188
+ When all eligible accounts are excluded (including quarantine) and the
189
+ request carries thinking controls, the error class depends on the
190
+ aggregated capability status across every provider entry for the model,
191
+ not just the collapsed row. A 400 (`CapabilityError`) is returned only
192
+ when the aggregated status is genuinely `unknown` or `unsupported`.
193
+ When the aggregated status is `supported` or `mixed` but every
194
+ supporting account is quarantined, a transient 502/503 (`UpstreamError`,
195
+ `ModelUnavailableError`) is returned so the client can retry. This
196
+ prevents a misleading `thinking capability status: unknown` 400 from
197
+ being surfaced when the underlying provider does support the request
198
+ but is currently unhealthy across every account.
199
+
188
200
  ## Score Components
189
201
 
190
202
  Every `routing_decisions` row carries `score_components_json`:
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "eggpool"
3
- version = "0.7.2"
3
+ version = "0.7.3"
4
4
  description = "A lightweight proxy that aggregates multiple LLM provider accounts behind an OpenAI Chat Completions-compatible endpoint"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -265,32 +265,31 @@ class EffectsApplier:
265
265
  self._health_manager.record_rate_limit(account, retry_after)
266
266
  self._health_manager.release_request(account)
267
267
  elif effects.account_effect == "failure":
268
- # Record the failure for the account's circuit breaker without
269
- # applying an account-wide cooldown. When the classifier also
270
- # set ``model_effect = "quarantine"`` the per-model disable
271
- # applied via ``_apply_model_effect`` is the only shared-state
272
- # penalty; the account itself stays eligible so sibling models
273
- # on the same account keep routing.
274
- self._health_manager.record_failure(
275
- account,
276
- model_id=obs.model_id,
277
- reason=effects.backoff_reason or "unknown",
278
- )
279
- if (
280
- effects.model_effect == "none"
281
- and effects.backoff_until
282
- and effects.backoff_reason
283
- ):
284
- # Legacy/fallback path: no per-model suppression was
285
- # requested, so fall back to the previous account-wide
286
- # bounded cooldown. This branch only fires for callers
287
- # that opt into account-only failure handling.
288
- delay = effects.backoff_until - now
289
- if delay > 0:
290
- health = self._health_manager.get_account_health(account)
291
- health.cooldown_until = self._health_manager.clock() + delay
292
- health.health_state = "cooldown"
293
- health.is_healthy = False
268
+ # Per-model failures (``model_effect = "quarantine"``) must
269
+ # never advance the account-wide circuit breaker: a single
270
+ # model/provider anomaly should not black-hole sibling
271
+ # models on the same account. The per-model disable applied
272
+ # via ``_apply_model_effect`` is the only shared-state
273
+ # penalty in that case. Only when the failure is genuinely
274
+ # account-wide (no per-model suppression requested) do we
275
+ # call ``record_failure`` to advance the breaker counter.
276
+ if effects.model_effect == "none":
277
+ self._health_manager.record_failure(
278
+ account,
279
+ model_id=obs.model_id,
280
+ reason=effects.backoff_reason or "unknown",
281
+ )
282
+ if effects.backoff_until and effects.backoff_reason:
283
+ # Legacy/fallback path: no per-model suppression was
284
+ # requested, so fall back to the previous account-wide
285
+ # bounded cooldown. This branch only fires for callers
286
+ # that opt into account-only failure handling.
287
+ delay = effects.backoff_until - now
288
+ if delay > 0:
289
+ health = self._health_manager.get_account_health(account)
290
+ health.cooldown_until = self._health_manager.clock() + delay
291
+ health.health_state = "cooldown"
292
+ health.is_healthy = False
294
293
  elif effects.account_effect == "cooldown":
295
294
  self._health_manager.record_failure(
296
295
  account,
@@ -2251,34 +2251,61 @@ class RequestCoordinator:
2251
2251
  context=context,
2252
2252
  thinking_req=thinking_req,
2253
2253
  )
2254
- if rejected_status == "unknown":
2255
- await _thinking_counter.increment_unknown_capability(
2254
+ # ``rejected_status`` is ``"unknown"`` or
2255
+ # ``"unsupported"`` when the capability row is
2256
+ # truly authoritative (built-in override plus any
2257
+ # provider-scoped row report the same status);
2258
+ # ``None`` means every supporting provider has been
2259
+ # quarantined or filtered for reasons unrelated to
2260
+ # the thinking capability — the underlying provider
2261
+ # still supports thinking, so do not surface a
2262
+ # misleading client-validation 400. Fall through to
2263
+ # the standard ``UpstreamExhaustedError`` /
2264
+ # ``ModelUnavailableError`` path so the operator
2265
+ # observes a transient upstream unavailability.
2266
+ if rejected_status in {"unknown", "unsupported"}:
2267
+ if rejected_status == "unknown":
2268
+ await _thinking_counter.increment_unknown_capability(
2269
+ client_protocol=thinking_req.client_protocol,
2270
+ )
2271
+ else:
2272
+ await _thinking_counter.increment_unsupported_capability(
2273
+ client_protocol=thinking_req.client_protocol,
2274
+ )
2275
+ await _thinking_counter.increment_rejected(
2256
2276
  client_protocol=thinking_req.client_protocol,
2277
+ capability_status="no_eligible_providers",
2257
2278
  )
2258
- elif rejected_status == "unsupported":
2259
- await _thinking_counter.increment_unsupported_capability(
2260
- client_protocol=thinking_req.client_protocol,
2279
+ if context.thinking_trace is not None:
2280
+ context.thinking_trace["decision"] = "rejected"
2281
+ context.thinking_trace["capability_status"] = rejected_status
2282
+ raise CapabilityError(
2283
+ model_id=context.model_id,
2284
+ capability="thinking",
2285
+ requested_fields=thinking_req.fields,
2286
+ message=(
2287
+ f"Model {context.model_id!r} is available, "
2288
+ f"but no eligible provider is known to "
2289
+ f"support requested thinking controls "
2290
+ f"(thinking capability status: "
2291
+ f"{rejected_status or 'unknown'})."
2292
+ ),
2261
2293
  )
2262
- await _thinking_counter.increment_rejected(
2263
- client_protocol=thinking_req.client_protocol,
2264
- capability_status="no_eligible_providers",
2265
- )
2266
- if context.thinking_trace is not None:
2267
- context.thinking_trace["decision"] = "rejected"
2268
- context.thinking_trace["capability_status"] = (
2269
- rejected_status or "no_eligible_providers"
2294
+ # Capability status is genuinely ``supported`` (or
2295
+ # ``mixed``) for the selected provider but every
2296
+ # supporting account is currently filtered out
2297
+ # (typically due to transient quarantine). Surface a
2298
+ # 502/503 transient unavailability so the request
2299
+ # can be retried after the upstream cools down.
2300
+ if self._all_accounts_attempted(
2301
+ context, capability_policy=_capability_policy
2302
+ ):
2303
+ raise UpstreamExhaustedError(
2304
+ f"All eligible accounts attempted for model "
2305
+ f"{context.model_id!r}"
2270
2306
  )
2271
- raise CapabilityError(
2272
- model_id=context.model_id,
2273
- capability="thinking",
2274
- requested_fields=thinking_req.fields,
2275
- message=(
2276
- f"Model {context.model_id!r} is available, "
2277
- f"but no eligible provider is known to "
2278
- f"support requested thinking controls "
2279
- f"(thinking capability status: "
2280
- f"{rejected_status or 'unknown'})."
2281
- ),
2307
+ raise ModelUnavailableError(
2308
+ f"No accounts available for model {context.model_id!r}"
2282
2309
  )
2283
2310
  if self._all_accounts_attempted(
2284
2311
  context, capability_policy=_capability_policy
@@ -4539,9 +4566,17 @@ class RequestCoordinator:
4539
4566
  provider-scoped thinking rows live in
4540
4567
  ``provider_model_metadata`` and are applied on the per-candidate
4541
4568
  eligibility check but never make it into the collapsed entry.
4542
- To avoid a false ``unknown`` reading that would mis-attribute a
4543
- rejection, also consult the provider-scoped entry when the
4544
- collapsed one reports ``unknown``.
4569
+ To avoid a false ``"unknown"`` reading that would mis-attribute a
4570
+ rejection, also consult the provider-scoped entries when the
4571
+ collapsed one reports ``"unknown"`` — first the routing
4572
+ ``context.provider_id`` if it is set, and otherwise every
4573
+ provider with a known provider-scoped row. Built-in capability
4574
+ overrides (e.g. the canonical OpenCode Go host capabilities for
4575
+ ``muse-spark-1.2-contributor``) are applied at request time by
4576
+ ``ModelCatalogCache.get_provider_model_entry``; the bare-model
4577
+ request path therefore still resolves the correct
4578
+ ``supported`` status even when ``provider_id`` was not parsed
4579
+ out of the client request.
4545
4580
  """
4546
4581
  from eggpool.catalog.capabilities import extract_thinking_status_from_entry
4547
4582
 
@@ -4549,15 +4584,45 @@ class RequestCoordinator:
4549
4584
  try:
4550
4585
  cache = self._catalog.cache
4551
4586
  collapsed_entry = cache.get_model(context.model_id)
4552
- status = extract_thinking_status_from_entry(collapsed_entry)
4553
- if status == "unknown" and context.provider_id is not None:
4587
+ collapsed_status = extract_thinking_status_from_entry(collapsed_entry)
4588
+ if collapsed_status != "unknown":
4589
+ status = collapsed_status
4590
+ elif context.provider_id is not None:
4554
4591
  provider_entry = cache.get_provider_model_entry(
4555
4592
  context.model_id,
4556
4593
  context.provider_id,
4557
4594
  )
4558
- provider_status = extract_thinking_status_from_entry(provider_entry)
4559
- if provider_status != "unknown":
4560
- status = provider_status
4595
+ status = extract_thinking_status_from_entry(provider_entry)
4596
+ else:
4597
+ # No provider hint: aggregate every provider with a
4598
+ # known provider-scoped entry for the model so per-
4599
+ # provider built-in overrides still surface as
4600
+ # ``supported`` rather than collapsing to a misleading
4601
+ # ``unknown``. The router may have just marked every
4602
+ # supporting account as quarantined for this model
4603
+ # while the provider entry (and its built-in override)
4604
+ # is still authoritative for the catalog.
4605
+ # ``get_provider_model_entries`` returns the override-
4606
+ # applied view for every ``(model_id, provider_id)``
4607
+ # row in the cache, including rows whose accounts were
4608
+ # removed from ``_account_support`` by quarantine. The
4609
+ # most permissive status wins because the failure is
4610
+ # attributed to the request, not to any specific
4611
+ # provider.
4612
+ statuses: set[str] = {collapsed_status}
4613
+ for model_id, provider_id in cache.get_provider_model_entries():
4614
+ if model_id != context.model_id:
4615
+ continue
4616
+ entry = cache.get_provider_model_entry(model_id, provider_id)
4617
+ statuses.add(extract_thinking_status_from_entry(entry))
4618
+ if "supported" in statuses:
4619
+ status = "supported"
4620
+ elif "mixed" in statuses:
4621
+ status = "mixed"
4622
+ elif "unsupported" in statuses:
4623
+ status = "unsupported"
4624
+ else:
4625
+ status = collapsed_status
4561
4626
  except Exception: # noqa: BLE001
4562
4627
  return None
4563
4628
  if status in ("unknown", "unsupported"):
@@ -251,8 +251,17 @@ class ProviderBoundRequest:
251
251
  is the ownership boundary for path-level transformations such as
252
252
  safe compression. Unknown or externally-owned graphs must use
253
253
  :meth:`set_provider_payload` instead.
254
+
255
+ When ``increment_generation=True`` the call begins a new generation
256
+ that supersedes any previously serialized/frozen dispatch body.
257
+ The dispatch-freeze flag is reset alongside the cached serialized
258
+ bytes so the next ``serialize_provider_payload`` call re-encodes
259
+ the freshly adopted graph. This is what allows the
260
+ post-selection cross-protocol transcoder to replace the
261
+ preflight-prepared body after a previous attempt has already
262
+ serialized and dispatched once.
254
263
  """
255
- if self._frozen:
264
+ if self._frozen and not increment_generation:
256
265
  raise RuntimeError("provider payload is frozen")
257
266
  object.__setattr__(self, "_provider_payload", dict(payload))
258
267
  object.__setattr__(self, "mutated", True)
@@ -261,6 +270,9 @@ class ProviderBoundRequest:
261
270
  self.diagnostics.generation_changes += 1
262
271
  self.mutation_log.append(PayloadMutation(self.payload_generation, reason))
263
272
  del self.mutation_log[: -self.mutation_log_limit]
273
+ # A new generation invalidates the previous dispatch freeze;
274
+ # the newly adopted graph is the canonical provider payload.
275
+ object.__setattr__(self, "_frozen", False)
264
276
  object.__setattr__(self, "_provider_bytes", None)
265
277
  object.__setattr__(self, "_serialized_generation", None)
266
278
 
@@ -309,6 +321,15 @@ class ProviderBoundRequest:
309
321
  incremented so
310
322
  downstream caches (segmentation, prepared-transcode) can detect
311
323
  staleness.
324
+
325
+ When ``increment_generation=True`` the call begins a brand-new
326
+ generation that supersedes any previously serialized/frozen
327
+ dispatch body. The serialized-bytes cache and the dispatch-freeze
328
+ flag are both reset so the next ``serialize_provider_payload`` call
329
+ re-encodes the new graph; the retry path through
330
+ ``_apply_selected_provider_transcode`` relies on this so a
331
+ cross-protocol translation against a different selected provider
332
+ can replace a body that the previous attempt already serialized.
312
333
  """
313
334
  if not increment_generation and self._frozen:
314
335
  raise RuntimeError("provider payload is frozen")
@@ -321,6 +342,10 @@ class ProviderBoundRequest:
321
342
  PayloadMutation(self.payload_generation, "set_provider_payload")
322
343
  )
323
344
  del self.mutation_log[: -self.mutation_log_limit]
345
+ # A new generation invalidates any prior serialized/frozen
346
+ # dispatch body — the new graph is the canonical provider
347
+ # payload until the next serialization call.
348
+ object.__setattr__(self, "_frozen", False)
324
349
  object.__setattr__(self, "_provider_bytes", None)
325
350
  object.__setattr__(self, "_serialized_generation", None)
326
351
 
@@ -178,7 +178,7 @@ toml = [
178
178
 
179
179
  [[package]]
180
180
  name = "eggpool"
181
- version = "0.7.2"
181
+ version = "0.7.3"
182
182
  source = { editable = "." }
183
183
  dependencies = [
184
184
  { name = "aiosqlite" },
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes