badgr-cli 1.0.37 → 1.0.39
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/package.json +5 -7
- package/src/badgr.js +23 -1
- package/src/catalog.js +479 -0
- package/src/commands/comfyui.js +1 -1
- package/src/commands/receipts.js +2 -1
- package/src/commands/run.js +134 -25
- package/src/commands/serve.js +100 -10
- package/src/commands/template.js +119 -0
- package/src/commands/test-run.js +4 -4
- package/src/commands/workload.js +197 -0
- package/src/commands/workspace.js +136 -0
- package/tests/commands.test.js +48 -0
- package/tests/run-lifecycle.test.js +55 -18
- package/tests/serve-lifecycle.test.js +165 -0
- package/tests/template.test.js +551 -0
- package/tests/workload-rerun.test.js +56 -0
- package/tests/workload-templates.test.js +1 -1
- package/tests/workload-workspace-paths.test.js +46 -0
package/README.md
CHANGED
|
@@ -275,7 +275,7 @@ Additional GPU types may be routable depending on current capacity — check wit
|
|
|
275
275
|
|
|
276
276
|
Pricing is confirmed before provisioning. Use `--dry-run` to see pricing before committing.
|
|
277
277
|
|
|
278
|
-
Full GPU support details: see [
|
|
278
|
+
Full GPU support details: see [NOTES.md](../../NOTES.md#gpu-support) in the repo root.
|
|
279
279
|
|
|
280
280
|
---
|
|
281
281
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "badgr-cli",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "Badgr, run or serve GPU workloads from one command",
|
|
3
|
+
"version": "1.0.39",
|
|
4
|
+
"description": "DEPRECATED: Use badgr-agent CLI instead. Badgr, run or serve GPU workloads from one command",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"badgr": "src/badgr.js"
|
|
@@ -12,23 +12,21 @@
|
|
|
12
12
|
"test:watch": "vitest"
|
|
13
13
|
},
|
|
14
14
|
"dependencies": {
|
|
15
|
-
"
|
|
16
|
-
"chalk": "^5.3.0"
|
|
15
|
+
"badgr-agent": "1.0.0"
|
|
17
16
|
},
|
|
18
17
|
"devDependencies": {
|
|
19
18
|
"vitest": "^4.1.8"
|
|
20
19
|
},
|
|
21
20
|
"engines": {
|
|
22
|
-
"node": ">=
|
|
21
|
+
"node": ">=20.10.0"
|
|
23
22
|
},
|
|
24
23
|
"keywords": [
|
|
25
24
|
"gpu",
|
|
26
25
|
"cli",
|
|
27
26
|
"ai",
|
|
28
27
|
"compute",
|
|
29
|
-
"modal",
|
|
30
28
|
"gateway",
|
|
31
29
|
"openai"
|
|
32
30
|
],
|
|
33
|
-
"license": "
|
|
31
|
+
"license": "Apache-2.0"
|
|
34
32
|
}
|
package/src/badgr.js
CHANGED
|
@@ -17,6 +17,9 @@ import { comfyuiCommand } from './commands/comfyui.js';
|
|
|
17
17
|
import { trainCommand } from './commands/train.js';
|
|
18
18
|
import { transcribeCommand } from './commands/transcribe.js';
|
|
19
19
|
import { embedCommand } from './commands/embed.js';
|
|
20
|
+
import { templateCommand } from './commands/template.js';
|
|
21
|
+
import { workloadCommand } from './commands/workload.js';
|
|
22
|
+
import { workspaceCommand } from './commands/workspace.js';
|
|
20
23
|
|
|
21
24
|
const HELP = `
|
|
22
25
|
${chalk.bold('badgr')} — run or serve GPU workloads from one command
|
|
@@ -32,12 +35,19 @@ ${chalk.bold('COMMANDS')}
|
|
|
32
35
|
${chalk.cyan('badgr test')} Run an end-to-end test (provision → run → teardown)
|
|
33
36
|
${chalk.cyan('badgr capacity')} Check what GPU capacity is available right now
|
|
34
37
|
${chalk.cyan('badgr billing')} Show balance and add funds
|
|
38
|
+
${chalk.cyan('badgr workload list')} List saved workloads
|
|
39
|
+
${chalk.cyan('badgr workload run <name>')} Rerun a saved workload
|
|
40
|
+
${chalk.cyan('badgr workspace list')} List workspace trackers (job history + cost per named context)
|
|
41
|
+
${chalk.cyan('badgr workspace create')} Create a workspace tracker (link jobs to a named storage path)
|
|
35
42
|
|
|
36
43
|
${chalk.bold('SHORTCUTS')} ${chalk.dim('(wrappers around run / serve for common workloads)')}
|
|
37
44
|
${chalk.cyan('badgr comfyui run <workflow.json>')} Launch ComfyUI, return endpoint URL
|
|
38
45
|
${chalk.cyan('badgr train <config.yaml>')} LoRA / fine-tuning job, stream logs
|
|
39
46
|
${chalk.cyan('badgr transcribe <audio>')} Whisper transcription, print transcript
|
|
40
47
|
${chalk.cyan('badgr embed <model> <input>')} Text embeddings, output JSONL
|
|
48
|
+
${chalk.cyan('badgr serve template <name>')} Launch an endpoint template (vllm, invokeai, comfyui, …)
|
|
49
|
+
${chalk.cyan('badgr run template <name>')} Launch a job template (axolotl, unsloth)
|
|
50
|
+
${chalk.cyan('badgr template list')} Browse all pre-built templates
|
|
41
51
|
|
|
42
52
|
${chalk.bold('EXAMPLES')}
|
|
43
53
|
${chalk.dim('# Verify the stack works end-to-end:')}
|
|
@@ -46,6 +56,12 @@ ${chalk.bold('EXAMPLES')}
|
|
|
46
56
|
${chalk.dim('# Serve a model (OpenAI-compatible):')}
|
|
47
57
|
badgr serve meta-llama/Llama-3.1-8B-Instruct --max-cost 10
|
|
48
58
|
|
|
59
|
+
${chalk.dim('# Serve a Hugging Face GGUF file via llama.cpp:')}
|
|
60
|
+
badgr serve --runtime llama.cpp \\
|
|
61
|
+
--hf-repo org/model-repo \\
|
|
62
|
+
--hf-file model.gguf \\
|
|
63
|
+
--max-cost 10
|
|
64
|
+
|
|
49
65
|
${chalk.dim('# Launch ComfyUI with a workflow:')}
|
|
50
66
|
badgr comfyui run workflow.json --max-cost 5
|
|
51
67
|
|
|
@@ -94,7 +110,10 @@ ${chalk.bold('badgr serve OPTIONS')}
|
|
|
94
110
|
--count <n> Number of GPUs (default: 1)
|
|
95
111
|
--region US|EU|AU Region preference
|
|
96
112
|
--max-price <$/hr> Hard spend cap per GPU-hour
|
|
97
|
-
--
|
|
113
|
+
--runtime llama.cpp Serve a HF GGUF file via llama.cpp instead of vLLM
|
|
114
|
+
--hf-repo <repo> Hugging Face repo (use with --runtime llama.cpp), e.g. org/model-repo
|
|
115
|
+
--hf-file <file> GGUF filename within that repo, e.g. model.gguf
|
|
116
|
+
--health-path <path> Readiness path to poll (auto-detected: comfyui → /system_stats, llama.cpp → /health)
|
|
98
117
|
--no-wait Skip endpoint health check
|
|
99
118
|
|
|
100
119
|
${chalk.bold('AFTER SERVING')}
|
|
@@ -133,6 +152,9 @@ async function main() {
|
|
|
133
152
|
case 'train': return trainCommand(config, rest, chalk);
|
|
134
153
|
case 'transcribe': return transcribeCommand(config, rest, chalk);
|
|
135
154
|
case 'embed': return embedCommand(config, rest, chalk);
|
|
155
|
+
case 'template': return templateCommand(config, rest, chalk);
|
|
156
|
+
case 'workload': return workloadCommand(config, rest, chalk);
|
|
157
|
+
case 'workspace': return workspaceCommand(config, rest, chalk);
|
|
136
158
|
// legacy aliases kept for compatibility
|
|
137
159
|
case 'up': return upCommand(config, rest, chalk);
|
|
138
160
|
case 'config': {
|
package/src/catalog.js
ADDED
|
@@ -0,0 +1,479 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Template catalog — shared by template.js, serve.js, and run.js.
|
|
3
|
+
* No imports from other badgr commands to avoid circular dependencies.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export const TEMPLATES = [
|
|
7
|
+
{
|
|
8
|
+
name: 'comfyui',
|
|
9
|
+
title: 'ComfyUI',
|
|
10
|
+
description: 'Stable Diffusion image generation with ComfyUI node editor',
|
|
11
|
+
type: 'endpoint',
|
|
12
|
+
image: process.env.COMFYUI_IMAGE || 'yanwk/comfyui-boot:cu126-megapak',
|
|
13
|
+
gpu: 'RTX_4090',
|
|
14
|
+
gpu_count: 1,
|
|
15
|
+
port: 8188,
|
|
16
|
+
health_path: '/system_stats',
|
|
17
|
+
min_vram_gb: 16,
|
|
18
|
+
env: {},
|
|
19
|
+
notes: [
|
|
20
|
+
'Access the ComfyUI UI at the returned endpoint URL.',
|
|
21
|
+
'Tip: use `badgr comfyui run workflow.json` to also queue a workflow at boot.',
|
|
22
|
+
],
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
name: 'axolotl',
|
|
26
|
+
title: 'Axolotl Fine-Tuning',
|
|
27
|
+
description: 'LLM LoRA/QLoRA fine-tuning with the Axolotl training framework',
|
|
28
|
+
type: 'job',
|
|
29
|
+
image: 'axolotlai/axolotl-cloud-uv:main-latest',
|
|
30
|
+
gpu: 'A100',
|
|
31
|
+
gpu_count: 1,
|
|
32
|
+
min_vram_gb: 40,
|
|
33
|
+
env: {
|
|
34
|
+
HF_TOKEN: '<your-hf-token>',
|
|
35
|
+
WANDB_API_KEY: '<optional>',
|
|
36
|
+
HF_HUB_CACHE: '/workspace/hf-cache',
|
|
37
|
+
},
|
|
38
|
+
notes: [
|
|
39
|
+
'Preferred: `badgr train config.yaml` auto-detects Axolotl configs.',
|
|
40
|
+
'Pass HF_TOKEN via --env or BADGR_HF_TOKEN env var.',
|
|
41
|
+
],
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
name: 'unsloth',
|
|
45
|
+
title: 'Unsloth Fine-Tuning',
|
|
46
|
+
description: 'Fast LoRA/QLoRA fine-tuning — 2x faster, 60% less VRAM than baseline',
|
|
47
|
+
type: 'job',
|
|
48
|
+
image: 'unslothai/unsloth:latest',
|
|
49
|
+
gpu: 'RTX_4090',
|
|
50
|
+
gpu_count: 1,
|
|
51
|
+
min_vram_gb: 16,
|
|
52
|
+
env: {
|
|
53
|
+
HF_TOKEN: '<your-hf-token>',
|
|
54
|
+
},
|
|
55
|
+
notes: [
|
|
56
|
+
'Use `badgr train config.yaml --framework unsloth` for config-driven runs.',
|
|
57
|
+
'Unsloth detects the GPU environment automatically.',
|
|
58
|
+
],
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
name: 'vllm',
|
|
62
|
+
title: 'vLLM OpenAI Server',
|
|
63
|
+
description: 'OpenAI-compatible LLM inference endpoint powered by vLLM',
|
|
64
|
+
type: 'endpoint',
|
|
65
|
+
image: 'vllm/vllm-openai:latest',
|
|
66
|
+
gpu: 'RTX_4090',
|
|
67
|
+
gpu_count: 1,
|
|
68
|
+
port: 8000,
|
|
69
|
+
health_path: '/v1/models',
|
|
70
|
+
min_vram_gb: 24,
|
|
71
|
+
env: {
|
|
72
|
+
MODEL: 'meta-llama/Llama-3.1-8B-Instruct',
|
|
73
|
+
HF_TOKEN: '<for-gated-models>',
|
|
74
|
+
MAX_MODEL_LEN: '8192',
|
|
75
|
+
TENSOR_PARALLEL_SIZE: '1',
|
|
76
|
+
},
|
|
77
|
+
command: [
|
|
78
|
+
'python', '-m', 'vllm.entrypoints.openai.api_server',
|
|
79
|
+
'--model', '${MODEL}',
|
|
80
|
+
'--host', '0.0.0.0',
|
|
81
|
+
'--port', '8000',
|
|
82
|
+
'--tensor-parallel-size', '${TENSOR_PARALLEL_SIZE}',
|
|
83
|
+
],
|
|
84
|
+
notes: [
|
|
85
|
+
'Override MODEL with any HuggingFace model ID via --env MODEL=<id>.',
|
|
86
|
+
'For multi-GPU: increase gpu_count and TENSOR_PARALLEL_SIZE to match.',
|
|
87
|
+
'HF_TOKEN required for gated models (Llama, Gemma, Mistral).',
|
|
88
|
+
'OpenAI client: OpenAI(base_url="<endpoint>/v1", api_key="<key>")',
|
|
89
|
+
],
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
name: 'llama-cpp',
|
|
93
|
+
title: 'llama.cpp Server',
|
|
94
|
+
description: 'Fast quantised (GGUF) model inference via llama.cpp HTTP server',
|
|
95
|
+
type: 'endpoint',
|
|
96
|
+
image: 'michaelmanleyx/llama-cpp:server-cuda',
|
|
97
|
+
gpu: 'RTX_4090',
|
|
98
|
+
gpu_count: 1,
|
|
99
|
+
port: 8080,
|
|
100
|
+
health_path: '/health',
|
|
101
|
+
min_vram_gb: 8,
|
|
102
|
+
env: {
|
|
103
|
+
LLAMA_ARG_HF_REPO: 'bartowski/Meta-Llama-3.1-8B-Instruct-GGUF',
|
|
104
|
+
LLAMA_ARG_HF_FILE: 'Meta-Llama-3.1-8B-Instruct-Q4_K_M.gguf',
|
|
105
|
+
},
|
|
106
|
+
notes: [
|
|
107
|
+
'Preferred: `badgr serve --runtime llama.cpp --hf-repo <repo> --hf-file <file>`.',
|
|
108
|
+
'Override LLAMA_ARG_HF_REPO and LLAMA_ARG_HF_FILE via --env.',
|
|
109
|
+
'Supports any GGUF from HuggingFace Hub.',
|
|
110
|
+
],
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
name: 'invokeai',
|
|
114
|
+
title: 'InvokeAI',
|
|
115
|
+
description: 'Professional Stable Diffusion image generation and editing with InvokeAI',
|
|
116
|
+
type: 'endpoint',
|
|
117
|
+
image: 'ghcr.io/invoke-ai/invokeai:latest',
|
|
118
|
+
gpu: 'RTX_4090',
|
|
119
|
+
gpu_count: 1,
|
|
120
|
+
port: 9090,
|
|
121
|
+
health_path: '/api/v1/app/version',
|
|
122
|
+
min_vram_gb: 16,
|
|
123
|
+
env: {
|
|
124
|
+
INVOKEAI_ROOT: '/workspace/invokeai',
|
|
125
|
+
PUBLIC_KEY: '<optional-ssh-public-key>',
|
|
126
|
+
},
|
|
127
|
+
notes: [
|
|
128
|
+
'First boot downloads models — allow 5–10 min before the UI is ready.',
|
|
129
|
+
'Set PUBLIC_KEY to inject an SSH key for SCP file transfers.',
|
|
130
|
+
'Models and outputs persist under INVOKEAI_ROOT.',
|
|
131
|
+
],
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
name: 'kohya-ss',
|
|
135
|
+
title: 'Kohya SS Training',
|
|
136
|
+
description: 'Stable Diffusion LoRA/DreamBooth/fine-tuning via Kohya SS web GUI',
|
|
137
|
+
type: 'endpoint',
|
|
138
|
+
image: 'bmaltais/kohya-ss-gui:latest',
|
|
139
|
+
gpu: 'RTX_4090',
|
|
140
|
+
gpu_count: 1,
|
|
141
|
+
port: 7860,
|
|
142
|
+
health_path: '/',
|
|
143
|
+
min_vram_gb: 8,
|
|
144
|
+
env: {
|
|
145
|
+
HUGGINGFACE_TOKEN: '<optional>',
|
|
146
|
+
},
|
|
147
|
+
notes: [
|
|
148
|
+
'Kohya SS GUI is available at the returned endpoint URL (port 7860).',
|
|
149
|
+
'For headless LoRA training, use `badgr train config.yaml` instead.',
|
|
150
|
+
],
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
name: 'text-gen-webui',
|
|
154
|
+
title: 'Text Generation WebUI',
|
|
155
|
+
description: 'Oobabooga text-generation-webui — feature-rich LLM front-end for many backends',
|
|
156
|
+
type: 'endpoint',
|
|
157
|
+
image: 'atinoda/text-generation-webui:default-nightly',
|
|
158
|
+
gpu: 'RTX_4090',
|
|
159
|
+
gpu_count: 1,
|
|
160
|
+
port: 7860,
|
|
161
|
+
health_path: '/',
|
|
162
|
+
min_vram_gb: 16,
|
|
163
|
+
env: {
|
|
164
|
+
HF_TOKEN: '<for-gated-models>',
|
|
165
|
+
},
|
|
166
|
+
notes: [
|
|
167
|
+
'Load models from the web UI or pre-set via command args inside the container.',
|
|
168
|
+
'Supports llama.cpp, ExLlamaV2, AutoGPTQ, transformers, and more.',
|
|
169
|
+
],
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
name: 'sglang',
|
|
173
|
+
title: 'SGLang Server',
|
|
174
|
+
description: 'High-throughput LLM serving optimised for structured generation (SGLang)',
|
|
175
|
+
type: 'endpoint',
|
|
176
|
+
image: 'lmsysorg/sglang:latest',
|
|
177
|
+
gpu: 'A100',
|
|
178
|
+
gpu_count: 1,
|
|
179
|
+
port: 30000,
|
|
180
|
+
health_path: '/health',
|
|
181
|
+
min_vram_gb: 40,
|
|
182
|
+
env: {
|
|
183
|
+
MODEL_PATH: 'meta-llama/Llama-3.1-8B-Instruct',
|
|
184
|
+
HF_TOKEN: '<for-gated-models>',
|
|
185
|
+
TP_SIZE: '1',
|
|
186
|
+
},
|
|
187
|
+
command: [
|
|
188
|
+
'python', '-m', 'sglang.launch_server',
|
|
189
|
+
'--model-path', '${MODEL_PATH}',
|
|
190
|
+
'--host', '0.0.0.0',
|
|
191
|
+
'--port', '30000',
|
|
192
|
+
'--tp', '${TP_SIZE}',
|
|
193
|
+
],
|
|
194
|
+
notes: [
|
|
195
|
+
'Optimised for structured output (JSON schema, regex) and RadixAttention.',
|
|
196
|
+
'OpenAI-compatible API on port 30000.',
|
|
197
|
+
'Increase TP_SIZE and gpu_count together for tensor parallelism.',
|
|
198
|
+
'HF_TOKEN required for gated models.',
|
|
199
|
+
],
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
name: 'tgi',
|
|
203
|
+
title: 'Text Generation Inference (TGI)',
|
|
204
|
+
description: 'Hugging Face TGI — production-grade LLM inference with continuous batching',
|
|
205
|
+
type: 'endpoint',
|
|
206
|
+
image: 'ghcr.io/huggingface/text-generation-inference:latest',
|
|
207
|
+
gpu: 'RTX_4090',
|
|
208
|
+
gpu_count: 1,
|
|
209
|
+
port: 80,
|
|
210
|
+
health_path: '/health',
|
|
211
|
+
min_vram_gb: 24,
|
|
212
|
+
env: {
|
|
213
|
+
MODEL_ID: 'meta-llama/Llama-3.1-8B-Instruct',
|
|
214
|
+
HF_TOKEN: '<for-gated-models>',
|
|
215
|
+
MAX_INPUT_LENGTH: '4096',
|
|
216
|
+
MAX_TOTAL_TOKENS: '8192',
|
|
217
|
+
NUM_SHARD: '1',
|
|
218
|
+
},
|
|
219
|
+
notes: [
|
|
220
|
+
'OpenAI-compatible API at /v1/chat/completions.',
|
|
221
|
+
'Set NUM_SHARD equal to gpu_count for sharded (tensor-parallel) inference.',
|
|
222
|
+
'HF_TOKEN required for Llama, Gemma, and other gated models.',
|
|
223
|
+
],
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
name: 'auto1111',
|
|
227
|
+
title: 'AUTOMATIC1111',
|
|
228
|
+
description: 'Stable Diffusion web UI — the largest SD ecosystem with ControlNet, LoRA, img2img, inpainting, and upscaling',
|
|
229
|
+
type: 'endpoint',
|
|
230
|
+
image: 'aidockorg/stable-diffusion-webui-cuda:latest',
|
|
231
|
+
gpu: 'RTX_4090',
|
|
232
|
+
gpu_count: 1,
|
|
233
|
+
port: 7860,
|
|
234
|
+
health_path: '/',
|
|
235
|
+
min_vram_gb: 6,
|
|
236
|
+
env: {
|
|
237
|
+
WEBUI_FLAGS: '--xformers',
|
|
238
|
+
},
|
|
239
|
+
notes: [
|
|
240
|
+
'Web UI at the returned endpoint URL (port 7860).',
|
|
241
|
+
'Place model checkpoints under /opt/dl-ui/repositories/stable-diffusion-webui/models/Stable-diffusion/.',
|
|
242
|
+
'Override WEBUI_FLAGS to pass extra launch args (e.g. --medvram for lower VRAM).',
|
|
243
|
+
],
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
name: 'forge',
|
|
247
|
+
title: 'Stable Diffusion WebUI Forge',
|
|
248
|
+
description: 'Optimised fork of A1111 with GPU memory improvements — runs models that OOM on standard A1111',
|
|
249
|
+
type: 'endpoint',
|
|
250
|
+
image: 'aidockorg/stable-diffusion-webui-forge-cuda:latest',
|
|
251
|
+
gpu: 'RTX_4090',
|
|
252
|
+
gpu_count: 1,
|
|
253
|
+
port: 7860,
|
|
254
|
+
health_path: '/',
|
|
255
|
+
min_vram_gb: 4,
|
|
256
|
+
env: {
|
|
257
|
+
WEBUI_FLAGS: '--xformers',
|
|
258
|
+
},
|
|
259
|
+
notes: [
|
|
260
|
+
'Web UI at the returned endpoint URL (port 7860).',
|
|
261
|
+
'Forge uses less VRAM than A1111 for the same model — good for 8–12 GB cards.',
|
|
262
|
+
'Compatible with most A1111 extensions.',
|
|
263
|
+
],
|
|
264
|
+
},
|
|
265
|
+
{
|
|
266
|
+
name: 'nerfstudio',
|
|
267
|
+
title: 'Nerfstudio',
|
|
268
|
+
description: 'NeRF and 3D Gaussian Splatting training and reconstruction from image captures',
|
|
269
|
+
type: 'job',
|
|
270
|
+
image: 'ghcr.io/nerfstudio-project/nerfstudio:latest',
|
|
271
|
+
gpu: 'RTX_4090',
|
|
272
|
+
gpu_count: 1,
|
|
273
|
+
min_vram_gb: 16,
|
|
274
|
+
env: {
|
|
275
|
+
METHOD: 'nerfacto',
|
|
276
|
+
},
|
|
277
|
+
notes: [
|
|
278
|
+
'Mount your images to /workspace/data and your output dir to /workspace/outputs.',
|
|
279
|
+
'Override METHOD to switch between nerfacto, splatfacto, instant-ngp, etc.',
|
|
280
|
+
'Training a typical scene takes 15–45 min depending on method and image count.',
|
|
281
|
+
],
|
|
282
|
+
},
|
|
283
|
+
{
|
|
284
|
+
name: 'openfold',
|
|
285
|
+
title: 'OpenFold',
|
|
286
|
+
description: 'Trainable, memory-efficient GPU-friendly PyTorch reproduction of AlphaFold 2 for protein structure prediction',
|
|
287
|
+
type: 'job',
|
|
288
|
+
image: 'nvidia/cuda:12.1.0-cudnn8-devel-ubuntu22.04',
|
|
289
|
+
gpu: 'A100',
|
|
290
|
+
gpu_count: 1,
|
|
291
|
+
min_vram_gb: 40,
|
|
292
|
+
env: {
|
|
293
|
+
OPENFOLD_REPO: 'https://github.com/aqlaboratory/openfold',
|
|
294
|
+
},
|
|
295
|
+
notes: [
|
|
296
|
+
'No pre-built public image exists — this uses a CUDA base; clone and install OpenFold at boot.',
|
|
297
|
+
'See https://github.com/aqlaboratory/openfold for full install steps.',
|
|
298
|
+
'Full inference on a single sequence: ~5–20 min on an A100.',
|
|
299
|
+
],
|
|
300
|
+
},
|
|
301
|
+
{
|
|
302
|
+
name: 'blender-render',
|
|
303
|
+
title: 'Blender GPU Render',
|
|
304
|
+
description: 'Headless Blender GPU rendering via CUDA — render .blend files to image sequences or video',
|
|
305
|
+
type: 'job',
|
|
306
|
+
image: 'blenderkit/headless-blender:blender-4.4',
|
|
307
|
+
gpu: 'RTX_4090',
|
|
308
|
+
gpu_count: 1,
|
|
309
|
+
min_vram_gb: 8,
|
|
310
|
+
env: {
|
|
311
|
+
BLEND_FILE: '/workspace/scene.blend',
|
|
312
|
+
FRAME_START: '1',
|
|
313
|
+
FRAME_END: '250',
|
|
314
|
+
},
|
|
315
|
+
notes: [
|
|
316
|
+
'Mount your .blend file to /workspace/scene.blend.',
|
|
317
|
+
'Set FRAME_START and FRAME_END to the frame range to render.',
|
|
318
|
+
'Output frames are written to /workspace/output/ by default.',
|
|
319
|
+
],
|
|
320
|
+
},
|
|
321
|
+
{
|
|
322
|
+
name: 'openmm',
|
|
323
|
+
title: 'OpenMM',
|
|
324
|
+
description: 'GPU-accelerated molecular dynamics simulation using the OpenMM toolkit',
|
|
325
|
+
type: 'job',
|
|
326
|
+
image: 'saladtechnologies/openmm:latest',
|
|
327
|
+
gpu: 'RTX_4090',
|
|
328
|
+
gpu_count: 1,
|
|
329
|
+
min_vram_gb: 8,
|
|
330
|
+
env: {
|
|
331
|
+
SIMULATION_SCRIPT: '/workspace/simulate.py',
|
|
332
|
+
},
|
|
333
|
+
notes: [
|
|
334
|
+
'Mount your Python simulation script to /workspace/simulate.py.',
|
|
335
|
+
'OpenMM auto-selects CUDA platform when a GPU is present.',
|
|
336
|
+
'Typical MD simulation runs: minutes to hours depending on system size and steps.',
|
|
337
|
+
],
|
|
338
|
+
},
|
|
339
|
+
{
|
|
340
|
+
name: 'gromacs',
|
|
341
|
+
title: 'GROMACS',
|
|
342
|
+
description: 'High-performance molecular dynamics package with GPU-accelerated bonded and non-bonded calculations',
|
|
343
|
+
type: 'job',
|
|
344
|
+
image: 'scientiflow/gromacs-gpu:2024.3',
|
|
345
|
+
gpu: 'RTX_4090',
|
|
346
|
+
gpu_count: 1,
|
|
347
|
+
min_vram_gb: 8,
|
|
348
|
+
env: {
|
|
349
|
+
GMX_GPU_DD_COMMS: 'true',
|
|
350
|
+
GMX_GPU_PME_PP_COMMS: 'true',
|
|
351
|
+
},
|
|
352
|
+
notes: [
|
|
353
|
+
'Mount your .tpr input file and output directory to /workspace.',
|
|
354
|
+
'GROMACS auto-detects CUDA GPU — no extra flags needed.',
|
|
355
|
+
'For multi-GPU: increase gpu_count and pass -ntmpi / -ntomp flags via command override.',
|
|
356
|
+
],
|
|
357
|
+
},
|
|
358
|
+
{
|
|
359
|
+
name: 'lammps',
|
|
360
|
+
title: 'LAMMPS',
|
|
361
|
+
description: 'Large-scale Atomic/Molecular Massively Parallel Simulator with GPU acceleration via Kokkos/CUDA',
|
|
362
|
+
type: 'job',
|
|
363
|
+
image: 'nvcr.io/hpc/lammps:release-29Sep2021',
|
|
364
|
+
gpu: 'RTX_4090',
|
|
365
|
+
gpu_count: 1,
|
|
366
|
+
min_vram_gb: 8,
|
|
367
|
+
env: {
|
|
368
|
+
LAMMPS_INPUT: '/workspace/in.lammps',
|
|
369
|
+
},
|
|
370
|
+
notes: [
|
|
371
|
+
'Mount your LAMMPS input script to /workspace/in.lammps.',
|
|
372
|
+
'GPU acceleration uses the Kokkos or GPU package — enable via -pk gpu 1 in your input.',
|
|
373
|
+
'Simulation runtime scales with system size; use --max-runtime to cap spend.',
|
|
374
|
+
],
|
|
375
|
+
},
|
|
376
|
+
{
|
|
377
|
+
name: 'diffusers',
|
|
378
|
+
title: 'Hugging Face Diffusers',
|
|
379
|
+
description: 'State-of-the-art diffusion model library for image, video, and audio generation',
|
|
380
|
+
type: 'job',
|
|
381
|
+
image: 'diffusers/diffusers-pytorch-cuda:latest',
|
|
382
|
+
gpu: 'RTX_4090',
|
|
383
|
+
gpu_count: 1,
|
|
384
|
+
min_vram_gb: 16,
|
|
385
|
+
env: {
|
|
386
|
+
HF_TOKEN: '<for-gated-models>',
|
|
387
|
+
MODEL_ID: 'stabilityai/stable-diffusion-xl-base-1.0',
|
|
388
|
+
SCRIPT: '/workspace/run.py',
|
|
389
|
+
},
|
|
390
|
+
notes: [
|
|
391
|
+
'Mount your generation or training script to /workspace/run.py.',
|
|
392
|
+
'HF_TOKEN required for gated models.',
|
|
393
|
+
'Supports SDXL, FLUX, Stable Diffusion 3, ControlNet, IP-Adapter, and more.',
|
|
394
|
+
],
|
|
395
|
+
},
|
|
396
|
+
{
|
|
397
|
+
name: 'torchtune',
|
|
398
|
+
title: 'torchtune',
|
|
399
|
+
description: 'PyTorch-native post-training library for LLM fine-tuning via config-driven YAML recipes',
|
|
400
|
+
type: 'job',
|
|
401
|
+
image: 'pytorch/pytorch:2.6.0-cuda12.4-cudnn9-devel',
|
|
402
|
+
gpu: 'A100',
|
|
403
|
+
gpu_count: 1,
|
|
404
|
+
min_vram_gb: 40,
|
|
405
|
+
env: {
|
|
406
|
+
HF_TOKEN: '<for-gated-models>',
|
|
407
|
+
RECIPE: 'lora_finetune_single_device',
|
|
408
|
+
CONFIG: '/workspace/config.yaml',
|
|
409
|
+
},
|
|
410
|
+
notes: [
|
|
411
|
+
'Install torchtune at boot: pip install torchtune.',
|
|
412
|
+
'Mount your YAML recipe config to /workspace/config.yaml.',
|
|
413
|
+
'HF_TOKEN required for Llama and other gated base models.',
|
|
414
|
+
'Supports LoRA, QLoRA, DPO, full fine-tune, and quantisation-aware training.',
|
|
415
|
+
],
|
|
416
|
+
},
|
|
417
|
+
];
|
|
418
|
+
|
|
419
|
+
export const TEMPLATE_MAP = Object.fromEntries(TEMPLATES.map(t => [t.name, t]));
|
|
420
|
+
|
|
421
|
+
/**
|
|
422
|
+
* Build the args array passed to serveCommand / runCommand.
|
|
423
|
+
* Template defaults are applied first; CLI overrides win.
|
|
424
|
+
*/
|
|
425
|
+
export function buildTemplateFlags(template, overrides) {
|
|
426
|
+
const args = ['--image', template.image];
|
|
427
|
+
|
|
428
|
+
args.push('--gpu', overrides.gpu ?? template.gpu);
|
|
429
|
+
|
|
430
|
+
const count = overrides.count ?? template.gpu_count ?? 1;
|
|
431
|
+
if (count > 1) args.push('--count', String(count));
|
|
432
|
+
|
|
433
|
+
if (overrides.region) args.push('--region', overrides.region);
|
|
434
|
+
if (overrides.maxCost != null) args.push('--max-cost', String(overrides.maxCost));
|
|
435
|
+
if (overrides.maxPrice != null) args.push('--max-price', String(overrides.maxPrice));
|
|
436
|
+
if (overrides.tier) args.push('--tier', overrides.tier);
|
|
437
|
+
if (overrides.name) args.push('--name', overrides.name);
|
|
438
|
+
if (overrides.noWait) args.push('--no-wait');
|
|
439
|
+
if (overrides.persistent) args.push('--persistent');
|
|
440
|
+
|
|
441
|
+
if (template.type === 'endpoint' && template.health_path) {
|
|
442
|
+
args.push('--health-path', template.health_path);
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
// Merge env: template defaults first, user overrides win
|
|
446
|
+
const mergedEnv = { ...(template.env || {}), ...(overrides.env || {}) };
|
|
447
|
+
for (const [k, v] of Object.entries(mergedEnv)) {
|
|
448
|
+
if (!v.startsWith('<')) args.push('--env', `${k}=${v}`);
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
return args;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
/** Parse the flags that follow `badgr serve template <name>` or `badgr run template <name>`. */
|
|
455
|
+
export function parseTemplateOverrides(args) {
|
|
456
|
+
const overrides = { env: {} };
|
|
457
|
+
let i = 0;
|
|
458
|
+
while (i < args.length) {
|
|
459
|
+
const a = args[i];
|
|
460
|
+
if (a === '--gpu') { overrides.gpu = args[++i]; i++; continue; }
|
|
461
|
+
if (a === '--count') { overrides.count = parseInt(args[++i], 10); i++; continue; }
|
|
462
|
+
if (a === '--region') { overrides.region = args[++i]; i++; continue; }
|
|
463
|
+
if (a === '--max-cost') { overrides.maxCost = parseFloat(args[++i]); i++; continue; }
|
|
464
|
+
if (a === '--max-price') { overrides.maxPrice = parseFloat(args[++i]); i++; continue; }
|
|
465
|
+
if (a === '--max-runtime') { overrides.maxRuntime = parseFloat(args[++i]); i++; continue; }
|
|
466
|
+
if (a === '--tier') { overrides.tier = args[++i]; i++; continue; }
|
|
467
|
+
if (a === '--name') { overrides.name = args[++i]; i++; continue; }
|
|
468
|
+
if (a === '--no-wait') { overrides.noWait = true; i++; continue; }
|
|
469
|
+
if (a === '--persistent') { overrides.persistent = true; i++; continue; }
|
|
470
|
+
if (a === '--env') {
|
|
471
|
+
const kv = args[++i]; i++;
|
|
472
|
+
const idx = kv.indexOf('=');
|
|
473
|
+
if (idx > 0) overrides.env[kv.slice(0, idx)] = kv.slice(idx + 1);
|
|
474
|
+
continue;
|
|
475
|
+
}
|
|
476
|
+
i++;
|
|
477
|
+
}
|
|
478
|
+
return overrides;
|
|
479
|
+
}
|
package/src/commands/comfyui.js
CHANGED
|
@@ -12,7 +12,7 @@ import { addDeployment, addReceipt, updateReceipt, generateReceiptId } from '../
|
|
|
12
12
|
import { normalizeTier, callWithFallback, HIGH_RATE_THRESHOLD } from '../fallback.js';
|
|
13
13
|
import { formatCliError } from '../errors.js';
|
|
14
14
|
|
|
15
|
-
const COMFYUI_IMAGE = 'yanwk/comfyui-boot:
|
|
15
|
+
const COMFYUI_IMAGE = process.env.COMFYUI_IMAGE || 'yanwk/comfyui-boot:cu126-megapak';
|
|
16
16
|
const HEALTH_PATH = '/system_stats';
|
|
17
17
|
const WAIT_TIMEOUT_MS = 10 * 60 * 1000; // 10 min — model downloads on first boot
|
|
18
18
|
const MAX_WORKFLOW_B = 1 * 1024 * 1024; // 1 MB workflow limit
|
package/src/commands/receipts.js
CHANGED