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.
- {eggpool-0.7.0 → eggpool-0.7.2}/CHANGELOG.md +24 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/PKG-INFO +1 -1
- {eggpool-0.7.0 → eggpool-0.7.2}/architecture/deep-dive-database.md +5 -2
- {eggpool-0.7.0 → eggpool-0.7.2}/architecture/deep-dive-health.md +2 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/architecture/deep-dive-runtime.md +2 -1
- {eggpool-0.7.0 → eggpool-0.7.2}/architecture/deep-dive-security.md +6 -2
- {eggpool-0.7.0 → eggpool-0.7.2}/config.example.toml +11 -3
- {eggpool-0.7.0 → eggpool-0.7.2}/docs/deployment.md +10 -7
- {eggpool-0.7.0 → eggpool-0.7.2}/docs/thinking.md +12 -8
- {eggpool-0.7.0 → eggpool-0.7.2}/docs/transcoding.md +2 -2
- {eggpool-0.7.0 → eggpool-0.7.2}/pyproject.toml +1 -1
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/_share/config.example.toml +11 -5
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/accounts/state.py +3 -3
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/api/proxy_request.py +26 -9
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/catalog/capabilities.py +3 -2
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/catalog/models_dev.py +28 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/catalog/pricing.py +32 -2
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/catalog/service.py +31 -20
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/config_reload_policy.py +7 -2
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/config_utils.py +22 -14
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/control/reload_manager.py +9 -1
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/cost_recompute.py +28 -6
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/cost_repair.py +29 -6
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/connection.py +76 -28
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/fastcli.py +1 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/generation_factory.py +21 -11
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/health/circuit_breaker.py +2 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/health/health_manager.py +6 -7
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/integrations/common.py +7 -2
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/lifecycle/backup.py +3 -3
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/logging.py +14 -2
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/models/config.py +48 -15
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/onboard.py +2 -1
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/providers/_templates.toml +8 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/providers/outbound.py +4 -3
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/proxy/client.py +2 -2
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/proxy/normalized_usage.py +15 -13
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/proxy/sse_observer.py +3 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/proxy/usage.py +7 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/request/coordinator.py +67 -12
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/request/finalizer.py +12 -1
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/request/provider_bound_request.py +22 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/routing/eligibility.py +15 -2
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/routing/router.py +3 -3
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/runtime.py +2 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/runtime_manager.py +25 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/security/redaction.py +85 -2
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/transcoder/budget_resolver.py +43 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/transcoder/builtin_contracts.py +61 -17
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/transcoder/openai_to_anthropic.py +21 -1
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/transcoder/policy.py +13 -10
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/transcoder/provider_adaptation.py +0 -16
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/transcoder/streaming.py +36 -1
- {eggpool-0.7.0 → eggpool-0.7.2}/uv.lock +1 -1
- {eggpool-0.7.0 → eggpool-0.7.2}/.env.example +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/.github/workflows/ci.yml +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/.gitignore +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/AGENTS.md +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/LICENSE +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/README.md +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/architecture/README.md +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/architecture/deep-dive-background.md +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/architecture/deep-dive-catalog.md +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/architecture/deep-dive-control.md +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/architecture/deep-dive-core.md +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/architecture/deep-dive-dashboard.md +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/architecture/deep-dive-deployment.md +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/architecture/deep-dive-integrations.md +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/architecture/deep-dive-lifecycle.md +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/architecture/deep-dive-metrics.md +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/architecture/deep-dive-model-info.md +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/architecture/deep-dive-models.md +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/architecture/deep-dive-observability.md +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/architecture/deep-dive-providers.md +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/architecture/deep-dive-request-lifecycle.md +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/architecture/deep-dive-retry.md +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/architecture/deep-dive-routing.md +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/architecture/deep-dive-transcoder.md +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/architecture/overview.md +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/artifacts/plan-019-evidence.md +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/artifacts/plan-020-evidence.md +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/artifacts/plan-021-evidence.md +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/artifacts/plan-023-baseline.md +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/artifacts/plan-030-exact-head-evidence.md +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/config-examples/claude-code.env +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/config-examples/opencode.jsonc +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/config.sbc.example.toml +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/deploy/eggpool-logrotate.conf +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/deploy/eggpool.service +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/deploy/env.example +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/docs/agent-configuration.md +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/docs/api-reference.md +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/docs/backup-restore.md +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/docs/config-profiles.md +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/docs/filesystem-layout.md +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/docs/firewall.md +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/docs/live-config-rehash.md +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/docs/model-info-openrouter-debug.md +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/docs/model-limits.md +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/docs/network-diagnostics.md +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/docs/opencode-stream-stability.md +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/docs/operations/dispatch-stability.md +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/docs/providers.md +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/docs/proxy.md +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/docs/raspberry-pi.md +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/docs/releasing.md +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/docs/runbooks/database-recovery.md +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/docs/stateless-responses.md +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/scripts/__init__.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/scripts/admission_race_stress.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/scripts/bench_sqlite_writepath.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/scripts/check_database.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/scripts/debug_model_info_openrouter.sh +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/scripts/install.sh +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/scripts/install_prompt.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/scripts/repro_high_concurrency_streams.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/scripts/run_tests_with_timeout.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/scripts/smoke_test.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/scripts/test_model_info_identity.sh +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/scripts/validate_routing.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/scripts/verify_upstream_auth.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/__init__.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/__main__.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/_share/.env.example +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/_share/config.sbc.example.toml +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/accounts/__init__.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/accounts/registry.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/api/__init__.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/api/backoff.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/api/chat_completions.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/api/errors.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/api/messages.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/api/model_info.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/api/models.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/api/network.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/api/responses.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/api/runtime.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/api/stats.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/api/update.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/app.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/auth.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/background/__init__.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/background/backup.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/background/cleanup.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/background/maintenance.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/catalog/__init__.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/catalog/_time.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/catalog/cache.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/catalog/catalog_resolvers.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/catalog/fetcher.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/catalog/limits.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/catalog/normalizer.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/catalog/pricing_aliases.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/catalog/pricing_resolver.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/catalog/protocols.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/cli.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/cli_exit_codes.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/cli_full.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/cli_rehash_format.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/cli_rehash_helper.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/config.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/config_validation.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/constants.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/control/__init__.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/control/accepted_finalization.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/control/client.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/control/server.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/__init__.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/_resources.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/escape.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/render.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/routes.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/static/chart.umd.min.js +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/static/dashboard.css +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/static/dashboard.js +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/static/favicon.svg +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/telemetry.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/theme.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/Booberry.toml +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/Catppuccin Latte.toml +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/Catppuccin Macchiato.toml +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/Catppuccin Mocha.toml +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/Cyber Red.toml +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/Cyberpunk.toml +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/Dark Green.toml +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/Discord (80_ Saturation).toml +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/Discord.toml +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/Dracula.toml +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/Ferra Light.toml +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/Flexor Dark.toml +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/Gruvbox.toml +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/Halcyon Dark.toml +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/IntelliJ Light.toml +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/Kanagawa.toml +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/Macaw Dark.toml +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/Macaw Light.toml +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/Matrix.toml +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/Noctis Lilac.toml +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/Nord.toml +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/Nostromo Terminal.toml +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/One Dark.toml +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/Oxocarbon.toml +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/Rose Pine Dawn.toml +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/Rose Pine Moon.toml +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/Rose Pine.toml +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/Solarized Dark.toml +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/Sonokai.toml +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/Tokyo Night Storm.toml +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/VESPER.toml +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/Zenburn.toml +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/acton.toml +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/bam.toml +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/base16-atelier-forest-light.toml +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/berlin.toml +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/black but with important highlights.toml +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/broc.toml +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/cork.toml +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/ferra.toml +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/forest.toml +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/lisbon.toml +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/midnight.toml +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/oslo.toml +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/plum.toml +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/portland.toml +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/sunset.toml +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/tofino.toml +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/vanimo.toml +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/themes/vik.toml +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/dashboard/timeseries_buckets.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/__init__.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/migrations.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/repositories.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/rollup_repository.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0001_initial.sql +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0002_indexes.sql +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0003_request_attempts.sql +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0004_integration_hardening.sql +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0005_price_microdollars.sql +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0006_correct_price_microdollars.sql +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0007_price_cache_rates.sql +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0008_proxy_request_identity.sql +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0009_model_protocol_source.sql +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0010_health_probe.sql +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0011_model_resolution_status.sql +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0012_drop_reservations_estimated_microdollars.sql +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0013_request_attempts_account_id_index.sql +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0014_bandwidth_tracking.sql +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0015_multi_provider.sql +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0016_requests_provider_id.sql +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0017_price_snapshots_provider_id.sql +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0018_provider_pings.sql +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0019_client_ip.sql +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0020_performance_indexes.sql +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0021_provider_model_metadata.sql +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0022_dashboard_indexes.sql +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0023_deprecated_model_placeholder.sql +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0024_account_backoffs.sql +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0025_stale_request_index.sql +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0026_attempt_observability.sql +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0027_routing_decisions.sql +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0028_operational_events.sql +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0029_latency_phases.sql +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0030_model_pricing_aliases.sql +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0031_price_snapshot_provenance.sql +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0032_usage_rollups.sql +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0033_request_provider_local_cost.sql +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0034_transcoding_daily.sql +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0035_routing_decision_score_components.sql +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0036_model_info.sql +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0037_model_info_failure_count.sql +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0038_model_info_phase5.sql +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0039_thinking_observability.sql +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0040_cache_token_observability.sql +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0041_canonical_request_segmentation.sql +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0042_observe_mode_compression_accounting.sql +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0043_safe_suffix_compression.sql +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0044_compression_policy_resolver.sql +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0045_synthetic_cache_controls.sql +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0046_closed_loop_threshold_tuning.sql +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0047_normalize_rollup_bucket_start.sql +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0048_request_cost_repairs.sql +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0049_model_info_match_evidence.sql +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0050_routing_decisions_retention_index.sql +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0051_model_quarantine.sql +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0052_catalog_refresh_state.sql +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0053_remove_attempt_status_analytics_index.sql +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/0054_model_quarantine_null_identity.sql +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/db/schema/checksums.json +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/deploy/__init__.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/deploy_user.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/errors.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/event_loop_lag.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/failure/__init__.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/failure/applier.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/failure/classifier.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/failure/effects.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/failure/observation.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/failure/quarantine.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/failure/signal.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/failure/signal_extract.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/health/__init__.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/health/backoff.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/health/writable_probe.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/integrations/__init__.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/integrations/aider.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/integrations/cline.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/integrations/codex.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/integrations/continue_dev.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/integrations/goose.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/integrations/kilo.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/integrations/opencode.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/integrations/openhands.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/integrations/qwen_code.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/integrations/roo_code.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/jsonx.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/lifecycle/__init__.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/lifecycle/uninstall.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/metrics/__init__.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/metrics/buffer.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/metrics/failure_effects.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/metrics/thinking.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/model_info/__init__.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/model_info/dedup.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/model_info/identity.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/model_info/matching.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/model_info/normalization.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/model_info/presentation.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/model_info/repository.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/model_info/scheduler.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/model_info/service.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/model_info/sources/__init__.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/model_info/sources/artificial_analysis.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/model_info/sources/base.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/model_info/sources/huggingface.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/model_info/sources/openrouter.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/model_info/sources/provider_catalog.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/model_info/types.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/models/__init__.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/models/api.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/models/database.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/models/domain.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/observability/__init__.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/observability/routing_trace_writer.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/providers/__init__.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/providers/auth.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/providers/client_pool.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/providers/connect.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/providers/contract.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/providers/pproxy_transport.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/proxy/__init__.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/proxy/cost_reporting.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/proxy/sse.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/py.typed +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/quota/__init__.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/quota/audit.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/quota/estimation.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/quota/reservation.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/quota/scorer.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/reload_diagnostics.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/reload_transaction.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/request/__init__.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/request/attempt_finalizer.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/request/backoff_persistence.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/request/body.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/request/claim_lifecycle.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/request/failure_helpers.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/request/finalization_job.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/request/limits.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/request/parsed_payload.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/request/parsed_upstream_response.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/request/payload_utils.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/request/response_handoff.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/request/routing_helpers.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/request/routing_trace_guard.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/request/selection_claim_diagnostics.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/request/static_helpers.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/request/stream_completion.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/request/stream_diagnostics.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/request/terminal_status.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/request/thinking_adaptation.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/request/transform_pipeline.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/request/upstream_execution.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/request/upstream_helpers.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/request/usage_helpers.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/retry/__init__.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/retry/classification.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/routing/__init__.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/routing/config.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/routing/fairness.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/routing/provider.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/runtime_dispatch.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/runtime_metrics.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/runtime_paths.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/runtime_task_inventory.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/runtime_tasks.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/security/__init__.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/stats/__init__.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/stats/cache_metrics.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/stats/dashboard_explain.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/stats/grouped_timeseries.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/stats/queries.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/stats/segmentation.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/stats/service.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/stats/transcoding.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/toml_edit.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/transcoder/__init__.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/transcoder/anthropic_to_openai.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/transcoder/cache_stability.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/transcoder/cache_translation.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/transcoder/context.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/transcoder/errors.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/transcoder/ids.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/transcoder/json_helpers.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/transcoder/prepared.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/transcoder/protocol.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/transcoder/sensitive_media.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/transcoder/static_headers.py +0 -0
- {eggpool-0.7.0 → eggpool-0.7.2}/src/eggpool/transcoder/usage.py +0 -0
- {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.
|
|
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`.
|
|
299
|
-
|
|
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`
|
|
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
|
|
25
|
-
|
|
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 =
|
|
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 =
|
|
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
|
|
354
|
-
install uses one SQLite worker, low-wear analytics, loopback binding,
|
|
355
|
-
|
|
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
|
|
364
|
-
for
|
|
365
|
-
|
|
366
|
-
|
|
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.
|
|
31
|
+
## 2. Thinking Transcoding
|
|
32
32
|
|
|
33
|
-
Thinking transcoding is **
|
|
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 =
|
|
38
|
+
thinking = false
|
|
38
39
|
```
|
|
39
40
|
|
|
40
|
-
When `thinking = false
|
|
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:
|
|
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
|
|
506
|
+
**Cause:** Thinking transcoding was explicitly disabled with
|
|
507
|
+
`[transcoder.features].thinking = false`.
|
|
504
508
|
|
|
505
|
-
**Fix:**
|
|
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:**
|
|
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:
|
|
63
|
-
thinking =
|
|
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)
|
|
@@ -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 =
|
|
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 =
|
|
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
|
|
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
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
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, "
|
|
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=
|
|
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
|
-
#
|
|
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
|
-
|
|
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
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
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
|
-
|
|
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
|