fix/various fixes

This commit is contained in:
Kevin
2026-03-20 15:15:35 +08:00
parent 7f57f96c25
commit 7317bf10cd
112 changed files with 3790 additions and 2242 deletions

View File

@@ -1,15 +1,21 @@
"""Conversation service — 对话编排(列表、创建、结束、删除、消息、整理)。"""
from app.core.logging import get_logger
import uuid
from datetime import datetime, timezone
from fastapi import HTTPException
from sqlalchemy.ext.asyncio import AsyncSession
from app.core.cos_url_keys import extract_cos_object_key_if_owned
from app.core.logging import get_logger
from app.core.redis import redis_service
from app.core.storage_purge import delete_object_storage_keys_best_effort
from app.features.conversation import repo
from app.features.conversation.models import Conversation, Segment
from app.features.conversation.models import Conversation
from app.features.conversation.session_history import segments_to_redis_history
from app.features.memory import repo as memory_repo
from app.features.quota.service import QuotaService
from app.ports.storage import ObjectStorage
from app.tasks.memoir_tasks import process_memoir_segments
logger = get_logger(__name__)
@@ -75,23 +81,44 @@ def _build_messages_from_history(
class ConversationService:
def __init__(self, db: AsyncSession, quota_service: QuotaService):
def __init__(
self,
db: AsyncSession,
quota_service: QuotaService,
*,
object_storage: ObjectStorage | None = None,
):
self._db = db
self._quota = quota_service
self._object_storage = object_storage
async def _get_history(self, conversation_id: str) -> list[dict]:
from app.core.redis import redis_service
return await redis_service.get_conversation_history(conversation_id)
async def _clear_history(self, conversation_id: str) -> None:
try:
from app.core.redis import redis_service
await redis_service.clear_conversation_history(conversation_id)
except Exception:
pass
async def ensure_redis_history_from_segments(
self, conversation_id: str
) -> list[dict]:
"""
供 WS 与 get_messages 使用:优先 Redis若为空则用 DB segments 重建并写回。
会话层编排,非 Agent 职责ChatOrchestrator 只读写已存在的 Redis 流)。
"""
history = await redis_service.get_conversation_history(conversation_id)
if history:
return history
segments = await repo.get_segments_for_conversation(conversation_id, self._db)
if not segments:
return []
rebuilt = segments_to_redis_history(segments)
if rebuilt:
await redis_service.set_conversation_history(conversation_id, rebuilt)
return rebuilt
async def list_for_user(self, user_id: str) -> list[dict]:
conversations = await repo.get_user_conversations(user_id, self._db)
result = []
@@ -134,7 +161,7 @@ class ConversationService:
async def get_or_404(self, conversation_id: str, user_id: str) -> Conversation:
conv = await repo.get_conversation(conversation_id, self._db)
if not conv or conv.user_id != user_id:
if not conv or conv.user_id != user_id or conv.deleted_at is not None:
raise HTTPException(status_code=404, detail="Conversation not found")
return conv
@@ -170,14 +197,31 @@ class ConversationService:
async def delete(self, conversation_id: str, user_id: str) -> None:
conv = await self.get_or_404(conversation_id, user_id)
cos_keys: set[str] = set(
await memory_repo.list_storage_keys_for_conversation(
self._db, conversation_id
)
)
segments = await repo.get_segments_for_conversation(conversation_id, self._db)
for seg in segments:
k = extract_cos_object_key_if_owned(seg.audio_url)
if k:
cos_keys.add(k)
await self._clear_history(conversation_id)
await self._db.delete(conv)
conv.deleted_at = datetime.now(timezone.utc)
await self._db.commit()
delete_object_storage_keys_best_effort(
self._object_storage,
sorted(cos_keys),
log_prefix=f"conversation_soft_delete id={conversation_id}",
)
async def get_messages(self, conversation_id: str, user_id: str) -> list[dict]:
conv = await self.get_or_404(conversation_id, user_id)
try:
history = await self._get_history(conversation_id)
history = await self.ensure_redis_history_from_segments(conversation_id)
return _build_messages_from_history(
conversation_id=conversation_id,
history=history,