mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
.NET: Declarative Agents (#1301)
* AgentFactory abstractions and ChatClient implementation * Add a getitng started sample * Update to latest M.B.OM * Add some additional samples * Work in progress * Merge latest from main * Start to add support for using different kinds of connections * Remove IsSupported * Remove IsSupported * Refactor code to create clients to support DI * Add some unit tests * Update based on the latest code review feedback * Add support for OOB tools when using persistent agent sdk * Fix sample naming * Fix error based on latest MEAI * Update M.B.OM package to latest * Update to the latest M.B.OM release * Remove some obsolete helper methods * Update to the latest M.B.OM version * Fix broken unit test * Update MCP sample * Bump to latest M.B.OM release * Update to latest M.B.OM release * Update to latest M.B.OM release * Switch to using ExternalModel * Update to latest M.B.OM * Resolve merge conflicts * All tests pass * All tests pass * Start to clean up the code * Start to clean up the code * More clean up * More clean up * More clean up * Fix apiType checks * Run dotnet format * Fix typo * Address code review feedback * Add all properties for MCP tool * Address code review feedback * Address code review feedback * Fix merge * Undo warnings * Undo test change * More copilot feedback * Make class sealed * Address additional core review feedback --------- Co-authored-by: Mark Wallace <markwallace@microsoft.com>
This commit is contained in:
committed by
GitHub
Unverified
parent
105dc82c39
commit
aaa91954c5
+95
@@ -0,0 +1,95 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using Azure.Identity;
|
||||
using Microsoft.Extensions.AI;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Microsoft.Agents.AI.Declarative.IntegrationTests;
|
||||
|
||||
/// <summary>
|
||||
/// Tests for declarative agents created using <see cref="AggregatorAgentFactory"/>.
|
||||
/// </summary>
|
||||
public sealed class AzureOpenAIDeclarativeAgentTests(ITestOutputHelper output) : BaseIntegrationTest(output)
|
||||
{
|
||||
[Fact]
|
||||
public async Task CanCreateAndRunChatAgentAsync()
|
||||
{
|
||||
// Example function tool that can be used by the agent.
|
||||
[Description("Get the weather for a given location.")]
|
||||
static string GetWeather(
|
||||
[Description("The city and state, e.g. San Francisco, CA")] string location,
|
||||
[Description("The unit of temperature. Possible values are 'celsius' and 'fahrenheit'.")] string unit)
|
||||
=> $"The weather in {location} is cloudy with a high of {(unit.Equals("celsius", StringComparison.Ordinal) ? "15°C" : "59°F")}.";
|
||||
|
||||
// Arrange
|
||||
var endpointUri = new Uri(this.FoundryConfiguration.Endpoint);
|
||||
var tokenCredential = new AzureCliCredential();
|
||||
var agentFactory = new AggregatorAgentFactory(
|
||||
[
|
||||
new OpenAIChatAgentFactory(endpointUri, tokenCredential),
|
||||
new OpenAIResponseAgentFactory(endpointUri, tokenCredential),
|
||||
new OpenAIAssistantAgentFactory(endpointUri, tokenCredential)
|
||||
]);
|
||||
var agentYaml = File.ReadAllText("../../../../../../agent-samples/azure/AzureOpenAIChat.yaml");
|
||||
agentYaml = agentYaml.Replace("=Env.AZURE_OPENAI_DEPLOYMENT_NAME", this.FoundryConfiguration.DeploymentName);
|
||||
|
||||
// Create agent run options
|
||||
var options = new ChatClientAgentRunOptions(new()
|
||||
{
|
||||
Tools = [AIFunctionFactory.Create(GetWeather, name: nameof(GetWeather))]
|
||||
});
|
||||
|
||||
// Act
|
||||
var agent = await agentFactory.CreateFromYamlAsync(agentYaml);
|
||||
var response = await agent!.RunAsync("What is the weather in Cambridge, MA in °C?", options: options);
|
||||
this.Output.WriteLine($"Agent Response: {response.Text}");
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(agent);
|
||||
Assert.NotNull(response);
|
||||
Assert.NotEmpty(response.Text);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CanCreateAndRunResponsesAgentAsync()
|
||||
{
|
||||
// Example function tool that can be used by the agent.
|
||||
[Description("Get the weather for a given location.")]
|
||||
static string GetWeather(
|
||||
[Description("The city and state, e.g. San Francisco, CA")] string location,
|
||||
[Description("The unit of temperature. Possible values are 'celsius' and 'fahrenheit'.")] string unit)
|
||||
=> $"The weather in {location} is cloudy with a high of {(unit.Equals("celsius", StringComparison.Ordinal) ? "15°C" : "59°F")}.";
|
||||
|
||||
// Arrange
|
||||
var endpointUri = new Uri(this.FoundryConfiguration.Endpoint);
|
||||
var tokenCredential = new AzureCliCredential();
|
||||
var agentFactory = new AggregatorAgentFactory(
|
||||
[
|
||||
new OpenAIChatAgentFactory(endpointUri, tokenCredential),
|
||||
new OpenAIResponseAgentFactory(endpointUri, tokenCredential),
|
||||
new OpenAIAssistantAgentFactory(endpointUri, tokenCredential)
|
||||
]);
|
||||
var agentYaml = File.ReadAllText("../../../../../../agent-samples/azure/AzureOpenAIResponses.yaml");
|
||||
agentYaml = agentYaml.Replace("=Env.AZURE_OPENAI_DEPLOYMENT_NAME", this.FoundryConfiguration.DeploymentName);
|
||||
|
||||
// Create agent run options
|
||||
var options = new ChatClientAgentRunOptions(new()
|
||||
{
|
||||
Tools = [AIFunctionFactory.Create(GetWeather, name: nameof(GetWeather))]
|
||||
});
|
||||
|
||||
// Act
|
||||
var agent = await agentFactory.CreateFromYamlAsync(agentYaml);
|
||||
var response = await agent!.RunAsync("What is the weather in Cambridge, MA in °C?", options: options);
|
||||
this.Output.WriteLine($"Agent Response: {response.Text}");
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(agent);
|
||||
Assert.NotNull(response);
|
||||
Assert.NotEmpty(response.Text);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Shared.IntegrationTests;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Microsoft.Agents.AI.Declarative.IntegrationTests;
|
||||
|
||||
/// <summary>
|
||||
/// Base class for integration tests.
|
||||
/// </summary>
|
||||
public abstract class BaseIntegrationTest : IDisposable
|
||||
{
|
||||
private IConfigurationRoot? _configuration;
|
||||
private AzureAIConfiguration? _foundryConfiguration;
|
||||
private OpenAIConfiguration? _openAIConfiguration;
|
||||
private FoundryProjectConfiguration? _foundryProjectConfiguration;
|
||||
|
||||
protected IConfigurationRoot Configuration => this._configuration ??= InitializeConfig();
|
||||
|
||||
internal AzureAIConfiguration FoundryConfiguration
|
||||
{
|
||||
get
|
||||
{
|
||||
this._foundryConfiguration ??= this.Configuration.GetSection("AzureAI").Get<AzureAIConfiguration>();
|
||||
Assert.NotNull(this._foundryConfiguration);
|
||||
return this._foundryConfiguration;
|
||||
}
|
||||
}
|
||||
|
||||
internal OpenAIConfiguration OpenAIConfiguration
|
||||
{
|
||||
get
|
||||
{
|
||||
this._openAIConfiguration ??= this.Configuration.GetSection("OpenAI").Get<OpenAIConfiguration>();
|
||||
Assert.NotNull(this._openAIConfiguration);
|
||||
return this._openAIConfiguration;
|
||||
}
|
||||
}
|
||||
|
||||
internal FoundryProjectConfiguration FoundryProjectConfiguration
|
||||
{
|
||||
get
|
||||
{
|
||||
this._foundryProjectConfiguration ??= this.Configuration.GetSection("FoundryProject").Get<FoundryProjectConfiguration>();
|
||||
Assert.NotNull(this._foundryProjectConfiguration);
|
||||
return this._foundryProjectConfiguration;
|
||||
}
|
||||
}
|
||||
|
||||
public TestOutputAdapter Output { get; }
|
||||
|
||||
protected BaseIntegrationTest(ITestOutputHelper output)
|
||||
{
|
||||
this.Output = new TestOutputAdapter(output);
|
||||
Console.SetOut(this.Output);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
this.Dispose(isDisposing: true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool isDisposing)
|
||||
{
|
||||
if (isDisposing)
|
||||
{
|
||||
this.Output.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private static IConfigurationRoot InitializeConfig() =>
|
||||
new ConfigurationBuilder()
|
||||
.AddJsonFile("appsettings.Development.json", true)
|
||||
.AddEnvironmentVariables()
|
||||
.AddUserSecrets(Assembly.GetExecutingAssembly())
|
||||
.Build();
|
||||
}
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using Azure.AI.OpenAI;
|
||||
using Azure.Identity;
|
||||
using Microsoft.Extensions.AI;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Microsoft.Agents.AI.Declarative.IntegrationTests;
|
||||
|
||||
/// <summary>
|
||||
/// Tests for declarative agents created using <see cref="ChatClientAgentFactory"/>.
|
||||
/// </summary>
|
||||
public sealed class ChatClientDeclarativeAgentTests(ITestOutputHelper output) : BaseIntegrationTest(output)
|
||||
{
|
||||
[Fact]
|
||||
public async Task CanCreateAndRunAssistantAgentAsync()
|
||||
{
|
||||
// Arrange
|
||||
var chatClient = this.CreateIChatClient();
|
||||
var agentFactory = new ChatClientAgentFactory(chatClient);
|
||||
var agentYaml = File.ReadAllText("../../../../../../agent-samples/chatclient/Assistant.yaml");
|
||||
|
||||
// Act
|
||||
var agent = await agentFactory.CreateFromYamlAsync(agentYaml);
|
||||
var response = await agent!.RunAsync("Tell me a joke about a pirate in Italian.");
|
||||
this.Output.WriteLine($"Agent Response: {response.Text}");
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(agent);
|
||||
Assert.NotNull(response);
|
||||
Assert.NotEmpty(response.Text);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CanCreateAndRunGetWeatherAgentAsync()
|
||||
{
|
||||
// Example function tool that can be used by the agent.
|
||||
[Description("Get the weather for a given location.")]
|
||||
static string GetWeather(
|
||||
[Description("The city and state, e.g. San Francisco, CA")] string location,
|
||||
[Description("The unit of temperature. Possible values are 'celsius' and 'fahrenheit'.")] string unit)
|
||||
=> $"The weather in {location} is cloudy with a high of {(unit.Equals("celsius", StringComparison.Ordinal) ? "15°C" : "59°F")}.";
|
||||
|
||||
// Arrange
|
||||
var chatClient = this.CreateIChatClient();
|
||||
var agentFactory = new ChatClientAgentFactory(chatClient, [AIFunctionFactory.Create(GetWeather, "GetWeather")]);
|
||||
var agentYaml = File.ReadAllText("../../../../../../agent-samples/chatclient/GetWeather.yaml");
|
||||
|
||||
// Act
|
||||
var agent = await agentFactory.CreateFromYamlAsync(agentYaml);
|
||||
var response = await agent!.RunAsync("What is the weather in Cambridge, MA in °C?");
|
||||
this.Output.WriteLine($"Agent Response: {response.Text}");
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(agent);
|
||||
Assert.NotNull(response);
|
||||
Assert.NotEmpty(response.Text);
|
||||
}
|
||||
|
||||
private IChatClient CreateIChatClient()
|
||||
{
|
||||
var endpoint = this.FoundryConfiguration.Endpoint;
|
||||
var deploymentName = this.FoundryConfiguration.DeploymentName;
|
||||
|
||||
// Create the chat client
|
||||
return new AzureOpenAIClient(
|
||||
new Uri(endpoint),
|
||||
new AzureCliCredential())
|
||||
.GetChatClient(deploymentName)
|
||||
.AsIChatClient();
|
||||
}
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using Azure.Identity;
|
||||
using Microsoft.Extensions.AI;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Microsoft.Agents.AI.Declarative.IntegrationTests;
|
||||
|
||||
/// <summary>
|
||||
/// Tests for declarative agents created using <see cref="FoundryPersistentAgentFactory"/>.
|
||||
/// </summary>
|
||||
public sealed class FoundryDeclarativeAgentTests(ITestOutputHelper output) : BaseIntegrationTest(output)
|
||||
{
|
||||
[Fact]
|
||||
public async Task CanCreateAndRunPersistentAgentAsync()
|
||||
{
|
||||
// Example function tool that can be used by the agent.
|
||||
[Description("Get the weather for a given location.")]
|
||||
static string GetWeather(
|
||||
[Description("The city and state, e.g. San Francisco, CA")] string location,
|
||||
[Description("The unit of temperature. Possible values are 'celsius' and 'fahrenheit'.")] string unit)
|
||||
=> $"The weather in {location} is cloudy with a high of {(unit.Equals("celsius", StringComparison.Ordinal) ? "15°C" : "59°F")}.";
|
||||
|
||||
// Arrange
|
||||
var agentFactory = new FoundryPersistentAgentFactory(new AzureCliCredential());
|
||||
var agentYaml = File.ReadAllText("../../../../../../agent-samples/foundry/PersistentAgent.yaml");
|
||||
agentYaml = agentYaml.Replace("=Env.AZURE_FOUNDRY_PROJECT_ENDPOINT", this.FoundryProjectConfiguration.Endpoint);
|
||||
agentYaml = agentYaml.Replace("=Env.AZURE_FOUNDRY_PROJECT_MODEL_ID", this.FoundryProjectConfiguration.ModelId);
|
||||
|
||||
// Create agent run options
|
||||
var options = new ChatClientAgentRunOptions(new()
|
||||
{
|
||||
Tools = [AIFunctionFactory.Create(GetWeather, name: nameof(GetWeather))]
|
||||
});
|
||||
|
||||
// Act
|
||||
var agent = await agentFactory.CreateFromYamlAsync(agentYaml);
|
||||
var response = await agent!.RunAsync("What is the weather in Cambridge, MA in °C?", options: options);
|
||||
this.Output.WriteLine($"Agent Response: {response.Text}");
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(agent);
|
||||
Assert.NotNull(response);
|
||||
Assert.NotEmpty(response.Text);
|
||||
}
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>$(ProjectsTargetFrameworks)</TargetFrameworks>
|
||||
<TargetFrameworks Condition="'$(Configuration)' == 'Debug'">$(ProjectsDebugTargetFrameworks)</TargetFrameworks>
|
||||
<InjectSharedIntegrationTestCode>True</InjectSharedIntegrationTestCode>
|
||||
<InjectSharedThrow>true</InjectSharedThrow>
|
||||
<NoWarn>$(NoWarn);CS1591</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<InjectSharedIntegrationTestCode>true</InjectSharedIntegrationTestCode>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="FluentAssertions" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" />
|
||||
<PackageReference Include="System.Linq.Async" />
|
||||
<PackageReference Include="Azure.AI.OpenAI" />
|
||||
<PackageReference Include="Azure.Identity" />
|
||||
<PackageReference Include="Microsoft.Extensions.AI.OpenAI" />
|
||||
<PackageReference Include="Microsoft.Bot.ObjectModel" />
|
||||
<PackageReference Include="Microsoft.Bot.ObjectModel.Json" />
|
||||
<PackageReference Include="Microsoft.Bot.ObjectModel.PowerFx" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\Microsoft.Agents.AI.Declarative.AzureAI\Microsoft.Agents.AI.Declarative.AzureAI.csproj" />
|
||||
<ProjectReference Include="..\..\src\Microsoft.Agents.AI.Declarative\Microsoft.Agents.AI.Declarative.csproj" />
|
||||
<ProjectReference Include="..\..\src\Microsoft.Agents.AI.OpenAI\Microsoft.Agents.AI.OpenAI.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.AI;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Microsoft.Agents.AI.Declarative.IntegrationTests;
|
||||
|
||||
/// <summary>
|
||||
/// Tests for declarative agents created using <see cref="AggregatorAgentFactory"/>.
|
||||
/// </summary>
|
||||
public sealed class OpenAIDeclarativeAgentTests(ITestOutputHelper output) : BaseIntegrationTest(output)
|
||||
{
|
||||
[Fact]
|
||||
public async Task CanCreateAndRunChatAgentAsync()
|
||||
{
|
||||
// Example function tool that can be used by the agent.
|
||||
[Description("Get the weather for a given location.")]
|
||||
static string GetWeather(
|
||||
[Description("The city and state, e.g. San Francisco, CA")] string location,
|
||||
[Description("The unit of temperature. Possible values are 'celsius' and 'fahrenheit'.")] string unit)
|
||||
=> $"The weather in {location} is cloudy with a high of {(unit.Equals("celsius", StringComparison.Ordinal) ? "15°C" : "59°F")}.";
|
||||
|
||||
// Arrange
|
||||
var agentFactory = new AggregatorAgentFactory(
|
||||
[
|
||||
new OpenAIChatAgentFactory(),
|
||||
new OpenAIResponseAgentFactory(),
|
||||
new OpenAIAssistantAgentFactory()
|
||||
]);
|
||||
var agentYaml = File.ReadAllText("../../../../../../agent-samples/openai/OpenAIChat.yaml");
|
||||
agentYaml = agentYaml.Replace("=Env.OPENAI_APIKEY", this.OpenAIConfiguration.ApiKey);
|
||||
agentYaml = agentYaml.Replace("=Env.OPENAI_MODEL", this.OpenAIConfiguration.ChatModelId);
|
||||
|
||||
// Create agent run options
|
||||
var options = new ChatClientAgentRunOptions(new()
|
||||
{
|
||||
Tools = [AIFunctionFactory.Create(GetWeather, name: nameof(GetWeather))]
|
||||
});
|
||||
|
||||
// Act
|
||||
var agent = await agentFactory.CreateFromYamlAsync(agentYaml);
|
||||
var response = await agent!.RunAsync("What is the weather in Cambridge, MA in °C?", options: options);
|
||||
this.Output.WriteLine($"Agent Response: {response.Text}");
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(agent);
|
||||
Assert.NotNull(response);
|
||||
Assert.NotEmpty(response.Text);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CanCreateAndRunResponsesAgentAsync()
|
||||
{
|
||||
// Example function tool that can be used by the agent.
|
||||
[Description("Get the weather for a given location.")]
|
||||
static string GetWeather(
|
||||
[Description("The city and state, e.g. San Francisco, CA")] string location,
|
||||
[Description("The unit of temperature. Possible values are 'celsius' and 'fahrenheit'.")] string unit)
|
||||
=> $"The weather in {location} is cloudy with a high of {(unit.Equals("celsius", StringComparison.Ordinal) ? "15°C" : "59°F")}.";
|
||||
|
||||
// Arrange
|
||||
var agentFactory = new AggregatorAgentFactory(
|
||||
[
|
||||
new OpenAIChatAgentFactory(),
|
||||
new OpenAIResponseAgentFactory(),
|
||||
new OpenAIAssistantAgentFactory()
|
||||
]);
|
||||
var agentYaml = File.ReadAllText("../../../../../../agent-samples/openai/OpenAIResponses.yaml");
|
||||
agentYaml = agentYaml.Replace("=Env.OPENAI_APIKEY", this.OpenAIConfiguration.ApiKey);
|
||||
agentYaml = agentYaml.Replace("=Env.OPENAI_MODEL", this.OpenAIConfiguration.ChatModelId);
|
||||
|
||||
// Create agent run options
|
||||
var options = new ChatClientAgentRunOptions(new()
|
||||
{
|
||||
Tools = [AIFunctionFactory.Create(GetWeather, name: nameof(GetWeather))]
|
||||
});
|
||||
|
||||
// Act
|
||||
var agent = await agentFactory.CreateFromYamlAsync(agentYaml);
|
||||
var response = await agent!.RunAsync("What is the weather in Cambridge, MA in °C?", options: options);
|
||||
this.Output.WriteLine($"Agent Response: {response.Text}");
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(agent);
|
||||
Assert.NotNull(response);
|
||||
Assert.NotEmpty(response.Text);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Microsoft.Agents.AI.Declarative.IntegrationTests;
|
||||
|
||||
public sealed class TestOutputAdapter(ITestOutputHelper output) : TextWriter, ILogger, ILoggerFactory
|
||||
{
|
||||
private readonly Stack<string> _scopes = [];
|
||||
|
||||
public override Encoding Encoding { get; } = Encoding.UTF8;
|
||||
|
||||
public void AddProvider(ILoggerProvider provider) => throw new NotSupportedException();
|
||||
|
||||
public ILogger CreateLogger(string categoryName) => this;
|
||||
|
||||
public bool IsEnabled(LogLevel logLevel) => true;
|
||||
|
||||
public override void WriteLine(object? value) => this.SafeWrite($"{value}");
|
||||
|
||||
public override void WriteLine(string? format, params object?[] arg) => this.SafeWrite(string.Format(format ?? string.Empty, arg));
|
||||
|
||||
public override void WriteLine(string? value) => this.SafeWrite(value ?? string.Empty);
|
||||
|
||||
public override void Write(object? value) => this.SafeWrite($"{value}");
|
||||
|
||||
public override void Write(char[]? buffer) => this.SafeWrite(new string(buffer));
|
||||
|
||||
public IDisposable BeginScope<TState>(TState state) where TState : notnull
|
||||
{
|
||||
this._scopes.Push($"{state}");
|
||||
return new LoggerScope(() => this._scopes.Pop());
|
||||
}
|
||||
|
||||
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter)
|
||||
{
|
||||
string message = formatter(state, exception);
|
||||
string scope = this._scopes.Count > 0 ? $"[{this._scopes.Peek()}] " : string.Empty;
|
||||
output.WriteLine($"{scope}{message}");
|
||||
}
|
||||
|
||||
private void SafeWrite(string value)
|
||||
{
|
||||
try
|
||||
{
|
||||
output.WriteLine(value ?? string.Empty);
|
||||
}
|
||||
catch (InvalidOperationException exception) when (exception.Message == "There is no currently active test.")
|
||||
{
|
||||
// This exception is thrown when the test output is accessed outside of a test context.
|
||||
// We can ignore it since we are not in a test context.
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class LoggerScope(Action action) : IDisposable
|
||||
{
|
||||
private bool _disposed;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (!this._disposed)
|
||||
{
|
||||
action.Invoke();
|
||||
this._disposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,256 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Text.Json.Serialization;
|
||||
using Microsoft.Bot.ObjectModel;
|
||||
using Microsoft.Extensions.AI;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
namespace Microsoft.Agents.AI.Declarative.UnitTests;
|
||||
|
||||
/// <summary>
|
||||
/// Unit tests for <see cref="AgentBotElementYaml"/>
|
||||
/// </summary>
|
||||
public sealed class AgentBotElementYamlTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData(PromptAgents.AgentWithEverything)]
|
||||
[InlineData(PromptAgents.AgentWithApiKeyConnection)]
|
||||
[InlineData(PromptAgents.AgentWithEnvironmentVariables)]
|
||||
[InlineData(PromptAgents.AgentWithOutputSchema)]
|
||||
[InlineData(PromptAgents.OpenAIChatAgent)]
|
||||
[InlineData(PromptAgents.AgentWithCurrentModels)]
|
||||
[InlineData(PromptAgents.AgentWithRemoteConnection)]
|
||||
public void FromYaml_DoesNotThrow(string text)
|
||||
{
|
||||
// Arrange & Act
|
||||
var agent = AgentBotElementYaml.FromYaml(text);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(agent);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FromYaml_Properties()
|
||||
{
|
||||
// Arrange & Act
|
||||
var agent = AgentBotElementYaml.FromYaml(PromptAgents.AgentWithEverything);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(agent);
|
||||
Assert.Equal("AgentName", agent.Name);
|
||||
Assert.Equal("Agent description", agent.Description);
|
||||
Assert.Equal("You are a helpful assistant.", agent.Instructions?.ToTemplateString());
|
||||
Assert.NotNull(agent.Model);
|
||||
Assert.True(agent.Tools.Length > 0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FromYaml_CurrentModels()
|
||||
{
|
||||
// Arrange & Act
|
||||
var agent = AgentBotElementYaml.FromYaml(PromptAgents.AgentWithCurrentModels);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(agent);
|
||||
Assert.NotNull(agent.Model);
|
||||
Assert.Equal("gpt-4o", agent.Model.ModelNameHint);
|
||||
Assert.NotNull(agent.Model.Options);
|
||||
Assert.Equal(0.7f, (float?)agent.Model.Options?.Temperature?.LiteralValue);
|
||||
Assert.Equal(0.9f, (float?)agent.Model.Options?.TopP?.LiteralValue);
|
||||
|
||||
// Assert contents using extension methods
|
||||
Assert.Equal(1024, agent.Model.Options?.MaxOutputTokens?.LiteralValue);
|
||||
Assert.Equal(50, agent.Model.Options?.TopK?.LiteralValue);
|
||||
Assert.Equal(0.7f, (float?)agent.Model.Options?.FrequencyPenalty?.LiteralValue);
|
||||
Assert.Equal(0.7f, (float?)agent.Model.Options?.PresencePenalty?.LiteralValue);
|
||||
Assert.Equal(42, agent.Model.Options?.Seed?.LiteralValue);
|
||||
Assert.Equal(PromptAgents.s_stopSequences, agent.Model.Options?.StopSequences);
|
||||
Assert.True(agent.Model.Options?.AllowMultipleToolCalls?.LiteralValue);
|
||||
Assert.Equal(ChatToolMode.Auto, agent.Model.Options?.AsChatToolMode());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FromYaml_OutputSchema()
|
||||
{
|
||||
// Arrange & Act
|
||||
var agent = AgentBotElementYaml.FromYaml(PromptAgents.AgentWithOutputSchema);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(agent);
|
||||
Assert.NotNull(agent.OutputType);
|
||||
var responseFormat = agent.OutputType.AsChatResponseFormat() as ChatResponseFormatJson;
|
||||
Assert.NotNull(responseFormat);
|
||||
Assert.NotNull(responseFormat.Schema);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FromYaml_CodeInterpreter()
|
||||
{
|
||||
// Arrange & Act
|
||||
var agent = AgentBotElementYaml.FromYaml(PromptAgents.AgentWithEverything);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(agent);
|
||||
var tools = agent.Tools;
|
||||
var codeInterpreterTools = tools.Where(t => t is CodeInterpreterTool).ToArray();
|
||||
Assert.Single(codeInterpreterTools);
|
||||
var codeInterpreterTool = codeInterpreterTools[0] as CodeInterpreterTool;
|
||||
Assert.NotNull(codeInterpreterTool);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FromYaml_FunctionTool()
|
||||
{
|
||||
// Arrange & Act
|
||||
var agent = AgentBotElementYaml.FromYaml(PromptAgents.AgentWithEverything);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(agent);
|
||||
var tools = agent.Tools;
|
||||
var functionTools = tools.Where(t => t is InvokeClientTaskAction).ToArray();
|
||||
Assert.Single(functionTools);
|
||||
var functionTool = functionTools[0] as InvokeClientTaskAction;
|
||||
Assert.NotNull(functionTool);
|
||||
Assert.Equal("GetWeather", functionTool.Name);
|
||||
Assert.Equal("Get the weather for a given location.", functionTool.Description);
|
||||
// TODO check schema
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FromYaml_MCP()
|
||||
{
|
||||
// Arrange & Act
|
||||
var agent = AgentBotElementYaml.FromYaml(PromptAgents.AgentWithEverything);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(agent);
|
||||
var tools = agent.Tools;
|
||||
var mcpTools = tools.Where(t => t is McpServerTool).ToArray();
|
||||
Assert.Single(mcpTools);
|
||||
var mcpTool = mcpTools[0] as McpServerTool;
|
||||
Assert.NotNull(mcpTool);
|
||||
Assert.Equal("PersonInfoTool", mcpTool.ServerName?.LiteralValue);
|
||||
var connection = mcpTool.Connection as AnonymousConnection;
|
||||
Assert.NotNull(connection);
|
||||
Assert.Equal("https://my-mcp-endpoint.com/api", connection.Endpoint?.LiteralValue);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FromYaml_WebSearchTool()
|
||||
{
|
||||
// Arrange & Act
|
||||
var agent = AgentBotElementYaml.FromYaml(PromptAgents.AgentWithEverything);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(agent);
|
||||
var tools = agent.Tools;
|
||||
var webSearchTools = tools.Where(t => t is WebSearchTool).ToArray();
|
||||
Assert.Single(webSearchTools);
|
||||
Assert.NotNull(webSearchTools[0] as WebSearchTool);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FromYaml_FileSearchTool()
|
||||
{
|
||||
// Arrange & Act
|
||||
var agent = AgentBotElementYaml.FromYaml(PromptAgents.AgentWithEverything);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(agent);
|
||||
var tools = agent.Tools;
|
||||
var fileSearchTools = tools.Where(t => t is FileSearchTool).ToArray();
|
||||
Assert.Single(fileSearchTools);
|
||||
var fileSearchTool = fileSearchTools[0] as FileSearchTool;
|
||||
Assert.NotNull(fileSearchTool);
|
||||
|
||||
// Verify vector store content property exists and has correct values
|
||||
Assert.NotNull(fileSearchTool.VectorStoreIds);
|
||||
Assert.Equal(3, fileSearchTool.VectorStoreIds.LiteralValue.Length);
|
||||
Assert.Equal("1", fileSearchTool.VectorStoreIds.LiteralValue[0]);
|
||||
Assert.Equal("2", fileSearchTool.VectorStoreIds.LiteralValue[1]);
|
||||
Assert.Equal("3", fileSearchTool.VectorStoreIds.LiteralValue[2]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FromYaml_ApiKeyConnection()
|
||||
{
|
||||
// Arrange & Act
|
||||
var agent = AgentBotElementYaml.FromYaml(PromptAgents.AgentWithApiKeyConnection);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(agent);
|
||||
Assert.NotNull(agent.Model);
|
||||
var model = agent.Model as CurrentModels;
|
||||
Assert.NotNull(model);
|
||||
Assert.NotNull(model.Connection);
|
||||
Assert.IsType<ApiKeyConnection>(model.Connection);
|
||||
var connection = model.Connection as ApiKeyConnection;
|
||||
Assert.NotNull(connection);
|
||||
Assert.Equal("https://my-azure-openai-endpoint.openai.azure.com/", connection.Endpoint?.LiteralValue);
|
||||
Assert.Equal("my-api-key", connection.Key?.LiteralValue);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FromYaml_RemoteConnection()
|
||||
{
|
||||
// Arrange & Act
|
||||
var agent = AgentBotElementYaml.FromYaml(PromptAgents.AgentWithRemoteConnection);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(agent);
|
||||
Assert.NotNull(agent.Model);
|
||||
var model = agent.Model as CurrentModels;
|
||||
Assert.NotNull(model);
|
||||
Assert.NotNull(model.Connection);
|
||||
Assert.IsType<RemoteConnection>(model.Connection);
|
||||
var connection = model.Connection as RemoteConnection;
|
||||
Assert.NotNull(connection);
|
||||
Assert.Equal("https://my-azure-openai-endpoint.openai.azure.com/", connection.Endpoint?.LiteralValue);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FromYaml_WithEnvironmentVariables()
|
||||
{
|
||||
// Arrange
|
||||
IConfiguration configuration = new ConfigurationBuilder()
|
||||
.AddInMemoryCollection(new Dictionary<string, string?>
|
||||
{
|
||||
["OpenAIEndpoint"] = "endpoint",
|
||||
["OpenAIModelId"] = "modelId",
|
||||
["OpenAIApiKey"] = "apiKey"
|
||||
})
|
||||
.Build();
|
||||
|
||||
// Act
|
||||
var agent = AgentBotElementYaml.FromYaml(PromptAgents.AgentWithEnvironmentVariables, configuration);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(agent);
|
||||
Assert.NotNull(agent.Model);
|
||||
var model = agent.Model as CurrentModels;
|
||||
Assert.NotNull(model);
|
||||
Assert.NotNull(model.Connection);
|
||||
Assert.IsType<ApiKeyConnection>(model.Connection);
|
||||
//Assert.Equal("https://my-azure-openai-endpoint.openai.azure.com/", agent.Model.Connection.Endpoint?.LiteralValue);
|
||||
//Assert.Equal("apiKey", connection.Key?.LiteralValue);
|
||||
//Assert.Equal("modelId", model.Id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents information about a person, including their name, age, and occupation, matched to the JSON schema used in the agent.
|
||||
/// </summary>
|
||||
[Description("Information about a person including their name, age, and occupation")]
|
||||
public class PersonInfo
|
||||
{
|
||||
[JsonPropertyName("name")]
|
||||
public string? Name { get; set; }
|
||||
|
||||
[JsonPropertyName("age")]
|
||||
public int? Age { get; set; }
|
||||
|
||||
[JsonPropertyName("occupation")]
|
||||
public string? Occupation { get; set; }
|
||||
}
|
||||
}
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.AI;
|
||||
using Moq;
|
||||
|
||||
namespace Microsoft.Agents.AI.Declarative.UnitTests.ChatClient;
|
||||
|
||||
/// <summary>
|
||||
/// Unit tests for <see cref="ChatClientAgentFactory"/>.
|
||||
/// </summary>
|
||||
public sealed class ChatClientAgentFactoryTests
|
||||
{
|
||||
private readonly Mock<IChatClient> _mockChatClient;
|
||||
|
||||
public ChatClientAgentFactoryTests()
|
||||
{
|
||||
this._mockChatClient = new();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task TryCreateAsync_WithChatClientInConstructor_CreatesAgentAsync()
|
||||
{
|
||||
// Arrange
|
||||
var promptAgent = PromptAgents.CreateTestPromptAgent();
|
||||
ChatClientAgentFactory factory = new(this._mockChatClient.Object);
|
||||
|
||||
// Act
|
||||
AIAgent? agent = await factory.TryCreateAsync(promptAgent);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(agent);
|
||||
Assert.IsType<ChatClientAgent>(agent);
|
||||
Assert.Equal("Test Agent", agent.Name);
|
||||
Assert.Equal("Test Description", agent.Description);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task TryCreateAsync_Creates_ChatClientAgentAsync()
|
||||
{
|
||||
// Arrange
|
||||
var promptAgent = PromptAgents.CreateTestPromptAgent();
|
||||
ChatClientAgentFactory factory = new(this._mockChatClient.Object);
|
||||
|
||||
// Act
|
||||
AIAgent? agent = await factory.TryCreateAsync(promptAgent);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(agent);
|
||||
Assert.IsType<ChatClientAgent>(agent);
|
||||
var chatClientAgent = agent as ChatClientAgent;
|
||||
Assert.NotNull(chatClientAgent);
|
||||
Assert.Equal("You are a helpful assistant.", chatClientAgent.Instructions);
|
||||
Assert.NotNull(chatClientAgent.ChatClient);
|
||||
Assert.NotNull(chatClientAgent.ChatOptions);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task TryCreateAsync_Creates_ChatOptionsAsync()
|
||||
{
|
||||
// Arrange
|
||||
var promptAgent = PromptAgents.CreateTestPromptAgent();
|
||||
ChatClientAgentFactory factory = new(this._mockChatClient.Object);
|
||||
|
||||
// Act
|
||||
AIAgent? agent = await factory.TryCreateAsync(promptAgent);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(agent);
|
||||
Assert.IsType<ChatClientAgent>(agent);
|
||||
var chatClientAgent = agent as ChatClientAgent;
|
||||
Assert.NotNull(chatClientAgent?.ChatOptions);
|
||||
Assert.Equal("Provide detailed and accurate responses.", chatClientAgent?.ChatOptions?.Instructions);
|
||||
Assert.Equal(0.7F, chatClientAgent?.ChatOptions?.Temperature);
|
||||
Assert.Equal(0.7F, chatClientAgent?.ChatOptions?.FrequencyPenalty);
|
||||
Assert.Equal(1024, chatClientAgent?.ChatOptions?.MaxOutputTokens);
|
||||
Assert.Equal(0.9F, chatClientAgent?.ChatOptions?.TopP);
|
||||
Assert.Equal(50, chatClientAgent?.ChatOptions?.TopK);
|
||||
Assert.Equal(0.7F, chatClientAgent?.ChatOptions?.PresencePenalty);
|
||||
Assert.Equal(42L, chatClientAgent?.ChatOptions?.Seed);
|
||||
Assert.NotNull(chatClientAgent?.ChatOptions?.ResponseFormat);
|
||||
Assert.Equal("gpt-4o", chatClientAgent?.ChatOptions?.ModelId);
|
||||
Assert.Equal(["###", "END", "STOP"], chatClientAgent?.ChatOptions?.StopSequences);
|
||||
Assert.True(chatClientAgent?.ChatOptions?.AllowMultipleToolCalls);
|
||||
Assert.Equal(ChatToolMode.Auto, chatClientAgent?.ChatOptions?.ToolMode);
|
||||
Assert.Equal("customValue", chatClientAgent?.ChatOptions?.AdditionalProperties?["customProperty"]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task TryCreateAsync_Creates_ToolsAsync()
|
||||
{
|
||||
// Arrange
|
||||
var promptAgent = PromptAgents.CreateTestPromptAgent();
|
||||
ChatClientAgentFactory factory = new(this._mockChatClient.Object);
|
||||
|
||||
// Act
|
||||
AIAgent? agent = await factory.TryCreateAsync(promptAgent);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(agent);
|
||||
Assert.IsType<ChatClientAgent>(agent);
|
||||
var chatClientAgent = agent as ChatClientAgent;
|
||||
Assert.NotNull(chatClientAgent?.ChatOptions?.Tools);
|
||||
var tools = chatClientAgent?.ChatOptions?.Tools;
|
||||
Assert.Equal(5, tools?.Count);
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>$(ProjectsTargetFrameworks)</TargetFrameworks>
|
||||
<NoWarn>$(NoWarn);IDE1006</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\Microsoft.Agents.AI.Declarative\Microsoft.Agents.AI.Declarative.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Bot.ObjectModel" />
|
||||
<PackageReference Include="Microsoft.Bot.ObjectModel.Json" />
|
||||
<PackageReference Include="Microsoft.Bot.ObjectModel.PowerFx" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,326 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using Microsoft.Bot.ObjectModel;
|
||||
|
||||
namespace Microsoft.Agents.AI.Declarative.UnitTests;
|
||||
internal static class PromptAgents
|
||||
{
|
||||
internal const string AgentWithEverything =
|
||||
"""
|
||||
kind: Prompt
|
||||
name: AgentName
|
||||
description: Agent description
|
||||
instructions: You are a helpful assistant.
|
||||
model:
|
||||
id: gpt-4o
|
||||
options:
|
||||
temperature: 0.7
|
||||
maxOutputTokens: 1024
|
||||
topP: 0.9
|
||||
topK: 50
|
||||
frequencyPenalty: 0.0
|
||||
presencePenalty: 0.0
|
||||
seed: 42
|
||||
responseFormat: text
|
||||
stopSequences:
|
||||
- "###"
|
||||
- "END"
|
||||
- "STOP"
|
||||
allowMultipleToolCalls: true
|
||||
tools:
|
||||
- kind: codeInterpreter
|
||||
inputs:
|
||||
- kind: HostedFileContent
|
||||
FileId: fileId123
|
||||
- kind: function
|
||||
name: GetWeather
|
||||
description: Get the weather for a given location.
|
||||
parameters:
|
||||
- name: location
|
||||
type: string
|
||||
description: The city and state, e.g. San Francisco, CA
|
||||
required: true
|
||||
- name: unit
|
||||
type: string
|
||||
description: The unit of temperature. Possible values are 'celsius' and 'fahrenheit'.
|
||||
required: false
|
||||
enum:
|
||||
- celsius
|
||||
- fahrenheit
|
||||
- kind: mcp
|
||||
serverName: PersonInfoTool
|
||||
serverDescription: Get information about a person.
|
||||
connection:
|
||||
kind: AnonymousConnection
|
||||
endpoint: https://my-mcp-endpoint.com/api
|
||||
allowedTools:
|
||||
- "GetPersonInfo"
|
||||
- "UpdatePersonInfo"
|
||||
- "DeletePersonInfo"
|
||||
approvalMode:
|
||||
kind: HostedMcpServerToolRequireSpecificApprovalMode
|
||||
AlwaysRequireApprovalToolNames:
|
||||
- "UpdatePersonInfo"
|
||||
- "DeletePersonInfo"
|
||||
NeverRequireApprovalToolNames:
|
||||
- "GetPersonInfo"
|
||||
- kind: webSearch
|
||||
name: WebSearchTool
|
||||
description: Search the web for information.
|
||||
- kind: fileSearch
|
||||
name: FileSearchTool
|
||||
description: Search files for information.
|
||||
ranker: default
|
||||
scoreThreshold: 0.5
|
||||
maxResults: 5
|
||||
maxContentLength: 2000
|
||||
vectorStoreIds:
|
||||
- 1
|
||||
- 2
|
||||
- 3
|
||||
""";
|
||||
|
||||
internal const string AgentWithOutputSchema =
|
||||
"""
|
||||
kind: Prompt
|
||||
name: Translation Assistant
|
||||
description: A helpful assistant that translates text to a specified language.
|
||||
model:
|
||||
id: gpt-4o
|
||||
options:
|
||||
temperature: 0.9
|
||||
topP: 0.95
|
||||
instructions: You are a helpful assistant. You answer questions in {language}. You return your answers in a JSON format.
|
||||
additionalInstructions: You must always respond in the specified language.
|
||||
tools:
|
||||
- kind: codeInterpreter
|
||||
template:
|
||||
format: PowerFx # Mustache is the other option
|
||||
parser: None # Prompty and XML are the other options
|
||||
inputSchema:
|
||||
properties:
|
||||
language: string
|
||||
outputSchema:
|
||||
properties:
|
||||
language:
|
||||
type: string
|
||||
required: true
|
||||
description: The language of the answer.
|
||||
answer:
|
||||
type: string
|
||||
required: true
|
||||
description: The answer text.
|
||||
""";
|
||||
|
||||
internal const string AgentWithApiKeyConnection =
|
||||
"""
|
||||
kind: Prompt
|
||||
name: AgentName
|
||||
description: Agent description
|
||||
instructions: You are a helpful assistant.
|
||||
model:
|
||||
id: gpt-4o
|
||||
connection:
|
||||
kind: ApiKey
|
||||
endpoint: https://my-azure-openai-endpoint.openai.azure.com/
|
||||
key: my-api-key
|
||||
""";
|
||||
|
||||
internal const string AgentWithRemoteConnection =
|
||||
"""
|
||||
kind: Prompt
|
||||
name: AgentName
|
||||
description: Agent description
|
||||
instructions: You are a helpful assistant.
|
||||
model:
|
||||
id: gpt-4o
|
||||
connection:
|
||||
kind: Remote
|
||||
endpoint: https://my-azure-openai-endpoint.openai.azure.com/
|
||||
""";
|
||||
|
||||
internal const string AgentWithEnvironmentVariables =
|
||||
"""
|
||||
kind: Prompt
|
||||
name: AgentName
|
||||
description: Agent description
|
||||
instructions: You are a helpful assistant.
|
||||
model:
|
||||
id: =Env.OpenAIModelId
|
||||
connection:
|
||||
kind: apiKey
|
||||
endpoint: =Env.OpenAIEndpoint
|
||||
key: =Env.OpenAIApiKey
|
||||
""";
|
||||
|
||||
internal const string OpenAIChatAgent =
|
||||
"""
|
||||
kind: Prompt
|
||||
name: Assistant
|
||||
description: Helpful assistant
|
||||
instructions: You are a helpful assistant. You answer questions in the language specified by the user. You return your answers in a JSON format.
|
||||
model:
|
||||
id: =Env.OPENAI_MODEL
|
||||
options:
|
||||
temperature: 0.9
|
||||
topP: 0.95
|
||||
connection:
|
||||
kind: apiKey
|
||||
key: =Env.OPENAI_APIKEY
|
||||
outputSchema:
|
||||
properties:
|
||||
language:
|
||||
type: string
|
||||
required: true
|
||||
description: The language of the answer.
|
||||
answer:
|
||||
type: string
|
||||
required: true
|
||||
description: The answer text.
|
||||
""";
|
||||
|
||||
internal const string AgentWithCurrentModels =
|
||||
"""
|
||||
kind: Prompt
|
||||
name: AgentName
|
||||
description: Agent description
|
||||
instructions: You are a helpful assistant.
|
||||
model:
|
||||
id: gpt-4o
|
||||
options:
|
||||
temperature: 0.7
|
||||
maxOutputTokens: 1024
|
||||
topP: 0.9
|
||||
topK: 50
|
||||
frequencyPenalty: 0.7
|
||||
presencePenalty: 0.7
|
||||
seed: 42
|
||||
responseFormat: text
|
||||
stopSequences:
|
||||
- "###"
|
||||
- "END"
|
||||
- "STOP"
|
||||
allowMultipleToolCalls: true
|
||||
chatToolMode: auto
|
||||
""";
|
||||
|
||||
internal const string AgentWithCurrentModelsSnakeCase =
|
||||
"""
|
||||
kind: Prompt
|
||||
name: AgentName
|
||||
description: Agent description
|
||||
instructions: You are a helpful assistant.
|
||||
model:
|
||||
id: gpt-4o
|
||||
options:
|
||||
temperature: 0.7
|
||||
max_output_tokens: 1024
|
||||
top_p: 0.9
|
||||
top_k: 50
|
||||
frequency_penalty: 0.7
|
||||
presence_penalty: 0.7
|
||||
seed: 42
|
||||
response_format: text
|
||||
stop_sequences:
|
||||
- "###"
|
||||
- "END"
|
||||
- "STOP"
|
||||
allow_multiple_tool_calls: true
|
||||
chat_tool_mode: auto
|
||||
""";
|
||||
|
||||
internal static readonly string[] s_stopSequences = ["###", "END", "STOP"];
|
||||
|
||||
internal static GptComponentMetadata CreateTestPromptAgent(string? publisher = "OpenAI", string? apiType = "Chat")
|
||||
{
|
||||
string agentYaml =
|
||||
$"""
|
||||
kind: Prompt
|
||||
name: Test Agent
|
||||
description: Test Description
|
||||
instructions: You are a helpful assistant.
|
||||
additionalInstructions: Provide detailed and accurate responses.
|
||||
model:
|
||||
id: gpt-4o
|
||||
publisher: {publisher}
|
||||
apiType: {apiType}
|
||||
options:
|
||||
modelId: gpt-4o
|
||||
temperature: 0.7
|
||||
maxOutputTokens: 1024
|
||||
topP: 0.9
|
||||
topK: 50
|
||||
frequencyPenalty: 0.7
|
||||
presencePenalty: 0.7
|
||||
seed: 42
|
||||
responseFormat: text
|
||||
stopSequences:
|
||||
- "###"
|
||||
- "END"
|
||||
- "STOP"
|
||||
allowMultipleToolCalls: true
|
||||
chatToolMode: auto
|
||||
customProperty: customValue
|
||||
connection:
|
||||
kind: apiKey
|
||||
endpoint: https://my-azure-openai-endpoint.openai.azure.com/
|
||||
key: my-api-key
|
||||
tools:
|
||||
- kind: codeInterpreter
|
||||
- kind: function
|
||||
name: GetWeather
|
||||
description: Get the weather for a given location.
|
||||
parameters:
|
||||
- name: location
|
||||
type: string
|
||||
description: The city and state, e.g. San Francisco, CA
|
||||
required: true
|
||||
- name: unit
|
||||
type: string
|
||||
description: The unit of temperature. Possible values are 'celsius' and 'fahrenheit'.
|
||||
required: false
|
||||
enum:
|
||||
- celsius
|
||||
- fahrenheit
|
||||
- kind: mcp
|
||||
serverName: PersonInfoTool
|
||||
serverDescription: Get information about a person.
|
||||
allowedTools:
|
||||
- "GetPersonInfo"
|
||||
- "UpdatePersonInfo"
|
||||
- "DeletePersonInfo"
|
||||
approvalMode:
|
||||
kind: HostedMcpServerToolRequireSpecificApprovalMode
|
||||
AlwaysRequireApprovalToolNames:
|
||||
- "UpdatePersonInfo"
|
||||
- "DeletePersonInfo"
|
||||
NeverRequireApprovalToolNames:
|
||||
- "GetPersonInfo"
|
||||
connection:
|
||||
kind: AnonymousConnection
|
||||
endpoint: https://my-mcp-endpoint.com/api
|
||||
- kind: webSearch
|
||||
name: WebSearchTool
|
||||
description: Search the web for information.
|
||||
- kind: fileSearch
|
||||
name: FileSearchTool
|
||||
description: Search files for information.
|
||||
vectorStoreIds:
|
||||
- 1
|
||||
- 2
|
||||
- 3
|
||||
outputSchema:
|
||||
properties:
|
||||
language:
|
||||
type: string
|
||||
required: true
|
||||
description: The language of the answer.
|
||||
answer:
|
||||
type: string
|
||||
required: true
|
||||
description: The answer text.
|
||||
""";
|
||||
|
||||
return AgentBotElementYaml.FromYaml(agentYaml);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"profiles": {
|
||||
"Microsoft.Agents.AI.Hosting.A2A.UnitTests": {
|
||||
"commandName": "Project",
|
||||
"launchBrowser": true,
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
},
|
||||
"applicationUrl": "https://localhost:54921;http://localhost:54922"
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user