Skip to content

Commit 23580e4

Browse files
committed
working version with bugs
1 parent 8a353e0 commit 23580e4

File tree

5 files changed

+125
-191
lines changed

5 files changed

+125
-191
lines changed

src/backend/config_kernel.py

Lines changed: 7 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,13 @@
33
import logging
44
import semantic_kernel as sk
55
from semantic_kernel.kernel import Kernel
6-
from semantic_kernel.contents import ChatHistory
6+
# Updated imports for compatibility
7+
try:
8+
# Try newer structure
9+
from semantic_kernel.contents import ChatHistory
10+
except ImportError:
11+
# Fall back to older structure for compatibility
12+
from semantic_kernel.connectors.ai.chat_completion_client import ChatHistory
713
from semantic_kernel.agents.azure_ai.azure_ai_agent import AzureAIAgent
814

915
# Import AppConfig from app_config
@@ -54,26 +60,3 @@ def CreateKernel():
5460
def GetAIProjectClient():
5561
"""Get an AIProjectClient using the AppConfig implementation."""
5662
return config.get_ai_project_client()
57-
58-
@staticmethod
59-
async def CreateAzureAIAgent(
60-
kernel: Kernel,
61-
agent_name: str,
62-
instructions: str,
63-
agent_type: str = "assistant",
64-
tools=None,
65-
tool_resources=None,
66-
response_format=None,
67-
temperature: float = 0.0
68-
):
69-
"""Creates a new Azure AI Agent using the AppConfig implementation."""
70-
return await config.create_azure_ai_agent(
71-
kernel=kernel,
72-
agent_name=agent_name,
73-
instructions=instructions,
74-
agent_type=agent_type,
75-
tools=tools,
76-
tool_resources=tool_resources,
77-
response_format=response_format,
78-
temperature=temperature
79-
)

src/backend/kernel_agents/agent_factory.py

Lines changed: 4 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
from kernel_agents.product_agent import ProductAgent
2424
from kernel_agents.planner_agent import PlannerAgent # Add PlannerAgent import
2525
from kernel_agents.group_chat_manager import GroupChatManager
26-
26+
from semantic_kernel.prompt_template.prompt_template_config import PromptTemplateConfig
2727
from context.cosmos_memory_kernel import CosmosMemoryContext
2828

2929

@@ -108,6 +108,7 @@ async def create_agent(
108108
user_id: str,
109109
temperature: float = 0.0,
110110
system_message: Optional[str] = None,
111+
response_format: Optional[Any] = None,
111112
**kwargs
112113
) -> BaseAgent:
113114
"""Create an agent of the specified type.
@@ -174,7 +175,7 @@ async def create_agent(
174175
name=agent_type_str,
175176
instructions=system_message,
176177
temperature=temperature,
177-
response_format=None # Add response_format if required
178+
response_format=response_format # Add response_format if required
178179
)
179180
logger.info(f"Successfully created agent definition for {agent_type_str}")
180181
except Exception as agent_exc:
@@ -224,57 +225,6 @@ async def create_agent(
224225

225226
return agent
226227

227-
@classmethod
228-
async def create_azure_ai_agent(
229-
cls,
230-
agent_name: str,
231-
session_id: str,
232-
system_prompt: str,
233-
tools: List[KernelFunction] = None
234-
) -> AzureAIAgent:
235-
"""Create an Azure AI Agent.
236-
237-
Args:
238-
agent_name: The name of the agent
239-
session_id: The session ID
240-
system_prompt: The system prompt for the agent
241-
tools: Optional list of tools for the agent
242-
243-
Returns:
244-
An Azure AI Agent instance
245-
"""
246-
# Check if we already have an agent in the cache
247-
cache_key = f"{session_id}_{agent_name}"
248-
if session_id in cls._azure_ai_agent_cache and cache_key in cls._azure_ai_agent_cache[session_id]:
249-
# If tools are provided, make sure they are registered with the cached agent
250-
agent = cls._azure_ai_agent_cache[session_id][cache_key]
251-
if tools:
252-
for tool in tools:
253-
agent.add_function(tool)
254-
return agent
255-
256-
# Create a kernel using the AppConfig instance
257-
kernel = config.create_kernel()
258-
259-
# Await creation since create_azure_ai_agent is async
260-
agent = await config.create_azure_ai_agent(
261-
kernel=kernel,
262-
agent_name=agent_name,
263-
instructions=system_prompt
264-
)
265-
266-
# Register tools if provided
267-
if tools:
268-
for tool in tools:
269-
agent.add_function(tool)
270-
271-
# Cache the agent instance
272-
if session_id not in cls._azure_ai_agent_cache:
273-
cls._azure_ai_agent_cache[session_id] = {}
274-
cls._azure_ai_agent_cache[session_id][cache_key] = agent
275-
276-
return agent
277-
278228
@classmethod
279229
async def _load_tools_for_agent(cls, kernel: Kernel, agent_type: str) -> List[KernelFunction]:
280230
"""Load tools for an agent from the tools directory.
@@ -310,7 +260,7 @@ async def _load_tools_for_agent(cls, kernel: Kernel, agent_type: str) -> List[Ke
310260
# For other agent types, try to create a simple fallback tool
311261
try:
312262
# Use PromptTemplateConfig to create a simple tool
313-
from semantic_kernel.prompt_template.prompt_template_config import PromptTemplateConfig
263+
314264

315265
# Simple minimal prompt
316266
prompt = f"""You are a helpful assistant specialized in {agent_type} tasks.

src/backend/kernel_agents/group_chat_manager.py

Lines changed: 114 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,30 @@
55
from typing import Dict, List, Optional, Any, Tuple
66

77
import semantic_kernel as sk
8-
from semantic_kernel.agents import AgentGroupChat
8+
from semantic_kernel.functions.kernel_arguments import KernelArguments
9+
from semantic_kernel.agents import AgentGroupChat # pylint: disable=E0611
10+
911
from semantic_kernel.agents.strategies import (
1012
SequentialSelectionStrategy,
1113
TerminationStrategy,
1214
)
15+
# Updated imports for compatibility
16+
try:
17+
# Try importing from newer structure first
18+
from semantic_kernel.contents import ChatMessageContent, ChatHistory
19+
except ImportError:
20+
# Fall back to older structure for compatibility
21+
class ChatMessageContent:
22+
"""Compatibility class for older SK versions."""
23+
def __init__(self, role="", content="", name=None):
24+
self.role = role
25+
self.content = content
26+
self.name = name
27+
28+
class ChatHistory:
29+
"""Compatibility class for older SK versions."""
30+
def __init__(self):
31+
self.messages = []
1332

1433
from kernel_agents.agent_base import BaseAgent
1534
from context.cosmos_memory_kernel import CosmosMemoryContext
@@ -103,42 +122,46 @@ async def select_agent(self, agents, history):
103122
104123
Args:
105124
agents: List of available agents
106-
history: Chat history
125+
history: Chat history (ChatHistory object)
107126
108127
Returns:
109128
The next agent to take the turn
110129
"""
111130
# If no history, start with the PlannerAgent
112-
if not history:
131+
if not history or not history.messages:
113132
return next((agent for agent in agents if agent.name == "PlannerAgent"), None)
114133

115134
# Get the last message
116-
last_message = history[-1]
135+
last_message = history.messages[-1]
117136

118-
match last_message.name:
119-
case "PlannerAgent":
120-
# After the planner creates a plan, HumanAgent should review it
121-
return next((agent for agent in agents if agent.name == "HumanAgent"), None)
122-
123-
case "HumanAgent":
124-
# After human feedback, the specific agent for the step should proceed
125-
# Need to extract which agent should be next from the plan
126-
# For demo purposes, going with a simple approach
127-
# In a real implementation, we would look up the next step in the plan
128-
return next((agent for agent in agents if agent.name == "GenericAgent"), None)
129-
130-
case "GroupChatManager":
131-
# If the manager just assigned a step, the specific agent should execute it
132-
# For demo purposes, we'll just use the next agent in a simple rotation
133-
current_agent_index = next((i for i, agent in enumerate(agents)
134-
if agent.name == last_message.name), 0)
135-
next_index = (current_agent_index + 1) % len(agents)
137+
# Extract name from the message - in SK ChatMessageContent
138+
last_sender = last_message.role
139+
if hasattr(last_message, 'name') and last_message.name:
140+
last_sender = last_message.name
141+
142+
# Route based on the last sender
143+
if last_sender == "PlannerAgent":
144+
# After the planner creates a plan, HumanAgent should review it
145+
return next((agent for agent in agents if agent.name == "HumanAgent"), None)
146+
elif last_sender == "HumanAgent":
147+
# After human feedback, the specific agent for the step should proceed
148+
# For simplicity, use GenericAgent as fallback
149+
return next((agent for agent in agents if agent.name == "GenericAgent"), None)
150+
elif last_sender == "GroupChatManager":
151+
# If the manager just assigned a step, find the agent that should execute it
152+
# For simplicity, just rotate to the next agent
153+
agent_names = [agent.name for agent in agents]
154+
try:
155+
current_index = agent_names.index(last_sender)
156+
next_index = (current_index + 1) % len(agents)
136157
return agents[next_index]
137-
138-
case _:
139-
# Default to the Group Chat Manager to coordinate next steps
140-
return next((agent for agent in agents if agent.name == "GroupChatManager"), None)
141-
158+
except ValueError:
159+
return agents[0] if agents else None
160+
else:
161+
# Default to the Group Chat Manager
162+
return next((agent for agent in agents if agent.name == "GroupChatManager"),
163+
agents[0] if agents else None)
164+
142165
class PlanTerminationStrategy(TerminationStrategy):
143166
"""Strategy for determining when the agent group chat should terminate.
144167
@@ -154,23 +177,29 @@ def __init__(self, agents, maximum_iterations=10, automatic_reset=True):
154177
maximum_iterations: Maximum number of iterations before termination
155178
automatic_reset: Whether to reset the agent after termination
156179
"""
157-
super().__init__(agents, maximum_iterations, automatic_reset)
180+
super().__init__(maximum_iterations, automatic_reset)
181+
self._agents = agents
158182

159-
async def should_agent_terminate(self, agent, history):
160-
"""Check if the agent should terminate.
183+
async def should_terminate(self, history, agents=None) -> bool:
184+
"""Check if the chat should terminate.
161185
162186
Args:
163-
agent: The current agent
164-
history: Chat history
187+
history: Chat history as a ChatHistory object
188+
agents: List of agents (optional, uses self._agents if not provided)
165189
166190
Returns:
167-
True if the agent should terminate, False otherwise
191+
True if the chat should terminate, False otherwise
168192
"""
169-
# Default termination conditions
170-
if not history:
193+
# Default termination conditions from parent class
194+
if await super().should_terminate(history, agents or self._agents):
195+
return True
196+
197+
# If no history, continue the chat
198+
if not history or not history.messages:
171199
return False
172200

173-
last_message = history[-1]
201+
# Get the last message
202+
last_message = history.messages[-1]
174203

175204
# End the chat if the plan is completed or if human intervention is required
176205
if "plan completed" in last_message.content.lower():
@@ -469,35 +498,72 @@ async def run_group_chat(self, user_input: str, plan_id: str = "", step_id: str
469498
await self.initialize_group_chat()
470499

471500
try:
472-
# Run the group chat
473-
chat_result = await self._agent_group_chat.invoke_async(user_input)
501+
# Run the group chat with Semantic Kernel
502+
result = await self._agent_group_chat.invoke_async(user_input)
503+
504+
# Process the result which could be a ChatHistory object or something else
505+
if hasattr(result, "messages") and result.messages:
506+
messages = result.messages
507+
elif hasattr(result, "value") and isinstance(result.value, list):
508+
messages = result.value
509+
else:
510+
# Fallback for other formats
511+
messages = []
512+
if isinstance(result, str):
513+
# If it's just a string response
514+
logging.debug(f"Group chat returned a string: {result[:100]}...")
515+
await self._memory_store.add_item(
516+
AgentMessage(
517+
session_id=self._session_id,
518+
user_id=self._user_id,
519+
plan_id=plan_id,
520+
content=result,
521+
source="GroupChatManager",
522+
step_id=step_id,
523+
)
524+
)
525+
return result
526+
527+
# Process the messages from the chat result
528+
final_response = None
474529

475-
# Process and store results
476-
messages = chat_result.value
477530
for msg in messages:
478531
# Skip the initial user message
479-
if msg.role == "user" and msg.content == user_input:
532+
if hasattr(msg, "role") and msg.role == "user" and msg.content == user_input:
480533
continue
481-
482-
# Store agent messages in the memory
534+
535+
# Determine the source/agent name
536+
source = "assistant"
537+
if hasattr(msg, "name") and msg.name:
538+
source = msg.name
539+
elif hasattr(msg, "role") and msg.role:
540+
source = msg.role
541+
542+
# Get the message content
543+
content = msg.content if hasattr(msg, "content") else str(msg)
544+
545+
# Store the message in memory
483546
await self._memory_store.add_item(
484547
AgentMessage(
485548
session_id=self._session_id,
486549
user_id=self._user_id,
487550
plan_id=plan_id,
488-
content=msg.content,
489-
source=msg.name if hasattr(msg, "name") else msg.role,
551+
content=content,
552+
source=source,
490553
step_id=step_id,
491554
)
492555
)
556+
557+
# Keep track of the final response
558+
final_response = content
493559

494560
# Return the final message from the chat
495-
if messages:
496-
return messages[-1].content
561+
if final_response:
562+
return final_response
497563
return "Group chat completed with no messages."
498564

499565
except Exception as e:
500-
logging.error(f"Error running group chat: {str(e)}")
566+
logging.exception(f"Error running group chat: {e}")
501567
return f"Error running group chat: {str(e)}"
502568

503569
async def execute_next_step(self, session_id: str, plan_id: str) -> str:

src/backend/kernel_agents/planner_agent.py

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -680,23 +680,5 @@ def _generate_instruction(self, objective: str) -> str:
680680
Limit the plan to 6 steps or less.
681681
682682
Choose from {agents_str} ONLY for planning your steps.
683-
684-
When generating the action in the plan, frame the action as an instruction you are passing to the agent to execute. It should be a short, single sentence. Include the function to use. For example, "Set up an Office 365 Account for Jessica Smith. Function: set_up_office_365_account"
685-
686-
Ensure the summary of the plan and the overall steps is less than 50 words.
687683
688-
Identify any additional information that might be required to complete the task. Include this information in the plan in the human_clarification_request field of the plan. If it is not required, leave it as null. Do not include information that you are waiting for clarification on in the string of the action field, as this otherwise won't get updated.
689-
690-
Return your response as a JSON object with the following structure:
691-
{{
692-
"initial_goal": "The goal of the plan",
693-
"steps": [
694-
{{
695-
"action": "Detailed description of the step action",
696-
"agent": "AgentName"
697-
}}
698-
],
699-
"summary_plan_and_steps": "Brief summary of the plan and steps",
700-
"human_clarification_request": "Any additional information needed from the human"
701-
}}
702684
"""

0 commit comments

Comments
 (0)