badgr-cli 1.0.42 → 1.0.43
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 +69 -2
- package/package.json +2 -1
- package/src/badgr.js +3 -0
- package/src/commands/comfyui.js +35 -8
- package/src/commands/run.js +14 -33
- package/src/commands/serve.js +43 -32
- package/src/commands/train.js +85 -11
- package/tests/productized-dry-run.test.js +141 -0
- package/tests/serve-lifecycle.test.js +103 -137
- package/tests/template.test.js +11 -13
- package/tests/workload-templates.test.js +37 -2
package/tests/template.test.js
CHANGED
|
@@ -327,18 +327,21 @@ describe('parseTemplateOverrides', () => {
|
|
|
327
327
|
// Sequence for serveCommand:
|
|
328
328
|
// callApi('/serve', ...) → dep (via callWithFallback → callApi)
|
|
329
329
|
// callApi('/deployments/<id>', ...) → { status: 'running' } (pre-health check)
|
|
330
|
-
//
|
|
330
|
+
// callApi('/deployments/<id>', ...) → { status: 'running', endpoint_ready: true } (waitForEndpoint poll)
|
|
331
|
+
//
|
|
332
|
+
// Readiness comes entirely from Badgr's own deployment status — the CLI never
|
|
333
|
+
// fetches the pod/RunPod-proxy endpoint directly.
|
|
331
334
|
|
|
332
|
-
function setupServe(depOverrides = {}) {
|
|
335
|
+
function setupServe(depOverrides = {}, readyOverrides = {}) {
|
|
333
336
|
api.callApi
|
|
334
337
|
.mockResolvedValueOnce(makeServeDep(depOverrides)) // POST /serve
|
|
335
|
-
.mockResolvedValueOnce({ status: 'running' })
|
|
336
|
-
|
|
338
|
+
.mockResolvedValueOnce({ status: 'running' }) // pre-health dep status
|
|
339
|
+
.mockResolvedValueOnce({ status: 'running', endpoint_ready: true, ...readyOverrides }); // waitForEndpoint poll
|
|
337
340
|
}
|
|
338
341
|
|
|
339
342
|
describe('badgr serve template <name>', () => {
|
|
340
|
-
it('vllm: dispatches with correct image, max_cost_usd, and
|
|
341
|
-
setupServe();
|
|
343
|
+
it('vllm: dispatches with correct image, max_cost_usd, and readiness on /v1/models', async () => {
|
|
344
|
+
setupServe({}, { health_path: '/v1/models' });
|
|
342
345
|
const p = serveCommand(config, ['template', 'vllm', '--max-cost', '5'], chalk);
|
|
343
346
|
await vi.advanceTimersByTimeAsync(5000);
|
|
344
347
|
await p;
|
|
@@ -347,22 +350,17 @@ describe('badgr serve template <name>', () => {
|
|
|
347
350
|
expect(route).toBe('/serve');
|
|
348
351
|
expect(opts.body.image).toBe('vllm/vllm-openai:latest');
|
|
349
352
|
expect(opts.body.max_cost_usd).toBe(5);
|
|
350
|
-
// health check fetch should use the /v1/models path from the template
|
|
351
|
-
const fetchUrl = global.fetch.mock.calls[0]?.[0] ?? '';
|
|
352
|
-
expect(fetchUrl).toContain('/v1/models');
|
|
353
353
|
expect(process.exitCode).toBeFalsy();
|
|
354
354
|
});
|
|
355
355
|
|
|
356
|
-
it('comfyui: dispatches with ComfyUI image and
|
|
357
|
-
setupServe();
|
|
356
|
+
it('comfyui: dispatches with ComfyUI image and readiness on /system_stats', async () => {
|
|
357
|
+
setupServe({}, { health_path: '/system_stats' });
|
|
358
358
|
const p = serveCommand(config, ['template', 'comfyui', '--max-cost', '3'], chalk);
|
|
359
359
|
await vi.advanceTimersByTimeAsync(5000);
|
|
360
360
|
await p;
|
|
361
361
|
|
|
362
362
|
const [, opts] = api.callApi.mock.calls[0];
|
|
363
363
|
expect(opts.body.image).toBe('yanwk/comfyui-boot:cu126-megapak');
|
|
364
|
-
const fetchUrl = global.fetch.mock.calls[0]?.[0] ?? '';
|
|
365
|
-
expect(fetchUrl).toContain('/system_stats');
|
|
366
364
|
expect(process.exitCode).toBeFalsy();
|
|
367
365
|
});
|
|
368
366
|
|
|
@@ -485,9 +485,44 @@ describe('trainCommand', () => {
|
|
|
485
485
|
expect(body.max_runtime_seconds).toBe(60 * 60);
|
|
486
486
|
});
|
|
487
487
|
|
|
488
|
-
it('
|
|
488
|
+
it('blocks unsloth config instead of running a mismatched command', async () => {
|
|
489
489
|
fs.existsSync.mockReturnValue(true);
|
|
490
490
|
fs.readFileSync.mockReturnValue('# unsloth training config\nmodel: llama\n');
|
|
491
|
+
|
|
492
|
+
await trainCommand(config, ['config.yaml', '--detach'], chalk);
|
|
493
|
+
|
|
494
|
+
expect(process.exitCode).toBe(1);
|
|
495
|
+
expect(fallback.callWithFallback).not.toHaveBeenCalled();
|
|
496
|
+
});
|
|
497
|
+
|
|
498
|
+
it('blocks generic (unrecognized) config instead of guessing a command', async () => {
|
|
499
|
+
fs.existsSync.mockReturnValue(true);
|
|
500
|
+
fs.readFileSync.mockReturnValue('some_key: some_value\nother: 123\n');
|
|
501
|
+
|
|
502
|
+
await trainCommand(config, ['config.yaml', '--detach'], chalk);
|
|
503
|
+
|
|
504
|
+
expect(process.exitCode).toBe(1);
|
|
505
|
+
expect(fallback.callWithFallback).not.toHaveBeenCalled();
|
|
506
|
+
});
|
|
507
|
+
|
|
508
|
+
it('uses the trl CLI command for a trl-detected config', async () => {
|
|
509
|
+
fs.existsSync.mockReturnValue(true);
|
|
510
|
+
fs.readFileSync.mockReturnValue('trainer: SFTTrainer\nmodel: mistral\n');
|
|
511
|
+
fallback.callWithFallback.mockResolvedValue(makeRunDep());
|
|
512
|
+
|
|
513
|
+
const p = trainCommand(config, ['config.yaml', '--detach'], chalk);
|
|
514
|
+
await vi.advanceTimersByTimeAsync(100);
|
|
515
|
+
await p;
|
|
516
|
+
|
|
517
|
+
const bodyBuilder = fallback.callWithFallback.mock.calls[0][2];
|
|
518
|
+
const body = bodyBuilder();
|
|
519
|
+
expect(body.image).toBe('huggingface/trl-source:latest');
|
|
520
|
+
expect(body.command[2]).toContain('trl sft --config /tmp/config.yaml');
|
|
521
|
+
});
|
|
522
|
+
|
|
523
|
+
it('uses the axolotl CLI command for an axolotl-detected config', async () => {
|
|
524
|
+
fs.existsSync.mockReturnValue(true);
|
|
525
|
+
fs.readFileSync.mockReturnValue('base_model: meta-llama/Llama-2-7b-hf\nsequence_len: 2048\n');
|
|
491
526
|
fallback.callWithFallback.mockResolvedValue(makeRunDep());
|
|
492
527
|
|
|
493
528
|
const p = trainCommand(config, ['config.yaml', '--detach'], chalk);
|
|
@@ -496,7 +531,7 @@ describe('trainCommand', () => {
|
|
|
496
531
|
|
|
497
532
|
const bodyBuilder = fallback.callWithFallback.mock.calls[0][2];
|
|
498
533
|
const body = bodyBuilder();
|
|
499
|
-
expect(body.
|
|
534
|
+
expect(body.command[2]).toContain('axolotl train /tmp/config.yaml');
|
|
500
535
|
});
|
|
501
536
|
});
|
|
502
537
|
|