eggpool 0.4.2__tar.gz → 0.4.4__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 (288) hide show
  1. {eggpool-0.4.2 → eggpool-0.4.4}/AGENTS.md +2 -1
  2. {eggpool-0.4.2 → eggpool-0.4.4}/CHANGELOG.md +20 -0
  3. {eggpool-0.4.2 → eggpool-0.4.4}/PKG-INFO +9 -8
  4. {eggpool-0.4.2 → eggpool-0.4.4}/README.md +8 -7
  5. {eggpool-0.4.2 → eggpool-0.4.4}/architecture/README.md +2 -0
  6. eggpool-0.4.4/docs/transcoding.md +595 -0
  7. {eggpool-0.4.2 → eggpool-0.4.4}/pyproject.toml +1 -1
  8. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/api/proxy_request.py +37 -3
  9. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/app.py +30 -1
  10. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/catalog/cache.py +13 -6
  11. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/catalog/service.py +71 -0
  12. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/cli_full.py +23 -0
  13. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/dashboard/render.py +26 -13
  14. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/dashboard/static/dashboard.css +71 -3
  15. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/dashboard/static/dashboard.js +22 -0
  16. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/db/rollup_repository.py +59 -14
  17. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/model_info/repository.py +10 -0
  18. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/request/coordinator.py +19 -1
  19. eggpool-0.4.4/src/eggpool/routing/config.py +21 -0
  20. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/routing/router.py +82 -1
  21. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/stats/service.py +42 -19
  22. eggpool-0.4.4/src/eggpool/transcoder/__init__.py +67 -0
  23. eggpool-0.4.4/src/eggpool/transcoder/anthropic_to_openai.py +726 -0
  24. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/transcoder/context.py +12 -0
  25. eggpool-0.4.4/src/eggpool/transcoder/ids.py +91 -0
  26. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/transcoder/json_helpers.py +38 -0
  27. eggpool-0.4.4/src/eggpool/transcoder/openai_to_anthropic.py +854 -0
  28. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/transcoder/policy.py +61 -0
  29. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/transcoder/protocol.py +5 -0
  30. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/transcoder/streaming.py +416 -9
  31. {eggpool-0.4.2 → eggpool-0.4.4}/uv.lock +1 -1
  32. eggpool-0.4.2/docs/transcoding.md +0 -340
  33. eggpool-0.4.2/src/eggpool/transcoder/__init__.py +0 -28
  34. eggpool-0.4.2/src/eggpool/transcoder/anthropic_to_openai.py +0 -249
  35. eggpool-0.4.2/src/eggpool/transcoder/ids.py +0 -49
  36. eggpool-0.4.2/src/eggpool/transcoder/openai_to_anthropic.py +0 -285
  37. {eggpool-0.4.2 → eggpool-0.4.4}/.env.example +0 -0
  38. {eggpool-0.4.2 → eggpool-0.4.4}/.github/workflows/ci.yml +0 -0
  39. {eggpool-0.4.2 → eggpool-0.4.4}/.github/workflows/release.yml +0 -0
  40. {eggpool-0.4.2 → eggpool-0.4.4}/.gitignore +0 -0
  41. {eggpool-0.4.2 → eggpool-0.4.4}/LICENSE +0 -0
  42. {eggpool-0.4.2 → eggpool-0.4.4}/config-examples/claude-code.env +0 -0
  43. {eggpool-0.4.2 → eggpool-0.4.4}/config-examples/opencode.jsonc +0 -0
  44. {eggpool-0.4.2 → eggpool-0.4.4}/config.example.toml +0 -0
  45. {eggpool-0.4.2 → eggpool-0.4.4}/deploy/eggpool-logrotate.conf +0 -0
  46. {eggpool-0.4.2 → eggpool-0.4.4}/deploy/eggpool.service +0 -0
  47. {eggpool-0.4.2 → eggpool-0.4.4}/deploy/env.example +0 -0
  48. {eggpool-0.4.2 → eggpool-0.4.4}/docs/backup-restore.md +0 -0
  49. {eggpool-0.4.2 → eggpool-0.4.4}/docs/deployment.md +0 -0
  50. {eggpool-0.4.2 → eggpool-0.4.4}/docs/filesystem-layout.md +0 -0
  51. {eggpool-0.4.2 → eggpool-0.4.4}/docs/firewall.md +0 -0
  52. {eggpool-0.4.2 → eggpool-0.4.4}/docs/model-limits.md +0 -0
  53. {eggpool-0.4.2 → eggpool-0.4.4}/docs/network-diagnostics.md +0 -0
  54. {eggpool-0.4.2 → eggpool-0.4.4}/docs/providers.md +0 -0
  55. {eggpool-0.4.2 → eggpool-0.4.4}/docs/proxy.md +0 -0
  56. {eggpool-0.4.2 → eggpool-0.4.4}/docs/raspberry-pi.md +0 -0
  57. {eggpool-0.4.2 → eggpool-0.4.4}/scripts/__init__.py +0 -0
  58. {eggpool-0.4.2 → eggpool-0.4.4}/scripts/check_database.py +0 -0
  59. {eggpool-0.4.2 → eggpool-0.4.4}/scripts/install.sh +0 -0
  60. {eggpool-0.4.2 → eggpool-0.4.4}/scripts/install_prompt.py +0 -0
  61. {eggpool-0.4.2 → eggpool-0.4.4}/scripts/smoke_test.py +0 -0
  62. {eggpool-0.4.2 → eggpool-0.4.4}/scripts/validate_routing.py +0 -0
  63. {eggpool-0.4.2 → eggpool-0.4.4}/scripts/verify_upstream_auth.py +0 -0
  64. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/__init__.py +0 -0
  65. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/__main__.py +0 -0
  66. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/_share/.env.example +0 -0
  67. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/_share/config.example.toml +0 -0
  68. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/accounts/__init__.py +0 -0
  69. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/accounts/registry.py +0 -0
  70. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/accounts/state.py +0 -0
  71. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/api/__init__.py +0 -0
  72. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/api/backoff.py +0 -0
  73. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/api/chat_completions.py +0 -0
  74. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/api/errors.py +0 -0
  75. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/api/messages.py +0 -0
  76. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/api/model_info.py +0 -0
  77. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/api/models.py +0 -0
  78. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/api/network.py +0 -0
  79. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/api/runtime.py +0 -0
  80. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/api/stats.py +0 -0
  81. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/api/update.py +0 -0
  82. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/auth.py +0 -0
  83. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/background/__init__.py +0 -0
  84. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/background/backup.py +0 -0
  85. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/background/cleanup.py +0 -0
  86. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/catalog/__init__.py +0 -0
  87. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/catalog/catalog_resolvers.py +0 -0
  88. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/catalog/fetcher.py +0 -0
  89. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/catalog/limits.py +0 -0
  90. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/catalog/normalizer.py +0 -0
  91. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/catalog/pricing.py +0 -0
  92. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/catalog/pricing_aliases.py +0 -0
  93. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/catalog/pricing_resolver.py +0 -0
  94. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/catalog/protocols.py +0 -0
  95. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/cli.py +0 -0
  96. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/config.py +0 -0
  97. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/config_utils.py +0 -0
  98. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/constants.py +0 -0
  99. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/cost_recompute.py +0 -0
  100. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/dashboard/__init__.py +0 -0
  101. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/dashboard/_resources.py +0 -0
  102. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/dashboard/escape.py +0 -0
  103. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/dashboard/routes.py +0 -0
  104. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/dashboard/static/chart.umd.min.js +0 -0
  105. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/dashboard/static/favicon.svg +0 -0
  106. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/dashboard/theme.py +0 -0
  107. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/dashboard/themes/Booberry.toml +0 -0
  108. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/dashboard/themes/Catppuccin Latte.toml +0 -0
  109. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/dashboard/themes/Catppuccin Macchiato.toml +0 -0
  110. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/dashboard/themes/Catppuccin Mocha.toml +0 -0
  111. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/dashboard/themes/Cyber Red.toml +0 -0
  112. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/dashboard/themes/Cyberpunk.toml +0 -0
  113. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/dashboard/themes/Dark Green.toml +0 -0
  114. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/dashboard/themes/Discord (80_ Saturation).toml +0 -0
  115. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/dashboard/themes/Discord.toml +0 -0
  116. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/dashboard/themes/Dracula.toml +0 -0
  117. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/dashboard/themes/Ferra Light.toml +0 -0
  118. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/dashboard/themes/Flexor Dark.toml +0 -0
  119. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/dashboard/themes/Gruvbox.toml +0 -0
  120. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/dashboard/themes/Halcyon Dark.toml +0 -0
  121. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/dashboard/themes/IntelliJ Light.toml +0 -0
  122. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/dashboard/themes/Kanagawa.toml +0 -0
  123. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/dashboard/themes/Macaw Dark.toml +0 -0
  124. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/dashboard/themes/Macaw Light.toml +0 -0
  125. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/dashboard/themes/Matrix.toml +0 -0
  126. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/dashboard/themes/Noctis Lilac.toml +0 -0
  127. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/dashboard/themes/Nord.toml +0 -0
  128. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/dashboard/themes/Nostromo Terminal.toml +0 -0
  129. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/dashboard/themes/One Dark.toml +0 -0
  130. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/dashboard/themes/Oxocarbon.toml +0 -0
  131. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/dashboard/themes/Rose Pine Dawn.toml +0 -0
  132. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/dashboard/themes/Rose Pine Moon.toml +0 -0
  133. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/dashboard/themes/Rose Pine.toml +0 -0
  134. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/dashboard/themes/Solarized Dark.toml +0 -0
  135. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/dashboard/themes/Sonokai.toml +0 -0
  136. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/dashboard/themes/Tokyo Night Storm.toml +0 -0
  137. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/dashboard/themes/VESPER.toml +0 -0
  138. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/dashboard/themes/Zenburn.toml +0 -0
  139. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/dashboard/themes/acton.toml +0 -0
  140. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/dashboard/themes/bam.toml +0 -0
  141. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/dashboard/themes/base16-atelier-forest-light.toml +0 -0
  142. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/dashboard/themes/berlin.toml +0 -0
  143. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/dashboard/themes/black but with important highlights.toml +0 -0
  144. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/dashboard/themes/broc.toml +0 -0
  145. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/dashboard/themes/cork.toml +0 -0
  146. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/dashboard/themes/ferra.toml +0 -0
  147. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/dashboard/themes/forest.toml +0 -0
  148. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/dashboard/themes/lisbon.toml +0 -0
  149. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/dashboard/themes/midnight.toml +0 -0
  150. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/dashboard/themes/oslo.toml +0 -0
  151. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/dashboard/themes/plum.toml +0 -0
  152. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/dashboard/themes/portland.toml +0 -0
  153. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/dashboard/themes/sunset.toml +0 -0
  154. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/dashboard/themes/tofino.toml +0 -0
  155. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/dashboard/themes/vanimo.toml +0 -0
  156. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/dashboard/themes/vik.toml +0 -0
  157. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/db/__init__.py +0 -0
  158. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/db/connection.py +0 -0
  159. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/db/migrations.py +0 -0
  160. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/db/repositories.py +0 -0
  161. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/db/schema/0001_initial.sql +0 -0
  162. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/db/schema/0002_indexes.sql +0 -0
  163. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/db/schema/0003_request_attempts.sql +0 -0
  164. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/db/schema/0004_integration_hardening.sql +0 -0
  165. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/db/schema/0005_price_microdollars.sql +0 -0
  166. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/db/schema/0006_correct_price_microdollars.sql +0 -0
  167. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/db/schema/0007_price_cache_rates.sql +0 -0
  168. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/db/schema/0008_proxy_request_identity.sql +0 -0
  169. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/db/schema/0009_model_protocol_source.sql +0 -0
  170. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/db/schema/0010_health_probe.sql +0 -0
  171. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/db/schema/0011_model_resolution_status.sql +0 -0
  172. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/db/schema/0012_drop_reservations_estimated_microdollars.sql +0 -0
  173. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/db/schema/0013_request_attempts_account_id_index.sql +0 -0
  174. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/db/schema/0014_bandwidth_tracking.sql +0 -0
  175. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/db/schema/0015_multi_provider.sql +0 -0
  176. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/db/schema/0016_requests_provider_id.sql +0 -0
  177. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/db/schema/0017_price_snapshots_provider_id.sql +0 -0
  178. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/db/schema/0018_provider_pings.sql +0 -0
  179. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/db/schema/0019_client_ip.sql +0 -0
  180. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/db/schema/0020_performance_indexes.sql +0 -0
  181. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/db/schema/0021_provider_model_metadata.sql +0 -0
  182. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/db/schema/0022_dashboard_indexes.sql +0 -0
  183. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/db/schema/0023_deprecated_model_placeholder.sql +0 -0
  184. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/db/schema/0024_account_backoffs.sql +0 -0
  185. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/db/schema/0025_stale_request_index.sql +0 -0
  186. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/db/schema/0026_attempt_observability.sql +0 -0
  187. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/db/schema/0027_routing_decisions.sql +0 -0
  188. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/db/schema/0028_operational_events.sql +0 -0
  189. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/db/schema/0029_latency_phases.sql +0 -0
  190. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/db/schema/0030_model_pricing_aliases.sql +0 -0
  191. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/db/schema/0031_price_snapshot_provenance.sql +0 -0
  192. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/db/schema/0032_usage_rollups.sql +0 -0
  193. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/db/schema/0033_request_provider_local_cost.sql +0 -0
  194. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/db/schema/0034_transcoding_daily.sql +0 -0
  195. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/db/schema/0035_routing_decision_score_components.sql +0 -0
  196. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/db/schema/0036_model_info.sql +0 -0
  197. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/db/schema/0037_model_info_failure_count.sql +0 -0
  198. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/db/schema/0038_model_info_phase5.sql +0 -0
  199. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/db/schema/checksums.json +0 -0
  200. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/deploy/__init__.py +0 -0
  201. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/deploy_user.py +0 -0
  202. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/errors.py +0 -0
  203. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/fastcli.py +0 -0
  204. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/health/__init__.py +0 -0
  205. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/health/backoff.py +0 -0
  206. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/health/circuit_breaker.py +0 -0
  207. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/health/health_manager.py +0 -0
  208. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/integrations/__init__.py +0 -0
  209. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/integrations/aider.py +0 -0
  210. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/integrations/cline.py +0 -0
  211. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/integrations/codex.py +0 -0
  212. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/integrations/common.py +0 -0
  213. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/integrations/continue_dev.py +0 -0
  214. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/integrations/goose.py +0 -0
  215. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/integrations/kilo.py +0 -0
  216. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/integrations/opencode.py +0 -0
  217. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/integrations/openhands.py +0 -0
  218. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/integrations/qwen_code.py +0 -0
  219. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/integrations/roo_code.py +0 -0
  220. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/lifecycle/__init__.py +0 -0
  221. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/lifecycle/backup.py +0 -0
  222. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/lifecycle/uninstall.py +0 -0
  223. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/logging.py +0 -0
  224. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/metrics/__init__.py +0 -0
  225. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/metrics/buffer.py +0 -0
  226. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/model_info/__init__.py +0 -0
  227. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/model_info/dedup.py +0 -0
  228. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/model_info/identity.py +0 -0
  229. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/model_info/scheduler.py +0 -0
  230. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/model_info/service.py +0 -0
  231. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/model_info/sources/__init__.py +0 -0
  232. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/model_info/sources/artificial_analysis.py +0 -0
  233. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/model_info/sources/base.py +0 -0
  234. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/model_info/sources/huggingface.py +0 -0
  235. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/model_info/sources/openrouter.py +0 -0
  236. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/model_info/sources/provider_catalog.py +0 -0
  237. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/model_info/types.py +0 -0
  238. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/models/__init__.py +0 -0
  239. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/models/api.py +0 -0
  240. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/models/config.py +0 -0
  241. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/models/database.py +0 -0
  242. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/models/domain.py +0 -0
  243. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/onboard.py +0 -0
  244. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/providers/__init__.py +0 -0
  245. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/providers/_templates.toml +0 -0
  246. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/providers/auth.py +0 -0
  247. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/providers/client_pool.py +0 -0
  248. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/providers/connect.py +0 -0
  249. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/providers/contract.py +0 -0
  250. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/providers/dns_cache.py +0 -0
  251. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/providers/outbound.py +0 -0
  252. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/providers/pproxy_transport.py +0 -0
  253. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/proxy/__init__.py +0 -0
  254. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/proxy/client.py +0 -0
  255. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/proxy/cost_reporting.py +0 -0
  256. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/proxy/sse_observer.py +0 -0
  257. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/proxy/usage.py +0 -0
  258. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/py.typed +0 -0
  259. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/quota/__init__.py +0 -0
  260. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/quota/audit.py +0 -0
  261. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/quota/estimation.py +0 -0
  262. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/quota/reservation.py +0 -0
  263. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/quota/scorer.py +0 -0
  264. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/request/__init__.py +0 -0
  265. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/request/attempt_finalizer.py +0 -0
  266. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/request/body.py +0 -0
  267. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/request/finalizer.py +0 -0
  268. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/request/limits.py +0 -0
  269. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/retry/__init__.py +0 -0
  270. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/retry/classification.py +0 -0
  271. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/routing/__init__.py +0 -0
  272. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/routing/eligibility.py +0 -0
  273. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/routing/fairness.py +0 -0
  274. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/routing/provider.py +0 -0
  275. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/runtime.py +0 -0
  276. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/runtime_dispatch.py +0 -0
  277. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/runtime_metrics.py +0 -0
  278. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/runtime_paths.py +0 -0
  279. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/security/__init__.py +0 -0
  280. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/security/redaction.py +0 -0
  281. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/stats/__init__.py +0 -0
  282. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/stats/grouped_timeseries.py +0 -0
  283. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/stats/queries.py +0 -0
  284. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/toml_edit.py +0 -0
  285. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/transcoder/errors.py +0 -0
  286. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/transcoder/static_headers.py +0 -0
  287. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/transcoder/usage.py +0 -0
  288. {eggpool-0.4.2 → eggpool-0.4.4}/src/eggpool/update_checker.py +0 -0
@@ -69,7 +69,7 @@ CI sets `PYTHONHASHSEED=0` and `TZ=UTC`; reproduce locally for deterministic res
69
69
  - **Request lifecycle**: `RequestCoordinator` orchestrates endpoint → routing → persistence → dispatch → finalization. See `architecture/README.md` § Request Lifecycle.
70
70
  - **Multi-provider architecture**: provider-suffixed model IDs (`model-id/provider-id`), `ProviderClientPool`, `OutboundClientManager`. See `architecture/README.md` § Multi-Provider Architecture.
71
71
  - **Provider contracts**: `compose_provider_url()` is the single source of truth for upstream URLs. See `architecture/README.md` § Provider Contracts and § Provider Contract Rendering.
72
- - **Protocol transcoding**: transparent request/response format conversion between OpenAI and Anthropic protocols. Phase 2 body translation, Phase 3 streaming SSE translation, Phase 4 routing eligibility widening, and Phase 5 operator controls and docs are implemented in `src/eggpool/transcoder/` and `src/eggpool/request/coordinator.py`. See `architecture/README.md` § Protocol Transcoding.
72
+ - **Protocol transcoding**: transparent request/response format conversion between OpenAI and Anthropic protocols. Phase 2 body translation, Phase 3 streaming SSE translation, Phase 4 routing eligibility widening, Phase 5 operator controls and docs, and Phase 6.1 tool-use/tool_calls body and streaming translation (including `pause_turn` sentinel handling and `stream_options.include_usage` lifting) are implemented in `src/eggpool/transcoder/` and `src/eggpool/request/coordinator.py`. See `architecture/README.md` § Protocol Transcoding.
73
73
  - **Database invariants**: SQLite WAL, single-connection serialization, `async with db.transaction():` for all DML. See `architecture/README.md` § Database.
74
74
  - **Quota and routing**: tier-based routing via `routing_priority`, `QuotaFairScorer`, upstream-authoritative suppression, same-tier fairness rotor. See `architecture/README.md` § Quota and Routing.
75
75
  - **Error hierarchy**: `AggregatorError` → `UpstreamError` → specific subclasses. See `architecture/README.md` § Error Hierarchy.
@@ -92,6 +92,7 @@ CI sets `PYTHONHASHSEED=0` and `TZ=UTC`; reproduce locally for deterministic res
92
92
  - **Backoff persistence**: upstream-derived backoffs survive restarts via the `account_backoffs` table (`src/eggpool/db/schema/0024_account_backoffs.sql`). Local cost overruns must never be persisted as backoff rows.
93
93
  - **Synthetic 503 vs 502**: `ModelUnavailableError` (503) is reserved for genuine pre-dispatch unavailability. `UpstreamExhaustedError` (502) is raised when every candidate account was attempted and exhausted mid-request.
94
94
  - **Streaming finalizer shielding**: streaming `_build_stream_generator` finalization runs under `asyncio.shield(asyncio.wait_for(..., timeout=10))` so ASGI task cancellation cannot kill the finalizer while it holds the DB lock. Leaks that escape this path are caught by the periodic `stale_request_finalizer` background task (`app._finalize_stale_requests`, runs every 60s).
95
+ - **model_info FK seeding**: `model_info_canonical.model_id`, `model_info_observations.model_id`, `model_info_aliases.model_id`, and `model_info_overrides.model_id` all carry FKs to `models.model_id` (`src/eggpool/db/schema/0036_model_info.sql`, `0038_model_info_phase5.sql`). Catalog entries for unresolved static models can reach the model-info paths before `_persist_catalog` writes them to the `models` table (and pre-0036 installations may have FK-violating rows for any reason). The repository seeds a placeholder `models` row inside the same transaction as the sidecar write — see `upsert_canonical_with_model` and `upsert_observation` in `src/eggpool/model_info/repository.py`. `INSERT OR IGNORE` is mandatory: catalog-authored fields (display_name, protocol, capabilities) must never be clobbered by an observation-side seed.
95
96
  - **Routing-decision score components** (`RoutingDecisionTrace.score_components`) carry the per-account score breakdown on every persisted `routing_decisions` row (`score_components_json`, migration `0035`). The dashboard and `eggpool accounts explain` consume this directly; do not re-score from quota tables when only the diagnostic breakdown is needed.
96
97
  - **`_select_lock` publish ordering**: in `RequestCoordinator._select_and_persist_attempt()`, runtime publication (`Router.increment_active_request_count` + `QuotaEstimator.add_reservation`) must run INSIDE `_select_lock` AFTER the durable transaction commits but BEFORE the lock releases. The two contexts are explicit nested `async with` blocks (outer `_select_lock`, inner `_db.transaction()`). Key invariant: block placement (publication outside DB transaction body, inside `_select_lock`), not context-exit order.
97
98
  - **`db.transaction()` nesting semantics** (`src/eggpool/db/connection.py`): nesting is detected via SQLite's per-connection `conn.in_transaction`, NOT task identity. A shielded or `create_task` child entering `db.transaction()` while the parent's `BEGIN IMMEDIATE` is still open piggybacks on the outer's commit boundary; the child never re-issues `BEGIN` and never acquires `_connection_lock` (the outer holds it). This fixes the AB/BA deadlock where a shielded child would block waiting for `_connection_lock` while the parent awaited `asyncio.shield()`. Two ContextVars back the semantics: `_in_transaction_context: ContextVar[bool]` is set/reset by both nested and outermost paths and is inherited by shielded/child tasks so they can `execute_write`; `_transaction_owner: ContextVar[Task]` is set only by the outermost path and is used by `vacuum()` to refuse running when the *current* task holds the lock (deadlock guard). `vacuum()` and `_require_transaction_owner()` consult `_in_transaction_context`, not `_transaction_owner`, so shielded/child task writes piggyback cleanly. Regression coverage lives in `tests/unit/test_database.py::TestTransactionNestingAcrossTaskBoundaries`.
@@ -7,8 +7,27 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ Nothing yet.
11
+
12
+ ## [0.4.4] - 2026-06-30
13
+
14
+ ### Fixed
15
+
16
+ - **Provider-scoped transcodable protocol lookup**. `get_transcodable_protocols` and `count_eligible_accounts_for_protocol` on `ModelCatalogCache` now accept an optional `provider_id` filter so protocol inference and eligibility counting only consider accounts that belong to a specific provider. `_infer_upstream_protocol` in `proxy_request.py` and `RequestCoordinator._resolve_upstream_protocol` pass the selected account's `provider_id`, preventing cross-provider protocol leakage when the same model is served by providers with different protocol surfaces.
17
+ - **MiniMax token-plan contract warning**. `check-config` now emits a warning when a MiniMax provider entry targets `api.minimax.io` with `openai` protocol but does not use the `/anthropic` path — token-plan keys hitting the OpenAI `/v1/chat/completions` surface can return upstream `insufficient balance (1008)` errors.
18
+ - **`build_upstream_headers` now receives the upstream protocol**. The coordinator passes `context.upstream_protocol` to `build_upstream_headers` so protocol-specific auth header construction (e.g. `x-api-key` for Anthropic vs `Authorization: Bearer` for OpenAI) uses the correct upstream protocol instead of the client's protocol.
19
+
20
+ ## [0.4.3] - 2026-06-30
21
+
10
22
  ### Added
11
23
 
24
+ - **Phase 6.1: Tool-Use Transcoding**. Bidirectional OpenAI ↔ Anthropic tool calling translation for both streaming and non-streaming requests. The transcoder now lifts `tools`, `tool_choice`, assistant `tool_calls` history, `role: "tool"` history, and `tool_use` / `tool_result` content blocks across protocols instead of dropping them with a warning.
25
+ - **Body translation**: `OpenAIToAnthropic.encode_request` / `decode_response` and `AnthropicToOpenAI.encode_request` / `decode_response` translate `tools[]`, `tool_choice` (string ↔ object shapes), `parallel_tool_calls` (`true` omitted; `false` dropped with `parallel_tool_calls_collapsed` warning since Anthropic has no parallel-disable knob), assistant `tool_calls[]` ↔ `content[].tool_use`, and `role: "tool"` ↔ `content[].tool_result` blocks. `tools[].function.strict` is dropped (no Anthropic equivalent); `tools[].cache_control` is dropped on the Anthropic → OpenAI path.
26
+ - **Streaming tool_call delta translation**: `OpenAIToAnthropicStreaming` and `AnthropicToOpenAIStreaming` extend their state machines to track `content_block_start` / `input_json_delta` / `content_block_stop` triples and emit OpenAI `tool_calls` deltas in insertion order; the reverse direction buffers `tool_calls[*].function.arguments` chunks keyed on `tool_calls[*].index` and flushes Anthropic `tool_use` blocks on `finish_reason: "tool_calls"`. `flush()` emits a final delta for any slot whose `content_block_stop` never arrived so the client never hangs on a missing terminal.
27
+ - **Tool-call ID translation map**: a per-request `ToolCallIdMap` (on `TranscodeContext.id_map`) mints `call_<24 hex>` and `toolu_<24 hex>` ids so the two namespaces never collide. Both `generate_openai_id()` and `generate_anthropic_id()` produce 24 hex characters after the prefix. The map is per-`TranscodeContext`, so concurrent requests cannot collide.
28
+ - **`pause_turn` sentinel handling**: Anthropic's `pause_turn` `stop_reason` maps to `finish_reason: "tool_calls"` plus a synthetic tool_call entry `{"id": "call_pause_turn_<request_id>", "type": "function", "function": {"name": "__eggpool_pause_turn__", "arguments": "{}"}}`. OpenAI clients detect the sentinel by name and resume the turn with the same `tool_use_id`. A `pause_turn` loss warning is appended whenever the sentinel is synthesized.
29
+ - **`stream_options.include_usage` lifting**: `OpenAIToAnthropic.encode_request` lifts `stream_options.include_usage` onto `TranscodeContext.request_include_usage` before the streaming transcoder runs. The streaming transcoder reads the flag to decide whether to forward upstream usage chunks to the OpenAI client. The wrapper object itself is dropped with a `dropped_field` warning.
30
+ - **New loss-warning kinds**: `tool_call_id_translated`, `tool_call_id_changed`, `parallel_tool_calls_collapsed`, `malformed_tool_arguments`, `invalid_tool_choice`, `unsupported_tool_type`, `empty_tool_use_block`, `tool_result_image_dropped`, `tool_result_error_passthrough`, `cache_control_dropped`, `pause_turn`, `non_text_content_dropped`, `tool_result_inferred`. All are registered in `eggpool.transcoder.LOSS_WARNING_KINDS`. See `docs/transcoding.md` § Tool-Use Transcoding for the full catalogue.
12
31
  - **Catalog non-destructive refresh contract.** `ModelCatalogCache.update_from_account()` now accepts `authoritative: bool = False, allow_withdrawals: bool = False`; both flags default to `False` so a failed, empty, or partial refresh cannot silently de-pool a healthy account. Operators opt into the old "refresh is the source of truth" behavior with `ModelsConfig.catalog_withdrawal_policy` (`preserve_until_health` default, `confirmed_once`, `confirmed_twice`). Under the default policy, health is the only de-pooling mechanism. `CatalogService._fetch_and_process_account()` returns an `AccountCatalogOutcome` enum (`SUCCESS_AUTHORITATIVE`, `SUCCESS_PARTIAL`, `SUCCESS_EMPTY`, `FAILED`, `SKIPPED`) plus an `AccountCatalogUpdateResult` summary row so the cache layer can stay non-destructive while operators still get full audit trail. `_log_refresh_summary` emits a single INFO line enumerating per-outcome counts after every cycle so catalog uncertainty shows up without enabling debug logging.
13
32
  - **`eggpool accounts explain --gates`** (`src/eggpool/cli_full.py`) renders the per-account gate breakdown (config, credentials, health, circuit, provider id registry/catalog match, provider-supports-protocol, model support row/availability/freshness, provider-metadata-exists, protocol_match, local_quota_gate, final_eligible) as a compact text table. Backed by `Router.explain_account_eligibility(include_gates=True)` and `Router._collect_gate_status(...)`; the breakdown is informational — the canonical decision still comes from `_classify_eligibility`.
14
33
  - **Same-tier account fairness rotor**: deterministic round-robin rotation for effectively tied same-tier accounts prevents routing starvation. Configurable via `[routing] fairness_mode`, `fairness_epsilon`, and `fairness_scope`. Defaults to `round_robin` with `provider_model_protocol` scope. Fairness decisions are recorded in `routing_decisions.score_components_json` under the `fairness` key for operator diagnostics. The `fairness` payload now includes the `scope` field, and each `top_candidates` entry carries `rank_before_fairness`, `rank_after_fairness`, and `fairness_band_member` for full traceability. The `FairnessRotor` position map is capped at 4096 entries (`_ROTOR_HARD_CAP`); when reached the entire map is cleared and rotation restarts from 0.
@@ -36,6 +55,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
36
55
 
37
56
  ### Fixed
38
57
 
58
+ - **Dashboard rollup filters now match request-table filters.** When buffered analytics are enabled, account-filtered summaries, unknown-account summaries, flat model-filtered timeseries, and grouped model-filtered timeseries now push filters into `usage_rollups` queries instead of accidentally returning global rollup data or falling back because the display series key did not contain the filtered model id.
39
59
  - **Cross-account protocol poisoning from a partial sibling refresh.** `_provider_models` is keyed by `(model_id, provider_id)` and shared by every account that lists that provider (e.g. all `opencode-go-0001`/`-0002`/`-0003` accounts share one row per model on the `opencode-go` provider). Prior to this fix a single sibling's partial refresh — transient upstream parse error, unresolved family prefix, or a model whose protocol cannot be re-derived this cycle — produced a model entry with `protocol=None` and clobbered the previously-resolved protocol on the shared row, silently dropping every sibling account from routing even though `_account_support` still listed them. `ModelCatalogCache._preserve_resolved_protocol()` now applies a sibling-wins guard: a non-destructive `update_from_account` will keep the existing resolved protocol when the new entry's `protocol` is `None` and the prior row carried a resolved value. The destructive path (`authoritative=True AND allow_withdrawals=True`) intentionally skips the guard so operator-initiated withdrawals remain effective. `tests/unit/test_catalog_withdrawal_policy.py::test_partial_refresh_does_not_clobber_shared_provider_protocol{,multiple}` and `test_explicit_destructive_update_can_still_clear_protocol` pin the new contract.
40
60
 
41
61
  ## [0.3.5] - 2026-06-27
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: eggpool
3
- Version: 0.4.2
3
+ Version: 0.4.4
4
4
  Summary: A lightweight proxy that aggregates multiple LLM provider accounts behind one OpenAI-compatible endpoint
5
5
  Project-URL: Homepage, https://github.com/eggstack/eggpool
6
6
  Project-URL: Repository, https://github.com/eggstack/eggpool
@@ -148,8 +148,8 @@ When `[transcoder] enabled = true`, EggPool bridges OpenAI Chat Completions and
148
148
 
149
149
  What gets translated:
150
150
 
151
- - Request bodies (text-only in v1)
152
- - Streaming SSE events
151
+ - Request bodies (text + tool-use + vision + thinking + structured outputs)
152
+ - Streaming SSE events (including tool-call deltas and thinking deltas)
153
153
  - Non-retryable error envelopes
154
154
  - Usage and cost fields (preserved exactly as the upstream reported them)
155
155
 
@@ -158,12 +158,13 @@ What is dropped with a structured warning log:
158
158
  - OpenAI fields with no Anthropic equivalent (`logit_bias`, `presence_penalty`, `top_logprobs`, etc.)
159
159
  - Anthropic fields with no OpenAI equivalent (`top_k`, `cache_control`)
160
160
 
161
- What is **not** translated in v1 (lands in phase 6):
161
+ Phase 6 feature flags (`[transcoder.features]`) — all **off** by default:
162
162
 
163
- - Tool calls / function calling
164
- - Vision / image content
165
- - Extended thinking / reasoning
166
- - Structured outputs (`response_format` / `json_schema`)
163
+ - `tools` — bidirectional tool calling translation
164
+ - `vision` — image/document content parts
165
+ - `thinking` — extended thinking ↔ reasoning_content
166
+ - `structured_outputs` — `response_format` / `json_schema` coercion
167
+ - `anthropic_primitives` — `top_k`, `cache_control`, `context_management`, `container`, `mcp_servers`
167
168
 
168
169
  See [docs/transcoding.md](docs/transcoding.md) for the full translation table and known limitations.
169
170
 
@@ -104,8 +104,8 @@ When `[transcoder] enabled = true`, EggPool bridges OpenAI Chat Completions and
104
104
 
105
105
  What gets translated:
106
106
 
107
- - Request bodies (text-only in v1)
108
- - Streaming SSE events
107
+ - Request bodies (text + tool-use + vision + thinking + structured outputs)
108
+ - Streaming SSE events (including tool-call deltas and thinking deltas)
109
109
  - Non-retryable error envelopes
110
110
  - Usage and cost fields (preserved exactly as the upstream reported them)
111
111
 
@@ -114,12 +114,13 @@ What is dropped with a structured warning log:
114
114
  - OpenAI fields with no Anthropic equivalent (`logit_bias`, `presence_penalty`, `top_logprobs`, etc.)
115
115
  - Anthropic fields with no OpenAI equivalent (`top_k`, `cache_control`)
116
116
 
117
- What is **not** translated in v1 (lands in phase 6):
117
+ Phase 6 feature flags (`[transcoder.features]`) — all **off** by default:
118
118
 
119
- - Tool calls / function calling
120
- - Vision / image content
121
- - Extended thinking / reasoning
122
- - Structured outputs (`response_format` / `json_schema`)
119
+ - `tools` — bidirectional tool calling translation
120
+ - `vision` — image/document content parts
121
+ - `thinking` — extended thinking ↔ reasoning_content
122
+ - `structured_outputs` — `response_format` / `json_schema` coercion
123
+ - `anthropic_primitives` — `top_k`, `cache_control`, `context_management`, `container`, `mcp_servers`
123
124
 
124
125
  See [docs/transcoding.md](docs/transcoding.md) for the full translation table and known limitations.
125
126
 
@@ -174,6 +174,8 @@ scope (phases 4–6).
174
174
 
175
175
  **Phase 5 — Operator controls and docs**: the default `[transcoder]` config block is documented in `config.example.toml`. `eggpool stats transcoding` reports transcoded request counts and loss-warning summaries. The dashboard `/runtime` page includes a "Transcoding" card showing real-time counters. Structured INFO logs are emitted for every transcoded request and a startup line announces transcoding state. See `docs/transcoding.md` for the full operator guide.
176
176
 
177
+ **Phase 6.1 — Tool-use transcoding**: bidirectional tool calling translation in both directions for non-streaming and streaming requests. `OpenAIToAnthropic.encode_request` / `decode_response` and `AnthropicToOpenAI.encode_request` / `decode_response` translate `tools`, `tool_choice`, `parallel_tool_calls`, assistant `tool_calls` history, `role: "tool"` history, and `tool_use` / `tool_result` content blocks. A per-request `ToolCallIdMap` (on `TranscodeContext.id_map`) mints `call_<24 hex>` and `toolu_<24 hex>` ids so the two namespaces never collide. The streaming transcoders (`OpenAIToAnthropicStreaming`, `AnthropicToOpenAIStreaming`) extend their state machines to track `content_block_start` / `input_json_delta` / `content_block_stop` triples and emit OpenAI `tool_calls` deltas in insertion order; the reverse direction buffers OpenAI `tool_calls[*].function.arguments` chunks and flushes Anthropic `tool_use` blocks on `finish_reason: "tool_calls"`. Anthropic's `pause_turn` `stop_reason` maps to `finish_reason: "tool_calls"` plus a synthetic `__eggpool_pause_turn__` tool_call entry so OpenAI clients can detect pause-and-resume flows. `stream_options.include_usage` is lifted onto `TranscodeContext.request_include_usage` so the streaming transcoder can decide whether to forward upstream usage chunks. New loss-warning kinds (`tool_call_id_translated`, `tool_call_id_changed`, `parallel_tool_calls_collapsed`, `malformed_tool_arguments`, `invalid_tool_choice`, `unsupported_tool_type`, `empty_tool_use_block`, `tool_result_image_dropped`, `tool_result_error_passthrough`, `cache_control_dropped`, `pause_turn`, `non_text_content_dropped`, `tool_result_inferred`) are added to `LOSS_WARNING_KINDS`. See `docs/transcoding.md` § Tool-Use Transcoding and `plans/tooltranscoding.md` for the full design.
178
+
177
179
  Token counts are mapped between protocol-specific fields (e.g.,
178
180
  `input_tokens` → `prompt_tokens`, `cache_creation_input_tokens` →
179
181
  separate cache counters). Controlled by `[transcoder]` config.