eggpool 0.3.4__tar.gz → 0.3.6__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.3.6/AGENTS.md +103 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/CHANGELOG.md +6 -0
- eggpool-0.3.6/PKG-INFO +175 -0
- eggpool-0.3.6/README.md +131 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/architecture/README.md +13 -7
- {eggpool-0.3.4 → eggpool-0.3.6}/config.example.toml +7 -22
- {eggpool-0.3.4 → eggpool-0.3.6}/docs/providers.md +16 -15
- {eggpool-0.3.4 → eggpool-0.3.6}/pyproject.toml +1 -1
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/_share/config.example.toml +7 -22
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/accounts/state.py +1 -1
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/app.py +18 -19
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/cli_full.py +132 -64
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/dashboard/render.py +21 -10
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/dashboard/static/dashboard.css +34 -1
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/dashboard/static/dashboard.js +16 -1
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/db/rollup_repository.py +9 -3
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/health/backoff.py +1 -1
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/health/circuit_breaker.py +1 -1
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/health/health_manager.py +2 -2
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/integrations/opencode.py +15 -2
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/lifecycle/uninstall.py +39 -6
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/metrics/buffer.py +5 -5
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/providers/_templates.toml +10 -20
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/proxy/cost_reporting.py +1 -1
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/proxy/sse_observer.py +8 -5
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/proxy/usage.py +1 -1
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/quota/estimation.py +6 -6
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/quota/scorer.py +2 -2
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/request/attempt_finalizer.py +2 -2
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/request/coordinator.py +3 -3
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/request/finalizer.py +1 -1
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/routing/router.py +3 -3
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/runtime_dispatch.py +1 -1
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/runtime_metrics.py +19 -13
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/update_checker.py +66 -39
- {eggpool-0.3.4 → eggpool-0.3.6}/uv.lock +1 -1
- eggpool-0.3.4/AGENTS.md +0 -243
- eggpool-0.3.4/PKG-INFO +0 -915
- eggpool-0.3.4/README.md +0 -871
- {eggpool-0.3.4 → eggpool-0.3.6}/.env.example +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/.github/workflows/ci.yml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/.github/workflows/release.yml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/.gitignore +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/LICENSE +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/config-examples/claude-code.env +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/config-examples/opencode.jsonc +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/deploy/eggpool-logrotate.conf +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/deploy/eggpool.service +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/deploy/env.example +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/docs/backup-restore.md +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/docs/deployment.md +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/docs/filesystem-layout.md +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/docs/firewall.md +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/docs/model-limits.md +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/docs/network-diagnostics.md +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/docs/proxy.md +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/docs/raspberry-pi.md +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/scripts/__init__.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/scripts/check_database.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/scripts/install.sh +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/scripts/install_prompt.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/scripts/smoke_test.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/scripts/verify_upstream_auth.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/__init__.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/__main__.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/_share/.env.example +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/accounts/__init__.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/accounts/registry.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/api/__init__.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/api/backoff.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/api/chat_completions.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/api/errors.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/api/messages.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/api/models.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/api/network.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/api/proxy_request.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/api/runtime.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/api/stats.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/api/update.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/auth.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/background/__init__.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/background/backup.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/background/cleanup.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/catalog/__init__.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/catalog/cache.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/catalog/catalog_resolvers.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/catalog/fetcher.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/catalog/limits.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/catalog/normalizer.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/catalog/pricing.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/catalog/pricing_aliases.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/catalog/pricing_resolver.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/catalog/protocols.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/catalog/service.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/cli.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/config.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/constants.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/cost_recompute.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/dashboard/__init__.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/dashboard/_resources.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/dashboard/escape.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/dashboard/routes.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/dashboard/static/chart.umd.min.js +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/dashboard/static/favicon.svg +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/dashboard/theme.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/dashboard/themes/Booberry.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/dashboard/themes/Catppuccin Latte.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/dashboard/themes/Catppuccin Macchiato.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/dashboard/themes/Catppuccin Mocha.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/dashboard/themes/Cyber Red.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/dashboard/themes/Cyberpunk.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/dashboard/themes/Dark Green.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/dashboard/themes/Discord (80_ Saturation).toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/dashboard/themes/Discord.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/dashboard/themes/Dracula.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/dashboard/themes/Ferra Light.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/dashboard/themes/Flexor Dark.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/dashboard/themes/Gruvbox.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/dashboard/themes/Halcyon Dark.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/dashboard/themes/IntelliJ Light.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/dashboard/themes/Kanagawa.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/dashboard/themes/Macaw Dark.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/dashboard/themes/Macaw Light.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/dashboard/themes/Matrix.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/dashboard/themes/Noctis Lilac.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/dashboard/themes/Nord.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/dashboard/themes/Nostromo Terminal.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/dashboard/themes/One Dark.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/dashboard/themes/Oxocarbon.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/dashboard/themes/Rose Pine Dawn.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/dashboard/themes/Rose Pine Moon.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/dashboard/themes/Rose Pine.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/dashboard/themes/Solarized Dark.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/dashboard/themes/Sonokai.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/dashboard/themes/Tokyo Night Storm.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/dashboard/themes/VESPER.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/dashboard/themes/Zenburn.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/dashboard/themes/acton.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/dashboard/themes/bam.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/dashboard/themes/base16-atelier-forest-light.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/dashboard/themes/berlin.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/dashboard/themes/black but with important highlights.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/dashboard/themes/broc.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/dashboard/themes/cork.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/dashboard/themes/ferra.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/dashboard/themes/forest.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/dashboard/themes/lisbon.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/dashboard/themes/midnight.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/dashboard/themes/oslo.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/dashboard/themes/plum.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/dashboard/themes/portland.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/dashboard/themes/sunset.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/dashboard/themes/tofino.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/dashboard/themes/vanimo.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/dashboard/themes/vik.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/db/__init__.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/db/connection.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/db/migrations.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/db/repositories.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/db/schema/0001_initial.sql +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/db/schema/0002_indexes.sql +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/db/schema/0003_request_attempts.sql +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/db/schema/0004_integration_hardening.sql +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/db/schema/0005_price_microdollars.sql +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/db/schema/0006_correct_price_microdollars.sql +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/db/schema/0007_price_cache_rates.sql +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/db/schema/0008_proxy_request_identity.sql +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/db/schema/0009_model_protocol_source.sql +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/db/schema/0010_health_probe.sql +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/db/schema/0011_model_resolution_status.sql +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/db/schema/0012_drop_reservations_estimated_microdollars.sql +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/db/schema/0013_request_attempts_account_id_index.sql +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/db/schema/0014_bandwidth_tracking.sql +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/db/schema/0015_multi_provider.sql +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/db/schema/0016_requests_provider_id.sql +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/db/schema/0017_price_snapshots_provider_id.sql +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/db/schema/0018_provider_pings.sql +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/db/schema/0019_client_ip.sql +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/db/schema/0020_performance_indexes.sql +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/db/schema/0021_provider_model_metadata.sql +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/db/schema/0022_dashboard_indexes.sql +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/db/schema/0023_deprecated_model_placeholder.sql +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/db/schema/0024_account_backoffs.sql +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/db/schema/0025_stale_request_index.sql +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/db/schema/0026_attempt_observability.sql +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/db/schema/0027_routing_decisions.sql +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/db/schema/0028_operational_events.sql +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/db/schema/0029_latency_phases.sql +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/db/schema/0030_model_pricing_aliases.sql +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/db/schema/0031_price_snapshot_provenance.sql +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/db/schema/0032_usage_rollups.sql +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/db/schema/0033_request_provider_local_cost.sql +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/db/schema/checksums.json +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/deploy/__init__.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/deploy_user.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/errors.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/fastcli.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/health/__init__.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/integrations/__init__.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/lifecycle/__init__.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/lifecycle/backup.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/logging.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/metrics/__init__.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/models/__init__.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/models/api.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/models/config.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/models/database.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/models/domain.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/onboard.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/providers/__init__.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/providers/auth.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/providers/client_pool.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/providers/connect.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/providers/contract.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/providers/dns_cache.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/providers/outbound.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/providers/pproxy_transport.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/proxy/__init__.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/proxy/client.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/py.typed +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/quota/__init__.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/quota/audit.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/quota/reservation.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/request/__init__.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/request/body.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/request/limits.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/retry/__init__.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/retry/classification.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/routing/__init__.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/routing/eligibility.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/routing/provider.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/runtime.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/runtime_paths.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/security/__init__.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/security/redaction.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/stats/__init__.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/stats/grouped_timeseries.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/stats/queries.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/stats/service.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.6}/src/eggpool/toml_edit.py +0 -0
eggpool-0.3.6/AGENTS.md
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# AGENTS.md
|
|
2
|
+
|
|
3
|
+
## Skills
|
|
4
|
+
|
|
5
|
+
Project-specific skills are in `.opencode/skills/`:
|
|
6
|
+
|
|
7
|
+
- `architecture` — design principles, request lifecycle, invariants, error hierarchy
|
|
8
|
+
- `deployment` — production deployment, systemd, operational scripts
|
|
9
|
+
- `development` — linting, testing, pre-commit checks, code style
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
- Package manager: **uv** (not pip). Install deps: `uv sync --extra dev`
|
|
14
|
+
- Entry point: `src/eggpool/cli.py` → `eggpool` console script
|
|
15
|
+
- Config: `config.toml` + `.env` for API keys
|
|
16
|
+
|
|
17
|
+
## Pre-commit Checks (run before every commit)
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
uv run ruff format --check src/ tests/ scripts/
|
|
21
|
+
uv run ruff check src/ tests/ scripts/
|
|
22
|
+
uv run pyright src/ scripts/
|
|
23
|
+
uv run pytest
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
All four must pass with zero errors.
|
|
27
|
+
|
|
28
|
+
## Code Style
|
|
29
|
+
|
|
30
|
+
- Python 3.11+ with `from __future__ import annotations` in ALL files
|
|
31
|
+
- Type hints on all function signatures and return types
|
|
32
|
+
- Ruff: E, F, W, I, N, UP, B, A, SIM, TCH rules
|
|
33
|
+
- Pyright strict mode — covers `src/` AND `scripts/` (not tests)
|
|
34
|
+
- Line length: 88 chars
|
|
35
|
+
- Use `NoReturn` for functions that never return (e.g., `sys.exit`)
|
|
36
|
+
|
|
37
|
+
## Testing
|
|
38
|
+
|
|
39
|
+
- pytest with `asyncio_mode = "strict"` (from `pyproject.toml`)
|
|
40
|
+
- respx for HTTPX upstream mocking
|
|
41
|
+
- Tests in `tests/unit/`, `tests/integration/`, `tests/contract/`
|
|
42
|
+
- Provider contract tests: `uv run pytest tests/unit/test_contract.py tests/unit/test_contract_urls.py -v`
|
|
43
|
+
|
|
44
|
+
## File Organization
|
|
45
|
+
|
|
46
|
+
- Source: `src/eggpool/`
|
|
47
|
+
- Tests: `tests/` (mirrors src structure)
|
|
48
|
+
- Config: `config.example.toml`, `.env.example`
|
|
49
|
+
- DB schema: `src/eggpool/db/schema/`
|
|
50
|
+
- Scripts: `scripts/` (operational, also type-checked by pyright)
|
|
51
|
+
- Deployment: `deploy/`
|
|
52
|
+
|
|
53
|
+
## Architecture Index
|
|
54
|
+
|
|
55
|
+
> Full design details are in `architecture/README.md` and the `architecture` skill.
|
|
56
|
+
|
|
57
|
+
- **Request lifecycle**: `RequestCoordinator` orchestrates endpoint → routing → persistence → dispatch → finalization. See `architecture/README.md` § Request Lifecycle.
|
|
58
|
+
- **Multi-provider architecture**: provider-suffixed model IDs (`model-id/provider-id`), `ProviderClientPool`, `OutboundClientManager`. See `architecture/README.md` § Multi-Provider Architecture.
|
|
59
|
+
- **Provider contracts**: `compose_provider_url()` is the single source of truth for upstream URLs. See `architecture/README.md` § Provider Contracts and § Provider Contract Rendering.
|
|
60
|
+
- **Database invariants**: SQLite WAL, single-connection serialization, `async with db.transaction():` for all DML. See `architecture/README.md` § Database.
|
|
61
|
+
- **Quota and routing**: tier-based routing via `routing_priority`, `QuotaFairScorer`, upstream-authoritative suppression. See `architecture/README.md` § Quota and Routing.
|
|
62
|
+
- **Error hierarchy**: `AggregatorError` → `UpstreamError` → specific subclasses. See `architecture/README.md` § Error Hierarchy.
|
|
63
|
+
- **Process model**: supervisor + Granian worker, PID file lifecycle, daemon mode. See `architecture/README.md` § Daemon Mode.
|
|
64
|
+
- **Dashboard**: server-rendered HTML, 12 pages, Chart.js v4, grouped timeseries, CSS tooltips. See `architecture` skill § Dashboard.
|
|
65
|
+
- **Observability**: attempt analytics, routing analytics, latency phases, pending health, runtime metrics. See `architecture` skill § Runtime Observability.
|
|
66
|
+
|
|
67
|
+
## Gotchas
|
|
68
|
+
|
|
69
|
+
- Configuration changes require a service restart; live reload is intentionally not supported
|
|
70
|
+
- No pre-commit hooks are configured in this repo; CI runs ruff, pyright, and pytest via GitHub Actions
|
|
71
|
+
- **`static_models` is the source of truth for provider-specific protocol** — `FAMILY_PROTOCOLS` (`src/eggpool/catalog/protocols.py`) is a global fallback that applies when no explicit override exists. Providers like `minimax-cn` that serve MiniMax models on the OpenAI-compatible surface **must** ship `[[providers.<id>.static_models]]` rows with `protocol = "openai"`, otherwise the live `/v1/models` fetch resolves `MiniMax-M*` via the `minimax-` family prefix to `anthropic` and the provider protocol constraint check (`src/eggpool/catalog/service.py:533-545`) clears the protocol to `None`, producing `ModelUnavailableError("Model 'MiniMax-M3' has unresolved protocol")` instead of the more obvious 400 `ProtocolMismatchError`. Static seeds with `protocol_source = "static_config"` survive the live merge via `ModelCatalogCache._preserve_static_fields` (`src/eggpool/catalog/cache.py:146-187`). The same model ID may also be flipped on a per-provider basis via `[providers.<id>.model_overrides."<model-id>"].protocol = "openai"` for operator overrides that go beyond the bundled template.
|
|
72
|
+
- **Upstream-authoritative suppression**: local quota estimates are advisory by default (`local_quota_mode = "score_only"`). Above-capacity accounts stay eligible; only upstream-observed failures (429/402/5xx/auth) and explicit operator disablement suppress routing. Switch to `hard_cap` only as an opt-in escape hatch.
|
|
73
|
+
- **Backoff persistence**: upstream-derived backoffs survive restarts via the `account_backoffs` table (`src/eggpool/db/schema/0024_account_backoffs.sql`). Hydration runs at startup after account sync, best-effort (never blocks boot). Local cost overruns must never be persisted as backoff rows.
|
|
74
|
+
- **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. Single-account upstream errors pass through to the client rather than becoming synthetic 503s.
|
|
75
|
+
- **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) which force-finalizes any request that has been `pending` longer than `upstream.read_timeout_s`.
|
|
76
|
+
- **Startup crash recovery**: `_crash_recovery` runs at every startup and recovers ALL pending requests and ALL active reservations with no time threshold. A process restart is a definitive boundary, so leaked state from the previous process is unconditionally cleaned up. If `Crash recovery: marked N stale requests` appears in logs, the safety net caught leaks from the previous run.
|
|
77
|
+
- **Pricing pipeline**: prices flow TOML override → upstream metadata → external catalog (OpenRouter / OpenCode Zen via the alias registry). Cost precedence: `provider_reported > derived/partial/exact > estimated (reservation fallback) > unknown (zero)`. See the `architecture` skill for the full pricing resolution details.
|
|
78
|
+
- **`eggpool stats recompute-costs [--dry-run|--apply] [--limit N]`**: walks the requests table in started_at DESC order, recomputes cost from the current price snapshots, and reports / applies the change. Default is `--dry-run`. Use after upgrading the resolver to fix inflated totals on cached-token-heavy models (e.g. MiMo 2.5). Implemented in `src/eggpool/cost_recompute.py` and reuses the live `CostCalculator` so the new values match what the finalizer would write today.
|
|
79
|
+
- **Automatic backups**: in-process daily backups run by default under the `automatic_backup` supervised task (`src/eggpool/background/backup.py`). Uses stdlib `sqlite3.Connection.backup()` for consistent snapshots, atomic archive publication (write-to-temp + rename), and count-based retention (default 14). Controlled by `[backup]` config section. The `eggpool deploy backup-cron` path remains available for operators who prefer external scheduling.
|
|
80
|
+
- **DNS cache**: `OutboundClientManager` and `ProviderClientPool` both integrate a `DnsNetworkBackend` that caches resolved DNS entries in memory. The cache reduces connection latency for repeated requests to the same upstream hosts. Controlled by `[network.dns_cache]` config. When a proxy is configured for an account, that account's client uses the proxy transport instead of the cached backend.
|
|
81
|
+
|
|
82
|
+
## Error Handling
|
|
83
|
+
|
|
84
|
+
Use the hierarchy in `errors.py`. Chain exceptions with `raise ... from err` or `raise ... from None`.
|
|
85
|
+
|
|
86
|
+
- `AggregatorError` → `ConfigError`, `DatabaseError`, `ProxyError`
|
|
87
|
+
- `UpstreamError` (has `status_code`) → `TemporaryUpstreamError`, `TransientUpstreamError`, `AuthenticationError`, `QuotaExhaustedError`, `RateLimitError` (has `retry_after`), `ModelUnavailableError`
|
|
88
|
+
- `ModelNotFoundError` (has `model_id`), `NoEligibleAccountError`, `CatalogUnavailableError`, `AuthenticationUnavailableError`, `UpstreamExhaustedError`, `AccountSuspendedError`, `RequestTooLargeError`, `ContextLimitExceededError`
|
|
89
|
+
|
|
90
|
+
## Fast-Path CLI
|
|
91
|
+
|
|
92
|
+
- `src/eggpool/cli.py` is a tiny bootstrap (74 lines)
|
|
93
|
+
- `main()` calls `eggpool.fastcli.maybe_run_fast_command()` first; recognized fast commands (`croncheck`, `ensure-running`) are dispatched without importing Click
|
|
94
|
+
- **Do not add transitive imports to `runtime_paths` or `fastcli`** — they are stdlib-only and must stay lightweight for the Raspberry Pi watchdog contract
|
|
95
|
+
- Unrecognized commands fall through to `eggpool.cli_full`, which holds the heavy Click CLI
|
|
96
|
+
- Public symbols (`cli`, helpers used by tests) are lazily forwarded from `cli_full` via PEP 562 `__getattr__` — so `from eggpool.cli import cli` and existing test imports still work without loading the full graph
|
|
97
|
+
- See `plans/lightweight-cli-watchdog.md` for the full design
|
|
98
|
+
|
|
99
|
+
## Git Workflow
|
|
100
|
+
|
|
101
|
+
- Branch: `main`
|
|
102
|
+
- Commit messages: concise, imperative mood
|
|
103
|
+
- Never commit secrets, API keys, or `.env` files
|
|
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.3.5] - 2026-06-27
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- **README rewrite**: condensed the README from 870 lines to a concise, user-focused overview with quick start, CLI reference, configuration summary, API endpoints, and a documentation table linking to dedicated docs. Detailed content (deployment, providers, proxy, backup, model limits, Raspberry Pi, firewall, filesystem layout, network diagnostics) now lives in `docs/` and is linked from the README where it makes sense.
|
|
15
|
+
|
|
10
16
|
## [0.3.4] - 2026-06-27
|
|
11
17
|
|
|
12
18
|
### Fixed
|
eggpool-0.3.6/PKG-INFO
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: eggpool
|
|
3
|
+
Version: 0.3.6
|
|
4
|
+
Summary: A lightweight proxy that aggregates multiple LLM provider accounts behind one OpenAI-compatible endpoint
|
|
5
|
+
Project-URL: Homepage, https://github.com/eggstack/eggpool
|
|
6
|
+
Project-URL: Repository, https://github.com/eggstack/eggpool
|
|
7
|
+
Project-URL: Issues, https://github.com/eggstack/eggpool/issues
|
|
8
|
+
Project-URL: Documentation, https://github.com/eggstack/eggpool/tree/main/docs
|
|
9
|
+
Project-URL: Changelog, https://github.com/eggstack/eggpool/blob/main/CHANGELOG.md
|
|
10
|
+
Author-email: David Bowman <dbowman91@proton.me>
|
|
11
|
+
License-Expression: MIT
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
Keywords: aggregation,anthropic,llm,multi-account,openai,opencode,proxy,router
|
|
14
|
+
Classifier: Development Status :: 4 - Beta
|
|
15
|
+
Classifier: Framework :: AsyncIO
|
|
16
|
+
Classifier: Framework :: FastAPI
|
|
17
|
+
Classifier: Intended Audience :: System Administrators
|
|
18
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
19
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
20
|
+
Classifier: Programming Language :: Python :: 3
|
|
21
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
24
|
+
Classifier: Topic :: System :: Monitoring
|
|
25
|
+
Classifier: Typing :: Typed
|
|
26
|
+
Requires-Python: >=3.11
|
|
27
|
+
Requires-Dist: aiosqlite
|
|
28
|
+
Requires-Dist: click
|
|
29
|
+
Requires-Dist: fastapi
|
|
30
|
+
Requires-Dist: granian[pname]
|
|
31
|
+
Requires-Dist: httpx
|
|
32
|
+
Requires-Dist: pproxy>=2.7.9
|
|
33
|
+
Requires-Dist: pydantic>=2.0
|
|
34
|
+
Provides-Extra: dev
|
|
35
|
+
Requires-Dist: coverage[toml]; extra == 'dev'
|
|
36
|
+
Requires-Dist: pre-commit; extra == 'dev'
|
|
37
|
+
Requires-Dist: pyright; extra == 'dev'
|
|
38
|
+
Requires-Dist: pytest; extra == 'dev'
|
|
39
|
+
Requires-Dist: pytest-asyncio; extra == 'dev'
|
|
40
|
+
Requires-Dist: pytest-cov; extra == 'dev'
|
|
41
|
+
Requires-Dist: respx; extra == 'dev'
|
|
42
|
+
Requires-Dist: ruff; extra == 'dev'
|
|
43
|
+
Description-Content-Type: text/markdown
|
|
44
|
+
|
|
45
|
+
[](https://pypi.org/project/eggpool/)
|
|
46
|
+
[](https://www.python.org/)
|
|
47
|
+
[](LICENSE)
|
|
48
|
+
[](https://github.com/eggstack/eggpool/actions/workflows/ci.yml)
|
|
49
|
+
|
|
50
|
+
# EggPool
|
|
51
|
+
|
|
52
|
+
A lightweight, LAN-hosted proxy that aggregates multiple AI provider accounts behind one OpenAI/Anthropic-compatible endpoint.
|
|
53
|
+
|
|
54
|
+
## Features
|
|
55
|
+
|
|
56
|
+
- Proxies model requests across multiple providers and accounts behind a single endpoint
|
|
57
|
+
- Supports OpenAI-compatible and Anthropic-compatible upstream request paths
|
|
58
|
+
- Dynamically discovers available models; routes by quota utilization
|
|
59
|
+
- Per-account outbound proxy support ([pproxy](https://pypi.org/project/pproxy/) — SOCKS5, HTTP, Shadowsocks)
|
|
60
|
+
- Tracks requests, tokens, latency, errors, and estimated costs in SQLite
|
|
61
|
+
- Multi-page dashboard with 50+ themes, reliability, routing, and runtime views
|
|
62
|
+
- Designed for lightweight deployments (Raspberry Pi, SBCs)
|
|
63
|
+
|
|
64
|
+
## Quick Start
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
# Install (one-shot)
|
|
68
|
+
curl -fsSL https://raw.githubusercontent.com/eggstack/eggpool/main/scripts/install.sh | bash
|
|
69
|
+
|
|
70
|
+
# Interactive onboarding — connect providers, validate, start
|
|
71
|
+
eggpool onboard
|
|
72
|
+
|
|
73
|
+
# Install as a systemd service
|
|
74
|
+
sudo env "PATH=$PATH" "$(command -v eggpool)" deploy systemd --install
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
See [Deployment](docs/deployment.md) for alternative install methods (pipx, manual, production) and the full deployment guide.
|
|
78
|
+
|
|
79
|
+
## CLI Reference
|
|
80
|
+
|
|
81
|
+
| Command | Description |
|
|
82
|
+
|---------|-------------|
|
|
83
|
+
| `eggpool serve` | Start the proxy server (`--daemon` to detach) |
|
|
84
|
+
| `eggpool onboard` | Interactive onboarding wizard |
|
|
85
|
+
| `eggpool connect` | Add a provider account interactively |
|
|
86
|
+
| `eggpool connect list` | List supported providers |
|
|
87
|
+
| `eggpool check-config` | Validate configuration |
|
|
88
|
+
| `eggpool migrate` | Run database migrations |
|
|
89
|
+
| `eggpool rehash` | Restart to apply config changes |
|
|
90
|
+
| `eggpool stop` | Stop the running server |
|
|
91
|
+
| `eggpool models refresh` | Refresh the model catalog |
|
|
92
|
+
| `eggpool accounts status` | Show configured account status |
|
|
93
|
+
| `eggpool runtime-status` | Print runtime health summary |
|
|
94
|
+
| `eggpool backup` | Create a timestamped backup |
|
|
95
|
+
| `eggpool recover` | Restore from a backup archive |
|
|
96
|
+
| `eggpool deploy systemd` | Install/manage systemd service |
|
|
97
|
+
| `eggpool deploy cron` | Install watchdog cron (non-systemd) |
|
|
98
|
+
| `eggpool update` | Check for and install updates |
|
|
99
|
+
|
|
100
|
+
All commands accept `--config /path/to/config.toml`. Config resolution: `--config` > `$EGGPOOL_CONFIG` > `~/.config/eggpool/config.toml` > `./config.toml`.
|
|
101
|
+
|
|
102
|
+
Full command reference: [docs/deployment.md](docs/deployment.md#deploy-commands-reference)
|
|
103
|
+
|
|
104
|
+
## Configuration
|
|
105
|
+
|
|
106
|
+
Configuration lives in a single TOML file. API keys are loaded from environment variables or `.env`.
|
|
107
|
+
|
|
108
|
+
```toml
|
|
109
|
+
# Example provider configuration
|
|
110
|
+
[providers.opencode-go]
|
|
111
|
+
id = "opencode-go"
|
|
112
|
+
base_url = "https://opencode.ai/zen/go/v1"
|
|
113
|
+
protocols = ["openai", "anthropic"]
|
|
114
|
+
|
|
115
|
+
[[providers.opencode-go.accounts]]
|
|
116
|
+
name = "personal"
|
|
117
|
+
api_key = "sk-your-opencode-go-key"
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Use `eggpool connect` for interactive provider setup. See [docs/providers.md](docs/providers.md) for the full provider catalog, configuration details, and troubleshooting.
|
|
121
|
+
|
|
122
|
+
### Key Config Sections
|
|
123
|
+
|
|
124
|
+
| Section | Purpose |
|
|
125
|
+
|---------|---------|
|
|
126
|
+
| `[server]` | Bind address, port (default 11300), API key, logging, threads |
|
|
127
|
+
| `[upstream]` | Upstream API base URL, timeouts, connection pool |
|
|
128
|
+
| `[database]` | SQLite path, WAL mode |
|
|
129
|
+
| `[models]` | Catalog refresh, exposure mode, model collapse |
|
|
130
|
+
| `[routing]` | Routing strategy, retry limits, quota mode |
|
|
131
|
+
| `[dashboard]` | Dashboard toggle, theme, refresh interval |
|
|
132
|
+
| `[providers.*]` | Provider configs with accounts and routing priority |
|
|
133
|
+
| `[network]` | Outbound transport, DNS cache |
|
|
134
|
+
|
|
135
|
+
Full config reference: [`config.example.toml`](config.example.toml) | [docs/providers.md](docs/providers.md)
|
|
136
|
+
|
|
137
|
+
## API Endpoints
|
|
138
|
+
|
|
139
|
+
| Method | Path | Description |
|
|
140
|
+
|--------|------|-------------|
|
|
141
|
+
| `GET` | `/v1/models` | List available models |
|
|
142
|
+
| `POST` | `/v1/chat/completions` | OpenAI-compatible chat completions |
|
|
143
|
+
| `POST` | `/v1/messages` | Anthropic-compatible messages |
|
|
144
|
+
| `GET` | `/v1/healthz` | Liveness check |
|
|
145
|
+
| `GET` | `/v1/readyz` | Readiness check |
|
|
146
|
+
|
|
147
|
+
When `[dashboard].enabled = true`, a multi-page dashboard is served at `/` with request stats, latency metrics, provider health, and more. Stats API available under `/api/stats/*`.
|
|
148
|
+
|
|
149
|
+
## Documentation
|
|
150
|
+
|
|
151
|
+
| Topic | Link |
|
|
152
|
+
|-------|------|
|
|
153
|
+
| Deployment (install, systemd, production) | [docs/deployment.md](docs/deployment.md) |
|
|
154
|
+
| Provider catalog & configuration | [docs/providers.md](docs/providers.md) |
|
|
155
|
+
| Backup & restore | [docs/backup-restore.md](docs/backup-restore.md) |
|
|
156
|
+
| Per-account outbound proxy | [docs/proxy.md](docs/proxy.md) |
|
|
157
|
+
| Model context limits | [docs/model-limits.md](docs/model-limits.md) |
|
|
158
|
+
| Raspberry Pi setup | [docs/raspberry-pi.md](docs/raspberry-pi.md) |
|
|
159
|
+
| Firewall configuration | [docs/firewall.md](docs/firewall.md) |
|
|
160
|
+
| Filesystem layout | [docs/filesystem-layout.md](docs/filesystem-layout.md) |
|
|
161
|
+
| Network & DNS diagnostics | [docs/network-diagnostics.md](docs/network-diagnostics.md) |
|
|
162
|
+
|
|
163
|
+
## Development
|
|
164
|
+
|
|
165
|
+
```bash
|
|
166
|
+
uv sync --extra dev
|
|
167
|
+
uv run ruff check src/ tests/ scripts/
|
|
168
|
+
uv run ruff format src/ tests/ scripts/
|
|
169
|
+
uv run pyright src/ scripts/
|
|
170
|
+
uv run pytest
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
## License
|
|
174
|
+
|
|
175
|
+
MIT
|
eggpool-0.3.6/README.md
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
[](https://pypi.org/project/eggpool/)
|
|
2
|
+
[](https://www.python.org/)
|
|
3
|
+
[](LICENSE)
|
|
4
|
+
[](https://github.com/eggstack/eggpool/actions/workflows/ci.yml)
|
|
5
|
+
|
|
6
|
+
# EggPool
|
|
7
|
+
|
|
8
|
+
A lightweight, LAN-hosted proxy that aggregates multiple AI provider accounts behind one OpenAI/Anthropic-compatible endpoint.
|
|
9
|
+
|
|
10
|
+
## Features
|
|
11
|
+
|
|
12
|
+
- Proxies model requests across multiple providers and accounts behind a single endpoint
|
|
13
|
+
- Supports OpenAI-compatible and Anthropic-compatible upstream request paths
|
|
14
|
+
- Dynamically discovers available models; routes by quota utilization
|
|
15
|
+
- Per-account outbound proxy support ([pproxy](https://pypi.org/project/pproxy/) — SOCKS5, HTTP, Shadowsocks)
|
|
16
|
+
- Tracks requests, tokens, latency, errors, and estimated costs in SQLite
|
|
17
|
+
- Multi-page dashboard with 50+ themes, reliability, routing, and runtime views
|
|
18
|
+
- Designed for lightweight deployments (Raspberry Pi, SBCs)
|
|
19
|
+
|
|
20
|
+
## Quick Start
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
# Install (one-shot)
|
|
24
|
+
curl -fsSL https://raw.githubusercontent.com/eggstack/eggpool/main/scripts/install.sh | bash
|
|
25
|
+
|
|
26
|
+
# Interactive onboarding — connect providers, validate, start
|
|
27
|
+
eggpool onboard
|
|
28
|
+
|
|
29
|
+
# Install as a systemd service
|
|
30
|
+
sudo env "PATH=$PATH" "$(command -v eggpool)" deploy systemd --install
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
See [Deployment](docs/deployment.md) for alternative install methods (pipx, manual, production) and the full deployment guide.
|
|
34
|
+
|
|
35
|
+
## CLI Reference
|
|
36
|
+
|
|
37
|
+
| Command | Description |
|
|
38
|
+
|---------|-------------|
|
|
39
|
+
| `eggpool serve` | Start the proxy server (`--daemon` to detach) |
|
|
40
|
+
| `eggpool onboard` | Interactive onboarding wizard |
|
|
41
|
+
| `eggpool connect` | Add a provider account interactively |
|
|
42
|
+
| `eggpool connect list` | List supported providers |
|
|
43
|
+
| `eggpool check-config` | Validate configuration |
|
|
44
|
+
| `eggpool migrate` | Run database migrations |
|
|
45
|
+
| `eggpool rehash` | Restart to apply config changes |
|
|
46
|
+
| `eggpool stop` | Stop the running server |
|
|
47
|
+
| `eggpool models refresh` | Refresh the model catalog |
|
|
48
|
+
| `eggpool accounts status` | Show configured account status |
|
|
49
|
+
| `eggpool runtime-status` | Print runtime health summary |
|
|
50
|
+
| `eggpool backup` | Create a timestamped backup |
|
|
51
|
+
| `eggpool recover` | Restore from a backup archive |
|
|
52
|
+
| `eggpool deploy systemd` | Install/manage systemd service |
|
|
53
|
+
| `eggpool deploy cron` | Install watchdog cron (non-systemd) |
|
|
54
|
+
| `eggpool update` | Check for and install updates |
|
|
55
|
+
|
|
56
|
+
All commands accept `--config /path/to/config.toml`. Config resolution: `--config` > `$EGGPOOL_CONFIG` > `~/.config/eggpool/config.toml` > `./config.toml`.
|
|
57
|
+
|
|
58
|
+
Full command reference: [docs/deployment.md](docs/deployment.md#deploy-commands-reference)
|
|
59
|
+
|
|
60
|
+
## Configuration
|
|
61
|
+
|
|
62
|
+
Configuration lives in a single TOML file. API keys are loaded from environment variables or `.env`.
|
|
63
|
+
|
|
64
|
+
```toml
|
|
65
|
+
# Example provider configuration
|
|
66
|
+
[providers.opencode-go]
|
|
67
|
+
id = "opencode-go"
|
|
68
|
+
base_url = "https://opencode.ai/zen/go/v1"
|
|
69
|
+
protocols = ["openai", "anthropic"]
|
|
70
|
+
|
|
71
|
+
[[providers.opencode-go.accounts]]
|
|
72
|
+
name = "personal"
|
|
73
|
+
api_key = "sk-your-opencode-go-key"
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Use `eggpool connect` for interactive provider setup. See [docs/providers.md](docs/providers.md) for the full provider catalog, configuration details, and troubleshooting.
|
|
77
|
+
|
|
78
|
+
### Key Config Sections
|
|
79
|
+
|
|
80
|
+
| Section | Purpose |
|
|
81
|
+
|---------|---------|
|
|
82
|
+
| `[server]` | Bind address, port (default 11300), API key, logging, threads |
|
|
83
|
+
| `[upstream]` | Upstream API base URL, timeouts, connection pool |
|
|
84
|
+
| `[database]` | SQLite path, WAL mode |
|
|
85
|
+
| `[models]` | Catalog refresh, exposure mode, model collapse |
|
|
86
|
+
| `[routing]` | Routing strategy, retry limits, quota mode |
|
|
87
|
+
| `[dashboard]` | Dashboard toggle, theme, refresh interval |
|
|
88
|
+
| `[providers.*]` | Provider configs with accounts and routing priority |
|
|
89
|
+
| `[network]` | Outbound transport, DNS cache |
|
|
90
|
+
|
|
91
|
+
Full config reference: [`config.example.toml`](config.example.toml) | [docs/providers.md](docs/providers.md)
|
|
92
|
+
|
|
93
|
+
## API Endpoints
|
|
94
|
+
|
|
95
|
+
| Method | Path | Description |
|
|
96
|
+
|--------|------|-------------|
|
|
97
|
+
| `GET` | `/v1/models` | List available models |
|
|
98
|
+
| `POST` | `/v1/chat/completions` | OpenAI-compatible chat completions |
|
|
99
|
+
| `POST` | `/v1/messages` | Anthropic-compatible messages |
|
|
100
|
+
| `GET` | `/v1/healthz` | Liveness check |
|
|
101
|
+
| `GET` | `/v1/readyz` | Readiness check |
|
|
102
|
+
|
|
103
|
+
When `[dashboard].enabled = true`, a multi-page dashboard is served at `/` with request stats, latency metrics, provider health, and more. Stats API available under `/api/stats/*`.
|
|
104
|
+
|
|
105
|
+
## Documentation
|
|
106
|
+
|
|
107
|
+
| Topic | Link |
|
|
108
|
+
|-------|------|
|
|
109
|
+
| Deployment (install, systemd, production) | [docs/deployment.md](docs/deployment.md) |
|
|
110
|
+
| Provider catalog & configuration | [docs/providers.md](docs/providers.md) |
|
|
111
|
+
| Backup & restore | [docs/backup-restore.md](docs/backup-restore.md) |
|
|
112
|
+
| Per-account outbound proxy | [docs/proxy.md](docs/proxy.md) |
|
|
113
|
+
| Model context limits | [docs/model-limits.md](docs/model-limits.md) |
|
|
114
|
+
| Raspberry Pi setup | [docs/raspberry-pi.md](docs/raspberry-pi.md) |
|
|
115
|
+
| Firewall configuration | [docs/firewall.md](docs/firewall.md) |
|
|
116
|
+
| Filesystem layout | [docs/filesystem-layout.md](docs/filesystem-layout.md) |
|
|
117
|
+
| Network & DNS diagnostics | [docs/network-diagnostics.md](docs/network-diagnostics.md) |
|
|
118
|
+
|
|
119
|
+
## Development
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
uv sync --extra dev
|
|
123
|
+
uv run ruff check src/ tests/ scripts/
|
|
124
|
+
uv run ruff format src/ tests/ scripts/
|
|
125
|
+
uv run pyright src/ scripts/
|
|
126
|
+
uv run pytest
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## License
|
|
130
|
+
|
|
131
|
+
MIT
|
|
@@ -70,7 +70,7 @@ EggPool supports 27+ upstream providers (OpenCode Go, OpenAI, Anthropic, Groq, D
|
|
|
70
70
|
|
|
71
71
|
### MiniMax templates
|
|
72
72
|
|
|
73
|
-
- **`minimax`** — international host `https://api.minimax.io/anthropic`. Anthropic-compatible transport (key sent as `x-api-key` plus `anthropic-version: 2023-06-01`). Model listing
|
|
73
|
+
- **`minimax`** — international host `https://api.minimax.io/anthropic`. Anthropic-compatible transport (key sent as `x-api-key` plus `anthropic-version: 2023-06-01`). Model listing is exclusively live via `/v1/models`; no static seeds are shipped because the provider already accepts the anthropic value produced by the family mapping. The Anthropic model-list normalizer auto-detects MiniMax's hybrid response shape. Default for keys from `minimax.io`.
|
|
74
74
|
- **`minimax-cn`** — China host `https://api.minimaxi.com/v1` with the same OpenAI paths as a standard provider. Live verification is required because the China endpoint family has not been confirmed against EggPool's Anthropic-compatible transport.
|
|
75
75
|
|
|
76
76
|
The stored key must be the raw token; EggPool prepends the configured auth scheme automatically. An optional `[providers.<id>.verify]` block lets the verifier know which model to probe when neither `--openai-model` nor `--anthropic-model` is passed on the CLI.
|
|
@@ -457,9 +457,15 @@ Production (`eggpool deploy systemd --install --production`):
|
|
|
457
457
|
|
|
458
458
|
## Background Tasks
|
|
459
459
|
|
|
460
|
-
`TaskSupervisor` (`background/__init__.py`) manages long-running loops with restart-on-failure and exponential backoff:
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
460
|
+
`TaskSupervisor` (`background/__init__.py`) manages long-running loops with restart-on-failure and exponential backoff. All tasks are registered in `app.py` during lifespan setup:
|
|
461
|
+
|
|
462
|
+
| Task | Condition | Description |
|
|
463
|
+
|------|-----------|-------------|
|
|
464
|
+
| `catalog_refresh` | `refresh_interval_s > 0` | Periodic upstream model catalog refresh |
|
|
465
|
+
| `retention_cleanup` | Always | Hourly cleanup of old requests, events, pings, rollups, and expired reservations |
|
|
466
|
+
| `checkpoint` | Always | Periodic SQLite WAL checkpoint (every 4h) |
|
|
467
|
+
| `usage_window_refresh` | Always | Refreshes persisted usage windows every 60s |
|
|
468
|
+
| `stale_request_finalizer` | Always | Safety net for leaked streaming requests (every 60s) |
|
|
469
|
+
| `metrics_flush` | `write_mode != "immediate"` | Buffered analytics flush to `usage_rollups` |
|
|
470
|
+
| `update_checker` | Always | Periodic PyPI update check (default 24h); see `update_checker.py` |
|
|
471
|
+
| `automatic_backup` | `backup.enabled` | In-process SQLite backup with count-based retention; see `background/backup.py` |
|
|
@@ -503,28 +503,13 @@ persist_redacted_error_detail = false
|
|
|
503
503
|
# path = "/v1/models"
|
|
504
504
|
# required = true
|
|
505
505
|
#
|
|
506
|
-
# #
|
|
507
|
-
# #
|
|
508
|
-
#
|
|
509
|
-
#
|
|
510
|
-
#
|
|
511
|
-
#
|
|
512
|
-
#
|
|
513
|
-
# supports_vision = false
|
|
514
|
-
#
|
|
515
|
-
# [[providers.minimax.static_models]]
|
|
516
|
-
# id = "MiniMax-M2.7"
|
|
517
|
-
# display_name = "MiniMax-M2.7"
|
|
518
|
-
# protocol = "anthropic"
|
|
519
|
-
# supports_tools = true
|
|
520
|
-
# supports_vision = false
|
|
521
|
-
#
|
|
522
|
-
# [[providers.minimax.static_models]]
|
|
523
|
-
# id = "MiniMax-M2.5"
|
|
524
|
-
# display_name = "MiniMax-M2.5"
|
|
525
|
-
# protocol = "anthropic"
|
|
526
|
-
# supports_tools = true
|
|
527
|
-
# supports_vision = false
|
|
506
|
+
# # Live discovery is the source of truth for the international
|
|
507
|
+
# # endpoint; no static model seeds are needed because the provider
|
|
508
|
+
# # already accepts the anthropic value produced by the family mapping.
|
|
509
|
+
# # Add [[providers.minimax.static_models]] rows here only if you want
|
|
510
|
+
# # a fallback when /v1/models is unreachable, and mirror the live
|
|
511
|
+
# # id / display_name exactly (e.g. "MiniMax-M2.7-highspeed" id with
|
|
512
|
+
# # display_name "MiniMax-M2.7-Highspeed").
|
|
528
513
|
|
|
529
514
|
# MiniMax China — live verification required before production use.
|
|
530
515
|
# The China endpoint family and auth shape have not been confirmed against
|
|
@@ -152,23 +152,16 @@ value = "2023-06-01"
|
|
|
152
152
|
method = "GET"
|
|
153
153
|
path = "/v1/models"
|
|
154
154
|
required = true
|
|
155
|
-
|
|
156
|
-
# Static model seeds are used as fallback when live discovery is
|
|
157
|
-
# unavailable. The listed models match MiniMax's documented IDs.
|
|
158
|
-
[[providers.minimax.static_models]]
|
|
159
|
-
id = "MiniMax-M3"
|
|
160
|
-
display_name = "MiniMax-M3"
|
|
161
|
-
protocol = "anthropic"
|
|
162
|
-
supports_tools = true
|
|
163
|
-
supports_vision = false
|
|
164
155
|
```
|
|
165
156
|
|
|
166
157
|
The composed upstream URL is
|
|
167
158
|
`https://api.minimax.io/anthropic/v1/messages`, sent with
|
|
168
159
|
`x-api-key: <token-plan-key>` and `anthropic-version: 2023-06-01`.
|
|
169
160
|
Live model discovery fetches the catalog from the MiniMax `/v1/models`
|
|
170
|
-
endpoint using the documented Anthropic-compatible listing.
|
|
171
|
-
seeds
|
|
161
|
+
endpoint using the documented Anthropic-compatible listing. No static
|
|
162
|
+
seeds ship with the international template; the provider already accepts
|
|
163
|
+
the anthropic value produced by the family mapping, and live discovery is
|
|
164
|
+
the source of truth.
|
|
172
165
|
|
|
173
166
|
`minimax-cn` (China console) is intentionally configured as plain
|
|
174
167
|
OpenAI-compatible in the bundled template because the China endpoint
|
|
@@ -227,18 +220,26 @@ account type, implement it as an opt-in alternate template (for example
|
|
|
227
220
|
When a provider's live model discovery is unavailable (e.g. the endpoint
|
|
228
221
|
does not expose a `/models` listing, or discovery is temporarily down),
|
|
229
222
|
static model seeds act as a fallback. Declare them under
|
|
230
|
-
`[[providers.<id>.static_models]]`.
|
|
231
|
-
|
|
223
|
+
`[[providers.<id>.static_models]]`. The international MiniMax template
|
|
224
|
+
ships without static seeds because live discovery is the source of
|
|
225
|
+
truth; providers like `minimax-cn` that need to pin a specific protocol
|
|
226
|
+
(where the family mapping would otherwise clear it) ship seeds as part
|
|
227
|
+
of the bundled template:
|
|
232
228
|
|
|
233
229
|
```toml
|
|
234
|
-
[[providers.minimax.static_models]]
|
|
230
|
+
[[providers.minimax-cn.static_models]]
|
|
235
231
|
id = "MiniMax-M3"
|
|
236
232
|
display_name = "MiniMax-M3"
|
|
237
|
-
protocol = "
|
|
233
|
+
protocol = "openai"
|
|
238
234
|
supports_tools = true
|
|
239
235
|
supports_vision = false
|
|
240
236
|
```
|
|
241
237
|
|
|
238
|
+
Always mirror the live `id` / `display_name` exactly when adding fallback
|
|
239
|
+
seeds so the cache stays consistent with dynamic loading (for example,
|
|
240
|
+
`id = "MiniMax-M2.7-highspeed"` ships from the live endpoint as
|
|
241
|
+
`display_name = "MiniMax-M2.7-Highspeed"`).
|
|
242
|
+
|
|
242
243
|
Static rows participate in the same protocol, limit, and exposure
|
|
243
244
|
machinery as live-discovered entries. When the provider's
|
|
244
245
|
`models_endpoint.method = "DISABLED"` (or when live refresh returns no
|
|
@@ -463,28 +463,13 @@ persist_redacted_error_detail = false
|
|
|
463
463
|
# path = "/v1/models"
|
|
464
464
|
# required = true
|
|
465
465
|
#
|
|
466
|
-
# #
|
|
467
|
-
# #
|
|
468
|
-
#
|
|
469
|
-
#
|
|
470
|
-
#
|
|
471
|
-
#
|
|
472
|
-
#
|
|
473
|
-
# supports_vision = false
|
|
474
|
-
#
|
|
475
|
-
# [[providers.minimax.static_models]]
|
|
476
|
-
# id = "MiniMax-M2.7"
|
|
477
|
-
# display_name = "MiniMax-M2.7"
|
|
478
|
-
# protocol = "anthropic"
|
|
479
|
-
# supports_tools = true
|
|
480
|
-
# supports_vision = false
|
|
481
|
-
#
|
|
482
|
-
# [[providers.minimax.static_models]]
|
|
483
|
-
# id = "MiniMax-M2.5"
|
|
484
|
-
# display_name = "MiniMax-M2.5"
|
|
485
|
-
# protocol = "anthropic"
|
|
486
|
-
# supports_tools = true
|
|
487
|
-
# supports_vision = false
|
|
466
|
+
# # Live discovery is the source of truth for the international
|
|
467
|
+
# # endpoint; no static model seeds are needed because the provider
|
|
468
|
+
# # already accepts the anthropic value produced by the family mapping.
|
|
469
|
+
# # Add [[providers.minimax.static_models]] rows here only if you want
|
|
470
|
+
# # a fallback when /v1/models is unreachable, and mirror the live
|
|
471
|
+
# # id / display_name exactly (e.g. "MiniMax-M2.7-highspeed" id with
|
|
472
|
+
# # display_name "MiniMax-M2.7-Highspeed").
|
|
488
473
|
|
|
489
474
|
# MiniMax China — live verification required before production use.
|
|
490
475
|
# The China endpoint family and auth shape have not been confirmed against
|