修复:CI 部署环境与 ref 错配、迁移碎片化、图片意图 source_span、章节物化脏版式、会话历史与本地语音不一致

新增:TTS 上传 COS 与分片、章节 reading_segments 物化与快照、markdown 清洗、会话消息 repository、语音 store 重构与相关测试
This commit is contained in:
Kevin
2026-03-20 16:36:42 +08:00
parent 7317bf10cd
commit 8af37e5e8e
65 changed files with 1704 additions and 504 deletions

View File

@@ -6,7 +6,11 @@ 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.cos_url_keys import (
collect_cos_keys_from_conversation_history,
collect_cos_keys_from_tts_url_list,
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
@@ -67,16 +71,24 @@ def _build_messages_from_history(
if voice_session_id in seen_audio_sessions:
continue
seen_audio_sessions.add(voice_session_id)
messages.append(
{
"id": f"{conversation_id}_msg_{idx}",
"conversationId": conversation_id,
"content": msg.get("content", ""),
"senderType": "user" if role == "human" else "assistant",
"timestamp": _message_timestamp_ms(msg, fallback_timestamp),
"messageType": message_type,
}
)
item: dict = {
"id": f"{conversation_id}_msg_{idx}",
"conversationId": conversation_id,
"content": msg.get("content", ""),
"senderType": "user" if role == "human" else "assistant",
"timestamp": _message_timestamp_ms(msg, fallback_timestamp),
"messageType": message_type,
}
if voice_session_id and role == "human":
item["voiceSessionId"] = voice_session_id
ds = msg.get("durationSeconds")
if isinstance(ds, (int, float)) and ds > 0:
item["durationSeconds"] = int(ds)
if role == "ai":
tts = msg.get("ttsAudioUrls")
if isinstance(tts, list) and tts:
item["ttsAudioUrls"] = [x for x in tts if isinstance(x, str)]
messages.append(item)
return messages
@@ -202,11 +214,21 @@ class ConversationService:
self._db, conversation_id
)
)
try:
hist = await redis_service.get_conversation_history(conversation_id)
cos_keys |= collect_cos_keys_from_conversation_history(hist)
except Exception:
pass
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)
raw_tts = getattr(seg, "tts_audio_urls", None)
if isinstance(raw_tts, list):
cos_keys |= collect_cos_keys_from_tts_url_list(
[str(x) for x in raw_tts if isinstance(x, str)]
)
await self._clear_history(conversation_id)
conv.deleted_at = datetime.now(timezone.utc)