mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
.NET: Clean / address some message warnings (#291)
* WIP * Structured Output sample * Update dotnet/samples/GettingStarted/Steps/Step06_ChatClientAgent_StructuredOutputs.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Address xml and comment targeting the Structured Output context * Update with proposed fix for Persistent ChatClient * Address PR feedback * Address minor warnings * Address initialization * Address initialization * Address PR comments, update suggestions * Revert changes to NullableAttributese.cs --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Chris <66376200+crickman@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
Unverified
parent
cbb05e210f
commit
9faa27b8eb
@@ -246,6 +246,12 @@ dotnet_diagnostic.IDE1006.severity = warning # Naming rule violations
|
||||
dotnet_diagnostic.IDE0046.severity = suggestion # If statement can be simplified
|
||||
dotnet_diagnostic.IDE0056.severity = suggestion # Indexing can be simplified
|
||||
dotnet_diagnostic.IDE0057.severity = suggestion # Substring can be simplified
|
||||
dotnet_diagnostic.IDE0079.severity = none # Remove unnecessary suppression
|
||||
dotnet_diagnostic.IDE0290.severity = none # Use primary constructor
|
||||
dotnet_diagnostic.IDE0046.severity = none # if statement can be simplified
|
||||
dotnet_diagnostic.IDE0057.severity = none # Substring can be simplified
|
||||
dotnet_diagnostic.IDE0042.severity = none # Variable declaration can be deconstructed
|
||||
dotnet_diagnostic.IDE0047.severity = none # Parentheses can be removed
|
||||
|
||||
dotnet_diagnostic.CA2007.severity = error # Do not directly await a Task
|
||||
dotnet_diagnostic.VSTHRD111.severity = error # Use .ConfigureAwait(bool)
|
||||
|
||||
+13
-18
@@ -105,26 +105,21 @@ public sealed class Step03_ChatClientAgent_UsingCodeInterpreterTools(ITestOutput
|
||||
/// <param name="provider">Provider of the chat client that is used to determine how to extract the output.</param>
|
||||
/// <returns>The code interpreter output as a string.</returns>
|
||||
private static string? GetCodeInterpreterOutput(object rawRepresentation, ChatClientProviders provider)
|
||||
{
|
||||
switch (provider)
|
||||
=> provider switch
|
||||
{
|
||||
case ChatClientProviders.OpenAIAssistant
|
||||
when rawRepresentation is OpenAI.Assistants.RunStepDetailsUpdate stepDetails:
|
||||
return $"{stepDetails.CodeInterpreterInput}{string.Join(
|
||||
string.Empty,
|
||||
stepDetails.CodeInterpreterOutputs.SelectMany(l => l.Logs)
|
||||
)}";
|
||||
ChatClientProviders.OpenAIAssistant
|
||||
when rawRepresentation is OpenAI.Assistants.RunStepDetailsUpdate stepDetails => $"{stepDetails.CodeInterpreterInput}{string.Join(
|
||||
string.Empty,
|
||||
stepDetails.CodeInterpreterOutputs.SelectMany(l => l.Logs)
|
||||
)}",
|
||||
|
||||
case ChatClientProviders.AzureAIAgentsPersistent
|
||||
when rawRepresentation is Azure.AI.Agents.Persistent.RunStepDetailsUpdate stepDetails:
|
||||
return $"{stepDetails.CodeInterpreterInput}{string.Join(
|
||||
string.Empty,
|
||||
stepDetails.CodeInterpreterOutputs.OfType<RunStepDeltaCodeInterpreterLogOutput>().SelectMany(l => l.Logs)
|
||||
)}";
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
ChatClientProviders.AzureAIAgentsPersistent
|
||||
when rawRepresentation is Azure.AI.Agents.Persistent.RunStepDetailsUpdate stepDetails => $"{stepDetails.CodeInterpreterInput}{string.Join(
|
||||
string.Empty,
|
||||
stepDetails.CodeInterpreterOutputs.OfType<RunStepDeltaCodeInterpreterLogOutput>().SelectMany(l => l.Logs)
|
||||
)}",
|
||||
_ => null,
|
||||
};
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ public sealed class Step04_ChatClientAgent_DependencyInjection(ITestOutputHelper
|
||||
loggerFactory: sp.GetRequiredService<ILoggerFactory>()));
|
||||
|
||||
// Build the service provider.
|
||||
using var serviceProvider = services.BuildServiceProvider();
|
||||
await using var serviceProvider = services.BuildServiceProvider();
|
||||
|
||||
// Get the agent from the service provider.
|
||||
var agent = serviceProvider.GetRequiredService<AIAgent>();
|
||||
|
||||
@@ -23,14 +23,16 @@ public sealed class Step06_ChatClientAgent_StructuredOutputs(ITestOutputHelper o
|
||||
[InlineData(ChatClientProviders.OpenAIResponses)]
|
||||
public async Task RunWithCustomSchema(ChatClientProviders provider)
|
||||
{
|
||||
var agentOptions = new ChatClientAgentOptions(name: "HelpfulAssistant", instructions: "You are a helpful assistant.");
|
||||
agentOptions.ChatOptions = new()
|
||||
var agentOptions = new ChatClientAgentOptions(name: "HelpfulAssistant", instructions: "You are a helpful assistant.")
|
||||
{
|
||||
ResponseFormat = ChatResponseFormatJson.ForJsonSchema(
|
||||
schema: AIJsonUtilities.CreateJsonSchema(typeof(PersonInfo)),
|
||||
schemaName: "PersonInfo",
|
||||
schemaDescription: "Information about a person including their name, age, and occupation"
|
||||
)
|
||||
ChatOptions = new()
|
||||
{
|
||||
ResponseFormat = ChatResponseFormatJson.ForJsonSchema(
|
||||
schema: AIJsonUtilities.CreateJsonSchema(typeof(PersonInfo)),
|
||||
schemaName: "PersonInfo",
|
||||
schemaDescription: "Information about a person including their name, age, and occupation"
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
// Create the server-side agent Id when applicable (depending on the provider).
|
||||
|
||||
@@ -100,17 +100,12 @@ public sealed class Step07_ChatClientAgent_UsingFileSearchTools(ITestOutputHelpe
|
||||
}
|
||||
|
||||
private Task<string> CreateVectorStoreAsync(IEnumerable<string> fileIds, ChatClientProviders provider)
|
||||
{
|
||||
switch (provider)
|
||||
=> provider switch
|
||||
{
|
||||
case ChatClientProviders.OpenAIAssistant:
|
||||
return CreateVectorStoreOpenAIAssistantAsync(fileIds);
|
||||
case ChatClientProviders.AzureAIAgentsPersistent:
|
||||
return CreateVectorStoreAzureAIAgentsPersistentAsync(fileIds);
|
||||
default:
|
||||
throw new NotSupportedException($"Client provider {provider} is not supported.");
|
||||
}
|
||||
}
|
||||
ChatClientProviders.OpenAIAssistant => CreateVectorStoreOpenAIAssistantAsync(fileIds),
|
||||
ChatClientProviders.AzureAIAgentsPersistent => CreateVectorStoreAzureAIAgentsPersistentAsync(fileIds),
|
||||
_ => throw new NotSupportedException($"Client provider {provider} is not supported."),
|
||||
};
|
||||
|
||||
private async Task<string> CreateVectorStoreOpenAIAssistantAsync(IEnumerable<string> fileIds)
|
||||
{
|
||||
|
||||
@@ -33,7 +33,7 @@ public class AgentClient(HttpClient httpClient, ILogger<AgentClient> logger)
|
||||
using var response = await httpClient.SendAsync(requestMessage, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
using var stream = await response.Content.ReadAsStreamAsync(cancellationToken);
|
||||
await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken);
|
||||
using var reader = new StreamReader(stream);
|
||||
|
||||
string? line;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
#pragma warning disable CA1019
|
||||
#pragma warning disable CA1019, RCS1251, IDE0300
|
||||
|
||||
namespace System.Diagnostics.CodeAnalysis;
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
#pragma warning disable RCS1157 // Composite enum value contains undefined flag
|
||||
|
||||
namespace System.Diagnostics.CodeAnalysis;
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -41,12 +41,17 @@ public partial class ConcurrentOrchestration : OrchestratingAgent
|
||||
return f;
|
||||
}
|
||||
|
||||
return static async (responses, cancellationToken) =>
|
||||
new AgentRunResponse([.. responses.Where(r => r.Messages.Count > 0).Select(r =>
|
||||
{
|
||||
var messages = r.Messages;
|
||||
return messages.Count > 0 ? messages[messages.Count - 1] : new();
|
||||
})]);
|
||||
return (responses, cancellationToken)
|
||||
=> Task.FromResult(
|
||||
new AgentRunResponse([.. responses
|
||||
.Where(r => r.Messages.Count > 0)
|
||||
.Select(r =>
|
||||
{
|
||||
var messages = r.Messages;
|
||||
return messages.Count > 0 ? messages[messages.Count - 1] : new();
|
||||
})
|
||||
])
|
||||
);
|
||||
}
|
||||
set => this._aggregationFunc = value;
|
||||
}
|
||||
|
||||
@@ -509,6 +509,7 @@ namespace Azure.AI.Agents.Persistent
|
||||
// We need to extract the run ID and ensure that the ToolOutput we send back to Azure
|
||||
// is only the call ID.
|
||||
string[]? runAndCallIDs;
|
||||
#pragma warning disable CA1031 // Do not catch general exception types
|
||||
try
|
||||
{
|
||||
runAndCallIDs = JsonSerializer.Deserialize(frc.CallId, AgentsChatClientJsonContext.Default.StringArray);
|
||||
@@ -517,6 +518,7 @@ namespace Azure.AI.Agents.Persistent
|
||||
{
|
||||
continue;
|
||||
}
|
||||
#pragma warning restore CA1031 // Do not catch general exception types
|
||||
|
||||
if (runAndCallIDs is null ||
|
||||
runAndCallIDs.Length != 2 ||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
@@ -126,7 +125,7 @@ public sealed class ChatClientAgent : AIAgent
|
||||
}
|
||||
|
||||
// Convert the chat response messages to a valid IReadOnlyCollection for notification signatures below.
|
||||
var chatResponseMessages = chatResponse.Messages as IReadOnlyCollection<ChatMessage> ?? chatResponse.Messages.ToArray();
|
||||
var chatResponseMessages = chatResponse.Messages as IReadOnlyCollection<ChatMessage> ?? [.. chatResponse.Messages];
|
||||
|
||||
await this.NotifyThreadOfNewMessagesAsync(chatClientThread, chatResponseMessages, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
@@ -174,7 +173,7 @@ public sealed class ChatClientAgent : AIAgent
|
||||
}
|
||||
|
||||
var chatResponse = responseUpdates.ToChatResponse();
|
||||
var chatResponseMessages = chatResponse.Messages as IReadOnlyCollection<ChatMessage> ?? chatResponse.Messages.ToArray();
|
||||
var chatResponseMessages = chatResponse.Messages as IReadOnlyCollection<ChatMessage> ?? [.. chatResponse.Messages];
|
||||
|
||||
// We can derive the type of supported thread from whether we have a conversation id,
|
||||
// so let's update it and set the conversation id for the service thread case.
|
||||
|
||||
@@ -72,28 +72,11 @@ public sealed class TestConfiguration
|
||||
this._configRoot = configRoot;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides access to the configuration root for the application.
|
||||
/// </summary>
|
||||
private static IConfigurationRoot? ConfigurationRoot => s_instance?._configRoot;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the configuration settings for the AzureAI integration.
|
||||
/// </summary>
|
||||
public static AzureAIConfig AzureAI => LoadSection<AzureAIConfig>();
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a configuration section based on the specified key.
|
||||
/// </summary>
|
||||
/// <param name="caller">The key identifying the configuration section to retrieve. Cannot be null or empty.</param>
|
||||
/// <returns>The <see cref="IConfigurationSection"/> corresponding to the specified key.</returns>
|
||||
/// <exception cref="InvalidOperationException">Thrown if the configuration root is not initialized or the specified key does not correspond to a valid section.</exception>
|
||||
private static IConfigurationSection GetSection(string caller)
|
||||
{
|
||||
return s_instance?._configRoot.GetSection(caller) ??
|
||||
throw new InvalidOperationException(caller);
|
||||
}
|
||||
|
||||
private static T LoadSection<T>([CallerMemberName] string? caller = null)
|
||||
{
|
||||
if (s_instance is null)
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
<IsAotCompatible>false</IsAotCompatible>
|
||||
<ProjectsTargetFrameworks>net472;net9.0</ProjectsTargetFrameworks>
|
||||
<UserSecretsId>b7762d10-e29b-4bb1-8b74-b6d69a667dd4</UserSecretsId>
|
||||
<NoWarn>$(NoWarn);Moq1410;xUnit2023</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
+1
-1
@@ -48,7 +48,7 @@ public class AgentRunResponseTests
|
||||
{
|
||||
ChatResponse chatResponse = new()
|
||||
{
|
||||
AdditionalProperties = new(),
|
||||
AdditionalProperties = [],
|
||||
CreatedAt = new DateTimeOffset(2022, 1, 1, 0, 0, 0, TimeSpan.Zero),
|
||||
Messages = [new(ChatRole.Assistant, "This is a test message.")],
|
||||
RawRepresentation = new object(),
|
||||
|
||||
+1
-1
@@ -29,7 +29,7 @@ public class AgentRunResponseUpdateTests
|
||||
{
|
||||
ChatResponseUpdate chatResponseUpdate = new()
|
||||
{
|
||||
AdditionalProperties = new(),
|
||||
AdditionalProperties = [],
|
||||
AuthorName = "author",
|
||||
Contents = [new TextContent("hello")],
|
||||
ConversationId = "conversationId",
|
||||
|
||||
+2
-2
@@ -18,9 +18,9 @@ public class JsonSerializationTests
|
||||
{
|
||||
WriteIndented = false, // Use compact JSON for easier testing
|
||||
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
|
||||
Converters = { new JsonStringEnumConverter() }
|
||||
Converters = { new JsonStringEnumConverter() },
|
||||
TypeInfoResolver = ActorJsonContext.Default
|
||||
};
|
||||
this._options.TypeInfoResolver = ActorJsonContext.Default;
|
||||
}
|
||||
|
||||
#region ActorMessage Tests
|
||||
|
||||
+2
@@ -7,6 +7,8 @@ using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Moq;
|
||||
|
||||
#pragma warning disable RCS1196 // Call extension method as instance method
|
||||
|
||||
namespace Microsoft.Extensions.AI.Agents.UnitTests.ChatCompletion;
|
||||
|
||||
public class ChatClientAgentExtensionsTests
|
||||
|
||||
+2
-2
@@ -24,8 +24,8 @@ public class ChatClientAgentThreadTests
|
||||
var thread = new ChatClientAgentThread();
|
||||
|
||||
// Assert
|
||||
Assert.IsAssignableFrom<IMessagesRetrievableThread>(thread);
|
||||
Assert.IsAssignableFrom<AgentThread>(thread);
|
||||
Assert.IsType<IMessagesRetrievableThread>(thread, exactMatch: false);
|
||||
Assert.IsType<AgentThread>(thread, exactMatch: false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -52,9 +52,7 @@ public class OpenAIResponseFixture(bool store) : IChatClientAgentFixture
|
||||
|
||||
// Concatenate the previous messages with the response message to get a full chat history
|
||||
// that includes the current response.
|
||||
return previousMessages
|
||||
.Concat([responseMessage])
|
||||
.ToList();
|
||||
return [.. previousMessages, responseMessage];
|
||||
}
|
||||
|
||||
return await chatClientThread.GetMessagesAsync().ToListAsync();
|
||||
|
||||
Reference in New Issue
Block a user