-
Notifications
You must be signed in to change notification settings - Fork 787
Expand file tree
/
Copy pathapp_config.py
More file actions
282 lines (230 loc) · 10.5 KB
/
Copy pathapp_config.py
File metadata and controls
282 lines (230 loc) · 10.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
# app_config.py
import logging
import os
from typing import Optional
from azure.ai.projects.aio import AIProjectClient
from azure.cosmos import CosmosClient
from azure.identity import DefaultAzureCredential, ManagedIdentityCredential
from dotenv import load_dotenv
from semantic_kernel import Kernel
# Load environment variables from .env file
load_dotenv()
class AppConfig:
"""Application configuration class that loads settings from environment variables."""
def __init__(self):
"""Initialize the application configuration with environment variables."""
self.logger = logging.getLogger(__name__)
# Azure authentication settings
self.AZURE_TENANT_ID = self._get_optional("AZURE_TENANT_ID")
self.AZURE_CLIENT_ID = self._get_optional("AZURE_CLIENT_ID")
self.AZURE_CLIENT_SECRET = self._get_optional("AZURE_CLIENT_SECRET")
# CosmosDB settings
self.COSMOSDB_ENDPOINT = self._get_optional("COSMOSDB_ENDPOINT")
self.COSMOSDB_DATABASE = self._get_optional("COSMOSDB_DATABASE")
self.COSMOSDB_CONTAINER = self._get_optional("COSMOSDB_CONTAINER")
self.APPLICATIONINSIGHTS_CONNECTION_STRING = self._get_required(
"APPLICATIONINSIGHTS_CONNECTION_STRING"
)
self.APP_ENV = self._get_required("APP_ENV", "prod")
# self.AZURE_AI_MODEL_DEPLOYMENT_NAME = self._get_required(
# "AZURE_AI_MODEL_DEPLOYMENT_NAME", "gpt-4o"
# )
self.AZURE_COGNITIVE_SERVICES = self._get_optional(
"AZURE_COGNITIVE_SERVICES", "https://cognitiveservices.azure.com/.default"
)
self.AZURE_MANAGEMENT_SCOPE = self._get_optional(
"AZURE_MANAGEMENT_SCOPE", "https://management.azure.com/.default"
)
# Azure OpenAI settings
self.AZURE_OPENAI_DEPLOYMENT_NAME = self._get_required(
"AZURE_OPENAI_DEPLOYMENT_NAME", "gpt-4o"
)
self.AZURE_OPENAI_API_VERSION = self._get_required(
"AZURE_OPENAI_API_VERSION", "2024-11-20"
)
self.AZURE_OPENAI_ENDPOINT = self._get_required("AZURE_OPENAI_ENDPOINT")
self.REASONING_MODEL_NAME = self._get_optional("REASONING_MODEL_NAME", "o3")
# self.AZURE_BING_CONNECTION_NAME = self._get_optional(
# "AZURE_BING_CONNECTION_NAME"
# )
self.SUPPORTED_MODELS = self._get_optional("SUPPORTED_MODELS")
# Frontend settings
self.FRONTEND_SITE_NAME = self._get_optional(
"FRONTEND_SITE_NAME", "http://127.0.0.1:3000"
)
# Azure AI settings
self.AZURE_AI_SUBSCRIPTION_ID = self._get_required("AZURE_AI_SUBSCRIPTION_ID")
self.AZURE_AI_RESOURCE_GROUP = self._get_required("AZURE_AI_RESOURCE_GROUP")
self.AZURE_AI_PROJECT_NAME = self._get_required("AZURE_AI_PROJECT_NAME")
self.AZURE_AI_AGENT_ENDPOINT = self._get_required("AZURE_AI_AGENT_ENDPOINT")
self.AZURE_AI_PROJECT_ENDPOINT = self._get_optional("AZURE_AI_PROJECT_ENDPOINT")
# Azure Search settings
self.AZURE_SEARCH_ENDPOINT = self._get_optional("AZURE_AI_SEARCH_ENDPOINT")
# Optional MCP server endpoint (for local MCP server or remote)
# Example: http://127.0.0.1:8000/mcp
self.MCP_SERVER_ENDPOINT = self._get_optional("MCP_SERVER_ENDPOINT")
self.MCP_SERVER_NAME = self._get_optional(
"MCP_SERVER_NAME", "MCPGreetingServer"
)
self.MCP_SERVER_DESCRIPTION = self._get_optional(
"MCP_SERVER_DESCRIPTION", "MCP server with greeting and planning tools"
)
self.TENANT_ID = self._get_optional("AZURE_TENANT_ID")
self.CLIENT_ID = self._get_optional("AZURE_CLIENT_ID")
self.AZURE_AI_SEARCH_CONNECTION_NAME = self._get_optional(
"AZURE_AI_SEARCH_CONNECTION_NAME"
)
self.AZURE_AI_SEARCH_INDEX_NAME = self._get_optional(
"AZURE_AI_SEARCH_INDEX_NAME"
)
self.AZURE_AI_SEARCH_ENDPOINT = self._get_optional("AZURE_AI_SEARCH_ENDPOINT")
self.AZURE_AI_SEARCH_API_KEY = self._get_optional("AZURE_AI_SEARCH_API_KEY")
# self.BING_CONNECTION_NAME = self._get_optional("BING_CONNECTION_NAME")
test_team_json = self._get_optional("TEST_TEAM_JSON")
self.AGENT_TEAM_FILE = f"../../data/agent_teams/{test_team_json}.json"
# Cached clients and resources
self._azure_credentials = None
self._cosmos_client = None
self._cosmos_database = None
self._ai_project_client = None
self._agents = {}
def get_azure_credential(self, client_id=None):
"""
Returns an Azure credential based on the application environment.
If the environment is 'dev', it uses DefaultAzureCredential.
Otherwise, it uses ManagedIdentityCredential.
Args:
client_id (str, optional): The client ID for the Managed Identity Credential.
Returns:
Credential object: Either DefaultAzureCredential or ManagedIdentityCredential.
"""
if self.APP_ENV == "dev":
return DefaultAzureCredential() # CodeQL [SM05139]: DefaultAzureCredential is safe here
else:
return ManagedIdentityCredential(client_id=client_id)
def get_azure_credentials(self):
"""Retrieve Azure credentials, either from environment variables or managed identity."""
if self._azure_credentials is None:
self._azure_credentials = self.get_azure_credential(self.AZURE_CLIENT_ID)
return self._azure_credentials
async def get_access_token(self) -> str:
"""Get Azure access token for API calls."""
try:
credential = self.get_azure_credentials()
token = credential.get_token(self.AZURE_COGNITIVE_SERVICES)
return token.token
except Exception as e:
self.logger.error(f"Failed to get access token: {e}")
raise
def _get_required(self, name: str, default: Optional[str] = None) -> str:
"""Get a required configuration value from environment variables.
Args:
name: The name of the environment variable
default: Optional default value if not found
Returns:
The value of the environment variable or default if provided
Raises:
ValueError: If the environment variable is not found and no default is provided
"""
if name in os.environ:
return os.environ[name]
if default is not None:
logging.warning(
"Environment variable %s not found, using default value", name
)
return default
raise ValueError(
f"Environment variable {name} not found and no default provided"
)
def _get_optional(self, name: str, default: str = "") -> str:
"""Get an optional configuration value from environment variables.
Args:
name: The name of the environment variable
default: Default value if not found (default: "")
Returns:
The value of the environment variable or the default value
"""
if name in os.environ:
return os.environ[name]
return default
def _get_bool(self, name: str) -> bool:
"""Get a boolean configuration value from environment variables.
Args:
name: The name of the environment variable
Returns:
True if the environment variable exists and is set to 'true' or '1', False otherwise
"""
return name in os.environ and os.environ[name].lower() in ["true", "1"]
def get_cosmos_database_client(self):
"""Get a Cosmos DB client for the configured database.
Returns:
A Cosmos DB database client
"""
try:
if self._cosmos_client is None:
self._cosmos_client = CosmosClient(
self.COSMOSDB_ENDPOINT,
credential=self.get_azure_credential(self.AZURE_CLIENT_ID),
)
if self._cosmos_database is None:
self._cosmos_database = self._cosmos_client.get_database_client(
self.COSMOSDB_DATABASE
)
return self._cosmos_database
except Exception as exc:
logging.error(
"Failed to create CosmosDB client: %s. CosmosDB is required for this application.",
exc,
)
raise
def create_kernel(self):
"""Creates a new Semantic Kernel instance.
Returns:
A new Semantic Kernel instance
"""
# Create a new kernel instance without manually configuring OpenAI services
# The agents will be created using Azure AI Agent Project pattern instead
kernel = Kernel()
return kernel
def get_ai_project_client(self):
"""Create and return an AIProjectClient for Azure AI Foundry using from_connection_string.
Returns:
An AIProjectClient instance
"""
if self._ai_project_client is not None:
return self._ai_project_client
try:
credential = self.get_azure_credential(self.AZURE_CLIENT_ID)
if credential is None:
raise RuntimeError(
"Unable to acquire Azure credentials; ensure Managed Identity is configured"
)
endpoint = self.AZURE_AI_AGENT_ENDPOINT
self._ai_project_client = AIProjectClient(
endpoint=endpoint, credential=credential
)
return self._ai_project_client
except Exception as exc:
logging.error("Failed to create AIProjectClient: %s", exc)
raise
def get_user_local_browser_language(self) -> str:
"""Get the user's local browser language from environment variables.
Returns:
The user's local browser language or 'en-US' if not set
"""
return self._get_optional("USER_LOCAL_BROWSER_LANGUAGE", "en-US")
def set_user_local_browser_language(self, language: str):
"""Set the user's local browser language in environment variables.
Args:
language: The language code to set (e.g., 'en-US')
"""
os.environ["USER_LOCAL_BROWSER_LANGUAGE"] = language
# Get agent team list by user_id dictionary index
def get_agents(self) -> dict[str, list]:
"""Get the list of agents configured in the application.
Returns:
A list of agent names or configurations
"""
return self._agents
# Create a global instance of AppConfig
config = AppConfig()