Skip to content

.NET: Features/3768-devui-aspire-integration#3771

Open
tommasodotNET wants to merge 25 commits intomicrosoft:mainfrom
tommasodotNET:features/3768-devui-aspire-integration
Open

.NET: Features/3768-devui-aspire-integration#3771
tommasodotNET wants to merge 25 commits intomicrosoft:mainfrom
tommasodotNET:features/3768-devui-aspire-integration

Conversation

@tommasodotNET
Copy link

@tommasodotNET tommasodotNET commented Feb 9, 2026

Motivation and Context

Adds a new Aspire hosting library that provides a DevUI resource for testing and debugging AI agents built with Microsoft Agent Framework.

Closes #3768

Description

This PR introduces Aspire.Hosting.AgentFramework — an extension library that enables Aspire AppHost projects to spin up a unified DevUI for interacting with multiple agent services during development.

Key Features

  • AddDevUI() extension method — Registers a DevUI resource in the Aspire AppHost
  • WithAgentService() fluent API — Connects agent services to the DevUI with optional metadata
  • In-process aggregator — A lightweight Kestrel server running inside the AppHost that:
    • Serves the DevUI frontend from embedded resources (no external container required)
    • Aggregates /v1/entities listings across all configured backends
    • Routes requests to the correct backend based on entity ID prefix
    • Streams SSE responses for real-time agent output

Usage

var writerAgent = builder.AddProject<Projects.WriterAgent>("writer-agent");
var editorAgent = builder.AddProject<Projects.EditorAgent>("editor-agent");

var devui = builder.AddDevUI("devui")
    .WithAgentService(writerAgent)
    .WithAgentService(editorAgent)
    .WaitFor(writerAgent)
    .WaitFor(editorAgent);

Contribution Checklist

  • The code builds clean without any errors or warnings
  • The PR follows the Contribution Guidelines
  • All unit tests pass, and I have added new tests where possible
  • Is this a breaking change? If yes, add "[BREAKING]" prefix to the title of the PR.

Copilot AI review requested due to automatic review settings February 9, 2026 18:43
@markwallace-microsoft markwallace-microsoft added documentation Improvements or additions to documentation .NET labels Feb 9, 2026
@github-actions github-actions bot changed the title Features/3768-devui-aspire-integration .NET: Features/3768-devui-aspire-integration Feb 9, 2026
Copy link
Contributor

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.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Copy link
Contributor

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

Copilot reviewed 29 out of 29 changed files in this pull request and generated no new comments.

Copy link
Contributor

@victordibia victordibia left a comment

Choose a reason for hiding this comment

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

Nice work — clean architecture, idiomatic Aspire API. I tested routing with mock backends and found a couple of things worth flagging:


Conversation routing for non-first backends

When a conversation is created on the second backend (e.g., editor-agent) via POST with agent_id context, the routing works perfectly. But when the frontend later does GET /v1/conversations/{id} or GET /v1/conversations/{id}/items to load history, there's no agent_id in the query string and no request body (it's a GET), so the aggregator falls through to the first backend at the backendUrl ??= this.ResolveBackends().Values.FirstOrDefault() fallback in DevUIAggregatorHostedService.cs around line 525. That backend doesn't have the conversation, so it returns a 404.

Suggested fix: An in-memory ConcurrentDictionary<string, string> mapping conversationId → backendPrefix, populated when the aggregator proxies POST /v1/conversations. On subsequent GETs, look up the conversation ID to route to the right backend before falling through to FirstOrDefault(). This avoids rewriting conversation IDs (which flow through responses, items, and checkpoints) and is consistent with the existing InMemoryAgentConversationIndex pattern in the .NET backend. The "lost on restart" trade-off is fine for a dev-only tool.


Default agent naming when agents: is omitted

In AgentFrameworkBuilderExtensions.cs line 175, agents ??= [new AgentEntityInfo(agentService.Resource.Name)] uses the Aspire resource name (e.g., "writer-agent") as the agent ID. But the backend typically registers the agent with a different name (e.g., builder.AddAIAgent("writer", ...)). So the aggregator sends entity_id="writer-agent" but the backend only knows "writer".

The sample works fine because it explicitly passes agents: [new("writer")], but someone following the API without reading the sample might hit this.

Suggested fix: When agents isn't provided, query /v1/entities from the backend at startup instead of defaulting to the resource name. The runtime discovery path already exists in AggregateEntitiesAsync — just reuse that fallback. Alternatively, make agents required so the intent is always explicit.


Minor things (not blocking)

  • DevUIAggregatorHostedService.cs ~line 221 — TryServeResourceAsync calls Assembly.Load() + GetManifestResourceStream() on each request. The existing DevUIMiddleware caches the content bytes at startup in a FrozenDictionary. Might be nice to align these, though it's fine for dev-only usage.
  • .csproj line 11 — The VSTHRD002 warning suppression is project-wide; could be scoped to just the shutdown handler in AgentFrameworkBuilderExtensions.cs lines 104-107.
  • The sample app hangs on "Provisioning foundry-roles..." during Azure RBAC provisioning, which can be slow or fail silently. Might be worth adding a note in the sample README about Azure prereqs, or providing a local-only configuration that doesn't require Foundry.

Overall, looks good

@tommasodotNET
Copy link
Author

Default agent naming when agents: is omitted
In AgentFrameworkBuilderExtensions.cs line 175, agents ??= [new AgentEntityInfo(agentService.Resource.Name)] uses the Aspire resource name (e.g., "writer-agent") as the agent ID. But the backend typically registers the agent with a different name (e.g., builder.AddAIAgent("writer", ...)). So the aggregator sends entity_id="writer-agent" but the backend only knows "writer".

The sample works fine because it explicitly passes agents: [new("writer")], but someone following the API without reading the sample might hit this.

Suggested fix: When agents isn't provided, query /v1/entities from the backend at startup instead of defaulting to the resource name. The runtime discovery path already exists in AggregateEntitiesAsync — just reuse that fallback. Alternatively, make agents required so the intent is always explicit.

this is not as easy. since the agents are not mapping devui, they don't have a /v1/entities endpoint. i might be wrong here, but i remember is MapDevUI that will make that path available.
dealing with a distributed system, we cannot rely on self discovery and it's easier to let the user map the correct name of the agent to inject the correct agent-id + endpoint in devui.
i've added a comment line to clarify this and also produced a detailed readme for the sample to describe this.

The sample app hangs on "Provisioning foundry-roles..." during Azure RBAC provisioning, which can be slow or fail silently. Might be worth adding a note in the sample README about Azure prereqs, or providing a local-only configuration that doesn't require Foundry.

when it comes to foundry, the apphost explicitly states that the resources are waiting for foundry to be ready. unexpected behaviours on aspire foundry integrations are known to aspire users, and the integration itself is being refactored (dotnet/aspire#14149). that being said, i've updated the apphost to explain foundry behaviour and make it easier for aspire to provision aspire for you, instead of connecting to an existing one.

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

Labels

documentation Improvements or additions to documentation .NET

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Python: [Feature]: Add Aspire DevUI Integration

4 participants