eggpool 0.3.4__tar.gz → 0.3.5__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.5/AGENTS.md +103 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/CHANGELOG.md +6 -0
- eggpool-0.3.5/PKG-INFO +175 -0
- eggpool-0.3.5/README.md +131 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/architecture/README.md +12 -6
- {eggpool-0.3.4 → eggpool-0.3.5}/pyproject.toml +1 -1
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/accounts/state.py +1 -1
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/app.py +18 -19
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/cli_full.py +132 -64
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/dashboard/render.py +18 -7
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/dashboard/static/dashboard.css +13 -1
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/dashboard/static/dashboard.js +16 -1
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/db/rollup_repository.py +9 -3
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/health/backoff.py +1 -1
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/health/circuit_breaker.py +1 -1
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/health/health_manager.py +2 -2
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/lifecycle/uninstall.py +39 -6
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/metrics/buffer.py +5 -5
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/proxy/cost_reporting.py +1 -1
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/proxy/sse_observer.py +8 -5
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/proxy/usage.py +1 -1
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/quota/estimation.py +6 -6
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/quota/scorer.py +2 -2
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/request/attempt_finalizer.py +2 -2
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/request/coordinator.py +3 -3
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/request/finalizer.py +1 -1
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/routing/router.py +3 -3
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/runtime_dispatch.py +1 -1
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/runtime_metrics.py +19 -13
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/update_checker.py +66 -39
- {eggpool-0.3.4 → eggpool-0.3.5}/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.5}/.env.example +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/.github/workflows/ci.yml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/.github/workflows/release.yml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/.gitignore +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/LICENSE +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/config-examples/claude-code.env +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/config-examples/opencode.jsonc +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/config.example.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/deploy/eggpool-logrotate.conf +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/deploy/eggpool.service +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/deploy/env.example +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/docs/backup-restore.md +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/docs/deployment.md +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/docs/filesystem-layout.md +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/docs/firewall.md +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/docs/model-limits.md +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/docs/network-diagnostics.md +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/docs/providers.md +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/docs/proxy.md +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/docs/raspberry-pi.md +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/scripts/__init__.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/scripts/check_database.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/scripts/install.sh +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/scripts/install_prompt.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/scripts/smoke_test.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/scripts/verify_upstream_auth.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/__init__.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/__main__.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/_share/.env.example +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/_share/config.example.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/accounts/__init__.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/accounts/registry.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/api/__init__.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/api/backoff.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/api/chat_completions.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/api/errors.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/api/messages.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/api/models.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/api/network.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/api/proxy_request.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/api/runtime.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/api/stats.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/api/update.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/auth.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/background/__init__.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/background/backup.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/background/cleanup.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/catalog/__init__.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/catalog/cache.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/catalog/catalog_resolvers.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/catalog/fetcher.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/catalog/limits.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/catalog/normalizer.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/catalog/pricing.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/catalog/pricing_aliases.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/catalog/pricing_resolver.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/catalog/protocols.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/catalog/service.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/cli.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/config.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/constants.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/cost_recompute.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/dashboard/__init__.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/dashboard/_resources.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/dashboard/escape.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/dashboard/routes.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/dashboard/static/chart.umd.min.js +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/dashboard/static/favicon.svg +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/dashboard/theme.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/dashboard/themes/Booberry.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/dashboard/themes/Catppuccin Latte.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/dashboard/themes/Catppuccin Macchiato.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/dashboard/themes/Catppuccin Mocha.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/dashboard/themes/Cyber Red.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/dashboard/themes/Cyberpunk.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/dashboard/themes/Dark Green.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/dashboard/themes/Discord (80_ Saturation).toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/dashboard/themes/Discord.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/dashboard/themes/Dracula.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/dashboard/themes/Ferra Light.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/dashboard/themes/Flexor Dark.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/dashboard/themes/Gruvbox.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/dashboard/themes/Halcyon Dark.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/dashboard/themes/IntelliJ Light.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/dashboard/themes/Kanagawa.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/dashboard/themes/Macaw Dark.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/dashboard/themes/Macaw Light.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/dashboard/themes/Matrix.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/dashboard/themes/Noctis Lilac.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/dashboard/themes/Nord.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/dashboard/themes/Nostromo Terminal.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/dashboard/themes/One Dark.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/dashboard/themes/Oxocarbon.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/dashboard/themes/Rose Pine Dawn.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/dashboard/themes/Rose Pine Moon.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/dashboard/themes/Rose Pine.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/dashboard/themes/Solarized Dark.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/dashboard/themes/Sonokai.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/dashboard/themes/Tokyo Night Storm.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/dashboard/themes/VESPER.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/dashboard/themes/Zenburn.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/dashboard/themes/acton.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/dashboard/themes/bam.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/dashboard/themes/base16-atelier-forest-light.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/dashboard/themes/berlin.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/dashboard/themes/black but with important highlights.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/dashboard/themes/broc.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/dashboard/themes/cork.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/dashboard/themes/ferra.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/dashboard/themes/forest.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/dashboard/themes/lisbon.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/dashboard/themes/midnight.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/dashboard/themes/oslo.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/dashboard/themes/plum.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/dashboard/themes/portland.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/dashboard/themes/sunset.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/dashboard/themes/tofino.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/dashboard/themes/vanimo.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/dashboard/themes/vik.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/db/__init__.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/db/connection.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/db/migrations.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/db/repositories.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/db/schema/0001_initial.sql +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/db/schema/0002_indexes.sql +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/db/schema/0003_request_attempts.sql +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/db/schema/0004_integration_hardening.sql +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/db/schema/0005_price_microdollars.sql +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/db/schema/0006_correct_price_microdollars.sql +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/db/schema/0007_price_cache_rates.sql +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/db/schema/0008_proxy_request_identity.sql +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/db/schema/0009_model_protocol_source.sql +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/db/schema/0010_health_probe.sql +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/db/schema/0011_model_resolution_status.sql +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/db/schema/0012_drop_reservations_estimated_microdollars.sql +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/db/schema/0013_request_attempts_account_id_index.sql +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/db/schema/0014_bandwidth_tracking.sql +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/db/schema/0015_multi_provider.sql +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/db/schema/0016_requests_provider_id.sql +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/db/schema/0017_price_snapshots_provider_id.sql +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/db/schema/0018_provider_pings.sql +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/db/schema/0019_client_ip.sql +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/db/schema/0020_performance_indexes.sql +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/db/schema/0021_provider_model_metadata.sql +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/db/schema/0022_dashboard_indexes.sql +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/db/schema/0023_deprecated_model_placeholder.sql +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/db/schema/0024_account_backoffs.sql +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/db/schema/0025_stale_request_index.sql +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/db/schema/0026_attempt_observability.sql +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/db/schema/0027_routing_decisions.sql +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/db/schema/0028_operational_events.sql +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/db/schema/0029_latency_phases.sql +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/db/schema/0030_model_pricing_aliases.sql +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/db/schema/0031_price_snapshot_provenance.sql +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/db/schema/0032_usage_rollups.sql +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/db/schema/0033_request_provider_local_cost.sql +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/db/schema/checksums.json +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/deploy/__init__.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/deploy_user.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/errors.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/fastcli.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/health/__init__.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/integrations/__init__.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/integrations/opencode.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/lifecycle/__init__.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/lifecycle/backup.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/logging.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/metrics/__init__.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/models/__init__.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/models/api.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/models/config.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/models/database.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/models/domain.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/onboard.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/providers/__init__.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/providers/_templates.toml +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/providers/auth.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/providers/client_pool.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/providers/connect.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/providers/contract.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/providers/dns_cache.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/providers/outbound.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/providers/pproxy_transport.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/proxy/__init__.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/proxy/client.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/py.typed +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/quota/__init__.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/quota/audit.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/quota/reservation.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/request/__init__.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/request/body.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/request/limits.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/retry/__init__.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/retry/classification.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/routing/__init__.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/routing/eligibility.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/routing/provider.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/runtime.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/runtime_paths.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/security/__init__.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/security/redaction.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/stats/__init__.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/stats/grouped_timeseries.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/stats/queries.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/stats/service.py +0 -0
- {eggpool-0.3.4 → eggpool-0.3.5}/src/eggpool/toml_edit.py +0 -0
eggpool-0.3.5/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.5/PKG-INFO
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: eggpool
|
|
3
|
+
Version: 0.3.5
|
|
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.5/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
|
|
@@ -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` |
|
|
@@ -1125,23 +1125,20 @@ def create_app(
|
|
|
1125
1125
|
def __init__(self, max_size: int = 3, ttl_s: int = 300) -> None:
|
|
1126
1126
|
self._max_size = max_size
|
|
1127
1127
|
self._ttl_s = ttl_s
|
|
1128
|
-
self._cache: dict[str, tuple[str, float]] = {}
|
|
1129
|
-
self._last_used: str =
|
|
1130
|
-
|
|
1131
|
-
def get(self,
|
|
1132
|
-
if
|
|
1133
|
-
css, ts = self._cache[
|
|
1134
|
-
if (
|
|
1135
|
-
|
|
1136
|
-
or theme_name == self._last_used
|
|
1137
|
-
):
|
|
1138
|
-
self._last_used = theme_name
|
|
1128
|
+
self._cache: dict[tuple[str, str | None], tuple[str, float]] = {}
|
|
1129
|
+
self._last_used: tuple[str, str | None] | None = None
|
|
1130
|
+
|
|
1131
|
+
def get(self, key: tuple[str, str | None]) -> str | None:
|
|
1132
|
+
if key in self._cache:
|
|
1133
|
+
css, ts = self._cache[key]
|
|
1134
|
+
if time.monotonic() - ts < self._ttl_s or key == self._last_used:
|
|
1135
|
+
self._last_used = key
|
|
1139
1136
|
return css
|
|
1140
|
-
del self._cache[
|
|
1137
|
+
del self._cache[key]
|
|
1141
1138
|
return None
|
|
1142
1139
|
|
|
1143
|
-
def put(self,
|
|
1144
|
-
if len(self._cache) >= self._max_size and
|
|
1140
|
+
def put(self, key: tuple[str, str | None], css: str) -> None:
|
|
1141
|
+
if len(self._cache) >= self._max_size and key not in self._cache:
|
|
1145
1142
|
now = time.monotonic()
|
|
1146
1143
|
to_evict = [
|
|
1147
1144
|
k
|
|
@@ -1157,15 +1154,17 @@ def create_app(
|
|
|
1157
1154
|
)
|
|
1158
1155
|
if oldest != self._last_used:
|
|
1159
1156
|
del self._cache[oldest]
|
|
1160
|
-
self._cache[
|
|
1161
|
-
self._last_used =
|
|
1157
|
+
self._cache[key] = (css, time.monotonic())
|
|
1158
|
+
self._last_used = key
|
|
1162
1159
|
|
|
1163
1160
|
_theme_css_cache = _ThemeCssCache()
|
|
1164
1161
|
|
|
1165
1162
|
@app.get("/static/theme.css")
|
|
1166
1163
|
async def theme_css(request: Request) -> Response: # pyright: ignore[reportUnusedFunction]
|
|
1167
1164
|
theme_name = request.query_params.get("theme", "default")
|
|
1168
|
-
|
|
1165
|
+
themes_dir = config.dashboard.themes_dir
|
|
1166
|
+
cache_key = (theme_name, themes_dir)
|
|
1167
|
+
cached = _theme_css_cache.get(cache_key)
|
|
1169
1168
|
if cached is not None:
|
|
1170
1169
|
return Response(
|
|
1171
1170
|
content=cached,
|
|
@@ -1174,8 +1173,8 @@ def create_app(
|
|
|
1174
1173
|
)
|
|
1175
1174
|
from eggpool.dashboard.render import get_theme_css
|
|
1176
1175
|
|
|
1177
|
-
css = get_theme_css(theme_name)
|
|
1178
|
-
_theme_css_cache.put(
|
|
1176
|
+
css = get_theme_css(theme_name, themes_dir)
|
|
1177
|
+
_theme_css_cache.put(cache_key, css)
|
|
1179
1178
|
return Response(
|
|
1180
1179
|
content=css,
|
|
1181
1180
|
media_type="text/css",
|