一个面向多上游、多协议场景的统一 LLM 接入层。
LLM_Proxy 用来把不同的模型服务、鉴权方式和传输协议收敛到同一个服务里,对下游稳定提供 OpenAI Chat Completions、OpenAI Responses 和 Claude Messages 兼容接口,并内置 Provider 管理、认证池调度、白名单控制、日志统计和 Hook 扩展能力。
它的定位不是“再做一个 API 转发器”,而是把代理、协议适配、认证管理和运维入口放到同一层里统一处理。对客户端来说,只需要记住一个地址;对服务端来说,模型路由、Key 池、访问控制和统计信息都可以集中维护。
- 统一接入多种上游协议:同一个代理服务可同时对接 OpenAI Chat Completions、OpenAI Responses、Claude Messages 和 Codex 风格接口
- 多下游兼容内建:同一个 Provider 不需要额外配置下游格式,默认即可同时服务
/v1/chat/completions、/v1/responses和/v1/messages - WebSocket 上游支持:上游可以是 HTTP,也可以是 WebSocket;下游接口保持不变
- Hook 扩展机制:用于补充 Header、请求护栏和成功响应清洗
- 多 Key 池化调度:
auth_group支持并发限制、429 冷却、分钟/日请求配额、分钟/日 Token 配额 - 控制平面模型测试:Provider 编辑页可按当前表单快照直接测试模型可用性、首字延迟和 TPS
- 后台可直接运维:内置 Provider 管理、Auth Group 管理、白名单管理、日志和统计面板
- 对下游保持稳定接口:支持
POST /v1/chat/completions、POST /v1/responses、POST /v1/messages、GET /v1/models
pip install flask gevent requests pyyaml urllib3 websocket-client创建 config.yaml,下面是一份最小可运行配置:
server:
host: 127.0.0.1
port: 8080
# chat:
# whitelist_enabled: false
# admin:
# username: admin
# password: admin123
providers:
- name: openai-chat
# enabled: false # 可选;默认 true;禁用后不会出现在 /v1/models
api: https://api.openai.com/v1/chat/completions
transport: http
source_format: openai_chat
api_key: sk-your-openai-key
verify_ssl: false
model_list:
- gpt-4.1
- gpt-4.1-mini也可以直接从 config.sample.yaml 开始裁剪。
注意:下游调用时的模型名不是裸模型名,而是 provider/model,例如 openai-chat/gpt-4.1。
如果未配置 providers[].enabled,默认就是启用状态。
python main.py或者显式指定配置文件:
python main.py --config path/to/config.yaml访问Web: http://127.0.0.1:8080
查询模型列表:
curl http://127.0.0.1:8080/v1/models发送第一条聊天请求:
curl http://127.0.0.1:8080/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "openai-chat/gpt-4.1",
"messages": [
{"role": "user", "content": "你好,帮我用一句话介绍 LLM_Proxy"}
],
"stream": false
}'后台页面始终可访问;如果配置了 admin.username 和 admin.password,访问后台时需要先登录;不配置则后台无需登录:
/login:登录页/:统计与日志面板/users:用户与白名单管理/providers:Provider / Auth Group 管理
把服务部署在 NAS、小主机或团队内网服务器上,让桌面客户端、脚本、自动化任务统一走一个地址访问模型。客户端不需要再分别维护不同厂商的 API 地址和鉴权方式。
同时接入 OpenAI、Anthropic、私有网关或自建兼容层,对外仍然提供统一接口。业务侧只关心模型名,不需要感知每个上游的差异。
当一个上游需要多个 Key 分摊压力时,可以把多个 Header 凭据放进 auth_group,由代理统一处理并发、配额、冷却和禁用状态。
当上游不是标准 OpenAI 格式时,优先通过 source_format 和当前路由对应的内置下游协议做转换。如果还需要补充 Cookie、自定义 Header、特殊字段,或者对成功响应做额外清洗,再用 Hook 在代理层补充这部分逻辑。
上游已经切到 Responses,但客户端还在走 Chat Completions;或者一部分客户端走 Claude Messages,另一部分走 OpenAI 风格接口。这类迁移期混合场景正适合由代理层统一兜住。
| 路由 | 说明 | 对应下游协议 |
|---|---|---|
POST /v1/chat/completions |
OpenAI Chat Completions 兼容接口 | openai_chat |
POST /v1/responses |
OpenAI Responses / Codex 风格接口 | openai_responses |
POST /v1/messages |
Claude Messages 兼容接口 | claude_chat |
GET /v1/models |
返回当前已注册模型列表 | 不适用 |
返回的模型 ID 形如:
openai-chat/gpt-4.1responses-upstream/gpt-4.1claude-messages/claude-sonnet-4-5
server.host:监听地址,默认127.0.0.1server.port:监听端口,默认8080chat.whitelist_enabled:是否启用聊天白名单admin.username/admin.password:配置后开启后台登录功能,用于提升后台访问安全性;不配置则后台无需登录auth_groups:凭据池配置providers:上游模型入口配置database.path:SQLite 数据库路径,默认data/requests.dblogging.path:日志目录,默认logslogging.level:日志级别,例如INFO、DEBUG
name:Provider 名称,必须唯一;下游模型名会以它作为前缀enabled:是否启用;默认true。设为false后该 Provider 不参与运行时注册,也不会出现在GET /v1/modelsapi:上游接口地址,支持http://、https://、ws://、wss://transport:上游传输方式,支持http和websocketsource_format:上游真实协议格式api_key:单 Provider 直接使用的凭据auth_group:绑定一个凭据池;和api_key二选一,不能同时使用proxy:上游代理地址timeout_seconds:上游请求超时,默认1200max_retries:失败重试次数,默认3verify_ssl:是否校验证书;代码默认值为false,公网 HTTPS 建议显式设为truemodel_list:当前 Provider 暴露的模型列表hook:Hook 文件路径;支持绝对路径。相对路径会从项目根目录下的hooks/目录加载,文件中需要导出名为Hook的类
支持的协议格式:
openai_chatopenai_responsesclaude_chat
如何理解 source_format:
source_format描述上游实际上接受什么协议- 一般按上游真实接口来选,例如
/v1/chat/completions对应openai_chat /v1/responses对应openai_responses/v1/messages对应claude_chat
name:认证组名称,必须唯一strategy:当前支持least_inflightcooldown_seconds_on_429:组级默认冷却时间entries[]:凭据条目列表,至少包含一个条目
entries[] 支持:
id:条目标识enabled:是否启用headers:发往上游时注入的 Header 集合,例如Authorizationmax_concurrency:单条目最大并发cooldown_seconds_on_429:条目级 429 冷却时间request_quota_per_minute/request_quota_per_day:请求数配额token_quota_per_minute/token_quota_per_day:Token 配额
运行时行为:
- 遇到
429时,会优先参考Retry-After,只让当前条目进入冷却 - 遇到
401时,当前条目会被标记为不可用,直到后台手动恢复 - 后台支持清除冷却、启用、禁用、恢复、重置分钟用量、重置运行时状态
providers:
- name: openai-chat
api: https://api.openai.com/v1/chat/completions
source_format: openai_chat
api_key: ${OPENAI_API_KEY}
verify_ssl: true
model_list:
- gpt-4.1
- gpt-4.1-miniproviders:
- name: responses-upstream
api: https://api.openai.com/v1/responses
source_format: openai_responses
api_key: ${OPENAI_API_KEY}
verify_ssl: true
model_list:
- gpt-4.1providers:
- name: openai-shared
api: https://api.openai.com/v1/chat/completions
source_format: openai_chat
api_key: ${OPENAI_API_KEY}
verify_ssl: true
model_list:
- gpt-4.1这个 Provider 保存后,客户端可以分别走:
POST /v1/chat/completionsPOST /v1/responsesPOST /v1/messages
auth_groups:
- name: openai-shared
strategy: least_inflight
cooldown_seconds_on_429: 60
entries:
- id: key-a
headers:
Authorization: Bearer sk-key-a
max_concurrency: 3
request_quota_per_minute: 60
- id: key-b
headers:
Authorization: Bearer sk-key-b
max_concurrency: 2
providers:
- name: openai-chat
api: https://api.openai.com/v1/chat/completions
source_format: openai_chat
auth_group: openai-shared
verify_ssl: true
model_list:
- gpt-4.1providers:
- name: custom-gateway
api: https://example.com/v1/chat/completions
source_format: openai_chat
model_list:
- my-model
hook: example_hook.py完整样例见:
- 按请求里的
model自动选择对应 Provider - 支持流式和非流式响应
- 自动识别 SSE、NDJSON、WebSocket JSON 等上游返回形态
GET /v1/models会返回当前已启用 Provider 的模型列表,以及provider_name、source_format、transport等元信息
- Provider 增删改查
- 支持行内启用 / 禁用,以及批量启用 / 禁用 / 删除
- 拉取上游模型列表并辅助填充
model_list - Provider 表单使用表格方式维护
model_list,支持底部+行新增模型 - Provider 表单支持单个或多个模型测试可用性、首字延迟与 TPS
- Auth Group 增删改查
- YAML 批量导入 Auth Entries
- 查看 Auth Group 运行时状态
- 对单个条目执行清冷却、禁用、启用、恢复、重置等运维动作
补充说明:
- “拉取模型” 属于控制平面预览请求;当 Provider 绑定
auth_group时,必须显式选择一个Auth Entry发起请求 - “测试模型” 也属于控制平面请求,要求显式选择一个
Auth Entry - 模型测试不会进入正式运行态调度,不会写入 Auth Group 的冷却、并发和配额状态
Provider 编辑页的模型测试基于当前表单快照执行,不要求先保存 Provider,适合在接入新上游时先做联通性和基础性能确认。
- 支持单个模型测试,也支持多选模型批量测试
- 返回模型是否可用、首字延迟和 TPS
- 兼容
legacy api_key和auth_group + auth_entry_id - 会复用当前 Provider 的
source_format、transport、proxy、timeout_seconds、verify_ssl和 Hook 配置 - 如果上游支持 usage 返回,测试链路会主动请求 usage,用于计算 TPS
- 批量测试时会先锁定本次选中的目标行,再按顺序逐条请求
- 每个模型一拿到结果就会立即回填到表格,不需要等待整批结束
当前行为边界:
- 模型测试只走 request side Hook:
header_hook、request_guard - 不会调用
response_guard - 如果使用
auth_group,测试固定走显式选择的Auth Entry - 即使测试成功,也不会修改正式代理链路里的 Provider 运行态或 Auth Group 运行态
- 批量测试属于当前页面会话内行为;如果刷新页面或离开当前页面,尚未开始的后续测试不会继续执行
- 登录页:
/login - 统计与日志面板:
/ - 用户管理:
/users - Provider / Auth Group 管理:
/providers - 白名单按 IP 控制
- 当
chat.whitelist_enabled=true时,只有已登记且启用白名单权限的用户 IP 才能访问代理接口
- 应用日志写入
logs/app.log - 访问日志写入
logs/access.log - 请求明细和每日聚合统计写入 SQLite
- 后台支持按日期、用户名、模型过滤统计和日志数据
Hook 文件需要导出一个名为 Hook 的类。通常继承 BaseHook,也可以直接实现同名方法:
from src.hooks import BaseHook, HookContext可选扩展点有 3 个:
header_hook(ctx, headers) -> headersrequest_guard(ctx, body) -> bodyresponse_guard(ctx, body) -> body
职责边界先说明白:
- 标准协议转换由
source_format和对应接口协议的 translator 完成 - Hook 不是协议转换层,不负责定义
openai_chat、openai_responses、claude_chat之间的标准映射 - Hook 负责的是请求进入上游前后的定制化处理,例如补 Header、做请求护栏、调整局部字段、清洗成功响应
触发顺序如下:
-
header_hook- 每次请求尝试都会调用一次,包括重试
- 调用时默认
content-type: application/json已经写入 - 如果 Provider 绑定了
auth_group或api_key,对应认证头也已经注入 - 适合补充或覆盖 Header、Cookie、Token 等信息
-
request_guard- 在协议转换之前调用
- 拿到的是下游原始请求体,而不是翻译给上游后的请求体
- 适合改写
messages、补字段、调整stream,或者做请求校验与护栏 - 返回
None时,代理会保留原始请求体
-
response_guard- 在协议转换之后调用
- 处理的是“已经翻译成下游协议的数据”,不是原始上游响应
- 非流式响应下,拿到的是完整的下游响应 payload
- 流式响应下,会对每个下游 chunk 的 payload 调用一次;终止 chunk 不经过这个 Hook
- 返回
None时,代理会保留原内容
当前行为边界:
response_guard可以改写成功响应,但不会处理上游HTTP >= 400的错误响应;这类错误会按当前代理逻辑直接返回- 对已经开始输出的流式响应,如果
response_guard抛出异常,当前流会被中断,而不是再包装成标准错误响应 - 如果
request_guard改写了body.stream,后续请求与响应流程会按新的流式设置继续执行 - Provider 编辑页里的模型测试只会复用
header_hook和request_guard,不会调用response_guard
适合用于:
- 注入额外 Header / Cookie / Token
- 在请求转发前做字段补充、删改和请求护栏
- 对成功响应做字段清洗、补全或内容改写
- 通过
HookAbortError主动中止当前请求
HookContext 会提供这些上下文信息:
retry:当前是第几次尝试,从0开始provider_namerequest_model:下游请求里的模型名,通常是provider/modelupstream_model:真正发往上游的模型名provider_source_formattransportstreamauth_group_nameauth_entry_idlast_status_codelast_error_type
其中:
- 首次尝试时,
last_status_code和last_error_type都是None - 如果上一轮重试失败是 HTTP 状态码导致的,例如
429,下一轮会看到last_status_code - 如果上一轮失败是本地传输错误,例如超时、连接错误或 WebSocket 错误,下一轮会看到
last_error_type
- 配置文件:
config.yaml - 配置样例:
config.sample.yaml - Hook 目录:
hooks/ - SQLite 默认库:
data/requests.db - 日志目录:
logs/ - 架构文档:docs/architecture-4plus1.md
- 公网 HTTPS 上游建议显式设置
verify_ssl: true - 新增 Provider 时,先用后台“拉取模型”确认接口可达,再用模型表格里的“测试”确认目标模型可用性后再保存
model_list - 多 Key 场景优先使用
auth_group,不要把轮询和限流逻辑下放到客户端 - 如果只是简单直连代理,不需要启用 Hook;只有在协议不兼容或鉴权不一致时再增加扩展逻辑
Hook 和认证扩展能力的设计目标是支持合法授权前提下的协议兼容与私有集成。请仅在拥有访问权限的前提下使用相关能力,并遵守上游服务的产品条款、访问策略和安全要求。