Skip to content

Commit c4d6173

Browse files
Merge branch 'macae-v3-dev' into planpage-uistreaming
2 parents c09dbb3 + d750021 commit c4d6173

File tree

26 files changed

+1731
-1118
lines changed

26 files changed

+1731
-1118
lines changed

data/agent_teams/default_team.json

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,11 @@
1414
"system_message": "You are an AI Agent. You have knowledge about HR (e.g., human resources), policies, procedures, and onboarding guidelines.",
1515
"description": "Handles employee onboarding, HR policies, and human resources management tasks.",
1616
"icon": "Person",
17-
"index_name": ""
17+
"index_name": "",
18+
"use_rag": false,
19+
"use_mcp": false,
20+
"coding_tools": false,
21+
"use_bing": false
1822
},
1923
{
2024
"input_key": "tech-support-001",
@@ -24,7 +28,11 @@
2428
"system_message": "You are a Product agent. You have knowledge about product management, development, and compliance guidelines. When asked to call a function, you should summarize back what was done.",
2529
"description": "Provides technical support for mobile plans, telecommunications, and IT services.",
2630
"icon": "Phone",
27-
"index_name": ""
31+
"index_name": "",
32+
"use_rag": false,
33+
"use_mcp": false,
34+
"coding_tools": false,
35+
"use_bing": false
2836
},
2937
{
3038
"input_key": "procurement-001",
@@ -34,7 +42,11 @@
3442
"system_message": "You are a Procurement agent. You specialize in purchasing, vendor management, supply chain operations, and inventory control. You help with creating purchase orders, managing vendors, tracking orders, and ensuring efficient procurement processes.",
3543
"description": "Manages purchasing decisions, add-ons, and procurement processes.",
3644
"icon": "ShoppingBag",
37-
"index_name": ""
45+
"index_name": "",
46+
"use_rag": false,
47+
"use_mcp": false,
48+
"coding_tools": false,
49+
"use_bing": false
3850
},
3951
{
4052
"input_key": "marketing-001",
@@ -44,7 +56,11 @@
4456
"system_message": "You are a Marketing agent. You specialize in marketing strategy, campaign development, content creation, and market analysis. You help create effective marketing campaigns, analyze market data, and develop promotional content for products and services.",
4557
"description": "Creates marketing content, press releases, and promotional materials.",
4658
"icon": "DocumentEdit",
47-
"index_name": ""
59+
"index_name": "",
60+
"use_rag": false,
61+
"use_mcp": false,
62+
"coding_tools": false,
63+
"use_bing": false
4864
},
4965
{
5066
"input_key": "generic-001",
@@ -54,7 +70,11 @@
5470
"system_message": "You are a Generic agent that can help with general questions and provide basic information. You can search for information and perform simple calculations.",
5571
"description": "Provides general assistance and handles miscellaneous tasks that don't require specialized expertise.",
5672
"icon": "Bot",
57-
"index_name": ""
73+
"index_name": "",
74+
"use_rag": false,
75+
"use_mcp": false,
76+
"coding_tools": false,
77+
"use_bing": false
5878
}
5979
],
6080
"description": "Business Operations team handles employee onboarding, telecommunications support, procurement, and marketing tasks for comprehensive business operations management.",

src/backend/app_kernel.py

Lines changed: 27 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,45 +2,36 @@
22
import asyncio
33
import logging
44
import os
5-
65
# Azure monitoring
76
import re
87
import uuid
8+
from contextlib import asynccontextmanager
99
from typing import Dict, List, Optional
1010

11-
# Semantic Kernel imports
12-
from common.config.app_config import config
1311
from auth.auth_utils import get_authenticated_user_details
1412
from azure.monitor.opentelemetry import configure_azure_monitor
13+
from common.config.app_config import config
14+
from common.database.database_factory import DatabaseFactory
15+
from common.models.messages_kernel import (AgentMessage, AgentType,
16+
HumanClarification, HumanFeedback,
17+
InputTask, Plan, PlanStatus,
18+
PlanWithSteps, Step, UserLanguage)
1519
from common.utils.event_utils import track_event_if_configured
16-
20+
from common.utils.utils_date import format_dates_in_messages
21+
# Updated import for KernelArguments
22+
from common.utils.utils_kernel import rai_success
1723
# FastAPI imports
1824
from fastapi import FastAPI, HTTPException, Query, Request, WebSocket
1925
from fastapi.middleware.cors import CORSMiddleware
2026
from kernel_agents.agent_factory import AgentFactory
21-
2227
# Local imports
2328
from middleware.health_check import HealthCheckMiddleware
24-
from common.models.messages_kernel import (
25-
AgentMessage,
26-
AgentType,
27-
HumanClarification,
28-
HumanFeedback,
29-
InputTask,
30-
Plan,
31-
PlanStatus,
32-
PlanWithSteps,
33-
Step,
34-
UserLanguage,
35-
)
36-
37-
# Updated import for KernelArguments
38-
from common.utils.utils_kernel import rai_success
39-
40-
from common.database.database_factory import DatabaseFactory
41-
from common.utils.utils_date import format_dates_in_messages
4229
from v3.api.router import app_v3
4330
from common.utils.websocket_streaming import websocket_streaming_endpoint, ws_manager
31+
# Semantic Kernel imports
32+
from v3.config.settings import orchestration_config
33+
from v3.magentic_agents.magentic_agent_factory import (cleanup_all_agents,
34+
get_agents)
4435

4536
# Check if the Application Insights Instrumentation Key is set in the environment variables
4637
connection_string = config.APPLICATIONINSIGHTS_CONNECTION_STRING
@@ -70,6 +61,14 @@
7061
logging.WARNING
7162
)
7263

64+
65+
# @asynccontextmanager
66+
# async def lifespan(app: FastAPI):
67+
# """Lifespan event handler to create and clean up agents."""
68+
# config.agents = await get_agents()
69+
# yield
70+
# await cleanup_all_agents()
71+
7372
# Initialize the FastAPI app
7473
app = FastAPI()
7574

@@ -605,7 +604,7 @@ async def approve_step_endpoint(
605604

606605
return {"status": "All steps approved"}
607606

608-
607+
# Get plans is called in the initial side rendering of the frontend
609608
@app.get("/api/plans")
610609
async def get_plans(
611610
request: Request,
@@ -670,13 +669,17 @@ async def get_plans(
670669
404:
671670
description: Plan not found
672671
"""
672+
673673
authenticated_user = get_authenticated_user_details(request_headers=request.headers)
674674
user_id = authenticated_user["user_principal_id"]
675675
if not user_id:
676676
track_event_if_configured(
677677
"UserIdNotFound", {"status_code": 400, "detail": "no user"}
678678
)
679679
raise HTTPException(status_code=400, detail="no user")
680+
681+
# Initialize agent team for this user session
682+
await orchestration_config.get_current_orchestration(user_id=user_id)
680683

681684
# Initialize memory context
682685
memory_store = await DatabaseFactory.get_database(user_id=user_id)

src/backend/common/config/app_config.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from typing import Optional
55

66
from azure.ai.projects.aio import AIProjectClient
7-
from azure.identity import ManagedIdentityCredential, DefaultAzureCredential
7+
from azure.identity import DefaultAzureCredential, ManagedIdentityCredential
88
from dotenv import load_dotenv
99

1010
# Load environment variables from .env file
@@ -81,6 +81,8 @@ def __init__(self):
8181
self._cosmos_database = None
8282
self._ai_project_client = None
8383

84+
self._agents = {}
85+
8486
def get_azure_credential(self, client_id=None):
8587
"""
8688
Returns an Azure credential based on the application environment.
@@ -208,6 +210,15 @@ def set_user_local_browser_language(self, language: str):
208210
"""
209211
os.environ["USER_LOCAL_BROWSER_LANGUAGE"] = language
210212

213+
# Get agent team list by user_id dictionary index
214+
def get_agents(self) -> dict[str, list]:
215+
"""Get the list of agents configured in the application.
216+
217+
Returns:
218+
A list of agent names or configurations
219+
"""
220+
return self._agents
221+
211222

212223
# Create a global instance of AppConfig
213224
config = AppConfig()

src/backend/common/models/messages_kernel.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,9 @@ class TeamAgent(KernelBaseModel):
160160
description: str = ""
161161
icon: str
162162
index_name: str = ""
163+
use_rag: bool = False
164+
use_mcp: bool = False
165+
coding_tools: bool = False
163166

164167

165168
class StartingTask(KernelBaseModel):

src/backend/v3/api/router.py

Lines changed: 66 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,44 @@
1-
import uuid
2-
import logging
31
import json
4-
from fastapi import APIRouter, Depends, HTTPException, UploadFile, File, Request
2+
import logging
3+
import uuid
54

65
from auth.auth_utils import get_authenticated_user_details
6+
from common.config.app_config import config
7+
from common.database.database_factory import DatabaseFactory
78
from common.models.messages_kernel import (
89
GeneratePlanRequest,
910
InputTask,
1011
Plan,
1112
PlanStatus,
1213
)
1314
from common.utils.event_utils import track_event_if_configured
14-
from common.utils.utils_kernel import (
15-
rai_success,
16-
rai_validate_team_config,
15+
from common.utils.utils_kernel import rai_success, rai_validate_team_config
16+
from fastapi import (
17+
APIRouter,
18+
Depends,
19+
File,
20+
HTTPException,
21+
Request,
22+
UploadFile,
23+
WebSocket,
24+
WebSocketDisconnect,
1725
)
18-
from v3.common.services.team_service import TeamService
1926
from kernel_agents.agent_factory import AgentFactory
20-
from common.database.database_factory import DatabaseFactory
27+
from semantic_kernel.agents.runtime import InProcessRuntime
28+
from v3.common.services.team_service import TeamService
29+
from v3.config.settings import orchestration_config
30+
from v3.models.models import MPlan, MStep
2131
from v3.models.orchestration_models import AgentType
22-
from common.config.app_config import config
2332

2433
app_v3 = APIRouter(
2534
prefix="/api/v3",
2635
responses={404: {"description": "Not found"}},
2736
)
2837

2938

39+
# To do: change endpoint to process request
3040
@app_v3.post("/create_plan")
31-
async def create_plan_endpoint(input_task: InputTask, request: Request):
41+
async def process_request(input_task: InputTask, request: Request):
3242
"""
3343
Create a new plan without full processing.
3444
@@ -128,15 +138,29 @@ async def create_plan_endpoint(input_task: InputTask, request: Request):
128138
memory_store = await DatabaseFactory.get_database(user_id=user_id)
129139

130140
# Create a new Plan object
131-
plan = Plan(
132-
session_id=input_task.session_id,
133-
team_id=input_task.team_id,
134-
user_id=user_id,
135-
initial_goal=input_task.description,
136-
overall_status=PlanStatus.in_progress,
137-
source=AgentType.PLANNER.value,
141+
plan = MPlan()
142+
# session_id=input_task.session_id,
143+
# team_id=input_task.team_id,
144+
# user_id=user_id,
145+
# initial_goal=input_task.description,
146+
# overall_status=PlanStatus.in_progress,
147+
# source=AgentType.PLANNER.value,
148+
# )
149+
150+
# setup and call the magentic orchestration
151+
magentic_orchestration = await orchestration_config.get_current_orchestration(
152+
user_id
138153
)
139154

155+
runtime = InProcessRuntime()
156+
runtime.start()
157+
158+
# invoke returns immediately, wait on result.get
159+
orchestration_result = await magentic_orchestration.invoke(
160+
task=input_task.description, runtime=runtime
161+
)
162+
team_result = await orchestration_result.get()
163+
140164
# Save the plan to the database
141165
await memory_store.add_plan(plan)
142166

@@ -630,3 +654,28 @@ async def get_search_indexes_endpoint(request: Request):
630654
except Exception as e:
631655
logging.error(f"Error retrieving search indexes: {str(e)}")
632656
raise HTTPException(status_code=500, detail="Internal server error occurred")
657+
658+
659+
# WebSocket endpoint for job status updates
660+
plans = {} # job_id -> current plan
661+
approvals = {} # job_id -> True/False/None
662+
sockets = {} # job_id -> WebSocket
663+
664+
665+
@app_v3.websocket("/ws/{job_id}")
666+
async def ws(job_id: str, websocket: WebSocket):
667+
await websocket.accept()
668+
sockets[job_id] = websocket
669+
try:
670+
if job_id in plans:
671+
await websocket.send_json({"type": "plan_ready", "plan": plans[job_id]})
672+
while True:
673+
msg = await websocket.receive_json()
674+
if msg.get("type") == "approve":
675+
approvals[job_id] = True
676+
await websocket.send_json({"type": "ack", "message": "approved"})
677+
elif msg.get("type") == "reject":
678+
approvals[job_id] = False
679+
await websocket.send_json({"type": "ack", "message": "rejected"})
680+
except WebSocketDisconnect:
681+
sockets.pop(job_id, None)

0 commit comments

Comments
 (0)