[milly] feat(xiaof): OpenAI-compatible adapter for general_qa LLM path#112
Merged
Conversation
Scott 拍板(2026-06-29): 主线 = 直连 LLM SDK + 轻 loop,OpenAI 兼容协议
为主路径,DeepSeek/Qwen/本地 vLLM 一份代码切;Anthropic Claude 作 swap target。
A-10「换 backend 改 1 个配置」落地为 base_url + api_key + model 三个环境变量。
What lands:
- forge_xiaof_openai.py: openai_compatible_adapter
* env-gated (XIAOF_OPENAI_BASE_URL/API_KEY/MODEL/TIMEOUT)
* SSE streaming chat.completions, no extra deps (urllib only)
* jargon scrubber on LLM output covering M1/M2/stub/adapter/milestone/占位
with cross-chunk look-ahead so the forbidden word can't reassemble
between two emitted token events (regression on milk's 6/14 硬线 #1).
* upstream / network failure → XiaofRequestError code='upstream_failed';
NEVER 'forbidden' (A-7.5 red line).
* chips always empty — LLM path cannot bypass M2's A-7 retrieval ACL
fixture (milk's 6/14 硬线 #2).
- forge_xiaof.py: new default_adapter routing
* intent=general_qa + local A-4 builtin matches → stub (no LLM call,
keeps PRD A-4 sub-5s time answer).
* intent=general_qa + env present + builtin miss → openai adapter.
* intent=thread_search → stub unconditionally (M2 owns retrieval).
* No env → falls back to stub neutral copy; MVP not broken.
Tests: 15 new cases (jargon scrub incl. cross-chunk, env gating,
all three routing branches, upstream failure mapping, contract event
order). Full pytest matrix + ruff clean.
Gateway path (milk's ① 默认): adapter only sees one base_url, so
pointing XIAOF_OPENAI_BASE_URL at milk's gateway is the entire
integration — no adapter changes.
🤖 bot-review (comment-only · phase 1)Diff: Red-line checks:
Needs human review — these paths are not eligible for future auto-approve:
Phase 2: auto-approve + auto-merge fire only when red-lines are clean, author is internal, and no needs-human path is touched. Block with |
Owner
Author
|
A-7 / contract review from Milk:
无 blocker,等 pytest matrix 全绿即可合。M2 那条 FTS + server-side viewer ACL 的 12-case fixture 仍是硬 gate,不动。 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Scott 拍板(2026-06-29,thread
th_19e697c79c1_ccc73b)落地:主线 = 直连 LLM SDK + 轻 loop,OpenAI 兼容协议为主路径,DeepSeek / Qwen / 本地 vLLM / milk gateway 一份代码切,Anthropic Claude 作 swap target。A-10「换 backend 改 1 个配置」首次真验证 =XIAOF_OPENAI_BASE_URL+XIAOF_OPENAI_API_KEY+XIAOF_OPENAI_MODEL三个值。What lands
forge_xiaof_openai.py—openai_compatible_adapter:env-gated SSE streaming chat.completions(zero new deps,纯 urllib)。forge_xiaof.py— 新default_adapter路由:general_qa+ 本地 A-4 命中(时间/日期)→ stub,不调 LLM,保 sub-5s 答时间。general_qa+ env 配齐 + 本地未命中 → 真 LLM。thread_search→ stub 无条件兜底(M2 owns 真检索,A-7 12-case fixture 是硬 gate)。tests/test_xiaof_openai.py— 15 case 覆盖路由 / env gate / 禁词 / upstream 失败映射。milk 两条硬线落地证据
① LLM 输出过禁词 regression(6/14 + 6/29 重申)——
scrub_jargon把M1 / M2 / stub / adapter / milestone / 占位在流式输出里直接掩成·。关键是 cross-chunk look-ahead:测试test_llm_output_jargon_scrubbed_across_chunk_boundary验证「mile+stone」两个 chunk 也会被合并捕获,不会在用户 DOM 里重新拼成milestone。② LLM 路径不能新增检索面——
thread_search在default_adapter第一步就走 stub 分支,LLM adapter 这条代码路径根本不会被进入;测试test_default_adapter_routes_thread_search_to_stub用 monkeypatched 「LLM-must-not-be-called」断言这一点。chips 永远[],A-7 12-case fixture 不受影响。契约红线
meta → token* → chips → done严格遵守,由forge_xiaof.stream_to_sse_frames现有校验托底。upstream_failed,绝不冒forbidden(A-7.5);测试test_upstream_failure_maps_to_upstream_failed钉死。Gateway 接入(milk ①默认)
Adapter 只看见一个
base_url,把XIAOF_OPENAI_BASE_URL指向 milk 的 gateway 就完成接入,零代码改动。Provider key / 限流 / 审计 / token 计费全在 gateway 那一侧统一。实测开放问题(mock LLM 跑过)
Scott 截图里的「现在 Asia/Shanghai 几点」:依然走本地 zoneinfo(不调 LLM)。
开放问题如「讲个冷笑话」:env 没配时回中性 fallback「暂时还回答不了…跨 thread 检索功能也正在接通中…」;env 配齐后会进 LLM 路径,token 流增量推送给前端。
验证
cc @MiLk(gateway 接入 + 禁词 regression review) @judy @scott