diff --git a/dotnet/Directory.Packages.props b/dotnet/Directory.Packages.props
index 98c7376aaf..d0227c1dbe 100644
--- a/dotnet/Directory.Packages.props
+++ b/dotnet/Directory.Packages.props
@@ -33,18 +33,18 @@
-
+
-
+
-
-
+
+
@@ -61,9 +61,9 @@
-
-
-
+
+
+
@@ -71,11 +71,11 @@
-
+
-
+
diff --git a/dotnet/samples/GettingStarted/AgentWithOpenAI/Agent_OpenAI_Step02_Reasoning/Program.cs b/dotnet/samples/GettingStarted/AgentWithOpenAI/Agent_OpenAI_Step02_Reasoning/Program.cs
index aa18fdd286..426b40f1f5 100644
--- a/dotnet/samples/GettingStarted/AgentWithOpenAI/Agent_OpenAI_Step02_Reasoning/Program.cs
+++ b/dotnet/samples/GettingStarted/AgentWithOpenAI/Agent_OpenAI_Step02_Reasoning/Program.cs
@@ -5,7 +5,6 @@
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
using OpenAI;
-using OpenAI.Responses;
var apiKey = Environment.GetEnvironmentVariable("OPENAI_API_KEY") ?? throw new InvalidOperationException("OPENAI_API_KEY is not set.");
var model = Environment.GetEnvironmentVariable("OPENAI_MODEL") ?? "gpt-5";
@@ -15,14 +14,10 @@
.AsIChatClient().AsBuilder()
.ConfigureOptions(o =>
{
- o.RawRepresentationFactory = _ => new CreateResponseOptions()
+ o.Reasoning = new()
{
- ReasoningOptions = new()
- {
- ReasoningEffortLevel = ResponseReasoningEffortLevel.Medium,
- // Verbosity requires OpenAI verified Organization
- ReasoningSummaryVerbosity = ResponseReasoningSummaryVerbosity.Detailed
- }
+ Effort = ReasoningEffort.Medium,
+ Output = ReasoningOutput.Full,
};
}).Build();
diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step10_UsingImages/Program.cs b/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step10_UsingImages/Program.cs
index 8f38378626..a266aed278 100644
--- a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step10_UsingImages/Program.cs
+++ b/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step10_UsingImages/Program.cs
@@ -21,7 +21,7 @@
ChatMessage message = new(ChatRole.User, [
new TextContent("What do you see in this image?"),
- new DataContent(File.ReadAllBytes("assets/walkway.jpg"), "image/jpeg")
+ await DataContent.LoadFromAsync("assets/walkway.jpg"),
]);
AgentSession session = await agent.CreateSessionAsync();
diff --git a/dotnet/src/Microsoft.Agents.AI.AzureAI/AzureAIProjectChatClientExtensions.cs b/dotnet/src/Microsoft.Agents.AI.AzureAI/AzureAIProjectChatClientExtensions.cs
index 18a5ba3b72..8433ff3b6f 100644
--- a/dotnet/src/Microsoft.Agents.AI.AzureAI/AzureAIProjectChatClientExtensions.cs
+++ b/dotnet/src/Microsoft.Agents.AI.AzureAI/AzureAIProjectChatClientExtensions.cs
@@ -283,8 +283,13 @@ public static async Task CreateAIAgentAsync(
TextOptions = new() { TextFormat = ToOpenAIResponseTextFormat(options.ChatOptions?.ResponseFormat, options.ChatOptions) }
};
- // Attempt to capture breaking glass options from the raw representation factory that match the agent definition.
- if (options.ChatOptions?.RawRepresentationFactory?.Invoke(new NoOpChatClient()) is CreateResponseOptions respCreationOptions)
+ // Map reasoning options from the abstraction-level ChatOptions.Reasoning,
+ // falling back to extracting from the raw representation factory for breaking glass scenarios.
+ if (options.ChatOptions?.Reasoning is { } reasoning)
+ {
+ agentDefinition.ReasoningOptions = ToResponseReasoningOptions(reasoning);
+ }
+ else if (options.ChatOptions?.RawRepresentationFactory?.Invoke(new NoOpChatClient()) is CreateResponseOptions respCreationOptions)
{
agentDefinition.ReasoningOptions = respCreationOptions.ReasoningOptions;
}
@@ -770,6 +775,36 @@ private static string ThrowIfInvalidAgentName(string? name)
}
return name;
}
+
+ private static ResponseReasoningOptions? ToResponseReasoningOptions(ReasoningOptions reasoning)
+ {
+ ResponseReasoningEffortLevel? effortLevel = reasoning.Effort switch
+ {
+ ReasoningEffort.Low => ResponseReasoningEffortLevel.Low,
+ ReasoningEffort.Medium => ResponseReasoningEffortLevel.Medium,
+ ReasoningEffort.High => ResponseReasoningEffortLevel.High,
+ ReasoningEffort.ExtraHigh => ResponseReasoningEffortLevel.High,
+ _ => null,
+ };
+
+ ResponseReasoningSummaryVerbosity? summary = reasoning.Output switch
+ {
+ ReasoningOutput.Summary => ResponseReasoningSummaryVerbosity.Concise,
+ ReasoningOutput.Full => ResponseReasoningSummaryVerbosity.Detailed,
+ _ => null,
+ };
+
+ if (effortLevel is null && summary is null)
+ {
+ return null;
+ }
+
+ return new ResponseReasoningOptions
+ {
+ ReasoningEffortLevel = effortLevel,
+ ReasoningSummaryVerbosity = summary,
+ };
+ }
}
[JsonSerializable(typeof(JsonElement))]
diff --git a/dotnet/src/Microsoft.Agents.AI.GitHub.Copilot/GitHubCopilotAgent.cs b/dotnet/src/Microsoft.Agents.AI.GitHub.Copilot/GitHubCopilotAgent.cs
index 06c7f24ee2..1812350120 100644
--- a/dotnet/src/Microsoft.Agents.AI.GitHub.Copilot/GitHubCopilotAgent.cs
+++ b/dotnet/src/Microsoft.Agents.AI.GitHub.Copilot/GitHubCopilotAgent.cs
@@ -217,16 +217,15 @@ protected override async IAsyncEnumerable RunCoreStreamingA
}
});
- List tempFiles = [];
+ string? tempDir = null;
try
{
// Build prompt from text content
string prompt = string.Join("\n", messages.Select(m => m.Text));
// Handle DataContent as attachments
- List? attachments = await ProcessDataContentAttachmentsAsync(
+ (List? attachments, tempDir) = await ProcessDataContentAttachmentsAsync(
messages,
- tempFiles,
cancellationToken).ConfigureAwait(false);
// Send the message with attachments
@@ -245,7 +244,7 @@ protected override async IAsyncEnumerable RunCoreStreamingA
}
finally
{
- CleanupTempFiles(tempFiles);
+ CleanupTempDir(tempDir);
}
}
finally
@@ -410,45 +409,23 @@ private AgentResponseUpdate ConvertToAgentResponseUpdate(SessionEvent sessionEve
return new SessionConfig { Tools = mappedTools, SystemMessage = systemMessage };
}
- private static readonly Dictionary s_mediaTypeExtensions = new(StringComparer.OrdinalIgnoreCase)
- {
- ["image/png"] = ".png",
- ["image/jpeg"] = ".jpg",
- ["image/jpg"] = ".jpg",
- ["image/gif"] = ".gif",
- ["image/webp"] = ".webp",
- ["image/svg+xml"] = ".svg",
- ["text/plain"] = ".txt",
- ["text/html"] = ".html",
- ["text/markdown"] = ".md",
- ["application/json"] = ".json",
- ["application/xml"] = ".xml",
- ["application/pdf"] = ".pdf"
- };
-
- private static string GetExtensionForMediaType(string? mediaType)
- {
- return mediaType is not null && s_mediaTypeExtensions.TryGetValue(mediaType, out string? extension) ? extension : ".dat";
- }
-
- private static async Task?> ProcessDataContentAttachmentsAsync(
+ private static async Task<(List? Attachments, string? TempDir)> ProcessDataContentAttachmentsAsync(
IEnumerable messages,
- List tempFiles,
CancellationToken cancellationToken)
{
List? attachments = null;
+ string? tempDir = null;
foreach (ChatMessage message in messages)
{
foreach (AIContent content in message.Contents)
{
if (content is DataContent dataContent)
{
- // Write DataContent to a temp file
- string tempFilePath = Path.Combine(Path.GetTempPath(), $"agentframework_copilot_data_{Guid.NewGuid()}{GetExtensionForMediaType(dataContent.MediaType)}");
- await File.WriteAllBytesAsync(tempFilePath, dataContent.Data.ToArray(), cancellationToken).ConfigureAwait(false);
- tempFiles.Add(tempFilePath);
+ tempDir ??= Directory.CreateDirectory(
+ Path.Combine(Path.GetTempPath(), $"af_copilot_{Guid.NewGuid():N}")).FullName;
+
+ string tempFilePath = await dataContent.SaveToAsync(tempDir, cancellationToken).ConfigureAwait(false);
- // Create attachment
attachments ??= [];
attachments.Add(new UserMessageDataAttachmentsItem
{
@@ -460,19 +437,16 @@ private static string GetExtensionForMediaType(string? mediaType)
}
}
- return attachments;
+ return (attachments, tempDir);
}
- private static void CleanupTempFiles(List tempFiles)
+ private static void CleanupTempDir(string? tempDir)
{
- foreach (string tempFile in tempFiles)
+ if (tempDir is not null)
{
try
{
- if (File.Exists(tempFile))
- {
- File.Delete(tempFile);
- }
+ Directory.Delete(tempDir, recursive: true);
}
catch
{
diff --git a/dotnet/src/Shared/Workflows/Execution/WorkflowRunner.cs b/dotnet/src/Shared/Workflows/Execution/WorkflowRunner.cs
index 380ea5eaeb..7649635aa7 100644
--- a/dotnet/src/Shared/Workflows/Execution/WorkflowRunner.cs
+++ b/dotnet/src/Shared/Workflows/Execution/WorkflowRunner.cs
@@ -304,7 +304,7 @@ private async IAsyncEnumerable ProcessInputMessageAsync(ChatMessage
ChatMessage? responseMessage =
requestItem switch
{
- FunctionCallContent functionCall => await InvokeFunctionAsync(functionCall).ConfigureAwait(false),
+ FunctionCallContent functionCall when !functionCall.InformationalOnly => await InvokeFunctionAsync(functionCall).ConfigureAwait(false),
FunctionApprovalRequestContent functionApprovalRequest => ApproveFunction(functionApprovalRequest),
McpServerToolApprovalRequestContent mcpApprovalRequest => ApproveMCP(mcpApprovalRequest),
_ => HandleUnknown(requestItem),
diff --git a/dotnet/tests/Microsoft.Agents.AI.UnitTests/FunctionInvocationDelegatingAgentTests.cs b/dotnet/tests/Microsoft.Agents.AI.UnitTests/FunctionInvocationDelegatingAgentTests.cs
index 695f8a4825..d54a0644a4 100644
--- a/dotnet/tests/Microsoft.Agents.AI.UnitTests/FunctionInvocationDelegatingAgentTests.cs
+++ b/dotnet/tests/Microsoft.Agents.AI.UnitTests/FunctionInvocationDelegatingAgentTests.cs
@@ -524,8 +524,15 @@ public async Task RunAsync_MiddlewareThrowsPreInvocation_ExceptionSurfacesAsync(
{
// Arrange
var testFunction = AIFunctionFactory.Create(() => "Function result", "TestFunction", "A test function");
- var functionCall = new FunctionCallContent("call_123", "TestFunction", new Dictionary());
- var mockChatClient = CreateMockChatClientWithFunctionCalls(functionCall);
+ var mockChatClient = new Mock();
+
+ mockChatClient.Setup(c => c.GetResponseAsync(
+ It.IsAny>(),
+ It.IsAny(),
+ It.IsAny()))
+ .ReturnsAsync(() => new ChatResponse([
+ new ChatMessage(ChatRole.Assistant, [new FunctionCallContent("call_123", "TestFunction", new Dictionary())])
+ ]));
var innerAgent = new ChatClientAgent(mockChatClient.Object);
var messages = new List { new(ChatRole.User, "Test message") };