"""历史 session 目录:列表 + 导出为用户轮次列表(用于评测快照)。""" from __future__ import annotations from dataclasses import dataclass 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, )