prism-inference 0.1.0

Bounded Rust CPU inference and tokenization for Prism-compatible GGUF models
Documentation
#requires -Version 7.6
<#
.SYNOPSIS
Builds the pinned Prism storage overlay in a fresh external source/build lane.
.DESCRIPTION
Requires the exact source ZIP identified by tools/prism/source.lock.json. No
downloads, tests, benchmarks, model loading, installation, or profile promotion.
PrepareOnly builds Rust and its generated ABI header, then returns verified patched source.
Probe builds ggml and the Rust native alias test; Engine builds completion and server.
The pinned Rust toolchain builds the storage policy as a static library.
Parallel defaults to two compiler jobs; CI can explicitly choose a bounded budget.
CompilerLauncher optionally enables a compiler cache with the Ninja generator.
RustTargetDirectory optionally reuses Cargo build artifacts in external storage;
engine and native-test targets remain separate to preserve the policy archive.
#>
[CmdletBinding()]
[OutputType([Collections.IDictionary])]
param(
    [Parameter(Mandatory)][string]$SourceArchive,
    [string]$CMakePath,
    [ValidateSet('Probe','Engine','All')][string]$BuildMode = 'Probe',
    [ValidateSet('Ninja','Visual Studio 18 2026','Visual Studio 17 2022')][string]$Generator = 'Ninja',
    [ValidateRange(1,12)][int]$Parallel = 2,
    [string]$CompilerLauncher,
    [string]$RustTargetDirectory,
    [switch]$PrepareOnly
)
Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'
$PSNativeCommandUseErrorActionPreference = $true
Import-Module (Join-Path $PSScriptRoot 'Bonsai.Common.psm1') -Force
Import-Module (Join-Path $PSScriptRoot 'Bonsai.Native.psm1')
$archive = Resolve-ExternalPath -Path $SourceArchive
$overlay = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../tools/prism'))
$lock = Get-Content -LiteralPath (Join-Path $overlay 'source.lock.json') -Raw | ConvertFrom-Json -DateKind String
$scratch = Resolve-ExternalPath -Path (Join-Path ([IO.Path]::GetTempPath()) ('SwiftBonsai-PrismBuild-' + [guid]::NewGuid().ToString('N')))
[void][IO.Directory]::CreateDirectory($scratch)
if ($env:GITHUB_ACTIONS -eq 'true' -and $env:GITHUB_ENV) {
    $environmentPath = Resolve-ExternalPath -Path $env:GITHUB_ENV
    if ($scratch.IndexOfAny([char[]]"`r`n") -ge 0) { throw 'The evidence directory cannot contain line breaks.' }
    [IO.File]::AppendAllText($environmentPath, "BONSAI_PRISM_EVIDENCE=$scratch`n")
}
$guard = [IO.FileStream]::new($archive, [IO.FileMode]::Open, [IO.FileAccess]::Read, [IO.FileShare]::Read)
try {
    if ($guard.Length -ne $lock.ArchiveBytes) { throw 'Pinned Prism source ZIP size mismatch.' }
    $archiveHash = [Convert]::ToHexStringLower([Security.Cryptography.SHA256]::HashData($guard))
    if ($archiveHash -cne $lock.ArchiveSha256) { throw 'Pinned Prism source ZIP hash mismatch.' }
    Expand-Archive -LiteralPath $archive -DestinationPath (Join-Path $scratch 'source')
} finally { $guard.Dispose() }
$script:prismCommandNumber = 0
function Invoke-PrismBuildCommand {
    [CmdletBinding()]
    [OutputType([string])]
    param([string[]]$Arguments, [string]$FilePath = $CMakePath, [Collections.IDictionary]$Environment)
    $script:prismCommandNumber++
    # Each build still uses fresh verified sources. ccache can normalize the
    # random lane prefix while retaining its compiler/header/option checks.
    $overrides = @{}
    if ($CompilerLauncher) { $overrides['CCACHE_BASEDIR'] = $scratch }
    if ($Environment) {
        foreach ($key in $Environment.Keys) { $overrides[$key] = $Environment[$key] }
    }
    $log = Join-Path $scratch ('command-{0:D2}.log' -f $script:prismCommandNumber)
    $result = Invoke-BonsaiNativeProcess -Executable $FilePath -ArgumentList $Arguments -WorkingDirectory $scratch -TimeoutSeconds 900 -Environment $overrides -LogPath $log
    if ($result.ExitCode) { throw "Prism build failed; see $log" }
    return ($result.StandardOutput + $result.StandardError).Trim()
}

$cargo = (Get-Command -Name cargo -CommandType Application -ErrorAction Stop).Source
$manifest = Join-Path $overlay 'Cargo.toml'
$rustRoot = if ($RustTargetDirectory) { Resolve-ExternalPath -Path $RustTargetDirectory } else { $scratch }
$rustBuild = Join-Path $rustRoot 'engine-loader'
# Opt-in native-test linking must never overwrite the policy archive used by
# the engine or invalidate its already recorded hash.
$rustNativeBuild = Join-Path $rustRoot 'native-tests'
$rustVersion = Invoke-PrismBuildCommand -FilePath $cargo -Arguments @('+nightly-2026-10-22','--version')
$cargoOutput = Invoke-PrismBuildCommand -FilePath $cargo -Arguments @('+nightly-2026-10-22','build','--locked','--offline','--release','--features','engine-loader','--message-format=json','--manifest-path',$manifest,'--target-dir',$rustBuild)
$records = @($cargoOutput -split "`n" | Where-Object { $_.StartsWith('{') } | ConvertFrom-Json -DateKind String)
$packages = @($records | Where-Object { $_.reason -eq 'compiler-artifact' -and $_.target.name -eq 'bonsai_prism' } | Select-Object -ExpandProperty package_id -Unique)
if ($packages.Count -ne 1) { throw 'Expected one Rust library package identity.' }
$buildScripts = @($records | Where-Object { $_.reason -eq 'build-script-executed' -and $_.package_id -ceq $packages[0] })
if ($buildScripts.Count -ne 1) { throw 'Expected one Rust header-generation build output.' }
$generatedHeader = Join-Path $buildScripts[0].out_dir 'bonsai_prism.h'
if (-not [IO.File]::Exists($generatedHeader)) { throw 'Rust did not generate its C ABI declarations.' }
$libraryName = if ($IsWindows) { 'bonsai_prism.lib' } else { 'libbonsai_prism.a' }
$rustLibrary = Join-Path $rustBuild ('release/' + $libraryName)
if (-not [IO.File]::Exists($rustLibrary)) { throw 'The Rust static library was not produced.' }
$rustReceipt = [ordered]@{
    Version = $rustVersion
    GeneratedHeader = $generatedHeader
    HeaderSha256 = (Get-FileHash -LiteralPath $generatedHeader -Algorithm SHA256).Hash.ToLowerInvariant()
    Library = $rustLibrary
    LibrarySha256 = (Get-FileHash -LiteralPath $rustLibrary -Algorithm SHA256).Hash.ToLowerInvariant()
    ManifestSha256 = (Get-FileHash -LiteralPath $manifest -Algorithm SHA256).Hash.ToLowerInvariant()
    WorkspaceManifestSha256 = (Get-FileHash -LiteralPath (Join-Path $PSScriptRoot '../Cargo.toml') -Algorithm SHA256).Hash.ToLowerInvariant()
    LockSha256 = (Get-FileHash -LiteralPath (Join-Path $PSScriptRoot '../Cargo.lock') -Algorithm SHA256).Hash.ToLowerInvariant()
}

$source = Join-Path $scratch ('source/' + $lock.ArchiveRoot)
$overlayReceipt = & (Join-Path $overlay 'Apply-Overlay.ps1') -SourceDirectory $source -GeneratedHeader $generatedHeader
$receipt = [ordered]@{
    SchemaVersion = 1
    RecordedAtUtc = [DateTime]::UtcNow.ToString('o')
    SourceCommit = $lock.Commit
    SourceArchiveSha256 = $archiveHash
    SourceArchiveBytes = $lock.ArchiveBytes
    Overlay = $overlayReceipt
    SourceDirectory = $source
    EvidenceDirectory = $scratch
    ProbeSourceDirectory = $overlay
    BuildMode = $BuildMode
    Generator = $Generator
    ParallelJobs = $Parallel
    CompilerLauncher = $null
    Rust = $rustReceipt
    Binaries = @()
    BuildConfigurations = @()
    Status = 'prepared'
    Qualification = 'source/build candidate only; no TPS, output-quality, or model qualification'
}
[IO.File]::WriteAllText((Join-Path $scratch 'build-receipt.json'), ($receipt | ConvertTo-Json -Depth 7))
if (-not $PrepareOnly) {
    if (-not $CMakePath) { $CMakePath = (Get-Command -Name cmake -CommandType Application -ErrorAction Stop).Source }
    if (-not [IO.Path]::IsPathFullyQualified($CMakePath) -or $CMakePath -match '%[^%]+%' -or -not [IO.File]::Exists($CMakePath)) { throw 'CMakePath must name an existing expanded absolute executable.' }
    if ($Generator -ne 'Ninja' -and -not $IsWindows) { throw 'Visual Studio generators require Windows.' }
    if ($CompilerLauncher) {
        if ($Generator -ne 'Ninja') { throw 'CompilerLauncher requires the Ninja generator.' }
        if (-not [IO.Path]::IsPathFullyQualified($CompilerLauncher) -or $CompilerLauncher -match '%[^%]+%' -or
            $CompilerLauncher.Contains(';') -or -not [IO.File]::Exists($CompilerLauncher)) {
            throw 'CompilerLauncher must name one existing expanded absolute executable without CMake list separators.'
        }
        $CompilerLauncher = [IO.Path]::GetFullPath($CompilerLauncher)
        $receipt.CompilerLauncher = [ordered]@{
            Path = $CompilerLauncher
            Sha256 = (Get-FileHash -LiteralPath $CompilerLauncher -Algorithm SHA256).Hash.ToLowerInvariant()
            CacheBaseDirectory = $scratch
        }
        [IO.File]::WriteAllText((Join-Path $scratch 'build-receipt.json'), ($receipt | ConvertTo-Json -Depth 7))
    }
    $receipt.CMakeVersion = Invoke-PrismBuildCommand -Arguments @('--version')
    $generatorArguments = @('-G', $Generator)
    if ($Generator -ne 'Ninja') { $generatorArguments += @('-A','ARM64','-T','ClangCL') }
    if ($CompilerLauncher) {
        # Use exactly one explicit launcher instead of ggml's auto-detected cache.
        $generatorArguments += @('-DGGML_CCACHE=OFF', "-DCMAKE_C_COMPILER_LAUNCHER=$CompilerLauncher", "-DCMAKE_CXX_COMPILER_LAUNCHER=$CompilerLauncher")
    }
    $modes = if ($BuildMode -eq 'All') { @('Probe','Engine') } else { @($BuildMode) }
    $binaries = [Collections.Generic.List[object]]::new()
    $configurations = [Collections.Generic.List[object]]::new()
    foreach ($mode in $modes) {
        $build = Join-Path $scratch ('build-' + $mode.ToLowerInvariant())
        if ($mode -eq 'Probe') {
            $arguments = @('-S',$overlay,'-B',$build) + $generatorArguments + @("-DPRISM_SOURCE_DIR=$source",'-DCMAKE_BUILD_TYPE=Release','-DFETCHCONTENT_FULLY_DISCONNECTED=ON')
            $targets = @('ggml','ggml-cpu')
        } else {
            $arguments = @('-S',$source,'-B',$build) + $generatorArguments + @(
                '-DCMAKE_BUILD_TYPE=Release','-DBUILD_SHARED_LIBS=OFF','-DFETCHCONTENT_FULLY_DISCONNECTED=ON',
                '-DLLAMA_BUILD_NUMBER=0',"-DLLAMA_BUILD_COMMIT=$($lock.Commit)+swift-lora-b-share-rust-v3",
                "-DBONSAI_PRISM_LIBRARY=$rustLibrary",
                '-DGGML_NATIVE=OFF','-DGGML_BACKEND_DL=OFF',
                '-DGGML_CPU_ALL_VARIANTS=OFF','-DGGML_OPENMP=OFF','-DGGML_CUDA=OFF',
                '-DGGML_METAL=OFF','-DGGML_VULKAN=OFF','-DGGML_OPENCL=OFF','-DGGML_BLAS=OFF',
                '-DGGML_RPC=OFF','-DLLAMA_BUILD_TESTS=OFF','-DLLAMA_BUILD_APP=OFF',
                '-DLLAMA_BUILD_EXAMPLES=OFF','-DLLAMA_BUILD_TOOLS=ON','-DLLAMA_BUILD_SERVER=ON',
                '-DLLAMA_BUILD_UI=OFF','-DLLAMA_USE_PREBUILT_UI=OFF','-DLLAMA_OPENSSL=OFF',
                '-DLLAMA_BUILD_BORINGSSL=OFF','-DLLAMA_BUILD_LIBRESSL=OFF','-DLLAMA_SUBPROCESS=OFF','-DLLAMA_LLGUIDANCE=OFF')
            $targets = @('llama-completion','llama-server')
        }
        $null = Invoke-PrismBuildCommand -Arguments $arguments
        $null = Invoke-PrismBuildCommand -Arguments (@('--build',$build,'--config','Release','--parallel',[string]$Parallel,'--target') + $targets)
        if ($mode -eq 'Probe') {
            $nativeLibraries = @(Get-ChildItem -LiteralPath $build -Recurse -File | Where-Object { $_.Name -in @('ggml.lib','ggml-cpu.lib','ggml-base.lib','libggml.a','libggml-cpu.a','libggml-base.a') })
            if ($nativeLibraries.Count -ne 3) { throw 'Expected the three pinned ggml native libraries.' }
            $libraryDirectories = @($nativeLibraries.DirectoryName | Sort-Object -Unique)
            $nativeEnvironment = @{ BONSAI_PRISM_NATIVE_LIB_DIR = $libraryDirectories -join [IO.Path]::PathSeparator }
            $cargoOutput = Invoke-PrismBuildCommand -FilePath $cargo -Environment $nativeEnvironment -Arguments @(
                '+nightly-2026-10-22','test','--locked','--offline','--release','--manifest-path',$manifest,'--target-dir',$rustNativeBuild,
                '--features','native-tests','--test','native','--no-run','--message-format=json')
            $executables = @($cargoOutput -split "`n" | Where-Object { $_.StartsWith('{') } | ForEach-Object {
                $record = $_ | ConvertFrom-Json -DateKind String
                if ($record.reason -eq 'compiler-artifact' -and $record.target.name -eq 'native' -and $record.executable) { $record.executable }
            })
            if ($executables.Count -ne 1) { throw 'Expected one Rust native test executable.' }
            $nativeTest = $executables[0]
            $null = Invoke-PrismBuildCommand -Arguments ($arguments + @("-DBONSAI_PRISM_NATIVE_TEST=$nativeTest"))
            $binaries.Add([ordered]@{ Target = 'prism-rust-native-test'; Path = $nativeTest; Sha256 = (Get-FileHash -LiteralPath $nativeTest -Algorithm SHA256).Hash.ToLowerInvariant() })
        }
        $cache = Join-Path $build 'CMakeCache.txt'
        $configurations.Add([ordered]@{
            Mode = $mode
            ConfigureArguments = $arguments
            CacheSha256 = (Get-FileHash -LiteralPath $cache -Algorithm SHA256).Hash.ToLowerInvariant()
            Compiler = @(Get-ChildItem -Path (Join-Path $build 'CMakeFiles/*/CMakeCXXCompiler.cmake') | ForEach-Object {
                @(Get-Content -LiteralPath $_.FullName | Where-Object { $_ -match '^set\(CMAKE_CXX_COMPILER(_ID|_VERSION|_ARCHITECTURE_ID|_FRONTEND_VARIANT|_TARGET)? ' })
            })
            Options = @(Get-Content -LiteralPath $cache | Where-Object { $_ -match '^(CMAKE_BUILD_TYPE|BUILD_SHARED_LIBS|FETCHCONTENT_FULLY_DISCONNECTED|GGML_\w+|LLAMA_\w+):BOOL=' })
        })
        foreach ($target in @($targets | Where-Object { $_ -like 'llama-*' })) {
            $extension = if ($IsWindows) { '.exe' } else { '' }
            $builtFiles = @(Get-ChildItem -LiteralPath $build -Recurse -File -Filter ($target + $extension))
            if ($builtFiles.Count -ne 1) { throw "Expected one built $target executable." }
            $binary = $builtFiles[0].FullName
            $binaries.Add([ordered]@{ Target = $target; Path = $binary; Sha256 = (Get-FileHash -LiteralPath $binary -Algorithm SHA256).Hash.ToLowerInvariant() })
        }
    }
    $receipt.Binaries = $binaries.ToArray()
    $receipt.BuildConfigurations = $configurations.ToArray()
    $receipt.Status = 'built'
}
[IO.File]::WriteAllText((Join-Path $scratch 'build-receipt.json'), ($receipt | ConvertTo-Json -Depth 7))
return $receipt