Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion dotnet/Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
<PackageVersion Include="JsonSchema.Net" Version="7.3.4" />
<PackageVersion Include="JsonSchema.Net.Generation" Version="5.0.2" />
<PackageVersion Include="Markdig" Version="0.40.0" />
<PackageVersion Include="Microsoft.Agents.AI.Abstractions" Version="1.0.0-preview.251009.1" />
<PackageVersion Include="Microsoft.Agents.CopilotStudio.Client" Version="1.1.107-beta" />
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="8.0.13" />
<PackageVersion Include="Microsoft.AspNetCore.OpenApi" Version="8.0.14" />
Expand Down Expand Up @@ -101,7 +102,7 @@
<PackageVersion Include="System.Memory.Data" Version="8.0.1" />
<PackageVersion Include="System.Net.Http" Version="4.3.4" />
<PackageVersion Include="System.Numerics.Tensors" Version="9.0.8" />
<PackageVersion Include="System.Text.Json" Version="8.0.6" />
<PackageVersion Include="System.Text.Json" Version="9.0.9" />
<PackageVersion Include="System.ValueTuple" Version="4.6.1" />
<PackageVersion Include="System.Threading.Tasks.Extensions" Version="4.6.3" />
<PackageVersion Include="A2A" Version="0.3.1-preview" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,16 @@
<PackageReference Include="Azure.AI.Projects" />
<PackageReference Include="Azure.Identity" />
<PackageReference Include="Azure.Monitor.OpenTelemetry.Exporter" />
<PackageReference Include="Microsoft.Extensions.Configuration" VersionOverride="9.0.7" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" VersionOverride="9.0.7" />
<PackageReference Include="Microsoft.Extensions.Configuration" VersionOverride="9.0.9" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" VersionOverride="9.0.9" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" VersionOverride="9.0.7" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" VersionOverride="9.0.7" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" VersionOverride="9.0.7" />
<PackageReference Include="Microsoft.Extensions.Http" VersionOverride="9.0.7" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" VersionOverride="9.0.9" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" VersionOverride="9.0.9" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" VersionOverride="9.0.9" />
<PackageReference Include="Microsoft.Extensions.Http" VersionOverride="9.0.9" />
<PackageReference Include="Microsoft.Extensions.Http.Resilience" VersionOverride="9.1.0" />
<PackageReference Include="Microsoft.Extensions.Logging" VersionOverride="9.0.7" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" />
<PackageReference Include="Microsoft.Extensions.Logging" VersionOverride="9.0.9" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" VersionOverride="9.0.9" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" />
<PackageReference Include="OpenTelemetry.Exporter.Console" />
<PackageReference Include="System.Linq.AsyncEnumerable" />
Expand Down
29 changes: 29 additions & 0 deletions dotnet/src/Agents/A2A/A2AAgentExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Copyright (c) Microsoft. All rights reserved.

using System.Diagnostics.CodeAnalysis;
using System.Text.Json;
using MAAI = Microsoft.Agents.AI;

namespace Microsoft.SemanticKernel.Agents.A2A;

/// <summary>
/// Exposes a Semantic Kernel Agent Framework <see cref="A2AAgent"/> as a Microsoft Agent Framework <see cref="MAAI.AIAgent"/>.
/// </summary>
public static class A2AAgentExtensions
{
/// <summary>
/// Exposes a Semantic Kernel Agent Framework <see cref="A2AAgent"/> as a Microsoft Agent Framework <see cref="MAAI.AIAgent"/>.
/// </summary>
/// <param name="a2aAgent">The Semantic Kernel <see cref="A2AAgent"/> to expose as a Microsoft Agent Framework <see cref="MAAI.AIAgent"/>.</param>
/// <returns>The Semantic Kernel Agent Framework <see cref="Agent"/> exposed as a Microsoft Agent Framework <see cref="MAAI.AIAgent"/></returns>
[Experimental("SKEXP0110")]
public static MAAI.AIAgent AsAIAgent(this A2AAgent a2aAgent)
=> a2aAgent.AsAIAgent(
() => new A2AAgentThread(a2aAgent.Client),
(json, options) =>
{
var agentId = JsonSerializer.Deserialize<string>(json);
return agentId is null ? new A2AAgentThread(a2aAgent.Client) : new A2AAgentThread(a2aAgent.Client, agentId);
},
(thread, options) => JsonSerializer.SerializeToElement((thread as A2AAgentThread)?.Id));
}
128 changes: 128 additions & 0 deletions dotnet/src/Agents/Abstractions/AIAgent/AIAgentAdapter.cs
Copy link
Member

@rogerbarreto rogerbarreto Oct 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit. (Internal)

For the name of this class I would do something in the same lines we did for the ChatCompletionService vs ChatClient.

i.e: SKAgentAIAgent, see ChatCompletionServiceChatClient.cs

Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
// Copyright (c) Microsoft. All rights reserved.

using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.AI;
using MAAI = Microsoft.Agents.AI;

namespace Microsoft.SemanticKernel.Agents;

/// <summary>
/// Exposes a Semantic Kernel Agent Framework <see cref="Agent"/> as a Microsoft Agent Framework <see cref="MAAI.AIAgent"/>.
/// </summary>
[Experimental("SKEXP0110")]
internal sealed class AIAgentAdapter : MAAI.AIAgent
{
private readonly Func<AgentThread> _threadFactory;
private readonly Func<JsonElement, JsonSerializerOptions?, AgentThread> _threadDeserializationFactory;
private readonly Func<AgentThread, JsonSerializerOptions?, JsonElement> _threadSerializer;

/// <summary>
/// Initializes a new instance of the <see cref="AIAgentAdapter"/> class.
/// </summary>
/// <param name="semanticKernelAgent">The Semantic Kernel <see cref="Agent"/> to expose as a Microsoft Agent Framework <see cref="MAAI.AIAgent"/>.</param>
/// <param name="threadFactory">A factory method to create the required <see cref="AgentThread"/> type to use with the agent.</param>
/// <param name="threadDeserializationFactory">A factory method to deserialize the required <see cref="AgentThread"/> type.</param>
/// <param name="threadSerializer">A method to serialize the <see cref="AgentThread"/> type.</param>
public AIAgentAdapter(
Agent semanticKernelAgent,
Func<AgentThread> threadFactory,
Func<JsonElement, JsonSerializerOptions?, AgentThread> threadDeserializationFactory,
Func<AgentThread, JsonSerializerOptions?, JsonElement> threadSerializer)
{
Throw.IfNull(semanticKernelAgent);
Throw.IfNull(threadFactory);
Throw.IfNull(threadDeserializationFactory);
Throw.IfNull(threadSerializer);

this.InnerAgent = semanticKernelAgent;
this._threadFactory = threadFactory;
this._threadDeserializationFactory = threadDeserializationFactory;
this._threadSerializer = threadSerializer;
}

/// <summary>
/// Gets the underlying Semantic Kernel Agent Framework <see cref="Agent"/>.
/// </summary>
public Agent InnerAgent { get; }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not fully sure if we need to expose the Agent, we could just recommend the GetService<Agent>() for this.

Suggested change
public Agent InnerAgent { get; }


/// <inheritdoc />
public override MAAI.AgentThread DeserializeThread(JsonElement serializedThread, JsonSerializerOptions? jsonSerializerOptions = null)
=> new AIAgentThreadAdapter(this._threadDeserializationFactory(serializedThread, jsonSerializerOptions), this._threadSerializer);

/// <inheritdoc />
public override MAAI.AgentThread GetNewThread() => new AIAgentThreadAdapter(this._threadFactory(), this._threadSerializer);

/// <inheritdoc />
public override async Task<MAAI.AgentRunResponse> RunAsync(IEnumerable<ChatMessage> messages, MAAI.AgentThread? thread = null, MAAI.AgentRunOptions? options = null, CancellationToken cancellationToken = default)
{
thread ??= this.GetNewThread();
if (thread is not AIAgentThreadAdapter typedThread)
{
throw new InvalidOperationException("The provided thread is not compatible with the agent. Only threads created by the agent can be used.");
}

List<ChatMessage> responseMessages = [];
var invokeOptions = new AgentInvokeOptions()
{
OnIntermediateMessage = (msg) =>
{
responseMessages.Add(msg.ToChatMessage());
return Task.CompletedTask;
}
};

AgentResponseItem<ChatMessageContent>? lastResponseItem = null;
ChatMessage? lastResponseMessage = null;
await foreach (var responseItem in this.InnerAgent.InvokeAsync(messages.Select(x => x.ToChatMessageContent()).ToList(), typedThread.InnerThread, invokeOptions, cancellationToken).ConfigureAwait(false))
{
lastResponseItem = responseItem;
lastResponseMessage = responseItem.Message.ToChatMessage();
responseMessages.Add(lastResponseMessage);
}

return new MAAI.AgentRunResponse(responseMessages)
{
AgentId = this.InnerAgent.Id,
RawRepresentation = lastResponseItem,
AdditionalProperties = lastResponseMessage?.AdditionalProperties,
CreatedAt = lastResponseMessage?.CreatedAt,
};
}

/// <inheritdoc />
public override async IAsyncEnumerable<MAAI.AgentRunResponseUpdate> RunStreamingAsync(
IEnumerable<ChatMessage> messages,
MAAI.AgentThread? thread = null,
MAAI.AgentRunOptions? options = null,
[EnumeratorCancellation] CancellationToken cancellationToken = default)
{
thread ??= this.GetNewThread();
if (thread is not AIAgentThreadAdapter typedThread)
{
throw new InvalidOperationException("The provided thread is not compatible with the agent. Only threads created by the agent can be used.");
}

await foreach (var responseItem in this.InnerAgent.InvokeAsync(messages.Select(x => x.ToChatMessageContent()).ToList(), typedThread.InnerThread, cancellationToken: cancellationToken).ConfigureAwait(false))
{
var chatMessage = responseItem.Message.ToChatMessage();

yield return new MAAI.AgentRunResponseUpdate
{
AgentId = this.InnerAgent.Id,
RawRepresentation = responseItem,
AdditionalProperties = chatMessage.AdditionalProperties,
MessageId = chatMessage.MessageId,
Role = chatMessage.Role,
CreatedAt = chatMessage.CreatedAt,
Contents = chatMessage.Contents
};
}
}
}
38 changes: 38 additions & 0 deletions dotnet/src/Agents/Abstractions/AIAgent/AIAgentThreadAdapter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright (c) Microsoft. All rights reserved.

using System;
using System.Diagnostics.CodeAnalysis;
using System.Text.Json;
using MAAI = Microsoft.Agents.AI;

namespace Microsoft.SemanticKernel.Agents;

[Experimental("SKEXP0110")]
internal sealed class AIAgentThreadAdapter : MAAI.AgentThread
{
private readonly Func<AgentThread, JsonSerializerOptions?, JsonElement> _threadSerializer;

internal AIAgentThreadAdapter(AgentThread thread, Func<AgentThread, JsonSerializerOptions?, JsonElement> threadSerializer)
{
Throw.IfNull(thread);
Throw.IfNull(threadSerializer);

this.InnerThread = thread;
this._threadSerializer = threadSerializer;
}

/// <summary>
/// Gets the underlying Semantic Kernel Agent Framework <see cref="AgentThread"/>.
/// </summary>
public AgentThread InnerThread { get; }
Comment on lines +24 to +27
Copy link
Member

@rogerbarreto rogerbarreto Oct 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The rationale here is a bit similar to where after the conversion we don't expose the converted type back as we assume the caller will have the reference already.

private readonly IChatCompletionService _chatCompletionService;

Point to the GetService usage.

Suggested change
/// <summary>
/// Gets the underlying Semantic Kernel Agent Framework <see cref="AgentThread"/>.
/// </summary>
public AgentThread InnerThread { get; }


/// <inheritdoc />
public override JsonElement Serialize(JsonSerializerOptions? jsonSerializerOptions = null)
=> this._threadSerializer(this.InnerThread, jsonSerializerOptions);

public override object? GetService(Type serviceType, object? serviceKey = null)
=> base.GetService(serviceType, serviceKey)
?? (serviceType == typeof(AgentThread) && serviceKey is null
? this.InnerThread
: null);
}
34 changes: 34 additions & 0 deletions dotnet/src/Agents/Abstractions/AgentExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Copyright (c) Microsoft. All rights reserved.

using System;
using System.Diagnostics.CodeAnalysis;
using System.Text.Json;
using MAAI = Microsoft.Agents.AI;

namespace Microsoft.SemanticKernel.Agents;

/// <summary>
/// Exposes a Semantic Kernel Agent Framework <see cref="Agent"/> as a Microsoft Agent Framework <see cref="MAAI.AIAgent"/>.
/// </summary>
public static class AgentExtensions
{
/// <summary>
/// Exposes a Semantic Kernel Agent Framework <see cref="Agent"/> as a Microsoft Agent Framework <see cref="MAAI.AIAgent"/>.
/// </summary>
/// <param name="semanticKernelAgent">The Semantic Kernel <see cref="Agent"/> to expose as a Microsoft Agent Framework <see cref="MAAI.AIAgent"/>.</param>
/// <param name="threadFactory">A factory method to create the required <see cref="AgentThread"/> type to use with the agent.</param>
/// <param name="threadDeserializationFactory">A factory method to deserialize the required <see cref="AgentThread"/> type.</param>
/// <param name="threadSerializer">A method to serialize the <see cref="AgentThread"/> type.</param>
/// <returns>The Semantic Kernel Agent Framework <see cref="Agent"/> exposed as a Microsoft Agent Framework <see cref="MAAI.AIAgent"/></returns>
[Experimental("SKEXP0110")]
public static MAAI.AIAgent AsAIAgent(
this Agent semanticKernelAgent,
Func<AgentThread> threadFactory,
Func<JsonElement, JsonSerializerOptions?, AgentThread> threadDeserializationFactory,
Func<AgentThread, JsonSerializerOptions?, JsonElement> threadSerializer)
=> new AIAgentAdapter(
semanticKernelAgent,
threadFactory,
threadDeserializationFactory,
threadSerializer);
}
4 changes: 3 additions & 1 deletion dotnet/src/Agents/Abstractions/Agents.Abstractions.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@
<AssemblyName>Microsoft.SemanticKernel.Agents.Abstractions</AssemblyName>
<RootNamespace>Microsoft.SemanticKernel.Agents</RootNamespace>
<TargetFrameworks>net8.0;netstandard2.0</TargetFrameworks>
<NoWarn>$(NoWarn)</NoWarn>
<NoWarn>$(NoWarn);NU5104</NoWarn>
Copy link
Member

@rogerbarreto rogerbarreto Oct 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be a strong breaking change for .Net Framework based customers as pipelines relying on GA releases will break to update a preview package.

We might need to rethink how bring this dependency...

Suggested change
<NoWarn>$(NoWarn);NU5104</NoWarn>
<NoWarn>$(NoWarn);</NoWarn>

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This error only surfaces on the package directly referencing the preview package. Anyone who references M.SK.Agents.Abstractions will not have this error for transitive references.
If this was not the case, I would have had to suppress this error for the core agents package as well, since it is also GA, and is referencing MAAI.A as a transitive reference.

<EnablePackageValidation>false</EnablePackageValidation>
</PropertyGroup>

<Import Project="$(RepoRoot)/dotnet/nuget/nuget-package.props" />
<Import Project="$(RepoRoot)/dotnet/src/InternalUtilities/meai/MEAIUtilities.props" />

<PropertyGroup>
<!-- NuGet Package Settings -->
Expand All @@ -27,6 +28,7 @@

<ItemGroup>
<PackageReference Include="System.Text.Json" />
<PackageReference Include="Microsoft.Agents.AI.Abstractions" />
</ItemGroup>

<ItemGroup>
Expand Down
29 changes: 29 additions & 0 deletions dotnet/src/Agents/AzureAI/AzureAIAgentExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Copyright (c) Microsoft. All rights reserved.

using System.Diagnostics.CodeAnalysis;
using System.Text.Json;
using MAAI = Microsoft.Agents.AI;

namespace Microsoft.SemanticKernel.Agents.AzureAI;

/// <summary>
/// Exposes a Semantic Kernel Agent Framework <see cref="AzureAIAgent"/> as a Microsoft Agent Framework <see cref="MAAI.AIAgent"/>.
/// </summary>
public static class AzureAIAgentExtensions
{
/// <summary>
/// Exposes a Semantic Kernel Agent Framework <see cref="AzureAIAgent"/> as a Microsoft Agent Framework <see cref="MAAI.AIAgent"/>.
/// </summary>
/// <param name="azureAIAgent">The Semantic Kernel <see cref="AzureAIAgent"/> to expose as a Microsoft Agent Framework <see cref="MAAI.AIAgent"/>.</param>
/// <returns>The Semantic Kernel Agent Framework <see cref="Agent"/> exposed as a Microsoft Agent Framework <see cref="MAAI.AIAgent"/></returns>
[Experimental("SKEXP0110")]
public static MAAI.AIAgent AsAIAgent(this AzureAIAgent azureAIAgent)
=> azureAIAgent.AsAIAgent(
() => new AzureAIAgentThread(azureAIAgent.Client),
(json, options) =>
{
var agentId = JsonSerializer.Deserialize<string>(json);
return agentId is null ? new AzureAIAgentThread(azureAIAgent.Client) : new AzureAIAgentThread(azureAIAgent.Client, agentId);
},
(thread, options) => JsonSerializer.SerializeToElement((thread as AzureAIAgentThread)?.Id));
}
19 changes: 19 additions & 0 deletions dotnet/src/Agents/Bedrock/Extensions/BedrockAgentExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
// Copyright (c) Microsoft. All rights reserved.

using System;
using System.Diagnostics.CodeAnalysis;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Amazon.BedrockAgent;
using Amazon.BedrockAgent.Model;
using Microsoft.Agents.AI;

namespace Microsoft.SemanticKernel.Agents.Bedrock;

Expand All @@ -13,6 +16,22 @@ namespace Microsoft.SemanticKernel.Agents.Bedrock;
/// </summary>
public static class BedrockAgentExtensions
{
/// <summary>
/// Exposes a Semantic Kernel Agent Framework <see cref="BedrockAgent"/> as a Microsoft Agent Framework <see cref="AIAgent"/>.
/// </summary>
/// <param name="bedrockAgent">The Semantic Kernel <see cref="BedrockAgent"/> to expose as a Microsoft Agent Framework <see cref="AIAgent"/>.</param>
/// <returns>The Semantic Kernel Agent Framework <see cref="Agent"/> exposed as a Microsoft Agent Framework <see cref="AIAgent"/></returns>
[Experimental("SKEXP0110")]
public static AIAgent AsAIAgent(this BedrockAgent bedrockAgent)
=> bedrockAgent.AsAIAgent(
() => new BedrockAgentThread(bedrockAgent.RuntimeClient),
(json, options) =>
{
var agentId = JsonSerializer.Deserialize<string>(json);
return agentId is null ? new BedrockAgentThread(bedrockAgent.RuntimeClient) : new BedrockAgentThread(bedrockAgent.RuntimeClient, agentId);
},
(thread, options) => JsonSerializer.SerializeToElement((thread as BedrockAgentThread)?.Id));

/// <summary>
/// Creates an agent.
/// </summary>
Expand Down
29 changes: 29 additions & 0 deletions dotnet/src/Agents/Copilot/CopilotStudioAgentExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Copyright (c) Microsoft. All rights reserved.

using System.Diagnostics.CodeAnalysis;
using System.Text.Json;
using MAAI = Microsoft.Agents.AI;

namespace Microsoft.SemanticKernel.Agents.Copilot;

/// <summary>
/// Exposes a Semantic Kernel Agent Framework <see cref="CopilotStudioAgent"/> as a Microsoft Agent Framework <see cref="MAAI.AIAgent"/>.
/// </summary>
public static class CopilotStudioAgentExtensions
{
/// <summary>
/// Exposes a Semantic Kernel Agent Framework <see cref="CopilotStudioAgent"/> as a Microsoft Agent Framework <see cref="MAAI.AIAgent"/>.
/// </summary>
/// <param name="copilotStudioAgent">The Semantic Kernel <see cref="CopilotStudioAgent"/> to expose as a Microsoft Agent Framework <see cref="MAAI.AIAgent"/>.</param>
/// <returns>The Semantic Kernel Agent Framework <see cref="Agent"/> exposed as a Microsoft Agent Framework <see cref="MAAI.AIAgent"/></returns>
[Experimental("SKEXP0110")]
public static MAAI.AIAgent AsAIAgent(this CopilotStudioAgent copilotStudioAgent)
=> copilotStudioAgent.AsAIAgent(
() => new CopilotStudioAgentThread(copilotStudioAgent.Client),
(json, options) =>
{
var agentId = JsonSerializer.Deserialize<string>(json);
return agentId is null ? new CopilotStudioAgentThread(copilotStudioAgent.Client) : new CopilotStudioAgentThread(copilotStudioAgent.Client, agentId);
},
(thread, options) => JsonSerializer.SerializeToElement((thread as CopilotStudioAgentThread)?.Id));
}
Loading
Loading