Files
SergeyMenshykh 9506fb28f6 .NET: [Breaking] Structured Output improvements (#3761)
* .NET: Delete AgentResponse.{Try}Deserialize<T> methods (#3518)

* delete deserialize method of agent response

* order usings

* Update dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step05_StructuredOutput/Program.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update dotnet/samples/GettingStarted/Workflows/_Foundational/08_WriterCriticWorkflow/Program.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update dotnet/samples/GettingStarted/AGUI/Step05_StateManagement/Server/SharedStateAgent.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update dotnet/samples/AGUIClientServer/AGUIDojoServer/SharedState/SharedStateAgent.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update dotnet/samples/M365Agent/Agents/WeatherForecastAgent.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* .NET:[Breaking] Add support for structured output (#3658)

* add support for so

* restore lost xml comment part

* fix using ordering

* Update dotnet/src/Microsoft.Agents.AI.Abstractions/AIAgentStructuredOutput.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update dotnet/src/Microsoft.Agents.AI.Abstractions/AIAgentStructuredOutput.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update dotnet/tests/Microsoft.Agents.AI.UnitTests/ChatClient/ChatClientAgent_SO_WithFormatResponseTests.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* addressw pr review comments

* address pr review feedback

* address pr review comments

* fix compilation issues after the latest merge with main

* remove unnecessry options

* remove RunAsync<object> methods

* address code review feedback

* address pr review feedback

* make copy constructor protected

* address pr review feedback

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* .NET: Add decorator for structured output support (#3694)

* add decorator that adds structured output support to agents that don't natively support it.

* Update dotnet/src/Microsoft.Agents.AI/StructuredOutput/StructuredOutputAgentResponse.cs

Co-authored-by: westey <164392973+westey-m@users.noreply.github.com>

* Update dotnet/samples/GettingStarted/Agents/Agent_Step05_StructuredOutput/Program.cs

Co-authored-by: westey <164392973+westey-m@users.noreply.github.com>

* address pr review feedback

---------

Co-authored-by: westey <164392973+westey-m@users.noreply.github.com>

* .NET: Support primitives and arrays for SO (#3696)

* wrap primitives and arrays

* fix file encoding

* address review comments

* add adr

* add missed change

* fix compilation issue

* address review comments

* rename adr file name

* reflect decision to have SO decorator as a reference implementation in samples

* .NET: Move SO agent to samples (#3820)

* move SO agent to samples

* change file encoding

* fix files encoding

* .NET: Preserve caller context (#3803)

* fix stuck orchestration

* add previously removed RunAsync<T> method to DurableAIAgent

* suppress IDE0005 warning

* update changelog and remove unused constructor of AgentResponse<T>

* updatge the changelog

* address PR review feedback

* .NET: Disable irrelevant integration test (#3913)

* disable irrelevant integration test

* Update dotnet/tests/AzureAI.IntegrationTests/AIProjectClientAgentStructuredOutputRunTests.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* forgotten change

* address pr review feedback

* disable intermittently failing integration test.

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: westey <164392973+westey-m@users.noreply.github.com>
9506fb28f6 ยท 2026-02-13 17:03:51 +00:00
History
..

AG-UI Client and Server Sample

This sample demonstrates how to use the AG-UI (Agent UI) protocol to enable communication between a client application and a remote agent server. The AG-UI protocol provides a standardized way for clients to interact with AI agents.

Overview

The demonstration has two components:

  1. AGUIServer - An ASP.NET Core web server that hosts an AI agent and exposes it via the AG-UI protocol
  2. AGUIClient - A console application that connects to the AG-UI server and displays streaming updates

Warning

The AG-UI protocol is still under development and changing. We will try to keep these samples updated as the protocol evolves.

Configuring Environment Variables

Configure the required Azure OpenAI environment variables:

$env:AZURE_OPENAI_ENDPOINT="<<your-model-endpoint>>"
$env:AZURE_OPENAI_DEPLOYMENT_NAME="gpt-4.1-mini"

Note: This sample uses DefaultAzureCredential for authentication. Make sure you're authenticated with Azure (e.g., via az login, Visual Studio, or environment variables).

Running the Sample

Step 1: Start the AG-UI Server

cd AGUIServer
dotnet build
dotnet run --urls "http://localhost:5100"

The server will start and listen on http://localhost:5100.

Step 2: Testing with the REST Client (Optional)

Before running the client, you can test the server using the included .http file:

  1. Open ./AGUIServer/AGUIServer.http in Visual Studio or VS Code with the REST Client extension
  2. Send a test request to verify the server is working
  3. Observe the server-sent events stream in the response

Sample request:

POST http://localhost:5100/
Content-Type: application/json

{
  "threadId": "thread_123",
  "runId": "run_456",
  "messages": [
    {
      "role": "user",
      "content": "What is the capital of France?"
    }
  ],
  "context": {}
}

Step 3: Run the AG-UI Client

In a new terminal window:

cd AGUIClient
dotnet run

Optionally, configure a different server URL:

$env:AGUI_SERVER_URL="http://localhost:5100"

Step 4: Interact with the Agent

  1. The client will connect to the AG-UI server
  2. Enter your message at the prompt
  3. Observe the streaming updates with color-coded output:
    • Yellow: Run started notification showing thread and run IDs
    • Cyan: Agent's text response (streamed character by character)
    • Green: Run finished notification
    • Red: Error messages (if any occur)
  4. Type :q or quit to exit

Sample Output

AGUIClient> dotnet run
info: AGUIClient[0]
      Connecting to AG-UI server at: http://localhost:5100

User (:q or quit to exit): What is the capital of France?

[Run Started - Thread: thread_abc123, Run: run_xyz789]
The capital of France is Paris. It is known for its rich history, culture, and iconic landmarks such as the Eiffel Tower and the Louvre Museum.
[Run Finished - Thread: thread_abc123, Run: run_xyz789]

User (:q or quit to exit): Tell me a fun fact about space

[Run Started - Thread: thread_abc123, Run: run_def456]
Here's a fun fact: A day on Venus is longer than its year! Venus takes about 243 Earth days to rotate once on its axis, but only about 225 Earth days to orbit the Sun.
[Run Finished - Thread: thread_abc123, Run: run_def456]

User (:q or quit to exit): :q

How It Works

Server Side

The AGUIServer uses the MapAGUI extension method to expose an agent through the AG-UI protocol:

AIAgent agent = new OpenAIClient(apiKey)
    .GetChatClient(model)
    .AsAIAgent(
        instructions: "You are a helpful assistant.",
        name: "AGUIAssistant");

app.MapAGUI("/", agent);

This automatically handles:

  • HTTP POST requests with message payloads
  • Converting agent responses to AG-UI event streams
  • Server-sent events (SSE) formatting
  • Thread and run management

Client Side

The AGUIClient uses the AGUIChatClient to connect to the remote server:

using HttpClient httpClient = new();
var chatClient = new AGUIChatClient(
    httpClient,
    endpoint: serverUrl,
    modelId: "agui-client",
    jsonSerializerOptions: null);

AIAgent agent = chatClient.AsAIAgent(
    instructions: null,
    name: "agui-client",
    description: "AG-UI Client Agent",
    tools: []);

bool isFirstUpdate = true;
AgentResponseUpdate? currentUpdate = null;

await foreach (AgentResponseUpdate update in agent.RunStreamingAsync(messages, thread))
{
    // First update indicates run started
    if (isFirstUpdate)
    {
        Console.WriteLine($"[Run Started - Thread: {update.ConversationId}, Run: {update.ResponseId}]");
        isFirstUpdate = false;
    }
    
    currentUpdate = update;
    
    foreach (AIContent content in update.Contents)
    {
        switch (content)
        {
            case TextContent textContent:
                // Display streaming text
                Console.Write(textContent.Text);
                break;
            case ErrorContent errorContent:
                // Display error notification
                Console.WriteLine($"[Error: {errorContent.Message}]");
                break;
        }
    }
}

// Last update indicates run finished
if (currentUpdate != null)
{
    Console.WriteLine($"\n[Run Finished - Thread: {currentUpdate.ConversationId}, Run: {currentUpdate.ResponseId}]");
}

The RunStreamingAsync method:

  1. Sends messages to the server via HTTP POST
  2. Receives server-sent events (SSE) stream
  3. Parses events into AgentResponseUpdate objects
  4. Yields updates as they arrive for real-time display

Key Concepts

  • Thread: Represents a conversation context that persists across multiple runs (accessed via ConversationId property)
  • Run: A single execution of the agent for a given set of messages (identified by ResponseId property)
  • AgentResponseUpdate: Contains the response data with:
    • ResponseId: The unique run identifier
    • ConversationId: The thread/conversation identifier
    • Contents: Collection of content items (TextContent, ErrorContent, etc.)
  • Run Lifecycle:
    • The first AgentResponseUpdate in a run indicates the run has started
    • Subsequent updates contain streaming content as the agent processes
    • The last AgentResponseUpdate in a run indicates the run has finished
    • If an error occurs, the update will contain ErrorContent