hugpy-media 0.2.2__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_media-0.2.2/LICENSE +41 -0
- hugpy_media-0.2.2/PKG-INFO +79 -0
- hugpy_media-0.2.2/README.md +9 -0
- hugpy_media-0.2.2/pyproject.toml +96 -0
- hugpy_media-0.2.2/setup.cfg +4 -0
- hugpy_media-0.2.2/src/hugpy_media/__init__.py +32 -0
- hugpy_media-0.2.2/src/hugpy_media/chunking.py +111 -0
- hugpy_media-0.2.2/src/hugpy_media/comfy/__init__.py +1 -0
- hugpy_media-0.2.2/src/hugpy_media/comfy/comfy_runner.py +536 -0
- hugpy_media-0.2.2/src/hugpy_media/embed/__init__.py +6 -0
- hugpy_media-0.2.2/src/hugpy_media/embed/embed_runner.py +154 -0
- hugpy_media-0.2.2/src/hugpy_media/extract.py +504 -0
- hugpy_media-0.2.2/src/hugpy_media/hooks.py +158 -0
- hugpy_media-0.2.2/src/hugpy_media/imagegen/__init__.py +6 -0
- hugpy_media-0.2.2/src/hugpy_media/imagegen/imagegen_runner.py +1156 -0
- hugpy_media-0.2.2/src/hugpy_media/imagegen/schemas.py +91 -0
- hugpy_media-0.2.2/src/hugpy_media/imagegen/vram_retry.py +89 -0
- hugpy_media-0.2.2/src/hugpy_media/keywords/__init__.py +6 -0
- hugpy_media-0.2.2/src/hugpy_media/keywords/keybert_model.py +608 -0
- hugpy_media-0.2.2/src/hugpy_media/keywords/keywords_runner.py +163 -0
- hugpy_media-0.2.2/src/hugpy_media/keywords/schemas.py +67 -0
- hugpy_media-0.2.2/src/hugpy_media/model_battery.py +349 -0
- hugpy_media-0.2.2/src/hugpy_media/pdfs/__init__.py +0 -0
- hugpy_media-0.2.2/src/hugpy_media/pdfs/utils.py +166 -0
- hugpy_media-0.2.2/src/hugpy_media/plugin.py +210 -0
- hugpy_media-0.2.2/src/hugpy_media/plugin_builders.py +534 -0
- hugpy_media-0.2.2/src/hugpy_media/py.typed +0 -0
- hugpy_media-0.2.2/src/hugpy_media/schemas/__init__.py +0 -0
- hugpy_media-0.2.2/src/hugpy_media/schemas/embeded_schemas.py +57 -0
- hugpy_media-0.2.2/src/hugpy_media/schemas/summarizer_schemas.py +176 -0
- hugpy_media-0.2.2/src/hugpy_media/schemas/whisper_schemas.py +175 -0
- hugpy_media-0.2.2/src/hugpy_media/seo/__init__.py +0 -0
- hugpy_media-0.2.2/src/hugpy_media/seo/pdf_utils.py +227 -0
- hugpy_media-0.2.2/src/hugpy_media/summarizers/__init__.py +21 -0
- hugpy_media-0.2.2/src/hugpy_media/summarizers/generation.py +56 -0
- hugpy_media-0.2.2/src/hugpy_media/summarizers/media.py +47 -0
- hugpy_media-0.2.2/src/hugpy_media/summarizers/summarize_runner.py +98 -0
- hugpy_media-0.2.2/src/hugpy_media/summarizers/summarizers.py +516 -0
- hugpy_media-0.2.2/src/hugpy_media/text/__init__.py +0 -0
- hugpy_media-0.2.2/src/hugpy_media/text/combined.py +281 -0
- hugpy_media-0.2.2/src/hugpy_media/tts/__init__.py +34 -0
- hugpy_media-0.2.2/src/hugpy_media/tts/_backend_main.py +103 -0
- hugpy_media-0.2.2/src/hugpy_media/tts/chatterbox_runner.py +610 -0
- hugpy_media-0.2.2/src/hugpy_media/tts/paths.py +9 -0
- hugpy_media-0.2.2/src/hugpy_media/tts/schemas.py +75 -0
- hugpy_media-0.2.2/src/hugpy_media/tts/seat.py +141 -0
- hugpy_media-0.2.2/src/hugpy_media/tts/tts_runner.py +263 -0
- hugpy_media-0.2.2/src/hugpy_media/vision/__init__.py +6 -0
- hugpy_media-0.2.2/src/hugpy_media/vision/schemas.py +107 -0
- hugpy_media-0.2.2/src/hugpy_media/vision/utils.py +26 -0
- hugpy_media-0.2.2/src/hugpy_media/vision/vision_backends.py +100 -0
- hugpy_media-0.2.2/src/hugpy_media/vision/vision_coder.py +377 -0
- hugpy_media-0.2.2/src/hugpy_media/vision/vision_runner.py +27 -0
- hugpy_media-0.2.2/src/hugpy_media/vision_analysis/__init__.py +8 -0
- hugpy_media-0.2.2/src/hugpy_media/vision_analysis/runner.py +233 -0
- hugpy_media-0.2.2/src/hugpy_media/vision_analysis/schemas.py +63 -0
- hugpy_media-0.2.2/src/hugpy_media/whisper_model/__init__.py +27 -0
- hugpy_media-0.2.2/src/hugpy_media/whisper_model/constants.py +35 -0
- hugpy_media-0.2.2/src/hugpy_media/whisper_model/src/__init__.py +6 -0
- hugpy_media-0.2.2/src/hugpy_media/whisper_model/src/model/__init__.py +7 -0
- hugpy_media-0.2.2/src/hugpy_media/whisper_model/src/model/execute.py +218 -0
- hugpy_media-0.2.2/src/hugpy_media/whisper_model/src/model/model.py +56 -0
- hugpy_media-0.2.2/src/hugpy_media/whisper_model/src/model/utils/__init__.py +5 -0
- hugpy_media-0.2.2/src/hugpy_media/whisper_model/src/model/utils/audio.py +81 -0
- hugpy_media-0.2.2/src/hugpy_media/whisper_model/src/model/utils/files/__init__.py +1 -0
- hugpy_media-0.2.2/src/hugpy_media/whisper_model/src/model/utils/files/artifacts/__init__.py +1 -0
- hugpy_media-0.2.2/src/hugpy_media/whisper_model/src/model/utils/files/artifacts/workspace.py +55 -0
- hugpy_media-0.2.2/src/hugpy_media/whisper_model/src/model/utils/files/frames/__init__.py +6 -0
- hugpy_media-0.2.2/src/hugpy_media/whisper_model/src/model/utils/files/frames/extract.py +92 -0
- hugpy_media-0.2.2/src/hugpy_media/whisper_model/src/model/utils/files/frames/utils.py +45 -0
- hugpy_media-0.2.2/src/hugpy_media/whisper_model/src/model/utils/files/save.py +17 -0
- hugpy_media-0.2.2/src/hugpy_media/whisper_model/src/runner.py +115 -0
- hugpy_media-0.2.2/src/hugpy_media/whisper_model/src/stream.py +168 -0
- hugpy_media-0.2.2/src/hugpy_media.egg-info/PKG-INFO +79 -0
- hugpy_media-0.2.2/src/hugpy_media.egg-info/SOURCES.txt +90 -0
- hugpy_media-0.2.2/src/hugpy_media.egg-info/dependency_links.txt +1 -0
- hugpy_media-0.2.2/src/hugpy_media.egg-info/entry_points.txt +2 -0
- hugpy_media-0.2.2/src/hugpy_media.egg-info/requires.txt +56 -0
- hugpy_media-0.2.2/src/hugpy_media.egg-info/scm_file_list.json +86 -0
- hugpy_media-0.2.2/src/hugpy_media.egg-info/scm_version.json +8 -0
- hugpy_media-0.2.2/src/hugpy_media.egg-info/top_level.txt +1 -0
- hugpy_media-0.2.2/tests/conftest.py +23 -0
- hugpy_media-0.2.2/tests/test_comfy_vram_retry.py +229 -0
- hugpy_media-0.2.2/tests/test_imagegen_device.py +93 -0
- hugpy_media-0.2.2/tests/test_imagegen_evict_to_fit.py +232 -0
- hugpy_media-0.2.2/tests/test_imagegen_footprint.py +332 -0
- hugpy_media-0.2.2/tests/test_imagegen_placement.py +233 -0
- hugpy_media-0.2.2/tests/test_imagegen_ram_only.py +272 -0
- hugpy_media-0.2.2/tests/test_import_policy.py +106 -0
- hugpy_media-0.2.2/tests/test_media_extract_readability.py +485 -0
- hugpy_media-0.2.2/tests/test_plugin.py +192 -0
- hugpy_media-0.2.2/tests/test_vision_coder_placement.py +226 -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,79 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: hugpy-media
|
|
3
|
+
Version: 0.2.2
|
|
4
|
+
Summary: Hugpy media: engine task plugins for speech recognition, TTS, embeddings, keywords, summaries, vision, image generation, ComfyUI and document/URL extraction
|
|
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/inference/hugpy_media/README.md
|
|
9
|
+
Project-URL: Repository, https://github.com/hugpy/hugpy
|
|
10
|
+
Project-URL: Source, https://github.com/hugpy/hugpy/tree/main/py/inference/hugpy_media
|
|
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,whisper,embeddings,vision,image-generation,tts,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-storage
|
|
26
|
+
Requires-Dist: hugpy-engine
|
|
27
|
+
Requires-Dist: abstract_essentials
|
|
28
|
+
Requires-Dist: pydantic>=2
|
|
29
|
+
Requires-Dist: requests
|
|
30
|
+
Requires-Dist: beautifulsoup4
|
|
31
|
+
Requires-Dist: PyPDF2
|
|
32
|
+
Requires-Dist: pdfplumber
|
|
33
|
+
Requires-Dist: pillow
|
|
34
|
+
Provides-Extra: test
|
|
35
|
+
Requires-Dist: pytest>=8; extra == "test"
|
|
36
|
+
Requires-Dist: pytest-timeout; extra == "test"
|
|
37
|
+
Provides-Extra: audio
|
|
38
|
+
Requires-Dist: openai-whisper; extra == "audio"
|
|
39
|
+
Provides-Extra: imagegen
|
|
40
|
+
Requires-Dist: diffusers>=0.39; extra == "imagegen"
|
|
41
|
+
Requires-Dist: accelerate; extra == "imagegen"
|
|
42
|
+
Requires-Dist: transformers>=5.12; extra == "imagegen"
|
|
43
|
+
Requires-Dist: bitsandbytes>=0.48.0; extra == "imagegen"
|
|
44
|
+
Provides-Extra: embed
|
|
45
|
+
Requires-Dist: sentence-transformers; extra == "embed"
|
|
46
|
+
Provides-Extra: transformers
|
|
47
|
+
Requires-Dist: torch; extra == "transformers"
|
|
48
|
+
Requires-Dist: transformers; extra == "transformers"
|
|
49
|
+
Requires-Dist: accelerate; extra == "transformers"
|
|
50
|
+
Requires-Dist: sentencepiece; extra == "transformers"
|
|
51
|
+
Requires-Dist: timm; extra == "transformers"
|
|
52
|
+
Provides-Extra: vision
|
|
53
|
+
Requires-Dist: opencv-python-headless; extra == "vision"
|
|
54
|
+
Requires-Dist: onnxruntime; extra == "vision"
|
|
55
|
+
Requires-Dist: aiohttp; extra == "vision"
|
|
56
|
+
Provides-Extra: extract
|
|
57
|
+
Requires-Dist: python-docx; extra == "extract"
|
|
58
|
+
Requires-Dist: openpyxl; extra == "extract"
|
|
59
|
+
Requires-Dist: brotli; extra == "extract"
|
|
60
|
+
Requires-Dist: zstandard; extra == "extract"
|
|
61
|
+
Provides-Extra: keywords
|
|
62
|
+
Requires-Dist: keybert; extra == "keywords"
|
|
63
|
+
Provides-Extra: tts
|
|
64
|
+
Requires-Dist: chatterbox-tts; extra == "tts"
|
|
65
|
+
Provides-Extra: comfy
|
|
66
|
+
Requires-Dist: aiohttp; extra == "comfy"
|
|
67
|
+
Provides-Extra: all
|
|
68
|
+
Requires-Dist: hugpy-media[audio,comfy,embed,extract,imagegen,keywords,transformers,vision]; extra == "all"
|
|
69
|
+
Dynamic: license-file
|
|
70
|
+
|
|
71
|
+
# hugpy-media
|
|
72
|
+
|
|
73
|
+
`hugpy_media` — extracted from `abstract_hugpy_dev` as part of the Hugpy
|
|
74
|
+
partition. Ownership and allowed dependencies are declared in
|
|
75
|
+
`py/partition.toml`; see `PARTITION.md` at the workspace root.
|
|
76
|
+
|
|
77
|
+
Allowed Python dependencies inside the ecosystem: hugpy_platform, hugpy_storage, hugpy_engine.
|
|
78
|
+
|
|
79
|
+
Register the media tasks with the engine via `hugpy_media.plugin.register()` (also the `hugpy_engine.tasks` entry point `media`). Heavy stacks are extras: `keywords`, `audio`, `imagegen`, `embed`, `transformers`, `vision`, `extract`, `tts`, `comfy`, umbrella `all`.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# hugpy-media
|
|
2
|
+
|
|
3
|
+
`hugpy_media` — extracted from `abstract_hugpy_dev` as part of the Hugpy
|
|
4
|
+
partition. Ownership and allowed dependencies are declared in
|
|
5
|
+
`py/partition.toml`; see `PARTITION.md` at the workspace root.
|
|
6
|
+
|
|
7
|
+
Allowed Python dependencies inside the ecosystem: hugpy_platform, hugpy_storage, hugpy_engine.
|
|
8
|
+
|
|
9
|
+
Register the media tasks with the engine via `hugpy_media.plugin.register()` (also the `hugpy_engine.tasks` entry point `media`). Heavy stacks are extras: `keywords`, `audio`, `imagegen`, `embed`, `transformers`, `vision`, `extract`, `tts`, `comfy`, umbrella `all`.
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=77", "setuptools-scm>=8"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "hugpy-media"
|
|
7
|
+
dynamic = ["version"]
|
|
8
|
+
description = "Hugpy media: engine task plugins for speech recognition, TTS, embeddings, keywords, summaries, vision, image generation, ComfyUI and document/URL extraction"
|
|
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
|
+
"whisper",
|
|
19
|
+
"embeddings",
|
|
20
|
+
"vision",
|
|
21
|
+
"image-generation",
|
|
22
|
+
"tts",
|
|
23
|
+
"media-intelligence",
|
|
24
|
+
]
|
|
25
|
+
classifiers = [
|
|
26
|
+
"Development Status :: 3 - Alpha",
|
|
27
|
+
"Intended Audience :: Developers",
|
|
28
|
+
"Operating System :: OS Independent",
|
|
29
|
+
"Programming Language :: Python :: 3",
|
|
30
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
31
|
+
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
32
|
+
]
|
|
33
|
+
# Base = importable and able to build requests / run the model-less extractors
|
|
34
|
+
# on any box. Every model stack is an extra, imported lazily inside its runner.
|
|
35
|
+
dependencies = [
|
|
36
|
+
"hugpy-platform",
|
|
37
|
+
"hugpy-storage",
|
|
38
|
+
"hugpy-engine",
|
|
39
|
+
"abstract_essentials", # derive_media_type / read_from_file / safe_dump_to_json
|
|
40
|
+
"pydantic>=2",
|
|
41
|
+
"requests",
|
|
42
|
+
"beautifulsoup4", # url-extraction, seo/text helpers
|
|
43
|
+
"PyPDF2", # pure-python PDF reading (pdfs/, text/)
|
|
44
|
+
"pdfplumber", # document-extraction PDF page text
|
|
45
|
+
"pillow", # image decoding in vision/imagegen request paths
|
|
46
|
+
]
|
|
47
|
+
|
|
48
|
+
[project.urls]
|
|
49
|
+
Homepage = "https://hugpy.ai"
|
|
50
|
+
Documentation = "https://github.com/hugpy/hugpy/blob/main/py/inference/hugpy_media/README.md"
|
|
51
|
+
Repository = "https://github.com/hugpy/hugpy"
|
|
52
|
+
Source = "https://github.com/hugpy/hugpy/tree/main/py/inference/hugpy_media"
|
|
53
|
+
Issues = "https://github.com/hugpy/hugpy/issues"
|
|
54
|
+
Changelog = "https://github.com/hugpy/hugpy/releases"
|
|
55
|
+
Architecture = "https://github.com/hugpy/hugpy/blob/main/PARTITION.md"
|
|
56
|
+
|
|
57
|
+
[project.optional-dependencies]
|
|
58
|
+
test = ["pytest>=8", "pytest-timeout"]
|
|
59
|
+
# Mirrors the monolith's extras, one per media stack.
|
|
60
|
+
audio = ["openai-whisper"]
|
|
61
|
+
imagegen = ["diffusers>=0.39", "accelerate", "transformers>=5.12", "bitsandbytes>=0.48.0"]
|
|
62
|
+
embed = ["sentence-transformers"]
|
|
63
|
+
transformers = ["torch", "transformers", "accelerate", "sentencepiece", "timm"]
|
|
64
|
+
vision = ["opencv-python-headless", "onnxruntime", "aiohttp"]
|
|
65
|
+
extract = ["python-docx", "openpyxl", "brotli", "zstandard"]
|
|
66
|
+
keywords = ["keybert"]
|
|
67
|
+
# tts is opt-in ONLY: chatterbox-tts pins torch/numpy/transformers versions that
|
|
68
|
+
# conflict with the rest of this set (numpy<2 / transformers==5.2 vs >=5.12), so
|
|
69
|
+
# `hugpy[gpu-worker]` (-> hugpy-media[all]) was unresolvable while it was in
|
|
70
|
+
# `all` (found at the 0.2.0 worker cutover, 2026-09-23).
|
|
71
|
+
tts = ["chatterbox-tts"]
|
|
72
|
+
comfy = ["aiohttp"]
|
|
73
|
+
all = ["hugpy-media[keywords,audio,imagegen,embed,transformers,vision,extract,comfy]"]
|
|
74
|
+
|
|
75
|
+
[project.entry-points."hugpy_engine.tasks"]
|
|
76
|
+
media = "hugpy_media.plugin:register"
|
|
77
|
+
|
|
78
|
+
[tool.setuptools.packages.find]
|
|
79
|
+
where = ["src"]
|
|
80
|
+
|
|
81
|
+
[tool.setuptools.package-data]
|
|
82
|
+
hugpy_media = ["py.typed", "**/*.json", "**/*.sh", "**/*.md", "**/*.txt"]
|
|
83
|
+
|
|
84
|
+
[tool.pytest.ini_options]
|
|
85
|
+
testpaths = ["tests"]
|
|
86
|
+
timeout = 120
|
|
87
|
+
|
|
88
|
+
# ---------------------------------------------------------------------------
|
|
89
|
+
# Lockstep workspace version (2026-09-22): every in-tree hugpy-* distribution
|
|
90
|
+
# takes ONE version from the workspace git tag (vX.Y.Z at the repo root), so a
|
|
91
|
+
# release is a tag and a build always carries the commit it came from
|
|
92
|
+
# (X.Y.Z.devN+g<sha>[.dirty] between tags). A checkout without git metadata
|
|
93
|
+
# builds as 0.0.0+unknown, which central refuses to advertise to workers.
|
|
94
|
+
[tool.setuptools_scm]
|
|
95
|
+
root = "../../.."
|
|
96
|
+
fallback_version = "0.0.0+unknown"
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"""hugpy-media: task plugins for the Hugpy engine.
|
|
2
|
+
|
|
3
|
+
Embeddings/similarity, summaries, keywords, speech recognition, TTS, vision
|
|
4
|
+
chat, vision analysis, image generation, ComfyUI and document/URL extraction.
|
|
5
|
+
Heavy frameworks are extras imported only inside the selected runner, so this
|
|
6
|
+
package imports without torch, diffusers, whisper, keybert,
|
|
7
|
+
sentence-transformers, OpenCV or llama.cpp installed.
|
|
8
|
+
|
|
9
|
+
Wiring: ``hugpy_media.plugin.register()`` (also the ``hugpy_engine.tasks``
|
|
10
|
+
entry point ``media``) puts every media task into ``hugpy_engine.tasks``;
|
|
11
|
+
``hugpy_media.hooks`` is where the fleet worker observes spawned processes.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
try: # the installed distribution's version: the workspace tag/commit, never a literal
|
|
17
|
+
from importlib.metadata import version as _dist_version
|
|
18
|
+
__version__ = _dist_version("hugpy-media")
|
|
19
|
+
except Exception: # noqa: BLE001 — source tree without metadata
|
|
20
|
+
__version__ = "0.0.0+unknown"
|
|
21
|
+
|
|
22
|
+
__all__ = ["__version__", "hooks", "plugin", "extract", "chunking"]
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def __getattr__(name: str):
|
|
26
|
+
# Submodules are exposed by name but imported on demand, keeping
|
|
27
|
+
# ``import hugpy_media`` free of even the pydantic schema imports.
|
|
28
|
+
if name in ("hooks", "plugin", "extract", "chunking"):
|
|
29
|
+
import importlib
|
|
30
|
+
|
|
31
|
+
return importlib.import_module(f"hugpy_media.{name}")
|
|
32
|
+
raise AttributeError(f"module 'hugpy_media' has no attribute {name!r}")
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
from typing import List, Optional
|
|
5
|
+
|
|
6
|
+
from hugpy_platform.module_imports import get_tiktoken
|
|
7
|
+
def get_encoder(model_name: str = "gpt-4", encoding_name: Optional[str] = None):
|
|
8
|
+
"""Return a tiktoken encoder for your model/encoding, or None when tiktoken
|
|
9
|
+
isn't installed. The package lazy-imports tiktoken, so on lean installs
|
|
10
|
+
get_tiktoken() yields a stub whose calls return None — handle that here
|
|
11
|
+
rather than crashing downstream (callers fall back to a heuristic)."""
|
|
12
|
+
tiktoken = get_tiktoken()
|
|
13
|
+
try:
|
|
14
|
+
enc = (tiktoken.get_encoding(encoding_name) if encoding_name
|
|
15
|
+
else tiktoken.encoding_for_model(model_name))
|
|
16
|
+
except Exception:
|
|
17
|
+
return None
|
|
18
|
+
return enc if hasattr(enc, "encode") else None
|
|
19
|
+
|
|
20
|
+
def count_tokens(text: str, encoder=None) -> int:
|
|
21
|
+
"""Count how many tokens `text` encodes to. Falls back to a ~4-chars/token
|
|
22
|
+
heuristic when no usable encoder is available, so chunking (and the
|
|
23
|
+
summarizer that drives it) still works on installs without tiktoken."""
|
|
24
|
+
s = str(text)
|
|
25
|
+
enc = getattr(encoder, "encode", None)
|
|
26
|
+
if callable(enc):
|
|
27
|
+
try:
|
|
28
|
+
return len(enc(s))
|
|
29
|
+
except Exception:
|
|
30
|
+
pass
|
|
31
|
+
return max(len(s) // 4, len(s.split()), 1)
|
|
32
|
+
|
|
33
|
+
def recursive_chunk(
|
|
34
|
+
text: str,
|
|
35
|
+
desired_tokens: int,
|
|
36
|
+
model_name: str = "gpt-4",
|
|
37
|
+
separators: Optional[List[str]] = None,
|
|
38
|
+
overlap: int = 0
|
|
39
|
+
) -> List[str]:
|
|
40
|
+
"""
|
|
41
|
+
Split `text` into chunks as close to `desired_tokens` tokens as possible,
|
|
42
|
+
preserving contiguous blocks via `separators`, and *only* splitting inside
|
|
43
|
+
a block if it can’t possibly fit otherwise.
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
text: the full string to split
|
|
47
|
+
desired_tokens: target token count per chunk (never exceed)
|
|
48
|
+
encoder: a tiktoken encoder
|
|
49
|
+
separators: list of splitters, from largest to smallest logical unit
|
|
50
|
+
overlap: how many tokens to overlap between adjacent chunks
|
|
51
|
+
"""
|
|
52
|
+
encoder = get_encoder(model_name)
|
|
53
|
+
if separators is None:
|
|
54
|
+
# from big (paragraphs) to small (words)
|
|
55
|
+
separators = ["\n\n", "\n", r"(?<=[\.\?\!])\s", ", ", " "]
|
|
56
|
+
|
|
57
|
+
# If it already fits, return it whole:
|
|
58
|
+
if count_tokens(text, encoder) <= desired_tokens:
|
|
59
|
+
return [text]
|
|
60
|
+
|
|
61
|
+
# Try splitting by each separator in turn
|
|
62
|
+
for sep in separators:
|
|
63
|
+
# use regex split when the separator is a lookbehind pattern
|
|
64
|
+
parts = re.split(sep, text) if sep.startswith("(?") else text.split(sep)
|
|
65
|
+
if len(parts) > 1:
|
|
66
|
+
chunks: List[str] = []
|
|
67
|
+
current = ""
|
|
68
|
+
current_tokens = 0
|
|
69
|
+
|
|
70
|
+
for part in parts:
|
|
71
|
+
part = part.strip()
|
|
72
|
+
if not part:
|
|
73
|
+
continue
|
|
74
|
+
part_tokens = count_tokens(part, encoder)
|
|
75
|
+
|
|
76
|
+
# If this block alone is too big, recurse into it with the next-level separators
|
|
77
|
+
if part_tokens > desired_tokens:
|
|
78
|
+
# flush current
|
|
79
|
+
if current:
|
|
80
|
+
chunks.extend(recursive_chunk(
|
|
81
|
+
current, desired_tokens, model_name, separators[1:], overlap
|
|
82
|
+
))
|
|
83
|
+
current, current_tokens = "", 0
|
|
84
|
+
# now chunk the oversized block
|
|
85
|
+
chunks.extend(recursive_chunk(
|
|
86
|
+
part, desired_tokens, model_name, separators[1:], overlap
|
|
87
|
+
))
|
|
88
|
+
else:
|
|
89
|
+
# can we add it to the current chunk?
|
|
90
|
+
if current_tokens + part_tokens <= desired_tokens:
|
|
91
|
+
# include the separator back in
|
|
92
|
+
current = sep.join([current, part]) if current else part
|
|
93
|
+
current_tokens += part_tokens
|
|
94
|
+
else:
|
|
95
|
+
# flush current, start new
|
|
96
|
+
chunks.append(current)
|
|
97
|
+
current, current_tokens = part, part_tokens
|
|
98
|
+
|
|
99
|
+
if current:
|
|
100
|
+
chunks.append(current)
|
|
101
|
+
return chunks
|
|
102
|
+
|
|
103
|
+
# Fallback: pure token sliding window (the only time we’ll split “inside” a block)
|
|
104
|
+
tokens = encoder.encode(text)
|
|
105
|
+
stride = desired_tokens - overlap
|
|
106
|
+
return [
|
|
107
|
+
encoder.decode(tokens[i : i + desired_tokens])
|
|
108
|
+
for i in range(0, len(tokens), stride)
|
|
109
|
+
]
|
|
110
|
+
|
|
111
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from hugpy_media.comfy.comfy_runner import ComfyRunner
|