Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
15 changes: 15 additions & 0 deletions examples/DotNetCore/ChatApp/ChatApp/AzureOpenAIConfiguration.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
//

namespace ChatApp
{
internal class AzureOpenAIConfiguration
{
public required string Endpoint { get; set; }

public required string DeploymentName { get; set; }

public string? ApiKey { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

namespace ChatApp
{
internal class ModelConfiguration
internal class ChatCompletionConfiguration
{
[ConfigurationKeyName("model")]
public string? Model { get; set; }
Expand Down
71 changes: 51 additions & 20 deletions examples/DotNetCore/ChatApp/ChatApp/Program.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
//

using Azure.AI.OpenAI;
using Azure.Core;
using Azure.Identity;
Expand All @@ -16,8 +17,8 @@
IConfiguration configuration = new ConfigurationBuilder()
.AddAzureAppConfiguration(options =>
{
Uri endpoint = new(Environment.GetEnvironmentVariable("AZURE_APPCONFIG_ENDPOINT") ??
throw new InvalidOperationException("The environment variable 'AZURE_APPCONFIG_ENDPOINT' is not set or is empty."));
Uri endpoint = new(Environment.GetEnvironmentVariable("AZURE_APPCONFIGURATION_ENDPOINT") ??
throw new InvalidOperationException("The environment variable 'AZURE_APPCONFIGURATION_ENDPOINT' is not set or is empty."));
options.Connect(endpoint, credential)
// Load all keys that start with "ChatApp:" and have no label.
.Select("ChatApp:*")
Expand All @@ -26,50 +27,80 @@
.ConfigureRefresh(refreshOptions =>
{
refreshOptions.RegisterAll();
})
.ConfigureKeyVault(keyVaultOptions =>
{
// Use the DefaultAzureCredential to access Key Vault secrets.
keyVaultOptions.SetCredential(credential);
});

refresher = options.GetRefresher();
})
.Build();

// Retrieve the OpenAI connection information from the configuration
Uri openaiEndpoint = new (configuration["ChatApp:AzureOpenAI:Endpoint"]);
string deploymentName = configuration["ChatApp:AzureOpenAI:DeploymentName"];
var azureOpenAIConfiguration = configuration.GetSection("ChatApp:AzureOpenAI").Get<AzureOpenAIConfiguration>();

// Create a chat client
AzureOpenAIClient azureClient = new(openaiEndpoint, credential);
ChatClient chatClient = azureClient.GetChatClient(deploymentName);
// Create a chat client using API key if available, otherwise use the DefaultAzureCredential
AzureOpenAIClient azureClient;
if (!string.IsNullOrEmpty(azureOpenAIConfiguration.ApiKey))
{
azureClient = new AzureOpenAIClient(new Uri(azureOpenAIConfiguration.Endpoint), new Azure.AzureKeyCredential(azureOpenAIConfiguration.ApiKey));
}
else
{
azureClient = new AzureOpenAIClient(new Uri(azureOpenAIConfiguration.Endpoint), credential);
}
ChatClient chatClient = azureClient.GetChatClient(azureOpenAIConfiguration.DeploymentName);

// Initialize chat conversation
var chatConversation = new List<ChatMessage>();
Console.WriteLine("Chat started! What's on your mind?");
while (true)
{
// Refresh the configuration from Azure App Configuration
await refresher.TryRefreshAsync();

// Configure chat completion with AI configuration
var modelConfiguration = configuration.GetSection("ChatApp:Model").Get<ModelConfiguration>();
var chatCompletionConfiguration = configuration.GetSection("ChatApp:ChatCompletion").Get<ChatCompletionConfiguration>();
var requestOptions = new ChatCompletionOptions()
{
MaxOutputTokenCount = modelConfiguration.MaxTokens,
Temperature = modelConfiguration.Temperature,
TopP = modelConfiguration.TopP
MaxOutputTokenCount = chatCompletionConfiguration.MaxTokens,
Temperature = chatCompletionConfiguration.Temperature,
TopP = chatCompletionConfiguration.TopP
};

foreach (var message in modelConfiguration.Messages)
// Get user input
Console.Write("You: ");
string? userInput = Console.ReadLine();

// Exit if user input is empty
if (string.IsNullOrEmpty(userInput))
{
Console.WriteLine($"{message.Role}: {message.Content}");
Console.WriteLine("Exiting chat. Goodbye!");
break;
}

// Get chat response from AI
var response = await chatClient.CompleteChatAsync(GetChatMessages(modelConfiguration), requestOptions);
System.Console.WriteLine($"AI response: {response.Value.Content[0].Text}");
// Add user message to chat conversation
chatConversation.Add(ChatMessage.CreateUserMessage(userInput));

// Get latest system message from AI configuration
var chatMessages = new List<ChatMessage>(GetChatMessages(chatCompletionConfiguration));
chatMessages.AddRange(chatConversation);

// Get AI response and add it to chat conversation
var response = await chatClient.CompleteChatAsync(chatMessages, requestOptions);
string aiResponse = response.Value.Content[0].Text;
Console.WriteLine($"AI: {aiResponse}");
chatConversation.Add(ChatMessage.CreateAssistantMessage(aiResponse));

Console.WriteLine("Press Enter to continue...");
Console.ReadLine();
Console.WriteLine();
}

static IEnumerable<ChatMessage> GetChatMessages(ModelConfiguration modelConfiguration)
// Helper method to convert configuration messages to ChatMessage objects
static IEnumerable<ChatMessage> GetChatMessages(ChatCompletionConfiguration chatCompletionConfiguration)
{
return modelConfiguration.Messages.Select<Message, ChatMessage>(message => message.Role switch
return chatCompletionConfiguration.Messages.Select<Message, ChatMessage>(message => message.Role switch
{
"system" => ChatMessage.CreateSystemMessage(message.Content),
"user" => ChatMessage.CreateUserMessage(message.Content),
Expand Down
90 changes: 90 additions & 0 deletions examples/DotNetCore/ChatApp/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
# Azure App Configuration - .NET ChatApp Sample

An interactive console chat application that integrates with Azure OpenAI services using Azure App Configuration for dynamic AI Configuration management.

## Overview

This .NET console application provides a seamless chat experience with Azure OpenAI, featuring:

- Integration with Azure OpenAI for chat completions
- Dynamic AI configuration refresh from Azure App Configuration
- Secure authentication options using API key or Microsoft Entra ID

## Prerequisites

- .NET 8.0 SDK
- Azure subscription
- Azure OpenAI service instance
- Azure App Configuration service instance

## Setup

### Environment Variables

Set the following environment variable:

- `AZURE_APPCONFIGURATION_ENDPOINT`: Endpoint URL of your Azure App Configuration instance

### Azure App Configuration Keys

Configure the following keys in your Azure App Configuration:

#### Azure OpenAI Connection Settings

- `ChatApp:AzureOpenAI:Endpoint` - Your Azure OpenAI endpoint URL
- `ChatApp:AzureOpenAI:DeploymentName` - Your Azure OpenAI deployment name
- `ChatApp:AzureOpenAI:ApiKey` - Key Vault reference to the API key for Azure OpenAI (optional)

#### Chat Completion Configuration

- `ChatApp:ChatCompletion` - An AI Configuration for chat completion containing the following settings:
- `model` - Model name (e.g., "gpt-4o")
- `max_tokens` - Maximum tokens for completion (e.g., 1000)
- `temperature` - Temperature parameter (e.g., 0.7)
- `top_p` - Top p parameter (e.g., 0.95)
- `messages` - An array of messages with role and content for each message

## Authentication

The application supports the following authentication methods:

- **Azure App Configuration**: Uses `DefaultAzureCredential` for authentication via Microsoft Entra ID.
- **Azure OpenAI**: Supports authentication using either an API key or `DefaultAzureCredential` via Microsoft Entra ID.
- **Azure Key Vault** *(optional, if using Key Vault references for API keys)*: Authenticates using `DefaultAzureCredential` via Microsoft Entra ID.

## Usage

1. **Start the Application**: Run the application using `dotnet run`
2. **Begin Chatting**: Type your messages when prompted with "You: "
3. **Continue Conversation**: The AI will respond and maintain conversation context
4. **Exit**: Press Enter without typing a message to exit gracefully

### Example Session
```
Chat started! What's on your mind?
You: Hello, how are you?
AI: Hello! I'm doing well, thank you for asking. How can I help you today?

You: What can you tell me about machine learning?
AI: Machine learning is a subset of artificial intelligence that focuses on...

You: [Press Enter to exit]
Exiting chat. Goodbye!
```

## Troubleshooting

**"AZURE_APPCONFIGURATION_ENDPOINT environment variable not set"**
- Ensure the environment variable is properly set
- Verify the endpoint URL is correct

**Authentication Failures**
- Ensure you have the `App Configuration Data Reader` role on the Azure App Configuration instance
- For Microsoft Entra ID authentication: Verify you have the `Cognitive Services OpenAI User` role on the Azure OpenAI instance
- For API key authentication:
- Confirm you have secret read access to the Key Vault storing the API key
- Verify that a Key Vault reference for the API key is properly configured in Azure App Configuration

**No AI Response**
- Verify deployment name matches your Azure OpenAI deployment
- Check token limits and quotas