234 lines
8.2 KiB
Python
234 lines
8.2 KiB
Python
from __future__ import annotations
|
|
|
|
from sqlalchemy import and_, select
|
|
|
|
from .shared import *
|
|
from .helpers import _resolve_tenant_id
|
|
from ...models import QAFeedback, QARecord
|
|
from ...services.feedback_center import FeedbackCenterService
|
|
|
|
@router.post("/qa-records/{qa_id}/feedback")
|
|
async def submit_qa_feedback(
|
|
qa_id: int,
|
|
request: QAFeedbackCreateRequest,
|
|
current_user: User = Depends(get_current_user),
|
|
db: AsyncSession = Depends(get_db),
|
|
tenant_id: int = Depends(get_tenant_id),
|
|
):
|
|
service = AdaptiveRAGService(db, tenant_id=_resolve_tenant_id(current_user, tenant_id))
|
|
try:
|
|
result = await service.submit_feedback(
|
|
qa_id=qa_id,
|
|
rating=request.rating,
|
|
helpfulness=request.helpfulness,
|
|
error_types=request.error_types,
|
|
correct_answer=request.correct_answer,
|
|
submitted_by=int(current_user.id),
|
|
)
|
|
except ValueError as exc:
|
|
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
|
try:
|
|
feedback_row = (
|
|
await db.execute(
|
|
select(QAFeedback)
|
|
.where(
|
|
and_(
|
|
QAFeedback.tenant_id == _resolve_tenant_id(current_user, tenant_id),
|
|
QAFeedback.qa_id == qa_id,
|
|
QAFeedback.submitted_by == int(current_user.id),
|
|
)
|
|
)
|
|
.order_by(QAFeedback.created_at.desc(), QAFeedback.id.desc())
|
|
)
|
|
).scalars().first()
|
|
qa_row = (
|
|
await db.execute(
|
|
select(QARecord).where(
|
|
and_(
|
|
QARecord.tenant_id == _resolve_tenant_id(current_user, tenant_id),
|
|
QARecord.id == qa_id,
|
|
)
|
|
)
|
|
)
|
|
).scalar_one_or_none()
|
|
if feedback_row and qa_row:
|
|
await FeedbackCenterService(db).ingest_qa_feedback(
|
|
qa_feedback=feedback_row,
|
|
qa_record=qa_row,
|
|
commit=True,
|
|
)
|
|
except Exception:
|
|
await db.rollback()
|
|
return success_response(result)
|
|
|
|
@router.get("/qa-records")
|
|
async def list_qa_records(
|
|
project_id: int | None = Query(default=None),
|
|
date_from: datetime | None = Query(default=None),
|
|
date_to: datetime | None = Query(default=None),
|
|
rating: str | None = Query(default=None),
|
|
page: int = Query(default=1, ge=1),
|
|
page_size: int = Query(default=20, ge=1, le=100),
|
|
current_user: User = Depends(get_current_user),
|
|
db: AsyncSession = Depends(get_db),
|
|
tenant_id: int = Depends(get_tenant_id),
|
|
):
|
|
_ = current_user
|
|
query = QAListQuery(
|
|
project_id=project_id,
|
|
date_from=date_from,
|
|
date_to=date_to,
|
|
rating=rating,
|
|
page=page,
|
|
page_size=page_size,
|
|
)
|
|
service = AdaptiveRAGService(db, tenant_id=_resolve_tenant_id(current_user, tenant_id))
|
|
result = await service.list_qa_records(
|
|
project_id=query.project_id,
|
|
date_from=query.date_from,
|
|
date_to=query.date_to,
|
|
rating=query.rating,
|
|
page=query.page,
|
|
page_size=query.page_size,
|
|
)
|
|
return success_response(result)
|
|
|
|
@router.get("/qa-sessions")
|
|
async def list_qa_sessions(
|
|
date_from: datetime | None = Query(default=None),
|
|
date_to: datetime | None = Query(default=None),
|
|
page: int = Query(default=1, ge=1),
|
|
page_size: int = Query(default=20, ge=1, le=100),
|
|
current_user: User = Depends(get_current_user),
|
|
db: AsyncSession = Depends(get_db),
|
|
tenant_id: int = Depends(get_tenant_id),
|
|
):
|
|
query = QASessionListQuery(
|
|
date_from=date_from,
|
|
date_to=date_to,
|
|
page=page,
|
|
page_size=page_size,
|
|
)
|
|
service = AdaptiveRAGService(db, tenant_id=_resolve_tenant_id(current_user, tenant_id))
|
|
result = await service.list_qa_sessions(
|
|
user_id=int(current_user.id),
|
|
date_from=query.date_from,
|
|
date_to=query.date_to,
|
|
page=query.page,
|
|
page_size=query.page_size,
|
|
)
|
|
return success_response(result)
|
|
|
|
@router.get("/qa-sessions/{session_id}")
|
|
async def qa_session_detail(
|
|
session_id: int,
|
|
current_user: User = Depends(get_current_user),
|
|
db: AsyncSession = Depends(get_db),
|
|
tenant_id: int = Depends(get_tenant_id),
|
|
):
|
|
service = AdaptiveRAGService(db, tenant_id=_resolve_tenant_id(current_user, tenant_id))
|
|
try:
|
|
result = await service.get_qa_session_detail(session_id=session_id, user_id=int(current_user.id))
|
|
except ValueError as exc:
|
|
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
|
return success_response(result)
|
|
|
|
@router.get("/qa-sessions/{session_id}/state")
|
|
async def qa_session_state_replay(
|
|
session_id: int,
|
|
current_user: User = Depends(get_current_user),
|
|
db: AsyncSession = Depends(get_db),
|
|
tenant_id: int = Depends(get_tenant_id),
|
|
):
|
|
service = AdaptiveRAGService(db, tenant_id=_resolve_tenant_id(current_user, tenant_id))
|
|
try:
|
|
result = await service.get_session_state_replay(
|
|
session_id=session_id,
|
|
user_id=int(current_user.id),
|
|
)
|
|
except ValueError as exc:
|
|
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
|
return success_response(result)
|
|
|
|
@router.patch("/qa-sessions/{session_id}/title")
|
|
async def rename_qa_session(
|
|
session_id: int,
|
|
request: QASessionRenameRequest,
|
|
current_user: User = Depends(get_current_user),
|
|
db: AsyncSession = Depends(get_db),
|
|
tenant_id: int = Depends(get_tenant_id),
|
|
):
|
|
service = AdaptiveRAGService(db, tenant_id=_resolve_tenant_id(current_user, tenant_id))
|
|
try:
|
|
result = await service.rename_qa_session(
|
|
session_id=session_id,
|
|
user_id=int(current_user.id),
|
|
title=request.title,
|
|
)
|
|
except ValueError as exc:
|
|
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
|
return success_response(result)
|
|
|
|
@router.delete("/qa-sessions/{session_id}")
|
|
async def delete_qa_session(
|
|
session_id: int,
|
|
current_user: User = Depends(get_current_user),
|
|
db: AsyncSession = Depends(get_db),
|
|
tenant_id: int = Depends(get_tenant_id),
|
|
):
|
|
service = AdaptiveRAGService(db, tenant_id=_resolve_tenant_id(current_user, tenant_id))
|
|
deleted = await service.delete_qa_session(session_id=session_id, user_id=int(current_user.id))
|
|
if not deleted:
|
|
raise HTTPException(status_code=404, detail="qa session 不存在")
|
|
return success_response({"deleted": True, "session_id": session_id})
|
|
|
|
@router.get("/qa-records/stats")
|
|
async def qa_records_stats(
|
|
project_id: int | None = Query(default=None),
|
|
date_from: datetime | None = Query(default=None),
|
|
date_to: datetime | None = Query(default=None),
|
|
current_user: User = Depends(get_current_user),
|
|
db: AsyncSession = Depends(get_db),
|
|
tenant_id: int = Depends(get_tenant_id),
|
|
):
|
|
service = AdaptiveRAGService(db, tenant_id=_resolve_tenant_id(current_user, tenant_id))
|
|
result = await service.get_qa_stats(
|
|
project_id=project_id,
|
|
date_from=date_from,
|
|
date_to=date_to,
|
|
)
|
|
return success_response(result)
|
|
|
|
@router.get("/qa-records/export")
|
|
async def export_qa_records_jsonl(
|
|
include_unrated: bool = Query(default=False),
|
|
project_id: int | None = Query(default=None),
|
|
rating: str | None = Query(default=None),
|
|
domain: str | None = Query(default=None),
|
|
date_from: datetime | None = Query(default=None),
|
|
date_to: datetime | None = Query(default=None),
|
|
current_user: User = Depends(require_roles("REVIEWER", "OWNER")),
|
|
db: AsyncSession = Depends(get_db),
|
|
tenant_id: int = Depends(get_tenant_id),
|
|
):
|
|
_ = current_user
|
|
query = QAExportQuery(
|
|
include_unrated=include_unrated,
|
|
project_id=project_id,
|
|
rating=rating,
|
|
domain=domain,
|
|
date_from=date_from,
|
|
date_to=date_to,
|
|
)
|
|
|
|
service = AdaptiveRAGService(db, tenant_id=_resolve_tenant_id(current_user, tenant_id))
|
|
payload = await service.export_qa_jsonl(
|
|
include_unrated=query.include_unrated,
|
|
project_id=query.project_id,
|
|
rating=query.rating,
|
|
domain=query.domain,
|
|
date_from=query.date_from,
|
|
date_to=query.date_to,
|
|
)
|
|
return Response(content=payload, media_type="application/x-ndjson")
|