这个项目是一个协议翻译型 LLM Proxy。
它的目标不是“支持所有厂商的所有接口”,而是用一套干净的首版架构,稳定支持:
- 上游协议族
openai_chatopenai_responsesclaude_chat
- 下游协议面
POST /v1/chat/completionsPOST /v1/responsesPOST /v1/messagesGET /v1/models
这个版本不保留 Gemini / Antigravity。配置加载阶段会清理少量历史废弃字段并回写配置文件;除此之外不再继续扩展旧字段兼容逻辑。
系统按下面的统一链路工作:
downstream request
-> controller
-> provider lookup
-> header_hook / request_guard
-> translator.translate_request()
-> executor
-> decoder
-> translator.translate_response()
-> response_guard
-> encoder
-> downstream response
补充:hook 在 retry 场景下还可以读取上一轮失败摘要,用于做轻量级重试决策:
last_status_codelast_error_type
ProxyController- 根据当前 route family 选择下游接口协议
- 构造标准错误体
WebController- 提供统计页面与系统设置页面
- 暴露系统设置读取与保存接口
ProviderManager- 加载 provider 配置
- 维护
provider/model -> provider映射
AuthGroupManager- 加载
auth_groups - 选择
auth_entry - 持久化冷却、禁用与配额运行态
- 加载
ProxyService- 组装整条代理链路
- 根据当前请求所处接口选择 translator 和 encoder
- 在开启
logging.llm_request_debug_enabled时输出独立 trace
ProviderModelTestService- 复用 translator / executor / request-side hook
- 按当前 Provider 表单快照直连上游测试模型可用性、首字延迟与 TPS
- 在协议支持时显式请求 usage 返回
- 批量测试按前端当前选择的模型行逐条执行并逐条回填结果
SettingsService- 维护
server、admin与logging - 管理立即生效项与重启生效项的边界
- 维护
ProviderRuntimeFactory- 负责临时 / 正式 Provider 运行时对象构建
- 统一 hook 加载与缓存
ExecutorRegistry- 负责 HTTP / WebSocket 上游连接
Decoder- 将上游流拆成统一事件
TranslatorRegistry- 负责协议适配
Encoder- 将统一 chunk 编码成下游协议
Hook- 只负责 header 和 guard
Hook 组件除了 header / guard,还会收到最小重试上下文:
retryauth_group_nameauth_entry_idlast_status_codelast_error_type
| family | 用途 |
|---|---|
openai_chat |
OpenAI Chat Completions 语义 |
openai_responses |
OpenAI Responses 语义 |
claude_chat |
Anthropic Messages 语义 |
route family 直接决定当前请求的下游接口协议:
| route | downstream protocol |
|---|---|
/v1/chat/completions |
openai_chat |
/v1/responses |
openai_responses |
/v1/messages |
claude_chat |
GET /v1/models 除了模型 id,还会返回模型所属 provider 的:
source_formattransport
系统设置页与配置接口:
- 页面
GET /settings
- API
GET /api/settings/systemPUT /api/settings/system/basicPUT /api/settings/system/debugPUT /api/settings/system
当前支持的配置项:
server.hostserver.portadmin.usernameadmin.passwordlogging.pathlogging.levellogging.llm_request_debug_enabled
行为约束:
server.*/admin.*- 归类为“基础设置”
- 需要显式点击保存后提交
server.host/server.port- 保存时写回配置文件
- 如果值发生变化,需要重启服务后生效
admin.username/admin.password- 两者都非空时启用后台登录
- 任一为空时关闭后台登录
- 保存后会清空进程内 session,避免旧凭据继续生效
logging.*- 归类为“Debug”
- 页面修改后自动生效
logging.path/logging.level- 保存后会重新装配 logger
- 新请求会按新的日志路径和日志级别输出
logging.llm_request_debug_enabled- 打开后写入独立 trace 日志
- 记录四个阶段:
- 下游请求
- 转换后的上游请求
- 上游响应
- 转换后的下游响应
- 每条记录包含起始行、header 与 payload
运行时内存状态补充:
Application- 在保存日志配置后可重新装配 logger handler
Provider 公共配置字段只有:
nameapitransportsource_formatapi_keyauth_groupproxytimeout_secondsmax_retriesverify_sslmodel_listhook
其中:
source_format- 上游真实协议
transport- HTTP 或 WebSocket
没有公共 stream_format 字段。
Hook 运行时上下文还会暴露最小重试状态:
retryauth_group_nameauth_entry_idlast_status_codelast_error_type
其中 last_error_type 使用 HookErrorType 枚举,当前值为:
TIMEOUTCONNECTION_ERRORWEBSOCKET_ERRORTRANSPORT_ERROR
流式识别完全是内部实现细节:
transport = websocket- 按 WebSocket JSON 消息处理
- HTTP
Content-Type = text/event-stream- 按 SSE JSON 处理
- HTTP
Content-Type含ndjson/jsonl- 按 NDJSON 处理
- 其他
- 按非流式处理
- 如果请求声明为流式,但首块看起来像 SSE
- 触发首块探测兜底
这层能力保留在 executor / decoder 中,不暴露给用户配置。
当 logging.llm_request_debug_enabled = true 时:
- 应用会写入
logs/llm_request_trace.log - 与
app.log、access.log分离 - 采用相同的滚动策略:
RotatingFileHandlermaxBytes = 10 MiBbackupCount = 3
Provider 编辑页包含两条控制平面上游探测链路:
GET /api/providers/fetch-modelsPOST /api/providers/test-models
链路如下:
provider editor form snapshot
-> controller
-> auth header resolve (api_key or auth_group + auth_entry)
-> ProviderRuntimeFactory
-> request_guard / header_hook
-> translator.translate_request()
-> usage request enrichment when protocol supports it
-> executor
-> decoder
-> translator.translate_response(openai_chat benchmark view)
-> metric collector
-> modal result table
模型拉取链路如下:
provider editor form snapshot
-> controller
-> auth header resolve (api_key or auth_group + auth_entry)
-> model endpoint inference
-> upstream fetch (/v1/models or /models)
-> fetched model picker
-> provider form model table
行为约束:
- 这两条都是控制平面能力,不经过下游
/v1/chat/completions//v1/responses//v1/messages - Provider 编辑页的
model_list采用表格编辑,并以当前前端行状态作为唯一数据源 - 只应用 request-side hook:
header_hookrequest_guard
- 不应用
response_guard auth_group模式下:- 拉取模型必须显式选择
auth_entry - 测试模型也必须显式选择
auth_entry - 两者都不经过
AuthGroupManager.acquire() - 两者都不写运行态冷却、并发、配额
- 拉取模型必须显式选择
- 首字延迟仅在真实流式首个文本增量到达时记录
- TPS 仅在拿到 completion usage 后计算
- 如果上游成功但未返回 usage:
available = truetps = null
- 批量测试会先锁定本次选中的目标行,再按顺序逐条请求
- 每一条测试结果一返回就立即回填到对应表格行
- 批量测试属于当前页面会话内行为;页面刷新或离开后,尚未开始的后续测试不会继续执行
补充说明:
- 数据平面主代理链路未变化
- 新增的是 Provider 编辑页上的控制平面上游模型拉取与性能测试链路
src/presentation/- HTTP route、管理页面、API controller
src/services/- 代理主流程和业务服务
src/config/- 配置加载、schema、provider runtime
src/executors/- transport executor
src/proxy_core/- decoder、encoder、shared contracts
src/translators/- protocol translators
src/hooks/- hook contracts
- src/services/proxy_service.py
- 主代理 orchestration
- src/services/settings_service.py
- 系统设置保存与生效边界
- src/config/provider_config.py
- Provider schema
- src/executors/registry.py
- HTTP / WebSocket executors
- src/proxy_core/decoders.py
- 流式解码
- src/proxy_core/encoder.py
- 下游编码
- src/translators/registry.py
- 4x4 translator registry
- src/presentation/templates/providers.html
- Provider 页面与
source_format/ Auth Group 编辑
- Provider 页面与
- src/presentation/templates/settings.html
- 系统设置页面与帮助说明
部署上是单体服务:
- 一个 Flask 应用
- 一个配置文件
- 一组滚动日志文件
- 多个 provider 指向多个真实上游
- 下游统一接入这个代理
Client / Agent / IDE
|
v
LLM Proxy
|
+--> OpenAI Chat upstream
+--> OpenAI Responses upstream
+--> Claude Messages upstream
+--> Codex upstream
sequenceDiagram
participant Client
participant Controller
participant Service
participant Translator
participant Executor
Client->>Controller: POST /v1/chat/completions
Controller->>Service: proxy_request()
Service->>Translator: openai_responses -> openai_chat
Translator-->>Service: translated upstream request
Service->>Executor: execute HTTP request
Executor-->>Service: stream events
Service->>Translator: translate stream events
Translator-->>Service: openai_chat chunks
Service-->>Controller: SSE response
Controller-->>Client: chat.completion.chunk stream
sequenceDiagram
participant Client
participant Controller
participant Service
participant Translator
participant Executor
Client->>Controller: POST /v1/messages
Controller->>Service: proxy_request()
Service->>Translator: openai_chat -> claude_chat
Translator-->>Service: upstream chat request
Service->>Executor: execute HTTP request
Executor-->>Service: chat SSE stream
Service->>Translator: translate to claude events
Translator-->>Service: message_start/content_block_delta/message_stop
Service-->>Client: Claude-style SSE
因为项目当前的目标客户端只需要:
- OpenCode
- Codex
- Claude Code
- Cherry Studio
Gemini / Antigravity 这类协议面会显著增加配置复杂度,但对当前目标收益很低,因此本版直接移除。
因为流格式判断应该是代理内部责任,而不是用户负担。
用户只需要清楚:
- 上游是什么协议
- 下游要暴露成什么协议集合
- 上游通过 HTTP 还是 WebSocket 连接
上游到底是 SSE、NDJSON 还是非流式,由 executor / decoder 自动判断。