@larkup/tool-video-intelligence 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +150 -0
- package/LICENSE +176 -0
- package/README.md +281 -0
- package/compose.gpu.yaml +14 -0
- package/compose.yaml +71 -0
- package/dist/agent.d.ts +131 -0
- package/dist/agent.js +2087 -0
- package/dist/brief.d.ts +2 -0
- package/dist/brief.js +37 -0
- package/dist/client.d.ts +46 -0
- package/dist/client.js +139 -0
- package/dist/contracts.d.ts +331 -0
- package/dist/contracts.js +1 -0
- package/dist/index.d.ts +87 -0
- package/dist/index.js +391 -0
- package/dist/runtime.d.ts +96 -0
- package/dist/runtime.js +592 -0
- package/dist/ui.d.ts +82 -0
- package/dist/ui.js +87 -0
- package/package.json +84 -0
- package/runtime/Dockerfile +119 -0
- package/runtime/app/__init__.py +3 -0
- package/runtime/app/__main__.py +19 -0
- package/runtime/app/api/__init__.py +0 -0
- package/runtime/app/api/deps.py +69 -0
- package/runtime/app/api/v1.py +166 -0
- package/runtime/app/config.py +78 -0
- package/runtime/app/db/__init__.py +0 -0
- package/runtime/app/db/schemas.py +162 -0
- package/runtime/app/db/store.py +466 -0
- package/runtime/app/main.py +27 -0
- package/runtime/app/model_configuration.py +157 -0
- package/runtime/app/services/__init__.py +0 -0
- package/runtime/app/services/brain.py +2221 -0
- package/runtime/app/services/embedding.py +473 -0
- package/runtime/app/services/jobs.py +237 -0
- package/runtime/app/services/motion.py +66 -0
- package/runtime/app/services/pipeline.py +1911 -0
- package/runtime/app/services/scene.py +161 -0
- package/runtime/app/services/storage.py +44 -0
- package/runtime/app/services/transcription.py +667 -0
- package/runtime/app/services/vision.py +1441 -0
- package/runtime/app/utils/__init__.py +0 -0
- package/runtime/app/utils/timing.py +99 -0
- package/runtime/app/worker.py +20 -0
- package/runtime/pyproject.toml +56 -0
- package/runtime/requirements-cpu.txt +15 -0
- package/runtime/requirements-smoke.txt +7 -0
- package/runtime/requirements.txt +14 -0
- package/runtime/uv.lock +3637 -0
- package/scripts/grant-cloud-credits.sh +43 -0
- package/scripts/runtime.mjs +156 -0
- package/scripts/validate-indexing.mjs +168 -0
- package/tool.manifest.json +617 -0
|
File without changes
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def normalized_important_ranges(
|
|
7
|
+
brief: dict[str, Any], duration_secs: float
|
|
8
|
+
) -> list[tuple[float, float]]:
|
|
9
|
+
"""Clamps a brief's requested ranges to the source duration, sorted and merged."""
|
|
10
|
+
ranges: list[tuple[float, float]] = []
|
|
11
|
+
for candidate in brief.get("importantRanges") or []:
|
|
12
|
+
try:
|
|
13
|
+
start = max(0.0, float(candidate.get("startSecs")))
|
|
14
|
+
end = min(duration_secs, float(candidate.get("endSecs")))
|
|
15
|
+
except (AttributeError, TypeError, ValueError):
|
|
16
|
+
continue
|
|
17
|
+
if end > start:
|
|
18
|
+
ranges.append((start, end))
|
|
19
|
+
ranges.sort()
|
|
20
|
+
merged: list[tuple[float, float]] = []
|
|
21
|
+
for start, end in ranges:
|
|
22
|
+
if merged and start <= merged[-1][1]:
|
|
23
|
+
merged[-1] = (merged[-1][0], max(merged[-1][1], end))
|
|
24
|
+
else:
|
|
25
|
+
merged.append((start, end))
|
|
26
|
+
return merged
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def visual_sampling_interval(mode: str, duration_seconds: float) -> float:
|
|
30
|
+
"""Seconds between sampled frames for a given indexing mode and source length."""
|
|
31
|
+
base_intervals = {"fast": 5.0, "balanced": 2.0, "thorough": 0.75}
|
|
32
|
+
max_samples = {"fast": 360, "balanced": 720, "thorough": 1_800}
|
|
33
|
+
return max(base_intervals[mode], duration_seconds / max_samples[mode])
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def rebase_result_timestamps(result: dict[str, Any], offset_secs: float) -> None:
|
|
37
|
+
"""Translates clip-relative evidence (a bounded/rebased inspection) to the source clock."""
|
|
38
|
+
offset_ms = round(offset_secs * 1_000)
|
|
39
|
+
|
|
40
|
+
def shift(item: dict[str, Any], *keys: str) -> None:
|
|
41
|
+
for key in keys:
|
|
42
|
+
if isinstance(item.get(key), (int, float)):
|
|
43
|
+
item[key] = round(float(item[key])) + offset_ms
|
|
44
|
+
|
|
45
|
+
for segment in result.get("transcript", []):
|
|
46
|
+
if not isinstance(segment, dict):
|
|
47
|
+
continue
|
|
48
|
+
shift(segment, "startMs", "endMs")
|
|
49
|
+
for word in segment.get("words", []):
|
|
50
|
+
if isinstance(word, dict):
|
|
51
|
+
shift(word, "startMs", "endMs")
|
|
52
|
+
for observation in result.get("visualObservations", []):
|
|
53
|
+
if isinstance(observation, dict):
|
|
54
|
+
shift(observation, "timeMs")
|
|
55
|
+
for track in result.get("tracks", []):
|
|
56
|
+
if isinstance(track, dict):
|
|
57
|
+
shift(track, "startMs", "endMs")
|
|
58
|
+
for overlay in result.get("recurringOverlayText", []):
|
|
59
|
+
if not isinstance(overlay, dict):
|
|
60
|
+
continue
|
|
61
|
+
shift(overlay, "firstSeenMs", "lastSeenMs")
|
|
62
|
+
if isinstance(overlay.get("timestampsMs"), list):
|
|
63
|
+
overlay["timestampsMs"] = [
|
|
64
|
+
round(float(timestamp)) + offset_ms
|
|
65
|
+
for timestamp in overlay["timestampsMs"]
|
|
66
|
+
if isinstance(timestamp, (int, float))
|
|
67
|
+
]
|
|
68
|
+
for observation in result.get("semanticObservations", []):
|
|
69
|
+
if isinstance(observation, dict):
|
|
70
|
+
shift(observation, "startMs", "endMs")
|
|
71
|
+
for embedding in result.get("videoEmbeddings", []):
|
|
72
|
+
if isinstance(embedding, dict):
|
|
73
|
+
shift(embedding, "startMs", "endMs")
|
|
74
|
+
for entity in result.get("entities", []):
|
|
75
|
+
if not isinstance(entity, dict) or not isinstance(entity.get("timestampsMs"), list):
|
|
76
|
+
continue
|
|
77
|
+
entity["timestampsMs"] = [
|
|
78
|
+
round(float(timestamp)) + offset_ms
|
|
79
|
+
for timestamp in entity["timestampsMs"]
|
|
80
|
+
if isinstance(timestamp, (int, float))
|
|
81
|
+
]
|
|
82
|
+
|
|
83
|
+
# Knowledge synthesis runs while a bounded source is still on its local
|
|
84
|
+
# clip clock. Its citations are persisted alongside raw evidence, so they
|
|
85
|
+
# must be translated by the same offset before a refinement is searchable.
|
|
86
|
+
summary = result.get("knowledgeSummary")
|
|
87
|
+
if not isinstance(summary, dict):
|
|
88
|
+
return
|
|
89
|
+
for key in ("stateHistory", "keyEvents"):
|
|
90
|
+
for item in summary.get(key, []):
|
|
91
|
+
if isinstance(item, dict):
|
|
92
|
+
shift(item, "startMs", "endMs")
|
|
93
|
+
for key in ("participants", "context"):
|
|
94
|
+
for item in summary.get(key, []):
|
|
95
|
+
if not isinstance(item, dict):
|
|
96
|
+
continue
|
|
97
|
+
for evidence in item.get("evidence", []):
|
|
98
|
+
if isinstance(evidence, dict):
|
|
99
|
+
shift(evidence, "startMs", "endMs")
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
|
|
5
|
+
from app.config import Settings
|
|
6
|
+
from app.db.store import Store
|
|
7
|
+
from app.services.jobs import JobService
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def main() -> None:
|
|
11
|
+
parser = argparse.ArgumentParser(description="Run one queued video intelligence job")
|
|
12
|
+
parser.add_argument("job_id")
|
|
13
|
+
args = parser.parse_args()
|
|
14
|
+
settings = Settings.from_env()
|
|
15
|
+
store = Store(settings.data_dir / "video-intelligence.sqlite3")
|
|
16
|
+
JobService(settings, store).run(args.job_id)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
if __name__ == "__main__":
|
|
20
|
+
main()
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=80"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "larkup-video-intelligence-runtime"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Portable FastAPI runtime for Larkup Video Intelligence"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.12,<3.14"
|
|
11
|
+
license = "Apache-2.0"
|
|
12
|
+
authors = [{ name = "Larkup" }]
|
|
13
|
+
dependencies = [
|
|
14
|
+
"boto3==1.40.1",
|
|
15
|
+
"cryptography>=46.0.7",
|
|
16
|
+
"fastapi>=0.115.6",
|
|
17
|
+
"numpy==1.26.4",
|
|
18
|
+
"opencv-python-headless==4.11.0.86",
|
|
19
|
+
"pydantic==2.11.7",
|
|
20
|
+
"python-multipart==0.0.32",
|
|
21
|
+
"requests==2.33.0",
|
|
22
|
+
"scenedetect==0.6.5",
|
|
23
|
+
"starlette>=0.49.1",
|
|
24
|
+
"uvicorn[standard]==0.35.0",
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
[project.optional-dependencies]
|
|
28
|
+
cpu = [
|
|
29
|
+
"faster-whisper==1.1.1",
|
|
30
|
+
"onnxruntime==1.22.0",
|
|
31
|
+
"pyclipper==1.4.0",
|
|
32
|
+
"rapidocr-onnxruntime==1.4.4",
|
|
33
|
+
"Shapely==2.1.2",
|
|
34
|
+
"six==1.17.0",
|
|
35
|
+
]
|
|
36
|
+
gpu = [
|
|
37
|
+
"faster-whisper==1.1.1",
|
|
38
|
+
"onnxruntime-gpu==1.22.0",
|
|
39
|
+
"paddleocr==3.0.3",
|
|
40
|
+
"rapidocr-onnxruntime==1.4.4",
|
|
41
|
+
"runpod>=1.7.14",
|
|
42
|
+
]
|
|
43
|
+
test = ["httpx==0.28.1"]
|
|
44
|
+
|
|
45
|
+
[project.scripts]
|
|
46
|
+
larkup-video-runtime = "app.__main__:main"
|
|
47
|
+
larkup-video-runtime-worker = "app.worker:main"
|
|
48
|
+
|
|
49
|
+
[project.urls]
|
|
50
|
+
Repository = "https://github.com/Larkup-AI/larkup/tree/main/packages/marketplace-tools/video-intelligence"
|
|
51
|
+
|
|
52
|
+
[tool.setuptools]
|
|
53
|
+
packages = ["app", "app.api", "app.db", "app.services", "app.utils"]
|
|
54
|
+
|
|
55
|
+
[tool.uv]
|
|
56
|
+
package = true
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
fastapi==0.116.1
|
|
2
|
+
faster-whisper==1.1.1
|
|
3
|
+
numpy==1.26.4
|
|
4
|
+
onnxruntime==1.22.0
|
|
5
|
+
opencv-python-headless==4.11.0.86
|
|
6
|
+
Pillow==12.3.0
|
|
7
|
+
pyclipper==1.4.0
|
|
8
|
+
pydantic==2.11.7
|
|
9
|
+
python-multipart==0.0.32
|
|
10
|
+
PyYAML==6.0.3
|
|
11
|
+
requests==2.33.0
|
|
12
|
+
scenedetect==0.6.5
|
|
13
|
+
Shapely==2.1.2
|
|
14
|
+
six==1.17.0
|
|
15
|
+
uvicorn[standard]==0.35.0
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
boto3==1.40.1
|
|
2
|
+
fastapi==0.116.1
|
|
3
|
+
faster-whisper==1.1.1
|
|
4
|
+
numpy==1.26.4
|
|
5
|
+
onnxruntime-gpu==1.22.0
|
|
6
|
+
opencv-python-headless==4.11.0.86
|
|
7
|
+
paddleocr==3.0.3
|
|
8
|
+
pydantic==2.11.7
|
|
9
|
+
rapidocr-onnxruntime==1.4.4
|
|
10
|
+
python-multipart==0.0.32
|
|
11
|
+
requests==2.33.0
|
|
12
|
+
runpod==1.7.10
|
|
13
|
+
scenedetect==0.6.5
|
|
14
|
+
uvicorn[standard]==0.35.0
|