fix(retrieval): list_gaps returns ORM entities; admin switches back to the domain API
T12 集成实测暴露:select(KbGap) 的结果调 .mappings(),行键是实体类, GapSummary 按列名取值必然 AttributeError。改为 .scalars() 返回实体; admin 的缺口看板从临时自实现切回 retrieval.list_gaps(语义单一定义)。
This commit is contained in:
+12
-27
@@ -3,11 +3,9 @@
|
||||
数据来自检索无结果时的自动埋点(T-P2-06),看板只读:它回答「下一批内容
|
||||
该生产什么」,是内容排期的输入,不是待办队列。
|
||||
|
||||
偏差说明(详见提交信息):协调提交提供的 ``packages.retrieval.list_gaps``
|
||||
对 ``select(KbGap)`` 的结果调 ``.mappings()``,行键是实体名而非列名,
|
||||
``GapSummary(row)`` 取 ``row.gap_id`` 必然 ``AttributeError``(该路径无既有
|
||||
测试覆盖,真库一跑即暴露)。``packages/**`` 本任务禁改,故此处以等价查询
|
||||
在 admin 包内自实现;上游修复后应切回 retrieval 的 ``list_gaps``。
|
||||
数据访问走 retrieval 域的 ``list_gaps``(T12 开发时它有个 mappings 误用
|
||||
的缺陷,当时在 admin 内自实现了等价查询;上游已在合并阶段修复,现切回,
|
||||
缺口语义只有一处定义)。
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
@@ -15,44 +13,31 @@ from __future__ import annotations
|
||||
from typing import Any
|
||||
|
||||
from fastapi import APIRouter, Depends, Query
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from apps.admin.deps import SessionDep, admin_auth
|
||||
from apps.admin.schemas import GapOut
|
||||
from packages.shared.models import KbGap
|
||||
from packages.retrieval import service as retrieval_service
|
||||
from packages.shared.response import ok
|
||||
|
||||
router = APIRouter(prefix="/kb", tags=["admin:gaps"], dependencies=[Depends(admin_auth)])
|
||||
|
||||
|
||||
async def list_gap_rows(session: AsyncSession, *, limit: int, offset: int) -> list[KbGap]:
|
||||
"""kb_gap 看板查询:hit_count 降序,同频次按最近出现降序(与 retrieval 语义一致)。"""
|
||||
result = await session.execute(
|
||||
select(KbGap)
|
||||
.order_by(KbGap.hit_count.desc(), KbGap.last_seen_at.desc())
|
||||
.limit(limit)
|
||||
.offset(offset)
|
||||
)
|
||||
return list(result.scalars().all())
|
||||
|
||||
|
||||
@router.get("/gaps", summary="知识缺口列表(hit_count 降序:最常被问到的排最前)")
|
||||
async def list_gaps(
|
||||
session: SessionDep,
|
||||
limit: int = Query(default=50, ge=1, le=200),
|
||||
offset: int = Query(default=0, ge=0),
|
||||
) -> dict[str, Any]:
|
||||
rows = await list_gap_rows(session, limit=limit, offset=offset)
|
||||
summaries = await retrieval_service.list_gaps(session, limit=limit, offset=offset)
|
||||
items = [
|
||||
GapOut(
|
||||
gap_id=str(row.gap_id),
|
||||
query_text=row.query_text,
|
||||
filter_ctx=dict(row.filter_ctx),
|
||||
hit_count=row.hit_count,
|
||||
first_seen_at=row.first_seen_at,
|
||||
last_seen_at=row.last_seen_at,
|
||||
gap_id=str(g.gap_id),
|
||||
query_text=g.query_text,
|
||||
filter_ctx=dict(g.filter_ctx),
|
||||
hit_count=g.hit_count,
|
||||
first_seen_at=g.first_seen_at,
|
||||
last_seen_at=g.last_seen_at,
|
||||
).model_dump()
|
||||
for row in rows
|
||||
for g in summaries
|
||||
]
|
||||
return ok({"items": items, "total": len(items), "limit": limit, "offset": offset})
|
||||
|
||||
@@ -268,8 +268,12 @@ async def record_gap(
|
||||
INVALIDATION_VERSION = 1
|
||||
|
||||
|
||||
async def list_gaps(session: AsyncSession, *, limit: int = 50, offset: int = 0) -> list[RowMapping]:
|
||||
"""缺口看板查询:hit_count 降序,同频次按最近出现降序。"""
|
||||
async def list_gaps(session: AsyncSession, *, limit: int = 50, offset: int = 0) -> list[KbGap]:
|
||||
"""缺口看板查询:hit_count 降序,同频次按最近出现降序。
|
||||
|
||||
返回 ORM 实体而非 mappings:单实体 select 的 mapping 行键是实体类,
|
||||
按列名取值必然 AttributeError(T12 集成实测暴露,此处修正)。
|
||||
"""
|
||||
from sqlalchemy import select
|
||||
|
||||
from packages.shared.models import KbGap
|
||||
@@ -281,4 +285,4 @@ async def list_gaps(session: AsyncSession, *, limit: int = 50, offset: int = 0)
|
||||
.offset(offset)
|
||||
)
|
||||
result = await session.execute(stmt)
|
||||
return list(result.mappings().all())
|
||||
return list(result.scalars().all())
|
||||
|
||||
Reference in New Issue
Block a user