Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

.Net: Change Agents.Abstractions to depend on SemanticKernel.Abstractions instead of SemanticKernel.Core #10574

Merged
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
8 changes: 5 additions & 3 deletions dotnet/samples/Concepts/Agents/ChatCompletion_Templating.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,9 @@ await InvokeChatCompletionAgentWithTemplateAsync(
"""
Write a one verse poem on the requested topic in the style of {{$style}}.
Always state the requested style of the poem.
""");
""",
PromptTemplateConfig.SemanticKernelTemplateFormat,
new KernelPromptTemplateFactory());
}

[Fact]
Expand Down Expand Up @@ -79,8 +81,8 @@ Always state the requested style of the poem.

private async Task InvokeChatCompletionAgentWithTemplateAsync(
string instructionTemplate,
string? templateFormat = null,
IPromptTemplateFactory? templateFactory = null)
string templateFormat,
IPromptTemplateFactory templateFactory)
{
// Define the agent
PromptTemplateConfig templateConfig =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,9 @@ await InvokeAssistantAgentWithTemplateAsync(
"""
Write a one verse poem on the requested topic in the styles of {{$style}}.
Always state the requested style of the poem.
""");
""",
PromptTemplateConfig.SemanticKernelTemplateFormat,
new KernelPromptTemplateFactory());
}

[Fact]
Expand Down
3 changes: 2 additions & 1 deletion dotnet/samples/GettingStartedWithAgents/Step01_Agent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,11 @@ public async Task UseTemplateForChatCompletionAgentAsync()
// Define the agent
string generateStoryYaml = EmbeddedResource.Read("GenerateStory.yaml");
PromptTemplateConfig templateConfig = KernelFunctionYaml.ToPromptTemplateConfig(generateStoryYaml);
KernelPromptTemplateFactory templateFactory = new();

// Instructions, Name and Description properties defined via the config.
ChatCompletionAgent agent =
new(templateConfig)
new(templateConfig, templateFactory)
{
Kernel = this.CreateKernelWithChatCompletion(),
Arguments =
Expand Down
3 changes: 2 additions & 1 deletion dotnet/samples/GettingStartedWithAgents/Step02_Plugins.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,12 @@ public async Task UseChatCompletionWithTemplateExecutionSettingsAsync()
// Read the template resource
string autoInvokeYaml = EmbeddedResource.Read("AutoInvokeTools.yaml");
PromptTemplateConfig templateConfig = KernelFunctionYaml.ToPromptTemplateConfig(autoInvokeYaml);
KernelPromptTemplateFactory templateFactory = new();

// Define the agent:
// Execution-settings with auto-invocation of plugins defined via the config.
ChatCompletionAgent agent =
new(templateConfig)
new(templateConfig, templateFactory)
{
Kernel = this.CreateKernelWithChatCompletion()
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\SemanticKernel.Core\SemanticKernel.Core.csproj" />
<ProjectReference Include="..\..\SemanticKernel.Abstractions\SemanticKernel.Abstractions.csproj" />
</ItemGroup>

<ItemGroup>
Expand Down
20 changes: 6 additions & 14 deletions dotnet/src/Agents/Abstractions/KernelAgent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,6 @@ public abstract class KernelAgent : Agent
/// <summary>
/// Gets the instructions for the agent (optional).
/// </summary>
/// <remarks>
/// Instructions can be formatted in "semantic-kernel" template format (<see cref="KernelPromptTemplateFactory"/>).
/// </remarks>
public string? Instructions { get; init; }

/// <summary>
Expand All @@ -39,7 +36,7 @@ public abstract class KernelAgent : Agent
/// <summary>
/// Gets or sets a prompt template based on the agent instructions.
/// </summary>
public IPromptTemplate? Template { get; protected set; }
protected IPromptTemplate? Template { get; set; }

/// <inheritdoc/>
protected override ILoggerFactory ActiveLoggerFactory => this.LoggerFactory ?? this.Kernel.LoggerFactory;
Expand All @@ -53,19 +50,14 @@ public abstract class KernelAgent : Agent
/// <returns>The formatted system instructions for the agent.</returns>
protected async Task<string?> FormatInstructionsAsync(Kernel kernel, KernelArguments? arguments, CancellationToken cancellationToken)
{
// If <see cref="Template"/> is not set, default instructions may be treated as "semantic-kernel" template.
if (this.Template == null)
// Use the provided template as the instructions
if (this.Template is not null)
{
if (string.IsNullOrWhiteSpace(this.Instructions))
{
return null;
}

KernelPromptTemplateFactory templateFactory = new(this.LoggerFactory);
this.Template = templateFactory.Create(new PromptTemplateConfig(this.Instructions!));
return await this.Template.RenderAsync(kernel, arguments, cancellationToken).ConfigureAwait(false);
}

return await this.Template.RenderAsync(kernel, arguments, cancellationToken).ConfigureAwait(false);
// Use the instructions as-is
return this.Instructions;
}

/// <summary>
Expand Down
10 changes: 6 additions & 4 deletions dotnet/src/Agents/AzureAI/AzureAIAgent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,23 +54,25 @@ public static class Tools
/// </summary>
/// <param name="model">The agent model definition.</param>
/// <param name="client">An <see cref="AgentsClient"/> instance.</param>
/// <param name="templateConfig">The prompt template configuration.</param>
/// <param name="templateFactory">An optional template factory.</param>
public AzureAIAgent(
Azure.AI.Projects.Agent model,
AgentsClient client,
PromptTemplateConfig? templateConfig = null,
IPromptTemplateFactory? templateFactory = null)
{
this.Client = client;
this.Definition = model;
this.Description = this.Definition.Description;
this.Id = this.Definition.Id;
this.Name = this.Definition.Name;
this.Instructions = this.Definition.Instructions;
this.Instructions = templateConfig?.Template ?? this.Definition.Instructions;

if (templateFactory != null)
if (templateConfig is not null)
{
PromptTemplateConfig templateConfig = new(this.Instructions);
this.Template = templateFactory.Create(templateConfig);
this.Template = templateFactory?.Create(templateConfig)
?? throw new KernelException($"Invalid prompt template factory {templateFactory} for format {templateConfig.TemplateFormat}");
}
}

Expand Down
1 change: 1 addition & 0 deletions dotnet/src/Agents/Core/Agents.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@

<ItemGroup>
<ProjectReference Include="..\Abstractions\Agents.Abstractions.csproj" />
<ProjectReference Include="..\..\SemanticKernel.Core\SemanticKernel.Core.csproj" />
</ItemGroup>

<ItemGroup>
Expand Down
13 changes: 4 additions & 9 deletions dotnet/src/Agents/Core/ChatCompletionAgent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,19 +32,16 @@ public ChatCompletionAgent() { }
/// a <see cref="PromptTemplateConfig"/>.
/// </summary>
/// <param name="templateConfig">The prompt template configuration.</param>
/// <param name="templateFactory">An optional factory to produce the <see cref="IPromptTemplate"/> for the agent.</param>
/// <remarks>
/// When a template factory argument isn't provided, the default <see cref="KernelPromptTemplateFactory"/> is used.
/// </remarks>
/// <param name="templateFactory">The prompt template factory used to produce the <see cref="IPromptTemplate"/> for the agent.</param>
public ChatCompletionAgent(
PromptTemplateConfig templateConfig,
IPromptTemplateFactory? templateFactory = null)
IPromptTemplateFactory templateFactory)
{
this.Name = templateConfig.Name;
this.Description = templateConfig.Description;
this.Instructions = templateConfig.Template;
this.Arguments = new(templateConfig.ExecutionSettings.Values);
this.Template = templateFactory?.Create(templateConfig);
this.Template = templateFactory.Create(templateConfig);
}

/// <summary>
Expand Down Expand Up @@ -99,12 +96,10 @@ protected override Task<AgentChannel> RestoreChannelAsync(string channelState, C

internal static (IChatCompletionService service, PromptExecutionSettings? executionSettings) GetChatCompletionService(Kernel kernel, KernelArguments? arguments)
{
// Need to provide a KernelFunction to the service selector as a container for the execution-settings.
KernelFunction nullPrompt = KernelFunctionFactory.CreateFromPrompt("placeholder", arguments?.ExecutionSettings?.Values);
(IChatCompletionService chatCompletionService, PromptExecutionSettings? executionSettings) =
kernel.ServiceSelector.SelectAIService<IChatCompletionService>(
kernel,
nullPrompt,
arguments?.ExecutionSettings,
arguments ?? []);

return (chatCompletionService, executionSettings);
Expand Down
12 changes: 7 additions & 5 deletions dotnet/src/Agents/OpenAI/OpenAIAssistantAgent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,10 @@ public OpenAIAssistantAgent(
this.Name = this.Definition.Name;
this.Instructions = templateConfig?.Template ?? this.Definition.Instructions;

if (templateConfig != null)
if (templateConfig is not null)
{
this.Template = templateFactory?.Create(templateConfig);
this.Template = templateFactory?.Create(templateConfig)
?? throw new KernelException($"Invalid prompt template factory {templateFactory} for format {templateConfig.TemplateFormat}");
}

if (plugins != null)
Expand Down Expand Up @@ -101,7 +102,7 @@ public OpenAIAssistantAgent(
/// <param name="kernel">The <see cref="Kernel"/> containing services, plugins, and other state for use throughout the operation.</param>
/// <param name="defaultArguments">Required arguments that provide default template parameters, including any <see cref="PromptExecutionSettings"/>.</param>
/// <param name="templateConfig">The prompt template configuration.</param>
/// <param name="templateFactory">An optional factory to produce the <see cref="IPromptTemplate"/> for the agent.</param>
/// <param name="templateFactory">An prompt template factory to produce the <see cref="IPromptTemplate"/> for the agent.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>An <see cref="OpenAIAssistantAgent"/> instance.</returns>
[Obsolete("Use the OpenAI.Assistants.AssistantClient to create an assistant (CreateAssistantFromTemplateAsync).")]
Expand All @@ -111,7 +112,7 @@ public static async Task<OpenAIAssistantAgent> CreateFromTemplateAsync(
Kernel kernel,
KernelArguments defaultArguments,
PromptTemplateConfig templateConfig,
IPromptTemplateFactory? templateFactory = null,
IPromptTemplateFactory templateFactory,
CancellationToken cancellationToken = default)
{
// Validate input
Expand All @@ -120,9 +121,10 @@ public static async Task<OpenAIAssistantAgent> CreateFromTemplateAsync(
Verify.NotNull(clientProvider, nameof(clientProvider));
Verify.NotNull(capabilities, nameof(capabilities));
Verify.NotNull(templateConfig, nameof(templateConfig));
Verify.NotNull(templateFactory, nameof(templateFactory));

// Ensure template is valid (avoid failure after posting assistant creation)
IPromptTemplate? template = templateFactory?.Create(templateConfig);
IPromptTemplate template = templateFactory.Create(templateConfig);

// Create the client
AssistantClient client = clientProvider.Client.GetAssistantClient();
Expand Down
1 change: 1 addition & 0 deletions dotnet/src/Agents/UnitTests/Agents.UnitTests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\Extensions\PromptTemplates.Handlebars\PromptTemplates.Handlebars.csproj" />
<ProjectReference Include="..\Abstractions\Agents.Abstractions.csproj" />
<ProjectReference Include="..\..\Connectors\Connectors.AzureOpenAI\Connectors.AzureOpenAI.csproj" />
<ProjectReference Include="..\Core\Agents.Core.csproj" />
Expand Down
34 changes: 28 additions & 6 deletions dotnet/src/Agents/UnitTests/Core/ChatCompletionAgentTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public void VerifyChatCompletionAgentDefinition()
[Fact]
public void VerifyChatCompletionAgentTemplate()
{
PromptTemplateConfig config =
PromptTemplateConfig promptConfig =
new()
{
Name = "TestName",
Expand All @@ -73,16 +73,38 @@ public void VerifyChatCompletionAgentTemplate()
},
}
};
KernelPromptTemplateFactory templateFactory = new();

// Arrange
ChatCompletionAgent agent = new(config);
ChatCompletionAgent agent = new(promptConfig, templateFactory);

// Assert
Assert.NotNull(agent.Id);
Assert.Equal(config.Template, agent.Instructions);
Assert.Equal(config.Description, agent.Description);
Assert.Equal(config.Name, agent.Name);
Assert.Equal(config.ExecutionSettings, agent.Arguments.ExecutionSettings);
Assert.Equal(promptConfig.Template, agent.Instructions);
Assert.Equal(promptConfig.Description, agent.Description);
Assert.Equal(promptConfig.Name, agent.Name);
Assert.Equal(promptConfig.ExecutionSettings, agent.Arguments.ExecutionSettings);
}

/// <summary>
/// Verify throws <see cref="KernelException"/> when invalid <see cref="IPromptTemplateFactory"/> is provided.
/// </summary>
[Fact]
public void VerifyThrowsForInvalidTemplateFactory()
{
// Arrange
PromptTemplateConfig promptConfig =
new()
{
Name = "TestName",
Description = "TestDescription",
Template = "TestInstructions",
TemplateFormat = "handlebars",
};
KernelPromptTemplateFactory templateFactory = new();

// Act and Assert
Assert.Throws<KernelException>(() => new ChatCompletionAgent(promptConfig, templateFactory));
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using Microsoft.SemanticKernel.Agents;
using Microsoft.SemanticKernel.Agents.OpenAI;
using Microsoft.SemanticKernel.ChatCompletion;
using Microsoft.SemanticKernel.PromptTemplates.Handlebars;
using OpenAI.Assistants;
using Xunit;

Expand Down Expand Up @@ -76,11 +77,9 @@ public async Task VerifyOpenAIAssistantAgentCreationDefaultTemplateAsync()

OpenAIAssistantCapabilities capabilities = new("testmodel");

// Act and Assert
await this.VerifyAgentTemplateAsync(capabilities, templateConfig);

// Act and Assert
await this.VerifyAgentTemplateAsync(capabilities, templateConfig, new KernelPromptTemplateFactory());
await Assert.ThrowsAsync<KernelException>(async () => await this.VerifyAgentTemplateAsync(capabilities, templateConfig, new HandlebarsPromptTemplateFactory()));
}

/// <summary>
Expand Down Expand Up @@ -734,7 +733,7 @@ await OpenAIAssistantAgent.CreateAsync(
private async Task VerifyAgentTemplateAsync(
OpenAIAssistantCapabilities capabilities,
PromptTemplateConfig templateConfig,
IPromptTemplateFactory? templateFactory = null)
IPromptTemplateFactory templateFactory)
{
this.SetupResponse(HttpStatusCode.OK, capabilities, templateConfig);

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

using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace Microsoft.SemanticKernel;

/// <summary>
/// Represents a kernel function that performs no operation.
/// </summary>
[RequiresUnreferencedCode("Uses reflection to handle various aspects of the function creation and invocation, making it incompatible with AOT scenarios.")]
[RequiresDynamicCode("Uses reflection to handle various aspects of the function creation and invocation, making it incompatible with AOT scenarios.")]
internal sealed class KernelFunctionNoop : KernelFunction
{
/// <summary>
/// Creates a new instance of the <see cref="KernelFunctionNoop"/> class.
/// </summary>
/// <param name="executionSettings">Option: Prompt execution settings.</param>
internal KernelFunctionNoop(IReadOnlyDictionary<string, PromptExecutionSettings>? executionSettings) :
base($"Function_{Guid.NewGuid():N}", string.Empty, [], null, executionSettings?.ToDictionary(static kv => kv.Key, static kv => kv.Value))
{
}

/// <inheritdoc/>
public override KernelFunction Clone(string pluginName)
{
Dictionary<string, PromptExecutionSettings>? executionSettings = this.ExecutionSettings?.ToDictionary(kv => kv.Key, kv => kv.Value);
return new KernelFunctionNoop(executionSettings);
}

/// <inheritdoc/>
protected override ValueTask<FunctionResult> InvokeCoreAsync(Kernel kernel, KernelArguments arguments, CancellationToken cancellationToken)
{
return new(new FunctionResult(this));
}

/// <inheritdoc/>
protected override IAsyncEnumerable<TResult> InvokeStreamingCoreAsync<TResult>(Kernel kernel, KernelArguments arguments, CancellationToken cancellationToken)
{
return AsyncEnumerable.Empty<TResult>();
}
}
Loading
Loading