-
Notifications
You must be signed in to change notification settings - Fork 79
Create Python ChatApp example for AI configuration #1065
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 20 commits
Commits
Show all changes
36 commits
Select commit
Hold shift + click to select a range
68fc507
Initial plan for issue
Copilot 0c6960e
Create Python ChatApp example for AI configuration
Copilot caf9cd0
Convert Flask web app to console app
Copilot e19bc99
Update azure-appconfiguration-provider to version 2.1.0
Copilot 5d8b373
Address review feedback for Python ChatApp example
Copilot 6466ad8
Implement data binding for Python ChatApp configuration
Copilot d377f84
Implement data binding without Pydantic dependency for Python ChatApp
Copilot 4cfe6d2
Update Python ChatApp README.md based on review feedback
Copilot f36d608
Read API version from App Configuration
Copilot da033f9
Update ChatApp:Model description to specify JSON object with AI chat …
Copilot bf6b106
Read API key from App Configuration with Key Vault reference support
Copilot 97c5be0
Revert ChatApp:Model description to focus on AI configuration
Copilot e2fd91f
Address reviewers' feedback on Python ChatApp example
Copilot 7325b6f
Add Key Vault credential for loading API key from Key Vault references
Copilot e3daa4d
Fix credential parameter name from key_vault_credential to keyvault_c…
Copilot c2c1e17
Fixing
mrm9084 907227e
Fixing auth
mrm9084 00136bd
Add user input chat functionality to Python ChatApp
Copilot 433030d
Updated to use black formating
mrm9084 3a0f7fe
Fixing a few pylint issues
mrm9084 334fb2c
Update examples/Python/ChatApp/app.py
mrm9084 5064f99
Updated to match dotnet
mrm9084 2afb137
review items
mrm9084 20e9d6d
review changes
mrm9084 b8e8a55
review comments
mrm9084 18fbda0
Update app.py
mrm9084 320a372
rework to remove global need
mrm9084 5fa4fff
code cleanup
mrm9084 0b34fc7
fixed formatting
mrm9084 e573db5
moved deployment type to AzureOpenAI
mrm9084 3ef2148
Update models.py
mrm9084 6cf8451
formatting
mrm9084 295c876
review changes
mrm9084 24aecfb
refresh update
mrm9084 d3cf9c5
review comments
mrm9084 c21857c
review comments
mrm9084 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,82 @@ | ||
| # Azure App Configuration - Python ChatApp Sample | ||
|
|
||
| This sample demonstrates using Azure App Configuration to configure Azure OpenAI settings for a chat application built with Python. | ||
|
|
||
| ## Features | ||
|
|
||
| - Integrates with Azure OpenAI for chat completions | ||
| - Dynamically refreshes configuration from Azure App Configuration | ||
|
|
||
| ## Prerequisites | ||
|
|
||
| - Python 3.8 or later | ||
| - An Azure subscription with access to: | ||
| - Azure App Configuration service | ||
| - Azure OpenAI service | ||
| - Required environment variables: | ||
| - `AZURE_APPCONFIG_ENDPOINT`: URL of your Azure App Configuration instance | ||
| - `AZURE_OPENAI_API_KEY`: API key for Azure OpenAI (optional if stored in Azure App Configuration) | ||
|
|
||
| ## Setup | ||
|
|
||
| 1. Clone the repository | ||
| 2. Install the required packages: | ||
| ```bash | ||
| pip install -r requirements.txt | ||
| ``` | ||
| 3. Configure your Azure App Configuration store with these settings: | ||
| ``` | ||
| ChatApp:AzureOpenAI:Endpoint - Your Azure OpenAI endpoint URL | ||
| ChatApp:AzureOpenAI:DeploymentName - Your Azure OpenAI deployment name | ||
| ChatApp:AzureOpenAI:ApiVersion - API version for Azure OpenAI (e.g., "2023-05-15") | ||
| ChatApp:AzureOpenAI:ApiKey - Your Azure OpenAI API key (preferably as a Key Vault reference) | ||
|
mrm9084 marked this conversation as resolved.
Outdated
|
||
| ChatApp:Model - An AI configuration entry containing the following settings: | ||
|
zhenlan marked this conversation as resolved.
|
||
| - model - Model name (e.g., "gpt-35-turbo") | ||
| - 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 | ||
|
|
||
| Example JSON object for ChatApp:Model: | ||
| ```json | ||
| { | ||
| "model": "gpt-35-turbo", | ||
| "max_tokens": 1000, | ||
| "temperature": 0.7, | ||
| "top_p": 0.95, | ||
| "messages": [ | ||
| { | ||
| "role": "system", | ||
| "content": "You are a helpful assistant." | ||
| }, | ||
| { | ||
| "role": "user", | ||
| "content": "Tell me about Azure App Configuration." | ||
| } | ||
| ] | ||
| } | ||
| ``` | ||
|
mrm9084 marked this conversation as resolved.
Outdated
|
||
| ``` | ||
|
mrm9084 marked this conversation as resolved.
Outdated
|
||
|
|
||
| 4. Set the required environment variables: | ||
| ```bash | ||
| export AZURE_APPCONFIG_ENDPOINT="https://your-appconfig.azconfig.io" | ||
|
mrm9084 marked this conversation as resolved.
Outdated
|
||
| export AZURE_OPENAI_API_KEY="your-openai-api-key" # Optional if stored in Azure App Configuration | ||
| ``` | ||
|
|
||
| ## Running the Application | ||
|
|
||
| Start the console application: | ||
| ```bash | ||
| python app.py | ||
| ``` | ||
|
|
||
| The application will: | ||
| 1. Display the initial configured messages from Azure App Configuration | ||
| 2. Generate a response from the AI | ||
| 3. Prompt you to enter your message (type 'exit' to quit) | ||
|
mrm9084 marked this conversation as resolved.
Outdated
|
||
| 4. Maintain conversation history during the session | ||
|
|
||
| ## Configuration Refresh | ||
|
|
||
| The application refreshes the configuration at the beginning of each conversation cycle, so any changes made to the base configuration in Azure App Configuration will be incorporated into the model parameters (temperature, max_tokens, etc.) while maintaining your ongoing conversation history. | ||
|
mrm9084 marked this conversation as resolved.
Outdated
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,141 @@ | ||
| # ------------------------------------------------------------------------- | ||
| # Copyright (c) Microsoft Corporation. All rights reserved. | ||
| # Licensed under the MIT License. See License.txt in the project root for | ||
| # license information. | ||
| # -------------------------------------------------------------------------- | ||
| """ | ||
| Azure App Configuration Chat Application using Azure OpenAI. | ||
| This module provides a simple chat application that uses configurations from Azure App Configuration | ||
| and connects to Azure OpenAI services for chat completions. | ||
| """ | ||
| import os | ||
| from typing import TypeVar, List | ||
| from azure.identity import DefaultAzureCredential, get_bearer_token_provider | ||
| from azure.appconfiguration.provider import load, SettingSelector | ||
| from openai import AzureOpenAI | ||
| from models import ModelConfiguration, Message | ||
|
|
||
| # Get Azure App Configuration endpoint from environment variable | ||
| ENDPOINT = os.environ.get("AZURE_APPCONFIG_ENDPOINT") | ||
| if not ENDPOINT: | ||
| raise ValueError( | ||
| "The environment variable 'AZURE_APPCONFIG_ENDPOINT' is not set or is empty." | ||
| ) | ||
|
|
||
| # Initialize Azure credentials | ||
| credential = DefaultAzureCredential() | ||
|
|
||
| # Create selector for ChatApp configuration | ||
| chat_app_selector = SettingSelector(key_filter="ChatApp:*") | ||
|
|
||
| # Load configuration from Azure App Configuration | ||
| config = load( | ||
|
mrm9084 marked this conversation as resolved.
Outdated
|
||
| endpoint=ENDPOINT, | ||
| selects=[chat_app_selector], | ||
| credential=credential, | ||
|
mrm9084 marked this conversation as resolved.
Outdated
|
||
| keyvault_credential=credential, # Use the same credential for Key Vault references | ||
| trim_prefixes=["ChatApp:"], | ||
| ) | ||
|
|
||
| T = TypeVar("T") | ||
|
|
||
|
|
||
|
mrm9084 marked this conversation as resolved.
Outdated
|
||
| # Get OpenAI configuration | ||
| def get_openai_client(): | ||
| """Create and return an Azure OpenAI client""" | ||
| endpoint = config.get("AzureOpenAI:Endpoint") | ||
| # Get API key from App Configuration or fall back to environment variable | ||
| api_key = config.get("AzureOpenAI:ApiKey", os.environ.get("AZURE_OPENAI_API_KEY")) | ||
| api_version = config.get( | ||
| "AzureOpenAI:ApiVersion", "2023-05-15" | ||
| ) # Read API version from config or use default | ||
|
|
||
| # For DefaultAzureCredential auth if no API key is available | ||
| if not api_key: | ||
| token_provider = get_bearer_token_provider( | ||
| DefaultAzureCredential(), "https://cognitiveservices.azure.com/.default" | ||
| ) | ||
| return AzureOpenAI( | ||
| azure_endpoint=endpoint, | ||
| api_version=api_version, | ||
| azure_ad_token_provider=token_provider, | ||
| ) | ||
|
mrm9084 marked this conversation as resolved.
|
||
|
|
||
| # For API key auth | ||
| return AzureOpenAI( | ||
| azure_endpoint=endpoint, api_key=api_key, api_version=api_version | ||
| ) | ||
|
|
||
|
|
||
| def get_chat_messages(messages: List[Message]): | ||
| """Convert from model Message objects to OpenAI messages format""" | ||
| return [{"role": msg.role, "content": msg.content} for msg in messages] | ||
|
|
||
|
|
||
| def main(): | ||
| """Main entry point for the console app""" | ||
| # Get OpenAI client | ||
| client = get_openai_client() | ||
|
|
||
| # Get deployment name | ||
| deployment_name = config.get("AzureOpenAI:DeploymentName") | ||
|
|
||
| # Initialize conversation history with the configuration messages | ||
| conversation_history = [] | ||
| first_run = True | ||
|
|
||
| print("Chat Application - type 'exit' to quit\n") | ||
|
|
||
| while True: | ||
| # Refresh configuration from Azure App Configuration | ||
| config.refresh() | ||
|
|
||
| # Get model configuration using data binding | ||
| model_config = ModelConfiguration.from_dict(config.get("Model")) | ||
|
|
||
| # On first run, initialize conversation history with configuration messages | ||
| # and display the initial messages | ||
| if first_run: | ||
| conversation_history = model_config.messages.copy() | ||
| for msg in conversation_history: | ||
| print(f"{msg.role}: {msg.content}") | ||
| first_run = False | ||
|
|
||
| # Get chat messages for the API | ||
| messages = get_chat_messages(conversation_history) | ||
|
|
||
| # Get response from OpenAI | ||
| response = client.chat.completions.create( | ||
| model=deployment_name, | ||
| messages=messages, | ||
| max_tokens=model_config.max_tokens, | ||
| temperature=model_config.temperature, | ||
| top_p=model_config.top_p, | ||
| ) | ||
|
|
||
| # Extract assistant message | ||
| assistant_message = response.choices[0].message.content | ||
|
|
||
| # Display the response | ||
| print(f"assistant: {assistant_message}") | ||
|
|
||
| # Add assistant response to conversation history | ||
| conversation_history.append( | ||
| Message(role="assistant", content=assistant_message) | ||
| ) | ||
|
|
||
| # Get user input for the next message | ||
| print("\nuser: ", end="") | ||
| user_input = input().strip() | ||
|
|
||
| # Check if user wants to exit | ||
| if user_input.lower() == "exit": | ||
| print("Exiting application...") | ||
| break | ||
|
|
||
| # Add user input to conversation history | ||
| conversation_history.append(Message(role="user", content=user_input)) | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| main() | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| # ------------------------------------------------------------------------- | ||
| # Copyright (c) Microsoft Corporation. All rights reserved. | ||
| # Licensed under the MIT License. See License.txt in the project root for | ||
| # license information. | ||
| # -------------------------------------------------------------------------- | ||
| """ | ||
| Model classes for Azure OpenAI Chat Application. | ||
| This module provides data classes for representing chat messages and model configurations | ||
|
mrm9084 marked this conversation as resolved.
Outdated
|
||
| used in the Azure OpenAI-powered chat application. | ||
| """ | ||
| from typing import List, Optional, Dict, Any | ||
|
|
||
|
|
||
|
mrm9084 marked this conversation as resolved.
|
||
| class Message: | ||
| """ | ||
| Represents a chat message with a role and content. | ||
| Maps to configuration values with keys 'role' and 'content'. | ||
| """ | ||
|
|
||
| def __init__(self, role: Optional[str] = None, content: Optional[str] = None): | ||
| """Initialize a Message instance with role and content.""" | ||
| self.role = role | ||
| self.content = content | ||
|
|
||
| @classmethod | ||
| def from_dict(cls, data: Dict[str, Any]) -> "Message": | ||
| """Create a Message instance from a dictionary.""" | ||
| return cls(role=data.get("role"), content=data.get("content")) | ||
|
|
||
|
|
||
| class ModelConfiguration: | ||
|
mrm9084 marked this conversation as resolved.
Outdated
|
||
| """ | ||
| Represents the configuration for an AI model including messages and parameters. | ||
|
mrm9084 marked this conversation as resolved.
|
||
| Maps to configuration values with keys 'model', 'messages', 'max_tokens', 'temperature', and 'top_p'. | ||
|
mrm9084 marked this conversation as resolved.
Outdated
|
||
| """ | ||
|
|
||
| def __init__( | ||
| self, | ||
| model: Optional[str] = None, | ||
| messages: Optional[List[Message]] = None, | ||
|
mrm9084 marked this conversation as resolved.
Outdated
|
||
| max_tokens: int = 1024, | ||
| temperature: float = 0.7, | ||
| top_p: float = 0.95, | ||
| ): | ||
| """Initialize model configuration with parameters for OpenAI API calls.""" | ||
|
mrm9084 marked this conversation as resolved.
Outdated
|
||
| self.model = model | ||
| self.messages = messages or [] | ||
| self.max_tokens = int(max_tokens) if max_tokens is not None else 1024 | ||
| self.temperature = float(temperature) if temperature is not None else 0.7 | ||
| self.top_p = float(top_p) if top_p is not None else 0.95 | ||
|
|
||
| @classmethod | ||
| def from_dict(cls, data: Dict[str, Any]) -> "ModelConfiguration": | ||
|
mrm9084 marked this conversation as resolved.
Outdated
|
||
| """Create a ModelConfiguration instance from a dictionary.""" | ||
| messages = [Message.from_dict(msg) for msg in data.get("messages", [])] | ||
|
|
||
| return cls( | ||
| model=data.get("model"), | ||
| messages=messages, | ||
| max_tokens=data.get("max_tokens", 1024), | ||
| temperature=data.get("temperature", 0.7), | ||
| top_p=data.get("top_p", 0.95), | ||
| ) | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| azure-identity | ||
| azure-appconfiguration-provider | ||
| openai | ||
|
mrm9084 marked this conversation as resolved.
Outdated
|
||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.