Skip to content

Commit 40e0e8b

Browse files
authored
Merge pull request #151 from Fr4nc3/main
fix: agents message
2 parents 0679a9e + 9115ee2 commit 40e0e8b

File tree

12 files changed

+179
-358
lines changed

12 files changed

+179
-358
lines changed

src/backend/app_kernel.py

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,10 @@ async def input_task_endpoint(input_task: InputTask, request: Request):
125125
input_task.session_id, user_id
126126
)
127127
agents = await AgentFactory.create_all_agents(
128-
session_id=input_task.session_id, user_id=user_id
128+
session_id=input_task.session_id,
129+
user_id=user_id,
130+
kernel=kernel,
131+
memory_store=memory_store,
129132
)
130133

131134
group_chat_manager = agents[AgentType.GROUP_CHAT_MANAGER.value]
@@ -249,7 +252,10 @@ async def human_feedback_endpoint(human_feedback: HumanFeedback, request: Reques
249252
human_feedback.session_id, user_id
250253
)
251254
agents = await AgentFactory.create_all_agents(
252-
session_id=human_feedback.session_id, user_id=user_id
255+
session_id=human_feedback.session_id,
256+
user_id=user_id,
257+
memory_store=memory_store,
258+
kernel=kernel,
253259
)
254260

255261
# Send the feedback to the human agent
@@ -333,7 +339,10 @@ async def human_clarification_endpoint(
333339
human_clarification.session_id, user_id
334340
)
335341
agents = await AgentFactory.create_all_agents(
336-
session_id=human_clarification.session_id, user_id=user_id
342+
session_id=human_clarification.session_id,
343+
user_id=user_id,
344+
memory_store=memory_store,
345+
kernel=kernel,
337346
)
338347

339348
# Send the feedback to the human agent
@@ -425,7 +434,10 @@ async def approve_step_endpoint(
425434
human_feedback.session_id, user_id
426435
)
427436
agents = await AgentFactory.create_all_agents(
428-
session_id=human_feedback.session_id, user_id=user_id
437+
session_id=human_feedback.session_id,
438+
user_id=user_id,
439+
kernel=kernel,
440+
memory_store=memory_store,
429441
)
430442

431443
# Send the approval to the group chat manager

src/backend/kernel_agents/agent_base.py

Lines changed: 6 additions & 191 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,6 @@ def __init__(
5959
memory_store: CosmosMemoryContext,
6060
tools: Optional[List[KernelFunction]] = None,
6161
system_message: Optional[str] = None,
62-
agent_type: Optional[str] = None,
6362
client=None,
6463
definition=None,
6564
):
@@ -77,20 +76,9 @@ def __init__(
7776
client: The client required by AzureAIAgent
7877
definition: The definition required by AzureAIAgent
7978
"""
80-
# Add plugins if not already set
81-
# if not self.plugins:
82-
# If agent_type is provided, load tools from config automatically
83-
if agent_type and not tools:
84-
tools = self.get_tools_from_config(kernel, agent_type)
85-
# If system_message isn't provided, try to get it from config
86-
if not system_message:
87-
config = self.load_tools_config(agent_type)
88-
system_message = config.get(
89-
"system_message", self._default_system_message(agent_name)
90-
)
91-
else:
92-
tools = tools or []
93-
system_message = system_message or self._default_system_message(agent_name)
79+
80+
tools = tools or []
81+
system_message = system_message or self.default_system_message(agent_name)
9482

9583
# Call AzureAIAgent constructor with required client and definition
9684
super().__init__(
@@ -128,9 +116,9 @@ def __init__(
128116
# A list of plugins, or None if not applicable.
129117
# """
130118
# return None
131-
132-
def _default_system_message(self, agent_name=None) -> str:
133-
name = agent_name or getattr(self, "_agent_name", "Agent")
119+
@staticmethod
120+
def default_system_message(agent_name=None) -> str:
121+
name = agent_name
134122
return f"You are an AI assistant named {name}. Help the user by providing accurate and helpful information."
135123

136124
async def async_init(self):
@@ -293,179 +281,6 @@ async def handle_action_request(self, action_request: ActionRequest) -> str:
293281

294282
return response.json()
295283

296-
@staticmethod
297-
def create_dynamic_function(
298-
name: str,
299-
response_template: str,
300-
description: Optional[str] = None,
301-
formatting_instr: str = DEFAULT_FORMATTING_INSTRUCTIONS,
302-
) -> Callable[..., Awaitable[str]]:
303-
"""Create a dynamic function for agent tools based on the name and template.
304-
305-
Args:
306-
name: The name of the function to create
307-
response_template: The template string to use for the response
308-
formatting_instr: Optional formatting instructions to append to the response
309-
310-
Returns:
311-
A dynamic async function that can be registered with the semantic kernel
312-
"""
313-
314-
# Truncate function name to 64 characters if it exceeds the limit
315-
if len(name) > 64:
316-
logging.warning(
317-
f"Function name '{name}' exceeds 64 characters (length: {len(name)}). Truncating to 64 characters."
318-
)
319-
name = name[:64]
320-
321-
async def dynamic_function(**kwargs) -> str:
322-
try:
323-
# Format the template with the provided kwargs
324-
formatted_response = response_template.format(**kwargs)
325-
# Append formatting instructions if not already included in the template
326-
if formatting_instr and formatting_instr not in formatted_response:
327-
formatted_response = f"{formatted_response}\n{formatting_instr}"
328-
return formatted_response
329-
except KeyError as e:
330-
return f"Error: Missing parameter {e} for {name}"
331-
except Exception as e:
332-
return f"Error processing {name}: {str(e)}"
333-
334-
# Name the function properly for better debugging
335-
dynamic_function.__name__ = name
336-
337-
# Create a wrapped kernel function that matches the expected signature
338-
logging.info(f"Creating dynamic function: {name} {len(name)}")
339-
340-
@kernel_function(description=f"Dynamic function {name}", name=name)
341-
async def kernel_wrapper(
342-
kernel_arguments: KernelArguments = None, **kwargs
343-
) -> str:
344-
# Combine all arguments into one dictionary
345-
all_args = {}
346-
if kernel_arguments:
347-
for key, value in kernel_arguments.items():
348-
all_args[key] = value
349-
all_args.update(kwargs)
350-
return await dynamic_function(**all_args)
351-
352-
return kernel_wrapper
353-
354-
@staticmethod
355-
def load_tools_config(
356-
filename: str, config_path: Optional[str] = None
357-
) -> Dict[str, Any]:
358-
"""Load tools configuration from a JSON file.
359-
360-
Args:
361-
filename: The filename without extension (e.g., "hr", "marketing")
362-
config_path: Optional explicit path to the configuration file
363-
364-
Returns:
365-
A dictionary containing the configuration
366-
"""
367-
if config_path is None:
368-
# Default path relative to the tools directory
369-
current_dir = os.path.dirname(os.path.abspath(__file__))
370-
backend_dir = os.path.dirname(
371-
current_dir
372-
) # Just one level up to get to backend dir
373-
374-
# Normalize filename to avoid issues with spaces and capitalization
375-
# Convert "Hr Agent" to "hr" and "TechSupport Agent" to "tech_support"
376-
logging.info(f"Normalizing filename: {filename}")
377-
normalized_filename = filename.replace(" ", "_").replace("-", "_").lower()
378-
# If it ends with "_agent", remove it
379-
if normalized_filename.endswith("_agent"):
380-
normalized_filename = normalized_filename[:-6]
381-
382-
config_path = os.path.join(
383-
backend_dir, "tools", f"{normalized_filename}_tools.json"
384-
)
385-
logging.info(f"Looking for tools config at: {config_path}")
386-
387-
try:
388-
with open(config_path, "r") as f:
389-
return json.load(f)
390-
except Exception as e:
391-
logging.error(f"Error loading {filename} tools configuration: {e}")
392-
# Return empty default configuration
393-
return {
394-
"agent_name": f"{filename.capitalize()}Agent",
395-
"system_message": "You are an AI assistant",
396-
"tools": [],
397-
}
398-
399-
@classmethod
400-
def get_tools_from_config(
401-
cls, kernel: sk.Kernel, agent_type: str, config_path: Optional[str] = None
402-
) -> List[KernelFunction]:
403-
"""Get the list of tools for an agent from configuration.
404-
405-
Args:
406-
kernel: The semantic kernel instance
407-
agent_type: The type of agent (e.g., "marketing", "hr")
408-
config_path: Optional explicit path to the configuration file
409-
410-
Returns:
411-
A list of KernelFunction objects representing the tools
412-
"""
413-
# Load configuration
414-
config = cls.load_tools_config(agent_type, config_path)
415-
416-
# Convert the configured tools to kernel functions
417-
kernel_functions = []
418-
plugin_name = f"{agent_type}_plugin"
419-
420-
# Early return if no tools defined - prevent empty iteration
421-
if not config.get("tools"): # or agent_type == "Product_Agent":
422-
logging.info(
423-
f"No tools defined for agent type '{agent_type}'. Returning empty list."
424-
)
425-
return kernel_functions
426-
427-
for tool in config.get("tools", []):
428-
try:
429-
function_name = tool["name"]
430-
description = tool.get("description", "")
431-
# Create a dynamic function using the JSON response_template
432-
response_template = (
433-
tool.get("response_template") or tool.get("prompt_template") or ""
434-
)
435-
436-
# Generate a dynamic function using our improved approach
437-
dynamic_fn = cls.create_dynamic_function(
438-
function_name, response_template
439-
)
440-
441-
# Create kernel function from the decorated function
442-
kernel_func = KernelFunction.from_method(dynamic_fn)
443-
444-
# Add parameter metadata from JSON to the kernel function
445-
for param in tool.get("parameters", []):
446-
param_name = param.get("name", "")
447-
param_desc = param.get("description", "")
448-
param_type = param.get("type", "string")
449-
450-
# Set this parameter in the function's metadata
451-
if param_name:
452-
logging.info(
453-
f"Adding parameter '{param_name}' to function '{function_name}'"
454-
)
455-
456-
# Register the function with the kernel
457-
458-
kernel_functions.append(kernel_func)
459-
logging.info(
460-
f"Successfully created dynamic tool '{function_name}' for {agent_type}"
461-
)
462-
except Exception as e:
463-
logging.error(
464-
f"Failed to create tool '{tool.get('name', 'unknown')}': {str(e)}"
465-
)
466-
467-
return kernel_functions
468-
469284
def save_state(self) -> Mapping[str, Any]:
470285
"""Save the state of this agent."""
471286
return {"memory": self._memory_store.save_state()}

0 commit comments

Comments
 (0)