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:
Kevin
2026-04-30 14:11:46 +08:00
parent ac436b87a2
commit 71fbd39e32
53 changed files with 953 additions and 2448 deletions

View File

@@ -8,6 +8,7 @@ from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import joinedload
from app.agents.state_schema import narrative_coverage_state
from app.core.logging import get_logger
from app.core.storage_purge import delete_object_storage_keys_best_effort
from app.features.memoir import repo
@@ -271,9 +272,10 @@ class MemoirService:
from app.features.memoir.state_service import get_or_create_state
state = await get_or_create_state(user_id, self._db)
narrative_state = narrative_coverage_state(state)
return {
"current_stage": state.current_stage,
"empty_slots": state.empty_slots_for_current_stage(),
"empty_slots": narrative_state.empty_slots_for_current_stage(),
"covered_stages": state.covered_stages,
}