From 56c57a490145bb23f191e1e6b2de5dea002f671c Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Mon, 20 Apr 2026 08:31:45 +0800 Subject: [PATCH] docs: sync DESIGN with current tooling behavior Co-Authored-By: Claude Opus 4.7 --- DESIGN.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/DESIGN.md b/DESIGN.md index 9f0198b..b87fe36 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -47,9 +47,9 @@ - **逆向 Lingma 后端协议**:之前评估过(曾经的"B1 终极方案"),需要反编译二进制,维护成本高、政策风险大,放弃。 - **多租户 / 水平扩缩**:单容器即可;真要大规模部署 → 套层反代 + N 个网关副本就够,不在进程内解决。 -- **请求侧完整 function calling / tools 透传**:OpenAI schema 里保留了字段,但目前不会把 `tools`/`tool_choice` 透传给 Lingma(上游无等价输入协议)。 +- **请求侧完整 function calling / tools 语义**:仍不是当前目标;现阶段仅支持 `tools`/`tool_choice` 在 `TOOL_FORWARD_ENABLED` 开关下灰度透传(默认关闭)。 - **响应侧工具事件桥接**:若 Lingma 上游产出 tool 事件,网关会向 OpenAI 输出 `tool_calls`,向 Anthropic 输出 `tool_use` / `tool_result`(stream + non-stream)。 -- **多模态**:请求里的 image/audio 会被降级成占位符 `[image]` / `[audio]`,因为 Lingma chat 不支持。 +- **强制工具回退闭环(non-stream)**:当上游未返回 tool 事件且请求为强制 `tool_choice` 时,网关会从文本里解析严格 JSON,合成 OpenAI `tool_calls` 与 Anthropic `tool_use` / `tool_result`。 --- @@ -592,7 +592,7 @@ FastAPI `lifespan` 退出 → `pool.close()` → 每个 `client.close()` → 进 | 需求 | 改哪些文件 | 关键入口 | |---|---|---| | 加一个新的 OpenAI 端点(如 embeddings) | `main.py`, `openai_schema.py` | 仿照 `v1_models` 加 `@app.post("/v1/embeddings", dependencies=[Depends(auth_guard)])` | -| 扩展 Anthropic 端点(如 count_tokens / tool_use 相关能力) | `main.py::v1_messages`, `anthropic_schema.py` | count_tokens 只读:复用 `estimate_tokens`;响应侧 `tool_use/tool_result` 桥接已支持,若要请求侧 tools 透传仍需改 `lingma_client.py` payload | +| 扩展 Anthropic 端点(如 count_tokens / tool_use 相关能力) | `main.py::v1_messages`, `anthropic_schema.py` | count_tokens 只读:复用 `estimate_tokens`;响应侧 `tool_use/tool_result` 桥接已支持;请求侧 `tools/tool_choice` 透传由 `TOOL_FORWARD_ENABLED` 控制并经 `lingma_client.py` payload 下发 | | 加一种新的实例调度策略(如加权轮询) | `lingma_pool.py::pick()` | 当前是 affinity → least-in-flight → round-robin | | 改认证为 JWT / OAuth | `auth.py` | 三个 `require_*` 函数是全部入口;`main.py` 里只有 `*_guard` 代理 | | 增加限流(按 api_key 配额) | `concurrency.py` 加 `PerKeyGuard`;`main.py` 在 `chat_guard.try_acquire()` 后再来一层 | 注意 ticket 释放顺序(内层先释放) | @@ -600,7 +600,7 @@ FastAPI `lifespan` 退出 → `pool.close()` → 每个 `client.close()` → 进 | 改 Prometheus 指标名 | 所有 `prometheus_lines()` 或 `prometheus_text()` | 注意生态兼容;更名要在 README 留 alias | | 接入 Jaeger / OpenTelemetry | `logging_config.py` 加 OTel instrumentation;`main.py::request_id_middleware` 注入 traceid | request_id 可以复用为 span_id | | 加一个 Lingma 新方法调用(比如 code/complete) | `lingma_client.py` 仿照 `query_models`:`await self.ensure_ready(); return await self.rpc.request("code/complete", ...)` | 原始上游响应形态需抓包确认 | -| 支持 function calling(假设 Lingma 将来支持) | `openai_schema.py` 已保留 `tools` / `tool_choice` 字段;`lingma_client.py::_build_payload` 加 `extra.tools` | 上游协议 TBD | +| 支持 function calling(假设 Lingma 将来支持) | `openai_schema.py` / `anthropic_schema.py` / `main.py` / `lingma_client.py` | 当前仅支持请求侧 `tools/tool_choice` 在开关控制下透传与响应侧桥接;若要完整 function calling 语义仍需按上游协议补齐 | | 多模态穿透 | `openai_schema.py::flatten_content` 不再降级;`lingma_client.py` payload 传 url | 前提:Lingma 支持(目前不支持) | | 换 session_cache 后端(如 Redis) | 实现同样接口的 `RedisSessionCache`,`main.py` 初始化换实现 | 接口是 `get / put / invalidate / stats / prometheus_lines / build_key / enabled`,内存换远端成本不高 | | 多容器副本(水平扩) | 外面套反代 + sticky session(根据 `Authorization` 或 `x-user` 做 hash);session cache 改 Redis | 或直接接受多副本 cache 独立,轻微浪费 KV cache 命中率 | @@ -612,7 +612,8 @@ pip install -r requirements.txt # 在容器外跑,需要自己准备 Lingma 二进制 export LINGMA_BIN=/path/to/Lingma export API_KEYS=sk-dev -uvicorn app.main:app --reload --port 8317 +export PORT=8317 +uvicorn app.main:app --reload --port ${PORT} ``` 主要断点位置: