param( [Parameter(Mandatory = $true)] [ValidateSet("vllm-text", "llama-gguf", "translation-api", "audio-api", "video-api", "image-api", "model-chat-ui")] [string]$Service, [Parameter(Mandatory = $true)] [ValidateSet("start", "stop", "restart", "status", "logs")] [string]$Action, [string]$DockerHost = "ssh://docker-gpu", [string]$SshTarget = "docker-gpu", [int]$Tail = 80 ) $ErrorActionPreference = "Stop" $services = @{ "vllm-text" = @{ Compose = "core/deploy/docker-gpu/vllm/compose.yaml" Env = "core/deploy/docker-gpu/vllm/.env.windows-z.example" Project = "llm-vllm-text" Container = "llm-vllm-text" } "llama-gguf" = @{ Compose = "core/deploy/docker-gpu/llama-cpp/compose.yaml" Env = "core/deploy/docker-gpu/llama-cpp/.env.windows-z.example" Project = "llm-llama-gguf" Container = "llm-llama-devstral-1c" ExtraContainers = @("llm-llama-qwen3-coder-q6-test") } "translation-api" = @{ Compose = "core/deploy/docker-gpu/transformers/translation.compose.yaml" Env = "core/deploy/docker-gpu/transformers/translation.env.example" Project = "llm-transformers-translation" Container = "llm-transformers-translation" } "audio-api" = @{ Compose = "core/deploy/docker-gpu/transformers/audio.compose.yaml" Env = "core/deploy/docker-gpu/transformers/audio.env.example" Project = "llm-transformers-audio" Container = "llm-transformers-audio" } "video-api" = @{ Compose = "core/deploy/docker-gpu/transformers/video.compose.yaml" Env = "core/deploy/docker-gpu/transformers/video.env.example" Project = "llm-transformers-video" Container = "llm-transformers-video" } "image-api" = @{ Compose = "core/deploy/docker-gpu/transformers/image.compose.yaml" Env = "core/deploy/docker-gpu/transformers/image.env.example" Project = "llm-transformers-image" Container = "llm-transformers-image" } "model-chat-ui" = @{ Compose = "core/deploy/docker-gpu/model-chat/compose.yaml" Env = "core/deploy/docker-gpu/model-chat/.env.windows-z.example" Project = "llm-model-chat-ui" Container = "llm-model-chat-ui" } } $plan = $services[$Service] if (-not (Test-Path -LiteralPath $plan.Compose)) { throw "Compose file not found: $($plan.Compose)" } $composeArgs = @("--host", $DockerHost, "compose", "-p", $plan.Project) if ($plan.Env -and (Test-Path -LiteralPath $plan.Env)) { $composeArgs += @("--env-file", $plan.Env) } $composeArgs += @("-f", $plan.Compose) switch ($Action) { "start" { $existing = docker --host $DockerHost ps -a --filter "name=^$($plan.Container)$" --format "{{.Names}}" if ($existing -contains $plan.Container) { docker --host $DockerHost start $plan.Container } else { docker @composeArgs up -d } } "stop" { $containers = @($plan.Container) if ($plan.ExtraContainers) { $containers += $plan.ExtraContainers } foreach ($container in $containers) { $existing = docker --host $DockerHost ps -a --filter "name=^$container$" --format "{{.Names}}" if ($existing -contains $container) { docker --host $DockerHost stop $container } } } "restart" { docker --host $DockerHost restart $plan.Container } "status" { $containers = @($plan.Container) if ($plan.ExtraContainers) { $containers += $plan.ExtraContainers } foreach ($container in $containers) { docker --host $DockerHost ps -a --filter "name=^$container$" } Write-Host "" Write-Host "GPU:" ssh $SshTarget "nvidia-smi --query-gpu=index,name,memory.used,memory.total,utilization.gpu --format=csv,noheader,nounits" } "logs" { docker --host $DockerHost logs --tail $Tail $plan.Container } } exit $LASTEXITCODE