Add LocalChatClientWithTools sample — on-device AI with Apple Intelligence tool calling#745
Add LocalChatClientWithTools sample — on-device AI with Apple Intelligence tool calling#745mattleibow wants to merge 19 commits intodotnet:mainfrom
Conversation
…th tools Add a new AI sample that converts the existing ChatClientWithTools (Azure OpenAI) to use on-device Apple Intelligence via Microsoft.Maui.Essentials.AI. All 5 tools are preserved (weather, calculator, file operations, system info, timer) with the weather tool rewritten to use the free open-meteo.com API instead of OpenWeatherMap. Key changes from ChatClientWithTools: - AppleIntelligenceChatClient replaces AzureOpenAIClient - UseFunctionInvocation() enables automatic tool dispatch - WeatherTool uses open-meteo.com (free, no API key) - iOS/macCatalyst only — other platforms throw PlatformNotSupportedException - No API keys or cloud services required Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Switch from GetResponseAsync to GetStreamingResponseAsync for progressive text display as tokens arrive - Add ObservableAIFunction wrapper that fires callbacks when tools are invoked, showing live status in the chat (🔧 Calling... → ✅ completed) - Tool-call indicators inserted before the streaming placeholder so the response text always stays at the bottom - Enhanced tool call UI with styled Border card in XAML Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Tool calls now render as small centered text with horizontal rules (--- 🔧 Calling Weather… ---) instead of full chat bubbles, keeping the conversation flow compact and uncluttered. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- prompts.png: Home screen with tool-category prompt buttons - weather.png: Weather tool call divider + streamed response - calculator.png: Calculator tool call + result - multi_tool.png: System info with 2 concurrent tool calls - README updated with 4-column screenshot table Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…elector - Refactor all 5 tools (Calculator, Weather, FileOperations, SystemInfo, Timer) from AIFunction subclasses to AIFunctionFactory.Create pattern - Add ToolJsonContext for AOT-compatible source-gen JSON serialization - Split ChatMessage into ChatMessageViewModel hierarchy: ChatMessageViewModel (base), TextMessageViewModel, ToolCallMessageViewModel - Add ChatTemplateSelector (DataTemplateSelector) for clean XAML templates - Redesign UI with avatar circles, polished chat bubbles, responsive FlexLayout landing page with 8 prompt cards (2-column on wide screens) - Format storage sizes as human-readable (GB/TB) instead of raw bytes - Fix unicode escaping in tool result JSON display - Add MauiDevFlow agent for debug UI automation - Add multi-tool prompt card demonstrating tool chaining - Update iOS screenshots Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Review fixes: - README: Update stale file references and API method names - CalculatorTool: Fix broken preprocessing (e→Math.E replaced all letters, ^→** invalid for DataTable), add proper math function evaluation (sqrt, sin, cos, tan), power operator, constants (pi, e), unicode symbol normalization (−, ⋅, ·, ², ³, √, π), natural language operator handling (divided by, times, plus, minus), and InvariantCulture throughout - TimerTool: Switch to ConcurrentDictionary for thread safety - WeatherTool: Remove raw exception text leak from user-facing output - ChatViewModel: Fix tool-call matching for duplicate tool names using IsCompleted flag instead of text prefix matching - GlobalXmlns: Remove dead Pages namespace registration - MainPage: Remove redundant OnTextChanged handler, extract all hardcoded colors to named resources in Colors.xaml - Colors.xaml: Add semantic color resources for chat UI - Fix low-contrast timestamp color (#AAA → #888888 for WCAG AA) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Implement multi-turn chat by maintaining conversation history (user messages, assistant text, tool calls, and tool results) and passing the full history to GetStreamingResponseAsync - Process FunctionCallContent and FunctionResultContent from the stream to build accurate history for follow-up turns - Clear conversation history alongside UI messages on chat reset - Remove stale NuGet feed section from README (packages now on nuget.org) - Fix timer description: 'console log' → 'DisplayAlertAsync dialog' Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Use $(MauiVersion) for Microsoft.Maui.Controls instead of hardcoded version to match CI build infrastructure (consistent with ChatClient sample) - Remove Redth.MauiDevFlow.Agent package, NU1605 suppression, and network.server entitlement (debug-only dev tooling, not for public sample) - Remove MauiDevFlow using/registration from MauiProgram.cs - Remove '(preview)' from .NET 10 SDK prerequisite in README Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Adds a new .NET MAUI sample app (LocalChatClientWithTools) that demonstrates fully on-device chat using Apple Intelligence via Microsoft.Extensions.AI, including function/tool calling and multi-turn conversation history.
Changes:
- Introduces a streaming chat UI + MVVM view models that maintain conversation history and surface tool-invocation progress.
- Adds a set of tools (weather, calculator, file listing, system info, timer) implemented via
AIFunctionFactory.Createwith AOT-friendly JSON source generation. - Adds platform/project scaffolding, assets, and documentation for iOS/macCatalyst-focused execution (with explicit unsupported-platform behavior).
Reviewed changes
Copilot reviewed 42 out of 53 changed files in this pull request and generated 13 comments.
Show a summary per file
| File | Description |
|---|---|
| 10.0/AI/LocalChatClientWithTools/src/ViewModels/ToolCallMessageViewModel.cs | Tool-call message VM (expandable raw JSON + completion state). |
| 10.0/AI/LocalChatClientWithTools/src/ViewModels/TextMessageViewModel.cs | Text message VM for user/assistant/error display. |
| 10.0/AI/LocalChatClientWithTools/src/ViewModels/ChatViewModel.cs | Core chat orchestration: streaming responses, tool wrapping, history tracking. |
| 10.0/AI/LocalChatClientWithTools/src/ViewModels/ChatMessageViewModel.cs | Base VM shared fields (user flag, timestamp). |
| 10.0/AI/LocalChatClientWithTools/src/Services/Tools/WeatherTool.cs | Weather tool (geocode + forecast via open-meteo). |
| 10.0/AI/LocalChatClientWithTools/src/Services/Tools/ToolJsonContext.cs | JSON source-gen context for tool schemas/results. |
| 10.0/AI/LocalChatClientWithTools/src/Services/Tools/TimerTool.cs | Timer tool that schedules a one-shot timer and alerts when done. |
| 10.0/AI/LocalChatClientWithTools/src/Services/Tools/SystemInfoTool.cs | Device/battery/storage info tool using MAUI Essentials + DriveInfo. |
| 10.0/AI/LocalChatClientWithTools/src/Services/Tools/FileOperationsTool.cs | File listing tool with common path resolution and entry limiting. |
| 10.0/AI/LocalChatClientWithTools/src/Services/Tools/CalculatorTool.cs | Calculator tool using preprocessing + DataTable.Compute. |
| 10.0/AI/LocalChatClientWithTools/src/Resources/Styles/Styles.xaml | UI styles for the sample (controls, layout defaults). |
| 10.0/AI/LocalChatClientWithTools/src/Resources/Styles/Colors.xaml | Color palette + chat-specific colors. |
| 10.0/AI/LocalChatClientWithTools/src/Resources/Splash/splash.svg | Splash asset for the sample. |
| 10.0/AI/LocalChatClientWithTools/src/Resources/Raw/AboutAssets.txt | Default MAUI raw-assets placeholder doc. |
| 10.0/AI/LocalChatClientWithTools/src/Resources/AppIcon/appiconfg.svg | App icon foreground. |
| 10.0/AI/LocalChatClientWithTools/src/Resources/AppIcon/appicon.svg | App icon background. |
| 10.0/AI/LocalChatClientWithTools/src/Properties/launchSettings.json | Launch profile for local debugging. |
| 10.0/AI/LocalChatClientWithTools/src/Platforms/iOS/Resources/PrivacyInfo.xcprivacy | iOS privacy manifest additions for accessed APIs. |
| 10.0/AI/LocalChatClientWithTools/src/Platforms/iOS/Program.cs | iOS entry point. |
| 10.0/AI/LocalChatClientWithTools/src/Platforms/iOS/Info.plist | iOS Info.plist configuration. |
| 10.0/AI/LocalChatClientWithTools/src/Platforms/iOS/AppDelegate.cs | iOS MAUI app delegate. |
| 10.0/AI/LocalChatClientWithTools/src/Platforms/Windows/app.manifest | Windows manifest (template scaffolding). |
| 10.0/AI/LocalChatClientWithTools/src/Platforms/Windows/Package.appxmanifest | Windows packaging manifest (template scaffolding). |
| 10.0/AI/LocalChatClientWithTools/src/Platforms/Windows/App.xaml.cs | Windows app host bootstrap. |
| 10.0/AI/LocalChatClientWithTools/src/Platforms/Windows/App.xaml | Windows XAML app host. |
| 10.0/AI/LocalChatClientWithTools/src/Platforms/MacCatalyst/Program.cs | macCatalyst entry point. |
| 10.0/AI/LocalChatClientWithTools/src/Platforms/MacCatalyst/Info.plist | macCatalyst Info.plist configuration. |
| 10.0/AI/LocalChatClientWithTools/src/Platforms/MacCatalyst/Entitlements.plist | macCatalyst entitlements (sandbox + network client). |
| 10.0/AI/LocalChatClientWithTools/src/Platforms/MacCatalyst/AppDelegate.cs | macCatalyst MAUI app delegate. |
| 10.0/AI/LocalChatClientWithTools/src/Platforms/Android/Resources/values/colors.xml | Android colors resource file. |
| 10.0/AI/LocalChatClientWithTools/src/Platforms/Android/MainApplication.cs | Android application host bootstrap. |
| 10.0/AI/LocalChatClientWithTools/src/Platforms/Android/MainActivity.cs | Android activity host bootstrap. |
| 10.0/AI/LocalChatClientWithTools/src/Platforms/Android/AndroidManifest.xml | Android manifest (includes internet permissions for weather). |
| 10.0/AI/LocalChatClientWithTools/src/MauiProgram.cs | DI setup: tools, chat client builder, platform gating. |
| 10.0/AI/LocalChatClientWithTools/src/MainPage.xaml.cs | Page code-behind wiring entry completion to Send command. |
| 10.0/AI/LocalChatClientWithTools/src/MainPage.xaml | Chat UI (empty prompts, message list, tool call indicator, input area). |
| 10.0/AI/LocalChatClientWithTools/src/LocalChatClientWithTools.sln | Sample solution file. |
| 10.0/AI/LocalChatClientWithTools/src/LocalChatClientWithTools.csproj | MAUI project targeting net10 TFMs + packages. |
| 10.0/AI/LocalChatClientWithTools/src/GlobalXmlns.cs | Global XAML xmlns mappings (implicit xmlns). |
| 10.0/AI/LocalChatClientWithTools/src/ChatTemplateSelector.cs | Template selector for text vs tool-call messages. |
| 10.0/AI/LocalChatClientWithTools/src/AppShell.xaml.cs | Shell code-behind. |
| 10.0/AI/LocalChatClientWithTools/src/AppShell.xaml | Shell layout + main route. |
| 10.0/AI/LocalChatClientWithTools/src/App.xaml.cs | App bootstrap + root window creation. |
| 10.0/AI/LocalChatClientWithTools/src/App.xaml | Resource dictionary merges (colors + styles). |
| 10.0/AI/LocalChatClientWithTools/images/calculator.png | Screenshot asset for README. |
| 10.0/AI/LocalChatClientWithTools/README.md | Sample documentation and usage instructions. |
10.0/AI/LocalChatClientWithTools/src/Services/Tools/FileOperationsTool.cs
Show resolved
Hide resolved
10.0/AI/LocalChatClientWithTools/src/ViewModels/ToolCallMessageViewModel.cs
Outdated
Show resolved
Hide resolved
10.0/AI/LocalChatClientWithTools/src/Services/Tools/CalculatorTool.cs
Outdated
Show resolved
Hide resolved
10.0/AI/LocalChatClientWithTools/src/ViewModels/ToolCallMessageViewModel.cs
Show resolved
Hide resolved
- Suppress CA2252 (AllowImplicitXmlnsDeclaration preview feature warning)
- Fix HasRawJson notification: add NotifyPropertyChangedFor so UI updates
when RawJson is set on tool call completion
- Guard ToggleExpanded to no-op when RawJson is not yet available
- Fix case-insensitive percentage detection ('15% Of 100' now works)
- Fix README: correct UI description and SystemInfoTool platform note
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace AddSingleton<HttpClient>() with AddHttpClient() in DI registration, and inject IHttpClientFactory into WeatherTool so each request gets a properly managed HttpClient instance with correct handler lifecycle. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Adds a new .NET MAUI sample app (“LocalChatClientWithTools”) demonstrating on-device Apple Intelligence chat via Microsoft.Extensions.AI with automatic tool/function invocation, streaming UI updates, and a set of built-in tools (weather, calculator, files, system info, timer).
Changes:
- Introduces the MAUI app shell/page/viewmodels for a chat UI with tool-call indicators and streaming responses.
- Adds tool implementations (weather, calculator, file listing, system info, timers) with source-generated JSON serialization for AOT compatibility.
- Adds platform/project scaffolding (TFMs, icons/splash, styles, manifests/plists) and documentation for running the sample.
Reviewed changes
Copilot reviewed 42 out of 53 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| 10.0/AI/LocalChatClientWithTools/src/ViewModels/ToolCallMessageViewModel.cs | ViewModel for tool-call status + expandable raw JSON payload. |
| 10.0/AI/LocalChatClientWithTools/src/ViewModels/TextMessageViewModel.cs | ViewModel for text chat messages (user/assistant/error). |
| 10.0/AI/LocalChatClientWithTools/src/ViewModels/ChatViewModel.cs | Core chat flow: history, streaming responses, tool observability wrappers. |
| 10.0/AI/LocalChatClientWithTools/src/ViewModels/ChatMessageViewModel.cs | Base message VM with user flag + timestamp. |
| 10.0/AI/LocalChatClientWithTools/src/Services/Tools/WeatherTool.cs | Weather tool using open-meteo geocoding + current forecast. |
| 10.0/AI/LocalChatClientWithTools/src/Services/Tools/ToolJsonContext.cs | Source-generated JSON context for tool schema/results. |
| 10.0/AI/LocalChatClientWithTools/src/Services/Tools/TimerTool.cs | Timer tool creating one-shot timers and alerting on completion. |
| 10.0/AI/LocalChatClientWithTools/src/Services/Tools/SystemInfoTool.cs | System info tool (battery/storage/device details). |
| 10.0/AI/LocalChatClientWithTools/src/Services/Tools/FileOperationsTool.cs | File listing tool with path resolution and entry limiting. |
| 10.0/AI/LocalChatClientWithTools/src/Services/Tools/CalculatorTool.cs | Calculator tool with expression preprocessing + evaluation. |
| 10.0/AI/LocalChatClientWithTools/src/Resources/Styles/Styles.xaml | App-wide control styles and visual states. |
| 10.0/AI/LocalChatClientWithTools/src/Resources/Styles/Colors.xaml | Color palette + chat UI color resources. |
| 10.0/AI/LocalChatClientWithTools/src/Resources/Splash/splash.svg | Splash asset. |
| 10.0/AI/LocalChatClientWithTools/src/Resources/Raw/AboutAssets.txt | Default MAUI raw assets guidance file. |
| 10.0/AI/LocalChatClientWithTools/src/Resources/AppIcon/appiconfg.svg | App icon foreground vector. |
| 10.0/AI/LocalChatClientWithTools/src/Resources/AppIcon/appicon.svg | App icon base/background vector. |
| 10.0/AI/LocalChatClientWithTools/src/Properties/launchSettings.json | Launch profile scaffolding. |
| 10.0/AI/LocalChatClientWithTools/src/Platforms/iOS/Resources/PrivacyInfo.xcprivacy | iOS privacy manifest for required accessed APIs. |
| 10.0/AI/LocalChatClientWithTools/src/Platforms/iOS/Program.cs | iOS entry point. |
| 10.0/AI/LocalChatClientWithTools/src/Platforms/iOS/Info.plist | iOS plist scaffold. |
| 10.0/AI/LocalChatClientWithTools/src/Platforms/iOS/AppDelegate.cs | iOS app delegate. |
| 10.0/AI/LocalChatClientWithTools/src/Platforms/Windows/app.manifest | Windows app manifest scaffold. |
| 10.0/AI/LocalChatClientWithTools/src/Platforms/Windows/Package.appxmanifest | Windows packaging manifest scaffold. |
| 10.0/AI/LocalChatClientWithTools/src/Platforms/Windows/App.xaml.cs | WinUI app bootstrap. |
| 10.0/AI/LocalChatClientWithTools/src/Platforms/Windows/App.xaml | WinUI XAML app definition. |
| 10.0/AI/LocalChatClientWithTools/src/Platforms/MacCatalyst/Program.cs | MacCatalyst entry point. |
| 10.0/AI/LocalChatClientWithTools/src/Platforms/MacCatalyst/Info.plist | MacCatalyst plist scaffold. |
| 10.0/AI/LocalChatClientWithTools/src/Platforms/MacCatalyst/Entitlements.plist | MacCatalyst entitlements (sandbox + network). |
| 10.0/AI/LocalChatClientWithTools/src/Platforms/MacCatalyst/AppDelegate.cs | MacCatalyst app delegate. |
| 10.0/AI/LocalChatClientWithTools/src/Platforms/Android/Resources/values/colors.xml | Android colors resource. |
| 10.0/AI/LocalChatClientWithTools/src/Platforms/Android/MainApplication.cs | Android application bootstrap. |
| 10.0/AI/LocalChatClientWithTools/src/Platforms/Android/MainActivity.cs | Android activity bootstrap. |
| 10.0/AI/LocalChatClientWithTools/src/Platforms/Android/AndroidManifest.xml | Android manifest + network permissions (for weather). |
| 10.0/AI/LocalChatClientWithTools/src/MauiProgram.cs | DI setup: chat client, tools, platform gating. |
| 10.0/AI/LocalChatClientWithTools/src/MainPage.xaml.cs | Main page code-behind (Entry Completed -> Send). |
| 10.0/AI/LocalChatClientWithTools/src/MainPage.xaml | Chat UI (EmptyView prompts, templates, tool-call expansion). |
| 10.0/AI/LocalChatClientWithTools/src/LocalChatClientWithTools.sln | New solution file for the sample. |
| 10.0/AI/LocalChatClientWithTools/src/LocalChatClientWithTools.csproj | New MAUI project file + package references. |
| 10.0/AI/LocalChatClientWithTools/src/GlobalXmlns.cs | Global XAML namespace mapping for implicit xmlns usage. |
| 10.0/AI/LocalChatClientWithTools/src/ChatTemplateSelector.cs | DataTemplateSelector for text vs tool-call messages. |
| 10.0/AI/LocalChatClientWithTools/src/AppShell.xaml.cs | Shell code-behind. |
| 10.0/AI/LocalChatClientWithTools/src/AppShell.xaml | Shell structure (routes MainPage). |
| 10.0/AI/LocalChatClientWithTools/src/App.xaml.cs | App bootstrap and window creation. |
| 10.0/AI/LocalChatClientWithTools/src/App.xaml | Resource dictionary merge (colors + styles). |
| 10.0/AI/LocalChatClientWithTools/images/calculator.png | Screenshot asset used in README. |
| 10.0/AI/LocalChatClientWithTools/README.md | Sample documentation (overview, tools, run instructions). |
10.0/AI/LocalChatClientWithTools/src/Services/Tools/FileOperationsTool.cs
Show resolved
Hide resolved
10.0/AI/LocalChatClientWithTools/src/Platforms/Windows/app.manifest
Outdated
Show resolved
Hide resolved
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
🔍 Multi-Model Code Review — PR #745 (LocalChatClientWithTools)CI Status: ✅ All checks passing Consensus Findings (flagged by 2+ of 5 models)🔴 CRITICAL —
|
| Issue | Notes |
|---|---|
textBuilder not cleared after FunctionCallContent — streamed text after a tool call is prefixed with pre-tool text |
Real functional bug worth fixing |
OperationCanceledException swallowed in WeatherTool.GetWeatherAsync — cancellation silently returns a fake result |
Should re-throw |
JsonDocument not disposed in BuildRawJson — ArrayPool memory not returned on large payloads |
Use using var |
WeatherTool URL uses culture-sensitive double formatting ({latitude:F4}) — commas in locale-specific cultures break the API URL |
Use CultureInfo.InvariantCulture |
Recommended Action: ⚠️ Request Changes
- Guard
ClearChatwithif (IsProcessing) return; - Sandbox paths in
ResolveCommonPathto allowed directories (or document that arbitrary paths are intentional) - Fix
EvaluatePowerOperatorto handle parenthesized bases, or update the[Description]to not advertise2^3syntax - Clear
textBuilderwhen aFunctionCallContentis received to prevent cross-tool text bleed
Review generated by multi-model consensus (2× Claude Opus 4.6, Claude Sonnet 4.6, Gemini 3 Pro, GPT-5.3-Codex)
|
Reviewed by Claude Opus 4.6, Gemini 3 Pro, and GPT-5.2 Codex. Only substantive bugs, security issues, and logic errors are reported below. File:
Suggested fix: Add File: The Suggested fix: Either remove the unsupported TFMs from the File: more text, all text is merged into a single bubble. Combined with Suggested fix: When a File: The manual JSON construction only escapes result = $"{{\"error\": \"{ex.GetType().Name}: {ex.Message.Replace("\"", "\\\"").Replace("\n", " ")}\"}}";Backslashes, Suggested fix: Use Summary
|
What this sample shows
A complete .NET MAUI chat app that runs entirely on-device using Apple Intelligence — no API keys, no cloud services, no cost. It demonstrates how to give an on-device LLM real capabilities through function calling (tools) and multi-turn conversation history using the standard
IChatClientinterface fromMicrosoft.Extensions.AI.Screenshots (iOS)
What Apple Intelligence can do with tools
The AI can autonomously decide which tool(s) to call based on the user's natural language input:
get_weather, geocodes the city, fetches live forecast from open-meteo.comcalculatewith the right math expressionlist_fileswith the resolved pathget_system_infoand reports battery, memory, storageset_timerwith duration and titleget_system_info+calculateautomaticallyHow the sample is built
MauiProgram.csAppleIntelligenceChatClientas anIChatClientwithUseFunctionInvocation()for automatic tool dispatchServices/Tools/*.csAIFunctionFactory.Createwith strongly-typed methods,[Description]attributes for the AI to understand parameters, and AOT-compatible JSON serializationChatViewModel.csChatOptions.Tools, stream responses withGetStreamingResponseAsync, and observe tool invocations in real-timeMainPage.xamlDataTemplateSelectorfor different message types, responsive FlexLayout landing page, and tool-call indicators with expandable raw JSONKey design decisions
IChatClientabstraction — The sample uses the standardMicrosoft.Extensions.AIinterface, so swapping Apple Intelligence for OpenAI/Ollama/etc. is a one-line DI changeAIFunctionFactory.Createpattern — Tools are plain C# methods with[Description]attributes, not custom base classes. This is the recommended approach for .NET AI toolsFunctionCallContentandFunctionResultContent) is accumulated and sent with each request, enabling follow-up questions like "double that" or "what about Tokyo?"JsonSerializerContextviaToolJsonContextPlatform support
PlatformNotSupportedExceptionat startup with a clear messageHow to run
Requires .NET 10 SDK.
Relationship to existing samples
This is the on-device companion to the existing
ChatClientWithToolssample (which uses Azure OpenAI). Same tool set, same UI patterns, but zero cloud dependencies.