// CRITICALimport { spawn, spawnSync } from "node:child_process";import { existsSync, readdirSync, statSync } from "node:fs";import { dirname, join, resolve } from "node:path";import { resolveBinary } from "../../core/command"; type CommandResult = { code: number | null; stdout: string; stderr: string;}; const DEFAULT_TIMEOUT_MS = 10_000; const runCommand = ( command: string, args: string[], timeoutMs = DEFAULT_TIMEOUT_MS): Promise<CommandResult> => { return new Promise((resolveResult) => { const child = spawn(command, args, { env: process.env }); let stdout = ""; let stderr = ""; const timer = setTimeout(() => { child.kill("SIGKILL"); }, timeoutMs); child.stdout?.on("data", (data) => { stdout += data.toString(); }); child.stderr?.on("data", (data) => { stderr += data.toString(); }); child.on("error", (error) => { clearTimeout(timer); resolveResult({ code: null, stdout: stdout.trim(), stderr: error.message }); }); child.on("close", (code) => { clearTimeout(timer); resolveResult({ code, stdout: stdout.trim(), stderr: stderr.trim() }); }); });}; const resolvePythonBinary = (): string | null => { const candidates: string[] = []; const override = process.env["VLLM_STUDIO_RUNTIME_PYTHON"]; if (override) { candidates.push(override); } candidates.push("python3", "python"); for (const candidate of candidates) { try { const result = spawnSync(candidate, ["--version"], { timeout: 2000 }); if (result.status === 0) { return candidate; } } catch { continue; } } return null;}; const resolveBundledWheel = (): { path: string; version: string | null } | null => { const runtimeDirectory = resolve(process.cwd(), "runtime", "wheels"); if (!existsSync(runtimeDirectory)) { return null; } const candidates = readdirSync(runtimeDirectory).filter( (file) => file.startsWith("vllm-") && file.endsWith(".whl") ); if (candidates.length === 0) { return null; } const withStats = candidates .map((file) => { const fullPath = join(runtimeDirectory, file); return { file, fullPath, mtime: statSync(fullPath).mtimeMs }; }) .sort((a, b) => b.mtime - a.mtime); const latest = withStats[0]; if (!latest) { return null; } const versionMatch = latest.file.match(/^vllm-([0-9A-Za-z.+-]+)-/); return { path: latest.fullPath, version: versionMatch?.[1] ?? null, };}; const resolveVllmBinary = (pythonPath: string | null): string | null => { if (pythonPath) { const vllmBin = join(dirname(pythonPath), "vllm"); if (existsSync(vllmBin)) { return vllmBin; } } return resolveBinary("vllm");}; export const getVllmRuntimeInfo = async (): Promise<{ installed: boolean; version: string | null; python_path: string | null; vllm_bin: string | null; bundled_wheel: { path: string; version: string | null } | null;}> => { const pythonPath = resolvePythonBinary(); const vllmBin = resolveVllmBinary(pythonPath); const bundledWheel = resolveBundledWheel(); if (!pythonPath) { return { installed: false, version: null, python_path: null, vllm_bin: vllmBin, bundled_wheel: bundledWheel, }; } const result = await runCommand(pythonPath, [ "-c", "import json, sys\ntry:\n import vllm\n print(json.dumps({'version': vllm.__version__, 'python': sys.executable}))\nexcept Exception:\n print(json.dumps({'version': None, 'python': sys.executable}))", ]); if (result.code !== 0) { return { installed: false, version: null, python_path: pythonPath, vllm_bin: vllmBin, bundled_wheel: bundledWheel, }; } let parsed: { version?: string | null; python?: string | null } | null = null; try { parsed = JSON.parse(result.stdout) as { version?: string | null; python?: string | null }; } catch { parsed = null; } return { installed: Boolean(parsed?.version), version: parsed?.version ?? null, python_path: parsed?.python ?? pythonPath, vllm_bin: vllmBin, bundled_wheel: bundledWheel, };}; export const getVllmConfigHelp = async (): Promise<{ config: string | null; error: string | null;}> => { const pythonPath = resolvePythonBinary(); const vllmBin = resolveVllmBinary(pythonPath); if (!pythonPath && !vllmBin) { return { config: null, error: "vLLM runtime not available" }; } const command = vllmBin ?? pythonPath ?? ""; const args = vllmBin ? ["serve", "--help"] : ["-m", "vllm.entrypoints.openai.api_server", "--help"]; const result = await runCommand(command, args, 15_000); if (result.code !== 0) { return { config: result.stdout || null, error: result.stderr || "Failed to fetch vLLM config", }; } return { config: result.stdout || null, error: null };}; export const upgradeVllmRuntime = async ( preferBundled = true): Promise<{ success: boolean; version: string | null; output: string | null; error: string | null; used_wheel: string | null;}> => { const pythonPath = resolvePythonBinary(); if (!pythonPath) { return { success: false, version: null, output: null, error: "Python runtime not found", used_wheel: null, }; } const bundledWheel = resolveBundledWheel(); const wheelPath = preferBundled && bundledWheel ? bundledWheel.path : null; const args = ["-m", "pip", "install", "--upgrade", wheelPath ?? "vllm"]; const result = await runCommand(pythonPath, args, 600_000); if (result.code !== 0) { return { success: false, version: null, output: result.stdout || null, error: result.stderr || "Upgrade failed", used_wheel: wheelPath, }; } const runtimeInfo = await getVllmRuntimeInfo(); return { success: true, version: runtimeInfo.version, output: result.stdout || null, error: result.stderr || null, used_wheel: wheelPath, };};
// CRITICAL
import { spawn, spawnSync } from "node:child_process";
import { existsSync, readdirSync, statSync } from "node:fs";
import { dirname, join, resolve } from "node:path";
import { resolveBinary } from "../../core/command";
type CommandResult = {
code: number | null;
stdout: string;
stderr: string;
};
const DEFAULT_TIMEOUT_MS = 10_000;
const runCommand = (
command: string,
args: string[],
timeoutMs = DEFAULT_TIMEOUT_MS
): Promise<CommandResult> => {
return new Promise((resolveResult) => {
const child = spawn(command, args, { env: process.env });
let stdout = "";
let stderr = "";
const timer = setTimeout(() => {
child.kill("SIGKILL");
}, timeoutMs);
child.stdout?.on("data", (data) => {
stdout += data.toString();
});
child.stderr?.on("data", (data) => {
stderr += data.toString();
});
child.on("error", (error) => {
clearTimeout(timer);
resolveResult({ code: null, stdout: stdout.trim(), stderr: error.message });
});
child.on("close", (code) => {
clearTimeout(timer);
resolveResult({ code, stdout: stdout.trim(), stderr: stderr.trim() });
});
});
};
const resolvePythonBinary = (): string | null => {
const candidates: string[] = [];
const override = process.env["VLLM_STUDIO_RUNTIME_PYTHON"];
if (override) {
candidates.push(override);
}
candidates.push("python3", "python");
for (const candidate of candidates) {
try {
const result = spawnSync(candidate, ["--version"], { timeout: 2000 });
if (result.status === 0) {
return candidate;
}
} catch {
continue;
}
}
return null;
};
const resolveBundledWheel = (): { path: string; version: string | null } | null => {
const runtimeDirectory = resolve(process.cwd(), "runtime", "wheels");
if (!existsSync(runtimeDirectory)) {
return null;
}
const candidates = readdirSync(runtimeDirectory).filter(
(file) => file.startsWith("vllm-") && file.endsWith(".whl")
);
if (candidates.length === 0) {
return null;
}
const withStats = candidates
.map((file) => {
const fullPath = join(runtimeDirectory, file);
return { file, fullPath, mtime: statSync(fullPath).mtimeMs };
})
.sort((a, b) => b.mtime - a.mtime);
const latest = withStats[0];
if (!latest) {
return null;
}
const versionMatch = latest.file.match(/^vllm-([0-9A-Za-z.+-]+)-/);
return {
path: latest.fullPath,
version: versionMatch?.[1] ?? null,
};
};
const resolveVllmBinary = (pythonPath: string | null): string | null => {
if (pythonPath) {
const vllmBin = join(dirname(pythonPath), "vllm");
if (existsSync(vllmBin)) {
return vllmBin;
}
}
return resolveBinary("vllm");
};
export const getVllmRuntimeInfo = async (): Promise<{
installed: boolean;
version: string | null;
python_path: string | null;
vllm_bin: string | null;
bundled_wheel: { path: string; version: string | null } | null;
}> => {
const pythonPath = resolvePythonBinary();
const vllmBin = resolveVllmBinary(pythonPath);
const bundledWheel = resolveBundledWheel();
if (!pythonPath) {
return {
installed: false,
version: null,
python_path: null,
vllm_bin: vllmBin,
bundled_wheel: bundledWheel,
};
}
const result = await runCommand(pythonPath, [
"-c",
"import json, sys\ntry:\n import vllm\n print(json.dumps({'version': vllm.__version__, 'python': sys.executable}))\nexcept Exception:\n print(json.dumps({'version': None, 'python': sys.executable}))",
]);
if (result.code !== 0) {
return {
installed: false,
version: null,
python_path: pythonPath,
vllm_bin: vllmBin,
bundled_wheel: bundledWheel,
};
}
let parsed: { version?: string | null; python?: string | null } | null = null;
try {
parsed = JSON.parse(result.stdout) as { version?: string | null; python?: string | null };
} catch {
parsed = null;
}
return {
installed: Boolean(parsed?.version),
version: parsed?.version ?? null,
python_path: parsed?.python ?? pythonPath,
vllm_bin: vllmBin,
bundled_wheel: bundledWheel,
};
};
export const getVllmConfigHelp = async (): Promise<{
config: string | null;
error: string | null;
}> => {
const pythonPath = resolvePythonBinary();
const vllmBin = resolveVllmBinary(pythonPath);
if (!pythonPath && !vllmBin) {
return { config: null, error: "vLLM runtime not available" };
}
const command = vllmBin ?? pythonPath ?? "";
const args = vllmBin
? ["serve", "--help"]
: ["-m", "vllm.entrypoints.openai.api_server", "--help"];
const result = await runCommand(command, args, 15_000);
if (result.code !== 0) {
return {
config: result.stdout || null,
error: result.stderr || "Failed to fetch vLLM config",
};
}
return { config: result.stdout || null, error: null };
};
export const upgradeVllmRuntime = async (
preferBundled = true
): Promise<{
success: boolean;
version: string | null;
output: string | null;
error: string | null;
used_wheel: string | null;
}> => {
const pythonPath = resolvePythonBinary();
if (!pythonPath) {
return {
success: false,
version: null,
output: null,
error: "Python runtime not found",
used_wheel: null,
};
}
const bundledWheel = resolveBundledWheel();
const wheelPath = preferBundled && bundledWheel ? bundledWheel.path : null;
const args = ["-m", "pip", "install", "--upgrade", wheelPath ?? "vllm"];
const result = await runCommand(pythonPath, args, 600_000);
if (result.code !== 0) {
return {
success: false,
version: null,
output: result.stdout || null,
error: result.stderr || "Upgrade failed",
used_wheel: wheelPath,
};
}
const runtimeInfo = await getVllmRuntimeInfo();
return {
success: true,
version: runtimeInfo.version,
output: result.stdout || null,
error: result.stderr || null,
used_wheel: wheelPath,
};
};