Building and deploying a chatbot with Azure Functions OpenAI Extension and model GPT-4o mini

Azure resources overview

Overview

Introduction

In this blog post, we will explore how to integrate Azure Functions with the OpenAI extension. This integration allows you to use triggers and bindings with the powerful capabilities of OpenAI within your serverless functions. We will walk through the setup, configuration, and deployment of an Azure Functions project that uses the OpenAI extension, using Bicep templates for infrastructure as code.

Prerequisites

Before we begin, ensure you have the following tools and resources:

Project Structure

Link to GitHub project

The project is organized as follows:

 1azure.yaml
 2infra/
 3    core/
 4        ai/
 5        host/
 6        monitor/
 7        security/
 8        storage/
 9    main.bicep
10    main.parameters.json
11src/
12    AzureFunctionsOpenAIExtension.csproj
13    AzureFunctionsOpenAIExtension.sln
14    Chat.cs
15    Models/
16    Program.cs
17    host.json
18    local.settings.json

Infrastructure Deployment

The infrastructure is defined using Bicep templates located in the infra directory.

Deploy the Infrastructure using azd

Navigate to the root of your project and run:

1azd up

This command will deploy all the necessary resources defined in the main.bicep file.

The azd outputs the progress of the provisioning. If everything succeeds an endpoint of the Function app is shown.

 1  (✓) Done: Resource group: rg-dev
 2  (✓) Done: Log Analytics workspace: asarash7gvzhctfaxmbm
 3  (✓) Done: Storage account: starash7gvzhctfaxmbm
 4  (✓) Done: App Service plan: plan-arash7gvzhctfaxmbm
 5  (✓) Done: Application Insights: appi-arash7gvzhctfaxmbm
 6  (✓) Done: Portal dashboard: arash
 7  (✓) Done: Azure OpenAI: cog--7gvzhctfaxmbm
 8  (✓) Done: Function App: func-arash7gvzhctfaxmbm
 9
10Deploying services (azd deploy)
11
12  (✓) Done: Deploying service api
13  - Endpoint: https://func-arash7gvzhctfaxmbm.azurewebsites.net/
14
15
16SUCCESS: Your up workflow to provision and deploy to Azure completed in 2 minutes 50 seconds.

Using the Chat function

Now that our infrastructure is set up, let's use the chat function that will interact with OpenAI.

Test the Function Locally

Run the function locally using the Azure Functions Core Tools. First navigate to the src directory.

1cd src

Add the following settings to local.settings.json:

 1{
 2  "IsEncrypted": false,
 3  "Values": {
 4    "AzureWebJobsStorage": "UseDevelopmentStorage=true",
 5    "FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated",
 6    "AZURE_OPENAI_ENDPOINT": "<your-openai-endpoint>",
 7    "AZURE_OPEN_KEY": "<your-openai-key>",
 8    "CHAT_MODEL_DEPLOYMENT_NAME": "<your-chat-model-deployment-name>"
 9  }
10}

Start the host:

1func start

The terminal outputs the available functions to call:

1Functions:
2
3  Completions: [POST] http://localhost:7071/api/Completions
4
5  CreateChatBot: [PUT] http://localhost:7071/api/chats/{chatId}
6
7  GetChatState: [GET] http://localhost:7071/api/chats/{chatId}
8
9  PostToChatBot: [POST] http://localhost:7071/api/chats/{chatId}

You can test the function using a tool like curl or Postman. When calling the Completions API the response will be like this:

Completions API with Postman

Verify the Deployment with Function app endpoint

After the deployment is complete, you can verify it by making a request to the function's endpoint. Replace <YourFunctionAppName> with the name of your Function App. To call the endpoint of the function app an app key needs to be included in the request. The app key can be found by going to the Azure portal and locating the deployed function app and then App keys.

Example Calls

Here are some example calls you can make to the deployed function:

  1. Create a chat bot:

    • HTTP Method: PUT
    • Endpoint: /chats/{chatId}
    • Request Body:
      1{
      2  "instructions": "You are a helpful assistant. You act like a DevOps Engineer."
      3}
      
    • Response:
      • Status Code: 201 (Created)
      • Body:
        1{
        2  "chatId": "{chatId}"
        3}
        
  2. Post a message to the chat bot:

    • HTTP Method: POST
    • Endpoint: /chats/{chatId}
    • Query param message: "How to create a basic CI/CD pipeline with Azure DevOps?"
    • Response:
      • Status Code: 200 (OK)
      • Body: The response message from the chat bot
  3. Get the current state of the chat:

    • HTTP Method: GET
    • Endpoint: /chats/{chatId}
    • Response:
      • Status Code: 200 (OK)
      • Body: The current state of the chat, including the chat messages and other relevant information
  4. Get text completions from the chat bot:

    • HTTP Method: POST
    • Endpoint: /completions
    • Query param Prompt
    • Response:
      • Status Code: 200 (OK)
      • Body: The completed text generated by the chat bot

See the chatbot in action

  1. Create chatbot

Give instructions to chatbot

  1. First prompt

First question

  1. Second prompt

Second question part 1

Second question part 2

  1. Chat history
 1{
 2    "id": "devops",
 3    "exists": true,
 4    "createdAt": "2024-08-05T16:25:15.2174459Z",
 5    "lastUpdatedAt": "2024-08-05T16:31:25.7453816Z",
 6    "totalMessages": 5,
 7    "totalTokens": 1089,
 8    "recentMessages": [
 9        {
10            "content": "You are a helpful assistant. You act like a DevOps Engineer.",
11            "role": "system",
12            "name": null
13        },
14        {
15            "content": "Create a yaml CI pipeline for building a dotnet core application in Azure DevOps",
16            "role": "user",
17            "name": null
18        },
19        {
20            "content": "Certainly! Below is a sample YAML CI pipeline for building a .NET Core application in Azure DevOps. This pipeline includes steps for setting up the environment, restoring dependencies, building the application, and running tests.\n\n```yaml\n# azure-pipelines.yml\ntrigger:\n- main  # This triggers the pipeline on changes to the 'main' branch.\n\npool:\n  vmImage: 'windows-latest'  # Use the latest Windows VM image.\n\nsteps:\n- task: UseDotNet@2\n  inputs:\n    packageType: 'sdk'\n    version: '6.x'  # Specify the version of the .NET SDK you want to use\n    installationPath: $(Agent.ToolsDirectory)/dotnet\n\n- script: |\n    echo \"Restoring dependencies...\"\n    dotnet restore\n  displayName: 'Restore dependencies'\n\n- script: |\n    echo \"Building the application...\"\n    dotnet build --configuration Release --no-restore\n  displayName: 'Build the application'\n\n- script: |\n    echo \"Running unit tests...\"\n    dotnet test --configuration Release --no-build --verbosity normal\n  displayName: 'Run Unit Tests'\n\n- script: |\n    echo \"Publishing the application...\"\n    dotnet publish --configuration Release --output $(Build.ArtifactStagingDirectory)\n  displayName: 'Publish the application'\n\n- publish: $(Build.ArtifactStagingDirectory)\n  artifact: drop\n  displayName: 'Publish Artifacts'\n```\n\n### Explanation:\n\n1. **Trigger**: This pipeline triggers whenever there’s a push to the `main` branch.\n\n2. **Pool**: Specifies the use of `windows-latest` VM image, which has the necessary tools already installed.\n\n3. **Use .NET SDK**:\n   - The `UseDotNet@2` task installs the specified version of the .NET SDK.\n\n4. **Restore**: Executes `dotnet restore` to restore dependencies specified in the project files.\n\n5. **Build**: Executes `dotnet build` to compile the application. The `--no-restore` option ensures that it doesn’t attempt to restore packages again, as this was already done in the previous step.\n\n6. **Test**: Executes `dotnet test` to run unit tests on the code. The `--no-build` option means it doesn't compile the project again since it was already built in a previous step.\n\n7. **Publish**: Executes `dotnet publish` to package the application into the `$(Build.ArtifactStagingDirectory)`.\n\n8. **Publish Artifacts**: Publishes the output of the `dotnet publish` command so that it can be used in subsequent stages or releases.\n\n### Customization:\n- Adjust the .NET version in the `UseDotNet` step to match the version you're using (e.g., 5.x, 6.x, etc.).\n- Modify the project or test specifics according to your repository structure.\n- You can include additional steps for further analysis, like code quality checks, deployment steps, etc., as needed.\n\nSave this YAML as `azure-pipelines.yml` in the root of your repository to start using the pipeline.",
21            "role": "assistant",
22            "name": null
23        },
24        {
25            "content": "Change the vmImage to ubuntu",
26            "role": "user",
27            "name": null
28        },
29        {
30            "content": "Certainly! Below is the modified YAML CI pipeline for building a .NET Core application in Azure DevOps, updated to use an Ubuntu VM image.\n\n```yaml\n# azure-pipelines.yml\ntrigger:\n- main  # This triggers the pipeline on changes to the 'main' branch.\n\npool:\n  vmImage: 'ubuntu-latest'  # Use the latest Ubuntu VM image.\n\nsteps:\n- task: UseDotNet@2\n  inputs:\n    packageType: 'sdk'\n    version: '6.x'  # Specify the version of the .NET SDK you want to use\n    installationPath: $(Agent.ToolsDirectory)/dotnet\n\n- script: |\n    echo \"Restoring dependencies...\"\n    dotnet restore\n  displayName: 'Restore dependencies'\n\n- script: |\n    echo \"Building the application...\"\n    dotnet build --configuration Release --no-restore\n  displayName: 'Build the application'\n\n- script: |\n    echo \"Running unit tests...\"\n    dotnet test --configuration Release --no-build --verbosity normal\n  displayName: 'Run Unit Tests'\n\n- script: |\n    echo \"Publishing the application...\"\n    dotnet publish --configuration Release --output $(Build.ArtifactStagingDirectory)\n  displayName: 'Publish the application'\n\n- publish: $(Build.ArtifactStagingDirectory)\n  artifact: drop\n  displayName: 'Publish Artifacts'\n```\n\n### Changes Made:\n- The `vmImage` has been changed from `windows-latest` to `ubuntu-latest`.\n\n### Note:\nEnsure that your .NET Core application works seamlessly on Linux, as the build and test steps will be executed in a Linux environment when using Ubuntu. This YAML file will still perform the same tasks: restoring dependencies, building, testing, publishing, and publishing artifacts, just on an Ubuntu agent. Save it as `azure-pipelines.yml` in the root of your repository.",
31            "role": "assistant",
32            "name": null
33        }
34    ]
35}

Conclusion

By following this guide, you have successfully integrated Azure Functions with the OpenAI extension. This setup allows you to build powerful serverless applications that leverage the capabilities of OpenAI. You can now extend this setup to create more complex and interactive applications.

For more detailed information, refer to the Azure OpenAI Extension for Function Apps documentation.