Skip to content

Commit 8abe10a

Browse files
committed
returns console results
1 parent 40eeb31 commit 8abe10a

File tree

8 files changed

+136
-228
lines changed

8 files changed

+136
-228
lines changed

src/backend/app_kernel.py

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010

1111
from auth.auth_utils import get_authenticated_user_details
1212
from azure.monitor.opentelemetry import configure_azure_monitor
13-
# Semantic Kernel imports
1413
from common.config.app_config import config
1514
from common.database.database_factory import DatabaseFactory
1615
from common.models.messages_kernel import (AgentMessage, AgentType,
@@ -28,6 +27,8 @@
2827
# Local imports
2928
from middleware.health_check import HealthCheckMiddleware
3029
from v3.api.router import app_v3
30+
# Semantic Kernel imports
31+
from v3.config.settings import orchestration_config
3132
from v3.magentic_agents.magentic_agent_factory import (cleanup_all_agents,
3233
get_agents)
3334

@@ -60,17 +61,15 @@
6061
)
6162

6263

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

7271
# Initialize the FastAPI app
73-
app = FastAPI(lifespan=lifespan)
72+
app = FastAPI()
7473

7574
frontend_url = config.FRONTEND_SITE_NAME
7675

@@ -598,7 +597,7 @@ async def approve_step_endpoint(
598597

599598
return {"status": "All steps approved"}
600599

601-
600+
# Get plans is called in the initial side rendering of the frontend
602601
@app.get("/api/plans")
603602
async def get_plans(
604603
request: Request,
@@ -662,13 +661,17 @@ async def get_plans(
662661
404:
663662
description: Plan not found
664663
"""
664+
665665
authenticated_user = get_authenticated_user_details(request_headers=request.headers)
666666
user_id = authenticated_user["user_principal_id"]
667667
if not user_id:
668668
track_event_if_configured(
669669
"UserIdNotFound", {"status_code": 400, "detail": "no user"}
670670
)
671671
raise HTTPException(status_code=400, detail="no user")
672+
673+
# Initialize agent team for this user session
674+
await orchestration_config.get_current_orchestration(user_id=user_id)
672675

673676
# Initialize memory context
674677
memory_store = await DatabaseFactory.get_database(user_id=user_id)

src/backend/common/config/app_config.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ def __init__(self):
7777
self._cosmos_database = None
7878
self._ai_project_client = None
7979

80-
self._agents = []
80+
self._agents = {}
8181

8282
def get_azure_credential(self, client_id=None):
8383
"""
@@ -206,6 +206,15 @@ def set_user_local_browser_language(self, language: str):
206206
"""
207207
os.environ["USER_LOCAL_BROWSER_LANGUAGE"] = language
208208

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

210219
# Create a global instance of AppConfig
211220
config = AppConfig()

src/backend/v3/api/router.py

Lines changed: 33 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,31 @@
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
7-
from common.models.messages_kernel import (
8-
GeneratePlanRequest,
9-
InputTask,
10-
Plan,
11-
PlanStatus,
12-
)
6+
from common.config.app_config import config
7+
from common.database.database_factory import DatabaseFactory
8+
from common.models.messages_kernel import (GeneratePlanRequest, InputTask,
9+
Plan, PlanStatus)
1310
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,
17-
)
18-
from v3.common.services.team_service import TeamService
11+
from common.utils.utils_kernel import rai_success, rai_validate_team_config
12+
from fastapi import (APIRouter, Depends, File, HTTPException, Request,
13+
UploadFile)
1914
from kernel_agents.agent_factory import AgentFactory
20-
from common.database.database_factory import DatabaseFactory
15+
from semantic_kernel.agents.runtime import InProcessRuntime
16+
from v3.common.services.team_service import TeamService
17+
from v3.config.settings import orchestration_config
18+
from v3.models.models import MPlan, MStep
2119
from v3.models.orchestration_models import AgentType
22-
from common.config.app_config import config
2320

2421
app_v3 = APIRouter(
2522
prefix="/api/v3",
2623
responses={404: {"description": "Not found"}},
2724
)
2825

29-
26+
# To do: change endpoint to process request
3027
@app_v3.post("/create_plan")
31-
async def create_plan_endpoint(input_task: InputTask, request: Request):
28+
async def process_request(input_task: InputTask, request: Request):
3229
"""
3330
Create a new plan without full processing.
3431
@@ -128,14 +125,24 @@ async def create_plan_endpoint(input_task: InputTask, request: Request):
128125
memory_store = await DatabaseFactory.get_database(user_id=user_id)
129126

130127
# 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,
138-
)
128+
plan = MPlan()
129+
# session_id=input_task.session_id,
130+
# team_id=input_task.team_id,
131+
# user_id=user_id,
132+
# initial_goal=input_task.description,
133+
# overall_status=PlanStatus.in_progress,
134+
# source=AgentType.PLANNER.value,
135+
# )
136+
137+
# setup and call the magentic orchestration
138+
magentic_orchestration = await orchestration_config.get_current_orchestration(user_id)
139+
140+
runtime = InProcessRuntime()
141+
runtime.start()
142+
143+
# invoke returns immediately, wait on result.get
144+
orchestration_result = await magentic_orchestration.invoke(task=input_task.description,runtime=runtime)
145+
team_result = await orchestration_result.get()
139146

140147
# Save the plan to the database
141148
await memory_store.add_plan(plan)

src/backend/v3/callbacks/response_handlers.py

Lines changed: 33 additions & 124 deletions
Original file line numberDiff line numberDiff line change
@@ -4,137 +4,46 @@
44
"""
55

66
import sys
7-
from semantic_kernel.contents import ChatMessageContent, StreamingChatMessageContent
87

9-
def enhanced_agent_response_callback(message: ChatMessageContent) -> None:
10-
"""Enhanced callback to monitor agent responses with detailed information."""
11-
12-
# Get basic message information
13-
message_type = type(message).__name__
14-
role = getattr(message, 'role', 'unknown')
15-
metadata = getattr(message, 'metadata', {})
8+
from semantic_kernel.contents import (ChatMessageContent,
9+
StreamingChatMessageContent)
10+
11+
coderagent = False
12+
13+
def agent_response_callback(message: ChatMessageContent) -> None:
14+
"""Observer function to print detailed information about streaming messages."""
15+
global coderagent
16+
# import sys
17+
18+
# Get agent name to determine handling
1619
agent_name = message.name or "Unknown Agent"
17-
18-
# Handle different agent types with specific formatting
19-
if "Coder" in agent_name:
20-
_handle_coder_response(message, agent_name, message_type)
21-
elif "Reasoning" in agent_name:
22-
_handle_reasoning_response(message, agent_name, message_type, role)
23-
elif "Research" in agent_name or "Enhanced" in agent_name:
24-
_handle_research_response(message, agent_name, message_type, role, metadata)
25-
else:
26-
_handle_default_response(message, agent_name, message_type, role)
2720

28-
def _handle_coder_response(message, agent_name, message_type):
29-
"""Handle coder agent responses with code execution details."""
30-
if hasattr(message, 'items') and message.items and len(message.items) > 0:
31-
for item in message.items:
32-
if hasattr(item, 'text') and item.text:
33-
print(item.text, end='', flush=True)
34-
35-
content = message.content or ""
36-
if content.strip():
37-
print(content, end='', flush=True)
21+
# Debug information about the message
22+
message_type = type(message).__name__
23+
metadata = getattr(message, 'metadata', {})
24+
# when streaming code - list the coder info first once -
25+
if 'code' in metadata and metadata['code'] is True:
26+
if coderagent == False:
27+
print(f"\n🧠 **{agent_name}** [{message_type}]")
28+
print("-" * (len(agent_name) + len(message_type) + 10))
29+
coderagent = True
30+
print(message.content, end='', flush=False)
31+
return
32+
elif coderagent == True:
33+
coderagent = False
34+
role = getattr(message, 'role', 'unknown')
3835

39-
def _handle_reasoning_response(message, agent_name, message_type, role):
40-
"""Handle reasoning agent responses with logical process details."""
4136
print(f"\n🧠 **{agent_name}** [{message_type}] (role: {role})")
42-
print("-" * (len(agent_name) + len(message_type) + 15))
43-
44-
if hasattr(message, 'items') and message.items:
45-
for item in message.items:
46-
if hasattr(item, 'function_name') and item.function_name:
47-
print(f"🔧 Function Call: {item.function_name}")
48-
if hasattr(item, 'text') and item.text:
49-
print(item.text, end='', flush=True)
50-
51-
content = message.content or ""
52-
if content.strip():
53-
print(f"💭 Reasoning: {content}")
54-
55-
sys.stdout.flush()
56-
print()
57-
58-
def _handle_research_response(message, agent_name, message_type, role, metadata):
59-
"""Handle research agent responses with search result details."""
60-
print(f"\n🔍 **{agent_name}** [{message_type}] (role: {role})")
61-
print("-" * (len(agent_name) + len(message_type) + 15))
62-
37+
print("-" * (len(agent_name) + len(message_type) + 10))
38+
if message.items[-1].content_type == 'function_call':
39+
print(f"🔧 Function call: {message.items[-1].function_name}, Arguments: {message.items[-1].arguments}")
6340
if metadata:
6441
print(f"📋 Metadata: {metadata}")
6542

66-
# Show detailed search results if available
67-
if hasattr(message, 'items') and message.items and len(message.items) > 0:
68-
print(f"🔧 Found {len(message.items)} items in message")
69-
70-
for i, item in enumerate(message.items):
71-
print(f" Item {i+1}:")
72-
73-
if hasattr(item, 'function_name'):
74-
print(f" Function Name: {item.function_name}")
75-
76-
if hasattr(item, 'arguments'):
77-
print(f" Arguments: {item.arguments}")
78-
79-
if hasattr(item, 'text') and item.text:
80-
print(f" Text: {item.text[:200]}...")
81-
82-
# Extract Bing search results
83-
if hasattr(item, 'response_metadata'):
84-
_parse_search_results(item.response_metadata)
85-
86-
content = message.content or ""
87-
if content.strip():
88-
print(f"💬 Content: {content}")
89-
else:
90-
print("💬 Content: [Empty]")
91-
92-
sys.stdout.flush()
93-
print()
94-
95-
def _parse_search_results(response_meta):
96-
"""Parse and display Bing search results from metadata."""
97-
print(f" Response Metadata: {str(response_meta)[:300]}...")
98-
99-
if isinstance(response_meta, str):
100-
try:
101-
import json
102-
parsed_meta = json.loads(response_meta)
103-
if 'webPages' in parsed_meta:
104-
web_pages = parsed_meta.get('webPages', {})
105-
total_docs = web_pages.get('totalEstimatedMatches', 0)
106-
available_docs = len(web_pages.get('value', []))
107-
print(f" 📊 BING SEARCH RESULTS: {available_docs} docs returned, ~{total_docs} total matches")
108-
109-
# Show first few results
110-
for j, page in enumerate(web_pages.get('value', [])[:3]):
111-
title = page.get('name', 'No title')[:50]
112-
url = page.get('url', 'No URL')[:80]
113-
print(f" Result {j+1}: {title} - {url}")
114-
except Exception as parse_error:
115-
print(f" ⚠️ Could not parse search results: {parse_error}")
116-
117-
def _handle_default_response(message, agent_name, message_type, role):
118-
"""Handle default agent responses."""
119-
print(f"\n🤖 **{agent_name}** [{message_type}] (role: {role})")
120-
print("-" * (len(agent_name) + len(message_type) + 15))
121-
122-
content = message.content or ""
123-
if content.strip():
124-
print(f"💬 Content: {content}")
125-
else:
126-
print("💬 Content: [Empty]")
127-
128-
sys.stdout.flush()
129-
print()
130-
43+
# Add this function after your agent_response_callback function
13144
async def streaming_agent_response_callback(streaming_message: StreamingChatMessageContent, is_final: bool) -> None:
13245
"""Simple streaming callback to show real-time agent responses."""
133-
134-
# Print streaming content as it arrives
135-
if hasattr(streaming_message, 'content') and streaming_message.content:
136-
print(streaming_message.content, end='', flush=True)
137-
138-
# Add a newline when the streaming is complete for this message
139-
if is_final:
140-
print()
46+
if streaming_message.name != "CoderAgent":
47+
# Print streaming content as it arrives
48+
if hasattr(streaming_message, 'content') and streaming_message.content:
49+
print(streaming_message.content, end='', flush=False)

src/backend/v3/config/settings.py

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,14 @@
44
"""
55

66
import os
7-
from semantic_kernel.connectors.ai.open_ai import (
8-
AzureChatCompletion,
9-
OpenAIChatPromptExecutionSettings,
10-
)
7+
118
from common.config.app_config import config
9+
from semantic_kernel.agents.orchestration.magentic import MagenticOrchestration
10+
from semantic_kernel.connectors.ai.open_ai import (
11+
AzureChatCompletion, OpenAIChatPromptExecutionSettings)
12+
from v3.magentic_agents.magentic_agent_factory import (cleanup_all_agents,
13+
get_agents)
14+
from v3.orchestration.manager import init_orchestration
1215

1316

1417
class AzureConfig:
@@ -55,8 +58,22 @@ def get_headers(self, token):
5558
if token
5659
else {}
5760
)
61+
62+
class OrchestrationConfig:
63+
"""Configuration for orchestration settings."""
64+
65+
def __init__(self):
66+
self._orchestrations = {}
67+
68+
async def get_current_orchestration(self, user_id: str) -> MagenticOrchestration:
69+
"""Initialize or get existing orchestration instance."""
70+
if user_id not in self._orchestrations:
71+
agents = await get_agents()
72+
self._orchestrations[user_id] = await init_orchestration(agents)
73+
return self._orchestrations[user_id]
5874

5975

6076
# Global config instances
6177
azure_config = AzureConfig()
6278
mcp_config = MCPConfig()
79+
orchestration_config = OrchestrationConfig()

0 commit comments

Comments
 (0)