File size: 1,957 Bytes
33962ff | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | """Embed every segment under every view and write data/index/<view>.npy (N x 768, float32, unit-norm)."""
import json
import time
from datetime import datetime, timezone
import numpy as np
from rich.console import Console
from .config import EMBED_MODEL, INDEX_DIR, VIEWS
from .embedder import encode_view
from .segment import transcript_engine
console = Console()
BATCH = {"text": 32, "frame": 16, "audio": 16, "video": 4, "fused": 4}
def run(views: list[str] | None = None, force: bool = False) -> None:
segs = json.loads((INDEX_DIR / "segments.json").read_text())
manifest_path = INDEX_DIR / "manifest.json"
manifest = json.loads(manifest_path.read_text()) if manifest_path.exists() else {"views": {}}
for view in views or list(VIEWS):
out = INDEX_DIR / f"{view}.npy"
if out.exists() and not force and np.load(out).shape[0] == len(segs):
console.print(f" {view}: cached")
continue
console.print(f"[bold]Embedding view '{view}'[/bold] for {len(segs)} segments")
t0 = time.time()
embs = encode_view(view, segs, BATCH[view])
elapsed = time.time() - t0
if not np.isfinite(embs).all():
raise RuntimeError(f"non-finite embeddings in view {view}")
np.save(out, embs)
manifest["views"][view] = {
"description": VIEWS[view],
"count": int(embs.shape[0]),
"dim": int(embs.shape[1]),
"seconds": round(elapsed, 1),
"ms_per_segment": round(1000 * elapsed / len(segs), 1),
}
console.print(f" {view}: {embs.shape} in {elapsed:.0f}s")
manifest.update({
"model": EMBED_MODEL,
"segments": len(segs),
"videos": len({s["video_id"] for s in segs}),
"transcripts": transcript_engine(),
"built_at": datetime.now(timezone.utc).isoformat(timespec="seconds"),
})
manifest_path.write_text(json.dumps(manifest, indent=2))
|