Skip to content

Commit b4cf98d

Browse files
authored
feat: Added support for Azure OpenAI and other LLMs in Semantic Kernel (#51)
* refactored the chat service configuration to use an enum-based approach for support of other LLMs * feat: refactored the chat service configuration to use an enum-based approach for support of other LLMs * formatting changes for Notes for markdown
1 parent 9d7446e commit b4cf98d

File tree

3 files changed

+163
-24
lines changed

3 files changed

+163
-24
lines changed
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# For OpenAI
2+
OPENAI_API_KEY="your_api_key_here"
3+
OPENAI_CHAT_MODEL_ID="your-model-id"
4+
5+
# For Azure OpenAI (default)
6+
AZURE_OPENAI_API_KEY="your-azure-api-key-here"
7+
AZURE_OPENAI_ENDPOINT="https://your-resource.openai.azure.com/"
8+
AZURE_OPENAI_CHAT_DEPLOYMENT_NAME="your-chat-deployment-name"
9+
AZURE_OPENAI_API_VERSION="2024-12-01-preview"

samples/python/agents/semantickernel/README.md

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,16 +53,39 @@ sequenceDiagram
5353
cd samples/python/agents/semantickernel
5454
```
5555

56-
2. **Create an environment file (.env) with your API key and the model ID (e.g., "gpt-4.1"):**:
56+
2. **Configure environment variables**:
57+
58+
Create a `.env` file based on `.envexample` file. The agent automatically detects whether to use Azure OpenAI or standard OpenAI based on which environment variables are set.
59+
60+
**For OpenAI:**
5761

5862
```bash
5963
OPENAI_API_KEY="your_api_key_here"
6064
OPENAI_CHAT_MODEL_ID="your-model-id"
6165
```
6266

67+
**For Azure OpenAI (default):**
68+
69+
```bash
70+
AZURE_OPENAI_API_KEY="your-azure-api-key-here"
71+
AZURE_OPENAI_ENDPOINT="https://your-resource.openai.azure.com/"
72+
AZURE_OPENAI_CHAT_DEPLOYMENT_NAME="your-deployment-name"
73+
AZURE_OPENAI_API_VERSION="2024-12-01-preview"
74+
```
75+
76+
> [!NOTE]
77+
> The agent will use Azure OpenAI if all four Azure variables are set, otherwise it will use standard OpenAI if both OpenAI variables are set.
78+
79+
> [!NOTE]
80+
> Other LLMs can be used as well, but you will need to modify the code to use the appropriate AI connector via the chat completion service method. See Semantic Kernel [documentation](https://learn.microsoft.com/en-us/semantic-kernel/concepts/ai-services/chat-completion/?tabs=csharp-AzureOpenAI%2Cpython-AzureOpenAI%2Cjava-AzureOpenAI&pivots=programming-language-python#creating-a-chat-completion-service) for more details on how to configure other AI services.
81+
82+
> [!NOTE]
83+
> For details on environment variables, refer to the [Semantic Kernel AI Service Settings](https://github.com/microsoft/semantic-kernel/blob/main/python/samples/concepts/setup/ALL_SETTINGS.md#semantic-kernel-settings) document.
84+
6385
3. **Set up the Python Environment**:
6486

65-
> Note: pin the Python version to your desired version (3.10+)
87+
> [!NOTE]
88+
> pin the Python version to your desired version (3.10+)
6689
6790
```bash
6891
uv python pin 3.12
@@ -73,6 +96,7 @@ source .venv/bin/activate
7396

7497
Choose one of the following options:
7598

99+
> [!NOTE]
76100
> Make sure you run `uv run .` from the following directory: `samples/python/agents/semantickernel`
77101
78102
```bash
@@ -88,6 +112,7 @@ uv run . --host 0.0.0.0 --port 8080
88112

89113
5. **In a separate terminal, run the A2A client**:
90114

115+
> [!NOTE]
91116
> Make sure you run `uv run . --agent http://localhost:10020` from the following directory: `samples/python/hosts/cli`
92117
93118
```bash

samples/python/agents/semantickernel/agent.py

Lines changed: 127 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import asyncio
22
import logging
33
import os
4+
from enum import Enum
45

56
from collections.abc import AsyncIterable
67
from typing import TYPE_CHECKING, Annotated, Any, Literal
@@ -11,6 +12,7 @@
1112
from pydantic import BaseModel
1213
from semantic_kernel.agents import ChatCompletionAgent, ChatHistoryAgentThread
1314
from semantic_kernel.connectors.ai.open_ai import (
15+
AzureChatCompletion,
1416
OpenAIChatCompletion,
1517
OpenAIChatPromptExecutionSettings,
1618
)
@@ -22,14 +24,127 @@
2224
)
2325
from semantic_kernel.functions import KernelArguments, kernel_function
2426

25-
2627
if TYPE_CHECKING:
2728
from semantic_kernel.contents import ChatMessageContent
29+
from semantic_kernel.connectors.ai.chat_completion_client_base import ChatCompletionClientBase
2830

2931
logger = logging.getLogger(__name__)
3032

3133
load_dotenv()
3234

35+
# region Chat Service Configuration
36+
37+
38+
class ChatServices(str, Enum):
39+
"""Enum for supported chat completion services."""
40+
41+
AZURE_OPENAI = "azure_openai"
42+
OPENAI = "openai"
43+
44+
45+
service_id = "default"
46+
47+
48+
def get_chat_completion_service(service_name: ChatServices) -> "ChatCompletionClientBase":
49+
"""Return an appropriate chat completion service based on the service name.
50+
51+
Args:
52+
service_name (ChatServices): Service name.
53+
54+
Returns:
55+
ChatCompletionClientBase: Configured chat completion service.
56+
57+
Raises:
58+
ValueError: If the service name is not supported or required environment variables are missing.
59+
"""
60+
if service_name == ChatServices.AZURE_OPENAI:
61+
return _get_azure_openai_chat_completion_service()
62+
elif service_name == ChatServices.OPENAI:
63+
return _get_openai_chat_completion_service()
64+
else:
65+
raise ValueError(f"Unsupported service name: {service_name}")
66+
67+
68+
def _get_azure_openai_chat_completion_service() -> AzureChatCompletion:
69+
"""Return Azure OpenAI chat completion service.
70+
71+
Returns:
72+
AzureChatCompletion: The configured Azure OpenAI service.
73+
74+
Raises:
75+
ValueError: If required environment variables are not set.
76+
"""
77+
required_env_vars = {
78+
'AZURE_OPENAI_ENDPOINT': os.getenv('AZURE_OPENAI_ENDPOINT'),
79+
'AZURE_OPENAI_API_KEY': os.getenv('AZURE_OPENAI_API_KEY'),
80+
'AZURE_OPENAI_CHAT_DEPLOYMENT_NAME': os.getenv('AZURE_OPENAI_CHAT_DEPLOYMENT_NAME'),
81+
'AZURE_OPENAI_API_VERSION': os.getenv('AZURE_OPENAI_API_VERSION')
82+
}
83+
84+
missing_vars = [var_name for var_name, var_value in required_env_vars.items() if not var_value]
85+
if missing_vars:
86+
raise ValueError(
87+
f"Missing required Azure OpenAI environment variables: {', '.join(missing_vars)}"
88+
)
89+
90+
return AzureChatCompletion(service_id=service_id)
91+
92+
93+
def _get_openai_chat_completion_service() -> OpenAIChatCompletion:
94+
"""Return OpenAI chat completion service.
95+
96+
Returns:
97+
OpenAIChatCompletion: Configured OpenAI service.
98+
99+
Raises:
100+
ValueError: If required environment variables are not set.
101+
"""
102+
required_env_vars = {
103+
'OPENAI_API_KEY': os.getenv('OPENAI_API_KEY'),
104+
'OPENAI_MODEL_ID': os.getenv('OPENAI_MODEL_ID')
105+
}
106+
107+
missing_vars = [var_name for var_name, var_value in required_env_vars.items() if not var_value]
108+
if missing_vars:
109+
raise ValueError(
110+
f"Missing required OpenAI environment variables: {', '.join(missing_vars)}"
111+
)
112+
113+
return OpenAIChatCompletion(service_id=service_id)
114+
115+
116+
def auto_detect_chat_service() -> "ChatCompletionClientBase":
117+
"""Auto-detect and return an appropriate chat completion service based on available environment variables set in the .env file.
118+
119+
Returns:
120+
ChatCompletionClientBase: The first available and properly configured chat service.
121+
122+
Raises:
123+
ValueError: If no supported service configuration is found.
124+
"""
125+
# Try Azure OpenAI first
126+
try:
127+
return get_chat_completion_service(ChatServices.AZURE_OPENAI)
128+
except ValueError:
129+
pass
130+
131+
# Try OpenAI next
132+
try:
133+
return get_chat_completion_service(ChatServices.OPENAI)
134+
except ValueError:
135+
pass
136+
137+
# If no chat completion service is available, raise an error
138+
raise ValueError(
139+
"No supported chat completion service configuration found. "
140+
"Please set either Azure OpenAI environment variables "
141+
"(AZURE_OPENAI_ENDPOINT, AZURE_OPENAI_API_KEY, AZURE_OPENAI_CHAT_DEPLOYMENT_NAME, AZURE_OPENAI_API_VERSION) "
142+
"or OpenAI environment variables (OPENAI_API_KEY, OPENAI_MODEL_ID)."
143+
)
144+
145+
146+
# endregion
147+
33148
# region Plugin
34149

35150

@@ -65,7 +180,7 @@ def get_exchange_rate(
65180
rate = data['rates'][currency_to]
66181
return f'1 {currency_from} = {rate} {currency_to}'
67182
except Exception as e:
68-
return f'Currency API call failed: {str(e)}'
183+
return f'Currency API call failed: {e!s}'
69184

70185

71186
# endregion
@@ -93,18 +208,14 @@ class SemanticKernelTravelAgent:
93208
SUPPORTED_CONTENT_TYPES = ['text', 'text/plain']
94209

95210
def __init__(self):
96-
api_key = os.getenv('OPENAI_API_KEY', None)
97-
if not api_key:
98-
raise ValueError('OPENAI_API_KEY environment variable not set.')
99-
100-
model_id = os.getenv('OPENAI_CHAT_MODEL_ID', 'gpt-4.1')
101-
102-
# Define a CurrencyExchangeAgent to handle currency-related tasks
211+
# Auto-detect and configure the chat completion service
212+
# To explicitly specify a service, use:
213+
# chat_service = get_chat_completion_service(ChatServices.AZURE_OPENAI)
214+
# or chat_service = get_chat_completion_service(ChatServices.OPENAI)
215+
chat_service = auto_detect_chat_service()
216+
103217
currency_exchange_agent = ChatCompletionAgent(
104-
service=OpenAIChatCompletion(
105-
api_key=api_key,
106-
ai_model_id=model_id,
107-
),
218+
service=chat_service,
108219
name='CurrencyExchangeAgent',
109220
instructions=(
110221
'You specialize in handling currency-related requests from travelers. '
@@ -117,10 +228,7 @@ def __init__(self):
117228

118229
# Define an ActivityPlannerAgent to handle activity-related tasks
119230
activity_planner_agent = ChatCompletionAgent(
120-
service=OpenAIChatCompletion(
121-
api_key=api_key,
122-
ai_model_id=model_id,
123-
),
231+
service=chat_service,
124232
name='ActivityPlannerAgent',
125233
instructions=(
126234
'You specialize in planning and recommending activities for travelers. '
@@ -133,10 +241,7 @@ def __init__(self):
133241

134242
# Define the main TravelManagerAgent to delegate tasks to the appropriate agents
135243
self.agent = ChatCompletionAgent(
136-
service=OpenAIChatCompletion(
137-
api_key=api_key,
138-
ai_model_id=model_id,
139-
),
244+
service=chat_service,
140245
name='TravelManagerAgent',
141246
instructions=(
142247
"Your role is to carefully analyze the traveler's request and forward it to the appropriate agent based on the "
@@ -301,4 +406,4 @@ async def _ensure_thread_exists(self, session_id: str) -> None:
301406
self.thread = ChatHistoryAgentThread(thread_id=session_id)
302407

303408

304-
# endregion
409+
# endregion

0 commit comments

Comments
 (0)