Skip to content

Commit 0a3069d

Browse files
authored
Merge pull request #645 from microsoft/temp-and-id-macae-rfp-agent-framework
fixes for the id stuff - added temp difference btwn reasoning and non…
2 parents e9cbbaa + ca93460 commit 0a3069d

File tree

13 files changed

+152
-74
lines changed

13 files changed

+152
-74
lines changed

data/agent_teams/hr.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"status": "visible",
66
"created": "",
77
"created_by": "",
8+
"deployment_name": "gpt-4.1-mini",
89
"agents": [
910
{
1011
"input_key": "",

data/agent_teams/marketing.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"status": "visible",
66
"created": "",
77
"created_by": "",
8+
"deployment_name": "gpt-4.1-mini",
89
"agents": [
910
{
1011
"input_key": "",

data/agent_teams/retail.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"status": "visible",
66
"created": "",
77
"created_by": "",
8+
"deployment_name": "gpt-4.1-mini",
89
"agents": [
910
{
1011
"input_key": "",

data/agent_teams/rfp_analysis_team.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"status": "visible",
66
"created": "",
77
"created_by": "",
8+
"deployment_name": "gpt-4.1-mini",
89
"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.",
910
"logo": "",
1011
"plan": "",

src/backend/common/models/messages_af.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,7 @@ class TeamConfiguration(BaseDataModel):
196196
status: str
197197
created: str
198198
created_by: str
199+
deployment_name: str
199200
agents: List[TeamAgent] = Field(default_factory=list)
200201
description: str = ""
201202
logo: str = ""

src/backend/common/utils/utils_af.py

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,20 @@
1-
"""Utility functions for agent_framework-based integration and agent management (converted from agent framework )."""
1+
"""Utility functions for agent_framework-based integration and agent management."""
22

33
import logging
4+
import uuid
5+
from common.config.app_config import config
46

5-
# Converted import path (agent_framework version of FoundryAgentTemplate)
67
from common.database.database_base import DatabaseBase
78
from common.models.messages_af import TeamConfiguration
89
from v4.common.services.team_service import TeamService
9-
from v4.magentic_agents.foundry_agent import FoundryAgentTemplate # formerly v4.magentic_agents.foundry_agent
1010
from v4.config.agent_registry import agent_registry
11-
from common.config.app_config import config
11+
from v4.magentic_agents.foundry_agent import (
12+
FoundryAgentTemplate,
13+
) # formerly v4.magentic_agents.foundry_agent
14+
1215
logging.basicConfig(level=logging.INFO)
1316

17+
1418
async def find_first_available_team(team_service: TeamService, user_id: str) -> str:
1519
"""
1620
Check teams in priority order (4 to 1) and return the first available team ID.
@@ -36,7 +40,10 @@ async def find_first_available_team(team_service: TeamService, user_id: str) ->
3640
print("No teams found in priority order")
3741
return None
3842

39-
async def create_RAI_agent(team: TeamConfiguration, memory_store: DatabaseBase) -> FoundryAgentTemplate:
43+
44+
async def create_RAI_agent(
45+
team: TeamConfiguration, memory_store: DatabaseBase
46+
) -> FoundryAgentTemplate:
4047
"""Create and initialize a FoundryAgentTemplate for Responsible AI (RAI) checks."""
4148
agent_name = "RAIAgent"
4249
agent_description = "A comprehensive research assistant for integration testing"
@@ -53,7 +60,7 @@ async def create_RAI_agent(team: TeamConfiguration, memory_store: DatabaseBase)
5360
"- Is completely meaningless, incoherent, or appears to be spam\n"
5461
"Respond with 'TRUE' if the input violates any rules and should be blocked, otherwise respond with 'FALSE'."
5562
)
56-
63+
5764
model_deployment_name = config.AZURE_OPENAI_DEPLOYMENT_NAME
5865
team.team_id = "rai_team" # Use a fixed team ID for RAI agent
5966
team.name = "RAI Team"
@@ -62,6 +69,7 @@ async def create_RAI_agent(team: TeamConfiguration, memory_store: DatabaseBase)
6269
agent_name=agent_name,
6370
agent_description=agent_description,
6471
agent_instructions=agent_instructions,
72+
use_reasoning=False,
6573
model_deployment_name=model_deployment_name,
6674
enable_code_interpreter=False,
6775
project_endpoint=config.AZURE_AI_PROJECT_ENDPOINT,
@@ -111,7 +119,9 @@ async def _get_agent_response(agent: FoundryAgentTemplate, query: str) -> str:
111119
return "TRUE" # Default to blocking on error
112120

113121

114-
async def rai_success(description: str, team_config: TeamConfiguration, memory_store: DatabaseBase) -> bool:
122+
async def rai_success(
123+
description: str, team_config: TeamConfiguration, memory_store: DatabaseBase
124+
) -> bool:
115125
"""
116126
Run a RAI compliance check on the provided description using the RAIAgent.
117127
Returns True if content is safe (should proceed), False if it should be blocked.
@@ -145,7 +155,9 @@ async def rai_success(description: str, team_config: TeamConfiguration, memory_
145155
pass
146156

147157

148-
async def rai_validate_team_config(team_config_json: dict, team_config: TeamConfiguration, memory_store: DatabaseBase) -> tuple[bool, str]:
158+
async def rai_validate_team_config(
159+
team_config_json: dict, memory_store: DatabaseBase
160+
) -> tuple[bool, str]:
149161
"""
150162
Validate a team configuration for RAI compliance.
151163
@@ -187,6 +199,22 @@ async def rai_validate_team_config(team_config_json: dict, team_config: TeamConf
187199
if not combined:
188200
return False, "Team configuration contains no readable text content."
189201

202+
team_config = TeamConfiguration(
203+
id=str(uuid.uuid4()),
204+
session_id=str(uuid.uuid4()),
205+
team_id=str(uuid.uuid4()),
206+
name="Uploaded Team",
207+
status="active",
208+
created=str(uuid.uuid4()),
209+
created_by=str(uuid.uuid4()),
210+
deployment_name="",
211+
agents=[],
212+
description="",
213+
logo="",
214+
plan="",
215+
starting_tasks=[],
216+
user_id=str(uuid.uuid4()),
217+
)
190218
if not await rai_success(combined, team_config, memory_store):
191219
return (
192220
False,
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
2+
import logging
3+
import secrets
4+
import string
5+
from typing import Optional
6+
7+
from common.database.database_base import DatabaseBase
8+
from common.models.messages_af import TeamConfiguration
9+
10+
11+
def generate_assistant_id(prefix: str = "asst_", length: int = 24) -> str:
12+
"""
13+
Generate a unique ID like 'asst_jRgR5t2U7o8nUPkNGv5HWOgV'.
14+
15+
- prefix: leading string (defaults to 'asst_')
16+
- length: number of random characters after the prefix
17+
"""
18+
# URL-safe characters similar to what OpenAI-style IDs use
19+
alphabet = string.ascii_letters + string.digits # a-zA-Z0-9
20+
21+
# cryptographically strong randomness
22+
random_part = "".join(secrets.choice(alphabet) for _ in range(length))
23+
return f"{prefix}{random_part}"
24+
25+
26+
async def get_database_team_agent_id(
27+
memory_store: DatabaseBase, team_config: TeamConfiguration, agent_name: str
28+
) -> Optional[str]:
29+
"""Retrieve existing team agent from database, if any."""
30+
agent_id = None
31+
try:
32+
currentAgent = await memory_store.get_team_agent(
33+
team_id=team_config.team_id, agent_name=agent_name
34+
)
35+
if currentAgent and currentAgent.agent_foundry_id:
36+
agent_id = currentAgent.agent_foundry_id
37+
38+
except (
39+
Exception
40+
) as ex: # Consider narrowing this to specific exceptions if possible
41+
logging.error("Failed to initialize Get database team agent: %s", ex)
42+
return agent_id

src/backend/v4/api/router.py

Lines changed: 9 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -438,13 +438,14 @@ async def plan_approval(
438438
return {"status": "approval recorded"}
439439
else:
440440
logging.warning(
441-
f"No orchestration or plan found for plan_id: {human_feedback.m_plan_id}"
441+
"No orchestration or plan found for plan_id: %s",
442+
human_feedback.m_plan_id
442443
)
443444
raise HTTPException(
444445
status_code=404, detail="No active plan found for approval"
445446
)
446447
except Exception as e:
447-
logging.error(f"Error processing plan approval: {e}")
448+
logging.error("Error processing plan approval: %s", e)
448449
raise HTTPException(status_code=500, detail="Internal server error")
449450

450451

@@ -710,16 +711,7 @@ async def upload_team_config(
710711
raise HTTPException(status_code=400, detail="no user found")
711712
try:
712713
memory_store = await DatabaseFactory.get_database(user_id=user_id)
713-
user_current_team = await memory_store.get_current_team(user_id=user_id)
714-
team_id = None
715-
if user_current_team:
716-
team_id = user_current_team.team_id
717-
team = await memory_store.get_team_by_id(team_id=team_id)
718-
if not team:
719-
raise HTTPException(
720-
status_code=404,
721-
detail=f"Team configuration '{team_id}' not found or access denied",
722-
)
714+
723715
except Exception as e:
724716
raise HTTPException(
725717
status_code=400,
@@ -740,11 +732,11 @@ async def upload_team_config(
740732
except json.JSONDecodeError as e:
741733
raise HTTPException(
742734
status_code=400, detail=f"Invalid JSON format: {str(e)}"
743-
)
735+
) from e
744736

745737
# Validate content with RAI before processing
746738
if not team_id:
747-
rai_valid, rai_error = await rai_validate_team_config(json_data, team, memory_store)
739+
rai_valid, rai_error = await rai_validate_team_config(json_data, memory_store)
748740
if not rai_valid:
749741
track_event_if_configured(
750742
"Team configuration RAI validation failed",
@@ -819,7 +811,7 @@ async def upload_team_config(
819811
json_data, user_id
820812
)
821813
except ValueError as e:
822-
raise HTTPException(status_code=400, detail=str(e))
814+
raise HTTPException(status_code=400, detail=str(e)) from e
823815

824816
# Save the configuration
825817
try:
@@ -831,7 +823,7 @@ async def upload_team_config(
831823
except ValueError as e:
832824
raise HTTPException(
833825
status_code=500, detail=f"Failed to save configuration: {str(e)}"
834-
)
826+
) from e
835827

836828
track_event_if_configured(
837829
"Team configuration uploaded",
@@ -855,7 +847,7 @@ async def upload_team_config(
855847
except HTTPException:
856848
raise
857849
except Exception as e:
858-
logging.error(f"Unexpected error uploading team configuration: {str(e)}")
850+
logging.error("Unexpected error uploading team configuration: %s", str(e))
859851
raise HTTPException(status_code=500, detail="Internal server error occurred")
860852

861853

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ async def validate_and_parse_team_config(
103103
team_id=unique_team_id, # Use generated GUID
104104
name=json_data["name"],
105105
status=json_data["status"],
106+
deployment_name=json_data.get("deployment_name", ""),
106107
created=current_timestamp, # Use generated timestamp
107108
created_by=user_id, # Use user_id who uploaded the config
108109
agents=agents,

src/backend/v4/magentic_agents/common/lifecycle.py

Lines changed: 16 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,27 @@
11
from __future__ import annotations
22

33
import logging
4-
import os
5-
from contextlib import AsyncExitStack
64
import secrets
75
import string
6+
from contextlib import AsyncExitStack
87
from typing import Any, Optional
98

109
from agent_framework import (
11-
AggregateContextProvider,
1210
ChatAgent,
13-
ChatClientProtocol,
14-
ChatMessage,
15-
ChatMessageStoreProtocol,
16-
ChatOptions,
17-
ContextProvider,
1811
HostedMCPTool,
1912
MCPStreamableHTTPTool,
20-
Middleware,
21-
Role,
22-
ToolMode,
23-
ToolProtocol,
2413
)
2514

2615
# from agent_framework.azure import AzureAIAgentClient
2716
from agent_framework_azure_ai import AzureAIAgentClient
2817
from azure.ai.agents.aio import AgentsClient
2918
from azure.identity.aio import DefaultAzureCredential
30-
from common.models.messages_af import CurrentTeamAgent, TeamConfiguration
3119
from common.database.database_base import DatabaseBase
20+
from common.models.messages_af import CurrentTeamAgent, TeamConfiguration
21+
from common.utils.utils_agents import (
22+
generate_assistant_id,
23+
get_database_team_agent_id,
24+
)
3225
from v4.common.services.team_service import TeamService
3326
from v4.config.agent_registry import agent_registry
3427
from v4.magentic_agents.models.agent_models import MCPConfig
@@ -157,19 +150,6 @@ def get_chat_client(self, chat_client) -> AzureAIAgentClient:
157150
extra={"agent_id": chat_client.agent_id},
158151
)
159152
return chat_client
160-
def generate_assistant_id(self, prefix: str = "asst_", length: int = 24) -> str:
161-
"""
162-
Generate a unique ID like 'asst_jRgR5t2U7o8nUPkNGv5HWOgV'.
163-
164-
- prefix: leading string (defaults to 'asst_')
165-
- length: number of random characters after the prefix
166-
"""
167-
# URL-safe characters similar to what OpenAI-style IDs use
168-
alphabet = string.ascii_letters + string.digits # a-zA-Z0-9
169-
170-
# cryptographically strong randomness
171-
random_part = "".join(secrets.choice(alphabet) for _ in range(length))
172-
return f"{prefix}{random_part}"
173153

174154
def get_agent_id(self, chat_client) -> str:
175155
"""Return the underlying agent ID."""
@@ -181,25 +161,24 @@ def get_agent_id(self, chat_client) -> str:
181161
and self._agent.chat_client.agent_id is not None
182162
):
183163
return self._agent.chat_client.agent_id # type: ignore
184-
id = self.generate_assistant_id()
164+
id = generate_assistant_id()
185165
self.logger.info("Generated new agent ID: %s", id)
186166
return id
187167

188168
async def get_database_team_agent(self) -> Optional[AzureAIAgentClient]:
189169
"""Retrieve existing team agent from database, if any."""
190170
chat_client = None
191171
try:
192-
currentAgent = await self.memory_store.get_team_agent(
193-
team_id=self.team_config.team_id, agent_name=self.agent_name
172+
agent_id = await get_database_team_agent_id(
173+
self.memory_store, self.team_config, self.agent_name
194174
)
195-
if currentAgent and currentAgent.agent_foundry_id:
196-
agent = await self.client.get_agent(
197-
agent_id=currentAgent.agent_foundry_id
198-
)
199-
if agent and agent.agent_id is not None:
175+
176+
if agent_id:
177+
agent = await self.client.get_agent(agent_id=agent_id)
178+
if agent and agent.id is not None:
200179
chat_client = AzureAIAgentClient(
201180
project_endpoint=self.project_endpoint,
202-
agent_id=agent.agent_id,
181+
agent_id=agent.id,
203182
model_deployment_name=self.model_deployment_name,
204183
async_credential=self.creds,
205184
)
@@ -213,15 +192,15 @@ async def get_database_team_agent(self) -> Optional[AzureAIAgentClient]:
213192
async def save_database_team_agent(self) -> None:
214193
"""Save current team agent to database."""
215194
try:
216-
if self._agent.chat_client.agent_id is None:
195+
if self._agent.id is None:
217196
self.logger.error("Cannot save database team agent: agent_id is None")
218197
return
219198

220199
currentAgent = CurrentTeamAgent(
221200
team_id=self.team_config.team_id,
222201
team_name=self.team_config.name,
223202
agent_name=self.agent_name,
224-
agent_foundry_id=self._agent.chat_client.agent_id,
203+
agent_foundry_id=self._agent.id,
225204
agent_description=self.agent_description,
226205
agent_instructions=self.agent_instructions,
227206
)

0 commit comments

Comments
 (0)