48 lines
1.7 KiB
Markdown
48 lines
1.7 KiB
Markdown
# 整体架构
|
||
|
||
## 核心流程
|
||
|
||
1. 客户端 POST /v1/chat/completions 到本地端口
|
||
2. 中间件做三件事:
|
||
- 校验本地 API Key(Authorization: Bearer <key>)
|
||
- 读 body 里的 model,按前缀路由
|
||
- 如果带前缀,把 model 改成去掉前缀后的上游模型名
|
||
3. 根据 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 → 聊天代理
|