feat(api)!: memory single chain — async MemoryService, strict eval closure
Route all memory ingest/retrieve/enrichment/compaction through async MemoryService. Remove legacy sync memory implementations (ingest/retrieve/compaction); Celery and memoir Phase2 call asyncio.run into MemoryService-backed helpers. Memoir Phase1 batch ingest uses MemoryService.ingest_transcripts_batch; drop chapters. evidence_bundle_json mirror (Alembic 0015). Evaluation uses snapshot/link-only bundles; raise EvidenceClosureMissing instead of partial/fallback lineage tiers. Split memoir state into NarrativeCoverageState and InterviewControlState; delete the _interview_meta_store adapter layer. Remove rolling-query and recent-fact fallback settings from config and evidence assembly. Update judges, docs, tests, and PlaygroundPage alignment. Made-with: Cursor
This commit is contained in:
@@ -15,15 +15,14 @@ from app.agents.stage_constants import (
|
||||
normalize_chat_stage,
|
||||
)
|
||||
from app.agents.state_schema import (
|
||||
InterviewControlState,
|
||||
KnownFact,
|
||||
MemoirStateSchema,
|
||||
PersonaThread,
|
||||
SlotData,
|
||||
default_state,
|
||||
narrative_coverage_state,
|
||||
)
|
||||
from app.core.config import settings
|
||||
from app.features.memoir import _interview_meta_store as interview_meta
|
||||
from app.features.memoir.models import MemoirState as MemoirStateModel
|
||||
|
||||
|
||||
@@ -35,19 +34,31 @@ def _slots_snapshot_for_merge(raw: Dict[str, Dict] | None) -> Dict[str, Dict]:
|
||||
|
||||
|
||||
def coerce_memoir_state(model: MemoirStateModel) -> MemoirStateSchema:
|
||||
"""把 ORM 行投影成 MemoirStateSchema;控制元数据的读法已隔离在 interview_meta 适配层。"""
|
||||
"""把 ORM 行投影成 MemoirStateSchema;控制元数据来自独立列。"""
|
||||
raw_slots = model.slots if isinstance(model.slots, dict) else None
|
||||
control = interview_meta.read(raw_slots)
|
||||
clean_slots = interview_meta.strip(raw_slots) or dict(default_state().slots)
|
||||
clean_slots = raw_slots or dict(default_state().slots)
|
||||
known_raw = (
|
||||
model.known_facts_json if isinstance(model.known_facts_json, list) else []
|
||||
)
|
||||
persona_raw = (
|
||||
model.persona_threads_json
|
||||
if isinstance(model.persona_threads_json, list)
|
||||
else []
|
||||
)
|
||||
recent_raw = (
|
||||
model.recent_questions_json
|
||||
if isinstance(model.recent_questions_json, list)
|
||||
else []
|
||||
)
|
||||
return MemoirStateSchema.model_validate(
|
||||
{
|
||||
"stage_order": model.stage_order or default_state().stage_order,
|
||||
"current_stage": model.current_stage,
|
||||
"covered_stages": model.covered_stages or [],
|
||||
"slots": clean_slots,
|
||||
"known_facts": control.known_facts,
|
||||
"persona_threads": control.persona_threads,
|
||||
"recent_questions": control.recent_questions,
|
||||
"known_facts": [x for x in known_raw if isinstance(x, dict)],
|
||||
"persona_threads": [x for x in persona_raw if isinstance(x, dict)],
|
||||
"recent_questions": [str(x).strip() for x in recent_raw if str(x).strip()],
|
||||
}
|
||||
)
|
||||
|
||||
@@ -173,7 +184,7 @@ async def mark_stage_complete(
|
||||
|
||||
async def get_empty_slots(user_id: str, db: AsyncSession) -> List[str]:
|
||||
state = await get_or_create_state(user_id, db)
|
||||
return state.empty_slots_for_current_stage()
|
||||
return narrative_coverage_state(state).empty_slots_for_current_stage()
|
||||
|
||||
|
||||
async def switch_stage(
|
||||
@@ -220,17 +231,9 @@ async def save_interview_state_meta(
|
||||
result = await db.execute(stmt)
|
||||
state = result.scalar_one()
|
||||
|
||||
slots = _slots_snapshot_for_merge(
|
||||
state.slots if isinstance(state.slots, dict) else None
|
||||
)
|
||||
state.slots = interview_meta.write(
|
||||
slots,
|
||||
control=InterviewControlState(
|
||||
known_facts=known_facts,
|
||||
persona_threads=persona_threads,
|
||||
recent_questions=recent_questions,
|
||||
),
|
||||
)
|
||||
state.known_facts_json = [x.model_dump() for x in known_facts]
|
||||
state.persona_threads_json = [x.model_dump() for x in persona_threads]
|
||||
state.recent_questions_json = list(recent_questions)
|
||||
await db.commit()
|
||||
await db.refresh(state)
|
||||
return coerce_memoir_state(state)
|
||||
|
||||
Reference in New Issue
Block a user