Skip to content

Commit

Permalink
Merge branch 'main' into userproxy_agent_vd
Browse files Browse the repository at this point in the history
  • Loading branch information
husseinmozannar authored Nov 23, 2024
2 parents e748cd4 + caeab68 commit 12fc01c
Show file tree
Hide file tree
Showing 33 changed files with 638 additions and 1,144 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
# AutoGen

> [!IMPORTANT]
>
> - (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).
> - (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.
> - (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).
Expand Down
1 change: 1 addition & 0 deletions dotnet/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ BenchmarkDotNet.Artifacts/
project.lock.json
project.fragment.lock.json
artifacts/
appsettings.Development.json

# Tye
.tye/
Expand Down
1 change: 1 addition & 0 deletions dotnet/src/AutoGen.OpenAI/AutoGen.OpenAI.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

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

<ItemGroup>
Expand Down
133 changes: 133 additions & 0 deletions dotnet/src/AutoGen.OpenAI/Orchestrator/RolePlayToolCallOrchestrator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// RolePlayToolCallOrchestrator.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using AutoGen.OpenAI.Extension;
using OpenAI.Chat;

namespace AutoGen.OpenAI.Orchestrator;

/// <summary>
/// Orchestrating group chat using role play tool call
/// </summary>
public partial class RolePlayToolCallOrchestrator : IOrchestrator
{
public readonly ChatClient chatClient;
private readonly Graph? workflow;

public RolePlayToolCallOrchestrator(ChatClient chatClient, Graph? workflow = null)
{
this.chatClient = chatClient;
this.workflow = workflow;
}

public async Task<IAgent?> GetNextSpeakerAsync(
OrchestrationContext context,
CancellationToken cancellationToken = default)
{
var candidates = context.Candidates.ToList();

if (candidates.Count == 0)
{
return null;
}

if (candidates.Count == 1)
{
return candidates.First();
}

// if there's a workflow
// and the next available agent from the workflow is in the group chat
// then return the next agent from the workflow
if (this.workflow != null)
{
var lastMessage = context.ChatHistory.LastOrDefault();
if (lastMessage == null)
{
return null;
}
var currentSpeaker = candidates.First(candidates => candidates.Name == lastMessage.From);
var nextAgents = await this.workflow.TransitToNextAvailableAgentsAsync(currentSpeaker, context.ChatHistory, cancellationToken);
nextAgents = nextAgents.Where(nextAgent => candidates.Any(candidate => candidate.Name == nextAgent.Name));
candidates = nextAgents.ToList();
if (!candidates.Any())
{
return null;
}

if (candidates is { Count: 1 })
{
return candidates.First();
}
}

// In this case, since there are more than one available agents from the workflow for the next speaker
// We need to invoke LLM to select the next speaker via select next speaker function

var chatHistoryStringBuilder = new StringBuilder();
foreach (var message in context.ChatHistory)
{
var chatHistoryPrompt = $"{message.From}: {message.GetContent()}";

chatHistoryStringBuilder.AppendLine(chatHistoryPrompt);
}

var chatHistory = chatHistoryStringBuilder.ToString();

var prompt = $"""
# Task: Select the next speaker

You are in a role-play game. Carefully read the conversation history and select the next speaker from the available roles.

# Conversation
{chatHistory}

# Available roles
- {string.Join(",", candidates.Select(candidate => candidate.Name))}

Select the next speaker from the available roles and provide a reason for your selection.
""";

// enforce the next speaker to be selected by the LLM
var option = new ChatCompletionOptions
{
ToolChoice = ChatToolChoice.CreateFunctionChoice(this.SelectNextSpeakerFunctionContract.Name),
};

option.Tools.Add(this.SelectNextSpeakerFunctionContract.ToChatTool());
var toolCallMiddleware = new FunctionCallMiddleware(
functions: [this.SelectNextSpeakerFunctionContract],
functionMap: new Dictionary<string, Func<string, Task<string>>>
{
[this.SelectNextSpeakerFunctionContract.Name] = this.SelectNextSpeakerWrapper,
});

var selectAgent = new OpenAIChatAgent(
chatClient,
"admin",
option)
.RegisterMessageConnector()
.RegisterMiddleware(toolCallMiddleware);

var reply = await selectAgent.SendAsync(prompt);

var nextSpeaker = candidates.FirstOrDefault(candidate => candidate.Name == reply.GetContent());

return nextSpeaker;
}

/// <summary>
/// Select the next speaker by name and reason
/// </summary>
[Function]
public async Task<string> SelectNextSpeaker(string name, string reason)
{
return name;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,13 @@ namespace Microsoft.AutoGen.Agents;
internal sealed class SubscriptionsGrain([PersistentState("state", "PubSubStore")] IPersistentState<SubscriptionsState> state) : Grain, ISubscriptionsGrain
{
private readonly Dictionary<string, List<string>> _subscriptions = new();
public ValueTask<Dictionary<string, List<string>>> GetSubscriptions(string agentType)
public ValueTask<Dictionary<string, List<string>>> GetSubscriptions(string? agentType = null)
{
//if agentType is null, return all subscriptions else filter on agentType
if (agentType != null)
{
return new ValueTask<Dictionary<string, List<string>>>(_subscriptions.Where(x => x.Value.Contains(agentType)).ToDictionary(x => x.Key, x => x.Value));
}
return new ValueTask<Dictionary<string, List<string>>>(_subscriptions);
}
public ValueTask Subscribe(string agentType, string topic)
Expand Down
Loading

0 comments on commit 12fc01c

Please sign in to comment.