1.7 KiB
1.7 KiB
整体架构
核心流程
- 客户端 POST /v1/chat/completions 到本地端口
- 中间件做三件事:
- 校验本地 API Key(Authorization: Bearer )
- 读 body 里的 model,按前缀路由
- 如果带前缀,把 model 改成去掉前缀后的上游模型名
- 根据 stream 字段选择转发方式:
- stream=true → 打开上游 SSE,字节流实时转发
- 非流式 → 缓冲上游完整响应后返回
模型前缀路由
| 前缀 | ProviderKind | Region |
|---|---|---|
| wbcn- / workbuddy-cn / codebuddy- | WORKBUDDY_CN | CN |
| wbintl- / workbuddy-intl / workbuddy- | WORKBUDDY_INTL | INTL |
| traecn- / trae-cn / trae- | TRAE_CN | CN |
| traeintl- / trae-intl | TRAE_INTL | INTL |
| 其它 | OPENAI_COMPATIBLE(Sub2API 透传) | - |
请求生命周期
POST /v1/chat/completions
-> authorized() // 401 if invalid key
-> extractModel(rawBody) // "traecn-glm-5.2"
-> router.route(model) // (TRAE_CN, CN)
-> stripModelPrefix(model) // "glm-5.2"
-> rewriteModelBody() // 替换 body.model
-> pooled = pool.next(kind, region, sessionKey)
-> traeChatProxy.openStreamingChat(body, pooled?.accountId, region)
-> respondOutputStream(...) // 逐块写上游字节
账号池
- 从 CredentialRepository.accountsFor(service) 读取实时账号
- 每个 ProviderKind 一个轮询游标(round-robin)
- 会话粘性:X-Conversation-Id 或 body user 作为 key,同会话复用同一账号
- 地区匹配:优先选择 credential.region == route.region,没有则回退全部
本地服务
- GET /health → 存活检查
- GET /v1/models → 透传 Sub2API 模型列表
- POST /v1/chat/completions → 聊天代理