eggpool 0.7.0__tar.gz → 0.7.2__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.0 → eggpool-0.7.2}/CHANGELOG.md +24 -0
  2. {eggpool-0.7.0 → eggpool-0.7.2}/PKG-INFO +1 -1
  3. {eggpool-0.7.0 → eggpool-0.7.2}/architecture/deep-dive-database.md +5 -2
  4. {eggpool-0.7.0 → eggpool-0.7.2}/architecture/deep-dive-health.md +2 -0
  5. {eggpool-0.7.0 → eggpool-0.7.2}/architecture/deep-dive-runtime.md +2 -1
  6. {eggpool-0.7.0 → eggpool-0.7.2}/architecture/deep-dive-security.md +6 -2
  7. {eggpool-0.7.0 → eggpool-0.7.2}/config.example.toml +11 -3
  8. {eggpool-0.7.0 → eggpool-0.7.2}/docs/deployment.md +10 -7
  9. {eggpool-0.7.0 → eggpool-0.7.2}/docs/thinking.md +12 -8
  10. {eggpool-0.7.0 → eggpool-0.7.2}/docs/transcoding.md +2 -2
  11. {eggpool-0.7.0 → eggpool-0.7.2}/pyproject.toml +1 -1
  12. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/_share/config.example.toml +11 -5
  13. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/accounts/state.py +3 -3
  14. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/api/proxy_request.py +26 -9
  15. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/catalog/capabilities.py +3 -2
  16. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/catalog/models_dev.py +28 -0
  17. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/catalog/pricing.py +32 -2
  18. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/catalog/service.py +31 -20
  19. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/config_reload_policy.py +7 -2
  20. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/config_utils.py +22 -14
  21. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/control/reload_manager.py +9 -1
  22. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/cost_recompute.py +28 -6
  23. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/cost_repair.py +29 -6
  24. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/connection.py +76 -28
  25. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/fastcli.py +1 -0
  26. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/generation_factory.py +21 -11
  27. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/health/circuit_breaker.py +2 -0
  28. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/health/health_manager.py +6 -7
  29. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/integrations/common.py +7 -2
  30. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/lifecycle/backup.py +3 -3
  31. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/logging.py +14 -2
  32. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/models/config.py +48 -15
  33. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/onboard.py +2 -1
  34. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/providers/_templates.toml +8 -0
  35. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/providers/outbound.py +4 -3
  36. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/proxy/client.py +2 -2
  37. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/proxy/normalized_usage.py +15 -13
  38. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/proxy/sse_observer.py +3 -0
  39. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/proxy/usage.py +7 -0
  40. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/request/coordinator.py +67 -12
  41. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/request/finalizer.py +12 -1
  42. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/request/provider_bound_request.py +22 -0
  43. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/routing/eligibility.py +15 -2
  44. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/routing/router.py +3 -3
  45. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/runtime.py +2 -0
  46. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/runtime_manager.py +25 -0
  47. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/security/redaction.py +85 -2
  48. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/transcoder/budget_resolver.py +43 -0
  49. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/transcoder/builtin_contracts.py +61 -17
  50. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/transcoder/openai_to_anthropic.py +21 -1
  51. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/transcoder/policy.py +13 -10
  52. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/transcoder/provider_adaptation.py +0 -16
  53. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/transcoder/streaming.py +36 -1
  54. {eggpool-0.7.0 → eggpool-0.7.2}/uv.lock +1 -1
  55. {eggpool-0.7.0 → eggpool-0.7.2}/.env.example +0 -0
  56. {eggpool-0.7.0 → eggpool-0.7.2}/.github/workflows/ci.yml +0 -0
  57. {eggpool-0.7.0 → eggpool-0.7.2}/.gitignore +0 -0
  58. {eggpool-0.7.0 → eggpool-0.7.2}/AGENTS.md +0 -0
  59. {eggpool-0.7.0 → eggpool-0.7.2}/LICENSE +0 -0
  60. {eggpool-0.7.0 → eggpool-0.7.2}/README.md +0 -0
  61. {eggpool-0.7.0 → eggpool-0.7.2}/architecture/README.md +0 -0
  62. {eggpool-0.7.0 → eggpool-0.7.2}/architecture/deep-dive-background.md +0 -0
  63. {eggpool-0.7.0 → eggpool-0.7.2}/architecture/deep-dive-catalog.md +0 -0
  64. {eggpool-0.7.0 → eggpool-0.7.2}/architecture/deep-dive-control.md +0 -0
  65. {eggpool-0.7.0 → eggpool-0.7.2}/architecture/deep-dive-core.md +0 -0
  66. {eggpool-0.7.0 → eggpool-0.7.2}/architecture/deep-dive-dashboard.md +0 -0
  67. {eggpool-0.7.0 → eggpool-0.7.2}/architecture/deep-dive-deployment.md +0 -0
  68. {eggpool-0.7.0 → eggpool-0.7.2}/architecture/deep-dive-integrations.md +0 -0
  69. {eggpool-0.7.0 → eggpool-0.7.2}/architecture/deep-dive-lifecycle.md +0 -0
  70. {eggpool-0.7.0 → eggpool-0.7.2}/architecture/deep-dive-metrics.md +0 -0
  71. {eggpool-0.7.0 → eggpool-0.7.2}/architecture/deep-dive-model-info.md +0 -0
  72. {eggpool-0.7.0 → eggpool-0.7.2}/architecture/deep-dive-models.md +0 -0
  73. {eggpool-0.7.0 → eggpool-0.7.2}/architecture/deep-dive-observability.md +0 -0
  74. {eggpool-0.7.0 → eggpool-0.7.2}/architecture/deep-dive-providers.md +0 -0
  75. {eggpool-0.7.0 → eggpool-0.7.2}/architecture/deep-dive-request-lifecycle.md +0 -0
  76. {eggpool-0.7.0 → eggpool-0.7.2}/architecture/deep-dive-retry.md +0 -0
  77. {eggpool-0.7.0 → eggpool-0.7.2}/architecture/deep-dive-routing.md +0 -0
  78. {eggpool-0.7.0 → eggpool-0.7.2}/architecture/deep-dive-transcoder.md +0 -0
  79. {eggpool-0.7.0 → eggpool-0.7.2}/architecture/overview.md +0 -0
  80. {eggpool-0.7.0 → eggpool-0.7.2}/artifacts/plan-019-evidence.md +0 -0
  81. {eggpool-0.7.0 → eggpool-0.7.2}/artifacts/plan-020-evidence.md +0 -0
  82. {eggpool-0.7.0 → eggpool-0.7.2}/artifacts/plan-021-evidence.md +0 -0
  83. {eggpool-0.7.0 → eggpool-0.7.2}/artifacts/plan-023-baseline.md +0 -0
  84. {eggpool-0.7.0 → eggpool-0.7.2}/artifacts/plan-030-exact-head-evidence.md +0 -0
  85. {eggpool-0.7.0 → eggpool-0.7.2}/config-examples/claude-code.env +0 -0
  86. {eggpool-0.7.0 → eggpool-0.7.2}/config-examples/opencode.jsonc +0 -0
  87. {eggpool-0.7.0 → eggpool-0.7.2}/config.sbc.example.toml +0 -0
  88. {eggpool-0.7.0 → eggpool-0.7.2}/deploy/eggpool-logrotate.conf +0 -0
  89. {eggpool-0.7.0 → eggpool-0.7.2}/deploy/eggpool.service +0 -0
  90. {eggpool-0.7.0 → eggpool-0.7.2}/deploy/env.example +0 -0
  91. {eggpool-0.7.0 → eggpool-0.7.2}/docs/agent-configuration.md +0 -0
  92. {eggpool-0.7.0 → eggpool-0.7.2}/docs/api-reference.md +0 -0
  93. {eggpool-0.7.0 → eggpool-0.7.2}/docs/backup-restore.md +0 -0
  94. {eggpool-0.7.0 → eggpool-0.7.2}/docs/config-profiles.md +0 -0
  95. {eggpool-0.7.0 → eggpool-0.7.2}/docs/filesystem-layout.md +0 -0
  96. {eggpool-0.7.0 → eggpool-0.7.2}/docs/firewall.md +0 -0
  97. {eggpool-0.7.0 → eggpool-0.7.2}/docs/live-config-rehash.md +0 -0
  98. {eggpool-0.7.0 → eggpool-0.7.2}/docs/model-info-openrouter-debug.md +0 -0
  99. {eggpool-0.7.0 → eggpool-0.7.2}/docs/model-limits.md +0 -0
  100. {eggpool-0.7.0 → eggpool-0.7.2}/docs/network-diagnostics.md +0 -0
  101. {eggpool-0.7.0 → eggpool-0.7.2}/docs/opencode-stream-stability.md +0 -0
  102. {eggpool-0.7.0 → eggpool-0.7.2}/docs/operations/dispatch-stability.md +0 -0
  103. {eggpool-0.7.0 → eggpool-0.7.2}/docs/providers.md +0 -0
  104. {eggpool-0.7.0 → eggpool-0.7.2}/docs/proxy.md +0 -0
  105. {eggpool-0.7.0 → eggpool-0.7.2}/docs/raspberry-pi.md +0 -0
  106. {eggpool-0.7.0 → eggpool-0.7.2}/docs/releasing.md +0 -0
  107. {eggpool-0.7.0 → eggpool-0.7.2}/docs/runbooks/database-recovery.md +0 -0
  108. {eggpool-0.7.0 → eggpool-0.7.2}/docs/stateless-responses.md +0 -0
  109. {eggpool-0.7.0 → eggpool-0.7.2}/scripts/__init__.py +0 -0
  110. {eggpool-0.7.0 → eggpool-0.7.2}/scripts/admission_race_stress.py +0 -0
  111. {eggpool-0.7.0 → eggpool-0.7.2}/scripts/bench_sqlite_writepath.py +0 -0
  112. {eggpool-0.7.0 → eggpool-0.7.2}/scripts/check_database.py +0 -0
  113. {eggpool-0.7.0 → eggpool-0.7.2}/scripts/debug_model_info_openrouter.sh +0 -0
  114. {eggpool-0.7.0 → eggpool-0.7.2}/scripts/install.sh +0 -0
  115. {eggpool-0.7.0 → eggpool-0.7.2}/scripts/install_prompt.py +0 -0
  116. {eggpool-0.7.0 → eggpool-0.7.2}/scripts/repro_high_concurrency_streams.py +0 -0
  117. {eggpool-0.7.0 → eggpool-0.7.2}/scripts/run_tests_with_timeout.py +0 -0
  118. {eggpool-0.7.0 → eggpool-0.7.2}/scripts/smoke_test.py +0 -0
  119. {eggpool-0.7.0 → eggpool-0.7.2}/scripts/test_model_info_identity.sh +0 -0
  120. {eggpool-0.7.0 → eggpool-0.7.2}/scripts/validate_routing.py +0 -0
  121. {eggpool-0.7.0 → eggpool-0.7.2}/scripts/verify_upstream_auth.py +0 -0
  122. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/__init__.py +0 -0
  123. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/__main__.py +0 -0
  124. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/_share/.env.example +0 -0
  125. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/_share/config.sbc.example.toml +0 -0
  126. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/accounts/__init__.py +0 -0
  127. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/accounts/registry.py +0 -0
  128. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/api/__init__.py +0 -0
  129. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/api/backoff.py +0 -0
  130. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/api/chat_completions.py +0 -0
  131. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/api/errors.py +0 -0
  132. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/api/messages.py +0 -0
  133. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/api/model_info.py +0 -0
  134. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/api/models.py +0 -0
  135. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/api/network.py +0 -0
  136. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/api/responses.py +0 -0
  137. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/api/runtime.py +0 -0
  138. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/api/stats.py +0 -0
  139. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/api/update.py +0 -0
  140. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/app.py +0 -0
  141. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/auth.py +0 -0
  142. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/background/__init__.py +0 -0
  143. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/background/backup.py +0 -0
  144. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/background/cleanup.py +0 -0
  145. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/background/maintenance.py +0 -0
  146. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/catalog/__init__.py +0 -0
  147. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/catalog/_time.py +0 -0
  148. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/catalog/cache.py +0 -0
  149. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/catalog/catalog_resolvers.py +0 -0
  150. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/catalog/fetcher.py +0 -0
  151. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/catalog/limits.py +0 -0
  152. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/catalog/normalizer.py +0 -0
  153. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/catalog/pricing_aliases.py +0 -0
  154. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/catalog/pricing_resolver.py +0 -0
  155. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/catalog/protocols.py +0 -0
  156. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/cli.py +0 -0
  157. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/cli_exit_codes.py +0 -0
  158. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/cli_full.py +0 -0
  159. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/cli_rehash_format.py +0 -0
  160. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/cli_rehash_helper.py +0 -0
  161. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/config.py +0 -0
  162. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/config_validation.py +0 -0
  163. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/constants.py +0 -0
  164. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/control/__init__.py +0 -0
  165. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/control/accepted_finalization.py +0 -0
  166. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/control/client.py +0 -0
  167. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/control/server.py +0 -0
  168. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/__init__.py +0 -0
  169. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/_resources.py +0 -0
  170. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/escape.py +0 -0
  171. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/render.py +0 -0
  172. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/routes.py +0 -0
  173. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/static/chart.umd.min.js +0 -0
  174. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/static/dashboard.css +0 -0
  175. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/static/dashboard.js +0 -0
  176. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/static/favicon.svg +0 -0
  177. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/telemetry.py +0 -0
  178. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/theme.py +0 -0
  179. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/Booberry.toml +0 -0
  180. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/Catppuccin Latte.toml +0 -0
  181. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/Catppuccin Macchiato.toml +0 -0
  182. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/Catppuccin Mocha.toml +0 -0
  183. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/Cyber Red.toml +0 -0
  184. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/Cyberpunk.toml +0 -0
  185. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/Dark Green.toml +0 -0
  186. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/Discord (80_ Saturation).toml +0 -0
  187. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/Discord.toml +0 -0
  188. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/Dracula.toml +0 -0
  189. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/Ferra Light.toml +0 -0
  190. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/Flexor Dark.toml +0 -0
  191. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/Gruvbox.toml +0 -0
  192. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/Halcyon Dark.toml +0 -0
  193. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/IntelliJ Light.toml +0 -0
  194. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/Kanagawa.toml +0 -0
  195. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/Macaw Dark.toml +0 -0
  196. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/Macaw Light.toml +0 -0
  197. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/Matrix.toml +0 -0
  198. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/Noctis Lilac.toml +0 -0
  199. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/Nord.toml +0 -0
  200. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/Nostromo Terminal.toml +0 -0
  201. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/One Dark.toml +0 -0
  202. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/Oxocarbon.toml +0 -0
  203. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/Rose Pine Dawn.toml +0 -0
  204. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/Rose Pine Moon.toml +0 -0
  205. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/Rose Pine.toml +0 -0
  206. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/Solarized Dark.toml +0 -0
  207. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/Sonokai.toml +0 -0
  208. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/Tokyo Night Storm.toml +0 -0
  209. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/VESPER.toml +0 -0
  210. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/Zenburn.toml +0 -0
  211. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/acton.toml +0 -0
  212. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/bam.toml +0 -0
  213. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/base16-atelier-forest-light.toml +0 -0
  214. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/berlin.toml +0 -0
  215. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/black but with important highlights.toml +0 -0
  216. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/broc.toml +0 -0
  217. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/cork.toml +0 -0
  218. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/ferra.toml +0 -0
  219. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/forest.toml +0 -0
  220. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/lisbon.toml +0 -0
  221. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/midnight.toml +0 -0
  222. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/oslo.toml +0 -0
  223. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/plum.toml +0 -0
  224. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/portland.toml +0 -0
  225. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/sunset.toml +0 -0
  226. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/tofino.toml +0 -0
  227. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/vanimo.toml +0 -0
  228. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/vik.toml +0 -0
  229. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/timeseries_buckets.py +0 -0
  230. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/__init__.py +0 -0
  231. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/migrations.py +0 -0
  232. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/repositories.py +0 -0
  233. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/rollup_repository.py +0 -0
  234. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0001_initial.sql +0 -0
  235. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0002_indexes.sql +0 -0
  236. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0003_request_attempts.sql +0 -0
  237. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0004_integration_hardening.sql +0 -0
  238. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0005_price_microdollars.sql +0 -0
  239. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0006_correct_price_microdollars.sql +0 -0
  240. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0007_price_cache_rates.sql +0 -0
  241. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0008_proxy_request_identity.sql +0 -0
  242. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0009_model_protocol_source.sql +0 -0
  243. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0010_health_probe.sql +0 -0
  244. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0011_model_resolution_status.sql +0 -0
  245. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0012_drop_reservations_estimated_microdollars.sql +0 -0
  246. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0013_request_attempts_account_id_index.sql +0 -0
  247. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0014_bandwidth_tracking.sql +0 -0
  248. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0015_multi_provider.sql +0 -0
  249. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0016_requests_provider_id.sql +0 -0
  250. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0017_price_snapshots_provider_id.sql +0 -0
  251. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0018_provider_pings.sql +0 -0
  252. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0019_client_ip.sql +0 -0
  253. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0020_performance_indexes.sql +0 -0
  254. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0021_provider_model_metadata.sql +0 -0
  255. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0022_dashboard_indexes.sql +0 -0
  256. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0023_deprecated_model_placeholder.sql +0 -0
  257. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0024_account_backoffs.sql +0 -0
  258. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0025_stale_request_index.sql +0 -0
  259. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0026_attempt_observability.sql +0 -0
  260. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0027_routing_decisions.sql +0 -0
  261. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0028_operational_events.sql +0 -0
  262. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0029_latency_phases.sql +0 -0
  263. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0030_model_pricing_aliases.sql +0 -0
  264. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0031_price_snapshot_provenance.sql +0 -0
  265. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0032_usage_rollups.sql +0 -0
  266. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0033_request_provider_local_cost.sql +0 -0
  267. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0034_transcoding_daily.sql +0 -0
  268. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0035_routing_decision_score_components.sql +0 -0
  269. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0036_model_info.sql +0 -0
  270. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0037_model_info_failure_count.sql +0 -0
  271. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0038_model_info_phase5.sql +0 -0
  272. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0039_thinking_observability.sql +0 -0
  273. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0040_cache_token_observability.sql +0 -0
  274. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0041_canonical_request_segmentation.sql +0 -0
  275. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0042_observe_mode_compression_accounting.sql +0 -0
  276. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0043_safe_suffix_compression.sql +0 -0
  277. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0044_compression_policy_resolver.sql +0 -0
  278. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0045_synthetic_cache_controls.sql +0 -0
  279. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0046_closed_loop_threshold_tuning.sql +0 -0
  280. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0047_normalize_rollup_bucket_start.sql +0 -0
  281. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0048_request_cost_repairs.sql +0 -0
  282. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0049_model_info_match_evidence.sql +0 -0
  283. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0050_routing_decisions_retention_index.sql +0 -0
  284. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0051_model_quarantine.sql +0 -0
  285. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0052_catalog_refresh_state.sql +0 -0
  286. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0053_remove_attempt_status_analytics_index.sql +0 -0
  287. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0054_model_quarantine_null_identity.sql +0 -0
  288. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/checksums.json +0 -0
  289. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/deploy/__init__.py +0 -0
  290. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/deploy_user.py +0 -0
  291. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/errors.py +0 -0
  292. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/event_loop_lag.py +0 -0
  293. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/failure/__init__.py +0 -0
  294. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/failure/applier.py +0 -0
  295. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/failure/classifier.py +0 -0
  296. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/failure/effects.py +0 -0
  297. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/failure/observation.py +0 -0
  298. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/failure/quarantine.py +0 -0
  299. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/failure/signal.py +0 -0
  300. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/failure/signal_extract.py +0 -0
  301. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/health/__init__.py +0 -0
  302. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/health/backoff.py +0 -0
  303. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/health/writable_probe.py +0 -0
  304. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/integrations/__init__.py +0 -0
  305. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/integrations/aider.py +0 -0
  306. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/integrations/cline.py +0 -0
  307. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/integrations/codex.py +0 -0
  308. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/integrations/continue_dev.py +0 -0
  309. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/integrations/goose.py +0 -0
  310. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/integrations/kilo.py +0 -0
  311. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/integrations/opencode.py +0 -0
  312. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/integrations/openhands.py +0 -0
  313. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/integrations/qwen_code.py +0 -0
  314. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/integrations/roo_code.py +0 -0
  315. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/jsonx.py +0 -0
  316. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/lifecycle/__init__.py +0 -0
  317. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/lifecycle/uninstall.py +0 -0
  318. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/metrics/__init__.py +0 -0
  319. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/metrics/buffer.py +0 -0
  320. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/metrics/failure_effects.py +0 -0
  321. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/metrics/thinking.py +0 -0
  322. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/model_info/__init__.py +0 -0
  323. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/model_info/dedup.py +0 -0
  324. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/model_info/identity.py +0 -0
  325. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/model_info/matching.py +0 -0
  326. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/model_info/normalization.py +0 -0
  327. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/model_info/presentation.py +0 -0
  328. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/model_info/repository.py +0 -0
  329. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/model_info/scheduler.py +0 -0
  330. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/model_info/service.py +0 -0
  331. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/model_info/sources/__init__.py +0 -0
  332. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/model_info/sources/artificial_analysis.py +0 -0
  333. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/model_info/sources/base.py +0 -0
  334. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/model_info/sources/huggingface.py +0 -0
  335. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/model_info/sources/openrouter.py +0 -0
  336. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/model_info/sources/provider_catalog.py +0 -0
  337. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/model_info/types.py +0 -0
  338. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/models/__init__.py +0 -0
  339. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/models/api.py +0 -0
  340. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/models/database.py +0 -0
  341. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/models/domain.py +0 -0
  342. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/observability/__init__.py +0 -0
  343. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/observability/routing_trace_writer.py +0 -0
  344. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/providers/__init__.py +0 -0
  345. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/providers/auth.py +0 -0
  346. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/providers/client_pool.py +0 -0
  347. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/providers/connect.py +0 -0
  348. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/providers/contract.py +0 -0
  349. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/providers/pproxy_transport.py +0 -0
  350. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/proxy/__init__.py +0 -0
  351. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/proxy/cost_reporting.py +0 -0
  352. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/proxy/sse.py +0 -0
  353. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/py.typed +0 -0
  354. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/quota/__init__.py +0 -0
  355. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/quota/audit.py +0 -0
  356. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/quota/estimation.py +0 -0
  357. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/quota/reservation.py +0 -0
  358. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/quota/scorer.py +0 -0
  359. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/reload_diagnostics.py +0 -0
  360. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/reload_transaction.py +0 -0
  361. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/request/__init__.py +0 -0
  362. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/request/attempt_finalizer.py +0 -0
  363. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/request/backoff_persistence.py +0 -0
  364. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/request/body.py +0 -0
  365. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/request/claim_lifecycle.py +0 -0
  366. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/request/failure_helpers.py +0 -0
  367. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/request/finalization_job.py +0 -0
  368. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/request/limits.py +0 -0
  369. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/request/parsed_payload.py +0 -0
  370. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/request/parsed_upstream_response.py +0 -0
  371. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/request/payload_utils.py +0 -0
  372. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/request/response_handoff.py +0 -0
  373. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/request/routing_helpers.py +0 -0
  374. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/request/routing_trace_guard.py +0 -0
  375. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/request/selection_claim_diagnostics.py +0 -0
  376. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/request/static_helpers.py +0 -0
  377. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/request/stream_completion.py +0 -0
  378. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/request/stream_diagnostics.py +0 -0
  379. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/request/terminal_status.py +0 -0
  380. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/request/thinking_adaptation.py +0 -0
  381. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/request/transform_pipeline.py +0 -0
  382. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/request/upstream_execution.py +0 -0
  383. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/request/upstream_helpers.py +0 -0
  384. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/request/usage_helpers.py +0 -0
  385. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/retry/__init__.py +0 -0
  386. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/retry/classification.py +0 -0
  387. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/routing/__init__.py +0 -0
  388. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/routing/config.py +0 -0
  389. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/routing/fairness.py +0 -0
  390. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/routing/provider.py +0 -0
  391. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/runtime_dispatch.py +0 -0
  392. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/runtime_metrics.py +0 -0
  393. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/runtime_paths.py +0 -0
  394. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/runtime_task_inventory.py +0 -0
  395. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/runtime_tasks.py +0 -0
  396. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/security/__init__.py +0 -0
  397. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/stats/__init__.py +0 -0
  398. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/stats/cache_metrics.py +0 -0
  399. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/stats/dashboard_explain.py +0 -0
  400. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/stats/grouped_timeseries.py +0 -0
  401. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/stats/queries.py +0 -0
  402. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/stats/segmentation.py +0 -0
  403. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/stats/service.py +0 -0
  404. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/stats/transcoding.py +0 -0
  405. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/toml_edit.py +0 -0
  406. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/transcoder/__init__.py +0 -0
  407. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/transcoder/anthropic_to_openai.py +0 -0
  408. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/transcoder/cache_stability.py +0 -0
  409. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/transcoder/cache_translation.py +0 -0
  410. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/transcoder/context.py +0 -0
  411. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/transcoder/errors.py +0 -0
  412. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/transcoder/ids.py +0 -0
  413. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/transcoder/json_helpers.py +0 -0
  414. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/transcoder/prepared.py +0 -0
  415. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/transcoder/protocol.py +0 -0
  416. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/transcoder/sensitive_media.py +0 -0
  417. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/transcoder/static_headers.py +0 -0
  418. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/transcoder/usage.py +0 -0
  419. {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/update_checker.py +0 -0
@@ -244,6 +244,30 @@ Documentation in `architecture/README.md`, `docs/live-config-rehash.md`,
244
244
  command inventory updated with slow-writer, extended soak, and soak
245
245
  runner commands.
246
246
 
247
+ ### Fixed
248
+
249
+ - **Provider-suffixed model ids no longer leak the ``/provider-id``
250
+ suffix to upstream.** ``/v1/models`` exposes provider-scoped entries in
251
+ the form ``<model-id>/<provider-id>``. When a client used one of those
252
+ suffixed ids against an endpoint whose body is transcoded or rewritten
253
+ -- ``/v1/chat/completions`` for an Anthropic model, or any cross-
254
+ protocol retry -- the suffix leaked into the JSON body the upstream
255
+ received, which rejected the request with ``Model <model-id>/<provider-id>
256
+ is not supported``. The proxy layer now normalizes the in-memory
257
+ payload's ``model`` field to the parsed base id at every point where a
258
+ body is rebuilt for upstream dispatch: the transcode preflight cache
259
+ (`handle_proxy_request` in `src/eggpool/api/proxy_request.py`), the
260
+ coordinator's re-translation reset
261
+ (`_apply_selected_provider_transcode` in
262
+ `src/eggpool/request/coordinator.py`), and the legacy provider-request
263
+ adapter (`RequestCoordinator._legacy_provider_request`). The
264
+ immutable ``client_payload`` contract is preserved -- only the
265
+ provider-bound snapshot loses the suffix. Regression coverage is
266
+ parameterized across multiple model ids in
267
+ `tests/unit/test_prepared_transcode.py` (`TestCoordinatorResetStripsProviderSuffix`)
268
+ so the fix is model-agnostic and future model names are covered by
269
+ default.
270
+
247
271
  ## [0.6.0] - 2026-07-09
248
272
 
249
273
  ### Fixed
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: eggpool
3
- Version: 0.7.0
3
+ Version: 0.7.2
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
@@ -295,8 +295,11 @@ Error handling transitions to `FAILED_CLOSED` from `CONNECTING`, `READY`, or
295
295
  must close admission regardless of the current state.
296
296
 
297
297
  The `_invalidate_connection()` method detaches the connection, records the
298
- failure reason, and transitions to `FAILED_CLOSED`. It does not attempt
299
- reconnection — the deployment contract is a worker restart.
298
+ failure reason, and transitions to `FAILED_CLOSED`. Transaction failures
299
+ detach under the connection lock and close the old connection after the lock
300
+ is released; direct callers perform the same handoff before awaiting close.
301
+ It does not attempt reconnection — the deployment contract is a worker
302
+ restart.
300
303
 
301
304
  ## Schema Baseline Decision (Plan 137)
302
305
 
@@ -47,6 +47,8 @@ Per-account health tracking:
47
47
  ### `health/circuit_breaker.py`
48
48
 
49
49
  Circuit breaker implementation:
50
+ - Synchronous state transitions use a short-held `threading.Lock` so the
51
+ breaker remains safe at thread/diagnostic boundaries.
50
52
  - **Closed** (healthy): requests flow normally
51
53
  - **Open** (unhealthy): requests blocked, cooldown timer running
52
54
  - **Half-open** (probe): single probe request allowed
@@ -182,7 +182,8 @@ failed durable clear leaves the current in-memory suppression intact.
182
182
  guarantees exactly one asyncio loop regardless of `runtime_threads`, which
183
183
  only sizes the Rust-side I/O pool
184
184
  - All `asyncio.Lock` objects are loop-bound
185
- - `MetricsWriteCoalescer` is the only component using `threading.Lock`
185
+ - `MetricsWriteCoalescer` and `CircuitBreaker` use short-held
186
+ `threading.Lock` guards for their synchronous cross-boundary methods
186
187
  - `fastcli` and `runtime_paths` are stdlib-only
187
188
  - PID file owned by supervisor
188
189
  - Generation swap never interrupts in-flight requests or accepted retained terminal jobs
@@ -21,8 +21,12 @@ Strips sensitive headers from upstream responses before they reach clients:
21
21
 
22
22
  `[security].trusted_proxies` is an exact immediate-peer allowlist. The proxy
23
23
  uses `X-Forwarded-For` or `X-Real-IP` only when the ASGI peer address is in
24
- that list, and accepts only the first bounded, control-character-free value.
25
- An empty list ignores forwarded attribution headers and falls back to the
24
+ that list. For `X-Forwarded-For`, it walks from the EggPool-facing side and
25
+ uses the first bounded, control-character-free hop that is not itself trusted,
26
+ so a leftmost client-supplied value cannot override a proxy-appended client
27
+ identity. Configured proxies must overwrite or correctly append these headers;
28
+ `X-Real-IP` is necessarily trusted as supplied by that configured peer. An
29
+ empty list ignores forwarded attribution headers and falls back to the
26
30
  immediate peer.
27
31
 
28
32
  ### `auth.py` — Local API Key Authentication
@@ -109,6 +109,11 @@ catalog_withdrawal_policy = "preserve_until_health"
109
109
  loss_policy = "warn" # "warn" or "reject" for loss-of-information
110
110
  prefer_native = true # prefer native-protocol accounts in routing
111
111
 
112
+ # Thinking/reasoning transcoding is enabled by default. Set this to false to
113
+ # drop thinking controls and content instead of translating them.
114
+ # [transcoder.features]
115
+ # thinking = false
116
+
112
117
  # Global effort→budget token mapping for thinking/reasoning.
113
118
  # Values are used when the model's capability does not carry a
114
119
  # per-model effort_to_budget_tokens mapping.
@@ -240,7 +245,7 @@ ttl_seconds = 86400
240
245
  # metadata with display names, context limits, benchmarks, and pipeline
241
246
  # tags. Manual overrides and alias expansion are also supported.
242
247
  # [model_info]
243
- # enabled = false # optional metadata sidecar (default: false)
248
+ # enabled = true # metadata sidecar (default: true)
244
249
  # include_in_models_endpoint = true # compact fields in /v1/models (default: true)
245
250
  # refresh_interval_s = 3600 # global refresh cadence (seconds)
246
251
  # sparse_refresh_interval_s = 300 # accelerated refresh for sparse models (seconds)
@@ -296,7 +301,7 @@ ttl_seconds = 86400
296
301
  enabled = false
297
302
 
298
303
  [model_info]
299
- enabled = false
304
+ enabled = true
300
305
 
301
306
  [update_checker]
302
307
  enabled = false
@@ -369,6 +374,8 @@ cleanup_max_rows_per_pass = 5000
369
374
  allowed_hosts = []
370
375
  cors_origins = []
371
376
  # Honor X-Forwarded-For/X-Real-IP only when the immediate peer is listed here.
377
+ # Configure only proxies that overwrite or correctly append these headers;
378
+ # X-Forwarded-For uses the nearest untrusted hop in the chain.
372
379
  # An empty list ignores forwarded client-attribution headers.
373
380
  #
374
381
  # List both IPv4 and IPv6 loopback addresses explicitly when the proxy is
@@ -447,7 +454,8 @@ enabled = false
447
454
  # header = "Authorization"
448
455
  # scheme = "Bearer"
449
456
  #
450
- # # OpenCode Go exposes low/medium/high thinking controls for mimo-v2.5.
457
+ # # OpenCode Go exposes low/medium/high thinking controls for mimo-v2.5 and
458
+ # # minimal/low/medium/high/xhigh for muse-spark-1.2-contributor.
451
459
  # [providers.opencode-go.model_capabilities."mimo-v2.5".thinking]
452
460
  # status = "supported"
453
461
  # source = "provider_catalog"
@@ -350,9 +350,11 @@ production installation is documented below.
350
350
 
351
351
  ## Performance Profiles
352
352
 
353
- EggPool ships with a lean default and explicit fuller profiles. The default
354
- install uses one SQLite worker, low-wear analytics, loopback binding, and
355
- disabled optional enrichment/diagnostics. All profiles use the supported
353
+ EggPool ships with a lean SBC default and a fuller standard default. The
354
+ standard install uses one SQLite worker, low-wear analytics, loopback binding,
355
+ and the metadata-enrichment sidecar (model_info) enabled by default. The SBC
356
+ profile keeps optional enrichment/diagnostics disabled to bound resource
357
+ use on Raspberry Pi class hardware. All profiles use the supported
356
358
  single-event-loop default (`threads = 1`).
357
359
 
358
360
  `eggpool onboard` asks whether to bind the API/dashboard to the LAN. Answering
@@ -360,10 +362,11 @@ yes writes `server.host = "0.0.0.0"`; noninteractive onboarding writes the safe
360
362
  loopback value. Dashboard authentication remains enabled by default through
361
363
  the generated server API key (`dashboard.public = false`).
362
364
 
363
- Optional background features are explicit: set `[model_info].enabled = true`
364
- for metadata enrichment, `[backup].enabled = true` for in-process archives,
365
- or `[update_checker].enabled = true` for the periodic PyPI status probe. Host
366
- DNS behavior is provided by the operating system; EggPool does not add a
365
+ Optional background features that remain explicit: set `[backup].enabled = true`
366
+ for in-process archives or `[update_checker].enabled = true` for the periodic
367
+ PyPI status probe. Set `[model_info].enabled = false` to disable the
368
+ metadata-enrichment sidecar (SBC profile does this by default). Host DNS
369
+ behavior is provided by the operating system; EggPool does not add a
367
370
  process-local resolver cache.
368
371
 
369
372
  For a short deployment comparison, use the same host, Python, config shape,
@@ -28,16 +28,19 @@ Status values (`CapabilityStatus`):
28
28
 
29
29
  Source: `src/eggpool/catalog/capabilities.py:36-44`
30
30
 
31
- ## 2. Enabling Thinking Transcoding
31
+ ## 2. Thinking Transcoding
32
32
 
33
- Thinking transcoding is **off by default**. The feature gate lives in `[transcoder.features]`:
33
+ Thinking transcoding is **enabled by default**. No configuration is required.
34
+ To disable it, set the feature gate in `[transcoder.features]`:
34
35
 
35
36
  ```toml
36
37
  [transcoder.features]
37
- thinking = true
38
+ thinking = false
38
39
  ```
39
40
 
40
- When `thinking = false` (the default), the transcoder **drops** thinking-related fields with a structured `reasoning_content_dropped` warning rather than forwarding them. Specifically:
41
+ When `thinking = false`, the transcoder **drops** thinking-related fields with a
42
+ structured `reasoning_content_dropped` warning rather than forwarding them.
43
+ Specifically:
41
44
 
42
45
  - **OpenAI → Anthropic**: `reasoning_content` in assistant messages is dropped.
43
46
  - **Anthropic → OpenAI**: `thinking` content blocks are dropped.
@@ -63,7 +66,7 @@ Source: `src/eggpool/transcoder/policy.py:217-225`
63
66
 
64
67
  ```toml
65
68
  [transcoder.features]
66
- thinking = true # Enable thinking/reasoning transcoding (default: false)
69
+ thinking = true # Enable thinking/reasoning transcoding (default: true)
67
70
  ```
68
71
 
69
72
  ### Budget Defaults
@@ -500,9 +503,10 @@ Source: `src/eggpool/metrics/thinking.py`, `src/eggpool/api/stats.py:452-463`, `
500
503
 
501
504
  **Symptoms:** Client sends `reasoning_effort` but the upstream receives no thinking controls.
502
505
 
503
- **Cause:** Thinking transcoding is disabled. The feature gate `[transcoder.features]` defaults to `thinking = false`.
506
+ **Cause:** Thinking transcoding was explicitly disabled with
507
+ `[transcoder.features].thinking = false`.
504
508
 
505
- **Fix:** Enable the feature:
509
+ **Fix:** Remove the override or enable the feature:
506
510
 
507
511
  ```toml
508
512
  [transcoder.features]
@@ -562,7 +566,7 @@ native_protocols = ["anthropic"]
562
566
 
563
567
  **Cause:** Thinking transcoding is likely disabled for streaming. The feature gate affects both streaming and non-streaming paths.
564
568
 
565
- **Fix:** Ensure `[transcoder.features] thinking = true` is set. Check `[transcoder.openai_reasoning_fields]` to verify the streaming delta field name matches your client's expectations (default: `"reasoning"`).
569
+ **Fix:** Remove the disabling override or ensure `[transcoder.features] thinking = true` is set. Check `[transcoder.openai_reasoning_fields]` to verify the streaming delta field name matches your client's expectations (default: `"reasoning"`).
566
570
 
567
571
  ---
568
572
 
@@ -59,8 +59,8 @@ prefer_native = true
59
59
  tools = true
60
60
  # Image / document content parts (default: false)
61
61
  vision = false
62
- # Extended thinking blocks ↔ reasoning_content (default: false)
63
- thinking = false
62
+ # Extended thinking blocks ↔ reasoning_content (default: true)
63
+ thinking = true
64
64
  # OpenAI response_format / json_schema coercion (default: false)
65
65
  structured_outputs = false
66
66
  # Anthropic-only primitives explicit handling (default: false)
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "eggpool"
3
- version = "0.7.0"
3
+ version = "0.7.2"
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"
@@ -104,6 +104,11 @@ catalog_withdrawal_policy = "preserve_until_health"
104
104
  loss_policy = "warn" # "warn" or "reject" for loss-of-information
105
105
  prefer_native = true # prefer native-protocol accounts in routing
106
106
 
107
+ # Thinking/reasoning transcoding is enabled by default. Set this to false to
108
+ # drop thinking controls and content instead of translating them.
109
+ # [transcoder.features]
110
+ # thinking = false
111
+
107
112
  # Global effort→budget token mapping for thinking/reasoning.
108
113
  # Values are used when the model's capability does not carry a
109
114
  # per-model effort_to_budget_tokens mapping.
@@ -232,7 +237,7 @@ ttl_seconds = 86400
232
237
  # metadata with display names, context limits, benchmarks, and pipeline
233
238
  # tags. Manual overrides and alias expansion are also supported.
234
239
  # [model_info]
235
- # enabled = false # optional metadata sidecar (default: false)
240
+ # enabled = true # metadata sidecar (default: true)
236
241
  # include_in_models_endpoint = true # compact fields in /v1/models (default: true)
237
242
  # refresh_interval_s = 3600 # global refresh cadence (seconds)
238
243
  # sparse_refresh_interval_s = 300 # accelerated refresh for sparse models (seconds)
@@ -288,7 +293,7 @@ ttl_seconds = 86400
288
293
  enabled = false
289
294
 
290
295
  [model_info]
291
- enabled = false
296
+ enabled = true
292
297
 
293
298
  [update_checker]
294
299
  enabled = false
@@ -361,6 +366,8 @@ cleanup_max_rows_per_pass = 5000
361
366
  allowed_hosts = []
362
367
  cors_origins = []
363
368
  # Honor X-Forwarded-For/X-Real-IP only when the immediate peer is listed here.
369
+ # Configure only proxies that overwrite or correctly append these headers;
370
+ # X-Forwarded-For uses the nearest untrusted hop in the chain.
364
371
  # An empty list ignores forwarded client-attribution headers.
365
372
  trusted_proxies = []
366
373
  redact_headers = ["authorization", "x-api-key"]
@@ -433,7 +440,8 @@ enabled = false
433
440
  # header = "Authorization"
434
441
  # scheme = "Bearer"
435
442
  #
436
- # # OpenCode Go exposes low/medium/high thinking controls for mimo-v2.5.
443
+ # # OpenCode Go exposes low/medium/high thinking controls for mimo-v2.5 and
444
+ # # minimal/low/medium/high/xhigh for muse-spark-1.2-contributor.
437
445
  # [providers.opencode-go.model_capabilities."mimo-v2.5".thinking]
438
446
  # status = "supported"
439
447
  # source = "provider_catalog"
@@ -912,5 +920,3 @@ protocol = "anthropic"
912
920
 
913
921
  [model_overrides."claude-sonnet-4"]
914
922
  protocol = "anthropic"
915
-
916
-
@@ -79,10 +79,12 @@ class AccountRuntimeState:
79
79
  if now is None:
80
80
  now = time.time()
81
81
  if self.health_state == "quota_exhausted":
82
- if self.cooldown_until > 0 and now >= self.cooldown_until:
82
+ if self.cooldown_until == 0.0 or now >= self.cooldown_until:
83
83
  self.health_state = "healthy"
84
84
  self.cooldown_until = 0.0
85
85
  self.consecutive_failures = 0
86
+ # Quota exhaustion has its own expiry semantics; do not fall
87
+ # through to the generic transient-state branch.
86
88
  return
87
89
  if self.health_state in ("rate_limited", "cooldown") and (
88
90
  self.cooldown_until == 0.0 or now >= self.cooldown_until
@@ -99,8 +101,6 @@ class AccountRuntimeState:
99
101
  return False
100
102
  if self.health_state in ("authentication_failed",):
101
103
  return False
102
- if self.health_state == "quota_exhausted":
103
- return self.cooldown_until > 0 and self.cooldown_until <= now
104
104
  return self.cooldown_until <= now
105
105
 
106
106
  def record_success(self) -> None:
@@ -257,13 +257,15 @@ def get_client_ip(
257
257
  if peer in trusted_proxies:
258
258
  forwarded_for = request.headers.get("x-forwarded-for")
259
259
  if forwarded_for is not None:
260
- first_forwarded = next(
261
- (part.strip() for part in forwarded_for.split(",") if part.strip()),
262
- "",
263
- )
264
- forwarded_ip = _valid_forwarded_client_ip(first_forwarded)
265
- if forwarded_ip is not None:
266
- return forwarded_ip
260
+ # Walk from the EggPool-facing side of the chain. The first
261
+ # valid hop that is not itself trusted is the nearest identity
262
+ # that the configured proxy chain can attest to; leftmost values
263
+ # remain client-controlled when a proxy appends rather than
264
+ # overwrites the header.
265
+ for part in reversed(forwarded_for.split(",")):
266
+ forwarded_ip = _valid_forwarded_client_ip(part)
267
+ if forwarded_ip is not None and forwarded_ip not in trusted_proxies:
268
+ return forwarded_ip
267
269
 
268
270
  real_ip = _valid_forwarded_client_ip(request.headers.get("x-real-ip"))
269
271
  if real_ip is not None:
@@ -474,7 +476,7 @@ async def handle_proxy_request(
474
476
  except Exception as exc:
475
477
  request_state = getattr(request, "state", None)
476
478
  request_id_or_none = (
477
- getattr(request_state, "request_id", None)
479
+ getattr(request_state, "proxy_request_id", None)
478
480
  if request_state is not None
479
481
  else None
480
482
  )
@@ -625,6 +627,21 @@ async def _handle_proxy_request_inner(
625
627
  with _span(span_recorder, SPAN_MODEL_PARSE):
626
628
  model_id, provider_id = parse_model_provider(model_value, known_providers)
627
629
 
630
+ # Normalize the payload's model field to the parsed model_id BEFORE the
631
+ # transcode preflight runs. The preflight caches the translated payload
632
+ # and a JSON-encoded body so the coordinator can skip a duplicate encode
633
+ # on the hot path. If the cached body still carries a provider-suffixed
634
+ # ``model-id/provider-id`` value (which is how ``/v1/models`` exposes
635
+ # provider-scoped entries), the upstream receives the suffix and rejects
636
+ # the request with ``Model <model-id>/<provider-id> is not supported``
637
+ # because the upstream does not understand eggpool's namespace. Rewriting
638
+ # the in-memory snapshot here keeps both the preflight encoded body and
639
+ # the provider-bound payload consistent.
640
+ preflight_payload = payload
641
+ if model_id != model_value:
642
+ preflight_payload = dict(payload)
643
+ preflight_payload["model"] = model_id
644
+
628
645
  # Preflight context limit check (guardrail, not primary enforcement).
629
646
  catalog = lease.runtime.catalog
630
647
  transcoder_policy = lease.runtime.transcoder_policy
@@ -662,7 +679,7 @@ async def _handle_proxy_request_inner(
662
679
  model_id=model_id,
663
680
  provider_id=provider_id,
664
681
  client_protocol=endpoint.protocol,
665
- payload=payload,
682
+ payload=preflight_payload,
666
683
  transcoder_policy=transcoder_policy,
667
684
  )
668
685
  if preflight is not None:
@@ -527,10 +527,11 @@ def aggregate_thinking_capabilities(
527
527
  ]
528
528
  budget_min = max(mins) if mins else None
529
529
  budget_max = min(maxes) if maxes else None
530
- # Invariant: min <= max. If violated, fall back to None.
530
+ # A collapsed model can span providers whose bounds do not overlap. An
531
+ # aggregate minimum would then be unsafe for providers with a lower
532
+ # maximum, so retain the conservative upper bound and omit the minimum.
531
533
  if budget_min is not None and budget_max is not None and budget_min > budget_max:
532
534
  budget_min = None
533
- budget_max = None
534
535
 
535
536
  # Merge effort metadata conservatively: retain the lowest budget for
536
537
  # each effort so the aggregate never promises more than one provider
@@ -21,6 +21,7 @@ MODELS_DEV_BASE_URL = "https://models.dev"
21
21
  OPENCODE_GO_MODELS_DEV_PROVIDER_ID = "opencode-go"
22
22
  OPENCODE_COMPATIBLE_EFFORTS = ["low", "medium", "high"]
23
23
  _EFFORT_BUDGET_DEFAULTS = {
24
+ "minimal": 1024,
24
25
  "low": 1024,
25
26
  "medium": 4096,
26
27
  "high": 16384,
@@ -116,6 +117,33 @@ def derive_opencode_go_supported_efforts(
116
117
  """
117
118
  if metadata.get("reasoning") is not True:
118
119
  return []
120
+
121
+ # models.dev mirrors OpenCode's provider catalog and can carry the exact
122
+ # effort set for newer models. Prefer it when present so newly released
123
+ # levels (for example Muse Spark's ``xhigh``) are not silently dropped.
124
+ reasoning_options = metadata.get("reasoning_options")
125
+ if isinstance(reasoning_options, list):
126
+ efforts: list[str] = []
127
+ for option in cast("list[object]", reasoning_options):
128
+ if not isinstance(option, dict):
129
+ continue
130
+ option_dict = cast("dict[str, object]", option)
131
+ if option_dict.get("type") != "effort":
132
+ continue
133
+ values = option_dict.get("values")
134
+ if not isinstance(values, list):
135
+ continue
136
+ for value in cast("list[object]", values):
137
+ if not isinstance(value, str):
138
+ continue
139
+ effort = value.strip().lower()
140
+ if effort == "med":
141
+ effort = "medium"
142
+ if effort and effort not in efforts:
143
+ efforts.append(effort)
144
+ if efforts:
145
+ return efforts
146
+
119
147
  lowered = model_id.lower()
120
148
  if "deepseek-v4" in lowered:
121
149
  return [*OPENCODE_COMPATIBLE_EFFORTS, "max"]
@@ -7,7 +7,7 @@ import math
7
7
  import re
8
8
  from dataclasses import dataclass
9
9
  from decimal import Decimal, InvalidOperation
10
- from typing import TYPE_CHECKING, Any
10
+ from typing import TYPE_CHECKING, Any, cast
11
11
 
12
12
  from eggpool.constants import (
13
13
  DEFAULT_PROVIDER_ID,
@@ -250,11 +250,21 @@ _MAX_ESTIMATED_COST_PER_TOKEN_MICRODOLLARS = 100 # $0.10 / token
250
250
  MAX_RESERVATION_COST_MICRODOLLARS = 1_000_000 # $1.00 per reservation
251
251
 
252
252
 
253
+ def usage_includes_cached_input(usage: object) -> bool:
254
+ """Return whether an OpenAI usage object reports cache as a subset."""
255
+ if not isinstance(usage, dict):
256
+ return False
257
+ usage_dict = cast("dict[str, Any]", usage)
258
+ prompt_details = usage_dict.get("prompt_tokens_details")
259
+ return isinstance(prompt_details, dict) and "cached_tokens" in prompt_details
260
+
261
+
253
262
  def total_billable_tokens(
254
263
  input_tokens: int,
255
264
  output_tokens: int,
256
265
  cache_read_tokens: int | None = None,
257
266
  cache_write_tokens: int | None = None,
267
+ reasoning_tokens: int | None = None,
258
268
  ) -> int:
259
269
  """Sum the billable token categories for a request.
260
270
 
@@ -265,7 +275,13 @@ def total_billable_tokens(
265
275
  """
266
276
  read = cache_read_tokens or 0
267
277
  write = cache_write_tokens or 0
268
- return input_tokens + output_tokens + read + write
278
+ # Providers commonly report reasoning tokens as a breakdown of
279
+ # completion_tokens, but some compatible providers report the breakdown
280
+ # without including it in output_tokens. Use the larger value so the
281
+ # reasoning-only shape is billable without double-counting the standard
282
+ # OpenAI shape.
283
+ output = max(output_tokens, reasoning_tokens or 0)
284
+ return input_tokens + output + read + write
269
285
 
270
286
 
271
287
  def is_plausible_request_cost(
@@ -275,6 +291,7 @@ def is_plausible_request_cost(
275
291
  output_tokens: int,
276
292
  cache_read_tokens: int | None = None,
277
293
  cache_write_tokens: int | None = None,
294
+ reasoning_tokens: int | None = None,
278
295
  max_cost_per_token: int = _MAX_TRUSTED_COST_PER_TOKEN_MICRODOLLARS,
279
296
  ) -> bool:
280
297
  """Whether a cost value is plausible for the billable tokens supplied.
@@ -291,6 +308,7 @@ def is_plausible_request_cost(
291
308
  output_tokens,
292
309
  cache_read_tokens,
293
310
  cache_write_tokens,
311
+ reasoning_tokens,
294
312
  )
295
313
  if total_tokens <= 0:
296
314
  return False
@@ -306,6 +324,7 @@ def choose_bounded_estimated_cost(
306
324
  output_tokens: int,
307
325
  cache_read_tokens: int | None = None,
308
326
  cache_write_tokens: int | None = None,
327
+ reasoning_tokens: int | None = None,
309
328
  ) -> tuple[int, str]:
310
329
  """Pick the most defensible bounded estimate for a request.
311
330
 
@@ -336,6 +355,7 @@ def choose_bounded_estimated_cost(
336
355
  output_tokens=output_tokens,
337
356
  cache_read_tokens=cache_read_tokens,
338
357
  cache_write_tokens=cache_write_tokens,
358
+ reasoning_tokens=reasoning_tokens,
339
359
  max_cost_per_token=_MAX_ESTIMATED_COST_PER_TOKEN_MICRODOLLARS,
340
360
  )
341
361
  reservation_bounded = (
@@ -355,6 +375,7 @@ def choose_bounded_estimated_cost(
355
375
  output_tokens=output_tokens,
356
376
  cache_read_tokens=cache_read_tokens,
357
377
  cache_write_tokens=cache_write_tokens,
378
+ reasoning_tokens=reasoning_tokens,
358
379
  max_cost_per_token=_MAX_ESTIMATED_COST_PER_TOKEN_MICRODOLLARS,
359
380
  )
360
381
  )
@@ -364,6 +385,7 @@ def choose_bounded_estimated_cost(
364
385
  output_tokens,
365
386
  cache_read_tokens,
366
387
  cache_write_tokens,
388
+ reasoning_tokens,
367
389
  )
368
390
  if total_tokens <= 0:
369
391
  return 0, "unknown"
@@ -622,6 +644,7 @@ class CostCalculator:
622
644
  provider_id: str | None = None,
623
645
  *,
624
646
  input_tokens_include_cache: bool = False,
647
+ reasoning_tokens: int = 0,
625
648
  ) -> tuple[int, str]:
626
649
  """Calculate cost in microdollars from token usage.
627
650
 
@@ -637,6 +660,11 @@ class CostCalculator:
637
660
  token counts, so they are subtracted before pricing;
638
661
  when ``False`` (Anthropic-protocol usage) all four
639
662
  categories are disjoint and billed as given.
663
+ reasoning_tokens: Optional reasoning-token breakdown. Providers
664
+ usually include this in ``output_tokens``; when they do not,
665
+ the larger of the two values is priced to avoid dropping
666
+ reasoning-only usage without double-counting the standard
667
+ shape.
640
668
 
641
669
  Returns:
642
670
  Tuple of (cost_microdollars, exactness_level). Exactness is
@@ -651,6 +679,8 @@ class CostCalculator:
651
679
  output_tokens = _normalize_token_count(output_tokens)
652
680
  cache_read_tokens = _normalize_token_count(cache_read_tokens)
653
681
  cache_write_tokens = _normalize_token_count(cache_write_tokens)
682
+ reasoning_tokens = _normalize_token_count(reasoning_tokens)
683
+ output_tokens = max(output_tokens, reasoning_tokens)
654
684
 
655
685
  if input_tokens_include_cache:
656
686
  # OpenAI-protocol ``prompt_tokens`` includes the cached
@@ -561,26 +561,37 @@ class CatalogService:
561
561
  )
562
562
  live_task_accounts.append(state.name)
563
563
 
564
- if static_tasks:
565
- await asyncio.gather(*static_tasks, return_exceptions=True)
566
- if live_tasks:
567
- fetch_results = await asyncio.gather(
568
- *live_tasks, return_exceptions=True
569
- )
570
- for account_name, fetch_result in zip(
571
- live_task_accounts, fetch_results, strict=True
572
- ):
573
- if isinstance(fetch_result, BaseException):
574
- logger.warning(
575
- "Catalog refresh for account %r preserved prior "
576
- "support after exception: %s",
577
- account_name,
578
- fetch_result,
579
- )
580
- outcomes_by_account[account_name] = AccountCatalogOutcome.FAILED
581
- continue
582
- outcome, _update = fetch_result
583
- outcomes_by_account[account_name] = outcome
564
+ try:
565
+ if static_tasks:
566
+ await asyncio.gather(*static_tasks, return_exceptions=True)
567
+ if live_tasks:
568
+ fetch_results = await asyncio.gather(
569
+ *live_tasks, return_exceptions=True
570
+ )
571
+ for account_name, fetch_result in zip(
572
+ live_task_accounts, fetch_results, strict=True
573
+ ):
574
+ if isinstance(fetch_result, BaseException):
575
+ logger.warning(
576
+ "Catalog refresh for account %r preserved prior "
577
+ "support after exception: %s",
578
+ account_name,
579
+ fetch_result,
580
+ )
581
+ outcomes_by_account[account_name] = (
582
+ AccountCatalogOutcome.FAILED
583
+ )
584
+ continue
585
+ outcome, _update = fetch_result
586
+ outcomes_by_account[account_name] = outcome
587
+ finally:
588
+ pending_tasks: list[asyncio.Task[Any]] = [
589
+ task for task in (*static_tasks, *live_tasks) if not task.done()
590
+ ]
591
+ for task in pending_tasks:
592
+ task.cancel()
593
+ if pending_tasks:
594
+ await asyncio.gather(*pending_tasks, return_exceptions=True)
584
595
 
585
596
  # Keep one outcome per enabled account. In particular, an
586
597
  # account skipped before task creation must not shift the result
@@ -29,6 +29,8 @@ from dataclasses import dataclass
29
29
  from enum import Enum
30
30
  from typing import TYPE_CHECKING, Final, cast
31
31
 
32
+ from eggpool.config_validation import ConfigInternalError
33
+
32
34
  if TYPE_CHECKING:
33
35
  from pathlib import Path
34
36
 
@@ -578,8 +580,11 @@ def _to_dict(value: object) -> dict[str, object]:
578
580
  try:
579
581
  dumped: object = dump()
580
582
  return dict(cast("Iterable[tuple[str, object]]", dumped))
581
- except Exception:
582
- return {}
583
+ except Exception as exc:
584
+ raise ConfigInternalError(
585
+ "configuration diff failed while serializing "
586
+ f"{type(value).__qualname__}: {type(exc).__qualname__}"
587
+ ) from exc
583
588
  if hasattr(value, "__dict__"):
584
589
  return {
585
590
  k: v