Skip to content

Add support for streaming text IAsyncEnumerable<string> results #50501

@alexminza

Description

@alexminza

Is there an existing issue for this?

  • I have searched the existing issues

Is your feature request related to a problem? Please describe the problem.

I am trying to return a streaming IAsyncEnumerable<string> SematicKernel chat completion from GetStreamingChatCompletionsAsync, GetStreamingChatMessageAsync methods.

Currently simply returning IAsyncEnumerable<string> produces a streaming JSON array of strings result. The desired behavior is simple streaming text strings result.

This would effectively produce a streaming ChatGTP-like completion response generated by the method as the results become available from the OpenAI endpoints.

Describe the solution you'd like

public class AsyncEnumerableStringsResult : IResult, IContentTypeHttpResult, IStatusCodeHttpResult
{
    protected readonly IAsyncEnumerable<string> chunks;

    public string? ContentType => "text/plain; charset=utf-8";

    public int StatusCode => StatusCodes.Status200OK;

    int? IStatusCodeHttpResult.StatusCode => StatusCode;

    public AsyncEnumerableStringsResult(IAsyncEnumerable<string> chunks) => this.chunks = chunks ?? throw new ArgumentNullException(nameof(chunks));

    public async Task ExecuteAsync(HttpContext httpContext)
    {
        if (httpContext == null)
            throw new ArgumentNullException(nameof(httpContext));

        httpContext.Response.ContentType = this.ContentType;
        httpContext.Response.StatusCode = this.StatusCode;

        await foreach (var chunk in this.chunks)
            if (!string.IsNullOrEmpty(chunk))
                await httpContext.Response.WriteAsync(chunk, cancellationToken: httpContext.RequestAborted);
    }
}

Usage example:

app.MapPost("/ChatAsyncStream", ([FromBody] ChatRequest chatRequest, ChatPlugin plugin, ILogger logger, CancellationToken cancellationToken) =>
    {
        if (string.IsNullOrWhiteSpace(chatRequest.Question))
            throw new ArgumentNullException(nameof(chatRequest.Question));

        var result = plugin.ChatAsyncStream(
            question: chatRequest.Question,
            chatHistory: chatRequest.ChatHistory,
            logger: logger,
            cancellationToken: cancellationToken
        );

        return new AsyncEnumerableStringsResult(result);
    })
    .WithName("ChatAsyncStream")
    .WithOpenApi()
    .Produces<IAsyncEnumerable<string>>();

Additional context

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    area-minimalIncludes minimal APIs, endpoint filters, parameter binding, request delegate generator etc

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions