Files
life-echo/api/app/features/evaluation/session_catalog_service.py
Kevin 78b61c076e feat(eval): Playground GLM 评分落库并可恢复
在 conversations 表增加 playground_conversation_judge_json,流式/非流式对话评审结束后写入最近一次快照(整体分、逐轮分、对比文案、错误与基线文件名等)。新增只读 GET 供前端按会话拉取;评测台 Playground 切换会话时自动恢复,并提示基线是否和当时一致。
2026-04-08 16:51:08 +08:00

123 lines
3.8 KiB
Python

"""历史 session 目录:列表 + 导出为用户轮次列表(用于评测快照)。"""
from __future__ import annotations
from dataclasses import dataclass
from typing import Any
from sqlalchemy.ext.asyncio import AsyncSession
from app.features.evaluation.schemas import (
SessionDialogueMessageOut,
SessionDialogueOut,
)
from app.features.evaluation.session_catalog_repo import SessionCatalogRepo
@dataclass
class SessionSummary:
id: str
user_id: str
user_phone: str | None
started_at: object | None
last_message_at: object | None
conversation_stage: str | None
current_topic: str | None
status: str | None
@dataclass
class SessionTranscript:
conversation_id: str
user_id: str
user_utterances_from_segments: list[str]
user_utterances_from_messages: list[str]
class SessionCatalogService:
def __init__(self, db: AsyncSession) -> None:
self._repo = SessionCatalogRepo(db)
async def list_sessions(
self,
*,
offset: int = 0,
limit: int = 50,
user_id: str | None = None,
q: str | None = None,
status: str | None = None,
) -> tuple[list[SessionSummary], int]:
total = await self._repo.count_conversations(status=status)
rows = await self._repo.list_conversations(
offset=offset,
limit=limit,
user_id=user_id,
q_text=q,
status=status,
)
out = [
SessionSummary(
id=c.id,
user_id=c.user_id,
user_phone=c.user.phone if c.user is not None else None,
started_at=c.started_at,
last_message_at=c.last_message_at,
conversation_stage=c.conversation_stage,
current_topic=c.current_topic,
status=c.status,
)
for c in rows
]
return out, total
async def get_session_dialogue(
self, conversation_id: str
) -> SessionDialogueOut | None:
c = await self._repo.get_conversation(conversation_id)
if not c or c.deleted_at:
return None
msgs = await self._repo.list_messages_for_conversation(conversation_id)
return SessionDialogueOut(
conversation_id=conversation_id,
messages=[
SessionDialogueMessageOut(
role=m.role,
content=m.content,
created_at=m.created_at,
)
for m in msgs
],
)
async def get_transcript(self, conversation_id: str) -> SessionTranscript | None:
c = await self._repo.get_conversation(conversation_id)
if not c or c.deleted_at:
return None
segs = await self._repo.list_segments_for_conversation(conversation_id)
msgs = await self._repo.list_messages_for_conversation(conversation_id)
from_segments = [
(s.user_input_text or "").strip()
for s in segs
if (s.user_input_text or "").strip()
]
from_messages = [
m.content.strip()
for m in msgs
if m.role == "human" and (m.content or "").strip()
]
return SessionTranscript(
conversation_id=c.id,
user_id=c.user_id,
user_utterances_from_segments=from_segments,
user_utterances_from_messages=from_messages,
)
async def get_playground_conversation_judge_json(
self, conversation_id: str
) -> dict[str, Any] | None:
c = await self._repo.get_conversation(conversation_id)
if not c or c.deleted_at:
return None
raw = c.playground_conversation_judge_json
return raw if isinstance(raw, dict) else None