major refactor

This commit is contained in:
2026-03-27 06:02:58 -03:00
parent bcf6f3dc71
commit 51ce14a812
18 changed files with 351 additions and 523 deletions

View File

@@ -11,7 +11,7 @@ that don't belong to any stage.
from __future__ import annotations
from core.schema.serializers._common import serialize_dataclass
from core.schema.serializers.detect_pipeline import (
from core.schema.serializers.pipeline import (
deserialize_pipeline_stats,
deserialize_text_candidates,
)

View File

@@ -33,83 +33,81 @@ def create_timeline(
Returns (timeline_id, checkpoint_id).
"""
from core.db.detect import create_timeline as db_create_timeline
from core.db.detect import save_checkpoint
# Create timeline
timeline = db_create_timeline(
source_video=source_video,
profile_name=profile_name,
source_asset_id=source_asset_id,
fps=fps,
)
tid = str(timeline.id)
# Upload frames to MinIO
manifest = save_frames(tid, frames)
# Store frame metadata on the timeline
frames_meta = [
{
"sequence": f.sequence,
"chunk_id": getattr(f, "chunk_id", 0),
"timestamp": f.timestamp,
"perceptual_hash": getattr(f, "perceptual_hash", ""),
}
for f in frames
]
timeline.frames_prefix = f"{CHECKPOINT_PREFIX}/{tid}/frames/"
timeline.frames_manifest = {str(k): v for k, v in manifest.items()}
timeline.frames_meta = frames_meta
from core.db.tables import Timeline, Checkpoint
from core.db.connection import get_session
with get_session() as session:
timeline = Timeline(
source_video=source_video,
profile_name=profile_name,
source_asset_id=source_asset_id,
fps=fps,
)
session.add(timeline)
session.flush()
tid = str(timeline.id)
# Upload frames to MinIO
manifest = save_frames(tid, frames)
frames_meta = [
{
"sequence": f.sequence,
"chunk_id": getattr(f, "chunk_id", 0),
"timestamp": f.timestamp,
"perceptual_hash": getattr(f, "perceptual_hash", ""),
}
for f in frames
]
timeline.frames_prefix = f"{CHECKPOINT_PREFIX}/{tid}/frames/"
timeline.frames_manifest = {str(k): v for k, v in manifest.items()}
timeline.frames_meta = frames_meta
checkpoint = Checkpoint(
timeline_id=timeline.id,
parent_id=None,
stage_outputs={},
stats={"frames_extracted": len(frames)},
)
session.add(checkpoint)
session.commit()
session.refresh(checkpoint)
cid = str(checkpoint.id)
# Create root checkpoint (no parent, no stage outputs yet)
checkpoint = save_checkpoint(
timeline_id=timeline.id,
parent_id=None,
stage_outputs={},
stats={"frames_extracted": len(frames)},
)
logger.info("Timeline created: %s (%d frames, root checkpoint %s)",
tid, len(frames), checkpoint.id)
return tid, str(checkpoint.id)
logger.info("Timeline created: %s (%d frames, root checkpoint %s)", tid, len(frames), cid)
return tid, cid
def get_timeline_frames(timeline_id: str) -> list:
"""Load frames from a timeline (from MinIO) as Frame objects."""
from core.db.detect import get_timeline
from core.db.tables import Timeline
from core.db.connection import get_session
timeline = get_timeline(timeline_id)
with get_session() as session:
timeline = session.get(Timeline, UUID(timeline_id))
if not timeline:
raise ValueError(f"Timeline not found: {timeline_id}")
raw_manifest = timeline.frames_manifest or {}
manifest = {int(k): v for k, v in raw_manifest.items()}
frame_metadata = timeline.frames_meta or []
return load_frames(manifest, frame_metadata)
return load_frames(manifest, timeline.frames_meta or [])
def get_timeline_frames_b64(timeline_id: str) -> list[dict]:
"""Load frames as base64 JPEG (lightweight, no numpy)."""
from core.db.detect import get_timeline
from core.db.tables import Timeline
from core.db.connection import get_session
from .frames import load_frames_b64
timeline = get_timeline(timeline_id)
with get_session() as session:
timeline = session.get(Timeline, UUID(timeline_id))
if not timeline:
raise ValueError(f"Timeline not found: {timeline_id}")
raw_manifest = timeline.frames_manifest or {}
manifest = {int(k): v for k, v in raw_manifest.items()}
frame_metadata = timeline.frames_meta or []
return load_frames_b64(manifest, frame_metadata)
return load_frames_b64(manifest, timeline.frames_meta or [])
# ---------------------------------------------------------------------------
@@ -132,47 +130,46 @@ def save_stage_output(
Carries forward stage outputs from parent + adds the new one.
Returns the new checkpoint ID.
"""
from core.db.detect import get_checkpoint, save_checkpoint
from core.db.tables import Checkpoint
from core.db.connection import get_session
# Carry forward from parent
parent_outputs = {}
parent_stats = {}
parent_config = {}
if parent_checkpoint_id:
parent = get_checkpoint(parent_checkpoint_id)
if parent:
parent_outputs = dict(parent.stage_outputs or {})
parent_stats = dict(parent.stats or {})
parent_config = dict(parent.config_overrides or {})
with get_session() as session:
parent_outputs = {}
parent_stats = {}
parent_config = {}
if parent_checkpoint_id:
parent = session.get(Checkpoint, UUID(parent_checkpoint_id))
if parent:
parent_outputs = dict(parent.stage_outputs or {})
parent_stats = dict(parent.stats or {})
parent_config = dict(parent.config_overrides or {})
# Add new stage output
stage_outputs = {**parent_outputs, stage_name: output_json}
# Merge stats and config
merged_stats = {**parent_stats, **(stats or {})}
merged_config = {**parent_config, **(config_overrides or {})}
checkpoint = save_checkpoint(
timeline_id=timeline_id,
parent_id=parent_checkpoint_id,
stage_outputs=stage_outputs,
config_overrides=merged_config,
stats=merged_stats,
is_scenario=is_scenario,
scenario_label=scenario_label,
)
checkpoint = Checkpoint(
timeline_id=UUID(timeline_id),
parent_id=UUID(parent_checkpoint_id) if parent_checkpoint_id else None,
stage_outputs={**parent_outputs, stage_name: output_json},
config_overrides={**parent_config, **(config_overrides or {})},
stats={**parent_stats, **(stats or {})},
is_scenario=is_scenario,
scenario_label=scenario_label,
)
session.add(checkpoint)
session.commit()
session.refresh(checkpoint)
cid = str(checkpoint.id)
logger.info("Checkpoint saved: %s (timeline %s, stage %s, parent %s)",
checkpoint.id, timeline_id, stage_name, parent_checkpoint_id)
return str(checkpoint.id)
cid, timeline_id, stage_name, parent_checkpoint_id)
return cid
def load_stage_output(checkpoint_id: str, stage_name: str) -> dict | None:
"""Load a stage's output from a checkpoint."""
from core.db.detect import get_checkpoint
from core.db.tables import Checkpoint
from core.db.connection import get_session
checkpoint = get_checkpoint(checkpoint_id)
with get_session() as session:
checkpoint = session.get(Checkpoint, UUID(checkpoint_id))
if not checkpoint:
return None
return (checkpoint.stage_outputs or {}).get(stage_name)