feat(api): 叙事 prompt、职业上下文、读路径章节、WS 解耦与错误脱敏
- 回忆录:事实边界补充允许清单;传记文体示例与 JSON 叙事要求对齐 - default 职业提示 occupation_context;cadre/military 退休语境 - GET 章节读路径零写入,prepare_chapter_read_view + markdown_for_response - 文本归一抽到 core/text_normalize;移除弃用 reply 策略与 recompose_chapters_for_story - ConversationService:WS 连接/用户段落/结束对话;对外错误固定文案 - 测试:HTTP 脱敏契约、章节读视图、occupation 与 background_voice
This commit is contained in:
@@ -18,13 +18,11 @@ from app.core.dependencies import get_asr_provider
|
||||
from app.core.logging import get_logger
|
||||
from app.core.security import verify_token
|
||||
from app.features.conversation.history_store import ConversationHistoryStore
|
||||
from app.features.conversation.models import Conversation, Segment
|
||||
from app.features.conversation.service import ConversationService
|
||||
from app.features.conversation.ws.connection_manager import manager
|
||||
from app.features.conversation.ws.message_types import MessageType
|
||||
from app.features.conversation.ws.pipeline import (
|
||||
_delayed_listening_feedback,
|
||||
_mark_conversation_active,
|
||||
_voice_session_id_from_client_segment_id,
|
||||
background_runner,
|
||||
bump_tts_cancel_epoch,
|
||||
@@ -106,49 +104,41 @@ async def websocket_endpoint(
|
||||
},
|
||||
)
|
||||
|
||||
conversation = await db.get(Conversation, conversation_id)
|
||||
if not conversation:
|
||||
conversation = Conversation(
|
||||
id=conversation_id,
|
||||
user_id=user_id,
|
||||
started_at=datetime.now(timezone.utc),
|
||||
status="active",
|
||||
conversation, ws_conn_err = await conversation_service.ensure_ws_connection(
|
||||
conversation_id, user_id
|
||||
)
|
||||
if ws_conn_err == "forbidden":
|
||||
try:
|
||||
await manager.send_message(
|
||||
conversation_id,
|
||||
{
|
||||
"type": MessageType.ERROR,
|
||||
"data": {"message": "无权访问此对话"},
|
||||
"timestamp": datetime.now(timezone.utc).isoformat(),
|
||||
},
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
await websocket.close(
|
||||
code=status.WS_1008_POLICY_VIOLATION, reason="无权访问此对话"
|
||||
)
|
||||
db.add(conversation)
|
||||
await db.commit()
|
||||
else:
|
||||
if conversation.user_id != user_id:
|
||||
try:
|
||||
await manager.send_message(
|
||||
conversation_id,
|
||||
{
|
||||
"type": MessageType.ERROR,
|
||||
"data": {"message": "无权访问此对话"},
|
||||
"timestamp": datetime.now(timezone.utc).isoformat(),
|
||||
},
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
await websocket.close(
|
||||
code=status.WS_1008_POLICY_VIOLATION, reason="无权访问此对话"
|
||||
return
|
||||
if ws_conn_err == "deleted":
|
||||
try:
|
||||
await manager.send_message(
|
||||
conversation_id,
|
||||
{
|
||||
"type": MessageType.ERROR,
|
||||
"data": {"message": "对话已删除"},
|
||||
"timestamp": datetime.now(timezone.utc).isoformat(),
|
||||
},
|
||||
)
|
||||
return
|
||||
if conversation.deleted_at is not None:
|
||||
try:
|
||||
await manager.send_message(
|
||||
conversation_id,
|
||||
{
|
||||
"type": MessageType.ERROR,
|
||||
"data": {"message": "对话已删除"},
|
||||
"timestamp": datetime.now(timezone.utc).isoformat(),
|
||||
},
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
await websocket.close(
|
||||
code=status.WS_1008_POLICY_VIOLATION, reason="对话已删除"
|
||||
)
|
||||
return
|
||||
except Exception:
|
||||
pass
|
||||
await websocket.close(
|
||||
code=status.WS_1008_POLICY_VIOLATION, reason="对话已删除"
|
||||
)
|
||||
return
|
||||
|
||||
history = await conversation_service.ensure_redis_history_from_db(
|
||||
conversation_id
|
||||
@@ -205,6 +195,7 @@ async def websocket_endpoint(
|
||||
background_voice=infer_background_voice(
|
||||
user.occupation
|
||||
),
|
||||
occupation=user.occupation or "",
|
||||
)
|
||||
)
|
||||
ai_msg_id = await ConversationHistoryStore(
|
||||
@@ -291,18 +282,12 @@ async def websocket_endpoint(
|
||||
)
|
||||
continue
|
||||
|
||||
segment = Segment(
|
||||
id=str(uuid.uuid4()),
|
||||
conversation_id=conversation_id,
|
||||
user_input_text=text_message,
|
||||
processed=False,
|
||||
segment = await conversation_service.create_user_segment(
|
||||
conversation,
|
||||
user_id,
|
||||
text_message,
|
||||
)
|
||||
db.add(segment)
|
||||
user_message_timestamp = _mark_conversation_active(
|
||||
conversation
|
||||
)
|
||||
await db.commit()
|
||||
await db.refresh(segment)
|
||||
user_message_timestamp = conversation.last_message_at
|
||||
await background_runner.queue_message(
|
||||
conversation.user_id,
|
||||
segment.id,
|
||||
@@ -554,20 +539,16 @@ async def websocket_endpoint(
|
||||
ads = int(audio_duration)
|
||||
except (TypeError, ValueError):
|
||||
ads = 0
|
||||
segment = Segment(
|
||||
id=str(uuid.uuid4()),
|
||||
conversation_id=conversation_id,
|
||||
user_input_text=asr_text,
|
||||
audio_url=f"audio:{audio_duration}s",
|
||||
audio_duration_seconds=ads if ads > 0 else None,
|
||||
processed=False,
|
||||
segment = (
|
||||
await conversation_service.create_user_segment(
|
||||
conversation,
|
||||
user_id,
|
||||
asr_text,
|
||||
audio_url=f"audio:{audio_duration}s",
|
||||
audio_duration_seconds=ads if ads > 0 else None,
|
||||
)
|
||||
)
|
||||
db.add(segment)
|
||||
user_message_timestamp = _mark_conversation_active(
|
||||
conversation
|
||||
)
|
||||
await db.commit()
|
||||
await db.refresh(segment)
|
||||
user_message_timestamp = conversation.last_message_at
|
||||
await background_runner.queue_message(
|
||||
conversation.user_id,
|
||||
segment.id,
|
||||
@@ -606,7 +587,7 @@ async def websocket_endpoint(
|
||||
{
|
||||
"type": MessageType.ERROR,
|
||||
"data": {
|
||||
"message": f"处理音频消息失败: {str(e)}"
|
||||
"message": "语音处理失败,请重试或使用文字输入"
|
||||
},
|
||||
"timestamp": datetime.now(
|
||||
timezone.utc
|
||||
@@ -646,7 +627,7 @@ async def websocket_endpoint(
|
||||
conversation_id,
|
||||
{
|
||||
"type": MessageType.ERROR,
|
||||
"data": {"message": f"转写失败: {str(e)}"},
|
||||
"data": {"message": "语音转写失败,请重试"},
|
||||
"timestamp": datetime.now(timezone.utc).isoformat(),
|
||||
},
|
||||
)
|
||||
@@ -655,9 +636,7 @@ async def websocket_endpoint(
|
||||
bump_tts_cancel_epoch(conversation_id)
|
||||
|
||||
elif msg_type == MessageType.END_CONVERSATION:
|
||||
conversation.status = "ended"
|
||||
conversation.ended_at = datetime.now(timezone.utc)
|
||||
await db.commit()
|
||||
await conversation_service.end(conversation_id, user_id)
|
||||
|
||||
await process_conversation_segments(
|
||||
conversation_id, db, quota_service
|
||||
@@ -715,7 +694,7 @@ async def websocket_endpoint(
|
||||
conversation_id,
|
||||
{
|
||||
"type": MessageType.ERROR,
|
||||
"data": {"message": str(e)},
|
||||
"data": {"message": "处理失败,请重试"},
|
||||
"timestamp": datetime.now(
|
||||
timezone.utc
|
||||
).isoformat(),
|
||||
@@ -739,7 +718,7 @@ async def websocket_endpoint(
|
||||
conversation_id,
|
||||
{
|
||||
"type": MessageType.ERROR,
|
||||
"data": {"message": str(e)},
|
||||
"data": {"message": "处理失败,请重试"},
|
||||
"timestamp": datetime.now(timezone.utc).isoformat(),
|
||||
},
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user