chore: scaffold Phase 0 - Tauri2 + Rust + React + llama.cpp integration
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
param(
|
||||
[ValidateSet("cpu", "cuda", "vulkan", "all")]
|
||||
[string]$Backend = "cpu",
|
||||
[string]$LlamaDir = "",
|
||||
[string]$OutDir = ""
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
if (-not $LlamaDir) {
|
||||
$LlamaDir = Join-Path $env:TEMP "llama.cpp"
|
||||
}
|
||||
if (-not $OutDir) {
|
||||
$OutDir = Join-Path $env:APPDATA "XianrenStudio\engines"
|
||||
}
|
||||
|
||||
if (-not (Get-Command cmake -ErrorAction SilentlyContinue)) {
|
||||
throw "CMake is required. Install it with: winget install Kitware.CMake"
|
||||
}
|
||||
|
||||
if (-not (Test-Path (Join-Path $LlamaDir "CMakeLists.txt"))) {
|
||||
Write-Host "Cloning llama.cpp into $LlamaDir"
|
||||
git clone --depth 1 https://github.com/ggerganov/llama.cpp.git $LlamaDir
|
||||
}
|
||||
|
||||
function Build-Backend([string]$name, [string]$extraFlag) {
|
||||
$buildDir = Join-Path $LlamaDir "build-$name"
|
||||
Write-Host "Configuring $name..."
|
||||
cmake -S $LlamaDir -B $buildDir -DCMAKE_BUILD_TYPE=Release -DGGML_NATIVE=ON $extraFlag
|
||||
Write-Host "Building $name..."
|
||||
cmake --build $buildDir --config Release --target llama-server -j 8
|
||||
$src = Join-Path $buildDir "bin\Release\llama-server.exe"
|
||||
if (-not (Test-Path $src)) { $src = Join-Path $buildDir "bin\llama-server.exe" }
|
||||
New-Item -ItemType Directory -Force -Path $OutDir | Out-Null
|
||||
Copy-Item $src (Join-Path $OutDir "llama-server-$name.exe") -Force
|
||||
Write-Host "Built llama-server-$name.exe"
|
||||
}
|
||||
|
||||
switch ($Backend) {
|
||||
"cpu" { Build-Backend "cpu" "-DGGML_CUDA=OFF" }
|
||||
"cuda" { Build-Backend "cuda" "-DGGML_CUDA=ON" }
|
||||
"vulkan" { Build-Backend "vulkan" "-DGGML_VULKAN=ON" }
|
||||
"all" {
|
||||
Build-Backend "cpu" "-DGGML_CUDA=OFF"
|
||||
if (Get-Command nvcc -ErrorAction SilentlyContinue) { Build-Backend "cuda" "-DGGML_CUDA=ON" }
|
||||
if (Get-Command glslc -ErrorAction SilentlyContinue) { Build-Backend "vulkan" "-DGGML_VULKAN=ON" }
|
||||
}
|
||||
}
|
||||
|
||||
Write-Host "All requested builds completed in $OutDir"
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
param(
|
||||
[string]$Endpoint = "https://hf-mirror.com",
|
||||
[string]$OutDir = ""
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
if (-not $OutDir) {
|
||||
$OutDir = Join-Path $env:APPDATA "XianrenStudio\models"
|
||||
}
|
||||
New-Item -ItemType Directory -Force -Path $OutDir | Out-Null
|
||||
|
||||
$modelPath = "Qwen/Qwen2.5-0.5B-Instruct-GGUF/resolve/main/qwen2.5-0.5b-instruct-q4_k_m.gguf"
|
||||
$url = "$Endpoint/$modelPath"
|
||||
$outFile = Join-Path $OutDir "qwen2.5-0.5b-instruct-q4_k_m.gguf"
|
||||
|
||||
Write-Host "Downloading $url"
|
||||
Write-Host "-> $outFile"
|
||||
curl.exe -L --fail --progress-bar -o $outFile $url
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "download failed with exit code $LASTEXITCODE"
|
||||
}
|
||||
Write-Host "Done."
|
||||
@@ -0,0 +1,49 @@
|
||||
param(
|
||||
[ValidateSet("cpu", "cuda", "vulkan")]
|
||||
[string]$Backend = "cpu",
|
||||
[string]$Tag = "",
|
||||
[string]$OutDir = ""
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
if (-not $OutDir) {
|
||||
$OutDir = Join-Path $env:APPDATA "XianrenStudio\engines"
|
||||
}
|
||||
New-Item -ItemType Directory -Force -Path $OutDir | Out-Null
|
||||
|
||||
$repo = "ggerganov/llama.cpp"
|
||||
$api = "https://api.github.com/repos/$repo/releases/latest"
|
||||
|
||||
if (-not $Tag) {
|
||||
Write-Host "Querying latest release from GitHub..."
|
||||
$release = Invoke-RestMethod -Uri $api -Headers @{ "User-Agent" = "xianren-studio" }
|
||||
$Tag = $release.tag_name
|
||||
Write-Host "Latest tag: $Tag"
|
||||
}
|
||||
|
||||
$assetName = "llama-$Tag-bin-win-$Backend-x64.zip"
|
||||
$assetUrl = "https://github.com/$repo/releases/download/$Tag/$assetName"
|
||||
$zipPath = Join-Path $env:TEMP $assetName
|
||||
|
||||
Write-Host "Downloading $assetUrl"
|
||||
curl.exe -L --fail --progress-bar -o $zipPath $assetUrl
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "download failed with exit code $LASTEXITCODE"
|
||||
}
|
||||
|
||||
Write-Host "Extracting llama-server.exe to $OutDir"
|
||||
$extract = Join-Path $env:TEMP "llama-$Backend-extract"
|
||||
if (Test-Path $extract) { Remove-Item -Recurse -Force $extract }
|
||||
Expand-Archive -Path $zipPath -DestinationPath $extract
|
||||
|
||||
$server = Get-ChildItem -Path $extract -Recurse -Filter "llama-server.exe" | Select-Object -First 1
|
||||
if (-not $server) {
|
||||
throw "llama-server.exe not found in archive"
|
||||
}
|
||||
$targetDir = Join-Path $OutDir $Backend
|
||||
New-Item -ItemType Directory -Force -Path $targetDir | Out-Null
|
||||
Copy-Item -Path (Join-Path $extract "*") -Destination $targetDir -Recurse -Force
|
||||
Remove-Item -Recurse -Force $extract
|
||||
|
||||
Write-Host "Done. Engine: $(Join-Path $targetDir "llama-server.exe")"
|
||||
@@ -0,0 +1,51 @@
|
||||
param([string]$OutDir = "apps/desktop/icons")
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
Add-Type -AssemblyName System.Drawing
|
||||
|
||||
$root = Split-Path $PSScriptRoot -Parent
|
||||
$dir = Join-Path $root $OutDir
|
||||
New-Item -ItemType Directory -Force -Path $dir | Out-Null
|
||||
|
||||
function New-IconPng([int]$size, [string]$path) {
|
||||
$bmp = New-Object System.Drawing.Bitmap $size, $size
|
||||
$g = [System.Drawing.Graphics]::FromImage($bmp)
|
||||
$g.SmoothingMode = [System.Drawing.Drawing2D.SmoothingMode]::AntiAlias
|
||||
$rect = New-Object System.Drawing.Rectangle 0, 0, $size, $size
|
||||
$brush = New-Object System.Drawing.Drawing2D.LinearGradientBrush $rect, `
|
||||
([System.Drawing.Color]::FromArgb(255, 79, 70, 229)), `
|
||||
([System.Drawing.Color]::FromArgb(255, 14, 165, 233)), 45
|
||||
$g.FillRectangle($brush, $rect)
|
||||
$font = New-Object System.Drawing.Font "Microsoft YaHei", ($size * 0.55), ([System.Drawing.FontStyle]::Bold)
|
||||
$sf = New-Object System.Drawing.StringFormat
|
||||
$sf.Alignment = [System.Drawing.StringAlignment]::Center
|
||||
$sf.LineAlignment = [System.Drawing.StringAlignment]::Center
|
||||
$rectF = New-Object System.Drawing.RectangleF 0, 0, $size, $size
|
||||
$g.DrawString([string][char]0x4ED9, $font, [System.Drawing.Brushes]::White, $rectF, $sf)
|
||||
$bmp.Save($path, [System.Drawing.Imaging.ImageFormat]::Png)
|
||||
$g.Dispose(); $brush.Dispose(); $bmp.Dispose()
|
||||
}
|
||||
|
||||
New-IconPng 32 (Join-Path $dir "32x32.png")
|
||||
New-IconPng 128 (Join-Path $dir "128x128.png")
|
||||
New-IconPng 256 (Join-Path $dir "128x128@2x.png")
|
||||
|
||||
$pngBytes = [System.IO.File]::ReadAllBytes((Join-Path $dir "128x128@2x.png"))
|
||||
$fs = [System.IO.File]::Create((Join-Path $dir "icon.ico"))
|
||||
$bw = New-Object System.IO.BinaryWriter $fs
|
||||
$bw.Write([UInt16]0) # reserved
|
||||
$bw.Write([UInt16]1) # type: icon
|
||||
$bw.Write([UInt16]1) # count
|
||||
$bw.Write([Byte]0) # width (256 -> 0)
|
||||
$bw.Write([Byte]0) # height
|
||||
$bw.Write([Byte]0) # palette
|
||||
$bw.Write([Byte]0) # reserved
|
||||
$bw.Write([UInt16]1) # color planes
|
||||
$bw.Write([UInt16]32) # bpp
|
||||
$bw.Write([UInt32]$pngBytes.Length)
|
||||
$bw.Write([UInt32]22) # data offset
|
||||
$bw.Write($pngBytes)
|
||||
$bw.Close()
|
||||
$fs.Close()
|
||||
|
||||
Write-Host "Icons written to $dir"
|
||||
@@ -0,0 +1,70 @@
|
||||
param(
|
||||
[string]$Engine = "$env:APPDATA\XianrenStudio\engines\cpu\llama-server.exe",
|
||||
[string]$Model = "$env:APPDATA\XianrenStudio\models\qwen2.5-0.5b-instruct-q4_k_m.gguf",
|
||||
[int]$Port = 8088
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
if (-not (Test-Path $Engine)) { throw "engine not found: $Engine" }
|
||||
if (-not (Test-Path $Model)) { throw "model not found: $Model" }
|
||||
|
||||
$log = Join-Path $env:TEMP "llama-server-smoke.log"
|
||||
$args = @(
|
||||
"--model", "`"$Model`"",
|
||||
"--host", "127.0.0.1",
|
||||
"--port", "$Port",
|
||||
"--ctx-size", "2048",
|
||||
"-ngl", "0",
|
||||
"--no-webui"
|
||||
)
|
||||
|
||||
$p = Start-Process -FilePath $Engine -ArgumentList $args `
|
||||
-RedirectStandardOutput $log -RedirectStandardError "$log.err" `
|
||||
-WindowStyle Hidden -PassThru
|
||||
|
||||
try {
|
||||
$ready = $false
|
||||
for ($i = 0; $i -lt 240; $i++) {
|
||||
Start-Sleep -Milliseconds 500
|
||||
try {
|
||||
$r = Invoke-RestMethod -Uri "http://127.0.0.1:$Port/health" -TimeoutSec 2
|
||||
if ($r.status -eq "ok") { $ready = $true; break }
|
||||
} catch {}
|
||||
}
|
||||
if (-not $ready) {
|
||||
Get-Content $log -ErrorAction SilentlyContinue | Select-Object -Last 20
|
||||
throw "engine did not become ready; log: $log"
|
||||
}
|
||||
Write-Host "engine ready (pid $($p.Id), port $Port)"
|
||||
|
||||
$body = @{
|
||||
model = "qwen-test"
|
||||
messages = @(@{ role = "user"; content = "Introduce yourself in one sentence." })
|
||||
stream = $false
|
||||
max_tokens = 128
|
||||
} | ConvertTo-Json -Depth 6
|
||||
$resp = Invoke-RestMethod -Uri "http://127.0.0.1:$Port/v1/chat/completions" `
|
||||
-Method Post -ContentType "application/json; charset=utf-8" -Body $body -TimeoutSec 120
|
||||
Write-Host "NON-STREAM REPLY: $($resp.choices[0].message.content)"
|
||||
|
||||
$body2 = @{
|
||||
model = "qwen-test"
|
||||
messages = @(@{ role = "user"; content = "Count from 1 to 5." })
|
||||
stream = $true
|
||||
max_tokens = 64
|
||||
} | ConvertTo-Json -Depth 6
|
||||
$sse = Invoke-RestMethod -Uri "http://127.0.0.1:$Port/v1/chat/completions" `
|
||||
-Method Post -ContentType "application/json; charset=utf-8" -Body $body2 -TimeoutSec 120
|
||||
$hasDone = $sse -match "\[DONE\]"
|
||||
$dataLines = ($sse -split "`n" | Where-Object { $_ -match "^data:" }).Count
|
||||
Write-Host "STREAM: done-marker=$hasDone data-lines=$dataLines"
|
||||
|
||||
if (-not $hasDone -or $dataLines -lt 2) {
|
||||
throw "streaming check failed"
|
||||
}
|
||||
Write-Host "SMOKE TEST PASSED"
|
||||
} finally {
|
||||
try { Invoke-RestMethod -Method Post -Uri "http://127.0.0.1:$Port/shutdown" -TimeoutSec 5 | Out-Null } catch {}
|
||||
Start-Sleep -Seconds 1
|
||||
if (-not $p.HasExited) { Stop-Process -Id $p.Id -Force -ErrorAction SilentlyContinue }
|
||||
}
|
||||
Reference in New Issue
Block a user