From fcb6ec9fd3877d846b8c9093e17dd71c29ea6d97 Mon Sep 17 00:00:00 2001 From: Javier Calvarro Nelson Date: Thu, 11 Dec 2025 16:10:59 +0100 Subject: [PATCH] More UI cleanups --- .../Messages/Contents/DataContentTemplate.cs | 0 .../AI/Messages/Contents/ErrorTemplate.cs | 43 +++++++++++++++++++ .../Messages/Contents/FunctionCallTemplate.cs | 32 ++------------ .../Contents/FunctionResultTemplate.cs | 39 +++++++++++++++++ .../AI/Messages/Contents/TextTemplate.cs | 6 +++ .../AI/Messages/DefaultMessageTemplate.cs | 19 +++++--- .../Components/AI/Messages/Messages.cs | 8 ++++ .../AgenticGenerativeUIDemo.razor | 4 ++ .../BackendToolRenderingDemo.razor | 5 +++ .../WeatherCallTemplate.cs | 2 +- .../HumanInTheLoop/HumanInTheLoopDemo.razor | 4 ++ .../PredictiveStateUpdatesDemo.razor | 4 ++ .../Demos/SharedState/SharedStateDemo.razor | 4 ++ .../ToolBasedGenerativeUIDemo.razor | 4 ++ .../AGUIDojo/AGUIDojoClient/wwwroot/app.css | 5 +++ 15 files changed, 145 insertions(+), 34 deletions(-) create mode 100644 dotnet/samples/AGUIDojo/AGUIDojoClient/Components/AI/Messages/Contents/DataContentTemplate.cs create mode 100644 dotnet/samples/AGUIDojo/AGUIDojoClient/Components/AI/Messages/Contents/ErrorTemplate.cs create mode 100644 dotnet/samples/AGUIDojo/AGUIDojoClient/Components/AI/Messages/Contents/FunctionResultTemplate.cs diff --git a/dotnet/samples/AGUIDojo/AGUIDojoClient/Components/AI/Messages/Contents/DataContentTemplate.cs b/dotnet/samples/AGUIDojo/AGUIDojoClient/Components/AI/Messages/Contents/DataContentTemplate.cs new file mode 100644 index 0000000000..e69de29bb2 diff --git a/dotnet/samples/AGUIDojo/AGUIDojoClient/Components/AI/Messages/Contents/ErrorTemplate.cs b/dotnet/samples/AGUIDojo/AGUIDojoClient/Components/AI/Messages/Contents/ErrorTemplate.cs new file mode 100644 index 0000000000..b6fedbb670 --- /dev/null +++ b/dotnet/samples/AGUIDojo/AGUIDojoClient/Components/AI/Messages/Contents/ErrorTemplate.cs @@ -0,0 +1,43 @@ +// Copyright (c) Microsoft. All rights reserved. + +using Microsoft.AspNetCore.Components; +using Microsoft.Extensions.AI; + +namespace Microsoft.AspNetCore.Components.AI; + +/// +/// Default template for rendering ErrorContent. Renders nothing by default +/// to prevent the error from crashing the UI. More specific templates can +/// override this to display error messages. +/// +public class ErrorTemplate : ContentTemplateBase +{ + [CascadingParameter] internal MessageListContext Context { get; set; } = default!; + + public override void Attach(RenderHandle renderHandle) + { + this.ChildContent = this.RenderError; + } + + public override Task SetParametersAsync(ParameterView parameters) + { + parameters.SetParameterProperties(this); + this.Context.RegisterContentTemplate(this); + return Task.CompletedTask; + } + + /// + /// Determines if this template should handle the given content. + /// Matches ErrorContent. + /// + public override bool When(ContentContext context) + { + return context.Content is ErrorContent; + } + + private RenderFragment RenderError(ContentContext content) => builder => + { + // By default, render nothing. + // Specific templates can override to display error messages. + }; +} diff --git a/dotnet/samples/AGUIDojo/AGUIDojoClient/Components/AI/Messages/Contents/FunctionCallTemplate.cs b/dotnet/samples/AGUIDojo/AGUIDojoClient/Components/AI/Messages/Contents/FunctionCallTemplate.cs index 663c9d42ca..6c73b6f8f9 100644 --- a/dotnet/samples/AGUIDojo/AGUIDojoClient/Components/AI/Messages/Contents/FunctionCallTemplate.cs +++ b/dotnet/samples/AGUIDojo/AGUIDojoClient/Components/AI/Messages/Contents/FunctionCallTemplate.cs @@ -35,7 +35,7 @@ public class FunctionCallTemplate : ContentTemplateBase /// /// The content context. /// True if this template should render the content. - public new bool When(ContentContext context) + public override bool When(ContentContext context) { if (context.Content is not FunctionCallContent call) { @@ -53,32 +53,8 @@ public class FunctionCallTemplate : ContentTemplateBase private RenderFragment RenderFunctionCall(ContentContext content) => builder => { - if (content.Content is FunctionCallContent call) - { - // Get or create the invocation context which tracks call and result - var invocation = this.Context.GetOrCreateInvocation(call); - - builder.OpenComponent>(0); - builder.AddComponentParameter(1, "Value", invocation); - builder.AddComponentParameter(2, "IsFixed", true); - builder.AddComponentParameter(3, "ChildContent", (RenderFragment)(innerBuilder => - { - // Default fallback rendering - shows function name and loading/result status - innerBuilder.OpenElement(0, "div"); - innerBuilder.AddAttribute(1, "class", "function-call"); - innerBuilder.AddContent(2, $"Function: {call.Name}"); - if (invocation.HasResult) - { - innerBuilder.AddContent(3, " [Result available]"); - } - else - { - innerBuilder.AddContent(3, " [Loading...]"); - } - - innerBuilder.CloseElement(); - })); - builder.CloseComponent(); - } + // By default, function calls are not rendered visually. + // Custom templates (like WeatherCallTemplate) can override this + // behavior for specific functions by registering before this template. }; } diff --git a/dotnet/samples/AGUIDojo/AGUIDojoClient/Components/AI/Messages/Contents/FunctionResultTemplate.cs b/dotnet/samples/AGUIDojo/AGUIDojoClient/Components/AI/Messages/Contents/FunctionResultTemplate.cs new file mode 100644 index 0000000000..fed7b01b8f --- /dev/null +++ b/dotnet/samples/AGUIDojo/AGUIDojoClient/Components/AI/Messages/Contents/FunctionResultTemplate.cs @@ -0,0 +1,39 @@ +// Copyright (c) Microsoft. All rights reserved. + +using Microsoft.Extensions.AI; + +namespace Microsoft.AspNetCore.Components.AI; + +/// +/// Default template for FunctionResultContent that renders nothing. +/// Function results are internal tool responses and typically don't need +/// visual representation in the chat UI. +/// +public class FunctionResultTemplate : ContentTemplateBase +{ + [CascadingParameter] internal MessageListContext Context { get; set; } = default!; + + public override void Attach(RenderHandle renderHandle) + { + // This component never renders anything by itself. + this.ChildContent = this.RenderFunctionResult; + } + + public override Task SetParametersAsync(ParameterView parameters) + { + parameters.SetParameterProperties(this); + this.Context.RegisterContentTemplate(this); + return Task.CompletedTask; + } + + /// + /// Only match FunctionResultContent. + /// + public override bool When(ContentContext context) => context.Content is FunctionResultContent; + + private RenderFragment RenderFunctionResult(ContentContext content) => builder => + { + // By default, function results are not rendered visually. + // The result data is typically processed by the agent to generate text responses. + }; +} diff --git a/dotnet/samples/AGUIDojo/AGUIDojoClient/Components/AI/Messages/Contents/TextTemplate.cs b/dotnet/samples/AGUIDojo/AGUIDojoClient/Components/AI/Messages/Contents/TextTemplate.cs index f698ac8e6e..1a1a5c17ff 100644 --- a/dotnet/samples/AGUIDojo/AGUIDojoClient/Components/AI/Messages/Contents/TextTemplate.cs +++ b/dotnet/samples/AGUIDojo/AGUIDojoClient/Components/AI/Messages/Contents/TextTemplate.cs @@ -21,6 +21,12 @@ public class TextTemplate : ContentTemplateBase return Task.CompletedTask; } + /// + /// Only match TextContent, allowing other content templates to handle + /// FunctionCallContent, FunctionResultContent, etc. + /// + public override bool When(ContentContext context) => context.Content is TextContent; + private RenderFragment RenderText(ContentContext content) => builder => { if (content.Content is TextContent textContent) diff --git a/dotnet/samples/AGUIDojo/AGUIDojoClient/Components/AI/Messages/DefaultMessageTemplate.cs b/dotnet/samples/AGUIDojo/AGUIDojoClient/Components/AI/Messages/DefaultMessageTemplate.cs index 9729a53542..b0defc8b26 100644 --- a/dotnet/samples/AGUIDojo/AGUIDojoClient/Components/AI/Messages/DefaultMessageTemplate.cs +++ b/dotnet/samples/AGUIDojo/AGUIDojoClient/Components/AI/Messages/DefaultMessageTemplate.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft. All rights reserved. +// Copyright (c) Microsoft. All rights reserved. using Microsoft.Extensions.AI; @@ -52,8 +52,10 @@ internal sealed class DefaultMessageTemplate : MessageTemplateBase /// /// Checks if a message has any visible content that should be displayed. - /// Messages that only contain FunctionCallContent or FunctionResultContent - /// are internal tool messages and should not be rendered as chat bubbles. + /// Messages that only contain FunctionResultContent are internal tool messages + /// and should not be rendered as chat bubbles. + /// FunctionCallContent is considered visible because content templates can render them + /// (e.g., WeatherCallTemplate renders weather tool calls as cards). /// private static bool HasVisibleContent(ChatMessage message) { @@ -70,8 +72,15 @@ internal sealed class DefaultMessageTemplate : MessageTemplateBase return true; } - // Other content types that are not function calls/results are visible - if (content is not FunctionCallContent && content is not FunctionResultContent) + // FunctionCallContent is visible - content templates can render them + // (e.g., WeatherCallTemplate renders weather tool calls as cards) + if (content is FunctionCallContent) + { + return true; + } + + // Other content types that are not function results are visible + if (content is not FunctionResultContent) { return true; } diff --git a/dotnet/samples/AGUIDojo/AGUIDojoClient/Components/AI/Messages/Messages.cs b/dotnet/samples/AGUIDojo/AGUIDojoClient/Components/AI/Messages/Messages.cs index e404ac7d95..82e5a4ac8b 100644 --- a/dotnet/samples/AGUIDojo/AGUIDojoClient/Components/AI/Messages/Messages.cs +++ b/dotnet/samples/AGUIDojo/AGUIDojoClient/Components/AI/Messages/Messages.cs @@ -27,6 +27,14 @@ public partial class Messages : IComponent { builder.OpenComponent(0); builder.CloseComponent(); + builder.OpenComponent(1); + builder.CloseComponent(); + builder.OpenComponent(2); + builder.CloseComponent(); + builder.OpenComponent(3); + builder.CloseComponent(); + builder.OpenComponent(4); + builder.CloseComponent(); }; public void Attach(RenderHandle renderHandle) diff --git a/dotnet/samples/AGUIDojo/AGUIDojoClient/Components/Demos/AgenticGenerativeUI/AgenticGenerativeUIDemo.razor b/dotnet/samples/AGUIDojo/AGUIDojoClient/Components/Demos/AgenticGenerativeUI/AgenticGenerativeUIDemo.razor index 24fec8e84b..3b5435fee6 100644 --- a/dotnet/samples/AGUIDojo/AGUIDojoClient/Components/Demos/AgenticGenerativeUI/AgenticGenerativeUIDemo.razor +++ b/dotnet/samples/AGUIDojo/AGUIDojoClient/Components/Demos/AgenticGenerativeUI/AgenticGenerativeUIDemo.razor @@ -28,6 +28,10 @@ + + + + diff --git a/dotnet/samples/AGUIDojo/AGUIDojoClient/Components/Demos/BackendToolRendering/BackendToolRenderingDemo.razor b/dotnet/samples/AGUIDojo/AGUIDojoClient/Components/Demos/BackendToolRendering/BackendToolRenderingDemo.razor index df21874677..64f962ada7 100644 --- a/dotnet/samples/AGUIDojo/AGUIDojoClient/Components/Demos/BackendToolRendering/BackendToolRenderingDemo.razor +++ b/dotnet/samples/AGUIDojo/AGUIDojoClient/Components/Demos/BackendToolRendering/BackendToolRenderingDemo.razor @@ -20,7 +20,12 @@
+ + + + + diff --git a/dotnet/samples/AGUIDojo/AGUIDojoClient/Components/Demos/BackendToolRendering/WeatherCallTemplate.cs b/dotnet/samples/AGUIDojo/AGUIDojoClient/Components/Demos/BackendToolRendering/WeatherCallTemplate.cs index e9ed8ab989..22a6b8f7e1 100644 --- a/dotnet/samples/AGUIDojo/AGUIDojoClient/Components/Demos/BackendToolRendering/WeatherCallTemplate.cs +++ b/dotnet/samples/AGUIDojo/AGUIDojoClient/Components/Demos/BackendToolRendering/WeatherCallTemplate.cs @@ -29,7 +29,7 @@ public class WeatherCallTemplate : ContentTemplateBase /// Determines if this template should handle the given content. /// Matches FunctionCallContent for the get_weather function. /// - public new bool When(ContentContext context) + public override bool When(ContentContext context) { // Only match FunctionCallContent for the get_weather function return context.Content is FunctionCallContent call && diff --git a/dotnet/samples/AGUIDojo/AGUIDojoClient/Components/Demos/HumanInTheLoop/HumanInTheLoopDemo.razor b/dotnet/samples/AGUIDojo/AGUIDojoClient/Components/Demos/HumanInTheLoop/HumanInTheLoopDemo.razor index d7023b0be2..c65d980250 100644 --- a/dotnet/samples/AGUIDojo/AGUIDojoClient/Components/Demos/HumanInTheLoop/HumanInTheLoopDemo.razor +++ b/dotnet/samples/AGUIDojo/AGUIDojoClient/Components/Demos/HumanInTheLoop/HumanInTheLoopDemo.razor @@ -24,6 +24,10 @@ + + + + diff --git a/dotnet/samples/AGUIDojo/AGUIDojoClient/Components/Demos/PredictiveStateUpdates/PredictiveStateUpdatesDemo.razor b/dotnet/samples/AGUIDojo/AGUIDojoClient/Components/Demos/PredictiveStateUpdates/PredictiveStateUpdatesDemo.razor index e1372ec673..925ed7a826 100644 --- a/dotnet/samples/AGUIDojo/AGUIDojoClient/Components/Demos/PredictiveStateUpdates/PredictiveStateUpdatesDemo.razor +++ b/dotnet/samples/AGUIDojo/AGUIDojoClient/Components/Demos/PredictiveStateUpdates/PredictiveStateUpdatesDemo.razor @@ -52,6 +52,10 @@ + + + + diff --git a/dotnet/samples/AGUIDojo/AGUIDojoClient/Components/Demos/SharedState/SharedStateDemo.razor b/dotnet/samples/AGUIDojo/AGUIDojoClient/Components/Demos/SharedState/SharedStateDemo.razor index f502a45146..6642fde60e 100644 --- a/dotnet/samples/AGUIDojo/AGUIDojoClient/Components/Demos/SharedState/SharedStateDemo.razor +++ b/dotnet/samples/AGUIDojo/AGUIDojoClient/Components/Demos/SharedState/SharedStateDemo.razor @@ -114,6 +114,10 @@ + + + + diff --git a/dotnet/samples/AGUIDojo/AGUIDojoClient/Components/Demos/ToolBasedGenerativeUI/ToolBasedGenerativeUIDemo.razor b/dotnet/samples/AGUIDojo/AGUIDojoClient/Components/Demos/ToolBasedGenerativeUI/ToolBasedGenerativeUIDemo.razor index e0fe672261..b9484b0895 100644 --- a/dotnet/samples/AGUIDojo/AGUIDojoClient/Components/Demos/ToolBasedGenerativeUI/ToolBasedGenerativeUIDemo.razor +++ b/dotnet/samples/AGUIDojo/AGUIDojoClient/Components/Demos/ToolBasedGenerativeUI/ToolBasedGenerativeUIDemo.razor @@ -28,6 +28,10 @@ + + + + diff --git a/dotnet/samples/AGUIDojo/AGUIDojoClient/wwwroot/app.css b/dotnet/samples/AGUIDojo/AGUIDojoClient/wwwroot/app.css index 59b961673b..88be8a8d21 100644 --- a/dotnet/samples/AGUIDojo/AGUIDojoClient/wwwroot/app.css +++ b/dotnet/samples/AGUIDojo/AGUIDojoClient/wwwroot/app.css @@ -283,6 +283,11 @@ h1:focus { line-height: 1.5; } +/* Hide empty message bubbles (e.g., function calls with no visual template) */ +.chat-message:empty { + display: none; +} + /* User messages - right aligned with blue background */ .chat-message.user-message { align-self: flex-end;