- circuitforge_core.signal_bus: SignalBus/SignalEvent generic pub-sub over
Server-Sent Events for real-time signal streams (thread-safe publish,
bounded per-subscriber queues with drop-oldest overflow, keepalive).
New `signal-bus` extra.
- circuitforge_core.video.app: POST /caption/upload accepts a multipart
video file for callers without filesystem access to the video-service
node; added test coverage. video-service extra now pulls in
python-multipart, required by UploadFile parsing.
- docs: mkdocs.yml blue-grey/cyan palette now wired to a central
docs/stylesheets/theme.css for consistent theme-aware styling.
- Bump to 0.22.0; update README module table/install line and CHANGELOG.
Closes: #58
Add the circuitforge_core.video package implementing the cf-video inference
service managed by cf-orch.
Service endpoints:
GET /health — liveness check; model name + VRAM
POST /caption — dense scene description + timestamped event list
POST /find — temporal grounding of a natural-language event query
Backend hierarchy:
VideoBackend (Protocol)
MarlinBackend — NemoStation/Marlin-2B via transformers>=5.7.0
MockVideoBackend — deterministic stub; no GPU required
Pydantic request/response models enforce parameter bounds at the API
boundary (max_new_tokens ge/le, event min_length=1). Span is serialized
as list[float] | None for JSON compatibility.
MarlinBackend loads eagerly in __init__ so cf-orch's 2-second liveness
poll catches load failures immediately. FORCE_QWENVL_VIDEO_READER env var
defaults to torchcodec (faster than av path) before transformers import.
pyproject.toml extras:
video-marlin — torch, transformers, torchcodec, qwen-vl-utils, av, Pillow
video-service — video-marlin + fastapi + uvicorn
Test coverage: 46 tests across test_mock_backend.py and test_app.py.
All passing without GPU or real video file.
Closes: #71