Skip to content

Commit 2c06e48

Browse files
authored
Merge pull request #640 from Fr4nc3/macae-rfp-af-101725
Macae rfp af 101725
2 parents 31a4672 + c327edd commit 2c06e48

File tree

10 files changed

+552
-444
lines changed

10 files changed

+552
-444
lines changed

src/backend/common/database/cosmosdb.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
AgentMessage,
1313
AgentMessageData,
1414
BaseDataModel,
15+
CurrentTeamAgent,
1516
DataType,
1617
Plan,
1718
Step,
@@ -491,3 +492,46 @@ async def get_agent_messages(self, plan_id: str) -> List[AgentMessageData]:
491492
]
492493

493494
return await self.query_items(query, parameters, AgentMessageData)
495+
496+
async def add_team_agent(self, team_agent: CurrentTeamAgent) -> None:
497+
"""Add an agent message to the database."""
498+
await self.delete_team_agent(team_agent.team_id, team_agent.agent_name) # Ensure no duplicates
499+
await self.add_item(team_agent)
500+
501+
async def delete_team_agent(self, team_id: str, agent_name: str) -> None:
502+
"""Delete the current team for a user."""
503+
query = "SELECT c.id, c.session_id FROM c WHERE c.team_id=@team_id AND c.data_type=@data_type AND c.agent_name=@agent_name"
504+
505+
params = [
506+
{"name": "@team_id", "value": team_id},
507+
{"name": "@agent_name", "value": agent_name},
508+
{"name": "@data_type", "value": DataType.current_team_agent},
509+
]
510+
items = self.container.query_items(query=query, parameters=params)
511+
print("Items to delete:", items)
512+
if items:
513+
async for doc in items:
514+
try:
515+
await self.container.delete_item(
516+
doc["id"], partition_key=doc["session_id"]
517+
)
518+
except Exception as e:
519+
self.logger.warning(
520+
"Failed deleting current team doc %s: %s", doc.get("id"), e
521+
)
522+
523+
return True
524+
525+
async def get_team_agent(
526+
self, team_id: str, agent_name: str
527+
) -> Optional[CurrentTeamAgent]:
528+
"""Retrieve a team agent by team_id and agent_name."""
529+
query = "SELECT * FROM c WHERE c.team_id=@team_id AND c.data_type=@data_type AND c.agent_name=@agent_name"
530+
params = [
531+
{"name": "@team_id", "value": team_id},
532+
{"name": "@agent_name", "value": agent_name},
533+
{"name": "@data_type", "value": DataType.current_team_agent},
534+
]
535+
536+
results = await self.query_items(query, params, CurrentTeamAgent)
537+
return results[0] if results else None

src/backend/common/database/database_base.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from ..models.messages_af import (
99
AgentMessageData,
1010
BaseDataModel,
11+
CurrentTeamAgent,
1112
Plan,
1213
Step,
1314
TeamConfiguration,
@@ -231,3 +232,20 @@ async def update_agent_message(self, message: AgentMessageData) -> None:
231232
async def get_agent_messages(self, plan_id: str) -> Optional[AgentMessageData]:
232233
"""Retrieve agent messages by plan_id."""
233234
pass
235+
236+
@abstractmethod
237+
async def add_team_agent(self, team_agent: CurrentTeamAgent) -> None:
238+
"""Add an agent message to the database."""
239+
pass
240+
241+
@abstractmethod
242+
async def delete_team_agent(self, team_id: str, agent_name: str) -> None:
243+
"""Delete a team agent from the database."""
244+
pass
245+
246+
@abstractmethod
247+
async def get_team_agent(
248+
self, team_id: str, agent_name: str
249+
) -> Optional[CurrentTeamAgent]:
250+
"""Retrieve a team agent by team_id and agent_name."""
251+
pass

src/backend/common/models/messages_af.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ class DataType(str, Enum):
2222
agent_message = "agent_message"
2323
team_config = "team_config"
2424
user_current_team = "user_current_team"
25+
current_team_agent = "current_team_agent"
2526
m_plan = "m_plan"
2627
m_plan_message = "m_plan_message"
2728

@@ -111,6 +112,17 @@ class UserCurrentTeam(BaseDataModel):
111112
user_id: str
112113
team_id: str
113114

115+
class CurrentTeamAgent(BaseDataModel):
116+
"""Represents the current agent of a user."""
117+
data_type: Literal[DataType.current_team_agent] = DataType.current_team_agent
118+
team_id: str
119+
team_name: str
120+
agent_name: str
121+
agent_description: str
122+
agent_instructions: str
123+
agent_foundry_id: str
124+
125+
114126

115127
class Plan(BaseDataModel):
116128
"""Represents a plan containing multiple steps."""

src/backend/common/utils/utils_af.py

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,40 @@
33
import logging
44

55
# Converted import path (agent_framework version of FoundryAgentTemplate)
6+
from common.database.database_base import DatabaseBase
7+
from common.models.messages_af import TeamConfiguration
8+
from v4.common.services.team_service import TeamService
69
from v4.magentic_agents.foundry_agent import FoundryAgentTemplate # formerly v4.magentic_agents.foundry_agent
710
from v4.config.agent_registry import agent_registry
811
from common.config.app_config import config
912
logging.basicConfig(level=logging.INFO)
1013

11-
12-
async def create_RAI_agent() -> FoundryAgentTemplate:
14+
async def find_first_available_team(team_service: TeamService, user_id: str) -> str:
15+
"""
16+
Check teams in priority order (4 to 1) and return the first available team ID.
17+
Priority: RFP (4) -> Retail (3) -> Marketing (2) -> HR (1)
18+
"""
19+
team_priority_order = [
20+
"00000000-0000-0000-0000-000000000004", # RFP
21+
"00000000-0000-0000-0000-000000000003", # Retail
22+
"00000000-0000-0000-0000-000000000002", # Marketing
23+
"00000000-0000-0000-0000-000000000001", # HR
24+
]
25+
26+
for team_id in team_priority_order:
27+
try:
28+
team_config = await team_service.get_team_configuration(team_id, user_id)
29+
if team_config is not None:
30+
print(f"Found available team: {team_id}")
31+
return team_id
32+
except Exception as e:
33+
print(f"Error checking team {team_id}: {str(e)}")
34+
continue
35+
36+
print("No teams found in priority order")
37+
return None
38+
39+
async def create_RAI_agent(team: TeamConfiguration, memory_store: DatabaseBase) -> FoundryAgentTemplate:
1340
"""Create and initialize a FoundryAgentTemplate for Responsible AI (RAI) checks."""
1441
agent_name = "RAIAgent"
1542
agent_description = "A comprehensive research assistant for integration testing"
@@ -26,7 +53,11 @@ async def create_RAI_agent() -> FoundryAgentTemplate:
2653
"- Is completely meaningless, incoherent, or appears to be spam\n"
2754
"Respond with 'TRUE' if the input violates any rules and should be blocked, otherwise respond with 'FALSE'."
2855
)
56+
2957
model_deployment_name = config.AZURE_OPENAI_DEPLOYMENT_NAME
58+
team.team_id = "rai_team" # Use a fixed team ID for RAI agent
59+
team.name = "RAI Team"
60+
team.description = "Team responsible for Responsible AI checks"
3061
agent = FoundryAgentTemplate(
3162
agent_name=agent_name,
3263
agent_description=agent_description,
@@ -36,6 +67,8 @@ async def create_RAI_agent() -> FoundryAgentTemplate:
3667
project_endpoint=config.AZURE_AI_PROJECT_ENDPOINT,
3768
mcp_config=None,
3869
search_config=None,
70+
team_config=team,
71+
memory_store=memory_store,
3972
)
4073

4174
await agent.open()
@@ -78,14 +111,14 @@ async def _get_agent_response(agent: FoundryAgentTemplate, query: str) -> str:
78111
return "TRUE" # Default to blocking on error
79112

80113

81-
async def rai_success(description: str) -> bool:
114+
async def rai_success(description: str, team_config: TeamConfiguration, memory_store: DatabaseBase) -> bool:
82115
"""
83116
Run a RAI compliance check on the provided description using the RAIAgent.
84117
Returns True if content is safe (should proceed), False if it should be blocked.
85118
"""
86119
agent: FoundryAgentTemplate | None = None
87120
try:
88-
agent = await create_RAI_agent()
121+
agent = await create_RAI_agent(team_config, memory_store)
89122
if not agent:
90123
logging.error("Failed to instantiate RAIAgent.")
91124
return False
@@ -112,7 +145,7 @@ async def rai_success(description: str) -> bool:
112145
pass
113146

114147

115-
async def rai_validate_team_config(team_config_json: dict) -> tuple[bool, str]:
148+
async def rai_validate_team_config(team_config_json: dict, team_config: TeamConfiguration, memory_store: DatabaseBase) -> tuple[bool, str]:
116149
"""
117150
Validate a team configuration for RAI compliance.
118151
@@ -154,7 +187,7 @@ async def rai_validate_team_config(team_config_json: dict) -> tuple[bool, str]:
154187
if not combined:
155188
return False, "Team configuration contains no readable text content."
156189

157-
if not await rai_success(combined):
190+
if not await rai_success(combined, team_config, memory_store):
158191
return (
159192
False,
160193
"Team configuration contains inappropriate content and cannot be uploaded.",

0 commit comments

Comments
 (0)