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.
Files changed (41) hide show
  1. {synckey-0.1.0 → synckey-0.2.0}/PKG-INFO +82 -27
  2. {synckey-0.1.0 → synckey-0.2.0}/README.md +819 -764
  3. synckey-0.2.0/assets/demo.gif +0 -0
  4. {synckey-0.1.0 → synckey-0.2.0}/pyproject.toml +1 -1
  5. {synckey-0.1.0 → synckey-0.2.0}/src/synckey/__init__.py +1 -1
  6. {synckey-0.1.0 → synckey-0.2.0}/src/synckey/bucket.py +21 -5
  7. {synckey-0.1.0 → synckey-0.2.0}/src/synckey/cli.py +578 -460
  8. {synckey-0.1.0 → synckey-0.2.0}/src/synckey/config.py +55 -9
  9. {synckey-0.1.0 → synckey-0.2.0}/src/synckey/db.py +4 -5
  10. {synckey-0.1.0 → synckey-0.2.0}/src/synckey/keypool.py +10 -3
  11. {synckey-0.1.0 → synckey-0.2.0}/src/synckey/prefs.py +1 -1
  12. {synckey-0.1.0 → synckey-0.2.0}/src/synckey/providers.py +10 -0
  13. {synckey-0.1.0 → synckey-0.2.0}/src/synckey/router.py +8 -2
  14. {synckey-0.1.0 → synckey-0.2.0}/src/synckey/server.py +53 -27
  15. {synckey-0.1.0 → synckey-0.2.0}/src/synckey/state.py +19 -9
  16. {synckey-0.1.0 → synckey-0.2.0}/src/synckey/tiers.py +2 -0
  17. {synckey-0.1.0 → synckey-0.2.0}/tests/test_bucket.py +31 -0
  18. synckey-0.2.0/tests/test_cli.py +186 -0
  19. synckey-0.2.0/tests/test_context.py +48 -0
  20. synckey-0.2.0/tests/test_crypto.py +32 -0
  21. {synckey-0.1.0 → synckey-0.2.0}/tests/test_deferred.py +267 -145
  22. {synckey-0.1.0 → synckey-0.2.0}/tests/test_keypool.py +28 -0
  23. {synckey-0.1.0 → synckey-0.2.0}/tests/test_providers.py +3 -1
  24. {synckey-0.1.0 → synckey-0.2.0}/tests/test_router.py +23 -0
  25. synckey-0.2.0/tests/test_server.py +448 -0
  26. synckey-0.2.0/tests/test_settings.py +38 -0
  27. {synckey-0.1.0 → synckey-0.2.0}/tests/test_tiers.py +7 -0
  28. synckey-0.2.0/tests/test_usage.py +48 -0
  29. synckey-0.1.0/tests/test_server.py +0 -194
  30. {synckey-0.1.0 → synckey-0.2.0}/.claude/settings.local.json +0 -0
  31. {synckey-0.1.0 → synckey-0.2.0}/.gitignore +0 -0
  32. {synckey-0.1.0 → synckey-0.2.0}/examples/ecosystem.config.js +0 -0
  33. {synckey-0.1.0 → synckey-0.2.0}/examples/synckey.service +0 -0
  34. {synckey-0.1.0 → synckey-0.2.0}/src/synckey/__main__.py +0 -0
  35. {synckey-0.1.0 → synckey-0.2.0}/src/synckey/context.py +0 -0
  36. {synckey-0.1.0 → synckey-0.2.0}/src/synckey/crypto.py +0 -0
  37. {synckey-0.1.0 → synckey-0.2.0}/src/synckey/deferred.py +0 -0
  38. {synckey-0.1.0 → synckey-0.2.0}/src/synckey/usage.py +0 -0
  39. {synckey-0.1.0 → synckey-0.2.0}/tests/conftest.py +0 -0
  40. {synckey-0.1.0 → synckey-0.2.0}/tests/test_defaults.py +0 -0
  41. {synckey-0.1.0 → synckey-0.2.0}/tests/test_state.py +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: synckey
3
- Version: 0.1.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
- **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.
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 (set during setup)
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
- {"id": "defer_abc123", "status": "queued", "retry_after": 15}
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
- **Running response:**
303
- ```json
304
- {"id": "defer_abc123", "status": "running"}
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
- **Done response:**
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-threaded async by default (uvicorn with 1 worker). For higher throughput, increase workers:
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 async workers
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 or wait — synckey runs it automatically when a key frees up. No code changes needed. Works with streaming too.
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
- 40+ providers supported out of the box:
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
- | **Open Source** | Ollama, LM Studio, LocalAI, Fireworks |
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 (runs in background)
525
- synckey serve --port 9000 # custom port
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
- PM2 keeps synckey running 24/7, auto-restarts on crash, and survives server reboots.
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