synckey 0.1.0__py3-none-any.whl
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.
- synckey/__init__.py +3 -0
- synckey/__main__.py +4 -0
- synckey/bucket.py +140 -0
- synckey/cli.py +1481 -0
- synckey/config.py +150 -0
- synckey/context.py +75 -0
- synckey/crypto.py +41 -0
- synckey/db.py +426 -0
- synckey/deferred.py +41 -0
- synckey/keypool.py +139 -0
- synckey/prefs.py +82 -0
- synckey/providers.py +189 -0
- synckey/router.py +291 -0
- synckey/server.py +672 -0
- synckey/state.py +152 -0
- synckey/tiers.py +157 -0
- synckey/usage.py +99 -0
- synckey-0.1.0.dist-info/METADATA +799 -0
- synckey-0.1.0.dist-info/RECORD +21 -0
- synckey-0.1.0.dist-info/WHEEL +4 -0
- synckey-0.1.0.dist-info/entry_points.txt +2 -0
|
@@ -0,0 +1,799 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: synckey
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: One API key for every AI provider. Automatic failover, tier-based fallback, and zero rate-limit headaches.
|
|
5
|
+
Project-URL: Homepage, https://github.com/pmdnawaz1/synckey
|
|
6
|
+
Project-URL: Documentation, https://github.com/pmdnawaz1/synckey#readme
|
|
7
|
+
Project-URL: Repository, https://github.com/pmdnawaz1/synckey
|
|
8
|
+
Project-URL: Issues, https://github.com/pmdnawaz1/synckey/issues
|
|
9
|
+
Project-URL: Changelog, https://github.com/pmdnawaz1/synckey/releases
|
|
10
|
+
Author-email: synckey <synckey@example.com>
|
|
11
|
+
License: MIT
|
|
12
|
+
Keywords: ai,api-key,cerebras,cohere,gateway,gemini,groq,llm,nvidia,openai,proxy,routing
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
22
|
+
Requires-Python: >=3.10
|
|
23
|
+
Requires-Dist: cryptography>=42.0
|
|
24
|
+
Requires-Dist: fastapi>=0.110
|
|
25
|
+
Requires-Dist: httpx>=0.27
|
|
26
|
+
Requires-Dist: orjson>=3.9
|
|
27
|
+
Requires-Dist: rich>=13.7
|
|
28
|
+
Requires-Dist: typer>=0.12
|
|
29
|
+
Requires-Dist: uvicorn[standard]>=0.29
|
|
30
|
+
Provides-Extra: dev
|
|
31
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
32
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
33
|
+
Requires-Dist: respx>=0.21; extra == 'dev'
|
|
34
|
+
Description-Content-Type: text/markdown
|
|
35
|
+
|
|
36
|
+
# synckey
|
|
37
|
+
|
|
38
|
+
<h1 align="center">
|
|
39
|
+
<img src="https://img.shields.io/github/stars/pmdnawaz1/synckey?style=flat&color=20MDFF" alt="Stars"/>
|
|
40
|
+
<img src="https://img.shields.io/pypi/v/synckey?style=flat&color=20MDFF" alt="PyPI"/>
|
|
41
|
+
<img src="https://img.shields.io/pypi/l/synckey?style=flat&color=20MDFF" alt="License"/>
|
|
42
|
+
<br/>
|
|
43
|
+
One API key. Every AI model. Zero headaches.
|
|
44
|
+
</h1>
|
|
45
|
+
|
|
46
|
+
**synckey** lets your app use Gemini, Groq, OpenAI, Anthropic, Cerebras, Mistral, DeepSeek, xAI, and 40+ other providers — through a single OpenAI-compatible API key. No code changes. No rate limit nightmares. Just one endpoint.
|
|
47
|
+
|
|
48
|
+
```python
|
|
49
|
+
from openai import OpenAI
|
|
50
|
+
|
|
51
|
+
client = OpenAI(
|
|
52
|
+
base_url="http://localhost:8787/v1",
|
|
53
|
+
api_key="sk-synckey-abc123..." # ← one key for everything
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
# Just works. synckey routes to whichever provider has the model.
|
|
57
|
+
client.chat.completions.create(
|
|
58
|
+
model="claude-sonnet-4-6",
|
|
59
|
+
messages=[{"role": "user", "content": "Write me a poem"}]
|
|
60
|
+
)
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## Why synckey?
|
|
66
|
+
|
|
67
|
+
| Problem | synckey Solution |
|
|
68
|
+
|---------|------------------|
|
|
69
|
+
| Managing 10 different API keys | **One key** for all providers |
|
|
70
|
+
| Rate limits breaking production | **Automatic failover** to the next available provider |
|
|
71
|
+
| Expensive models during peak | **Tier-based fallback** → uses cheaper models when others are throttled |
|
|
72
|
+
| Switching providers mid-code | **Zero code changes** — just change the model name |
|
|
73
|
+
| Key rotation nightmares | **One command** to rotate: `synckey renew` |
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## Quick Start
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
# 1. Install
|
|
81
|
+
pip install synckey
|
|
82
|
+
|
|
83
|
+
# 2. Guided setup (30 seconds)
|
|
84
|
+
synckey setup
|
|
85
|
+
|
|
86
|
+
# 3. Start the gateway
|
|
87
|
+
synckey serve
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Your app now uses `http://localhost:8787/v1` with the unified key from setup.
|
|
91
|
+
|
|
92
|
+
**That's it.** Your OpenAI SDK, LangChain agent, or curl command now routes to 40+ models across all providers automatically.
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## How to Connect Your App
|
|
97
|
+
|
|
98
|
+
### Python OpenAI SDK
|
|
99
|
+
|
|
100
|
+
```python
|
|
101
|
+
from openai import OpenAI
|
|
102
|
+
|
|
103
|
+
client = OpenAI(
|
|
104
|
+
base_url="http://localhost:8787/v1",
|
|
105
|
+
api_key="sk-synckey-xxx"
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
# Use default model (set during setup)
|
|
109
|
+
client.chat.completions.create(
|
|
110
|
+
messages=[{"role": "user", "content": "Hello!"}]
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
# Specify a model - synckey routes automatically
|
|
114
|
+
client.chat.completions.create(
|
|
115
|
+
model="gpt-4o",
|
|
116
|
+
messages=[{"role": "user", "content": "Hello!"}]
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
# Streaming responses
|
|
120
|
+
stream = client.chat.completions.create(
|
|
121
|
+
model="gpt-4o",
|
|
122
|
+
messages=[{"role": "user", "content": "Tell me a story"}],
|
|
123
|
+
stream=True
|
|
124
|
+
)
|
|
125
|
+
for chunk in stream:
|
|
126
|
+
print(chunk.choices[0].delta.content, end="")
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### Node.js
|
|
130
|
+
|
|
131
|
+
```javascript
|
|
132
|
+
import OpenAI from 'openai';
|
|
133
|
+
|
|
134
|
+
const client = new OpenAI({
|
|
135
|
+
baseURL: 'http://localhost:8787/v1',
|
|
136
|
+
apiKey: 'sk-synckey-xxx'
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
const response = await client.chat.completions.create({
|
|
140
|
+
model: 'gpt-4o',
|
|
141
|
+
messages: [{ role: 'user', content: 'Hello!' }]
|
|
142
|
+
});
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Go
|
|
146
|
+
|
|
147
|
+
```go
|
|
148
|
+
package main
|
|
149
|
+
|
|
150
|
+
import (
|
|
151
|
+
"github.com/sashabaranov/go-openai"
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
func main() {
|
|
155
|
+
client := openai.NewClient("sk-synckey-xxx")
|
|
156
|
+
client.BaseURL = "http://localhost:8787/v1"
|
|
157
|
+
|
|
158
|
+
resp, err := client.CreateChatCompletion(
|
|
159
|
+
context.Background(),
|
|
160
|
+
openai.ChatCompletionRequest{
|
|
161
|
+
Model: "gpt-4o",
|
|
162
|
+
Messages: []openai.ChatMessage{
|
|
163
|
+
{Role: "user", Content: "Hello!"},
|
|
164
|
+
},
|
|
165
|
+
},
|
|
166
|
+
)
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### curl
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
# Basic request
|
|
174
|
+
curl http://localhost:8787/v1/chat/completions \
|
|
175
|
+
-H "Authorization: Bearer sk-synckey-xxx" \
|
|
176
|
+
-H "Content-Type: application/json" \
|
|
177
|
+
-d '{"model": "gpt-4o", "messages": [{"role": "user", "content": "Hello"}]}'
|
|
178
|
+
|
|
179
|
+
# Streaming
|
|
180
|
+
curl http://localhost:8787/v1/chat/completions \
|
|
181
|
+
-H "Authorization: Bearer sk-synckey-xxx" \
|
|
182
|
+
-H "Content-Type: application/json" \
|
|
183
|
+
-d '{"model": "gpt-4o", "messages": [{"role": "user", "content": "Hello"}], "stream": true}'
|
|
184
|
+
|
|
185
|
+
# Use default model (omit model)
|
|
186
|
+
curl http://localhost:8787/v1/chat/completions \
|
|
187
|
+
-H "Authorization: Bearer sk-synckey-xxx" \
|
|
188
|
+
-H "Content-Type: application/json" \
|
|
189
|
+
-d '{"messages": [{"role": "user", "content": "Hello"}]}'
|
|
190
|
+
|
|
191
|
+
# Force specific provider
|
|
192
|
+
curl http://localhost:8787/v1/chat/completions \
|
|
193
|
+
-H "Authorization: Bearer sk-synckey-xxx" \
|
|
194
|
+
-H "X-Provider: groq" \
|
|
195
|
+
-H "Content-Type: application/json" \
|
|
196
|
+
-d '{"model": "llama-3.3-70b-versatile", "messages": [{"role": "user", "content": "Hello"}]}'
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### LangChain
|
|
200
|
+
|
|
201
|
+
```python
|
|
202
|
+
from langchain_openai import ChatOpenAI
|
|
203
|
+
|
|
204
|
+
llm = ChatOpenAI(
|
|
205
|
+
base_url="http://localhost:8787/v1",
|
|
206
|
+
api_key="sk-synckey-xxx",
|
|
207
|
+
model="gpt-4o"
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
response = llm.invoke("Hello!")
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
---
|
|
214
|
+
|
|
215
|
+
## API Reference
|
|
216
|
+
|
|
217
|
+
### Endpoints
|
|
218
|
+
|
|
219
|
+
| Endpoint | Method | Auth | Description |
|
|
220
|
+
|----------|--------|------|-------------|
|
|
221
|
+
| `/v1/chat/completions` | POST | Yes | Chat completions (streaming supported) |
|
|
222
|
+
| `/v1/embeddings` | POST | Yes | Embeddings |
|
|
223
|
+
| `/v1/completions` | POST | Yes | Legacy completions |
|
|
224
|
+
| `/v1/models` | GET | Yes | List available models |
|
|
225
|
+
| `/v1/usage` | GET | Yes | Token usage totals |
|
|
226
|
+
| `/v1/events` | GET | Yes | Recent routing events |
|
|
227
|
+
| `/v1/requests/{id}` | GET | Yes | Poll deferred request status |
|
|
228
|
+
| `/healthz` | GET | No | Health check |
|
|
229
|
+
|
|
230
|
+
### Headers
|
|
231
|
+
|
|
232
|
+
| Header | Values | Description |
|
|
233
|
+
|--------|--------|-------------|
|
|
234
|
+
| `Authorization` | `Bearer sk-synckey-xxx` | **Required.** Your unified API key |
|
|
235
|
+
| `X-Quality-Floor` | `frontier`, `high`, `mid`, `low` | Minimum tier to use (never drop below this) |
|
|
236
|
+
| `X-Provider` | Provider ID (e.g., `groq`, `gemini`) | Hint to prefer this provider first |
|
|
237
|
+
|
|
238
|
+
### POST /v1/chat/completions
|
|
239
|
+
|
|
240
|
+
**Request:**
|
|
241
|
+
```json
|
|
242
|
+
{
|
|
243
|
+
"model": "gpt-4o",
|
|
244
|
+
"messages": [
|
|
245
|
+
{"role": "system", "content": "You are a helpful assistant."},
|
|
246
|
+
{"role": "user", "content": "Hello!"}
|
|
247
|
+
],
|
|
248
|
+
"temperature": 0.7,
|
|
249
|
+
"max_tokens": 1000,
|
|
250
|
+
"stream": false
|
|
251
|
+
}
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
**Response (non-streaming):**
|
|
255
|
+
```json
|
|
256
|
+
{
|
|
257
|
+
"id": "chatcmpl-xxx",
|
|
258
|
+
"object": "chat.completion",
|
|
259
|
+
"created": 1700000000,
|
|
260
|
+
"model": "gpt-4o",
|
|
261
|
+
"choices": [{
|
|
262
|
+
"index": 0,
|
|
263
|
+
"message": {
|
|
264
|
+
"role": "assistant",
|
|
265
|
+
"content": "Hello! How can I help you today?"
|
|
266
|
+
},
|
|
267
|
+
"finish_reason": "stop"
|
|
268
|
+
}],
|
|
269
|
+
"usage": {
|
|
270
|
+
"prompt_tokens": 20,
|
|
271
|
+
"completion_tokens": 15,
|
|
272
|
+
"total_tokens": 35
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
**Response (deferred/queued - 202):**
|
|
278
|
+
```json
|
|
279
|
+
{
|
|
280
|
+
"id": "defer_abc123",
|
|
281
|
+
"object": "chat.deferred",
|
|
282
|
+
"status": "queued",
|
|
283
|
+
"result_url": "/v1/requests/defer_abc123",
|
|
284
|
+
"retry_after": 28
|
|
285
|
+
}
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
### GET /v1/requests/{id}
|
|
289
|
+
|
|
290
|
+
Poll for deferred request completion:
|
|
291
|
+
|
|
292
|
+
```bash
|
|
293
|
+
curl http://localhost:8787/v1/requests/defer_abc123 \
|
|
294
|
+
-H "Authorization: Bearer sk-synckey-xxx"
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
**Queued response:**
|
|
298
|
+
```json
|
|
299
|
+
{"id": "defer_abc123", "status": "queued", "retry_after": 15}
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
**Running response:**
|
|
303
|
+
```json
|
|
304
|
+
{"id": "defer_abc123", "status": "running"}
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
**Done response:**
|
|
308
|
+
```json
|
|
309
|
+
{
|
|
310
|
+
"id": "defer_abc123",
|
|
311
|
+
"status": "done",
|
|
312
|
+
"response": { /* full completion response */ }
|
|
313
|
+
}
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
---
|
|
317
|
+
|
|
318
|
+
## Architecture
|
|
319
|
+
|
|
320
|
+
```
|
|
321
|
+
┌────────────────────────────────────────────────────────────────────────┐
|
|
322
|
+
│ Your Application │
|
|
323
|
+
│ (OpenAI SDK, LangChain, curl, any) │
|
|
324
|
+
└─────────────────────────────────────┬──────────────────────────────────┘
|
|
325
|
+
│
|
|
326
|
+
▼
|
|
327
|
+
┌─────────────────────────────────┐
|
|
328
|
+
│ synckey Gateway │
|
|
329
|
+
│ http://localhost:8787/v1 │
|
|
330
|
+
│ │
|
|
331
|
+
│ ┌─────────────────────────┐ │
|
|
332
|
+
│ │ 1. Auth Middleware │ │
|
|
333
|
+
│ │ (unified key) │ │
|
|
334
|
+
│ └─────────────────────────┘ │
|
|
335
|
+
│ │ │
|
|
336
|
+
│ ▼ │
|
|
337
|
+
│ ┌─────────────────────────┐ │
|
|
338
|
+
│ │ 2. Router │ │
|
|
339
|
+
│ │ - resolve model │ │
|
|
340
|
+
│ │ - check tier floor │ │
|
|
341
|
+
│ │ - priority ordering │ │
|
|
342
|
+
│ └─────────────────────────┘ │
|
|
343
|
+
│ │ │
|
|
344
|
+
│ ▼ │
|
|
345
|
+
│ ┌─────────────────────────┐ │
|
|
346
|
+
│ │ 3. Key Pool │ │
|
|
347
|
+
│ │ - per-key buckets │ │
|
|
348
|
+
│ │ - RPM/TPM tracking │ │
|
|
349
|
+
│ │ - health states │ │
|
|
350
|
+
│ └─────────────────────────┘ │
|
|
351
|
+
│ │ │
|
|
352
|
+
│ ▼ │
|
|
353
|
+
│ ┌─────────────────────────┐ │
|
|
354
|
+
│ │ 4. Proxy Layer │ │
|
|
355
|
+
│ │ - forward to upstream │ │
|
|
356
|
+
│ │ - stream passthrough │ │
|
|
357
|
+
│ │ - log usage/cost │ │
|
|
358
|
+
│ └─────────────────────────┘ │
|
|
359
|
+
│ │ │
|
|
360
|
+
│ ▼ │
|
|
361
|
+
│ ┌─────────────────────────┐ │
|
|
362
|
+
│ │ 5. Deferred Queue │ │
|
|
363
|
+
│ │ - 202 + poll │ │
|
|
364
|
+
│ │ - auto-retry │ │
|
|
365
|
+
│ └─────────────────────────┘ │
|
|
366
|
+
└──────────────────────┬──────────┘
|
|
367
|
+
│
|
|
368
|
+
┌───────────────────────────┼───────────────────────────┐
|
|
369
|
+
▼ ▼ ▼
|
|
370
|
+
┌─────────┐ ┌─────────┐ ┌─────────┐
|
|
371
|
+
│ Groq │ │ Gemini │ │Anthropic│
|
|
372
|
+
└─────────┘ └─────────┘ └─────────┘
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
### Internal Components
|
|
376
|
+
|
|
377
|
+
| Component | Purpose | Resources |
|
|
378
|
+
|-----------|---------|-----------|
|
|
379
|
+
| **Auth Middleware** | Validates unified key (SHA-256 hash) | ~1MB RAM |
|
|
380
|
+
| **Router** | Resolves model → provider, enforces tier floor | ~5MB RAM |
|
|
381
|
+
| **Key Pool** | Tracks per-key state (LIVE/COOLING/DEAD), RPM/TPM buckets | ~10KB per key |
|
|
382
|
+
| **Proxy Layer** | Forwards requests, handles streaming, logs responses | Scales with concurrency |
|
|
383
|
+
| **Deferred Queue** | SQLite-backed queue for when all keys are cooling | ~1MB RAM + disk |
|
|
384
|
+
|
|
385
|
+
### Resource Usage
|
|
386
|
+
|
|
387
|
+
| Metric | Typical | Under Load |
|
|
388
|
+
|--------|---------|------------|
|
|
389
|
+
| **RAM** | 50-100MB | 200MB max |
|
|
390
|
+
| **CPU** | <1% idle | Scales with concurrent requests |
|
|
391
|
+
| **Disk** | SQLite DB (~1MB per 10K requests) | Configurable TTL |
|
|
392
|
+
| **Latency Overhead** | 2-5ms | Adds ~10ms at 100 concurrent |
|
|
393
|
+
|
|
394
|
+
synckey is designed for **minimal footprint**. The gateway is single-threaded async by default (uvicorn with 1 worker). For higher throughput, increase workers:
|
|
395
|
+
|
|
396
|
+
```bash
|
|
397
|
+
synckey serve --workers 4 # 4 async workers
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
---
|
|
401
|
+
|
|
402
|
+
## Smart Routing
|
|
403
|
+
|
|
404
|
+
synckey doesn't just blindly forward requests. It intelligently manages the entire lifecycle:
|
|
405
|
+
|
|
406
|
+
### Automatic Failover
|
|
407
|
+
|
|
408
|
+
```python
|
|
409
|
+
# You request: claude-opus-4-8
|
|
410
|
+
# Groq has it and is fast → routes there
|
|
411
|
+
# Groq hits rate limit → automatically tries Anthropic
|
|
412
|
+
# Anthropic succeeds → returns response, logs the fallback
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
### Tier-Based Fallback
|
|
416
|
+
|
|
417
|
+
Every model is classified into a quality tier. If your preferred model is unavailable, synckey finds alternatives at the **same tier first** — never drops you to a cheaper tier unless explicitly configured.
|
|
418
|
+
|
|
419
|
+
```
|
|
420
|
+
FRONTIER: claude-opus-4-8, gpt-5, gemini-2.5-pro
|
|
421
|
+
HIGH: claude-sonnet-4-6, gpt-4o, gemini-1.5-pro
|
|
422
|
+
MID: gpt-4o-mini, llama-3.3-70b, gemini-2.0-flash
|
|
423
|
+
LOW: gemma-2b, llama-3.2-1b
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
### Deferred Requests (Queue)
|
|
427
|
+
|
|
428
|
+
When **every** key is rate-limited, synckey queues the request instead of failing:
|
|
429
|
+
|
|
430
|
+
```json
|
|
431
|
+
{
|
|
432
|
+
"id": "defer_abc123",
|
|
433
|
+
"status": "queued",
|
|
434
|
+
"result_url": "/v1/requests/defer_abc123",
|
|
435
|
+
"retry_after": 28
|
|
436
|
+
}
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
Poll the result URL or wait — synckey runs it automatically when a key frees up. No code changes needed. Works with streaming too.
|
|
440
|
+
|
|
441
|
+
---
|
|
442
|
+
|
|
443
|
+
## Use Cases
|
|
444
|
+
|
|
445
|
+
### Load Balance Across Providers
|
|
446
|
+
|
|
447
|
+
```python
|
|
448
|
+
# All these route intelligently based on your configured priority
|
|
449
|
+
client.chat.completions.create(model="gpt-4o") # Groq → Gemini → OpenAI
|
|
450
|
+
client.chat.completions.create(model="claude-sonnet-4-6") # Anthropic → Groq → Gemini
|
|
451
|
+
client.chat.completions.create(model="llama-3.3-70b") # Groq → Cerebras → Together
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
### Cost Optimization
|
|
455
|
+
|
|
456
|
+
```bash
|
|
457
|
+
# Set your default to a tier, not a specific model
|
|
458
|
+
synckey config set-default mid
|
|
459
|
+
|
|
460
|
+
# synckey picks the best MID-tier model that's available and affordable
|
|
461
|
+
# Falls back to HIGH tier only if MID is completely unavailable
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
### Zero-Downtime Deployments
|
|
465
|
+
|
|
466
|
+
```python
|
|
467
|
+
# When OpenAI has an outage, your app keeps working
|
|
468
|
+
# synckey automatically routes to the next available provider
|
|
469
|
+
# Your users never see an error
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
---
|
|
473
|
+
|
|
474
|
+
## Live Dashboard
|
|
475
|
+
|
|
476
|
+
```bash
|
|
477
|
+
synckey dash
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
```
|
|
481
|
+
┌──────────────────────────────────────────────────────────────┐
|
|
482
|
+
│ synckey dashboard [1]overview │
|
|
483
|
+
├──────────────────────────────────────────────────────────────┤
|
|
484
|
+
│ │
|
|
485
|
+
│ Keys (3) Requests Today Events │
|
|
486
|
+
│ ┌──────────┐ 1,247 12 rate limits │
|
|
487
|
+
│ │ ● Groq │ Avg: 142ms 2 tier fallbacks │
|
|
488
|
+
│ │ ○ Gemini │ Errors: 0.2% 0 key deaths │
|
|
489
|
+
│ │ ● Cohere │ │
|
|
490
|
+
│ └──────────┘ Last Request │
|
|
491
|
+
│ Model: gpt-4o-mini │
|
|
492
|
+
│ Provider: Groq │
|
|
493
|
+
│ Latency: 89ms │
|
|
494
|
+
│ │
|
|
495
|
+
│ [1]overview [2]keys [3]events [4]routing [q]quit │
|
|
496
|
+
└──────────────────────────────────────────────────────────────┘
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
---
|
|
500
|
+
|
|
501
|
+
## Supported Providers
|
|
502
|
+
|
|
503
|
+
40+ providers supported out of the box:
|
|
504
|
+
|
|
505
|
+
| Category | Providers |
|
|
506
|
+
|----------|-----------|
|
|
507
|
+
| **Fast/Cheap** | Groq, Cerebras, GitHub Models, DeepSeek |
|
|
508
|
+
| **Frontier** | OpenAI, Anthropic, Google Gemini, xAI |
|
|
509
|
+
| **Open Source** | Ollama, LM Studio, LocalAI, Fireworks |
|
|
510
|
+
| **Aggregators** | OpenRouter, Together AI, SambaNova |
|
|
511
|
+
|
|
512
|
+
Run `synckey providers` to see the full list.
|
|
513
|
+
|
|
514
|
+
---
|
|
515
|
+
|
|
516
|
+
## CLI Reference
|
|
517
|
+
|
|
518
|
+
```bash
|
|
519
|
+
# First time setup
|
|
520
|
+
synckey setup # guided interactive setup
|
|
521
|
+
synckey setup --force # regenerate unified key
|
|
522
|
+
|
|
523
|
+
# Gateway
|
|
524
|
+
synckey serve # start (runs in background)
|
|
525
|
+
synckey serve --port 9000 # custom port
|
|
526
|
+
|
|
527
|
+
# Key management
|
|
528
|
+
synckey keys # list all keys
|
|
529
|
+
synckey keys --rm <id> # remove a key
|
|
530
|
+
synckey keys --disable <id> # temporarily disable
|
|
531
|
+
synckey keys --enable <id> # re-enable a disabled key
|
|
532
|
+
|
|
533
|
+
# Testing
|
|
534
|
+
synckey test # health check all keys
|
|
535
|
+
synckey test groq llama-3.3-70b-versatile 5 # test a route
|
|
536
|
+
|
|
537
|
+
# Routing
|
|
538
|
+
synckey routing priority # show provider order
|
|
539
|
+
synckey routing priority groq gemini # set preference
|
|
540
|
+
synckey routing preset reliable # never drop tier
|
|
541
|
+
synckey routing preset cheap # prefer cost over quality
|
|
542
|
+
|
|
543
|
+
# Config
|
|
544
|
+
synckey config set-default gemini-2.0-flash # set default model
|
|
545
|
+
synckey config provider-default groq llama-3.3-70b-versatile # per-provider
|
|
546
|
+
synckey config alias fast mid # create alias
|
|
547
|
+
|
|
548
|
+
# Tiers
|
|
549
|
+
synckey tier set my-model frontier # override tier
|
|
550
|
+
synckey tier list # show all overrides
|
|
551
|
+
|
|
552
|
+
# Monitoring
|
|
553
|
+
synckey dash # live dashboard
|
|
554
|
+
synckey status # quick overview
|
|
555
|
+
synckey usage # totals
|
|
556
|
+
synckey usage --spend # cost breakdown
|
|
557
|
+
synckey usage --recent # last 25 requests
|
|
558
|
+
synckey events # rate limits, fallbacks, deaths
|
|
559
|
+
synckey deferred list # queued requests
|
|
560
|
+
|
|
561
|
+
# Key rotation
|
|
562
|
+
synckey renew # regenerate unified key
|
|
563
|
+
```
|
|
564
|
+
|
|
565
|
+
---
|
|
566
|
+
|
|
567
|
+
## Environment Variables
|
|
568
|
+
|
|
569
|
+
| Variable | Default | Description |
|
|
570
|
+
|----------|---------|-------------|
|
|
571
|
+
| `SYNCKEY_HOME` | `~/.synckey/` | Config and state directory |
|
|
572
|
+
| `SYNCKEY_DB` | `{SYNCKEY_HOME}/synckey.db` | SQLite database path |
|
|
573
|
+
| Provider env vars | — | `GROQ_API_KEY`, `ANTHROPIC_API_KEY`, etc. |
|
|
574
|
+
|
|
575
|
+
---
|
|
576
|
+
|
|
577
|
+
## Configuration
|
|
578
|
+
|
|
579
|
+
Config file: `~/.synckey/config.toml`
|
|
580
|
+
|
|
581
|
+
```toml
|
|
582
|
+
[gateway]
|
|
583
|
+
host = "127.0.0.1"
|
|
584
|
+
port = 8787
|
|
585
|
+
request_timeout = 120
|
|
586
|
+
|
|
587
|
+
[routing]
|
|
588
|
+
priority = ["groq", "gemini"]
|
|
589
|
+
tier_fallback = true
|
|
590
|
+
max_retries = 6
|
|
591
|
+
default_cooldown = 20
|
|
592
|
+
deep_cooling_threshold = 60
|
|
593
|
+
|
|
594
|
+
[deferred]
|
|
595
|
+
enabled = true
|
|
596
|
+
ttl = 3600
|
|
597
|
+
poll = 5
|
|
598
|
+
max_queue = 1000
|
|
599
|
+
```
|
|
600
|
+
|
|
601
|
+
---
|
|
602
|
+
|
|
603
|
+
## Production Deployment
|
|
604
|
+
|
|
605
|
+
### PM2 (Recommended for all platforms)
|
|
606
|
+
|
|
607
|
+
PM2 keeps synckey running 24/7, auto-restarts on crash, and survives server reboots.
|
|
608
|
+
|
|
609
|
+
```bash
|
|
610
|
+
# Install PM2
|
|
611
|
+
npm install -g pm2
|
|
612
|
+
|
|
613
|
+
# Start synckey (runs in background, daemonized)
|
|
614
|
+
pm2 start examples/ecosystem.config.js
|
|
615
|
+
|
|
616
|
+
# Save current process list (persists across reboots)
|
|
617
|
+
pm2 save
|
|
618
|
+
|
|
619
|
+
# Generate startup script for your OS
|
|
620
|
+
pm2 startup
|
|
621
|
+
|
|
622
|
+
# Other useful commands
|
|
623
|
+
pm2 logs synckey # view logs
|
|
624
|
+
pm2 restart synckey # restart
|
|
625
|
+
pm2 stop synckey # stop
|
|
626
|
+
pm2 monit # real-time dashboard
|
|
627
|
+
```
|
|
628
|
+
|
|
629
|
+
**ecosystem.config.js** (included in `examples/`):
|
|
630
|
+
```javascript
|
|
631
|
+
module.exports = {
|
|
632
|
+
apps: [{
|
|
633
|
+
name: 'synckey',
|
|
634
|
+
script: 'python.exe',
|
|
635
|
+
args: '-m synckey serve',
|
|
636
|
+
cwd: 'C:\\path\\to\\synckey',
|
|
637
|
+
interpreter: 'none',
|
|
638
|
+
watch: false,
|
|
639
|
+
autorestart: true,
|
|
640
|
+
restart_delay: 10000,
|
|
641
|
+
max_restarts: 10,
|
|
642
|
+
min_uptime: 5000,
|
|
643
|
+
}]
|
|
644
|
+
};
|
|
645
|
+
```
|
|
646
|
+
|
|
647
|
+
### systemd (Linux/WSL)
|
|
648
|
+
|
|
649
|
+
For servers running systemd:
|
|
650
|
+
|
|
651
|
+
```bash
|
|
652
|
+
# 1. Install synckey
|
|
653
|
+
pip install synckey
|
|
654
|
+
|
|
655
|
+
# 2. Create dedicated user (optional but recommended)
|
|
656
|
+
sudo useradd -r -s /bin/false synckey
|
|
657
|
+
sudo mkdir /var/lib/synckey /run/synckey
|
|
658
|
+
sudo chown synckey:synckey /var/lib/synckey /run/synckey
|
|
659
|
+
|
|
660
|
+
# 3. Install service file
|
|
661
|
+
sudo cp examples/synckey.service /etc/systemd/system/
|
|
662
|
+
sudo systemctl daemon-reload
|
|
663
|
+
|
|
664
|
+
# 4. Enable and start
|
|
665
|
+
sudo systemctl enable synckey
|
|
666
|
+
sudo systemctl start synckey
|
|
667
|
+
sudo systemctl status synckey
|
|
668
|
+
```
|
|
669
|
+
|
|
670
|
+
**synckey.service** (included in `examples/`):
|
|
671
|
+
```ini
|
|
672
|
+
[Unit]
|
|
673
|
+
Description=synckey unified AI gateway
|
|
674
|
+
Documentation=https://github.com/pmdnawaz1/synckey
|
|
675
|
+
After=network-online.target
|
|
676
|
+
Wants=network-online.target
|
|
677
|
+
|
|
678
|
+
[Service]
|
|
679
|
+
Type=simple
|
|
680
|
+
User=synckey
|
|
681
|
+
Group=synckey
|
|
682
|
+
WorkingDirectory=/opt/synckey
|
|
683
|
+
ExecStart=/usr/bin/python3 -m synckey serve
|
|
684
|
+
ExecReload=/bin/kill -HUP $MAINPID
|
|
685
|
+
Restart=on-failure
|
|
686
|
+
RestartSec=10
|
|
687
|
+
StandardOutput=journal
|
|
688
|
+
StandardError=journal
|
|
689
|
+
SyslogIdentifier=synckey
|
|
690
|
+
|
|
691
|
+
# Security hardening
|
|
692
|
+
NoNewPrivileges=true
|
|
693
|
+
PrivateTmp=true
|
|
694
|
+
ProtectSystem=strict
|
|
695
|
+
ProtectHome=true
|
|
696
|
+
ReadWritePaths=/var/lib/synckey /run/synckey
|
|
697
|
+
Environment=PYTHONUNBUFFERED=1
|
|
698
|
+
|
|
699
|
+
[Install]
|
|
700
|
+
WantedBy=multi-user.target
|
|
701
|
+
```
|
|
702
|
+
|
|
703
|
+
### Windows Service (NSSM)
|
|
704
|
+
|
|
705
|
+
```bash
|
|
706
|
+
nssm install synckey "C:\path\to\python.exe" "-m synckey serve"
|
|
707
|
+
nssm set synckey AppDirectory "C:\path\to\synckey"
|
|
708
|
+
nssm set synckey Start SERVICE_AUTO_START
|
|
709
|
+
nssm start synckey
|
|
710
|
+
```
|
|
711
|
+
|
|
712
|
+
### Auto-Restart Behavior
|
|
713
|
+
|
|
714
|
+
| Event | Action |
|
|
715
|
+
|-------|--------|
|
|
716
|
+
| Server reboot | PM2/systemd auto-starts synckey |
|
|
717
|
+
| Crash (OOM, segfault) | Auto-restart after 10s delay |
|
|
718
|
+
| All keys cooling | synckey returns 202 + poll URL (no crash) |
|
|
719
|
+
|
|
720
|
+
### Expose on Domain
|
|
721
|
+
|
|
722
|
+
```bash
|
|
723
|
+
synckey serve --host 0.0.0.0 --port 8787
|
|
724
|
+
```
|
|
725
|
+
|
|
726
|
+
**nginx:**
|
|
727
|
+
```nginx
|
|
728
|
+
server {
|
|
729
|
+
listen 443 ssl;
|
|
730
|
+
server_name ai.example.com;
|
|
731
|
+
ssl_certificate /etc/ssl/certs/ai.example.com.crt;
|
|
732
|
+
ssl_certificate_key /etc/ssl/private/ai.example.com.key;
|
|
733
|
+
|
|
734
|
+
location / {
|
|
735
|
+
proxy_pass http://127.0.0.1:8787;
|
|
736
|
+
proxy_set_header Host $host;
|
|
737
|
+
proxy_buffering off;
|
|
738
|
+
proxy_read_timeout 300s;
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
```
|
|
742
|
+
|
|
743
|
+
**Caddy:**
|
|
744
|
+
```caddyfile
|
|
745
|
+
ai.example.com {
|
|
746
|
+
reverse_proxy localhost:8787 {
|
|
747
|
+
flush_interval -1
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
```
|
|
751
|
+
|
|
752
|
+
---
|
|
753
|
+
|
|
754
|
+
## Security
|
|
755
|
+
|
|
756
|
+
- Provider secrets are **Fernet-encrypted** at rest
|
|
757
|
+
- Unified key stored as **SHA-256 hash** (not reversible)
|
|
758
|
+
- Gateway binds to `localhost` by default
|
|
759
|
+
- Secrets directory is `0700` permissions
|
|
760
|
+
|
|
761
|
+
---
|
|
762
|
+
|
|
763
|
+
## Troubleshooting
|
|
764
|
+
|
|
765
|
+
### 401 Unauthorized
|
|
766
|
+
- Verify your unified key is correct: `synckey status`
|
|
767
|
+
- Regenerate if needed: `synckey renew`
|
|
768
|
+
|
|
769
|
+
### Connection refused
|
|
770
|
+
- Ensure synckey is running: `synckey serve`
|
|
771
|
+
- Check if port is in use: `netstat -an | grep 8787`
|
|
772
|
+
|
|
773
|
+
### All requests returning 202 queued
|
|
774
|
+
- Keys may be in cooldown: `synckey status`
|
|
775
|
+
- Check events: `synckey events`
|
|
776
|
+
|
|
777
|
+
### Model not found (404)
|
|
778
|
+
- Model may not be in index: `synckey models --refresh`
|
|
779
|
+
- Provider may not have the model
|
|
780
|
+
|
|
781
|
+
---
|
|
782
|
+
|
|
783
|
+
## Compare
|
|
784
|
+
|
|
785
|
+
| Feature | synckey | LiteLLM | Portkey | Direct |
|
|
786
|
+
|---------|---------|---------|---------|--------|
|
|
787
|
+
| One API key | ✅ | ✅ | ✅ | ❌ |
|
|
788
|
+
| Auto-failover | ✅ | ✅ | ✅ | ❌ |
|
|
789
|
+
| Tier fallback | ✅ | ❌ | ❌ | ❌ |
|
|
790
|
+
| Deferred queue | ✅ | ❌ | ❌ | ❌ |
|
|
791
|
+
| No DB needed | ✅ | ❌ | ❌ | ✅ |
|
|
792
|
+
| Local gateway | ✅ | ❌ | ❌ | ✅ |
|
|
793
|
+
| Zero config | ✅ | ❌ | ❌ | ✅ |
|
|
794
|
+
|
|
795
|
+
---
|
|
796
|
+
|
|
797
|
+
## License
|
|
798
|
+
|
|
799
|
+
MIT
|