Skip to content

Commit

Permalink
Create classes ad sample code for Azure Functions (#47319)
Browse files Browse the repository at this point in the history
* Add Azure Functions
  • Loading branch information
nick863 authored Dec 13, 2024
1 parent 68fcfb6 commit b36b9cf
Show file tree
Hide file tree
Showing 65 changed files with 3,693 additions and 101 deletions.
6 changes: 6 additions & 0 deletions sdk/ai/Azure.AI.Projects/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,16 @@

### Features Added

* Added `AzureFunctionToolDefinition` support to inform Agents about Azure Functions.
* Added `OpenApiTool` for Agents, which creates and executes a REST function defined by an OpenAPI spec.
* Add `parallelToolCalls` parameter to `CreateRunRequest`, `CreateRunAsync`, `CreateRunStreaming` and `CreateRunStreamingAsync`, which allows parallel tool execution for Agents.

### Breaking Changes

### Bugs Fixed

* Fix a bug preventing additional messages to be created when using `CreateRunStreamingAsync` and `CreateRunAsync` see [issue](https://github.com/Azure/azure-sdk-for-net/issues/47244).

### Other Changes

## 1.0.0-beta.1 (2024-11-19)
Expand Down
123 changes: 123 additions & 0 deletions sdk/ai/Azure.AI.Projects/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ Use the AI Projects client library to:
- [Retrieve messages](#retrieve-messages)
- [File search](#file-search)
- [Function call](#function-call)
- [Azure function call](#azure-function-call)
- [Azure Function Call](#create-agent-with-azure-function-call)
- [OpenAPI](#create-agent-with-openapi)
- [Troubleshooting](#troubleshooting)
- [Next steps](#next-steps)
- [Contributing](#contributing)
Expand Down Expand Up @@ -347,6 +350,126 @@ while (runResponse.Value.Status == RunStatus.Queued
|| runResponse.Value.Status == RunStatus.InProgress);
```

#### Azure function call

We also can use Azure Function from inside the agent. In the example below we are calling function "foo", which responds "Bar". In this example we create `AzureFunctionToolDefinition` object, with the function name, description, input and output queues, followed by function parameters.
```C# Snippet:AzureFunctionsDefineFunctionTools
AzureFunctionToolDefinition azureFnTool = new(
name: "foo",
description: "Get answers from the foo bot.",
inputBinding: new AzureFunctionBinding(
new AzureFunctionStorageQueue(
queueName: "azure-function-foo-input",
storageServiceEndpoint: storageQueueUri
)
),
outputBinding: new AzureFunctionBinding(
new AzureFunctionStorageQueue(
queueName: "azure-function-tool-output",
storageServiceEndpoint: storageQueueUri
)
),
parameters: BinaryData.FromObjectAsJson(
new
{
Type = "object",
Properties = new
{
query = new
{
Type = "string",
Description = "The question to ask.",
},
outputqueueuri = new
{
Type = "string",
Description = "The full output queue uri."
}
},
},
new JsonSerializerOptions() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }
)
);
```

Note that in this scenario we are asking agent to supply storage queue URI to the azure function whenever it is called.
```C# Snippet:AzureFunctionsCreateAgentWithFunctionTools
Response<Agent> agentResponse = await client.CreateAgentAsync(
model: "gpt-4",
name: "azure-function-agent-foo",
instructions: "You are a helpful support agent. Use the provided function any "
+ "time the prompt contains the string 'What would foo say?'. When you invoke "
+ "the function, ALWAYS specify the output queue uri parameter as "
+ $"'{storageQueueUri}/azure-function-tool-output'. Always responds with "
+ "\"Foo says\" and then the response from the tool.",
tools: new List<ToolDefinition> { azureFnTool }
);
Agent agent = agentResponse.Value;
```

After we have created a message with request to ask "What would foo say?", we need to wait while the run is in queued, in progress or requires action states.
```C# Snippet:AzureFunctionsHandlePollingWithRequiredAction
Response<ThreadMessage> messageResponse = await client.CreateMessageAsync(
thread.Id,
MessageRole.User,
"What is the most prevalent element in the universe? What would foo say?");
ThreadMessage message = messageResponse.Value;

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

do
{
await Task.Delay(TimeSpan.FromMilliseconds(500));
runResponse = await client.GetRunAsync(thread.Id, runResponse.Value.Id);
}
while (runResponse.Value.Status == RunStatus.Queued
|| runResponse.Value.Status == RunStatus.InProgress
|| runResponse.Value.Status == RunStatus.RequiresAction);
```

#### Create Agent With OpenAPI

OpenAPI specifications describe REST operations against a specific endpoint. Agents SDK can read an OpenAPI spec, create a function from it, and call that function against the REST endpoint without additional client-side execution.

Here is an example creating an OpenAPI tool (using anonymous authentication):
```C# Snippet:OpenAPIDefineFunctionTools
OpenApiAnonymousAuthDetails oaiAuth = new();
OpenApiToolDefinition openapiTool = new(
name: "get_weather",
description: "Retrieve weather information for a location",
spec: BinaryData.FromBytes(File.ReadAllBytes(file_path)),
auth: oaiAuth
);

Response<Agent> agentResponse = await client.CreateAgentAsync(
model: "gpt-4",
name: "azure-function-agent-foo",
instructions: "You are a helpful assistant.",
tools: new List<ToolDefinition> { openapiTool }
);
Agent agent = agentResponse.Value;
```

In this example we are using the `weather_openapi.json` file and agent will request the wttr.in website for the weather in a location fron the prompt.
```C# Snippet:OpenAPIHandlePollingWithRequiredAction
Response<ThreadMessage> messageResponse = await client.CreateMessageAsync(
thread.Id,
MessageRole.User,
"What's the weather in Seattle?");
ThreadMessage message = messageResponse.Value;

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

do
{
await Task.Delay(TimeSpan.FromMilliseconds(500));
runResponse = await client.GetRunAsync(thread.Id, runResponse.Value.Id);
}
while (runResponse.Value.Status == RunStatus.Queued
|| runResponse.Value.Status == RunStatus.InProgress
|| runResponse.Value.Status == RunStatus.RequiresAction);
```

## Troubleshooting

Any operation that fails will throw a [RequestFailedException][RequestFailedException]. The exception's `code` will hold the HTTP response status code. The exception's `message` contains a detailed message that may be helpful in diagnosing the issue:
Expand Down
Loading

0 comments on commit b36b9cf

Please sign in to comment.