synckey 0.1.0__tar.gz → 0.2.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.
- {synckey-0.1.0 → synckey-0.2.0}/PKG-INFO +82 -27
- {synckey-0.1.0 → synckey-0.2.0}/README.md +819 -764
- synckey-0.2.0/assets/demo.gif +0 -0
- {synckey-0.1.0 → synckey-0.2.0}/pyproject.toml +1 -1
- {synckey-0.1.0 → synckey-0.2.0}/src/synckey/__init__.py +1 -1
- {synckey-0.1.0 → synckey-0.2.0}/src/synckey/bucket.py +21 -5
- {synckey-0.1.0 → synckey-0.2.0}/src/synckey/cli.py +578 -460
- {synckey-0.1.0 → synckey-0.2.0}/src/synckey/config.py +55 -9
- {synckey-0.1.0 → synckey-0.2.0}/src/synckey/db.py +4 -5
- {synckey-0.1.0 → synckey-0.2.0}/src/synckey/keypool.py +10 -3
- {synckey-0.1.0 → synckey-0.2.0}/src/synckey/prefs.py +1 -1
- {synckey-0.1.0 → synckey-0.2.0}/src/synckey/providers.py +10 -0
- {synckey-0.1.0 → synckey-0.2.0}/src/synckey/router.py +8 -2
- {synckey-0.1.0 → synckey-0.2.0}/src/synckey/server.py +53 -27
- {synckey-0.1.0 → synckey-0.2.0}/src/synckey/state.py +19 -9
- {synckey-0.1.0 → synckey-0.2.0}/src/synckey/tiers.py +2 -0
- {synckey-0.1.0 → synckey-0.2.0}/tests/test_bucket.py +31 -0
- synckey-0.2.0/tests/test_cli.py +186 -0
- synckey-0.2.0/tests/test_context.py +48 -0
- synckey-0.2.0/tests/test_crypto.py +32 -0
- {synckey-0.1.0 → synckey-0.2.0}/tests/test_deferred.py +267 -145
- {synckey-0.1.0 → synckey-0.2.0}/tests/test_keypool.py +28 -0
- {synckey-0.1.0 → synckey-0.2.0}/tests/test_providers.py +3 -1
- {synckey-0.1.0 → synckey-0.2.0}/tests/test_router.py +23 -0
- synckey-0.2.0/tests/test_server.py +448 -0
- synckey-0.2.0/tests/test_settings.py +38 -0
- {synckey-0.1.0 → synckey-0.2.0}/tests/test_tiers.py +7 -0
- synckey-0.2.0/tests/test_usage.py +48 -0
- synckey-0.1.0/tests/test_server.py +0 -194
- {synckey-0.1.0 → synckey-0.2.0}/.claude/settings.local.json +0 -0
- {synckey-0.1.0 → synckey-0.2.0}/.gitignore +0 -0
- {synckey-0.1.0 → synckey-0.2.0}/examples/ecosystem.config.js +0 -0
- {synckey-0.1.0 → synckey-0.2.0}/examples/synckey.service +0 -0
- {synckey-0.1.0 → synckey-0.2.0}/src/synckey/__main__.py +0 -0
- {synckey-0.1.0 → synckey-0.2.0}/src/synckey/context.py +0 -0
- {synckey-0.1.0 → synckey-0.2.0}/src/synckey/crypto.py +0 -0
- {synckey-0.1.0 → synckey-0.2.0}/src/synckey/deferred.py +0 -0
- {synckey-0.1.0 → synckey-0.2.0}/src/synckey/usage.py +0 -0
- {synckey-0.1.0 → synckey-0.2.0}/tests/conftest.py +0 -0
- {synckey-0.1.0 → synckey-0.2.0}/tests/test_defaults.py +0 -0
- {synckey-0.1.0 → synckey-0.2.0}/tests/test_state.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: synckey
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: One API key for every AI provider. Automatic failover, tier-based fallback, and zero rate-limit headaches.
|
|
5
5
|
Project-URL: Homepage, https://github.com/pmdnawaz1/synckey
|
|
6
6
|
Project-URL: Documentation, https://github.com/pmdnawaz1/synckey#readme
|
|
@@ -43,7 +43,11 @@ Description-Content-Type: text/markdown
|
|
|
43
43
|
One API key. Every AI model. Zero headaches.
|
|
44
44
|
</h1>
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
<p align="center">
|
|
47
|
+
<img src="https://raw.githubusercontent.com/pmdnawaz1/synckey/main/assets/demo.gif" alt="synckey demo: status, keys cooling, tier-fallback events, live dashboard" width="720"/>
|
|
48
|
+
</p>
|
|
49
|
+
|
|
50
|
+
**synckey** lets your app use Gemini, Groq, OpenAI, Anthropic, Cerebras, Mistral, DeepSeek, xAI, and more — through a single OpenAI-compatible API key. No code changes. No rate limit nightmares. Just one endpoint.
|
|
47
51
|
|
|
48
52
|
```python
|
|
49
53
|
from openai import OpenAI
|
|
@@ -79,12 +83,17 @@ client.chat.completions.create(
|
|
|
79
83
|
```bash
|
|
80
84
|
# 1. Install
|
|
81
85
|
pip install synckey
|
|
86
|
+
# …or globally, isolated from your system Python:
|
|
87
|
+
pipx install synckey # (install pipx first: python -m pip install pipx && pipx ensurepath)
|
|
82
88
|
|
|
83
89
|
# 2. Guided setup (30 seconds)
|
|
84
90
|
synckey setup
|
|
85
91
|
|
|
86
|
-
# 3. Start the gateway
|
|
92
|
+
# 3. Start the gateway (foreground; Ctrl+C to stop)
|
|
87
93
|
synckey serve
|
|
94
|
+
|
|
95
|
+
# …or detached, in the background:
|
|
96
|
+
synckey serve --background
|
|
88
97
|
```
|
|
89
98
|
|
|
90
99
|
Your app now uses `http://localhost:8787/v1` with the unified key from setup.
|
|
@@ -105,8 +114,9 @@ client = OpenAI(
|
|
|
105
114
|
api_key="sk-synckey-xxx"
|
|
106
115
|
)
|
|
107
116
|
|
|
108
|
-
# Use default model
|
|
117
|
+
# Use the default model configured during setup
|
|
109
118
|
client.chat.completions.create(
|
|
119
|
+
model="default",
|
|
110
120
|
messages=[{"role": "user", "content": "Hello!"}]
|
|
111
121
|
)
|
|
112
122
|
|
|
@@ -294,25 +304,29 @@ curl http://localhost:8787/v1/requests/defer_abc123 \
|
|
|
294
304
|
-H "Authorization: Bearer sk-synckey-xxx"
|
|
295
305
|
```
|
|
296
306
|
|
|
297
|
-
**Queued response:**
|
|
307
|
+
**Queued/running response (202):**
|
|
298
308
|
```json
|
|
299
|
-
{
|
|
309
|
+
{
|
|
310
|
+
"id": "defer_abc123",
|
|
311
|
+
"object": "chat.deferred",
|
|
312
|
+
"status": "queued",
|
|
313
|
+
"retry_after": 15,
|
|
314
|
+
"result_url": "/v1/requests/defer_abc123"
|
|
315
|
+
}
|
|
300
316
|
```
|
|
301
317
|
|
|
302
|
-
**
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
```
|
|
318
|
+
**Done response (200):** the upstream completion is returned directly — the body is
|
|
319
|
+
exactly what the provider sent (a standard `chat.completion` JSON object), so your
|
|
320
|
+
client parses it as if it had never been queued.
|
|
306
321
|
|
|
307
|
-
**
|
|
322
|
+
**Error response (4xx/5xx):** a standard error envelope with the upstream status code:
|
|
308
323
|
```json
|
|
309
|
-
{
|
|
310
|
-
"id": "defer_abc123",
|
|
311
|
-
"status": "done",
|
|
312
|
-
"response": { /* full completion response */ }
|
|
313
|
-
}
|
|
324
|
+
{"error": {"message": "...", "type": "deferred_error", "request_id": "defer_abc123"}}
|
|
314
325
|
```
|
|
315
326
|
|
|
327
|
+
Deferred requests always run **non-streaming**: the polled result is the complete
|
|
328
|
+
JSON response, ready to use.
|
|
329
|
+
|
|
316
330
|
---
|
|
317
331
|
|
|
318
332
|
## Architecture
|
|
@@ -391,12 +405,16 @@ curl http://localhost:8787/v1/requests/defer_abc123 \
|
|
|
391
405
|
| **Disk** | SQLite DB (~1MB per 10K requests) | Configurable TTL |
|
|
392
406
|
| **Latency Overhead** | 2-5ms | Adds ~10ms at 100 concurrent |
|
|
393
407
|
|
|
394
|
-
synckey is designed for **minimal footprint**. The gateway is single-
|
|
408
|
+
synckey is designed for **minimal footprint**. The gateway is single-process async by default (uvicorn with 1 worker), which handles hundreds of concurrent requests. For CPU-bound throughput you can add workers:
|
|
395
409
|
|
|
396
410
|
```bash
|
|
397
|
-
synckey serve --workers 4 # 4
|
|
411
|
+
synckey serve --workers 4 # 4 uvicorn worker processes
|
|
398
412
|
```
|
|
399
413
|
|
|
414
|
+
> **Note:** each worker keeps its own in-memory key pool, so per-key rate
|
|
415
|
+
> limits apply *per worker* (4 workers × 60 RPM cap ≈ 240 RPM total). Durable
|
|
416
|
+
> health state (cooling/dead keys) is still shared via SQLite.
|
|
417
|
+
|
|
400
418
|
---
|
|
401
419
|
|
|
402
420
|
## Smart Routing
|
|
@@ -436,7 +454,7 @@ When **every** key is rate-limited, synckey queues the request instead of failin
|
|
|
436
454
|
}
|
|
437
455
|
```
|
|
438
456
|
|
|
439
|
-
Poll the result URL
|
|
457
|
+
Poll the result URL — synckey runs the request automatically when a key frees up and serves the stored response on your next poll.
|
|
440
458
|
|
|
441
459
|
---
|
|
442
460
|
|
|
@@ -500,16 +518,17 @@ synckey dash
|
|
|
500
518
|
|
|
501
519
|
## Supported Providers
|
|
502
520
|
|
|
503
|
-
|
|
521
|
+
15 providers supported out of the box:
|
|
504
522
|
|
|
505
523
|
| Category | Providers |
|
|
506
524
|
|----------|-----------|
|
|
507
525
|
| **Fast/Cheap** | Groq, Cerebras, GitHub Models, DeepSeek |
|
|
508
526
|
| **Frontier** | OpenAI, Anthropic, Google Gemini, xAI |
|
|
509
|
-
| **
|
|
527
|
+
| **Local** | Ollama — plus any custom OpenAI-compatible endpoint (see `[[providers]]` in config) |
|
|
510
528
|
| **Aggregators** | OpenRouter, Together AI, SambaNova |
|
|
529
|
+
| **Specialty** | Mistral, Cohere, NVIDIA NIM |
|
|
511
530
|
|
|
512
|
-
Run `synckey providers` to see the full list.
|
|
531
|
+
Run `synckey providers` to see the full list with signup links.
|
|
513
532
|
|
|
514
533
|
---
|
|
515
534
|
|
|
@@ -521,14 +540,22 @@ synckey setup # guided interactive setup
|
|
|
521
540
|
synckey setup --force # regenerate unified key
|
|
522
541
|
|
|
523
542
|
# Gateway
|
|
524
|
-
synckey serve # start (
|
|
525
|
-
synckey serve --
|
|
543
|
+
synckey serve # start in the foreground (Ctrl+C to stop)
|
|
544
|
+
synckey serve --background # detach; logs to ~/.synckey/gateway.log
|
|
545
|
+
synckey serve --port 9000 # custom port
|
|
546
|
+
synckey serve --workers 4 # multi-process (see note in Resource Usage)
|
|
547
|
+
synckey stop # stop a --background gateway
|
|
526
548
|
|
|
527
549
|
# Key management
|
|
528
550
|
synckey keys # list all keys
|
|
551
|
+
synckey keys add groq --key gsk_... # add a key non-interactively
|
|
552
|
+
synckey keys add groq --key "k1,k2,k3" # bulk add (comma-separated)
|
|
553
|
+
synckey keys add groq --file keys.txt # bulk import from a file
|
|
554
|
+
synckey keys add groq --from-env # read from $GROQ_API_KEY
|
|
529
555
|
synckey keys --rm <id> # remove a key
|
|
530
556
|
synckey keys --disable <id> # temporarily disable
|
|
531
557
|
synckey keys --enable <id> # re-enable a disabled key
|
|
558
|
+
synckey keys --limits <id> --rpm 60 --tpm 100000 --weight 2 # set caps
|
|
532
559
|
|
|
533
560
|
# Testing
|
|
534
561
|
synckey test # health check all keys
|
|
@@ -560,6 +587,12 @@ synckey deferred list # queued requests
|
|
|
560
587
|
|
|
561
588
|
# Key rotation
|
|
562
589
|
synckey renew # regenerate unified key
|
|
590
|
+
|
|
591
|
+
# Discovery
|
|
592
|
+
synckey providers # list supported providers
|
|
593
|
+
synckey models --refresh # fetch models your keys can call
|
|
594
|
+
synckey version # print version
|
|
595
|
+
synckey --guide # interactive walkthrough
|
|
563
596
|
```
|
|
564
597
|
|
|
565
598
|
---
|
|
@@ -583,19 +616,35 @@ Config file: `~/.synckey/config.toml`
|
|
|
583
616
|
host = "127.0.0.1"
|
|
584
617
|
port = 8787
|
|
585
618
|
request_timeout = 120
|
|
619
|
+
max_connections = 600
|
|
620
|
+
max_keepalive = 300
|
|
586
621
|
|
|
587
622
|
[routing]
|
|
588
623
|
priority = ["groq", "gemini"]
|
|
589
624
|
tier_fallback = true
|
|
625
|
+
cross_provider_fallback = true
|
|
590
626
|
max_retries = 6
|
|
591
627
|
default_cooldown = 20
|
|
592
628
|
deep_cooling_threshold = 60
|
|
593
629
|
|
|
630
|
+
[tiers]
|
|
631
|
+
overrides = { "my-model" = "frontier" } # manual tier assignments
|
|
632
|
+
prices = { "my-model" = [1.0, 3.0] } # $/M tokens: input, output
|
|
633
|
+
|
|
594
634
|
[deferred]
|
|
595
635
|
enabled = true
|
|
596
636
|
ttl = 3600
|
|
597
637
|
poll = 5
|
|
598
638
|
max_queue = 1000
|
|
639
|
+
max_queue_age = 86400
|
|
640
|
+
|
|
641
|
+
# Custom OpenAI-compatible providers:
|
|
642
|
+
# [[providers]]
|
|
643
|
+
# id = "lmstudio"
|
|
644
|
+
# name = "LM Studio"
|
|
645
|
+
# base_url = "http://localhost:1234/v1"
|
|
646
|
+
# patterns = ["^lmstudio/"]
|
|
647
|
+
# auth = "bearer"
|
|
599
648
|
```
|
|
600
649
|
|
|
601
650
|
---
|
|
@@ -604,7 +653,10 @@ max_queue = 1000
|
|
|
604
653
|
|
|
605
654
|
### PM2 (Recommended for all platforms)
|
|
606
655
|
|
|
607
|
-
|
|
656
|
+
`synckey serve` runs in the foreground by default — exactly what a process
|
|
657
|
+
supervisor expects. PM2 keeps synckey running 24/7, auto-restarts on crash, and
|
|
658
|
+
survives server reboots. (Do **not** use `--background` under PM2/systemd; that
|
|
659
|
+
flag is only for running without a supervisor.)
|
|
608
660
|
|
|
609
661
|
```bash
|
|
610
662
|
# Install PM2
|
|
@@ -716,6 +768,7 @@ nssm start synckey
|
|
|
716
768
|
| Server reboot | PM2/systemd auto-starts synckey |
|
|
717
769
|
| Crash (OOM, segfault) | Auto-restart after 10s delay |
|
|
718
770
|
| All keys cooling | synckey returns 202 + poll URL (no crash) |
|
|
771
|
+
| Manual stop (no supervisor) | `synckey stop` (kills the `--background` process) |
|
|
719
772
|
|
|
720
773
|
### Expose on Domain
|
|
721
774
|
|
|
@@ -788,10 +841,12 @@ ai.example.com {
|
|
|
788
841
|
| Auto-failover | ✅ | ✅ | ✅ | ❌ |
|
|
789
842
|
| Tier fallback | ✅ | ❌ | ❌ | ❌ |
|
|
790
843
|
| Deferred queue | ✅ | ❌ | ❌ | ❌ |
|
|
791
|
-
| No DB needed | ✅ | ❌ | ❌ | ✅ |
|
|
792
844
|
| Local gateway | ✅ | ❌ | ❌ | ✅ |
|
|
793
845
|
| Zero config | ✅ | ❌ | ❌ | ✅ |
|
|
794
846
|
|
|
847
|
+
synckey keeps its state in a local SQLite database (`~/.synckey/synckey.db`) — no
|
|
848
|
+
external database server required.
|
|
849
|
+
|
|
795
850
|
---
|
|
796
851
|
|
|
797
852
|
## License
|