Skip to content

Commit 9fd3e4d

Browse files
Updated team config with isdefault and logic to display teams is updated
1 parent f1b191e commit 9fd3e4d

10 files changed

Lines changed: 265 additions & 189 deletions

File tree

data/agent_teams/contract_compliance_team.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
"created": "",
77
"created_by": "",
88
"deployment_name": "gpt-5.4-mini",
9+
"is_default": true,
910
"description": "A multi-agent compliance review team that summarizes NDAs, identifies risks, checks compliance, and recommends improvements using advanced legal reasoning and retrieval-augmented analysis.",
1011
"logo": "",
1112
"plan": "",

data/agent_teams/hr.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
"created": "",
77
"created_by": "",
88
"deployment_name": "gpt-5.4-mini",
9+
"is_default": true,
910
"agents": [
1011
{
1112
"input_key": "",

data/agent_teams/marketing.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
"created": "",
77
"created_by": "",
88
"deployment_name": "gpt-5.4-mini",
9+
"is_default": true,
910
"agents": [
1011
{
1112
"input_key": "",

data/agent_teams/retail.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
"created": "",
77
"created_by": "",
88
"deployment_name": "gpt-5.4-mini",
9+
"is_default": true,
910
"agents": [
1011
{
1112
"input_key": "",

data/agent_teams/rfp_analysis_team.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
"created": "",
77
"created_by": "",
88
"deployment_name": "gpt-5.4-mini",
9+
"is_default": true,
910
"description": "A specialized multi-agent team that analyzes RFP and contract documents to summarize content, identify potential risks, check compliance gaps, and provide action plans for contract improvement.",
1011
"logo": "",
1112
"plan": "",

src/backend/common/database/cosmosdb.py

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -273,10 +273,11 @@ async def get_team(self, team_id: str) -> Optional[TeamConfiguration]:
273273
Returns:
274274
TeamConfiguration object or None if not found
275275
"""
276-
query = "SELECT * FROM c WHERE c.team_id=@team_id AND c.data_type=@data_type"
276+
query = "SELECT * FROM c WHERE c.team_id=@team_id AND c.data_type=@data_type AND (c.user_id=@user_id OR c.is_default=true)"
277277
parameters = [
278278
{"name": "@team_id", "value": team_id},
279279
{"name": "@data_type", "value": DataType.team_config},
280+
{"name": "@user_id", "value": self.user_id},
280281
]
281282
teams = await self.query_items(query, parameters, TeamConfiguration)
282283
return teams[0] if teams else None
@@ -290,33 +291,34 @@ async def get_team_by_id(self, team_id: str) -> Optional[TeamConfiguration]:
290291
Returns:
291292
TeamConfiguration object or None if not found
292293
"""
293-
query = "SELECT * FROM c WHERE c.team_id=@team_id AND c.data_type=@data_type"
294+
query = "SELECT * FROM c WHERE c.team_id=@team_id AND c.data_type=@data_type AND (c.user_id=@user_id OR c.is_default=true)"
294295
parameters = [
295296
{"name": "@team_id", "value": team_id},
296297
{"name": "@data_type", "value": DataType.team_config},
298+
{"name": "@user_id", "value": self.user_id},
297299
]
298300
teams = await self.query_items(query, parameters, TeamConfiguration)
299301
return teams[0] if teams else None
300302

301303
async def get_all_teams(self) -> List[TeamConfiguration]:
302-
"""Retrieve all team configurations for a specific user.
303-
304-
Args:
305-
user_id: The user_id to get team configurations for
304+
"""Retrieve all team configurations visible to the current user.
306305
307306
Returns:
308-
List of TeamConfiguration objects
307+
List of TeamConfiguration objects: default teams plus user-specific teams
309308
"""
310-
query = "SELECT * FROM c WHERE c.data_type=@data_type ORDER BY c.created DESC"
309+
query = "SELECT * FROM c WHERE c.data_type=@data_type AND (c.user_id=@user_id OR c.is_default=true) ORDER BY c.created DESC"
311310
parameters = [
312311
{"name": "@data_type", "value": DataType.team_config},
312+
{"name": "@user_id", "value": self.user_id},
313313
]
314314
teams = await self.query_items(query, parameters, TeamConfiguration)
315315
return teams
316316

317317
async def delete_team(self, team_id: str) -> bool:
318318
"""Delete a team configuration by team_id.
319319
320+
Only user-owned teams can be deleted; default teams cannot be deleted.
321+
320322
Args:
321323
team_id: The team_id of the team configuration to delete
322324
@@ -328,9 +330,12 @@ async def delete_team(self, team_id: str) -> bool:
328330
try:
329331
# First find the team to get its document id and partition key
330332
team = await self.get_team(team_id)
331-
print(team)
332-
if team:
333-
await self.delete_item(item_id=team.id, partition_key=team.session_id)
333+
if not team:
334+
return False
335+
# Prevent deletion of default teams
336+
if team.is_default:
337+
return False
338+
await self.delete_item(item_id=team.id, partition_key=team.session_id)
334339
return True
335340
except Exception as e:
336341
logging.exception(f"Failed to delete team from Cosmos DB: {e}")

src/backend/common/database/database_factory.py

Lines changed: 42 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,16 @@
1010

1111

1212
class DatabaseFactory:
13-
"""Factory class for creating database instances."""
13+
"""Factory class for creating database instances.
1414
15-
_instance: Optional[DatabaseBase] = None
15+
Caches the expensive CosmosDB connection infrastructure (client, database,
16+
container) while creating per-request CosmosDBClient instances scoped to
17+
the calling user_id. This ensures ownership filters in queries always
18+
reference the correct user without race conditions across concurrent
19+
asyncio tasks.
20+
"""
21+
22+
_shared_instance: Optional[CosmosDBClient] = None
1623
_logger = logging.getLogger(__name__)
1724

1825
@staticmethod
@@ -21,44 +28,55 @@ async def get_database(
2128
force_new: bool = False,
2229
) -> DatabaseBase:
2330
"""
24-
Get a database instance.
31+
Get a database instance scoped to the given user_id.
32+
33+
The underlying CosmosDB connection (client, database, container) is
34+
shared across all requests. Each call returns a lightweight wrapper
35+
bound to *user_id* so that query-level ownership predicates are
36+
always correct — even under concurrent async execution.
2537
2638
Args:
27-
endpoint: CosmosDB endpoint URL
28-
credential: Azure credential for authentication
29-
database_name: Name of the CosmosDB database
30-
container_name: Name of the CosmosDB container
31-
session_id: Session ID for partitioning
32-
user_id: User ID for data isolation
33-
force_new: Force creation of new instance
39+
user_id: User ID for data isolation (required for ownership checks)
40+
force_new: Force re-creation of the shared connection
3441
3542
Returns:
36-
DatabaseBase: Database instance
43+
DatabaseBase: Database instance scoped to user_id
3744
"""
3845

39-
# Create new instance if forced or if singleton doesn't exist
40-
if force_new or DatabaseFactory._instance is None:
41-
cosmos_db_client = CosmosDBClient(
46+
# Ensure the shared connection infrastructure is initialized
47+
if force_new or DatabaseFactory._shared_instance is None:
48+
shared = CosmosDBClient(
4249
endpoint=config.COSMOSDB_ENDPOINT,
4350
credential=config.get_azure_credentials(),
4451
database_name=config.COSMOSDB_DATABASE,
4552
container_name=config.COSMOSDB_CONTAINER,
4653
session_id="",
4754
user_id=user_id,
4855
)
56+
await shared.initialize()
57+
DatabaseFactory._shared_instance = shared
4958

50-
await cosmos_db_client.initialize()
51-
52-
if not force_new:
53-
DatabaseFactory._instance = cosmos_db_client
54-
55-
return cosmos_db_client
59+
# Create a per-request instance that shares the connection but is
60+
# bound to the caller's user_id
61+
instance = CosmosDBClient(
62+
endpoint=config.COSMOSDB_ENDPOINT,
63+
credential=config.get_azure_credentials(),
64+
database_name=config.COSMOSDB_DATABASE,
65+
container_name=config.COSMOSDB_CONTAINER,
66+
session_id="",
67+
user_id=user_id,
68+
)
69+
# Share the already-initialized connection objects
70+
instance.client = DatabaseFactory._shared_instance.client
71+
instance.database = DatabaseFactory._shared_instance.database
72+
instance.container = DatabaseFactory._shared_instance.container
73+
instance._initialized = True
5674

57-
return DatabaseFactory._instance
75+
return instance
5876

5977
@staticmethod
6078
async def close_all():
6179
"""Close all database connections."""
62-
if DatabaseFactory._instance:
63-
await DatabaseFactory._instance.close()
64-
DatabaseFactory._instance = None
80+
if DatabaseFactory._shared_instance:
81+
await DatabaseFactory._shared_instance.close()
82+
DatabaseFactory._shared_instance = None

src/backend/common/models/messages_af.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,7 @@ class TeamConfiguration(BaseDataModel):
203203
plan: str = ""
204204
starting_tasks: List[StartingTask] = Field(default_factory=list)
205205
user_id: str # who uploaded this configuration
206+
is_default: bool = False # default teams are visible to all users
206207

207208

208209
class PlanWithSteps(Plan):

src/backend/v4/common/services/team_service.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ async def validate_and_parse_team_config(
112112
plan=json_data.get("plan", ""),
113113
starting_tasks=starting_tasks,
114114
user_id=user_id,
115+
is_default=json_data.get("is_default", False),
115116
)
116117

117118
self.logger.info(

0 commit comments

Comments
 (0)