数据库与模型:新增多版迁移(章节证据快照、对话血缘、记忆事实/时间线 lineage 等),把「成稿 ↔ 对话/记忆」的溯源信息落到表结构里。 业务链路:会话与 WS、回忆录/故事流水线、记忆写入与 enrichment 等跟着接上线索与快照;新增章节证据快照与评测侧 EvalTraceService 等模块,方便组评审用的证据包。 内部评测:自动化 run 与手工 memoir 评审共用可追溯证据;rubric/ judge 相关脚本与文档有配套调整。 app-eval-web:Memoir/实验详情里能展开看证据摘要与 evidence_trace(含对话轮次 id);Vite 代理与 development.sh 注入的 API 端口与当前默认内部评测端口一致,避免改端口后页面连错服务。 工程杂项:GitHub Actions / 仓库说明有更新;各适配器与支付/配额/plan 等多处为小改动或跟随主改动的收尾;新增/扩充了?
70 lines
2.1 KiB
Python
70 lines
2.1 KiB
Python
"""对话落库返回人/助 message id,供 segment lineage 配对。"""
|
||
|
||
from __future__ import annotations
|
||
|
||
from unittest.mock import AsyncMock, MagicMock
|
||
|
||
import pytest
|
||
from sqlalchemy.ext.asyncio import AsyncSession
|
||
|
||
from app.features.conversation.history_store import HumanAiTurnIds
|
||
|
||
|
||
@pytest.mark.asyncio
|
||
async def test_record_human_ai_turn_returns_both_message_ids(monkeypatch) -> None:
|
||
conv_id = "conv-1"
|
||
captured: list[object] = []
|
||
|
||
class FakeMsg:
|
||
def __init__(self, **kwargs) -> None:
|
||
for k, v in kwargs.items():
|
||
setattr(self, k, v)
|
||
|
||
class _FakeRepo:
|
||
@staticmethod
|
||
def add_conversation_message(msg: object, db) -> None:
|
||
captured.append(msg)
|
||
|
||
monkeypatch.setattr(
|
||
"app.features.conversation.history_store.ConversationMessage",
|
||
FakeMsg,
|
||
)
|
||
|
||
db = MagicMock(spec=AsyncSession)
|
||
db.commit = AsyncMock()
|
||
db.refresh = AsyncMock()
|
||
|
||
import app.features.conversation.history_store as hs_mod
|
||
|
||
orig_repo = hs_mod.repo
|
||
hs_mod.repo = _FakeRepo # type: ignore[misc]
|
||
try:
|
||
from app.features.conversation import history_store as hs
|
||
|
||
store = hs.ConversationHistoryStore(db)
|
||
store._sync_redis_best_effort = AsyncMock() # type: ignore[method-assign]
|
||
store._touch_conversation = AsyncMock() # type: ignore[method-assign]
|
||
|
||
out = await store.record_human_ai_turn(
|
||
conv_id,
|
||
"hello",
|
||
["reply a", "reply b"],
|
||
user_message_timestamp=None,
|
||
is_from_voice=False,
|
||
voice_session_id=None,
|
||
audio_duration_seconds=None,
|
||
tts_audio_urls=None,
|
||
segment_id="seg-1",
|
||
memory_retrieval_trace=None,
|
||
)
|
||
finally:
|
||
hs_mod.repo = orig_repo # type: ignore[misc]
|
||
|
||
assert isinstance(out, HumanAiTurnIds)
|
||
assert len(captured) == 2
|
||
assert captured[0].role == "human"
|
||
assert captured[1].role == "ai"
|
||
assert captured[0].segment_id == "seg-1"
|
||
assert out.human_message_id == captured[0].id
|
||
assert out.assistant_message_id == captured[1].id
|