studio-workers 0.0.1__tar.gz → 1.4.0__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.
Files changed (115) hide show
  1. studio_workers-1.4.0/LICENSE +141 -0
  2. studio_workers-1.4.0/PKG-INFO +86 -0
  3. studio_workers-1.4.0/README.md +56 -0
  4. studio_workers-1.4.0/README.pypi.md +46 -0
  5. studio_workers-1.4.0/pyproject.toml +104 -0
  6. studio_workers-1.4.0/studio_workers/__init__.py +3 -0
  7. studio_workers-1.4.0/studio_workers/adapters/__init__.py +3 -0
  8. studio_workers-1.4.0/studio_workers/cli.py +108 -0
  9. studio_workers-1.4.0/studio_workers/constants.py +1 -0
  10. studio_workers-1.4.0/studio_workers/contracts/__init__.py +82 -0
  11. studio_workers-1.4.0/studio_workers/contracts/downloaded_file.py +72 -0
  12. studio_workers-1.4.0/studio_workers/contracts/group_expansion.py +246 -0
  13. studio_workers-1.4.0/studio_workers/contracts/job_claim.py +49 -0
  14. studio_workers-1.4.0/studio_workers/contracts/log_schema.py +71 -0
  15. studio_workers-1.4.0/studio_workers/contracts/redaction.py +250 -0
  16. studio_workers-1.4.0/studio_workers/contracts/schema_projection.py +84 -0
  17. studio_workers-1.4.0/studio_workers/contracts/step_result.py +26 -0
  18. studio_workers-1.4.0/studio_workers/contracts/version.py +6 -0
  19. studio_workers-1.4.0/studio_workers/contracts/webhook_completion.py +185 -0
  20. studio_workers-1.4.0/studio_workers/contracts/workspace_paths.py +45 -0
  21. studio_workers-1.4.0/studio_workers/engines/__init__.py +3 -0
  22. studio_workers-1.4.0/studio_workers/engines/audio/__init__.py +3 -0
  23. studio_workers-1.4.0/studio_workers/engines/audio/handler.py +408 -0
  24. studio_workers-1.4.0/studio_workers/engines/audio/settings.py +41 -0
  25. studio_workers-1.4.0/studio_workers/engines/comfyui/__init__.py +13 -0
  26. studio_workers-1.4.0/studio_workers/engines/comfyui/client.py +260 -0
  27. studio_workers-1.4.0/studio_workers/engines/comfyui/handler.py +578 -0
  28. studio_workers-1.4.0/studio_workers/engines/comfyui/presets/fooocus_styles.json +741 -0
  29. studio_workers-1.4.0/studio_workers/engines/comfyui/presets/fooocus_styles_compact.json +129 -0
  30. studio_workers-1.4.0/studio_workers/engines/comfyui/server.py +197 -0
  31. studio_workers-1.4.0/studio_workers/engines/comfyui/settings.py +38 -0
  32. studio_workers-1.4.0/studio_workers/engines/comfyui/templates.py +390 -0
  33. studio_workers-1.4.0/studio_workers/engines/comfyui/workflows/comfyui_imgedit.json +144 -0
  34. studio_workers-1.4.0/studio_workers/engines/comfyui/workflows/comfyui_txt2img.json +162 -0
  35. studio_workers-1.4.0/studio_workers/engines/comfyui/workflows/comfyui_txt2img_flux2.json +108 -0
  36. studio_workers-1.4.0/studio_workers/engines/comfyui/workflows/comfyui_txt2img_flux2_9b.json +150 -0
  37. studio_workers-1.4.0/studio_workers/engines/comfyui/workflows/comfyui_txt2img_fp8.json +104 -0
  38. studio_workers-1.4.0/studio_workers/engines/comfyui/workflows/comfyui_txt2img_gguf.json +103 -0
  39. studio_workers-1.4.0/studio_workers/engines/general/__init__.py +1 -0
  40. studio_workers-1.4.0/studio_workers/engines/general/executor.py +1804 -0
  41. studio_workers-1.4.0/studio_workers/engines/general/handler.py +300 -0
  42. studio_workers-1.4.0/studio_workers/engines/transfer/__init__.py +3 -0
  43. studio_workers-1.4.0/studio_workers/engines/transfer/handler.py +827 -0
  44. studio_workers-1.4.0/studio_workers/engines/video/__init__.py +72 -0
  45. studio_workers-1.4.0/studio_workers/engines/video/common.py +443 -0
  46. studio_workers-1.4.0/studio_workers/engines/video/create_video.py +709 -0
  47. studio_workers-1.4.0/studio_workers/engines/video/fonts/ComicNeue-Bold.ttf +0 -0
  48. studio_workers-1.4.0/studio_workers/engines/video/fonts/ComicNeue-Regular.ttf +0 -0
  49. studio_workers-1.4.0/studio_workers/engines/video/fonts/FrederickatheGreat-Regular.ttf +0 -0
  50. studio_workers-1.4.0/studio_workers/engines/video/fonts/LibreBaskerville-Regular.ttf +0 -0
  51. studio_workers-1.4.0/studio_workers/engines/video/fonts/Lobster-Regular.ttf +0 -0
  52. studio_workers-1.4.0/studio_workers/engines/video/fonts/LuckiestGuy-Regular.ttf +0 -0
  53. studio_workers-1.4.0/studio_workers/engines/video/fonts/NanumPenScript-Regular.ttf +0 -0
  54. studio_workers-1.4.0/studio_workers/engines/video/fonts/NotoSansKR-Bold.ttf +0 -0
  55. studio_workers-1.4.0/studio_workers/engines/video/fonts/NotoSansKR-Regular.ttf +0 -0
  56. studio_workers-1.4.0/studio_workers/engines/video/fonts/NotoSansSC-Regular.ttf +0 -0
  57. studio_workers-1.4.0/studio_workers/engines/video/fonts/NotoSansTC-Regular.ttf +0 -0
  58. studio_workers-1.4.0/studio_workers/engines/video/fonts/Nunito-Regular.ttf +0 -0
  59. studio_workers-1.4.0/studio_workers/engines/video/fonts/Oswald-Bold.ttf +0 -0
  60. studio_workers-1.4.0/studio_workers/engines/video/fonts/Oswald-Regular.ttf +0 -0
  61. studio_workers-1.4.0/studio_workers/engines/video/fonts/Pacifico-Regular.ttf +0 -0
  62. studio_workers-1.4.0/studio_workers/engines/video/fonts/PermanentMarker-Regular.ttf +0 -0
  63. studio_workers-1.4.0/studio_workers/engines/video/fonts/Roboto-Regular.ttf +0 -0
  64. studio_workers-1.4.0/studio_workers/engines/video/fonts/Shrikhand-Regular.ttf +0 -0
  65. studio_workers-1.4.0/studio_workers/engines/video/fonts/licenses/Comic_Neue-OFL.txt +93 -0
  66. studio_workers-1.4.0/studio_workers/engines/video/fonts/licenses/Fredericka_the_Great-OFL.txt +94 -0
  67. studio_workers-1.4.0/studio_workers/engines/video/fonts/licenses/Libre_Baskerville-OFL.txt +94 -0
  68. studio_workers-1.4.0/studio_workers/engines/video/fonts/licenses/Lobster-OFL.txt +93 -0
  69. studio_workers-1.4.0/studio_workers/engines/video/fonts/licenses/LuckiestGuy-OFL.txt +94 -0
  70. studio_workers-1.4.0/studio_workers/engines/video/fonts/licenses/Nanum_Pen_Script-OFL.txt +97 -0
  71. studio_workers-1.4.0/studio_workers/engines/video/fonts/licenses/Noto_Sans_KR-OFL.txt +93 -0
  72. studio_workers-1.4.0/studio_workers/engines/video/fonts/licenses/Noto_Sans_SC-OFL.txt +93 -0
  73. studio_workers-1.4.0/studio_workers/engines/video/fonts/licenses/Noto_Sans_TC-OFL.txt +93 -0
  74. studio_workers-1.4.0/studio_workers/engines/video/fonts/licenses/Nunito-OFL.txt +93 -0
  75. studio_workers-1.4.0/studio_workers/engines/video/fonts/licenses/Oswald-OFL.txt +93 -0
  76. studio_workers-1.4.0/studio_workers/engines/video/fonts/licenses/Pacifico-OFL.txt +93 -0
  77. studio_workers-1.4.0/studio_workers/engines/video/fonts/licenses/Permanent_Marker-LICENSE.txt +202 -0
  78. studio_workers-1.4.0/studio_workers/engines/video/fonts/licenses/Roboto-LICENSE.txt +202 -0
  79. studio_workers-1.4.0/studio_workers/engines/video/fonts/licenses/Shrikhand-OFL.txt +93 -0
  80. studio_workers-1.4.0/studio_workers/engines/video/handler.py +1048 -0
  81. studio_workers-1.4.0/studio_workers/engines/video/merge_video_audio.py +525 -0
  82. studio_workers-1.4.0/studio_workers/engines/video/normalize.py +751 -0
  83. studio_workers-1.4.0/studio_workers/engines/video/settings.py +41 -0
  84. studio_workers-1.4.0/studio_workers/engines/video/subtitle_utils.py +923 -0
  85. studio_workers-1.4.0/studio_workers/engines/video/utils.py +428 -0
  86. studio_workers-1.4.0/studio_workers/env_files.py +17 -0
  87. studio_workers-1.4.0/studio_workers/settings.py +147 -0
  88. studio_workers-1.4.0/studio_workers/utils/__init__.py +29 -0
  89. studio_workers-1.4.0/studio_workers/utils/cf_access.py +17 -0
  90. studio_workers-1.4.0/studio_workers/utils/credential_client.py +176 -0
  91. studio_workers-1.4.0/studio_workers/utils/dead_letter.py +91 -0
  92. studio_workers-1.4.0/studio_workers/utils/error_codes.py +33 -0
  93. studio_workers-1.4.0/studio_workers/utils/file_upload_client.py +331 -0
  94. studio_workers-1.4.0/studio_workers/utils/http_job_client.py +251 -0
  95. studio_workers-1.4.0/studio_workers/utils/logging_config.py +463 -0
  96. studio_workers-1.4.0/studio_workers/utils/redaction.py +26 -0
  97. studio_workers-1.4.0/studio_workers/utils/result_publisher.py +128 -0
  98. studio_workers-1.4.0/studio_workers/utils/security.py +253 -0
  99. studio_workers-1.4.0/studio_workers/utils/storage.py +117 -0
  100. studio_workers-1.4.0/studio_workers/utils/worker_base.py +519 -0
  101. studio_workers-1.4.0/studio_workers/worker.py +166 -0
  102. studio_workers-1.4.0/studio_workers/worker_types.py +390 -0
  103. studio_workers-1.4.0/studio_workers.egg-info/PKG-INFO +86 -0
  104. studio_workers-1.4.0/studio_workers.egg-info/SOURCES.txt +107 -0
  105. studio_workers-1.4.0/studio_workers.egg-info/entry_points.txt +2 -0
  106. studio_workers-1.4.0/studio_workers.egg-info/requires.txt +33 -0
  107. studio_workers-1.4.0/studio_workers.egg-info/top_level.txt +1 -0
  108. studio_workers-0.0.1/PKG-INFO +0 -17
  109. studio_workers-0.0.1/README.md +0 -8
  110. studio_workers-0.0.1/pyproject.toml +0 -19
  111. studio_workers-0.0.1/studio_workers.egg-info/PKG-INFO +0 -17
  112. studio_workers-0.0.1/studio_workers.egg-info/SOURCES.txt +0 -6
  113. studio_workers-0.0.1/studio_workers.egg-info/top_level.txt +0 -1
  114. {studio_workers-0.0.1 → studio_workers-1.4.0}/setup.cfg +0 -0
  115. {studio_workers-0.0.1 → studio_workers-1.4.0}/studio_workers.egg-info/dependency_links.txt +0 -0
@@ -0,0 +1,141 @@
1
+ Studio Use License
2
+
3
+ Copyright (c) 2025-2026 Kawika Ohumukini. All rights reserved.
4
+
5
+ This license governs the use of the Studio software platform ("Software"),
6
+ including its API, frontend, worker, and documentation components.
7
+
8
+ This is not an open-source license. The source code is publicly available
9
+ for transparency and community contribution, but commercial use is subject
10
+ to the terms below.
11
+
12
+
13
+ 1. PERMITTED USE
14
+
15
+ You may:
16
+
17
+ (a) Deploy and operate the Software for your own business purposes.
18
+
19
+ (b) Operate a hosted instance of the Software and charge customers for
20
+ access to it (SaaS model). There is no limit on the number of
21
+ customers or organizations on your instance.
22
+
23
+ (c) Provide consulting, installation, deployment, configuration, training,
24
+ and support services related to the Software.
25
+
26
+ (d) Deploy the Software for individual clients as part of bespoke service
27
+ engagements (one instance per client engagement).
28
+
29
+ (e) Provide the Software source code to your clients so they may deploy
30
+ and operate their own instance.
31
+
32
+ (f) Create, sell, and distribute your own workflow configurations,
33
+ templates, and automations. These are your intellectual property.
34
+
35
+ (g) Sell any output produced by the Software (images, video, text, data,
36
+ or any other generated content).
37
+
38
+ (h) Modify the Software for your own use.
39
+
40
+ (i) Contribute to this project.
41
+
42
+
43
+ 2. RESTRICTIONS
44
+
45
+ You may not:
46
+
47
+ (a) Offer automated or self-service provisioning of Software instances
48
+ to third parties. You may not build a platform, tool, or service
49
+ whose primary function is deploying, hosting, or managing Software
50
+ instances on behalf of others.
51
+
52
+ (b) Distribute or sell pre-configured images, containers, deployment
53
+ packages, automation, or infrastructure templates that contain or
54
+ install the Software for third-party use.
55
+
56
+ Installing the Software on a client's own infrastructure using the
57
+ official Studio Console, as part of a service engagement, is
58
+ permitted and is not distribution under this section.
59
+
60
+ (c) Fork or rebrand the Software for resale or distribution as a
61
+ competing workflow automation platform or service.
62
+
63
+ (d) Redistribute, sublicense, or sell copies of the Software source
64
+ code as a standalone product or component.
65
+
66
+ (e) Remove or alter any copyright, trademark, or license notices
67
+ included in the Software.
68
+
69
+ The distinction this license draws is between OPERATING and DISTRIBUTING.
70
+ You may build a business that runs on Studio. You may not build a business
71
+ that distributes Studio.
72
+
73
+ For avoidance of doubt: operating a single Studio deployment as a
74
+ multi-tenant service for customers is permitted. Offering Studio
75
+ deployment, provisioning, or instance creation as a service to third
76
+ parties is not.
77
+
78
+
79
+ 3. MARKETPLACE PACKAGES
80
+
81
+ The Studio Marketplace (the studio-community and studio-plus repositories)
82
+ provides optional provider packages, services, and templates under separate
83
+ terms. Marketplace packages:
84
+
85
+ (a) Are licensed separately from this Software under their own terms.
86
+
87
+ (b) May not be redistributed, sideloaded, bundled, or transferred to
88
+ third-party deployments. Each deploying party must obtain marketplace
89
+ packages directly from the official marketplace.
90
+
91
+ (c) Are subject to the Studio Marketplace Package License, which governs
92
+ access, usage, and distribution.
93
+
94
+ For clarity: workflow configurations that reference marketplace packages
95
+ may be freely shared or sold. The marketplace packages themselves may not.
96
+
97
+ Forking the public catalog repository to propose a contribution is
98
+ permitted. Publishing or distributing packages from such a fork is not.
99
+
100
+
101
+ 4. USER-CREATED CONTENT
102
+
103
+ Workflow configurations, templates, automations, field mappings, and other
104
+ user-created content (typically JSON or similar configuration files) are the
105
+ intellectual property of their creators. This license makes no claim on
106
+ user-created content.
107
+
108
+
109
+ 5. TRADEMARKS
110
+
111
+ "Studio," "Self-Host Studio," "Self-Host Hub," and associated logos and
112
+ marks are trademarks of the copyright holder. Use of these trademarks is
113
+ subject to applicable trademark law. You may use these names to truthfully
114
+ describe your relationship with the Software (e.g., "built on Self-Host
115
+ Studio," "powered by Studio"). You may not use the trademarks in a way
116
+ that implies endorsement, affiliation, or origin without prior written
117
+ consent. Nothing in this license grants the right to use these names or
118
+ marks as part of a product name, company name, or primary branding.
119
+
120
+
121
+ 6. CONTRIBUTIONS
122
+
123
+ Contributions to this project are assigned to the copyright holder and
124
+ their successors and assigns. By submitting a contribution, you agree to
125
+ this assignment and represent that you have the right to make the
126
+ contribution.
127
+
128
+
129
+ 7. NO WARRANTY
130
+
131
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
132
+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
133
+ FITNESS FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT.
134
+
135
+ IN NO EVENT SHALL THE AUTHORS, COPYRIGHT HOLDERS, OR CONTRIBUTORS BE LIABLE
136
+ FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY, WHETHER IN AN ACTION OF
137
+ CONTRACT, TORT, OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION WITH
138
+ THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
139
+
140
+ Operators are solely responsible for compliance with applicable laws and
141
+ third-party service terms. See LEGAL.md for detailed operator obligations.
@@ -0,0 +1,86 @@
1
+ Metadata-Version: 2.4
2
+ Name: studio-workers
3
+ Version: 1.4.0
4
+ Summary: Studio workflow job workers
5
+ Author: Kawika Ohumukini
6
+ License-Expression: LicenseRef-Studio-Use-License
7
+ Requires-Python: >=3.12
8
+ Description-Content-Type: text/markdown
9
+ License-File: LICENSE
10
+ Requires-Dist: httpx>=0.28.1
11
+ Requires-Dist: psutil>=5.9.0
12
+ Requires-Dist: jsonpath-ng>=1.6.0
13
+ Requires-Dist: Pillow>=10.0.0
14
+ Requires-Dist: rich>=13.0.0
15
+ Requires-Dist: mutagen>=1.47.0
16
+ Requires-Dist: pydantic>=2.0.0
17
+ Requires-Dist: pydantic-settings>=2.0.0
18
+ Provides-Extra: audio
19
+ Requires-Dist: chatterbox-tts; extra == "audio"
20
+ Requires-Dist: torch; extra == "audio"
21
+ Requires-Dist: soundfile; extra == "audio"
22
+ Requires-Dist: setuptools<81,>=78.1.1; extra == "audio"
23
+ Provides-Extra: comfyui
24
+ Requires-Dist: websocket-client>=1.6.0; extra == "comfyui"
25
+ Provides-Extra: video
26
+ Requires-Dist: openai-whisper>=20231117; extra == "video"
27
+ Requires-Dist: faster-whisper>=1.0.0; extra == "video"
28
+ Provides-Extra: test
29
+ Requires-Dist: pytest>=8.0.0; extra == "test"
30
+ Requires-Dist: pytest-asyncio>=0.23.0; extra == "test"
31
+ Requires-Dist: pytest-cov>=4.1.0; extra == "test"
32
+ Requires-Dist: pytest-httpx>=0.30.0; extra == "test"
33
+ Requires-Dist: pytest-xdist>=3.0.0; extra == "test"
34
+ Requires-Dist: respx>=0.21.0; extra == "test"
35
+ Requires-Dist: freezegun>=1.2.0; extra == "test"
36
+ Requires-Dist: pyright>=1.1.405; extra == "test"
37
+ Requires-Dist: flake8>=7.1.1; extra == "test"
38
+ Requires-Dist: python-dotenv>=1.2.2; extra == "test"
39
+ Dynamic: license-file
40
+
41
+ # studio-workers
42
+
43
+ Native (no-Docker) workers for [SelfHostHub Studio](https://www.skool.com/selfhosthub). Run any Studio worker engine directly on the host, pointed at your Studio API. This is the supported path for GPU work on Apple Silicon, where Docker cannot reach the Mac GPU, and for installing workers on demand inside a single GPU pod.
44
+
45
+ ## Install
46
+
47
+ Pick the extra for the engine you want to run:
48
+
49
+ ```bash
50
+ pip install "studio-workers[audio]" # TTS (Chatterbox, torch)
51
+ pip install "studio-workers[video]" # transcription and video (whisper)
52
+ pip install "studio-workers[comfyui]" # ComfyUI bridge
53
+ pip install "studio-workers" # general + transfer (CPU only)
54
+ ```
55
+
56
+ The `[audio]` extra currently requires Python 3.12: its Chatterbox dependency chain does not yet resolve on 3.13 (the install fails with an `llvmlite` build error). The other extras run on 3.12+.
57
+
58
+ ## Version pinning
59
+
60
+ Install the `studio-workers` version that matches your Studio release; the release notes name it. The API checks the worker version at registration and refuses a mismatch.
61
+
62
+ ## Check the install
63
+
64
+ ```bash
65
+ studio-workers doctor --engine audio
66
+ ```
67
+
68
+ `doctor` fails loudly when a GPU host would silently run on CPU: it asserts torch sees MPS on Apple Silicon and CUDA when an NVIDIA driver is present.
69
+
70
+ ## Run
71
+
72
+ ```bash
73
+ SHS_API_BASE_URL=https://your-studio-api \
74
+ SHS_PUBLIC_BASE_URL=https://your-studio \
75
+ SHS_WORKER_SHARED_SECRET=... \
76
+ SHS_WORKSPACE_ROOT=/path/to/workspace \
77
+ studio-workers run --type audio
78
+ ```
79
+
80
+ (`python -m studio_workers.worker` with `SHS_WORKER_TYPE` set still works; `run` is the same loop.)
81
+
82
+ The worker polls the API for jobs over HTTP; no inbound port is required. See your Studio instance's worker access documentation for the sanctioned connection paths.
83
+
84
+ ## Third-party software
85
+
86
+ Installing an engine extra pulls third-party packages (torch, Chatterbox TTS, whisper, ComfyUI dependencies) from their own upstreams under their own licenses; model weights download on first run.
@@ -0,0 +1,56 @@
1
+ # Studio Worker
2
+
3
+ > **Community & support:** [SelfHostHub Community](https://www.skool.com/selfhosthub) · [Innovators (Plus)](https://www.skool.com/selfhostinnovators)
4
+
5
+ HTTP polling-based background job processor for Studio.
6
+
7
+ ## Overview
8
+
9
+ Handles async tasks: image generation, video processing, webhook delivery.
10
+
11
+ ## Local Development (Mac)
12
+
13
+ ### Prerequisites
14
+
15
+ ```bash
16
+ # ffmpeg with libass (required for subtitle burn)
17
+ # The default Homebrew bottle does NOT include libass.
18
+ brew tap homebrew-ffmpeg/ffmpeg
19
+ brew install homebrew-ffmpeg/ffmpeg/ffmpeg
20
+
21
+ # Verify the ass filter is available
22
+ ffmpeg -filters 2>&1 | grep -w ass
23
+ ```
24
+
25
+ If you previously installed the default `ffmpeg` formula, replace it:
26
+
27
+ ```bash
28
+ brew uninstall ffmpeg
29
+ brew install homebrew-ffmpeg/ffmpeg/ffmpeg
30
+ ```
31
+
32
+ ### Video Worker
33
+
34
+ ```bash
35
+ make video-local-setup # Creates venv, installs deps, fonts
36
+ make video-run-local # Starts worker (polls API at localhost:8000)
37
+ ```
38
+
39
+ **Apple Silicon GPU (MPS)**: Whisper transcription runs on the MPS GPU automatically. A monkey-patch handles Whisper's DTW alignment step which calls `.double()` (float64) - unsupported on MPS - by moving that tensor to CPU before conversion. Inference stays on GPU.
40
+
41
+ ### Audio Worker
42
+
43
+ ```bash
44
+ make audio-local-setup # Creates venv, installs Chatterbox TTS
45
+ make audio-run-local # Starts worker (MPS/CPU)
46
+ ```
47
+
48
+ ## Documentation
49
+
50
+ See [docs/](../docs/) in the monorepo for full documentation.
51
+
52
+ ## License
53
+
54
+ Studio Use License - See [LICENSE](LICENSE)
55
+
56
+ For operator responsibilities and legal notices, see [LEGAL.md](LEGAL.md).
@@ -0,0 +1,46 @@
1
+ # studio-workers
2
+
3
+ Native (no-Docker) workers for [SelfHostHub Studio](https://www.skool.com/selfhosthub). Run any Studio worker engine directly on the host, pointed at your Studio API. This is the supported path for GPU work on Apple Silicon, where Docker cannot reach the Mac GPU, and for installing workers on demand inside a single GPU pod.
4
+
5
+ ## Install
6
+
7
+ Pick the extra for the engine you want to run:
8
+
9
+ ```bash
10
+ pip install "studio-workers[audio]" # TTS (Chatterbox, torch)
11
+ pip install "studio-workers[video]" # transcription and video (whisper)
12
+ pip install "studio-workers[comfyui]" # ComfyUI bridge
13
+ pip install "studio-workers" # general + transfer (CPU only)
14
+ ```
15
+
16
+ The `[audio]` extra currently requires Python 3.12: its Chatterbox dependency chain does not yet resolve on 3.13 (the install fails with an `llvmlite` build error). The other extras run on 3.12+.
17
+
18
+ ## Version pinning
19
+
20
+ Install the `studio-workers` version that matches your Studio release; the release notes name it. The API checks the worker version at registration and refuses a mismatch.
21
+
22
+ ## Check the install
23
+
24
+ ```bash
25
+ studio-workers doctor --engine audio
26
+ ```
27
+
28
+ `doctor` fails loudly when a GPU host would silently run on CPU: it asserts torch sees MPS on Apple Silicon and CUDA when an NVIDIA driver is present.
29
+
30
+ ## Run
31
+
32
+ ```bash
33
+ SHS_API_BASE_URL=https://your-studio-api \
34
+ SHS_PUBLIC_BASE_URL=https://your-studio \
35
+ SHS_WORKER_SHARED_SECRET=... \
36
+ SHS_WORKSPACE_ROOT=/path/to/workspace \
37
+ studio-workers run --type audio
38
+ ```
39
+
40
+ (`python -m studio_workers.worker` with `SHS_WORKER_TYPE` set still works; `run` is the same loop.)
41
+
42
+ The worker polls the API for jobs over HTTP; no inbound port is required. See your Studio instance's worker access documentation for the sanctioned connection paths.
43
+
44
+ ## Third-party software
45
+
46
+ Installing an engine extra pulls third-party packages (torch, Chatterbox TTS, whisper, ComfyUI dependencies) from their own upstreams under their own licenses; model weights download on first run.
@@ -0,0 +1,104 @@
1
+ [build-system]
2
+ requires = ["setuptools>=77"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "studio-workers"
7
+ dynamic = ["version"]
8
+ description = "Studio workflow job workers"
9
+ readme = "README.pypi.md"
10
+ requires-python = ">=3.12"
11
+ license = "LicenseRef-Studio-Use-License"
12
+ license-files = ["LICENSE"]
13
+ authors = [
14
+ {name = "Kawika Ohumukini"}
15
+ ]
16
+ dependencies = [
17
+ "httpx>=0.28.1",
18
+ "psutil>=5.9.0",
19
+ "jsonpath-ng>=1.6.0",
20
+ "Pillow>=10.0.0",
21
+ "rich>=13.0.0",
22
+ "mutagen>=1.47.0",
23
+ "pydantic>=2.0.0",
24
+ "pydantic-settings>=2.0.0",
25
+ ]
26
+
27
+ [project.scripts]
28
+ studio-workers = "studio_workers.cli:main"
29
+
30
+ [project.optional-dependencies]
31
+ audio = [
32
+ "chatterbox-tts",
33
+ "torch",
34
+ "soundfile",
35
+ # resemble-perth needs pkg_resources (gone in setuptools 82, warns in 81); uv venvs ship
36
+ # no setuptools at all, so the extra must provide a CVE-fixed one itself.
37
+ "setuptools>=78.1.1,<81",
38
+ ]
39
+ comfyui = [
40
+ "websocket-client>=1.6.0",
41
+ ]
42
+ video = [
43
+ "openai-whisper>=20231117",
44
+ "faster-whisper>=1.0.0",
45
+ ]
46
+ test = [
47
+ "pytest>=8.0.0",
48
+ "pytest-asyncio>=0.23.0",
49
+ "pytest-cov>=4.1.0",
50
+ "pytest-httpx>=0.30.0",
51
+ "pytest-xdist>=3.0.0",
52
+ "respx>=0.21.0",
53
+ "freezegun>=1.2.0",
54
+ "pyright>=1.1.405",
55
+ "flake8>=7.1.1",
56
+ "python-dotenv>=1.2.2", # test-time only: conftest.py pre-loads envs/.env.test before shared.settings imports
57
+ ]
58
+
59
+ [tool.setuptools.dynamic]
60
+ version = {attr = "studio_workers.contracts.version.WORKERS_VERSION"}
61
+
62
+ [tool.pytest.ini_options]
63
+ testpaths = ["tests"]
64
+ python_files = ["test_*.py"]
65
+ python_functions = ["test_*"]
66
+ asyncio_mode = "auto"
67
+ asyncio_default_fixture_loop_scope = "function"
68
+ addopts = [
69
+ "-v",
70
+ "--tb=short",
71
+ "--strict-markers",
72
+ ]
73
+ markers = [
74
+ "unit: Unit tests (no external dependencies)",
75
+ "integration: Integration tests (may need mocked services)",
76
+ "slow: Tests that take significant time",
77
+ ]
78
+
79
+ [tool.coverage.run]
80
+ source = ["studio_workers"]
81
+ branch = true
82
+ omit = [
83
+ "*/tests/*",
84
+ "*/.venv/*",
85
+ # Shared contracts package: gated by api/tests/unit/contracts, not the workers suite.
86
+ "studio_workers/contracts/*",
87
+ ]
88
+
89
+ [tool.coverage.report]
90
+ exclude_lines = [
91
+ "pragma: no cover",
92
+ "if TYPE_CHECKING:",
93
+ "if __name__ == .__main__.:",
94
+ "raise NotImplementedError",
95
+ ]
96
+ show_missing = true
97
+
98
+ [tool.setuptools.packages.find]
99
+ include = ["studio_workers*"]
100
+ exclude = ["tests*", "docker*", "typestubs*"]
101
+
102
+ [tool.setuptools.package-data]
103
+ "studio_workers.engines.comfyui" = ["presets/*.json", "workflows/*.json"]
104
+ "studio_workers.engines.video" = ["fonts/*", "fonts/licenses/*"]
@@ -0,0 +1,3 @@
1
+ # workers/studio_workers/__init__.py
2
+
3
+ """Shared utilities, constants, and base classes for all worker types."""
@@ -0,0 +1,3 @@
1
+ # workers/studio_workers/adapters/__init__.py
2
+
3
+ # Worker adapters - standalone implementations for worker execution
@@ -0,0 +1,108 @@
1
+ # workers/studio_workers/cli.py
2
+
3
+ """studio-workers console entrypoint: `doctor` (environment check) and `run` (start a worker)."""
4
+
5
+ import argparse
6
+ import platform
7
+ import shutil
8
+ import sys
9
+
10
+ from studio_workers.contracts.version import WORKERS_VERSION
11
+
12
+ # Engines whose inference runs torch locally; the others need no GPU here
13
+ # (comfyui proxies to an external ComfyUI server, general/transfer are CPU).
14
+ TORCH_ENGINES = {"audio", "video"}
15
+
16
+
17
+ def _expected_accelerator() -> str | None:
18
+ """The accelerator this host should expose: mps on Apple Silicon, cuda when
19
+ an NVIDIA driver is present, else None (CPU is legitimate)."""
20
+ if sys.platform == "darwin" and platform.machine() == "arm64":
21
+ return "mps"
22
+ if sys.platform.startswith("linux") and shutil.which("nvidia-smi"):
23
+ return "cuda"
24
+ return None
25
+
26
+
27
+ def _torch_device() -> tuple[str | None, str | None]:
28
+ """(device, torch version); device None when torch is not installed."""
29
+ try:
30
+ import torch
31
+ except ImportError:
32
+ return None, None
33
+ if torch.cuda.is_available():
34
+ return "cuda", torch.__version__
35
+ if hasattr(torch.backends, "mps") and torch.backends.mps.is_available():
36
+ return "mps", torch.__version__
37
+ return "cpu", torch.__version__
38
+
39
+
40
+ def doctor(engine: str | None) -> int:
41
+ """Report the environment and fail loudly when a GPU host would silently run on CPU."""
42
+ print(f"studio-workers {WORKERS_VERSION}")
43
+ print(f"python {platform.python_version()} on {sys.platform}/{platform.machine()}")
44
+
45
+ expected = _expected_accelerator()
46
+ device, torch_version = _torch_device()
47
+
48
+ if device is None:
49
+ if engine in TORCH_ENGINES:
50
+ print(
51
+ f"ERROR: torch is not installed but the {engine} engine needs it. "
52
+ f'Install the engine extra: pip install "studio-workers[{engine}]=={WORKERS_VERSION}"'
53
+ )
54
+ return 1
55
+ print("torch: not installed (not required for this engine)")
56
+ return 0
57
+
58
+ print(f"torch {torch_version}, device={device}")
59
+ if expected and device == "cpu":
60
+ hint = (
61
+ "Docker cannot reach the Mac GPU; run natively (this install) with an MPS-enabled torch wheel."
62
+ if expected == "mps"
63
+ else "Reinstall torch from the matching CUDA index (https://download.pytorch.org/whl/) for your driver."
64
+ )
65
+ print(
66
+ f"ERROR: this host exposes {expected} but torch only sees the CPU; "
67
+ f"generation would run uselessly slow instead of failing. {hint}"
68
+ )
69
+ return 1
70
+ return 0
71
+
72
+
73
+ def run(worker_type: str | None) -> int:
74
+ """Start the worker loop; --type overrides SHS_WORKER_TYPE."""
75
+ import os
76
+
77
+ if worker_type:
78
+ os.environ["SHS_WORKER_TYPE"] = worker_type
79
+
80
+ from studio_workers import worker
81
+
82
+ worker.main()
83
+ return 0
84
+
85
+
86
+ def main(argv: list[str] | None = None) -> int:
87
+ parser = argparse.ArgumentParser(prog="studio-workers")
88
+ parser.add_argument("--version", action="version", version=WORKERS_VERSION)
89
+ sub = parser.add_subparsers(dest="command", required=True)
90
+
91
+ p_doctor = sub.add_parser("doctor", help="check this host can run a worker")
92
+ p_doctor.add_argument(
93
+ "--engine",
94
+ choices=["general", "transfer", "video", "audio", "comfyui"],
95
+ help="engine you intend to run; makes torch mandatory for audio/video",
96
+ )
97
+
98
+ p_run = sub.add_parser("run", help="start a worker (same loop as python -m studio_workers.worker)")
99
+ p_run.add_argument("--type", dest="worker_type", help="worker type; overrides SHS_WORKER_TYPE")
100
+
101
+ args = parser.parse_args(argv)
102
+ if args.command == "doctor":
103
+ return doctor(args.engine)
104
+ return run(args.worker_type)
105
+
106
+
107
+ if __name__ == "__main__":
108
+ sys.exit(main())
@@ -0,0 +1 @@
1
+ # workers/studio_workers/constants.py
@@ -0,0 +1,82 @@
1
+ # workers/studio_workers/contracts/__init__.py
2
+
3
+ """
4
+ Shared contract types between API and workers.
5
+
6
+ Pydantic models define canonical payload shapes for cross-component
7
+ communication. Log schema defines canonical JSON log field names.
8
+ Both sides import from here - one source of truth.
9
+
10
+ Python 3.11 compatible (workers use 3.11, API uses 3.12).
11
+ """
12
+
13
+ from studio_workers.contracts.log_schema import (
14
+ FIELD_CORRELATION_ID,
15
+ FIELD_EXCEPTION,
16
+ FIELD_EXTRA,
17
+ FIELD_HOST,
18
+ FIELD_LEVEL,
19
+ FIELD_LOGGER,
20
+ FIELD_MESSAGE,
21
+ FIELD_SERVICE,
22
+ FIELD_TIMESTAMP,
23
+ VERBOSITY_FULL,
24
+ VERBOSITY_MINIMAL,
25
+ VERBOSITY_STANDARD,
26
+ format_timestamp,
27
+ get_log_verbosity,
28
+ )
29
+ from studio_workers.contracts.redaction import (
30
+ PII_KEYS,
31
+ REDACTED_PLACEHOLDER,
32
+ SENSITIVE_KEYS,
33
+ is_pii_key,
34
+ is_sensitive_key,
35
+ redact_sensitive_data,
36
+ redact_url,
37
+ )
38
+
39
+ __all__ = [
40
+ "DownloadedFileContract",
41
+ "JobClaimContract",
42
+ "StepResultContract",
43
+ "FIELD_TIMESTAMP",
44
+ "FIELD_LEVEL",
45
+ "FIELD_SERVICE",
46
+ "FIELD_MESSAGE",
47
+ "FIELD_HOST",
48
+ "FIELD_LOGGER",
49
+ "FIELD_CORRELATION_ID",
50
+ "FIELD_EXCEPTION",
51
+ "FIELD_EXTRA",
52
+ "VERBOSITY_MINIMAL",
53
+ "VERBOSITY_STANDARD",
54
+ "VERBOSITY_FULL",
55
+ "get_log_verbosity",
56
+ "format_timestamp",
57
+ "SENSITIVE_KEYS",
58
+ "PII_KEYS",
59
+ "REDACTED_PLACEHOLDER",
60
+ "is_sensitive_key",
61
+ "is_pii_key",
62
+ "redact_sensitive_data",
63
+ "redact_url",
64
+ ]
65
+
66
+
67
+ def __getattr__(name: str):
68
+ """Lazy-import pydantic-dependent contracts to avoid pulling in pydantic
69
+ for lightweight consumers (e.g., workers that only need log_schema)."""
70
+ if name == "DownloadedFileContract":
71
+ from studio_workers.contracts.downloaded_file import DownloadedFileContract
72
+
73
+ return DownloadedFileContract
74
+ if name == "JobClaimContract":
75
+ from studio_workers.contracts.job_claim import JobClaimContract
76
+
77
+ return JobClaimContract
78
+ if name == "StepResultContract":
79
+ from studio_workers.contracts.step_result import StepResultContract
80
+
81
+ return StepResultContract
82
+ raise AttributeError(f"module 'contracts' has no attribute {name!r}")