powergrid-ai 1.0.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.
- powergrid_ai-1.0.0/.github/workflows/ci.yml +66 -0
- powergrid_ai-1.0.0/.gitignore +52 -0
- powergrid_ai-1.0.0/CHANGELOG.md +37 -0
- powergrid_ai-1.0.0/LICENSE +21 -0
- powergrid_ai-1.0.0/PKG-INFO +416 -0
- powergrid_ai-1.0.0/README.md +375 -0
- powergrid_ai-1.0.0/config.example.yaml +102 -0
- powergrid_ai-1.0.0/powergrid/__init__.py +3 -0
- powergrid_ai-1.0.0/powergrid/__main__.py +6 -0
- powergrid_ai-1.0.0/powergrid/api/__init__.py +0 -0
- powergrid_ai-1.0.0/powergrid/api/app.py +232 -0
- powergrid_ai-1.0.0/powergrid/api/middleware/__init__.py +0 -0
- powergrid_ai-1.0.0/powergrid/api/middleware/logging.py +51 -0
- powergrid_ai-1.0.0/powergrid/api/routes/__init__.py +0 -0
- powergrid_ai-1.0.0/powergrid/api/routes/chat.py +202 -0
- powergrid_ai-1.0.0/powergrid/api/routes/models.py +37 -0
- powergrid_ai-1.0.0/powergrid/api/routes/status.py +84 -0
- powergrid_ai-1.0.0/powergrid/cli/__init__.py +0 -0
- powergrid_ai-1.0.0/powergrid/cli/app.py +748 -0
- powergrid_ai-1.0.0/powergrid/config/__init__.py +0 -0
- powergrid_ai-1.0.0/powergrid/config/settings.py +178 -0
- powergrid_ai-1.0.0/powergrid/core/__init__.py +0 -0
- powergrid_ai-1.0.0/powergrid/core/cache/__init__.py +0 -0
- powergrid_ai-1.0.0/powergrid/core/cache/manager.py +85 -0
- powergrid_ai-1.0.0/powergrid/core/health/__init__.py +0 -0
- powergrid_ai-1.0.0/powergrid/core/health/tracker.py +127 -0
- powergrid_ai-1.0.0/powergrid/core/quota/__init__.py +0 -0
- powergrid_ai-1.0.0/powergrid/core/quota/engine.py +175 -0
- powergrid_ai-1.0.0/powergrid/core/retry/__init__.py +0 -0
- powergrid_ai-1.0.0/powergrid/core/retry/handler.py +222 -0
- powergrid_ai-1.0.0/powergrid/core/router/__init__.py +0 -0
- powergrid_ai-1.0.0/powergrid/core/router/analyzer.py +156 -0
- powergrid_ai-1.0.0/powergrid/core/router/engine.py +288 -0
- powergrid_ai-1.0.0/powergrid/credentials.py +235 -0
- powergrid_ai-1.0.0/powergrid/models/__init__.py +0 -0
- powergrid_ai-1.0.0/powergrid/models/schemas.py +152 -0
- powergrid_ai-1.0.0/powergrid/providers/__init__.py +4 -0
- powergrid_ai-1.0.0/powergrid/providers/base.py +171 -0
- powergrid_ai-1.0.0/powergrid/providers/gemini.py +152 -0
- powergrid_ai-1.0.0/powergrid/providers/mock.py +153 -0
- powergrid_ai-1.0.0/powergrid/providers/openai_compatible.py +119 -0
- powergrid_ai-1.0.0/powergrid/providers/registry.py +63 -0
- powergrid_ai-1.0.0/powergrid/storage/__init__.py +0 -0
- powergrid_ai-1.0.0/powergrid/storage/database.py +407 -0
- powergrid_ai-1.0.0/pyproject.toml +75 -0
- powergrid_ai-1.0.0/tests/__init__.py +0 -0
- powergrid_ai-1.0.0/tests/smoke_test.py +74 -0
- powergrid_ai-1.0.0/tests/test_api.py +149 -0
- powergrid_ai-1.0.0/tests/test_cache.py +128 -0
- powergrid_ai-1.0.0/tests/test_failover.py +86 -0
- powergrid_ai-1.0.0/tests/test_mock_provider.py +55 -0
- powergrid_ai-1.0.0/tests/test_quota.py +133 -0
- powergrid_ai-1.0.0/tests/test_real_e2e.py +155 -0
- powergrid_ai-1.0.0/tests/test_real_groq.py +44 -0
- powergrid_ai-1.0.0/tests/test_routing.py +268 -0
- powergrid_ai-1.0.0/tests/test_security.py +89 -0
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main, master]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main, master]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
runs-on: ${{ matrix.os }}
|
|
12
|
+
strategy:
|
|
13
|
+
matrix:
|
|
14
|
+
os: [ubuntu-latest, windows-latest]
|
|
15
|
+
python-version: ["3.11", "3.12", "3.13"]
|
|
16
|
+
fail-fast: false
|
|
17
|
+
|
|
18
|
+
steps:
|
|
19
|
+
- uses: actions/checkout@v4
|
|
20
|
+
|
|
21
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
22
|
+
uses: actions/setup-python@v5
|
|
23
|
+
with:
|
|
24
|
+
python-version: ${{ matrix.python-version }}
|
|
25
|
+
|
|
26
|
+
- name: Install dependencies
|
|
27
|
+
run: |
|
|
28
|
+
python -m pip install --upgrade pip
|
|
29
|
+
pip install -e ".[dev]"
|
|
30
|
+
|
|
31
|
+
- name: Lint with ruff
|
|
32
|
+
run: |
|
|
33
|
+
ruff check powergrid/ tests/
|
|
34
|
+
ruff format --check powergrid/ tests/
|
|
35
|
+
|
|
36
|
+
- name: Type check with mypy
|
|
37
|
+
run: mypy powergrid/ --ignore-missing-imports
|
|
38
|
+
continue-on-error: true
|
|
39
|
+
|
|
40
|
+
- name: Run tests
|
|
41
|
+
run: pytest tests/ -v --tb=short -x
|
|
42
|
+
|
|
43
|
+
publish:
|
|
44
|
+
needs: test
|
|
45
|
+
runs-on: ubuntu-latest
|
|
46
|
+
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
|
47
|
+
permissions:
|
|
48
|
+
id-token: write
|
|
49
|
+
steps:
|
|
50
|
+
- uses: actions/checkout@v4
|
|
51
|
+
|
|
52
|
+
- name: Set up Python
|
|
53
|
+
uses: actions/setup-python@v5
|
|
54
|
+
with:
|
|
55
|
+
python-version: "3.12"
|
|
56
|
+
|
|
57
|
+
- name: Install build tools
|
|
58
|
+
run: pip install build
|
|
59
|
+
|
|
60
|
+
- name: Build package
|
|
61
|
+
run: python -m build
|
|
62
|
+
|
|
63
|
+
- name: Publish to PyPI
|
|
64
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
65
|
+
with:
|
|
66
|
+
skip-existing: true
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
*.so
|
|
6
|
+
*.egg
|
|
7
|
+
*.egg-info/
|
|
8
|
+
dist/
|
|
9
|
+
build/
|
|
10
|
+
eggs/
|
|
11
|
+
*.whl
|
|
12
|
+
pip-log.txt
|
|
13
|
+
pip-delete-this-directory.txt
|
|
14
|
+
|
|
15
|
+
# Virtual environments
|
|
16
|
+
venv/
|
|
17
|
+
.venv/
|
|
18
|
+
env/
|
|
19
|
+
ENV/
|
|
20
|
+
|
|
21
|
+
# IDE
|
|
22
|
+
.vscode/
|
|
23
|
+
.idea/
|
|
24
|
+
*.swp
|
|
25
|
+
*.swo
|
|
26
|
+
*~
|
|
27
|
+
|
|
28
|
+
# OS
|
|
29
|
+
.DS_Store
|
|
30
|
+
Thumbs.db
|
|
31
|
+
desktop.ini
|
|
32
|
+
|
|
33
|
+
# PowerGrid runtime
|
|
34
|
+
*.db
|
|
35
|
+
*.db-journal
|
|
36
|
+
powergrid.pid
|
|
37
|
+
|
|
38
|
+
# Test / coverage
|
|
39
|
+
.pytest_cache/
|
|
40
|
+
htmlcov/
|
|
41
|
+
.coverage
|
|
42
|
+
.coverage.*
|
|
43
|
+
coverage.xml
|
|
44
|
+
|
|
45
|
+
# Distribution
|
|
46
|
+
*.tar.gz
|
|
47
|
+
*.zip
|
|
48
|
+
|
|
49
|
+
# Environment / secrets
|
|
50
|
+
.env
|
|
51
|
+
.env.*
|
|
52
|
+
!.env.example
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to PowerGrid will be 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
|
+
## [1.0.0] - 2026-09-01
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- **Routing Engine** — intelligent, round-robin, and priority routing strategies
|
|
12
|
+
- **Provider Adapters** — OpenAI-compatible, Google Gemini, and Mock providers
|
|
13
|
+
- **Health Tracking** — automatic provider health monitoring with cooldown and exponential backoff
|
|
14
|
+
- **Quota Engine** — RPM/TPM/RPD/TPD tracking with predictive rate limit protection
|
|
15
|
+
- **Retry & Failover** — automatic retries across providers with exponential backoff and jitter
|
|
16
|
+
- **Response Cache** — TTL-based exact-match caching to avoid redundant API calls
|
|
17
|
+
- **Request Analyzer** — task type classification (coding, reasoning, creative, etc.)
|
|
18
|
+
- **CLI** — 15+ commands: init, start, stop, status, providers, models, logs, test, config, cache-clear
|
|
19
|
+
- **Interactive Provider Wizard** — add providers with templates for 8+ services (Groq, Cerebras, Gemini, OpenRouter, Together, NVIDIA, Mistral, Azure OpenAI)
|
|
20
|
+
- **Secure Credential Storage** — OS keyring integration (Windows Credential Manager, macOS Keychain, Linux Secret Service) with file fallback
|
|
21
|
+
- **OpenAI-Compatible API** — drop-in replacement for any OpenAI SDK usage
|
|
22
|
+
- **Streaming Support** — SSE streaming for chat completions
|
|
23
|
+
- **Auth Protection** — optional API token for endpoint security
|
|
24
|
+
- **Request Logging** — middleware with correlation IDs and timing headers
|
|
25
|
+
- **Mock Provider** — simulate success, 429s, timeouts, and errors for testing
|
|
26
|
+
- **GitHub Actions CI** — automated testing on Python 3.11/3.12/3.13, Ubuntu and Windows
|
|
27
|
+
- **Graceful Shutdown** — clean provider connection cleanup on SIGTERM/SIGINT
|
|
28
|
+
- **Startup Health Checks** — verify provider connectivity on boot
|
|
29
|
+
- **Request Validation** — message count and size limits to prevent abuse
|
|
30
|
+
- **Localhost-Only CORS** — secure default for local development
|
|
31
|
+
|
|
32
|
+
### Security
|
|
33
|
+
- API keys never appear in logs, API responses, or config dumps
|
|
34
|
+
- Auth token protection on `/v1/` endpoints
|
|
35
|
+
- Localhost-only binding by default with explicit warning for `0.0.0.0`
|
|
36
|
+
- OS credential store for API keys (not plaintext config files)
|
|
37
|
+
- Request body size limits (1MB)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Michael Moses
|
|
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.
|
|
@@ -0,0 +1,416 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: powergrid-ai
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Local-first AI routing runtime — one AI, many engines
|
|
5
|
+
Project-URL: Homepage, https://github.com/micymike/powergrid
|
|
6
|
+
Project-URL: Repository, https://github.com/micymike/powergrid
|
|
7
|
+
Project-URL: Issues, https://github.com/micymike/powergrid/issues
|
|
8
|
+
Project-URL: Changelog, https://github.com/micymike/powergrid/blob/main/CHANGELOG.md
|
|
9
|
+
Author-email: Michael Moses <micymike@example.com>
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: ai,failover,llm,local-first,multi-provider,openai,routing
|
|
13
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
21
|
+
Requires-Python: >=3.11
|
|
22
|
+
Requires-Dist: aiosqlite>=0.20.0
|
|
23
|
+
Requires-Dist: click>=8.1.0
|
|
24
|
+
Requires-Dist: fastapi>=0.115.0
|
|
25
|
+
Requires-Dist: httpx>=0.27.0
|
|
26
|
+
Requires-Dist: keyring>=25.0.0
|
|
27
|
+
Requires-Dist: pydantic-settings>=2.5.0
|
|
28
|
+
Requires-Dist: pydantic>=2.9.0
|
|
29
|
+
Requires-Dist: pyyaml>=6.0.0
|
|
30
|
+
Requires-Dist: rich>=13.9.0
|
|
31
|
+
Requires-Dist: sqlalchemy>=2.0.0
|
|
32
|
+
Requires-Dist: sse-starlette>=2.1.0
|
|
33
|
+
Requires-Dist: typer>=0.12.0
|
|
34
|
+
Requires-Dist: uvicorn[standard]>=0.30.0
|
|
35
|
+
Provides-Extra: dev
|
|
36
|
+
Requires-Dist: mypy>=1.11.0; extra == 'dev'
|
|
37
|
+
Requires-Dist: pytest-asyncio>=0.24.0; extra == 'dev'
|
|
38
|
+
Requires-Dist: pytest>=8.3.0; extra == 'dev'
|
|
39
|
+
Requires-Dist: ruff>=0.6.0; extra == 'dev'
|
|
40
|
+
Description-Content-Type: text/markdown
|
|
41
|
+
|
|
42
|
+
# PowerGrid
|
|
43
|
+
|
|
44
|
+
**Local-first AI routing runtime — one AI, many engines.**
|
|
45
|
+
|
|
46
|
+
PowerGrid runs on your machine and makes multiple AI providers appear as a single OpenAI-compatible endpoint. Your applications talk to `localhost:8787`, and PowerGrid intelligently routes requests across your available providers.
|
|
47
|
+
|
|
48
|
+
[](https://github.com/micymike/powergrid/actions/workflows/ci.yml)
|
|
49
|
+
[](https://pypi.org/project/powergrid/)
|
|
50
|
+
[](https://www.python.org/downloads/)
|
|
51
|
+
|
|
52
|
+
## Why PowerGrid?
|
|
53
|
+
|
|
54
|
+
Developers often have access to multiple AI providers (Groq, Gemini, Cerebras, OpenRouter, etc.). PowerGrid lets you:
|
|
55
|
+
|
|
56
|
+
- **Use all your providers through one endpoint** — no app changes needed
|
|
57
|
+
- **Get automatic failover** — if one provider is down or rate-limited, traffic shifts seamlessly
|
|
58
|
+
- **Predict rate limits** — PowerGrid tracks usage and shifts traffic *before* you hit 429s
|
|
59
|
+
- **Stay local** — your API keys never leave your machine
|
|
60
|
+
- **Stay free** — PowerGrid itself costs $0/month
|
|
61
|
+
|
|
62
|
+
## Quick Start
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
# Install
|
|
66
|
+
pip install powergrid-ai
|
|
67
|
+
|
|
68
|
+
# Initialize
|
|
69
|
+
powergrid init
|
|
70
|
+
|
|
71
|
+
# Add a provider
|
|
72
|
+
powergrid provider
|
|
73
|
+
|
|
74
|
+
# Start
|
|
75
|
+
powergrid start
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Then use it:
|
|
79
|
+
|
|
80
|
+
```python
|
|
81
|
+
from openai import OpenAI
|
|
82
|
+
|
|
83
|
+
client = OpenAI(
|
|
84
|
+
base_url="http://localhost:8787/v1",
|
|
85
|
+
api_key="powergrid"
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
response = client.chat.completions.create(
|
|
89
|
+
model="powergrid-auto",
|
|
90
|
+
messages=[{"role": "user", "content": "Hello!"}]
|
|
91
|
+
)
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Architecture
|
|
95
|
+
|
|
96
|
+
```
|
|
97
|
+
APPLICATION
|
|
98
|
+
|
|
|
99
|
+
v
|
|
100
|
+
+----------------+
|
|
101
|
+
| POWERGRID |
|
|
102
|
+
| Local Runtime |
|
|
103
|
+
+-------+--------+
|
|
104
|
+
|
|
|
105
|
+
Request Analyzer
|
|
106
|
+
|
|
|
107
|
+
Routing Engine
|
|
108
|
+
|
|
|
109
|
+
+---------------+---------------+
|
|
110
|
+
| | |
|
|
111
|
+
v v v
|
|
112
|
+
Gemini Groq Cerebras
|
|
113
|
+
| | |
|
|
114
|
+
+---------------+---------------+
|
|
115
|
+
|
|
|
116
|
+
v
|
|
117
|
+
Unified Response
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Core Components
|
|
121
|
+
|
|
122
|
+
| Component | Purpose |
|
|
123
|
+
|-----------|---------|
|
|
124
|
+
| **Provider Adapters** | Normalize provider APIs into a common format |
|
|
125
|
+
| **Routing Engine** | Score and select the best provider per request |
|
|
126
|
+
| **Health Tracker** | Monitor provider availability and apply cooldowns |
|
|
127
|
+
| **Quota Engine** | Track usage and predict rate limit exhaustion |
|
|
128
|
+
| **Retry Handler** | Manage retries, failover, and backoff |
|
|
129
|
+
| **Cache** | Exact-match response caching with TTL |
|
|
130
|
+
| **Request Analyzer** | Classify task types with heuristics |
|
|
131
|
+
|
|
132
|
+
## Provider Configuration
|
|
133
|
+
|
|
134
|
+
### Interactive Setup
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
powergrid provider
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### YAML Configuration
|
|
141
|
+
|
|
142
|
+
Copy `config.example.yaml` to `~/.config/powergrid/config.yaml`:
|
|
143
|
+
|
|
144
|
+
```yaml
|
|
145
|
+
providers:
|
|
146
|
+
- name: groq
|
|
147
|
+
type: openai_compatible
|
|
148
|
+
base_url: https://api.groq.com/openai/v1
|
|
149
|
+
api_key_env: GROQ_API_KEY
|
|
150
|
+
models:
|
|
151
|
+
- llama-3.3-70b-versatile
|
|
152
|
+
priority: 90
|
|
153
|
+
rpm_limit: 30
|
|
154
|
+
|
|
155
|
+
- name: gemini
|
|
156
|
+
type: gemini
|
|
157
|
+
api_key_env: GEMINI_API_KEY
|
|
158
|
+
models:
|
|
159
|
+
- gemini-2.0-flash
|
|
160
|
+
priority: 85
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### Environment Variables
|
|
164
|
+
|
|
165
|
+
Set your API keys:
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
export GROQ_API_KEY=gsk_...
|
|
169
|
+
export GEMINI_API_KEY=AI...
|
|
170
|
+
export CEREBRAS_API_KEY=...
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
## Running
|
|
174
|
+
|
|
175
|
+
### Foreground
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
powergrid start
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### Background
|
|
182
|
+
|
|
183
|
+
```bash
|
|
184
|
+
powergrid start --background
|
|
185
|
+
powergrid status
|
|
186
|
+
powergrid stop
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### Custom Port/Host
|
|
190
|
+
|
|
191
|
+
```bash
|
|
192
|
+
powergrid start --port 9000 --host 127.0.0.1
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
## API Endpoints
|
|
196
|
+
|
|
197
|
+
| Endpoint | Method | Description |
|
|
198
|
+
|----------|--------|-------------|
|
|
199
|
+
| `/v1/models` | GET | List available models |
|
|
200
|
+
| `/v1/chat/completions` | POST | Chat completions (OpenAI-compatible) |
|
|
201
|
+
| `/health` | GET | Health check |
|
|
202
|
+
| `/status` | GET | PowerGrid status and stats |
|
|
203
|
+
| `/providers` | GET | Provider details |
|
|
204
|
+
| `/routing/logs` | GET | Recent routing decisions |
|
|
205
|
+
| `/routing/stats` | GET | Aggregate statistics |
|
|
206
|
+
|
|
207
|
+
## Agent Configuration
|
|
208
|
+
|
|
209
|
+
### OpenCode
|
|
210
|
+
|
|
211
|
+
In `opencode.json`:
|
|
212
|
+
|
|
213
|
+
```json
|
|
214
|
+
{
|
|
215
|
+
"provider": {
|
|
216
|
+
"name": "powergrid",
|
|
217
|
+
"model": "powergrid-auto",
|
|
218
|
+
"api_key": "powergrid",
|
|
219
|
+
"base_url": "http://localhost:8787/v1"
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
### Codex
|
|
225
|
+
|
|
226
|
+
```bash
|
|
227
|
+
export OPENAI_BASE_URL=http://localhost:8787/v1
|
|
228
|
+
export OPENAI_API_KEY=powergrid
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
### Cline
|
|
232
|
+
|
|
233
|
+
In VS Code settings:
|
|
234
|
+
|
|
235
|
+
```json
|
|
236
|
+
{
|
|
237
|
+
"cline.apiProvider": "openai-compatible",
|
|
238
|
+
"cline.openaiCompatibleBaseUrl": "http://localhost:8787/v1",
|
|
239
|
+
"cline.openaiCompatibleApiKey": "powergrid",
|
|
240
|
+
"cline.openaiCompatibleModelId": "powergrid-auto"
|
|
241
|
+
}
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
### Python (OpenAI SDK)
|
|
245
|
+
|
|
246
|
+
```python
|
|
247
|
+
from openai import OpenAI
|
|
248
|
+
|
|
249
|
+
client = OpenAI(
|
|
250
|
+
base_url="http://localhost:8787/v1",
|
|
251
|
+
api_key="powergrid"
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
response = client.chat.completions.create(
|
|
255
|
+
model="powergrid-auto",
|
|
256
|
+
messages=[{"role": "user", "content": "Hello!"}]
|
|
257
|
+
)
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
### Node.js
|
|
261
|
+
|
|
262
|
+
```javascript
|
|
263
|
+
import OpenAI from "openai";
|
|
264
|
+
|
|
265
|
+
const client = new OpenAI({
|
|
266
|
+
baseURL: "http://localhost:8787/v1",
|
|
267
|
+
apiKey: "powergrid",
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
const response = await client.chat.completions.create({
|
|
271
|
+
model: "powergrid-auto",
|
|
272
|
+
messages: [{ role: "user", content: "Hello!" }],
|
|
273
|
+
});
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
### curl
|
|
277
|
+
|
|
278
|
+
```bash
|
|
279
|
+
curl http://localhost:8787/v1/chat/completions \
|
|
280
|
+
-H "Content-Type: application/json" \
|
|
281
|
+
-d '{
|
|
282
|
+
"model": "powergrid-auto",
|
|
283
|
+
"messages": [{"role": "user", "content": "Hello!"}]
|
|
284
|
+
}'
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
## Routing
|
|
288
|
+
|
|
289
|
+
PowerGrid supports three routing strategies:
|
|
290
|
+
|
|
291
|
+
### Intelligent (default)
|
|
292
|
+
|
|
293
|
+
Scores providers on health, quota, capability, priority, and latency. Best for most use cases.
|
|
294
|
+
|
|
295
|
+
### Round Robin
|
|
296
|
+
|
|
297
|
+
Cycles through providers evenly. Good for load distribution.
|
|
298
|
+
|
|
299
|
+
### Priority
|
|
300
|
+
|
|
301
|
+
Always selects the highest-priority provider. Good when you have a clear preference order.
|
|
302
|
+
|
|
303
|
+
## How Failover Works
|
|
304
|
+
|
|
305
|
+
1. Request arrives and is routed to the best provider
|
|
306
|
+
2. If the provider returns **429** (rate limited), PowerGrid:
|
|
307
|
+
- Parses the `Retry-After` header
|
|
308
|
+
- Puts the provider in cooldown
|
|
309
|
+
- Selects the next-best provider
|
|
310
|
+
3. If the provider returns **5xx** or **times out**:
|
|
311
|
+
- Applies exponential backoff with jitter
|
|
312
|
+
- Fails over to another provider
|
|
313
|
+
4. A request retries up to `max_retries` times across different providers
|
|
314
|
+
5. If all providers fail, a clean error is returned
|
|
315
|
+
|
|
316
|
+
## Predictive Rate Limit Protection
|
|
317
|
+
|
|
318
|
+
PowerGrid tracks request rates over time and estimates when a provider is approaching its limits:
|
|
319
|
+
|
|
320
|
+
```
|
|
321
|
+
Groq RPM = 30
|
|
322
|
+
Recent: 18:01 → 21 requests, 18:02 → 26 requests, 18:03 → 29 requests
|
|
323
|
+
→ PowerGrid shifts traffic BEFORE the 429 hits
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
## CLI Commands
|
|
327
|
+
|
|
328
|
+
| Command | Description |
|
|
329
|
+
|---------|-------------|
|
|
330
|
+
| `powergrid init` | Initialize configuration |
|
|
331
|
+
| `powergrid start` | Start the runtime |
|
|
332
|
+
| `powergrid stop` | Stop background process |
|
|
333
|
+
| `powergrid status` | Show runtime status |
|
|
334
|
+
| `powergrid providers` | List configured providers |
|
|
335
|
+
| `powergrid provider` | Add provider interactively |
|
|
336
|
+
| `powergrid provider-remove NAME` | Remove a provider |
|
|
337
|
+
| `powergrid models` | List virtual models |
|
|
338
|
+
| `powergrid logs` | Show recent routing decisions |
|
|
339
|
+
| `powergrid test` | Test provider connectivity |
|
|
340
|
+
| `powergrid config` | Show config (redacted) |
|
|
341
|
+
| `powergrid cache-clear` | Clear response cache |
|
|
342
|
+
|
|
343
|
+
## Security
|
|
344
|
+
|
|
345
|
+
- **Local-only by default** — binds to `127.0.0.1`
|
|
346
|
+
- **API key protection** — optional `auth_token` for the endpoint
|
|
347
|
+
- **No key leakage** — API keys never appear in logs or API responses
|
|
348
|
+
- **Config redaction** — `powergrid config` masks secrets
|
|
349
|
+
- **Explicit 0.0.0.0 warning** — warns if you bind publicly
|
|
350
|
+
|
|
351
|
+
## Privacy
|
|
352
|
+
|
|
353
|
+
- PowerGrid is fully local
|
|
354
|
+
- No telemetry is sent anywhere
|
|
355
|
+
- No API keys leave your machine
|
|
356
|
+
- No prompts or responses are stored externally
|
|
357
|
+
- All state lives in `~/.config/powergrid/`
|
|
358
|
+
|
|
359
|
+
## Development
|
|
360
|
+
|
|
361
|
+
```bash
|
|
362
|
+
git clone https://github.com/micymike/powergrid.git
|
|
363
|
+
cd powergrid
|
|
364
|
+
pip install -e ".[dev]"
|
|
365
|
+
pytest
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
### Mock Provider
|
|
369
|
+
|
|
370
|
+
For testing without real API keys:
|
|
371
|
+
|
|
372
|
+
```yaml
|
|
373
|
+
providers:
|
|
374
|
+
- name: mock
|
|
375
|
+
type: mock
|
|
376
|
+
models:
|
|
377
|
+
- mock-model
|
|
378
|
+
priority: 50
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
The mock provider simulates success, 429s, timeouts, and errors.
|
|
382
|
+
|
|
383
|
+
## Adding Providers
|
|
384
|
+
|
|
385
|
+
Implement the `ProviderAdapter` interface:
|
|
386
|
+
|
|
387
|
+
```python
|
|
388
|
+
from powergrid.providers.base import ProviderAdapter, ProviderModel, ProviderStatus
|
|
389
|
+
|
|
390
|
+
class MyProvider(ProviderAdapter):
|
|
391
|
+
name = "my_provider"
|
|
392
|
+
display_name = "My Provider"
|
|
393
|
+
|
|
394
|
+
async def chat_completion(self, request, model_id):
|
|
395
|
+
# Call provider API
|
|
396
|
+
...
|
|
397
|
+
|
|
398
|
+
async def chat_completion_stream(self, request, model_id):
|
|
399
|
+
# Stream SSE chunks
|
|
400
|
+
...
|
|
401
|
+
|
|
402
|
+
async def health_check(self):
|
|
403
|
+
return ProviderStatus.HEALTHY
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
Register it:
|
|
407
|
+
|
|
408
|
+
```python
|
|
409
|
+
from powergrid.providers.registry import registry
|
|
410
|
+
|
|
411
|
+
registry.register(MyProvider(name="my_provider", api_key="..."))
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
## License
|
|
415
|
+
|
|
416
|
+
MIT
|