Skip to content

Commit 8185048

Browse files
json upload files authentications, deletion and model validation
1 parent ef4bb56 commit 8185048

File tree

12 files changed

+529
-103
lines changed

12 files changed

+529
-103
lines changed

src/backend/.env.sample

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ AZURE_OPENAI_MODEL_NAME=gpt-4o
77
AZURE_OPENAI_DEPLOYMENT_NAME=gpt-4o
88
AZURE_OPENAI_API_VERSION=2024-08-01-preview
99

10+
AZURE_SEARCH_ENDPOINT=
11+
AZURE_SEARCH_INDEX_NAME=
12+
1013
APPLICATIONINSIGHTS_INSTRUMENTATION_KEY=
1114
AZURE_AI_PROJECT_ENDPOINT=
1215
AZURE_AI_SUBSCRIPTION_ID=

src/backend/app_kernel.py

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
TeamConfiguration,
4141
)
4242
from services.json_service import JsonService
43+
from services.model_validation_service import ModelValidationService
4344

4445
# Updated import for KernelArguments
4546
from utils_kernel import initialize_runtime_and_context, rai_success, rai_validate_team_config
@@ -1504,6 +1505,42 @@ async def upload_team_config_endpoint(request: Request, file: UploadFile = File(
15041505
},
15051506
)
15061507

1508+
# Validate model deployments
1509+
model_validator = ModelValidationService()
1510+
models_valid, missing_models = await model_validator.validate_team_models(json_data)
1511+
1512+
if not models_valid:
1513+
error_message = (
1514+
f"The following required models are not deployed in your Azure AI project: {', '.join(missing_models)}. "
1515+
f"Please deploy these models in Azure AI Foundry before uploading this team configuration."
1516+
)
1517+
1518+
# Track model validation failure
1519+
track_event_if_configured(
1520+
"Team configuration model validation failed",
1521+
{
1522+
"status": "failed",
1523+
"user_id": user_id,
1524+
"filename": file.filename,
1525+
"missing_models": missing_models,
1526+
},
1527+
)
1528+
1529+
raise HTTPException(
1530+
status_code=400,
1531+
detail=error_message
1532+
)
1533+
1534+
# Track successful model validation
1535+
track_event_if_configured(
1536+
"Team configuration model validation passed",
1537+
{
1538+
"status": "passed",
1539+
"user_id": user_id,
1540+
"filename": file.filename,
1541+
},
1542+
)
1543+
15071544
# Initialize memory store and service
15081545
kernel, memory_store = await initialize_runtime_and_context("", user_id)
15091546
json_service = JsonService(memory_store)
@@ -1784,6 +1821,43 @@ async def delete_team_config_endpoint(config_id: str, request: Request):
17841821
raise HTTPException(status_code=500, detail="Internal server error occurred")
17851822

17861823

1824+
@app.get("/api/model_deployments")
1825+
async def get_model_deployments_endpoint(request: Request):
1826+
"""
1827+
Get information about available model deployments for debugging/validation.
1828+
1829+
---
1830+
tags:
1831+
- Model Validation
1832+
responses:
1833+
200:
1834+
description: List of available model deployments
1835+
401:
1836+
description: Missing or invalid user information
1837+
"""
1838+
# Validate user authentication
1839+
authenticated_user = get_authenticated_user_details(request_headers=request.headers)
1840+
user_id = authenticated_user["user_principal_id"]
1841+
if not user_id:
1842+
raise HTTPException(
1843+
status_code=401, detail="Missing or invalid user information"
1844+
)
1845+
1846+
try:
1847+
model_validator = ModelValidationService()
1848+
deployments = await model_validator.list_model_deployments()
1849+
summary = await model_validator.get_deployment_status_summary()
1850+
1851+
return {
1852+
"deployments": deployments,
1853+
"summary": summary
1854+
}
1855+
1856+
except Exception as e:
1857+
logging.error(f"Error retrieving model deployments: {str(e)}")
1858+
raise HTTPException(status_code=500, detail="Internal server error occurred")
1859+
1860+
17871861
# Run the app
17881862
if __name__ == "__main__":
17891863
import uvicorn

src/backend/context/cosmos_memory_kernel.py

Lines changed: 50 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -92,15 +92,15 @@ async def initialize(self):
9292
id=self._cosmos_container,
9393
partition_key=PartitionKey(path="/session_id"),
9494
)
95+
# Only set initialized flag if we successfully got a container
96+
self._initialized.set()
9597
except Exception as e:
9698
logging.error(
9799
f"Failed to initialize CosmosDB container: {e}. Continuing without CosmosDB for testing."
98100
)
99-
# Do not raise to prevent test failures
101+
# Do not set initialized flag if initialization failed
100102
self._container = None
101103

102-
self._initialized.set()
103-
104104
# Helper method for awaiting initialization
105105
async def ensure_initialized(self):
106106
"""Ensure that the container is initialized."""
@@ -110,8 +110,10 @@ async def ensure_initialized(self):
110110

111111
# If after initialization the container is still None, that means initialization failed
112112
if self._container is None:
113-
# Re-attempt initialization once in case the previous attempt failed
113+
# Re-attempt initialization with a small delay for credential refresh
114114
try:
115+
import asyncio
116+
await asyncio.sleep(0.5) # Small delay for credential refresh
115117
await self.initialize()
116118
except Exception as e:
117119
logging.error(f"Re-initialization attempt failed: {e}")
@@ -130,6 +132,10 @@ async def add_item(self, item: BaseDataModel) -> None:
130132
# Convert the model to a dict
131133
document = item.model_dump()
132134

135+
# Ensure all documents have session_id for consistent partitioning
136+
if "session_id" not in document:
137+
document["session_id"] = self.session_id
138+
133139
# Handle datetime objects by converting them to ISO format strings
134140
for key, value in list(document.items()):
135141
if isinstance(value, datetime.datetime):
@@ -461,14 +467,46 @@ async def delete_team_by_id(self, id: str) -> bool:
461467
# First find the team to get its partition key
462468
team = await self.get_team_by_id(id)
463469
if team:
464-
# Use the session_id as partition key, or fall back to user_id if no session_id
465-
partition_key = (
466-
team.session_id
467-
if hasattr(team, "session_id") and team.session_id
468-
else team.user_id
469-
)
470-
await self._container.delete_item(item=id, partition_key=partition_key)
471-
return True
470+
# Debug logging
471+
logging.info(f"Attempting to delete team {id}")
472+
logging.info(f"Team user_id: {team.user_id}")
473+
logging.info(f"Team session_id: {getattr(team, 'session_id', 'NOT_SET')}")
474+
475+
# Try different partition key strategies
476+
partition_keys_to_try = []
477+
478+
# Strategy 1: Use session_id if it exists
479+
if hasattr(team, "session_id") and team.session_id:
480+
partition_keys_to_try.append(team.session_id)
481+
logging.info(f"Will try session_id: {team.session_id}")
482+
483+
# Strategy 2: Use user_id as fallback
484+
if team.user_id:
485+
partition_keys_to_try.append(team.user_id)
486+
logging.info(f"Will try user_id: {team.user_id}")
487+
488+
# Strategy 3: Use current context session_id
489+
if self.session_id:
490+
partition_keys_to_try.append(self.session_id)
491+
logging.info(f"Will try context session_id: {self.session_id}")
492+
493+
# Strategy 4: Try null/empty partition key (for documents created without session_id)
494+
partition_keys_to_try.extend([None, ""])
495+
logging.info("Will try null and empty partition keys")
496+
497+
# Try each partition key until one works
498+
for partition_key in partition_keys_to_try:
499+
try:
500+
logging.info(f"Attempting delete with partition key: {partition_key}")
501+
await self._container.delete_item(item=id, partition_key=partition_key)
502+
logging.info(f"Successfully deleted team {id} with partition key: {partition_key}")
503+
return True
504+
except Exception as e:
505+
logging.warning(f"Delete failed with partition key {partition_key}: {str(e)}")
506+
continue
507+
508+
logging.error(f"All partition key strategies failed for team {id}")
509+
return False
472510
return False
473511
except Exception as e:
474512
logging.exception(f"Failed to delete team from Cosmos DB: {e}")

src/backend/requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
fastapi
22
uvicorn
3+
aiohttp
34

45
azure-cosmos
56
azure-monitor-opentelemetry

src/backend/services/json_service.py

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -238,15 +238,7 @@ async def delete_team_configuration(self, config_id: str, user_id: str) -> bool:
238238
)
239239
return False
240240

241-
# Verify the configuration belongs to the user
242-
if team_config.user_id != user_id:
243-
self.logger.warning(
244-
"Access denied: cannot delete config %s for user %s",
245-
config_id,
246-
user_id,
247-
)
248-
return False
249-
241+
250242
# Delete the configuration using the specific delete_team_by_id method
251243
success = await self.memory_context.delete_team_by_id(config_id)
252244

0 commit comments

Comments
 (0)