Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 6 additions & 9 deletions src/vidxp/capabilities/actor/definition.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,21 +32,18 @@
)
from vidxp.capabilities.visual import index_capabilities
from vidxp.core.contracts import IndexConfig, VideoSource
from vidxp.core.indexing_common import ProgressCallback
from vidxp.core.indexing_common import ProgressCallback, report_preparation


def prepare_models(
context: PreparationContext,
progress: ProgressCallback | None,
) -> tuple[str, ...]:
if progress is not None:
progress(
{
"state": "preparing",
"stage": "actor_models",
"message": "Preparing OpenCV Zoo YuNet and SFace models.",
}
)
report_preparation(
progress,
"actor_models",
"Preparing OpenCV Zoo YuNet and SFace models.",
)
get_actor_models(context.runtime, download=True, progress=progress)
return (YUNET_MODEL.filename, SFACE_MODEL.filename)

Expand Down
14 changes: 6 additions & 8 deletions src/vidxp/capabilities/actor/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from typing import Any, Callable

from vidxp.ports import ModelRuntimePort
from vidxp.core.indexing_common import report_preparation
from vidxp.capabilities.actor.specs import (
SFACE_MODEL,
YUNET_MODEL,
Expand Down Expand Up @@ -38,14 +39,11 @@ def load() -> ActorModels:
download=download,
progress=progress,
)
if progress is not None:
progress(
{
"state": "preparing",
"stage": "loading_model",
"message": "Loading OpenCV Zoo YuNet and SFace models.",
}
)
report_preparation(
progress,
"loading_model",
"Loading OpenCV Zoo YuNet and SFace models.",
)
models = ActorModels(
detector=cv2.FaceDetectorYN.create(
str(detector_path),
Expand Down
11 changes: 2 additions & 9 deletions src/vidxp/capabilities/dialogue/definition.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
)
from vidxp.capabilities.schemas import SearchInput, SearchResult
from vidxp.core.contracts import IndexConfig, VideoSource
from vidxp.core.indexing_common import ProgressCallback
from vidxp.core.indexing_common import ProgressCallback, report_preparation
def filter_requirements_for_source(
source: VideoSource,
requirements: tuple[Requirement, ...],
Expand All @@ -49,14 +49,7 @@ def prepare_models(
prepared = []

def report(stage: str, message: str) -> None:
if progress is not None:
progress(
{
"state": "preparing",
"stage": stage,
"message": message,
}
)
report_preparation(progress, stage, message)

report(
"dialogue_model",
Expand Down
31 changes: 11 additions & 20 deletions src/vidxp/capabilities/dialogue/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from typing import Any, Callable

from vidxp.ports import ModelRuntimePort
from vidxp.core.indexing_common import report_preparation
from vidxp.model_contracts import loaded_compute_precision
from vidxp.capabilities.dialogue.specs import (
FASTER_WHISPER_MODEL,
Expand All @@ -28,16 +29,11 @@ def load() -> Any:
download=download,
progress=progress,
)
if progress is not None:
progress(
{
"state": "preparing",
"stage": "loading_model",
"message": (
f"Loading {QWEN3_EMBEDDING_MODEL.model_id}."
),
}
)
report_preparation(
progress,
"loading_model",
f"Loading {QWEN3_EMBEDDING_MODEL.model_id}.",
)
model = SentenceTransformer(
str(snapshot),
device=device,
Expand Down Expand Up @@ -74,16 +70,11 @@ def load() -> Any:
download=download,
progress=progress,
)
if progress is not None:
progress(
{
"state": "preparing",
"stage": "loading_model",
"message": (
f"Loading {FASTER_WHISPER_MODEL.model_id}."
),
}
)
report_preparation(
progress,
"loading_model",
f"Loading {FASTER_WHISPER_MODEL.model_id}.",
)
model = WhisperModel(
str(snapshot),
device=device.split(":", 1)[0],
Expand Down
17 changes: 6 additions & 11 deletions src/vidxp/capabilities/scene/definition.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,23 +19,18 @@
from vidxp.capabilities.schemas import SearchInput, SearchResult
from vidxp.capabilities.visual import index_capabilities
from vidxp.core.contracts import IndexConfig, VideoSource
from vidxp.core.indexing_common import ProgressCallback
from vidxp.core.indexing_common import ProgressCallback, report_preparation

def prepare_models(
context: PreparationContext,
progress: ProgressCallback | None,
) -> tuple[str, ...]:
SceneConfig.model_validate(context.settings)
if progress is not None:
progress(
{
"state": "preparing",
"stage": "scene_model",
"message": (
f"Preparing scene model: SigLIP2 {SIGLIP2_MODEL.model_id}"
),
}
)
report_preparation(
progress,
"scene_model",
f"Preparing scene model: SigLIP2 {SIGLIP2_MODEL.model_id}",
)
get_scene_model(context.runtime, download=True, progress=progress)
return (SIGLIP2_MODEL.model_id,)

Expand Down
14 changes: 6 additions & 8 deletions src/vidxp/capabilities/scene/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from typing import Any, Callable

from vidxp.ports import ModelRuntimePort
from vidxp.core.indexing_common import report_preparation
from vidxp.model_contracts import loaded_compute_precision
from vidxp.capabilities.scene.specs import SIGLIP2_MODEL

Expand Down Expand Up @@ -32,14 +33,11 @@ def load() -> SceneModel:
download=download,
progress=progress,
)
if progress is not None:
progress(
{
"state": "preparing",
"stage": "loading_model",
"message": f"Loading {SIGLIP2_MODEL.model_id}.",
}
)
report_preparation(
progress,
"loading_model",
f"Loading {SIGLIP2_MODEL.model_id}.",
)
common = {
"cache_dir": str(runtime.model_cache),
"local_files_only": True,
Expand Down
22 changes: 22 additions & 0 deletions src/vidxp/core/indexing_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,28 @@
ProgressCallback = Callable[[dict[str, Any]], None]


def report_preparation(
callback: ProgressCallback | None,
stage: str,
message: str,
*,
current: int | None = None,
total: int | None = None,
) -> None:
if callback is None:
return
event: dict[str, Any] = {
"state": "preparing",
"stage": stage,
"message": message,
}
if current is not None:
event["current"] = current
if total is not None:
event["total"] = total
callback(event)


def report_progress(
callback: ProgressCallback | None,
stage: str,
Expand Down
22 changes: 21 additions & 1 deletion src/vidxp/model_contracts.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from __future__ import annotations

from dataclasses import dataclass
import hashlib
from pathlib import Path
import re
from typing import Any
Expand Down Expand Up @@ -174,4 +175,23 @@ def model_artifact_cached(
cache: Path,
spec: ModelSpec | ArtifactSpec,
) -> bool:
return model_artifact_path(cache, spec).is_file()
return model_artifact_valid(model_artifact_path(cache, spec), spec)


def model_artifact_valid(
path: Path,
spec: ModelSpec | ArtifactSpec,
) -> bool:
if not path.is_file():
return False
expected = (
spec.weights_sha256 if isinstance(spec, ModelSpec) else spec.sha256
)
digest = hashlib.sha256()
try:
with path.open("rb") as stream:
for block in iter(lambda: stream.read(1024 * 1024), b""):
digest.update(block)
except OSError:
return False
return digest.hexdigest() == expected
Loading