Layla API
OpenAI 兼容的 LLM API 中转平台 —— 统一管理上游渠道、模型映射与下游 Key 分发
一、项目简介
Layla API 是一个 OpenAI 协议兼容的 API 中转/聚合平台,核心解决三个问题:
- 上游渠道分散 —— 多个 LLM 服务商(OpenAI、智谱、小米、本地 LM Studio 等),各自有独立的地址和 Key
- 模型名称混乱 —— 同一个模型在不同渠道可能叫不同名字,需要统一对外名称
- 下游应用管理 —— 多个应用需要访问 API,需要独立的 Key、独立的授权范围、独立的故障转移策略
核心能力
- ✅ 上游渠道管理(增删改查、连通性测试、模型同步)
- ✅ 模型名称映射(对外名 → 上游名,支持多渠道多映射)
- ✅ 下游 Key 分发(应用级 Key,多渠道按优先级故障转移)
- ✅ OpenAI 协议代理(万能透传,SSE 流式,multipart ASR)
- ✅ 请求日志(完整请求/响应记录,音频 token 统计,TTFT 追踪)
- ✅ 运维看板(KPI 卡片、趋势图、热力图、排行分析、下钻明细)
- ✅ 对话历史(按应用/关键词搜索)
- ✅ 通知系统(NTFY / Email / Webhook)
- ✅ 用户管理(bcrypt 密码、Session 认证)
二、核心概念:渠道、模型、Key
2.1 三层实体关系
┌─────────────────────────────────────────────────────────────┐
│ 渠道 (Channel) │
│ 上游 API 服务,有地址和 Key,涉及费用和可用性 │
│ 例: Xiaomi_139, Xiaomi_171, LM Studio, OpenAI │
└─────────────────────────┬───────────────────────────────────┘
│ model_channels (多对多)
│ 含 per-channel 上游模型名
┌─────────────────────────▼───────────────────────────────────┐
│ 模型 (Model) │
│ 对外名 → 上游名 的映射规则 │
│ 例: mimo-v2.5 → mimo-v2.5, gpt-4o → glm-ocr │
└─────────────────────────┬───────────────────────────────────┘
│ token_tier_models (多对多)
┌─────────────────────────▼───────────────────────────────────┐
│ Key (Token) │
│ 下游应用的 API Key,选择多个渠道+一组模型 │
│ 渠道按选择顺序作为优先级,故障转移依次尝试 │
└─────────────────────────────────────────────────────────────┘
2.2 渠道 (Channel) —— 上游服务的抽象
渠道代表一个上游 LLM API 服务。
| 属性 |
说明 |
| 名称 |
自定义标识,如 Xiaomi_139 |
| Base URL |
上游地址,如 https://api.openai.com |
| API Key |
上游服务的密钥 |
| 启用/禁用 |
禁用渠道 = 停止计费 + 阻止下游调用 |
禁用级联:禁用渠道时,系统检查该渠道关联的每个模型是否还有其他已启用的渠道。如果没有,自动禁用该模型。重新启用渠道时,自动恢复。
2.3 模型 (Model) —— 映射规则
一条模型记录 = 一条映射规则:对外名(model_name) → 上游名(upstream_model)
ID | 对外名 (model_name) | 上游名 (upstream_model) | 渠道
1 | mimo-v2.5 | mimo-v2.5 | Xiaomi_139, Xiaomi_171
6 | gpt-4o | glm-ocr | LM Studio
关键规则:
(model_name, upstream_model) 唯一约束 —— 同一对不能重复
- 对外名可以重复(同名不同记录 = 同名映射到不同上游模型)
- 一条模型记录可关联多个渠道(通过
model_channels 表)
- 每个渠道可指定不同的上游模型名(
model_channels.upstream_model)
两种创建模式:
| 模式 |
入口 |
说明 |
| 自上而下 |
渠道管理 → 同步 |
从上游拉取模型,自动创建(对外名=上游名) |
| 自下而上 |
模型管理 → 新建 |
手动创建映射(对外名≠上游名),选择支持的渠道 |
2.4 Key (Token) —— 下游应用的访问凭证
创建 Key 时需要选择:
- 渠道列表 —— 多个渠道,按选择顺序作为优先级
- 模型列表 —— 该 Key 可以使用的模型
双向筛选:
- 选择渠道后,模型列表自动过滤为这些渠道都支持的模型
- 选择模型后,渠道列表自动过滤为都支持这些模型的渠道
- 切换选择时,不兼容的对端选项自动移除
故障转移:当请求到达时,代理层按渠道优先级依次尝试。如果第一个渠道失败,自动尝试下一个,直到成功或全部失败。
2.5 完整请求链路
客户端: POST /v1/chat/completions
Body: { "model": "mimo-v2.5", "messages": [...] }
Header: Authorization: Bearer sk-abc123...
↓ 1. 验证 Key
tokens 表找到 sk-abc123 → 应用 ChatX
↓ 2. 加载该 Key 的渠道配置(tier="primary", 按 priority 排序)
Tier 1 (priority=1): Xiaomi_139, 模型 [mimo-v2.5(ID=1)]
Tier 2 (priority=2): Xiaomi_171, 模型 [mimo-v2.5(ID=1)]
↓ 3. 遍历渠道,匹配模型
Xiaomi_139: 有 mimo-v2.5(ID=1) → 匹配成功
↓ 4. 查找上游模型名
model_channels: model_id=1 + channel_id=Xiaomi_139 → upstream_model = "mimo-v2.5"
↓ 5. 替换并转发
实际发给 Xiaomi_139: { "model": "mimo-v2.5" }
↓ 如果 Xiaomi_139 失败 → 尝试 Xiaomi_171
2.6 渠道能力 (ChannelModelCapability)
系统会记录每个渠道实际支持的上游模型列表(通过"拉取模型"功能从上游 /v1/models 获取)。这个数据用于:
- 同步模型时校验:只能同步渠道实际支持的模型
- 模型管理编辑时校验:选择的渠道必须支持该上游模型
- 与
model_channels(已映射的模型)区分:capability = 渠道能提供什么,model_channels = 平台实际用了什么
三、功能模块
3.1 渠道管理 (/channels)
| 功能 |
说明 |
| 新建渠道 |
填写名称、Base URL、API Key |
| 编辑渠道 |
修改配置 |
| 删除渠道 |
需先解除模型关联和 Key 引用 |
| 启用/禁用 |
禁用会级联禁用无其他渠道的模型 |
| 拉取模型 |
从上游 /v1/models 实时获取,更新 channel_model_capabilities |
| 同步模型 |
选择需要的模型,自动创建映射并关联到渠道 |
| 测试连通性 |
选择一个模型发送测试请求,显示延迟和结果 |
3.2 模型管理 (/models)
| 功能 |
说明 |
| 新建模型 |
创建映射规则(对外名→上游名),选择关联渠道 |
| 编辑模型 |
修改映射、价格、类型、渠道关联 |
| 删除模型 |
需先解除 Key 引用 |
| 渠道配置 |
每个模型可关联多个渠道,每个渠道可指定不同的上游模型名 |
| 价格配置 |
输入/缓存/输出/音频价格(¥/百万 tokens) |
| 类型标记 |
chat / completion / embedding / image / audio / moderation / other |
校验规则:
- 同一条模型映射的渠道上游名称必须统一
- 选择的渠道必须在
channel_model_capabilities 中包含该上游模型
- 同一渠道的同一上游模型不能映射到多个平台模型
3.3 Key 管理 (/tokens)
| 功能 |
说明 |
| 新建 Key |
填写应用名,选择渠道和模型 |
| 编辑 Key |
修改渠道和模型授权 |
| 删除 Key |
级联删除 Tier 和关联 |
| 启用/禁用 |
禁用 Key = 停止该应用的所有访问 |
| 密钥轮换 |
预览新密钥 → 确认后旧密钥失效(两步流程) |
| 复制 Key |
一键复制到剪贴板 |
API 数据结构:
{
"name": "ChatX",
"channel_ids": [1, 2],
"model_ids": [1, 5]
}
channel_ids:选择的渠道,数组顺序 = 优先级顺序
model_ids:选择的模型,应用到所有选中的渠道
3.4 使用记录 (/logs)
| 功能 |
说明 |
| 请求列表 |
分页展示所有 API 调用记录 |
| 筛选 |
按应用、模型、状态(成功/失败)筛选 |
| 排序 |
按时间升序/降序 |
| 日志详情 |
点击"详情"查看完整请求体和响应体 |
| 导出 CSV |
支持按时间范围导出 |
| 音频统计 |
ASR 请求的 audio_tokens 和 audio_seconds |
| TTFT |
流式请求的首 Token 延迟 |
3.5 运维看板 (/)
| 功能 |
说明 |
| KPI 卡片 |
Token 消耗、消费金额、API 请求数(支持环比对比) |
| 小指标 |
TTFT P90、成功率、单次成本、百万均价 |
| 应用排行 |
Top 10 应用的 Token/请求数/费用/均价,可下钻查看明细 |
| 分布饼图 |
按应用或按模型的 Token 分布 |
| RPM/TPM |
实时监控折线图 |
| 热力图 |
24 小时请求分布 |
| 趋势分析 |
7/30 天的请求数(柱状)+费用(折线) 双轴图,PC 默认 30 天,移动端默认 7 天 |
3.6 对话历史 (/conversations)
按应用名和关键词搜索请求记录,解析消息内容展示对话流。
3.7 通知系统 (/notifications)
支持三种通知渠道:
- NTFY —— 推送到 NTFY 服务
- Email —— SMTP 邮件通知
- Webhook —— HTTP 回调
每种通知可独立启用/禁用,支持测试发送。
3.8 个人设置 (/profile)
修改显示名称、邮箱、密码、头像。
四、技术栈
| 层 |
技术 |
版本 |
| 后端 |
Go + Gin + GORM |
Go 1.25 |
| 前端 |
React + TypeScript + TailwindCSS v4 + shadcn/ui |
React 19 |
| 数据库 |
PostgreSQL |
15+ |
| 图表 |
Recharts |
3.x |
| 图标 |
Lucide React |
1.x |
| 状态管理 |
Zustand |
5.x |
| 构建 |
Vite |
8.x |
| 部署 |
systemd / Docker |
- |
五、项目结构
Layla_API/
├── main.go # 入口,路由注册,优雅关闭
├── config/config.go # 环境变量配置
├── database/database.go # 数据库连接 + 迁移 + 密码升级
├── models/models.go # 数据模型定义
├── handlers/
│ ├── auth.go # 登录/登出/用户资料 (bcrypt)
│ ├── channel.go # 渠道 CRUD + 模型同步 + 测试
│ ├── model.go # 模型 CRUD + 渠道关联 + 校验
│ ├── token.go # Key CRUD + 渠道授权 + 密钥轮换
│ ├── proxy.go # OpenAI 协议代理核心
│ ├── stats.go # 统计接口 (30s 缓存)
│ ├── log.go # 日志查询 + 导出 + 对话历史
│ └── notification.go # 通知 CRUD + 发送
├── middleware/auth.go # Session 认证中间件
├── frontend/
│ ├── src/
│ │ ├── App.tsx # 侧栏导航 + 路由
│ │ ├── pages/ # 各功能页面
│ │ ├── components/ui/ # shadcn/ui 组件
│ │ ├── lib/ # API 封装 + 工具函数
│ │ └── stores/ # Zustand 状态管理
│ └── package.json
├── web/ # 前端构建产物 (Go 静态服务)
├── Dockerfile # 预编译方案
├── docker-compose.yml
└── .env # 环境变量
六、API 端点
认证
| 方法 |
路径 |
说明 |
| POST |
/api/login |
登录 (bcrypt 验证) |
| POST |
/api/logout |
登出 |
| GET |
/api/me |
当前用户信息 |
| PUT |
/api/me |
更新用户资料 |
渠道管理
| 方法 |
路径 |
说明 |
| GET |
/api/channels |
渠道列表 |
| POST |
/api/channels |
创建渠道 |
| PUT |
/api/channels/:id |
更新渠道 (支持级联禁用) |
| DELETE |
/api/channels/:id |
删除渠道 (需先解除关联) |
| GET |
/api/channels/:id/fetch-models |
拉取上游模型 (实时, 更新 capabilities) |
| POST |
/api/channels/:id/sync-models |
同步选中的模型到数据库 |
| GET |
/api/channels/:id/models |
渠道关联的模型列表 |
| GET |
/api/channel-model-capabilities |
渠道能力清单 |
| POST |
/api/channels/:id/test |
测试连通性 |
模型管理
| 方法 |
路径 |
说明 |
| GET |
/api/models |
模型列表 (含 channel_configs) |
| POST |
/api/models |
创建模型 (含 channel_configs) |
| PUT |
/api/models/:id |
更新模型 |
| DELETE |
/api/models/:id |
删除模型 |
| GET |
/api/tokens/:tokenId/available-models |
Key 可用的模型 |
Key 管理
| 方法 |
路径 |
说明 |
| GET |
/api/tokens |
Key 列表 (含 Tier 详情) |
| POST |
/api/tokens |
创建 Key (channel_ids + model_ids) |
| PUT |
/api/tokens/:id |
更新 Key |
| DELETE |
/api/tokens/:id |
删除 Key |
| POST |
/api/tokens/:id/rotate?preview=true |
预览新密钥 |
| POST |
/api/tokens/:id/confirm-rotate |
确认轮换 (old_key + new_key) |
日志
| 方法 |
路径 |
说明 |
| GET |
/api/logs |
查询 (分页、筛选、排序) |
| GET |
/api/logs/:id |
日志详情 |
| GET |
/api/logs/export |
导出 CSV |
| GET |
/api/logs/options |
筛选选项 |
| GET |
/api/conversations |
对话历史 |
统计
| 方法 |
路径 |
说明 |
| GET |
/api/stats |
看板数据 (range/model 过滤) |
| GET |
/api/stats/drill-down |
下钻明细 |
| GET |
/api/stats/rpm-tpm |
RPM/TPM 实时数据 |
通知
| 方法 |
路径 |
说明 |
| GET |
/api/notifications |
通知配置列表 |
| POST |
/api/notifications |
创建 |
| PUT |
/api/notifications/:id |
更新 |
| DELETE |
/api/notifications/:id |
删除 |
| POST |
/api/notifications/:id/test |
测试发送 |
OpenAI 兼容代理
| 方法 |
路径 |
说明 |
| ANY |
/v1/*path |
透传到上游 (自动故障转移) |
| GET |
/v1/models |
返回 Key 可用的模型列表 (不转发) |
七、代理层特性
| 特性 |
说明 |
| 万能透传 |
只替换 model 字段,其他原样转发 |
| 认证兼容 |
支持 Authorization: Bearer 和 api-key 两种格式 |
| 流式支持 |
SSE 流式透传,测量 TTFT |
| 故障转移 |
多渠道按 priority 依次尝试 |
| multipart |
ASR 接口的 multipart/form-data 透传 |
| URL 自动补全 |
base_url 自动补 /v1 |
| 请求限制 |
MAX_REQUEST_BYTES (默认 32MB) |
| 请求头透传 |
过滤敏感头后透传客户端请求头 |
| 响应头透传 |
过滤 hop-by-hop 头后透传上游响应头 |
八、数据库表结构
| 表 |
说明 |
关键约束 |
channels |
上游渠道 |
- |
models |
模型映射 (对外名→上游名) |
unique(model_name, upstream_model) |
model_channels |
模型-渠道关联 |
unique(model_id, channel_id), 含 upstream_model |
channel_model_capabilities |
渠道支持的上游模型 |
unique(channel_id, upstream_model) |
tokens |
下游 Key |
unique(key), unique(parent_id) |
token_tiers |
Key 的渠道配置 (全部 tier="primary") |
unique(token_id, tier) |
token_tier_models |
渠道-模型关联 |
unique(token_tier_id, model_id) |
request_logs |
请求日志 |
index(created_at) |
users |
用户 |
unique(username) |
notification_configs |
通知配置 |
- |
九、环境变量
| 变量 |
默认值 |
说明 |
PORT |
1234 |
服务端口 |
DB_HOST |
- |
PostgreSQL 地址 |
DB_PORT |
5432 |
PostgreSQL 端口 |
DB_USER |
- |
数据库用户名 |
DB_PASSWORD |
- |
数据库密码 |
DB_NAME |
- |
数据库名 |
JWT_SECRET |
- |
Session 签名密钥 (≥32 字节) |
ADMIN_USER |
admin |
管理员用户名 |
ADMIN_PASS |
- |
初始管理员密码 |
PROXY_TIMEOUT |
600 |
代理请求超时 (秒) |
FORWARD_TIMEOUT |
30 |
GET 转发超时 (秒) |
COOKIE_SECURE |
跟随 GIN_MODE |
Cookie Secure 标志 (HTTP 设 false) |
MAX_REQUEST_BYTES |
33554432 |
最大请求体 (32MB) |
GIN_MODE |
release |
Gin 运行模式 |
十、部署
方式一:systemd
cd /opt/Layla_API
CGO_ENABLED=0 go build -o layla-api .
cd frontend && npm install && npm run build && cd ..
systemctl restart layla-api
方式二:Docker
docker build -t cloverdo/layla-api:YYYYMMDD .
docker push 10.10.10.100:3333/cloverdo/layla-api:YYYYMMDD
docker run -d -p 1234:1234 --env-file .env cloverdo/layla-api:YYYYMMDD
十一、前端页面
| 页面 |
路径 |
功能 |
| 登录 |
/login |
用户名密码登录 |
| 运维看板 |
/ |
KPI、排行、饼图、热力图、趋势图、下钻 |
| 渠道管理 |
/channels |
CRUD、拉取/同步模型、测试连通性 |
| 模型管理 |
/models |
CRUD、渠道关联、价格配置 |
| Key 管理 |
/tokens |
CRUD、双向筛选授权、密钥轮换 |
| 使用记录 |
/logs |
查询、筛选、详情、导出 |
| 对话历史 |
/conversations |
按应用/关键词搜索 |
| 通知系统 |
/notifications |
NTFY/Email/Webhook |
| API 文档 |
/docs |
接口文档 |
| 个人设置 |
/profile |
头像、密码、显示名 |
十二、设计规范
- 主题:亮色/暗色切换
- 主色:蓝色 (
#3b82f6)
- Badge:七彩彩虹色 (赤橙黄绿青蓝紫),按名称哈希分配
- 价格符号:人民币 ¥
- 文字:全中文 UI
- 响应式:PC 侧栏固定,移动端抽屉式
十三、已知限制
- 单用户系统,仅支持一个管理员
- Session 密钥弱密钥时生成临时密钥,重启失效
- 日志无自动清理
- 无计费/配额功能
- 无 API 速率限制