Skip to content

Foundry Playground only receives 3 SSE events from custom IChatClient hosted agents #645

@davidjrh

Description

@davidjrh

Foundry Playground only receives 3 SSE events from custom IChatClient hosted agents — even with hardcoded responses

Bug Description

When deploying a custom hosted agent using Azure.AI.AgentServer.AgentFramework with a custom IChatClient implementation, the Azure AI Foundry playground only receives 3 SSE events before the stream appears to terminate:

  1. response.created
  2. response.in_progress
  3. response.output_item.added — with status: "completed" and content: []

All subsequent events (content_part.added, output_text.delta, content_part.done, output_item.done, response.completed) are never received by the browser, even though server-side logs confirm the agent produces them correctly.

Reproduction

Minimal IChatClient (50 lines, no external dependencies)

using System.Runtime.CompilerServices;
using Microsoft.Extensions.AI;

namespace LibreChatAgent;

public sealed class SimpleChatClient : IChatClient
{
    public ChatClientMetadata Metadata => new("SimpleTest");

    public object? GetService(Type serviceType, object? serviceKey = null)
    {
        if (serviceKey is null && serviceType?.IsInstanceOfType(this) == true)
            return this;
        return null;
    }

    public Task<ChatResponse> GetResponseAsync(
        IEnumerable<ChatMessage> chatMessages,
        ChatOptions? options = null,
        CancellationToken cancellationToken = default)
    {
        return Task.FromResult(
            new ChatResponse([new ChatMessage(ChatRole.Assistant, "Hello from SimpleAgent!")]));
    }

    public async IAsyncEnumerable<ChatResponseUpdate> GetStreamingResponseAsync(
        IEnumerable<ChatMessage> chatMessages,
        ChatOptions? options = null,
        [EnumeratorCancellation] CancellationToken cancellationToken = default)
    {
        var words = new[] { "Hello ", "from ", "Simple", "Agent!" };
        foreach (var word in words)
        {
            yield return new ChatResponseUpdate
            {
                Role = ChatRole.Assistant,
                Contents = [new TextContent(word)]
            };
            await Task.Delay(50, cancellationToken);
        }
    }

    public void Dispose() { }
}

Program.cs (matches official sample pattern exactly)

using Azure.AI.AgentServer.AgentFramework.Extensions;
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;

IChatClient chatClient = new LibreChatAgent.SimpleChatClient()
    .AsBuilder()
    .Build();

var agent = new ChatClientAgent(chatClient, name: "LibreChatAgent")
    .AsBuilder()
    .Build();

await agent.RunAIAgentAsync(telemetrySourceName: "LibreChatAgent");

agent.yaml

kind: hosted
name: LibreChatAgent
description: Minimal test agent
protocols:
    - protocol: responses
      version: v1

Dockerfile

FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
WORKDIR /src
COPY src/LibreChatAgent/ .
RUN dotnet restore
RUN dotnet build -c Release --no-restore
RUN dotnet publish -c Release --no-build -o /app

FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS final
WORKDIR /app
COPY --from=build /app .
EXPOSE 8088
ENTRYPOINT ["dotnet", "LibreChatAgent.dll"]

.csproj

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net10.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Azure.AI.AgentServer.AgentFramework" Version="1.0.0-beta.11" />
  </ItemGroup>
</Project>

What happens locally

When tested locally via curl http://localhost:8088/responses, the agent produces all 11 SSE events correctly:

event: response.created
event: response.in_progress
event: response.output_item.added
event: content_part.added
event: output_text.delta (x4, one per word)
event: content_part.done
event: output_item.done
event: response.completed

The response completes in ~200ms with the full text "Hello from SimpleAgent!".

What happens in the Foundry playground

The browser's EventSource receives only 3 events, then the stream ends:

// Event 1: response.created
{"id":"...","object":"response","status":"in_progress",...}

// Event 2: response.in_progress  
{"id":"...","object":"response","status":"in_progress",...}

// Event 3: response.output_item.added
{"id":"...","object":"response","status":"completed","output":[{"type":"message","status":"completed","content":[]}],...}

Note the suspicious "status": "completed" and "content": [] in the 3rd event. The response is marked as completed with no content before any text deltas arrive.

What we've ruled out

Hypothesis Test Result
Issue in our backend (LibreChat) Replaced with hardcoded SimpleChatClient above Same 3 events
Framework version mismatch Tested both beta.10 (matching official sample) and beta.11 Same 3 events
Docker base image issue Tested both Alpine and standard images Same 3 events
Missing .AsBuilder().Build() Added wrapping on both IChatClient and ChatClientAgent Same 3 events
Streaming timing/delays Tested burst mode (no delays), keepalive mode, various delays Same 3 events
ASPNETCORE_URLS or env vars Removed all custom env vars Same 3 events

Environment

  • Region: Sweden Central
  • Framework: Azure.AI.AgentServer.AgentFramework v1.0.0-beta.11 (also tested beta.10)
  • Runtime: .NET 10
  • Protocol: Responses v1
  • Deployed via: azd deploy (tested versions 31–37)
  • Official sample reference: scenario-1-intro/src/time-zone-agent

Key Observation

The official time-zone-agent sample uses AzureOpenAIClient as the IChatClient, which delegates to an Azure OpenAI model deployment. This means the framework's SSE-to-Responses translation path for Azure OpenAI models may work, but the path for custom IChatClient implementations that generate their own streaming updates appears broken.

The output_item.added event arriving with status: "completed" suggests the Foundry gateway or the ItemResourceGenerator in the framework is prematurely marking the response as complete before consuming the streaming output from GetStreamingResponseAsync.

Expected Behavior

All SSE events generated by the framework should be forwarded to the playground browser, including text deltas and completion events. The agent should display "Hello from SimpleAgent!" in the playground chat.

Actual Behavior

Only 3 events arrive. The playground shows an empty response or no visible text content.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions