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.
@@ -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
- // global.fetch(endpoint + health_path) → { ok: true }
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' }); // pre-health dep status
336
- global.fetch = vi.fn().mockResolvedValue({ ok: true, status: 200, json: async () => ({}) });
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 health check on /v1/models', async () => {
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 health check on /system_stats', async () => {
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('uses unsloth image for unsloth config', async () => {
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.image).toBe('unslothai/unsloth:latest');
534
+ expect(body.command[2]).toContain('axolotl train /tmp/config.yaml');
500
535
  });
501
536
  });
502
537