Skip to content

Commit 456e7d1

Browse files
authored
Round 2 of cleanup for agent runtime and orchestrations (#164)
- Moved InProcessRuntime type into abstractions package and deleted InProcess package. - Moved several members of IAgentRuntime to be extension methods instead, e.g. multiple GetActorAsync overloads. - Added synchronous RegisterMessageHandler overloads and used them to avoid unnecessary async usage at call sites. - Removed unnecessary surface area from InProcessRuntime, e.g. StopAsync, RunUntilIdleAsync, etc. - Fixed spin loop in InProcessRuntime that would consume an entire core for the duration of the orchestration's operation. - Removed a bunch of allocation from InProcessRuntime. - Made a runtime optional for orchestrations, defaulting to using a temporary InProcessRuntime if none is provided. - Removed custom delegate types from orchestrations. - Consolidated namespaces. - Used records to simplify message classes. - Tweaked naming on AgentActor to make purpose of protected methods more clear. - Removed invocation in AgentActor.InvokeAsync of empty update / isFinal parameter. - Changed OrchestrationHandoffs to avoid needing to pass in agents duplicatively. - Made various extension methods, such as those on OrchestrationHandoffsExtensions, into instance methods.
1 parent eca0eb9 commit 456e7d1

83 files changed

Lines changed: 939 additions & 1664 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

dotnet/Directory.Packages.props

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
<PackageVersion Include="System.Linq.Async" Version="6.0.1" />
1414
<PackageVersion Include="System.Text.Json" Version="8.0.6" />
1515
<PackageVersion Include="System.Diagnostics.DiagnosticSource" Version="9.0.7" />
16+
<PackageVersion Include="System.Threading.Channels" Version="9.0.7" />
1617
<PackageVersion Include="System.Threading.Tasks.Extensions" Version="4.6.3" />
1718
<!-- Microsoft.Extensions.* -->
1819
<PackageVersion Include="Microsoft.Bcl.HashCode" Version="6.0.0" />

dotnet/agent-framework-dotnet.slnx

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,6 @@
115115
<Project Path="src/Microsoft.Extensions.AI.Agents.Abstractions/Microsoft.Extensions.AI.Agents.Abstractions.csproj" />
116116
<Project Path="src/Microsoft.Extensions.AI.Agents.AzureAI/Microsoft.Extensions.AI.Agents.AzureAI.csproj" />
117117
<Project Path="src/Microsoft.Extensions.AI.Agents.Runtime.Abstractions/Microsoft.Extensions.AI.Agents.Runtime.Abstractions.csproj" />
118-
<Project Path="src/Microsoft.Extensions.AI.Agents.Runtime.InProcess/Microsoft.Extensions.AI.Agents.Runtime.InProcess.csproj" />
119118
<Project Path="src/Microsoft.Extensions.AI.Agents/Microsoft.Extensions.AI.Agents.csproj" />
120119
<Project Path="src/Microsoft.Extensions.AI.Agents.CopilotStudio/Microsoft.Extensions.AI.Agents.CopilotStudio.csproj" />
121120
</Folder>
@@ -129,9 +128,6 @@
129128
<Project Path="tests/Microsoft.Extensions.AI.Agents.Runtime.Abstractions.UnitTests/Microsoft.Extensions.AI.Agents.Runtime.Abstractions.UnitTests.csproj">
130129
<BuildType Solution="Publish|*" Project="Debug" />
131130
</Project>
132-
<Project Path="tests/Microsoft.Extensions.AI.Agents.Runtime.InProcess.UnitTests/Microsoft.Extensions.AI.Agents.Runtime.InProcess.UnitTests.csproj">
133-
<BuildType Solution="Publish|*" Project="Debug" />
134-
</Project>
135131
<Project Path="tests/Microsoft.Extensions.AI.Agents.UnitTests/Microsoft.Extensions.AI.Agents.UnitTests.csproj">
136132
<BuildType Solution="Publish|*" Project="Debug" />
137133
</Project>

dotnet/samples/GettingStarted/AgentSample.cs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -159,11 +159,8 @@ private NewOpenAIAssistantChatClient GetOpenAIAssistantChatClient(ChatClientAgen
159159

160160
private async Task AzureAIAgentsPersistentAgentCleanUpAsync(ChatClientAgent agent, AgentThread? thread, CancellationToken cancellationToken)
161161
{
162-
var persistentAgentsClient = agent.ChatClient.GetService<PersistentAgentsClient>();
163-
if (persistentAgentsClient is null)
164-
{
162+
var persistentAgentsClient = agent.ChatClient.GetService<PersistentAgentsClient>() ??
165163
throw new InvalidOperationException("The provided chat client is not a Persistent Agents Chat Client");
166-
}
167164

168165
await persistentAgentsClient.Administration.DeleteAgentAsync(agent.Id, cancellationToken);
169166

dotnet/samples/GettingStarted/Orchestration/ConcurrentOrchestration_Intro.cs

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
// Copyright (c) Microsoft. All rights reserved.
22

33
using Microsoft.Agents.Orchestration;
4-
using Microsoft.Agents.Orchestration.Concurrent;
54
using Microsoft.Extensions.AI.Agents;
6-
using Microsoft.Extensions.AI.Agents.Runtime.InProcess;
75

86
namespace Orchestration;
97

@@ -42,20 +40,14 @@ public async Task RunOrchestrationAsync(bool streamedResponse)
4240
StreamingResponseCallback = streamedResponse ? monitor.StreamingResultCallback : null,
4341
};
4442

45-
// Start the runtime
46-
await using InProcessRuntime runtime = new();
47-
await runtime.StartAsync();
48-
4943
// Run the orchestration
5044
string input = "What is temperature?";
5145
Console.WriteLine($"\n# INPUT: {input}\n");
52-
OrchestrationResult<string[]> result = await orchestration.InvokeAsync(input, runtime);
46+
OrchestrationResult<string[]> result = await orchestration.InvokeAsync(input);
5347

54-
string[] output = await result.GetValueAsync(TimeSpan.FromSeconds(ResultTimeoutInSeconds));
48+
string[] output = await result;
5549
Console.WriteLine($"\n# RESULT:\n{string.Join("\n\n", output.Select(text => $"{text}"))}");
5650

57-
await runtime.RunUntilIdleAsync();
58-
5951
this.DisplayHistory(monitor.History);
6052
}
6153
}

dotnet/samples/GettingStarted/Orchestration/ConcurrentOrchestration_With_StructuredOutput.cs

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,7 @@
22

33
using System.Text.Json;
44
using Microsoft.Agents.Orchestration;
5-
using Microsoft.Agents.Orchestration.Concurrent;
6-
using Microsoft.Agents.Orchestration.Transforms;
75
using Microsoft.Extensions.AI.Agents;
8-
using Microsoft.Extensions.AI.Agents.Runtime.InProcess;
96
using Microsoft.Shared.Samples;
107

118
namespace Orchestration;
@@ -43,20 +40,14 @@ public async Task RunOrchestrationAsync()
4340
ResultTransform = outputTransform.TransformAsync,
4441
};
4542

46-
// Start the runtime
47-
await using InProcessRuntime runtime = new();
48-
await runtime.StartAsync();
49-
5043
// Run the orchestration
5144
const string resourceId = "Hamlet_full_play_summary.txt";
5245
string input = Resources.Read(resourceId);
5346
Console.WriteLine($"\n# INPUT: @{resourceId}\n");
54-
OrchestrationResult<Analysis> result = await orchestration.InvokeAsync(input, runtime);
47+
OrchestrationResult<Analysis> result = await orchestration.InvokeAsync(input);
5548

56-
Analysis output = await result.GetValueAsync(TimeSpan.FromSeconds(ResultTimeoutInSeconds * 2));
49+
Analysis output = await result;
5750
Console.WriteLine($"\n# RESULT:\n{JsonSerializer.Serialize(output, s_options)}");
58-
59-
await runtime.RunUntilIdleAsync();
6051
}
6152

6253
#pragma warning disable CA1812 // Avoid uninstantiated internal classes

dotnet/samples/GettingStarted/Orchestration/GroupChatOrchestration_Intro.cs

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
// Copyright (c) Microsoft. All rights reserved.
22

33
using Microsoft.Agents.Orchestration;
4-
using Microsoft.Agents.Orchestration.GroupChat;
54
using Microsoft.Extensions.AI.Agents;
6-
using Microsoft.Extensions.AI.Agents.Runtime.InProcess;
75

86
namespace Orchestration;
97

@@ -68,17 +66,10 @@ Consider suggestions when refining an idea.
6866
StreamingResponseCallback = streamedResponse ? monitor.StreamingResultCallback : null,
6967
};
7068

71-
// Start the runtime
72-
await using InProcessRuntime runtime = new();
73-
await runtime.StartAsync();
74-
7569
string input = "Create a slogon for a new eletric SUV that is affordable and fun to drive.";
7670
Console.WriteLine($"\n# INPUT: {input}\n");
77-
OrchestrationResult<string> result = await orchestration.InvokeAsync(input, runtime);
78-
string text = await result.GetValueAsync(TimeSpan.FromSeconds(ResultTimeoutInSeconds * 3));
79-
Console.WriteLine($"\n# RESULT: {text}");
80-
81-
await runtime.RunUntilIdleAsync();
71+
OrchestrationResult<string> result = await orchestration.InvokeAsync(input);
72+
Console.WriteLine($"\n# RESULT: {await result}");
8273

8374
this.DisplayHistory(monitor.History);
8475
}

dotnet/samples/GettingStarted/Orchestration/GroupChatOrchestration_With_AIManager.cs

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
// Copyright (c) Microsoft. All rights reserved.
22

33
using Microsoft.Agents.Orchestration;
4-
using Microsoft.Agents.Orchestration.GroupChat;
54
using Microsoft.Extensions.AI;
65
using Microsoft.Extensions.AI.Agents;
7-
using Microsoft.Extensions.AI.Agents.Runtime.InProcess;
86

97
namespace Orchestration;
108

@@ -132,17 +130,10 @@ You are in a debate. Feel free to challenge the other participants with respect.
132130
ResponseCallback = monitor.ResponseCallback,
133131
};
134132

135-
// Start the runtime
136-
await using InProcessRuntime runtime = new();
137-
await runtime.StartAsync();
138-
139133
// Run the orchestration
140134
Console.WriteLine($"\n# INPUT: {topic}\n");
141-
OrchestrationResult<string> result = await orchestration.InvokeAsync(topic, runtime);
142-
string text = await result.GetValueAsync(TimeSpan.FromSeconds(ResultTimeoutInSeconds * 3));
143-
Console.WriteLine($"\n# RESULT: {text}");
144-
145-
await runtime.RunUntilIdleAsync();
135+
OrchestrationResult<string> result = await orchestration.InvokeAsync(topic);
136+
Console.WriteLine($"\n# RESULT: {await result}");
146137

147138
this.DisplayHistory(monitor.History);
148139
}

dotnet/samples/GettingStarted/Orchestration/GroupChatOrchestration_With_HumanInTheLoop.cs

Lines changed: 7 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
// Copyright (c) Microsoft. All rights reserved.
22

33
using Microsoft.Agents.Orchestration;
4-
using Microsoft.Agents.Orchestration.GroupChat;
54
using Microsoft.Extensions.AI;
65
using Microsoft.Extensions.AI.Agents;
7-
using Microsoft.Extensions.AI.Agents.Runtime.InProcess;
86

97
namespace Orchestration;
108

@@ -68,18 +66,11 @@ Consider suggestions when refining an idea.
6866
ResponseCallback = monitor.ResponseCallback,
6967
};
7068

71-
// Start the runtime
72-
await using InProcessRuntime runtime = new();
73-
await runtime.StartAsync();
74-
7569
// Run the orchestration
7670
string input = "Create a slogon for a new eletric SUV that is affordable and fun to drive.";
7771
Console.WriteLine($"\n# INPUT: {input}\n");
78-
OrchestrationResult<string> result = await orchestration.InvokeAsync(input, runtime);
79-
string text = await result.GetValueAsync(TimeSpan.FromSeconds(ResultTimeoutInSeconds * 3));
80-
Console.WriteLine($"\n# RESULT: {text}");
81-
82-
await runtime.RunUntilIdleAsync();
72+
OrchestrationResult<string> result = await orchestration.InvokeAsync(input);
73+
Console.WriteLine($"\n# RESULT: {await result}");
8374

8475
this.DisplayHistory(monitor.History);
8576
}
@@ -97,23 +88,12 @@ public override ValueTask<GroupChatManagerResult<bool>> ShouldRequestUserInput(I
9788
{
9889
string? lastAgent = history.LastOrDefault()?.AuthorName;
9990

100-
GroupChatManagerResult<bool> result;
101-
102-
if (lastAgent is null)
103-
{
104-
result = new GroupChatManagerResult<bool>(false) { Reason = "No agents have spoken yet." };
105-
}
106-
107-
if (lastAgent == "Reviewer")
108-
{
109-
result = new GroupChatManagerResult<bool>(true) { Reason = "User input is needed after the reviewer's message." };
110-
}
111-
else
112-
{
113-
result = new GroupChatManagerResult<bool>(false) { Reason = "User input is not needed until the reviewer's message." };
114-
}
91+
GroupChatManagerResult<bool> result =
92+
lastAgent is null ? new(false) { Reason = "No agents have spoken yet." } :
93+
lastAgent is "Reviewer" ? new(true) { Reason = "User input is needed after the reviewer's message." } :
94+
new(false) { Reason = "User input is not needed until the reviewer's message." };
11595

116-
return new ValueTask<GroupChatManagerResult<bool>>(result);
96+
return new(result);
11797
}
11898
}
11999
}

dotnet/samples/GettingStarted/Orchestration/HandoffOrchestration_Intro.cs

Lines changed: 3 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
// Copyright (c) Microsoft. All rights reserved.
22

33
using Microsoft.Agents.Orchestration;
4-
using Microsoft.Agents.Orchestration.Handoff;
54
using Microsoft.Extensions.AI;
65
using Microsoft.Extensions.AI.Agents;
7-
using Microsoft.Extensions.AI.Agents.Runtime.InProcess;
86

97
namespace Orchestration;
108

@@ -65,11 +63,7 @@ public async Task RunOrchestrationAsync(bool streamedResponse)
6563
.Add(triageAgent, statusAgent, returnAgent, refundAgent)
6664
.Add(statusAgent, triageAgent, "Transfer to this agent if the issue is not status related")
6765
.Add(returnAgent, triageAgent, "Transfer to this agent if the issue is not return related")
68-
.Add(refundAgent, triageAgent, "Transfer to this agent if the issue is not refund related"),
69-
triageAgent,
70-
statusAgent,
71-
returnAgent,
72-
refundAgent)
66+
.Add(refundAgent, triageAgent, "Transfer to this agent if the issue is not refund related"))
7367
{
7468
InteractiveCallback = () =>
7569
{
@@ -84,18 +78,11 @@ public async Task RunOrchestrationAsync(bool streamedResponse)
8478
StreamingResponseCallback = streamedResponse ? monitor.StreamingResultCallback : null,
8579
};
8680

87-
// Start the runtime
88-
await using InProcessRuntime runtime = new();
89-
await runtime.StartAsync();
90-
9181
// Run the orchestration
9282
Console.WriteLine($"\n# INPUT:\n{task}\n");
93-
OrchestrationResult<string> result = await orchestration.InvokeAsync(task, runtime);
94-
95-
string text = await result.GetValueAsync(TimeSpan.FromSeconds(300));
96-
Console.WriteLine($"\n# RESULT: {text}");
83+
OrchestrationResult<string> result = await orchestration.InvokeAsync(task);
9784

98-
await runtime.RunUntilIdleAsync();
85+
Console.WriteLine($"\n# RESULT: {await result}");
9986

10087
this.DisplayHistory(monitor.History);
10188
}

dotnet/samples/GettingStarted/Orchestration/HandoffOrchestration_With_StructuredInput.cs

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,8 @@
22

33
using System.Text.Json.Serialization;
44
using Microsoft.Agents.Orchestration;
5-
using Microsoft.Agents.Orchestration.Handoff;
65
using Microsoft.Extensions.AI;
76
using Microsoft.Extensions.AI.Agents;
8-
using Microsoft.Extensions.AI.Agents.Runtime.InProcess;
97

108
namespace Orchestration;
119

@@ -49,10 +47,7 @@ public async Task RunOrchestrationAsync()
4947
HandoffOrchestration<GithubIssue, string> orchestration =
5048
new(OrchestrationHandoffs
5149
.StartWith(triageAgent)
52-
.Add(triageAgent, dotnetAgent, pythonAgent),
53-
triageAgent,
54-
pythonAgent,
55-
dotnetAgent)
50+
.Add(triageAgent, dotnetAgent, pythonAgent))
5651
{
5752
LoggerFactory = this.LoggerFactory,
5853
ResponseCallback = monitor.ResponseCallback,
@@ -83,18 +78,11 @@ Normal DBContext logging shows only normal context queries. Queries run by Vecto
8378
Labels = []
8479
};
8580

86-
// Start the runtime
87-
await using InProcessRuntime runtime = new();
88-
await runtime.StartAsync();
89-
9081
// Run the orchestration
9182
Console.WriteLine($"\n# INPUT:\n{input.Id}: {input.Title}\n");
92-
OrchestrationResult<string> result = await orchestration.InvokeAsync(input, runtime);
93-
string text = await result.GetValueAsync(TimeSpan.FromSeconds(ResultTimeoutInSeconds));
94-
Console.WriteLine($"\n# RESULT: {text}");
83+
OrchestrationResult<string> result = await orchestration.InvokeAsync(input);
84+
Console.WriteLine($"\n# RESULT: {await result}");
9585
Console.WriteLine($"\n# LABELS: {string.Join(",", githubPlugin.Labels["12345"])}\n");
96-
97-
await runtime.RunUntilIdleAsync();
9886
}
9987

10088
private sealed class GithubIssue

0 commit comments

Comments
 (0)