feat(proxy): T-P6 语义缓存(L1/L2 倒排+singleflight+SSE 回放,M2 核心)
- semcache.py:SemanticCache——L1 精确(LRU max_entries=30万)+ L2 字符 2/3-gram 倒排索引(启动自 sqlite q_norm 重建)+ 加权 Jaccard(3-gram 权 2)+ 共享 gram>=3 候选门限 + 阈值 0.92 + TTL 滑动过期 + L2 命中 5 次晋升 L1 (别名键写回表);SingleFlight(dict[hash->Future] 上限 256/60s 超时降级); synth_sse_chunks 命中回放(分块 delta+finish+[DONE] 合法 SSE) - ledger:semcache_rows/put_semcache/promote_semcache/purge_expired - 测试 +10:gram/精确/语义上下阈值/TTL 假时钟/LRU/重建/晋升/singleflight/SSE 合法性, 全量 422 passed - 待接线:routes 缓存分支(T-P7 顺带接入,M2 完整闭环在压测前完成)
This commit is contained in:
@@ -187,6 +187,51 @@ class Ledger(BillingMixin):
|
||||
(used + 1, today, key_id))
|
||||
return True
|
||||
|
||||
# ---------- 语义缓存持久化(T-P6,供 semcache.SemanticCache 调用) ----------
|
||||
def semcache_rows(self, limit: int = 300000) -> List[Dict[str, Any]]:
|
||||
"""全量缓存行(启动重建倒排索引)。"""
|
||||
with self._lock, self._connect() as conn:
|
||||
rows = conn.execute(
|
||||
"SELECT cache_key, q_norm, answer, model, created_ts, ttl_ts,"
|
||||
" doc_version, hits FROM semcache LIMIT ?", (limit,)).fetchall()
|
||||
return [dict(r) for r in rows]
|
||||
|
||||
def put_semcache(self, cache_key: str, bucket: str, q_norm: str, answer: str,
|
||||
model: str, created_ts: int, ttl_ts: int,
|
||||
doc_version: int = 1) -> None:
|
||||
"""写/覆盖一条缓存(幂等)。"""
|
||||
with self._lock, self._connect() as conn:
|
||||
conn.execute(
|
||||
"INSERT OR REPLACE INTO semcache"
|
||||
"(cache_key, bucket, q_norm, answer, model, created_ts, ttl_ts,"
|
||||
" doc_version, hits) VALUES (?,?,?,?,?,?,?,?,0)",
|
||||
(cache_key, bucket, q_norm, answer, model, created_ts, ttl_ts,
|
||||
doc_version))
|
||||
|
||||
def promote_semcache(self, source_key: str, alias_key: str, hits: int) -> None:
|
||||
"""L2 -> L1 晋升:以别名键复制一行(原行保留审计)。"""
|
||||
with self._lock, self._connect() as conn:
|
||||
row = conn.execute(
|
||||
"SELECT * FROM semcache WHERE cache_key = ?", (source_key,)).fetchone()
|
||||
if row is None:
|
||||
return
|
||||
conn.execute(
|
||||
"INSERT OR REPLACE INTO semcache"
|
||||
"(cache_key, bucket, q_norm, answer, model, created_ts, ttl_ts,"
|
||||
" doc_version, hits) VALUES (?,?,?,?,?,?,?,?,?)",
|
||||
(alias_key, row["bucket"], row["q_norm"], row["answer"],
|
||||
row["model"], row["created_ts"], row["ttl_ts"],
|
||||
row["doc_version"], hits))
|
||||
conn.execute("UPDATE semcache SET hits = ? WHERE cache_key = ?",
|
||||
(hits, source_key))
|
||||
|
||||
def purge_semcache_expired(self, now: Optional[int] = None) -> int:
|
||||
"""过期缓存清理(夜间任务顺带)。"""
|
||||
now = int(now if now is not None else time.time())
|
||||
with self._lock, self._connect() as conn:
|
||||
cur = conn.execute("DELETE FROM semcache WHERE ttl_ts < ?", (now,))
|
||||
return cur.rowcount
|
||||
|
||||
# ---------- 自省(测试/验收用) ----------
|
||||
def table_names(self) -> List[str]:
|
||||
"""列出已建表名(测试验收)。"""
|
||||
|
||||
Reference in New Issue
Block a user