Skip to content

Commit 56eb1c3

Browse files
authored
Merge branch 'main' into grpc_optional
2 parents 6ff19dd + 0b5eaf1 commit 56eb1c3

File tree

26 files changed

+596
-1133
lines changed

26 files changed

+596
-1133
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
# AutoGen
1111

1212
> [!IMPORTANT]
13-
>
13+
> - (11/14/24) ⚠️ In response to a number of asks to clarify and distinguish between official AutoGen and its forks that created confusion, we issued a [clarification statement](https://github.com/microsoft/autogen/discussions/4217).
1414
> - (10/13/24) Interested in the standard AutoGen as a prior user? Find it at the actively-maintained *AutoGen* [0.2 branch](https://github.com/microsoft/autogen/tree/0.2) and `autogen-agentchat~=0.2` PyPi package.
1515
> - (10/02/24) [AutoGen 0.4](https://microsoft.github.io/autogen/dev) is a from-the-ground-up rewrite of AutoGen. Learn more about the history, goals and future at [this blog post](https://microsoft.github.io/autogen/blog). We’re excited to work with the community to gather feedback, refine, and improve the project before we officially release 0.4. This is a big change, so AutoGen 0.2 is still available, maintained, and developed in the [0.2 branch](https://github.com/microsoft/autogen/tree/0.2).
1616

dotnet/src/AutoGen.OpenAI/AutoGen.OpenAI.csproj

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
<ItemGroup>
2020
<PackageReference Include="OpenAI" Version="$(OpenAISDKVersion)" />
21+
<ProjectReference Include="..\AutoGen.SourceGenerator\AutoGen.SourceGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
2122
</ItemGroup>
2223

2324
<ItemGroup>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// RolePlayToolCallOrchestrator.cs
3+
4+
using System;
5+
using System.Collections.Generic;
6+
using System.Linq;
7+
using System.Text;
8+
using System.Threading;
9+
using System.Threading.Tasks;
10+
using AutoGen.OpenAI.Extension;
11+
using OpenAI.Chat;
12+
13+
namespace AutoGen.OpenAI.Orchestrator;
14+
15+
/// <summary>
16+
/// Orchestrating group chat using role play tool call
17+
/// </summary>
18+
public partial class RolePlayToolCallOrchestrator : IOrchestrator
19+
{
20+
public readonly ChatClient chatClient;
21+
private readonly Graph? workflow;
22+
23+
public RolePlayToolCallOrchestrator(ChatClient chatClient, Graph? workflow = null)
24+
{
25+
this.chatClient = chatClient;
26+
this.workflow = workflow;
27+
}
28+
29+
public async Task<IAgent?> GetNextSpeakerAsync(
30+
OrchestrationContext context,
31+
CancellationToken cancellationToken = default)
32+
{
33+
var candidates = context.Candidates.ToList();
34+
35+
if (candidates.Count == 0)
36+
{
37+
return null;
38+
}
39+
40+
if (candidates.Count == 1)
41+
{
42+
return candidates.First();
43+
}
44+
45+
// if there's a workflow
46+
// and the next available agent from the workflow is in the group chat
47+
// then return the next agent from the workflow
48+
if (this.workflow != null)
49+
{
50+
var lastMessage = context.ChatHistory.LastOrDefault();
51+
if (lastMessage == null)
52+
{
53+
return null;
54+
}
55+
var currentSpeaker = candidates.First(candidates => candidates.Name == lastMessage.From);
56+
var nextAgents = await this.workflow.TransitToNextAvailableAgentsAsync(currentSpeaker, context.ChatHistory, cancellationToken);
57+
nextAgents = nextAgents.Where(nextAgent => candidates.Any(candidate => candidate.Name == nextAgent.Name));
58+
candidates = nextAgents.ToList();
59+
if (!candidates.Any())
60+
{
61+
return null;
62+
}
63+
64+
if (candidates is { Count: 1 })
65+
{
66+
return candidates.First();
67+
}
68+
}
69+
70+
// In this case, since there are more than one available agents from the workflow for the next speaker
71+
// We need to invoke LLM to select the next speaker via select next speaker function
72+
73+
var chatHistoryStringBuilder = new StringBuilder();
74+
foreach (var message in context.ChatHistory)
75+
{
76+
var chatHistoryPrompt = $"{message.From}: {message.GetContent()}";
77+
78+
chatHistoryStringBuilder.AppendLine(chatHistoryPrompt);
79+
}
80+
81+
var chatHistory = chatHistoryStringBuilder.ToString();
82+
83+
var prompt = $"""
84+
# Task: Select the next speaker
85+
86+
You are in a role-play game. Carefully read the conversation history and select the next speaker from the available roles.
87+
88+
# Conversation
89+
{chatHistory}
90+
91+
# Available roles
92+
- {string.Join(",", candidates.Select(candidate => candidate.Name))}
93+
94+
Select the next speaker from the available roles and provide a reason for your selection.
95+
""";
96+
97+
// enforce the next speaker to be selected by the LLM
98+
var option = new ChatCompletionOptions
99+
{
100+
ToolChoice = ChatToolChoice.CreateFunctionChoice(this.SelectNextSpeakerFunctionContract.Name),
101+
};
102+
103+
option.Tools.Add(this.SelectNextSpeakerFunctionContract.ToChatTool());
104+
var toolCallMiddleware = new FunctionCallMiddleware(
105+
functions: [this.SelectNextSpeakerFunctionContract],
106+
functionMap: new Dictionary<string, Func<string, Task<string>>>
107+
{
108+
[this.SelectNextSpeakerFunctionContract.Name] = this.SelectNextSpeakerWrapper,
109+
});
110+
111+
var selectAgent = new OpenAIChatAgent(
112+
chatClient,
113+
"admin",
114+
option)
115+
.RegisterMessageConnector()
116+
.RegisterMiddleware(toolCallMiddleware);
117+
118+
var reply = await selectAgent.SendAsync(prompt);
119+
120+
var nextSpeaker = candidates.FirstOrDefault(candidate => candidate.Name == reply.GetContent());
121+
122+
return nextSpeaker;
123+
}
124+
125+
/// <summary>
126+
/// Select the next speaker by name and reason
127+
/// </summary>
128+
[Function]
129+
public async Task<string> SelectNextSpeaker(string name, string reason)
130+
{
131+
return name;
132+
}
133+
}

0 commit comments

Comments
 (0)