hugpy-video 0.2.3__tar.gz
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.
- hugpy_video-0.2.3/LICENSE +41 -0
- hugpy_video-0.2.3/PKG-INFO +104 -0
- hugpy_video-0.2.3/README.md +53 -0
- hugpy_video-0.2.3/pyproject.toml +81 -0
- hugpy_video-0.2.3/setup.cfg +4 -0
- hugpy_video-0.2.3/src/hugpy_video/__init__.py +33 -0
- hugpy_video-0.2.3/src/hugpy_video/chat_video/__init__.py +1 -0
- hugpy_video-0.2.3/src/hugpy_video/chat_video/video_analyzer.py +174 -0
- hugpy_video-0.2.3/src/hugpy_video/cli.py +130 -0
- hugpy_video-0.2.3/src/hugpy_video/config.py +79 -0
- hugpy_video-0.2.3/src/hugpy_video/hooks.py +152 -0
- hugpy_video-0.2.3/src/hugpy_video/intel/__init__.py +10 -0
- hugpy_video-0.2.3/src/hugpy_video/intel/audio_schema.py +44 -0
- hugpy_video-0.2.3/src/hugpy_video/intel/chains.py +174 -0
- hugpy_video-0.2.3/src/hugpy_video/intel/crop_schema.py +53 -0
- hugpy_video-0.2.3/src/hugpy_video/intel/ffprobe.py +28 -0
- hugpy_video-0.2.3/src/hugpy_video/intel/frame_schema.py +81 -0
- hugpy_video-0.2.3/src/hugpy_video/intel/gen_schema.py +132 -0
- hugpy_video-0.2.3/src/hugpy_video/intel/identity_from_video_schema.py +118 -0
- hugpy_video-0.2.3/src/hugpy_video/intel/identity_profiles.py +2182 -0
- hugpy_video-0.2.3/src/hugpy_video/intel/identity_reconstruction_schema.py +567 -0
- hugpy_video-0.2.3/src/hugpy_video/intel/identity_video_extract_schema.py +180 -0
- hugpy_video-0.2.3/src/hugpy_video/intel/job_bridge.py +345 -0
- hugpy_video-0.2.3/src/hugpy_video/intel/job_lifecycle.py +1188 -0
- hugpy_video-0.2.3/src/hugpy_video/intel/job_schema.py +149 -0
- hugpy_video-0.2.3/src/hugpy_video/intel/media_bus.py +2496 -0
- hugpy_video-0.2.3/src/hugpy_video/intel/media_schema.py +98 -0
- hugpy_video-0.2.3/src/hugpy_video/intel/media_store.py +216 -0
- hugpy_video-0.2.3/src/hugpy_video/intel/mlt_render_schema.py +215 -0
- hugpy_video-0.2.3/src/hugpy_video/intel/movie_schema.py +325 -0
- hugpy_video-0.2.3/src/hugpy_video/intel/net.py +16 -0
- hugpy_video-0.2.3/src/hugpy_video/intel/placement.py +203 -0
- hugpy_video-0.2.3/src/hugpy_video/intel/plane.py +39 -0
- hugpy_video-0.2.3/src/hugpy_video/intel/presets.py +645 -0
- hugpy_video-0.2.3/src/hugpy_video/intel/prompt_intent.py +234 -0
- hugpy_video-0.2.3/src/hugpy_video/intel/prompt_seeds.py +256 -0
- hugpy_video-0.2.3/src/hugpy_video/intel/prompt_spread.py +880 -0
- hugpy_video-0.2.3/src/hugpy_video/intel/reservation/__init__.py +67 -0
- hugpy_video-0.2.3/src/hugpy_video/intel/reservation/engine.py +832 -0
- hugpy_video-0.2.3/src/hugpy_video/intel/reservation/registry.py +348 -0
- hugpy_video-0.2.3/src/hugpy_video/intel/reservation/templates.py +467 -0
- hugpy_video-0.2.3/src/hugpy_video/intel/result_schema.py +67 -0
- hugpy_video-0.2.3/src/hugpy_video/intel/runners/__init__.py +81 -0
- hugpy_video-0.2.3/src/hugpy_video/intel/runners/_gpu_guard.py +119 -0
- hugpy_video-0.2.3/src/hugpy_video/intel/runners/_img2img.py +137 -0
- hugpy_video-0.2.3/src/hugpy_video/intel/runners/ffmpeg_audio.py +96 -0
- hugpy_video-0.2.3/src/hugpy_video/intel/runners/ffmpeg_crop.py +120 -0
- hugpy_video-0.2.3/src/hugpy_video/intel/runners/ffmpeg_frames.py +148 -0
- hugpy_video-0.2.3/src/hugpy_video/intel/runners/identity_from_video.py +386 -0
- hugpy_video-0.2.3/src/hugpy_video/intel/runners/identity_mesh.py +221 -0
- hugpy_video-0.2.3/src/hugpy_video/intel/runners/identity_reconstruction.py +337 -0
- hugpy_video-0.2.3/src/hugpy_video/intel/runners/identity_render_client.py +298 -0
- hugpy_video-0.2.3/src/hugpy_video/intel/runners/identity_render_relay.py +657 -0
- hugpy_video-0.2.3/src/hugpy_video/intel/runners/identity_video_extract_relay.py +439 -0
- hugpy_video-0.2.3/src/hugpy_video/intel/runners/imagegen.py +293 -0
- hugpy_video-0.2.3/src/hugpy_video/intel/runners/mlt_render.py +472 -0
- hugpy_video-0.2.3/src/hugpy_video/intel/runners/movie.py +622 -0
- hugpy_video-0.2.3/src/hugpy_video/intel/runners/scene.py +693 -0
- hugpy_video-0.2.3/src/hugpy_video/intel/runners/sidecar.py +28 -0
- hugpy_video-0.2.3/src/hugpy_video/intel/runners/studio_i2v.py +1116 -0
- hugpy_video-0.2.3/src/hugpy_video/intel/runners/studio_movie.py +1348 -0
- hugpy_video-0.2.3/src/hugpy_video/intel/runners/studio_placement.py +221 -0
- hugpy_video-0.2.3/src/hugpy_video/intel/runners/studio_tester.py +26 -0
- hugpy_video-0.2.3/src/hugpy_video/intel/runners/tts_chatterbox.py +85 -0
- hugpy_video-0.2.3/src/hugpy_video/intel/scene_schema.py +161 -0
- hugpy_video-0.2.3/src/hugpy_video/intel/shot_intent.py +127 -0
- hugpy_video-0.2.3/src/hugpy_video/intel/studio/__init__.py +114 -0
- hugpy_video-0.2.3/src/hugpy_video/intel/studio/artifacts.py +25 -0
- hugpy_video-0.2.3/src/hugpy_video/intel/studio/editor_handoff.py +171 -0
- hugpy_video-0.2.3/src/hugpy_video/intel/studio/enums.py +173 -0
- hugpy_video-0.2.3/src/hugpy_video/intel/studio/env.py +77 -0
- hugpy_video-0.2.3/src/hugpy_video/intel/studio/errors.py +170 -0
- hugpy_video-0.2.3/src/hugpy_video/intel/studio/job.py +469 -0
- hugpy_video-0.2.3/src/hugpy_video/intel/studio/manifest.py +567 -0
- hugpy_video-0.2.3/src/hugpy_video/intel/studio/models_seed.py +538 -0
- hugpy_video-0.2.3/src/hugpy_video/intel/studio/movie_plan.py +314 -0
- hugpy_video-0.2.3/src/hugpy_video/intel/studio/presence.py +97 -0
- hugpy_video-0.2.3/src/hugpy_video/intel/studio/presets.py +707 -0
- hugpy_video-0.2.3/src/hugpy_video/intel/studio/produce.py +355 -0
- hugpy_video-0.2.3/src/hugpy_video/intel/studio/registry.py +283 -0
- hugpy_video-0.2.3/src/hugpy_video/intel/studio/router.py +486 -0
- hugpy_video-0.2.3/src/hugpy_video/intel/studio/runners/__init__.py +20 -0
- hugpy_video-0.2.3/src/hugpy_video/intel/studio/runners/cancel.py +18 -0
- hugpy_video-0.2.3/src/hugpy_video/intel/studio/runners/ffmpeg_enhance.py +362 -0
- hugpy_video-0.2.3/src/hugpy_video/intel/studio/runners/ltx_upscale.py +134 -0
- hugpy_video-0.2.3/src/hugpy_video/intel/studio/runners/rife_interpolate.py +117 -0
- hugpy_video-0.2.3/src/hugpy_video/intel/studio/runners/source.py +10 -0
- hugpy_video-0.2.3/src/hugpy_video/intel/studio/runners/synthetic.py +524 -0
- hugpy_video-0.2.3/src/hugpy_video/intel/studio/runners/wan_i2v.py +2026 -0
- hugpy_video-0.2.3/src/hugpy_video/intel/studio/runners/wan_t2v.py +61 -0
- hugpy_video-0.2.3/src/hugpy_video/intel/studio/runners/wan_vace.py +696 -0
- hugpy_video-0.2.3/src/hugpy_video/intel/studio/schemas.py +515 -0
- hugpy_video-0.2.3/src/hugpy_video/intel/studio/storage.py +40 -0
- hugpy_video-0.2.3/src/hugpy_video/intel/studio/tester.py +542 -0
- hugpy_video-0.2.3/src/hugpy_video/intel/studio_movie_schema.py +699 -0
- hugpy_video-0.2.3/src/hugpy_video/intel/studio_presets.py +512 -0
- hugpy_video-0.2.3/src/hugpy_video/jobs.py +74 -0
- hugpy_video-0.2.3/src/hugpy_video/plugin.py +61 -0
- hugpy_video-0.2.3/src/hugpy_video/plugin_builders.py +54 -0
- hugpy_video-0.2.3/src/hugpy_video/py.typed +0 -0
- hugpy_video-0.2.3/src/hugpy_video/schemas/__init__.py +0 -0
- hugpy_video-0.2.3/src/hugpy_video/schemas/video_schemas.py +45 -0
- hugpy_video-0.2.3/src/hugpy_video/selftest.py +92 -0
- hugpy_video-0.2.3/src/hugpy_video/state.py +111 -0
- hugpy_video-0.2.3/src/hugpy_video/studio_assist_log.py +434 -0
- hugpy_video-0.2.3/src/hugpy_video/video_gen/__init__.py +9 -0
- hugpy_video-0.2.3/src/hugpy_video/video_gen/schemas.py +71 -0
- hugpy_video-0.2.3/src/hugpy_video/video_gen/video_gen_runner.py +100 -0
- hugpy_video-0.2.3/src/hugpy_video.egg-info/PKG-INFO +104 -0
- hugpy_video-0.2.3/src/hugpy_video.egg-info/SOURCES.txt +163 -0
- hugpy_video-0.2.3/src/hugpy_video.egg-info/dependency_links.txt +1 -0
- hugpy_video-0.2.3/src/hugpy_video.egg-info/entry_points.txt +5 -0
- hugpy_video-0.2.3/src/hugpy_video.egg-info/requires.txt +30 -0
- hugpy_video-0.2.3/src/hugpy_video.egg-info/scm_file_list.json +159 -0
- hugpy_video-0.2.3/src/hugpy_video.egg-info/scm_version.json +8 -0
- hugpy_video-0.2.3/src/hugpy_video.egg-info/top_level.txt +1 -0
- hugpy_video-0.2.3/tests/conftest.py +59 -0
- hugpy_video-0.2.3/tests/test_cli.py +60 -0
- hugpy_video-0.2.3/tests/test_clip_length.py +632 -0
- hugpy_video-0.2.3/tests/test_generate_image_worker_jail_ingest.py +255 -0
- hugpy_video-0.2.3/tests/test_hooks.py +83 -0
- hugpy_video-0.2.3/tests/test_id_lock_never_silently_drops.py +101 -0
- hugpy_video-0.2.3/tests/test_identity_cleanup_prompt_schema.py +136 -0
- hugpy_video-0.2.3/tests/test_identity_cleanup_prompt_wiring.py +237 -0
- hugpy_video-0.2.3/tests/test_identity_video_extract_schema.py +203 -0
- hugpy_video-0.2.3/tests/test_import_policy.py +108 -0
- hugpy_video-0.2.3/tests/test_invariants_conformance.py +404 -0
- hugpy_video-0.2.3/tests/test_job_lifecycle.py +638 -0
- hugpy_video-0.2.3/tests/test_jobs_api.py +89 -0
- hugpy_video-0.2.3/tests/test_k120_continuity_refresh.py +164 -0
- hugpy_video-0.2.3/tests/test_media_bus_fork_reset.py +85 -0
- hugpy_video-0.2.3/tests/test_media_bus_reaper.py +328 -0
- hugpy_video-0.2.3/tests/test_media_bus_stale_handle.py +71 -0
- hugpy_video-0.2.3/tests/test_media_placement.py +166 -0
- hugpy_video-0.2.3/tests/test_media_stage_timeline.py +232 -0
- hugpy_video-0.2.3/tests/test_model_battery.py +396 -0
- hugpy_video-0.2.3/tests/test_movie_presets_selftest.py +74 -0
- hugpy_video-0.2.3/tests/test_placement_need.py +730 -0
- hugpy_video-0.2.3/tests/test_plugin.py +40 -0
- hugpy_video-0.2.3/tests/test_presets.py +876 -0
- hugpy_video-0.2.3/tests/test_prompt_seeds.py +128 -0
- hugpy_video-0.2.3/tests/test_refuse_not_route.py +516 -0
- hugpy_video-0.2.3/tests/test_reservation_admission.py +386 -0
- hugpy_video-0.2.3/tests/test_reservation_engine.py +228 -0
- hugpy_video-0.2.3/tests/test_reservation_media_bus.py +129 -0
- hugpy_video-0.2.3/tests/test_reservation_registry.py +130 -0
- hugpy_video-0.2.3/tests/test_reservation_templates.py +128 -0
- hugpy_video-0.2.3/tests/test_studio_assist_log.py +131 -0
- hugpy_video-0.2.3/tests/test_studio_cancel.py +282 -0
- hugpy_video-0.2.3/tests/test_studio_conformance.py +773 -0
- hugpy_video-0.2.3/tests/test_studio_device_budget.py +112 -0
- hugpy_video-0.2.3/tests/test_studio_hot_weights.py +214 -0
- hugpy_video-0.2.3/tests/test_studio_movie_offload.py +544 -0
- hugpy_video-0.2.3/tests/test_studio_placement.py +287 -0
- hugpy_video-0.2.3/tests/test_studio_prompt.py +322 -0
- hugpy_video-0.2.3/tests/test_studio_runner_gate.py +339 -0
- hugpy_video-0.2.3/tests/test_studio_sampler_defaults.py +535 -0
- hugpy_video-0.2.3/tests/test_studio_t2v.py +401 -0
- hugpy_video-0.2.3/tests/test_tester_settings.py +51 -0
- hugpy_video-0.2.3/tests/test_video_gen_runner.py +73 -0
- hugpy_video-0.2.3/tests/test_video_legacy_chain.py +157 -0
- hugpy_video-0.2.3/tests/test_video_movie.py +624 -0
- hugpy_video-0.2.3/tests/test_video_progress_archive.py +337 -0
- hugpy_video-0.2.3/tests/test_video_start_image_required.py +195 -0
- hugpy_video-0.2.3/tests/test_wan_i2v_shape_incident.py +381 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
hugpy — Source-Available License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 putkoff (hugpy.ai). All rights reserved.
|
|
4
|
+
|
|
5
|
+
Permission is granted, free of charge, to use this software ("hugpy") for
|
|
6
|
+
personal and non-commercial purposes, and for time-limited commercial
|
|
7
|
+
evaluation, subject to the following conditions:
|
|
8
|
+
|
|
9
|
+
1. Non-commercial use means use by an individual for personal purposes, or
|
|
10
|
+
use by a non-profit or educational institution for its own internal
|
|
11
|
+
purposes. Any use by, for, or on behalf of a for-profit business or in
|
|
12
|
+
connection with revenue-generating activity is commercial use — including
|
|
13
|
+
internal business use, use in producing goods or services, and use on
|
|
14
|
+
paid engagements.
|
|
15
|
+
|
|
16
|
+
2. Commercial use requires a commercial license from the copyright holder.
|
|
17
|
+
Exception: a business may evaluate the software internally for up to
|
|
18
|
+
thirty (30) days free of charge; continued use after that requires a
|
|
19
|
+
commercial license.
|
|
20
|
+
|
|
21
|
+
3. Redistribution of this software, in source or binary form, modified or
|
|
22
|
+
unmodified, is not permitted without prior written permission from the
|
|
23
|
+
copyright holder. Downloading the software from an official distribution
|
|
24
|
+
channel (PyPI, npm, hugpy.ai) is not redistribution.
|
|
25
|
+
|
|
26
|
+
4. Modification for personal use or internal evaluation is permitted;
|
|
27
|
+
distribution of modified versions is not.
|
|
28
|
+
|
|
29
|
+
5. This notice must be retained in all copies or substantial portions of
|
|
30
|
+
the software.
|
|
31
|
+
|
|
32
|
+
6. Any use outside these terms automatically terminates this license.
|
|
33
|
+
|
|
34
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
35
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
36
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
37
|
+
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY ARISING
|
|
38
|
+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
39
|
+
IN THE SOFTWARE.
|
|
40
|
+
|
|
41
|
+
For commercial licensing or redistribution permission: https://hugpy.ai
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: hugpy-video
|
|
3
|
+
Version: 0.2.3
|
|
4
|
+
Summary: Hugpy video: media library and job bus, ffmpeg, synthetic and studio runners, reservations and studio sessions for video intelligence
|
|
5
|
+
Author-email: putkoff <support@hugpy.ai>
|
|
6
|
+
License-Expression: LicenseRef-Proprietary
|
|
7
|
+
Project-URL: Homepage, https://hugpy.ai
|
|
8
|
+
Project-URL: Documentation, https://github.com/hugpy/hugpy/blob/main/py/cinema/hugpy_video/README.md
|
|
9
|
+
Project-URL: Repository, https://github.com/hugpy/hugpy
|
|
10
|
+
Project-URL: Source, https://github.com/hugpy/hugpy/tree/main/py/cinema/hugpy_video
|
|
11
|
+
Project-URL: Issues, https://github.com/hugpy/hugpy/issues
|
|
12
|
+
Project-URL: Changelog, https://github.com/hugpy/hugpy/releases
|
|
13
|
+
Project-URL: Architecture, https://github.com/hugpy/hugpy/blob/main/PARTITION.md
|
|
14
|
+
Keywords: hugpy,llm,self-hosted,video,ffmpeg,video-generation,media-intelligence
|
|
15
|
+
Classifier: Development Status :: 3 - Alpha
|
|
16
|
+
Classifier: Intended Audience :: Developers
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
20
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
21
|
+
Requires-Python: >=3.10
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
License-File: LICENSE
|
|
24
|
+
Requires-Dist: hugpy-platform
|
|
25
|
+
Requires-Dist: hugpy-control
|
|
26
|
+
Requires-Dist: hugpy-engine
|
|
27
|
+
Requires-Dist: hugpy-media
|
|
28
|
+
Requires-Dist: pydantic>=2
|
|
29
|
+
Requires-Dist: abstract-essentials
|
|
30
|
+
Provides-Extra: render
|
|
31
|
+
Requires-Dist: numpy; extra == "render"
|
|
32
|
+
Requires-Dist: Pillow; extra == "render"
|
|
33
|
+
Requires-Dist: requests; extra == "render"
|
|
34
|
+
Provides-Extra: studio
|
|
35
|
+
Requires-Dist: torch; extra == "studio"
|
|
36
|
+
Requires-Dist: diffusers; extra == "studio"
|
|
37
|
+
Requires-Dist: transformers; extra == "studio"
|
|
38
|
+
Requires-Dist: numpy; extra == "studio"
|
|
39
|
+
Requires-Dist: Pillow; extra == "studio"
|
|
40
|
+
Requires-Dist: opencv-python-headless; extra == "studio"
|
|
41
|
+
Requires-Dist: imageio; extra == "studio"
|
|
42
|
+
Requires-Dist: imageio-ffmpeg; extra == "studio"
|
|
43
|
+
Requires-Dist: requests; extra == "studio"
|
|
44
|
+
Provides-Extra: identity
|
|
45
|
+
Requires-Dist: abstract-identity; extra == "identity"
|
|
46
|
+
Requires-Dist: requests; extra == "identity"
|
|
47
|
+
Provides-Extra: test
|
|
48
|
+
Requires-Dist: pytest>=8; extra == "test"
|
|
49
|
+
Requires-Dist: pytest-timeout; extra == "test"
|
|
50
|
+
Dynamic: license-file
|
|
51
|
+
|
|
52
|
+
# hugpy-video
|
|
53
|
+
|
|
54
|
+
Execution and artifacts for Hugpy video work, extracted from the monolith's
|
|
55
|
+
`abstract_hugpy_dev.video_intel` (+ `managers/video`, `managers/video_gen`,
|
|
56
|
+
`comms/studio_assist_log`). Import name `hugpy_video`.
|
|
57
|
+
|
|
58
|
+
**Status: extracted.** Runtime code lives here; the package imports with the
|
|
59
|
+
monolith blocked and with no optional stack (torch/diffusers/ffmpeg bindings)
|
|
60
|
+
installed. Allowed ecosystem imports: `hugpy_platform`, `hugpy_control`,
|
|
61
|
+
`hugpy_engine`, `hugpy_media`. Never `hugpy_oracle`, `hugpy_fleet`,
|
|
62
|
+
`hugpy_server` (see `py/partition.toml`, `PARTITION.md`).
|
|
63
|
+
|
|
64
|
+
## Layout
|
|
65
|
+
- `intel/` — media references + jail (`media_store`), sqlite media job bus
|
|
66
|
+
(`media_bus`) and its control-plane bridge (`job_bridge`), frozen job specs
|
|
67
|
+
and registry (`job_schema`), presets, GPU reservation engine
|
|
68
|
+
(`reservation/`), runners (ffmpeg, diffusers plane, studio, identity relays,
|
|
69
|
+
MLT), the studio spine (`studio/`).
|
|
70
|
+
- `chat_video/` — chat-side frame analysis over a `hugpy_media` vision runner.
|
|
71
|
+
- `video_gen/` — `StudioVideoRunner` / `VideoGenRequest`: the engine task
|
|
72
|
+
runner for text-to-video / image-to-video.
|
|
73
|
+
- `plugin.py` — `hugpy_engine.tasks` entry point (`hugpy_video.plugin:register`);
|
|
74
|
+
import-light, runner resolved lazily.
|
|
75
|
+
- `hooks.py` — `PromptCoordinator` protocol (oracle installs its implementation;
|
|
76
|
+
no-op default). `jobs.py` — public bus API incl. `register_job` for
|
|
77
|
+
upper-layer jobs (the oracle's `video_performance`).
|
|
78
|
+
- `state.py` — injectable state roots (`HUGPY_VIDEO_STATE_DIR`,
|
|
79
|
+
`HUGPY_MEDIA_JOBS_DB`, `HUGPY_RESERVATIONS_DB`, platform storage roots).
|
|
80
|
+
- `intel/plane.py` — the one seam onto the engine's `execute_prompt`.
|
|
81
|
+
- `cli.py` — `hugpy-video --help | jobs list | jobs registry | selftest | state`.
|
|
82
|
+
- `hugpy-video models audit [--json]` inventories every declared model's
|
|
83
|
+
runner gaps, weight pin and minimum VRAM without loading weights.
|
|
84
|
+
- `config.py` — canonical `HUGPY_API_KEY` / `HUGPY_BASE` loader.
|
|
85
|
+
|
|
86
|
+
Extras: `render` (numpy/Pillow/requests), `studio` (torch/diffusers zoo),
|
|
87
|
+
`identity` (abstract-identity client), `test`.
|
|
88
|
+
|
|
89
|
+
The `identity` extra installs the separate `abstract-identity` distribution.
|
|
90
|
+
The central video runner relays mesh and char360 jobs to its service using
|
|
91
|
+
`IDENTITY_RENDER_URL` and `IDENTITY_RENDER_TOKEN`; installing the extra alone
|
|
92
|
+
does not start a GPU service. Studio model sweeps use
|
|
93
|
+
`POST /video/studio/tester`; each attempt records its model, input image,
|
|
94
|
+
sampler settings, frame count and negative prompt in the battery log.
|
|
95
|
+
|
|
96
|
+
## Rules (inherited from char360 / hugpy-agent)
|
|
97
|
+
- Heavy imports lazy — must import on a CPU-only box.
|
|
98
|
+
- Version single-sourced from pyproject (importlib.metadata; no hardcoded `__version__`).
|
|
99
|
+
- Credentials only via `hugpy_video.config.load_config` — canonical names
|
|
100
|
+
`HUGPY_API_KEY` / `HUGPY_BASE`; legacy aliases accepted but recorded in
|
|
101
|
+
`Config.deprecations`.
|
|
102
|
+
- Build: `python -m build`; publish = copy dists (console dir needs the sha256
|
|
103
|
+
sidecar; the pypi index computes hashes itself, dir name must be the
|
|
104
|
+
normalized dashed name `hugpy-video`).
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# hugpy-video
|
|
2
|
+
|
|
3
|
+
Execution and artifacts for Hugpy video work, extracted from the monolith's
|
|
4
|
+
`abstract_hugpy_dev.video_intel` (+ `managers/video`, `managers/video_gen`,
|
|
5
|
+
`comms/studio_assist_log`). Import name `hugpy_video`.
|
|
6
|
+
|
|
7
|
+
**Status: extracted.** Runtime code lives here; the package imports with the
|
|
8
|
+
monolith blocked and with no optional stack (torch/diffusers/ffmpeg bindings)
|
|
9
|
+
installed. Allowed ecosystem imports: `hugpy_platform`, `hugpy_control`,
|
|
10
|
+
`hugpy_engine`, `hugpy_media`. Never `hugpy_oracle`, `hugpy_fleet`,
|
|
11
|
+
`hugpy_server` (see `py/partition.toml`, `PARTITION.md`).
|
|
12
|
+
|
|
13
|
+
## Layout
|
|
14
|
+
- `intel/` — media references + jail (`media_store`), sqlite media job bus
|
|
15
|
+
(`media_bus`) and its control-plane bridge (`job_bridge`), frozen job specs
|
|
16
|
+
and registry (`job_schema`), presets, GPU reservation engine
|
|
17
|
+
(`reservation/`), runners (ffmpeg, diffusers plane, studio, identity relays,
|
|
18
|
+
MLT), the studio spine (`studio/`).
|
|
19
|
+
- `chat_video/` — chat-side frame analysis over a `hugpy_media` vision runner.
|
|
20
|
+
- `video_gen/` — `StudioVideoRunner` / `VideoGenRequest`: the engine task
|
|
21
|
+
runner for text-to-video / image-to-video.
|
|
22
|
+
- `plugin.py` — `hugpy_engine.tasks` entry point (`hugpy_video.plugin:register`);
|
|
23
|
+
import-light, runner resolved lazily.
|
|
24
|
+
- `hooks.py` — `PromptCoordinator` protocol (oracle installs its implementation;
|
|
25
|
+
no-op default). `jobs.py` — public bus API incl. `register_job` for
|
|
26
|
+
upper-layer jobs (the oracle's `video_performance`).
|
|
27
|
+
- `state.py` — injectable state roots (`HUGPY_VIDEO_STATE_DIR`,
|
|
28
|
+
`HUGPY_MEDIA_JOBS_DB`, `HUGPY_RESERVATIONS_DB`, platform storage roots).
|
|
29
|
+
- `intel/plane.py` — the one seam onto the engine's `execute_prompt`.
|
|
30
|
+
- `cli.py` — `hugpy-video --help | jobs list | jobs registry | selftest | state`.
|
|
31
|
+
- `hugpy-video models audit [--json]` inventories every declared model's
|
|
32
|
+
runner gaps, weight pin and minimum VRAM without loading weights.
|
|
33
|
+
- `config.py` — canonical `HUGPY_API_KEY` / `HUGPY_BASE` loader.
|
|
34
|
+
|
|
35
|
+
Extras: `render` (numpy/Pillow/requests), `studio` (torch/diffusers zoo),
|
|
36
|
+
`identity` (abstract-identity client), `test`.
|
|
37
|
+
|
|
38
|
+
The `identity` extra installs the separate `abstract-identity` distribution.
|
|
39
|
+
The central video runner relays mesh and char360 jobs to its service using
|
|
40
|
+
`IDENTITY_RENDER_URL` and `IDENTITY_RENDER_TOKEN`; installing the extra alone
|
|
41
|
+
does not start a GPU service. Studio model sweeps use
|
|
42
|
+
`POST /video/studio/tester`; each attempt records its model, input image,
|
|
43
|
+
sampler settings, frame count and negative prompt in the battery log.
|
|
44
|
+
|
|
45
|
+
## Rules (inherited from char360 / hugpy-agent)
|
|
46
|
+
- Heavy imports lazy — must import on a CPU-only box.
|
|
47
|
+
- Version single-sourced from pyproject (importlib.metadata; no hardcoded `__version__`).
|
|
48
|
+
- Credentials only via `hugpy_video.config.load_config` — canonical names
|
|
49
|
+
`HUGPY_API_KEY` / `HUGPY_BASE`; legacy aliases accepted but recorded in
|
|
50
|
+
`Config.deprecations`.
|
|
51
|
+
- Build: `python -m build`; publish = copy dists (console dir needs the sha256
|
|
52
|
+
sidecar; the pypi index computes hashes itself, dir name must be the
|
|
53
|
+
normalized dashed name `hugpy-video`).
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=77", "setuptools-scm>=8"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "hugpy-video"
|
|
7
|
+
dynamic = ["version"]
|
|
8
|
+
description = "Hugpy video: media library and job bus, ffmpeg, synthetic and studio runners, reservations and studio sessions for video intelligence"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = "LicenseRef-Proprietary"
|
|
12
|
+
license-files = ["LICENSE"]
|
|
13
|
+
authors = [{ name = "putkoff", email = "support@hugpy.ai" }]
|
|
14
|
+
keywords = [
|
|
15
|
+
"hugpy",
|
|
16
|
+
"llm",
|
|
17
|
+
"self-hosted",
|
|
18
|
+
"video",
|
|
19
|
+
"ffmpeg",
|
|
20
|
+
"video-generation",
|
|
21
|
+
"media-intelligence",
|
|
22
|
+
]
|
|
23
|
+
classifiers = [
|
|
24
|
+
"Development Status :: 3 - Alpha",
|
|
25
|
+
"Intended Audience :: Developers",
|
|
26
|
+
"Operating System :: OS Independent",
|
|
27
|
+
"Programming Language :: Python :: 3",
|
|
28
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
29
|
+
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
30
|
+
]
|
|
31
|
+
# Heavy engine deps (torch, wan, ltx, rife, opencv, ...) are deliberately NOT
|
|
32
|
+
# listed: declare only what import-time needs and keep every heavy import
|
|
33
|
+
# lazy inside the function that uses it so the package imports on a CPU-only box.
|
|
34
|
+
dependencies = [
|
|
35
|
+
"hugpy-platform",
|
|
36
|
+
"hugpy-control",
|
|
37
|
+
"hugpy-engine",
|
|
38
|
+
"hugpy-media",
|
|
39
|
+
"pydantic>=2",
|
|
40
|
+
"abstract-essentials",
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
[project.urls]
|
|
44
|
+
Homepage = "https://hugpy.ai"
|
|
45
|
+
Documentation = "https://github.com/hugpy/hugpy/blob/main/py/cinema/hugpy_video/README.md"
|
|
46
|
+
Repository = "https://github.com/hugpy/hugpy"
|
|
47
|
+
Source = "https://github.com/hugpy/hugpy/tree/main/py/cinema/hugpy_video"
|
|
48
|
+
Issues = "https://github.com/hugpy/hugpy/issues"
|
|
49
|
+
Changelog = "https://github.com/hugpy/hugpy/releases"
|
|
50
|
+
Architecture = "https://github.com/hugpy/hugpy/blob/main/PARTITION.md"
|
|
51
|
+
|
|
52
|
+
[project.optional-dependencies]
|
|
53
|
+
# ffmpeg/diffusers-plane runners and the identity relays (HTTP).
|
|
54
|
+
render = ["numpy", "Pillow", "requests"]
|
|
55
|
+
# The studio spine's GPU zoo (Wan / VACE / LTX / RIFE).
|
|
56
|
+
studio = ["torch", "diffusers", "transformers", "numpy", "Pillow", "opencv-python-headless", "imageio", "imageio-ffmpeg", "requests"]
|
|
57
|
+
# Identity rendering stays behind the external abstract-identity service client.
|
|
58
|
+
identity = ["abstract-identity", "requests"]
|
|
59
|
+
test = ["pytest>=8", "pytest-timeout"]
|
|
60
|
+
|
|
61
|
+
[project.scripts]
|
|
62
|
+
hugpy-video = "hugpy_video.cli:main"
|
|
63
|
+
|
|
64
|
+
[project.entry-points."hugpy_engine.tasks"]
|
|
65
|
+
video = "hugpy_video.plugin:register"
|
|
66
|
+
|
|
67
|
+
[tool.setuptools.packages.find]
|
|
68
|
+
where = ["src"]
|
|
69
|
+
|
|
70
|
+
[tool.setuptools.package-data]
|
|
71
|
+
hugpy_video = ["py.typed", "**/*.json", "**/*.md", "**/*.txt"]
|
|
72
|
+
|
|
73
|
+
# ---------------------------------------------------------------------------
|
|
74
|
+
# Lockstep workspace version (2026-09-22): every in-tree hugpy-* distribution
|
|
75
|
+
# takes ONE version from the workspace git tag (vX.Y.Z at the repo root), so a
|
|
76
|
+
# release is a tag and a build always carries the commit it came from
|
|
77
|
+
# (X.Y.Z.devN+g<sha>[.dirty] between tags). A checkout without git metadata
|
|
78
|
+
# builds as 0.0.0+unknown, which central refuses to advertise to workers.
|
|
79
|
+
[tool.setuptools_scm]
|
|
80
|
+
root = "../../.."
|
|
81
|
+
fallback_version = "0.0.0+unknown"
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"""hugpy-video: execution and artifacts for Hugpy video work.
|
|
2
|
+
|
|
3
|
+
Owns the media library jail, the sqlite media job bus and its lifecycle
|
|
4
|
+
bridge, the frozen job specs and presets, the GPU reservation engine, the
|
|
5
|
+
ffmpeg/diffusers/studio/identity runners and the studio spine
|
|
6
|
+
(``intel``), the chat-side video analyzer (``chat_video``), the engine
|
|
7
|
+
task runner for text-/image-to-video (``video_gen``) and the studio assist
|
|
8
|
+
log. Decisions and plans belong to ``hugpy_oracle``, which plugs in through
|
|
9
|
+
``hugpy_video.hooks`` (prompt coordination) and ``hugpy_video.jobs``
|
|
10
|
+
(``register_job``) — this package never imports the oracle, the fleet or
|
|
11
|
+
the server.
|
|
12
|
+
|
|
13
|
+
Import-light: nothing here pulls torch/ffmpeg/studio stacks. Sub-modules are
|
|
14
|
+
imported explicitly by callers (``hugpy_video.jobs``, ``hugpy_video.hooks``,
|
|
15
|
+
``hugpy_video.plugin``, ``hugpy_video.state``, ``hugpy_video.config``).
|
|
16
|
+
|
|
17
|
+
Version is single-sourced from pyproject.toml via importlib.metadata.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
21
|
+
|
|
22
|
+
try:
|
|
23
|
+
__version__ = version("hugpy-video")
|
|
24
|
+
except PackageNotFoundError: # running from a checkout without install
|
|
25
|
+
__version__ = "0.0.0+unknown"
|
|
26
|
+
|
|
27
|
+
from hugpy_video.config import Config, load_config # stdlib-only, cheap
|
|
28
|
+
|
|
29
|
+
__all__ = [
|
|
30
|
+
"__version__",
|
|
31
|
+
"Config",
|
|
32
|
+
"load_config",
|
|
33
|
+
]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Chat-side video helpers (old ``managers/video``). Import-light; see ``video_analyzer``."""
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
"""Frame-by-frame video analysis over a vision runner (chat-side helper).
|
|
2
|
+
|
|
3
|
+
Import-light: the vision runner/schemas come from ``hugpy_media`` (allowed),
|
|
4
|
+
the no-think helpers from ``hugpy_engine`` (lazy, inside the loop).
|
|
5
|
+
"""
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import copy
|
|
9
|
+
import os.path as osp
|
|
10
|
+
import time
|
|
11
|
+
import uuid
|
|
12
|
+
from typing import Any, Union
|
|
13
|
+
|
|
14
|
+
from abstract_essentials import safe_dump_to_file, safe_load_from_json
|
|
15
|
+
from hugpy_media.vision.schemas import VisionRequest
|
|
16
|
+
from hugpy_media.vision.vision_runner import VisionRunner
|
|
17
|
+
from hugpy_platform.utils import get_base_64_image, require_file
|
|
18
|
+
|
|
19
|
+
from hugpy_video.schemas.video_schemas import (
|
|
20
|
+
FrameAnalysis,
|
|
21
|
+
VideoAnalysisConfig,
|
|
22
|
+
VideoAnalysisSummary,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
def _resolve_manifest_path(source: Union[str, Any]) -> str:
|
|
26
|
+
if isinstance(source, str):
|
|
27
|
+
return source
|
|
28
|
+
p = getattr(source, "manifest_path", None)
|
|
29
|
+
if isinstance(p, str):
|
|
30
|
+
return p
|
|
31
|
+
raise TypeError(
|
|
32
|
+
f"expected manifest path str or object with .manifest_path, "
|
|
33
|
+
f"got {type(source).__name__}"
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
def _build_prompt(user_prompt: str, frame_context: dict) -> str:
|
|
37
|
+
return (
|
|
38
|
+
"-----------prompt---------\n"
|
|
39
|
+
f"{user_prompt}\n"
|
|
40
|
+
"-----------frame context---------\n"
|
|
41
|
+
f"{frame_context}\n"
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
async def analyze_video(
|
|
46
|
+
source: Union[str, Any],
|
|
47
|
+
runner: VisionRunner,
|
|
48
|
+
config: VideoAnalysisConfig,
|
|
49
|
+
) -> VideoAnalysisSummary:
|
|
50
|
+
manifest_path = require_file(_resolve_manifest_path(source), "manifest_path")
|
|
51
|
+
manifest_data = safe_load_from_json(manifest_path)
|
|
52
|
+
if not isinstance(manifest_data, dict):
|
|
53
|
+
raise ValueError(f"manifest is not a dict: {manifest_path}")
|
|
54
|
+
|
|
55
|
+
workspace_dir = manifest_data.get("workspace_dir")
|
|
56
|
+
if not workspace_dir or not osp.isdir(workspace_dir):
|
|
57
|
+
raise FileNotFoundError(f"workspace_dir invalid: {workspace_dir!r}")
|
|
58
|
+
|
|
59
|
+
files = manifest_data.get("files") or {}
|
|
60
|
+
frame_context_path = require_file(files.get("frame_context"), "files.frame_context")
|
|
61
|
+
|
|
62
|
+
frame_contexts = safe_load_from_json(frame_context_path)
|
|
63
|
+
if not isinstance(frame_contexts, list) or not frame_contexts:
|
|
64
|
+
raise ValueError(
|
|
65
|
+
f"frame_context must be a non-empty list, "
|
|
66
|
+
f"got {type(frame_contexts).__name__}"
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
total_frames = len(frame_contexts)
|
|
70
|
+
last = frame_contexts[-1]
|
|
71
|
+
total_video_length = last.get("timestamp") if isinstance(last, dict) else None
|
|
72
|
+
|
|
73
|
+
analysis_json_path = osp.join(workspace_dir, "analysis.json")
|
|
74
|
+
manifest_data.setdefault("files", {})["analysis_json"] = analysis_json_path
|
|
75
|
+
safe_dump_to_file(data=manifest_data, file_path=manifest_path)
|
|
76
|
+
|
|
77
|
+
# Resume keyed by frame_path so reruns are idempotent.
|
|
78
|
+
done_by_frame: dict[str, dict] = {}
|
|
79
|
+
if config.resume and osp.isfile(analysis_json_path):
|
|
80
|
+
prior = safe_load_from_json(analysis_json_path) or []
|
|
81
|
+
for entry in prior:
|
|
82
|
+
fp = entry.get("frame_path")
|
|
83
|
+
if fp and entry.get("error") is None:
|
|
84
|
+
done_by_frame[fp] = entry
|
|
85
|
+
|
|
86
|
+
records: list[dict] = list(done_by_frame.values())
|
|
87
|
+
model_key = runner.cfg.model_key # runner is the source of truth
|
|
88
|
+
|
|
89
|
+
for i, raw_ctx in enumerate(frame_contexts):
|
|
90
|
+
if not isinstance(raw_ctx, dict):
|
|
91
|
+
records.append({
|
|
92
|
+
"frame_index": i,
|
|
93
|
+
"error": f"frame_context[{i}] is not a dict",
|
|
94
|
+
})
|
|
95
|
+
continue
|
|
96
|
+
|
|
97
|
+
ctx = copy.deepcopy(raw_ctx) # never mutate caller's data
|
|
98
|
+
frame_path = ctx.get("frame_path")
|
|
99
|
+
|
|
100
|
+
if not frame_path or not osp.isfile(frame_path):
|
|
101
|
+
err = f"frame_path missing or not found: {frame_path!r}"
|
|
102
|
+
if config.raise_on_frame_error:
|
|
103
|
+
raise FileNotFoundError(err)
|
|
104
|
+
ctx.update({"frame_index": i, "error": err})
|
|
105
|
+
records.append(ctx)
|
|
106
|
+
continue
|
|
107
|
+
|
|
108
|
+
if frame_path in done_by_frame:
|
|
109
|
+
continue
|
|
110
|
+
|
|
111
|
+
ctx["frame_index"] = i
|
|
112
|
+
ctx["total_frames"] = total_frames
|
|
113
|
+
ctx["total_video_length"] = total_video_length
|
|
114
|
+
# NO-THINK (utils/no_think.py). Every frame's `analysis` is VALIDATED and
|
|
115
|
+
# PERSISTED to analysis.json — a monologue here poisons the whole dataset,
|
|
116
|
+
# and nothing downstream re-reads the frame to notice. The reasoning is
|
|
117
|
+
# kept alongside (FrameAnalysis is extra="allow") rather than discarded.
|
|
118
|
+
from hugpy_engine.utils.no_think import with_no_think, strip_think
|
|
119
|
+
rendered_prompt = with_no_think(_build_prompt(config.prompt, ctx))
|
|
120
|
+
image_b64 = get_base_64_image(frame_path)
|
|
121
|
+
req = VisionRequest(
|
|
122
|
+
request_id=f"frame-{i}-{uuid.uuid4().hex[:8]}",
|
|
123
|
+
model_key=model_key,
|
|
124
|
+
prompt=rendered_prompt,
|
|
125
|
+
max_new_tokens=config.max_new_tokens,
|
|
126
|
+
max_tokens=config.max_tokens,
|
|
127
|
+
image_b64=image_b64,
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
t0 = time.time()
|
|
131
|
+
try:
|
|
132
|
+
vresult = await runner.run(req)
|
|
133
|
+
text, err = vresult.text, vresult.error
|
|
134
|
+
except Exception as e:
|
|
135
|
+
if config.raise_on_frame_error:
|
|
136
|
+
raise
|
|
137
|
+
text, err = None, f"{type(e).__name__}: {e}"
|
|
138
|
+
duration = time.time() - t0
|
|
139
|
+
|
|
140
|
+
reasoning = ""
|
|
141
|
+
if text:
|
|
142
|
+
text, reasoning = strip_think(text)
|
|
143
|
+
if not text:
|
|
144
|
+
# Nothing but thinking — record it as an error rather than
|
|
145
|
+
# persisting an empty analysis that reads like a clean run.
|
|
146
|
+
err = err or ("model returned only reasoning and no analysis "
|
|
147
|
+
"(ignored the no-think directive)")
|
|
148
|
+
|
|
149
|
+
ctx.update({
|
|
150
|
+
"analysis_prompt": rendered_prompt,
|
|
151
|
+
"analysis": text,
|
|
152
|
+
"analysis_reasoning": reasoning,
|
|
153
|
+
"model_key": model_key,
|
|
154
|
+
"analysis_duration": duration,
|
|
155
|
+
"error": err,
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
# Validate-on-write: schema drift fails fast, not three days from now
|
|
159
|
+
FrameAnalysis.model_validate(ctx)
|
|
160
|
+
records.append(ctx)
|
|
161
|
+
|
|
162
|
+
if (i + 1) % config.save_every == 0 or (i + 1) == total_frames:
|
|
163
|
+
safe_dump_to_file(data=records, file_path=analysis_json_path)
|
|
164
|
+
|
|
165
|
+
succeeded = sum(1 for r in records if r.get("error") is None)
|
|
166
|
+
failed = sum(1 for r in records if r.get("error") is not None)
|
|
167
|
+
|
|
168
|
+
return VideoAnalysisSummary(
|
|
169
|
+
manifest_path=manifest_path,
|
|
170
|
+
analysis_json_path=analysis_json_path,
|
|
171
|
+
frames_total=total_frames,
|
|
172
|
+
frames_succeeded=succeeded,
|
|
173
|
+
frames_failed=failed,
|
|
174
|
+
)
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"""``hugpy-video`` command line (minimal; the console script in pyproject).
|
|
2
|
+
|
|
3
|
+
hugpy-video --help
|
|
4
|
+
hugpy-video jobs list [--all] [--limit N] [--json]
|
|
5
|
+
hugpy-video jobs registry
|
|
6
|
+
hugpy-video selftest # in-process checks, no GPU/service
|
|
7
|
+
hugpy-video models audit --json # declared model roster and readiness gates
|
|
8
|
+
|
|
9
|
+
Everything heavy is imported inside the sub-command that needs it.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import argparse
|
|
15
|
+
import json
|
|
16
|
+
import sys
|
|
17
|
+
from typing import List, Optional
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _cmd_jobs_list(args) -> int:
|
|
21
|
+
from hugpy_video import jobs
|
|
22
|
+
|
|
23
|
+
rows = jobs.list_jobs(include_terminal=args.all, limit=args.limit)
|
|
24
|
+
if args.json:
|
|
25
|
+
print(json.dumps(rows, indent=2, default=str))
|
|
26
|
+
return 0
|
|
27
|
+
if not rows:
|
|
28
|
+
print("no jobs")
|
|
29
|
+
return 0
|
|
30
|
+
for r in rows:
|
|
31
|
+
print(f"{r.get('job_id','?'):34} {r.get('name','?'):26} {r.get('status','?')}")
|
|
32
|
+
return 0
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _cmd_jobs_registry(args) -> int:
|
|
36
|
+
from hugpy_video import jobs
|
|
37
|
+
|
|
38
|
+
for name, spec in sorted(jobs.registered_jobs().items()):
|
|
39
|
+
print(f"{name:26} {spec.runner_key[0]}/{spec.runner_key[1]:18} "
|
|
40
|
+
f"queue={spec.queue} timeout={spec.timeout_s}s")
|
|
41
|
+
return 0
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _cmd_selftest(args) -> int:
|
|
45
|
+
from hugpy_video.selftest import run_selftest
|
|
46
|
+
|
|
47
|
+
failures = run_selftest(verbose=True)
|
|
48
|
+
return 1 if failures else 0
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _cmd_state(args) -> int:
|
|
52
|
+
from dataclasses import asdict
|
|
53
|
+
|
|
54
|
+
from hugpy_video.state import get_state_roots
|
|
55
|
+
|
|
56
|
+
print(json.dumps(asdict(get_state_roots()), indent=2))
|
|
57
|
+
return 0
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def _cmd_models_audit(args) -> int:
|
|
61
|
+
"""Inventory every declared model without loading weights or allocating a GPU."""
|
|
62
|
+
from hugpy_video.intel.studio.registry import MODEL_REGISTRY, model_gate_reasons
|
|
63
|
+
|
|
64
|
+
rows = []
|
|
65
|
+
for model_id, cfg in sorted(MODEL_REGISTRY.items()):
|
|
66
|
+
precision_gb = {precision.value: gb for precision, gb in cfg.vram.per_precision}
|
|
67
|
+
rows.append({
|
|
68
|
+
"model_id": model_id,
|
|
69
|
+
"family": cfg.family.value,
|
|
70
|
+
"capabilities": sorted(cap.value for cap in cfg.capabilities),
|
|
71
|
+
"tasks": sorted(task.value for task in cfg.tasks),
|
|
72
|
+
"minimum_vram_gb": min(precision_gb.values()) if precision_gb else None,
|
|
73
|
+
"vram_by_precision_gb": precision_gb,
|
|
74
|
+
"weights_pinned": cfg.weight_hash is not None,
|
|
75
|
+
"runner_gaps": model_gate_reasons(model_id),
|
|
76
|
+
})
|
|
77
|
+
if args.json:
|
|
78
|
+
print(json.dumps({"models": rows}, indent=2, sort_keys=True))
|
|
79
|
+
else:
|
|
80
|
+
for row in rows:
|
|
81
|
+
status = ", ".join(row["runner_gaps"].values()) or "runner present"
|
|
82
|
+
pin = "pinned" if row["weights_pinned"] else "unpinned"
|
|
83
|
+
print(f"{row['model_id']:32} {row['minimum_vram_gb']!s:>6} GB {pin:8} {status}")
|
|
84
|
+
print(f"{len(rows)} declared models; audit does not prove installed weights or GPU readiness")
|
|
85
|
+
return 0
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
89
|
+
from hugpy_video import __version__
|
|
90
|
+
|
|
91
|
+
p = argparse.ArgumentParser(prog="hugpy-video",
|
|
92
|
+
description="Hugpy video: media job bus, runners, studio.")
|
|
93
|
+
p.add_argument("--version", action="version", version=f"hugpy-video {__version__}")
|
|
94
|
+
sub = p.add_subparsers(dest="cmd")
|
|
95
|
+
|
|
96
|
+
jobs = sub.add_parser("jobs", help="media job bus")
|
|
97
|
+
jsub = jobs.add_subparsers(dest="jobs_cmd")
|
|
98
|
+
ls = jsub.add_parser("list", help="list jobs")
|
|
99
|
+
ls.add_argument("--all", action="store_true", help="include terminal jobs")
|
|
100
|
+
ls.add_argument("--limit", type=int, default=50)
|
|
101
|
+
ls.add_argument("--json", action="store_true")
|
|
102
|
+
ls.set_defaults(func=_cmd_jobs_list)
|
|
103
|
+
reg = jsub.add_parser("registry", help="registered job names and runner keys")
|
|
104
|
+
reg.set_defaults(func=_cmd_jobs_registry)
|
|
105
|
+
|
|
106
|
+
st = sub.add_parser("selftest", help="in-process self checks (no GPU, no service)")
|
|
107
|
+
st.set_defaults(func=_cmd_selftest)
|
|
108
|
+
|
|
109
|
+
state = sub.add_parser("state", help="print the resolved state roots")
|
|
110
|
+
state.set_defaults(func=_cmd_state)
|
|
111
|
+
models = sub.add_parser("models", help="studio model inventory")
|
|
112
|
+
msub = models.add_subparsers(dest="models_cmd")
|
|
113
|
+
audit = msub.add_parser("audit", help="report runner, pin and VRAM requirements")
|
|
114
|
+
audit.add_argument("--json", action="store_true")
|
|
115
|
+
audit.set_defaults(func=_cmd_models_audit)
|
|
116
|
+
return p
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def main(argv: Optional[List[str]] = None) -> int:
|
|
120
|
+
parser = build_parser()
|
|
121
|
+
args = parser.parse_args(argv)
|
|
122
|
+
func = getattr(args, "func", None)
|
|
123
|
+
if func is None:
|
|
124
|
+
parser.print_help()
|
|
125
|
+
return 0
|
|
126
|
+
return int(func(args) or 0)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
if __name__ == "__main__": # pragma: no cover
|
|
130
|
+
sys.exit(main())
|