merge with latest main

This commit is contained in:
SergeyMenshykh
2026-02-09 13:26:06 +00:00
667 changed files with 27645 additions and 19375 deletions
@@ -15,7 +15,7 @@ public interface IAgentFixture : IAsyncLifetime
{
AIAgent Agent { get; }
Task<List<ChatMessage>> GetChatHistoryAsync(AgentSession session);
Task<List<ChatMessage>> GetChatHistoryAsync(AIAgent agent, AgentSession session);
Task DeleteSessionAsync(AgentSession session);
}
@@ -106,7 +106,7 @@ public abstract class RunStreamingTests<TAgentFixture>(Func<TAgentFixture> creat
Assert.Contains("Paris", response1Text);
Assert.Contains("Vienna", response2Text);
var chatHistory = await this.Fixture.GetChatHistoryAsync(session);
var chatHistory = await this.Fixture.GetChatHistoryAsync(agent, session);
Assert.Equal(4, chatHistory.Count);
Assert.Equal(2, chatHistory.Count(x => x.Role == ChatRole.User));
Assert.Equal(2, chatHistory.Count(x => x.Role == ChatRole.Assistant));
@@ -111,7 +111,7 @@ public abstract class RunTests<TAgentFixture>(Func<TAgentFixture> createAgentFix
Assert.Contains("Paris", result1.Text);
Assert.Contains("Vienna", result2.Text);
var chatHistory = await this.Fixture.GetChatHistoryAsync(session);
var chatHistory = await this.Fixture.GetChatHistoryAsync(agent, session);
Assert.Equal(4, chatHistory.Count);
Assert.Equal(2, chatHistory.Count(x => x.Role == ChatRole.User));
Assert.Equal(2, chatHistory.Count(x => x.Role == ChatRole.Assistant));
@@ -35,7 +35,7 @@ public class AnthropicChatCompletionFixture : IChatClientAgentFixture
public IChatClient ChatClient => this._agent.ChatClient;
public async Task<List<ChatMessage>> GetChatHistoryAsync(AgentSession session)
public async Task<List<ChatMessage>> GetChatHistoryAsync(AIAgent agent, AgentSession session)
{
var typedSession = (ChatClientAgentSession)session;
@@ -44,7 +44,7 @@ public class AnthropicChatCompletionFixture : IChatClientAgentFixture
return [];
}
return (await typedSession.ChatHistoryProvider.InvokingAsync(new([]))).ToList();
return (await typedSession.ChatHistoryProvider.InvokingAsync(new(agent, session, []))).ToList();
}
public Task<ChatClientAgent> CreateChatClientAgentAsync(
@@ -0,0 +1,71 @@
// Copyright (c) Microsoft. All rights reserved.
using System.Threading.Tasks;
using AgentConformance.IntegrationTests.Support;
using Anthropic;
using Anthropic.Models.Beta;
using Anthropic.Models.Beta.Messages;
using Anthropic.Models.Beta.Skills;
using Anthropic.Services;
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
using Shared.IntegrationTests;
namespace AnthropicChatCompletion.IntegrationTests;
/// <summary>
/// Integration tests for Anthropic Skills functionality.
/// These tests are designed to be run locally with a valid Anthropic API key.
/// </summary>
public sealed class AnthropicSkillsIntegrationTests
{
// All tests for Anthropic are intended to be ran locally as the CI pipeline for Anthropic is not setup.
private const string SkipReason = "Integrations tests for local execution only";
private static readonly AnthropicConfiguration s_config = TestConfiguration.LoadSection<AnthropicConfiguration>();
[Fact(Skip = SkipReason)]
public async Task CreateAgentWithPptxSkillAsync()
{
// Arrange
AnthropicClient anthropicClient = new() { ApiKey = s_config.ApiKey };
string model = s_config.ChatModelId;
BetaSkillParams pptxSkill = new()
{
Type = BetaSkillParamsType.Anthropic,
SkillID = "pptx",
Version = "latest"
};
ChatClientAgent agent = anthropicClient.Beta.AsAIAgent(
model: model,
instructions: "You are a helpful agent for creating PowerPoint presentations.",
tools: [pptxSkill.AsAITool()]);
// Act
AgentResponse response = await agent.RunAsync(
"Create a simple 2-slide presentation: a title slide and one content slide about AI.");
// Assert
Assert.NotNull(response);
Assert.NotNull(response.Text);
Assert.NotEmpty(response.Text);
}
[Fact(Skip = SkipReason)]
public async Task ListAnthropicManagedSkillsAsync()
{
// Arrange
AnthropicClient anthropicClient = new() { ApiKey = s_config.ApiKey };
// Act
SkillListPage skills = await anthropicClient.Beta.Skills.List(
new SkillListParams { Source = "anthropic", Betas = [AnthropicBeta.Skills2025_10_02] });
// Assert
Assert.NotNull(skills);
Assert.NotNull(skills.Items);
Assert.Contains(skills.Items, skill => skill.ID == "pptx");
}
}
@@ -33,7 +33,7 @@ public class AIProjectClientFixture : IChatClientAgentFixture
return response.Value.Id;
}
public async Task<List<ChatMessage>> GetChatHistoryAsync(AgentSession session)
public async Task<List<ChatMessage>> GetChatHistoryAsync(AIAgent agent, AgentSession session)
{
var chatClientSession = (ChatClientAgentSession)session;
@@ -53,7 +53,7 @@ public class AIProjectClientFixture : IChatClientAgentFixture
return [];
}
return (await chatClientSession.ChatHistoryProvider.InvokingAsync(new([]))).ToList();
return (await chatClientSession.ChatHistoryProvider.InvokingAsync(new(agent, session, []))).ToList();
}
private async Task<List<ChatMessage>> GetChatHistoryFromResponsesChainAsync(string conversationId)
@@ -24,7 +24,7 @@ public class AzureAIAgentsPersistentFixture : IChatClientAgentFixture
public AIAgent Agent => this._agent;
public async Task<List<ChatMessage>> GetChatHistoryAsync(AgentSession session)
public async Task<List<ChatMessage>> GetChatHistoryAsync(AIAgent agent, AgentSession session)
{
List<ChatMessage> messages = [];
var typedSession = (ChatClientAgentSession)session;
@@ -20,7 +20,7 @@ public class CopilotStudioFixture : IAgentFixture
{
public AIAgent Agent { get; private set; } = null!;
public Task<List<ChatMessage>> GetChatHistoryAsync(AgentSession session) =>
public Task<List<ChatMessage>> GetChatHistoryAsync(AIAgent agent, AgentSession session) =>
throw new NotSupportedException("CopilotStudio doesn't allow retrieval of chat history.");
public Task DeleteSessionAsync(AgentSession session) =>
@@ -251,7 +251,7 @@ public sealed class AGUIAgentTests
var chatClient = new AGUIChatClient(httpClient, "http://localhost/agent", null, AGUIJsonSerializerContext.Default.Options);
AIAgent agent = chatClient.AsAIAgent(instructions: null, name: "agent1", description: "Test agent", tools: []);
AgentSession originalSession = await agent.CreateSessionAsync();
JsonElement serialized = originalSession.Serialize();
JsonElement serialized = agent.SerializeSession(originalSession);
// Act
AgentSession deserialized = await agent.DeserializeSessionAsync(serialized);
@@ -0,0 +1,42 @@
// Copyright (c) Microsoft. All rights reserved.
namespace Microsoft.Agents.AI.Abstractions.UnitTests;
/// <summary>
/// Unit tests for the <see cref="AIAgentMetadata"/> class.
/// </summary>
public class AIAgentMetadataTests
{
[Fact]
public void Constructor_WithNoArguments_SetsProviderNameToNull()
{
// Arrange & Act
AIAgentMetadata metadata = new();
// Assert
Assert.Null(metadata.ProviderName);
}
[Fact]
public void Constructor_WithProviderName_SetsProperty()
{
// Arrange
const string ProviderName = "TestProvider";
// Act
AIAgentMetadata metadata = new(ProviderName);
// Assert
Assert.Equal(ProviderName, metadata.ProviderName);
}
[Fact]
public void Constructor_WithNullProviderName_SetsProviderNameToNull()
{
// Arrange & Act
AIAgentMetadata metadata = new(null);
// Assert
Assert.Null(metadata.ProviderName);
}
}
@@ -220,6 +220,133 @@ public class AIAgentTests
ItExpr.Is<CancellationToken>(ct => ct == cancellationToken));
}
/// <summary>
/// Theory data for RunAsync overloads.
/// </summary>
public static TheoryData<string> RunAsyncOverloads => new()
{
"NoMessage",
"StringMessage",
"ChatMessage",
"MessagesCollection"
};
/// <summary>
/// Verifies that CurrentRunContext is properly set and accessible from RunCoreAsync for all RunAsync overloads.
/// </summary>
[Theory]
[MemberData(nameof(RunAsyncOverloads))]
public async Task RunAsync_SetsCurrentRunContext_AccessibleFromRunCoreAsync(string overload)
{
// Arrange
AgentRunContext? capturedContext = null;
var session = new TestAgentSession();
var options = new AgentRunOptions();
var agentMock = new Mock<AIAgent> { CallBase = true };
agentMock
.Protected()
.Setup<Task<AgentResponse>>("RunCoreAsync",
ItExpr.IsAny<IEnumerable<ChatMessage>>(),
ItExpr.IsAny<AgentSession?>(),
ItExpr.IsAny<AgentRunOptions?>(),
ItExpr.IsAny<CancellationToken>())
.Returns((IEnumerable<ChatMessage> _, AgentSession? _, AgentRunOptions? _, CancellationToken _) =>
{
capturedContext = AIAgent.CurrentRunContext;
return Task.FromResult(new AgentResponse(new ChatMessage(ChatRole.Assistant, "Response")));
});
// Act
switch (overload)
{
case "NoMessage":
await agentMock.Object.RunAsync(session, options);
break;
case "StringMessage":
await agentMock.Object.RunAsync("Hello", session, options);
break;
case "ChatMessage":
await agentMock.Object.RunAsync(new ChatMessage(ChatRole.User, "Hello"), session, options);
break;
case "MessagesCollection":
await agentMock.Object.RunAsync([new ChatMessage(ChatRole.User, "Hello")], session, options);
break;
}
// Assert
Assert.NotNull(capturedContext);
Assert.Same(agentMock.Object, capturedContext!.Agent);
Assert.Same(session, capturedContext.Session);
Assert.Same(options, capturedContext.RunOptions);
if (overload == "NoMessage")
{
Assert.Empty(capturedContext.RequestMessages);
}
else
{
Assert.Single(capturedContext.RequestMessages);
}
}
/// <summary>
/// Verifies that CurrentRunContext is properly set and accessible from RunCoreStreamingAsync for all RunStreamingAsync overloads.
/// </summary>
[Theory]
[MemberData(nameof(RunAsyncOverloads))]
public async Task RunStreamingAsync_SetsCurrentRunContext_AccessibleFromRunCoreStreamingAsync(string overload)
{
// Arrange
AgentRunContext? capturedContext = null;
var session = new TestAgentSession();
var options = new AgentRunOptions();
var agentMock = new Mock<AIAgent> { CallBase = true };
agentMock
.Protected()
.Setup<IAsyncEnumerable<AgentResponseUpdate>>("RunCoreStreamingAsync",
ItExpr.IsAny<IEnumerable<ChatMessage>>(),
ItExpr.IsAny<AgentSession?>(),
ItExpr.IsAny<AgentRunOptions?>(),
ItExpr.IsAny<CancellationToken>())
.Returns((IEnumerable<ChatMessage> _, AgentSession? _, AgentRunOptions? _, CancellationToken _) =>
{
capturedContext = AIAgent.CurrentRunContext;
return ToAsyncEnumerableAsync([new AgentResponseUpdate(ChatRole.Assistant, "Response")]);
});
// Act
IAsyncEnumerable<AgentResponseUpdate> stream = overload switch
{
"NoMessage" => agentMock.Object.RunStreamingAsync(session, options),
"StringMessage" => agentMock.Object.RunStreamingAsync("Hello", session, options),
"ChatMessage" => agentMock.Object.RunStreamingAsync(new ChatMessage(ChatRole.User, "Hello"), session, options),
"MessagesCollection" => agentMock.Object.RunStreamingAsync(new[] { new ChatMessage(ChatRole.User, "Hello") }, session, options),
_ => throw new InvalidOperationException($"Unknown overload: {overload}")
};
await foreach (AgentResponseUpdate _ in stream)
{
// Consume the stream
}
// Assert
Assert.NotNull(capturedContext);
Assert.Same(agentMock.Object, capturedContext!.Agent);
Assert.Same(session, capturedContext.Session);
Assert.Same(options, capturedContext.RunOptions);
if (overload == "NoMessage")
{
Assert.Empty(capturedContext.RequestMessages);
}
else
{
Assert.Single(capturedContext.RequestMessages);
}
}
[Fact]
public void ValidateAgentIDIsIdempotent()
{
@@ -364,10 +491,78 @@ public class AIAgentTests
#endregion
#region Name and Description Property Tests
/// <summary>
/// Typed mock session.
/// Verify that Name property returns the value from the derived class.
/// </summary>
public abstract class TestAgentSession : AgentSession;
[Fact]
public void Name_ReturnsValueFromDerivedClass()
{
// Arrange
var agent = new MockAgentWithName("TestAgentName", "TestAgentDescription");
// Act
string? name = agent.Name;
// Assert
Assert.Equal("TestAgentName", name);
}
/// <summary>
/// Verify that Description property returns the value from the derived class.
/// </summary>
[Fact]
public void Description_ReturnsValueFromDerivedClass()
{
// Arrange
var agent = new MockAgentWithName("TestAgentName", "TestAgentDescription");
// Act
string? description = agent.Description;
// Assert
Assert.Equal("TestAgentDescription", description);
}
/// <summary>
/// Verify that Name property returns null when not overridden.
/// </summary>
[Fact]
public void Name_ReturnsNullByDefault()
{
// Arrange
var agent = new MockAgent();
// Act
string? name = agent.Name;
// Assert
Assert.Null(name);
}
/// <summary>
/// Verify that Description property returns null when not overridden.
/// </summary>
[Fact]
public void Description_ReturnsNullByDefault()
{
// Arrange
var agent = new MockAgent();
// Act
string? description = agent.Description;
// Assert
Assert.Null(description);
}
#endregion
/// <summary>
/// Typed mock session for testing purposes.
/// </summary>
private sealed class TestAgentSession : AgentSession;
private sealed class MockAgent : AIAgent
{
@@ -378,10 +573,51 @@ public class AIAgentTests
protected override string? IdCore { get; }
public override async ValueTask<AgentSession> CreateSessionAsync(CancellationToken cancellationToken = default)
protected override ValueTask<AgentSession> CreateSessionCoreAsync(CancellationToken cancellationToken = default)
=> throw new NotImplementedException();
public override async ValueTask<AgentSession> DeserializeSessionAsync(JsonElement serializedSession, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default)
protected override JsonElement SerializeSessionCore(AgentSession session, JsonSerializerOptions? jsonSerializerOptions = null)
=> throw new NotImplementedException();
protected override ValueTask<AgentSession> DeserializeSessionCoreAsync(JsonElement serializedState, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default)
=> throw new NotImplementedException();
protected override Task<AgentResponse> RunCoreAsync(
IEnumerable<ChatMessage> messages,
AgentSession? session = null,
AgentRunOptions? options = null,
CancellationToken cancellationToken = default) =>
throw new NotImplementedException();
protected override IAsyncEnumerable<AgentResponseUpdate> RunCoreStreamingAsync(
IEnumerable<ChatMessage> messages,
AgentSession? session = null,
AgentRunOptions? options = null,
CancellationToken cancellationToken = default) =>
throw new NotImplementedException();
}
private sealed class MockAgentWithName : AIAgent
{
private readonly string? _name;
private readonly string? _description;
public MockAgentWithName(string? name, string? description)
{
this._name = name;
this._description = description;
}
public override string? Name => this._name;
public override string? Description => this._description;
protected override ValueTask<AgentSession> CreateSessionCoreAsync(CancellationToken cancellationToken = default)
=> throw new NotImplementedException();
protected override ValueTask<AgentSession> DeserializeSessionCoreAsync(JsonElement serializedState, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default)
=> throw new NotImplementedException();
protected override JsonElement SerializeSessionCore(AgentSession session, JsonSerializerOptions? jsonSerializerOptions = null)
=> throw new NotImplementedException();
protected override Task<AgentResponse> RunCoreAsync(
@@ -1,21 +1,26 @@
// Copyright (c) Microsoft. All rights reserved.
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.AI;
using Moq;
namespace Microsoft.Agents.AI.Abstractions.UnitTests;
public class AIContextProviderTests
{
private static readonly AIAgent s_mockAgent = new Mock<AIAgent>().Object;
private static readonly AgentSession s_mockSession = new Mock<AgentSession>().Object;
[Fact]
public async Task InvokedAsync_ReturnsCompletedTaskAsync()
{
var provider = new TestAIContextProvider();
var messages = new ReadOnlyCollection<ChatMessage>([]);
var task = provider.InvokedAsync(new(messages, aiContextProviderMessages: null));
var task = provider.InvokedAsync(new(s_mockAgent, s_mockSession, messages, aiContextProviderMessages: null));
Assert.Equal(default, task);
}
@@ -30,13 +35,13 @@ public class AIContextProviderTests
[Fact]
public void InvokingContext_Constructor_ThrowsForNullMessages()
{
Assert.Throws<ArgumentNullException>(() => new AIContextProvider.InvokingContext(null!));
Assert.Throws<ArgumentNullException>(() => new AIContextProvider.InvokingContext(s_mockAgent, s_mockSession, null!));
}
[Fact]
public void InvokedContext_Constructor_ThrowsForNullMessages()
{
Assert.Throws<ArgumentNullException>(() => new AIContextProvider.InvokedContext(null!, aiContextProviderMessages: null));
Assert.Throws<ArgumentNullException>(() => new AIContextProvider.InvokedContext(s_mockAgent, s_mockSession, null!, aiContextProviderMessages: null));
}
#region GetService Method Tests
@@ -155,6 +160,209 @@ public class AIContextProviderTests
#endregion
#region InvokingContext Tests
[Fact]
public void InvokingContext_RequestMessages_SetterThrowsForNull()
{
// Arrange
var messages = new ReadOnlyCollection<ChatMessage>([new(ChatRole.User, "Hello")]);
var context = new AIContextProvider.InvokingContext(s_mockAgent, s_mockSession, messages);
// Act & Assert
Assert.Throws<ArgumentNullException>(() => context.RequestMessages = null!);
}
[Fact]
public void InvokingContext_RequestMessages_SetterRoundtrips()
{
// Arrange
var initialMessages = new ReadOnlyCollection<ChatMessage>([new(ChatRole.User, "Hello")]);
var newMessages = new List<ChatMessage> { new(ChatRole.User, "New message") };
var context = new AIContextProvider.InvokingContext(s_mockAgent, s_mockSession, initialMessages);
// Act
context.RequestMessages = newMessages;
// Assert
Assert.Same(newMessages, context.RequestMessages);
}
[Fact]
public void InvokingContext_Agent_ReturnsConstructorValue()
{
// Arrange
var messages = new ReadOnlyCollection<ChatMessage>([new(ChatRole.User, "Hello")]);
// Act
var context = new AIContextProvider.InvokingContext(s_mockAgent, s_mockSession, messages);
// Assert
Assert.Same(s_mockAgent, context.Agent);
}
[Fact]
public void InvokingContext_Session_ReturnsConstructorValue()
{
// Arrange
var messages = new ReadOnlyCollection<ChatMessage>([new(ChatRole.User, "Hello")]);
// Act
var context = new AIContextProvider.InvokingContext(s_mockAgent, s_mockSession, messages);
// Assert
Assert.Same(s_mockSession, context.Session);
}
[Fact]
public void InvokingContext_Session_CanBeNull()
{
// Arrange
var messages = new ReadOnlyCollection<ChatMessage>([new(ChatRole.User, "Hello")]);
// Act
var context = new AIContextProvider.InvokingContext(s_mockAgent, null, messages);
// Assert
Assert.Null(context.Session);
}
[Fact]
public void InvokingContext_Constructor_ThrowsForNullAgent()
{
// Arrange
var messages = new ReadOnlyCollection<ChatMessage>([new(ChatRole.User, "Hello")]);
// Act & Assert
Assert.Throws<ArgumentNullException>(() => new AIContextProvider.InvokingContext(null!, s_mockSession, messages));
}
#endregion
#region InvokedContext Tests
[Fact]
public void InvokedContext_RequestMessages_SetterThrowsForNull()
{
// Arrange
var messages = new ReadOnlyCollection<ChatMessage>([new(ChatRole.User, "Hello")]);
var context = new AIContextProvider.InvokedContext(s_mockAgent, s_mockSession, messages, aiContextProviderMessages: null);
// Act & Assert
Assert.Throws<ArgumentNullException>(() => context.RequestMessages = null!);
}
[Fact]
public void InvokedContext_RequestMessages_SetterRoundtrips()
{
// Arrange
var initialMessages = new ReadOnlyCollection<ChatMessage>([new(ChatRole.User, "Hello")]);
var newMessages = new List<ChatMessage> { new(ChatRole.User, "New message") };
var context = new AIContextProvider.InvokedContext(s_mockAgent, s_mockSession, initialMessages, aiContextProviderMessages: null);
// Act
context.RequestMessages = newMessages;
// Assert
Assert.Same(newMessages, context.RequestMessages);
}
[Fact]
public void InvokedContext_AIContextProviderMessages_Roundtrips()
{
// Arrange
var requestMessages = new ReadOnlyCollection<ChatMessage>([new(ChatRole.User, "Hello")]);
var aiContextMessages = new List<ChatMessage> { new(ChatRole.System, "AI context message") };
var context = new AIContextProvider.InvokedContext(s_mockAgent, s_mockSession, requestMessages, aiContextProviderMessages: null);
// Act
context.AIContextProviderMessages = aiContextMessages;
// Assert
Assert.Same(aiContextMessages, context.AIContextProviderMessages);
}
[Fact]
public void InvokedContext_ResponseMessages_Roundtrips()
{
// Arrange
var requestMessages = new ReadOnlyCollection<ChatMessage>([new(ChatRole.User, "Hello")]);
var responseMessages = new List<ChatMessage> { new(ChatRole.Assistant, "Response message") };
var context = new AIContextProvider.InvokedContext(s_mockAgent, s_mockSession, requestMessages, aiContextProviderMessages: null);
// Act
context.ResponseMessages = responseMessages;
// Assert
Assert.Same(responseMessages, context.ResponseMessages);
}
[Fact]
public void InvokedContext_InvokeException_Roundtrips()
{
// Arrange
var requestMessages = new ReadOnlyCollection<ChatMessage>([new(ChatRole.User, "Hello")]);
var exception = new InvalidOperationException("Test exception");
var context = new AIContextProvider.InvokedContext(s_mockAgent, s_mockSession, requestMessages, aiContextProviderMessages: null);
// Act
context.InvokeException = exception;
// Assert
Assert.Same(exception, context.InvokeException);
}
[Fact]
public void InvokedContext_Agent_ReturnsConstructorValue()
{
// Arrange
var requestMessages = new ReadOnlyCollection<ChatMessage>([new(ChatRole.User, "Hello")]);
// Act
var context = new AIContextProvider.InvokedContext(s_mockAgent, s_mockSession, requestMessages, aiContextProviderMessages: null);
// Assert
Assert.Same(s_mockAgent, context.Agent);
}
[Fact]
public void InvokedContext_Session_ReturnsConstructorValue()
{
// Arrange
var requestMessages = new ReadOnlyCollection<ChatMessage>([new(ChatRole.User, "Hello")]);
// Act
var context = new AIContextProvider.InvokedContext(s_mockAgent, s_mockSession, requestMessages, aiContextProviderMessages: null);
// Assert
Assert.Same(s_mockSession, context.Session);
}
[Fact]
public void InvokedContext_Session_CanBeNull()
{
// Arrange
var requestMessages = new ReadOnlyCollection<ChatMessage>([new(ChatRole.User, "Hello")]);
// Act
var context = new AIContextProvider.InvokedContext(s_mockAgent, null, requestMessages, aiContextProviderMessages: null);
// Assert
Assert.Null(context.Session);
}
[Fact]
public void InvokedContext_Constructor_ThrowsForNullAgent()
{
// Arrange
var requestMessages = new ReadOnlyCollection<ChatMessage>([new(ChatRole.User, "Hello")]);
// Act & Assert
Assert.Throws<ArgumentNullException>(() => new AIContextProvider.InvokedContext(null!, s_mockSession, requestMessages, aiContextProviderMessages: null));
}
#endregion
private sealed class TestAIContextProvider : AIContextProvider
{
public override ValueTask<AIContext> InvokingAsync(InvokingContext context, CancellationToken cancellationToken = default)
@@ -230,4 +230,103 @@ public class AgentResponseTests
Assert.Equal(expectedResult.FullName, animal.FullName);
Assert.Equal(expectedResult.Species, animal.Species);
}
#endif
[Fact]
public void TryParseAsStructuredOutputWithJSOSuccess()
{
// Arrange.
var expectedResult = new Animal { Id = 1, FullName = "Tigger", Species = Species.Tiger };
var response = new AgentResponse(new ChatMessage(ChatRole.Assistant, JsonSerializer.Serialize(expectedResult, TestJsonSerializerContext.Default.Animal)));
// Act.
response.TryDeserialize(TestJsonSerializerContext.Default.Options, out Animal? animal);
// Assert.
Assert.NotNull(animal);
Assert.Equal(expectedResult.Id, animal.Id);
Assert.Equal(expectedResult.FullName, animal.FullName);
Assert.Equal(expectedResult.Species, animal.Species);
}
[Fact]
public void TryParseAsStructuredOutputFailsWithEmptyText()
{
// Arrange.
var response = new AgentResponse(new ChatMessage(ChatRole.Assistant, string.Empty));
// Act & Assert.
Assert.False(response.TryDeserialize<Animal>(TestJsonSerializerContext.Default.Options, out _));
}
[Fact]
public void TryParseAsStructuredOutputFailsWithIncorrectTypedJson()
{
// Arrange.
var response = new AgentResponse(new ChatMessage(ChatRole.Assistant, "[]"));
// Act & Assert.
Assert.False(response.TryDeserialize<Animal>(TestJsonSerializerContext.Default.Options, out _));
}
[Fact]
public void ToAgentResponseUpdatesWithNoMessagesProducesEmptyArray()
{
// Arrange
AgentResponse response = new();
// Act
AgentResponseUpdate[] updates = response.ToAgentResponseUpdates();
// Assert
Assert.Empty(updates);
}
[Fact]
public void ToAgentResponseUpdatesWithUsageOnlyProducesSingleUpdate()
{
// Arrange
AgentResponse response = new()
{
Usage = new UsageDetails { TotalTokenCount = 100 }
};
// Act
AgentResponseUpdate[] updates = response.ToAgentResponseUpdates();
// Assert
AgentResponseUpdate update = Assert.Single(updates);
UsageContent usageContent = Assert.IsType<UsageContent>(update.Contents[0]);
Assert.Equal(100, usageContent.Details.TotalTokenCount);
}
[Fact]
public void ToAgentResponseUpdatesWithAdditionalPropertiesOnlyProducesSingleUpdate()
{
// Arrange
AgentResponse response = new()
{
AdditionalProperties = new() { ["key"] = "value" }
};
// Act
AgentResponseUpdate[] updates = response.ToAgentResponseUpdates();
// Assert
AgentResponseUpdate update = Assert.Single(updates);
Assert.NotNull(update.AdditionalProperties);
Assert.Equal("value", update.AdditionalProperties!["key"]);
}
[Fact]
public void Deserialize_ThrowsWhenDeserializationReturnsNull()
{
// Arrange
AgentResponse response = new(new ChatMessage(ChatRole.Assistant, "null"));
// Act & Assert
InvalidOperationException exception = Assert.Throws<InvalidOperationException>(
() => response.Deserialize<Animal>(TestJsonSerializerContext.Default.Options));
Assert.Equal("The deserialized response is null.", exception.Message);
}
}
@@ -299,6 +299,161 @@ public class AgentResponseUpdateExtensionsTests
Assert.Equal(expected, response.CreatedAt);
}
#region AsChatResponse Tests
[Fact]
public void AsChatResponse_WithNullArgument_ThrowsArgumentNullException()
{
// Arrange & Act & Assert
Assert.Throws<ArgumentNullException>("response", () => ((AgentResponse)null!).AsChatResponse());
}
[Fact]
public void AsChatResponse_WithRawRepresentationAsChatResponse_ReturnsSameInstance()
{
// Arrange
ChatResponse originalChatResponse = new()
{
ResponseId = "original-response",
Messages = [new ChatMessage(ChatRole.Assistant, "Hello")]
};
AgentResponse agentResponse = new(originalChatResponse);
// Act
ChatResponse result = agentResponse.AsChatResponse();
// Assert
Assert.Same(originalChatResponse, result);
}
[Fact]
public void AsChatResponse_WithoutRawRepresentation_CreatesNewChatResponse()
{
// Arrange
AgentResponse agentResponse = new(new ChatMessage(ChatRole.Assistant, "Test message"))
{
ResponseId = "test-response-id",
CreatedAt = new DateTimeOffset(2024, 1, 1, 12, 0, 0, TimeSpan.Zero),
Usage = new UsageDetails { TotalTokenCount = 50 },
AdditionalProperties = new() { ["key"] = "value" },
ContinuationToken = ResponseContinuationToken.FromBytes(new byte[] { 1, 2, 3 }),
};
// Act
ChatResponse result = agentResponse.AsChatResponse();
// Assert
Assert.NotNull(result);
Assert.Equal("test-response-id", result.ResponseId);
Assert.Equal(new DateTimeOffset(2024, 1, 1, 12, 0, 0, TimeSpan.Zero), result.CreatedAt);
Assert.Same(agentResponse.Messages, result.Messages);
Assert.Same(agentResponse, result.RawRepresentation);
Assert.Same(agentResponse.Usage, result.Usage);
Assert.Same(agentResponse.AdditionalProperties, result.AdditionalProperties);
Assert.Equal(agentResponse.ContinuationToken, result.ContinuationToken);
}
#endregion
#region AsChatResponseUpdate Tests
[Fact]
public void AsChatResponseUpdate_WithNullArgument_ThrowsArgumentNullException()
{
// Arrange & Act & Assert
Assert.Throws<ArgumentNullException>("responseUpdate", () => ((AgentResponseUpdate)null!).AsChatResponseUpdate());
}
[Fact]
public void AsChatResponseUpdate_WithRawRepresentationAsChatResponseUpdate_ReturnsSameInstance()
{
// Arrange
ChatResponseUpdate originalChatResponseUpdate = new()
{
ResponseId = "original-update",
Contents = [new TextContent("Hello")]
};
AgentResponseUpdate agentResponseUpdate = new(originalChatResponseUpdate);
// Act
ChatResponseUpdate result = agentResponseUpdate.AsChatResponseUpdate();
// Assert
Assert.Same(originalChatResponseUpdate, result);
}
[Fact]
public void AsChatResponseUpdate_WithoutRawRepresentation_CreatesNewChatResponseUpdate()
{
// Arrange
AgentResponseUpdate agentResponseUpdate = new(ChatRole.Assistant, "Test")
{
AuthorName = "TestAuthor",
ResponseId = "update-id",
MessageId = "message-id",
CreatedAt = new DateTimeOffset(2024, 1, 1, 12, 0, 0, TimeSpan.Zero),
AdditionalProperties = new() { ["key"] = "value" },
ContinuationToken = ResponseContinuationToken.FromBytes(new byte[] { 1, 2, 3 }),
};
// Act
ChatResponseUpdate result = agentResponseUpdate.AsChatResponseUpdate();
// Assert
Assert.NotNull(result);
Assert.Equal("TestAuthor", result.AuthorName);
Assert.Equal("update-id", result.ResponseId);
Assert.Equal("message-id", result.MessageId);
Assert.Equal(new DateTimeOffset(2024, 1, 1, 12, 0, 0, TimeSpan.Zero), result.CreatedAt);
Assert.Equal(ChatRole.Assistant, result.Role);
Assert.Same(agentResponseUpdate.Contents, result.Contents);
Assert.Same(agentResponseUpdate, result.RawRepresentation);
Assert.Same(agentResponseUpdate.AdditionalProperties, result.AdditionalProperties);
Assert.Equal(agentResponseUpdate.ContinuationToken, result.ContinuationToken);
}
#endregion
#region AsChatResponseUpdatesAsync Tests
[Fact]
public async Task AsChatResponseUpdatesAsync_WithNullArgument_ThrowsArgumentNullExceptionAsync()
{
// Arrange & Act & Assert
await Assert.ThrowsAsync<ArgumentNullException>("responseUpdates", async () =>
{
await foreach (ChatResponseUpdate _ in ((IAsyncEnumerable<AgentResponseUpdate>)null!).AsChatResponseUpdatesAsync())
{
// Do nothing
}
});
}
[Fact]
public async Task AsChatResponseUpdatesAsync_ConvertsUpdatesAsync()
{
// Arrange
AgentResponseUpdate[] updates =
[
new(ChatRole.Assistant, "First"),
new(ChatRole.Assistant, "Second"),
];
// Act
List<ChatResponseUpdate> results = [];
await foreach (ChatResponseUpdate update in YieldAsync(updates).AsChatResponseUpdatesAsync())
{
results.Add(update);
}
// Assert
Assert.Equal(2, results.Count);
Assert.Equal("First", Assert.IsType<TextContent>(results[0].Contents[0]).Text);
Assert.Equal("Second", Assert.IsType<TextContent>(results[1].Contents[0]).Text);
}
#endregion
private static async IAsyncEnumerable<AgentResponseUpdate> YieldAsync(IEnumerable<AgentResponseUpdate> updates)
{
foreach (AgentResponseUpdate update in updates)
@@ -0,0 +1,233 @@
// Copyright (c) Microsoft. All rights reserved.
using System;
using System.Collections.Generic;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.AI;
namespace Microsoft.Agents.AI.Abstractions.UnitTests;
/// <summary>
/// Unit tests for the <see cref="AgentRunContext"/> class.
/// </summary>
public sealed class AgentRunContextTests
{
#region Constructor Validation Tests
/// <summary>
/// Verifies that passing null for agent throws ArgumentNullException.
/// </summary>
[Fact]
public void Constructor_NullAgent_ThrowsArgumentNullException()
{
// Arrange
AgentSession session = new TestAgentSession();
IReadOnlyCollection<ChatMessage> messages = new List<ChatMessage>();
AgentRunOptions options = new();
// Act & Assert
Assert.Throws<ArgumentNullException>(() => new AgentRunContext(null!, session, messages, options));
}
/// <summary>
/// Verifies that passing null for session does not throw
/// </summary>
[Fact]
public void Constructor_NullSession_DoesNotThrow()
{
// Arrange
AIAgent agent = new TestAgent();
IReadOnlyCollection<ChatMessage> messages = new List<ChatMessage>();
AgentRunOptions options = new();
// Act
AgentRunContext context = new(agent, null, messages, options);
// Assert
Assert.NotNull(context);
Assert.Null(context.Session);
}
/// <summary>
/// Verifies that passing null for requestMessages throws ArgumentNullException.
/// </summary>
[Fact]
public void Constructor_NullRequestMessages_ThrowsArgumentNullException()
{
// Arrange
AIAgent agent = new TestAgent();
AgentSession session = new TestAgentSession();
AgentRunOptions options = new();
// Act & Assert
Assert.Throws<ArgumentNullException>(() => new AgentRunContext(agent, session, null!, options));
}
/// <summary>
/// Verifies that passing null for agentRunOptions does not throw.
/// </summary>
[Fact]
public void Constructor_NullAgentRunOptions_DoesNotThrow()
{
// Arrange
AIAgent agent = new TestAgent();
AgentSession session = new TestAgentSession();
IReadOnlyCollection<ChatMessage> messages = new List<ChatMessage>();
// Act
AgentRunContext context = new(agent, session, messages, null);
// Assert
Assert.NotNull(context);
Assert.Null(context.RunOptions);
}
#endregion
#region Property Roundtrip Tests
/// <summary>
/// Verifies that the Agent property returns the value passed to the constructor.
/// </summary>
[Fact]
public void Agent_ReturnsValueFromConstructor()
{
// Arrange
AIAgent agent = new TestAgent();
AgentSession session = new TestAgentSession();
IReadOnlyCollection<ChatMessage> messages = new List<ChatMessage>();
AgentRunOptions options = new();
// Act
AgentRunContext context = new(agent, session, messages, options);
// Assert
Assert.Same(agent, context.Agent);
}
/// <summary>
/// Verifies that the Session property returns the value passed to the constructor.
/// </summary>
[Fact]
public void Session_ReturnsValueFromConstructor()
{
// Arrange
AIAgent agent = new TestAgent();
AgentSession session = new TestAgentSession();
IReadOnlyCollection<ChatMessage> messages = new List<ChatMessage>();
AgentRunOptions options = new();
// Act
AgentRunContext context = new(agent, session, messages, options);
// Assert
Assert.Same(session, context.Session);
}
/// <summary>
/// Verifies that the RequestMessages property returns the value passed to the constructor.
/// </summary>
[Fact]
public void RequestMessages_ReturnsValueFromConstructor()
{
// Arrange
AIAgent agent = new TestAgent();
AgentSession session = new TestAgentSession();
IReadOnlyCollection<ChatMessage> messages = new List<ChatMessage>
{
new(ChatRole.User, "Hello"),
new(ChatRole.Assistant, "Hi there!")
};
AgentRunOptions options = new();
// Act
AgentRunContext context = new(agent, session, messages, options);
// Assert
Assert.Same(messages, context.RequestMessages);
Assert.Equal(2, context.RequestMessages.Count);
}
/// <summary>
/// Verifies that the RunOptions property returns the value passed to the constructor.
/// </summary>
[Fact]
public void RunOptions_ReturnsValueFromConstructor()
{
// Arrange
AIAgent agent = new TestAgent();
AgentSession session = new TestAgentSession();
IReadOnlyCollection<ChatMessage> messages = new List<ChatMessage>();
AgentRunOptions options = new()
{
AllowBackgroundResponses = true,
AdditionalProperties = new AdditionalPropertiesDictionary
{
["key1"] = "value1"
}
};
// Act
AgentRunContext context = new(agent, session, messages, options);
// Assert
Assert.Same(options, context.RunOptions);
Assert.True(context.RunOptions!.AllowBackgroundResponses);
}
/// <summary>
/// Verifies that an empty messages collection is handled correctly.
/// </summary>
[Fact]
public void RequestMessages_EmptyCollection_ReturnsEmptyCollection()
{
// Arrange
AIAgent agent = new TestAgent();
AgentSession session = new TestAgentSession();
IReadOnlyCollection<ChatMessage> messages = new List<ChatMessage>();
AgentRunOptions options = new();
// Act
AgentRunContext context = new(agent, session, messages, options);
// Assert
Assert.NotNull(context.RequestMessages);
Assert.Empty(context.RequestMessages);
}
#endregion
#region Test Helpers
private sealed class TestAgentSession : AgentSession;
private sealed class TestAgent : AIAgent
{
protected override ValueTask<AgentSession> CreateSessionCoreAsync(CancellationToken cancellationToken = default)
=> throw new NotImplementedException();
protected override ValueTask<AgentSession> DeserializeSessionCoreAsync(JsonElement serializedState, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default)
=> throw new NotImplementedException();
protected override JsonElement SerializeSessionCore(AgentSession session, JsonSerializerOptions? jsonSerializerOptions = null)
=> throw new NotImplementedException();
protected override Task<AgentResponse> RunCoreAsync(
IEnumerable<ChatMessage> messages,
AgentSession? session = null,
AgentRunOptions? options = null,
CancellationToken cancellationToken = default) =>
throw new NotImplementedException();
protected override IAsyncEnumerable<AgentResponseUpdate> RunCoreStreamingAsync(
IEnumerable<ChatMessage> messages,
AgentSession? session = null,
AgentRunOptions? options = null,
CancellationToken cancellationToken = default) =>
throw new NotImplementedException();
}
#endregion
}
@@ -11,14 +11,6 @@ namespace Microsoft.Agents.AI.Abstractions.UnitTests;
/// </summary>
public class AgentSessionTests
{
[Fact]
public void Serialize_ReturnsDefaultJsonElement()
{
var session = new TestAgentSession();
var result = session.Serialize();
Assert.Equal(default, result);
}
#region GetService Method Tests
/// <summary>
@@ -14,6 +14,9 @@ namespace Microsoft.Agents.AI.Abstractions.UnitTests;
/// </summary>
public sealed class ChatHistoryProviderExtensionsTests
{
private static readonly AIAgent s_mockAgent = new Mock<AIAgent>().Object;
private static readonly AgentSession s_mockSession = new Mock<AgentSession>().Object;
[Fact]
public void WithMessageFilters_ReturnsChatHistoryProviderMessageFilter()
{
@@ -35,7 +38,7 @@ public sealed class ChatHistoryProviderExtensionsTests
// Arrange
Mock<ChatHistoryProvider> providerMock = new();
List<ChatMessage> innerMessages = [new(ChatRole.User, "Hello"), new(ChatRole.Assistant, "Hi")];
ChatHistoryProvider.InvokingContext context = new([new ChatMessage(ChatRole.User, "Test")]);
ChatHistoryProvider.InvokingContext context = new(s_mockAgent, s_mockSession, [new ChatMessage(ChatRole.User, "Test")]);
providerMock
.Setup(p => p.InvokingAsync(context, It.IsAny<CancellationToken>()))
@@ -59,7 +62,7 @@ public sealed class ChatHistoryProviderExtensionsTests
Mock<ChatHistoryProvider> providerMock = new();
List<ChatMessage> requestMessages = [new(ChatRole.User, "Hello")];
List<ChatMessage> chatHistoryProviderMessages = [new(ChatRole.System, "System")];
ChatHistoryProvider.InvokedContext context = new(requestMessages, chatHistoryProviderMessages)
ChatHistoryProvider.InvokedContext context = new(s_mockAgent, s_mockSession, requestMessages, chatHistoryProviderMessages)
{
ResponseMessages = [new ChatMessage(ChatRole.Assistant, "Response")]
};
@@ -106,7 +109,7 @@ public sealed class ChatHistoryProviderExtensionsTests
List<ChatMessage> requestMessages = [new(ChatRole.User, "Hello")];
List<ChatMessage> chatHistoryProviderMessages = [new(ChatRole.System, "System")];
List<ChatMessage> aiContextProviderMessages = [new(ChatRole.System, "Context")];
ChatHistoryProvider.InvokedContext context = new(requestMessages, chatHistoryProviderMessages)
ChatHistoryProvider.InvokedContext context = new(s_mockAgent, s_mockSession, requestMessages, chatHistoryProviderMessages)
{
AIContextProviderMessages = aiContextProviderMessages
};
@@ -16,6 +16,9 @@ namespace Microsoft.Agents.AI.Abstractions.UnitTests;
/// </summary>
public sealed class ChatHistoryProviderMessageFilterTests
{
private static readonly AIAgent s_mockAgent = new Mock<AIAgent>().Object;
private static readonly AgentSession s_mockSession = new Mock<AgentSession>().Object;
[Fact]
public void Constructor_WithNullInnerProvider_ThrowsArgumentNullException()
{
@@ -59,7 +62,7 @@ public sealed class ChatHistoryProviderMessageFilterTests
new(ChatRole.User, "Hello"),
new(ChatRole.Assistant, "Hi there!")
};
var context = new ChatHistoryProvider.InvokingContext([new ChatMessage(ChatRole.User, "Test")]);
var context = new ChatHistoryProvider.InvokingContext(s_mockAgent, s_mockSession, [new ChatMessage(ChatRole.User, "Test")]);
innerProviderMock
.Setup(s => s.InvokingAsync(context, It.IsAny<CancellationToken>()))
@@ -88,7 +91,7 @@ public sealed class ChatHistoryProviderMessageFilterTests
new(ChatRole.Assistant, "Hi there!"),
new(ChatRole.User, "How are you?")
};
var context = new ChatHistoryProvider.InvokingContext([new ChatMessage(ChatRole.User, "Test")]);
var context = new ChatHistoryProvider.InvokingContext(s_mockAgent, s_mockSession, [new ChatMessage(ChatRole.User, "Test")]);
innerProviderMock
.Setup(s => s.InvokingAsync(context, It.IsAny<CancellationToken>()))
@@ -118,7 +121,7 @@ public sealed class ChatHistoryProviderMessageFilterTests
new(ChatRole.User, "Hello"),
new(ChatRole.Assistant, "Hi there!")
};
var context = new ChatHistoryProvider.InvokingContext([new ChatMessage(ChatRole.User, "Test")]);
var context = new ChatHistoryProvider.InvokingContext(s_mockAgent, s_mockSession, [new ChatMessage(ChatRole.User, "Test")]);
innerProviderMock
.Setup(s => s.InvokingAsync(context, It.IsAny<CancellationToken>()))
@@ -147,7 +150,7 @@ public sealed class ChatHistoryProviderMessageFilterTests
var requestMessages = new List<ChatMessage> { new(ChatRole.User, "Hello") };
var chatHistoryProviderMessages = new List<ChatMessage> { new(ChatRole.System, "System") };
var responseMessages = new List<ChatMessage> { new(ChatRole.Assistant, "Response") };
var context = new ChatHistoryProvider.InvokedContext(requestMessages, chatHistoryProviderMessages)
var context = new ChatHistoryProvider.InvokedContext(s_mockAgent, s_mockSession, requestMessages, chatHistoryProviderMessages)
{
ResponseMessages = responseMessages
};
@@ -162,7 +165,7 @@ public sealed class ChatHistoryProviderMessageFilterTests
ChatHistoryProvider.InvokedContext InvokedFilter(ChatHistoryProvider.InvokedContext ctx)
{
var modifiedRequestMessages = ctx.RequestMessages.Select(m => new ChatMessage(m.Role, $"[FILTERED] {m.Text}")).ToList();
return new ChatHistoryProvider.InvokedContext(modifiedRequestMessages, ctx.ChatHistoryProviderMessages)
return new ChatHistoryProvider.InvokedContext(s_mockAgent, s_mockSession, modifiedRequestMessages, ctx.ChatHistoryProviderMessages)
{
ResponseMessages = ctx.ResponseMessages,
AIContextProviderMessages = ctx.AIContextProviderMessages,
@@ -6,6 +6,7 @@ using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.AI;
using Moq;
namespace Microsoft.Agents.AI.Abstractions.UnitTests;
@@ -14,6 +15,9 @@ namespace Microsoft.Agents.AI.Abstractions.UnitTests;
/// </summary>
public class ChatHistoryProviderTests
{
private static readonly AIAgent s_mockAgent = new Mock<AIAgent>().Object;
private static readonly AgentSession s_mockSession = new Mock<AgentSession>().Object;
#region GetService Method Tests
[Fact]
@@ -76,6 +80,238 @@ public class ChatHistoryProviderTests
#endregion
#region InvokingContext Tests
[Fact]
public void InvokingContext_Constructor_ThrowsForNullMessages()
{
// Arrange & Act & Assert
Assert.Throws<ArgumentNullException>(() => new ChatHistoryProvider.InvokingContext(s_mockAgent, s_mockSession, null!));
}
[Fact]
public void InvokingContext_RequestMessages_SetterThrowsForNull()
{
// Arrange
var messages = new List<ChatMessage> { new(ChatRole.User, "Hello") };
var context = new ChatHistoryProvider.InvokingContext(s_mockAgent, s_mockSession, messages);
// Act & Assert
Assert.Throws<ArgumentNullException>(() => context.RequestMessages = null!);
}
[Fact]
public void InvokingContext_RequestMessages_SetterRoundtrips()
{
// Arrange
var initialMessages = new List<ChatMessage> { new(ChatRole.User, "Hello") };
var newMessages = new List<ChatMessage> { new(ChatRole.User, "New message") };
var context = new ChatHistoryProvider.InvokingContext(s_mockAgent, s_mockSession, initialMessages);
// Act
context.RequestMessages = newMessages;
// Assert
Assert.Same(newMessages, context.RequestMessages);
}
[Fact]
public void InvokingContext_Agent_ReturnsConstructorValue()
{
// Arrange
var messages = new List<ChatMessage> { new(ChatRole.User, "Hello") };
// Act
var context = new ChatHistoryProvider.InvokingContext(s_mockAgent, s_mockSession, messages);
// Assert
Assert.Same(s_mockAgent, context.Agent);
}
[Fact]
public void InvokingContext_Session_ReturnsConstructorValue()
{
// Arrange
var messages = new List<ChatMessage> { new(ChatRole.User, "Hello") };
// Act
var context = new ChatHistoryProvider.InvokingContext(s_mockAgent, s_mockSession, messages);
// Assert
Assert.Same(s_mockSession, context.Session);
}
[Fact]
public void InvokingContext_Session_CanBeNull()
{
// Arrange
var messages = new List<ChatMessage> { new(ChatRole.User, "Hello") };
// Act
var context = new ChatHistoryProvider.InvokingContext(s_mockAgent, null, messages);
// Assert
Assert.Null(context.Session);
}
[Fact]
public void InvokingContext_Constructor_ThrowsForNullAgent()
{
// Arrange
var messages = new List<ChatMessage> { new(ChatRole.User, "Hello") };
// Act & Assert
Assert.Throws<ArgumentNullException>(() => new ChatHistoryProvider.InvokingContext(null!, s_mockSession, messages));
}
#endregion
#region InvokedContext Tests
[Fact]
public void InvokedContext_Constructor_ThrowsForNullRequestMessages()
{
// Arrange & Act & Assert
Assert.Throws<ArgumentNullException>(() => new ChatHistoryProvider.InvokedContext(s_mockAgent, s_mockSession, null!, []));
}
[Fact]
public void InvokedContext_RequestMessages_SetterThrowsForNull()
{
// Arrange
var requestMessages = new List<ChatMessage> { new(ChatRole.User, "Hello") };
var context = new ChatHistoryProvider.InvokedContext(s_mockAgent, s_mockSession, requestMessages, []);
// Act & Assert
Assert.Throws<ArgumentNullException>(() => context.RequestMessages = null!);
}
[Fact]
public void InvokedContext_RequestMessages_SetterRoundtrips()
{
// Arrange
var initialMessages = new List<ChatMessage> { new(ChatRole.User, "Hello") };
var newMessages = new List<ChatMessage> { new(ChatRole.User, "New message") };
var context = new ChatHistoryProvider.InvokedContext(s_mockAgent, s_mockSession, initialMessages, []);
// Act
context.RequestMessages = newMessages;
// Assert
Assert.Same(newMessages, context.RequestMessages);
}
[Fact]
public void InvokedContext_ChatHistoryProviderMessages_SetterRoundtrips()
{
// Arrange
var requestMessages = new List<ChatMessage> { new(ChatRole.User, "Hello") };
var newProviderMessages = new List<ChatMessage> { new(ChatRole.System, "System message") };
var context = new ChatHistoryProvider.InvokedContext(s_mockAgent, s_mockSession, requestMessages, []);
// Act
context.ChatHistoryProviderMessages = newProviderMessages;
// Assert
Assert.Same(newProviderMessages, context.ChatHistoryProviderMessages);
}
[Fact]
public void InvokedContext_AIContextProviderMessages_Roundtrips()
{
// Arrange
var requestMessages = new List<ChatMessage> { new(ChatRole.User, "Hello") };
var aiContextMessages = new List<ChatMessage> { new(ChatRole.System, "AI context message") };
var context = new ChatHistoryProvider.InvokedContext(s_mockAgent, s_mockSession, requestMessages, []);
// Act
context.AIContextProviderMessages = aiContextMessages;
// Assert
Assert.Same(aiContextMessages, context.AIContextProviderMessages);
}
[Fact]
public void InvokedContext_ResponseMessages_Roundtrips()
{
// Arrange
var requestMessages = new List<ChatMessage> { new(ChatRole.User, "Hello") };
var responseMessages = new List<ChatMessage> { new(ChatRole.Assistant, "Response message") };
var context = new ChatHistoryProvider.InvokedContext(s_mockAgent, s_mockSession, requestMessages, []);
// Act
context.ResponseMessages = responseMessages;
// Assert
Assert.Same(responseMessages, context.ResponseMessages);
}
[Fact]
public void InvokedContext_InvokeException_Roundtrips()
{
// Arrange
var requestMessages = new List<ChatMessage> { new(ChatRole.User, "Hello") };
var exception = new InvalidOperationException("Test exception");
var context = new ChatHistoryProvider.InvokedContext(s_mockAgent, s_mockSession, requestMessages, []);
// Act
context.InvokeException = exception;
// Assert
Assert.Same(exception, context.InvokeException);
}
[Fact]
public void InvokedContext_Agent_ReturnsConstructorValue()
{
// Arrange
var requestMessages = new List<ChatMessage> { new(ChatRole.User, "Hello") };
// Act
var context = new ChatHistoryProvider.InvokedContext(s_mockAgent, s_mockSession, requestMessages, []);
// Assert
Assert.Same(s_mockAgent, context.Agent);
}
[Fact]
public void InvokedContext_Session_ReturnsConstructorValue()
{
// Arrange
var requestMessages = new List<ChatMessage> { new(ChatRole.User, "Hello") };
// Act
var context = new ChatHistoryProvider.InvokedContext(s_mockAgent, s_mockSession, requestMessages, []);
// Assert
Assert.Same(s_mockSession, context.Session);
}
[Fact]
public void InvokedContext_Session_CanBeNull()
{
// Arrange
var requestMessages = new List<ChatMessage> { new(ChatRole.User, "Hello") };
// Act
var context = new ChatHistoryProvider.InvokedContext(s_mockAgent, null, requestMessages, []);
// Assert
Assert.Null(context.Session);
}
[Fact]
public void InvokedContext_Constructor_ThrowsForNullAgent()
{
// Arrange
var requestMessages = new List<ChatMessage> { new(ChatRole.User, "Hello") };
// Act & Assert
Assert.Throws<ArgumentNullException>(() => new ChatHistoryProvider.InvokedContext(null!, s_mockSession, requestMessages, []));
}
#endregion
private sealed class TestChatHistoryProvider : ChatHistoryProvider
{
public override ValueTask<IEnumerable<ChatMessage>> InvokingAsync(InvokingContext context, CancellationToken cancellationToken = default)
@@ -2,6 +2,7 @@
using System;
using System.Collections.Generic;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.AI;
@@ -26,7 +27,7 @@ public class DelegatingAIAgentTests
/// </summary>
public DelegatingAIAgentTests()
{
this._innerAgentMock = new Mock<AIAgent>();
this._innerAgentMock = new Mock<AIAgent> { CallBase = true };
this._testResponse = new AgentResponse(new ChatMessage(ChatRole.Assistant, "Test response"));
this._testStreamingResponses = [new AgentResponseUpdate(ChatRole.Assistant, "Test streaming response")];
this._testSession = new TestAgentSession();
@@ -35,7 +36,10 @@ public class DelegatingAIAgentTests
this._innerAgentMock.Protected().SetupGet<string>("IdCore").Returns("test-agent-id");
this._innerAgentMock.Setup(x => x.Name).Returns("Test Agent");
this._innerAgentMock.Setup(x => x.Description).Returns("Test Description");
this._innerAgentMock.Setup(x => x.CreateSessionAsync()).ReturnsAsync(this._testSession);
this._innerAgentMock
.Protected()
.Setup<ValueTask<AgentSession>>("CreateSessionCoreAsync", ItExpr.IsAny<CancellationToken>())
.ReturnsAsync(this._testSession);
this._innerAgentMock
.Protected()
@@ -142,7 +146,32 @@ public class DelegatingAIAgentTests
// Assert
Assert.Same(this._testSession, session);
this._innerAgentMock.Verify(x => x.CreateSessionAsync(), Times.Once);
this._innerAgentMock
.Protected()
.Verify<ValueTask<AgentSession>>("CreateSessionCoreAsync", Times.Once(), ItExpr.IsAny<CancellationToken>());
}
/// <summary>
/// Verify that DeserializeSessionAsync delegates to inner agent.
/// </summary>
[Fact]
public async Task DeserializeSessionAsync_DelegatesToInnerAgentAsync()
{
// Arrange
var serializedSession = JsonSerializer.SerializeToElement("test-session-id", TestJsonSerializerContext.Default.String);
this._innerAgentMock
.Protected()
.Setup<ValueTask<AgentSession>>("DeserializeSessionCoreAsync", ItExpr.IsAny<JsonElement>(), ItExpr.IsAny<JsonSerializerOptions?>(), ItExpr.IsAny<CancellationToken>())
.ReturnsAsync(this._testSession);
// Act
var session = await this._delegatingAgent.DeserializeSessionAsync(serializedSession);
// Assert
Assert.Same(this._testSession, session);
this._innerAgentMock
.Protected()
.Verify<ValueTask<AgentSession>>("DeserializeSessionCoreAsync", Times.Once(), ItExpr.IsAny<JsonElement>(), ItExpr.IsAny<JsonSerializerOptions?>(), ItExpr.IsAny<CancellationToken>());
}
/// <summary>
@@ -18,6 +18,9 @@ namespace Microsoft.Agents.AI.Abstractions.UnitTests;
/// </summary>
public class InMemoryChatHistoryProviderTests
{
private static readonly AIAgent s_mockAgent = new Mock<AIAgent>().Object;
private static readonly AgentSession s_mockSession = new Mock<AgentSession>().Object;
[Fact]
public void Constructor_Throws_ForNullReducer() =>
// Arrange & Act & Assert
@@ -68,7 +71,7 @@ public class InMemoryChatHistoryProviderTests
var provider = new InMemoryChatHistoryProvider();
provider.Add(providerMessages[0]);
var context = new ChatHistoryProvider.InvokedContext(requestMessages, providerMessages)
var context = new ChatHistoryProvider.InvokedContext(s_mockAgent, s_mockSession, requestMessages, providerMessages)
{
AIContextProviderMessages = aiContextProviderMessages,
ResponseMessages = responseMessages
@@ -87,7 +90,7 @@ public class InMemoryChatHistoryProviderTests
{
var provider = new InMemoryChatHistoryProvider();
var context = new ChatHistoryProvider.InvokedContext([], []);
var context = new ChatHistoryProvider.InvokedContext(s_mockAgent, s_mockSession, [], []);
await provider.InvokedAsync(context, CancellationToken.None);
Assert.Empty(provider);
@@ -102,7 +105,7 @@ public class InMemoryChatHistoryProviderTests
new ChatMessage(ChatRole.Assistant, "Test2")
};
var context = new ChatHistoryProvider.InvokingContext([]);
var context = new ChatHistoryProvider.InvokingContext(s_mockAgent, s_mockSession, []);
var result = (await provider.InvokingAsync(context, CancellationToken.None)).ToList();
Assert.Equal(2, result.Count);
@@ -183,7 +186,7 @@ public class InMemoryChatHistoryProviderTests
var provider = new InMemoryChatHistoryProvider();
var messages = new List<ChatMessage>();
var context = new ChatHistoryProvider.InvokedContext(messages, []);
var context = new ChatHistoryProvider.InvokedContext(s_mockAgent, s_mockSession, messages, []);
await provider.InvokedAsync(context, CancellationToken.None);
Assert.Empty(provider);
@@ -520,7 +523,7 @@ public class InMemoryChatHistoryProviderTests
var provider = new InMemoryChatHistoryProvider(reducerMock.Object, InMemoryChatHistoryProvider.ChatReducerTriggerEvent.AfterMessageAdded);
// Act
var context = new ChatHistoryProvider.InvokedContext(originalMessages, []);
var context = new ChatHistoryProvider.InvokedContext(s_mockAgent, s_mockSession, originalMessages, []);
await provider.InvokedAsync(context, CancellationToken.None);
// Assert
@@ -556,7 +559,7 @@ public class InMemoryChatHistoryProviderTests
}
// Act
var invokingContext = new ChatHistoryProvider.InvokingContext(Array.Empty<ChatMessage>());
var invokingContext = new ChatHistoryProvider.InvokingContext(s_mockAgent, s_mockSession, Array.Empty<ChatMessage>());
var result = (await provider.InvokingAsync(invokingContext, CancellationToken.None)).ToList();
// Assert
@@ -579,7 +582,7 @@ public class InMemoryChatHistoryProviderTests
var provider = new InMemoryChatHistoryProvider(reducerMock.Object, InMemoryChatHistoryProvider.ChatReducerTriggerEvent.BeforeMessagesRetrieval);
// Act
var context = new ChatHistoryProvider.InvokedContext(originalMessages, []);
var context = new ChatHistoryProvider.InvokedContext(s_mockAgent, s_mockSession, originalMessages, []);
await provider.InvokedAsync(context, CancellationToken.None);
// Assert
@@ -605,7 +608,7 @@ public class InMemoryChatHistoryProviderTests
};
// Act
var invokingContext = new ChatHistoryProvider.InvokingContext(Array.Empty<ChatMessage>());
var invokingContext = new ChatHistoryProvider.InvokingContext(s_mockAgent, s_mockSession, Array.Empty<ChatMessage>());
var result = (await provider.InvokingAsync(invokingContext, CancellationToken.None)).ToList();
// Assert
@@ -614,6 +617,42 @@ public class InMemoryChatHistoryProviderTests
reducerMock.Verify(r => r.ReduceAsync(It.IsAny<IEnumerable<ChatMessage>>(), It.IsAny<CancellationToken>()), Times.Never);
}
[Fact]
public async Task InvokedAsync_WithException_DoesNotAddMessagesAsync()
{
// Arrange
var provider = new InMemoryChatHistoryProvider();
var requestMessages = new List<ChatMessage>
{
new(ChatRole.User, "Hello")
};
var responseMessages = new List<ChatMessage>
{
new(ChatRole.Assistant, "Hi there!")
};
var context = new ChatHistoryProvider.InvokedContext(s_mockAgent, s_mockSession, requestMessages, [])
{
ResponseMessages = responseMessages,
InvokeException = new InvalidOperationException("Test exception")
};
// Act
await provider.InvokedAsync(context, CancellationToken.None);
// Assert
Assert.Empty(provider);
}
[Fact]
public async Task InvokingAsync_WithNullContext_ThrowsArgumentNullExceptionAsync()
{
// Arrange
var provider = new InMemoryChatHistoryProvider();
// Act & Assert
await Assert.ThrowsAsync<ArgumentNullException>(() => provider.InvokingAsync(null!, CancellationToken.None).AsTask());
}
public class TestAIContent(string testData) : AIContent
{
public string TestData => testData;
@@ -2384,6 +2384,134 @@ public sealed class AzureAIProjectChatClientExtensionsTests
#endregion
#region Empty Version and ID Handling Tests
/// <summary>
/// Verify that GetAIAgentAsync handles an agent with empty version by using "latest" as fallback.
/// </summary>
[Fact]
public async Task GetAIAgentAsync_WithEmptyVersion_CreatesAgentSuccessfullyAsync()
{
// Arrange
AIProjectClient client = this.CreateTestAgentClientWithEmptyVersion();
var options = new ChatClientAgentOptions
{
Name = "test-agent",
ChatOptions = new ChatOptions { Instructions = "Test" }
};
// Act
ChatClientAgent agent = await client.GetAIAgentAsync(options);
// Assert
Assert.NotNull(agent);
Assert.IsType<ChatClientAgent>(agent);
// Verify the agent ID is generated from server-returned name ("agent_abc123") and "latest"
Assert.Equal("agent_abc123:latest", agent.Id);
}
/// <summary>
/// Verify that AsAIAgent with AgentRecord handles empty version by using "latest" as fallback.
/// </summary>
[Fact]
public void AsAIAgent_WithAgentRecordEmptyVersion_CreatesAgentWithGeneratedId()
{
// Arrange
AIProjectClient client = this.CreateTestAgentClientWithEmptyVersion();
AgentRecord agentRecord = this.CreateTestAgentRecordWithEmptyVersion();
// Act
var agent = client.AsAIAgent(agentRecord);
// Assert
Assert.NotNull(agent);
// Verify the agent ID is generated from agent record name ("agent_abc123") and "latest"
Assert.Equal("agent_abc123:latest", agent.Id);
}
/// <summary>
/// Verify that AsAIAgent with AgentVersion handles empty version by using "latest" as fallback.
/// </summary>
[Fact]
public void AsAIAgent_WithAgentVersionEmptyVersion_CreatesAgentWithGeneratedId()
{
// Arrange
AIProjectClient client = this.CreateTestAgentClientWithEmptyVersion();
AgentVersion agentVersion = this.CreateTestAgentVersionWithEmptyVersion();
// Act
var agent = client.AsAIAgent(agentVersion);
// Assert
Assert.NotNull(agent);
// Verify the agent ID is generated from agent version name ("agent_abc123") and "latest"
Assert.Equal("agent_abc123:latest", agent.Id);
}
/// <summary>
/// Verify that GetAIAgentAsync handles an agent with whitespace-only version by using "latest" as fallback.
/// </summary>
[Fact]
public async Task GetAIAgentAsync_WithWhitespaceVersion_CreatesAgentSuccessfullyAsync()
{
// Arrange
AIProjectClient client = this.CreateTestAgentClientWithWhitespaceVersion();
var options = new ChatClientAgentOptions
{
Name = "test-agent",
ChatOptions = new ChatOptions { Instructions = "Test" }
};
// Act
ChatClientAgent agent = await client.GetAIAgentAsync(options);
// Assert
Assert.NotNull(agent);
Assert.IsType<ChatClientAgent>(agent);
// Verify the agent ID is generated from server-returned name ("agent_abc123") and "latest"
Assert.Equal("agent_abc123:latest", agent.Id);
}
/// <summary>
/// Verify that AsAIAgent with AgentRecord handles whitespace-only version by using "latest" as fallback.
/// </summary>
[Fact]
public void AsAIAgent_WithAgentRecordWhitespaceVersion_CreatesAgentWithGeneratedId()
{
// Arrange
AIProjectClient client = this.CreateTestAgentClientWithWhitespaceVersion();
AgentRecord agentRecord = this.CreateTestAgentRecordWithWhitespaceVersion();
// Act
var agent = client.AsAIAgent(agentRecord);
// Assert
Assert.NotNull(agent);
// Verify the agent ID is generated from agent record name ("agent_abc123") and "latest"
Assert.Equal("agent_abc123:latest", agent.Id);
}
/// <summary>
/// Verify that AsAIAgent with AgentVersion handles whitespace-only version by using "latest" as fallback.
/// </summary>
[Fact]
public void AsAIAgent_WithAgentVersionWhitespaceVersion_CreatesAgentWithGeneratedId()
{
// Arrange
AIProjectClient client = this.CreateTestAgentClientWithWhitespaceVersion();
AgentVersion agentVersion = this.CreateTestAgentVersionWithWhitespaceVersion();
// Act
var agent = client.AsAIAgent(agentVersion);
// Assert
Assert.NotNull(agent);
// Verify the agent ID is generated from agent version name ("agent_abc123") and "latest"
Assert.Equal("agent_abc123:latest", agent.Id);
}
#endregion
#region ApplyToolsToAgentDefinition Tests
/// <summary>
@@ -2678,6 +2806,54 @@ public sealed class AzureAIProjectChatClientExtensionsTests
return ModelReaderWriter.Read<AgentRecord>(BinaryData.FromString(TestDataUtil.GetAgentResponseJson(agentDefinition: agentDefinition)))!;
}
/// <summary>
/// Creates a test AIProjectClient with empty version fields for testing hosted MCP agents.
/// </summary>
private FakeAgentClient CreateTestAgentClientWithEmptyVersion(string? agentName = null, string? instructions = null, string? description = null, AgentDefinition? agentDefinitionResponse = null)
{
return new FakeAgentClient(agentName, instructions, description, agentDefinitionResponse, useEmptyVersion: true);
}
/// <summary>
/// Creates a test AgentRecord with empty version for testing hosted MCP agents.
/// </summary>
private AgentRecord CreateTestAgentRecordWithEmptyVersion(AgentDefinition? agentDefinition = null)
{
return ModelReaderWriter.Read<AgentRecord>(BinaryData.FromString(TestDataUtil.GetAgentResponseJsonWithEmptyVersion(agentDefinition: agentDefinition)))!;
}
/// <summary>
/// Creates a test AgentVersion with empty version for testing hosted MCP agents.
/// </summary>
private AgentVersion CreateTestAgentVersionWithEmptyVersion()
{
return ModelReaderWriter.Read<AgentVersion>(BinaryData.FromString(TestDataUtil.GetAgentVersionResponseJsonWithEmptyVersion()))!;
}
/// <summary>
/// Creates a test AIProjectClient with whitespace-only version fields for testing hosted MCP agents.
/// </summary>
private FakeAgentClient CreateTestAgentClientWithWhitespaceVersion(string? agentName = null, string? instructions = null, string? description = null, AgentDefinition? agentDefinitionResponse = null)
{
return new FakeAgentClient(agentName, instructions, description, agentDefinitionResponse, versionMode: VersionMode.Whitespace);
}
/// <summary>
/// Creates a test AgentRecord with whitespace-only version for testing hosted MCP agents.
/// </summary>
private AgentRecord CreateTestAgentRecordWithWhitespaceVersion(AgentDefinition? agentDefinition = null)
{
return ModelReaderWriter.Read<AgentRecord>(BinaryData.FromString(TestDataUtil.GetAgentResponseJsonWithWhitespaceVersion(agentDefinition: agentDefinition)))!;
}
/// <summary>
/// Creates a test AgentVersion with whitespace-only version for testing hosted MCP agents.
/// </summary>
private AgentVersion CreateTestAgentVersionWithWhitespaceVersion()
{
return ModelReaderWriter.Read<AgentVersion>(BinaryData.FromString(TestDataUtil.GetAgentVersionResponseJsonWithWhitespaceVersion()))!;
}
private const string OpenAPISpec = """
{
"openapi": "3.0.3",
@@ -2716,14 +2892,26 @@ public sealed class AzureAIProjectChatClientExtensionsTests
return ModelReaderWriter.Read<AgentVersion>(BinaryData.FromString(TestDataUtil.GetAgentVersionResponseJson()))!;
}
/// <summary>
/// Specifies the version mode for test data generation.
/// </summary>
private enum VersionMode
{
Normal,
Empty,
Whitespace
}
/// <summary>
/// Fake AIProjectClient for testing.
/// </summary>
private sealed class FakeAgentClient : AIProjectClient
{
public FakeAgentClient(string? agentName = null, string? instructions = null, string? description = null, AgentDefinition? agentDefinitionResponse = null)
public FakeAgentClient(string? agentName = null, string? instructions = null, string? description = null, AgentDefinition? agentDefinitionResponse = null, bool useEmptyVersion = false, VersionMode versionMode = VersionMode.Normal)
{
this.Agents = new FakeAIProjectAgentsOperations(agentName, instructions, description, agentDefinitionResponse);
// Handle backward compatibility with bool parameter
var effectiveVersionMode = useEmptyVersion ? VersionMode.Empty : versionMode;
this.Agents = new FakeAIProjectAgentsOperations(agentName, instructions, description, agentDefinitionResponse, effectiveVersionMode);
}
public override ClientConnection GetConnection(string connectionId)
@@ -2739,60 +2927,82 @@ public sealed class AzureAIProjectChatClientExtensionsTests
private readonly string? _instructions;
private readonly string? _description;
private readonly AgentDefinition? _agentDefinition;
private readonly VersionMode _versionMode;
public FakeAIProjectAgentsOperations(string? agentName = null, string? instructions = null, string? description = null, AgentDefinition? agentDefinitionResponse = null)
public FakeAIProjectAgentsOperations(string? agentName = null, string? instructions = null, string? description = null, AgentDefinition? agentDefinitionResponse = null, VersionMode versionMode = VersionMode.Normal)
{
this._agentName = agentName;
this._instructions = instructions;
this._description = description;
this._agentDefinition = agentDefinitionResponse;
this._versionMode = versionMode;
}
private string GetAgentResponseJson()
{
return this._versionMode switch
{
VersionMode.Empty => TestDataUtil.GetAgentResponseJsonWithEmptyVersion(this._agentName, this._agentDefinition, this._instructions, this._description),
VersionMode.Whitespace => TestDataUtil.GetAgentResponseJsonWithWhitespaceVersion(this._agentName, this._agentDefinition, this._instructions, this._description),
_ => TestDataUtil.GetAgentResponseJson(this._agentName, this._agentDefinition, this._instructions, this._description)
};
}
private string GetAgentVersionResponseJson()
{
return this._versionMode switch
{
VersionMode.Empty => TestDataUtil.GetAgentVersionResponseJsonWithEmptyVersion(this._agentName, this._agentDefinition, this._instructions, this._description),
VersionMode.Whitespace => TestDataUtil.GetAgentVersionResponseJsonWithWhitespaceVersion(this._agentName, this._agentDefinition, this._instructions, this._description),
_ => TestDataUtil.GetAgentVersionResponseJson(this._agentName, this._agentDefinition, this._instructions, this._description)
};
}
public override ClientResult GetAgent(string agentName, RequestOptions options)
{
var responseJson = TestDataUtil.GetAgentResponseJson(this._agentName, this._agentDefinition, this._instructions, this._description);
var responseJson = this.GetAgentResponseJson();
return ClientResult.FromValue(ModelReaderWriter.Read<AgentRecord>(BinaryData.FromString(responseJson))!, new MockPipelineResponse(200, BinaryData.FromString(responseJson)));
}
public override ClientResult<AgentRecord> GetAgent(string agentName, CancellationToken cancellationToken = default)
{
var responseJson = TestDataUtil.GetAgentResponseJson(this._agentName, this._agentDefinition, this._instructions, this._description);
var responseJson = this.GetAgentResponseJson();
return ClientResult.FromValue(ModelReaderWriter.Read<AgentRecord>(BinaryData.FromString(responseJson))!, new MockPipelineResponse(200));
}
public override Task<ClientResult> GetAgentAsync(string agentName, RequestOptions options)
{
var responseJson = TestDataUtil.GetAgentResponseJson(this._agentName, this._agentDefinition, this._instructions, this._description);
var responseJson = this.GetAgentResponseJson();
return Task.FromResult<ClientResult>(ClientResult.FromValue(ModelReaderWriter.Read<AgentRecord>(BinaryData.FromString(responseJson))!, new MockPipelineResponse(200, BinaryData.FromString(responseJson))));
}
public override Task<ClientResult<AgentRecord>> GetAgentAsync(string agentName, CancellationToken cancellationToken = default)
{
var responseJson = TestDataUtil.GetAgentResponseJson(this._agentName, this._agentDefinition, this._instructions, this._description);
var responseJson = this.GetAgentResponseJson();
return Task.FromResult(ClientResult.FromValue(ModelReaderWriter.Read<AgentRecord>(BinaryData.FromString(responseJson))!, new MockPipelineResponse(200)));
}
public override ClientResult CreateAgentVersion(string agentName, BinaryContent content, RequestOptions? options = null)
{
var responseJson = TestDataUtil.GetAgentVersionResponseJson(this._agentName, this._agentDefinition, this._instructions, this._description);
var responseJson = this.GetAgentVersionResponseJson();
return ClientResult.FromValue(ModelReaderWriter.Read<AgentVersion>(BinaryData.FromString(responseJson))!, new MockPipelineResponse(200, BinaryData.FromString(responseJson)));
}
public override ClientResult<AgentVersion> CreateAgentVersion(string agentName, AgentVersionCreationOptions? options = null, CancellationToken cancellationToken = default)
{
var responseJson = TestDataUtil.GetAgentVersionResponseJson(this._agentName, this._agentDefinition, this._instructions, this._description);
var responseJson = this.GetAgentVersionResponseJson();
return ClientResult.FromValue(ModelReaderWriter.Read<AgentVersion>(BinaryData.FromString(responseJson))!, new MockPipelineResponse(200));
}
public override Task<ClientResult> CreateAgentVersionAsync(string agentName, BinaryContent content, RequestOptions? options = null)
{
var responseJson = TestDataUtil.GetAgentVersionResponseJson(this._agentName, this._agentDefinition, this._instructions, this._description);
var responseJson = this.GetAgentVersionResponseJson();
return Task.FromResult<ClientResult>(ClientResult.FromValue(ModelReaderWriter.Read<AgentVersion>(BinaryData.FromString(responseJson))!, new MockPipelineResponse(200, BinaryData.FromString(responseJson))));
}
public override Task<ClientResult<AgentVersion>> CreateAgentVersionAsync(string agentName, AgentVersionCreationOptions? options = null, CancellationToken cancellationToken = default)
{
var responseJson = TestDataUtil.GetAgentVersionResponseJson(this._agentName, this._agentDefinition, this._instructions, this._description);
var responseJson = this.GetAgentVersionResponseJson();
return Task.FromResult(ClientResult.FromValue(ModelReaderWriter.Read<AgentVersion>(BinaryData.FromString(responseJson))!, new MockPipelineResponse(200)));
}
}
@@ -52,6 +52,70 @@ internal static class TestDataUtil
return json;
}
/// <summary>
/// Gets the agent version response JSON with empty version and ID fields for testing hosted agents like MCP agents.
/// </summary>
public static string GetAgentVersionResponseJsonWithEmptyVersion(string? agentName = null, AgentDefinition? agentDefinition = null, string? instructions = null, string? description = null)
{
var json = s_agentVersionResponseJson;
json = ApplyAgentName(json, agentName);
json = ApplyAgentDefinition(json, agentDefinition);
json = ApplyInstructions(json, instructions);
json = ApplyDescription(json, description);
// Remove the version and id fields to simulate hosted agents without version
json = json.Replace("\"version\": \"1\",", "\"version\": \"\",");
json = json.Replace("\"id\": \"agent_abc123:1\",", "\"id\": \"\",");
return json;
}
/// <summary>
/// Gets the agent response JSON with empty version and ID fields in the latest version for testing hosted agents like MCP agents.
/// </summary>
public static string GetAgentResponseJsonWithEmptyVersion(string? agentName = null, AgentDefinition? agentDefinition = null, string? instructions = null, string? description = null)
{
var json = s_agentResponseJson;
json = ApplyAgentName(json, agentName);
json = ApplyAgentDefinition(json, agentDefinition);
json = ApplyInstructions(json, instructions);
json = ApplyDescription(json, description);
// Remove the version and id fields to simulate hosted agents without version
json = json.Replace("\"version\": \"1\",", "\"version\": \"\",");
json = json.Replace("\"id\": \"agent_abc123:1\",", "\"id\": \"\",");
return json;
}
/// <summary>
/// Gets the agent version response JSON with whitespace-only version and ID fields for testing hosted agents like MCP agents.
/// </summary>
public static string GetAgentVersionResponseJsonWithWhitespaceVersion(string? agentName = null, AgentDefinition? agentDefinition = null, string? instructions = null, string? description = null)
{
var json = s_agentVersionResponseJson;
json = ApplyAgentName(json, agentName);
json = ApplyAgentDefinition(json, agentDefinition);
json = ApplyInstructions(json, instructions);
json = ApplyDescription(json, description);
// Use whitespace-only version and id fields to simulate hosted agents without version
return json
.Replace("\"version\": \"1\",", "\"version\": \" \",")
.Replace("\"id\": \"agent_abc123:1\",", "\"id\": \" \",");
}
/// <summary>
/// Gets the agent response JSON with whitespace-only version and ID fields in the latest version for testing hosted agents like MCP agents.
/// </summary>
public static string GetAgentResponseJsonWithWhitespaceVersion(string? agentName = null, AgentDefinition? agentDefinition = null, string? instructions = null, string? description = null)
{
var json = s_agentResponseJson;
json = ApplyAgentName(json, agentName);
json = ApplyAgentDefinition(json, agentDefinition);
json = ApplyInstructions(json, instructions);
json = ApplyDescription(json, description);
// Use whitespace-only version and id fields to simulate hosted agents without version
return json
.Replace("\"version\": \"1\",", "\"version\": \" \",")
.Replace("\"id\": \"agent_abc123:1\",", "\"id\": \" \",");
}
/// <summary>
/// Gets the OpenAI default response JSON with optional placeholder replacements applied.
/// </summary>
@@ -41,6 +41,9 @@ namespace Microsoft.Agents.AI.CosmosNoSql.UnitTests;
[Collection("CosmosDB")]
public sealed class CosmosChatHistoryProviderTests : IAsyncLifetime, IDisposable
{
private static readonly AIAgent s_mockAgent = new Moq.Mock<AIAgent>().Object;
private static readonly AgentSession s_mockSession = new Moq.Mock<AgentSession>().Object;
// Cosmos DB Emulator connection settings
private const string EmulatorEndpoint = "https://localhost:8081";
private const string EmulatorKey = "C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==";
@@ -214,7 +217,7 @@ public sealed class CosmosChatHistoryProviderTests : IAsyncLifetime, IDisposable
using var provider = new CosmosChatHistoryProvider(this._connectionString, s_testDatabaseId, TestContainerId, conversationId);
var message = new ChatMessage(ChatRole.User, "Hello, world!");
var context = new ChatHistoryProvider.InvokedContext([message], [])
var context = new ChatHistoryProvider.InvokedContext(s_mockAgent, s_mockSession, [message], [])
{
ResponseMessages = []
};
@@ -226,7 +229,7 @@ public sealed class CosmosChatHistoryProviderTests : IAsyncLifetime, IDisposable
await Task.Delay(100);
// Assert
var invokingContext = new ChatHistoryProvider.InvokingContext([]);
var invokingContext = new ChatHistoryProvider.InvokingContext(s_mockAgent, s_mockSession, []);
var messages = await provider.InvokingAsync(invokingContext);
var messageList = messages.ToList();
@@ -293,7 +296,7 @@ public sealed class CosmosChatHistoryProviderTests : IAsyncLifetime, IDisposable
new ChatMessage(ChatRole.Assistant, "Response message")
};
var context = new ChatHistoryProvider.InvokedContext(requestMessages, [])
var context = new ChatHistoryProvider.InvokedContext(s_mockAgent, s_mockSession, requestMessages, [])
{
AIContextProviderMessages = aiContextProviderMessages,
ResponseMessages = responseMessages
@@ -303,7 +306,7 @@ public sealed class CosmosChatHistoryProviderTests : IAsyncLifetime, IDisposable
await provider.InvokedAsync(context);
// Assert
var invokingContext = new ChatHistoryProvider.InvokingContext([]);
var invokingContext = new ChatHistoryProvider.InvokingContext(s_mockAgent, s_mockSession, []);
var retrievedMessages = await provider.InvokingAsync(invokingContext);
var messageList = retrievedMessages.ToList();
Assert.Equal(5, messageList.Count);
@@ -327,7 +330,7 @@ public sealed class CosmosChatHistoryProviderTests : IAsyncLifetime, IDisposable
using var provider = new CosmosChatHistoryProvider(this._connectionString, s_testDatabaseId, TestContainerId, Guid.NewGuid().ToString());
// Act
var invokingContext = new ChatHistoryProvider.InvokingContext([]);
var invokingContext = new ChatHistoryProvider.InvokingContext(s_mockAgent, s_mockSession, []);
var messages = await provider.InvokingAsync(invokingContext);
// Assert
@@ -346,15 +349,15 @@ public sealed class CosmosChatHistoryProviderTests : IAsyncLifetime, IDisposable
using var store1 = new CosmosChatHistoryProvider(this._connectionString, s_testDatabaseId, TestContainerId, conversation1);
using var store2 = new CosmosChatHistoryProvider(this._connectionString, s_testDatabaseId, TestContainerId, conversation2);
var context1 = new ChatHistoryProvider.InvokedContext([new ChatMessage(ChatRole.User, "Message for conversation 1")], []);
var context2 = new ChatHistoryProvider.InvokedContext([new ChatMessage(ChatRole.User, "Message for conversation 2")], []);
var context1 = new ChatHistoryProvider.InvokedContext(s_mockAgent, s_mockSession, [new ChatMessage(ChatRole.User, "Message for conversation 1")], []);
var context2 = new ChatHistoryProvider.InvokedContext(s_mockAgent, s_mockSession, [new ChatMessage(ChatRole.User, "Message for conversation 2")], []);
await store1.InvokedAsync(context1);
await store2.InvokedAsync(context2);
// Act
var invokingContext1 = new ChatHistoryProvider.InvokingContext([]);
var invokingContext2 = new ChatHistoryProvider.InvokingContext([]);
var invokingContext1 = new ChatHistoryProvider.InvokingContext(s_mockAgent, s_mockSession, []);
var invokingContext2 = new ChatHistoryProvider.InvokingContext(s_mockAgent, s_mockSession, []);
var messages1 = await store1.InvokingAsync(invokingContext1);
var messages2 = await store2.InvokingAsync(invokingContext2);
@@ -391,11 +394,11 @@ public sealed class CosmosChatHistoryProviderTests : IAsyncLifetime, IDisposable
};
// Act 1: Add messages
var invokedContext = new ChatHistoryProvider.InvokedContext(messages, []);
var invokedContext = new ChatHistoryProvider.InvokedContext(s_mockAgent, s_mockSession, messages, []);
await originalStore.InvokedAsync(invokedContext);
// Act 2: Verify messages were added
var invokingContext = new ChatHistoryProvider.InvokingContext([]);
var invokingContext = new ChatHistoryProvider.InvokingContext(s_mockAgent, s_mockSession, []);
var retrievedMessages = await originalStore.InvokingAsync(invokingContext);
var retrievedList = retrievedMessages.ToList();
Assert.Equal(5, retrievedList.Count);
@@ -545,7 +548,7 @@ public sealed class CosmosChatHistoryProviderTests : IAsyncLifetime, IDisposable
using var provider = new CosmosChatHistoryProvider(this._connectionString, s_testDatabaseId, HierarchicalTestContainerId, TenantId, UserId, SessionId);
var message = new ChatMessage(ChatRole.User, "Hello from hierarchical partitioning!");
var context = new ChatHistoryProvider.InvokedContext([message], []);
var context = new ChatHistoryProvider.InvokedContext(s_mockAgent, s_mockSession, [message], []);
// Act
await provider.InvokedAsync(context);
@@ -554,7 +557,7 @@ public sealed class CosmosChatHistoryProviderTests : IAsyncLifetime, IDisposable
await Task.Delay(100);
// Assert
var invokingContext = new ChatHistoryProvider.InvokingContext([]);
var invokingContext = new ChatHistoryProvider.InvokingContext(s_mockAgent, s_mockSession, []);
var messages = await provider.InvokingAsync(invokingContext);
var messageList = messages.ToList();
@@ -602,7 +605,7 @@ public sealed class CosmosChatHistoryProviderTests : IAsyncLifetime, IDisposable
new ChatMessage(ChatRole.User, "Third hierarchical message")
};
var context = new ChatHistoryProvider.InvokedContext(messages, []);
var context = new ChatHistoryProvider.InvokedContext(s_mockAgent, s_mockSession, messages, []);
// Act
await provider.InvokedAsync(context);
@@ -611,7 +614,7 @@ public sealed class CosmosChatHistoryProviderTests : IAsyncLifetime, IDisposable
await Task.Delay(100);
// Assert
var invokingContext = new ChatHistoryProvider.InvokingContext([]);
var invokingContext = new ChatHistoryProvider.InvokingContext(s_mockAgent, s_mockSession, []);
var retrievedMessages = await provider.InvokingAsync(invokingContext);
var messageList = retrievedMessages.ToList();
@@ -637,8 +640,8 @@ public sealed class CosmosChatHistoryProviderTests : IAsyncLifetime, IDisposable
using var store2 = new CosmosChatHistoryProvider(this._connectionString, s_testDatabaseId, HierarchicalTestContainerId, TenantId, UserId2, SessionId);
// Add messages to both stores
var context1 = new ChatHistoryProvider.InvokedContext([new ChatMessage(ChatRole.User, "Message from user 1")], []);
var context2 = new ChatHistoryProvider.InvokedContext([new ChatMessage(ChatRole.User, "Message from user 2")], []);
var context1 = new ChatHistoryProvider.InvokedContext(s_mockAgent, s_mockSession, [new ChatMessage(ChatRole.User, "Message from user 1")], []);
var context2 = new ChatHistoryProvider.InvokedContext(s_mockAgent, s_mockSession, [new ChatMessage(ChatRole.User, "Message from user 2")], []);
await store1.InvokedAsync(context1);
await store2.InvokedAsync(context2);
@@ -647,8 +650,8 @@ public sealed class CosmosChatHistoryProviderTests : IAsyncLifetime, IDisposable
await Task.Delay(100);
// Act & Assert
var invokingContext1 = new ChatHistoryProvider.InvokingContext([]);
var invokingContext2 = new ChatHistoryProvider.InvokingContext([]);
var invokingContext1 = new ChatHistoryProvider.InvokingContext(s_mockAgent, s_mockSession, []);
var invokingContext2 = new ChatHistoryProvider.InvokingContext(s_mockAgent, s_mockSession, []);
var messages1 = await store1.InvokingAsync(invokingContext1);
var messageList1 = messages1.ToList();
@@ -675,7 +678,7 @@ public sealed class CosmosChatHistoryProviderTests : IAsyncLifetime, IDisposable
using var originalStore = new CosmosChatHistoryProvider(this._connectionString, s_testDatabaseId, HierarchicalTestContainerId, TenantId, UserId, SessionId);
var context = new ChatHistoryProvider.InvokedContext([new ChatMessage(ChatRole.User, "Test serialization message")], []);
var context = new ChatHistoryProvider.InvokedContext(s_mockAgent, s_mockSession, [new ChatMessage(ChatRole.User, "Test serialization message")], []);
await originalStore.InvokedAsync(context);
// Act - Serialize the provider state
@@ -693,7 +696,7 @@ public sealed class CosmosChatHistoryProviderTests : IAsyncLifetime, IDisposable
await Task.Delay(100);
// Assert - The deserialized provider should have the same functionality
var invokingContext = new ChatHistoryProvider.InvokingContext([]);
var invokingContext = new ChatHistoryProvider.InvokingContext(s_mockAgent, s_mockSession, []);
var messages = await deserializedStore.InvokingAsync(invokingContext);
var messageList = messages.ToList();
@@ -717,8 +720,8 @@ public sealed class CosmosChatHistoryProviderTests : IAsyncLifetime, IDisposable
using var hierarchicalProvider = new CosmosChatHistoryProvider(this._connectionString, s_testDatabaseId, HierarchicalTestContainerId, "tenant-coexist", "user-coexist", SessionId);
// Add messages to both
var simpleContext = new ChatHistoryProvider.InvokedContext([new ChatMessage(ChatRole.User, "Simple partitioning message")], []);
var hierarchicalContext = new ChatHistoryProvider.InvokedContext([new ChatMessage(ChatRole.User, "Hierarchical partitioning message")], []);
var simpleContext = new ChatHistoryProvider.InvokedContext(s_mockAgent, s_mockSession, [new ChatMessage(ChatRole.User, "Simple partitioning message")], []);
var hierarchicalContext = new ChatHistoryProvider.InvokedContext(s_mockAgent, s_mockSession, [new ChatMessage(ChatRole.User, "Hierarchical partitioning message")], []);
await simpleProvider.InvokedAsync(simpleContext);
await hierarchicalProvider.InvokedAsync(hierarchicalContext);
@@ -727,7 +730,7 @@ public sealed class CosmosChatHistoryProviderTests : IAsyncLifetime, IDisposable
await Task.Delay(100);
// Act & Assert
var invokingContext = new ChatHistoryProvider.InvokingContext([]);
var invokingContext = new ChatHistoryProvider.InvokingContext(s_mockAgent, s_mockSession, []);
var simpleMessages = await simpleProvider.InvokingAsync(invokingContext);
var simpleMessageList = simpleMessages.ToList();
@@ -760,7 +763,7 @@ public sealed class CosmosChatHistoryProviderTests : IAsyncLifetime, IDisposable
await Task.Delay(10); // Small delay to ensure different timestamps
}
var context = new ChatHistoryProvider.InvokedContext(messages, []);
var context = new ChatHistoryProvider.InvokedContext(s_mockAgent, s_mockSession, messages, []);
await provider.InvokedAsync(context);
// Wait for eventual consistency
@@ -768,7 +771,7 @@ public sealed class CosmosChatHistoryProviderTests : IAsyncLifetime, IDisposable
// Act - Set max to 5 and retrieve
provider.MaxMessagesToRetrieve = 5;
var invokingContext = new ChatHistoryProvider.InvokingContext([]);
var invokingContext = new ChatHistoryProvider.InvokingContext(s_mockAgent, s_mockSession, []);
var retrievedMessages = await provider.InvokingAsync(invokingContext);
var messageList = retrievedMessages.ToList();
@@ -798,14 +801,14 @@ public sealed class CosmosChatHistoryProviderTests : IAsyncLifetime, IDisposable
messages.Add(new ChatMessage(ChatRole.User, $"Message {i}"));
}
var context = new ChatHistoryProvider.InvokedContext(messages, []);
var context = new ChatHistoryProvider.InvokedContext(s_mockAgent, s_mockSession, messages, []);
await provider.InvokedAsync(context);
// Wait for eventual consistency
await Task.Delay(100);
// Act - No limit set (default null)
var invokingContext = new ChatHistoryProvider.InvokingContext([]);
var invokingContext = new ChatHistoryProvider.InvokingContext(s_mockAgent, s_mockSession, []);
var retrievedMessages = await provider.InvokingAsync(invokingContext);
var messageList = retrievedMessages.ToList();
@@ -66,12 +66,17 @@ public sealed class AggregatorPromptAgentFactoryTests
private sealed class TestAgent : AIAgent
{
public override ValueTask<AgentSession> DeserializeSessionAsync(JsonElement serializedSession, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default)
protected override ValueTask<AgentSession> DeserializeSessionCoreAsync(JsonElement serializedState, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public override ValueTask<AgentSession> CreateSessionAsync(CancellationToken cancellationToken = default)
protected override JsonElement SerializeSessionCore(AgentSession session, JsonSerializerOptions? jsonSerializerOptions = null)
{
throw new NotImplementedException();
}
protected override ValueTask<AgentSession> CreateSessionCoreAsync(CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
@@ -10,7 +10,7 @@ public sealed class DurableAgentSessionTests
public void BuiltInSerialization()
{
AgentSessionId sessionId = AgentSessionId.WithRandomKey("test-agent");
AgentSession session = new DurableAgentSession(sessionId);
DurableAgentSession session = new(sessionId);
JsonElement serializedSession = session.Serialize();
@@ -175,7 +175,10 @@ public sealed class AIAgentExtensionsTests
{
Mock<AIAgent> agentMock = new() { CallBase = true };
agentMock.SetupGet(x => x.Name).Returns("TestAgent");
agentMock.Setup(x => x.CreateSessionAsync()).ReturnsAsync(new TestAgentSession());
agentMock
.Protected()
.Setup<ValueTask<AgentSession>>("CreateSessionCoreAsync", ItExpr.IsAny<CancellationToken>())
.ReturnsAsync(new TestAgentSession());
agentMock
.Protected()
.Setup<Task<AgentResponse>>("RunCoreAsync",
@@ -194,7 +197,10 @@ public sealed class AIAgentExtensionsTests
{
Mock<AIAgent> agentMock = new() { CallBase = true };
agentMock.SetupGet(x => x.Name).Returns("TestAgent");
agentMock.Setup(x => x.CreateSessionAsync()).ReturnsAsync(new TestAgentSession());
agentMock
.Protected()
.Setup<ValueTask<AgentSession>>("CreateSessionCoreAsync", ItExpr.IsAny<CancellationToken>())
.ReturnsAsync(new TestAgentSession());
agentMock
.Protected()
.Setup<Task<AgentResponse>>("RunCoreAsync",
@@ -280,11 +280,14 @@ internal sealed class FakeChatClientAgent : AIAgent
public override string? Description => "A fake agent for testing";
public override ValueTask<AgentSession> CreateSessionAsync(CancellationToken cancellationToken = default) =>
protected override ValueTask<AgentSession> CreateSessionCoreAsync(CancellationToken cancellationToken = default) =>
new(new FakeInMemoryAgentSession());
public override ValueTask<AgentSession> DeserializeSessionAsync(JsonElement serializedSession, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default) =>
new(new FakeInMemoryAgentSession(serializedSession, jsonSerializerOptions));
protected override ValueTask<AgentSession> DeserializeSessionCoreAsync(JsonElement serializedState, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default) =>
new(new FakeInMemoryAgentSession(serializedState, jsonSerializerOptions));
protected override JsonElement SerializeSessionCore(AgentSession session, JsonSerializerOptions? jsonSerializerOptions = null)
=> throw new NotImplementedException();
protected override async Task<AgentResponse> RunCoreAsync(
IEnumerable<ChatMessage> messages,
@@ -344,11 +347,21 @@ internal sealed class FakeMultiMessageAgent : AIAgent
public override string? Description => "A fake agent that sends multiple messages for testing";
public override ValueTask<AgentSession> CreateSessionAsync(CancellationToken cancellationToken = default) =>
protected override ValueTask<AgentSession> CreateSessionCoreAsync(CancellationToken cancellationToken = default) =>
new(new FakeInMemoryAgentSession());
public override ValueTask<AgentSession> DeserializeSessionAsync(JsonElement serializedSession, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default) =>
new(new FakeInMemoryAgentSession(serializedSession, jsonSerializerOptions));
protected override ValueTask<AgentSession> DeserializeSessionCoreAsync(JsonElement serializedState, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default) =>
new(new FakeInMemoryAgentSession(serializedState, jsonSerializerOptions));
protected override JsonElement SerializeSessionCore(AgentSession session, JsonSerializerOptions? jsonSerializerOptions = null)
{
if (session is not FakeInMemoryAgentSession fakeSession)
{
throw new InvalidOperationException("The provided session is not compatible with the agent. Only sessions created by the agent can be serialized.");
}
return fakeSession.Serialize(jsonSerializerOptions);
}
protected override async Task<AgentResponse> RunCoreAsync(
IEnumerable<ChatMessage> messages,
@@ -425,6 +438,8 @@ internal sealed class FakeMultiMessageAgent : AIAgent
: base(serializedSession, jsonSerializerOptions)
{
}
internal new JsonElement Serialize(JsonSerializerOptions? jsonSerializerOptions = null)
=> base.Serialize(jsonSerializerOptions);
}
public override object? GetService(Type serviceType, object? serviceKey = null) => null;
@@ -334,11 +334,21 @@ internal sealed class FakeForwardedPropsAgent : AIAgent
await Task.CompletedTask;
}
public override ValueTask<AgentSession> CreateSessionAsync(CancellationToken cancellationToken = default) =>
protected override ValueTask<AgentSession> CreateSessionCoreAsync(CancellationToken cancellationToken = default) =>
new(new FakeInMemoryAgentSession());
public override ValueTask<AgentSession> DeserializeSessionAsync(JsonElement serializedSession, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default) =>
new(new FakeInMemoryAgentSession(serializedSession, jsonSerializerOptions));
protected override ValueTask<AgentSession> DeserializeSessionCoreAsync(JsonElement serializedState, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default) =>
new(new FakeInMemoryAgentSession(serializedState, jsonSerializerOptions));
protected override JsonElement SerializeSessionCore(AgentSession session, JsonSerializerOptions? jsonSerializerOptions = null)
{
if (session is not FakeInMemoryAgentSession fakeSession)
{
throw new InvalidOperationException("The provided session is not compatible with the agent. Only sessions created by the agent can be serialized.");
}
return fakeSession.Serialize(jsonSerializerOptions);
}
private sealed class FakeInMemoryAgentSession : InMemoryAgentSession
{
@@ -351,6 +361,9 @@ internal sealed class FakeForwardedPropsAgent : AIAgent
: base(serializedSession, jsonSerializerOptions)
{
}
internal new JsonElement Serialize(JsonSerializerOptions? jsonSerializerOptions = null)
=> base.Serialize(jsonSerializerOptions);
}
public override object? GetService(Type serviceType, object? serviceKey = null) => null;
@@ -417,11 +417,21 @@ internal sealed class FakeStateAgent : AIAgent
await Task.CompletedTask;
}
public override ValueTask<AgentSession> CreateSessionAsync(CancellationToken cancellationToken = default) =>
protected override ValueTask<AgentSession> CreateSessionCoreAsync(CancellationToken cancellationToken = default) =>
new(new FakeInMemoryAgentSession());
public override ValueTask<AgentSession> DeserializeSessionAsync(JsonElement serializedSession, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default) =>
new(new FakeInMemoryAgentSession(serializedSession, jsonSerializerOptions));
protected override ValueTask<AgentSession> DeserializeSessionCoreAsync(JsonElement serializedState, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default) =>
new(new FakeInMemoryAgentSession(serializedState, jsonSerializerOptions));
protected override JsonElement SerializeSessionCore(AgentSession session, JsonSerializerOptions? jsonSerializerOptions = null)
{
if (session is not FakeInMemoryAgentSession fakeSession)
{
throw new InvalidOperationException("The provided session is not compatible with the agent. Only sessions created by the agent can be serialized.");
}
return fakeSession.Serialize(jsonSerializerOptions);
}
private sealed class FakeInMemoryAgentSession : InMemoryAgentSession
{
@@ -434,6 +444,9 @@ internal sealed class FakeStateAgent : AIAgent
: base(serializedSession, jsonSerializerOptions)
{
}
internal new JsonElement Serialize(JsonSerializerOptions? jsonSerializerOptions = null)
=> base.Serialize(jsonSerializerOptions);
}
public override object? GetService(Type serviceType, object? serviceKey = null) => null;
@@ -425,11 +425,21 @@ public sealed class AGUIEndpointRouteBuilderExtensionsTests
public override string? Description => "Agent that produces multiple text chunks";
public override ValueTask<AgentSession> CreateSessionAsync(CancellationToken cancellationToken = default) =>
protected override ValueTask<AgentSession> CreateSessionCoreAsync(CancellationToken cancellationToken = default) =>
new(new TestInMemoryAgentSession());
public override ValueTask<AgentSession> DeserializeSessionAsync(JsonElement serializedSession, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default) =>
new(new TestInMemoryAgentSession(serializedSession, jsonSerializerOptions));
protected override ValueTask<AgentSession> DeserializeSessionCoreAsync(JsonElement serializedState, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default) =>
new(new TestInMemoryAgentSession(serializedState, jsonSerializerOptions));
protected override JsonElement SerializeSessionCore(AgentSession session, JsonSerializerOptions? jsonSerializerOptions = null)
{
if (session is not TestInMemoryAgentSession testSession)
{
throw new InvalidOperationException("The provided session is not compatible with the agent. Only sessions created by the agent can be serialized.");
}
return testSession.Serialize(jsonSerializerOptions);
}
protected override Task<AgentResponse> RunCoreAsync(IEnumerable<ChatMessage> messages, AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default)
{
@@ -507,6 +517,9 @@ public sealed class AGUIEndpointRouteBuilderExtensionsTests
: base(serializedSessionState, jsonSerializerOptions, null)
{
}
internal new JsonElement Serialize(JsonSerializerOptions? jsonSerializerOptions = null)
=> base.Serialize(jsonSerializerOptions);
}
private sealed class TestAgent : AIAgent
@@ -515,11 +528,21 @@ public sealed class AGUIEndpointRouteBuilderExtensionsTests
public override string? Description => "Test agent";
public override ValueTask<AgentSession> CreateSessionAsync(CancellationToken cancellationToken = default) =>
protected override ValueTask<AgentSession> CreateSessionCoreAsync(CancellationToken cancellationToken = default) =>
new(new TestInMemoryAgentSession());
public override ValueTask<AgentSession> DeserializeSessionAsync(JsonElement serializedSession, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default) =>
new(new TestInMemoryAgentSession(serializedSession, jsonSerializerOptions));
protected override ValueTask<AgentSession> DeserializeSessionCoreAsync(JsonElement serializedState, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default) =>
new(new TestInMemoryAgentSession(serializedState, jsonSerializerOptions));
protected override JsonElement SerializeSessionCore(AgentSession session, JsonSerializerOptions? jsonSerializerOptions = null)
{
if (session is not TestInMemoryAgentSession testSession)
{
throw new InvalidOperationException("The provided session is not compatible with the agent. Only sessions created by the agent can be serialized.");
}
return testSession.Serialize(jsonSerializerOptions);
}
protected override Task<AgentResponse> RunCoreAsync(IEnumerable<ChatMessage> messages, AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default)
{
@@ -11,10 +11,13 @@ internal sealed class TestAgent(string name, string description) : AIAgent
public override string? Description => description;
public override ValueTask<AgentSession> CreateSessionAsync(CancellationToken cancellationToken = default) => new(new DummyAgentSession());
protected override ValueTask<AgentSession> CreateSessionCoreAsync(CancellationToken cancellationToken = default) => new(new DummyAgentSession());
public override ValueTask<AgentSession> DeserializeSessionAsync(
JsonElement serializedSession,
protected override JsonElement SerializeSessionCore(AgentSession session, JsonSerializerOptions? jsonSerializerOptions = null)
=> throw new NotImplementedException();
protected override ValueTask<AgentSession> DeserializeSessionCoreAsync(
JsonElement serializedState,
JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default) => new(new DummyAgentSession());
protected override Task<AgentResponse> RunCoreAsync(
@@ -18,6 +18,9 @@ public sealed class Mem0ProviderTests : IDisposable
{
private const string SkipReason = "Requires a Mem0 service configured"; // Set to null to enable.
private static readonly AIAgent s_mockAgent = new Moq.Mock<AIAgent>().Object;
private static readonly AgentSession s_mockSession = new Moq.Mock<AgentSession>().Object;
private readonly HttpClient _httpClient;
public Mem0ProviderTests()
@@ -49,14 +52,14 @@ public sealed class Mem0ProviderTests : IDisposable
var sut = new Mem0Provider(this._httpClient, storageScope);
await sut.ClearStoredMemoriesAsync();
var ctxBefore = await sut.InvokingAsync(new AIContextProvider.InvokingContext([question]));
var ctxBefore = await sut.InvokingAsync(new AIContextProvider.InvokingContext(s_mockAgent, s_mockSession, [question]));
Assert.DoesNotContain("Caoimhe", ctxBefore.Messages?[0].Text ?? string.Empty);
// Act
await sut.InvokedAsync(new AIContextProvider.InvokedContext([input], aiContextProviderMessages: null));
await sut.InvokedAsync(new AIContextProvider.InvokedContext(s_mockAgent, s_mockSession, [input], aiContextProviderMessages: null));
var ctxAfterAdding = await GetContextWithRetryAsync(sut, question);
await sut.ClearStoredMemoriesAsync();
var ctxAfterClearing = await sut.InvokingAsync(new AIContextProvider.InvokingContext([question]));
var ctxAfterClearing = await sut.InvokingAsync(new AIContextProvider.InvokingContext(s_mockAgent, s_mockSession, [question]));
// Assert
Assert.Contains("Caoimhe", ctxAfterAdding.Messages?[0].Text ?? string.Empty);
@@ -73,14 +76,14 @@ public sealed class Mem0ProviderTests : IDisposable
var sut = new Mem0Provider(this._httpClient, storageScope);
await sut.ClearStoredMemoriesAsync();
var ctxBefore = await sut.InvokingAsync(new AIContextProvider.InvokingContext([question]));
var ctxBefore = await sut.InvokingAsync(new AIContextProvider.InvokingContext(s_mockAgent, s_mockSession, [question]));
Assert.DoesNotContain("Caoimhe", ctxBefore.Messages?[0].Text ?? string.Empty);
// Act
await sut.InvokedAsync(new AIContextProvider.InvokedContext([assistantIntro], aiContextProviderMessages: null));
await sut.InvokedAsync(new AIContextProvider.InvokedContext(s_mockAgent, s_mockSession, [assistantIntro], aiContextProviderMessages: null));
var ctxAfterAdding = await GetContextWithRetryAsync(sut, question);
await sut.ClearStoredMemoriesAsync();
var ctxAfterClearing = await sut.InvokingAsync(new AIContextProvider.InvokingContext([question]));
var ctxAfterClearing = await sut.InvokingAsync(new AIContextProvider.InvokingContext(s_mockAgent, s_mockSession, [question]));
// Assert
Assert.Contains("Caoimhe", ctxAfterAdding.Messages?[0].Text ?? string.Empty);
@@ -99,13 +102,13 @@ public sealed class Mem0ProviderTests : IDisposable
await sut1.ClearStoredMemoriesAsync();
await sut2.ClearStoredMemoriesAsync();
var ctxBefore1 = await sut1.InvokingAsync(new AIContextProvider.InvokingContext([question]));
var ctxBefore2 = await sut2.InvokingAsync(new AIContextProvider.InvokingContext([question]));
var ctxBefore1 = await sut1.InvokingAsync(new AIContextProvider.InvokingContext(s_mockAgent, s_mockSession, [question]));
var ctxBefore2 = await sut2.InvokingAsync(new AIContextProvider.InvokingContext(s_mockAgent, s_mockSession, [question]));
Assert.DoesNotContain("Caoimhe", ctxBefore1.Messages?[0].Text ?? string.Empty);
Assert.DoesNotContain("Caoimhe", ctxBefore2.Messages?[0].Text ?? string.Empty);
// Act
await sut1.InvokedAsync(new AIContextProvider.InvokedContext([assistantIntro], aiContextProviderMessages: null));
await sut1.InvokedAsync(new AIContextProvider.InvokedContext(s_mockAgent, s_mockSession, [assistantIntro], aiContextProviderMessages: null));
var ctxAfterAdding1 = await GetContextWithRetryAsync(sut1, question);
var ctxAfterAdding2 = await GetContextWithRetryAsync(sut2, question);
@@ -123,7 +126,7 @@ public sealed class Mem0ProviderTests : IDisposable
AIContext? ctx = null;
for (int i = 0; i < attempts; i++)
{
ctx = await provider.InvokingAsync(new AIContextProvider.InvokingContext([question]), CancellationToken.None);
ctx = await provider.InvokingAsync(new AIContextProvider.InvokingContext(s_mockAgent, s_mockSession, [question]), CancellationToken.None);
var text = ctx.Messages?[0].Text;
if (!string.IsNullOrEmpty(text) && text.IndexOf("Caoimhe", StringComparison.OrdinalIgnoreCase) >= 0)
{
@@ -18,6 +18,9 @@ namespace Microsoft.Agents.AI.Mem0.UnitTests;
/// </summary>
public sealed class Mem0ProviderTests : IDisposable
{
private static readonly AIAgent s_mockAgent = new Mock<AIAgent>().Object;
private static readonly AgentSession s_mockSession = new Mock<AgentSession>().Object;
private readonly Mock<ILogger<Mem0Provider>> _loggerMock;
private readonly Mock<ILoggerFactory> _loggerFactoryMock;
private readonly RecordingHandler _handler = new();
@@ -96,7 +99,7 @@ public sealed class Mem0ProviderTests : IDisposable
UserId = "user"
};
var sut = new Mem0Provider(this._httpClient, storageScope, options: new() { EnableSensitiveTelemetryData = true }, loggerFactory: this._loggerFactoryMock.Object);
var invokingContext = new AIContextProvider.InvokingContext([new ChatMessage(ChatRole.User, "What is my name?")]);
var invokingContext = new AIContextProvider.InvokingContext(s_mockAgent, s_mockSession, [new ChatMessage(ChatRole.User, "What is my name?")]);
// Act
var aiContext = await sut.InvokingAsync(invokingContext);
@@ -161,7 +164,7 @@ public sealed class Mem0ProviderTests : IDisposable
var options = new Mem0ProviderOptions { EnableSensitiveTelemetryData = enableSensitiveTelemetryData };
var sut = new Mem0Provider(this._httpClient, storageScope, options: options, loggerFactory: this._loggerFactoryMock.Object);
var invokingContext = new AIContextProvider.InvokingContext([new ChatMessage(ChatRole.User, "Who am I?")]);
var invokingContext = new AIContextProvider.InvokingContext(s_mockAgent, s_mockSession, [new ChatMessage(ChatRole.User, "Who am I?")]);
// Act
await sut.InvokingAsync(invokingContext, CancellationToken.None);
@@ -215,7 +218,7 @@ public sealed class Mem0ProviderTests : IDisposable
};
// Act
await sut.InvokedAsync(new AIContextProvider.InvokedContext(requestMessages, aiContextProviderMessages: null) { ResponseMessages = responseMessages });
await sut.InvokedAsync(new AIContextProvider.InvokedContext(s_mockAgent, s_mockSession, requestMessages, aiContextProviderMessages: null) { ResponseMessages = responseMessages });
// Assert
var memoryPosts = this._handler.Requests.Where(r => r.RequestMessage.RequestUri!.AbsolutePath == "/v1/memories/" && r.RequestMessage.Method == HttpMethod.Post).ToList();
@@ -242,7 +245,7 @@ public sealed class Mem0ProviderTests : IDisposable
};
// Act
await sut.InvokedAsync(new AIContextProvider.InvokedContext(requestMessages, aiContextProviderMessages: null) { ResponseMessages = null, InvokeException = new InvalidOperationException("Request Failed") });
await sut.InvokedAsync(new AIContextProvider.InvokedContext(s_mockAgent, s_mockSession, requestMessages, aiContextProviderMessages: null) { ResponseMessages = null, InvokeException = new InvalidOperationException("Request Failed") });
// Assert
Assert.Empty(this._handler.Requests);
@@ -268,7 +271,7 @@ public sealed class Mem0ProviderTests : IDisposable
};
// Act
await sut.InvokedAsync(new AIContextProvider.InvokedContext(requestMessages, aiContextProviderMessages: null) { ResponseMessages = responseMessages });
await sut.InvokedAsync(new AIContextProvider.InvokedContext(s_mockAgent, s_mockSession, requestMessages, aiContextProviderMessages: null) { ResponseMessages = responseMessages });
// Assert
this._loggerMock.Verify(
@@ -318,7 +321,7 @@ public sealed class Mem0ProviderTests : IDisposable
};
// Act
await sut.InvokedAsync(new AIContextProvider.InvokedContext(requestMessages, aiContextProviderMessages: null) { ResponseMessages = responseMessages });
await sut.InvokedAsync(new AIContextProvider.InvokedContext(s_mockAgent, s_mockSession, requestMessages, aiContextProviderMessages: null) { ResponseMessages = responseMessages });
// Assert
Assert.Equal(expectedLogCount, this._loggerMock.Invocations.Count);
@@ -400,7 +403,7 @@ public sealed class Mem0ProviderTests : IDisposable
// Arrange
var storageScope = new Mem0ProviderScope { ApplicationId = "app" };
var provider = new Mem0Provider(this._httpClient, storageScope, loggerFactory: this._loggerFactoryMock.Object);
var invokingContext = new AIContextProvider.InvokingContext([new ChatMessage(ChatRole.User, "Q?")]);
var invokingContext = new AIContextProvider.InvokingContext(s_mockAgent, s_mockSession, [new ChatMessage(ChatRole.User, "Q?")]);
// Act
var aiContext = await provider.InvokingAsync(invokingContext, CancellationToken.None);
@@ -418,6 +418,51 @@ public class AIAgentBuilderTests
Assert.IsType<AnonymousDelegatingAIAgent>(result);
}
/// <summary>
/// Verify that Use with both delegates allows both to access AgentRunContext.
/// </summary>
[Fact]
public async Task Use_WithBothDelegates_AllowsDelegateToAccessAgentRunContextAsync()
{
// Arrange
var mockAgent = new Mock<AIAgent>();
var mockSession = new Mock<AgentSession>();
var builder = new AIAgentBuilder(mockAgent.Object);
AIAgent? builtAgent = null;
bool nonStreamingMiddlewareExecuted = false;
bool streamingMiddlwareExecuted = true;
builtAgent = builder.Use(
(_, _, _, _, _) =>
{
Assert.NotNull(AIAgent.CurrentRunContext);
Assert.Same(builtAgent, AIAgent.CurrentRunContext.Agent);
Assert.Same(mockSession.Object, AIAgent.CurrentRunContext.Session);
nonStreamingMiddlewareExecuted = true;
return Task.FromResult(new AgentResponse());
},
(_, _, _, _, _) =>
{
Assert.NotNull(AIAgent.CurrentRunContext);
Assert.Same(builtAgent, AIAgent.CurrentRunContext.Agent);
Assert.Same(mockSession.Object, AIAgent.CurrentRunContext.Session);
streamingMiddlwareExecuted = true;
return AsyncEnumerable.Empty<AgentResponseUpdate>();
}).Build();
// Act
await builtAgent.RunAsync("Input message", mockSession.Object);
await foreach (var update in builtAgent.RunStreamingAsync("Input message", mockSession.Object))
{
}
// Assert
Assert.True(nonStreamingMiddlewareExecuted);
Assert.True(streamingMiddlwareExecuted);
}
#endregion
/// <summary>
@@ -382,10 +382,13 @@ public class AgentExtensionsTests
this._exceptionToThrow = exceptionToThrow;
}
public override ValueTask<AgentSession> CreateSessionAsync(CancellationToken cancellationToken = default)
protected override ValueTask<AgentSession> CreateSessionCoreAsync(CancellationToken cancellationToken = default)
=> throw new NotImplementedException();
public override ValueTask<AgentSession> DeserializeSessionAsync(JsonElement serializedSession, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default)
protected override JsonElement SerializeSessionCore(AgentSession session, JsonSerializerOptions? jsonSerializerOptions = null)
=> throw new NotImplementedException();
protected override ValueTask<AgentSession> DeserializeSessionCoreAsync(JsonElement serializedState, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default)
=> throw new NotImplementedException();
public override string? Name { get; }
@@ -327,4 +327,9 @@ public class ChatClientAgentSessionTests
}
#endregion
internal sealed class Animal
{
public string Name { get; set; } = string.Empty;
}
}
@@ -17,6 +17,9 @@ namespace Microsoft.Agents.AI.UnitTests.Data;
/// </summary>
public sealed class TextSearchProviderTests
{
private static readonly AIAgent s_mockAgent = new Mock<AIAgent>().Object;
private static readonly AgentSession s_mockSession = new Mock<AgentSession>().Object;
private readonly Mock<ILogger<TextSearchProvider>> _loggerMock;
private readonly Mock<ILoggerFactory> _loggerFactoryMock;
@@ -64,10 +67,12 @@ public sealed class TextSearchProviderTests
var provider = new TextSearchProvider(SearchDelegateAsync, default, null, options, withLogging ? this._loggerFactoryMock.Object : null);
var invokingContext = new AIContextProvider.InvokingContext(
[
new ChatMessage(ChatRole.User, "Sample user question?"),
new ChatMessage(ChatRole.User, "Additional part")
]);
s_mockAgent,
s_mockSession,
[
new ChatMessage(ChatRole.User, "Sample user question?"),
new ChatMessage(ChatRole.User, "Additional part")
]);
// Act
var aiContext = await provider.InvokingAsync(invokingContext, CancellationToken.None);
@@ -139,7 +144,7 @@ public sealed class TextSearchProviderTests
FunctionToolDescription = overrideDescription
};
var provider = new TextSearchProvider(this.NoResultSearchAsync, default, null, options);
var invokingContext = new AIContextProvider.InvokingContext([new ChatMessage(ChatRole.User, "Q?")]);
var invokingContext = new AIContextProvider.InvokingContext(s_mockAgent, s_mockSession, [new ChatMessage(ChatRole.User, "Q?")]);
// Act
var aiContext = await provider.InvokingAsync(invokingContext, CancellationToken.None);
@@ -158,7 +163,7 @@ public sealed class TextSearchProviderTests
{
// Arrange
var provider = new TextSearchProvider(this.FailingSearchAsync, default, null, loggerFactory: this._loggerFactoryMock.Object);
var invokingContext = new AIContextProvider.InvokingContext([new ChatMessage(ChatRole.User, "Q?")]);
var invokingContext = new AIContextProvider.InvokingContext(s_mockAgent, s_mockSession, [new ChatMessage(ChatRole.User, "Q?")]);
// Act
var aiContext = await provider.InvokingAsync(invokingContext, CancellationToken.None);
@@ -251,7 +256,7 @@ public sealed class TextSearchProviderTests
ContextFormatter = r => $"Custom formatted context with {r.Count} results."
};
var provider = new TextSearchProvider(SearchDelegateAsync, default, null, options);
var invokingContext = new AIContextProvider.InvokingContext([new ChatMessage(ChatRole.User, "Q?")]);
var invokingContext = new AIContextProvider.InvokingContext(s_mockAgent, s_mockSession, [new ChatMessage(ChatRole.User, "Q?")]);
// Act
var aiContext = await provider.InvokingAsync(invokingContext, CancellationToken.None);
@@ -285,7 +290,7 @@ public sealed class TextSearchProviderTests
ContextFormatter = r => string.Join(",", r.Select(x => ((RawPayload)x.RawRepresentation!).Id))
};
var provider = new TextSearchProvider(SearchDelegateAsync, default, null, options);
var invokingContext = new AIContextProvider.InvokingContext([new ChatMessage(ChatRole.User, "Q?")]);
var invokingContext = new AIContextProvider.InvokingContext(s_mockAgent, s_mockSession, [new ChatMessage(ChatRole.User, "Q?")]);
// Act
var aiContext = await provider.InvokingAsync(invokingContext, CancellationToken.None);
@@ -302,7 +307,7 @@ public sealed class TextSearchProviderTests
// Arrange
var options = new TextSearchProviderOptions { SearchTime = TextSearchProviderOptions.TextSearchBehavior.BeforeAIInvoke };
var provider = new TextSearchProvider(this.NoResultSearchAsync, default, null, options);
var invokingContext = new AIContextProvider.InvokingContext([new ChatMessage(ChatRole.User, "Q?")]);
var invokingContext = new AIContextProvider.InvokingContext(s_mockAgent, s_mockSession, [new ChatMessage(ChatRole.User, "Q?")]);
// Act
var aiContext = await provider.InvokingAsync(invokingContext, CancellationToken.None);
@@ -340,12 +345,14 @@ public sealed class TextSearchProviderTests
new ChatMessage(ChatRole.User, "C"),
new ChatMessage(ChatRole.Assistant, "D"),
};
await provider.InvokedAsync(new(initialMessages, aiContextProviderMessages: null) { InvokeException = new InvalidOperationException("Request Failed") });
await provider.InvokedAsync(new(s_mockAgent, s_mockSession, initialMessages, aiContextProviderMessages: null) { InvokeException = new InvalidOperationException("Request Failed") });
var invokingContext = new AIContextProvider.InvokingContext(
[
new ChatMessage(ChatRole.User, "E")
]);
s_mockAgent,
s_mockSession,
[
new ChatMessage(ChatRole.User, "E")
]);
// Act
await provider.InvokingAsync(invokingContext, CancellationToken.None);
@@ -380,12 +387,14 @@ public sealed class TextSearchProviderTests
new ChatMessage(ChatRole.User, "C"),
new ChatMessage(ChatRole.Assistant, "D"),
};
await provider.InvokedAsync(new(initialMessages, aiContextProviderMessages: null));
await provider.InvokedAsync(new(s_mockAgent, s_mockSession, initialMessages, aiContextProviderMessages: null));
var invokingContext = new AIContextProvider.InvokingContext(
[
new ChatMessage(ChatRole.User, "E")
]);
s_mockAgent,
s_mockSession,
[
new ChatMessage(ChatRole.User, "E")
]);
// Act
await provider.InvokingAsync(invokingContext, CancellationToken.None);
@@ -414,20 +423,24 @@ public sealed class TextSearchProviderTests
// First memory update (A,B)
await provider.InvokedAsync(new(
[
new ChatMessage(ChatRole.User, "A"),
new ChatMessage(ChatRole.Assistant, "B"),
], aiContextProviderMessages: null));
s_mockAgent,
s_mockSession,
[
new ChatMessage(ChatRole.User, "A"),
new ChatMessage(ChatRole.Assistant, "B"),
], aiContextProviderMessages: null));
// Second memory update (C,D,E)
await provider.InvokedAsync(new(
[
new ChatMessage(ChatRole.User, "C"),
new ChatMessage(ChatRole.Assistant, "D"),
new ChatMessage(ChatRole.User, "E"),
], aiContextProviderMessages: null));
s_mockAgent,
s_mockSession,
[
new ChatMessage(ChatRole.User, "C"),
new ChatMessage(ChatRole.Assistant, "D"),
new ChatMessage(ChatRole.User, "E"),
], aiContextProviderMessages: null));
var invokingContext = new AIContextProvider.InvokingContext([new ChatMessage(ChatRole.User, "F")]);
var invokingContext = new AIContextProvider.InvokingContext(s_mockAgent, s_mockSession, [new ChatMessage(ChatRole.User, "F")]);
// Act
await provider.InvokingAsync(invokingContext, CancellationToken.None);
@@ -462,12 +475,14 @@ public sealed class TextSearchProviderTests
new ChatMessage(ChatRole.User, "U2"),
new ChatMessage(ChatRole.Assistant, "A2"),
};
await provider.InvokedAsync(new(initialMessages, null));
await provider.InvokedAsync(new(s_mockAgent, s_mockSession, initialMessages, null));
var invokingContext = new AIContextProvider.InvokingContext(
[
new ChatMessage(ChatRole.User, "Question?") // Current request message always appended.
]);
s_mockAgent,
s_mockSession,
[
new ChatMessage(ChatRole.User, "Question?") // Current request message always appended.
]);
// Act
await provider.InvokingAsync(invokingContext, CancellationToken.None);
@@ -518,7 +533,7 @@ public sealed class TextSearchProviderTests
};
// Act
await provider.InvokedAsync(new(messages, aiContextProviderMessages: null)); // Populate recent memory.
await provider.InvokedAsync(new(s_mockAgent, s_mockSession, messages, aiContextProviderMessages: null)); // Populate recent memory.
var state = provider.Serialize();
// Assert
@@ -547,7 +562,7 @@ public sealed class TextSearchProviderTests
new ChatMessage(ChatRole.User, "C"),
new ChatMessage(ChatRole.Assistant, "D"),
};
await provider.InvokedAsync(new(messages, aiContextProviderMessages: null));
await provider.InvokedAsync(new(s_mockAgent, s_mockSession, messages, aiContextProviderMessages: null));
// Act
var state = provider.Serialize();
@@ -563,7 +578,7 @@ public sealed class TextSearchProviderTests
RecentMessageMemoryLimit = 4
});
var emptyMessages = Array.Empty<ChatMessage>();
await roundTrippedProvider.InvokingAsync(new(emptyMessages), CancellationToken.None); // Trigger search to read memory.
await roundTrippedProvider.InvokingAsync(new(s_mockAgent, s_mockSession, emptyMessages), CancellationToken.None); // Trigger search to read memory.
// Assert
Assert.NotNull(capturedInput);
@@ -588,7 +603,7 @@ public sealed class TextSearchProviderTests
new ChatMessage(ChatRole.Assistant, "L4"),
new ChatMessage(ChatRole.User, "L5"),
};
await initialProvider.InvokedAsync(new(messages, aiContextProviderMessages: null));
await initialProvider.InvokedAsync(new(s_mockAgent, s_mockSession, messages, aiContextProviderMessages: null));
var state = initialProvider.Serialize();
string? capturedInput = null;
@@ -604,7 +619,7 @@ public sealed class TextSearchProviderTests
SearchTime = TextSearchProviderOptions.TextSearchBehavior.BeforeAIInvoke,
RecentMessageMemoryLimit = 3 // Lower limit
});
await restoredProvider.InvokingAsync(new(Array.Empty<ChatMessage>()), CancellationToken.None);
await restoredProvider.InvokingAsync(new(s_mockAgent, s_mockSession, Array.Empty<ChatMessage>()), CancellationToken.None);
// Assert
Assert.NotNull(capturedInput);
@@ -631,7 +646,7 @@ public sealed class TextSearchProviderTests
RecentMessageMemoryLimit = 3
});
var emptyMessages = Array.Empty<ChatMessage>();
await provider.InvokingAsync(new(emptyMessages), CancellationToken.None);
await provider.InvokingAsync(new(s_mockAgent, s_mockSession, emptyMessages), CancellationToken.None);
// Assert
Assert.NotNull(capturedInput);
@@ -18,6 +18,9 @@ namespace Microsoft.Agents.AI.Memory.UnitTests;
/// </summary>
public class ChatHistoryMemoryProviderTests
{
private static readonly AIAgent s_mockAgent = new Mock<AIAgent>().Object;
private static readonly AgentSession s_mockSession = new Mock<AgentSession>().Object;
private readonly Mock<ILogger<ChatHistoryMemoryProvider>> _loggerMock;
private readonly Mock<ILoggerFactory> _loggerFactoryMock;
@@ -116,7 +119,7 @@ public class ChatHistoryMemoryProviderTests
var requestMsgWithNulls = new ChatMessage(ChatRole.User, "request text nulls");
var responseMsg = new ChatMessage(ChatRole.Assistant, "response text") { MessageId = "resp-1", AuthorName = "assistant" };
var invokedContext = new AIContextProvider.InvokedContext([requestMsgWithValues, requestMsgWithNulls], aiContextProviderMessages: null)
var invokedContext = new AIContextProvider.InvokedContext(s_mockAgent, s_mockSession, [requestMsgWithValues, requestMsgWithNulls], aiContextProviderMessages: null)
{
ResponseMessages = [responseMsg]
};
@@ -174,7 +177,7 @@ public class ChatHistoryMemoryProviderTests
1,
new ChatHistoryMemoryProviderScope() { UserId = "UID" });
var requestMsg = new ChatMessage(ChatRole.User, "request text") { MessageId = "req-1" };
var invokedContext = new AIContextProvider.InvokedContext([requestMsg], aiContextProviderMessages: null)
var invokedContext = new AIContextProvider.InvokedContext(s_mockAgent, s_mockSession, [requestMsg], aiContextProviderMessages: null)
{
InvokeException = new InvalidOperationException("Invoke failed")
};
@@ -203,7 +206,7 @@ public class ChatHistoryMemoryProviderTests
new ChatHistoryMemoryProviderScope() { UserId = "UID" },
loggerFactory: this._loggerFactoryMock.Object);
var requestMsg = new ChatMessage(ChatRole.User, "request text") { MessageId = "req-1" };
var invokedContext = new AIContextProvider.InvokedContext([requestMsg], aiContextProviderMessages: null);
var invokedContext = new AIContextProvider.InvokedContext(s_mockAgent, s_mockSession, [requestMsg], aiContextProviderMessages: null);
// Act
await provider.InvokedAsync(invokedContext, CancellationToken.None);
@@ -254,7 +257,7 @@ public class ChatHistoryMemoryProviderTests
loggerFactory: this._loggerFactoryMock.Object);
var requestMsg = new ChatMessage(ChatRole.User, "request text");
var invokedContext = new AIContextProvider.InvokedContext([requestMsg], aiContextProviderMessages: null);
var invokedContext = new AIContextProvider.InvokedContext(s_mockAgent, s_mockSession, [requestMsg], aiContextProviderMessages: null);
// Act
await provider.InvokedAsync(invokedContext, CancellationToken.None);
@@ -327,7 +330,7 @@ public class ChatHistoryMemoryProviderTests
options: providerOptions);
var requestMsg = new ChatMessage(ChatRole.User, "requesting relevant history");
var invokingContext = new AIContextProvider.InvokingContext([requestMsg]);
var invokingContext = new AIContextProvider.InvokingContext(s_mockAgent, s_mockSession, [requestMsg]);
// Act
await provider.InvokingAsync(invokingContext, CancellationToken.None);
@@ -378,7 +381,7 @@ public class ChatHistoryMemoryProviderTests
var provider = new ChatHistoryMemoryProvider(this._vectorStoreMock.Object, TestCollectionName, 1, options: providerOptions, storageScope: searchScope, searchScope: searchScope);
var requestMsg = new ChatMessage(ChatRole.User, "requesting relevant history");
var invokingContext = new AIContextProvider.InvokingContext([requestMsg]);
var invokingContext = new AIContextProvider.InvokingContext(s_mockAgent, s_mockSession, [requestMsg]);
// Act
await provider.InvokingAsync(invokingContext, CancellationToken.None);
@@ -442,7 +445,7 @@ public class ChatHistoryMemoryProviderTests
options: options,
loggerFactory: this._loggerFactoryMock.Object);
var invokingContext = new AIContextProvider.InvokingContext([new ChatMessage(ChatRole.User, "requesting relevant history")]);
var invokingContext = new AIContextProvider.InvokingContext(s_mockAgent, s_mockSession, [new ChatMessage(ChatRole.User, "requesting relevant history")]);
// Act
await provider.InvokingAsync(invokingContext, CancellationToken.None);
@@ -24,10 +24,13 @@ internal sealed class TestAIAgent : AIAgent
public override string? Description => this.DescriptionFunc?.Invoke() ?? base.Description;
public override ValueTask<AgentSession> DeserializeSessionAsync(JsonElement serializedSession, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default) =>
new(this.DeserializeSessionFunc(serializedSession, jsonSerializerOptions));
protected override JsonElement SerializeSessionCore(AgentSession session, JsonSerializerOptions? jsonSerializerOptions = null)
=> throw new NotImplementedException();
public override ValueTask<AgentSession> CreateSessionAsync(CancellationToken cancellationToken = default) =>
protected override ValueTask<AgentSession> DeserializeSessionCoreAsync(JsonElement serializedState, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default) =>
new(this.DeserializeSessionFunc(serializedState, jsonSerializerOptions));
protected override ValueTask<AgentSession> CreateSessionCoreAsync(CancellationToken cancellationToken = default) =>
new(this.CreateSessionFunc());
protected override Task<AgentResponse> RunCoreAsync(IEnumerable<ChatMessage> messages, AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) =>
@@ -14,4 +14,5 @@ namespace Microsoft.Agents.AI.UnitTests;
[JsonSerializable(typeof(string))]
[JsonSerializable(typeof(string[]))]
[JsonSerializable(typeof(Dictionary<string, object?>))]
[JsonSerializable(typeof(ChatClientAgentSessionTests.Animal))]
internal sealed partial class TestJsonSerializerContext : JsonSerializerContext;
@@ -135,12 +135,15 @@ public class AgentWorkflowBuilderTests
{
public override string Name => name;
public override ValueTask<AgentSession> CreateSessionAsync(CancellationToken cancellationToken = default)
protected override ValueTask<AgentSession> CreateSessionCoreAsync(CancellationToken cancellationToken = default)
=> new(new DoubleEchoAgentSession());
public override ValueTask<AgentSession> DeserializeSessionAsync(JsonElement serializedSession, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default)
protected override ValueTask<AgentSession> DeserializeSessionCoreAsync(JsonElement serializedState, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default)
=> new(new DoubleEchoAgentSession());
protected override JsonElement SerializeSessionCore(AgentSession session, JsonSerializerOptions? jsonSerializerOptions = null)
=> default;
protected override Task<AgentResponse> RunCoreAsync(
IEnumerable<ChatMessage> messages, AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) =>
throw new NotImplementedException();
@@ -144,11 +144,14 @@ public class InProcessExecutionTests
public override string Name { get; }
public override ValueTask<AgentSession> CreateSessionAsync(CancellationToken cancellationToken = default) => new(new SimpleTestAgentSession());
protected override ValueTask<AgentSession> CreateSessionCoreAsync(CancellationToken cancellationToken = default) => new(new SimpleTestAgentSession());
public override ValueTask<AgentSession> DeserializeSessionAsync(System.Text.Json.JsonElement serializedSession,
protected override ValueTask<AgentSession> DeserializeSessionCoreAsync(System.Text.Json.JsonElement serializedState,
System.Text.Json.JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default) => new(new SimpleTestAgentSession());
protected override System.Text.Json.JsonElement SerializeSessionCore(AgentSession session, System.Text.Json.JsonSerializerOptions? jsonSerializerOptions = null)
=> default;
protected override Task<AgentResponse> RunCoreAsync(
IEnumerable<ChatMessage> messages,
AgentSession? session = null,
@@ -1,5 +1,7 @@
// Copyright (c) Microsoft. All rights reserved.
#pragma warning disable CS0618 // Type or member is obsolete - Testing legacy reflection-based pattern
using System;
using System.Threading;
using System.Threading.Tasks;
@@ -24,10 +24,13 @@ public class RepresentationTests
private sealed class TestAgent : AIAgent
{
public override ValueTask<AgentSession> CreateSessionAsync(CancellationToken cancellationToken = default)
protected override ValueTask<AgentSession> CreateSessionCoreAsync(CancellationToken cancellationToken = default)
=> throw new NotImplementedException();
public override ValueTask<AgentSession> DeserializeSessionAsync(JsonElement serializedSession, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default)
protected override ValueTask<AgentSession> DeserializeSessionCoreAsync(JsonElement serializedState, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default)
=> throw new NotImplementedException();
protected override JsonElement SerializeSessionCore(AgentSession session, JsonSerializerOptions? jsonSerializerOptions = null)
=> throw new NotImplementedException();
protected override Task<AgentResponse> RunCoreAsync(IEnumerable<ChatMessage> messages, AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) =>
@@ -16,10 +16,13 @@ internal sealed class RoleCheckAgent(bool allowOtherAssistantRoles, string? id =
public override string? Name => name;
public override ValueTask<AgentSession> DeserializeSessionAsync(JsonElement serializedSession, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default)
protected override ValueTask<AgentSession> DeserializeSessionCoreAsync(JsonElement serializedState, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default)
=> new(new RoleCheckAgentSession());
public override ValueTask<AgentSession> CreateSessionAsync(CancellationToken cancellationToken = default) => new(new RoleCheckAgentSession());
protected override JsonElement SerializeSessionCore(AgentSession session, JsonSerializerOptions? jsonSerializerOptions = null)
=> default;
protected override ValueTask<AgentSession> CreateSessionCoreAsync(CancellationToken cancellationToken = default) => new(new RoleCheckAgentSession());
protected override Task<AgentResponse> RunCoreAsync(IEnumerable<ChatMessage> messages, AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default)
=> this.RunStreamingAsync(messages, session, options, cancellationToken).ToAgentResponseAsync(cancellationToken);
@@ -1,5 +1,7 @@
// Copyright (c) Microsoft. All rights reserved.
#pragma warning disable CS0618 // Type or member is obsolete - Testing legacy reflection-based pattern
using System;
using System.IO;
using System.Linq;
@@ -1,5 +1,7 @@
// Copyright (c) Microsoft. All rights reserved.
#pragma warning disable CS0618 // Type or member is obsolete - Testing legacy reflection-based pattern
using System;
using System.IO;
using System.Linq;
@@ -1,5 +1,7 @@
// Copyright (c) Microsoft. All rights reserved.
#pragma warning disable CS0618 // Type or member is obsolete - Testing legacy reflection-based pattern
using System;
using System.IO;
using System.Threading;
@@ -60,12 +60,15 @@ internal sealed class HelloAgent(string id = nameof(HelloAgent)) : AIAgent
protected override string? IdCore => id;
public override string? Name => id;
public override ValueTask<AgentSession> CreateSessionAsync(CancellationToken cancellationToken = default)
protected override ValueTask<AgentSession> CreateSessionCoreAsync(CancellationToken cancellationToken = default)
=> new(new HelloAgentSession());
public override ValueTask<AgentSession> DeserializeSessionAsync(JsonElement serializedSession, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default)
protected override ValueTask<AgentSession> DeserializeSessionCoreAsync(JsonElement serializedState, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default)
=> new(new HelloAgentSession());
protected override JsonElement SerializeSessionCore(AgentSession session, JsonSerializerOptions? jsonSerializerOptions = null)
=> default;
protected override async Task<AgentResponse> RunCoreAsync(IEnumerable<ChatMessage> messages, AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default)
{
IEnumerable<AgentResponseUpdate> update = [
@@ -16,12 +16,22 @@ internal class TestEchoAgent(string? id = null, string? name = null, string? pre
protected override string? IdCore => id;
public override string? Name => name ?? base.Name;
public override async ValueTask<AgentSession> DeserializeSessionAsync(JsonElement serializedSession, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default)
protected override async ValueTask<AgentSession> DeserializeSessionCoreAsync(JsonElement serializedState, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default)
{
return serializedSession.Deserialize<EchoAgentSession>(jsonSerializerOptions) ?? await this.CreateSessionAsync(cancellationToken);
return serializedState.Deserialize<EchoAgentSession>(jsonSerializerOptions) ?? await this.CreateSessionAsync(cancellationToken);
}
public override ValueTask<AgentSession> CreateSessionAsync(CancellationToken cancellationToken = default) =>
protected override JsonElement SerializeSessionCore(AgentSession session, JsonSerializerOptions? jsonSerializerOptions = null)
{
if (session is not EchoAgentSession typedSession)
{
throw new InvalidOperationException("The provided session is not compatible with the agent. Only sessions created by the agent can be serialized.");
}
return typedSession.Serialize(jsonSerializerOptions);
}
protected override ValueTask<AgentSession> CreateSessionCoreAsync(CancellationToken cancellationToken = default) =>
new(new EchoAgentSession());
private static ChatMessage UpdateSession(ChatMessage message, InMemoryAgentSession? session = null)
@@ -89,5 +99,11 @@ internal class TestEchoAgent(string? id = null, string? name = null, string? pre
}
}
private sealed class EchoAgentSession : InMemoryAgentSession;
private sealed class EchoAgentSession : InMemoryAgentSession
{
internal new JsonElement Serialize(JsonSerializerOptions? jsonSerializerOptions = null)
{
return base.Serialize(jsonSerializerOptions);
}
}
}
@@ -45,12 +45,15 @@ public class TestReplayAgent(List<ChatMessage>? messages = null, string? id = nu
return result;
}
public override ValueTask<AgentSession> CreateSessionAsync(CancellationToken cancellationToken = default)
protected override ValueTask<AgentSession> CreateSessionCoreAsync(CancellationToken cancellationToken = default)
=> new(new ReplayAgentSession());
public override ValueTask<AgentSession> DeserializeSessionAsync(JsonElement serializedSession, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default)
protected override ValueTask<AgentSession> DeserializeSessionCoreAsync(JsonElement serializedState, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default)
=> new(new ReplayAgentSession());
protected override JsonElement SerializeSessionCore(AgentSession session, JsonSerializerOptions? jsonSerializerOptions = null)
=> default;
public static TestReplayAgent FromStrings(params string[] messages) =>
new(ToChatMessages(messages));
@@ -29,7 +29,7 @@ internal sealed class TestRequestAgent(TestAgentRequestType requestType, int unp
protected override string? IdCore => id;
public override string? Name => name;
public override ValueTask<AgentSession> CreateSessionAsync(CancellationToken cancellationToken)
protected override ValueTask<AgentSession> CreateSessionCoreAsync(CancellationToken cancellationToken)
=> new(requestType switch
{
TestAgentRequestType.FunctionCall => new TestRequestAgentSession<FunctionCallContent, FunctionResultContent>(),
@@ -37,7 +37,7 @@ internal sealed class TestRequestAgent(TestAgentRequestType requestType, int unp
_ => throw new NotSupportedException(),
});
public override ValueTask<AgentSession> DeserializeSessionAsync(JsonElement serializedSession, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default)
protected override ValueTask<AgentSession> DeserializeSessionCoreAsync(JsonElement serializedState, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default)
=> new(requestType switch
{
TestAgentRequestType.FunctionCall => new TestRequestAgentSession<FunctionCallContent, FunctionResultContent>(),
@@ -45,6 +45,9 @@ internal sealed class TestRequestAgent(TestAgentRequestType requestType, int unp
_ => throw new NotSupportedException(),
});
protected override JsonElement SerializeSessionCore(AgentSession session, JsonSerializerOptions? jsonSerializerOptions = null)
=> default;
protected override Task<AgentResponse> RunCoreAsync(IEnumerable<ChatMessage> messages, AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default)
=> this.RunStreamingAsync(messages, session, options, cancellationToken).ToAgentResponseAsync(cancellationToken);
@@ -361,7 +364,7 @@ internal sealed class TestRequestAgent(TestAgentRequestType requestType, int unp
this.PairedRequests = state.PairedRequests;
}
public override JsonElement Serialize(JsonSerializerOptions? jsonSerializerOptions = null)
protected override JsonElement Serialize(JsonSerializerOptions? jsonSerializerOptions = null)
{
JsonElement sessionState = base.Serialize(jsonSerializerOptions);
@@ -41,16 +41,19 @@ public class WorkflowHostSmokeTests
{ }
}
public override ValueTask<AgentSession> DeserializeSessionAsync(JsonElement serializedSession, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default)
protected override ValueTask<AgentSession> DeserializeSessionCoreAsync(JsonElement serializedState, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default)
{
return new(new Session(serializedSession, jsonSerializerOptions));
return new(new Session(serializedState, jsonSerializerOptions));
}
public override ValueTask<AgentSession> CreateSessionAsync(CancellationToken cancellationToken = default)
protected override ValueTask<AgentSession> CreateSessionCoreAsync(CancellationToken cancellationToken = default)
{
return new(new Session());
}
protected override JsonElement SerializeSessionCore(AgentSession session, JsonSerializerOptions? jsonSerializerOptions = null)
=> default;
protected override async Task<AgentResponse> RunCoreAsync(IEnumerable<ChatMessage> messages, AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default)
{
return await this.RunStreamingAsync(messages, session, options, cancellationToken)
@@ -23,7 +23,7 @@ public class OpenAIAssistantFixture : IChatClientAgentFixture
public IChatClient ChatClient => this._agent.ChatClient;
public async Task<List<ChatMessage>> GetChatHistoryAsync(AgentSession session)
public async Task<List<ChatMessage>> GetChatHistoryAsync(AIAgent agent, AgentSession session)
{
var typedSession = (ChatClientAgentSession)session;
List<ChatMessage> messages = [];
@@ -28,7 +28,7 @@ public class OpenAIChatCompletionFixture : IChatClientAgentFixture
public IChatClient ChatClient => this._agent.ChatClient;
public async Task<List<ChatMessage>> GetChatHistoryAsync(AgentSession session)
public async Task<List<ChatMessage>> GetChatHistoryAsync(AIAgent agent, AgentSession session)
{
var typedSession = (ChatClientAgentSession)session;
@@ -37,7 +37,7 @@ public class OpenAIChatCompletionFixture : IChatClientAgentFixture
return [];
}
return (await typedSession.ChatHistoryProvider.InvokingAsync(new([]))).ToList();
return (await typedSession.ChatHistoryProvider.InvokingAsync(new(agent, session, []))).ToList();
}
public Task<ChatClientAgent> CreateChatClientAgentAsync(
@@ -25,7 +25,7 @@ public class OpenAIResponseFixture(bool store) : IChatClientAgentFixture
public IChatClient ChatClient => this._agent.ChatClient;
public async Task<List<ChatMessage>> GetChatHistoryAsync(AgentSession session)
public async Task<List<ChatMessage>> GetChatHistoryAsync(AIAgent agent, AgentSession session)
{
var typedSession = (ChatClientAgentSession)session;
@@ -55,7 +55,7 @@ public class OpenAIResponseFixture(bool store) : IChatClientAgentFixture
return [];
}
return (await typedSession.ChatHistoryProvider.InvokingAsync(new([]))).ToList();
return (await typedSession.ChatHistoryProvider.InvokingAsync(new(agent, session, []))).ToList();
}
private static ChatMessage ConvertToChatMessage(ResponseItem item)