badgr-cli 1.1.5 → 1.1.6
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/package.json +1 -1
- package/src/commands/serve.js +63 -9
package/package.json
CHANGED
package/src/commands/serve.js
CHANGED
|
@@ -151,7 +151,12 @@ function _resolveHealthPath({ healthPath, isLlamaCpp, customImage, task }) {
|
|
|
151
151
|
* directly. The backend does the real health_path probing (see
|
|
152
152
|
* DeploymentService.check_endpoint_readiness); a pod reporting RUNNING only
|
|
153
153
|
* means infrastructure is up, not that the app inside is serving.
|
|
154
|
-
* Returns { ready: boolean, timedOut: boolean, depFailed: boolean, failReason?: string
|
|
154
|
+
* Returns { ready: boolean, timedOut: boolean, depFailed: boolean, failReason?: string,
|
|
155
|
+
* dep? }. `dep` (only present when ready) is the last polled
|
|
156
|
+
* /deployments/{id} response — carries supports_completions/
|
|
157
|
+
* supports_chat_completions, probed server-side once the endpoint first
|
|
158
|
+
* reports ready, so the caller's final usage example can match what the
|
|
159
|
+
* model actually serves instead of guessing.
|
|
155
160
|
*/
|
|
156
161
|
// vLLM cold start (model download + load) often exceeds 5 min on first boot.
|
|
157
162
|
const VLLM_SERVE_WAIT_MS = 15 * 60 * 1000;
|
|
@@ -183,7 +188,7 @@ async function waitForEndpoint(deploymentId, config, timeoutMs = VLLM_SERVE_WAIT
|
|
|
183
188
|
}
|
|
184
189
|
if (dep.endpoint_ready) {
|
|
185
190
|
if (blockLines > 0) { _clearBlock(blockLines); blockLines = 0; }
|
|
186
|
-
return { ready: true, timedOut: false, depFailed: false };
|
|
191
|
+
return { ready: true, timedOut: false, depFailed: false, dep };
|
|
187
192
|
}
|
|
188
193
|
|
|
189
194
|
const now = Date.now();
|
|
@@ -793,6 +798,11 @@ export async function serveCommand(config, args, chalk) {
|
|
|
793
798
|
if (endpointReady) {
|
|
794
799
|
console.log(chalk.dim(_stage(healthStageN, STAGE_TOTAL, 'Checking endpoint health...')));
|
|
795
800
|
console.log(chalk.green(_stage(readyStageN, STAGE_TOTAL, 'Ready')));
|
|
801
|
+
if (healthResult.dep) {
|
|
802
|
+
dep.supports_completions = healthResult.dep.supports_completions;
|
|
803
|
+
dep.supports_chat_completions = healthResult.dep.supports_chat_completions;
|
|
804
|
+
dep.capability_source = healthResult.dep.capability_source;
|
|
805
|
+
}
|
|
796
806
|
} else {
|
|
797
807
|
updateReceipt(rcptId, { status: 'health_check_timeout' });
|
|
798
808
|
}
|
|
@@ -820,7 +830,14 @@ export async function serveCommand(config, args, chalk) {
|
|
|
820
830
|
console.log();
|
|
821
831
|
}
|
|
822
832
|
|
|
823
|
-
|
|
833
|
+
// dep.service_url is the Badgr-owned https://<id>.serve.aibadgr.com URL
|
|
834
|
+
// once that proxy is actually live (see backend/service_proxy.py) --
|
|
835
|
+
// until then it's absent/equal to endpointUrl, so this falls back to the
|
|
836
|
+
// raw provider URL exactly as before. Only the customer-facing display
|
|
837
|
+
// and usage examples switch to it; polling/comfy-node validation above
|
|
838
|
+
// still talk to the real internal endpointUrl directly.
|
|
839
|
+
const displayUrl = dep.service_url || endpointUrl;
|
|
840
|
+
console.log(` ${chalk.bold('Base URL:')} ${chalk.cyan(displayUrl)}`);
|
|
824
841
|
if (isLlamaCpp) {
|
|
825
842
|
console.log(` ${chalk.bold('HF Repo:')} ${flags.hfRepo}`);
|
|
826
843
|
console.log(` ${chalk.bold('HF File:')} ${flags.hfFile}`);
|
|
@@ -843,7 +860,7 @@ export async function serveCommand(config, args, chalk) {
|
|
|
843
860
|
|
|
844
861
|
if (endpointReady && isOllama) {
|
|
845
862
|
console.log(` ${chalk.bold('Test with curl:')}`);
|
|
846
|
-
console.log(chalk.dim(` curl ${
|
|
863
|
+
console.log(chalk.dim(` curl ${displayUrl}/api/generate \\`));
|
|
847
864
|
console.log(chalk.dim(` -H "Content-Type: application/json" \\`));
|
|
848
865
|
console.log(chalk.dim(` -d '{"model":"${dep.model || effectiveModel}","prompt":"Hello","stream":false}'`));
|
|
849
866
|
console.log();
|
|
@@ -860,13 +877,45 @@ export async function serveCommand(config, args, chalk) {
|
|
|
860
877
|
console.log(` ${chalk.bold('API key:')} ${chalk.yellow(dep.endpoint_api_key)}`);
|
|
861
878
|
console.log(chalk.dim(' Shown once — copy it now. This key is scoped to this endpoint only.'));
|
|
862
879
|
}
|
|
880
|
+
// dep.supports_chat_completions is probed server-side (see backend
|
|
881
|
+
// model_capabilities.py): a live call against this exact endpoint is
|
|
882
|
+
// authoritative when it's reachable (dep.capability_source ===
|
|
883
|
+
// "runtime"); otherwise it falls back to a best-effort hint from the
|
|
884
|
+
// model's public HF chat-template config (capability_source ===
|
|
885
|
+
// "hf_hint"). `true`/`false` here always mean one of those two actually
|
|
886
|
+
// produced a real signal -- a model without a chat template (e.g.
|
|
887
|
+
// facebook/opt-125m) answers /v1/completions but 400s on
|
|
888
|
+
// /v1/chat/completions, so a generic chat example would be wrong.
|
|
889
|
+
// `undefined`/`null` means NEITHER produced a signal (auth-protected
|
|
890
|
+
// endpoint + HF lookup failed, or a non-vLLM/task image) -- unlike this
|
|
891
|
+
// command's old behavior, that must never silently default to showing
|
|
892
|
+
// a chat example that might not work; show neither example instead.
|
|
893
|
+
const noTask = !flags.task;
|
|
894
|
+
const chatKnown = dep.supports_chat_completions === true || dep.supports_chat_completions === false;
|
|
895
|
+
const showChatExample = noTask && chatKnown && dep.supports_chat_completions === true;
|
|
896
|
+
const showCompletionsExample = noTask && chatKnown && dep.supports_chat_completions === false;
|
|
897
|
+
const capabilityUnverified = noTask && !chatKnown;
|
|
898
|
+
const unconfirmedNote = dep.capability_source === 'hf_hint'
|
|
899
|
+
? chalk.dim(' (based on the model\'s public Hugging Face config — not confirmed against this live endpoint)')
|
|
900
|
+
: null;
|
|
863
901
|
console.log(` ${chalk.bold('Test with curl:')}`);
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
902
|
+
if (showChatExample) {
|
|
903
|
+
console.log(chalk.dim(` curl ${displayUrl}/chat/completions \\`));
|
|
904
|
+
console.log(chalk.dim(` -H "Authorization: Bearer ${authKey}" -H "Content-Type: application/json" \\`));
|
|
905
|
+
console.log(chalk.dim(` -d '{"model":"${sdkModel}","messages":[{"role":"user","content":"Hello"}]}'`));
|
|
906
|
+
if (unconfirmedNote) console.log(unconfirmedNote);
|
|
907
|
+
} else if (showCompletionsExample) {
|
|
908
|
+
console.log(chalk.dim(` curl ${displayUrl}/completions \\`));
|
|
909
|
+
console.log(chalk.dim(` -H "Authorization: Bearer ${authKey}" -H "Content-Type: application/json" \\`));
|
|
910
|
+
console.log(chalk.dim(` -d '{"model":"${sdkModel}","prompt":"Hello","max_tokens":32}'`));
|
|
911
|
+
if (unconfirmedNote) console.log(unconfirmedNote);
|
|
912
|
+
} else if (capabilityUnverified) {
|
|
913
|
+
console.log(chalk.yellow(' Capability not verified yet — check available models first:'));
|
|
914
|
+
console.log(chalk.dim(` curl ${displayUrl}/models -H "Authorization: Bearer ${authKey}"`));
|
|
915
|
+
}
|
|
867
916
|
console.log(` ${chalk.bold('Use with OpenAI SDK:')}`);
|
|
868
917
|
console.log(chalk.dim(` from openai import OpenAI`));
|
|
869
|
-
console.log(chalk.dim(` client = OpenAI(base_url="${
|
|
918
|
+
console.log(chalk.dim(` client = OpenAI(base_url="${displayUrl}", api_key="${authKey}")`));
|
|
870
919
|
if (flags.task === 'transcribe') {
|
|
871
920
|
console.log(chalk.dim(` with open("audio.mp3", "rb") as f:`));
|
|
872
921
|
console.log(chalk.dim(` t = client.audio.transcriptions.create(model="${sdkModel}", file=f, response_format="text")`));
|
|
@@ -875,8 +924,13 @@ export async function serveCommand(config, args, chalk) {
|
|
|
875
924
|
console.log(chalk.dim(` # resp.data[0].b64_json contains the base64-encoded PNG`));
|
|
876
925
|
} else if (flags.task === 'embed') {
|
|
877
926
|
console.log(chalk.dim(` resp = client.embeddings.create(model="${sdkModel}", input=["hello world"])`));
|
|
878
|
-
} else {
|
|
927
|
+
} else if (showChatExample) {
|
|
879
928
|
console.log(chalk.dim(` resp = client.chat.completions.create(model="${sdkModel}", messages=[{"role": "user", "content": "Hello"}])`));
|
|
929
|
+
} else if (showCompletionsExample) {
|
|
930
|
+
console.log(chalk.dim(` resp = client.completions.create(model="${sdkModel}", prompt="Hello", max_tokens=32)`));
|
|
931
|
+
console.log(chalk.dim(` # this model has no chat template — /v1/chat/completions returns 400 for it`));
|
|
932
|
+
} else {
|
|
933
|
+
console.log(chalk.dim(` resp = client.models.list() # check capability_unverified — chat vs completions support isn't confirmed yet`));
|
|
880
934
|
}
|
|
881
935
|
console.log();
|
|
882
936
|
if (flags.idleTimeout) {
|