-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Add LocalChatClientWithTools sample — on-device AI with Apple Intelligence tool calling #745
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
mattleibow
wants to merge
19
commits into
dotnet:main
Choose a base branch
from
mattleibow:ai/local-chat-client-with-tools
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
19 commits
Select commit
Hold shift + click to select a range
bc6e38b
Add LocalChatClientWithTools sample — on-device Apple Intelligence wi…
mattleibow 7cc87a0
Add streaming responses and real-time tool call visibility
mattleibow b865cc5
Use inline divider for tool call indicators instead of speech bubbles
mattleibow f7e84a1
Replace screenshots with 4 actual app captures
mattleibow e9cd0eb
Retake calculator and multi-tool screenshots (remove overlay)
mattleibow db8b4fb
Remove NuGet.config — no longer needed for preview feed
mattleibow ac4d1be
Pin Microsoft.Maui.Essentials.AI to 10.0.50-preview.1.26161.6
mattleibow bf36bd0
Pin Microsoft.Maui.Essentials.AI to 10.0.50-preview.1.26158.1
mattleibow 6a84d2a
Refactor tools to AIFunctionFactory, redesign chat UI with template s…
mattleibow c4b5dc3
Fix code review issues and improve CalculatorTool robustness
mattleibow fb127e5
Add multi-turn conversation history and fix README
mattleibow 56d5822
Fix CI build: use $(MauiVersion), remove MauiDevFlow references
mattleibow 15bf43a
Delete 10.0/AI/LocalChatClientWithTools/src/Resources/Images/dotnet_b…
mattleibow e3721e8
Remove unused dotnet_bot.png template asset
mattleibow 4935b6f
Update LocalChatClientWithTools.sln
mattleibow 4a2a283
Fix CI CA2252 error, address PR review comments
mattleibow 07b46e9
Use IHttpClientFactory instead of singleton HttpClient in WeatherTool
mattleibow 84bc243
Fix Windows app.manifest assemblyIdentity to match project name
mattleibow 2affe63
Merge branch 'main' into ai/local-chat-client-with-tools
mattleibow File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,127 @@ | ||
| --- | ||
| name: .NET MAUI - Local Chat Client with Tools | ||
| description: Chat UI demonstrating on-device Apple Intelligence function calling (tools) with weather, calculator, file listing, system info, and timers — no API keys or cloud services required. | ||
| page_type: sample | ||
| languages: | ||
| - csharp | ||
| - xaml | ||
| products: | ||
| - dotnet-maui | ||
| urlFragment: local-chat-client-with-tools | ||
| --- | ||
|
|
||
| # LocalChatClientWithTools (MAUI + On-Device Apple Intelligence Tools) | ||
|
|
||
| A .NET MAUI sample showing how to enhance an on-device LLM with `Microsoft.Extensions.AI` function calling (tools) using Apple Intelligence. All inference runs locally — **no API keys, no cloud services, no cost**. | ||
|
|
||
| | Prompt buttons | Weather tool | Calculator tool | Multi-tool calls | | ||
| |:-:|:-:|:-:|:-:| | ||
| |  |  |  |  | | ||
|
|
||
| ## What you'll learn | ||
|
|
||
| - Using `AppleIntelligenceChatClient` from `Microsoft.Maui.Essentials.AI` as a local `IChatClient` | ||
| - Supplying a set of `AIFunction` tools to `IChatClient` via `ChatOptions.Tools` | ||
| - Implementing strongly described tool schemas (JSON schema) for better argument selection | ||
| - Resolving user-friendly locations to coordinates with the free [open-meteo.com](https://open-meteo.com) API | ||
| - Building a MAUI chat UI with template selectors for different message types (text, tool calls) | ||
| - Running AI inference entirely on-device with no external API keys | ||
|
|
||
| ## Prerequisites | ||
|
|
||
| - .NET 10 SDK | ||
| - iOS 26.0+ or macCatalyst 26.0+ device/simulator with Apple Intelligence enabled | ||
| - No API keys or cloud accounts needed | ||
|
|
||
| ## Tools implemented | ||
|
|
||
| | Tool | Name | Purpose | Notes | | ||
| |------|------|---------|-------| | ||
| | WeatherTool | `get_weather` | Current conditions for a location | Geocodes via open-meteo.com, then fetches forecast — free, no API key | | ||
| | CalculatorTool | `calculate` | Evaluate arithmetic / percentages | Sanitizes expression & returns formatted result | | ||
| | FileOperationsTool | `list_files` | List files & folders in a common or given path | Limits count; resolves shortcuts (Documents, Desktop, Downloads) | | ||
| | SystemInfoTool | `get_system_info` | Battery, storage, memory, device info | Returns live device values on Apple platforms | | ||
| | TimerTool | `set_timer` | Create a one‑shot timer with title | Keeps in-memory timers; shows alert on completion | | ||
|
|
||
mattleibow marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| Each tool is created via `AIFunctionFactory.Create` using strongly-typed methods and AOT-compatible JSON serialization via `ToolJsonContext`. | ||
|
|
||
| ## Key files | ||
|
|
||
| - `MauiProgram.cs` – Registers `IChatClient` via `AppleIntelligenceChatClient` with `UseFunctionInvocation()` for automatic tool dispatch, and registers all tool functions. | ||
| - `Services/Tools/*.cs` – Tool implementations using `AIFunctionFactory.Create` for clean, factory-based tool registration. | ||
| - `ViewModels/ChatViewModel.cs` – Collects tools and invokes `_chatClient.GetStreamingResponseAsync` passing `ChatOptions.Tools`. | ||
| - `ViewModels/ChatMessageViewModel.cs` – Base view model for chat messages, with `TextMessageViewModel` and `ToolCallMessageViewModel` subclasses. | ||
| - `MainPage.xaml` – Chat layout with `ChatTemplateSelector`, EmptyView sample prompts, and a `Clear` toolbar item (resets to EmptyView). | ||
|
|
||
| ## Sample prompts | ||
|
|
||
| Try: | ||
|
|
||
| - "What's the weather in Seattle?" | ||
| - "Calculate 15% tip on $47.50" | ||
| - "List the files in my Documents folder" | ||
| - "Show me current battery level and available storage" | ||
| - "Set a 5-minute timer for my coffee break" | ||
| - "What's 25 × 18 + 150?" | ||
| - "Check the weather in Tokyo and Cape Town" | ||
|
|
||
| ## Run | ||
|
|
||
| ```bash | ||
| # iOS simulator | ||
| dotnet build -f net10.0-ios -t:Run | ||
|
|
||
| # macCatalyst | ||
| dotnet build -f net10.0-maccatalyst -t:Run | ||
| ``` | ||
|
|
||
| Use a sample prompt or type your own. Use the toolbar **Clear** to reset and revisit the instructional prompt list. | ||
|
|
||
| > **Note:** This sample only runs on Apple platforms (iOS / macCatalyst) with Apple Intelligence. Other platforms will throw `PlatformNotSupportedException` at startup. | ||
|
|
||
| ## Architecture highlights | ||
|
|
||
| ### Tool invocation flow | ||
|
|
||
| 1. User sends natural language input. | ||
| 2. `ChatViewModel` builds `ChatOptions` with the registered tool objects. | ||
| 3. `AppleIntelligenceChatClient` (wrapped with `UseFunctionInvocation()`) decides if any tool(s) should be invoked and executes them, passing JSON arguments that conform to each tool's declared schema. | ||
| 4. Tool result objects surface in the assistant response and are bound to specialized UI cards. | ||
| 5. UI shows structured cards or plain text depending on `ChatMessage` flags. | ||
|
|
||
| ### On-device vs. cloud | ||
|
|
||
| This sample is a local-only variant of the `ChatClientWithTools` sample. The key differences: | ||
|
|
||
| | | ChatClientWithTools | LocalChatClientWithTools | | ||
| |---|---|---| | ||
| | **LLM provider** | Azure OpenAI (cloud) | Apple Intelligence (on-device) | | ||
| | **API keys** | Required (Azure + optional OpenWeatherMap) | None | | ||
| | **Weather API** | OpenWeatherMap (key required) | open-meteo.com (free, keyless) | | ||
| | **Platforms** | Windows, iOS, macCatalyst, Android | iOS, macCatalyst only | | ||
| | **NuGet packages** | Azure.AI.OpenAI, Microsoft.Extensions.AI.OpenAI | Microsoft.Maui.Essentials.AI | | ||
|
|
||
| ### UI features | ||
|
|
||
| - EmptyView with categorized starter buttons (Weather / Calculator / Files / System Info / Timers). | ||
| - Rich result cards styled with color-coded borders. | ||
| - Toolbar "Clear" to wipe conversation state quickly (teaching scenario friendly). | ||
| - Auto-scroll to the latest message. | ||
|
|
||
| ## Useful references | ||
|
|
||
| - Microsoft.Extensions.AI overview: <https://learn.microsoft.com/dotnet/ai/microsoft-extensions-ai> | ||
| - Apple Intelligence in .NET MAUI: <https://learn.microsoft.com/dotnet/maui/platform-integration/communication/ai> | ||
| - CommunityToolkit.Mvvm: <https://learn.microsoft.com/dotnet/communitytoolkit/mvvm/> | ||
|
|
||
| ## Notes | ||
|
|
||
| - Requires iOS 26.0+ or macCatalyst 26.0+ with Apple Intelligence available and enabled. | ||
| - All AI inference runs on-device — no data leaves the device for LLM processing. | ||
| - Weather data comes from [open-meteo.com](https://open-meteo.com) (free, open-source weather API). | ||
| - Timers are in-memory only (lost on app exit) and surface completion via a `DisplayAlertAsync` dialog. | ||
| - File operations are intentionally conservative; expand with care for security. | ||
| - This sample emphasizes pedagogy over exhaustive production hardening. | ||
|
|
||
| --- | ||
| For the cloud-hosted version of this sample, see `ChatClientWithTools` in the same folder tree. | ||
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| <?xml version = "1.0" encoding = "UTF-8" ?> | ||
| <Application xmlns="http://schemas.microsoft.com/dotnet/2021/maui" | ||
| xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" | ||
| xmlns:local="clr-namespace:LocalChatClientWithTools" | ||
| x:Class="LocalChatClientWithTools.App"> | ||
| <Application.Resources> | ||
| <ResourceDictionary> | ||
| <ResourceDictionary.MergedDictionaries> | ||
| <ResourceDictionary Source="Resources/Styles/Colors.xaml" /> | ||
| <ResourceDictionary Source="Resources/Styles/Styles.xaml" /> | ||
| </ResourceDictionary.MergedDictionaries> | ||
| </ResourceDictionary> | ||
| </Application.Resources> | ||
| </Application> |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| namespace LocalChatClientWithTools; | ||
|
|
||
| public partial class App : Application | ||
| { | ||
| public App() | ||
| { | ||
| InitializeComponent(); | ||
| } | ||
|
|
||
| protected override Window CreateWindow(IActivationState? activationState) | ||
| { | ||
| return new Window(new AppShell()); | ||
| } | ||
| } |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| <?xml version="1.0" encoding="UTF-8" ?> | ||
| <Shell | ||
| x:Class="LocalChatClientWithTools.AppShell" | ||
| xmlns="http://schemas.microsoft.com/dotnet/2021/maui" | ||
| xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" | ||
| xmlns:local="clr-namespace:LocalChatClientWithTools" | ||
| Title="LocalChatClientWithTools"> | ||
|
|
||
| <ShellContent | ||
| Title="Home" | ||
| ContentTemplate="{DataTemplate local:MainPage}" | ||
| Route="MainPage" /> | ||
|
|
||
| </Shell> |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| namespace LocalChatClientWithTools; | ||
|
|
||
| public partial class AppShell : Shell | ||
| { | ||
| public AppShell() | ||
| { | ||
| InitializeComponent(); | ||
| } | ||
| } |
16 changes: 16 additions & 0 deletions
16
10.0/AI/LocalChatClientWithTools/src/ChatTemplateSelector.cs
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| using LocalChatClientWithTools.ViewModels; | ||
|
|
||
| namespace LocalChatClientWithTools; | ||
|
|
||
| public class ChatTemplateSelector : DataTemplateSelector | ||
| { | ||
| public DataTemplate? TextTemplate { get; set; } | ||
| public DataTemplate? ToolCallTemplate { get; set; } | ||
|
|
||
| protected override DataTemplate OnSelectTemplate(object item, BindableObject container) => item switch | ||
| { | ||
| ToolCallMessageViewModel => ToolCallTemplate!, | ||
| TextMessageViewModel => TextTemplate!, | ||
| _ => TextTemplate! | ||
| }; | ||
| } |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| [assembly: XmlnsDefinition("http://schemas.microsoft.com/dotnet/maui/global", "LocalChatClientWithTools")] | ||
| [assembly: XmlnsDefinition("http://schemas.microsoft.com/dotnet/maui/global", "LocalChatClientWithTools.ViewModels")] | ||
| [assembly: XmlnsDefinition("http://schemas.microsoft.com/dotnet/maui/global", "Syncfusion.Maui.Toolkit.TextInputLayout", AssemblyName = "Syncfusion.Maui.Toolkit")] | ||
mattleibow marked this conversation as resolved.
Show resolved
Hide resolved
|
||
74 changes: 74 additions & 0 deletions
74
10.0/AI/LocalChatClientWithTools/src/LocalChatClientWithTools.csproj
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,74 @@ | ||
| <Project Sdk="Microsoft.NET.Sdk"> | ||
|
|
||
| <PropertyGroup> | ||
| <TargetFrameworks>net10.0-android;net10.0-ios;net10.0-maccatalyst</TargetFrameworks> | ||
| <TargetFrameworks Condition="$([MSBuild]::IsOSPlatform('windows'))">$(TargetFrameworks);net10.0-windows10.0.19041.0</TargetFrameworks> | ||
|
|
||
| <!-- Note for MacCatalyst: | ||
| The default runtime is maccatalyst-x64, except in Release config, in which case the default is maccatalyst-x64;maccatalyst-arm64. | ||
| When specifying both architectures, use the plural <RuntimeIdentifiers> instead of the singular <RuntimeIdentifier>. | ||
| The Mac App Store will NOT accept apps with ONLY maccatalyst-arm64 indicated; | ||
| either BOTH runtimes must be indicated or ONLY macatalyst-x64. --> | ||
| <!-- For example: <RuntimeIdentifiers>maccatalyst-x64;maccatalyst-arm64</RuntimeIdentifiers> --> | ||
|
|
||
| <OutputType>Exe</OutputType> | ||
| <RootNamespace>LocalChatClientWithTools</RootNamespace> | ||
| <UseMaui>true</UseMaui> | ||
| <SingleProject>true</SingleProject> | ||
| <ImplicitUsings>enable</ImplicitUsings> | ||
| <Nullable>enable</Nullable> | ||
|
|
||
| <NoWarn>$(NoWarn);MAUIAI0001;CA2252</NoWarn> | ||
|
|
||
| <!-- Enable implicit + global XAML xmlns for .NET 10 previews --> | ||
| <DefineConstants>$(DefineConstants);MauiAllowImplicitXmlnsDeclaration</DefineConstants> | ||
| <EnablePreviewFeatures>true</EnablePreviewFeatures> | ||
|
|
||
| <!-- Display name --> | ||
| <ApplicationTitle>LocalChatClientWithTools</ApplicationTitle> | ||
|
|
||
| <!-- App Identifier --> | ||
| <ApplicationId>com.companyname.localchatclientwithtools</ApplicationId> | ||
|
|
||
| <!-- Versions --> | ||
| <ApplicationDisplayVersion>1.0</ApplicationDisplayVersion> | ||
| <ApplicationVersion>1</ApplicationVersion> | ||
|
|
||
| <!-- To develop, package, and publish an app to the Microsoft Store, see: https://aka.ms/MauiTemplateUnpackaged --> | ||
| <WindowsPackageType>None</WindowsPackageType> | ||
|
|
||
| <SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios'">26.0</SupportedOSPlatformVersion> | ||
| <SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'maccatalyst'">26.0</SupportedOSPlatformVersion> | ||
| <SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'android'">21.0</SupportedOSPlatformVersion> | ||
| <SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'windows'">10.0.17763.0</SupportedOSPlatformVersion> | ||
| <TargetPlatformMinVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'windows'">10.0.17763.0</TargetPlatformMinVersion> | ||
| </PropertyGroup> | ||
|
|
||
| <ItemGroup> | ||
| <!-- App Icon --> | ||
| <MauiIcon Include="Resources\AppIcon\appicon.svg" ForegroundFile="Resources\AppIcon\appiconfg.svg" Color="#512BD4" /> | ||
|
|
||
| <!-- Splash Screen --> | ||
| <MauiSplashScreen Include="Resources\Splash\splash.svg" Color="#512BD4" BaseSize="128,128" /> | ||
|
|
||
| <!-- Images --> | ||
| <MauiImage Include="Resources\Images\*" /> | ||
|
|
||
| <!-- Custom Fonts --> | ||
| <MauiFont Include="Resources\Fonts\*" /> | ||
|
|
||
| <!-- Raw Assets (also remove the "Resources\Raw" prefix) --> | ||
| <MauiAsset Include="Resources\Raw\**" LogicalName="%(RecursiveDir)%(Filename)%(Extension)" /> | ||
| </ItemGroup> | ||
|
|
||
| <ItemGroup> | ||
| <PackageReference Include="Microsoft.Extensions.AI" Version="10.3.0" /> | ||
| <PackageReference Include="Microsoft.Maui.Essentials.AI" Version="10.0.50-preview.1.26158.1" /> | ||
| <PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" /> | ||
| <PackageReference Include="Microsoft.Maui.Controls" Version="$(MauiVersion)" /> | ||
| <PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="10.0.0" /> | ||
| <PackageReference Include="Syncfusion.Maui.Toolkit" Version="1.0.6" /> | ||
| <PackageReference Include="Microsoft.Extensions.Http" Version="10.0.0" /> | ||
| </ItemGroup> | ||
|
|
||
| </Project> |
22 changes: 22 additions & 0 deletions
22
10.0/AI/LocalChatClientWithTools/src/LocalChatClientWithTools.sln
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| | ||
| Microsoft Visual Studio Solution File, Format Version 12.00 | ||
| # Visual Studio Version 17 | ||
| VisualStudioVersion = 17.0.31903.59 | ||
| MinimumVisualStudioVersion = 10.0.40219.1 | ||
| Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LocalChatClientWithTools", "LocalChatClientWithTools.csproj", "{9FB143A1-5BC5-40A3-B332-BA816CF1CE04}" | ||
| EndProject | ||
| Global | ||
| GlobalSection(SolutionConfigurationPlatforms) = preSolution | ||
| Debug|Any CPU = Debug|Any CPU | ||
| Release|Any CPU = Release|Any CPU | ||
| EndGlobalSection | ||
| GlobalSection(ProjectConfigurationPlatforms) = postSolution | ||
| {9FB143A1-5BC5-40A3-B332-BA816CF1CE04}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||
| {9FB143A1-5BC5-40A3-B332-BA816CF1CE04}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||
| {9FB143A1-5BC5-40A3-B332-BA816CF1CE04}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||
| {9FB143A1-5BC5-40A3-B332-BA816CF1CE04}.Release|Any CPU.Build.0 = Release|Any CPU | ||
| EndGlobalSection | ||
| GlobalSection(SolutionProperties) = preSolution | ||
| HideSolutionNode = FALSE | ||
| EndGlobalSection | ||
| EndGlobal |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.