Skip to content

Add LocalChatClientWithAgents sample (Apple Intelligence + Agent Framework)#744

Open
mattleibow wants to merge 16 commits intodotnet:mainfrom
mattleibow:ai/local-chat-client-with-agents
Open

Add LocalChatClientWithAgents sample (Apple Intelligence + Agent Framework)#744
mattleibow wants to merge 16 commits intodotnet:mainfrom
mattleibow:ai/local-chat-client-with-agents

Conversation

@mattleibow
Copy link
Member

@mattleibow mattleibow commented Mar 10, 2026

Summary

Adds a new AI sample under 10.0/AI/LocalChatClientWithAgents/ — a .NET MAUI Travel Planner app powered entirely by on-device Apple Intelligence. No cloud API keys or endpoints required.

Landmarks Trip Planning Itinerary Chat
landmarks trip planning itinerary chat

What it demonstrates

  • Microsoft.Extensions.AI (IChatClient) backed by Apple Intelligence on iOS/macCatalyst
  • Microsoft Agent Framework (Microsoft.Agents.AI) with a 4-agent pipeline:
    • Travel Planner → Researcher (RAG) → Itinerary Planner (tools + streaming) → Translator (conditional)
  • On-device RAG using NLEmbeddingGenerator for semantic landmark and continent search
  • Streaming JSON deserialization for progressive UI updates
  • Tool calling with agents to discover points of interest
  • Conditional routing (translation skipped for English)
  • Chat assistant with floating overlay, tool calling, and embedding-based search

App flow

  1. Landmarks page — Browse 21 world landmarks across 7 continents with semantic search
  2. Trip planning page — Select duration (1-7 days) and language for a selected landmark
  3. Itinerary page — Stream a multi-day itinerary with weather forecasts and map views
  4. Chat assistant — Floating chat overlay with tool calling (landmark search, continent listing, trip planning)

Architecture

User Input → [Travel Planner] → [Researcher] → [Itinerary Planner] → [Translator?] → Output
Agent Purpose Key Feature
Travel Planner Extracts destination, duration, and language NLP intent parsing
Researcher Matches destination against local landmark database RAG with NL embeddings
Itinerary Planner Generates multi-day itinerary with real places Tool calling + streaming JSON
Translator Translates itinerary if non-English language requested Conditional routing

Platform support

  • ✅ iOS 26+ / macCatalyst 26+ (Apple Intelligence)
  • ❌ Android / Windows (PlatformNotSupportedException at startup)

Key packages

  • Microsoft.Maui.Essentials.AI (dotnet10 preview feed)
  • Microsoft.Agents.AI / Microsoft.Agents.AI.Hosting / Microsoft.Agents.AI.Workflows
  • Microsoft.Extensions.AI 10.3.0
  • CommunityToolkit.Mvvm 8.4.0

Structure

Follows the same folder structure as the existing 10.0/AI/ChatClient and 10.0/AI/ChatClientWithTools samples with README (frontmatter, screenshots, key files, sample prompts), images directory, and NuGet.config.

mattleibow and others added 9 commits March 10, 2026 20:12
AI Travel Planner using on-device Apple Intelligence with the Microsoft
Agent Framework, Microsoft.Extensions.AI, and .NET MAUI.

Features a 4-agent workflow pipeline:
- Travel Planner: NLP intent extraction
- Researcher: RAG-based destination matching with NL embeddings
- Itinerary Planner: Streaming itinerary generation with tool calling
- Translator: Conditional translation for non-English requests

Only supported on iOS and macCatalyst (Apple Intelligence).
Other platforms throw PlatformNotSupportedException at startup.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Move ChatOverlayView, ChatBubbleTemplateSelector, MarkdownConverter to Views/Chat/
- Update namespaces to LocalChatClientWithAgents.Views.Chat
- Pin Microsoft.Agents.AI packages to 1.0.0-rc3 (Hosting: 1.0.0-preview.260304.1)
- Pin Microsoft.Extensions.AI to 10.3.0
- Pin Microsoft.Extensions.* to 10.0.0
- Fix Microsoft.Maui.Essentials.AI version glob to 10.0.*-*

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Replace manual AddSingleton<IChatClient> with MEAI's AddChatClient()
- Replace AddKeyedSingleton<IChatClient> with AddKeyedChatClient()
- Use .Build(sp) to pass IServiceProvider to middleware pipeline
- Remove Arabic from SetLanguage tool description (not in SupportedLanguages)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Split TripPlanningPage into landmark detail page (TripPlanningPage)
  and itinerary generation page (ItineraryPage)
- ItineraryPage starts generating immediately on navigation
- PlanTrip chat tool navigates directly to ItineraryPage with day count
- Add day counter stepper (1-7) on landmark detail page
- Remove chat overlay from inner pages (FAB only on LandmarksPage)
- Remove LandmarkTripView wrapper (inlined into pages)
- Fix NullReferenceException in ItineraryViewModel.CreateDays()
- Change Itinerary.Days constraint from [Length(3,3)] to [Length(1,7)]

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…eferenceService

- Remove LanguagePreferenceService singleton and DI registration
- Add language picker directly to TripPlanningPage
- Pass language as parameter through navigation and service calls
- Remove SetLanguage chat tool (chat specifies language via PlanTrip tool)
- Remove floating language button from LandmarksPage
- Clean up LandmarksViewModel language properties

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Replace bottom panel with floating card: rounded corners, shadow, max-width 400px
- Vertical layout with uppercase DURATION/LANGUAGE section headers
- Fix redundant day counter (single '3 days' label instead of big number + caption)
- Full-width language picker with globe icon in bordered field
- Full-width generate button with drop shadow
- Card centers on wide screens, fills on narrow screens
- Add system prompt instruction to use default values for unspecified params

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Fix duplicate ChatService DI registration
- Fix CancellationTokenSource leak in ItineraryPageViewModel and TripPlanningViewModel
- Refactor ChatViewModel to use [RelayCommand] for consistency
- Replace hardcoded colors with StaticResource/AppThemeBinding (Primary, tool borders, error text)
- Migrate BackgroundColor to Background across all XAML files
- Add SafeAreaEdges=None on root Grid layouts for edge-to-edge content
- Wrap back buttons in Grid with SafeAreaEdges=Container for status bar clearance
- Add negative margin + padding on horizontal scroll lists for edge-to-edge scrolling
- Update README: expand Run section, add ItineraryPage to architecture docs

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@mattleibow mattleibow changed the title Add LocalChatClientWithAgents sample Add LocalChatClientWithAgents sample (Apple Intelligence + Agent Framework) Mar 10, 2026
@mattleibow mattleibow requested a review from Copilot March 10, 2026 23:07
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new .NET MAUI AI sample (10.0/AI/LocalChatClientWithAgents/) demonstrating an on-device Apple Intelligence travel planner that combines Microsoft.Extensions.AI (IChatClient) with a multi-agent Microsoft Agent Framework workflow (RAG + streaming itinerary + conditional translation), plus a chat overlay with tool calling.

Changes:

  • Introduces a 4-agent itinerary workflow (Travel Planner → Researcher/RAG → Itinerary Planner/tools+streaming → Translator/conditional) and associated streaming/merge utilities.
  • Adds new MAUI pages/views/viewmodels for landmark browsing, trip planning, streaming itinerary rendering, and a chat overlay UI with markdown rendering.
  • Adds sample resources (styles, icons, splash, embedded JSON datasets) and platform scaffolding (iOS/macCatalyst/Android/Windows).

Reviewed changes

Copilot reviewed 93 out of 141 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
10.0/AI/LocalChatClientWithAgents/src/Views/Landmarks/LandmarkListItemView.xaml.cs Landmark list item control code-behind + tap event plumbing
10.0/AI/LocalChatClientWithAgents/src/Views/Landmarks/LandmarkListItemView.xaml Landmark list item UI layout (thumbnail + title)
10.0/AI/LocalChatClientWithAgents/src/Views/Landmarks/LandmarkHorizontalListView.xaml.cs Horizontal list wrapper control code-behind + event forwarding
10.0/AI/LocalChatClientWithAgents/src/Views/Landmarks/LandmarkHorizontalListView.xaml Horizontal scroll + BindableLayout templating for landmarks
10.0/AI/LocalChatClientWithAgents/src/Views/Landmarks/LandmarkFeaturedItemView.xaml.cs Featured landmark control code-behind + tap event plumbing
10.0/AI/LocalChatClientWithAgents/src/Views/Landmarks/LandmarkFeaturedItemView.xaml Featured landmark UI (background image + title overlay)
10.0/AI/LocalChatClientWithAgents/src/Views/Itinerary/MessageView.xaml.cs Simple wrapper view code-behind for itinerary status/error display
10.0/AI/LocalChatClientWithAgents/src/Views/Itinerary/MessageView.xaml Status/error overlay UI for itinerary page
10.0/AI/LocalChatClientWithAgents/src/Views/Itinerary/LandmarkDetailMapView.xaml.cs Map view code-behind (pins + region positioning)
10.0/AI/LocalChatClientWithAgents/src/Views/Itinerary/LandmarkDetailMapView.xaml Map view XAML with pin template
10.0/AI/LocalChatClientWithAgents/src/Views/Itinerary/LandmarkDescriptionView.xaml.cs Simple wrapper view code-behind for landmark details section
10.0/AI/LocalChatClientWithAgents/src/Views/Itinerary/LandmarkDescriptionView.xaml Landmark title/tags/description UI
10.0/AI/LocalChatClientWithAgents/src/Views/Itinerary/ItineraryView.xaml.cs Simple wrapper view code-behind for itinerary content section
10.0/AI/LocalChatClientWithAgents/src/Views/Itinerary/ItineraryView.xaml Itinerary header/rationale/day list UI
10.0/AI/LocalChatClientWithAgents/src/Views/Itinerary/ItineraryPlanningView.xaml.cs Simple wrapper view code-behind for planning progress section
10.0/AI/LocalChatClientWithAgents/src/Views/Itinerary/ItineraryPlanningView.xaml “planning” progress UI with fading status trail
10.0/AI/LocalChatClientWithAgents/src/Views/Itinerary/DayView.xaml.cs Simple wrapper view code-behind for a single day card
10.0/AI/LocalChatClientWithAgents/src/Views/Itinerary/DayView.xaml Day card UI (map + weather/title/subtitle + activities)
10.0/AI/LocalChatClientWithAgents/src/Views/Itinerary/ActivityListView.xaml.cs Simple wrapper view code-behind for day activities list
10.0/AI/LocalChatClientWithAgents/src/Views/Itinerary/ActivityListView.xaml Activities list UI (bullet + title/description)
10.0/AI/LocalChatClientWithAgents/src/Views/Chat/MarkdownConverter.cs Markdig-based markdown → FormattedString converter for chat bubbles
10.0/AI/LocalChatClientWithAgents/src/Views/Chat/ChatOverlayView.xaml.cs Chat overlay behavior (sizing, show/hide animation, autoscroll)
10.0/AI/LocalChatClientWithAgents/src/Views/Chat/ChatOverlayView.xaml Chat overlay UI templates (user/assistant/tool call/result)
10.0/AI/LocalChatClientWithAgents/src/Views/Chat/ChatBubbleTemplateSelector.cs Template selection based on bubble type
10.0/AI/LocalChatClientWithAgents/src/ViewModels/TripPlanningViewModel.cs Trip configuration VM (day count, language, tag generation)
10.0/AI/LocalChatClientWithAgents/src/ViewModels/LandmarksViewModel.cs Landmarks browsing VM + embedding progress UI state
10.0/AI/LocalChatClientWithAgents/src/ViewModels/ItineraryViewModel.cs Itinerary VM wrapper + day VM creation
10.0/AI/LocalChatClientWithAgents/src/ViewModels/ItineraryPageViewModel.cs Orchestrates streaming itinerary generation + weather enrichment
10.0/AI/LocalChatClientWithAgents/src/ViewModels/DayPlanViewModel.cs Day plan VM (activities, weather)
10.0/AI/LocalChatClientWithAgents/src/ViewModels/ChatViewModel.cs Chat UI VM (streaming updates, tool call/result bubbles)
10.0/AI/LocalChatClientWithAgents/src/ViewModels/ChatBubbleViewModel.cs Bubble VM + expand/collapse for tool details
10.0/AI/LocalChatClientWithAgents/src/Services/WeatherService.cs Open-Meteo integration for daily forecasts
10.0/AI/LocalChatClientWithAgents/src/Services/TaggingService.cs Tag extraction via IChatClient structured output
10.0/AI/LocalChatClientWithAgents/src/Services/StreamingJsonDeserializer.cs Streaming JSON reconstruction + incremental deserialization helper
10.0/AI/LocalChatClientWithAgents/src/Services/JsonMerger.cs Deep-merge utility for progressively overlaying translated JSON
10.0/AI/LocalChatClientWithAgents/src/Services/ItineraryService.cs Streams workflow output, incremental parsing, translation merge handling
10.0/AI/LocalChatClientWithAgents/src/Services/DataService.cs Loads datasets + generates embeddings + semantic search
10.0/AI/LocalChatClientWithAgents/src/Services/ChatService.cs Tool-enabled chat service + navigation tool callback
10.0/AI/LocalChatClientWithAgents/src/Services/BufferedChatClient.cs Delegating chat client that buffers streaming text updates
10.0/AI/LocalChatClientWithAgents/src/Resources/Styles/Styles.xaml App-wide control styles
10.0/AI/LocalChatClientWithAgents/src/Resources/Styles/Colors.xaml App-wide color palette + brushes
10.0/AI/LocalChatClientWithAgents/src/Resources/Splash/splash.svg Splash SVG asset
10.0/AI/LocalChatClientWithAgents/src/Resources/Raw/pointsOfInterestData.json Embedded points-of-interest dataset for the sample
10.0/AI/LocalChatClientWithAgents/src/Resources/Raw/landmarkData.json Embedded landmarks dataset for the sample
10.0/AI/LocalChatClientWithAgents/src/Resources/Raw/AboutAssets.txt MAUI template raw-assets guidance file
10.0/AI/LocalChatClientWithAgents/src/Resources/AppIcon/appiconfg.svg App icon foreground SVG
10.0/AI/LocalChatClientWithAgents/src/Resources/AppIcon/appicon.svg App icon background SVG
10.0/AI/LocalChatClientWithAgents/src/Properties/launchSettings.json Debug launch profile settings
10.0/AI/LocalChatClientWithAgents/src/Platforms/iOS/Resources/PrivacyInfo.xcprivacy iOS privacy manifest
10.0/AI/LocalChatClientWithAgents/src/Platforms/iOS/Program.cs iOS app entry point
10.0/AI/LocalChatClientWithAgents/src/Platforms/iOS/Info.plist iOS app metadata
10.0/AI/LocalChatClientWithAgents/src/Platforms/iOS/AppDelegate.cs iOS app delegate
10.0/AI/LocalChatClientWithAgents/src/Platforms/Windows/app.manifest Windows manifest (template)
10.0/AI/LocalChatClientWithAgents/src/Platforms/Windows/Package.appxmanifest Windows packaging manifest (template)
10.0/AI/LocalChatClientWithAgents/src/Platforms/Windows/App.xaml.cs Windows app bootstrap
10.0/AI/LocalChatClientWithAgents/src/Platforms/Windows/App.xaml Windows XAML app definition
10.0/AI/LocalChatClientWithAgents/src/Platforms/MacCatalyst/Program.cs MacCatalyst app entry point
10.0/AI/LocalChatClientWithAgents/src/Platforms/MacCatalyst/Info.plist MacCatalyst app metadata
10.0/AI/LocalChatClientWithAgents/src/Platforms/MacCatalyst/Entitlements.plist MacCatalyst entitlements (network client + sandbox)
10.0/AI/LocalChatClientWithAgents/src/Platforms/MacCatalyst/AppDelegate.cs MacCatalyst app delegate
10.0/AI/LocalChatClientWithAgents/src/Platforms/Android/Resources/values/colors.xml Android color resources
10.0/AI/LocalChatClientWithAgents/src/Platforms/Android/MainApplication.cs Android application bootstrap
10.0/AI/LocalChatClientWithAgents/src/Platforms/Android/MainActivity.cs Android main activity
10.0/AI/LocalChatClientWithAgents/src/Platforms/Android/AndroidManifest.xml Android manifest + network permissions
10.0/AI/LocalChatClientWithAgents/src/Pages/TripPlanningPage.xaml.cs Trip planning page navigation + lifecycle cancellation
10.0/AI/LocalChatClientWithAgents/src/Pages/TripPlanningPage.xaml Trip planning UI (duration/language picker + generate CTA)
10.0/AI/LocalChatClientWithAgents/src/Pages/LandmarksPage.xaml.cs Landmarks page interactions + chat overlay wiring
10.0/AI/LocalChatClientWithAgents/src/Pages/LandmarksPage.xaml Landmarks browsing UI + embedding-generation blocker overlay
10.0/AI/LocalChatClientWithAgents/src/Pages/ItineraryPage.xaml.cs Itinerary page lifecycle (generate/cancel) + back navigation
10.0/AI/LocalChatClientWithAgents/src/Pages/ItineraryPage.xaml Itinerary UI switching (planning/results/error)
10.0/AI/LocalChatClientWithAgents/src/NuGet.config Adds dotnet10 preview feed for Apple Intelligence package
10.0/AI/LocalChatClientWithAgents/src/Models/Weather.cs Weather forecast DTOs + weather code mapping helpers
10.0/AI/LocalChatClientWithAgents/src/Models/PointOfInterest.cs POI model + embeddings field
10.0/AI/LocalChatClientWithAgents/src/Models/Landmark.cs Landmark model + computed image names + location + embeddings field
10.0/AI/LocalChatClientWithAgents/src/Models/Itinerary.cs Itinerary schema + validation metadata + example generator
10.0/AI/LocalChatClientWithAgents/src/MauiProgram.cs DI registration + Apple Intelligence gating + workflow registration
10.0/AI/LocalChatClientWithAgents/src/MauiAppBuilderExtensions.cs Apple Intelligence + embeddings DI helpers
10.0/AI/LocalChatClientWithAgents/src/LocalChatClientWithAgents.slnx Solution entry for the new sample
10.0/AI/LocalChatClientWithAgents/src/LocalChatClientWithAgents.csproj Project definition + NuGet package references + assets
10.0/AI/LocalChatClientWithAgents/src/Converters/IsNotNullOrEmptyConverter.cs Converter used for conditional UI visibility
10.0/AI/LocalChatClientWithAgents/src/Converters/IsNotNullConverter.cs Converter used for null checks in XAML
10.0/AI/LocalChatClientWithAgents/src/Converters/InvertedBoolConverter.cs Converter used for boolean inversion in XAML
10.0/AI/LocalChatClientWithAgents/src/AppShell.xaml.cs Shell route registration
10.0/AI/LocalChatClientWithAgents/src/AppShell.xaml Shell structure (LandmarksPage as root)
10.0/AI/LocalChatClientWithAgents/src/App.xaml.cs Application bootstrap
10.0/AI/LocalChatClientWithAgents/src/App.xaml Resource dictionaries + converters registration
10.0/AI/LocalChatClientWithAgents/src/AI/WorkflowModels.cs Workflow model types exchanged between executors/agents
10.0/AI/LocalChatClientWithAgents/src/AI/WorkflowEvents.cs Custom workflow events for status + streaming text chunks
10.0/AI/LocalChatClientWithAgents/src/AI/ItineraryWorkflowTools.cs RAG provider + POI tool exposed to itinerary agent
10.0/AI/LocalChatClientWithAgents/src/AI/ItineraryWorkflowExtensions.cs Agent registrations + workflow graph + conditional translation routing
10.0/AI/LocalChatClientWithAgents/src/AI/5_OutputExecutor.cs Workflow output executor (final status event)
10.0/AI/LocalChatClientWithAgents/src/AI/4_TranslatorExecutor.cs Translator executor streaming translated JSON as workflow events
10.0/AI/LocalChatClientWithAgents/src/AI/3_ItineraryPlannerExecutor.cs Itinerary planner executor streaming itinerary JSON as workflow events
10.0/AI/LocalChatClientWithAgents/src/AI/2_ResearcherExecutor.cs Researcher executor using RAG context to pick best destination
10.0/AI/LocalChatClientWithAgents/src/AI/1_TravelPlannerExecutor.cs Travel planner executor extracting intent into a typed message
10.0/AI/LocalChatClientWithAgents/README.md Sample documentation (architecture, prerequisites, run instructions)

mattleibow and others added 7 commits March 11, 2026 16:01
…README

- Fix chat overlay layout: edge-to-edge backdrop (SafeAreaEdges=None),
  safe area + keyboard-aware wrapper (SoftInput,Container), floating card
  with CascadeInputTransparent=False for proper touch handling
- Remove iOS keyboard Done button on chat Entry (InputAccessoryView=null)
- Use embedding cosine similarity for continent matching instead of string
  Contains — 'Oceania' now correctly matches 'Australia/Oceania'
- Generate continent embeddings at startup in DataService
- Add all 4 screenshots: landmarks, trip planning, itinerary, chat
- Update README: add screenshot gallery, Key files, Sample prompts sections
  to match structure of ChatClient and ChatClientWithTools samples

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…review.1.26161.6

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Change [Length(1, 7)] to [Length(3, 3)] on Itinerary.Days to match reference sample
  and produce consistent 3-day itineraries from Apple Intelligence
- Pin Microsoft.Maui.Essentials.AI to 10.0.50-preview.1.26158.1

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Fix TaggingService double deserialization: use ChatResponse<T>.TryGetResult()
  instead of ToString() + JsonSerializer.Deserialize round-trip
- Fix Windows app.manifest assemblyIdentity to match project name
- Remove unused dotnet_bot.png template asset
- Add Microsoft.Maui.Controls with $(MauiVersion) for CI compatibility
- Use $(MauiVersion) for Microsoft.Maui.Controls.Maps

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add GlobalXmlns.cs registering all app namespaces with the global URI.
Enable MauiAllowImplicitXmlnsDeclaration and EnablePreviewFeatures.
Remove redundant xmlns declarations and type prefixes from all 18 XAML
files (50 lines removed). Maps xmlns kept where needed.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The [Length(3,3)] attribute on Itinerary.Days forced exactly 3 days
regardless of user selection (2-7). Changed to [Length(1,7)] to match
the UI range.
@mattleibow mattleibow force-pushed the ai/local-chat-client-with-agents branch from 66534ed to 84dbd72 Compare March 13, 2026 21:53
@PureWeen
Copy link
Member

�� Multi-Model Code Review -- PR #744 (LocalChatClientWithAgents)

CI Status: ✅ All checks passing

Consensus Findings (flagged by 2+ of 5 models)

🟡 MODERATE -- Race condition: embeddings never awaited (5/5 consensus)

File: src/Services/DataService.cs:44,114

ContinueWith schedules the _embeddingsTask assignment asynchronously after _initializationTask completes, on a thread pool thread. WaitUntilReadyAsync() awaits _initializationTask, then immediately checks if (_embeddingsTask is not null) -- but the ContinueWith callback may not have run yet, so _embeddingsTask is still null. The method returns with embeddings not yet started.

Fix:

// Replace ContinueWith with direct chaining:
_embeddingsTask = _initializationTask.ContinueWith(_ => Task.Run(GenerateEmbeddingsAsync)).Unwrap();

🟡 MODERATE -- Cancel() leaves disposed CTS; ObjectDisposedException on iOS swipe-abort (5/5 consensus)

File: src/ViewModels/ItineraryPageViewModel.cs:113-117

Cancel() disposes _cancellationTokenSource without replacing it. On iOS, a swipe-back gesture fires NavigatingFrom (which calls Cancel()) but can be aborted -- the page stays alive with a disposed CTS. Any subsequent GenerateAsync() call hits _cancellationTokenSource.Cancel() on the disposed instance and throws ObjectDisposedException.

Fix:

public void Cancel()
{
    _cancellationTokenSource.Cancel();
    _cancellationTokenSource.Dispose();
    _cancellationTokenSource = new CancellationTokenSource();
}

🟡 MODERATE -- JsonDocument never disposed; memory leak per streaming session (5/5 consensus)

Files: src/Services/JsonMerger.cs:22 + src/Services/ItineraryService.cs:79

JsonMerger holds _baseDocument (a JsonDocument which rents pooled/native memory) but never implements IDisposable. In ItineraryService.StreamItineraryAsync, old merger instances are silently overwritten on executor switch without disposal. Per streaming session, at least one JsonDocument's pooled memory leaks.

Fix: JsonMerger must implement IDisposable and dispose _baseDocument; ItineraryService must use using at call sites.

🟡 MODERATE -- SearchLandmarksAsync skips embedding wait; silent semantic-search degradation (5/5 consensus)

File: src/Services/DataService.cs:118

Both SearchLandmarksAsync and SearchPointsOfInterestAsync only await _initializationTask (data load), never _embeddingsTask. If called before embedding generation completes, every item's Embeddings is null, scores -1f, and results are ranked by keyword substring boost only. Semantic search silently falls back to substring matching.

🟢 MINOR -- Single embedding failure stops all remaining items (2/5 consensus)

File: src/Services/DataService.cs:178

The entire embedding loop is wrapped in a single try/catch. An exception on one item aborts all remaining landmarks and POIs.

🟢 MINOR -- Full ItineraryViewModel rebuild per streaming chunk (2/5 consensus)

File: src/ViewModels/ItineraryPageViewModel.cs:82

A new ItineraryViewModel is allocated on every streamed JSON chunk, forcing full UI rebind and causing visible flicker.


Recommended Action: ⚠️ Request Changes

  1. Fix ContinueWith race with .Unwrap() chaining in DataService
  2. Replace disposed CTS in Cancel()
  3. Make JsonMerger implement IDisposable and use using at call sites
  4. Await embedding readiness in SearchLandmarksAsync / SearchPointsOfInterestAsync

Review generated by multi-model consensus (Claude Opus, Claude Sonnet, Gemini Pro, GPT-Codex)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants