Move tool bridge and responses adapter helpers out of app.main so the main entrypoint can shrink without changing route orchestration behavior. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
11 KiB
app/main.py 渐进拆分计划
- 日期:2026-04-21
- 目标文件:
app/main.py - 当前判断:适合拆分,但不适合一次性大拆;建议按阶段渐进拆分。
1. 目标
把 app/main.py 从“单文件总编排”逐步收敛为“组合根 + 路由/辅助模块”,在不破坏以下关键行为的前提下,降低文件复杂度并提高后续维护性:
- OpenAI / Anthropic / Responses 三条协议路径行为一致
- session cache 命中、回写、失效语义保持不变
- 单请求固定实例绑定不变
- streaming 路径中的 in-flight ticket 释放语义不变
- SSE 帧格式、finish reason / stop reason 行为不变
- 现有测试尽量少改,尤其避免首轮就大面积修改对
app.main的 patch 点
2. 当前结构判断
app/main.py 当前可以分成这些职责块:
-
应用启动与全局装配
app/main.py:46-154- 包括
settings、pool、stats_collector、chat_guard、session_cache、lifespan、middleware
-
鉴权包装与告警
app/main.py:157-196
-
健康检查与通用请求辅助逻辑
app/main.py:199-353
-
共享 tool / stream / bridge helper
app/main.py:356-752
-
OpenAI Chat 主编排
app/main.py:769-1192
-
Responses API 适配层
app/main.py:1197-1640
-
Anthropic Messages 适配层
app/main.py:1679-2180
-
admin / internal / metrics 路由
app/main.py:2183-2356
3. 风险判断
3.1 高风险区域(第一阶段不要碰)
以下区域不建议作为第一刀拆分目标:
app/main.py:906左右的 OpenAI streaming generatorapp/main.py:1886左右的 Anthropic streaming generatorv1_chat_completions主编排逻辑v1_messages主编排逻辑- session cache lookup / write-back / invalidate 的共享编排逻辑
3.2 原因
这些区域都同时依赖:
- route-local 状态
pool/chat_guard/session_cache/stats_collector- session continuity
- 流式 finally 中的 ticket 释放与写回时机
- OpenAI / Anthropic / Responses 之间的共享行为约束
这类代码即使功能不变,单纯移动位置也容易引发细微回归。
4. 建议的目标结构
建议最终逐步演进到以下结构:
app/
main.py # 组合根:app 创建、lifespan、router 注册、共享单例
http/
lifecycle.py # middleware / startup posture / pool guards(可后置)
chat_shared.py # 跨协议的 prompt/tool/stream helper
openai_chat.py # /v1/chat/completions
openai_responses.py # /responses 与 /v1/responses
anthropic_messages.py # /v1/messages* 与 anthropic helper
admin_routes.py # /internal/*, /metrics, /healthz, /v1/models(按需要划分)
注意:这个结构是目标结构,不是第一阶段必须一步到位完成的结构。
5. 分阶段执行计划
Phase 0:保护性准备(只做分析,不改行为)
目标:为后续拆分建立安全边界。
动作:
-
梳理并固定当前回归验证命令
python3 -m unittest tests/test_tool_call_bridge.pypython3 -m unittest discover -s tests -p "test_*.py"
-
在实际动代码前,对准备修改的关键符号做 impact analysis
- 尤其是:
v1_chat_completionsv1_messages_messages_to_prompt_responses_to_chat_request_openai_tool_call_anthropic_tool_use_block
- 尤其是:
-
先确认测试里对
app.main的 patch 点,避免首轮拆分后直接把测试打碎
完成标准:
- 有固定回归命令
- 清楚哪些符号必须在首轮保留兼容出口
Phase 1:提取纯 helper(最低风险)
目标:在不改主路由编排的前提下,先减轻 app/main.py 的噪音和长度。
建议新文件:
1) app/http/tool_bridge.py
建议迁移函数:
_json_string_openai_forced_tool_name_anthropic_forced_tool_name_json_object_from_text_tool_code_single_arg_name_tool_code_object_from_text_forced_tool_event_from_text_openai_tool_call_anthropic_tool_use_block_anthropic_tool_result_block
2) app/http/responses_adapter.py
建议迁移函数:
_responses_input_to_messages_responses_to_chat_request_responses_id_from_chat_id_responses_usage_from_chat_responses_non_stream_from_chat_payload_sse_data
3) app/http/tool_policy.py(可选)
如果首轮还想再减一点,可迁移:
_include_usage_tool_allowlist_openai_tool_name_anthropic_tool_name_filter_allowed_tools_ensure_tool_choice_allowed_openai_tool_config_anthropic_tool_config_openai_has_tooling_context_anthropic_content_has_tool_blocks_anthropic_has_tooling_context_resolve_ask_mode
首轮兼容策略:
app.main中先保留同名导入出口,例如:from .http.tool_bridge import _openai_tool_call, ...
- 这样即使测试仍然 patch
app.main._openai_tool_call,改动面也最小
完成标准:
app/main.py明显变短- 路由逻辑不变
- 现有测试全过
- 首轮不改 streaming 主体
Phase 2:提取 Responses 路由(低到中风险)
目标:把 /responses 和 /v1/responses 的适配层单独放出去。
建议新文件:
app/http/openai_responses.py
建议包含:
v1_responses_responses_stream_from_chat_stream- 以及它依赖的 responses helper(如果 Phase 1 已迁移则直接复用)
注意事项:
v1_responses当前是直接包装v1_chat_completions- 拆分时优先保持这个关系不变,不要同步重构 chat 主路径
- 如果测试直接 patch
main.v1_chat_completions,则需要确保新模块仍从app.main可拿到兼容入口,或同步最小化调整测试
完成标准:
/responses逻辑从main.py分离v1_chat_completions仍保持原行为- responses 相关测试不回归
Phase 3:提取 admin / health / metrics 路由(低风险)
目标:把非核心协议路径先搬走。
建议新文件:
app/http/admin_routes.py
可迁移内容:
healthzv1_models(可按需一起搬)/internal/auto-login/*/internal/session/export/internal/models/raw/internal/stats/metrics
注意事项:
- 这些路由依赖全局
settings/pool/ 鉴权 wrapper - 首轮可以通过“从
main注入依赖”或“保留共享单例模块”来降低改动面
完成标准:
- 运营/admin 路由从主文件剥离
- 对 chat/messages 主编排零行为影响
Phase 4:提取 Anthropic 路由与 helper(中风险)
目标:将 /v1/messages* 独立为单独模块。
建议新文件:
app/http/anthropic_messages.py
建议迁移:
_anthropic_error_anthropic_stop_reasonv1_messages_count_tokensv1_messages
前提:
- Phase 1 已把共享 tool / prompt / policy helper 先抽出
- 已明确哪些共享状态通过参数传入,哪些保持模块共享
注意:
- 暂时不重构 Anthropic stream generator 内部逻辑,只做“整体迁移”而不是“逻辑改写”
完成标准:
- Anthropic 适配层从主文件分离
- 与 OpenAI 的共享行为仍保持一致
Phase 5:最后再考虑提取 OpenAI Chat 主路由(最高风险)
目标:在前几阶段都稳定之后,再处理核心编排。
建议新文件:
app/http/openai_chat.py
建议迁移:
v1_chat_completions- 仅与其强耦合、且不适合保留在
main.py的少量辅助逻辑
关键原则:
- 不要在这一阶段同时改 session/cache/streaming 逻辑
- 只做“位置迁移 + 依赖显式化”
- 如需引入 service 层,也要在这个阶段之后再单独评估,不要和文件拆分绑定进行
完成标准:
app/main.py基本收敛为组合根- 主编排仍行为一致
- 全量测试通过
6. 每阶段的验证要求
每一阶段完成后,至少执行:
python3 -m unittest tests/test_tool_call_bridge.py
python3 -m unittest discover -s tests -p "test_*.py"
如果本地服务可启动,建议补一轮 smoke:
uvicorn app.main:app --reload --port 8317
curl -s http://127.0.0.1:8317/healthz
如果是改动了 /responses 或 /v1/messages 路径,应额外做协议 smoke,确认:
- SSE 帧格式不变
- stop reason / finish reason 不变
- tool call / tool_use bridge 不变
7. 兼容策略
为减少首轮测试与调用方震荡,建议:
- 先迁移实现,再从
app.mainre-export 同名符号- 例如:
from .http.responses_adapter import _responses_to_chat_request
- 例如:
- 首轮不要改函数名
- 首轮不要顺手重命名模块级全局变量
- 首轮不要引入新的抽象层(例如 service / manager / context object)
原则:
- 第一轮目标是“降噪和减重”,不是“顺便重构架构”
8. 不建议做的事
以下动作不建议与本次拆分绑定:
- 同时重写 streaming generator 内部结构
- 同时改 session cache 语义
- 同时改 pool / guard / stats 注入方式
- 同时大改测试结构
- 同时引入新的 service 层 / context 容器 / 抽象基类
这些都应该是后续独立变更,不要混在第一次拆分里。
9. 推荐的首个落地 PR 范围
如果要开始实际实施,建议第一批只做一个小 PR:
PR-1:Helper extraction only
内容:
- 新增
app/http/tool_bridge.py - 新增
app/http/responses_adapter.py app/main.py改为导入这些 helper- 保留
app.main的兼容出口 - 不动
v1_chat_completions/v1_messages的主逻辑
预期收益:
app/main.py先减少几百行- 风险最可控
- 为后续路由级拆分打基础
10. 后续记录方式
建议后续每完成一个 phase,就在本文件底部追加一段进展记录,例如:
## Progress Log
- 2026-04-21: 创建拆分计划
- 2026-04-22: 完成 Phase 1,抽离 responses helper 与 tool bridge helper
- 2026-04-23: 运行全量 unittest 通过
这样后续可以持续在同一份计划上回填,不需要再重新整理上下文。
Progress Log
- 2026-04-21: 创建拆分计划。
- 2026-04-21: 完成 Phase 1 helper extraction,新增
app/http/tool_bridge.py、app/http/responses_adapter.py,并在app.main保留兼容导入出口。 - 2026-04-21: 修复 Phase 1 后暴露的 tool bridge 回归;放宽 tool event allow 判断,仅在存在显式 tool 列表时做名称过滤,并保留 forced-tool 回退语义。
- 2026-04-21: 调整 OpenAI 流式 forced-tool 回退,先缓冲
tool_code文本,能解析为结构化 tool call 时只输出tool_callschunk,不能解析时再回放文本。 - 2026-04-21: 验证通过:
python3 -m py_compile app/main.py app/http/tool_bridge.py app/http/responses_adapter.py、python3 -m unittest tests/test_tool_call_bridge.py、python3 -m unittest discover -s tests -p "test_*.py"。