Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions dotnet/src/Microsoft.Agents.AI.AGUI/AGUIChatClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,7 @@ public async IAsyncEnumerable<ChatResponseUpdate> GetStreamingResponseAsync(
RunId = runId,
Messages = messagesList.AsAGUIMessages(this._jsonSerializerOptions),
State = state,
ForwardedProperties = ExtractForwardedPropertiesFromOptions(options),
};

// Add tools if provided
Expand Down Expand Up @@ -288,6 +289,18 @@ public async IAsyncEnumerable<ChatResponseUpdate> GetStreamingResponseAsync(
return threadId;
}

private static JsonElement ExtractForwardedPropertiesFromOptions(ChatOptions? options)
{
if (options?.AdditionalProperties is null ||
!options.AdditionalProperties.TryGetValue("ag_ui_forwarded_properties", out object? forwardedProperties) ||
forwardedProperties is not JsonElement jsonElement)
{
return default;
}

return jsonElement;
}
Comment on lines +292 to +302

// Extract the session id from the second last message's function call content additional properties
private static string? ExtractTemporaryThreadId(List<ChatMessage> messagesList)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1395,6 +1395,44 @@ public async Task GetStreamingResponseAsync_WithNoStateDataContent_SendsEmptySta
Assert.Null(captureHandler.CapturedState);
}

[Fact]
public async Task GetStreamingResponseAsync_ForwardsAdditionalPropertiesInRunInputAsync()
{
// Arrange
var forwardedProperties = new { tenant = "contoso", conversation = "abc123" };

var captureHandler = new StateCapturingTestDelegatingHandler();
captureHandler.AddResponse(
[
new RunStartedEvent { ThreadId = "thread1", RunId = "run1" },
new RunFinishedEvent { ThreadId = "thread1", RunId = "run1" }
]);
using HttpClient httpClient = new(captureHandler);

var chatClient = new AGUIChatClient(httpClient, "http://localhost/agent", null, AGUIJsonSerializerContext.Default.Options);
var options = new ChatOptions
{
AdditionalProperties = new AdditionalPropertiesDictionary
{
["ag_ui_forwarded_properties"] = JsonSerializer.SerializeToElement(forwardedProperties)
}
};
List<ChatMessage> messages = [new ChatMessage(ChatRole.User, "Hello")];

// Act
await foreach (var _ in chatClient.GetStreamingResponseAsync(messages, options))
{
// Just consume the stream
}

// Assert
Assert.True(captureHandler.RequestWasMade);
Assert.NotNull(captureHandler.CapturedForwardedProperties);
Assert.Equal(JsonValueKind.Object, captureHandler.CapturedForwardedProperties.Value.ValueKind);
Assert.Equal("contoso", captureHandler.CapturedForwardedProperties.Value.GetProperty("tenant").GetString());
Assert.Equal("abc123", captureHandler.CapturedForwardedProperties.Value.GetProperty("conversation").GetString());
}

[Fact]
public async Task GetStreamingResponseAsync_WithMalformedStateJson_ThrowsInvalidOperationExceptionAsync()
{
Expand Down Expand Up @@ -1730,6 +1768,7 @@ internal sealed class StateCapturingTestDelegatingHandler : DelegatingHandler

public bool RequestWasMade { get; private set; }
public JsonElement? CapturedState { get; private set; }
public JsonElement? CapturedForwardedProperties { get; private set; }
public int CapturedMessageCount { get; private set; }
public IReadOnlyList<int> CapturedMessageCounts => this._capturedMessageCounts;

Expand All @@ -1755,6 +1794,10 @@ protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage
{
this.CapturedState = input.State;
}
if (input.ForwardedProperties.ValueKind is not JsonValueKind.Undefined and not JsonValueKind.Null)
{
this.CapturedForwardedProperties = input.ForwardedProperties;
}
this.CapturedMessageCount = input.Messages.Count();
this._capturedMessageCounts.Add(this.CapturedMessageCount);
}
Expand Down