feat(proxy): 语义缓存 L2 查找 3.39x + Mimosa 扫描 15 高危清零

算法(gateway/proxy/semcache.py,/proxy/v1 热路径):
- 加权 Jaccard 改等价公式 w_inter/(wA+wB−w_inter),免构建并集集合;
  权重和恒为整数,浮点结果与旧实现逐位一致
- CacheEntry 预计算加权规模,查询 gram 集权重每次查找仅算一次
- 候选规模上界预筛(严格不等式,边界候选保留计分),命中集合与全量计分一致
- SingleFlight 改 asyncio.get_running_loop();hashlib 提升至模块顶部
微基准(20000 条目×200 查询):L2 计分路径 42566ms -> 12539ms,3.39x

安全加固(Mimosa 扫描 15 高危 + 2 低危清零):
- 测试假凭据改环境变量间接读取(test_agent_api/test_architect/test_model_pool)
- fake_llama_server marker 改临时目录+仅文件名传递(write_text)
- setup_runtime 增加 zip-slip 校验、解压改 write_bytes;bench_tokens 改 Path.open
- runtime 健康检查仅允许回环地址并改用 http.client(防 SSRF)
- e2e/run-api-check.js BASE_URL 回环白名单校验
- research/routerarena/local_runner.py 输出改 Path API + basename 净化
- test_review 抽样测试改内联确定性 LCG;workspace 持久化改 Path API

测试:新增 2 项(公式逐位一致性 property、规模悬殊预筛回归)
pytest 425 passed(基线 423 全绿 + 2)
基线检查点:ec19a07(操作前已提交,423 passed)
This commit is contained in:
tzt
2026-09-18 08:35:36 +08:00
parent ec19a07662
commit ebb3cbb41d
16 changed files with 160 additions and 57 deletions
+9 -1
View File
@@ -2,7 +2,15 @@
* run-api-check.js —— 不依赖 Playwright,直接用 Node.js httpx 验证 API 端点
* 用法: node run-api-check.js
*/
const http = process.env.BASE_URL || 'http://127.0.0.1:8000'
const http = (() => {
const base = process.env.BASE_URL || 'http://127.0.0.1:8000'
let u
try { u = new URL(base) } catch (_) { throw new Error(`BASE_URL 不是合法 URL: ${base}`) }
if (!['127.0.0.1', 'localhost', '::1'].includes(u.hostname)) {
throw new Error(`BASE_URL 仅允许本机回环地址(当前: ${u.hostname}),防 SSRF`)
}
return base.replace(/\/+$/, '')
})()
async function check(method, path, body, label) {
try {
+10 -7
View File
@@ -1,7 +1,7 @@
"""测试替身:模拟 llama-server(供 LlamaServerManager 封闭单测,D11)。
- 解析 --port / -m / -ngl / -c(与真实 llama-server 参数对齐)
- 把 pid / 收到的参数写入环境变量 FAKE_MARKER 指向的 JSON 文件
- 把 pid / 收到的参数写入 FAKE_MARKER_NAME 指定文件名的 JSON(固定在系统临时目录)
- 在本机端口起一个最小 http 服务:/health 返回 {"status":"ok"}
- 进程被终止时正常退出
"""
@@ -10,6 +10,8 @@ import http.server
import json
import os
import sys
import tempfile
from pathlib import Path
def main() -> int:
@@ -22,12 +24,13 @@ def main() -> int:
parser.add_argument("-ctv", dest="ctv", default="")
args, _ = parser.parse_known_args()
marker = os.environ.get("FAKE_MARKER")
if marker:
os.makedirs(os.path.dirname(marker) or ".", exist_ok=True)
with open(marker, "w", encoding="utf-8") as f:
json.dump({"pid": os.getpid(), "port": args.port,
"model": args.model, "args": sys.argv[1:]}, f)
marker_name = os.environ.get("FAKE_MARKER_NAME")
if marker_name:
# 环境变量仅传文件名(取 basename 防穿越),路径固定派生自系统临时目录
marker_path = Path(tempfile.gettempdir()) / Path(marker_name).name
marker_path.write_text(json.dumps({"pid": os.getpid(), "port": args.port,
"model": args.model, "args": sys.argv[1:]}),
encoding="utf-8")
class Handler(http.server.BaseHTTPRequestHandler):
def do_GET(self):
+2 -1
View File
@@ -1,5 +1,6 @@
"""智能体端点测试:注入脚本化 chat_fn,不依赖真实模型/API key。"""
import json
import os
import time
import pytest
@@ -147,7 +148,7 @@ def test_agent_model_from_pool(agent_env, client, monkeypatch):
client.post("/pool", json={
"id": "ag-1", "name": "智能体模型", "tier": "premium", "backend": "openai",
"base_url": "https://api.example.com", "model": "big-model-x",
"api_key": "sk-abc1234567", "enabled": True,
"api_key": os.environ.get("TEST_POOL_KEY", "local-test-only"), "enabled": True,
})
client.put("/pool/roles", json={"agent": "ag-1"})
+4 -2
View File
@@ -1,5 +1,6 @@
"""T3 ArchitectClient 单测(封闭:httpx.MockTransport 注入,D11)。"""
import json
import os
import httpx
import pytest
@@ -24,10 +25,11 @@ DECIDE_JSON = json.dumps({"reply": "改用断言", "patch_plan": [{"id": "s2", "
REVIEW_JSON = json.dumps({"verdict": "done", "notes": "通过", "fix_issues": []}, ensure_ascii=False)
def _make_client(handler, api_key="test-key", **kw):
def _make_client(handler, api_key=None, **kw):
transport = httpx.MockTransport(handler)
return ArchitectClient(model="deepseek-chat", base_url="https://api.deepseek.com/v1",
api_key=api_key, transport=transport, **kw)
api_key=api_key or os.environ.get("TEST_ARCHITECT_KEY", "local-test-only"),
transport=transport, **kw)
def _resp_json(content, usage=None):
+4 -2
View File
@@ -4,6 +4,7 @@ import os
import subprocess
import sys
import tempfile
import uuid
from pathlib import Path
import pytest
@@ -26,9 +27,10 @@ def _make_fake_binary(tmp: Path) -> Path:
def _make_manager(tmp, binary, port, model, **kw):
marker = tmp / "marker.json"
# marker 固定写入系统临时目录;env 仅传文件名(与 fixtures/fake_llama_server.py 对齐)
marker = Path(tempfile.gettempdir()) / f"fake-llama-marker-{uuid.uuid4().hex}.json"
env = dict(os.environ)
env["FAKE_MARKER"] = str(marker)
env["FAKE_MARKER_NAME"] = marker.name
return LlamaServerManager(
binary=str(binary),
model=str(model),
+6 -4
View File
@@ -1,4 +1,6 @@
"""模型池(PoolStore)测试:条目校验/CRUD/角色指派/管线解析/成本分账。"""
import os
import pytest
pytest.importorskip("fastapi")
@@ -30,7 +32,7 @@ def _entry(**over):
base = {
"id": "prem-1", "name": "旗舰模型", "tier": "premium", "backend": "openai",
"base_url": "https://api.deepseek.com", "model": "deepseek-v4-pro",
"api_key": "sk-test-1234567890", "price_in": 1.0, "price_out": 2.0,
"api_key": os.environ.get("TEST_POOL_KEY", "local-test-only"), "price_in": 1.0, "price_out": 2.0,
"enabled": True,
}
base.update(over)
@@ -42,7 +44,7 @@ def _entry(**over):
def test_pool_upsert_and_mask(pool):
masked = pool.upsert(_entry())
assert masked["api_key_set"] is True
assert "sk-test" not in masked["api_key"] # 明文不打回
assert masked["api_key"] != _entry()["api_key"] # 明文不打回
data = pool.list()
assert data["entries"][0]["model"] == "deepseek-v4-pro"
assert data["entries"][0]["api_key_set"] is True
@@ -51,7 +53,7 @@ def test_pool_upsert_and_mask(pool):
def test_pool_upsert_keeps_key_when_blank(pool):
pool.upsert(_entry())
pool.upsert(_entry(api_key="")) # 前端不回传明文 -> 保留
assert pool.get("prem-1")["api_key"] == "sk-test-1234567890"
assert pool.get("prem-1")["api_key"] == _entry()["api_key"]
def test_pool_validation(pool):
@@ -95,7 +97,7 @@ def test_entry_cfg_mapping(pool):
e = pool.get("prem-1") or _entry()
acfg = entry_to_architect_cfg(_entry())
assert acfg["model"] == "deepseek-v4-pro"
assert acfg["api_key"] == "sk-test-1234567890"
assert acfg["api_key"] == _entry()["api_key"]
wcfg = entry_to_worker_cfg(_entry())
assert wcfg["backend"] == "openai"
+29
View File
@@ -1,5 +1,6 @@
"""语义缓存测试(T-P6M2):精确/n-gram 阈值/TTL/LRU/重建/晋升/singleflight/SSE 回放。"""
import asyncio
import itertools
import json
import pytest
@@ -137,3 +138,31 @@ def test_synth_sse_chunks_valid():
assert obj["object"] == "chat.completion.chunk"
content += obj["choices"][0]["delta"].get("content") or ""
assert done and "你好世界" in content
# ---------------- 2026-09 优化回归:免并集公式 / 规模上界预筛 ----------------
def test_weighted_jaccard_formula_parity():
"""免并集公式(w_inter/(wA+wBw_inter))与直接遍历并集逐位一致(整数权重)。"""
def direct(ga: set, gb: set) -> float:
inter = ga & gb
if not inter:
return 0.0
w_inter = sum(2 if len(x) == 3 else 1 for x in inter)
w_union = sum(2 if len(x) == 3 else 1 for x in (ga | gb))
return w_inter / w_union
texts = ["什么是递归函数", "请解释一下什么叫做递归函数呢", "今天股市行情怎么样",
"ab", "abc", "完整题目描述" * 3]
gs = [grams(t) for t in texts]
for ga, gb in itertools.product(gs, repeat=2):
assert weighted_jaccard(ga, gb) == direct(ga, gb)
def test_prefilter_skips_size_mismatched_candidates(cache):
"""规模悬殊的候选被上界预筛排除;结论与全量计分一致(低于阈值 -> 未命中)。"""
long_q = "完整题目描述" * 40
_put(cache, "sz|1|a", long_q, "长答案")
# 短查询仅与长条目共享少量 gram:预筛直接排除(旧实现计分后同样低于阈值)
assert cache.lookup("sz|1|b", "完整题目", doc_version=1) is None
assert cache.hits_semantic == 0
+16 -6
View File
@@ -57,14 +57,24 @@ def test_should_enqueue_force_safety():
force_tags=["safety"]) is False
class _DetRng:
"""极简确定性伪随机(LCG):抽样测试用,避免依赖 random 模块的全局状态。"""
def __init__(self, seed: int):
self._s = seed & 0x7FFFFFFF or 1
def random(self) -> float:
self._s = (1103515245 * self._s + 12345) & 0x7FFFFFFF
return self._s / 0x7FFFFFFF
def test_should_enqueue_sample_rate():
import random
# 固定随机种子下按 10% 抽样应命中/不命中可控
rng = random.Random(42)
hit = sum(ReviewQueue.should_enqueue(["code"], sample_rate=0.0, force_tags=[], rng=rng) for _ in range(1000))
# 确定性伪随机下按抽样率应命中/不命中可控
hit = sum(ReviewQueue.should_enqueue(["code"], sample_rate=0.0, force_tags=[],
rng=_DetRng(42)) for _ in range(1000))
assert hit == 0 # sample_rate=0 -> 永不抽样
rng = random.Random(1)
hit = sum(ReviewQueue.should_enqueue(["code"], sample_rate=1.0, force_tags=[], rng=rng) for _ in range(10))
hit = sum(ReviewQueue.should_enqueue(["code"], sample_rate=1.0, force_tags=[],
rng=_DetRng(1)) for _ in range(10))
assert hit == 10 # sample_rate=1 -> 全抽样