@mariozechner/pi 0.25.3 → 0.25.4

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.
@@ -1 +1 @@
1
- {"version":3,"file":"models.d.ts","sourceRoot":"","sources":["../../src/commands/models.ts"],"names":[],"mappings":"AA0EA;;GAEG;AACH,eAAO,MAAM,UAAU;;;;;;mBA+UtB,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,SAAS;;mBA0BrB,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,aAAa;;mBA2BzB,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,UAAU;;mBAwEtB,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,QAAQ;;mBA+BpB,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,eAAe,qBA+J3B,CAAC","sourcesContent":["import chalk from \"chalk\";\nimport { spawn } from \"child_process\";\nimport { readFileSync } from \"fs\";\nimport { dirname, join } from \"path\";\nimport { fileURLToPath } from \"url\";\nimport { getActivePod, loadConfig, saveConfig } from \"../config.js\";\nimport { getModelConfig, getModelName, isKnownModel } from \"../model-configs.js\";\nimport { sshExec } from \"../ssh.js\";\nimport type { Pod } from \"../types.js\";\n\n/**\n * Get the pod to use (active or override)\n */\nconst getPod = (podOverride?: string): { name: string; pod: Pod } => {\n\tif (podOverride) {\n\t\tconst config = loadConfig();\n\t\tconst pod = config.pods[podOverride];\n\t\tif (!pod) {\n\t\t\tconsole.error(chalk.red(`Pod '${podOverride}' not found`));\n\t\t\tprocess.exit(1);\n\t\t}\n\t\treturn { name: podOverride, pod };\n\t}\n\n\tconst active = getActivePod();\n\tif (!active) {\n\t\tconsole.error(chalk.red(\"No active pod. Use 'pi pods active <name>' to set one.\"));\n\t\tprocess.exit(1);\n\t}\n\treturn active;\n};\n\n/**\n * Find next available port starting from 8001\n */\nconst getNextPort = (pod: Pod): number => {\n\tconst usedPorts = Object.values(pod.models).map((m) => m.port);\n\tlet port = 8001;\n\twhile (usedPorts.includes(port)) {\n\t\tport++;\n\t}\n\treturn port;\n};\n\n/**\n * Select GPUs for model deployment (round-robin)\n */\nconst selectGPUs = (pod: Pod, count: number = 1): number[] => {\n\tif (count === pod.gpus.length) {\n\t\t// Use all GPUs\n\t\treturn pod.gpus.map((g) => g.id);\n\t}\n\n\t// Count GPU usage across all models\n\tconst gpuUsage = new Map<number, number>();\n\tfor (const gpu of pod.gpus) {\n\t\tgpuUsage.set(gpu.id, 0);\n\t}\n\n\tfor (const model of Object.values(pod.models)) {\n\t\tfor (const gpuId of model.gpu) {\n\t\t\tgpuUsage.set(gpuId, (gpuUsage.get(gpuId) || 0) + 1);\n\t\t}\n\t}\n\n\t// Sort GPUs by usage (least used first)\n\tconst sortedGPUs = Array.from(gpuUsage.entries())\n\t\t.sort((a, b) => a[1] - b[1])\n\t\t.map((entry) => entry[0]);\n\n\t// Return the least used GPUs\n\treturn sortedGPUs.slice(0, count);\n};\n\n/**\n * Start a model\n */\nexport const startModel = async (\n\tmodelId: string,\n\tname: string,\n\toptions: {\n\t\tpod?: string;\n\t\tvllmArgs?: string[];\n\t\tmemory?: string;\n\t\tcontext?: string;\n\t\tgpus?: number;\n\t},\n) => {\n\tconst { name: podName, pod } = getPod(options.pod);\n\n\t// Validation\n\tif (!pod.modelsPath) {\n\t\tconsole.error(chalk.red(\"Pod does not have a models path configured\"));\n\t\tprocess.exit(1);\n\t}\n\tif (pod.models[name]) {\n\t\tconsole.error(chalk.red(`Model '${name}' already exists on pod '${podName}'`));\n\t\tprocess.exit(1);\n\t}\n\n\tconst port = getNextPort(pod);\n\n\t// Determine GPU allocation and vLLM args\n\tlet gpus: number[] = [];\n\tlet vllmArgs: string[] = [];\n\tlet modelConfig = null;\n\n\tif (options.vllmArgs?.length) {\n\t\t// Custom args override everything\n\t\tvllmArgs = options.vllmArgs;\n\t\tconsole.log(chalk.gray(\"Using custom vLLM args, GPU allocation managed by vLLM\"));\n\t} else if (isKnownModel(modelId)) {\n\t\t// Handle --gpus parameter for known models\n\t\tif (options.gpus) {\n\t\t\t// Validate GPU count\n\t\t\tif (options.gpus > pod.gpus.length) {\n\t\t\t\tconsole.error(chalk.red(`Error: Requested ${options.gpus} GPUs but pod only has ${pod.gpus.length}`));\n\t\t\t\tprocess.exit(1);\n\t\t\t}\n\n\t\t\t// Try to find config for requested GPU count\n\t\t\tmodelConfig = getModelConfig(modelId, pod.gpus, options.gpus);\n\t\t\tif (modelConfig) {\n\t\t\t\tgpus = selectGPUs(pod, options.gpus);\n\t\t\t\tvllmArgs = [...(modelConfig.args || [])];\n\t\t\t} else {\n\t\t\t\tconsole.error(\n\t\t\t\t\tchalk.red(`Model '${getModelName(modelId)}' does not have a configuration for ${options.gpus} GPU(s)`),\n\t\t\t\t);\n\t\t\t\tconsole.error(chalk.yellow(\"Available configurations:\"));\n\n\t\t\t\t// Show available configurations\n\t\t\t\tfor (let gpuCount = 1; gpuCount <= pod.gpus.length; gpuCount++) {\n\t\t\t\t\tconst config = getModelConfig(modelId, pod.gpus, gpuCount);\n\t\t\t\t\tif (config) {\n\t\t\t\t\t\tconsole.error(chalk.gray(` - ${gpuCount} GPU(s)`));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tprocess.exit(1);\n\t\t\t}\n\t\t} else {\n\t\t\t// Find best config for this hardware (original behavior)\n\t\t\tfor (let gpuCount = pod.gpus.length; gpuCount >= 1; gpuCount--) {\n\t\t\t\tmodelConfig = getModelConfig(modelId, pod.gpus, gpuCount);\n\t\t\t\tif (modelConfig) {\n\t\t\t\t\tgpus = selectGPUs(pod, gpuCount);\n\t\t\t\t\tvllmArgs = [...(modelConfig.args || [])];\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!modelConfig) {\n\t\t\t\tconsole.error(chalk.red(`Model '${getModelName(modelId)}' not compatible with this pod's GPUs`));\n\t\t\t\tprocess.exit(1);\n\t\t\t}\n\t\t}\n\t} else {\n\t\t// Unknown model\n\t\tif (options.gpus) {\n\t\t\tconsole.error(chalk.red(\"Error: --gpus can only be used with predefined models\"));\n\t\t\tconsole.error(chalk.yellow(\"For custom models, use --vllm with tensor-parallel-size or similar arguments\"));\n\t\t\tprocess.exit(1);\n\t\t}\n\t\t// Single GPU default\n\t\tgpus = selectGPUs(pod, 1);\n\t\tconsole.log(chalk.gray(\"Unknown model, defaulting to single GPU\"));\n\t}\n\n\t// Apply memory/context overrides\n\tif (!options.vllmArgs?.length) {\n\t\tif (options.memory) {\n\t\t\tconst fraction = parseFloat(options.memory.replace(\"%\", \"\")) / 100;\n\t\t\tvllmArgs = vllmArgs.filter((arg) => !arg.includes(\"gpu-memory-utilization\"));\n\t\t\tvllmArgs.push(\"--gpu-memory-utilization\", String(fraction));\n\t\t}\n\t\tif (options.context) {\n\t\t\tconst contextSizes: Record<string, number> = {\n\t\t\t\t\"4k\": 4096,\n\t\t\t\t\"8k\": 8192,\n\t\t\t\t\"16k\": 16384,\n\t\t\t\t\"32k\": 32768,\n\t\t\t\t\"64k\": 65536,\n\t\t\t\t\"128k\": 131072,\n\t\t\t};\n\t\t\tconst maxTokens = contextSizes[options.context.toLowerCase()] || parseInt(options.context, 10);\n\t\t\tvllmArgs = vllmArgs.filter((arg) => !arg.includes(\"max-model-len\"));\n\t\t\tvllmArgs.push(\"--max-model-len\", String(maxTokens));\n\t\t}\n\t}\n\n\t// Show what we're doing\n\tconsole.log(chalk.green(`Starting model '${name}' on pod '${podName}'...`));\n\tconsole.log(`Model: ${modelId}`);\n\tconsole.log(`Port: ${port}`);\n\tconsole.log(`GPU(s): ${gpus.length ? gpus.join(\", \") : \"Managed by vLLM\"}`);\n\tif (modelConfig?.notes) console.log(chalk.yellow(`Note: ${modelConfig.notes}`));\n\tconsole.log(\"\");\n\n\t// Read and customize model_run.sh script with our values\n\tconst scriptPath = join(dirname(fileURLToPath(import.meta.url)), \"../../scripts/model_run.sh\");\n\tlet scriptContent = readFileSync(scriptPath, \"utf-8\");\n\n\t// Replace placeholders - no escaping needed, heredoc with 'EOF' is literal\n\tscriptContent = scriptContent\n\t\t.replace(\"{{MODEL_ID}}\", modelId)\n\t\t.replace(\"{{NAME}}\", name)\n\t\t.replace(\"{{PORT}}\", String(port))\n\t\t.replace(\"{{VLLM_ARGS}}\", vllmArgs.join(\" \"));\n\n\t// Upload customized script\n\tconst result = await sshExec(\n\t\tpod.ssh,\n\t\t`cat > /tmp/model_run_${name}.sh << 'EOF'\n${scriptContent}\nEOF\nchmod +x /tmp/model_run_${name}.sh`,\n\t);\n\n\t// Prepare environment\n\tconst env = [\n\t\t`HF_TOKEN='${process.env.HF_TOKEN}'`,\n\t\t`PI_API_KEY='${process.env.PI_API_KEY}'`,\n\t\t`HF_HUB_ENABLE_HF_TRANSFER=1`,\n\t\t`VLLM_NO_USAGE_STATS=1`,\n\t\t`PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True`,\n\t\t`FORCE_COLOR=1`,\n\t\t`TERM=xterm-256color`,\n\t\t...(gpus.length === 1 ? [`CUDA_VISIBLE_DEVICES=${gpus[0]}`] : []),\n\t\t...Object.entries(modelConfig?.env || {}).map(([k, v]) => `${k}='${v}'`),\n\t]\n\t\t.map((e) => `export ${e}`)\n\t\t.join(\"\\n\");\n\n\t// Start the model runner with script command for pseudo-TTY (preserves colors)\n\t// Note: We use script to preserve colors and create a log file\n\t// setsid creates a new session so it survives SSH disconnection\n\tconst startCmd = `\n\t\t${env}\n\t\tmkdir -p ~/.vllm_logs\n\t\t# Create a wrapper that monitors the script command\n\t\tcat > /tmp/model_wrapper_${name}.sh << 'WRAPPER'\n#!/bin/bash\nscript -q -f -c \"/tmp/model_run_${name}.sh\" ~/.vllm_logs/${name}.log\nexit_code=$?\necho \"Script exited with code $exit_code\" >> ~/.vllm_logs/${name}.log\nexit $exit_code\nWRAPPER\n\t\tchmod +x /tmp/model_wrapper_${name}.sh\n\t\tsetsid /tmp/model_wrapper_${name}.sh </dev/null >/dev/null 2>&1 &\n\t\techo $!\n\t\texit 0\n\t`;\n\n\tconst pidResult = await sshExec(pod.ssh, startCmd);\n\tconst pid = parseInt(pidResult.stdout.trim(), 10);\n\tif (!pid) {\n\t\tconsole.error(chalk.red(\"Failed to start model runner\"));\n\t\tprocess.exit(1);\n\t}\n\n\t// Save to config\n\tconst config = loadConfig();\n\tconfig.pods[podName].models[name] = { model: modelId, port, gpu: gpus, pid };\n\tsaveConfig(config);\n\n\tconsole.log(`Model runner started with PID: ${pid}`);\n\tconsole.log(\"Streaming logs... (waiting for startup)\\n\");\n\n\t// Small delay to ensure log file is created\n\tawait new Promise((resolve) => setTimeout(resolve, 500));\n\n\t// Stream logs with color support, watching for startup complete\n\tconst sshParts = pod.ssh.split(\" \");\n\tconst sshCommand = sshParts[0]; // \"ssh\"\n\tconst sshArgs = sshParts.slice(1); // [\"root@86.38.238.55\"]\n\tconst host = sshArgs[0].split(\"@\")[1] || \"localhost\";\n\tconst tailCmd = `tail -f ~/.vllm_logs/${name}.log`;\n\n\t// Build the full args array for spawn\n\tconst fullArgs = [...sshArgs, tailCmd];\n\n\tconst logProcess = spawn(sshCommand, fullArgs, {\n\t\tstdio: [\"inherit\", \"pipe\", \"pipe\"], // capture stdout and stderr\n\t\tenv: { ...process.env, FORCE_COLOR: \"1\" },\n\t});\n\n\tlet interrupted = false;\n\tlet startupComplete = false;\n\tlet startupFailed = false;\n\tlet failureReason = \"\";\n\n\t// Handle Ctrl+C\n\tconst sigintHandler = () => {\n\t\tinterrupted = true;\n\t\tlogProcess.kill();\n\t};\n\tprocess.on(\"SIGINT\", sigintHandler);\n\n\t// Process log output line by line\n\tconst processOutput = (data: Buffer) => {\n\t\tconst lines = data.toString().split(\"\\n\");\n\t\tfor (const line of lines) {\n\t\t\tif (line) {\n\t\t\t\tconsole.log(line); // Echo the line to console\n\n\t\t\t\t// Check for startup complete message\n\t\t\t\tif (line.includes(\"Application startup complete\")) {\n\t\t\t\t\tstartupComplete = true;\n\t\t\t\t\tlogProcess.kill(); // Stop tailing logs\n\t\t\t\t}\n\n\t\t\t\t// Check for failure indicators\n\t\t\t\tif (line.includes(\"Model runner exiting with code\") && !line.includes(\"code 0\")) {\n\t\t\t\t\tstartupFailed = true;\n\t\t\t\t\tfailureReason = \"Model runner failed to start\";\n\t\t\t\t\tlogProcess.kill();\n\t\t\t\t}\n\t\t\t\tif (line.includes(\"Script exited with code\") && !line.includes(\"code 0\")) {\n\t\t\t\t\tstartupFailed = true;\n\t\t\t\t\tfailureReason = \"Script failed to execute\";\n\t\t\t\t\tlogProcess.kill();\n\t\t\t\t}\n\t\t\t\tif (line.includes(\"torch.OutOfMemoryError\") || line.includes(\"CUDA out of memory\")) {\n\t\t\t\t\tstartupFailed = true;\n\t\t\t\t\tfailureReason = \"Out of GPU memory (OOM)\";\n\t\t\t\t\t// Don't kill immediately - let it show more error context\n\t\t\t\t}\n\t\t\t\tif (line.includes(\"RuntimeError: Engine core initialization failed\")) {\n\t\t\t\t\tstartupFailed = true;\n\t\t\t\t\tfailureReason = \"vLLM engine initialization failed\";\n\t\t\t\t\tlogProcess.kill();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t};\n\n\tlogProcess.stdout?.on(\"data\", processOutput);\n\tlogProcess.stderr?.on(\"data\", processOutput);\n\n\tawait new Promise<void>((resolve) => logProcess.on(\"exit\", resolve));\n\tprocess.removeListener(\"SIGINT\", sigintHandler);\n\n\tif (startupFailed) {\n\t\t// Model failed to start - clean up and report error\n\t\tconsole.log(\"\\n\" + chalk.red(`✗ Model failed to start: ${failureReason}`));\n\n\t\t// Remove the failed model from config\n\t\tconst config = loadConfig();\n\t\tdelete config.pods[podName].models[name];\n\t\tsaveConfig(config);\n\n\t\tconsole.log(chalk.yellow(\"\\nModel has been removed from configuration.\"));\n\n\t\t// Provide helpful suggestions based on failure reason\n\t\tif (failureReason.includes(\"OOM\") || failureReason.includes(\"memory\")) {\n\t\t\tconsole.log(\"\\n\" + chalk.bold(\"Suggestions:\"));\n\t\t\tconsole.log(\" • Try reducing GPU memory utilization: --memory 50%\");\n\t\t\tconsole.log(\" • Use a smaller context window: --context 4k\");\n\t\t\tconsole.log(\" • Use a quantized version of the model (e.g., FP8)\");\n\t\t\tconsole.log(\" • Use more GPUs with tensor parallelism\");\n\t\t\tconsole.log(\" • Try a smaller model variant\");\n\t\t}\n\n\t\tconsole.log(\"\\n\" + chalk.cyan('Check full logs: pi ssh \"tail -100 ~/.vllm_logs/' + name + '.log\"'));\n\t\tprocess.exit(1);\n\t} else if (startupComplete) {\n\t\t// Model started successfully - output connection details\n\t\tconsole.log(\"\\n\" + chalk.green(\"✓ Model started successfully!\"));\n\t\tconsole.log(\"\\n\" + chalk.bold(\"Connection Details:\"));\n\t\tconsole.log(chalk.cyan(\"─\".repeat(50)));\n\t\tconsole.log(chalk.white(\"Base URL: \") + chalk.yellow(`http://${host}:${port}/v1`));\n\t\tconsole.log(chalk.white(\"Model: \") + chalk.yellow(modelId));\n\t\tconsole.log(chalk.white(\"API Key: \") + chalk.yellow(process.env.PI_API_KEY || \"(not set)\"));\n\t\tconsole.log(chalk.cyan(\"─\".repeat(50)));\n\n\t\tconsole.log(\"\\n\" + chalk.bold(\"Export for shell:\"));\n\t\tconsole.log(chalk.gray(`export OPENAI_BASE_URL=\"http://${host}:${port}/v1\"`));\n\t\tconsole.log(chalk.gray(`export OPENAI_API_KEY=\"${process.env.PI_API_KEY || \"your-api-key\"}\"`));\n\t\tconsole.log(chalk.gray(`export OPENAI_MODEL=\"${modelId}\"`));\n\n\t\tconsole.log(\"\\n\" + chalk.bold(\"Example usage:\"));\n\t\tconsole.log(\n\t\t\tchalk.gray(`\n # Python\n from openai import OpenAI\n client = OpenAI() # Uses env vars\n response = client.chat.completions.create(\n model=\"${modelId}\",\n messages=[{\"role\": \"user\", \"content\": \"Hello!\"}]\n )\n\n # CLI\n curl $OPENAI_BASE_URL/chat/completions \\\\\n -H \"Authorization: Bearer $OPENAI_API_KEY\" \\\\\n -H \"Content-Type: application/json\" \\\\\n -d '{\"model\":\"${modelId}\",\"messages\":[{\"role\":\"user\",\"content\":\"Hi\"}]}'`),\n\t\t);\n\t\tconsole.log(\"\");\n\t\tconsole.log(chalk.cyan(`Chat with model: pi agent ${name} \"Your message\"`));\n\t\tconsole.log(chalk.cyan(`Interactive mode: pi agent ${name} -i`));\n\t\tconsole.log(chalk.cyan(`Monitor logs: pi logs ${name}`));\n\t\tconsole.log(chalk.cyan(`Stop model: pi stop ${name}`));\n\t} else if (interrupted) {\n\t\tconsole.log(chalk.yellow(\"\\n\\nStopped monitoring. Model deployment continues in background.\"));\n\t\tconsole.log(chalk.cyan(`Chat with model: pi agent ${name} \"Your message\"`));\n\t\tconsole.log(chalk.cyan(`Check status: pi logs ${name}`));\n\t\tconsole.log(chalk.cyan(`Stop model: pi stop ${name}`));\n\t} else {\n\t\tconsole.log(chalk.yellow(\"\\n\\nLog stream ended. Model may still be running.\"));\n\t\tconsole.log(chalk.cyan(`Chat with model: pi agent ${name} \"Your message\"`));\n\t\tconsole.log(chalk.cyan(`Check status: pi logs ${name}`));\n\t\tconsole.log(chalk.cyan(`Stop model: pi stop ${name}`));\n\t}\n};\n\n/**\n * Stop a model\n */\nexport const stopModel = async (name: string, options: { pod?: string }) => {\n\tconst { name: podName, pod } = getPod(options.pod);\n\n\tconst model = pod.models[name];\n\tif (!model) {\n\t\tconsole.error(chalk.red(`Model '${name}' not found on pod '${podName}'`));\n\t\tprocess.exit(1);\n\t}\n\n\tconsole.log(chalk.yellow(`Stopping model '${name}' on pod '${podName}'...`));\n\n\t// Kill the script process and all its children\n\t// Using pkill to kill the process and all children\n\tconst killCmd = `\n\t\t# Kill the script process and all its children\n\t\tpkill -TERM -P ${model.pid} 2>/dev/null || true\n\t\tkill ${model.pid} 2>/dev/null || true\n\t`;\n\tawait sshExec(pod.ssh, killCmd);\n\n\t// Remove from config\n\tconst config = loadConfig();\n\tdelete config.pods[podName].models[name];\n\tsaveConfig(config);\n\n\tconsole.log(chalk.green(`✓ Model '${name}' stopped`));\n};\n\n/**\n * Stop all models on a pod\n */\nexport const stopAllModels = async (options: { pod?: string }) => {\n\tconst { name: podName, pod } = getPod(options.pod);\n\n\tconst modelNames = Object.keys(pod.models);\n\tif (modelNames.length === 0) {\n\t\tconsole.log(`No models running on pod '${podName}'`);\n\t\treturn;\n\t}\n\n\tconsole.log(chalk.yellow(`Stopping ${modelNames.length} model(s) on pod '${podName}'...`));\n\n\t// Kill all script processes and their children\n\tconst pids = Object.values(pod.models).map((m) => m.pid);\n\tconst killCmd = `\n\t\tfor PID in ${pids.join(\" \")}; do\n\t\t\tpkill -TERM -P $PID 2>/dev/null || true\n\t\t\tkill $PID 2>/dev/null || true\n\t\tdone\n\t`;\n\tawait sshExec(pod.ssh, killCmd);\n\n\t// Clear all models from config\n\tconst config = loadConfig();\n\tconfig.pods[podName].models = {};\n\tsaveConfig(config);\n\n\tconsole.log(chalk.green(`✓ Stopped all models: ${modelNames.join(\", \")}`));\n};\n\n/**\n * List all models\n */\nexport const listModels = async (options: { pod?: string }) => {\n\tconst { name: podName, pod } = getPod(options.pod);\n\n\tconst modelNames = Object.keys(pod.models);\n\tif (modelNames.length === 0) {\n\t\tconsole.log(`No models running on pod '${podName}'`);\n\t\treturn;\n\t}\n\n\t// Get pod SSH host for URL display\n\tconst sshParts = pod.ssh.split(\" \");\n\tconst host = sshParts.find((p) => p.includes(\"@\"))?.split(\"@\")[1] || \"unknown\";\n\n\tconsole.log(`Models on pod '${chalk.bold(podName)}':`);\n\tfor (const name of modelNames) {\n\t\tconst model = pod.models[name];\n\t\tconst gpuStr =\n\t\t\tmodel.gpu.length > 1\n\t\t\t\t? `GPUs ${model.gpu.join(\",\")}`\n\t\t\t\t: model.gpu.length === 1\n\t\t\t\t\t? `GPU ${model.gpu[0]}`\n\t\t\t\t\t: \"GPU unknown\";\n\t\tconsole.log(` ${chalk.green(name)} - Port ${model.port} - ${gpuStr} - PID ${model.pid}`);\n\t\tconsole.log(` Model: ${chalk.gray(model.model)}`);\n\t\tconsole.log(` URL: ${chalk.cyan(`http://${host}:${model.port}/v1`)}`);\n\t}\n\n\t// Optionally verify processes are still running\n\tconsole.log(\"\");\n\tconsole.log(\"Verifying processes...\");\n\tlet anyDead = false;\n\tfor (const name of modelNames) {\n\t\tconst model = pod.models[name];\n\t\t// Check both the wrapper process and if vLLM is responding\n\t\tconst checkCmd = `\n\t\t\t# Check if wrapper process exists\n\t\t\tif ps -p ${model.pid} > /dev/null 2>&1; then\n\t\t\t\t# Process exists, now check if vLLM is responding\n\t\t\t\tif curl -s -f http://localhost:${model.port}/health > /dev/null 2>&1; then\n\t\t\t\t\techo \"running\"\n\t\t\t\telse\n\t\t\t\t\t# Check if it's still starting up\n\t\t\t\t\tif tail -n 20 ~/.vllm_logs/${name}.log 2>/dev/null | grep -q \"ERROR\\\\|Failed\\\\|Cuda error\\\\|died\"; then\n\t\t\t\t\t\techo \"crashed\"\n\t\t\t\t\telse\n\t\t\t\t\t\techo \"starting\"\n\t\t\t\t\tfi\n\t\t\t\tfi\n\t\t\telse\n\t\t\t\techo \"dead\"\n\t\t\tfi\n\t\t`;\n\t\tconst result = await sshExec(pod.ssh, checkCmd);\n\t\tconst status = result.stdout.trim();\n\t\tif (status === \"dead\") {\n\t\t\tconsole.log(chalk.red(` ${name}: Process ${model.pid} is not running`));\n\t\t\tanyDead = true;\n\t\t} else if (status === \"crashed\") {\n\t\t\tconsole.log(chalk.red(` ${name}: vLLM crashed (check logs with 'pi logs ${name}')`));\n\t\t\tanyDead = true;\n\t\t} else if (status === \"starting\") {\n\t\t\tconsole.log(chalk.yellow(` ${name}: Still starting up...`));\n\t\t}\n\t}\n\n\tif (anyDead) {\n\t\tconsole.log(\"\");\n\t\tconsole.log(chalk.yellow(\"Some models are not running. Clean up with:\"));\n\t\tconsole.log(chalk.cyan(\" pi stop <name>\"));\n\t} else {\n\t\tconsole.log(chalk.green(\"✓ All processes verified\"));\n\t}\n};\n\n/**\n * View model logs\n */\nexport const viewLogs = async (name: string, options: { pod?: string }) => {\n\tconst { name: podName, pod } = getPod(options.pod);\n\n\tconst model = pod.models[name];\n\tif (!model) {\n\t\tconsole.error(chalk.red(`Model '${name}' not found on pod '${podName}'`));\n\t\tprocess.exit(1);\n\t}\n\n\tconsole.log(chalk.green(`Streaming logs for '${name}' on pod '${podName}'...`));\n\tconsole.log(chalk.gray(\"Press Ctrl+C to stop\"));\n\tconsole.log(\"\");\n\n\t// Stream logs with color preservation\n\tconst sshParts = pod.ssh.split(\" \");\n\tconst sshCommand = sshParts[0]; // \"ssh\"\n\tconst sshArgs = sshParts.slice(1); // [\"root@86.38.238.55\"]\n\tconst tailCmd = `tail -f ~/.vllm_logs/${name}.log`;\n\n\tconst logProcess = spawn(sshCommand, [...sshArgs, tailCmd], {\n\t\tstdio: \"inherit\",\n\t\tenv: {\n\t\t\t...process.env,\n\t\t\tFORCE_COLOR: \"1\",\n\t\t},\n\t});\n\n\t// Wait for process to exit\n\tawait new Promise<void>((resolve) => {\n\t\tlogProcess.on(\"exit\", () => resolve());\n\t});\n};\n\n/**\n * Show known models and their hardware requirements\n */\nexport const showKnownModels = async () => {\n\tconst __filename = fileURLToPath(import.meta.url);\n\tconst __dirname = dirname(__filename);\n\tconst modelsJsonPath = join(__dirname, \"..\", \"models.json\");\n\tconst modelsJson = JSON.parse(readFileSync(modelsJsonPath, \"utf-8\"));\n\tconst models = modelsJson.models;\n\n\t// Get active pod info if available\n\tconst activePod = getActivePod();\n\tlet podGpuCount = 0;\n\tlet podGpuType = \"\";\n\n\tif (activePod) {\n\t\tpodGpuCount = activePod.pod.gpus.length;\n\t\t// Extract GPU type from name (e.g., \"NVIDIA H200\" -> \"H200\")\n\t\tpodGpuType = activePod.pod.gpus[0]?.name?.replace(\"NVIDIA\", \"\")?.trim()?.split(\" \")[0] || \"\";\n\n\t\tconsole.log(chalk.bold(`Known Models for ${activePod.name} (${podGpuCount}x ${podGpuType || \"GPU\"}):\\n`));\n\t} else {\n\t\tconsole.log(chalk.bold(\"Known Models:\\n\"));\n\t\tconsole.log(chalk.yellow(\"No active pod. Use 'pi pods active <name>' to filter compatible models.\\n\"));\n\t}\n\n\tconsole.log(\"Usage: pi start <model> --name <name> [options]\\n\");\n\n\t// Group models by compatibility and family\n\tconst compatible: Record<string, Array<{ id: string; name: string; config: string; notes?: string }>> = {};\n\tconst incompatible: Record<string, Array<{ id: string; name: string; minGpu: string; notes?: string }>> = {};\n\n\tfor (const [modelId, info] of Object.entries(models)) {\n\t\tconst modelInfo = info as any;\n\t\tconst family = modelInfo.name.split(\"-\")[0] || \"Other\";\n\n\t\tlet isCompatible = false;\n\t\tlet compatibleConfig = \"\";\n\t\tlet minGpu = \"Unknown\";\n\t\tlet minNotes: string | undefined;\n\n\t\tif (modelInfo.configs && modelInfo.configs.length > 0) {\n\t\t\t// Sort configs by GPU count to find minimum\n\t\t\tconst sortedConfigs = [...modelInfo.configs].sort((a: any, b: any) => (a.gpuCount || 1) - (b.gpuCount || 1));\n\n\t\t\t// Find minimum requirements\n\t\t\tconst minConfig = sortedConfigs[0];\n\t\t\tconst minGpuCount = minConfig.gpuCount || 1;\n\t\t\tconst gpuTypes = minConfig.gpuTypes?.join(\"/\") || \"H100/H200\";\n\n\t\t\tif (minGpuCount === 1) {\n\t\t\t\tminGpu = `1x ${gpuTypes}`;\n\t\t\t} else {\n\t\t\t\tminGpu = `${minGpuCount}x ${gpuTypes}`;\n\t\t\t}\n\n\t\t\tminNotes = minConfig.notes || modelInfo.notes;\n\n\t\t\t// Check compatibility with active pod\n\t\t\tif (activePod && podGpuCount > 0) {\n\t\t\t\t// Find best matching config for this pod\n\t\t\t\tfor (const config of sortedConfigs) {\n\t\t\t\t\tconst configGpuCount = config.gpuCount || 1;\n\t\t\t\t\tconst configGpuTypes = config.gpuTypes || [];\n\n\t\t\t\t\t// Check if we have enough GPUs\n\t\t\t\t\tif (configGpuCount <= podGpuCount) {\n\t\t\t\t\t\t// Check if GPU type matches (if specified)\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\tconfigGpuTypes.length === 0 ||\n\t\t\t\t\t\t\tconfigGpuTypes.some((type: string) => podGpuType.includes(type) || type.includes(podGpuType))\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\tisCompatible = true;\n\t\t\t\t\t\t\tif (configGpuCount === 1) {\n\t\t\t\t\t\t\t\tcompatibleConfig = `1x ${podGpuType}`;\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tcompatibleConfig = `${configGpuCount}x ${podGpuType}`;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tminNotes = config.notes || modelInfo.notes;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tconst modelEntry = {\n\t\t\tid: modelId,\n\t\t\tname: modelInfo.name,\n\t\t\tnotes: minNotes,\n\t\t};\n\n\t\tif (activePod && isCompatible) {\n\t\t\tif (!compatible[family]) {\n\t\t\t\tcompatible[family] = [];\n\t\t\t}\n\t\t\tcompatible[family].push({ ...modelEntry, config: compatibleConfig });\n\t\t} else {\n\t\t\tif (!incompatible[family]) {\n\t\t\t\tincompatible[family] = [];\n\t\t\t}\n\t\t\tincompatible[family].push({ ...modelEntry, minGpu });\n\t\t}\n\t}\n\n\t// Display compatible models first\n\tif (activePod && Object.keys(compatible).length > 0) {\n\t\tconsole.log(chalk.green.bold(\"✓ Compatible Models:\\n\"));\n\n\t\tconst sortedFamilies = Object.keys(compatible).sort();\n\t\tfor (const family of sortedFamilies) {\n\t\t\tconsole.log(chalk.cyan(`${family} Models:`));\n\n\t\t\tconst modelList = compatible[family].sort((a, b) => a.name.localeCompare(b.name));\n\n\t\t\tfor (const model of modelList) {\n\t\t\t\tconsole.log(` ${chalk.green(model.id)}`);\n\t\t\t\tconsole.log(` Name: ${model.name}`);\n\t\t\t\tconsole.log(` Config: ${model.config}`);\n\t\t\t\tif (model.notes) {\n\t\t\t\t\tconsole.log(chalk.gray(` Note: ${model.notes}`));\n\t\t\t\t}\n\t\t\t\tconsole.log(\"\");\n\t\t\t}\n\t\t}\n\t}\n\n\t// Display incompatible models\n\tif (Object.keys(incompatible).length > 0) {\n\t\tif (activePod && Object.keys(compatible).length > 0) {\n\t\t\tconsole.log(chalk.red.bold(\"✗ Incompatible Models (need more/different GPUs):\\n\"));\n\t\t}\n\n\t\tconst sortedFamilies = Object.keys(incompatible).sort();\n\t\tfor (const family of sortedFamilies) {\n\t\t\tif (!activePod) {\n\t\t\t\tconsole.log(chalk.cyan(`${family} Models:`));\n\t\t\t} else {\n\t\t\t\tconsole.log(chalk.gray(`${family} Models:`));\n\t\t\t}\n\n\t\t\tconst modelList = incompatible[family].sort((a, b) => a.name.localeCompare(b.name));\n\n\t\t\tfor (const model of modelList) {\n\t\t\t\tconst color = activePod ? chalk.gray : chalk.green;\n\t\t\t\tconsole.log(` ${color(model.id)}`);\n\t\t\t\tconsole.log(chalk.gray(` Name: ${model.name}`));\n\t\t\t\tconsole.log(chalk.gray(` Min Hardware: ${model.minGpu}`));\n\t\t\t\tif (model.notes && !activePod) {\n\t\t\t\t\tconsole.log(chalk.gray(` Note: ${model.notes}`));\n\t\t\t\t}\n\t\t\t\tif (activePod) {\n\t\t\t\t\tconsole.log(\"\"); // Less verbose for incompatible models when filtered\n\t\t\t\t} else {\n\t\t\t\t\tconsole.log(\"\");\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tconsole.log(chalk.gray(\"\\nFor unknown models, defaults to single GPU deployment.\"));\n\tconsole.log(chalk.gray(\"Use --vllm to pass custom arguments to vLLM.\"));\n};\n"]}
1
+ {"version":3,"file":"models.d.ts","sourceRoot":"","sources":["../../src/commands/models.ts"],"names":[],"mappings":"AA0EA;;GAEG;AACH,eAAO,MAAM,UAAU;;;;;;mBA+UtB,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,SAAS;;mBA0BrB,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,aAAa;;mBA2BzB,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,UAAU;;mBAwEtB,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,QAAQ;;mBA+BpB,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,eAAe,qBA+J3B,CAAC","sourcesContent":["import chalk from \"chalk\";\nimport { spawn } from \"child_process\";\nimport { readFileSync } from \"fs\";\nimport { dirname, join } from \"path\";\nimport { fileURLToPath } from \"url\";\nimport { getActivePod, loadConfig, saveConfig } from \"../config.js\";\nimport { getModelConfig, getModelName, isKnownModel } from \"../model-configs.js\";\nimport { sshExec } from \"../ssh.js\";\nimport type { Pod } from \"../types.js\";\n\n/**\n * Get the pod to use (active or override)\n */\nconst getPod = (podOverride?: string): { name: string; pod: Pod } => {\n\tif (podOverride) {\n\t\tconst config = loadConfig();\n\t\tconst pod = config.pods[podOverride];\n\t\tif (!pod) {\n\t\t\tconsole.error(chalk.red(`Pod '${podOverride}' not found`));\n\t\t\tprocess.exit(1);\n\t\t}\n\t\treturn { name: podOverride, pod };\n\t}\n\n\tconst active = getActivePod();\n\tif (!active) {\n\t\tconsole.error(chalk.red(\"No active pod. Use 'pi pods active <name>' to set one.\"));\n\t\tprocess.exit(1);\n\t}\n\treturn active;\n};\n\n/**\n * Find next available port starting from 8001\n */\nconst getNextPort = (pod: Pod): number => {\n\tconst usedPorts = Object.values(pod.models).map((m) => m.port);\n\tlet port = 8001;\n\twhile (usedPorts.includes(port)) {\n\t\tport++;\n\t}\n\treturn port;\n};\n\n/**\n * Select GPUs for model deployment (round-robin)\n */\nconst selectGPUs = (pod: Pod, count: number = 1): number[] => {\n\tif (count === pod.gpus.length) {\n\t\t// Use all GPUs\n\t\treturn pod.gpus.map((g) => g.id);\n\t}\n\n\t// Count GPU usage across all models\n\tconst gpuUsage = new Map<number, number>();\n\tfor (const gpu of pod.gpus) {\n\t\tgpuUsage.set(gpu.id, 0);\n\t}\n\n\tfor (const model of Object.values(pod.models)) {\n\t\tfor (const gpuId of model.gpu) {\n\t\t\tgpuUsage.set(gpuId, (gpuUsage.get(gpuId) || 0) + 1);\n\t\t}\n\t}\n\n\t// Sort GPUs by usage (least used first)\n\tconst sortedGPUs = Array.from(gpuUsage.entries())\n\t\t.sort((a, b) => a[1] - b[1])\n\t\t.map((entry) => entry[0]);\n\n\t// Return the least used GPUs\n\treturn sortedGPUs.slice(0, count);\n};\n\n/**\n * Start a model\n */\nexport const startModel = async (\n\tmodelId: string,\n\tname: string,\n\toptions: {\n\t\tpod?: string;\n\t\tvllmArgs?: string[];\n\t\tmemory?: string;\n\t\tcontext?: string;\n\t\tgpus?: number;\n\t},\n) => {\n\tconst { name: podName, pod } = getPod(options.pod);\n\n\t// Validation\n\tif (!pod.modelsPath) {\n\t\tconsole.error(chalk.red(\"Pod does not have a models path configured\"));\n\t\tprocess.exit(1);\n\t}\n\tif (pod.models[name]) {\n\t\tconsole.error(chalk.red(`Model '${name}' already exists on pod '${podName}'`));\n\t\tprocess.exit(1);\n\t}\n\n\tconst port = getNextPort(pod);\n\n\t// Determine GPU allocation and vLLM args\n\tlet gpus: number[] = [];\n\tlet vllmArgs: string[] = [];\n\tlet modelConfig = null;\n\n\tif (options.vllmArgs?.length) {\n\t\t// Custom args override everything\n\t\tvllmArgs = options.vllmArgs;\n\t\tconsole.log(chalk.gray(\"Using custom vLLM args, GPU allocation managed by vLLM\"));\n\t} else if (isKnownModel(modelId)) {\n\t\t// Handle --gpus parameter for known models\n\t\tif (options.gpus) {\n\t\t\t// Validate GPU count\n\t\t\tif (options.gpus > pod.gpus.length) {\n\t\t\t\tconsole.error(chalk.red(`Error: Requested ${options.gpus} GPUs but pod only has ${pod.gpus.length}`));\n\t\t\t\tprocess.exit(1);\n\t\t\t}\n\n\t\t\t// Try to find config for requested GPU count\n\t\t\tmodelConfig = getModelConfig(modelId, pod.gpus, options.gpus);\n\t\t\tif (modelConfig) {\n\t\t\t\tgpus = selectGPUs(pod, options.gpus);\n\t\t\t\tvllmArgs = [...(modelConfig.args || [])];\n\t\t\t} else {\n\t\t\t\tconsole.error(\n\t\t\t\t\tchalk.red(`Model '${getModelName(modelId)}' does not have a configuration for ${options.gpus} GPU(s)`),\n\t\t\t\t);\n\t\t\t\tconsole.error(chalk.yellow(\"Available configurations:\"));\n\n\t\t\t\t// Show available configurations\n\t\t\t\tfor (let gpuCount = 1; gpuCount <= pod.gpus.length; gpuCount++) {\n\t\t\t\t\tconst config = getModelConfig(modelId, pod.gpus, gpuCount);\n\t\t\t\t\tif (config) {\n\t\t\t\t\t\tconsole.error(chalk.gray(` - ${gpuCount} GPU(s)`));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tprocess.exit(1);\n\t\t\t}\n\t\t} else {\n\t\t\t// Find best config for this hardware (original behavior)\n\t\t\tfor (let gpuCount = pod.gpus.length; gpuCount >= 1; gpuCount--) {\n\t\t\t\tmodelConfig = getModelConfig(modelId, pod.gpus, gpuCount);\n\t\t\t\tif (modelConfig) {\n\t\t\t\t\tgpus = selectGPUs(pod, gpuCount);\n\t\t\t\t\tvllmArgs = [...(modelConfig.args || [])];\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!modelConfig) {\n\t\t\t\tconsole.error(chalk.red(`Model '${getModelName(modelId)}' not compatible with this pod's GPUs`));\n\t\t\t\tprocess.exit(1);\n\t\t\t}\n\t\t}\n\t} else {\n\t\t// Unknown model\n\t\tif (options.gpus) {\n\t\t\tconsole.error(chalk.red(\"Error: --gpus can only be used with predefined models\"));\n\t\t\tconsole.error(chalk.yellow(\"For custom models, use --vllm with tensor-parallel-size or similar arguments\"));\n\t\t\tprocess.exit(1);\n\t\t}\n\t\t// Single GPU default\n\t\tgpus = selectGPUs(pod, 1);\n\t\tconsole.log(chalk.gray(\"Unknown model, defaulting to single GPU\"));\n\t}\n\n\t// Apply memory/context overrides\n\tif (!options.vllmArgs?.length) {\n\t\tif (options.memory) {\n\t\t\tconst fraction = parseFloat(options.memory.replace(\"%\", \"\")) / 100;\n\t\t\tvllmArgs = vllmArgs.filter((arg) => !arg.includes(\"gpu-memory-utilization\"));\n\t\t\tvllmArgs.push(\"--gpu-memory-utilization\", String(fraction));\n\t\t}\n\t\tif (options.context) {\n\t\t\tconst contextSizes: Record<string, number> = {\n\t\t\t\t\"4k\": 4096,\n\t\t\t\t\"8k\": 8192,\n\t\t\t\t\"16k\": 16384,\n\t\t\t\t\"32k\": 32768,\n\t\t\t\t\"64k\": 65536,\n\t\t\t\t\"128k\": 131072,\n\t\t\t};\n\t\t\tconst maxTokens = contextSizes[options.context.toLowerCase()] || parseInt(options.context, 10);\n\t\t\tvllmArgs = vllmArgs.filter((arg) => !arg.includes(\"max-model-len\"));\n\t\t\tvllmArgs.push(\"--max-model-len\", String(maxTokens));\n\t\t}\n\t}\n\n\t// Show what we're doing\n\tconsole.log(chalk.green(`Starting model '${name}' on pod '${podName}'...`));\n\tconsole.log(`Model: ${modelId}`);\n\tconsole.log(`Port: ${port}`);\n\tconsole.log(`GPU(s): ${gpus.length ? gpus.join(\", \") : \"Managed by vLLM\"}`);\n\tif (modelConfig?.notes) console.log(chalk.yellow(`Note: ${modelConfig.notes}`));\n\tconsole.log(\"\");\n\n\t// Read and customize model_run.sh script with our values\n\tconst scriptPath = join(dirname(fileURLToPath(import.meta.url)), \"../../scripts/model_run.sh\");\n\tlet scriptContent = readFileSync(scriptPath, \"utf-8\");\n\n\t// Replace placeholders - no escaping needed, heredoc with 'EOF' is literal\n\tscriptContent = scriptContent\n\t\t.replace(\"{{MODEL_ID}}\", modelId)\n\t\t.replace(\"{{NAME}}\", name)\n\t\t.replace(\"{{PORT}}\", String(port))\n\t\t.replace(\"{{VLLM_ARGS}}\", vllmArgs.join(\" \"));\n\n\t// Upload customized script\n\tawait sshExec(\n\t\tpod.ssh,\n\t\t`cat > /tmp/model_run_${name}.sh << 'EOF'\n${scriptContent}\nEOF\nchmod +x /tmp/model_run_${name}.sh`,\n\t);\n\n\t// Prepare environment\n\tconst env = [\n\t\t`HF_TOKEN='${process.env.HF_TOKEN}'`,\n\t\t`PI_API_KEY='${process.env.PI_API_KEY}'`,\n\t\t`HF_HUB_ENABLE_HF_TRANSFER=1`,\n\t\t`VLLM_NO_USAGE_STATS=1`,\n\t\t`PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True`,\n\t\t`FORCE_COLOR=1`,\n\t\t`TERM=xterm-256color`,\n\t\t...(gpus.length === 1 ? [`CUDA_VISIBLE_DEVICES=${gpus[0]}`] : []),\n\t\t...Object.entries(modelConfig?.env || {}).map(([k, v]) => `${k}='${v}'`),\n\t]\n\t\t.map((e) => `export ${e}`)\n\t\t.join(\"\\n\");\n\n\t// Start the model runner with script command for pseudo-TTY (preserves colors)\n\t// Note: We use script to preserve colors and create a log file\n\t// setsid creates a new session so it survives SSH disconnection\n\tconst startCmd = `\n\t\t${env}\n\t\tmkdir -p ~/.vllm_logs\n\t\t# Create a wrapper that monitors the script command\n\t\tcat > /tmp/model_wrapper_${name}.sh << 'WRAPPER'\n#!/bin/bash\nscript -q -f -c \"/tmp/model_run_${name}.sh\" ~/.vllm_logs/${name}.log\nexit_code=$?\necho \"Script exited with code $exit_code\" >> ~/.vllm_logs/${name}.log\nexit $exit_code\nWRAPPER\n\t\tchmod +x /tmp/model_wrapper_${name}.sh\n\t\tsetsid /tmp/model_wrapper_${name}.sh </dev/null >/dev/null 2>&1 &\n\t\techo $!\n\t\texit 0\n\t`;\n\n\tconst pidResult = await sshExec(pod.ssh, startCmd);\n\tconst pid = parseInt(pidResult.stdout.trim(), 10);\n\tif (!pid) {\n\t\tconsole.error(chalk.red(\"Failed to start model runner\"));\n\t\tprocess.exit(1);\n\t}\n\n\t// Save to config\n\tconst config = loadConfig();\n\tconfig.pods[podName].models[name] = { model: modelId, port, gpu: gpus, pid };\n\tsaveConfig(config);\n\n\tconsole.log(`Model runner started with PID: ${pid}`);\n\tconsole.log(\"Streaming logs... (waiting for startup)\\n\");\n\n\t// Small delay to ensure log file is created\n\tawait new Promise((resolve) => setTimeout(resolve, 500));\n\n\t// Stream logs with color support, watching for startup complete\n\tconst sshParts = pod.ssh.split(\" \");\n\tconst sshCommand = sshParts[0]; // \"ssh\"\n\tconst sshArgs = sshParts.slice(1); // [\"root@86.38.238.55\"]\n\tconst host = sshArgs[0].split(\"@\")[1] || \"localhost\";\n\tconst tailCmd = `tail -f ~/.vllm_logs/${name}.log`;\n\n\t// Build the full args array for spawn\n\tconst fullArgs = [...sshArgs, tailCmd];\n\n\tconst logProcess = spawn(sshCommand, fullArgs, {\n\t\tstdio: [\"inherit\", \"pipe\", \"pipe\"], // capture stdout and stderr\n\t\tenv: { ...process.env, FORCE_COLOR: \"1\" },\n\t});\n\n\tlet interrupted = false;\n\tlet startupComplete = false;\n\tlet startupFailed = false;\n\tlet failureReason = \"\";\n\n\t// Handle Ctrl+C\n\tconst sigintHandler = () => {\n\t\tinterrupted = true;\n\t\tlogProcess.kill();\n\t};\n\tprocess.on(\"SIGINT\", sigintHandler);\n\n\t// Process log output line by line\n\tconst processOutput = (data: Buffer) => {\n\t\tconst lines = data.toString().split(\"\\n\");\n\t\tfor (const line of lines) {\n\t\t\tif (line) {\n\t\t\t\tconsole.log(line); // Echo the line to console\n\n\t\t\t\t// Check for startup complete message\n\t\t\t\tif (line.includes(\"Application startup complete\")) {\n\t\t\t\t\tstartupComplete = true;\n\t\t\t\t\tlogProcess.kill(); // Stop tailing logs\n\t\t\t\t}\n\n\t\t\t\t// Check for failure indicators\n\t\t\t\tif (line.includes(\"Model runner exiting with code\") && !line.includes(\"code 0\")) {\n\t\t\t\t\tstartupFailed = true;\n\t\t\t\t\tfailureReason = \"Model runner failed to start\";\n\t\t\t\t\tlogProcess.kill();\n\t\t\t\t}\n\t\t\t\tif (line.includes(\"Script exited with code\") && !line.includes(\"code 0\")) {\n\t\t\t\t\tstartupFailed = true;\n\t\t\t\t\tfailureReason = \"Script failed to execute\";\n\t\t\t\t\tlogProcess.kill();\n\t\t\t\t}\n\t\t\t\tif (line.includes(\"torch.OutOfMemoryError\") || line.includes(\"CUDA out of memory\")) {\n\t\t\t\t\tstartupFailed = true;\n\t\t\t\t\tfailureReason = \"Out of GPU memory (OOM)\";\n\t\t\t\t\t// Don't kill immediately - let it show more error context\n\t\t\t\t}\n\t\t\t\tif (line.includes(\"RuntimeError: Engine core initialization failed\")) {\n\t\t\t\t\tstartupFailed = true;\n\t\t\t\t\tfailureReason = \"vLLM engine initialization failed\";\n\t\t\t\t\tlogProcess.kill();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t};\n\n\tlogProcess.stdout?.on(\"data\", processOutput);\n\tlogProcess.stderr?.on(\"data\", processOutput);\n\n\tawait new Promise<void>((resolve) => logProcess.on(\"exit\", resolve));\n\tprocess.removeListener(\"SIGINT\", sigintHandler);\n\n\tif (startupFailed) {\n\t\t// Model failed to start - clean up and report error\n\t\tconsole.log(`\\n${chalk.red(`✗ Model failed to start: ${failureReason}`)}`);\n\n\t\t// Remove the failed model from config\n\t\tconst config = loadConfig();\n\t\tdelete config.pods[podName].models[name];\n\t\tsaveConfig(config);\n\n\t\tconsole.log(chalk.yellow(\"\\nModel has been removed from configuration.\"));\n\n\t\t// Provide helpful suggestions based on failure reason\n\t\tif (failureReason.includes(\"OOM\") || failureReason.includes(\"memory\")) {\n\t\t\tconsole.log(`\\n${chalk.bold(\"Suggestions:\")}`);\n\t\t\tconsole.log(\" • Try reducing GPU memory utilization: --memory 50%\");\n\t\t\tconsole.log(\" • Use a smaller context window: --context 4k\");\n\t\t\tconsole.log(\" • Use a quantized version of the model (e.g., FP8)\");\n\t\t\tconsole.log(\" • Use more GPUs with tensor parallelism\");\n\t\t\tconsole.log(\" • Try a smaller model variant\");\n\t\t}\n\n\t\tconsole.log(`\\n${chalk.cyan(`Check full logs: pi ssh \"tail -100 ~/.vllm_logs/${name}.log\"`)}`);\n\t\tprocess.exit(1);\n\t} else if (startupComplete) {\n\t\t// Model started successfully - output connection details\n\t\tconsole.log(`\\n${chalk.green(\"✓ Model started successfully!\")}`);\n\t\tconsole.log(`\\n${chalk.bold(\"Connection Details:\")}`);\n\t\tconsole.log(chalk.cyan(\"─\".repeat(50)));\n\t\tconsole.log(chalk.white(\"Base URL: \") + chalk.yellow(`http://${host}:${port}/v1`));\n\t\tconsole.log(chalk.white(\"Model: \") + chalk.yellow(modelId));\n\t\tconsole.log(chalk.white(\"API Key: \") + chalk.yellow(process.env.PI_API_KEY || \"(not set)\"));\n\t\tconsole.log(chalk.cyan(\"─\".repeat(50)));\n\n\t\tconsole.log(`\\n${chalk.bold(\"Export for shell:\")}`);\n\t\tconsole.log(chalk.gray(`export OPENAI_BASE_URL=\"http://${host}:${port}/v1\"`));\n\t\tconsole.log(chalk.gray(`export OPENAI_API_KEY=\"${process.env.PI_API_KEY || \"your-api-key\"}\"`));\n\t\tconsole.log(chalk.gray(`export OPENAI_MODEL=\"${modelId}\"`));\n\n\t\tconsole.log(`\\n${chalk.bold(\"Example usage:\")}`);\n\t\tconsole.log(\n\t\t\tchalk.gray(`\n # Python\n from openai import OpenAI\n client = OpenAI() # Uses env vars\n response = client.chat.completions.create(\n model=\"${modelId}\",\n messages=[{\"role\": \"user\", \"content\": \"Hello!\"}]\n )\n\n # CLI\n curl $OPENAI_BASE_URL/chat/completions \\\\\n -H \"Authorization: Bearer $OPENAI_API_KEY\" \\\\\n -H \"Content-Type: application/json\" \\\\\n -d '{\"model\":\"${modelId}\",\"messages\":[{\"role\":\"user\",\"content\":\"Hi\"}]}'`),\n\t\t);\n\t\tconsole.log(\"\");\n\t\tconsole.log(chalk.cyan(`Chat with model: pi agent ${name} \"Your message\"`));\n\t\tconsole.log(chalk.cyan(`Interactive mode: pi agent ${name} -i`));\n\t\tconsole.log(chalk.cyan(`Monitor logs: pi logs ${name}`));\n\t\tconsole.log(chalk.cyan(`Stop model: pi stop ${name}`));\n\t} else if (interrupted) {\n\t\tconsole.log(chalk.yellow(\"\\n\\nStopped monitoring. Model deployment continues in background.\"));\n\t\tconsole.log(chalk.cyan(`Chat with model: pi agent ${name} \"Your message\"`));\n\t\tconsole.log(chalk.cyan(`Check status: pi logs ${name}`));\n\t\tconsole.log(chalk.cyan(`Stop model: pi stop ${name}`));\n\t} else {\n\t\tconsole.log(chalk.yellow(\"\\n\\nLog stream ended. Model may still be running.\"));\n\t\tconsole.log(chalk.cyan(`Chat with model: pi agent ${name} \"Your message\"`));\n\t\tconsole.log(chalk.cyan(`Check status: pi logs ${name}`));\n\t\tconsole.log(chalk.cyan(`Stop model: pi stop ${name}`));\n\t}\n};\n\n/**\n * Stop a model\n */\nexport const stopModel = async (name: string, options: { pod?: string }) => {\n\tconst { name: podName, pod } = getPod(options.pod);\n\n\tconst model = pod.models[name];\n\tif (!model) {\n\t\tconsole.error(chalk.red(`Model '${name}' not found on pod '${podName}'`));\n\t\tprocess.exit(1);\n\t}\n\n\tconsole.log(chalk.yellow(`Stopping model '${name}' on pod '${podName}'...`));\n\n\t// Kill the script process and all its children\n\t// Using pkill to kill the process and all children\n\tconst killCmd = `\n\t\t# Kill the script process and all its children\n\t\tpkill -TERM -P ${model.pid} 2>/dev/null || true\n\t\tkill ${model.pid} 2>/dev/null || true\n\t`;\n\tawait sshExec(pod.ssh, killCmd);\n\n\t// Remove from config\n\tconst config = loadConfig();\n\tdelete config.pods[podName].models[name];\n\tsaveConfig(config);\n\n\tconsole.log(chalk.green(`✓ Model '${name}' stopped`));\n};\n\n/**\n * Stop all models on a pod\n */\nexport const stopAllModels = async (options: { pod?: string }) => {\n\tconst { name: podName, pod } = getPod(options.pod);\n\n\tconst modelNames = Object.keys(pod.models);\n\tif (modelNames.length === 0) {\n\t\tconsole.log(`No models running on pod '${podName}'`);\n\t\treturn;\n\t}\n\n\tconsole.log(chalk.yellow(`Stopping ${modelNames.length} model(s) on pod '${podName}'...`));\n\n\t// Kill all script processes and their children\n\tconst pids = Object.values(pod.models).map((m) => m.pid);\n\tconst killCmd = `\n\t\tfor PID in ${pids.join(\" \")}; do\n\t\t\tpkill -TERM -P $PID 2>/dev/null || true\n\t\t\tkill $PID 2>/dev/null || true\n\t\tdone\n\t`;\n\tawait sshExec(pod.ssh, killCmd);\n\n\t// Clear all models from config\n\tconst config = loadConfig();\n\tconfig.pods[podName].models = {};\n\tsaveConfig(config);\n\n\tconsole.log(chalk.green(`✓ Stopped all models: ${modelNames.join(\", \")}`));\n};\n\n/**\n * List all models\n */\nexport const listModels = async (options: { pod?: string }) => {\n\tconst { name: podName, pod } = getPod(options.pod);\n\n\tconst modelNames = Object.keys(pod.models);\n\tif (modelNames.length === 0) {\n\t\tconsole.log(`No models running on pod '${podName}'`);\n\t\treturn;\n\t}\n\n\t// Get pod SSH host for URL display\n\tconst sshParts = pod.ssh.split(\" \");\n\tconst host = sshParts.find((p) => p.includes(\"@\"))?.split(\"@\")[1] || \"unknown\";\n\n\tconsole.log(`Models on pod '${chalk.bold(podName)}':`);\n\tfor (const name of modelNames) {\n\t\tconst model = pod.models[name];\n\t\tconst gpuStr =\n\t\t\tmodel.gpu.length > 1\n\t\t\t\t? `GPUs ${model.gpu.join(\",\")}`\n\t\t\t\t: model.gpu.length === 1\n\t\t\t\t\t? `GPU ${model.gpu[0]}`\n\t\t\t\t\t: \"GPU unknown\";\n\t\tconsole.log(` ${chalk.green(name)} - Port ${model.port} - ${gpuStr} - PID ${model.pid}`);\n\t\tconsole.log(` Model: ${chalk.gray(model.model)}`);\n\t\tconsole.log(` URL: ${chalk.cyan(`http://${host}:${model.port}/v1`)}`);\n\t}\n\n\t// Optionally verify processes are still running\n\tconsole.log(\"\");\n\tconsole.log(\"Verifying processes...\");\n\tlet anyDead = false;\n\tfor (const name of modelNames) {\n\t\tconst model = pod.models[name];\n\t\t// Check both the wrapper process and if vLLM is responding\n\t\tconst checkCmd = `\n\t\t\t# Check if wrapper process exists\n\t\t\tif ps -p ${model.pid} > /dev/null 2>&1; then\n\t\t\t\t# Process exists, now check if vLLM is responding\n\t\t\t\tif curl -s -f http://localhost:${model.port}/health > /dev/null 2>&1; then\n\t\t\t\t\techo \"running\"\n\t\t\t\telse\n\t\t\t\t\t# Check if it's still starting up\n\t\t\t\t\tif tail -n 20 ~/.vllm_logs/${name}.log 2>/dev/null | grep -q \"ERROR\\\\|Failed\\\\|Cuda error\\\\|died\"; then\n\t\t\t\t\t\techo \"crashed\"\n\t\t\t\t\telse\n\t\t\t\t\t\techo \"starting\"\n\t\t\t\t\tfi\n\t\t\t\tfi\n\t\t\telse\n\t\t\t\techo \"dead\"\n\t\t\tfi\n\t\t`;\n\t\tconst result = await sshExec(pod.ssh, checkCmd);\n\t\tconst status = result.stdout.trim();\n\t\tif (status === \"dead\") {\n\t\t\tconsole.log(chalk.red(` ${name}: Process ${model.pid} is not running`));\n\t\t\tanyDead = true;\n\t\t} else if (status === \"crashed\") {\n\t\t\tconsole.log(chalk.red(` ${name}: vLLM crashed (check logs with 'pi logs ${name}')`));\n\t\t\tanyDead = true;\n\t\t} else if (status === \"starting\") {\n\t\t\tconsole.log(chalk.yellow(` ${name}: Still starting up...`));\n\t\t}\n\t}\n\n\tif (anyDead) {\n\t\tconsole.log(\"\");\n\t\tconsole.log(chalk.yellow(\"Some models are not running. Clean up with:\"));\n\t\tconsole.log(chalk.cyan(\" pi stop <name>\"));\n\t} else {\n\t\tconsole.log(chalk.green(\"✓ All processes verified\"));\n\t}\n};\n\n/**\n * View model logs\n */\nexport const viewLogs = async (name: string, options: { pod?: string }) => {\n\tconst { name: podName, pod } = getPod(options.pod);\n\n\tconst model = pod.models[name];\n\tif (!model) {\n\t\tconsole.error(chalk.red(`Model '${name}' not found on pod '${podName}'`));\n\t\tprocess.exit(1);\n\t}\n\n\tconsole.log(chalk.green(`Streaming logs for '${name}' on pod '${podName}'...`));\n\tconsole.log(chalk.gray(\"Press Ctrl+C to stop\"));\n\tconsole.log(\"\");\n\n\t// Stream logs with color preservation\n\tconst sshParts = pod.ssh.split(\" \");\n\tconst sshCommand = sshParts[0]; // \"ssh\"\n\tconst sshArgs = sshParts.slice(1); // [\"root@86.38.238.55\"]\n\tconst tailCmd = `tail -f ~/.vllm_logs/${name}.log`;\n\n\tconst logProcess = spawn(sshCommand, [...sshArgs, tailCmd], {\n\t\tstdio: \"inherit\",\n\t\tenv: {\n\t\t\t...process.env,\n\t\t\tFORCE_COLOR: \"1\",\n\t\t},\n\t});\n\n\t// Wait for process to exit\n\tawait new Promise<void>((resolve) => {\n\t\tlogProcess.on(\"exit\", () => resolve());\n\t});\n};\n\n/**\n * Show known models and their hardware requirements\n */\nexport const showKnownModels = async () => {\n\tconst __filename = fileURLToPath(import.meta.url);\n\tconst __dirname = dirname(__filename);\n\tconst modelsJsonPath = join(__dirname, \"..\", \"models.json\");\n\tconst modelsJson = JSON.parse(readFileSync(modelsJsonPath, \"utf-8\"));\n\tconst models = modelsJson.models;\n\n\t// Get active pod info if available\n\tconst activePod = getActivePod();\n\tlet podGpuCount = 0;\n\tlet podGpuType = \"\";\n\n\tif (activePod) {\n\t\tpodGpuCount = activePod.pod.gpus.length;\n\t\t// Extract GPU type from name (e.g., \"NVIDIA H200\" -> \"H200\")\n\t\tpodGpuType = activePod.pod.gpus[0]?.name?.replace(\"NVIDIA\", \"\")?.trim()?.split(\" \")[0] || \"\";\n\n\t\tconsole.log(chalk.bold(`Known Models for ${activePod.name} (${podGpuCount}x ${podGpuType || \"GPU\"}):\\n`));\n\t} else {\n\t\tconsole.log(chalk.bold(\"Known Models:\\n\"));\n\t\tconsole.log(chalk.yellow(\"No active pod. Use 'pi pods active <name>' to filter compatible models.\\n\"));\n\t}\n\n\tconsole.log(\"Usage: pi start <model> --name <name> [options]\\n\");\n\n\t// Group models by compatibility and family\n\tconst compatible: Record<string, Array<{ id: string; name: string; config: string; notes?: string }>> = {};\n\tconst incompatible: Record<string, Array<{ id: string; name: string; minGpu: string; notes?: string }>> = {};\n\n\tfor (const [modelId, info] of Object.entries(models)) {\n\t\tconst modelInfo = info as any;\n\t\tconst family = modelInfo.name.split(\"-\")[0] || \"Other\";\n\n\t\tlet isCompatible = false;\n\t\tlet compatibleConfig = \"\";\n\t\tlet minGpu = \"Unknown\";\n\t\tlet minNotes: string | undefined;\n\n\t\tif (modelInfo.configs && modelInfo.configs.length > 0) {\n\t\t\t// Sort configs by GPU count to find minimum\n\t\t\tconst sortedConfigs = [...modelInfo.configs].sort((a: any, b: any) => (a.gpuCount || 1) - (b.gpuCount || 1));\n\n\t\t\t// Find minimum requirements\n\t\t\tconst minConfig = sortedConfigs[0];\n\t\t\tconst minGpuCount = minConfig.gpuCount || 1;\n\t\t\tconst gpuTypes = minConfig.gpuTypes?.join(\"/\") || \"H100/H200\";\n\n\t\t\tif (minGpuCount === 1) {\n\t\t\t\tminGpu = `1x ${gpuTypes}`;\n\t\t\t} else {\n\t\t\t\tminGpu = `${minGpuCount}x ${gpuTypes}`;\n\t\t\t}\n\n\t\t\tminNotes = minConfig.notes || modelInfo.notes;\n\n\t\t\t// Check compatibility with active pod\n\t\t\tif (activePod && podGpuCount > 0) {\n\t\t\t\t// Find best matching config for this pod\n\t\t\t\tfor (const config of sortedConfigs) {\n\t\t\t\t\tconst configGpuCount = config.gpuCount || 1;\n\t\t\t\t\tconst configGpuTypes = config.gpuTypes || [];\n\n\t\t\t\t\t// Check if we have enough GPUs\n\t\t\t\t\tif (configGpuCount <= podGpuCount) {\n\t\t\t\t\t\t// Check if GPU type matches (if specified)\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\tconfigGpuTypes.length === 0 ||\n\t\t\t\t\t\t\tconfigGpuTypes.some((type: string) => podGpuType.includes(type) || type.includes(podGpuType))\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\tisCompatible = true;\n\t\t\t\t\t\t\tif (configGpuCount === 1) {\n\t\t\t\t\t\t\t\tcompatibleConfig = `1x ${podGpuType}`;\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tcompatibleConfig = `${configGpuCount}x ${podGpuType}`;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tminNotes = config.notes || modelInfo.notes;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tconst modelEntry = {\n\t\t\tid: modelId,\n\t\t\tname: modelInfo.name,\n\t\t\tnotes: minNotes,\n\t\t};\n\n\t\tif (activePod && isCompatible) {\n\t\t\tif (!compatible[family]) {\n\t\t\t\tcompatible[family] = [];\n\t\t\t}\n\t\t\tcompatible[family].push({ ...modelEntry, config: compatibleConfig });\n\t\t} else {\n\t\t\tif (!incompatible[family]) {\n\t\t\t\tincompatible[family] = [];\n\t\t\t}\n\t\t\tincompatible[family].push({ ...modelEntry, minGpu });\n\t\t}\n\t}\n\n\t// Display compatible models first\n\tif (activePod && Object.keys(compatible).length > 0) {\n\t\tconsole.log(chalk.green.bold(\"✓ Compatible Models:\\n\"));\n\n\t\tconst sortedFamilies = Object.keys(compatible).sort();\n\t\tfor (const family of sortedFamilies) {\n\t\t\tconsole.log(chalk.cyan(`${family} Models:`));\n\n\t\t\tconst modelList = compatible[family].sort((a, b) => a.name.localeCompare(b.name));\n\n\t\t\tfor (const model of modelList) {\n\t\t\t\tconsole.log(` ${chalk.green(model.id)}`);\n\t\t\t\tconsole.log(` Name: ${model.name}`);\n\t\t\t\tconsole.log(` Config: ${model.config}`);\n\t\t\t\tif (model.notes) {\n\t\t\t\t\tconsole.log(chalk.gray(` Note: ${model.notes}`));\n\t\t\t\t}\n\t\t\t\tconsole.log(\"\");\n\t\t\t}\n\t\t}\n\t}\n\n\t// Display incompatible models\n\tif (Object.keys(incompatible).length > 0) {\n\t\tif (activePod && Object.keys(compatible).length > 0) {\n\t\t\tconsole.log(chalk.red.bold(\"✗ Incompatible Models (need more/different GPUs):\\n\"));\n\t\t}\n\n\t\tconst sortedFamilies = Object.keys(incompatible).sort();\n\t\tfor (const family of sortedFamilies) {\n\t\t\tif (!activePod) {\n\t\t\t\tconsole.log(chalk.cyan(`${family} Models:`));\n\t\t\t} else {\n\t\t\t\tconsole.log(chalk.gray(`${family} Models:`));\n\t\t\t}\n\n\t\t\tconst modelList = incompatible[family].sort((a, b) => a.name.localeCompare(b.name));\n\n\t\t\tfor (const model of modelList) {\n\t\t\t\tconst color = activePod ? chalk.gray : chalk.green;\n\t\t\t\tconsole.log(` ${color(model.id)}`);\n\t\t\t\tconsole.log(chalk.gray(` Name: ${model.name}`));\n\t\t\t\tconsole.log(chalk.gray(` Min Hardware: ${model.minGpu}`));\n\t\t\t\tif (model.notes && !activePod) {\n\t\t\t\t\tconsole.log(chalk.gray(` Note: ${model.notes}`));\n\t\t\t\t}\n\t\t\t\tif (activePod) {\n\t\t\t\t\tconsole.log(\"\"); // Less verbose for incompatible models when filtered\n\t\t\t\t} else {\n\t\t\t\t\tconsole.log(\"\");\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tconsole.log(chalk.gray(\"\\nFor unknown models, defaults to single GPU deployment.\"));\n\tconsole.log(chalk.gray(\"Use --vllm to pass custom arguments to vLLM.\"));\n};\n"]}
@@ -179,7 +179,7 @@ export const startModel = async (modelId, name, options) => {
179
179
  .replace("{{PORT}}", String(port))
180
180
  .replace("{{VLLM_ARGS}}", vllmArgs.join(" "));
181
181
  // Upload customized script
182
- const result = await sshExec(pod.ssh, `cat > /tmp/model_run_${name}.sh << 'EOF'
182
+ await sshExec(pod.ssh, `cat > /tmp/model_run_${name}.sh << 'EOF'
183
183
  ${scriptContent}
184
184
  EOF
185
185
  chmod +x /tmp/model_run_${name}.sh`);
@@ -293,7 +293,7 @@ WRAPPER
293
293
  process.removeListener("SIGINT", sigintHandler);
294
294
  if (startupFailed) {
295
295
  // Model failed to start - clean up and report error
296
- console.log("\n" + chalk.red(`✗ Model failed to start: ${failureReason}`));
296
+ console.log(`\n${chalk.red(`✗ Model failed to start: ${failureReason}`)}`);
297
297
  // Remove the failed model from config
298
298
  const config = loadConfig();
299
299
  delete config.pods[podName].models[name];
@@ -301,30 +301,30 @@ WRAPPER
301
301
  console.log(chalk.yellow("\nModel has been removed from configuration."));
302
302
  // Provide helpful suggestions based on failure reason
303
303
  if (failureReason.includes("OOM") || failureReason.includes("memory")) {
304
- console.log("\n" + chalk.bold("Suggestions:"));
304
+ console.log(`\n${chalk.bold("Suggestions:")}`);
305
305
  console.log(" • Try reducing GPU memory utilization: --memory 50%");
306
306
  console.log(" • Use a smaller context window: --context 4k");
307
307
  console.log(" • Use a quantized version of the model (e.g., FP8)");
308
308
  console.log(" • Use more GPUs with tensor parallelism");
309
309
  console.log(" • Try a smaller model variant");
310
310
  }
311
- console.log("\n" + chalk.cyan('Check full logs: pi ssh "tail -100 ~/.vllm_logs/' + name + '.log"'));
311
+ console.log(`\n${chalk.cyan(`Check full logs: pi ssh "tail -100 ~/.vllm_logs/${name}.log"`)}`);
312
312
  process.exit(1);
313
313
  }
314
314
  else if (startupComplete) {
315
315
  // Model started successfully - output connection details
316
- console.log("\n" + chalk.green("✓ Model started successfully!"));
317
- console.log("\n" + chalk.bold("Connection Details:"));
316
+ console.log(`\n${chalk.green("✓ Model started successfully!")}`);
317
+ console.log(`\n${chalk.bold("Connection Details:")}`);
318
318
  console.log(chalk.cyan("─".repeat(50)));
319
319
  console.log(chalk.white("Base URL: ") + chalk.yellow(`http://${host}:${port}/v1`));
320
320
  console.log(chalk.white("Model: ") + chalk.yellow(modelId));
321
321
  console.log(chalk.white("API Key: ") + chalk.yellow(process.env.PI_API_KEY || "(not set)"));
322
322
  console.log(chalk.cyan("─".repeat(50)));
323
- console.log("\n" + chalk.bold("Export for shell:"));
323
+ console.log(`\n${chalk.bold("Export for shell:")}`);
324
324
  console.log(chalk.gray(`export OPENAI_BASE_URL="http://${host}:${port}/v1"`));
325
325
  console.log(chalk.gray(`export OPENAI_API_KEY="${process.env.PI_API_KEY || "your-api-key"}"`));
326
326
  console.log(chalk.gray(`export OPENAI_MODEL="${modelId}"`));
327
- console.log("\n" + chalk.bold("Example usage:"));
327
+ console.log(`\n${chalk.bold("Example usage:")}`);
328
328
  console.log(chalk.gray(`
329
329
  # Python
330
330
  from openai import OpenAI
@@ -1 +1 @@
1
- {"version":3,"file":"models.js","sourceRoot":"","sources":["../../src/commands/models.ts"],"names":[],"mappings":"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","sourcesContent":["import chalk from \"chalk\";\nimport { spawn } from \"child_process\";\nimport { readFileSync } from \"fs\";\nimport { dirname, join } from \"path\";\nimport { fileURLToPath } from \"url\";\nimport { getActivePod, loadConfig, saveConfig } from \"../config.js\";\nimport { getModelConfig, getModelName, isKnownModel } from \"../model-configs.js\";\nimport { sshExec } from \"../ssh.js\";\nimport type { Pod } from \"../types.js\";\n\n/**\n * Get the pod to use (active or override)\n */\nconst getPod = (podOverride?: string): { name: string; pod: Pod } => {\n\tif (podOverride) {\n\t\tconst config = loadConfig();\n\t\tconst pod = config.pods[podOverride];\n\t\tif (!pod) {\n\t\t\tconsole.error(chalk.red(`Pod '${podOverride}' not found`));\n\t\t\tprocess.exit(1);\n\t\t}\n\t\treturn { name: podOverride, pod };\n\t}\n\n\tconst active = getActivePod();\n\tif (!active) {\n\t\tconsole.error(chalk.red(\"No active pod. Use 'pi pods active <name>' to set one.\"));\n\t\tprocess.exit(1);\n\t}\n\treturn active;\n};\n\n/**\n * Find next available port starting from 8001\n */\nconst getNextPort = (pod: Pod): number => {\n\tconst usedPorts = Object.values(pod.models).map((m) => m.port);\n\tlet port = 8001;\n\twhile (usedPorts.includes(port)) {\n\t\tport++;\n\t}\n\treturn port;\n};\n\n/**\n * Select GPUs for model deployment (round-robin)\n */\nconst selectGPUs = (pod: Pod, count: number = 1): number[] => {\n\tif (count === pod.gpus.length) {\n\t\t// Use all GPUs\n\t\treturn pod.gpus.map((g) => g.id);\n\t}\n\n\t// Count GPU usage across all models\n\tconst gpuUsage = new Map<number, number>();\n\tfor (const gpu of pod.gpus) {\n\t\tgpuUsage.set(gpu.id, 0);\n\t}\n\n\tfor (const model of Object.values(pod.models)) {\n\t\tfor (const gpuId of model.gpu) {\n\t\t\tgpuUsage.set(gpuId, (gpuUsage.get(gpuId) || 0) + 1);\n\t\t}\n\t}\n\n\t// Sort GPUs by usage (least used first)\n\tconst sortedGPUs = Array.from(gpuUsage.entries())\n\t\t.sort((a, b) => a[1] - b[1])\n\t\t.map((entry) => entry[0]);\n\n\t// Return the least used GPUs\n\treturn sortedGPUs.slice(0, count);\n};\n\n/**\n * Start a model\n */\nexport const startModel = async (\n\tmodelId: string,\n\tname: string,\n\toptions: {\n\t\tpod?: string;\n\t\tvllmArgs?: string[];\n\t\tmemory?: string;\n\t\tcontext?: string;\n\t\tgpus?: number;\n\t},\n) => {\n\tconst { name: podName, pod } = getPod(options.pod);\n\n\t// Validation\n\tif (!pod.modelsPath) {\n\t\tconsole.error(chalk.red(\"Pod does not have a models path configured\"));\n\t\tprocess.exit(1);\n\t}\n\tif (pod.models[name]) {\n\t\tconsole.error(chalk.red(`Model '${name}' already exists on pod '${podName}'`));\n\t\tprocess.exit(1);\n\t}\n\n\tconst port = getNextPort(pod);\n\n\t// Determine GPU allocation and vLLM args\n\tlet gpus: number[] = [];\n\tlet vllmArgs: string[] = [];\n\tlet modelConfig = null;\n\n\tif (options.vllmArgs?.length) {\n\t\t// Custom args override everything\n\t\tvllmArgs = options.vllmArgs;\n\t\tconsole.log(chalk.gray(\"Using custom vLLM args, GPU allocation managed by vLLM\"));\n\t} else if (isKnownModel(modelId)) {\n\t\t// Handle --gpus parameter for known models\n\t\tif (options.gpus) {\n\t\t\t// Validate GPU count\n\t\t\tif (options.gpus > pod.gpus.length) {\n\t\t\t\tconsole.error(chalk.red(`Error: Requested ${options.gpus} GPUs but pod only has ${pod.gpus.length}`));\n\t\t\t\tprocess.exit(1);\n\t\t\t}\n\n\t\t\t// Try to find config for requested GPU count\n\t\t\tmodelConfig = getModelConfig(modelId, pod.gpus, options.gpus);\n\t\t\tif (modelConfig) {\n\t\t\t\tgpus = selectGPUs(pod, options.gpus);\n\t\t\t\tvllmArgs = [...(modelConfig.args || [])];\n\t\t\t} else {\n\t\t\t\tconsole.error(\n\t\t\t\t\tchalk.red(`Model '${getModelName(modelId)}' does not have a configuration for ${options.gpus} GPU(s)`),\n\t\t\t\t);\n\t\t\t\tconsole.error(chalk.yellow(\"Available configurations:\"));\n\n\t\t\t\t// Show available configurations\n\t\t\t\tfor (let gpuCount = 1; gpuCount <= pod.gpus.length; gpuCount++) {\n\t\t\t\t\tconst config = getModelConfig(modelId, pod.gpus, gpuCount);\n\t\t\t\t\tif (config) {\n\t\t\t\t\t\tconsole.error(chalk.gray(` - ${gpuCount} GPU(s)`));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tprocess.exit(1);\n\t\t\t}\n\t\t} else {\n\t\t\t// Find best config for this hardware (original behavior)\n\t\t\tfor (let gpuCount = pod.gpus.length; gpuCount >= 1; gpuCount--) {\n\t\t\t\tmodelConfig = getModelConfig(modelId, pod.gpus, gpuCount);\n\t\t\t\tif (modelConfig) {\n\t\t\t\t\tgpus = selectGPUs(pod, gpuCount);\n\t\t\t\t\tvllmArgs = [...(modelConfig.args || [])];\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!modelConfig) {\n\t\t\t\tconsole.error(chalk.red(`Model '${getModelName(modelId)}' not compatible with this pod's GPUs`));\n\t\t\t\tprocess.exit(1);\n\t\t\t}\n\t\t}\n\t} else {\n\t\t// Unknown model\n\t\tif (options.gpus) {\n\t\t\tconsole.error(chalk.red(\"Error: --gpus can only be used with predefined models\"));\n\t\t\tconsole.error(chalk.yellow(\"For custom models, use --vllm with tensor-parallel-size or similar arguments\"));\n\t\t\tprocess.exit(1);\n\t\t}\n\t\t// Single GPU default\n\t\tgpus = selectGPUs(pod, 1);\n\t\tconsole.log(chalk.gray(\"Unknown model, defaulting to single GPU\"));\n\t}\n\n\t// Apply memory/context overrides\n\tif (!options.vllmArgs?.length) {\n\t\tif (options.memory) {\n\t\t\tconst fraction = parseFloat(options.memory.replace(\"%\", \"\")) / 100;\n\t\t\tvllmArgs = vllmArgs.filter((arg) => !arg.includes(\"gpu-memory-utilization\"));\n\t\t\tvllmArgs.push(\"--gpu-memory-utilization\", String(fraction));\n\t\t}\n\t\tif (options.context) {\n\t\t\tconst contextSizes: Record<string, number> = {\n\t\t\t\t\"4k\": 4096,\n\t\t\t\t\"8k\": 8192,\n\t\t\t\t\"16k\": 16384,\n\t\t\t\t\"32k\": 32768,\n\t\t\t\t\"64k\": 65536,\n\t\t\t\t\"128k\": 131072,\n\t\t\t};\n\t\t\tconst maxTokens = contextSizes[options.context.toLowerCase()] || parseInt(options.context, 10);\n\t\t\tvllmArgs = vllmArgs.filter((arg) => !arg.includes(\"max-model-len\"));\n\t\t\tvllmArgs.push(\"--max-model-len\", String(maxTokens));\n\t\t}\n\t}\n\n\t// Show what we're doing\n\tconsole.log(chalk.green(`Starting model '${name}' on pod '${podName}'...`));\n\tconsole.log(`Model: ${modelId}`);\n\tconsole.log(`Port: ${port}`);\n\tconsole.log(`GPU(s): ${gpus.length ? gpus.join(\", \") : \"Managed by vLLM\"}`);\n\tif (modelConfig?.notes) console.log(chalk.yellow(`Note: ${modelConfig.notes}`));\n\tconsole.log(\"\");\n\n\t// Read and customize model_run.sh script with our values\n\tconst scriptPath = join(dirname(fileURLToPath(import.meta.url)), \"../../scripts/model_run.sh\");\n\tlet scriptContent = readFileSync(scriptPath, \"utf-8\");\n\n\t// Replace placeholders - no escaping needed, heredoc with 'EOF' is literal\n\tscriptContent = scriptContent\n\t\t.replace(\"{{MODEL_ID}}\", modelId)\n\t\t.replace(\"{{NAME}}\", name)\n\t\t.replace(\"{{PORT}}\", String(port))\n\t\t.replace(\"{{VLLM_ARGS}}\", vllmArgs.join(\" \"));\n\n\t// Upload customized script\n\tconst result = await sshExec(\n\t\tpod.ssh,\n\t\t`cat > /tmp/model_run_${name}.sh << 'EOF'\n${scriptContent}\nEOF\nchmod +x /tmp/model_run_${name}.sh`,\n\t);\n\n\t// Prepare environment\n\tconst env = [\n\t\t`HF_TOKEN='${process.env.HF_TOKEN}'`,\n\t\t`PI_API_KEY='${process.env.PI_API_KEY}'`,\n\t\t`HF_HUB_ENABLE_HF_TRANSFER=1`,\n\t\t`VLLM_NO_USAGE_STATS=1`,\n\t\t`PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True`,\n\t\t`FORCE_COLOR=1`,\n\t\t`TERM=xterm-256color`,\n\t\t...(gpus.length === 1 ? [`CUDA_VISIBLE_DEVICES=${gpus[0]}`] : []),\n\t\t...Object.entries(modelConfig?.env || {}).map(([k, v]) => `${k}='${v}'`),\n\t]\n\t\t.map((e) => `export ${e}`)\n\t\t.join(\"\\n\");\n\n\t// Start the model runner with script command for pseudo-TTY (preserves colors)\n\t// Note: We use script to preserve colors and create a log file\n\t// setsid creates a new session so it survives SSH disconnection\n\tconst startCmd = `\n\t\t${env}\n\t\tmkdir -p ~/.vllm_logs\n\t\t# Create a wrapper that monitors the script command\n\t\tcat > /tmp/model_wrapper_${name}.sh << 'WRAPPER'\n#!/bin/bash\nscript -q -f -c \"/tmp/model_run_${name}.sh\" ~/.vllm_logs/${name}.log\nexit_code=$?\necho \"Script exited with code $exit_code\" >> ~/.vllm_logs/${name}.log\nexit $exit_code\nWRAPPER\n\t\tchmod +x /tmp/model_wrapper_${name}.sh\n\t\tsetsid /tmp/model_wrapper_${name}.sh </dev/null >/dev/null 2>&1 &\n\t\techo $!\n\t\texit 0\n\t`;\n\n\tconst pidResult = await sshExec(pod.ssh, startCmd);\n\tconst pid = parseInt(pidResult.stdout.trim(), 10);\n\tif (!pid) {\n\t\tconsole.error(chalk.red(\"Failed to start model runner\"));\n\t\tprocess.exit(1);\n\t}\n\n\t// Save to config\n\tconst config = loadConfig();\n\tconfig.pods[podName].models[name] = { model: modelId, port, gpu: gpus, pid };\n\tsaveConfig(config);\n\n\tconsole.log(`Model runner started with PID: ${pid}`);\n\tconsole.log(\"Streaming logs... (waiting for startup)\\n\");\n\n\t// Small delay to ensure log file is created\n\tawait new Promise((resolve) => setTimeout(resolve, 500));\n\n\t// Stream logs with color support, watching for startup complete\n\tconst sshParts = pod.ssh.split(\" \");\n\tconst sshCommand = sshParts[0]; // \"ssh\"\n\tconst sshArgs = sshParts.slice(1); // [\"root@86.38.238.55\"]\n\tconst host = sshArgs[0].split(\"@\")[1] || \"localhost\";\n\tconst tailCmd = `tail -f ~/.vllm_logs/${name}.log`;\n\n\t// Build the full args array for spawn\n\tconst fullArgs = [...sshArgs, tailCmd];\n\n\tconst logProcess = spawn(sshCommand, fullArgs, {\n\t\tstdio: [\"inherit\", \"pipe\", \"pipe\"], // capture stdout and stderr\n\t\tenv: { ...process.env, FORCE_COLOR: \"1\" },\n\t});\n\n\tlet interrupted = false;\n\tlet startupComplete = false;\n\tlet startupFailed = false;\n\tlet failureReason = \"\";\n\n\t// Handle Ctrl+C\n\tconst sigintHandler = () => {\n\t\tinterrupted = true;\n\t\tlogProcess.kill();\n\t};\n\tprocess.on(\"SIGINT\", sigintHandler);\n\n\t// Process log output line by line\n\tconst processOutput = (data: Buffer) => {\n\t\tconst lines = data.toString().split(\"\\n\");\n\t\tfor (const line of lines) {\n\t\t\tif (line) {\n\t\t\t\tconsole.log(line); // Echo the line to console\n\n\t\t\t\t// Check for startup complete message\n\t\t\t\tif (line.includes(\"Application startup complete\")) {\n\t\t\t\t\tstartupComplete = true;\n\t\t\t\t\tlogProcess.kill(); // Stop tailing logs\n\t\t\t\t}\n\n\t\t\t\t// Check for failure indicators\n\t\t\t\tif (line.includes(\"Model runner exiting with code\") && !line.includes(\"code 0\")) {\n\t\t\t\t\tstartupFailed = true;\n\t\t\t\t\tfailureReason = \"Model runner failed to start\";\n\t\t\t\t\tlogProcess.kill();\n\t\t\t\t}\n\t\t\t\tif (line.includes(\"Script exited with code\") && !line.includes(\"code 0\")) {\n\t\t\t\t\tstartupFailed = true;\n\t\t\t\t\tfailureReason = \"Script failed to execute\";\n\t\t\t\t\tlogProcess.kill();\n\t\t\t\t}\n\t\t\t\tif (line.includes(\"torch.OutOfMemoryError\") || line.includes(\"CUDA out of memory\")) {\n\t\t\t\t\tstartupFailed = true;\n\t\t\t\t\tfailureReason = \"Out of GPU memory (OOM)\";\n\t\t\t\t\t// Don't kill immediately - let it show more error context\n\t\t\t\t}\n\t\t\t\tif (line.includes(\"RuntimeError: Engine core initialization failed\")) {\n\t\t\t\t\tstartupFailed = true;\n\t\t\t\t\tfailureReason = \"vLLM engine initialization failed\";\n\t\t\t\t\tlogProcess.kill();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t};\n\n\tlogProcess.stdout?.on(\"data\", processOutput);\n\tlogProcess.stderr?.on(\"data\", processOutput);\n\n\tawait new Promise<void>((resolve) => logProcess.on(\"exit\", resolve));\n\tprocess.removeListener(\"SIGINT\", sigintHandler);\n\n\tif (startupFailed) {\n\t\t// Model failed to start - clean up and report error\n\t\tconsole.log(\"\\n\" + chalk.red(`✗ Model failed to start: ${failureReason}`));\n\n\t\t// Remove the failed model from config\n\t\tconst config = loadConfig();\n\t\tdelete config.pods[podName].models[name];\n\t\tsaveConfig(config);\n\n\t\tconsole.log(chalk.yellow(\"\\nModel has been removed from configuration.\"));\n\n\t\t// Provide helpful suggestions based on failure reason\n\t\tif (failureReason.includes(\"OOM\") || failureReason.includes(\"memory\")) {\n\t\t\tconsole.log(\"\\n\" + chalk.bold(\"Suggestions:\"));\n\t\t\tconsole.log(\" • Try reducing GPU memory utilization: --memory 50%\");\n\t\t\tconsole.log(\" • Use a smaller context window: --context 4k\");\n\t\t\tconsole.log(\" • Use a quantized version of the model (e.g., FP8)\");\n\t\t\tconsole.log(\" • Use more GPUs with tensor parallelism\");\n\t\t\tconsole.log(\" • Try a smaller model variant\");\n\t\t}\n\n\t\tconsole.log(\"\\n\" + chalk.cyan('Check full logs: pi ssh \"tail -100 ~/.vllm_logs/' + name + '.log\"'));\n\t\tprocess.exit(1);\n\t} else if (startupComplete) {\n\t\t// Model started successfully - output connection details\n\t\tconsole.log(\"\\n\" + chalk.green(\"✓ Model started successfully!\"));\n\t\tconsole.log(\"\\n\" + chalk.bold(\"Connection Details:\"));\n\t\tconsole.log(chalk.cyan(\"─\".repeat(50)));\n\t\tconsole.log(chalk.white(\"Base URL: \") + chalk.yellow(`http://${host}:${port}/v1`));\n\t\tconsole.log(chalk.white(\"Model: \") + chalk.yellow(modelId));\n\t\tconsole.log(chalk.white(\"API Key: \") + chalk.yellow(process.env.PI_API_KEY || \"(not set)\"));\n\t\tconsole.log(chalk.cyan(\"─\".repeat(50)));\n\n\t\tconsole.log(\"\\n\" + chalk.bold(\"Export for shell:\"));\n\t\tconsole.log(chalk.gray(`export OPENAI_BASE_URL=\"http://${host}:${port}/v1\"`));\n\t\tconsole.log(chalk.gray(`export OPENAI_API_KEY=\"${process.env.PI_API_KEY || \"your-api-key\"}\"`));\n\t\tconsole.log(chalk.gray(`export OPENAI_MODEL=\"${modelId}\"`));\n\n\t\tconsole.log(\"\\n\" + chalk.bold(\"Example usage:\"));\n\t\tconsole.log(\n\t\t\tchalk.gray(`\n # Python\n from openai import OpenAI\n client = OpenAI() # Uses env vars\n response = client.chat.completions.create(\n model=\"${modelId}\",\n messages=[{\"role\": \"user\", \"content\": \"Hello!\"}]\n )\n\n # CLI\n curl $OPENAI_BASE_URL/chat/completions \\\\\n -H \"Authorization: Bearer $OPENAI_API_KEY\" \\\\\n -H \"Content-Type: application/json\" \\\\\n -d '{\"model\":\"${modelId}\",\"messages\":[{\"role\":\"user\",\"content\":\"Hi\"}]}'`),\n\t\t);\n\t\tconsole.log(\"\");\n\t\tconsole.log(chalk.cyan(`Chat with model: pi agent ${name} \"Your message\"`));\n\t\tconsole.log(chalk.cyan(`Interactive mode: pi agent ${name} -i`));\n\t\tconsole.log(chalk.cyan(`Monitor logs: pi logs ${name}`));\n\t\tconsole.log(chalk.cyan(`Stop model: pi stop ${name}`));\n\t} else if (interrupted) {\n\t\tconsole.log(chalk.yellow(\"\\n\\nStopped monitoring. Model deployment continues in background.\"));\n\t\tconsole.log(chalk.cyan(`Chat with model: pi agent ${name} \"Your message\"`));\n\t\tconsole.log(chalk.cyan(`Check status: pi logs ${name}`));\n\t\tconsole.log(chalk.cyan(`Stop model: pi stop ${name}`));\n\t} else {\n\t\tconsole.log(chalk.yellow(\"\\n\\nLog stream ended. Model may still be running.\"));\n\t\tconsole.log(chalk.cyan(`Chat with model: pi agent ${name} \"Your message\"`));\n\t\tconsole.log(chalk.cyan(`Check status: pi logs ${name}`));\n\t\tconsole.log(chalk.cyan(`Stop model: pi stop ${name}`));\n\t}\n};\n\n/**\n * Stop a model\n */\nexport const stopModel = async (name: string, options: { pod?: string }) => {\n\tconst { name: podName, pod } = getPod(options.pod);\n\n\tconst model = pod.models[name];\n\tif (!model) {\n\t\tconsole.error(chalk.red(`Model '${name}' not found on pod '${podName}'`));\n\t\tprocess.exit(1);\n\t}\n\n\tconsole.log(chalk.yellow(`Stopping model '${name}' on pod '${podName}'...`));\n\n\t// Kill the script process and all its children\n\t// Using pkill to kill the process and all children\n\tconst killCmd = `\n\t\t# Kill the script process and all its children\n\t\tpkill -TERM -P ${model.pid} 2>/dev/null || true\n\t\tkill ${model.pid} 2>/dev/null || true\n\t`;\n\tawait sshExec(pod.ssh, killCmd);\n\n\t// Remove from config\n\tconst config = loadConfig();\n\tdelete config.pods[podName].models[name];\n\tsaveConfig(config);\n\n\tconsole.log(chalk.green(`✓ Model '${name}' stopped`));\n};\n\n/**\n * Stop all models on a pod\n */\nexport const stopAllModels = async (options: { pod?: string }) => {\n\tconst { name: podName, pod } = getPod(options.pod);\n\n\tconst modelNames = Object.keys(pod.models);\n\tif (modelNames.length === 0) {\n\t\tconsole.log(`No models running on pod '${podName}'`);\n\t\treturn;\n\t}\n\n\tconsole.log(chalk.yellow(`Stopping ${modelNames.length} model(s) on pod '${podName}'...`));\n\n\t// Kill all script processes and their children\n\tconst pids = Object.values(pod.models).map((m) => m.pid);\n\tconst killCmd = `\n\t\tfor PID in ${pids.join(\" \")}; do\n\t\t\tpkill -TERM -P $PID 2>/dev/null || true\n\t\t\tkill $PID 2>/dev/null || true\n\t\tdone\n\t`;\n\tawait sshExec(pod.ssh, killCmd);\n\n\t// Clear all models from config\n\tconst config = loadConfig();\n\tconfig.pods[podName].models = {};\n\tsaveConfig(config);\n\n\tconsole.log(chalk.green(`✓ Stopped all models: ${modelNames.join(\", \")}`));\n};\n\n/**\n * List all models\n */\nexport const listModels = async (options: { pod?: string }) => {\n\tconst { name: podName, pod } = getPod(options.pod);\n\n\tconst modelNames = Object.keys(pod.models);\n\tif (modelNames.length === 0) {\n\t\tconsole.log(`No models running on pod '${podName}'`);\n\t\treturn;\n\t}\n\n\t// Get pod SSH host for URL display\n\tconst sshParts = pod.ssh.split(\" \");\n\tconst host = sshParts.find((p) => p.includes(\"@\"))?.split(\"@\")[1] || \"unknown\";\n\n\tconsole.log(`Models on pod '${chalk.bold(podName)}':`);\n\tfor (const name of modelNames) {\n\t\tconst model = pod.models[name];\n\t\tconst gpuStr =\n\t\t\tmodel.gpu.length > 1\n\t\t\t\t? `GPUs ${model.gpu.join(\",\")}`\n\t\t\t\t: model.gpu.length === 1\n\t\t\t\t\t? `GPU ${model.gpu[0]}`\n\t\t\t\t\t: \"GPU unknown\";\n\t\tconsole.log(` ${chalk.green(name)} - Port ${model.port} - ${gpuStr} - PID ${model.pid}`);\n\t\tconsole.log(` Model: ${chalk.gray(model.model)}`);\n\t\tconsole.log(` URL: ${chalk.cyan(`http://${host}:${model.port}/v1`)}`);\n\t}\n\n\t// Optionally verify processes are still running\n\tconsole.log(\"\");\n\tconsole.log(\"Verifying processes...\");\n\tlet anyDead = false;\n\tfor (const name of modelNames) {\n\t\tconst model = pod.models[name];\n\t\t// Check both the wrapper process and if vLLM is responding\n\t\tconst checkCmd = `\n\t\t\t# Check if wrapper process exists\n\t\t\tif ps -p ${model.pid} > /dev/null 2>&1; then\n\t\t\t\t# Process exists, now check if vLLM is responding\n\t\t\t\tif curl -s -f http://localhost:${model.port}/health > /dev/null 2>&1; then\n\t\t\t\t\techo \"running\"\n\t\t\t\telse\n\t\t\t\t\t# Check if it's still starting up\n\t\t\t\t\tif tail -n 20 ~/.vllm_logs/${name}.log 2>/dev/null | grep -q \"ERROR\\\\|Failed\\\\|Cuda error\\\\|died\"; then\n\t\t\t\t\t\techo \"crashed\"\n\t\t\t\t\telse\n\t\t\t\t\t\techo \"starting\"\n\t\t\t\t\tfi\n\t\t\t\tfi\n\t\t\telse\n\t\t\t\techo \"dead\"\n\t\t\tfi\n\t\t`;\n\t\tconst result = await sshExec(pod.ssh, checkCmd);\n\t\tconst status = result.stdout.trim();\n\t\tif (status === \"dead\") {\n\t\t\tconsole.log(chalk.red(` ${name}: Process ${model.pid} is not running`));\n\t\t\tanyDead = true;\n\t\t} else if (status === \"crashed\") {\n\t\t\tconsole.log(chalk.red(` ${name}: vLLM crashed (check logs with 'pi logs ${name}')`));\n\t\t\tanyDead = true;\n\t\t} else if (status === \"starting\") {\n\t\t\tconsole.log(chalk.yellow(` ${name}: Still starting up...`));\n\t\t}\n\t}\n\n\tif (anyDead) {\n\t\tconsole.log(\"\");\n\t\tconsole.log(chalk.yellow(\"Some models are not running. Clean up with:\"));\n\t\tconsole.log(chalk.cyan(\" pi stop <name>\"));\n\t} else {\n\t\tconsole.log(chalk.green(\"✓ All processes verified\"));\n\t}\n};\n\n/**\n * View model logs\n */\nexport const viewLogs = async (name: string, options: { pod?: string }) => {\n\tconst { name: podName, pod } = getPod(options.pod);\n\n\tconst model = pod.models[name];\n\tif (!model) {\n\t\tconsole.error(chalk.red(`Model '${name}' not found on pod '${podName}'`));\n\t\tprocess.exit(1);\n\t}\n\n\tconsole.log(chalk.green(`Streaming logs for '${name}' on pod '${podName}'...`));\n\tconsole.log(chalk.gray(\"Press Ctrl+C to stop\"));\n\tconsole.log(\"\");\n\n\t// Stream logs with color preservation\n\tconst sshParts = pod.ssh.split(\" \");\n\tconst sshCommand = sshParts[0]; // \"ssh\"\n\tconst sshArgs = sshParts.slice(1); // [\"root@86.38.238.55\"]\n\tconst tailCmd = `tail -f ~/.vllm_logs/${name}.log`;\n\n\tconst logProcess = spawn(sshCommand, [...sshArgs, tailCmd], {\n\t\tstdio: \"inherit\",\n\t\tenv: {\n\t\t\t...process.env,\n\t\t\tFORCE_COLOR: \"1\",\n\t\t},\n\t});\n\n\t// Wait for process to exit\n\tawait new Promise<void>((resolve) => {\n\t\tlogProcess.on(\"exit\", () => resolve());\n\t});\n};\n\n/**\n * Show known models and their hardware requirements\n */\nexport const showKnownModels = async () => {\n\tconst __filename = fileURLToPath(import.meta.url);\n\tconst __dirname = dirname(__filename);\n\tconst modelsJsonPath = join(__dirname, \"..\", \"models.json\");\n\tconst modelsJson = JSON.parse(readFileSync(modelsJsonPath, \"utf-8\"));\n\tconst models = modelsJson.models;\n\n\t// Get active pod info if available\n\tconst activePod = getActivePod();\n\tlet podGpuCount = 0;\n\tlet podGpuType = \"\";\n\n\tif (activePod) {\n\t\tpodGpuCount = activePod.pod.gpus.length;\n\t\t// Extract GPU type from name (e.g., \"NVIDIA H200\" -> \"H200\")\n\t\tpodGpuType = activePod.pod.gpus[0]?.name?.replace(\"NVIDIA\", \"\")?.trim()?.split(\" \")[0] || \"\";\n\n\t\tconsole.log(chalk.bold(`Known Models for ${activePod.name} (${podGpuCount}x ${podGpuType || \"GPU\"}):\\n`));\n\t} else {\n\t\tconsole.log(chalk.bold(\"Known Models:\\n\"));\n\t\tconsole.log(chalk.yellow(\"No active pod. Use 'pi pods active <name>' to filter compatible models.\\n\"));\n\t}\n\n\tconsole.log(\"Usage: pi start <model> --name <name> [options]\\n\");\n\n\t// Group models by compatibility and family\n\tconst compatible: Record<string, Array<{ id: string; name: string; config: string; notes?: string }>> = {};\n\tconst incompatible: Record<string, Array<{ id: string; name: string; minGpu: string; notes?: string }>> = {};\n\n\tfor (const [modelId, info] of Object.entries(models)) {\n\t\tconst modelInfo = info as any;\n\t\tconst family = modelInfo.name.split(\"-\")[0] || \"Other\";\n\n\t\tlet isCompatible = false;\n\t\tlet compatibleConfig = \"\";\n\t\tlet minGpu = \"Unknown\";\n\t\tlet minNotes: string | undefined;\n\n\t\tif (modelInfo.configs && modelInfo.configs.length > 0) {\n\t\t\t// Sort configs by GPU count to find minimum\n\t\t\tconst sortedConfigs = [...modelInfo.configs].sort((a: any, b: any) => (a.gpuCount || 1) - (b.gpuCount || 1));\n\n\t\t\t// Find minimum requirements\n\t\t\tconst minConfig = sortedConfigs[0];\n\t\t\tconst minGpuCount = minConfig.gpuCount || 1;\n\t\t\tconst gpuTypes = minConfig.gpuTypes?.join(\"/\") || \"H100/H200\";\n\n\t\t\tif (minGpuCount === 1) {\n\t\t\t\tminGpu = `1x ${gpuTypes}`;\n\t\t\t} else {\n\t\t\t\tminGpu = `${minGpuCount}x ${gpuTypes}`;\n\t\t\t}\n\n\t\t\tminNotes = minConfig.notes || modelInfo.notes;\n\n\t\t\t// Check compatibility with active pod\n\t\t\tif (activePod && podGpuCount > 0) {\n\t\t\t\t// Find best matching config for this pod\n\t\t\t\tfor (const config of sortedConfigs) {\n\t\t\t\t\tconst configGpuCount = config.gpuCount || 1;\n\t\t\t\t\tconst configGpuTypes = config.gpuTypes || [];\n\n\t\t\t\t\t// Check if we have enough GPUs\n\t\t\t\t\tif (configGpuCount <= podGpuCount) {\n\t\t\t\t\t\t// Check if GPU type matches (if specified)\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\tconfigGpuTypes.length === 0 ||\n\t\t\t\t\t\t\tconfigGpuTypes.some((type: string) => podGpuType.includes(type) || type.includes(podGpuType))\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\tisCompatible = true;\n\t\t\t\t\t\t\tif (configGpuCount === 1) {\n\t\t\t\t\t\t\t\tcompatibleConfig = `1x ${podGpuType}`;\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tcompatibleConfig = `${configGpuCount}x ${podGpuType}`;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tminNotes = config.notes || modelInfo.notes;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tconst modelEntry = {\n\t\t\tid: modelId,\n\t\t\tname: modelInfo.name,\n\t\t\tnotes: minNotes,\n\t\t};\n\n\t\tif (activePod && isCompatible) {\n\t\t\tif (!compatible[family]) {\n\t\t\t\tcompatible[family] = [];\n\t\t\t}\n\t\t\tcompatible[family].push({ ...modelEntry, config: compatibleConfig });\n\t\t} else {\n\t\t\tif (!incompatible[family]) {\n\t\t\t\tincompatible[family] = [];\n\t\t\t}\n\t\t\tincompatible[family].push({ ...modelEntry, minGpu });\n\t\t}\n\t}\n\n\t// Display compatible models first\n\tif (activePod && Object.keys(compatible).length > 0) {\n\t\tconsole.log(chalk.green.bold(\"✓ Compatible Models:\\n\"));\n\n\t\tconst sortedFamilies = Object.keys(compatible).sort();\n\t\tfor (const family of sortedFamilies) {\n\t\t\tconsole.log(chalk.cyan(`${family} Models:`));\n\n\t\t\tconst modelList = compatible[family].sort((a, b) => a.name.localeCompare(b.name));\n\n\t\t\tfor (const model of modelList) {\n\t\t\t\tconsole.log(` ${chalk.green(model.id)}`);\n\t\t\t\tconsole.log(` Name: ${model.name}`);\n\t\t\t\tconsole.log(` Config: ${model.config}`);\n\t\t\t\tif (model.notes) {\n\t\t\t\t\tconsole.log(chalk.gray(` Note: ${model.notes}`));\n\t\t\t\t}\n\t\t\t\tconsole.log(\"\");\n\t\t\t}\n\t\t}\n\t}\n\n\t// Display incompatible models\n\tif (Object.keys(incompatible).length > 0) {\n\t\tif (activePod && Object.keys(compatible).length > 0) {\n\t\t\tconsole.log(chalk.red.bold(\"✗ Incompatible Models (need more/different GPUs):\\n\"));\n\t\t}\n\n\t\tconst sortedFamilies = Object.keys(incompatible).sort();\n\t\tfor (const family of sortedFamilies) {\n\t\t\tif (!activePod) {\n\t\t\t\tconsole.log(chalk.cyan(`${family} Models:`));\n\t\t\t} else {\n\t\t\t\tconsole.log(chalk.gray(`${family} Models:`));\n\t\t\t}\n\n\t\t\tconst modelList = incompatible[family].sort((a, b) => a.name.localeCompare(b.name));\n\n\t\t\tfor (const model of modelList) {\n\t\t\t\tconst color = activePod ? chalk.gray : chalk.green;\n\t\t\t\tconsole.log(` ${color(model.id)}`);\n\t\t\t\tconsole.log(chalk.gray(` Name: ${model.name}`));\n\t\t\t\tconsole.log(chalk.gray(` Min Hardware: ${model.minGpu}`));\n\t\t\t\tif (model.notes && !activePod) {\n\t\t\t\t\tconsole.log(chalk.gray(` Note: ${model.notes}`));\n\t\t\t\t}\n\t\t\t\tif (activePod) {\n\t\t\t\t\tconsole.log(\"\"); // Less verbose for incompatible models when filtered\n\t\t\t\t} else {\n\t\t\t\t\tconsole.log(\"\");\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tconsole.log(chalk.gray(\"\\nFor unknown models, defaults to single GPU deployment.\"));\n\tconsole.log(chalk.gray(\"Use --vllm to pass custom arguments to vLLM.\"));\n};\n"]}
1
+ {"version":3,"file":"models.js","sourceRoot":"","sources":["../../src/commands/models.ts"],"names":[],"mappings":"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","sourcesContent":["import chalk from \"chalk\";\nimport { spawn } from \"child_process\";\nimport { readFileSync } from \"fs\";\nimport { dirname, join } from \"path\";\nimport { fileURLToPath } from \"url\";\nimport { getActivePod, loadConfig, saveConfig } from \"../config.js\";\nimport { getModelConfig, getModelName, isKnownModel } from \"../model-configs.js\";\nimport { sshExec } from \"../ssh.js\";\nimport type { Pod } from \"../types.js\";\n\n/**\n * Get the pod to use (active or override)\n */\nconst getPod = (podOverride?: string): { name: string; pod: Pod } => {\n\tif (podOverride) {\n\t\tconst config = loadConfig();\n\t\tconst pod = config.pods[podOverride];\n\t\tif (!pod) {\n\t\t\tconsole.error(chalk.red(`Pod '${podOverride}' not found`));\n\t\t\tprocess.exit(1);\n\t\t}\n\t\treturn { name: podOverride, pod };\n\t}\n\n\tconst active = getActivePod();\n\tif (!active) {\n\t\tconsole.error(chalk.red(\"No active pod. Use 'pi pods active <name>' to set one.\"));\n\t\tprocess.exit(1);\n\t}\n\treturn active;\n};\n\n/**\n * Find next available port starting from 8001\n */\nconst getNextPort = (pod: Pod): number => {\n\tconst usedPorts = Object.values(pod.models).map((m) => m.port);\n\tlet port = 8001;\n\twhile (usedPorts.includes(port)) {\n\t\tport++;\n\t}\n\treturn port;\n};\n\n/**\n * Select GPUs for model deployment (round-robin)\n */\nconst selectGPUs = (pod: Pod, count: number = 1): number[] => {\n\tif (count === pod.gpus.length) {\n\t\t// Use all GPUs\n\t\treturn pod.gpus.map((g) => g.id);\n\t}\n\n\t// Count GPU usage across all models\n\tconst gpuUsage = new Map<number, number>();\n\tfor (const gpu of pod.gpus) {\n\t\tgpuUsage.set(gpu.id, 0);\n\t}\n\n\tfor (const model of Object.values(pod.models)) {\n\t\tfor (const gpuId of model.gpu) {\n\t\t\tgpuUsage.set(gpuId, (gpuUsage.get(gpuId) || 0) + 1);\n\t\t}\n\t}\n\n\t// Sort GPUs by usage (least used first)\n\tconst sortedGPUs = Array.from(gpuUsage.entries())\n\t\t.sort((a, b) => a[1] - b[1])\n\t\t.map((entry) => entry[0]);\n\n\t// Return the least used GPUs\n\treturn sortedGPUs.slice(0, count);\n};\n\n/**\n * Start a model\n */\nexport const startModel = async (\n\tmodelId: string,\n\tname: string,\n\toptions: {\n\t\tpod?: string;\n\t\tvllmArgs?: string[];\n\t\tmemory?: string;\n\t\tcontext?: string;\n\t\tgpus?: number;\n\t},\n) => {\n\tconst { name: podName, pod } = getPod(options.pod);\n\n\t// Validation\n\tif (!pod.modelsPath) {\n\t\tconsole.error(chalk.red(\"Pod does not have a models path configured\"));\n\t\tprocess.exit(1);\n\t}\n\tif (pod.models[name]) {\n\t\tconsole.error(chalk.red(`Model '${name}' already exists on pod '${podName}'`));\n\t\tprocess.exit(1);\n\t}\n\n\tconst port = getNextPort(pod);\n\n\t// Determine GPU allocation and vLLM args\n\tlet gpus: number[] = [];\n\tlet vllmArgs: string[] = [];\n\tlet modelConfig = null;\n\n\tif (options.vllmArgs?.length) {\n\t\t// Custom args override everything\n\t\tvllmArgs = options.vllmArgs;\n\t\tconsole.log(chalk.gray(\"Using custom vLLM args, GPU allocation managed by vLLM\"));\n\t} else if (isKnownModel(modelId)) {\n\t\t// Handle --gpus parameter for known models\n\t\tif (options.gpus) {\n\t\t\t// Validate GPU count\n\t\t\tif (options.gpus > pod.gpus.length) {\n\t\t\t\tconsole.error(chalk.red(`Error: Requested ${options.gpus} GPUs but pod only has ${pod.gpus.length}`));\n\t\t\t\tprocess.exit(1);\n\t\t\t}\n\n\t\t\t// Try to find config for requested GPU count\n\t\t\tmodelConfig = getModelConfig(modelId, pod.gpus, options.gpus);\n\t\t\tif (modelConfig) {\n\t\t\t\tgpus = selectGPUs(pod, options.gpus);\n\t\t\t\tvllmArgs = [...(modelConfig.args || [])];\n\t\t\t} else {\n\t\t\t\tconsole.error(\n\t\t\t\t\tchalk.red(`Model '${getModelName(modelId)}' does not have a configuration for ${options.gpus} GPU(s)`),\n\t\t\t\t);\n\t\t\t\tconsole.error(chalk.yellow(\"Available configurations:\"));\n\n\t\t\t\t// Show available configurations\n\t\t\t\tfor (let gpuCount = 1; gpuCount <= pod.gpus.length; gpuCount++) {\n\t\t\t\t\tconst config = getModelConfig(modelId, pod.gpus, gpuCount);\n\t\t\t\t\tif (config) {\n\t\t\t\t\t\tconsole.error(chalk.gray(` - ${gpuCount} GPU(s)`));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tprocess.exit(1);\n\t\t\t}\n\t\t} else {\n\t\t\t// Find best config for this hardware (original behavior)\n\t\t\tfor (let gpuCount = pod.gpus.length; gpuCount >= 1; gpuCount--) {\n\t\t\t\tmodelConfig = getModelConfig(modelId, pod.gpus, gpuCount);\n\t\t\t\tif (modelConfig) {\n\t\t\t\t\tgpus = selectGPUs(pod, gpuCount);\n\t\t\t\t\tvllmArgs = [...(modelConfig.args || [])];\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!modelConfig) {\n\t\t\t\tconsole.error(chalk.red(`Model '${getModelName(modelId)}' not compatible with this pod's GPUs`));\n\t\t\t\tprocess.exit(1);\n\t\t\t}\n\t\t}\n\t} else {\n\t\t// Unknown model\n\t\tif (options.gpus) {\n\t\t\tconsole.error(chalk.red(\"Error: --gpus can only be used with predefined models\"));\n\t\t\tconsole.error(chalk.yellow(\"For custom models, use --vllm with tensor-parallel-size or similar arguments\"));\n\t\t\tprocess.exit(1);\n\t\t}\n\t\t// Single GPU default\n\t\tgpus = selectGPUs(pod, 1);\n\t\tconsole.log(chalk.gray(\"Unknown model, defaulting to single GPU\"));\n\t}\n\n\t// Apply memory/context overrides\n\tif (!options.vllmArgs?.length) {\n\t\tif (options.memory) {\n\t\t\tconst fraction = parseFloat(options.memory.replace(\"%\", \"\")) / 100;\n\t\t\tvllmArgs = vllmArgs.filter((arg) => !arg.includes(\"gpu-memory-utilization\"));\n\t\t\tvllmArgs.push(\"--gpu-memory-utilization\", String(fraction));\n\t\t}\n\t\tif (options.context) {\n\t\t\tconst contextSizes: Record<string, number> = {\n\t\t\t\t\"4k\": 4096,\n\t\t\t\t\"8k\": 8192,\n\t\t\t\t\"16k\": 16384,\n\t\t\t\t\"32k\": 32768,\n\t\t\t\t\"64k\": 65536,\n\t\t\t\t\"128k\": 131072,\n\t\t\t};\n\t\t\tconst maxTokens = contextSizes[options.context.toLowerCase()] || parseInt(options.context, 10);\n\t\t\tvllmArgs = vllmArgs.filter((arg) => !arg.includes(\"max-model-len\"));\n\t\t\tvllmArgs.push(\"--max-model-len\", String(maxTokens));\n\t\t}\n\t}\n\n\t// Show what we're doing\n\tconsole.log(chalk.green(`Starting model '${name}' on pod '${podName}'...`));\n\tconsole.log(`Model: ${modelId}`);\n\tconsole.log(`Port: ${port}`);\n\tconsole.log(`GPU(s): ${gpus.length ? gpus.join(\", \") : \"Managed by vLLM\"}`);\n\tif (modelConfig?.notes) console.log(chalk.yellow(`Note: ${modelConfig.notes}`));\n\tconsole.log(\"\");\n\n\t// Read and customize model_run.sh script with our values\n\tconst scriptPath = join(dirname(fileURLToPath(import.meta.url)), \"../../scripts/model_run.sh\");\n\tlet scriptContent = readFileSync(scriptPath, \"utf-8\");\n\n\t// Replace placeholders - no escaping needed, heredoc with 'EOF' is literal\n\tscriptContent = scriptContent\n\t\t.replace(\"{{MODEL_ID}}\", modelId)\n\t\t.replace(\"{{NAME}}\", name)\n\t\t.replace(\"{{PORT}}\", String(port))\n\t\t.replace(\"{{VLLM_ARGS}}\", vllmArgs.join(\" \"));\n\n\t// Upload customized script\n\tawait sshExec(\n\t\tpod.ssh,\n\t\t`cat > /tmp/model_run_${name}.sh << 'EOF'\n${scriptContent}\nEOF\nchmod +x /tmp/model_run_${name}.sh`,\n\t);\n\n\t// Prepare environment\n\tconst env = [\n\t\t`HF_TOKEN='${process.env.HF_TOKEN}'`,\n\t\t`PI_API_KEY='${process.env.PI_API_KEY}'`,\n\t\t`HF_HUB_ENABLE_HF_TRANSFER=1`,\n\t\t`VLLM_NO_USAGE_STATS=1`,\n\t\t`PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True`,\n\t\t`FORCE_COLOR=1`,\n\t\t`TERM=xterm-256color`,\n\t\t...(gpus.length === 1 ? [`CUDA_VISIBLE_DEVICES=${gpus[0]}`] : []),\n\t\t...Object.entries(modelConfig?.env || {}).map(([k, v]) => `${k}='${v}'`),\n\t]\n\t\t.map((e) => `export ${e}`)\n\t\t.join(\"\\n\");\n\n\t// Start the model runner with script command for pseudo-TTY (preserves colors)\n\t// Note: We use script to preserve colors and create a log file\n\t// setsid creates a new session so it survives SSH disconnection\n\tconst startCmd = `\n\t\t${env}\n\t\tmkdir -p ~/.vllm_logs\n\t\t# Create a wrapper that monitors the script command\n\t\tcat > /tmp/model_wrapper_${name}.sh << 'WRAPPER'\n#!/bin/bash\nscript -q -f -c \"/tmp/model_run_${name}.sh\" ~/.vllm_logs/${name}.log\nexit_code=$?\necho \"Script exited with code $exit_code\" >> ~/.vllm_logs/${name}.log\nexit $exit_code\nWRAPPER\n\t\tchmod +x /tmp/model_wrapper_${name}.sh\n\t\tsetsid /tmp/model_wrapper_${name}.sh </dev/null >/dev/null 2>&1 &\n\t\techo $!\n\t\texit 0\n\t`;\n\n\tconst pidResult = await sshExec(pod.ssh, startCmd);\n\tconst pid = parseInt(pidResult.stdout.trim(), 10);\n\tif (!pid) {\n\t\tconsole.error(chalk.red(\"Failed to start model runner\"));\n\t\tprocess.exit(1);\n\t}\n\n\t// Save to config\n\tconst config = loadConfig();\n\tconfig.pods[podName].models[name] = { model: modelId, port, gpu: gpus, pid };\n\tsaveConfig(config);\n\n\tconsole.log(`Model runner started with PID: ${pid}`);\n\tconsole.log(\"Streaming logs... (waiting for startup)\\n\");\n\n\t// Small delay to ensure log file is created\n\tawait new Promise((resolve) => setTimeout(resolve, 500));\n\n\t// Stream logs with color support, watching for startup complete\n\tconst sshParts = pod.ssh.split(\" \");\n\tconst sshCommand = sshParts[0]; // \"ssh\"\n\tconst sshArgs = sshParts.slice(1); // [\"root@86.38.238.55\"]\n\tconst host = sshArgs[0].split(\"@\")[1] || \"localhost\";\n\tconst tailCmd = `tail -f ~/.vllm_logs/${name}.log`;\n\n\t// Build the full args array for spawn\n\tconst fullArgs = [...sshArgs, tailCmd];\n\n\tconst logProcess = spawn(sshCommand, fullArgs, {\n\t\tstdio: [\"inherit\", \"pipe\", \"pipe\"], // capture stdout and stderr\n\t\tenv: { ...process.env, FORCE_COLOR: \"1\" },\n\t});\n\n\tlet interrupted = false;\n\tlet startupComplete = false;\n\tlet startupFailed = false;\n\tlet failureReason = \"\";\n\n\t// Handle Ctrl+C\n\tconst sigintHandler = () => {\n\t\tinterrupted = true;\n\t\tlogProcess.kill();\n\t};\n\tprocess.on(\"SIGINT\", sigintHandler);\n\n\t// Process log output line by line\n\tconst processOutput = (data: Buffer) => {\n\t\tconst lines = data.toString().split(\"\\n\");\n\t\tfor (const line of lines) {\n\t\t\tif (line) {\n\t\t\t\tconsole.log(line); // Echo the line to console\n\n\t\t\t\t// Check for startup complete message\n\t\t\t\tif (line.includes(\"Application startup complete\")) {\n\t\t\t\t\tstartupComplete = true;\n\t\t\t\t\tlogProcess.kill(); // Stop tailing logs\n\t\t\t\t}\n\n\t\t\t\t// Check for failure indicators\n\t\t\t\tif (line.includes(\"Model runner exiting with code\") && !line.includes(\"code 0\")) {\n\t\t\t\t\tstartupFailed = true;\n\t\t\t\t\tfailureReason = \"Model runner failed to start\";\n\t\t\t\t\tlogProcess.kill();\n\t\t\t\t}\n\t\t\t\tif (line.includes(\"Script exited with code\") && !line.includes(\"code 0\")) {\n\t\t\t\t\tstartupFailed = true;\n\t\t\t\t\tfailureReason = \"Script failed to execute\";\n\t\t\t\t\tlogProcess.kill();\n\t\t\t\t}\n\t\t\t\tif (line.includes(\"torch.OutOfMemoryError\") || line.includes(\"CUDA out of memory\")) {\n\t\t\t\t\tstartupFailed = true;\n\t\t\t\t\tfailureReason = \"Out of GPU memory (OOM)\";\n\t\t\t\t\t// Don't kill immediately - let it show more error context\n\t\t\t\t}\n\t\t\t\tif (line.includes(\"RuntimeError: Engine core initialization failed\")) {\n\t\t\t\t\tstartupFailed = true;\n\t\t\t\t\tfailureReason = \"vLLM engine initialization failed\";\n\t\t\t\t\tlogProcess.kill();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t};\n\n\tlogProcess.stdout?.on(\"data\", processOutput);\n\tlogProcess.stderr?.on(\"data\", processOutput);\n\n\tawait new Promise<void>((resolve) => logProcess.on(\"exit\", resolve));\n\tprocess.removeListener(\"SIGINT\", sigintHandler);\n\n\tif (startupFailed) {\n\t\t// Model failed to start - clean up and report error\n\t\tconsole.log(`\\n${chalk.red(`✗ Model failed to start: ${failureReason}`)}`);\n\n\t\t// Remove the failed model from config\n\t\tconst config = loadConfig();\n\t\tdelete config.pods[podName].models[name];\n\t\tsaveConfig(config);\n\n\t\tconsole.log(chalk.yellow(\"\\nModel has been removed from configuration.\"));\n\n\t\t// Provide helpful suggestions based on failure reason\n\t\tif (failureReason.includes(\"OOM\") || failureReason.includes(\"memory\")) {\n\t\t\tconsole.log(`\\n${chalk.bold(\"Suggestions:\")}`);\n\t\t\tconsole.log(\" • Try reducing GPU memory utilization: --memory 50%\");\n\t\t\tconsole.log(\" • Use a smaller context window: --context 4k\");\n\t\t\tconsole.log(\" • Use a quantized version of the model (e.g., FP8)\");\n\t\t\tconsole.log(\" • Use more GPUs with tensor parallelism\");\n\t\t\tconsole.log(\" • Try a smaller model variant\");\n\t\t}\n\n\t\tconsole.log(`\\n${chalk.cyan(`Check full logs: pi ssh \"tail -100 ~/.vllm_logs/${name}.log\"`)}`);\n\t\tprocess.exit(1);\n\t} else if (startupComplete) {\n\t\t// Model started successfully - output connection details\n\t\tconsole.log(`\\n${chalk.green(\"✓ Model started successfully!\")}`);\n\t\tconsole.log(`\\n${chalk.bold(\"Connection Details:\")}`);\n\t\tconsole.log(chalk.cyan(\"─\".repeat(50)));\n\t\tconsole.log(chalk.white(\"Base URL: \") + chalk.yellow(`http://${host}:${port}/v1`));\n\t\tconsole.log(chalk.white(\"Model: \") + chalk.yellow(modelId));\n\t\tconsole.log(chalk.white(\"API Key: \") + chalk.yellow(process.env.PI_API_KEY || \"(not set)\"));\n\t\tconsole.log(chalk.cyan(\"─\".repeat(50)));\n\n\t\tconsole.log(`\\n${chalk.bold(\"Export for shell:\")}`);\n\t\tconsole.log(chalk.gray(`export OPENAI_BASE_URL=\"http://${host}:${port}/v1\"`));\n\t\tconsole.log(chalk.gray(`export OPENAI_API_KEY=\"${process.env.PI_API_KEY || \"your-api-key\"}\"`));\n\t\tconsole.log(chalk.gray(`export OPENAI_MODEL=\"${modelId}\"`));\n\n\t\tconsole.log(`\\n${chalk.bold(\"Example usage:\")}`);\n\t\tconsole.log(\n\t\t\tchalk.gray(`\n # Python\n from openai import OpenAI\n client = OpenAI() # Uses env vars\n response = client.chat.completions.create(\n model=\"${modelId}\",\n messages=[{\"role\": \"user\", \"content\": \"Hello!\"}]\n )\n\n # CLI\n curl $OPENAI_BASE_URL/chat/completions \\\\\n -H \"Authorization: Bearer $OPENAI_API_KEY\" \\\\\n -H \"Content-Type: application/json\" \\\\\n -d '{\"model\":\"${modelId}\",\"messages\":[{\"role\":\"user\",\"content\":\"Hi\"}]}'`),\n\t\t);\n\t\tconsole.log(\"\");\n\t\tconsole.log(chalk.cyan(`Chat with model: pi agent ${name} \"Your message\"`));\n\t\tconsole.log(chalk.cyan(`Interactive mode: pi agent ${name} -i`));\n\t\tconsole.log(chalk.cyan(`Monitor logs: pi logs ${name}`));\n\t\tconsole.log(chalk.cyan(`Stop model: pi stop ${name}`));\n\t} else if (interrupted) {\n\t\tconsole.log(chalk.yellow(\"\\n\\nStopped monitoring. Model deployment continues in background.\"));\n\t\tconsole.log(chalk.cyan(`Chat with model: pi agent ${name} \"Your message\"`));\n\t\tconsole.log(chalk.cyan(`Check status: pi logs ${name}`));\n\t\tconsole.log(chalk.cyan(`Stop model: pi stop ${name}`));\n\t} else {\n\t\tconsole.log(chalk.yellow(\"\\n\\nLog stream ended. Model may still be running.\"));\n\t\tconsole.log(chalk.cyan(`Chat with model: pi agent ${name} \"Your message\"`));\n\t\tconsole.log(chalk.cyan(`Check status: pi logs ${name}`));\n\t\tconsole.log(chalk.cyan(`Stop model: pi stop ${name}`));\n\t}\n};\n\n/**\n * Stop a model\n */\nexport const stopModel = async (name: string, options: { pod?: string }) => {\n\tconst { name: podName, pod } = getPod(options.pod);\n\n\tconst model = pod.models[name];\n\tif (!model) {\n\t\tconsole.error(chalk.red(`Model '${name}' not found on pod '${podName}'`));\n\t\tprocess.exit(1);\n\t}\n\n\tconsole.log(chalk.yellow(`Stopping model '${name}' on pod '${podName}'...`));\n\n\t// Kill the script process and all its children\n\t// Using pkill to kill the process and all children\n\tconst killCmd = `\n\t\t# Kill the script process and all its children\n\t\tpkill -TERM -P ${model.pid} 2>/dev/null || true\n\t\tkill ${model.pid} 2>/dev/null || true\n\t`;\n\tawait sshExec(pod.ssh, killCmd);\n\n\t// Remove from config\n\tconst config = loadConfig();\n\tdelete config.pods[podName].models[name];\n\tsaveConfig(config);\n\n\tconsole.log(chalk.green(`✓ Model '${name}' stopped`));\n};\n\n/**\n * Stop all models on a pod\n */\nexport const stopAllModels = async (options: { pod?: string }) => {\n\tconst { name: podName, pod } = getPod(options.pod);\n\n\tconst modelNames = Object.keys(pod.models);\n\tif (modelNames.length === 0) {\n\t\tconsole.log(`No models running on pod '${podName}'`);\n\t\treturn;\n\t}\n\n\tconsole.log(chalk.yellow(`Stopping ${modelNames.length} model(s) on pod '${podName}'...`));\n\n\t// Kill all script processes and their children\n\tconst pids = Object.values(pod.models).map((m) => m.pid);\n\tconst killCmd = `\n\t\tfor PID in ${pids.join(\" \")}; do\n\t\t\tpkill -TERM -P $PID 2>/dev/null || true\n\t\t\tkill $PID 2>/dev/null || true\n\t\tdone\n\t`;\n\tawait sshExec(pod.ssh, killCmd);\n\n\t// Clear all models from config\n\tconst config = loadConfig();\n\tconfig.pods[podName].models = {};\n\tsaveConfig(config);\n\n\tconsole.log(chalk.green(`✓ Stopped all models: ${modelNames.join(\", \")}`));\n};\n\n/**\n * List all models\n */\nexport const listModels = async (options: { pod?: string }) => {\n\tconst { name: podName, pod } = getPod(options.pod);\n\n\tconst modelNames = Object.keys(pod.models);\n\tif (modelNames.length === 0) {\n\t\tconsole.log(`No models running on pod '${podName}'`);\n\t\treturn;\n\t}\n\n\t// Get pod SSH host for URL display\n\tconst sshParts = pod.ssh.split(\" \");\n\tconst host = sshParts.find((p) => p.includes(\"@\"))?.split(\"@\")[1] || \"unknown\";\n\n\tconsole.log(`Models on pod '${chalk.bold(podName)}':`);\n\tfor (const name of modelNames) {\n\t\tconst model = pod.models[name];\n\t\tconst gpuStr =\n\t\t\tmodel.gpu.length > 1\n\t\t\t\t? `GPUs ${model.gpu.join(\",\")}`\n\t\t\t\t: model.gpu.length === 1\n\t\t\t\t\t? `GPU ${model.gpu[0]}`\n\t\t\t\t\t: \"GPU unknown\";\n\t\tconsole.log(` ${chalk.green(name)} - Port ${model.port} - ${gpuStr} - PID ${model.pid}`);\n\t\tconsole.log(` Model: ${chalk.gray(model.model)}`);\n\t\tconsole.log(` URL: ${chalk.cyan(`http://${host}:${model.port}/v1`)}`);\n\t}\n\n\t// Optionally verify processes are still running\n\tconsole.log(\"\");\n\tconsole.log(\"Verifying processes...\");\n\tlet anyDead = false;\n\tfor (const name of modelNames) {\n\t\tconst model = pod.models[name];\n\t\t// Check both the wrapper process and if vLLM is responding\n\t\tconst checkCmd = `\n\t\t\t# Check if wrapper process exists\n\t\t\tif ps -p ${model.pid} > /dev/null 2>&1; then\n\t\t\t\t# Process exists, now check if vLLM is responding\n\t\t\t\tif curl -s -f http://localhost:${model.port}/health > /dev/null 2>&1; then\n\t\t\t\t\techo \"running\"\n\t\t\t\telse\n\t\t\t\t\t# Check if it's still starting up\n\t\t\t\t\tif tail -n 20 ~/.vllm_logs/${name}.log 2>/dev/null | grep -q \"ERROR\\\\|Failed\\\\|Cuda error\\\\|died\"; then\n\t\t\t\t\t\techo \"crashed\"\n\t\t\t\t\telse\n\t\t\t\t\t\techo \"starting\"\n\t\t\t\t\tfi\n\t\t\t\tfi\n\t\t\telse\n\t\t\t\techo \"dead\"\n\t\t\tfi\n\t\t`;\n\t\tconst result = await sshExec(pod.ssh, checkCmd);\n\t\tconst status = result.stdout.trim();\n\t\tif (status === \"dead\") {\n\t\t\tconsole.log(chalk.red(` ${name}: Process ${model.pid} is not running`));\n\t\t\tanyDead = true;\n\t\t} else if (status === \"crashed\") {\n\t\t\tconsole.log(chalk.red(` ${name}: vLLM crashed (check logs with 'pi logs ${name}')`));\n\t\t\tanyDead = true;\n\t\t} else if (status === \"starting\") {\n\t\t\tconsole.log(chalk.yellow(` ${name}: Still starting up...`));\n\t\t}\n\t}\n\n\tif (anyDead) {\n\t\tconsole.log(\"\");\n\t\tconsole.log(chalk.yellow(\"Some models are not running. Clean up with:\"));\n\t\tconsole.log(chalk.cyan(\" pi stop <name>\"));\n\t} else {\n\t\tconsole.log(chalk.green(\"✓ All processes verified\"));\n\t}\n};\n\n/**\n * View model logs\n */\nexport const viewLogs = async (name: string, options: { pod?: string }) => {\n\tconst { name: podName, pod } = getPod(options.pod);\n\n\tconst model = pod.models[name];\n\tif (!model) {\n\t\tconsole.error(chalk.red(`Model '${name}' not found on pod '${podName}'`));\n\t\tprocess.exit(1);\n\t}\n\n\tconsole.log(chalk.green(`Streaming logs for '${name}' on pod '${podName}'...`));\n\tconsole.log(chalk.gray(\"Press Ctrl+C to stop\"));\n\tconsole.log(\"\");\n\n\t// Stream logs with color preservation\n\tconst sshParts = pod.ssh.split(\" \");\n\tconst sshCommand = sshParts[0]; // \"ssh\"\n\tconst sshArgs = sshParts.slice(1); // [\"root@86.38.238.55\"]\n\tconst tailCmd = `tail -f ~/.vllm_logs/${name}.log`;\n\n\tconst logProcess = spawn(sshCommand, [...sshArgs, tailCmd], {\n\t\tstdio: \"inherit\",\n\t\tenv: {\n\t\t\t...process.env,\n\t\t\tFORCE_COLOR: \"1\",\n\t\t},\n\t});\n\n\t// Wait for process to exit\n\tawait new Promise<void>((resolve) => {\n\t\tlogProcess.on(\"exit\", () => resolve());\n\t});\n};\n\n/**\n * Show known models and their hardware requirements\n */\nexport const showKnownModels = async () => {\n\tconst __filename = fileURLToPath(import.meta.url);\n\tconst __dirname = dirname(__filename);\n\tconst modelsJsonPath = join(__dirname, \"..\", \"models.json\");\n\tconst modelsJson = JSON.parse(readFileSync(modelsJsonPath, \"utf-8\"));\n\tconst models = modelsJson.models;\n\n\t// Get active pod info if available\n\tconst activePod = getActivePod();\n\tlet podGpuCount = 0;\n\tlet podGpuType = \"\";\n\n\tif (activePod) {\n\t\tpodGpuCount = activePod.pod.gpus.length;\n\t\t// Extract GPU type from name (e.g., \"NVIDIA H200\" -> \"H200\")\n\t\tpodGpuType = activePod.pod.gpus[0]?.name?.replace(\"NVIDIA\", \"\")?.trim()?.split(\" \")[0] || \"\";\n\n\t\tconsole.log(chalk.bold(`Known Models for ${activePod.name} (${podGpuCount}x ${podGpuType || \"GPU\"}):\\n`));\n\t} else {\n\t\tconsole.log(chalk.bold(\"Known Models:\\n\"));\n\t\tconsole.log(chalk.yellow(\"No active pod. Use 'pi pods active <name>' to filter compatible models.\\n\"));\n\t}\n\n\tconsole.log(\"Usage: pi start <model> --name <name> [options]\\n\");\n\n\t// Group models by compatibility and family\n\tconst compatible: Record<string, Array<{ id: string; name: string; config: string; notes?: string }>> = {};\n\tconst incompatible: Record<string, Array<{ id: string; name: string; minGpu: string; notes?: string }>> = {};\n\n\tfor (const [modelId, info] of Object.entries(models)) {\n\t\tconst modelInfo = info as any;\n\t\tconst family = modelInfo.name.split(\"-\")[0] || \"Other\";\n\n\t\tlet isCompatible = false;\n\t\tlet compatibleConfig = \"\";\n\t\tlet minGpu = \"Unknown\";\n\t\tlet minNotes: string | undefined;\n\n\t\tif (modelInfo.configs && modelInfo.configs.length > 0) {\n\t\t\t// Sort configs by GPU count to find minimum\n\t\t\tconst sortedConfigs = [...modelInfo.configs].sort((a: any, b: any) => (a.gpuCount || 1) - (b.gpuCount || 1));\n\n\t\t\t// Find minimum requirements\n\t\t\tconst minConfig = sortedConfigs[0];\n\t\t\tconst minGpuCount = minConfig.gpuCount || 1;\n\t\t\tconst gpuTypes = minConfig.gpuTypes?.join(\"/\") || \"H100/H200\";\n\n\t\t\tif (minGpuCount === 1) {\n\t\t\t\tminGpu = `1x ${gpuTypes}`;\n\t\t\t} else {\n\t\t\t\tminGpu = `${minGpuCount}x ${gpuTypes}`;\n\t\t\t}\n\n\t\t\tminNotes = minConfig.notes || modelInfo.notes;\n\n\t\t\t// Check compatibility with active pod\n\t\t\tif (activePod && podGpuCount > 0) {\n\t\t\t\t// Find best matching config for this pod\n\t\t\t\tfor (const config of sortedConfigs) {\n\t\t\t\t\tconst configGpuCount = config.gpuCount || 1;\n\t\t\t\t\tconst configGpuTypes = config.gpuTypes || [];\n\n\t\t\t\t\t// Check if we have enough GPUs\n\t\t\t\t\tif (configGpuCount <= podGpuCount) {\n\t\t\t\t\t\t// Check if GPU type matches (if specified)\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\tconfigGpuTypes.length === 0 ||\n\t\t\t\t\t\t\tconfigGpuTypes.some((type: string) => podGpuType.includes(type) || type.includes(podGpuType))\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\tisCompatible = true;\n\t\t\t\t\t\t\tif (configGpuCount === 1) {\n\t\t\t\t\t\t\t\tcompatibleConfig = `1x ${podGpuType}`;\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tcompatibleConfig = `${configGpuCount}x ${podGpuType}`;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tminNotes = config.notes || modelInfo.notes;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tconst modelEntry = {\n\t\t\tid: modelId,\n\t\t\tname: modelInfo.name,\n\t\t\tnotes: minNotes,\n\t\t};\n\n\t\tif (activePod && isCompatible) {\n\t\t\tif (!compatible[family]) {\n\t\t\t\tcompatible[family] = [];\n\t\t\t}\n\t\t\tcompatible[family].push({ ...modelEntry, config: compatibleConfig });\n\t\t} else {\n\t\t\tif (!incompatible[family]) {\n\t\t\t\tincompatible[family] = [];\n\t\t\t}\n\t\t\tincompatible[family].push({ ...modelEntry, minGpu });\n\t\t}\n\t}\n\n\t// Display compatible models first\n\tif (activePod && Object.keys(compatible).length > 0) {\n\t\tconsole.log(chalk.green.bold(\"✓ Compatible Models:\\n\"));\n\n\t\tconst sortedFamilies = Object.keys(compatible).sort();\n\t\tfor (const family of sortedFamilies) {\n\t\t\tconsole.log(chalk.cyan(`${family} Models:`));\n\n\t\t\tconst modelList = compatible[family].sort((a, b) => a.name.localeCompare(b.name));\n\n\t\t\tfor (const model of modelList) {\n\t\t\t\tconsole.log(` ${chalk.green(model.id)}`);\n\t\t\t\tconsole.log(` Name: ${model.name}`);\n\t\t\t\tconsole.log(` Config: ${model.config}`);\n\t\t\t\tif (model.notes) {\n\t\t\t\t\tconsole.log(chalk.gray(` Note: ${model.notes}`));\n\t\t\t\t}\n\t\t\t\tconsole.log(\"\");\n\t\t\t}\n\t\t}\n\t}\n\n\t// Display incompatible models\n\tif (Object.keys(incompatible).length > 0) {\n\t\tif (activePod && Object.keys(compatible).length > 0) {\n\t\t\tconsole.log(chalk.red.bold(\"✗ Incompatible Models (need more/different GPUs):\\n\"));\n\t\t}\n\n\t\tconst sortedFamilies = Object.keys(incompatible).sort();\n\t\tfor (const family of sortedFamilies) {\n\t\t\tif (!activePod) {\n\t\t\t\tconsole.log(chalk.cyan(`${family} Models:`));\n\t\t\t} else {\n\t\t\t\tconsole.log(chalk.gray(`${family} Models:`));\n\t\t\t}\n\n\t\t\tconst modelList = incompatible[family].sort((a, b) => a.name.localeCompare(b.name));\n\n\t\t\tfor (const model of modelList) {\n\t\t\t\tconst color = activePod ? chalk.gray : chalk.green;\n\t\t\t\tconsole.log(` ${color(model.id)}`);\n\t\t\t\tconsole.log(chalk.gray(` Name: ${model.name}`));\n\t\t\t\tconsole.log(chalk.gray(` Min Hardware: ${model.minGpu}`));\n\t\t\t\tif (model.notes && !activePod) {\n\t\t\t\t\tconsole.log(chalk.gray(` Note: ${model.notes}`));\n\t\t\t\t}\n\t\t\t\tif (activePod) {\n\t\t\t\t\tconsole.log(\"\"); // Less verbose for incompatible models when filtered\n\t\t\t\t} else {\n\t\t\t\t\tconsole.log(\"\");\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tconsole.log(chalk.gray(\"\\nFor unknown models, defaults to single GPU deployment.\"));\n\tconsole.log(chalk.gray(\"Use --vllm to pass custom arguments to vLLM.\"));\n};\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mariozechner/pi",
3
- "version": "0.25.3",
3
+ "version": "0.25.4",
4
4
  "description": "CLI tool for managing vLLM deployments on GPU pods",
5
5
  "type": "module",
6
6
  "bin": {
@@ -34,7 +34,7 @@
34
34
  "node": ">=20.0.0"
35
35
  },
36
36
  "dependencies": {
37
- "@mariozechner/pi-agent-core": "^0.25.3",
37
+ "@mariozechner/pi-agent-core": "^0.25.4",
38
38
  "chalk": "^5.5.0"
39
39
  },
40
40
  "devDependencies": {}