Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
9428389
Azure AI Agents
Oct 1, 2024
a96d37a
Move to ai folder
Oct 1, 2024
719865d
Public API
Oct 1, 2024
f94acf0
Regen
Oct 9, 2024
ce11704
Merge branch 'main' of https://github.com/Azure/azure-sdk-for-net int…
Oct 10, 2024
0805a9b
Fix custom code
Oct 10, 2024
f4effd3
Fix factory
Oct 11, 2024
a36bef2
Fix issues
Oct 11, 2024
7f95fee
Update API
Oct 11, 2024
5b92ea7
Add a working sample
Oct 12, 2024
abf3be3
Update samples
Oct 14, 2024
8cb71bf
Update sample
Oct 15, 2024
1ff225f
Update samples
Oct 15, 2024
9f8ae77
Add connection string support
Oct 17, 2024
f272773
Remove model namespace
Oct 17, 2024
3edb6eb
Add config
Oct 17, 2024
09bf4ab
Cleanup
Oct 17, 2024
1e3957a
Update generated code
Oct 22, 2024
4243f96
Update package name
Oct 22, 2024
a620eb4
Add streaming
Oct 24, 2024
7b3bff7
Update package name to Azure.AI.Projects
Oct 24, 2024
ee76588
Add adapters to convert Azure.Core Response to SCM ClientResult to su…
annelo-msft Oct 24, 2024
70d2f82
Update public API
Oct 24, 2024
90cc20a
Merge branch 'main' of https://github.com/Azure/azure-sdk-for-net int…
Oct 25, 2024
aaac798
Regen code
Oct 25, 2024
dbfbd35
Rename OpenAI to Agent
Oct 28, 2024
e455117
[Azure.AI.Projects] Add support for Inference (#46972)
ShivangiReja Nov 5, 2024
e1fa8fe
Regenerate code with the latest TypeSpec
Nov 11, 2024
fbab0be
Add bing grounding sample
Nov 11, 2024
98c009d
Cleanup
Nov 11, 2024
cd55a6f
Add azure ai sample
Nov 12, 2024
bbc0c2f
Update README
Nov 14, 2024
6478b7d
Merge branch 'main' of https://github.com/Azure/azure-sdk-for-net int…
Nov 14, 2024
a6a9979
Code regen
Nov 14, 2024
5f47da9
Merge branch 'main' of https://github.com/Azure/azure-sdk-for-net int…
Nov 14, 2024
c7ed2d8
Update API
Nov 14, 2024
8cbe0d5
Update sample
Nov 14, 2024
b497659
Update README
Nov 14, 2024
6c89204
Add support for unbuffered response streams in Azure clients (#47166)
annelo-msft Nov 14, 2024
d1fa538
Update link
Nov 14, 2024
b9fd45f
Merge branch 'feature/azure-ai-sdk/agents' of https://github.com/Azur…
Nov 14, 2024
61550fb
Update eng/Packages.Data.props
ShivangiReja Nov 14, 2024
34dedad
Update sdk/ai/Azure.AI.Projects/README.md
ShivangiReja Nov 14, 2024
140d7ce
Fix csproj file
Nov 14, 2024
0dd6991
Merge branch 'feature/azure-ai-sdk/agents' of https://github.com/Azur…
Nov 14, 2024
8f05825
Setup a separate pipeline for projects
Nov 14, 2024
38e6942
Fix readme
Nov 14, 2024
b052b6e
Add empty line
Nov 14, 2024
2b9bd49
Update paths
Nov 14, 2024
49ce764
Fix sample
Nov 15, 2024
7442be1
Fix pielines
Nov 15, 2024
32750a4
Fix package name
Nov 15, 2024
18e8496
Feedback
Nov 15, 2024
5131594
Fix link issue
Nov 15, 2024
3b350f4
Azure.AI.projects sample update
Nov 15, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Update samples
  • Loading branch information
ShivangiReja committed Oct 14, 2024
commit abf3be3cf2607696433a8355bfe12ba0d1349129
87 changes: 39 additions & 48 deletions sdk/ai/Azure.AI.Client/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,12 @@ creating, running, and using assistants and threads.

To get started, create an `AssistantsClient`:
```C# Snippet:OverviewCreateClient
AssistantsClient client = isAzureOpenAI
? new AssistantsClient(new Uri(azureResourceUrl), new AzureKeyCredential(azureApiKey))
: new AssistantsClient(nonAzureApiKey);
Agents client = new AzureAIClient(
endpoint: new Uri(Environment.GetEnvironmentVariable("AZURE_AI_ENDPOINT")),
subscriptionId: Environment.GetEnvironmentVariable("AZURE_AI_SUBSCRIPTION_ID"),
resourceGroupName: Environment.GetEnvironmentVariable("AZURE_AI_RESOURCE_GROUP_NAME"),
projectName: Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_NAME"),
credential: new DefaultAzureCredential()).GetAgentsClient();
```

> **NOTE**: The Assistants API should always be used from a trusted device. Because the same authentication mechanism for running threads also allows changing persistent resources like Assistant instructions, a malicious user could extract an API key and modify Assistant behavior for other customers.
Expand All @@ -51,21 +54,19 @@ For an overview of Assistants and the pertinent key concepts like Threads, Messa
### Examples

With an authenticated client, an assistant can be created:
```C# Snippet:OverviewCreateAssistant
Response<Assistant> assistantResponse = await client.CreateAssistantAsync(
new AssistantCreationOptions("gpt-4-1106-preview")
{
Name = "Math Tutor",
Instructions = "You are a personal math tutor. Write and run code to answer math questions.",
Tools = { new CodeInterpreterToolDefinition() }
});
Assistant assistant = assistantResponse.Value;
```C# Snippet:OverviewCreateAgent
Response<Agent> agentResponse = await client.CreateAgentAsync(
model: "gpt-4-1106-preview",
name: "Math Tutor",
instructions: "You are a personal math tutor. Write and run code to answer math questions.",
tools: new List<ToolDefinition> { new CodeInterpreterToolDefinition() });
Agent agent = agentResponse.Value;
```

Next, create a thread:
```C# Snippet:OverviewCreateThread
Response<AssistantThread> threadResponse = await client.CreateThreadAsync();
AssistantThread thread = threadResponse.Value;
Response<AgentThread> threadResponse = await client.CreateThreadAsync();
AgentThread thread = threadResponse.Value;
```

With a thread created, messages can be created on it:
Expand All @@ -81,10 +82,8 @@ A run can then be started that evaluates the thread against an assistant:
```C# Snippet:OverviewCreateRun
Response<ThreadRun> runResponse = await client.CreateRunAsync(
thread.Id,
new CreateRunOptions(assistant.Id)
{
AdditionalInstructions = "Please address the user as Jane Doe. The user has a premium account.",
});
agent.Id,
additionalInstructions: "Please address the user as Jane Doe. The user has a premium account.");
ThreadRun run = runResponse.Value;
```

Expand Down Expand Up @@ -135,28 +134,26 @@ Example output from this sequence:

Files can be uploaded and then referenced by assistants or messages. First, use the generalized upload API with a
purpose of 'assistants' to make a file ID available:
```C# Snippet:UploadAssistantFilesToUse
```C# Snippet:UploadAgentFilesToUse
File.WriteAllText(
path: "sample_file_for_upload.txt",
contents: "The word 'apple' uses the code 442345, while the word 'banana' uses the code 673457.");
Response<OpenAIFile> uploadAssistantFileResponse = await client.UploadFileAsync(
Response<OpenAIFile> uploadAgentFileResponse = await client.UploadFileAsync(
localFilePath: "sample_file_for_upload.txt",
purpose: OpenAIFilePurpose.Assistants);
OpenAIFile uploadedAssistantFile = uploadAssistantFileResponse.Value;
purpose: OpenAIFilePurpose.Agents);
OpenAIFile uploadedAgentFile = uploadAgentFileResponse.Value;
```

Once uploaded, the file ID can then be provided to an assistant upon creation. Note that file IDs will only be used
if an appropriate tool like Code Interpreter or Retrieval is enabled.
```C# Snippet:CreateAssistantWithFiles
Response<Assistant> assistantResponse = await client.CreateAssistantAsync(
new AssistantCreationOptions("gpt-4-1106-preview")
{
Name = "SDK Test Assistant - Retrieval",
Instructions = "You are a helpful assistant that can help fetch data from files you know about.",
Tools = { new RetrievalToolDefinition() },
FileIds = { uploadedAssistantFile.Id },
});
Assistant assistant = assistantResponse.Value;
```C# Snippet:CreateAgentWithFiles
Response<Agent> agentResponse = await client.CreateAgentAsync(
model: "gpt-4-1106-preview",
name: "SDK Test Agent - Retrieval",
instructions: "You are a helpful agent that can help fetch data from files you know about.",
tools: new List<ToolDefinition> { new FileSearchToolDefinition() });
//toolResources: new ToolResources() { FileSearch = new FileSearchToolResource() { VectorStoreIds.Add(uploadedAgentFile.Id) },
Agent agent = agentResponse.Value;
```

With a file ID association and a supported tool enabled, the assistant will then be able to consume the associated
Expand Down Expand Up @@ -238,23 +235,17 @@ FunctionToolDefinition getCurrentWeatherAtLocationTool = new(

With the functions defined in their appropriate tools, an assistant can be now created that has those tools enabled:

```C# Snippet:FunctionsCreateAssistantWithFunctionTools
Response<Assistant> assistantResponse = await client.CreateAssistantAsync(
// note: parallel function calling is only supported with newer models like gpt-4-1106-preview
new AssistantCreationOptions("gpt-4-1106-preview")
{
Name = "SDK Test Assistant - Functions",
Instructions = "You are a weather bot. Use the provided functions to help answer questions. "
```C# Snippet:FunctionsCreateAgentWithFunctionTools
// note: parallel function calling is only supported with newer models like gpt-4-1106-preview
Response<Agent> agentResponse = await client.CreateAgentAsync(
model: "gpt-4-1106-preview",
name: "SDK Test Agent - Functions",
instructions: "You are a weather bot. Use the provided functions to help answer questions. "
+ "Customize your responses to the user's preferences as much as possible and use friendly "
+ "nicknames for cities whenever possible.",
Tools =
{
getUserFavoriteCityTool,
getCityNicknameTool,
getCurrentWeatherAtLocationTool,
},
});
Assistant assistant = assistantResponse.Value;
tools: new List<ToolDefinition> { getUserFavoriteCityTool, getCityNicknameTool, getCurrentWeatherAtLocationTool }
);
Agent agent = agentResponse.Value;
```

If the assistant calls tools, the calling code will need to resolve `ToolCall` instances into matching
Expand Down Expand Up @@ -410,7 +401,7 @@ You have the flexibility to explicitly select a supported service API version wh

For example,

```C# Snippet:Create<YourService>ClientForSpecificApiVersion
```C#
Uri endpoint = new Uri("<your endpoint>");
DefaultAzureCredential credential = new DefaultAzureCredential();
<YourService>ClientOptions options = new <YourService>ClientOptions(<YourService>ClientOptions.ServiceVersion.<API Version>)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

// <auto-generated/>

#nullable disable

using System;
Expand All @@ -15,10 +13,10 @@

namespace Azure.AI.Client.Tests;

public partial class Samples_Agents
public partial class Sample_Agent_Basics
{
[Test]
public async Task AgentSample()
public async Task BasicExample()
{
#region Snippet:OverviewCreateClient
Agents client = new AzureAIClient(
Expand All @@ -29,18 +27,17 @@ public async Task AgentSample()
credential: new DefaultAzureCredential()).GetAgentsClient();
#endregion

// Step 1: Create an assistant
#region Snippet:OverviewCreateAssistant
// Step 1: Create an agent
#region Snippet:OverviewCreateAgent
Response<Agent> agentResponse = await client.CreateAgentAsync(
model: "gpt-4-1106-preview",
name: "Math Tutor",
instructions: "You are a personal math tutor. Write and run code to answer math questions.",
tools: new List<ToolDefinition> { new CodeInterpreterToolDefinition() }
);
Agent assistant = agentResponse.Value;
tools: new List<ToolDefinition> { new CodeInterpreterToolDefinition() });
Agent agent = agentResponse.Value;
#endregion

// Intermission: assistant should now be listed
// Intermission: agent should now be listed

Response<PageableList<Agent>> agentListResponse = await client.GetAgentsAsync();

Expand Down Expand Up @@ -70,7 +67,7 @@ public async Task AgentSample()
#region Snippet:OverviewCreateRun
Response<ThreadRun> runResponse = await client.CreateRunAsync(
thread.Id,
assistant.Id,
agent.Id,
additionalInstructions: "Please address the user as Jane Doe. The user has a premium account.");
ThreadRun run = runResponse.Value;
#endregion
Expand Down
184 changes: 184 additions & 0 deletions sdk/ai/Azure.AI.Client/tests/Samples/Sample_Agent_Functions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

#nullable disable

using System;
using System.Collections.Generic;
using System.Text.Json;
using System.Threading.Tasks;
using Azure.AI.Client.Models;
using Azure.Identity;
using NUnit.Framework;

namespace Azure.AI.Client.Tests;

public partial class Sample_Agent_Functions
{
[Test]
public async Task FunctionCallingExample()
{
Agents client = new AzureAIClient(
endpoint: new Uri(Environment.GetEnvironmentVariable("AZURE_AI_ENDPOINT")),
subscriptionId: Environment.GetEnvironmentVariable("AZURE_AI_SUBSCRIPTION_ID"),
resourceGroupName: Environment.GetEnvironmentVariable("AZURE_AI_RESOURCE_GROUP_NAME"),
projectName: Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_NAME"),
credential: new DefaultAzureCredential()).GetAgentsClient();

#region Snippet:FunctionsDefineFunctionTools
// Example of a function that defines no parameters
string GetUserFavoriteCity() => "Seattle, WA";
FunctionToolDefinition getUserFavoriteCityTool = new("getUserFavoriteCity", "Gets the user's favorite city.");
// Example of a function with a single required parameter
string GetCityNickname(string location) => location switch
{
"Seattle, WA" => "The Emerald City",
_ => throw new NotImplementedException(),
};
FunctionToolDefinition getCityNicknameTool = new(
name: "getCityNickname",
description: "Gets the nickname of a city, e.g. 'LA' for 'Los Angeles, CA'.",
parameters: BinaryData.FromObjectAsJson(
new
{
Type = "object",
Properties = new
{
Location = new
{
Type = "string",
Description = "The city and state, e.g. San Francisco, CA",
},
},
Required = new[] { "location" },
},
new JsonSerializerOptions() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }));
// Example of a function with one required and one optional, enum parameter
string GetWeatherAtLocation(string location, string temperatureUnit = "f") => location switch
{
"Seattle, WA" => temperatureUnit == "f" ? "70f" : "21c",
_ => throw new NotImplementedException()
};
FunctionToolDefinition getCurrentWeatherAtLocationTool = new(
name: "getCurrentWeatherAtLocation",
description: "Gets the current weather at a provided location.",
parameters: BinaryData.FromObjectAsJson(
new
{
Type = "object",
Properties = new
{
Location = new
{
Type = "string",
Description = "The city and state, e.g. San Francisco, CA",
},
Unit = new
{
Type = "string",
Enum = new[] { "c", "f" },
},
},
Required = new[] { "location" },
},
new JsonSerializerOptions() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }));
#endregion

#region Snippet:FunctionsHandleFunctionCalls
ToolOutput GetResolvedToolOutput(RequiredToolCall toolCall)
{
if (toolCall is RequiredFunctionToolCall functionToolCall)
{
if (functionToolCall.Name == getUserFavoriteCityTool.Name)
{
return new ToolOutput(toolCall, GetUserFavoriteCity());
}
using JsonDocument argumentsJson = JsonDocument.Parse(functionToolCall.Arguments);
if (functionToolCall.Name == getCityNicknameTool.Name)
{
string locationArgument = argumentsJson.RootElement.GetProperty("location").GetString();
return new ToolOutput(toolCall, GetCityNickname(locationArgument));
}
if (functionToolCall.Name == getCurrentWeatherAtLocationTool.Name)
{
string locationArgument = argumentsJson.RootElement.GetProperty("location").GetString();
if (argumentsJson.RootElement.TryGetProperty("unit", out JsonElement unitElement))
{
string unitArgument = unitElement.GetString();
return new ToolOutput(toolCall, GetWeatherAtLocation(locationArgument, unitArgument));
}
return new ToolOutput(toolCall, GetWeatherAtLocation(locationArgument));
}
}
return null;
}
#endregion

#region Snippet:FunctionsCreateAgentWithFunctionTools
// note: parallel function calling is only supported with newer models like gpt-4-1106-preview
Response<Agent> agentResponse = await client.CreateAgentAsync(
model: "gpt-4-1106-preview",
name: "SDK Test Agent - Functions",
instructions: "You are a weather bot. Use the provided functions to help answer questions. "
+ "Customize your responses to the user's preferences as much as possible and use friendly "
+ "nicknames for cities whenever possible.",
tools: new List<ToolDefinition> { getUserFavoriteCityTool, getCityNicknameTool, getCurrentWeatherAtLocationTool }
);
Agent agent = agentResponse.Value;
#endregion

Response<AgentThread> threadResponse = await client.CreateThreadAsync();
AgentThread thread = threadResponse.Value;

Response<ThreadMessage> messageResponse = await client.CreateMessageAsync(
thread.Id,
MessageRole.User,
"What's the weather like in my favorite city?");
ThreadMessage message = messageResponse.Value;

Response<ThreadRun> runResponse = await client.CreateRunAsync(thread, agent);

#region Snippet:FunctionsHandlePollingWithRequiredAction
do
{
await Task.Delay(TimeSpan.FromMilliseconds(500));
runResponse = await client.GetRunAsync(thread.Id, runResponse.Value.Id);

if (runResponse.Value.Status == RunStatus.RequiresAction
&& runResponse.Value.RequiredAction is SubmitToolOutputsAction submitToolOutputsAction)
{
List<ToolOutput> toolOutputs = new();
foreach (RequiredToolCall toolCall in submitToolOutputsAction.ToolCalls)
{
toolOutputs.Add(GetResolvedToolOutput(toolCall));
}
runResponse = await client.SubmitToolOutputsToRunAsync(runResponse.Value, toolOutputs);
}
}
while (runResponse.Value.Status == RunStatus.Queued
|| runResponse.Value.Status == RunStatus.InProgress);
#endregion

Response<PageableList<ThreadMessage>> afterRunMessagesResponse
= await client.GetMessagesAsync(thread.Id);
IReadOnlyList<ThreadMessage> messages = afterRunMessagesResponse.Value.Data;

// Note: messages iterate from newest to oldest, with the messages[0] being the most recent
foreach (ThreadMessage threadMessage in messages)
{
Console.Write($"{threadMessage.CreatedAt:yyyy-MM-dd HH:mm:ss} - {threadMessage.Role,10}: ");
foreach (MessageContent contentItem in threadMessage.ContentItems)
{
if (contentItem is MessageTextContent textItem)
{
Console.Write(textItem.Text);
}
else if (contentItem is MessageImageFileContent imageFileItem)
{
Console.Write($"<image from ID: {imageFileItem.FileId}");
}
Console.WriteLine();
}
}
}
}
Loading