autogen/dotnet/test/AutoGen.OpenAI.Tests/RolePlayToolCallOrchestratorTests.cs
Xiaoyun Zhang 8f4d8c89c3
.NET add roleplay tool call orchestrator in AutoGen.OpenAI (#4323)
* add roleplay tool call orchestrator

* add chinese business workflow test

* update
2024-11-22 13:51:08 -08:00

270 lines
11 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Copyright (c) Microsoft Corporation. All rights reserved.
// RolePlayToolCallOrchestratorTests.cs
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using AutoGen.OpenAI.Orchestrator;
using AutoGen.Tests;
using Azure.AI.OpenAI;
using FluentAssertions;
using Moq;
using OpenAI;
using OpenAI.Chat;
using Xunit;
namespace AutoGen.OpenAI.Tests;
public class RolePlayToolCallOrchestratorTests
{
[Fact]
public async Task ItReturnNullWhenNoCandidateIsAvailableAsync()
{
var chatClient = Mock.Of<ChatClient>();
var orchestrator = new RolePlayToolCallOrchestrator(chatClient);
var context = new OrchestrationContext
{
Candidates = [],
ChatHistory = [],
};
var speaker = await orchestrator.GetNextSpeakerAsync(context);
speaker.Should().BeNull();
}
[Fact]
public async Task ItReturnCandidateWhenOnlyOneCandidateIsAvailableAsync()
{
var chatClient = Mock.Of<ChatClient>();
var alice = new EchoAgent("Alice");
var orchestrator = new RolePlayToolCallOrchestrator(chatClient);
var context = new OrchestrationContext
{
Candidates = [alice],
ChatHistory = [],
};
var speaker = await orchestrator.GetNextSpeakerAsync(context);
speaker.Should().Be(alice);
}
[Fact]
public async Task ItSelectNextSpeakerFromWorkflowIfProvided()
{
var workflow = new Graph();
var alice = new EchoAgent("Alice");
var bob = new EchoAgent("Bob");
var charlie = new EchoAgent("Charlie");
workflow.AddTransition(Transition.Create(alice, bob));
workflow.AddTransition(Transition.Create(bob, charlie));
workflow.AddTransition(Transition.Create(charlie, alice));
var client = Mock.Of<ChatClient>();
var orchestrator = new RolePlayToolCallOrchestrator(client, workflow);
var context = new OrchestrationContext
{
Candidates = [alice, bob, charlie],
ChatHistory =
[
new TextMessage(Role.User, "Hello, Bob", from: "Alice"),
],
};
var speaker = await orchestrator.GetNextSpeakerAsync(context);
speaker.Should().Be(bob);
}
[Fact]
public async Task ItReturnNullIfNoAvailableAgentFromWorkflowAsync()
{
var workflow = new Graph();
var alice = new EchoAgent("Alice");
var bob = new EchoAgent("Bob");
workflow.AddTransition(Transition.Create(alice, bob));
var client = Mock.Of<ChatClient>();
var orchestrator = new RolePlayToolCallOrchestrator(client, workflow);
var context = new OrchestrationContext
{
Candidates = [alice, bob],
ChatHistory =
[
new TextMessage(Role.User, "Hello, Alice", from: "Bob"),
],
};
var speaker = await orchestrator.GetNextSpeakerAsync(context);
speaker.Should().BeNull();
}
[ApiKeyFact("AZURE_OPENAI_API_KEY", "AZURE_OPENAI_ENDPOINT", "AZURE_OPENAI_DEPLOY_NAME")]
public async Task GPT_3_5_CoderReviewerRunnerTestAsync()
{
var endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") ?? throw new Exception("Please set AZURE_OPENAI_ENDPOINT environment variable.");
var key = Environment.GetEnvironmentVariable("AZURE_OPENAI_API_KEY") ?? throw new Exception("Please set AZURE_OPENAI_API_KEY environment variable.");
var deployName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOY_NAME") ?? throw new Exception("Please set AZURE_OPENAI_DEPLOY_NAME environment variable.");
var openaiClient = new AzureOpenAIClient(new Uri(endpoint), new System.ClientModel.ApiKeyCredential(key));
var chatClient = openaiClient.GetChatClient(deployName);
await BusinessWorkflowTest(chatClient);
await CoderReviewerRunnerTestAsync(chatClient);
}
[ApiKeyFact("OPENAI_API_KEY")]
public async Task GPT_4o_CoderReviewerRunnerTestAsync()
{
var apiKey = Environment.GetEnvironmentVariable("OPENAI_API_KEY") ?? throw new InvalidOperationException("OPENAI_API_KEY is not set");
var model = "gpt-4o";
var openaiClient = new OpenAIClient(apiKey);
var chatClient = openaiClient.GetChatClient(model);
await BusinessWorkflowTest(chatClient);
await CoderReviewerRunnerTestAsync(chatClient);
}
[ApiKeyFact("OPENAI_API_KEY")]
public async Task GPT_4o_mini_CoderReviewerRunnerTestAsync()
{
var apiKey = Environment.GetEnvironmentVariable("OPENAI_API_KEY") ?? throw new InvalidOperationException("OPENAI_API_KEY is not set");
var model = "gpt-4o-mini";
var openaiClient = new OpenAIClient(apiKey);
var chatClient = openaiClient.GetChatClient(model);
await BusinessWorkflowTest(chatClient);
await CoderReviewerRunnerTestAsync(chatClient);
}
/// <summary>
/// This test is to mimic the conversation among coder, reviewer and runner.
/// The coder will write the code, the reviewer will review the code, and the runner will run the code.
/// </summary>
/// <param name="client"></param>
/// <returns></returns>
private async Task CoderReviewerRunnerTestAsync(ChatClient client)
{
var coder = new EchoAgent("Coder");
var reviewer = new EchoAgent("Reviewer");
var runner = new EchoAgent("Runner");
var user = new EchoAgent("User");
var initializeMessage = new List<IMessage>
{
new TextMessage(Role.User, "Hello, I am user, I will provide the coding task, please write the code first, then review and run it", from: "User"),
new TextMessage(Role.User, "Hello, I am coder, I will write the code", from: "Coder"),
new TextMessage(Role.User, "Hello, I am reviewer, I will review the code", from: "Reviewer"),
new TextMessage(Role.User, "Hello, I am runner, I will run the code", from: "Runner"),
new TextMessage(Role.User, "how to print 'hello world' using C#", from: user.Name),
};
var chatHistory = new List<IMessage>()
{
new TextMessage(Role.User, """
```csharp
Console.WriteLine("Hello World");
```
""", from: coder.Name),
new TextMessage(Role.User, "The code looks good", from: reviewer.Name),
new TextMessage(Role.User, "The code runs successfully, the output is 'Hello World'", from: runner.Name),
};
var orchestrator = new RolePlayToolCallOrchestrator(client);
foreach (var message in chatHistory)
{
var context = new OrchestrationContext
{
Candidates = [coder, reviewer, runner, user],
ChatHistory = initializeMessage,
};
var speaker = await orchestrator.GetNextSpeakerAsync(context);
speaker!.Name.Should().Be(message.From);
initializeMessage.Add(message);
}
// the last next speaker should be the user
var lastSpeaker = await orchestrator.GetNextSpeakerAsync(new OrchestrationContext
{
Candidates = [coder, reviewer, runner, user],
ChatHistory = initializeMessage,
});
lastSpeaker!.Name.Should().Be(user.Name);
}
// test if the tool call orchestrator still run business workflow when the conversation is not in English
private async Task BusinessWorkflowTest(ChatClient client)
{
var ceo = new EchoAgent("乙方首席执行官");
var pm = new EchoAgent("乙方项目经理");
var dev = new EchoAgent("乙方开发人员");
var user = new EchoAgent("甲方");
var initializeMessage = new List<IMessage>
{
new TextMessage(Role.User, "你好,我是你们的甲方", from: user.Name),
new TextMessage(Role.User, "你好,我是乙方首席执行官,我将负责对接甲方和给项目经理及开发人员分配任务", from: ceo.Name),
new TextMessage(Role.User, "你好,我是乙方项目经理,我将负责项目的进度和质量", from: pm.Name),
new TextMessage(Role.User, "你好,我是乙方开发人员 我将负责项目的具体开发", from: dev.Name),
new TextMessage(Role.User, "开发一个淘宝预算1W", from: user.Name),
};
var workflow = new Graph();
workflow.AddTransition(Transition.Create(ceo, pm));
workflow.AddTransition(Transition.Create(ceo, dev));
workflow.AddTransition(Transition.Create(pm, ceo));
workflow.AddTransition(Transition.Create(dev, ceo));
workflow.AddTransition(Transition.Create(user, ceo));
workflow.AddTransition(Transition.Create(ceo, user));
var chatHistory = new List<IMessage>()
{
new TextMessage(Role.User, """
使1W预算开发一个淘宝
""", from: ceo.Name),
new TextMessage(Role.User, """
1,:
:
- 使, 便,2000/
-
- :
- 1,(6000)
- 1UI设计(2000)
- :
- 1,
""", from: pm.Name),
new TextMessage(Role.User, "好的,开发人员,请根据项目经理的规划开始开发", from: ceo.Name),
new TextMessage(Role.User, """
```html
<button class="taobao-button" onclick="window.location.href='https://www.taobao.com'">
Visit Taobao
</button>
```
""", from: dev.Name),
new TextMessage(Role.User, "好的,项目已完成,甲方请付款", from: ceo.Name),
};
var orchestrator = new RolePlayToolCallOrchestrator(client, workflow);
foreach (var message in chatHistory)
{
var context = new OrchestrationContext
{
Candidates = [ceo, pm, dev, user],
ChatHistory = initializeMessage,
};
var speaker = await orchestrator.GetNextSpeakerAsync(context);
speaker!.Name.Should().Be(message.From);
initializeMessage.Add(message);
}
// the last next speaker should be the user
var lastSpeaker = await orchestrator.GetNextSpeakerAsync(new OrchestrationContext
{
Candidates = [ceo, pm, dev, user],
ChatHistory = initializeMessage,
});
lastSpeaker!.Name.Should().Be(user.Name);
}
}