diff --git a/build.psm1 b/build.psm1
index b521571..f84f5de 100644
--- a/build.psm1
+++ b/build.psm1
@@ -6,6 +6,7 @@
$metadata = Get-Content $PSScriptRoot/tools/metadata.json | ConvertFrom-Json
$dotnetSDKVersion = $(Get-Content $PSScriptRoot/global.json | ConvertFrom-Json).Sdk.Version
$dotnetLocalDir = if ($IsWindows) { "$env:LocalAppData\Microsoft\dotnet" } else { "$env:HOME/.dotnet" }
+$windowsOnlyAgents = @('phisilica')
function Start-Build
{
@@ -20,7 +21,7 @@ function Start-Build
[string] $Runtime = [NullString]::Value,
[Parameter()]
- [ValidateSet('openai-gpt', 'msaz', 'interpreter', 'ollama')]
+ [ValidateSet('openai-gpt', 'msaz', 'interpreter', 'ollama', 'phisilica')]
[string[]] $AgentToInclude,
[Parameter()]
@@ -43,13 +44,18 @@ function Start-Build
$MyInvocation.MyCommand.Parameters["AgentToInclude"].Attributes |
Where-Object { $_ -is [ValidateSet] } |
Select-Object -First 1 |
- ForEach-Object ValidValues
+ ForEach-Object ValidValues |
+ Skip-Unapplicable
} else {
$agents.Split(",", [System.StringSplitOptions]::TrimEntries)
Write-Verbose "Include agents specified in Metadata.json"
}
}
+ if (HasUnapplicableAgent $AgentToInclude) {
+ throw "The following specified agent(s) cannot be built on the current platform: $($windowsOnlyAgents -join ', ')."
+ }
+
$RID = $Runtime ?? (dotnet --info |
Select-String '^\s*RID:\s+(\w+-\w+)$' |
Select-Object -First 1 |
@@ -69,6 +75,7 @@ function Start-Build
$msaz_dir = Join-Path $agent_dir "Microsoft.Azure.Agent"
$interpreter_agent_dir = Join-Path $agent_dir "AIShell.Interpreter.Agent"
$ollama_agent_dir = Join-Path $agent_dir "AIShell.Ollama.Agent"
+ $phisilica_agent_dir = Join-Path $agent_dir "AIShell.PhiSilica.Agent"
$config = $Configuration.ToLower()
$out_dir = Join-Path $PSScriptRoot "out"
@@ -79,6 +86,7 @@ function Start-Build
$msaz_out_dir = Join-Path $app_out_dir "agents" "Microsoft.Azure.Agent"
$interpreter_out_dir = Join-Path $app_out_dir "agents" "AIShell.Interpreter.Agent"
$ollama_out_dir = Join-Path $app_out_dir "agents" "AIShell.Ollama.Agent"
+ $phisilica_out_dir = Join-Path $app_out_dir "agents" "AIShell.PhiSilica.Agent"
if ($Clean) {
if (Test-Path $out_dir) {
@@ -152,6 +160,12 @@ function Start-Build
dotnet publish $ollama_csproj -c $Configuration -o $ollama_out_dir
}
+ if ($LASTEXITCODE -eq 0 -and $AgentToInclude -contains 'phisilica') {
+ Write-Host "`n[Build the PhiSilica agent ...]`n" -ForegroundColor Green
+ $phisilica_csproj = GetProjectFile $phisilica_agent_dir
+ dotnet publish $phisilica_csproj -c $Configuration -o $phisilica_out_dir
+ }
+
if ($LASTEXITCODE -eq 0 -and -not $NotIncludeModule) {
Write-Host "`n[Build the AIShell module ...]`n" -ForegroundColor Green
$aish_module_csproj = GetProjectFile $module_dir
@@ -175,6 +189,26 @@ function Start-Build
}
}
+filter Skip-Unapplicable {
+ if ($IsWindows -or $windowsOnlyAgents -notcontains $_) {
+ $_
+ }
+}
+
+function HasUnapplicableAgent($specifiedAgents) {
+ if ($IsWindows) {
+ return $false
+ }
+
+ foreach ($agent in $windowsOnlyAgents) {
+ if ($specifiedAgents -contains $agent) {
+ return $true
+ }
+ }
+
+ return $false
+}
+
function GetProjectFile($dir)
{
return Get-Item "$dir/*.csproj" | ForEach-Object FullName
diff --git a/shell/agents/AIShell.PhiSilica.Agent/AIShell.PhiSilica.Agent.csproj b/shell/agents/AIShell.PhiSilica.Agent/AIShell.PhiSilica.Agent.csproj
new file mode 100644
index 0000000..abc3f55
--- /dev/null
+++ b/shell/agents/AIShell.PhiSilica.Agent/AIShell.PhiSilica.Agent.csproj
@@ -0,0 +1,29 @@
+
+
+ Library
+ net8.0-windows10.0.26100.0
+ enable
+ CS8305
+ AnyCPU
+ None
+ true
+ true
+ true
+ win-arm64
+ true
+
+
+
+
+
+ false
+
+ runtime
+
+
+
+
+
+
+
+
diff --git a/shell/agents/AIShell.PhiSilica.Agent/PhiSilicaAgent.cs b/shell/agents/AIShell.PhiSilica.Agent/PhiSilicaAgent.cs
new file mode 100644
index 0000000..f10a415
--- /dev/null
+++ b/shell/agents/AIShell.PhiSilica.Agent/PhiSilicaAgent.cs
@@ -0,0 +1,97 @@
+using AIShell.Abstraction;
+using Microsoft.Windows.AI;
+using Microsoft.Windows.AI.Generative;
+
+namespace AIShell.PhiSilica.Agent;
+
+public sealed partial class PhiSilicaAgent : ILLMAgent
+{
+ private readonly Task _initTask;
+ private LanguageModel _model;
+
+ public string Name => "PhiSilica";
+ public string Description => "This is the AI Shell Agent for talking to the inbox Phi Silica model on Copilot+ PCs.";
+ public string SettingFile => null;
+
+ public IEnumerable GetCommands() => null;
+ public bool CanAcceptFeedback(UserAction action) => false;
+ public Task RefreshChatAsync(IShell shell, bool force) => Task.CompletedTask;
+ public void OnUserAction(UserActionPayload actionPayload) { }
+ public void Initialize(AgentConfig config) { }
+ public void Dispose() { }
+
+ public PhiSilicaAgent()
+ {
+ // Start the initialization for AI feature and model on a background thread.
+ _initTask = Task.Run(InitFeatureAndModelAsync);
+ }
+
+ private async Task InitFeatureAndModelAsync()
+ {
+ AIFeatureReadyState state = LanguageModel.GetReadyState();
+ if (state is AIFeatureReadyState.NotSupportedOnCurrentSystem)
+ {
+ throw new PlatformNotSupportedException("The Phi Silica feature is not supported on current system.");
+ }
+
+ if (state is AIFeatureReadyState.DisabledByUser)
+ {
+ throw new PlatformNotSupportedException("The Phi Silica feature is currently disabled.");
+ }
+
+ if (state is AIFeatureReadyState.EnsureNeeded)
+ {
+ // Initialize the WinRT runtime.
+ AIFeatureReadyResult result = await LanguageModel.EnsureReadyAsync();
+ // Do not proceed if it failed to get the feature ready.
+ if (result.Status is not AIFeatureReadyResultState.Success)
+ {
+ throw new InvalidOperationException(result.ErrorDisplayText, result.Error);
+ }
+ }
+
+ _model = await LanguageModel.CreateAsync();
+ }
+
+ public async Task ChatAsync(string input, IShell shell)
+ {
+ IHost host = shell.Host;
+
+ try
+ {
+ // Wait for the init task to finish. Once it's finished, calling this again is a non-op.
+ await _initTask;
+ }
+ catch (Exception e)
+ {
+ host.WriteErrorLine(e.Message);
+ if (e is InvalidOperationException && e.InnerException is not null)
+ {
+ host.WriteErrorLine(e.InnerException.StackTrace);
+ }
+ else if (e is not PlatformNotSupportedException)
+ {
+ // Show stack trace for non-PNS exception.
+ host.WriteErrorLine(e.StackTrace);
+ }
+
+ return false;
+ }
+
+ var result = await host.RunWithSpinnerAsync(
+ status: "Thinking ...",
+ func: async () => await _model.GenerateResponseAsync(input)
+ );
+
+ if (result is not null && !string.IsNullOrEmpty(result.Text))
+ {
+ host.RenderFullResponse(result.Text);
+ }
+ else
+ {
+ host.WriteErrorLine("No response received from the language model.");
+ }
+
+ return true;
+ }
+}