在 conversations 表增加 playground_conversation_judge_json,流式/非流式对话评审结束后写入最近一次快照(整体分、逐轮分、对比文案、错误与基线文件名等)。新增只读 GET 供前端按会话拉取;评测台 Playground 切换会话时自动恢复,并提示基线是否和当时一致。
123 lines
3.8 KiB
Python
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
|