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.
Files changed (193) hide show
  1. eggpool-0.1.0/.env.example +9 -0
  2. eggpool-0.1.0/.github/workflows/ci.yml +59 -0
  3. eggpool-0.1.0/.github/workflows/release.yml +31 -0
  4. eggpool-0.1.0/.gitignore +67 -0
  5. eggpool-0.1.0/AGENTS.md +113 -0
  6. eggpool-0.1.0/CHANGELOG.md +30 -0
  7. eggpool-0.1.0/LICENSE +21 -0
  8. eggpool-0.1.0/PKG-INFO +512 -0
  9. eggpool-0.1.0/README.md +469 -0
  10. eggpool-0.1.0/architecture/README.md +308 -0
  11. eggpool-0.1.0/config-examples/claude-code.env +12 -0
  12. eggpool-0.1.0/config-examples/opencode.jsonc +27 -0
  13. eggpool-0.1.0/config.example.toml +557 -0
  14. eggpool-0.1.0/deploy/eggpool-logrotate.conf +12 -0
  15. eggpool-0.1.0/deploy/eggpool.service +51 -0
  16. eggpool-0.1.0/deploy/env.example +31 -0
  17. eggpool-0.1.0/docs/backup-restore.md +137 -0
  18. eggpool-0.1.0/docs/deployment.md +322 -0
  19. eggpool-0.1.0/docs/filesystem-layout.md +43 -0
  20. eggpool-0.1.0/docs/firewall.md +77 -0
  21. eggpool-0.1.0/docs/model-limits.md +89 -0
  22. eggpool-0.1.0/docs/providers.md +268 -0
  23. eggpool-0.1.0/docs/proxy.md +243 -0
  24. eggpool-0.1.0/docs/raspberry-pi.md +356 -0
  25. eggpool-0.1.0/pyproject.toml +115 -0
  26. eggpool-0.1.0/scripts/__init__.py +6 -0
  27. eggpool-0.1.0/scripts/check_database.py +510 -0
  28. eggpool-0.1.0/scripts/install.sh +155 -0
  29. eggpool-0.1.0/scripts/install_prompt.py +65 -0
  30. eggpool-0.1.0/scripts/smoke_test.py +514 -0
  31. eggpool-0.1.0/scripts/verify_upstream_auth.py +857 -0
  32. eggpool-0.1.0/src/eggpool/__init__.py +5 -0
  33. eggpool-0.1.0/src/eggpool/__main__.py +7 -0
  34. eggpool-0.1.0/src/eggpool/_share/.env.example +9 -0
  35. eggpool-0.1.0/src/eggpool/_share/config.example.toml +551 -0
  36. eggpool-0.1.0/src/eggpool/accounts/__init__.py +1 -0
  37. eggpool-0.1.0/src/eggpool/accounts/registry.py +181 -0
  38. eggpool-0.1.0/src/eggpool/accounts/state.py +170 -0
  39. eggpool-0.1.0/src/eggpool/api/__init__.py +1 -0
  40. eggpool-0.1.0/src/eggpool/api/chat_completions.py +31 -0
  41. eggpool-0.1.0/src/eggpool/api/errors.py +51 -0
  42. eggpool-0.1.0/src/eggpool/api/messages.py +31 -0
  43. eggpool-0.1.0/src/eggpool/api/models.py +76 -0
  44. eggpool-0.1.0/src/eggpool/api/proxy_request.py +242 -0
  45. eggpool-0.1.0/src/eggpool/api/stats.py +284 -0
  46. eggpool-0.1.0/src/eggpool/app.py +893 -0
  47. eggpool-0.1.0/src/eggpool/auth.py +92 -0
  48. eggpool-0.1.0/src/eggpool/background/__init__.py +147 -0
  49. eggpool-0.1.0/src/eggpool/background/cleanup.py +206 -0
  50. eggpool-0.1.0/src/eggpool/catalog/__init__.py +1 -0
  51. eggpool-0.1.0/src/eggpool/catalog/cache.py +645 -0
  52. eggpool-0.1.0/src/eggpool/catalog/fetcher.py +188 -0
  53. eggpool-0.1.0/src/eggpool/catalog/limits.py +306 -0
  54. eggpool-0.1.0/src/eggpool/catalog/normalizer.py +119 -0
  55. eggpool-0.1.0/src/eggpool/catalog/pricing.py +438 -0
  56. eggpool-0.1.0/src/eggpool/catalog/protocols.py +194 -0
  57. eggpool-0.1.0/src/eggpool/catalog/service.py +920 -0
  58. eggpool-0.1.0/src/eggpool/cli.py +1481 -0
  59. eggpool-0.1.0/src/eggpool/constants.py +34 -0
  60. eggpool-0.1.0/src/eggpool/dashboard/__init__.py +3 -0
  61. eggpool-0.1.0/src/eggpool/dashboard/_resources.py +14 -0
  62. eggpool-0.1.0/src/eggpool/dashboard/escape.py +104 -0
  63. eggpool-0.1.0/src/eggpool/dashboard/render.py +1448 -0
  64. eggpool-0.1.0/src/eggpool/dashboard/routes.py +451 -0
  65. eggpool-0.1.0/src/eggpool/dashboard/static/chart.umd.min.js +20 -0
  66. eggpool-0.1.0/src/eggpool/dashboard/static/dashboard.css +341 -0
  67. eggpool-0.1.0/src/eggpool/dashboard/static/favicon.svg +23 -0
  68. eggpool-0.1.0/src/eggpool/dashboard/theme.py +514 -0
  69. eggpool-0.1.0/src/eggpool/dashboard/themes/Booberry.toml +42 -0
  70. eggpool-0.1.0/src/eggpool/dashboard/themes/Catppuccin Latte.toml +42 -0
  71. eggpool-0.1.0/src/eggpool/dashboard/themes/Catppuccin Macchiato.toml +42 -0
  72. eggpool-0.1.0/src/eggpool/dashboard/themes/Catppuccin Mocha.toml +42 -0
  73. eggpool-0.1.0/src/eggpool/dashboard/themes/Cyber Red.toml +42 -0
  74. eggpool-0.1.0/src/eggpool/dashboard/themes/Cyberpunk.toml +42 -0
  75. eggpool-0.1.0/src/eggpool/dashboard/themes/Dark Green.toml +43 -0
  76. eggpool-0.1.0/src/eggpool/dashboard/themes/Discord (80_ Saturation).toml +50 -0
  77. eggpool-0.1.0/src/eggpool/dashboard/themes/Discord.toml +50 -0
  78. eggpool-0.1.0/src/eggpool/dashboard/themes/Dracula.toml +42 -0
  79. eggpool-0.1.0/src/eggpool/dashboard/themes/Ferra Light.toml +42 -0
  80. eggpool-0.1.0/src/eggpool/dashboard/themes/Flexor Dark.toml +42 -0
  81. eggpool-0.1.0/src/eggpool/dashboard/themes/Gruvbox.toml +42 -0
  82. eggpool-0.1.0/src/eggpool/dashboard/themes/Halcyon Dark.toml +42 -0
  83. eggpool-0.1.0/src/eggpool/dashboard/themes/IntelliJ Light.toml +42 -0
  84. eggpool-0.1.0/src/eggpool/dashboard/themes/Kanagawa.toml +42 -0
  85. eggpool-0.1.0/src/eggpool/dashboard/themes/Macaw Dark.toml +42 -0
  86. eggpool-0.1.0/src/eggpool/dashboard/themes/Macaw Light.toml +42 -0
  87. eggpool-0.1.0/src/eggpool/dashboard/themes/Matrix.toml +42 -0
  88. eggpool-0.1.0/src/eggpool/dashboard/themes/Noctis Lilac.toml +42 -0
  89. eggpool-0.1.0/src/eggpool/dashboard/themes/Nord.toml +42 -0
  90. eggpool-0.1.0/src/eggpool/dashboard/themes/Nostromo Terminal.toml +42 -0
  91. eggpool-0.1.0/src/eggpool/dashboard/themes/One Dark.toml +42 -0
  92. eggpool-0.1.0/src/eggpool/dashboard/themes/Oxocarbon.toml +42 -0
  93. eggpool-0.1.0/src/eggpool/dashboard/themes/Rose Pine Dawn.toml +42 -0
  94. eggpool-0.1.0/src/eggpool/dashboard/themes/Rose Pine Moon.toml +42 -0
  95. eggpool-0.1.0/src/eggpool/dashboard/themes/Rose Pine.toml +42 -0
  96. eggpool-0.1.0/src/eggpool/dashboard/themes/Solarized Dark.toml +42 -0
  97. eggpool-0.1.0/src/eggpool/dashboard/themes/Sonokai.toml +42 -0
  98. eggpool-0.1.0/src/eggpool/dashboard/themes/Tokyo Night Storm.toml +42 -0
  99. eggpool-0.1.0/src/eggpool/dashboard/themes/VESPER.toml +42 -0
  100. eggpool-0.1.0/src/eggpool/dashboard/themes/Zenburn.toml +42 -0
  101. eggpool-0.1.0/src/eggpool/dashboard/themes/acton.toml +42 -0
  102. eggpool-0.1.0/src/eggpool/dashboard/themes/bam.toml +42 -0
  103. eggpool-0.1.0/src/eggpool/dashboard/themes/base16-atelier-forest-light.toml +45 -0
  104. eggpool-0.1.0/src/eggpool/dashboard/themes/berlin.toml +42 -0
  105. eggpool-0.1.0/src/eggpool/dashboard/themes/black but with important highlights.toml +42 -0
  106. eggpool-0.1.0/src/eggpool/dashboard/themes/broc.toml +42 -0
  107. eggpool-0.1.0/src/eggpool/dashboard/themes/cork.toml +42 -0
  108. eggpool-0.1.0/src/eggpool/dashboard/themes/ferra.toml +42 -0
  109. eggpool-0.1.0/src/eggpool/dashboard/themes/forest.toml +42 -0
  110. eggpool-0.1.0/src/eggpool/dashboard/themes/lisbon.toml +42 -0
  111. eggpool-0.1.0/src/eggpool/dashboard/themes/midnight.toml +42 -0
  112. eggpool-0.1.0/src/eggpool/dashboard/themes/oslo.toml +42 -0
  113. eggpool-0.1.0/src/eggpool/dashboard/themes/plum.toml +43 -0
  114. eggpool-0.1.0/src/eggpool/dashboard/themes/portland.toml +42 -0
  115. eggpool-0.1.0/src/eggpool/dashboard/themes/sunset.toml +42 -0
  116. eggpool-0.1.0/src/eggpool/dashboard/themes/tofino.toml +42 -0
  117. eggpool-0.1.0/src/eggpool/dashboard/themes/vanimo.toml +42 -0
  118. eggpool-0.1.0/src/eggpool/dashboard/themes/vik.toml +42 -0
  119. eggpool-0.1.0/src/eggpool/db/__init__.py +0 -0
  120. eggpool-0.1.0/src/eggpool/db/connection.py +401 -0
  121. eggpool-0.1.0/src/eggpool/db/migrations.py +125 -0
  122. eggpool-0.1.0/src/eggpool/db/repositories.py +989 -0
  123. eggpool-0.1.0/src/eggpool/db/schema/0001_initial.sql +78 -0
  124. eggpool-0.1.0/src/eggpool/db/schema/0002_indexes.sql +24 -0
  125. eggpool-0.1.0/src/eggpool/db/schema/0003_request_attempts.sql +20 -0
  126. eggpool-0.1.0/src/eggpool/db/schema/0004_integration_hardening.sql +34 -0
  127. eggpool-0.1.0/src/eggpool/db/schema/0005_price_microdollars.sql +15 -0
  128. eggpool-0.1.0/src/eggpool/db/schema/0006_correct_price_microdollars.sql +12 -0
  129. eggpool-0.1.0/src/eggpool/db/schema/0007_price_cache_rates.sql +5 -0
  130. eggpool-0.1.0/src/eggpool/db/schema/0008_proxy_request_identity.sql +9 -0
  131. eggpool-0.1.0/src/eggpool/db/schema/0009_model_protocol_source.sql +3 -0
  132. eggpool-0.1.0/src/eggpool/db/schema/0010_health_probe.sql +5 -0
  133. eggpool-0.1.0/src/eggpool/db/schema/0011_model_resolution_status.sql +1 -0
  134. eggpool-0.1.0/src/eggpool/db/schema/0012_drop_reservations_estimated_microdollars.sql +6 -0
  135. eggpool-0.1.0/src/eggpool/db/schema/0013_request_attempts_account_id_index.sql +7 -0
  136. eggpool-0.1.0/src/eggpool/db/schema/0014_bandwidth_tracking.sql +2 -0
  137. eggpool-0.1.0/src/eggpool/db/schema/0015_multi_provider.sql +23 -0
  138. eggpool-0.1.0/src/eggpool/db/schema/0016_requests_provider_id.sql +2 -0
  139. eggpool-0.1.0/src/eggpool/db/schema/0017_price_snapshots_provider_id.sql +4 -0
  140. eggpool-0.1.0/src/eggpool/db/schema/0018_provider_pings.sql +16 -0
  141. eggpool-0.1.0/src/eggpool/db/schema/0019_client_ip.sql +4 -0
  142. eggpool-0.1.0/src/eggpool/db/schema/0020_performance_indexes.sql +19 -0
  143. eggpool-0.1.0/src/eggpool/db/schema/0021_provider_model_metadata.sql +19 -0
  144. eggpool-0.1.0/src/eggpool/db/schema/0022_dashboard_indexes.sql +14 -0
  145. eggpool-0.1.0/src/eggpool/db/schema/checksums.json +26 -0
  146. eggpool-0.1.0/src/eggpool/deploy/__init__.py +126 -0
  147. eggpool-0.1.0/src/eggpool/errors.py +123 -0
  148. eggpool-0.1.0/src/eggpool/health/__init__.py +8 -0
  149. eggpool-0.1.0/src/eggpool/health/circuit_breaker.py +146 -0
  150. eggpool-0.1.0/src/eggpool/health/health_manager.py +339 -0
  151. eggpool-0.1.0/src/eggpool/integrations/__init__.py +1 -0
  152. eggpool-0.1.0/src/eggpool/integrations/opencode.py +90 -0
  153. eggpool-0.1.0/src/eggpool/logging.py +50 -0
  154. eggpool-0.1.0/src/eggpool/models/__init__.py +0 -0
  155. eggpool-0.1.0/src/eggpool/models/api.py +32 -0
  156. eggpool-0.1.0/src/eggpool/models/config.py +658 -0
  157. eggpool-0.1.0/src/eggpool/models/database.py +99 -0
  158. eggpool-0.1.0/src/eggpool/models/domain.py +59 -0
  159. eggpool-0.1.0/src/eggpool/onboard.py +111 -0
  160. eggpool-0.1.0/src/eggpool/providers/__init__.py +0 -0
  161. eggpool-0.1.0/src/eggpool/providers/_templates.toml +574 -0
  162. eggpool-0.1.0/src/eggpool/providers/client_pool.py +131 -0
  163. eggpool-0.1.0/src/eggpool/providers/connect.py +988 -0
  164. eggpool-0.1.0/src/eggpool/providers/contract.py +91 -0
  165. eggpool-0.1.0/src/eggpool/providers/pproxy_transport.py +293 -0
  166. eggpool-0.1.0/src/eggpool/proxy/__init__.py +1 -0
  167. eggpool-0.1.0/src/eggpool/proxy/client.py +140 -0
  168. eggpool-0.1.0/src/eggpool/proxy/sse_observer.py +283 -0
  169. eggpool-0.1.0/src/eggpool/proxy/usage.py +114 -0
  170. eggpool-0.1.0/src/eggpool/py.typed +1 -0
  171. eggpool-0.1.0/src/eggpool/quota/__init__.py +13 -0
  172. eggpool-0.1.0/src/eggpool/quota/estimation.py +639 -0
  173. eggpool-0.1.0/src/eggpool/quota/reservation.py +193 -0
  174. eggpool-0.1.0/src/eggpool/quota/scorer.py +215 -0
  175. eggpool-0.1.0/src/eggpool/request/__init__.py +13 -0
  176. eggpool-0.1.0/src/eggpool/request/attempt_finalizer.py +125 -0
  177. eggpool-0.1.0/src/eggpool/request/body.py +70 -0
  178. eggpool-0.1.0/src/eggpool/request/coordinator.py +1638 -0
  179. eggpool-0.1.0/src/eggpool/request/finalizer.py +392 -0
  180. eggpool-0.1.0/src/eggpool/request/limits.py +152 -0
  181. eggpool-0.1.0/src/eggpool/retry/__init__.py +7 -0
  182. eggpool-0.1.0/src/eggpool/retry/classification.py +207 -0
  183. eggpool-0.1.0/src/eggpool/routing/__init__.py +1 -0
  184. eggpool-0.1.0/src/eggpool/routing/eligibility.py +95 -0
  185. eggpool-0.1.0/src/eggpool/routing/provider.py +20 -0
  186. eggpool-0.1.0/src/eggpool/routing/router.py +395 -0
  187. eggpool-0.1.0/src/eggpool/security/__init__.py +7 -0
  188. eggpool-0.1.0/src/eggpool/security/redaction.py +327 -0
  189. eggpool-0.1.0/src/eggpool/stats/__init__.py +37 -0
  190. eggpool-0.1.0/src/eggpool/stats/queries.py +598 -0
  191. eggpool-0.1.0/src/eggpool/stats/service.py +548 -0
  192. eggpool-0.1.0/src/eggpool/toml_edit.py +101 -0
  193. 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/*
@@ -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
@@ -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.