diff --git a/src/CodexSdk.MAF/CodexAIAgent.cs b/src/CodexSdk.MAF/CodexAIAgent.cs index 0b7b28f..3cb19c2 100644 --- a/src/CodexSdk.MAF/CodexAIAgent.cs +++ b/src/CodexSdk.MAF/CodexAIAgent.cs @@ -112,7 +112,7 @@ protected override async Task RunCoreAsync( break; } - var update = threadEvent.ToAgentResponseUpdate(); + var update = threadEvent.ToAgentResponseUpdate(_options.ThreadOptions.Model); if (update?.ShouldReturnAsResponseMessage() == true) { responseMessages.Add(update.ToChatMessage()); @@ -174,7 +174,7 @@ protected override async IAsyncEnumerable RunCoreStreamingA cancellationToken); } - var update = threadEvent.ToAgentResponseUpdate(); + var update = threadEvent.ToAgentResponseUpdate(_options.ThreadOptions.Model); if (update is null) { continue; diff --git a/src/CodexSdk.MAF/Internal/AgentResponseUpdateExtensions.cs b/src/CodexSdk.MAF/Internal/AgentResponseUpdateExtensions.cs index 75d40b9..744e874 100644 --- a/src/CodexSdk.MAF/Internal/AgentResponseUpdateExtensions.cs +++ b/src/CodexSdk.MAF/Internal/AgentResponseUpdateExtensions.cs @@ -24,7 +24,7 @@ public static ChatMessage ToChatMessage([NotNull] this AgentResponseUpdate updat return new ChatMessage { AdditionalProperties = update.AdditionalProperties, - AuthorName = update.AuthorName, + AuthorName = ThreadEventExtensions.AgentName, Contents = update.Contents, CreatedAt = update.CreatedAt, MessageId = update.MessageId, diff --git a/src/CodexSdk.MAF/Internal/ThreadEventExtensions.cs b/src/CodexSdk.MAF/Internal/ThreadEventExtensions.cs index 9afda80..d01fb43 100644 --- a/src/CodexSdk.MAF/Internal/ThreadEventExtensions.cs +++ b/src/CodexSdk.MAF/Internal/ThreadEventExtensions.cs @@ -7,10 +7,13 @@ namespace OpenAI.CodexSdk.MAF.Internal; internal static class ThreadEventExtensions { - private const string AgentName = "codex"; + internal const string AgentName = "codex"; + private const string ModelNamePropertyName = "modelName"; private const string CommandExecutionFunctionName = "command_execution"; - public static AgentResponseUpdate? ToAgentResponseUpdate(this ThreadEvent threadEvent) + public static AgentResponseUpdate? ToAgentResponseUpdate( + this ThreadEvent threadEvent, + string? modelName = null) { var update = threadEvent switch { @@ -26,15 +29,17 @@ internal static class ThreadEventExtensions if (update is not null) { - if (string.IsNullOrWhiteSpace(update.AuthorName)) - { - update.AuthorName = AgentName; - } + update.AuthorName = AgentName; + update.AdditionalProperties ??= []; + update.AdditionalProperties[ModelNamePropertyName] = NormalizeModelName(modelName); } return update; } + private static string NormalizeModelName(string? modelName) => + string.IsNullOrWhiteSpace(modelName) ? string.Empty : modelName.Trim(); + public static UsageDetails ToUsageDetails(this Usage usage) { return new UsageDetails @@ -54,7 +59,6 @@ private static AgentResponseUpdate CreateLifecycleUpdate(string eventType) Role = ChatRole.System, AdditionalProperties = new AdditionalPropertiesDictionary { - { "agentName", AgentName }, { "type", eventType }, }, Contents = [new TextContent(eventType)], @@ -68,7 +72,6 @@ private static AgentResponseUpdate CreateUsageUpdate(Usage usage) Role = ChatRole.System, AdditionalProperties = new AdditionalPropertiesDictionary { - { "agentName", AgentName }, { "type", "turn.completed" }, }, Contents = @@ -85,7 +88,6 @@ private static AgentResponseUpdate CreateErrorUpdate(string message, string even Role = ChatRole.System, AdditionalProperties = new AdditionalPropertiesDictionary { - { "agentName", AgentName }, { "type", eventType }, }, Contents = [CreateErrorContent(message, terminal)], @@ -101,7 +103,6 @@ private static AgentResponseUpdate CreateItemUpdate(string eventType, ThreadItem Role = role, AdditionalProperties = new AdditionalPropertiesDictionary { - { "agentName", AgentName }, { "type", eventType }, { "itemType", item.GetType().Name }, }, diff --git a/tests/CodexSdk.MAF.Tests/ThreadEventExtensionsTests.cs b/tests/CodexSdk.MAF.Tests/ThreadEventExtensionsTests.cs index c566b80..a0d5c3b 100644 --- a/tests/CodexSdk.MAF.Tests/ThreadEventExtensionsTests.cs +++ b/tests/CodexSdk.MAF.Tests/ThreadEventExtensionsTests.cs @@ -1,3 +1,4 @@ +using Microsoft.Agents.AI; using Microsoft.Extensions.AI; using OpenAI.CodexSdk; using OpenAI.CodexSdk.MAF.Internal; @@ -71,6 +72,9 @@ public void ToAgentResponseUpdate_WhenCommandExecutionStartedEvent_ReturnsFuncti Assert.NotNull(update); Assert.Equal("item_3", update.MessageId); Assert.Equal(ChatRole.Assistant, update.Role); + Assert.Equal("codex", update.AuthorName); + Assert.False(update.AdditionalProperties!.ContainsKey("agentName")); + Assert.Equal(string.Empty, update.AdditionalProperties["modelName"]); var content = Assert.IsType(Assert.Single(update.Contents)); Assert.Equal("item_3", content.CallId); Assert.Equal("command_execution", content.Name); @@ -81,17 +85,60 @@ public void ToAgentResponseUpdate_WhenCommandExecutionStartedEvent_ReturnsFuncti [Fact] public void ToAgentResponseUpdate_WhenTurnStarts_ReturnsVisibleSystemContent() { - var update = new TurnStartedEvent().ToAgentResponseUpdate(); + var update = new TurnStartedEvent().ToAgentResponseUpdate("gpt-5.6-sol"); Assert.NotNull(update); Assert.Equal(ChatRole.System, update.Role); Assert.Equal("codex", update.AuthorName); + Assert.False(update.AdditionalProperties!.ContainsKey("agentName")); + Assert.Equal("gpt-5.6-sol", update.AdditionalProperties["modelName"]); Assert.Equal("turn.started", update.AdditionalProperties!["type"]); Assert.Equal("turn.started", Assert.IsType(Assert.Single(update.Contents)).Text); Assert.True(update.ShouldReturnAsResponseMessage()); Assert.False(update.ShouldSaveAsResponseMessage()); } + [Theory] + [InlineData(" gpt-5.6-sol ", "gpt-5.6-sol")] + [InlineData(null, "")] + [InlineData(" ", "")] + public void ToAgentResponseUpdate_WhenAssistantItem_MapsConfiguredModelName( + string? configuredModel, + string expectedModelName) + { + // Arrange + var threadEvent = new ItemCompletedEvent + { + Item = new AgentMessageItem { Id = "message-1", Text = "done" }, + }; + + // Act + var update = threadEvent.ToAgentResponseUpdate(configuredModel); + var persisted = update!.ToChatMessage(); + + // Assert + Assert.Equal(ChatRole.Assistant, update.Role); + Assert.Equal("codex", update.AuthorName); + Assert.False(update.AdditionalProperties!.ContainsKey("agentName")); + Assert.Equal(expectedModelName, update.AdditionalProperties["modelName"]); + Assert.Equal("codex", persisted.AuthorName); + Assert.False(persisted.AdditionalProperties!.ContainsKey("agentName")); + Assert.Equal(expectedModelName, persisted.AdditionalProperties!["modelName"]); + } + + [Fact] + public void ToChatMessage_WhenUpdateAuthorIsMissing_UsesAgentName() + { + // Arrange + var update = new AgentResponseUpdate(ChatRole.Assistant, "done"); + + // Act + var message = update.ToChatMessage(); + + // Assert + Assert.Equal("codex", message.AuthorName); + } + [Fact] public void ToAgentResponseUpdate_WhenTurnCompletes_MapsCompleteUsage() { @@ -108,10 +155,13 @@ public void ToAgentResponseUpdate_WhenTurnCompletes_MapsCompleteUsage() }; // Act - var update = threadEvent.ToAgentResponseUpdate(); + var update = threadEvent.ToAgentResponseUpdate("gpt-5.6-sol"); // Assert Assert.NotNull(update); + Assert.Equal("codex", update.AuthorName); + Assert.False(update.AdditionalProperties!.ContainsKey("agentName")); + Assert.Equal("gpt-5.6-sol", update.AdditionalProperties["modelName"]); var usage = Assert.IsType(Assert.Single(update.Contents)).Details; Assert.Equal(100L, usage.InputTokenCount); Assert.Equal(60L, usage.CachedInputTokenCount); @@ -136,11 +186,14 @@ public void ToAgentResponseUpdate_WhenCommandExecutionCompletedEvent_ReturnsFunc }, }; - var update = threadEvent.ToAgentResponseUpdate(); + var update = threadEvent.ToAgentResponseUpdate("gpt-5.6-sol"); Assert.NotNull(update); Assert.Equal("item_3", update.MessageId); Assert.Equal(ChatRole.Tool, update.Role); + Assert.Equal("codex", update.AuthorName); + Assert.False(update.AdditionalProperties!.ContainsKey("agentName")); + Assert.Equal("gpt-5.6-sol", update.AdditionalProperties["modelName"]); var content = Assert.IsType(Assert.Single(update.Contents)); Assert.Equal("item_3", content.CallId); var result = Assert.IsType>(content.Result);