Last active
April 30, 2026 19:48
-
-
Save romero126/acc0c8909a25e6e30edb8863212f2417 to your computer and use it in GitHub Desktop.
Phi Silica
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #Requires -Version 7.0 | |
| <# | |
| .SYNOPSIS | |
| Invoke the on-device Phi Silica AI model through the Windows App SDK. | |
| .DESCRIPTION | |
| Uses the Microsoft.Windows.AI.Text.LanguageModel API from the Windows App SDK | |
| to run the Phi Silica small language model locally on Copilot+ PCs. | |
| All inference runs on the device NPU — no cloud connection required. | |
| On first run the script downloads the Windows App SDK NuGet package to a local | |
| .sdk folder for the .NET projection assemblies. The Phi Silica model itself is | |
| managed by Windows and may download via Windows Update if not already present. | |
| .PARAMETER Prompt | |
| A single prompt to send to the model. Output is written to the pipeline. | |
| .PARAMETER SystemPrompt | |
| System-level instructions that guide the model's behavior and persona. | |
| .PARAMETER Temperature | |
| Sampling temperature (0.0-2.0). Lower values produce more deterministic output. | |
| .PARAMETER TopP | |
| Nucleus sampling threshold. Only tokens whose cumulative probability exceeds | |
| this value are considered. | |
| .PARAMETER TopK | |
| Maximum number of top-probability tokens to consider at each step. | |
| .PARAMETER Interactive | |
| Enter an interactive chat session with conversation history. | |
| Type 'exit' or 'quit' to end the session. | |
| .PARAMETER SdkVersion | |
| Windows App SDK NuGet package version. Defaults to the latest experimental | |
| build that includes AI APIs. | |
| .EXAMPLE | |
| .\Invoke-WinCopilot.ps1 "Explain quantum computing in simple terms" | |
| .EXAMPLE | |
| .\Invoke-WinCopilot.ps1 -Interactive -SystemPrompt "You are a concise coding assistant" | |
| .EXAMPLE | |
| .\Invoke-WinCopilot.ps1 -Prompt "Translate to French: Good morning" -Temperature 0.3 | |
| .NOTES | |
| Requirements: | |
| - Windows 11 24H2+ on a Copilot+ PC (NPU required) | |
| - PowerShell 7.0+ | |
| - Windows App Runtime installed (pre-installed on Copilot+ PCs) | |
| - Internet access on first run to download the SDK NuGet package | |
| #> | |
| [CmdletBinding(DefaultParameterSetName = 'Query')] | |
| [OutputType([string])] | |
| param( | |
| [Parameter(ParameterSetName = 'Query', Position = 0, Mandatory)] | |
| [string]$Prompt, | |
| [Parameter()] | |
| [string]$SystemPrompt, | |
| [Parameter()] | |
| [ValidateRange(0.0, 2.0)] | |
| [double]$Temperature = 0.7, | |
| [Parameter()] | |
| [ValidateRange(0.0, 1.0)] | |
| [double]$TopP, | |
| [Parameter()] | |
| [ValidateRange(1, 500)] | |
| [int]$TopK, | |
| [Parameter(ParameterSetName = 'Chat', Mandatory)] | |
| [switch]$Interactive, | |
| [Parameter()] | |
| [string]$SdkVersion = '1.8.260416003' | |
| ) | |
| $ErrorActionPreference = 'Stop' | |
| $SdkCache = Join-Path $PSScriptRoot '.sdk' | |
| $PkgRoot = Join-Path $SdkCache "microsoft.windowsappsdk.$SdkVersion" | |
| $CsWinRTVer = '2.1.6' | |
| $CsWinRTDir = Join-Path $SdkCache "microsoft.windows.cswinrt.$CsWinRTVer" | |
| $WinSdkNetVer = '10.0.26100.84' | |
| $WinSdkNetDir = Join-Path $SdkCache "microsoft.windows.sdk.net.ref.$WinSdkNetVer" | |
| # --------------------------------------------------------------------------- | |
| # SDK acquisition — download NuGet package for .NET projection assemblies | |
| # --------------------------------------------------------------------------- | |
| function Install-NuGetPackage { | |
| [CmdletBinding()] | |
| param( | |
| [string]$PackageName, | |
| [string]$Version, | |
| [string]$DestPath | |
| ) | |
| if (Test-Path $DestPath) { | |
| Write-Verbose "$PackageName cached at $DestPath" | |
| return | |
| } | |
| Write-Host "[*] Downloading $PackageName $Version..." -ForegroundColor Cyan | |
| $null = New-Item $SdkCache -ItemType Directory -Force | |
| $zip = Join-Path $SdkCache 'pkg.zip' | |
| $url = "https://api.nuget.org/v3-flatcontainer/$($PackageName.ToLower())/$Version/$($PackageName.ToLower()).$Version.nupkg" | |
| try { | |
| Invoke-WebRequest $url -OutFile $zip -UseBasicParsing | |
| } | |
| catch { | |
| throw "Failed to download $PackageName v$Version. Verify the version exists on nuget.org. $_" | |
| } | |
| Expand-Archive $zip -DestinationPath $DestPath -Force | |
| Remove-Item $zip | |
| Write-Host "[+] $PackageName extracted." -ForegroundColor Green | |
| } | |
| function Install-SdkPackage { | |
| [CmdletBinding()] | |
| param() | |
| Install-NuGetPackage -PackageName 'Microsoft.WindowsAppSDK' -Version $SdkVersion -DestPath $PkgRoot | |
| $nuspec = Join-Path $PkgRoot 'Microsoft.WindowsAppSDK.nuspec' | |
| if (Test-Path $nuspec) { | |
| [xml]$spec = Get-Content $nuspec | |
| $ns = @{ n = 'http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd' } | |
| $deps = Select-Xml -Xml $spec -XPath '//n:dependency' -Namespace $ns | | |
| ForEach-Object { $_.Node } | |
| foreach ($dep in $deps) { | |
| $depId = $dep.id | |
| $depVer = $dep.version -replace '[\[\]]', '' | |
| $depDir = Join-Path $SdkCache "$($depId.ToLower()).$depVer" | |
| Install-NuGetPackage -PackageName $depId -Version $depVer -DestPath $depDir | |
| } | |
| } | |
| Install-NuGetPackage -PackageName 'Microsoft.Windows.CsWinRT' -Version $CsWinRTVer -DestPath $CsWinRTDir | |
| Install-NuGetPackage -PackageName 'Microsoft.Windows.SDK.NET.Ref' -Version $WinSdkNetVer -DestPath $WinSdkNetDir | |
| } | |
| # --------------------------------------------------------------------------- | |
| # Assembly loading and Windows App Runtime bootstrap | |
| # --------------------------------------------------------------------------- | |
| function Initialize-Runtime { | |
| [CmdletBinding()] | |
| param() | |
| $arch = if ($env:PROCESSOR_ARCHITECTURE -eq 'ARM64') { 'win-arm64' } else { 'win-x64' } | |
| $pkgDirs = Get-ChildItem $SdkCache -Directory | Select-Object -ExpandProperty FullName | |
| $libDirs = @() | |
| $nativeDirs = @() | |
| foreach ($pkg in $pkgDirs) { | |
| $libParent = Join-Path $pkg 'lib' | |
| if (Test-Path $libParent) { | |
| $best = Get-ChildItem $libParent -Directory | | |
| Where-Object Name -like 'net8.0*' | | |
| Sort-Object Name -Descending | | |
| Select-Object -First 1 -ExpandProperty FullName | |
| if (-not $best) { | |
| $best = Get-ChildItem $libParent -Directory | | |
| Where-Object Name -like 'net6.0*' | | |
| Sort-Object Name -Descending | | |
| Select-Object -First 1 -ExpandProperty FullName | |
| } | |
| if ($best) { $libDirs += $best } | |
| } | |
| $natDir = Join-Path $pkg "runtimes/$arch/native" | |
| if (Test-Path $natDir) { $nativeDirs += $natDir } | |
| } | |
| if ($libDirs.Count -eq 0) { throw "No .NET projection folders found in SDK packages." } | |
| if ($nativeDirs.Count -eq 0) { throw "No native runtime found for $arch." } | |
| Write-Verbose "Projection dirs : $($libDirs -join ', ')" | |
| Write-Verbose "Native dirs : $($nativeDirs -join ', ')" | |
| foreach ($nd in $nativeDirs) { | |
| $env:PATH = "$nd;$env:PATH" | |
| $bootstrapNative = Join-Path $nd 'Microsoft.WindowsAppRuntime.Bootstrap.dll' | |
| if (Test-Path $bootstrapNative) { | |
| [System.Runtime.InteropServices.NativeLibrary]::Load($bootstrapNative) | Out-Null | |
| Write-Verbose "Pre-loaded native bootstrap DLL from $nd" | |
| } | |
| } | |
| $dllNames = @( | |
| 'WinRT.Runtime.dll' | |
| 'Microsoft.Windows.SDK.NET.dll' | |
| 'Microsoft.WindowsAppRuntime.Bootstrap.Net.dll' | |
| 'Microsoft.Windows.ApplicationModel.DynamicDependency.Projection.dll' | |
| 'Microsoft.Windows.AI.Projection.dll' | |
| 'Microsoft.Windows.AI.Foundation.Projection.dll' | |
| 'Microsoft.Windows.AI.Text.Projection.dll' | |
| 'Microsoft.Windows.AI.ContentSafety.Projection.dll' | |
| ) | |
| foreach ($dll in $dllNames) { | |
| $found = $false | |
| foreach ($dir in $libDirs) { | |
| $path = Join-Path $dir $dll | |
| if (Test-Path $path) { | |
| Add-Type -Path $path | |
| Write-Verbose "Loaded $dll" | |
| $found = $true | |
| break | |
| } | |
| } | |
| if (-not $found) { Write-Verbose "Skipped $dll (not in any package)" } | |
| } | |
| try { | |
| $versionTag = if ($SdkVersion -match '-(experimental\d*)$') { $Matches[1] } else { '' } | |
| $minVer = [Microsoft.Windows.ApplicationModel.DynamicDependency.PackageVersion]::new([uint16]0) | |
| $opts = [Microsoft.Windows.ApplicationModel.DynamicDependency.Bootstrap+InitializeOptions]::OnPackageIdentity_NOOP | |
| # 0x00010008 = major 1, minor 8 | |
| [Microsoft.Windows.ApplicationModel.DynamicDependency.Bootstrap]::Initialize( | |
| [uint32]0x00010008, $versionTag, $minVer, $opts) | |
| Write-Verbose "Windows App Runtime bootstrapped (tag: '$versionTag')." | |
| } | |
| catch { | |
| $msg = @( | |
| 'Failed to bootstrap the Windows App Runtime.' | |
| 'Ensure the Windows App SDK 1.8+ runtime is installed.' | |
| 'On Copilot+ PCs it should be pre-installed; otherwise install from:' | |
| 'https://learn.microsoft.com/windows/apps/windows-app-sdk/downloads' | |
| ) -join "`n" | |
| throw "$msg`n$_" | |
| } | |
| } | |
| # --------------------------------------------------------------------------- | |
| # Model readiness check | |
| # --------------------------------------------------------------------------- | |
| function Assert-ModelReady { | |
| [CmdletBinding()] | |
| param() | |
| try { | |
| $state = [Microsoft.Windows.AI.Text.LanguageModel]::GetReadyState() | |
| } | |
| catch { | |
| if ($_.Exception.InnerException -and | |
| $_.Exception.InnerException.HResult -eq [int]0x80040154) { | |
| throw @( | |
| 'The Phi Silica AI runtime is not registered on this system.' | |
| 'This requires a Copilot+ PC with Windows 11 24H2+ and an NPU.' | |
| 'Ensure the Windows AI feature is enabled in Settings > Windows Update > Advanced options.' | |
| ) -join "`n" | |
| } | |
| throw | |
| } | |
| Write-Verbose "Model readiness: $state" | |
| switch ($state.ToString()) { | |
| 'Ready' { return } | |
| 'NotReady' { | |
| Write-Host '[*] Phi Silica model is downloading — this may take several minutes...' -ForegroundColor Yellow | |
| [Microsoft.Windows.AI.Text.LanguageModel]::EnsureReadyAsync().GetAwaiter().GetResult() | |
| Write-Host '[+] Model download complete.' -ForegroundColor Green | |
| } | |
| 'NotSupportedOnCurrentSystem' { | |
| throw 'This device does not support Phi Silica. A Copilot+ PC with an NPU is required.' | |
| } | |
| 'DisabledByUser' { | |
| throw 'The Phi Silica model has been disabled or removed. Re-enable it in Settings > Windows Update.' | |
| } | |
| default { | |
| throw "Phi Silica is not available (state: $state)." | |
| } | |
| } | |
| } | |
| # --------------------------------------------------------------------------- | |
| # Session management | |
| # --------------------------------------------------------------------------- | |
| function New-AISession { | |
| [CmdletBinding()] | |
| [OutputType([hashtable])] | |
| param( | |
| [string]$SysPrompt, | |
| [double]$Temp, | |
| [double]$P, | |
| [int]$K | |
| ) | |
| $model = [Microsoft.Windows.AI.Text.LanguageModel]::CreateAsync().GetAwaiter().GetResult() | |
| $options = [Microsoft.Windows.AI.Text.LanguageModelOptions]::new() | |
| $options.Temperature = [float]$Temp | |
| if ($P -gt 0) { $options.TopP = [float]$P } | |
| if ($K -gt 0) { $options.TopK = [float]$K } | |
| $context = if ($SysPrompt) { | |
| $model.CreateContext($SysPrompt) | |
| } | |
| else { | |
| $model.CreateContext() | |
| } | |
| @{ | |
| Model = $model | |
| Options = $options | |
| Context = $context | |
| } | |
| } | |
| function Send-Prompt { | |
| [CmdletBinding()] | |
| [OutputType([string])] | |
| param( | |
| [hashtable]$Session, | |
| [string]$Text | |
| ) | |
| $result = $Session.Model.GenerateResponseAsync( | |
| $Session.Context, | |
| $Text, | |
| $Session.Options | |
| ).GetAwaiter().GetResult() | |
| $status = $result.Status.ToString() | |
| if ($status -ne 'Success') { | |
| $detail = if ($result.ExtendedError) { $result.ExtendedError.Message } else { $status } | |
| Write-Warning "Model response failed: $detail" | |
| return $null | |
| } | |
| $result.Text | |
| } | |
| # --------------------------------------------------------------------------- | |
| # Entry point | |
| # --------------------------------------------------------------------------- | |
| try { | |
| Install-SdkPackage | |
| Initialize-Runtime | |
| Assert-ModelReady | |
| $session = New-AISession -SysPrompt $SystemPrompt -Temp $Temperature -P $TopP -K $TopK | |
| Write-Host "[+] Phi Silica ready.`n" -ForegroundColor Green | |
| if ($Interactive) { | |
| Write-Host " Type 'exit' or 'quit' to end the session." -ForegroundColor DarkGray | |
| Write-Host " Conversation context is preserved between turns.`n" -ForegroundColor DarkGray | |
| while ($true) { | |
| Write-Host 'You> ' -NoNewline -ForegroundColor Yellow | |
| $userInput = Read-Host | |
| if ($userInput -in 'exit', 'quit') { break } | |
| if ([string]::IsNullOrWhiteSpace($userInput)) { continue } | |
| $response = Send-Prompt -Session $session -Text $userInput | |
| if ($response) { | |
| Write-Host "Copilot> " -NoNewline -ForegroundColor Cyan | |
| Write-Host "$response`n" | |
| } | |
| } | |
| Write-Host "`nSession ended." -ForegroundColor DarkGray | |
| } | |
| else { | |
| $response = Send-Prompt -Session $session -Text $Prompt | |
| if ($response) { Write-Output $response } | |
| } | |
| } | |
| finally { | |
| if ($session -and $session.Model) { | |
| $session.Model.Dispose() | |
| Write-Verbose 'Model disposed.' | |
| } | |
| try { | |
| [Microsoft.Windows.ApplicationModel.DynamicDependency.Bootstrap]::Shutdown() | |
| Write-Verbose 'Runtime shut down.' | |
| } | |
| catch {} | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment