diff --git a/dotnet/Directory.Packages.props b/dotnet/Directory.Packages.props
index f1fd531194..f01315c214 100644
--- a/dotnet/Directory.Packages.props
+++ b/dotnet/Directory.Packages.props
@@ -72,8 +72,8 @@
-
-
+
+
diff --git a/dotnet/samples/AgentWebChat/AgentWebChat.Web/A2AAgentClient.cs b/dotnet/samples/AgentWebChat/AgentWebChat.Web/A2AAgentClient.cs
index 3a2d3560a9..db690950da 100644
--- a/dotnet/samples/AgentWebChat/AgentWebChat.Web/A2AAgentClient.cs
+++ b/dotnet/samples/AgentWebChat/AgentWebChat.Web/A2AAgentClient.cs
@@ -58,7 +58,7 @@ internal sealed class A2AAgentClient : AgentClientBase
if (a2aResponse is AgentMessage message)
{
var responseMessage = message.ToChatMessage();
- if (responseMessage is not null)
+ if (responseMessage is { Contents.Count: > 0 })
{
results.Add(new AgentRunResponseUpdate(responseMessage.Role, responseMessage.Contents)
{
@@ -78,11 +78,7 @@ internal sealed class A2AAgentClient : AgentClientBase
foreach (var part in artifact.Parts)
{
- var aiContent = ConvertPartToAIContent(part);
- if (aiContent != null)
- {
- (aiContents ??= []).Add(aiContent);
- }
+ (aiContents ??= []).Add(part.ToAIContent());
}
if (aiContents is not null)
@@ -155,20 +151,6 @@ internal sealed class A2AAgentClient : AgentClientBase
return (a2aClient, a2aCardResolver);
});
- private static AIContent? ConvertPartToAIContent(Part part) =>
- part switch
- {
- TextPart textPart => new TextContent(textPart.Text)
- {
- RawRepresentation = textPart
- },
- FilePart filePart when filePart.File is FileWithUri fileWithUrl => new HostedFileContent(fileWithUrl.Uri)
- {
- RawRepresentation = filePart
- },
- _ => null
- };
-
private static AdditionalPropertiesDictionary? ConvertMetadataToAdditionalProperties(Dictionary? metadata)
{
if (metadata is not { Count: > 0 })
@@ -184,22 +166,3 @@ internal sealed class A2AAgentClient : AgentClientBase
return additionalProperties;
}
}
-
-// Extension method to convert multiple chat messages to A2A messages
-internal static class ChatMessageExtensions
-{
- public static List ToA2AMessages(this IList chatMessages)
- {
- if (chatMessages is null || chatMessages.Count == 0)
- {
- return [];
- }
-
- var result = new List();
- foreach (var chatMessage in chatMessages)
- {
- result.Add(chatMessage.ToA2AMessage());
- }
- return result;
- }
-}
diff --git a/dotnet/src/Microsoft.Agents.AI.A2A/A2AHostAgent.cs b/dotnet/src/Microsoft.Agents.AI.A2A/A2AHostAgent.cs
index a59bbab39e..34fc058d38 100644
--- a/dotnet/src/Microsoft.Agents.AI.A2A/A2AHostAgent.cs
+++ b/dotnet/src/Microsoft.Agents.AI.A2A/A2AHostAgent.cs
@@ -4,6 +4,7 @@ using System;
using System.Threading;
using System.Threading.Tasks;
using A2A;
+using Microsoft.Extensions.AI;
using Microsoft.Shared.Diagnostics;
namespace Microsoft.Agents.AI.A2A;
diff --git a/dotnet/src/Microsoft.Agents.AI.A2A/Extensions/A2AAIContentExtensions.cs b/dotnet/src/Microsoft.Agents.AI.A2A/Extensions/A2AAIContentExtensions.cs
index 8c585c44f3..31e257e8bd 100644
--- a/dotnet/src/Microsoft.Agents.AI.A2A/Extensions/A2AAIContentExtensions.cs
+++ b/dotnet/src/Microsoft.Agents.AI.A2A/Extensions/A2AAIContentExtensions.cs
@@ -15,13 +15,13 @@ internal static class A2AAIContentExtensions
///
/// The collection of AI contents to convert."
/// The list of A2A objects.
- internal static List? ToA2AParts(this IEnumerable contents)
+ internal static List? ToParts(this IEnumerable contents)
{
List? parts = null;
foreach (var content in contents)
{
- var part = content.ToA2APart();
+ var part = content.ToPart();
if (part is not null)
{
(parts ??= []).Add(part);
@@ -30,18 +30,4 @@ internal static class A2AAIContentExtensions
return parts;
}
-
- ///
- /// Converts a to a object."/>
- ///
- /// AI content to convert.
- /// The corresponding A2A object, or null if the content type is not supported.
- internal static Part? ToA2APart(this AIContent content) =>
- content switch
- {
- TextContent textContent => new TextPart { Text = textContent.Text },
- HostedFileContent hostedFileContent => new FilePart { File = new FileWithUri { Uri = hostedFileContent.FileId } },
- // Ignore unknown content types (FunctionCallContent, FunctionResultContent, etc.)
- _ => null,
- };
}
diff --git a/dotnet/src/Microsoft.Agents.AI.A2A/Extensions/A2AMessageExtensions.cs b/dotnet/src/Microsoft.Agents.AI.A2A/Extensions/A2AMessageExtensions.cs
deleted file mode 100644
index 90b15f5d8c..0000000000
--- a/dotnet/src/Microsoft.Agents.AI.A2A/Extensions/A2AMessageExtensions.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright (c) Microsoft. All rights reserved.
-
-using System.Collections.Generic;
-using Microsoft.Extensions.AI;
-
-namespace A2A;
-
-///
-/// Extension methods for the class.
-///
-internal static class A2AMessageExtensions
-{
- internal static ChatMessage ToChatMessage(this AgentMessage message)
- {
- List? aiContents = null;
-
- foreach (var part in message.Parts)
- {
- var content = part.ToAIContent();
- if (content is not null)
- {
- (aiContents ??= []).Add(content);
- }
- }
-
- return new ChatMessage(ChatRole.Assistant, aiContents)
- {
- AdditionalProperties = message.Metadata.ToAdditionalProperties(),
- RawRepresentation = message,
- };
- }
-}
diff --git a/dotnet/src/Microsoft.Agents.AI.A2A/Extensions/A2APartExtensions.cs b/dotnet/src/Microsoft.Agents.AI.A2A/Extensions/A2APartExtensions.cs
deleted file mode 100644
index ac2da0ddaf..0000000000
--- a/dotnet/src/Microsoft.Agents.AI.A2A/Extensions/A2APartExtensions.cs
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright (c) Microsoft. All rights reserved.
-
-using Microsoft.Extensions.AI;
-
-namespace A2A;
-
-///
-/// Extension methods for the class.
-///
-internal static class A2APartExtensions
-{
- ///
- /// Converts an A2A to an .
- ///
- /// The A2A part to convert.
- /// The corresponding , or null if the part type is not supported.
- internal static AIContent? ToAIContent(this Part part) =>
- part switch
- {
- TextPart textPart => new TextContent(textPart.Text)
- {
- RawRepresentation = textPart,
- AdditionalProperties = textPart.Metadata.ToAdditionalProperties()
- },
-
- FilePart filePart when filePart.File is FileWithUri fileWithUrl => new HostedFileContent(fileWithUrl.Uri)
- {
- RawRepresentation = filePart,
- AdditionalProperties = filePart.Metadata.ToAdditionalProperties()
- },
-
- // Ignore unknown part types (DataPart, etc.)
- _ => null
- };
-}
diff --git a/dotnet/src/Microsoft.Agents.AI.A2A/Extensions/ChatMessageExtensions.cs b/dotnet/src/Microsoft.Agents.AI.A2A/Extensions/ChatMessageExtensions.cs
index 7165fcc628..b1f1bd643a 100644
--- a/dotnet/src/Microsoft.Agents.AI.A2A/Extensions/ChatMessageExtensions.cs
+++ b/dotnet/src/Microsoft.Agents.AI.A2A/Extensions/ChatMessageExtensions.cs
@@ -17,7 +17,7 @@ internal static class ChatMessageExtensions
foreach (var message in messages)
{
- if (message.Contents.ToA2AParts() is { Count: > 0 } ps)
+ if (message.Contents.ToParts() is { Count: > 0 } ps)
{
allParts.AddRange(ps);
}
diff --git a/dotnet/src/Microsoft.Agents.AI.Hosting.A2A/Converters/MessageConverter.cs b/dotnet/src/Microsoft.Agents.AI.Hosting.A2A/Converters/MessageConverter.cs
index 38f4edff39..5d2381a235 100644
--- a/dotnet/src/Microsoft.Agents.AI.Hosting.A2A/Converters/MessageConverter.cs
+++ b/dotnet/src/Microsoft.Agents.AI.Hosting.A2A/Converters/MessageConverter.cs
@@ -1,8 +1,6 @@
// Copyright (c) Microsoft. All rights reserved.
-using System;
using System.Collections.Generic;
-using System.Text.Json;
using A2A;
using Microsoft.Extensions.AI;
@@ -22,23 +20,16 @@ internal static class MessageConverter
{
foreach (var content in chatMessage.Contents)
{
- var part = ConvertAIContentToPart(content);
+ var part = content.ToPart();
if (part is not null)
{
parts.Add(part);
}
}
-
- // If no parts were created from content, create a text part from the message text
- if (chatMessage.Contents.Count == 0 && !string.IsNullOrEmpty(chatMessage.Text))
- {
- parts.Add(new TextPart { Text = chatMessage.Text });
- }
}
return parts;
}
-
///
/// Converts A2A MessageSendParams to a collection of Microsoft.Extensions.AI ChatMessage objects.
///
@@ -54,203 +45,9 @@ internal static class MessageConverter
var result = new List();
if (messageSendParams.Message?.Parts is not null)
{
- var chatMessage = ToChatMessage(messageSendParams.Message);
- if (chatMessage is not null)
- {
- result.Add(chatMessage);
- }
+ result.Add(messageSendParams.Message.ToChatMessage());
}
return result;
}
-
- ///
- /// Converts collection of A2A to a collection of objects.
- ///
- /// A read-only collection of ChatMessage objects.
- public static IReadOnlyCollection ToChatMessages(this ICollection messages)
- {
- if (messages is null || messages.Count == 0)
- {
- return [];
- }
-
- var result = new List();
- foreach (var message in messages)
- {
- var chatMessage = ToChatMessage(message);
- if (chatMessage is not null)
- {
- result.Add(chatMessage);
- }
- }
-
- return result;
- }
-
- ///
- /// Converts a single to a .
- ///
- /// The A2A message to convert.
- /// A ChatMessage object, or null if conversion is not possible.
- public static ChatMessage? ToChatMessage(this AgentMessage message)
- {
- if (message?.Parts is not { Count: > 0 })
- {
- return null;
- }
-
- var chatRole = ConvertMessageRoleToChatRole(message.Role);
-
- var content = new List();
- foreach (var part in message.Parts)
- {
- var aiContent = ConvertPartToAIContent(part);
- if (aiContent is not null)
- {
- content.Add(aiContent);
- }
- }
-
- // If no valid content was extracted, return null
- if (content.Count == 0)
- {
- return null;
- }
-
- // Create the ChatMessage with appropriate metadata
- var chatMessage = new ChatMessage(chatRole, content)
- {
- MessageId = message.MessageId,
- RawRepresentation = message
- };
-
- // Add any additional properties if needed
- if (message.Metadata is not null)
- {
- chatMessage.AdditionalProperties = message.Metadata.ToAdditionalPropertiesDictionary();
- }
-
- return chatMessage;
- }
-
- ///
- /// Converts A2A MessageRole to Microsoft.Extensions.AI ChatRole.
- ///
- /// The A2A message role.
- /// The corresponding ChatRole.
- private static ChatRole ConvertMessageRoleToChatRole(MessageRole messageRole) => messageRole switch
- {
- MessageRole.User => ChatRole.User,
- MessageRole.Agent => ChatRole.Assistant,
- _ => ChatRole.User
- };
-
- ///
- /// Converts an A2A Part to Microsoft.Extensions.AI AIContent.
- ///
- /// The A2A part to convert.
- /// An AIContent object, or null if conversion is not possible.
-#pragma warning disable CA1859 // Use concrete types when possible for improved performance
- private static AIContent? ConvertPartToAIContent(Part part) =>
- part switch
- {
- TextPart textPart => new TextContent(textPart.Text)
- {
- RawRepresentation = textPart,
- AdditionalProperties = textPart.Metadata?.ToAdditionalPropertiesDictionary()
- },
- // Ignore unknown content types (FilePart, DataPart, etc.)
- _ => null
- };
-
- ///
- /// Converts Microsoft.Extensions.AI ChatMessage back to A2A Message format.
- /// This is useful for the reverse operation.
- ///
- /// The ChatMessage to convert.
- /// An A2A Message object.
- public static AgentMessage ToA2AMessage(this ChatMessage chatMessage)
- {
- if (chatMessage is null)
- {
- throw new ArgumentNullException(nameof(chatMessage));
- }
-
- var message = new AgentMessage
- {
- MessageId = chatMessage.MessageId ?? Guid.NewGuid().ToString("N"),
- Role = ConvertChatRoleToMessageRole(chatMessage.Role),
- Parts = []
- };
-
- // Convert content to parts
- foreach (var content in chatMessage.Contents)
- {
- var part = ConvertAIContentToPart(content);
- if (part is not null)
- {
- message.Parts.Add(part);
- }
- }
-
- // If no parts were created from content, create a text part from the message text
- if (message.Parts.Count == 0 && !string.IsNullOrEmpty(chatMessage.Text))
- {
- message.Parts.Add(new TextPart { Text = chatMessage.Text });
- }
-
- return message;
- }
-
- ///
- /// Converts Microsoft.Extensions.AI ChatRole to A2A MessageRole.
- ///
- /// The ChatRole to convert.
- /// The corresponding MessageRole.
- private static MessageRole ConvertChatRoleToMessageRole(ChatRole chatRole)
- {
- if (chatRole == ChatRole.User)
- {
- return MessageRole.User;
- }
- if (chatRole == ChatRole.Assistant)
- {
- return MessageRole.Agent;
- }
-
- return MessageRole.User; // Default fallback
- }
-
- ///
- /// Converts Microsoft.Extensions.AI AIContent to A2A Part.
- ///
- /// The AIContent to convert.
- /// A Part object, or null if conversion is not possible.
-#pragma warning disable CA1859 // Use concrete types when possible for improved performance
- private static Part? ConvertAIContentToPart(AIContent content) =>
- content switch
- {
- TextContent textContent => new TextPart
- {
- Text = textContent.Text
- },
- // Ignore unknown content types (FunctionCallContent, FunctionResultContent, etc.)
- _ => null
- };
-
- private static AdditionalPropertiesDictionary? ToAdditionalPropertiesDictionary(this Dictionary metadata)
- {
- if (metadata is not { Count: > 0 })
- {
- return null;
- }
-
- var additionalProperties = new AdditionalPropertiesDictionary();
- foreach (var kvp in metadata)
- {
- additionalProperties[kvp.Key] = kvp.Value;
- }
- return additionalProperties;
- }
}
diff --git a/dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/A2AAgentTests.cs b/dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/A2AAgentTests.cs
index a7c6b42359..39d486f114 100644
--- a/dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/A2AAgentTests.cs
+++ b/dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/A2AAgentTests.cs
@@ -371,7 +371,7 @@ public sealed class A2AAgentTests : IDisposable
new(ChatRole.User,
[
new TextContent("Check this file:"),
- new HostedFileContent("https://example.com/file.pdf")
+ new UriContent("https://example.com/file.pdf", "application/pdf")
])
};
@@ -385,7 +385,7 @@ public sealed class A2AAgentTests : IDisposable
Assert.IsType(message.Parts[0]);
Assert.Equal("Check this file:", ((TextPart)message.Parts[0]).Text);
Assert.IsType(message.Parts[1]);
- Assert.Equal("https://example.com/file.pdf", ((FileWithUri)((FilePart)message.Parts[1]).File).Uri);
+ Assert.Equal("https://example.com/file.pdf", ((FilePart)message.Parts[1]).File.Uri?.ToString());
}
public void Dispose()
diff --git a/dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/Extensions/A2AAIContentExtensionsTests.cs b/dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/Extensions/A2AAIContentExtensionsTests.cs
index 857ab0f1d6..358bdfb152 100644
--- a/dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/Extensions/A2AAIContentExtensionsTests.cs
+++ b/dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/Extensions/A2AAIContentExtensionsTests.cs
@@ -11,55 +11,6 @@ namespace Microsoft.Agents.AI.A2A.UnitTests;
///
public sealed class A2AAIContentExtensionsTests
{
- [Fact]
- public void ToA2APart_WithTextContent_ReturnsTextPart()
- {
- // Arrange
- var textContent = new TextContent("Hello, world!");
-
- // Act
- var result = textContent.ToA2APart();
-
- // Assert
- Assert.NotNull(result);
-
- var textPart = Assert.IsType(result);
- Assert.Equal("Hello, world!", textPart.Text);
- }
-
- [Fact]
- public void ToA2APart_WithHostedFileContent_ReturnsFilePart()
- {
- // Arrange
- const string Uri = "https://example.com/file.txt";
- var hostedFileContent = new HostedFileContent(Uri);
-
- // Act
- var result = hostedFileContent.ToA2APart();
-
- // Assert
- Assert.NotNull(result);
-
- var filePart = Assert.IsType(result);
- Assert.NotNull(filePart.File);
-
- var fileWithUri = Assert.IsType(filePart.File);
- Assert.Equal(Uri, fileWithUri.Uri);
- }
-
- [Fact]
- public void ToA2APart_WithUnsupportedContentType_ReturnsNull()
- {
- // Arrange
- var unsupportedContent = new MockAIContent();
-
- // Act
- var result = unsupportedContent.ToA2APart();
-
- // Assert
- Assert.Null(result);
- }
-
[Fact]
public void ToA2AParts_WithEmptyCollection_ReturnsNull()
{
@@ -67,7 +18,7 @@ public sealed class A2AAIContentExtensionsTests
var emptyContents = new List();
// Act
- var result = emptyContents.ToA2AParts();
+ var result = emptyContents.ToParts();
// Assert
Assert.Null(result);
@@ -80,31 +31,25 @@ public sealed class A2AAIContentExtensionsTests
var contents = new List
{
new TextContent("First text"),
- new HostedFileContent("https://example.com/file1.txt"),
+ new UriContent("https://example.com/file1.txt", "file/txt"),
new TextContent("Second text"),
- new HostedFileContent("https://example.com/file2.txt")
};
// Act
- var result = contents.ToA2AParts();
+ var result = contents.ToParts();
// Assert
Assert.NotNull(result);
- Assert.Equal(4, result.Count);
+ Assert.Equal(3, result.Count);
var firstTextPart = Assert.IsType(result[0]);
Assert.Equal("First text", firstTextPart.Text);
- var firstFilePart = Assert.IsType(result[1]);
- var firstFileWithUri = Assert.IsType(firstFilePart.File);
- Assert.Equal("https://example.com/file1.txt", firstFileWithUri.Uri);
+ var filePart = Assert.IsType(result[1]);
+ Assert.Equal("https://example.com/file1.txt", filePart.File.Uri?.ToString());
var secondTextPart = Assert.IsType(result[2]);
Assert.Equal("Second text", secondTextPart.Text);
-
- var secondFilePart = Assert.IsType(result[3]);
- var secondFileWithUri = Assert.IsType(secondFilePart.File);
- Assert.Equal("https://example.com/file2.txt", secondFileWithUri.Uri);
}
[Fact]
@@ -115,13 +60,13 @@ public sealed class A2AAIContentExtensionsTests
{
new TextContent("First text"),
new MockAIContent(), // Unsupported - should be ignored
- new HostedFileContent("https://example.com/file.txt"),
+ new UriContent("https://example.com/file.txt", "file/txt"),
new MockAIContent(), // Unsupported - should be ignored
new TextContent("Second text")
};
// Act
- var result = contents.ToA2AParts();
+ var result = contents.ToParts();
// Assert
Assert.NotNull(result);
@@ -131,8 +76,7 @@ public sealed class A2AAIContentExtensionsTests
Assert.Equal("First text", firstTextPart.Text);
var filePart = Assert.IsType(result[1]);
- var fileWithUri = Assert.IsType(filePart.File);
- Assert.Equal("https://example.com/file.txt", fileWithUri.Uri);
+ Assert.Equal("https://example.com/file.txt", filePart.File.Uri?.ToString());
var secondTextPart = Assert.IsType(result[2]);
Assert.Equal("Second text", secondTextPart.Text);
diff --git a/dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/Extensions/A2AMessageExtensionsTests.cs b/dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/Extensions/A2AMessageExtensionsTests.cs
deleted file mode 100644
index 96fca5f34c..0000000000
--- a/dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/Extensions/A2AMessageExtensionsTests.cs
+++ /dev/null
@@ -1,65 +0,0 @@
-// Copyright (c) Microsoft. All rights reserved.
-
-using System.Collections.Generic;
-using System.Text.Json;
-using A2A;
-using Microsoft.Extensions.AI;
-
-namespace Microsoft.Agents.AI.A2A.UnitTests;
-
-///
-/// Unit tests for the class.
-///
-public sealed class A2AMessageExtensionsTests
-{
- [Fact]
- public void ToChatMessage_WithMixedParts_ReturnsChatMessageWithMixedContents()
- {
- // Arrange
- const string Uri = "https://example.com/image.jpg";
-
- var metadata = new Dictionary
- {
- ["isUrgent"] = JsonDocument.Parse("true").RootElement
- };
-
- var message = new AgentMessage
- {
- MessageId = "mixed-parts-id",
- Role = MessageRole.Agent,
- Parts =
- [
- new TextPart { Text = "Here's an image:" },
- new FilePart { File = new FileWithUri { Uri = Uri } },
- new TextPart { Text = "What do you think?" }
- ],
- Metadata = metadata
- };
-
- // Act
- var result = message.ToChatMessage();
-
- // Assert
- Assert.NotNull(result);
- Assert.Equal(ChatRole.Assistant, result.Role);
- Assert.Equal(message, result.RawRepresentation);
-
- Assert.NotNull(result.Contents);
- Assert.Equal(3, result.Contents.Count);
-
- var firstContent = Assert.IsType(result.Contents[0]);
- Assert.Equal("Here's an image:", firstContent.Text);
-
- var fileContent = Assert.IsType(result.Contents[1]);
- Assert.Equal(Uri, fileContent.FileId);
-
- var lastContent = Assert.IsType(result.Contents[2]);
- Assert.Equal("What do you think?", lastContent.Text);
-
- Assert.NotNull(result.AdditionalProperties);
- Assert.Single(result.AdditionalProperties);
-
- Assert.True(result.AdditionalProperties.ContainsKey("isUrgent"));
- Assert.True(((JsonElement)result.AdditionalProperties["isUrgent"]!).GetBoolean());
- }
-}
diff --git a/dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/Extensions/A2APartExtensionsTests.cs b/dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/Extensions/A2APartExtensionsTests.cs
deleted file mode 100644
index 21be340d56..0000000000
--- a/dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/Extensions/A2APartExtensionsTests.cs
+++ /dev/null
@@ -1,96 +0,0 @@
-// Copyright (c) Microsoft. All rights reserved.
-
-using System.Collections.Generic;
-using System.Text.Json;
-using A2A;
-using Microsoft.Extensions.AI;
-
-namespace Microsoft.Agents.AI.A2A.UnitTests;
-
-///
-/// Unit tests for the class.
-///
-public sealed class A2APartExtensionsTests
-{
- [Fact]
- public void ToAIContent_WithTextPart_ReturnsTextContent()
- {
- // Arrange
- var textPart = new TextPart { Text = "Hello, world!" };
-
- // Act
- var result = textPart.ToAIContent();
-
- // Assert
- Assert.NotNull(result);
- Assert.Equal(textPart, result.RawRepresentation);
-
- var textContent = Assert.IsType(result);
- Assert.Equal("Hello, world!", textContent.Text);
- }
-
- [Fact]
- public void ToAIContent_WithTextPartWithMetadata_ReturnsTextContentWithAdditionalProperties()
- {
- // Arrange
- var metadata = new Dictionary
- {
- ["key1"] = JsonDocument.Parse("\"value1\"").RootElement,
- ["key2"] = JsonDocument.Parse("42").RootElement,
- ["key3"] = JsonDocument.Parse("true").RootElement
- };
- var textPart = new TextPart
- {
- Text = "Hello with metadata!",
- Metadata = metadata
- };
-
- // Act
- var result = textPart.ToAIContent();
-
- // Assert
- Assert.NotNull(result);
- var textContent = Assert.IsType(result);
- Assert.Equal("Hello with metadata!", textContent.Text);
- Assert.NotNull(textContent.AdditionalProperties);
- Assert.Equal(3, textContent.AdditionalProperties.Count);
- Assert.True(textContent.AdditionalProperties.ContainsKey("key1"));
- Assert.True(textContent.AdditionalProperties.ContainsKey("key2"));
- Assert.True(textContent.AdditionalProperties.ContainsKey("key3"));
- }
-
- [Fact]
- public void ToAIContent_WithFilePartWithFileWithUri_ReturnsHostedFileContent()
- {
- // Arrange
- const string Uri = "https://example.com/file.txt";
- var filePart = new FilePart { File = new FileWithUri { Uri = Uri } };
-
- // Act
- var result = filePart.ToAIContent();
-
- // Assert
- Assert.NotNull(result);
- Assert.Equal(filePart, result.RawRepresentation);
-
- var hostedFileContent = Assert.IsType(result);
- Assert.Equal(Uri, hostedFileContent.FileId);
- Assert.Null(hostedFileContent.AdditionalProperties);
- }
-
- [Fact]
- public void ToAIContent_WithCustomPartType_ReturnsNull()
- {
- // Arrange
- var customPart = new MockPart();
-
- // Act
- var result = customPart.ToAIContent();
-
- // Assert
- Assert.Null(result);
- }
-
- // Mock class for testing unsupported scenarios
- private sealed class MockPart : Part;
-}
diff --git a/dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/Extensions/ChatMessageExtensionsTests.cs b/dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/Extensions/ChatMessageExtensionsTests.cs
index bee5762649..8d771c679c 100644
--- a/dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/Extensions/ChatMessageExtensionsTests.cs
+++ b/dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/Extensions/ChatMessageExtensionsTests.cs
@@ -17,7 +17,7 @@ public sealed class ChatMessageExtensionsTests
// Arrange
var contents = new List
{
- new HostedFileContent("https://example.com/report.pdf"),
+ new UriContent("https://example.com/report.pdf", "file/pdf"),
new TextContent("please summarize the file content"),
new TextContent("and send it to me over email")
};
@@ -39,8 +39,7 @@ public sealed class ChatMessageExtensionsTests
var filePart = Assert.IsType(a2aMessage.Parts[0]);
Assert.NotNull(filePart.File);
- var fileWithUri = Assert.IsType(filePart.File);
- Assert.Equal("https://example.com/report.pdf", fileWithUri.Uri);
+ Assert.Equal("https://example.com/report.pdf", filePart.File.Uri?.ToString());
var secondTextPart = Assert.IsType(a2aMessage.Parts[1]);
Assert.Equal("please summarize the file content", secondTextPart.Text);
@@ -54,7 +53,7 @@ public sealed class ChatMessageExtensionsTests
{
// Arrange
var firstMessage = new ChatMessage(ChatRole.User, [
- new HostedFileContent("https://example.com/report.pdf")
+ new UriContent("https://example.com/report.pdf", "file/pdf"),
]);
var secondMessage = new ChatMessage(ChatRole.User, [
new TextContent("please summarize the file content")
@@ -79,8 +78,7 @@ public sealed class ChatMessageExtensionsTests
var filePart = Assert.IsType(a2aMessage.Parts[0]);
Assert.NotNull(filePart.File);
- var fileWithUri = Assert.IsType(filePart.File);
- Assert.Equal("https://example.com/report.pdf", fileWithUri.Uri);
+ Assert.Equal("https://example.com/report.pdf", filePart.File.Uri?.ToString());
var secondTextPart = Assert.IsType(a2aMessage.Parts[1]);
Assert.Equal("please summarize the file content", secondTextPart.Text);
diff --git a/dotnet/tests/Microsoft.Agents.AI.Hosting.A2A.Tests/Converters/MessageConverterTests.cs b/dotnet/tests/Microsoft.Agents.AI.Hosting.A2A.Tests/Converters/MessageConverterTests.cs
index 5791a0c238..81ce582870 100644
--- a/dotnet/tests/Microsoft.Agents.AI.Hosting.A2A.Tests/Converters/MessageConverterTests.cs
+++ b/dotnet/tests/Microsoft.Agents.AI.Hosting.A2A.Tests/Converters/MessageConverterTests.cs
@@ -1,9 +1,6 @@
// Copyright (c) Microsoft. All rights reserved.
-using System;
-using System.Collections.Generic;
using System.Linq;
-using System.Text.Json;
using A2A;
using Microsoft.Agents.AI.Hosting.A2A.Converters;
using Microsoft.Extensions.AI;
@@ -85,510 +82,4 @@ public class MessageConverterTests
var textContent = Assert.IsType(chatMessage.Contents.First());
Assert.Equal("Hello, world!", textContent.Text);
}
-
- [Fact]
- public void ToChatMessages_MessageCollection_Null_ReturnsEmptyCollection()
- {
- ICollection? messages = null;
-
- var result = messages!.ToChatMessages();
-
- Assert.NotNull(result);
- Assert.Empty(result);
- }
-
- [Fact]
- public void ToChatMessages_MessageCollection_Empty_ReturnsEmptyCollection()
- {
- var messages = new List();
-
- var result = messages.ToChatMessages();
-
- Assert.NotNull(result);
- Assert.Empty(result);
- }
-
- [Fact]
- public void ToChatMessages_MessageCollection_WithValidMessages_ReturnsCorrectChatMessages()
- {
- var messages = new List
- {
- new()
- {
- MessageId = "user-msg",
- Role = MessageRole.User,
- Parts = [new TextPart { Text = "User message" }]
- },
- new()
- {
- MessageId = "agent-msg",
- Role = MessageRole.Agent,
- Parts = [new TextPart { Text = "Agent response" }]
- }
- };
-
- var result = messages.ToChatMessages();
-
- Assert.NotNull(result);
- Assert.Equal(2, result.Count);
-
- var userMessage = result.First();
- Assert.Equal("user-msg", userMessage.MessageId);
- Assert.Equal(ChatRole.User, userMessage.Role);
- Assert.Equal("User message", ((TextContent)userMessage.Contents.First()).Text);
-
- var agentMessage = result.Skip(1).First();
- Assert.Equal("agent-msg", agentMessage.MessageId);
- Assert.Equal(ChatRole.Assistant, agentMessage.Role);
- Assert.Equal("Agent response", ((TextContent)agentMessage.Contents.First()).Text);
- }
-
- [Fact]
- public void ToChatMessages_MessageCollection_SkipsInvalidMessages_ReturnsValidChatMessages()
- {
- var messages = new List
- {
- new()
- {
- MessageId = "valid-msg",
- Role = MessageRole.User,
- Parts = [new TextPart { Text = "Valid message" }]
- },
- new()
- {
- MessageId = "invalid-msg",
- Role = MessageRole.User,
- Parts = null! // Invalid - no parts
- }
- };
-
- var result = messages.ToChatMessages();
-
- Assert.NotNull(result);
- Assert.Single(result);
-
- var validMessage = result.First();
- Assert.Equal("valid-msg", validMessage.MessageId);
- Assert.Equal("Valid message", ((TextContent)validMessage.Contents.First()).Text);
- }
-
- [Fact]
- public void ToA2AMessage_NullChatMessage_ThrowsArgumentNullException()
- {
- ChatMessage? chatMessage = null;
-
- Assert.Throws(() => chatMessage!.ToA2AMessage());
- }
-
- [Fact]
- public void ToA2AMessage_ValidChatMessage_ReturnsCorrectA2AMessage()
- {
- var chatMessage = new ChatMessage(ChatRole.User, "Hello, world!")
- {
- MessageId = "test-id"
- };
-
- var result = chatMessage.ToA2AMessage();
-
- Assert.NotNull(result);
- Assert.Equal("test-id", result.MessageId);
- Assert.Equal(MessageRole.User, result.Role);
- Assert.Single(result.Parts);
-
- var textPart = Assert.IsType(result.Parts.First());
- Assert.Equal("Hello, world!", textPart.Text);
- }
-
- [Fact]
- public void ToA2AMessage_ChatMessageWithoutMessageId_GeneratesNewMessageId()
- {
- var chatMessage = new ChatMessage(ChatRole.Assistant, "Response message");
-
- var result = chatMessage.ToA2AMessage();
-
- Assert.NotNull(result);
- Assert.NotNull(result.MessageId);
- Assert.NotEmpty(result.MessageId);
- Assert.Equal(MessageRole.Agent, result.Role);
- }
-
- [Fact]
- public void ToA2AMessage_ChatMessageWithTextContent_ReturnsCorrectTextPart()
- {
- var textContent = new TextContent("Test content");
- var chatMessage = new ChatMessage(ChatRole.User, [textContent]);
-
- var result = chatMessage.ToA2AMessage();
-
- Assert.NotNull(result);
- Assert.Single(result.Parts);
-
- var textPart = Assert.IsType(result.Parts.First());
- Assert.Equal("Test content", textPart.Text);
- }
-
- [Fact]
- public void ToA2AMessage_ChatMessageWithUnsupportedContent_IgnoresUnsupportedContent()
- {
- var unsupportedContent = new DataContent(new byte[] { 1, 2, 3 }, "image/png");
- var chatMessage = new ChatMessage(ChatRole.User, [unsupportedContent]);
-
- var result = chatMessage.ToA2AMessage();
-
- // Should create a message but ignore the unsupported content
- Assert.NotNull(result);
- Assert.Empty(result.Parts);
- }
-
- [Fact]
- public void ToA2AMessage_ChatMessageWithEmptyContent_CreatesTextPartFromMessageText()
- {
- var chatMessage = new ChatMessage(ChatRole.User, "Fallback text");
-
- var result = chatMessage.ToA2AMessage();
-
- Assert.NotNull(result);
- Assert.Single(result.Parts);
-
- var textPart = Assert.IsType(result.Parts.First());
- Assert.Equal("Fallback text", textPart.Text);
- }
-
- [Fact]
- public void ConvertMessageRoleToChatRole_UserRole_ReturnsUserChatRole()
- {
- var message = new AgentMessage
- {
- MessageId = "test",
- Role = MessageRole.User,
- Parts = [new TextPart { Text = "Test" }]
- };
-
- var result = new List { message }.ToChatMessages();
-
- var chatMessage = result.First();
- Assert.Equal(ChatRole.User, chatMessage.Role);
- }
-
- [Fact]
- public void ConvertMessageRoleToChatRole_AgentRole_ReturnsAssistantChatRole()
- {
- var message = new AgentMessage
- {
- MessageId = "test",
- Role = MessageRole.Agent,
- Parts = [new TextPart { Text = "Test" }]
- };
-
- var result = new List { message }.ToChatMessages();
-
- var chatMessage = result.First();
- Assert.Equal(ChatRole.Assistant, chatMessage.Role);
- }
-
- [Fact]
- public void ConvertMessageRoleToChatRole_UnknownRole_ReturnsUserChatRole()
- {
- var message = new AgentMessage
- {
- MessageId = "test",
- Role = (MessageRole)999, // Unknown role
- Parts = [new TextPart { Text = "Test" }]
- };
-
- var result = new List { message }.ToChatMessages();
-
- var chatMessage = result.First();
- Assert.Equal(ChatRole.User, chatMessage.Role);
- }
-
- [Fact]
- public void ConvertChatRoleToMessageRole_UserRole_ReturnsUserMessageRole()
- {
- var chatMessage = new ChatMessage(ChatRole.User, "Test message");
-
- var result = chatMessage.ToA2AMessage();
-
- Assert.Equal(MessageRole.User, result.Role);
- }
-
- [Fact]
- public void ConvertChatRoleToMessageRole_AssistantRole_ReturnsAgentMessageRole()
- {
- var chatMessage = new ChatMessage(ChatRole.Assistant, "Test message");
-
- var result = chatMessage.ToA2AMessage();
-
- Assert.Equal(MessageRole.Agent, result.Role);
- }
-
- [Fact]
- public void ConvertChatRoleToMessageRole_SystemRole_ReturnsUserMessageRole()
- {
- var chatMessage = new ChatMessage(ChatRole.System, "Test message");
-
- var result = chatMessage.ToA2AMessage();
-
- Assert.Equal(MessageRole.User, result.Role);
- }
-
- [Fact]
- public void ConvertChatRoleToMessageRole_ToolRole_ReturnsUserMessageRole()
- {
- var chatMessage = new ChatMessage(ChatRole.Tool, "Test message");
-
- var result = chatMessage.ToA2AMessage();
-
- Assert.Equal(MessageRole.User, result.Role);
- }
-
- [Fact]
- public void ConvertPartToAIContent_TextPart_ReturnsTextContent()
- {
- var textPart = new TextPart { Text = "Sample text" };
- var message = new AgentMessage
- {
- MessageId = "test",
- Role = MessageRole.User,
- Parts = [textPart]
- };
-
- var result = new List { message }.ToChatMessages();
-
- var chatMessage = result.First();
- var textContent = Assert.IsType(chatMessage.Contents.First());
- Assert.Equal("Sample text", textContent.Text);
- Assert.Equal(textPart, textContent.RawRepresentation);
- }
-
- [Fact]
- public void ConvertPartToAIContent_TextPartWithMetadata_PreservesMetadata()
- {
- var metadata = new Dictionary
- {
- ["key1"] = JsonDocument.Parse("\"value1\"").RootElement,
- ["key2"] = JsonDocument.Parse("42").RootElement
- };
- var textPart = new TextPart
- {
- Text = "Text with metadata",
- Metadata = metadata
- };
- var message = new AgentMessage
- {
- MessageId = "test",
- Role = MessageRole.User,
- Parts = [textPart]
- };
-
- var result = new List { message }.ToChatMessages();
-
- var chatMessage = result.First();
- var textContent = Assert.IsType(chatMessage.Contents.First());
- Assert.NotNull(textContent.AdditionalProperties);
- Assert.Equal(2, textContent.AdditionalProperties.Count);
- Assert.True(textContent.AdditionalProperties.ContainsKey("key1"));
- Assert.True(textContent.AdditionalProperties.ContainsKey("key2"));
- }
-
- [Fact]
- public void ConvertPartToAIContent_FilePart_IgnoresUnsupportedPart()
- {
- var filePart = new FilePart();
- var message = new AgentMessage
- {
- MessageId = "test",
- Role = MessageRole.User,
- Parts = [filePart]
- };
-
- var result = new List { message }.ToChatMessages();
-
- // Should return empty collection since FilePart is ignored
- Assert.NotNull(result);
- Assert.Empty(result);
- }
-
- [Fact]
- public void ConvertPartToAIContent_DataPart_IgnoresUnsupportedPart()
- {
- var dataPart = new DataPart();
- var message = new AgentMessage
- {
- MessageId = "test",
- Role = MessageRole.User,
- Parts = [dataPart]
- };
-
- var result = new List { message }.ToChatMessages();
-
- // Should return empty collection since DataPart is ignored
- Assert.NotNull(result);
- Assert.Empty(result);
- }
-
- [Fact]
- public void ConvertMessageToChatMessage_WithMetadata_PreservesMetadataInAdditionalProperties()
- {
- var metadata = new Dictionary
- {
- ["timestamp"] = JsonDocument.Parse("\"2024-01-01T00:00:00Z\"").RootElement,
- ["priority"] = JsonDocument.Parse("1").RootElement
- };
- var message = new AgentMessage
- {
- MessageId = "test-id",
- Role = MessageRole.User,
- Parts = [new TextPart { Text = "Test" }],
- Metadata = metadata
- };
-
- var result = new List { message }.ToChatMessages();
-
- var chatMessage = result.First();
- Assert.NotNull(chatMessage.AdditionalProperties);
- Assert.Equal(2, chatMessage.AdditionalProperties.Count);
- Assert.True(chatMessage.AdditionalProperties.ContainsKey("timestamp"));
- Assert.True(chatMessage.AdditionalProperties.ContainsKey("priority"));
- }
-
- [Fact]
- public void ConvertMessageToChatMessage_WithRawRepresentation_PreservesOriginalMessage()
- {
- var message = new AgentMessage
- {
- MessageId = "test-id",
- Role = MessageRole.Agent,
- Parts = [new TextPart { Text = "Test response" }]
- };
-
- var result = new List { message }.ToChatMessages();
-
- var chatMessage = result.First();
- Assert.Equal(message, chatMessage.RawRepresentation);
- }
-
- [Fact]
- public void ToChatMessages_MessageWithEmptyParts_ReturnsEmptyCollection()
- {
- var message = new AgentMessage
- {
- MessageId = "test-id",
- Role = MessageRole.User,
- Parts = [] // Empty list
- };
-
- var result = new List { message }.ToChatMessages();
-
- Assert.NotNull(result);
- Assert.Empty(result);
- }
-
- [Fact]
- public void ToA2AMessage_ChatMessageWithMultipleTextContents_CreatesMultipleParts()
- {
- var contents = new List
- {
- new TextContent("First part"),
- new TextContent("Second part")
- };
- var chatMessage = new ChatMessage(ChatRole.User, contents);
-
- var result = chatMessage.ToA2AMessage();
-
- Assert.NotNull(result);
- Assert.Equal(2, result.Parts.Count);
-
- var firstPart = Assert.IsType(result.Parts[0]);
- var secondPart = Assert.IsType(result.Parts[1]);
- Assert.Equal("First part", firstPart.Text);
- Assert.Equal("Second part", secondPart.Text);
- }
-
- [Fact]
- public void ToAdditionalPropertiesDictionary_NullMetadata_ReturnsNull()
- {
- var message = new AgentMessage
- {
- MessageId = "test-id",
- Role = MessageRole.User,
- Parts = [new TextPart { Text = "Test" }],
- Metadata = null
- };
-
- var result = new List { message }.ToChatMessages();
-
- var chatMessage = result.First();
- Assert.Null(chatMessage.AdditionalProperties);
- }
-
- [Fact]
- public void ToAdditionalPropertiesDictionary_EmptyMetadata_ReturnsNull()
- {
- var message = new AgentMessage
- {
- MessageId = "test-id",
- Role = MessageRole.User,
- Parts = [new TextPart { Text = "Test" }],
- Metadata = []
- };
-
- var result = new List { message }.ToChatMessages();
-
- var chatMessage = result.First();
- Assert.Null(chatMessage.AdditionalProperties);
- }
-
- [Fact]
- public void ConvertPartToAIContent_MixedPartsWithUnsupported_IgnoresUnsupportedParts()
- {
- var message = new AgentMessage
- {
- MessageId = "test",
- Role = MessageRole.User,
- Parts = [
- new TextPart { Text = "First part" },
- new DataPart(), // Unsupported - should be ignored
- new TextPart { Text = "Second part" },
- new FilePart() // Unsupported - should be ignored
- ]
- };
-
- var result = new List { message }.ToChatMessages();
-
- Assert.NotNull(result);
- Assert.Single(result);
-
- var chatMessage = result.First();
- Assert.Equal(2, chatMessage.Contents.Count);
-
- var firstContent = Assert.IsType(chatMessage.Contents[0]);
- Assert.Equal("First part", firstContent.Text);
-
- var secondContent = Assert.IsType(chatMessage.Contents[1]);
- Assert.Equal("Second part", secondContent.Text);
- }
-
- [Fact]
- public void ToA2AMessage_MixedContentWithUnsupported_IgnoresUnsupportedContent()
- {
- var contents = new List
- {
- new TextContent("First text"),
- new DataContent(new byte[] { 1, 2, 3 }, "image/png"), // Unsupported - should be ignored
- new TextContent("Second text")
- };
- var chatMessage = new ChatMessage(ChatRole.User, contents);
-
- var result = chatMessage.ToA2AMessage();
-
- Assert.NotNull(result);
- Assert.Equal(2, result.Parts.Count);
-
- var firstPart = Assert.IsType(result.Parts[0]);
- Assert.Equal("First text", firstPart.Text);
-
- var secondPart = Assert.IsType(result.Parts[1]);
- Assert.Equal("Second text", secondPart.Text);
- }
}