Skip to content

Commit 624709e

Browse files
Add some OpenAI and Foundry extension methods (#225)
* Add some OpenAI specific extensions * Update samples and extension methods * Apply suggestion from @Copilot Co-authored-by: Copilot <[email protected]> * Apply suggestion from @Copilot Co-authored-by: Copilot <[email protected]> * Add extension methods for creating agents using the Assistant API * Add orchestration sample * Add orchestration sample * Sample for the Foundry alignment document * Address code review feedback * Rename provider samples * Sample showing how to get an AI agent for Foundry SDK * Add OpenAI chat completion based implementation of AIAgent * Split OpenAI client extension methods by client type * Remove OpenAIClient extension methods * Rename AsRunnableAgent * Fix XML comments --------- Co-authored-by: Copilot <[email protected]>
1 parent 42c7a59 commit 624709e

22 files changed

+1330
-160
lines changed
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// Copyright (c) Microsoft. All rights reserved.
2+
3+
using Microsoft.Extensions.Logging;
4+
using Microsoft.Shared.Samples;
5+
using OpenAI;
6+
using OpenAI.Chat;
7+
8+
namespace Custom;
9+
10+
/// <summary>
11+
/// End-to-end sample showing how to use a custom <see cref="OpenAIChatClientAgent"/>.
12+
/// </summary>
13+
public sealed class Custom_OpenAIChatClientAgent(ITestOutputHelper output) : AgentSample(output)
14+
{
15+
/// <summary>
16+
/// This will create an instance of <see cref="MyOpenAIChatClientAgent"/> and run it.
17+
/// </summary>
18+
[Fact]
19+
public async Task RunCustomChatClientAgent()
20+
{
21+
var chatClient = new OpenAIClient(TestConfiguration.OpenAI.ApiKey).GetChatClient(TestConfiguration.OpenAI.ChatModelId);
22+
23+
var agent = new MyOpenAIChatClientAgent(chatClient);
24+
25+
var chatMessage = new UserChatMessage("Tell me a joke about a pirate.");
26+
var chatCompletion = await agent.RunAsync(chatMessage);
27+
28+
Console.WriteLine(chatCompletion.Content.Last().Text);
29+
}
30+
}
31+
32+
public class MyOpenAIChatClientAgent : OpenAIChatClientAgent
33+
{
34+
private const string JokerName = "Joker";
35+
private const string JokerInstructions = "You are good at telling jokes.";
36+
37+
public MyOpenAIChatClientAgent(ChatClient client, ILoggerFactory? loggerFactory = null) :
38+
base(client, instructions: JokerInstructions, name: JokerName, loggerFactory: loggerFactory)
39+
{
40+
}
41+
}

dotnet/samples/GettingStarted/GettingStarted.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<RootNamespace>GettingStarted</RootNamespace>
55
<OutputType>Library</OutputType>
66
<UserSecretsId>5ee045b0-aea3-4f08-8d31-32d1a6f8fed0</UserSecretsId>
7-
<NoWarn>$(NoWarn);CA1707;CA1716;IDE0009;IDE1006;OPENAI001;</NoWarn>
7+
<NoWarn>$(NoWarn);CA1707;CA1716;IDE0009;IDE1006; OPENAI001;</NoWarn>
88
<ImplicitUsings>enable</ImplicitUsings>
99
<InjectSharedSamples>true</InjectSharedSamples>
1010
<InjectSharedThrow>true</InjectSharedThrow>
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
// Copyright (c) Microsoft. All rights reserved.
2+
3+
using Azure.AI.Agents.Persistent;
4+
using Azure.Identity;
5+
using Microsoft.Agents.Orchestration;
6+
using Microsoft.Extensions.AI.Agents;
7+
using Microsoft.Shared.Samples;
8+
9+
namespace Orchestration;
10+
11+
/// <summary>
12+
/// Demonstrates how to use the <see cref="SequentialOrchestration"/> for
13+
/// executing multiple Foundry agents in sequence.
14+
/// </summary>
15+
public class SequentialOrchestration_Foundry_Agents(ITestOutputHelper output) : OrchestrationSample(output)
16+
{
17+
[Theory]
18+
[InlineData(false)]
19+
[InlineData(true)]
20+
public async Task RunOrchestrationAsync(bool streamedResponse)
21+
{
22+
// Get a client to create server side agents with.
23+
var persistentAgentsClient = new PersistentAgentsClient(TestConfiguration.AzureAI.Endpoint, new AzureCliCredential());
24+
var model = TestConfiguration.OpenAI.ChatModelId;
25+
26+
// Define the agents
27+
AIAgent analystAgent =
28+
await persistentAgentsClient.CreateAIAgentAsync(
29+
model,
30+
name: "Analyst",
31+
instructions:
32+
"""
33+
You are a marketing analyst. Given a product description, identify:
34+
- Key features
35+
- Target audience
36+
- Unique selling points
37+
""",
38+
description: "A agent that extracts key concepts from a product description.");
39+
AIAgent writerAgent =
40+
await persistentAgentsClient.CreateAIAgentAsync(
41+
model,
42+
name: "copywriter",
43+
instructions:
44+
"""
45+
You are a marketing copywriter. Given a block of text describing features, audience, and USPs,
46+
compose a compelling marketing copy (like a newsletter section) that highlights these points.
47+
Output should be short (around 150 words), output just the copy as a single text block.
48+
""",
49+
description: "An agent that writes a marketing copy based on the extracted concepts.");
50+
AIAgent editorAgent =
51+
await persistentAgentsClient.CreateAIAgentAsync(
52+
model,
53+
name: "editor",
54+
instructions:
55+
"""
56+
You are an editor. Given the draft copy, correct grammar, improve clarity, ensure consistent tone,
57+
give format and make it polished. Output the final improved copy as a single text block.
58+
""",
59+
description: "An agent that formats and proofreads the marketing copy.");
60+
61+
// Create a monitor to capturing agent responses (via ResponseCallback)
62+
// to display at the end of this sample. (optional)
63+
// NOTE: Create your own callback to capture responses in your application or service.
64+
OrchestrationMonitor monitor = new();
65+
// Define the orchestration
66+
SequentialOrchestration orchestration =
67+
new(analystAgent, writerAgent, editorAgent)
68+
{
69+
LoggerFactory = this.LoggerFactory,
70+
ResponseCallback = monitor.ResponseCallback,
71+
StreamingResponseCallback = streamedResponse ? monitor.StreamingResultCallback : null,
72+
};
73+
74+
// Run the orchestration
75+
string input = "An eco-friendly stainless steel water bottle that keeps drinks cold for 24 hours";
76+
Console.WriteLine($"\n# INPUT: {input}\n");
77+
AgentRunResponse result = await orchestration.RunAsync(input);
78+
Console.WriteLine($"\n# RESULT: {result}");
79+
80+
this.DisplayHistory(monitor.History);
81+
82+
// Cleanup
83+
await persistentAgentsClient.Administration.DeleteAgentAsync(editorAgent.Id);
84+
await persistentAgentsClient.Administration.DeleteAgentAsync(writerAgent.Id);
85+
await persistentAgentsClient.Administration.DeleteAgentAsync(analystAgent.Id);
86+
}
87+
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
// Copyright (c) Microsoft. All rights reserved.
2+
3+
using Microsoft.Agents.Orchestration;
4+
using Microsoft.Extensions.AI.Agents;
5+
using Microsoft.Shared.Samples;
6+
using OpenAI;
7+
8+
namespace Orchestration;
9+
10+
/// <summary>
11+
/// Demonstrates how to use the <see cref="SequentialOrchestration"/> for
12+
/// executing multiple heterogeneous agents in sequence.
13+
/// </summary>
14+
public class SequentialOrchestration_Multi_Agent(ITestOutputHelper output) : OrchestrationSample(output)
15+
{
16+
[Theory]
17+
[InlineData(false)]
18+
[InlineData(true)]
19+
public async Task RunOrchestrationAsync(bool streamedResponse)
20+
{
21+
var openAIClient = new OpenAIClient(TestConfiguration.OpenAI.ApiKey);
22+
var model = TestConfiguration.OpenAI.ChatModelId;
23+
24+
// Define the agents
25+
AIAgent analystAgent =
26+
openAIClient.GetChatClient(model).CreateAIAgent(
27+
name: "Analyst",
28+
instructions:
29+
"""
30+
You are a marketing analyst. Given a product description, identify:
31+
- Key features
32+
- Target audience
33+
- Unique selling points
34+
""",
35+
description: "A agent that extracts key concepts from a product description.");
36+
AIAgent writerAgent =
37+
openAIClient.GetOpenAIResponseClient(model).CreateAIAgent(
38+
name: "copywriter",
39+
instructions:
40+
"""
41+
You are a marketing copywriter. Given a block of text describing features, audience, and USPs,
42+
compose a compelling marketing copy (like a newsletter section) that highlights these points.
43+
Output should be short (around 150 words), output just the copy as a single text block.
44+
""",
45+
description: "An agent that writes a marketing copy based on the extracted concepts.");
46+
AIAgent editorAgent =
47+
openAIClient.GetAssistantClient().CreateAIAgent(
48+
model,
49+
name: "editor",
50+
instructions:
51+
"""
52+
You are an editor. Given the draft copy, correct grammar, improve clarity, ensure consistent tone,
53+
give format and make it polished. Output the final improved copy as a single text block.
54+
""",
55+
description: "An agent that formats and proofreads the marketing copy.");
56+
57+
// Create a monitor to capturing agent responses (via ResponseCallback)
58+
// to display at the end of this sample. (optional)
59+
// NOTE: Create your own callback to capture responses in your application or service.
60+
OrchestrationMonitor monitor = new();
61+
// Define the orchestration
62+
SequentialOrchestration orchestration =
63+
new(analystAgent, writerAgent, editorAgent)
64+
{
65+
LoggerFactory = this.LoggerFactory,
66+
ResponseCallback = monitor.ResponseCallback,
67+
StreamingResponseCallback = streamedResponse ? monitor.StreamingResultCallback : null,
68+
};
69+
70+
// Run the orchestration
71+
string input = "An eco-friendly stainless steel water bottle that keeps drinks cold for 24 hours";
72+
Console.WriteLine($"\n# INPUT: {input}\n");
73+
AgentRunResponse result = await orchestration.RunAsync(input);
74+
Console.WriteLine($"\n# RESULT: {result}");
75+
76+
this.DisplayHistory(monitor.History);
77+
78+
// Cleanup
79+
var assistantClient = openAIClient.GetAssistantClient();
80+
await assistantClient.DeleteAssistantAsync(editorAgent.Id);
81+
// Need to know how to get the assistant thread ID to delete the thread (issue #260)
82+
}
83+
}
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
// Copyright (c) Microsoft. All rights reserved.
2+
3+
using Azure.AI.Agents.Persistent;
4+
using Azure.Identity;
5+
using Microsoft.Extensions.AI.Agents;
6+
using Microsoft.Shared.Samples;
7+
8+
namespace Providers;
9+
10+
/// <summary>
11+
/// Shows how to use <see cref="AIAgent"/> with Azure AI Persistent Agents.
12+
/// </summary>
13+
/// <remarks>
14+
/// Running "az login" command in terminal is required for authentication with Azure AI service.
15+
/// </remarks>
16+
public sealed class AIAgent_With_AzureAIAgentsPersistent(ITestOutputHelper output) : AgentSample(output)
17+
{
18+
private const string JokerName = "Joker";
19+
private const string JokerInstructions = "You are good at telling jokes.";
20+
21+
[Fact]
22+
public async Task GetWithAzureAIAgentsPersistent()
23+
{
24+
// Get a client to create server side agents with.
25+
var persistentAgentsClient = new PersistentAgentsClient(TestConfiguration.AzureAI.Endpoint, new AzureCliCredential());
26+
27+
// Create a service side persistent agent.
28+
var persistentAgent = await persistentAgentsClient.Administration.CreateAgentAsync(
29+
model: TestConfiguration.AzureAI.DeploymentName!,
30+
name: JokerName,
31+
instructions: JokerInstructions);
32+
33+
// Get a server side agent.
34+
AIAgent agent = await persistentAgentsClient.GetAIAgentAsync(persistentAgent.Value.Id);
35+
36+
// Start a new thread for the agent conversation.
37+
AgentThread thread = agent.GetNewThread();
38+
39+
// Respond to user input
40+
await RunAgentAsync("Tell me a joke about a pirate.");
41+
await RunAgentAsync("Now add some emojis to the joke.");
42+
43+
// Local function to run agent and display the conversation messages for the thread.
44+
async Task RunAgentAsync(string input)
45+
{
46+
Console.WriteLine(input);
47+
48+
var response = await agent.RunAsync(input, thread);
49+
50+
Console.WriteLine(response);
51+
}
52+
53+
// Cleanup
54+
await persistentAgentsClient.Threads.DeleteThreadAsync(thread.Id);
55+
await persistentAgentsClient.Administration.DeleteAgentAsync(agent.Id);
56+
}
57+
58+
[Fact]
59+
public async Task CreateWithAzureAIAgentsPersistent()
60+
{
61+
// Get a client to create server side agents with.
62+
var persistentAgentsClient = new PersistentAgentsClient(TestConfiguration.AzureAI.Endpoint, new AzureCliCredential());
63+
64+
// Create a server side persistent agent.
65+
AIAgent agent = await persistentAgentsClient.CreateAIAgentAsync(
66+
model: TestConfiguration.AzureAI.DeploymentName!,
67+
name: JokerName,
68+
instructions: JokerInstructions);
69+
70+
// Start a new thread for the agent conversation.
71+
AgentThread thread = agent.GetNewThread();
72+
73+
// Respond to user input
74+
await RunAgentAsync("Tell me a joke about a pirate.");
75+
await RunAgentAsync("Now add some emojis to the joke.");
76+
77+
// Local function to run agent and display the conversation messages for the thread.
78+
async Task RunAgentAsync(string input)
79+
{
80+
Console.WriteLine(input);
81+
82+
var response = await agent.RunAsync(input, thread);
83+
84+
Console.WriteLine(response);
85+
}
86+
87+
// Cleanup
88+
await persistentAgentsClient.Threads.DeleteThreadAsync(thread.Id);
89+
await persistentAgentsClient.Administration.DeleteAgentAsync(agent.Id);
90+
}
91+
}
Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,33 +3,31 @@
33
using System.ClientModel;
44
using Azure.AI.OpenAI;
55
using Azure.Identity;
6-
using Microsoft.Extensions.AI;
76
using Microsoft.Extensions.AI.Agents;
87
using Microsoft.Shared.Samples;
8+
using OpenAI;
99

1010
namespace Providers;
1111

1212
/// <summary>
1313
/// End-to-end sample showing how to use <see cref="ChatClientAgent"/> with Azure OpenAI Chat Completion.
1414
/// </summary>
15-
public sealed class ChatClientAgent_With_AzureOpenAIChatCompletion(ITestOutputHelper output) : AgentSample(output)
15+
public sealed class AIAgent_With_AzureOpenAIChatCompletion(ITestOutputHelper output) : AgentSample(output)
1616
{
1717
private const string JokerName = "Joker";
1818
private const string JokerInstructions = "You are good at telling jokes.";
1919

2020
[Fact]
2121
public async Task RunWithChatCompletion()
2222
{
23-
// Get the chat client to use for the agent.
24-
using var chatClient = ((TestConfiguration.AzureOpenAI.ApiKey is null)
23+
// Get the OpenAI client to use for the agent.
24+
var openAIClient = (TestConfiguration.AzureOpenAI.ApiKey is null)
2525
// Use Azure CLI credentials if API key is not provided.
2626
? new AzureOpenAIClient(TestConfiguration.AzureOpenAI.Endpoint, new AzureCliCredential())
27-
: new AzureOpenAIClient(TestConfiguration.AzureOpenAI.Endpoint, new ApiKeyCredential(TestConfiguration.AzureOpenAI.ApiKey)))
28-
.GetChatClient(TestConfiguration.AzureOpenAI.DeploymentName)
29-
.AsIChatClient();
27+
: new AzureOpenAIClient(TestConfiguration.AzureOpenAI.Endpoint, new ApiKeyCredential(TestConfiguration.AzureOpenAI.ApiKey));
3028

31-
// Define the agent
32-
ChatClientAgent agent = new(chatClient, JokerInstructions, JokerName);
29+
// Create the agent
30+
AIAgent agent = openAIClient.GetChatClient(TestConfiguration.AzureOpenAI.DeploymentName).CreateAIAgent(JokerInstructions, JokerName);
3331

3432
// Start a new thread for the agent conversation.
3533
AgentThread thread = agent.GetNewThread();

0 commit comments

Comments
 (0)