feat(eval): internal-eval stack, judge fixes, and eval web overhaul

- Merge internal-eval into development.sh (single Celery/infra); internal-eval.sh
  wraps with LIFE_ECHO_WITH_INTERNAL_EVAL; EVAL_ATTACH_ONLY for attaching 8001
  when :8000 is already up; document in api/docs/internal-eval.md.
- Evaluation: transcript_for_judge, judge error surfacing, rubric/schema tweaks,
  execution_service and router updates; tests for judge and composite eval.
- Memory: ingest nested transaction for embedding/enrichment rollback safety.
- Conversation WS: logger.exception for pipeline errors (avoid loguru KeyError).
- app-eval-web: Playground saved replays, dialogue turns helper, hash user_id
  for Memoir; Memoir chapter baseline↔DB row compare with title heuristics;
  Stories page (#memoir-stories); Markdown + copy buttons; toolbar/panel UI;
  react-markdown; development proxy and fixture updates.
This commit is contained in:
Kevin
2026-04-07 17:15:01 +08:00
parent a50b72e7b5
commit 99543d04c6
47 changed files with 4968 additions and 1279 deletions

View File

@@ -255,35 +255,48 @@ def ingest_transcript_sync(
vectors_written = 0
embedding_available = False
enrichment_ok: bool | None = None
try:
embedding_provider = get_embedding_provider()
if embedding_provider is not None:
embedding_available = embedding_provider.is_available()
if chunk_records and embedding_provider is not None:
texts = [content for _, content in chunk_records]
embeddings = embedding_provider.embed_texts_sync(texts)
for (chunk_id, _), emb in zip(chunk_records, embeddings):
if emb:
vectors_written += 1
update_chunk_embedding_sync(session, chunk_id, emb)
except Exception as e:
logger.warning(
"memory embedding 跳过(sync): {} exc_type={}", e, type(e).__name__
"memory embedding provider 不可用(sync): {} exc_type={}",
e,
type(e).__name__,
)
embedding_provider = None
enrichment_ok: bool | None = None
# 向量写入与 enrichment 任一失败时,此前若在**同一事务内**已开始失败的 SQL
# 会导致 session 进入「必须 rollback」状态进而让后续 commit 抛出
# PendingRollbackError污染 Celery 里共用的 `db`。
# 用 SAVEPOINT 包一层失败仅回滚本段source/chunks 主体仍可由外层提交。
try:
from app.features.memory.enrichment import enrich_memory_after_ingest_sync
with session.begin_nested():
if chunk_records and embedding_provider is not None:
texts = [content for _, content in chunk_records]
embeddings = embedding_provider.embed_texts_sync(texts)
for (chunk_id, _), emb in zip(chunk_records, embeddings):
if emb:
vectors_written += 1
update_chunk_embedding_sync(session, chunk_id, emb)
if settings.memory_enrichment_enabled:
from app.features.memory.enrichment import enrich_memory_after_ingest_sync
if settings.memory_enrichment_enabled:
enrich_memory_after_ingest_sync(session, user_id, source.id, llm=None)
enrichment_ok = True
enrich_memory_after_ingest_sync(
session, user_id, source.id, llm=None
)
enrichment_ok = True
except Exception as e:
logger.warning(
"memory embedding/enrichment 跳过(sync): {} exc_type={}",
e,
type(e).__name__,
)
if settings.memory_enrichment_enabled:
enrichment_ok = False
logger.warning(
"memory enrichment 跳过(sync): {} exc_type={}", e, type(e).__name__
)
session.commit()
logger.info(