feat(eval): server-side replay/phase1 timing + memoir phase1 batch chunking
- Replay and memoir-submit responses include started/finished UTC and elapsed_ms; Phase1 poll exposes Redis-backed submit time and elapsed_ms_since_submit. - Phase1 batch LLM splits segments by memoir_phase1_batch_llm_chunk_size with bisect fallback per chunk; Playground shows server timings. Made-with: Cursor
This commit is contained in:
@@ -2,15 +2,23 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import time
|
||||
from datetime import datetime, timezone
|
||||
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.core.db import utc_now
|
||||
from app.features.conversation.models import Conversation, Segment
|
||||
from app.features.conversation.ws.pipeline import background_runner
|
||||
from app.features.evaluation.errors import (
|
||||
EvaluationBadRequestError,
|
||||
EvaluationNotFoundError,
|
||||
)
|
||||
from app.features.evaluation.phase1_job_timing import (
|
||||
load_phase1_job_meta,
|
||||
record_phase1_job_submitted,
|
||||
)
|
||||
from app.features.evaluation.schemas import MemoirPhase1ReadyOut, MemoirSubmitOut
|
||||
|
||||
|
||||
@@ -51,10 +59,36 @@ class MemoirReadinessService:
|
||||
|
||||
pending = [s.id for s in rows if s.topic_category is None]
|
||||
ready = len(pending) == 0
|
||||
job_submitted_at_utc: datetime | None = None
|
||||
elapsed_ms_since_submit: int | None = None
|
||||
durations_ms: dict[str, int] = {}
|
||||
meta = await load_phase1_job_meta(cid)
|
||||
if meta:
|
||||
raw_sub = meta.get("submitted_at_utc")
|
||||
if isinstance(raw_sub, str) and raw_sub.strip():
|
||||
try:
|
||||
iso = raw_sub.strip().replace("Z", "+00:00")
|
||||
job_submitted_at_utc = datetime.fromisoformat(iso)
|
||||
if job_submitted_at_utc.tzinfo is None:
|
||||
job_submitted_at_utc = job_submitted_at_utc.replace(
|
||||
tzinfo=timezone.utc
|
||||
)
|
||||
now = utc_now()
|
||||
elapsed_ms_since_submit = max(
|
||||
0,
|
||||
int((now - job_submitted_at_utc).total_seconds() * 1000),
|
||||
)
|
||||
durations_ms["since_playground_submit"] = elapsed_ms_since_submit
|
||||
except ValueError:
|
||||
job_submitted_at_utc = None
|
||||
elapsed_ms_since_submit = None
|
||||
return MemoirPhase1ReadyOut(
|
||||
ready=ready,
|
||||
checked_segment_ids=ids,
|
||||
pending_segment_ids=pending,
|
||||
job_submitted_at_utc=job_submitted_at_utc,
|
||||
elapsed_ms_since_submit=elapsed_ms_since_submit,
|
||||
durations_ms=durations_ms,
|
||||
)
|
||||
|
||||
async def submit_memoir_phase1_for_conversation(
|
||||
@@ -87,13 +121,24 @@ class MemoirReadinessService:
|
||||
user_id=uid,
|
||||
segment_ids=[],
|
||||
celery_task_id=None,
|
||||
submitted_at_utc=None,
|
||||
elapsed_ms=None,
|
||||
)
|
||||
t0 = time.perf_counter()
|
||||
task_id = await background_runner.flush_pending(
|
||||
uid, extra_segment_ids=segment_ids
|
||||
)
|
||||
elapsed_ms = max(0, int((time.perf_counter() - t0) * 1000))
|
||||
submitted_at = await record_phase1_job_submitted(
|
||||
cid,
|
||||
celery_task_id=task_id,
|
||||
segment_count=len(segment_ids),
|
||||
)
|
||||
return MemoirSubmitOut(
|
||||
conversation_id=cid,
|
||||
user_id=uid,
|
||||
segment_ids=segment_ids,
|
||||
celery_task_id=task_id,
|
||||
submitted_at_utc=submitted_at,
|
||||
elapsed_ms=elapsed_ms,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user