diff --git a/AIDevGallery/Samples/WCRAPIs/AppIndexCapability.xaml b/AIDevGallery/Samples/WCRAPIs/AppIndexCapability.xaml index cf6114b4..01c8e58c 100644 --- a/AIDevGallery/Samples/WCRAPIs/AppIndexCapability.xaml +++ b/AIDevGallery/Samples/WCRAPIs/AppIndexCapability.xaml @@ -8,71 +8,84 @@ xmlns:samples="using:AIDevGallery.Samples" mc:Ignorable="d"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - + + diff --git a/AIDevGallery/Samples/WCRAPIs/AppIndexCapability.xaml.cs b/AIDevGallery/Samples/WCRAPIs/AppIndexCapability.xaml.cs index bd5c2d62..14f09aeb 100644 --- a/AIDevGallery/Samples/WCRAPIs/AppIndexCapability.xaml.cs +++ b/AIDevGallery/Samples/WCRAPIs/AppIndexCapability.xaml.cs @@ -40,6 +40,14 @@ protected override async Task LoadModelAsync(SampleNavigationParameters samplePa { await Task.Run(async () => { + // GetOrCreateIndex uses default options (all capabilities enabled). + // When using GetOrCreateIndexWithOptions, be aware of coupling rules: + // - Suppressing TextLexical is only honored when TextSemantic is also Suppressed; + // otherwise TextLexical suppression is silently treated as Default. + // - ImageOcr and ImageSemantic are independent; both must be Suppressed + // to fully disable image content. + // - Suppressing all text or all image capabilities makes that content kind + // unsupported (regions return UnsupportedContentKind). var result = AppContentIndexer.GetOrCreateIndex("indexCapabilityIndex"); if (!result.Succeeded) @@ -69,6 +77,9 @@ await Task.Run(async () => sampleParams.NotifyCompletion(); }); + + // Demonstrate GetOrCreateIndexWithOptions with suppressed capabilities + await DemoSuppressedCapabilitiesAsync(); } protected override void OnNavigatedFrom(NavigationEventArgs e) @@ -119,6 +130,10 @@ private async void LoadAppIndexCapabilities() // Each status will be one of: Unknown, Initialized, Initializing, Suppressed, Unsupported, DisabledByPolicy, InitializationError // If status is Initialized, that capability is ready for use + // + // Coupling rule: TextLexical suppression is only honored when + // TextSemantic is also Suppressed. If TextSemantic is Default or + // Required, TextLexical=Suppressed is silently treated as Default. if (capabilities.GetCapabilityState(IndexCapability.TextLexical).InitializationStatus == IndexCapabilityInitializationStatus.Initialized) { indexLexicalCapabilityResultText.Text = "Available"; @@ -139,7 +154,10 @@ private async void LoadAppIndexCapabilities() unavailable.Add("TextSemantic"); } - if (capabilities.GetCapabilityState(IndexCapability.ImageSemantic).InitializationStatus == IndexCapabilityInitializationStatus.Initialized) + // ImageOcr and ImageSemantic are independent capabilities; suppressing + // one does not affect the other. Both must be Suppressed to fully disable + // image content support. + if (capabilities.GetCapabilityState(IndexCapability.ImageOcr).InitializationStatus == IndexCapabilityInitializationStatus.Initialized) { indexOCRCapabilityResultText.Text = "Available"; } @@ -149,7 +167,7 @@ private async void LoadAppIndexCapabilities() unavailable.Add("ImageOcr"); } - if (capabilities.GetCapabilityState(IndexCapability.ImageOcr).InitializationStatus == IndexCapabilityInitializationStatus.Initialized) + if (capabilities.GetCapabilityState(IndexCapability.ImageSemantic).InitializationStatus == IndexCapabilityInitializationStatus.Initialized) { indexSemanticImageCapabilityResultText.Text = "Available"; } @@ -172,6 +190,80 @@ private async void LoadAppIndexCapabilities() }); } + /// + /// Demonstrates GetOrCreateIndexWithOptions to show how capability suppression + /// affects index behavior. Uses a dedicated temp index name to avoid + /// IncompatibleWithExistingOptions errors. + /// + private async Task DemoSuppressedCapabilitiesAsync() + { + await Task.Run(() => + { + try + { + // First, delete any leftover temp index from a previous run + AppContentIndexer.DeleteIndex("capabilityDemoSuppressed", DeleteIndexWhileInUseBehavior.DeferIfInUse); + + // Suppress both image capabilities to fully disable image content support. + // Per the IDL coupling rules: + // - ImageOcr and ImageSemantic are independent; both must be Suppressed + // to fully disable image content. + // - If only one is Suppressed, image content is still partially supported. + var options = new GetOrCreateIndexOptions + { + ImageOcrRequirement = IndexCapabilityRequirement.Suppressed, + ImageSemanticRequirement = IndexCapabilityRequirement.Suppressed, + }; + + var result = AppContentIndexer.GetOrCreateIndex("capabilityDemoSuppressed", options); + + if (result.Succeeded) + { + using var tempIndexer = result.Indexer; + IndexCapabilities caps = tempIndexer.GetIndexCapabilities(); + + // Read values before leaving the using block to avoid use-after-dispose + var ocrState = caps.GetCapabilityState(IndexCapability.ImageOcr).InitializationStatus.ToString(); + var imgSemanticState = caps.GetCapabilityState(IndexCapability.ImageSemantic).InitializationStatus.ToString(); + var textLexState = caps.GetCapabilityState(IndexCapability.TextLexical).InitializationStatus.ToString(); + var textSemState = caps.GetCapabilityState(IndexCapability.TextSemantic).InitializationStatus.ToString(); + + DispatcherQueue.TryEnqueue(() => + { + suppressedOcrText.Text = ocrState; + suppressedImageSemanticText.Text = imgSemanticState; + suppressedTextLexicalText.Text = textLexState; + suppressedTextSemanticText.Text = textSemState; + }); + } + else + { + DispatcherQueue.TryEnqueue(() => + { + suppressedOcrText.Text = $"Error: {result.Status}"; + suppressedImageSemanticText.Text = $"Error: {result.Status}"; + suppressedTextLexicalText.Text = $"Error: {result.Status}"; + suppressedTextSemanticText.Text = $"Error: {result.Status}"; + }); + } + + // Clean up the temporary index + AppContentIndexer.DeleteIndex("capabilityDemoSuppressed", DeleteIndexWhileInUseBehavior.DeferIfInUse); + } + catch (Exception ex) + { + DispatcherQueue.TryEnqueue(() => + { + suppressedOcrText.Text = "Error"; + suppressedImageSemanticText.Text = "Error"; + suppressedTextLexicalText.Text = "Error"; + suppressedTextSemanticText.Text = "Error"; + Debug.WriteLine($"DemoSuppressedCapabilitiesAsync failed: {ex.Message}"); + }); + } + }); + } + private void Listener_IndexCapabilitiesChanged(AppContentIndexer indexer, IndexCapabilities statusResult) { LoadAppIndexCapabilities(); diff --git a/AIDevGallery/Samples/WCRAPIs/KnowledgeRetrieval.xaml.cs b/AIDevGallery/Samples/WCRAPIs/KnowledgeRetrieval.xaml.cs index 64e10558..49c3afe8 100644 --- a/AIDevGallery/Samples/WCRAPIs/KnowledgeRetrieval.xaml.cs +++ b/AIDevGallery/Samples/WCRAPIs/KnowledgeRetrieval.xaml.cs @@ -96,6 +96,13 @@ await Task.Run(async () => } // Load AppContentIndexer + // GetOrCreateIndex uses default options (all capabilities enabled). + // To selectively suppress capabilities, use GetOrCreateIndexWithOptions. + // Coupling rules to keep in mind: + // - TextLexical suppression is only honored when TextSemantic is also + // Suppressed (semantic text requires the lexical pipeline). + // - ImageOcr and ImageSemantic are independent; both must be + // Suppressed to fully disable image content. var result = AppContentIndexer.GetOrCreateIndex("knowledgeRetrievalIndex"); if (!result.Succeeded) diff --git a/AIDevGallery/Samples/WCRAPIs/SemanticSearch.xaml.cs b/AIDevGallery/Samples/WCRAPIs/SemanticSearch.xaml.cs index ebc6be47..4b2dcd5f 100644 --- a/AIDevGallery/Samples/WCRAPIs/SemanticSearch.xaml.cs +++ b/AIDevGallery/Samples/WCRAPIs/SemanticSearch.xaml.cs @@ -48,7 +48,7 @@ internal sealed partial class SemanticSearch : BaseSamplePage // This is some text data that we want to add to the index: private Dictionary simpleTextData = new Dictionary { - { "item1", "Preparing a hearty vegetable stew begins with chopping fresh carrots, onions, and celery. Sauté them in olive oil until fragrant, then add diced tomatoes, herbs, and vegetable broth. Simmer gently for an hour, allowing flavors to meld into a comforting dish perfect for cold evenings." }, + { "item1", "Preparing a hearty vegetable stew begins with chopping fresh carrots, onions, and celery. Sauté them in olive oil until fragrant, then add diced tomatoes, herbs, and vegetable broth. Simmer gently for an hour, allowing flavors to meld into a comforting dish perfect for cold evenings." }, { "item2", "Modern exhibition design combines narrative flow with spatial strategy. Lighting emphasizes focal objects while circulation paths avoid bottlenecks. Materials complement artifacts without visual competition. Interactive elements invite engagement but remain intuitive. Environmental controls protect sensitive works. Success balances scholarship, aesthetics, and visitor experience through thoughtful, cohesive design choices." }, { "item3", "Domestic cats communicate through posture, tail flicks, and vocalizations. Play mimics hunting behaviors like stalking and pouncing, supporting agility and mental stimulation. Scratching maintains claws and marks territory, so provide sturdy posts. Balanced diets, hydration, and routine veterinary care sustain health. Safe retreats and vertical spaces reduce stress and encourage exploration." }, { "item4", "Snowboarding across pristine slopes combines agility, balance, and speed. Riders carve smooth turns on powder, adjust stance for control, and master jumps in terrain parks. Essential gear includes boots, bindings, and helmets for safety. Embrace crisp alpine air while perfecting tricks and enjoying the thrill of winter adventure." }, @@ -82,6 +82,13 @@ protected override async Task LoadModelAsync(SampleNavigationParameters samplePa { await Task.Run(async () => { + // GetOrCreateIndex uses default options (all capabilities enabled). + // To selectively suppress capabilities, use GetOrCreateIndexWithOptions. + // Coupling rules to keep in mind: + // - TextLexical suppression is only honored when TextSemantic is also + // Suppressed (semantic text requires the lexical pipeline). + // - ImageOcr and ImageSemantic are independent; both must be + // Suppressed to fully disable image content. var result = AppContentIndexer.GetOrCreateIndex("semanticSearchIndex"); if (!result.Succeeded) @@ -274,15 +281,26 @@ private void SearchBox_QuerySubmitted(AutoSuggestBox sender, AutoSuggestBoxQuery if (match.ContentKind == QueryMatchContentKind.AppManagedText) { AppManagedTextQueryMatch textResult = (AppManagedTextQueryMatch)match; - string matchingData = simpleTextData[match.ContentId]; - int offset = textResult.TextOffset; - int length = textResult.TextLength; - string matchingString = matchingData.Substring(offset, length); - textResults += matchingString + "\n\n"; + if (simpleTextData.TryGetValue(match.ContentId, out string? matchingData)) + { + int offset = textResult.TextOffset; + int length = textResult.TextLength; + + // Guard against stale offsets after content updates + if (offset >= 0 && offset < matchingData.Length && length > 0 && offset + length <= matchingData.Length) + { + string matchingString = matchingData.Substring(offset, length); + textResults += matchingString + "\n\n"; + } + else + { + textResults += matchingData + "\n\n"; + } + } } } - // Create text query + // Create image query AppIndexImageQuery imageQuery = _indexer.CreateImageQuery(searchText, imageQueryOptions); // Get image matches @@ -550,7 +568,7 @@ private async void LoadAppIndexCapabilities() // Disable text sample if both text capabilities are unavailable textDataItemsView.IsEnabled = textLexicalAvailable || textSemanticAvailable; - uploadTextButton.IsEnabled = imageSemanticAvailable || imageOcrAvailable; + uploadTextButton.IsEnabled = textLexicalAvailable || textSemanticAvailable; // Disable image sample if both image capabilities are unavailable ImageDataItemsView.IsEnabled = imageSemanticAvailable || imageOcrAvailable;