eggpool 0.1.0__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.1.0/.env.example +9 -0
- eggpool-0.1.0/.github/workflows/ci.yml +59 -0
- eggpool-0.1.0/.github/workflows/release.yml +31 -0
- eggpool-0.1.0/.gitignore +67 -0
- eggpool-0.1.0/AGENTS.md +113 -0
- eggpool-0.1.0/CHANGELOG.md +30 -0
- eggpool-0.1.0/LICENSE +21 -0
- eggpool-0.1.0/PKG-INFO +512 -0
- eggpool-0.1.0/README.md +469 -0
- eggpool-0.1.0/architecture/README.md +308 -0
- eggpool-0.1.0/config-examples/claude-code.env +12 -0
- eggpool-0.1.0/config-examples/opencode.jsonc +27 -0
- eggpool-0.1.0/config.example.toml +557 -0
- eggpool-0.1.0/deploy/eggpool-logrotate.conf +12 -0
- eggpool-0.1.0/deploy/eggpool.service +51 -0
- eggpool-0.1.0/deploy/env.example +31 -0
- eggpool-0.1.0/docs/backup-restore.md +137 -0
- eggpool-0.1.0/docs/deployment.md +322 -0
- eggpool-0.1.0/docs/filesystem-layout.md +43 -0
- eggpool-0.1.0/docs/firewall.md +77 -0
- eggpool-0.1.0/docs/model-limits.md +89 -0
- eggpool-0.1.0/docs/providers.md +268 -0
- eggpool-0.1.0/docs/proxy.md +243 -0
- eggpool-0.1.0/docs/raspberry-pi.md +356 -0
- eggpool-0.1.0/pyproject.toml +115 -0
- eggpool-0.1.0/scripts/__init__.py +6 -0
- eggpool-0.1.0/scripts/check_database.py +510 -0
- eggpool-0.1.0/scripts/install.sh +155 -0
- eggpool-0.1.0/scripts/install_prompt.py +65 -0
- eggpool-0.1.0/scripts/smoke_test.py +514 -0
- eggpool-0.1.0/scripts/verify_upstream_auth.py +857 -0
- eggpool-0.1.0/src/eggpool/__init__.py +5 -0
- eggpool-0.1.0/src/eggpool/__main__.py +7 -0
- eggpool-0.1.0/src/eggpool/_share/.env.example +9 -0
- eggpool-0.1.0/src/eggpool/_share/config.example.toml +551 -0
- eggpool-0.1.0/src/eggpool/accounts/__init__.py +1 -0
- eggpool-0.1.0/src/eggpool/accounts/registry.py +181 -0
- eggpool-0.1.0/src/eggpool/accounts/state.py +170 -0
- eggpool-0.1.0/src/eggpool/api/__init__.py +1 -0
- eggpool-0.1.0/src/eggpool/api/chat_completions.py +31 -0
- eggpool-0.1.0/src/eggpool/api/errors.py +51 -0
- eggpool-0.1.0/src/eggpool/api/messages.py +31 -0
- eggpool-0.1.0/src/eggpool/api/models.py +76 -0
- eggpool-0.1.0/src/eggpool/api/proxy_request.py +242 -0
- eggpool-0.1.0/src/eggpool/api/stats.py +284 -0
- eggpool-0.1.0/src/eggpool/app.py +893 -0
- eggpool-0.1.0/src/eggpool/auth.py +92 -0
- eggpool-0.1.0/src/eggpool/background/__init__.py +147 -0
- eggpool-0.1.0/src/eggpool/background/cleanup.py +206 -0
- eggpool-0.1.0/src/eggpool/catalog/__init__.py +1 -0
- eggpool-0.1.0/src/eggpool/catalog/cache.py +645 -0
- eggpool-0.1.0/src/eggpool/catalog/fetcher.py +188 -0
- eggpool-0.1.0/src/eggpool/catalog/limits.py +306 -0
- eggpool-0.1.0/src/eggpool/catalog/normalizer.py +119 -0
- eggpool-0.1.0/src/eggpool/catalog/pricing.py +438 -0
- eggpool-0.1.0/src/eggpool/catalog/protocols.py +194 -0
- eggpool-0.1.0/src/eggpool/catalog/service.py +920 -0
- eggpool-0.1.0/src/eggpool/cli.py +1481 -0
- eggpool-0.1.0/src/eggpool/constants.py +34 -0
- eggpool-0.1.0/src/eggpool/dashboard/__init__.py +3 -0
- eggpool-0.1.0/src/eggpool/dashboard/_resources.py +14 -0
- eggpool-0.1.0/src/eggpool/dashboard/escape.py +104 -0
- eggpool-0.1.0/src/eggpool/dashboard/render.py +1448 -0
- eggpool-0.1.0/src/eggpool/dashboard/routes.py +451 -0
- eggpool-0.1.0/src/eggpool/dashboard/static/chart.umd.min.js +20 -0
- eggpool-0.1.0/src/eggpool/dashboard/static/dashboard.css +341 -0
- eggpool-0.1.0/src/eggpool/dashboard/static/favicon.svg +23 -0
- eggpool-0.1.0/src/eggpool/dashboard/theme.py +514 -0
- eggpool-0.1.0/src/eggpool/dashboard/themes/Booberry.toml +42 -0
- eggpool-0.1.0/src/eggpool/dashboard/themes/Catppuccin Latte.toml +42 -0
- eggpool-0.1.0/src/eggpool/dashboard/themes/Catppuccin Macchiato.toml +42 -0
- eggpool-0.1.0/src/eggpool/dashboard/themes/Catppuccin Mocha.toml +42 -0
- eggpool-0.1.0/src/eggpool/dashboard/themes/Cyber Red.toml +42 -0
- eggpool-0.1.0/src/eggpool/dashboard/themes/Cyberpunk.toml +42 -0
- eggpool-0.1.0/src/eggpool/dashboard/themes/Dark Green.toml +43 -0
- eggpool-0.1.0/src/eggpool/dashboard/themes/Discord (80_ Saturation).toml +50 -0
- eggpool-0.1.0/src/eggpool/dashboard/themes/Discord.toml +50 -0
- eggpool-0.1.0/src/eggpool/dashboard/themes/Dracula.toml +42 -0
- eggpool-0.1.0/src/eggpool/dashboard/themes/Ferra Light.toml +42 -0
- eggpool-0.1.0/src/eggpool/dashboard/themes/Flexor Dark.toml +42 -0
- eggpool-0.1.0/src/eggpool/dashboard/themes/Gruvbox.toml +42 -0
- eggpool-0.1.0/src/eggpool/dashboard/themes/Halcyon Dark.toml +42 -0
- eggpool-0.1.0/src/eggpool/dashboard/themes/IntelliJ Light.toml +42 -0
- eggpool-0.1.0/src/eggpool/dashboard/themes/Kanagawa.toml +42 -0
- eggpool-0.1.0/src/eggpool/dashboard/themes/Macaw Dark.toml +42 -0
- eggpool-0.1.0/src/eggpool/dashboard/themes/Macaw Light.toml +42 -0
- eggpool-0.1.0/src/eggpool/dashboard/themes/Matrix.toml +42 -0
- eggpool-0.1.0/src/eggpool/dashboard/themes/Noctis Lilac.toml +42 -0
- eggpool-0.1.0/src/eggpool/dashboard/themes/Nord.toml +42 -0
- eggpool-0.1.0/src/eggpool/dashboard/themes/Nostromo Terminal.toml +42 -0
- eggpool-0.1.0/src/eggpool/dashboard/themes/One Dark.toml +42 -0
- eggpool-0.1.0/src/eggpool/dashboard/themes/Oxocarbon.toml +42 -0
- eggpool-0.1.0/src/eggpool/dashboard/themes/Rose Pine Dawn.toml +42 -0
- eggpool-0.1.0/src/eggpool/dashboard/themes/Rose Pine Moon.toml +42 -0
- eggpool-0.1.0/src/eggpool/dashboard/themes/Rose Pine.toml +42 -0
- eggpool-0.1.0/src/eggpool/dashboard/themes/Solarized Dark.toml +42 -0
- eggpool-0.1.0/src/eggpool/dashboard/themes/Sonokai.toml +42 -0
- eggpool-0.1.0/src/eggpool/dashboard/themes/Tokyo Night Storm.toml +42 -0
- eggpool-0.1.0/src/eggpool/dashboard/themes/VESPER.toml +42 -0
- eggpool-0.1.0/src/eggpool/dashboard/themes/Zenburn.toml +42 -0
- eggpool-0.1.0/src/eggpool/dashboard/themes/acton.toml +42 -0
- eggpool-0.1.0/src/eggpool/dashboard/themes/bam.toml +42 -0
- eggpool-0.1.0/src/eggpool/dashboard/themes/base16-atelier-forest-light.toml +45 -0
- eggpool-0.1.0/src/eggpool/dashboard/themes/berlin.toml +42 -0
- eggpool-0.1.0/src/eggpool/dashboard/themes/black but with important highlights.toml +42 -0
- eggpool-0.1.0/src/eggpool/dashboard/themes/broc.toml +42 -0
- eggpool-0.1.0/src/eggpool/dashboard/themes/cork.toml +42 -0
- eggpool-0.1.0/src/eggpool/dashboard/themes/ferra.toml +42 -0
- eggpool-0.1.0/src/eggpool/dashboard/themes/forest.toml +42 -0
- eggpool-0.1.0/src/eggpool/dashboard/themes/lisbon.toml +42 -0
- eggpool-0.1.0/src/eggpool/dashboard/themes/midnight.toml +42 -0
- eggpool-0.1.0/src/eggpool/dashboard/themes/oslo.toml +42 -0
- eggpool-0.1.0/src/eggpool/dashboard/themes/plum.toml +43 -0
- eggpool-0.1.0/src/eggpool/dashboard/themes/portland.toml +42 -0
- eggpool-0.1.0/src/eggpool/dashboard/themes/sunset.toml +42 -0
- eggpool-0.1.0/src/eggpool/dashboard/themes/tofino.toml +42 -0
- eggpool-0.1.0/src/eggpool/dashboard/themes/vanimo.toml +42 -0
- eggpool-0.1.0/src/eggpool/dashboard/themes/vik.toml +42 -0
- eggpool-0.1.0/src/eggpool/db/__init__.py +0 -0
- eggpool-0.1.0/src/eggpool/db/connection.py +401 -0
- eggpool-0.1.0/src/eggpool/db/migrations.py +125 -0
- eggpool-0.1.0/src/eggpool/db/repositories.py +989 -0
- eggpool-0.1.0/src/eggpool/db/schema/0001_initial.sql +78 -0
- eggpool-0.1.0/src/eggpool/db/schema/0002_indexes.sql +24 -0
- eggpool-0.1.0/src/eggpool/db/schema/0003_request_attempts.sql +20 -0
- eggpool-0.1.0/src/eggpool/db/schema/0004_integration_hardening.sql +34 -0
- eggpool-0.1.0/src/eggpool/db/schema/0005_price_microdollars.sql +15 -0
- eggpool-0.1.0/src/eggpool/db/schema/0006_correct_price_microdollars.sql +12 -0
- eggpool-0.1.0/src/eggpool/db/schema/0007_price_cache_rates.sql +5 -0
- eggpool-0.1.0/src/eggpool/db/schema/0008_proxy_request_identity.sql +9 -0
- eggpool-0.1.0/src/eggpool/db/schema/0009_model_protocol_source.sql +3 -0
- eggpool-0.1.0/src/eggpool/db/schema/0010_health_probe.sql +5 -0
- eggpool-0.1.0/src/eggpool/db/schema/0011_model_resolution_status.sql +1 -0
- eggpool-0.1.0/src/eggpool/db/schema/0012_drop_reservations_estimated_microdollars.sql +6 -0
- eggpool-0.1.0/src/eggpool/db/schema/0013_request_attempts_account_id_index.sql +7 -0
- eggpool-0.1.0/src/eggpool/db/schema/0014_bandwidth_tracking.sql +2 -0
- eggpool-0.1.0/src/eggpool/db/schema/0015_multi_provider.sql +23 -0
- eggpool-0.1.0/src/eggpool/db/schema/0016_requests_provider_id.sql +2 -0
- eggpool-0.1.0/src/eggpool/db/schema/0017_price_snapshots_provider_id.sql +4 -0
- eggpool-0.1.0/src/eggpool/db/schema/0018_provider_pings.sql +16 -0
- eggpool-0.1.0/src/eggpool/db/schema/0019_client_ip.sql +4 -0
- eggpool-0.1.0/src/eggpool/db/schema/0020_performance_indexes.sql +19 -0
- eggpool-0.1.0/src/eggpool/db/schema/0021_provider_model_metadata.sql +19 -0
- eggpool-0.1.0/src/eggpool/db/schema/0022_dashboard_indexes.sql +14 -0
- eggpool-0.1.0/src/eggpool/db/schema/checksums.json +26 -0
- eggpool-0.1.0/src/eggpool/deploy/__init__.py +126 -0
- eggpool-0.1.0/src/eggpool/errors.py +123 -0
- eggpool-0.1.0/src/eggpool/health/__init__.py +8 -0
- eggpool-0.1.0/src/eggpool/health/circuit_breaker.py +146 -0
- eggpool-0.1.0/src/eggpool/health/health_manager.py +339 -0
- eggpool-0.1.0/src/eggpool/integrations/__init__.py +1 -0
- eggpool-0.1.0/src/eggpool/integrations/opencode.py +90 -0
- eggpool-0.1.0/src/eggpool/logging.py +50 -0
- eggpool-0.1.0/src/eggpool/models/__init__.py +0 -0
- eggpool-0.1.0/src/eggpool/models/api.py +32 -0
- eggpool-0.1.0/src/eggpool/models/config.py +658 -0
- eggpool-0.1.0/src/eggpool/models/database.py +99 -0
- eggpool-0.1.0/src/eggpool/models/domain.py +59 -0
- eggpool-0.1.0/src/eggpool/onboard.py +111 -0
- eggpool-0.1.0/src/eggpool/providers/__init__.py +0 -0
- eggpool-0.1.0/src/eggpool/providers/_templates.toml +574 -0
- eggpool-0.1.0/src/eggpool/providers/client_pool.py +131 -0
- eggpool-0.1.0/src/eggpool/providers/connect.py +988 -0
- eggpool-0.1.0/src/eggpool/providers/contract.py +91 -0
- eggpool-0.1.0/src/eggpool/providers/pproxy_transport.py +293 -0
- eggpool-0.1.0/src/eggpool/proxy/__init__.py +1 -0
- eggpool-0.1.0/src/eggpool/proxy/client.py +140 -0
- eggpool-0.1.0/src/eggpool/proxy/sse_observer.py +283 -0
- eggpool-0.1.0/src/eggpool/proxy/usage.py +114 -0
- eggpool-0.1.0/src/eggpool/py.typed +1 -0
- eggpool-0.1.0/src/eggpool/quota/__init__.py +13 -0
- eggpool-0.1.0/src/eggpool/quota/estimation.py +639 -0
- eggpool-0.1.0/src/eggpool/quota/reservation.py +193 -0
- eggpool-0.1.0/src/eggpool/quota/scorer.py +215 -0
- eggpool-0.1.0/src/eggpool/request/__init__.py +13 -0
- eggpool-0.1.0/src/eggpool/request/attempt_finalizer.py +125 -0
- eggpool-0.1.0/src/eggpool/request/body.py +70 -0
- eggpool-0.1.0/src/eggpool/request/coordinator.py +1638 -0
- eggpool-0.1.0/src/eggpool/request/finalizer.py +392 -0
- eggpool-0.1.0/src/eggpool/request/limits.py +152 -0
- eggpool-0.1.0/src/eggpool/retry/__init__.py +7 -0
- eggpool-0.1.0/src/eggpool/retry/classification.py +207 -0
- eggpool-0.1.0/src/eggpool/routing/__init__.py +1 -0
- eggpool-0.1.0/src/eggpool/routing/eligibility.py +95 -0
- eggpool-0.1.0/src/eggpool/routing/provider.py +20 -0
- eggpool-0.1.0/src/eggpool/routing/router.py +395 -0
- eggpool-0.1.0/src/eggpool/security/__init__.py +7 -0
- eggpool-0.1.0/src/eggpool/security/redaction.py +327 -0
- eggpool-0.1.0/src/eggpool/stats/__init__.py +37 -0
- eggpool-0.1.0/src/eggpool/stats/queries.py +598 -0
- eggpool-0.1.0/src/eggpool/stats/service.py +548 -0
- eggpool-0.1.0/src/eggpool/toml_edit.py +101 -0
- eggpool-0.1.0/uv.lock +731 -0
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# EggPool environment variables
|
|
2
|
+
#
|
|
3
|
+
# API keys are now stored inline in config.toml via the `connect` command.
|
|
4
|
+
# This file is only needed if you use api_key_env (env var) instead of inline keys.
|
|
5
|
+
#
|
|
6
|
+
# Example:
|
|
7
|
+
# OPENCODE_GO_KEY=sk-your-opencode-go-key
|
|
8
|
+
#
|
|
9
|
+
# See config.example.toml for all available configuration options.
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
env:
|
|
10
|
+
PYTHONHASHSEED: "0"
|
|
11
|
+
TZ: UTC
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
lint:
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
- uses: astral-sh/setup-uv@v4
|
|
19
|
+
- uses: actions/setup-python@v5
|
|
20
|
+
with:
|
|
21
|
+
python-version: "3.12"
|
|
22
|
+
- run: uv sync --frozen --extra dev
|
|
23
|
+
- run: uv run ruff format --check src/ tests/ scripts/
|
|
24
|
+
- run: uv run ruff check src/ tests/ scripts/
|
|
25
|
+
|
|
26
|
+
typecheck:
|
|
27
|
+
runs-on: ubuntu-latest
|
|
28
|
+
steps:
|
|
29
|
+
- uses: actions/checkout@v4
|
|
30
|
+
- uses: astral-sh/setup-uv@v4
|
|
31
|
+
- uses: actions/setup-python@v5
|
|
32
|
+
with:
|
|
33
|
+
python-version: "3.12"
|
|
34
|
+
- run: uv sync --frozen --extra dev
|
|
35
|
+
- run: uv run pyright src/ scripts/
|
|
36
|
+
|
|
37
|
+
test:
|
|
38
|
+
runs-on: ubuntu-latest
|
|
39
|
+
steps:
|
|
40
|
+
- uses: actions/checkout@v4
|
|
41
|
+
- uses: astral-sh/setup-uv@v4
|
|
42
|
+
- uses: actions/setup-python@v5
|
|
43
|
+
with:
|
|
44
|
+
python-version: "3.12"
|
|
45
|
+
- run: uv sync --frozen --extra dev
|
|
46
|
+
- name: Verify coverage plugin is present
|
|
47
|
+
run: uv run pytest --help | grep -- --cov
|
|
48
|
+
- name: Run tests with coverage
|
|
49
|
+
run: >-
|
|
50
|
+
uv run pytest
|
|
51
|
+
--cov=eggpool
|
|
52
|
+
--cov-report=term-missing
|
|
53
|
+
--cov-report=xml
|
|
54
|
+
- name: Upload coverage artifact
|
|
55
|
+
if: always()
|
|
56
|
+
uses: actions/upload-artifact@v4
|
|
57
|
+
with:
|
|
58
|
+
name: coverage-report
|
|
59
|
+
path: coverage.xml
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*"
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: write
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
release:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
|
|
17
|
+
- uses: actions/setup-python@v5
|
|
18
|
+
with:
|
|
19
|
+
python-version: "3.12"
|
|
20
|
+
|
|
21
|
+
- name: Install build tools
|
|
22
|
+
run: pip install build
|
|
23
|
+
|
|
24
|
+
- name: Build wheel and sdist
|
|
25
|
+
run: python -m build
|
|
26
|
+
|
|
27
|
+
- name: Create GitHub release
|
|
28
|
+
uses: softprops/action-gh-release@v2
|
|
29
|
+
with:
|
|
30
|
+
generate_release_notes: true
|
|
31
|
+
files: dist/*
|
eggpool-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
*.egg-info/
|
|
6
|
+
*.egg
|
|
7
|
+
dist/
|
|
8
|
+
build/
|
|
9
|
+
.eggs/
|
|
10
|
+
|
|
11
|
+
# Virtual environments
|
|
12
|
+
.venv/
|
|
13
|
+
venv/
|
|
14
|
+
|
|
15
|
+
# uv
|
|
16
|
+
.uv/
|
|
17
|
+
|
|
18
|
+
# IDE
|
|
19
|
+
.vscode/
|
|
20
|
+
.idea/
|
|
21
|
+
*.swp
|
|
22
|
+
*.swo
|
|
23
|
+
*~
|
|
24
|
+
|
|
25
|
+
# Testing
|
|
26
|
+
.coverage
|
|
27
|
+
.coverage.*
|
|
28
|
+
htmlcov/
|
|
29
|
+
.pytest_cache/
|
|
30
|
+
.mypy_cache/
|
|
31
|
+
coverage.xml
|
|
32
|
+
|
|
33
|
+
# Type checking
|
|
34
|
+
.pyright/
|
|
35
|
+
.pytype/
|
|
36
|
+
|
|
37
|
+
# Ruff
|
|
38
|
+
.ruff_cache/
|
|
39
|
+
|
|
40
|
+
# Database
|
|
41
|
+
*.sqlite3
|
|
42
|
+
*.sqlite3-wal
|
|
43
|
+
*.sqlite3-shm
|
|
44
|
+
|
|
45
|
+
# Environment files
|
|
46
|
+
.env
|
|
47
|
+
.env.local
|
|
48
|
+
.env.production
|
|
49
|
+
.envrc
|
|
50
|
+
.env.*
|
|
51
|
+
!.env.example
|
|
52
|
+
secrets.env
|
|
53
|
+
*.env.local
|
|
54
|
+
gorouter.env
|
|
55
|
+
gorouter.env.local
|
|
56
|
+
|
|
57
|
+
# Configuration with secrets
|
|
58
|
+
config.toml
|
|
59
|
+
|
|
60
|
+
# OS
|
|
61
|
+
.DS_Store
|
|
62
|
+
Thumbs.db
|
|
63
|
+
|
|
64
|
+
# Project specific
|
|
65
|
+
/var/
|
|
66
|
+
*.log
|
|
67
|
+
bugs.md
|
eggpool-0.1.0/AGENTS.md
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
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.12+ 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
|
+
## Multi-Provider Architecture
|
|
54
|
+
|
|
55
|
+
- Provider-suffixed model IDs: `model-id/provider-id` (e.g., `claude-sonnet-4/opencode-go`)
|
|
56
|
+
- `ProviderClientPool` manages per-provider `httpx.AsyncClient` with independent connection pools
|
|
57
|
+
- Flat `[[accounts]]` configs auto-normalize to a default `opencode-go` provider
|
|
58
|
+
- `parse_model_provider()` and `format_model_provider()` in `routing/provider.py`
|
|
59
|
+
|
|
60
|
+
## Provider Contracts
|
|
61
|
+
|
|
62
|
+
Key design rules that are easy to get wrong:
|
|
63
|
+
|
|
64
|
+
- `compose_provider_url()` is the single source of truth for upstream URLs — catalog fetch, non-streaming chat, and streaming chat all call it
|
|
65
|
+
- `build_auth_headers()` reads from `ProviderConfig.auth` — never hardcode Bearer
|
|
66
|
+
- `base_url` ending `/v1` + path beginning `/v1/` is rejected (duplicate version prefix)
|
|
67
|
+
- `auth.mode = "none"` sends no upstream auth (Ollama local)
|
|
68
|
+
- All outbound dispatch paths use `compose_provider_url()` so providers cannot list at one host and dispatch to another
|
|
69
|
+
|
|
70
|
+
## Bearer-prefix Guard
|
|
71
|
+
|
|
72
|
+
`AppConfig.validate_account_credentials()` rejects API keys beginning with `Bearer` for `auth.mode = "bearer"` providers. EggPool prepends `Bearer ` automatically; storing `Bearer <token>` produces `Authorization: Bearer Bearer <token>` and causes 401s. Providers using `auth.mode = "raw_authorization"` are unaffected.
|
|
73
|
+
|
|
74
|
+
## Error Handling
|
|
75
|
+
|
|
76
|
+
Use the hierarchy in `errors.py`. Chain exceptions with `raise ... from err` or `raise ... from None`.
|
|
77
|
+
|
|
78
|
+
- `AggregatorError` → `ConfigError`, `DatabaseError`, `ProxyError`
|
|
79
|
+
- `UpstreamError` (has `status_code`) → `TemporaryUpstreamError`, `TransientUpstreamError`, `AuthenticationError`, `QuotaExhaustedError`, `RateLimitError` (has `retry_after`), `ModelUnavailableError`
|
|
80
|
+
- `ModelNotFoundError` (has `model_id`), `NoEligibleAccountError`, `CatalogUnavailableError`, `AuthenticationUnavailableError`, `UpstreamExhaustedError`, `AccountSuspendedError`, `RequestTooLargeError`, `ContextLimitExceededError`
|
|
81
|
+
|
|
82
|
+
## Gotchas
|
|
83
|
+
|
|
84
|
+
- Configuration changes require a service restart; live reload is intentionally not supported
|
|
85
|
+
- No CI workflows or pre-commit hooks are configured in this repo
|
|
86
|
+
- `Database.vacuum()` is the only sanctioned path for `VACUUM` in production code
|
|
87
|
+
- Every DML write must run inside `async with db.transaction():`
|
|
88
|
+
- SQLite transactions are serialized across concurrent tasks via a single connection lock + ContextVar
|
|
89
|
+
- Requests must be persisted before upstream dispatch; pre-body failures can retry, but no retry after first downstream byte
|
|
90
|
+
|
|
91
|
+
## CLI Commands
|
|
92
|
+
|
|
93
|
+
| Command | Description |
|
|
94
|
+
|---------|-------------|
|
|
95
|
+
| `eggpool serve` | Start the aggregation proxy server (default command) |
|
|
96
|
+
| `eggpool check-config` | Validate the configuration file |
|
|
97
|
+
| `eggpool migrate` | Run database migrations |
|
|
98
|
+
| `eggpool onboard` | Run the interactive onboarding setup |
|
|
99
|
+
| `eggpool connect` | Connect to a new provider interactively |
|
|
100
|
+
| `eggpool connect list` | List available providers |
|
|
101
|
+
| `eggpool logout` | Remove a configured provider account |
|
|
102
|
+
| `eggpool rehash` | Restart to apply config changes |
|
|
103
|
+
| `eggpool models refresh` | Refresh model catalog from upstream |
|
|
104
|
+
| `eggpool configsetup opencode` | Print OpenCode provider config JSON with model limits |
|
|
105
|
+
| `eggpool db vacuum` | Reclaim SQLite space |
|
|
106
|
+
|
|
107
|
+
All commands accept `--config /path/to/config.toml` (defaults to `config.toml`).
|
|
108
|
+
|
|
109
|
+
## Git Workflow
|
|
110
|
+
|
|
111
|
+
- Branch: `main`
|
|
112
|
+
- Commit messages: concise, imperative mood
|
|
113
|
+
- Never commit secrets, API keys, or `.env` files
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to EggPool are documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [0.1.0] - 2026-06-23
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- Multi-provider aggregation across OpenAI- and Anthropic-compatible
|
|
13
|
+
upstreams with quota-aware routing.
|
|
14
|
+
- SQLite-backed request, token, latency, error, and cost statistics.
|
|
15
|
+
- Multi-page HTML dashboard (overview, accounts, models, latency, pings,
|
|
16
|
+
events, timeseries, bandwidth) with 50+ Halloy themes.
|
|
17
|
+
- CLI commands: `serve`, `check-config`, `migrate`, `onboard`,
|
|
18
|
+
`connect`, `connect list`, `logout`, `accounts list`,
|
|
19
|
+
`accounts status`, `models refresh`, `db vacuum`, `dashboard public`,
|
|
20
|
+
`rehash`, `restart`, `stop`, `update`, `getkey`, `newkey`, `edit`,
|
|
21
|
+
`configsetup opencode`, `configsetup claude-code`, `set`,
|
|
22
|
+
`init-config`, and the `deploy` group (`systemd`, `logrotate`,
|
|
23
|
+
`cron`, `all`).
|
|
24
|
+
- Operational scripts: `install.sh`, `install_prompt.py`,
|
|
25
|
+
`check_database.py`, `smoke_test.py`, `verify_upstream_auth.py`.
|
|
26
|
+
|
|
27
|
+
### Notes
|
|
28
|
+
|
|
29
|
+
- See the README and `docs/deployment.md` for install, configuration,
|
|
30
|
+
and deployment.
|
eggpool-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 David Bowman
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|