keyway-router 0.1.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.
- keyway_router-0.1.0/LICENSE +21 -0
- keyway_router-0.1.0/PKG-INFO +383 -0
- keyway_router-0.1.0/README.md +350 -0
- keyway_router-0.1.0/pyproject.toml +52 -0
- keyway_router-0.1.0/setup.cfg +4 -0
- keyway_router-0.1.0/src/keyway/__init__.py +5 -0
- keyway_router-0.1.0/src/keyway/__main__.py +22 -0
- keyway_router-0.1.0/src/keyway/app.py +780 -0
- keyway_router-0.1.0/src/keyway/config.py +110 -0
- keyway_router-0.1.0/src/keyway/crypto.py +41 -0
- keyway_router-0.1.0/src/keyway/llm_keys.py +27 -0
- keyway_router-0.1.0/src/keyway/llm_protocol.py +490 -0
- keyway_router-0.1.0/src/keyway/llm_router.py +502 -0
- keyway_router-0.1.0/src/keyway/llm_store.py +960 -0
- keyway_router-0.1.0/src/keyway/llm_tools.py +105 -0
- keyway_router-0.1.0/src/keyway/models.py +93 -0
- keyway_router-0.1.0/src/keyway/web/admin.html +218 -0
- keyway_router-0.1.0/src/keyway/web/assets/admin.js +776 -0
- keyway_router-0.1.0/src/keyway/web/docs.html +259 -0
- keyway_router-0.1.0/src/keyway/web/index.html +73 -0
- keyway_router-0.1.0/src/keyway_router.egg-info/PKG-INFO +383 -0
- keyway_router-0.1.0/src/keyway_router.egg-info/SOURCES.txt +25 -0
- keyway_router-0.1.0/src/keyway_router.egg-info/dependency_links.txt +1 -0
- keyway_router-0.1.0/src/keyway_router.egg-info/entry_points.txt +2 -0
- keyway_router-0.1.0/src/keyway_router.egg-info/requires.txt +9 -0
- keyway_router-0.1.0/src/keyway_router.egg-info/top_level.txt +1 -0
- keyway_router-0.1.0/tests/test_keyway.py +531 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Keyway Contributors
|
|
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,383 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: keyway-router
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A lightweight self-hosted LLM routing gateway with OpenAI & Anthropic dual-protocol support
|
|
5
|
+
Author: Keyway Contributors
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/DeconBear/keyway
|
|
8
|
+
Project-URL: Issues, https://github.com/DeconBear/keyway/issues
|
|
9
|
+
Keywords: llm,router,gateway,openai,anthropic,proxy,claude
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Framework :: FastAPI
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Topic :: Internet :: Proxy Servers
|
|
21
|
+
Requires-Python: >=3.10
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
License-File: LICENSE
|
|
24
|
+
Requires-Dist: fastapi>=0.100.0
|
|
25
|
+
Requires-Dist: uvicorn>=0.23.0
|
|
26
|
+
Requires-Dist: httpx>=0.27.0
|
|
27
|
+
Requires-Dist: cryptography>=42.0.0
|
|
28
|
+
Requires-Dist: pydantic>=2.0.0
|
|
29
|
+
Provides-Extra: dev
|
|
30
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
31
|
+
Requires-Dist: httpx>=0.27.0; extra == "dev"
|
|
32
|
+
Dynamic: license-file
|
|
33
|
+
|
|
34
|
+
# Keyway
|
|
35
|
+
|
|
36
|
+
**A lightweight self-hosted LLM routing gateway with OpenAI & Anthropic dual-protocol support.**
|
|
37
|
+
|
|
38
|
+
[English](#english) | [中文](#中文)
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
<a id="english"></a>
|
|
43
|
+
|
|
44
|
+
## English
|
|
45
|
+
|
|
46
|
+
Keyway lets you route LLM requests from any OpenAI/Anthropic-compatible client (Claude Code, Cursor, OpenAI SDK, etc.) to multiple upstream providers (DeepSeek, OpenAI, Anthropic, Volcengine, Zhipu GLM, Qwen, and more) — all through a single self-issued `db_sk_` API key.
|
|
47
|
+
|
|
48
|
+
### Features
|
|
49
|
+
|
|
50
|
+
- **Dual-protocol proxy**: OpenAI `/v1/chat/completions` + Anthropic `/v1/messages` — both work out of the box
|
|
51
|
+
- **Model aliases**: map a client-facing name (e.g. `deepseek-v4-pro`) to any upstream model
|
|
52
|
+
- **Group isolation**: keys are bound to groups; a client's key can only access its group's providers/routes
|
|
53
|
+
- **Self-issued API keys**: generate `db_sk_` keys for your team; plaintext is retrievable by admin
|
|
54
|
+
- **Encrypted at rest**: all upstream API keys and self-issued key plaintexts are Fernet-encrypted
|
|
55
|
+
- **Built-in tools**: Tavily web search auto-injected into OpenAI tool-use loops
|
|
56
|
+
- **Request logging**: every upstream call is logged with status, latency, and token counts
|
|
57
|
+
- **E2E testing**: one-click probe of all enabled routes
|
|
58
|
+
- **Generation forwarding**: image/video/3D endpoints via configurable `upstream_path`
|
|
59
|
+
- **No external dependencies**: just Python + SQLite — no database server, no Redis
|
|
60
|
+
- **Web admin UI**: full CRUD management interface included
|
|
61
|
+
|
|
62
|
+
### Quick Start
|
|
63
|
+
|
|
64
|
+
#### Option 1: pip install
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
pip install keyway-router
|
|
68
|
+
|
|
69
|
+
# Generate a secret and create .env
|
|
70
|
+
python -c "import secrets; print('KEYWAY_SECRET=' + secrets.token_urlsafe(48))" > .env
|
|
71
|
+
echo "KEYWAY_ADMIN_TOKEN=my-admin-token" >> .env
|
|
72
|
+
|
|
73
|
+
# Run
|
|
74
|
+
keyway
|
|
75
|
+
# → Server starts on http://localhost:9233
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
#### Option 2: Docker
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
cd docker
|
|
82
|
+
cp .env.example .env
|
|
83
|
+
# Edit .env: set KEYWAY_SECRET and KEYWAY_ADMIN_TOKEN
|
|
84
|
+
docker compose up -d
|
|
85
|
+
# → Server at http://localhost:9233
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
#### Option 3: From source
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
git clone https://github.com/keyway-gateway/keyway.git
|
|
92
|
+
cd keyway
|
|
93
|
+
pip install -e ".[dev]"
|
|
94
|
+
|
|
95
|
+
cp .env.example .env
|
|
96
|
+
# Edit .env: set KEYWAY_SECRET (required) and KEYWAY_ADMIN_TOKEN
|
|
97
|
+
|
|
98
|
+
python -m keyway
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Configuration
|
|
102
|
+
|
|
103
|
+
All settings are via environment variables (or `.env` file):
|
|
104
|
+
|
|
105
|
+
| Variable | Default | Description |
|
|
106
|
+
|---|---|---|
|
|
107
|
+
| `KEYWAY_SECRET` | *(required)* | Fernet encryption key for at-rest secrets. Generate with `python -c "import secrets; print(secrets.token_urlsafe(48))"` |
|
|
108
|
+
| `KEYWAY_ADMIN_TOKEN` | *(auto-generated)* | Admin token for the management UI. If empty, a random token is printed to console on startup |
|
|
109
|
+
| `KEYWAY_HOST` | `127.0.0.1` | Bind address |
|
|
110
|
+
| `KEYWAY_PORT` | `9233` | Bind port |
|
|
111
|
+
| `KEYWAY_DATA_DIR` | `./data` | SQLite database location |
|
|
112
|
+
| `KEYWAY_CORS_ORIGINS` | `http://127.0.0.1:9233,...` | CORS allowed origins |
|
|
113
|
+
| `KEYWAY_PUBLIC_BASE_URL` | *(auto-inferred)* | Base URL shown in admin UI for key setup |
|
|
114
|
+
| `KEYWAY_LOG_LEVEL` | `info` | Log level |
|
|
115
|
+
|
|
116
|
+
### Usage Guide
|
|
117
|
+
|
|
118
|
+
#### 1. Access the admin UI
|
|
119
|
+
|
|
120
|
+
Open `http://localhost:9233/` in your browser. Log in with your `KEYWAY_ADMIN_TOKEN`.
|
|
121
|
+
|
|
122
|
+
#### 2. Add an upstream provider
|
|
123
|
+
|
|
124
|
+
In the "default" group, scroll to "Upstream Providers", fill in:
|
|
125
|
+
- **ID**: e.g. `deepseek`
|
|
126
|
+
- **Name**: e.g. `DeepSeek`
|
|
127
|
+
- **Protocol**: `openai` or `anthropic`
|
|
128
|
+
- **Base URL**: e.g. `https://api.deepseek.com/v1`
|
|
129
|
+
- **API Key**: your upstream provider key
|
|
130
|
+
|
|
131
|
+
#### 3. Create a model route
|
|
132
|
+
|
|
133
|
+
Scroll to "Model Routes", fill in:
|
|
134
|
+
- **Alias**: the name clients will use, e.g. `deepseek-v4-pro`
|
|
135
|
+
- **Provider**: select the provider you just created
|
|
136
|
+
- **Upstream Model**: the real model name at the provider, e.g. `deepseek-chat`
|
|
137
|
+
|
|
138
|
+
#### 4. Create an API key
|
|
139
|
+
|
|
140
|
+
Scroll to "Self-issued API Keys", create a key. The plaintext `db_sk_...` is shown once — save it. You can re-retrieve it later from the key list.
|
|
141
|
+
|
|
142
|
+
#### 5. Connect your client
|
|
143
|
+
|
|
144
|
+
##### Claude Code
|
|
145
|
+
|
|
146
|
+
Create `.claude/settings.local.json` in your project:
|
|
147
|
+
|
|
148
|
+
```json
|
|
149
|
+
{
|
|
150
|
+
"env": {
|
|
151
|
+
"ANTHROPIC_BASE_URL": "http://localhost:9233",
|
|
152
|
+
"ANTHROPIC_AUTH_TOKEN": "db_sk_your-key-here",
|
|
153
|
+
"ANTHROPIC_MODEL": "deepseek-v4-pro",
|
|
154
|
+
"ANTHROPIC_DEFAULT_HAIKU_MODEL": "deepseek-v4-pro",
|
|
155
|
+
"ANTHROPIC_DEFAULT_SONNET_MODEL": "deepseek-v4-pro",
|
|
156
|
+
"ANTHROPIC_DEFAULT_OPUS_MODEL": "deepseek-v4-pro"
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
##### OpenAI SDK (Python)
|
|
162
|
+
|
|
163
|
+
```python
|
|
164
|
+
from openai import OpenAI
|
|
165
|
+
|
|
166
|
+
client = OpenAI(
|
|
167
|
+
base_url="http://localhost:9233/v1",
|
|
168
|
+
api_key="db_sk_your-key-here",
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
resp = client.chat.completions.create(
|
|
172
|
+
model="deepseek-v4-pro",
|
|
173
|
+
messages=[{"role": "user", "content": "Hello!"}],
|
|
174
|
+
)
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### API Reference
|
|
178
|
+
|
|
179
|
+
#### Proxy endpoints (Bearer `db_sk_` auth)
|
|
180
|
+
|
|
181
|
+
| Method | Path | Description |
|
|
182
|
+
|---|---|---|
|
|
183
|
+
| `POST` | `/v1/chat/completions` | OpenAI-compatible chat completions (stream + non-stream) |
|
|
184
|
+
| `GET` | `/v1/models` | OpenAI-compatible model list (returns enabled route aliases) |
|
|
185
|
+
| `POST` | `/v1/messages` | Anthropic Messages-compatible (stream + non-stream) |
|
|
186
|
+
| `POST` | `/v1/messages/count_tokens` | Anthropic token count estimate |
|
|
187
|
+
| `POST` | `/v1/generations` | Generic generation forwarding (image/video/3D) |
|
|
188
|
+
|
|
189
|
+
#### Admin endpoints (`X-Admin-Token` header or session cookie)
|
|
190
|
+
|
|
191
|
+
| Method | Path | Description |
|
|
192
|
+
|---|---|---|
|
|
193
|
+
| `POST` | `/admin/login` | Login with admin token → session cookie |
|
|
194
|
+
| `GET` | `/admin/session` | Verify session |
|
|
195
|
+
| `POST` | `/admin/logout` | Logout |
|
|
196
|
+
| `GET` | `/admin/config` | Runtime config (base URL for key setup) |
|
|
197
|
+
| `GET/POST` | `/admin/llm/groups` | List / create groups |
|
|
198
|
+
| `GET/PATCH/DELETE` | `/admin/llm/groups/{id}` | Get / update / delete a group |
|
|
199
|
+
| `POST` | `/admin/llm/groups/{id}/copy` | Deep-copy a group (re-issues new keys) |
|
|
200
|
+
| `GET/POST` | `/admin/llm/groups/{id}/providers` | List / create providers in a group |
|
|
201
|
+
| `GET/PATCH/DELETE` | `/admin/llm/providers/{id}` | Get / update / delete a provider |
|
|
202
|
+
| `GET/POST` | `/admin/llm/groups/{id}/routes` | List / create routes in a group |
|
|
203
|
+
| `GET/PATCH/DELETE` | `/admin/llm/routes/{id}` | Get / update / delete a route |
|
|
204
|
+
| `GET/POST` | `/admin/llm/groups/{id}/keys` | List / create API keys in a group |
|
|
205
|
+
| `GET` | `/admin/llm/keys/{id}/plaintext` | Retrieve key plaintext (admin only) |
|
|
206
|
+
| `PATCH/DELETE` | `/admin/llm/keys/{id}` | Update / delete an API key |
|
|
207
|
+
| `GET/POST` | `/admin/llm/groups/{id}/tool-providers` | List / create tool providers |
|
|
208
|
+
| `PATCH/DELETE` | `/admin/llm/tool-providers/{id}` | Update / delete a tool provider |
|
|
209
|
+
| `GET` | `/admin/llm/logs` | Request logs (filter by api_key_id, group_id) |
|
|
210
|
+
| `GET` | `/admin/llm/stats` | Stats for a specific key |
|
|
211
|
+
| `POST` | `/admin/llm/test` | Probe a single provider or route |
|
|
212
|
+
| `POST` | `/admin/llm/e2e` | End-to-end test of all enabled routes |
|
|
213
|
+
|
|
214
|
+
### Development
|
|
215
|
+
|
|
216
|
+
```bash
|
|
217
|
+
pip install -e ".[dev]"
|
|
218
|
+
pytest -q
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
### Architecture
|
|
222
|
+
|
|
223
|
+
- **Backend**: FastAPI + uvicorn, 5 pip dependencies (`fastapi`, `uvicorn`, `httpx`, `cryptography`, `pydantic`)
|
|
224
|
+
- **Storage**: SQLite (single file, `keyway.db`) — no external database
|
|
225
|
+
- **Auth**: Admin token (single-admin) + self-issued `db_sk_` API keys (group-scoped)
|
|
226
|
+
- **Encryption**: Fernet (from `cryptography`) — SHA-256 of `KEYWAY_SECRET` derives the key
|
|
227
|
+
- **Frontend**: Zero-dependency vanilla JS, served by FastAPI's StaticFiles
|
|
228
|
+
|
|
229
|
+
---
|
|
230
|
+
|
|
231
|
+
<a id="中文"></a>
|
|
232
|
+
|
|
233
|
+
## 中文
|
|
234
|
+
|
|
235
|
+
Keyway 是一个轻量级的可自部署 LLM 路由网关,支持 OpenAI 和 Anthropic 双协议。通过一条自签发的 `db_sk_` API Key,即可将 Claude Code、Cursor、OpenAI SDK 等客户端的请求路由到多个上游提供商(DeepSeek、OpenAI、Anthropic、火山方舟、智谱 GLM、千问等)。
|
|
236
|
+
|
|
237
|
+
### 功能特性
|
|
238
|
+
|
|
239
|
+
- **双协议代理**:OpenAI `/v1/chat/completions` + Anthropic `/v1/messages` —— 开箱即用
|
|
240
|
+
- **模型别名**:将客户端可见的名称(如 `deepseek-v4-pro`)映射到任意上游模型
|
|
241
|
+
- **分组隔离**:Key 绑定到组;客户端持有的 Key 只能访问该组的 provider/route
|
|
242
|
+
- **自签发 API Key**:为团队生成 `db_sk_` Key;管理员可随时取回明文
|
|
243
|
+
- **加密存储**:所有上游 API Key 和自签发 Key 明文均使用 Fernet 加密
|
|
244
|
+
- **内置工具**:Tavily 网络搜索自动注入 OpenAI 工具调用循环
|
|
245
|
+
- **请求日志**:每次上游调用均记录状态码、延迟和 token 数
|
|
246
|
+
- **端到端测试**:一键探测所有已启用路由
|
|
247
|
+
- **生成式转发**:通过可配置的 `upstream_path` 支持图片/视频/3D 端点
|
|
248
|
+
- **零外部依赖**:仅需 Python + SQLite —— 无需数据库服务器、无需 Redis
|
|
249
|
+
- **Web 管理界面**:包含完整的图形化 CRUD 管理
|
|
250
|
+
|
|
251
|
+
### 快速开始
|
|
252
|
+
|
|
253
|
+
#### 方式一:pip 安装
|
|
254
|
+
|
|
255
|
+
```bash
|
|
256
|
+
pip install keyway-router
|
|
257
|
+
|
|
258
|
+
# 生成密钥并创建 .env
|
|
259
|
+
python -c "import secrets; print('KEYWAY_SECRET=' + secrets.token_urlsafe(48))" > .env
|
|
260
|
+
echo "KEYWAY_ADMIN_TOKEN=my-admin-token" >> .env
|
|
261
|
+
|
|
262
|
+
# 启动
|
|
263
|
+
keyway
|
|
264
|
+
# → 服务启动在 http://localhost:9233
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
#### 方式二:Docker
|
|
268
|
+
|
|
269
|
+
```bash
|
|
270
|
+
cd docker
|
|
271
|
+
cp .env.example .env
|
|
272
|
+
# 编辑 .env:设置 KEYWAY_SECRET 和 KEYWAY_ADMIN_TOKEN
|
|
273
|
+
docker compose up -d
|
|
274
|
+
# → 服务在 http://localhost:9233
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
#### 方式三:从源码运行
|
|
278
|
+
|
|
279
|
+
```bash
|
|
280
|
+
git clone https://github.com/keyway-gateway/keyway.git
|
|
281
|
+
cd keyway
|
|
282
|
+
pip install -e ".[dev]"
|
|
283
|
+
|
|
284
|
+
cp .env.example .env
|
|
285
|
+
# 编辑 .env:设置 KEYWAY_SECRET(必填)和 KEYWAY_ADMIN_TOKEN
|
|
286
|
+
|
|
287
|
+
python -m keyway
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
### 配置项
|
|
291
|
+
|
|
292
|
+
所有配置通过环境变量(或 `.env` 文件)设置:
|
|
293
|
+
|
|
294
|
+
| 变量 | 默认值 | 说明 |
|
|
295
|
+
|---|---|---|
|
|
296
|
+
| `KEYWAY_SECRET` | *(必填)* | Fernet 加密密钥,用于加密存储的秘密。用 `python -c "import secrets; print(secrets.token_urlsafe(48))"` 生成 |
|
|
297
|
+
| `KEYWAY_ADMIN_TOKEN` | *(自动生成)* | 管理界面 token。留空则启动时随机生成并打印到控制台 |
|
|
298
|
+
| `KEYWAY_HOST` | `127.0.0.1` | 监听地址 |
|
|
299
|
+
| `KEYWAY_PORT` | `9233` | 监听端口 |
|
|
300
|
+
| `KEYWAY_DATA_DIR` | `./data` | SQLite 数据库存放目录 |
|
|
301
|
+
| `KEYWAY_CORS_ORIGINS` | `http://127.0.0.1:9233,...` | CORS 允许的来源 |
|
|
302
|
+
| `KEYWAY_PUBLIC_BASE_URL` | *(自动推断)* | 管理界面中显示给用户的 Base URL |
|
|
303
|
+
| `KEYWAY_LOG_LEVEL` | `info` | 日志级别 |
|
|
304
|
+
|
|
305
|
+
### 使用指南
|
|
306
|
+
|
|
307
|
+
#### 1. 打开管理界面
|
|
308
|
+
|
|
309
|
+
浏览器访问 `http://localhost:9233/`,输入 `KEYWAY_ADMIN_TOKEN` 登录。
|
|
310
|
+
|
|
311
|
+
#### 2. 添加上游 Provider
|
|
312
|
+
|
|
313
|
+
在"default"组中,找到"Upstream Providers",填写:
|
|
314
|
+
- **ID**:如 `deepseek`
|
|
315
|
+
- **名称**:如 `DeepSeek`
|
|
316
|
+
- **协议**:`openai` 或 `anthropic`
|
|
317
|
+
- **Base URL**:如 `https://api.deepseek.com/v1`
|
|
318
|
+
- **API Key**:你的上游提供商密钥
|
|
319
|
+
|
|
320
|
+
#### 3. 创建模型路由
|
|
321
|
+
|
|
322
|
+
找到"Model Routes",填写:
|
|
323
|
+
- **Alias**:客户端使用的名称,如 `deepseek-v4-pro`
|
|
324
|
+
- **Provider**:选择刚创建的 provider
|
|
325
|
+
- **Upstream Model**:上游真实模型名,如 `deepseek-chat`
|
|
326
|
+
|
|
327
|
+
#### 4. 创建 API Key
|
|
328
|
+
|
|
329
|
+
找到"Self-issued API Keys",创建 Key。明文 `db_sk_...` 仅显示一次——请妥善保存。管理员可随时从列表重新获取明文。
|
|
330
|
+
|
|
331
|
+
#### 5. 连接客户端
|
|
332
|
+
|
|
333
|
+
##### Claude Code
|
|
334
|
+
|
|
335
|
+
在项目根目录创建 `.claude/settings.local.json`:
|
|
336
|
+
|
|
337
|
+
```json
|
|
338
|
+
{
|
|
339
|
+
"env": {
|
|
340
|
+
"ANTHROPIC_BASE_URL": "http://localhost:9233",
|
|
341
|
+
"ANTHROPIC_AUTH_TOKEN": "db_sk_你的key",
|
|
342
|
+
"ANTHROPIC_MODEL": "deepseek-v4-pro",
|
|
343
|
+
"ANTHROPIC_DEFAULT_HAIKU_MODEL": "deepseek-v4-pro",
|
|
344
|
+
"ANTHROPIC_DEFAULT_SONNET_MODEL": "deepseek-v4-pro",
|
|
345
|
+
"ANTHROPIC_DEFAULT_OPUS_MODEL": "deepseek-v4-pro"
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
##### OpenAI SDK(Python)
|
|
351
|
+
|
|
352
|
+
```python
|
|
353
|
+
from openai import OpenAI
|
|
354
|
+
|
|
355
|
+
client = OpenAI(
|
|
356
|
+
base_url="http://localhost:9233/v1",
|
|
357
|
+
api_key="db_sk_你的key",
|
|
358
|
+
)
|
|
359
|
+
|
|
360
|
+
resp = client.chat.completions.create(
|
|
361
|
+
model="deepseek-v4-pro",
|
|
362
|
+
messages=[{"role": "user", "content": "你好!"}],
|
|
363
|
+
)
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
### 开发
|
|
367
|
+
|
|
368
|
+
```bash
|
|
369
|
+
pip install -e ".[dev]"
|
|
370
|
+
pytest -q
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
### 架构
|
|
374
|
+
|
|
375
|
+
- **后端**:FastAPI + uvicorn,5 个 pip 依赖(`fastapi`、`uvicorn`、`httpx`、`cryptography`、`pydantic`)
|
|
376
|
+
- **存储**:SQLite(单文件 `keyway.db`)—— 无外部数据库
|
|
377
|
+
- **鉴权**:管理员 token(单管理员)+ 自签发 `db_sk_` API Key(分组隔离)
|
|
378
|
+
- **加密**:Fernet(来自 `cryptography`)—— 用 `KEYWAY_SECRET` 的 SHA-256 派生密钥
|
|
379
|
+
- **前端**:零依赖原生 JS,由 FastAPI StaticFiles 提供服务
|
|
380
|
+
|
|
381
|
+
## License
|
|
382
|
+
|
|
383
|
+
MIT — see [LICENSE](LICENSE).
|