1数据模型概览
核心实体:product(内部产品)→ 持有多个 api_key;channel(上游渠道)提供多个 model(真实模型);logical_model(对外逻辑模型名)经 route 映射到候选 channel+model;每次调用写一行 request(记账);quota 控制每 key 额度;config_audit 记录管理变更。
product 1───* api_key ───* request *───1 logical_model
│ │
* * (route 候选)
quota channel 1───* model
config_audit (所有管理变更) (provider 白名单)
2D1 DDL(SQLite · 可执行)
-- 0001_init.sql
CREATE TABLE products (
id TEXT PRIMARY KEY, -- prod_xxx
name TEXT NOT NULL,
status TEXT NOT NULL DEFAULT 'active', -- active|suspended
created_at INTEGER NOT NULL -- epoch ms
);
CREATE TABLE api_keys (
id TEXT PRIMARY KEY, -- key_xxx
product_id TEXT NOT NULL REFERENCES products(id),
key_hash TEXT NOT NULL, -- sha256(明文),明文仅创建时返回一次
key_prefix TEXT NOT NULL, -- sk-aigw-xxxx(前 8 位,便于识别)
scopes TEXT NOT NULL DEFAULT 'chat,embeddings',
status TEXT NOT NULL DEFAULT 'active', -- active|revoked
rpm_limit INTEGER NOT NULL DEFAULT 60,
concurrency INTEGER NOT NULL DEFAULT 10,
created_at INTEGER NOT NULL,
revoked_at INTEGER
);
CREATE UNIQUE INDEX idx_api_keys_hash ON api_keys(key_hash);
CREATE INDEX idx_api_keys_product ON api_keys(product_id);
CREATE TABLE channels (
id TEXT PRIMARY KEY, -- ch_openrouter / ch_deepseek ...
provider TEXT NOT NULL, -- openrouter|deepseek|groq|workers_ai|...(白名单)
base_url TEXT NOT NULL,
secret_ref TEXT NOT NULL, -- 指向 Secret 名,不存明文 key
enabled INTEGER NOT NULL DEFAULT 1,
weight INTEGER NOT NULL DEFAULT 100,
created_at INTEGER NOT NULL
);
CREATE TABLE models (
id TEXT PRIMARY KEY, -- m_xxx
channel_id TEXT NOT NULL REFERENCES channels(id),
provider_model TEXT NOT NULL, -- 厂商真实模型名,如 deepseek/deepseek-v3.2
modality TEXT NOT NULL DEFAULT 'chat', -- chat|embedding
in_price REAL NOT NULL, -- USD / 1M input tokens
out_price REAL NOT NULL, -- USD / 1M output tokens
context_len INTEGER,
supports_stream INTEGER NOT NULL DEFAULT 1,
enabled INTEGER NOT NULL DEFAULT 1
);
CREATE INDEX idx_models_channel ON models(channel_id);
CREATE TABLE logical_models (
id TEXT PRIMARY KEY, -- 对外名: cheap-default / smart / embed-default
tier TEXT NOT NULL, -- free|cheap|premium|flagship|embedding
multiplier REAL NOT NULL DEFAULT 1.0, -- 计费倍率(折算额度单位)
cache_ttl INTEGER NOT NULL DEFAULT 0, -- 响应缓存 TTL 秒,0=不缓存
enabled INTEGER NOT NULL DEFAULT 1
);
CREATE TABLE routes (
id TEXT PRIMARY KEY,
logical_model_id TEXT NOT NULL REFERENCES logical_models(id),
model_id TEXT NOT NULL REFERENCES models(id),
priority INTEGER NOT NULL, -- 小=优先;同优先按 weight 分流
weight INTEGER NOT NULL DEFAULT 100,
enabled INTEGER NOT NULL DEFAULT 1
);
CREATE INDEX idx_routes_lm ON routes(logical_model_id, priority);
CREATE TABLE requests (
id TEXT PRIMARY KEY, -- req_xxx (=x-request-id)
ts INTEGER NOT NULL, -- epoch ms
product_id TEXT NOT NULL,
api_key_id TEXT NOT NULL,
logical_model TEXT NOT NULL,
channel_id TEXT, -- 实际命中渠道
provider_model TEXT,
in_tokens INTEGER NOT NULL DEFAULT 0,
out_tokens INTEGER NOT NULL DEFAULT 0,
cost_usd REAL NOT NULL DEFAULT 0, -- 实际上游成本
billed_units REAL NOT NULL DEFAULT 0, -- cost×倍率,扣配额用
cache_hit INTEGER NOT NULL DEFAULT 0,
fallback INTEGER NOT NULL DEFAULT 0,
status INTEGER NOT NULL, -- HTTP 状态
latency_ms INTEGER NOT NULL DEFAULT 0,
error_code TEXT
);
CREATE INDEX idx_requests_ts ON requests(ts);
CREATE INDEX idx_requests_product_ts ON requests(product_id, ts);
CREATE TABLE quotas (
api_key_id TEXT NOT NULL REFERENCES api_keys(id),
period TEXT NOT NULL, -- 'day' | 'month'
limit_units REAL NOT NULL, -- 额度单位上限
used_units REAL NOT NULL DEFAULT 0,
reset_at INTEGER NOT NULL,
PRIMARY KEY (api_key_id, period)
);
CREATE TABLE config_audit (
id TEXT PRIMARY KEY,
ts INTEGER NOT NULL,
actor TEXT NOT NULL, -- 管理员标识
action TEXT NOT NULL, -- create_channel|update_route|revoke_key...
entity TEXT NOT NULL,
detail TEXT -- JSON
);
KV:响应缓存 cache:<hash>→响应体;配置热缓存 cfg:*。Durable Object:每 key 一个限流实例。详见 23。
3鉴权
- 数据面:
Authorization: Bearer sk-aigw-...。引擎对明文 sha256 后查api_keys.key_hash(KV 缓存校验结果,短 TTL)。status≠active 或吊销 → 401。 - 管理端:独立
X-Admin-Token(Secret)或 Cloudflare Access(JWT);与数据面分离;所有写操作记config_audit。 - 明文 key 仅在创建时返回一次,库内只存哈希与前缀。
4数据面 API(OpenAI 兼容)
POST /v1/chat/completions
// 请求(OpenAI 兼容子集,Zod)
{
"model": "cheap-default", // 逻辑模型名(必填)
"messages": [{"role":"user","content":"..."}],
"stream": false, // true→SSE
"temperature": 0.7,
"max_tokens": 1024,
"metadata": { "feature": "dream-interpret" } // 可选,记账维度
}
// 响应(非流式,OpenAI 兼容)
{
"id": "chatcmpl_...",
"object": "chat.completion",
"model": "cheap-default", // 回显逻辑模型
"choices": [{ "index":0, "message":{"role":"assistant","content":"..."}, "finish_reason":"stop" }],
"usage": { "prompt_tokens":12, "completion_tokens":34, "total_tokens":46 }
}
// 响应头:x-request-id, x-gw-channel: ch_deepseek, x-gw-model: deepseek/deepseek-v3.2,
// x-gw-cache: hit|miss, x-gw-fallback: 0|1, x-gw-cost-usd: 0.0000
流式:text/event-stream,逐 chunk 透传 chat.completion.chunk,末尾 [DONE];usage 在最后一帧或由引擎按 token 计估。
POST /v1/embeddings
{ "model":"embed-default", "input":["text a","text b"] }
→ { "object":"list", "data":[{"object":"embedding","index":0,"embedding":[...]}], "usage":{...} }
GET /v1/models
→ { "object":"list", "data":[ {"id":"cheap-default","object":"model","tier":"cheap"}, ... ] }
// 仅返回 enabled 的逻辑模型
5管理端 API(/admin/*,需 Admin 鉴权)
| 方法 + 路径 | 作用 |
|---|---|
POST /admin/products | 创建产品 |
POST /admin/keys | 为产品签发 key(响应含明文一次);body: product_id, scopes, rpm_limit, quota |
DELETE /admin/keys/:id | 吊销 key |
POST /admin/channels / PATCH /admin/channels/:id | 增改渠道(provider 必须在白名单,否则 422) |
POST /admin/models | 登记真实模型 + 定价 |
PUT /admin/logical-models/:id/routes | 设置逻辑模型的候选路由(优先级+权重) |
GET /admin/usage?group=product|model&from=&to= | 用量/成本聚合(读 requests) |
GET /admin/keys/:id/quota | 查 key 当前配额与已用 |
// POST /admin/channels 示例(白名单校验)
{ "provider":"deepseek", "base_url":"https://api.deepseek.com",
"secret_ref":"DEEPSEEK_API_KEY", "weight":100 }
// provider ∈ {openrouter, deepseek, groq, workers_ai, openai, anthropic, google, mistral, together, deepinfra}
// 非白名单(如逆向/养号端点)→ 422 channel_provider_not_allowed(记 config_audit)
6错误码与限流头
| HTTP | error code | 含义 |
|---|---|---|
| 401 | invalid_api_key / key_revoked | 鉴权失败 |
| 403 | scope_forbidden | key 无该端点权限 |
| 404 | unknown_logical_model | 逻辑模型不存在/未启用 |
| 422 | channel_provider_not_allowed | 渠道非白名单(红线护栏) |
| 429 | rate_limited / quota_exceeded | RPM 超限 / 额度用尽(带 Retry-After) |
| 502 | upstream_error | 所有候选渠道均失败(已尝试回退) |
| 503 | no_available_channel | 逻辑模型无可用渠道 |
错误体(OpenAI 风格):{ "error": { "code":"quota_exceeded", "message":"...", "type":"gateway" } }。限流响应带 Retry-After 与 X-RateLimit-Remaining。