[CmdletBinding()] param( [string]$InstallRoot = "" ) $ErrorActionPreference = "Stop" if (-not $InstallRoot) { $InstallRoot = Join-Path $PSScriptRoot "..\.local\llama.cpp" } function Get-LlamaVersion([string]$ServerPath) { return (& cmd.exe /d /c "`"$ServerPath`" --version 2>&1" | Out-String).Trim() } $release = Invoke-RestMethod -Uri "https://api.github.com/repos/ggml-org/llama.cpp/releases/latest" $tag = [string]$release.tag_name if (-not $tag) { throw "GitHub did not return a latest llama.cpp release tag." } $InstallRoot = [System.IO.Path]::GetFullPath($InstallRoot) $versionDir = Join-Path $InstallRoot $tag $manifestPath = Join-Path $InstallRoot "installed.json" if (Test-Path $manifestPath) { $installed = Get-Content $manifestPath -Raw | ConvertFrom-Json $installedNumber = [int](([string]$installed.tag) -replace "\D", "") $latestNumber = [int]($tag -replace "\D", "") if ($installedNumber -gt $latestNumber) { throw "Refusing to replace newer local llama.cpp $($installed.tag) with $tag." } } $cudaAssets = @($release.assets | Where-Object { $_.name -match "win-cuda-12.*-x64\.zip$" }) $binaryAsset = $cudaAssets | Where-Object { $_.name -notmatch "cudart" } | Select-Object -First 1 $runtimeAsset = $cudaAssets | Where-Object { $_.name -match "cudart" } | Select-Object -First 1 if (-not $binaryAsset -or -not $runtimeAsset) { throw "The latest release does not contain both Windows x64 CUDA 12 binary and runtime archives." } New-Item -ItemType Directory -Force -Path $versionDir | Out-Null $existingServer = Join-Path $versionDir "llama-server.exe" if (Test-Path $existingServer) { $versionText = Get-LlamaVersion $existingServer [ordered]@{ tag = $tag installed_at = (Get-Date).ToString("o") binary_asset = $binaryAsset.name runtime_asset = $runtimeAsset.name version = $versionText } | ConvertTo-Json | Set-Content -LiteralPath $manifestPath -Encoding UTF8 $versionText exit 0 } $tempDir = Join-Path ([System.IO.Path]::GetTempPath()) "tower-llama-$tag" New-Item -ItemType Directory -Force -Path $tempDir | Out-Null try { foreach ($asset in @($binaryAsset, $runtimeAsset)) { $archive = Join-Path $tempDir $asset.name Invoke-WebRequest -Uri $asset.browser_download_url -OutFile $archive Expand-Archive -LiteralPath $archive -DestinationPath $versionDir -Force } $server = Get-ChildItem -LiteralPath $versionDir -Filter "llama-server.exe" -Recurse | Select-Object -First 1 if (-not $server) { throw "llama-server.exe was not found after extraction." } if ([System.IO.Path]::GetFullPath($server.DirectoryName) -ne [System.IO.Path]::GetFullPath($versionDir)) { Get-ChildItem -LiteralPath $server.DirectoryName -Force | Copy-Item -Destination $versionDir -Recurse -Force } $versionText = Get-LlamaVersion (Join-Path $versionDir "llama-server.exe") [ordered]@{ tag = $tag installed_at = (Get-Date).ToString("o") binary_asset = $binaryAsset.name runtime_asset = $runtimeAsset.name version = $versionText } | ConvertTo-Json | Set-Content -LiteralPath $manifestPath -Encoding UTF8 $versionText } finally { if (Test-Path $tempDir) { Remove-Item -LiteralPath $tempDir -Recurse -Force } }