.NET: Update AGUI service to support session storage#5193
.NET: Update AGUI service to support session storage#5193westey-m wants to merge 2 commits intomicrosoft:mainfrom
Conversation
There was a problem hiding this comment.
Pull request overview
This PR updates the .NET AGUI ASP.NET Core hosting endpoint to integrate with the core hosting AgentSessionStore abstraction, enabling session persistence across requests (keyed by AG-UI thread ID). It also adds/updates tests and a sample to demonstrate the DI + in-memory session store setup.
Changes:
- Add
MapAGUIoverloads that resolve a named agent from DI (viaIHostedAgentBuilderoragentName). - Wrap agent execution with
AIHostAgentand persist/restore sessions using a keyedAgentSessionStore(fallback to noop when not registered). - Add unit/integration tests and update the end-to-end sample to use
WithInMemorySessionStore.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| dotnet/src/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore/AGUIEndpointRouteBuilderExtensions.cs | Adds DI-based MapAGUI overloads and session persistence logic using AIHostAgent + AgentSessionStore. |
| dotnet/src/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.csproj | Adds reference to the core hosting project to access session store abstractions. |
| dotnet/tests/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.UnitTests/AGUIEndpointRouteBuilderExtensionsTests.cs | Adds unit tests for new overloads and session store/noop fallback behavior. |
| dotnet/tests/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.IntegrationTests/SessionPersistenceTests.cs | Adds integration tests validating multi-turn session persistence via in-memory session store. |
| dotnet/tests/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.IntegrationTests/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.IntegrationTests.csproj | Adds hosting project reference needed for WithInMemorySessionStore() usage in tests. |
| dotnet/samples/05-end-to-end/AGUIClientServer/AGUIServer/Program.cs | Updates sample to register a named agent in DI and enable in-memory session persistence. |
| using System.Linq; | ||
| using System.Runtime.CompilerServices; | ||
| using System.Threading; | ||
| using System.Threading.Tasks; |
There was a problem hiding this comment.
using System.Threading.Tasks; appears unused in this file and will trigger CS8019 (unnecessary using directive) in typical builds. Please remove it (and any other unused usings) to keep builds warning-free.
| using System.Threading.Tasks; |
| this IEndpointRouteBuilder endpoints, | ||
| IHostedAgentBuilder agentBuilder, | ||
| [StringSyntax("route")] string pattern) | ||
| { |
There was a problem hiding this comment.
The MapAGUI(IHostedAgentBuilder, ...) overload doesn't validate endpoints. If a caller passes null explicitly, this will throw a NullReferenceException when calling endpoints.MapAGUI(...) instead of an ArgumentNullException. Add ArgumentNullException.ThrowIfNull(endpoints); at the start for consistency with the other overloads.
| { | |
| { | |
| ArgumentNullException.ThrowIfNull(endpoints); |
| ArgumentNullException.ThrowIfNull(endpoints); | ||
| ArgumentNullException.ThrowIfNull(aiAgent); | ||
|
|
||
| var agentSessionStore = endpoints.ServiceProvider.GetKeyedService<AgentSessionStore>(aiAgent.Name); |
There was a problem hiding this comment.
aiAgent.Name is nullable. Passing a null/whitespace key into GetKeyedService<AgentSessionStore>(aiAgent.Name) can lead to surprising behavior (e.g., resolving an unkeyed store, or later failing when creating sessions). Consider guarding with string.IsNullOrWhiteSpace(aiAgent.Name) and treating that as "no store registered" (use NoopAgentSessionStore).
| var agentSessionStore = endpoints.ServiceProvider.GetKeyedService<AgentSessionStore>(aiAgent.Name); | |
| var agentSessionStore = string.IsNullOrWhiteSpace(aiAgent.Name) | |
| ? null | |
| : endpoints.ServiceProvider.GetKeyedService<AgentSessionStore>(aiAgent.Name); |
| var agentSessionStore = endpoints.ServiceProvider.GetKeyedService<AgentSessionStore>(aiAgent.Name); | ||
| var hostAgent = new AIHostAgent(aiAgent, agentSessionStore ?? new NoopAgentSessionStore()); | ||
|
|
There was a problem hiding this comment.
The session store (and AIHostAgent) are resolved/constructed at endpoint-mapping time via endpoints.ServiceProvider. If a user registers an AgentSessionStore with Scoped/Transient lifetime (supported by WithSessionStore(..., lifetime)), resolving it from the root provider can throw ("Cannot resolve scoped service...") or capture the wrong lifetime. Resolve the store from HttpContext.RequestServices inside the request handler (or otherwise create the AIHostAgent per request) to respect DI lifetimes.
| AdditionalProperties = new AdditionalPropertiesDictionary | ||
| { | ||
| ["ag_ui_state"] = input.State, | ||
| ["ag_ui_context"] = input.Context?.Select(c => new KeyValuePair<string, string>(c.Description, c.Value)).ToArray(), | ||
| ["ag_ui_forwarded_properties"] = input.ForwardedProperties, | ||
| ["ag_ui_thread_id"] = input.ThreadId, | ||
| ["ag_ui_run_id"] = input.RunId | ||
| } | ||
| } | ||
| }; | ||
|
|
||
| var threadId = input.ThreadId ?? Guid.NewGuid().ToString("N"); | ||
| var session = await hostAgent.GetOrCreateSessionAsync(threadId, cancellationToken).ConfigureAwait(false); |
There was a problem hiding this comment.
threadId is only regenerated when input.ThreadId is null, but ThreadId can be empty/whitespace (and AIHostAgent.GetOrCreateSessionAsync will throw on whitespace). Compute threadId using string.IsNullOrWhiteSpace(input.ThreadId) and then use that computed threadId consistently, including in ChatOptions.AdditionalProperties["ag_ui_thread_id"] (currently it stores the original input.ThreadId).
dotnet/samples/05-end-to-end/AGUIClientServer/AGUIServer/Program.cs
Outdated
Show resolved
Hide resolved
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Motivation and Context
AGUI wasn't supporting session store as defined in the core hosting package.
Description
Contribution Checklist