File size: 6,215 Bytes
43668cf
f69bc37
 
 
 
43668cf
 
 
 
f69bc37
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
---
title: Unified AI Proxy
emoji: πŸ”€
colorFrom: blue
colorTo: indigo
sdk: docker
pinned: false
---

# πŸ”€ Unified AI Proxy

A self-hosted, OpenAI-compatible API gateway that routes requests to any AI provider from a single endpoint. Manage all your provider keys through a clean dashboard and hit one URL for everything.

---

## Features

- **Single endpoint** for OpenAI, Anthropic, Groq, Together, Mistral, Cohere, DeepSeek, OpenRouter, or any OpenAI-compatible API
- **Provider routing** via `providerName/model-name` prefix in the model field
- **Streaming support** β€” SSE responses forwarded without buffering
- **Unified model list** β€” `GET /v1/models` aggregates all providers, prefixed by name
- **Dashboard** β€” add/edit/delete providers, view request logs, manage your master key
- **Persistent SQLite** β€” data survives Space restarts (mount `/data` as persistent storage)
- **Env var seeding** β€” pre-seed providers via `PROVIDER_<NAME>_URL` + `PROVIDER_<NAME>_KEY`

---

## Quick Start

### 1. Deploy to Hugging Face Spaces

1. Create a new Space β†’ SDK: **Docker**
2. Upload these files: `Dockerfile`, `main.py`, `requirements.txt`, `README.md`
3. Enable **Persistent Storage** in Space settings β†’ mount at `/data`
4. The Space starts and prints your **Master API Key** to the build logs β€” copy it

### 2. Open the Dashboard

Navigate to your Space URL. Enter your master key to unlock the dashboard.

### 3. Add Providers

In the **Providers** tab, add any OpenAI-compatible backend:

| Provider | Base URL |
|----------|----------|
| OpenAI | `https://api.openai.com/v1` |
| Anthropic | `https://api.anthropic.com/v1` |
| Groq | `https://api.groq.com/openai/v1` |
| Together | `https://api.together.xyz/v1` |
| Mistral | `https://api.mistral.ai/v1` |
| Cohere | `https://api.cohere.ai/compatibility/v1` |
| DeepSeek | `https://api.deepseek.com/v1` |
| OpenRouter | `https://openrouter.ai/api/v1` |

### 4. Make Requests

Use your Space URL as the base URL in any OpenAI-compatible client:

```bash
# Route to a specific provider using providerName/ prefix
curl https://your-space.hf.space/v1/chat/completions \
  -H "Authorization: Bearer YOUR_MASTER_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "groq/llama3-8b-8192",
    "messages": [{"role": "user", "content": "Hello!"}]
  }'

# Together AI with a nested model path
curl https://your-space.hf.space/v1/chat/completions \
  -H "Authorization: Bearer YOUR_MASTER_KEY" \
  -d '{
    "model": "together/meta-llama/Llama-3-70b-chat-hf",
    "messages": [{"role": "user", "content": "Hi"}],
    "stream": true
  }'

# Use default provider (no prefix needed)
curl https://your-space.hf.space/v1/chat/completions \
  -H "Authorization: Bearer YOUR_MASTER_KEY" \
  -d '{"model": "gpt-4o", "messages": [{"role": "user", "content": "Hello"}]}'

# List all models across all providers
curl https://your-space.hf.space/v1/models \
  -H "Authorization: Bearer YOUR_MASTER_KEY"
```

### Use with the OpenAI Python SDK

```python
from openai import OpenAI

client = OpenAI(
    base_url="https://your-space.hf.space/v1",
    api_key="YOUR_MASTER_KEY",
)

# Route to Groq
response = client.chat.completions.create(
    model="groq/llama3-8b-8192",
    messages=[{"role": "user", "content": "Hello!"}],
)

# Route to Anthropic (via OpenAI-compat layer)
response = client.chat.completions.create(
    model="anthropic/claude-3-5-sonnet-20241022",
    messages=[{"role": "user", "content": "Hello!"}],
)
```

---

## Environment Variable Seeding

Pre-seed providers without touching the dashboard by setting Space secrets:

```
PROVIDER_OPENAI_URL=https://api.openai.com/v1
PROVIDER_OPENAI_KEY=sk-...

PROVIDER_GROQ_URL=https://api.groq.com/openai/v1
PROVIDER_GROQ_KEY=gsk_...

PROVIDER_TOGETHER_URL=https://api.together.xyz/v1
PROVIDER_TOGETHER_KEY=...
```

These are imported on startup if the provider name doesn't already exist in the database.

---

## API Reference

| Route | Auth | Description |
|-------|------|-------------|
| `GET /` | Master key (dashboard login) | Web dashboard |
| `GET /health` | None | Health check + provider count |
| `GET /v1/models` | Master key | Aggregated model list from all providers |
| `POST /v1/chat/completions` | Master key | Proxied chat completions |
| `POST /v1/completions` | Master key | Proxied text completions |
| `POST /v1/embeddings` | Master key | Proxied embeddings |
| `GET /v1/*` | Master key | Any other OpenAI-compatible endpoint |

---

## Model Routing

The proxy determines which provider to use based on the `model` field:

1. **With prefix** β€” `groq/llama3-8b-8192` β†’ routes to the provider named `Groq`, forwards model as `llama3-8b-8192`
2. **Nested paths** β€” `together/meta-llama/Llama-3-70b` β†’ routes to `Together`, forwards `meta-llama/Llama-3-70b`
3. **No prefix** β€” uses the provider marked as **Default** in Settings
4. **No default set** β€” uses the first enabled provider

Matching is **case-insensitive**: `GROQ/`, `groq/`, and `Groq/` all route to a provider named `Groq`.

---

## Dashboard Tabs

### Providers
Add, edit, enable/disable, and delete providers. Set one as the default. API keys are stored encrypted in SQLite and only the last 4 characters are ever displayed.

### Logs
Auto-refreshing table (every 5s) of the last 200 requests: timestamp, provider, model, endpoint, status code, and latency. Automatically truncated at 500 stored rows.

### Settings
View and copy your master key, regenerate it (invalidates immediately), and set the default provider.

---

## Security Notes

- Provider API keys are **never returned** by any API endpoint β€” only the last 4 characters are shown in the dashboard
- The master key protects all `/v1/*` endpoints and the dashboard
- CORS is open (`*`) so the proxy can be called from any frontend β€” restrict this in production if needed
- The dashboard login stores the master key in `sessionStorage` (cleared on tab close)

---

## Local Development

```bash
pip install fastapi uvicorn httpx sqlalchemy
uvicorn main:app --reload --port 7860
# Open http://localhost:7860
# Master key printed to terminal on first run
```

Data is stored in `./db.sqlite` when `/data` doesn't exist.