- 新增 router_system/llm_client.py:OpenAICompatClient 统一 experts/judge/fallback
三处复制的懒建 AsyncClient + /chat/completions + choices/usage 解析(~60 行去重);
密钥解析统一走 config.get_api_key(激活原死代码,顺带消除 experts 默认环境名不一致)
- 语义缓存 L2:条目容器 list→OrderedDict(提升/淘汰 O(n)→O(1)),按 query 天然去重;
n-gram 向量 lru_cache 复用(同一次 miss 的 get/put 免重复分词);
A/B:淘汰路径 0.040→0.034s,miss→put 往返 9.41→8.57s(-9%)
- RuleJudge 覆盖度:response.lower() 提出逐词循环(原 O(terms×len) 重复复制)
- extract_content_terms 纯函数 lru_cache 化(专家与 Judge 对同一查询免重复分词),返回 tuple
- RuleClassifier:_score 去掉败者领域白建的命中词 list(胜出后单独收集);
修复 code 规则 ("api",0.7) 重复登记(原命中计 1.4 分)
- difficulty:正则模块级预编译
- tests:恢复上一轮引入的乱码中文 docstring;网关测试输入串恢复为可判 code 的中文查询
79 lines
2.7 KiB
Python
79 lines
2.7 KiB
Python
from router_system.cache import RouterCache
|
||
|
||
|
||
def test_semantic_lookup_after_many_entries():
|
||
"""多条目下语义命中正确(范数预计算 + 单遍扫描的回归)。"""
|
||
c = RouterCache(similarity_threshold=0.5)
|
||
for i in range(50):
|
||
c.put(f"完全不相关的查询主题编号{i}关于烹饪的意见", {"response": f"r{i}"})
|
||
c.put("用 Python 实现快速排序函数", {"response": "code-answer"})
|
||
level, got = c.get("用 Python 实现快速排序的函数写法") # 相似但不完全相同
|
||
assert level in ("semantic", "exact")
|
||
assert got["response"] == "code-answer"
|
||
|
||
|
||
def test_promotion_clears_semantic_state():
|
||
"""提升为精确缓存后,语义列表与范数索引无残留。"""
|
||
c = RouterCache(promote_frequency=2)
|
||
c.put("查询甲", {"response": "a"})
|
||
first = c.get("查询甲") # 相似度=1.0 计 exact,hits 达阈值即提升
|
||
assert first is not None and first[0] == "exact"
|
||
second = c.get("查询甲")
|
||
assert second is not None and second[0] == "exact"
|
||
assert c.stats()["exact_size"] == 1
|
||
assert c.stats()["semantic_size"] == 0
|
||
assert len(c._sem_norms) == 0
|
||
|
||
|
||
def test_semantic_eviction_clears_norms():
|
||
"""语义缓存满员淘汰最旧条目时,向量与范数索引同步清理。"""
|
||
c = RouterCache(max_semantic=2)
|
||
c.put("查询一", {"response": "1"})
|
||
c.put("查询二", {"response": "2"})
|
||
c.put("查询三", {"response": "3"}) # 淘汰查询一
|
||
assert len(c._semantic) == 2
|
||
assert len(c._sem_vecs) == 2
|
||
assert len(c._sem_norms) == 2
|
||
assert c.get("查询一") is None
|
||
|
||
|
||
def test_exact_hit():
|
||
c = RouterCache()
|
||
result = {"response": "hello", "domain": "general"}
|
||
assert c.get("query") is None
|
||
c.put("query", result)
|
||
level, got = c.get("query")
|
||
assert level == "exact"
|
||
assert got["response"] == "hello"
|
||
|
||
|
||
def test_semantic_hit():
|
||
c = RouterCache(semantic_enabled=True, similarity_threshold=0.5)
|
||
c.put("用 python 实现快速排序", {"response": "code", "domain": "code"})
|
||
# 相似但不完全相同的查询应命中 L2 语义缓存
|
||
hit = c.get("用 python 实现快速排序的算法")
|
||
assert hit is not None
|
||
assert hit[0] == "semantic"
|
||
|
||
|
||
def test_promote_to_exact():
|
||
c = RouterCache(promote_frequency=3)
|
||
result = {"response": "x", "domain": "general"}
|
||
c.put("query", result)
|
||
# 语义命中累计 3 次后提升为精确缓存
|
||
for _ in range(3):
|
||
hit = c.get("query")
|
||
assert hit is not None
|
||
assert c.stats()["exact_size"] == 1
|
||
|
||
|
||
def test_stats():
|
||
c = RouterCache()
|
||
c.put("q", {"response": "r"})
|
||
c.get("q")
|
||
c.get("q")
|
||
c.get("miss")
|
||
s = c.stats()
|
||
assert s["exact_hits"] == 2
|
||
assert s["misses"] == 1
|