Skip to content

Commit a763d12

Browse files
authored
Update azure-functions-samples.md
Removed sync and async headings. removed async examples and intended code blocks.
1 parent eb00472 commit a763d12

File tree

1 file changed

+78
-159
lines changed

1 file changed

+78
-159
lines changed

articles/ai-services/agents/how-to/tools/azure-functions-samples.md

Lines changed: 78 additions & 159 deletions
Original file line numberDiff line numberDiff line change
@@ -308,100 +308,103 @@ For any issues with the TypeScript code, create an issue on the [sample code rep
308308
### Prerequisites
309309
To make a function call we need to create and deploy the Azure function. In the code snippet below, we have an example of function on C# which can be used by the code above.
310310

311-
```csharp
312-
namespace FunctionProj
313-
{
314-
public class Response
315-
{
316-
public required string Value { get; set; }
317-
public required string CorrelationId { get; set; }
318-
}
319-
320-
public class Arguments
321-
{
322-
public required string OutputQueueUri { get; set; }
323-
public required string CorrelationId { get; set; }
324-
}
325-
326-
public class Foo
311+
```csharp
312+
namespace FunctionProj
327313
{
328-
private readonly ILogger<Foo> _logger;
329-
330-
public Foo(ILogger<Foo> logger)
314+
public class Response
331315
{
332-
_logger = logger;
316+
public required string Value { get; set; }
317+
public required string CorrelationId { get; set; }
333318
}
334-
335-
[Function("Foo")]
336-
public void Run([QueueTrigger("azure-function-foo-input")] Arguments input, FunctionContext executionContext)
319+
320+
public class Arguments
337321
{
338-
var logger = executionContext.GetLogger("Foo");
339-
logger.LogInformation("C# Queue function processed a request.");
340-
341-
// We have to provide the Managed identity for function resource
342-
// and allow this identity a Queue Data Contributor role on the storage account.
343-
var cred = new DefaultAzureCredential();
344-
var queueClient = new QueueClient(new Uri(input.OutputQueueUri), cred,
345-
new QueueClientOptions { MessageEncoding = QueueMessageEncoding.Base64 });
346-
347-
var response = new Response
322+
public required string OutputQueueUri { get; set; }
323+
public required string CorrelationId { get; set; }
324+
}
325+
326+
public class Foo
327+
{
328+
private readonly ILogger<Foo> _logger;
329+
330+
public Foo(ILogger<Foo> logger)
348331
{
349-
Value = "Bar",
350-
// Important! Correlation ID must match the input correlation ID.
351-
CorrelationId = input.CorrelationId
352-
};
353-
354-
var jsonResponse = JsonSerializer.Serialize(response);
355-
queueClient.SendMessage(jsonResponse);
332+
_logger = logger;
333+
}
334+
335+
[Function("Foo")]
336+
public void Run([QueueTrigger("azure-function-foo-input")] Arguments input, FunctionContext executionContext)
337+
{
338+
var logger = executionContext.GetLogger("Foo");
339+
logger.LogInformation("C# Queue function processed a request.");
340+
341+
// We have to provide the Managed identity for function resource
342+
// and allow this identity a Queue Data Contributor role on the storage account.
343+
var cred = new DefaultAzureCredential();
344+
var queueClient = new QueueClient(new Uri(input.OutputQueueUri), cred,
345+
new QueueClientOptions { MessageEncoding = QueueMessageEncoding.Base64 });
346+
347+
var response = new Response
348+
{
349+
Value = "Bar",
350+
// Important! Correlation ID must match the input correlation ID.
351+
CorrelationId = input.CorrelationId
352+
};
353+
354+
var jsonResponse = JsonSerializer.Serialize(response);
355+
queueClient.SendMessage(jsonResponse);
356+
}
356357
}
357358
}
358-
}
359-
```
359+
```
360360

361361
In this code we define function input and output class: `Arguments` and `Response` respectively. These two data classes will be serialized in JSON. It is important that these both contain field `CorrelationId`, which is the same between input and output.
362362

363363
In our example the function will be stored in the storage account, created with the AI hub. For that we need to allow key access to that storage. In Azure portal go to Storage account > Settings > Configuration and set "Allow storage account key access" to Enabled. If it is not done, the error will be displayed "The remote server returned an error: (403) Forbidden." To create the function resource that will host our function, install azure-cli python package and run the next command:
364364

365-
```shell
366-
pip install -U azure-cli
367-
az login
368-
az functionapp create --resource-group your-resource-group --consumption-plan-location region --runtime dotnet-isolated --functions-version 4 --name function_name --storage-account storage_account_already_present_in_resource_group --app-insights existing_or_new_application_insights_name
369-
```
365+
```shell
366+
pip install -U azure-cli
367+
az login
368+
az functionapp create --resource-group your-resource-group --consumption-plan-location region --runtime dotnet-isolated --functions-version 4 --name function_name --storage-account storage_account_already_present_in_resource_group --app-insights existing_or_new_application_insights_name
369+
```
370370

371-
This function writes data to the output queue and hence needs to be authenticated to Azure, so we will need to assign the function system identity and provide it `Storage Queue Data Contributor`. To do that in Azure portal select the function, located in `your-resource-group` resource group and in Settings>Identity, switch it on and click Save. After that assign the `Storage Queue Data Contributor` permission on storage account used by our function (`storage_account_already_present_in_resource_group` in the script above) for just assigned System Managed identity.
371+
This function writes data to the output queue and hence needs to be authenticated to Azure, so we will need to assign the function system identity and provide it `Storage Queue Data Contributor`. To do that in Azure portal select the function, located in `your-resource-group` resource group and in Settings>Identity, switch it on and click Save. After that assign the `Storage Queue Data Contributor` permission on storage account used by our function (storage_account_already_present_in_resource_group in the script above) for just assigned System Managed identity.
372372

373373
Now we will create the function itself. Install [.NET](https://dotnet.microsoft.com/download) and [Core Tools](https://go.microsoft.com/fwlink/?linkid=2174087) and create the function project using next commands.
374-
```
375-
func init FunctionProj --worker-runtime dotnet-isolated --target-framework net8.0
376-
cd FunctionProj
377-
func new --name foo --template "HTTP trigger" --authlevel "anonymous"
378-
dotnet add package Azure.Identity
379-
dotnet add package Microsoft.Azure.Functions.Worker.Extensions.Storage.Queues --prerelease
380-
```
374+
375+
```console
376+
func init FunctionProj --worker-runtime dotnet-isolated --target-framework net8.0
377+
cd FunctionProj
378+
func new --name foo --template "HTTP trigger" --authlevel "anonymous"
379+
dotnet add package Azure.Identity
380+
dotnet add package Microsoft.Azure.Functions.Worker.Extensions.Storage.Queues --prerelease
381+
```
381382

382383
**Note:** There is a "Azure Queue Storage trigger", however the attempt to use it results in error for now.
383384
We have created a project, containing HTTP-triggered azure function with the logic in `Foo.cs` file. As far as we need to trigger Azure function by a new message in the queue, we will replace the content of a Foo.cs by the C# sample code above.
384385
To deploy the function run the command from dotnet project folder:
385386

386-
```
387-
func azure functionapp publish function_name
388-
```
387+
```console
388+
func azure functionapp publish function_name
389+
```
389390

390391
In the `storage_account_already_present_in_resource_group` select the `Queue service` and create two queues: `azure-function-foo-input` and `azure-function-tool-output`. Note that the same queues are used in our sample. To check that the function is working, place the next message into the `azure-function-foo-input` and replace `storage_account_already_present_in_resource_group` by the actual resource group name, or just copy the output queue address.
391-
```json
392-
{
393-
"OutputQueueUri": "https://storage_account_already_present_in_resource_group.queue.core.windows.net/azure-function-tool-output",
394-
"CorrelationId": "42"
395-
}
396-
```
392+
393+
```json
394+
{
395+
"OutputQueueUri": "https://storage_account_already_present_in_resource_group.queue.core.windows.net/azure-function-tool-output",
396+
"CorrelationId": "42"
397+
}
398+
```
397399

398400
Next, we will monitor the output queue or the message. You should receive the next message.
399-
```json
400-
{
401-
"Value": "Bar",
402-
"CorrelationId": "42"
403-
}
404-
```
401+
402+
```json
403+
{
404+
"Value": "Bar",
405+
"CorrelationId": "42"
406+
}
407+
```
405408
Please note that the input `CorrelationId` is the same as output.
406409
*Hint:* Place multiple messages to input queue and keep second internet browser window with the output queue open and hit the refresh button on the portal user interface, so that you will not miss the message. If the message instead went to `azure-function-foo-input-poison` queue, the function completed with error, please check your setup.
407410
After we have tested the function and made sure it works, please make sure that the Azure AI Project have the next roles for the storage account: `Storage Account Contributor`, `Storage Blob Data Contributor`, `Storage File Data Privileged Contributor`, `Storage Queue Data Contributor` and `Storage Table Data Contributor`. Now the function is ready to be used by the agent.
@@ -412,8 +415,6 @@ In the example below we are calling function "foo", which responds "Bar".
412415

413416
1. First, we set up the necessary configuration, initialize the `PersistentAgentsClient`, define the `AzureFunctionToolDefinition` for our Azure Function, and then create the agent. This step includes all necessary `using` directives.
414417

415-
Common setup:
416-
417418
```csharp
418419
using Azure;
419420
using Azure.AI.Agents.Persistent;
@@ -467,11 +468,7 @@ In the example below we are calling function "foo", which responds "Bar".
467468
new JsonSerializerOptions() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }
468469
)
469470
);
470-
```
471-
472-
Synchronous sample:
473-
474-
```csharp
471+
475472
PersistentAgent agent = client.Administration.CreateAgent(
476473
model: modelDeploymentName,
477474
name: "azure-function-agent-foo",
@@ -484,26 +481,9 @@ In the example below we are calling function "foo", which responds "Bar".
484481
);
485482
```
486483

487-
Asynchronous sample:
488-
489-
```csharp
490-
PersistentAgent agent = await client.Administration.CreateAgentAsync(
491-
model: modelDeploymentName,
492-
name: "azure-function-agent-foo",
493-
instructions: "You are a helpful support agent. Use the provided function any "
494-
+ "time the prompt contains the string 'What would foo say?'. When you invoke "
495-
+ "the function, ALWAYS specify the output queue uri parameter as "
496-
+ $"'{storageQueueUri}/azure-function-tool-output'. Always responds with "
497-
+ "\"Foo says\" and then the response from the tool.",
498-
tools: [azureFnTool]
499-
);
500-
```
501-
502484
2. Next, we create a new persistent agent thread and add an initial user message to it.
503485

504-
Synchronous sample:
505-
506-
```csharp
486+
```csharp
507487
PersistentAgentThread thread = client.Threads.CreateThread();
508488

509489
client.Messages.CreateMessage(
@@ -512,20 +492,7 @@ In the example below we are calling function "foo", which responds "Bar".
512492
"What is the most prevalent element in the universe? What would foo say?");
513493
```
514494

515-
Asynchronous sample:
516-
517-
```csharp
518-
PersistentAgentThread thread = await client.Threads.CreateThreadAsync();
519-
520-
await client.Messages.CreateMessageAsync(
521-
thread.Id,
522-
MessageRole.User,
523-
"What is the most prevalent element in the universe? What would foo say?");
524-
```
525-
526-
3. Then, we create a run for the agent on the thread and poll its status until it completes or requires action.
527-
528-
Synchronous sample:
495+
4. Then, we create a run for the agent on the thread and poll its status until it completes or requires action.
529496

530497
```csharp
531498
ThreadRun run = client.Runs.CreateRun(thread.Id, agent.Id);
@@ -540,24 +507,7 @@ In the example below we are calling function "foo", which responds "Bar".
540507
|| run.Status == RunStatus.RequiresAction);
541508
```
542509

543-
Asynchronous sample:
544-
545-
```csharp
546-
ThreadRun run = await client.Runs.CreateRunAsync(thread.Id, agent.Id);
547-
548-
do
549-
{
550-
await Task.Delay(TimeSpan.FromMilliseconds(500));
551-
run = await client.Runs.GetRunAsync(thread.Id, run.Id);
552-
}
553-
while (run.Status == RunStatus.Queued
554-
|| run.Status == RunStatus.InProgress
555-
|| run.Status == RunStatus.RequiresAction);
556-
```
557-
558-
4. After the run is complete, we retrieve and process the messages from the thread.
559-
560-
Synchronous sample:
510+
5. After the run is complete, we retrieve and process the messages from the thread.
561511

562512
```csharp
563513
Pageable<ThreadMessage> messages = client.Messages.GetMessages(
@@ -579,43 +529,12 @@ In the example below we are calling function "foo", which responds "Bar".
579529
}
580530
```
581531

582-
Asynchronous sample:
583-
584-
```csharp
585-
AsyncPageable<ThreadMessage> messages = client.Messages.GetMessagesAsync(
586-
threadId: thread.Id,
587-
order: ListSortOrder.Ascending
588-
);
589-
590-
await foreach (ThreadMessage threadMessage in messages)
591-
{
592-
foreach (MessageContent content in threadMessage.ContentItems)
593-
{
594-
switch (content)
595-
{
596-
case MessageTextContent textItem:
597-
Console.WriteLine($"[{threadMessage.Role}]: {textItem.Text}");
598-
break;
599-
}
600-
}
601-
}
602-
```
603-
604-
5. Finally, we clean up the created resources by deleting the thread and the agent.
605-
606-
Synchronous sample:
532+
6. Finally, we clean up the created resources by deleting the thread and the agent.
607533

608534
```csharp
609535
client.Threads.DeleteThread(thread.Id);
610536
client.Administration.DeleteAgent(agent.Id);
611537
```
612-
613-
Asynchronous sample:
614-
615-
```csharp
616-
await client.Threads.DeleteThreadAsync(thread.Id);
617-
await client.Administration.DeleteAgentAsync(agent.Id);
618-
```
619538

620539
::: zone-end
621540

0 commit comments

Comments
 (0)