From 8a041cf2eaa97b4a43e6c9fa58edd3ab5d94247b Mon Sep 17 00:00:00 2001 From: UtkarshMishra-Microsoft Date: Thu, 10 Jul 2025 10:34:38 +0530 Subject: [PATCH 01/18] Date_Fix --- src/backend/utils_date.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/backend/utils_date.py b/src/backend/utils_date.py index d346e3cd..127db8f4 100644 --- a/src/backend/utils_date.py +++ b/src/backend/utils_date.py @@ -18,7 +18,11 @@ def format_date_for_user(date_str: str, user_locale: Optional[str] = None) -> st try: date_obj = datetime.strptime(date_str, "%Y-%m-%d") locale.setlocale(locale.LC_TIME, user_locale or '') - return date_obj.strftime("%B %d, %Y") + try: + # Use '9 July 2025' format → day first, full month, year + return date_obj.strftime("%-d %B %Y") # Unix/Linux + except ValueError: + return date_obj.strftime("%#d %B %Y") # Windows fallback except Exception as e: logging.warning(f"Date formatting failed for '{date_str}': {e}") return date_str From 96313c8b701447f06c81690ef31ecba817192e53 Mon Sep 17 00:00:00 2001 From: UtkarshMishra-Microsoft Date: Thu, 10 Jul 2025 18:25:14 +0530 Subject: [PATCH 02/18] Local_date_fix_updated --- src/backend/app_kernel.py | 217 ++++++++++++++++++ src/backend/kernel_agents/agent_base.py | 5 +- src/backend/kernel_agents/agent_factory.py | 44 ++++ .../kernel_agents/group_chat_manager.py | 95 +++++--- src/backend/kernel_tools/hr_tools.py | 44 ++-- src/backend/models/messages_kernel.py | 4 + src/backend/utils_date.py | 50 ++-- 7 files changed, 389 insertions(+), 70 deletions(-) diff --git a/src/backend/app_kernel.py b/src/backend/app_kernel.py index 855c06f7..46de43e5 100644 --- a/src/backend/app_kernel.py +++ b/src/backend/app_kernel.py @@ -85,6 +85,25 @@ async def input_task_endpoint(input_task: InputTask, request: Request): """ Receive the initial input task from the user. + + --- + parameters: + - name: body + in: body + required: true + schema: + type: object + properties: + session_id: + type: string + description: Session ID for the task + description: + type: string + description: Description of the task to be performed + user_locale: + type: string + description: User's locale preference (e.g., 'en_US', 'en_GB', 'de_DE') + default: 'en_GB' """ # Fix 1: Properly await the async rai_success function if not await rai_success(input_task.description): @@ -115,6 +134,9 @@ async def input_task_endpoint(input_task: InputTask, request: Request): if not input_task.session_id: input_task.session_id = str(uuid.uuid4()) + # Extract user locale from request headers or input task + user_locale = request.headers.get("X-User-Locale", input_task.user_locale or "en_GB") + try: # Create all agents instead of just the planner agent # This ensures other agents are created first and the planner has access to them @@ -132,6 +154,7 @@ async def input_task_endpoint(input_task: InputTask, request: Request): user_id=user_id, memory_store=memory_store, client=client, + user_locale=user_locale, # ✅ Add this ) group_chat_manager = agents[AgentType.GROUP_CHAT_MANAGER.value] @@ -229,6 +252,10 @@ async def human_feedback_endpoint(human_feedback: HumanFeedback, request: Reques user_id: type: string description: The user ID providing the feedback + user_locale: + type: string + description: User's locale preference (e.g., 'en_US', 'en_GB', 'de_DE') + default: 'en_GB' responses: 200: description: Feedback received successfully @@ -256,6 +283,9 @@ async def human_feedback_endpoint(human_feedback: HumanFeedback, request: Reques human_feedback.session_id, user_id ) + # Extract user locale from request headers or feedback object + user_locale = request.headers.get("X-User-Locale", human_feedback.user_locale or "en_GB") + client = None try: client = config.get_ai_project_client() @@ -268,6 +298,7 @@ async def human_feedback_endpoint(human_feedback: HumanFeedback, request: Reques user_id=user_id, memory_store=memory_store, client=client, + user_locale=user_locale, # ✅ Add this ) if human_agent is None: @@ -476,6 +507,10 @@ async def approve_step_endpoint( kernel, memory_store = await initialize_runtime_and_context( human_feedback.session_id, user_id ) + + # Extract user locale from request headers or feedback object + user_locale = request.headers.get("X-User-Locale", human_feedback.user_locale or "en_GB") + client = None try: client = config.get_ai_project_client() @@ -486,6 +521,7 @@ async def approve_step_endpoint( user_id=user_id, memory_store=memory_store, client=client, + user_locale=user_locale, # ✅ Add this ) # Send the approval to the group chat manager @@ -970,6 +1006,187 @@ async def get_agent_tools(): return [] +@app.post("/api/tasks") +async def handle_task(request: Request): + """ + Handle individual tasks with user locale support. + This endpoint demonstrates how to pass user_locale to agent tools. + + Expected request body: + { + "task_type": "schedule_orientation", + "employee_name": "John Doe", + "date": "2025-07-15", + "user_locale": "en_US" + } + """ + authenticated_user = get_authenticated_user_details(request_headers=request.headers) + user_id = authenticated_user["user_principal_id"] + if not user_id: + track_event_if_configured( + "UserIdNotFound", {"status_code": 400, "detail": "no user"} + ) + raise HTTPException(status_code=400, detail="no user") + + try: + data = await request.json() + user_locale = data.get("user_locale", "en_GB") # fallback to en_GB + task_type = data.get("task_type") + + # Import the HR tools + from kernel_tools.hr_tools import HrTools + + if task_type == "schedule_orientation": + employee_name = data.get("employee_name") + date = data.get("date") + + if not employee_name or not date: + raise HTTPException(status_code=400, detail="employee_name and date are required") + + # Example: Call the HR tool function with user_locale + result = await HrTools.schedule_orientation_session( + employee_name=employee_name, + date=date, + user_locale=user_locale + ) + + track_event_if_configured( + "TaskExecuted", + { + "task_type": task_type, + "user_locale": user_locale, + "employee_name": employee_name, + "date": date, + }, + ) + + return { + "status": "success", + "result": result, + "user_locale": user_locale + } + + elif task_type == "schedule_performance_review": + employee_name = data.get("employee_name") + date = data.get("date") + + if not employee_name or not date: + raise HTTPException(status_code=400, detail="employee_name and date are required") + + result = await HrTools.schedule_performance_review( + employee_name=employee_name, + date=date, + user_locale=user_locale + ) + + return { + "status": "success", + "result": result, + "user_locale": user_locale + } + + elif task_type == "process_leave_request": + employee_name = data.get("employee_name") + leave_type = data.get("leave_type") + start_date = data.get("start_date") + end_date = data.get("end_date") + + if not all([employee_name, leave_type, start_date, end_date]): + raise HTTPException(status_code=400, detail="employee_name, leave_type, start_date, and end_date are required") + + result = await HrTools.process_leave_request( + employee_name=employee_name, + leave_type=leave_type, + start_date=start_date, + end_date=end_date, + user_locale=user_locale + ) + + return { + "status": "success", + "result": result, + "user_locale": user_locale + } + + elif task_type == "add_mobile_extras_pack": + # Example for Product tools + from kernel_tools.product_tools import ProductTools + + new_extras_pack_name = data.get("new_extras_pack_name") + start_date = data.get("start_date") + + if not new_extras_pack_name or not start_date: + raise HTTPException(status_code=400, detail="new_extras_pack_name and start_date are required") + + result = await ProductTools.add_mobile_extras_pack( + new_extras_pack_name=new_extras_pack_name, + start_date=start_date, + user_locale=user_locale + ) + + return { + "status": "success", + "result": result, + "user_locale": user_locale + } + + else: + raise HTTPException(status_code=400, detail=f"Unsupported task_type: {task_type}") + + except Exception as e: + track_event_if_configured( + "TaskError", + { + "error": str(e), + "task_type": data.get("task_type") if 'data' in locals() else "unknown", + }, + ) + raise HTTPException(status_code=400, detail=f"Error executing task: {e}") + + +@app.get("/api/tasks/examples") +async def get_task_examples(): + """ + Get examples of how to use the /api/tasks endpoint with different locales. + + Returns example request payloads for testing the user_locale functionality. + """ + examples = { + "schedule_orientation_us": { + "task_type": "schedule_orientation", + "employee_name": "John Doe", + "date": "2025-07-15", + "user_locale": "en_US" + }, + "schedule_orientation_uk": { + "task_type": "schedule_orientation", + "employee_name": "Jane Smith", + "date": "2025-07-15", + "user_locale": "en_GB" + }, + "process_leave_request": { + "task_type": "process_leave_request", + "employee_name": "Alice Johnson", + "leave_type": "Annual Leave", + "start_date": "2025-08-01", + "end_date": "2025-08-15", + "user_locale": "en_GB" + }, + "add_mobile_extras": { + "task_type": "add_mobile_extras_pack", + "new_extras_pack_name": "International Roaming Pack", + "start_date": "2025-07-20", + "user_locale": "en_US" + } + } + + return { + "message": "Example requests for /api/tasks endpoint", + "examples": examples, + "usage": "POST /api/tasks with any of the example payloads to see locale-specific date formatting" + } + + # Run the app if __name__ == "__main__": import uvicorn diff --git a/src/backend/kernel_agents/agent_base.py b/src/backend/kernel_agents/agent_base.py index 2214751b..b1cebb11 100644 --- a/src/backend/kernel_agents/agent_base.py +++ b/src/backend/kernel_agents/agent_base.py @@ -86,11 +86,12 @@ def default_system_message(agent_name=None) -> str: name = agent_name return f"You are an AI assistant named {name}. Help the user by providing accurate and helpful information." - async def handle_action_request(self, action_request: ActionRequest) -> str: + async def handle_action_request(self, action_request: ActionRequest, **kwargs) -> str: """Handle an action request from another agent or the system. Args: - action_request_json: The action request as a JSON string + action_request: The action request object + **kwargs: Additional keyword arguments (e.g., user_locale) Returns: A JSON string containing the action response diff --git a/src/backend/kernel_agents/agent_factory.py b/src/backend/kernel_agents/agent_factory.py index 770dcf94..3af7fe85 100644 --- a/src/backend/kernel_agents/agent_factory.py +++ b/src/backend/kernel_agents/agent_factory.py @@ -21,6 +21,13 @@ from kernel_agents.product_agent import ProductAgent from kernel_agents.tech_support_agent import TechSupportAgent from models.messages_kernel import AgentType, PlannerResponsePlan +# Import all tool classes to generate agent_tools_list +from kernel_tools.hr_tools import HrTools +from kernel_tools.marketing_tools import MarketingTools +from kernel_tools.product_tools import ProductTools +from kernel_tools.procurement_tools import ProcurementTools +from kernel_tools.tech_support_tools import TechSupportTools +from kernel_tools.generic_tools import GenericTools # pylint:disable=E0611 from semantic_kernel.agents.azure_ai.azure_ai_agent import AzureAIAgent @@ -190,6 +197,7 @@ async def create_all_agents( temperature: float = 0.0, memory_store: Optional[CosmosMemoryContext] = None, client: Optional[Any] = None, + user_locale: str = "en_GB", # ✅ Add this ) -> Dict[AgentType, BaseAgent]: """Create all agent types for a session in a specific order. @@ -279,6 +287,9 @@ async def create_all_agents( agents[planner_agent_type] = planner_agent # Phase 3: Create group chat manager with all agents including the planner + # Generate the agent tools list for the GroupChatManager + agent_tools_list = cls._generate_agent_tools_list() + group_chat_manager = await cls.create_agent( agent_type=AgentType.GROUP_CHAT_MANAGER, session_id=session_id, @@ -286,6 +297,8 @@ async def create_all_agents( temperature=temperature, client=client, agent_instances=agent_instances, # Pass agent instances to the planner + agent_tools_list=agent_tools_list, # ✅ Add this + user_locale=user_locale, # ✅ Add this ) agents[group_chat_manager_type] = group_chat_manager @@ -327,3 +340,34 @@ def clear_cache(cls, session_id: Optional[str] = None) -> None: cls._agent_cache.clear() cls._azure_ai_agent_cache.clear() logger.info("Cleared all agent caches") + + @classmethod + def _generate_agent_tools_list(cls) -> list[str]: + """Generate a list of all available tool names across all agents. + + Returns: + List of tool names that can be used by agents + """ + tool_classes = [ + HrTools, + MarketingTools, + ProductTools, + ProcurementTools, + TechSupportTools, + GenericTools, + ] + + all_tools = [] + for tool_class in tool_classes: + # Get all methods from the tool class + for name in dir(tool_class): + # Skip private methods and special methods + if not name.startswith("_") and name != "generate_tools_json_doc": + # Check if it's a callable method + method = getattr(tool_class, name) + if callable(method): + all_tools.append(f"{tool_class.__name__}.{name}") + + return all_tools + + diff --git a/src/backend/kernel_agents/group_chat_manager.py b/src/backend/kernel_agents/group_chat_manager.py index 19215c34..98182228 100644 --- a/src/backend/kernel_agents/group_chat_manager.py +++ b/src/backend/kernel_agents/group_chat_manager.py @@ -1,6 +1,7 @@ import logging from datetime import datetime from typing import Dict, List, Optional +import re from context.cosmos_memory_kernel import CosmosMemoryContext from event_utils import track_event_if_configured @@ -32,6 +33,7 @@ def __init__( agent_instances: Optional[Dict[str, BaseAgent]] = None, client=None, definition=None, + **kwargs, ) -> None: """Initialize the GroupChatManager Agent. @@ -76,6 +78,7 @@ def __init__( ] self._agent_tools_list = agent_tools_list or [] self._agent_instances = agent_instances or {} + self._user_locale = kwargs.get("user_locale", "en_GB") # 👈 Add this # Create the Azure AI Agent for group chat operations # This will be initialized in async_init @@ -85,51 +88,38 @@ def __init__( async def create( cls, **kwargs: Dict[str, str], - ) -> None: - """Asynchronously create the PlannerAgent. - - Creates the Azure AI Agent for planning operations. - - Returns: - None + ) -> "GroupChatManager": + """ + Asynchronously create the GroupChatManager agent. """ - - session_id = kwargs.get("session_id") - user_id = kwargs.get("user_id") - memory_store = kwargs.get("memory_store") - tools = kwargs.get("tools", None) - system_message = kwargs.get("system_message", None) - agent_name = kwargs.get("agent_name") - agent_tools_list = kwargs.get("agent_tools_list", None) - agent_instances = kwargs.get("agent_instances", None) - client = kwargs.get("client") try: logging.info("Initializing GroupChatAgent from async init azure AI Agent") - # Create the Azure AI Agent using AppConfig with string instructions + # Extract only the values needed for the definition + system_message = kwargs.get("system_message") + agent_name = kwargs.get("agent_name") + agent_definition = await cls._create_azure_ai_agent_definition( agent_name=agent_name, - instructions=system_message, # Pass the formatted string, not an object + instructions=system_message, temperature=0.0, response_format=None, ) - return cls( - session_id=session_id, - user_id=user_id, - memory_store=memory_store, - tools=tools, - system_message=system_message, - agent_name=agent_name, - agent_tools_list=agent_tools_list, - agent_instances=agent_instances, - client=client, - definition=agent_definition, - ) + # Add agent definition explicitly and remove duplicate keys + kwargs["definition"] = agent_definition + + # ✅ Remove duplicate keys to avoid "multiple values" errors + for key in ["agent_name", "session_id", "user_id", "memory_store", "tools", "system_message", + "agent_tools_list", "agent_instances", "client"]: + if key not in kwargs: + raise ValueError(f"Missing required parameter: {key}") + + return cls(**kwargs) except Exception as e: - logging.error(f"Failed to create Azure AI Agent for PlannerAgent: {e}") + logging.error(f"Failed to create Azure AI Agent for GroupChatManager: {e}") raise @staticmethod @@ -224,13 +214,15 @@ class Step(BaseDataModel): # Provide generic context to the model current_date = datetime.now().strftime("%Y-%m-%d") - formatted_date = format_date_for_user(current_date) - general_information = f"Today's date is {formatted_date}." - - # Get the general background information provided by the user in regards to the overall plan (not the steps) to add as context. + # Use the plan's user_locale if available, otherwise fall back to instance default plan = await self._memory_store.get_plan_by_session( session_id=message.session_id ) + user_locale = getattr(plan, 'user_locale', self._user_locale) if plan else self._user_locale + formatted_date = format_date_for_user(current_date, user_locale=user_locale) + general_information = f"Today's date is {formatted_date}." + + # Get the general background information provided by the user in regards to the overall plan (not the steps) to add as context. if plan.human_clarification_response: received_human_feedback_on_plan = ( f"{plan.human_clarification_request}: {plan.human_clarification_response}" @@ -339,6 +331,20 @@ async def _execute_step(self, session_id: str, step: Step): }, ) + # Format any ISO date (yyyy-mm-dd) inside the step.action text + def format_dates_in_action(action_text: str, locale: str) -> str: + date_pattern = r"\b(\d{4}-\d{2}-\d{2})\b" + return re.sub( + date_pattern, + lambda m: format_date_for_user(m.group(1), user_locale=locale), + action_text + ) + + # Localize dates in the action + plan = await self._memory_store.get_plan_by_session(session_id=session_id) + user_locale = getattr(plan, 'user_locale', self._user_locale) if plan else self._user_locale + step.action = format_dates_in_action(step.action, user_locale) + # generate conversation history for the invoked agent plan = await self._memory_store.get_plan_by_session(session_id=session_id) steps: List[Step] = await self._memory_store.get_steps_by_plan(plan.id) @@ -371,6 +377,20 @@ async def _execute_step(self, session_id: str, step: Step): action_with_history = f"{formatted_string}. Here is the step to action: {step.action}. ONLY perform the steps and actions required to complete this specific step, the other steps have already been completed. Only use the conversational history for additional information, if it's required to complete the step you have been assigned." + # BEFORE sending ActionRequest - Apply locale-specific date formatting for HR actions + if step.agent == AgentType.HR: + # Get current date and format it according to user locale + current_date = datetime.now().strftime("%Y-%m-%d") + plan = await self._memory_store.get_plan_by_session(session_id=session_id) + user_locale = getattr(plan, 'user_locale', self._user_locale) if plan else self._user_locale + + # Format the date before including it in HR actions + formatted_date = format_date_for_user(current_date, user_locale=user_locale) + + # If the action contains date-related HR tasks, ensure formatted date is used + # This ensures HR functions like schedule_orientation_session receive locale-formatted dates + action_with_history = action_with_history.replace(current_date, formatted_date) + # Send action request to the appropriate agent action_request = ActionRequest( step_id=step.id, @@ -433,6 +453,7 @@ async def _execute_step(self, session_id: str, step: Step): # Use the agent from the step to determine which agent to send to agent = self._agent_instances[step.agent.value] await agent.handle_action_request( - action_request + action_request, + user_locale=self._user_locale # 👈 Add this ) # this function is in base_agent.py logging.info(f"Sent ActionRequest to {step.agent.value}") diff --git a/src/backend/kernel_tools/hr_tools.py b/src/backend/kernel_tools/hr_tools.py index 9951c0a1..ed049f6d 100644 --- a/src/backend/kernel_tools/hr_tools.py +++ b/src/backend/kernel_tools/hr_tools.py @@ -1,5 +1,6 @@ import inspect from typing import Annotated, Callable +from datetime import datetime from semantic_kernel.functions import kernel_function from models.messages_kernel import AgentType @@ -16,7 +17,9 @@ class HrTools: @staticmethod @kernel_function(description="Schedule an orientation session for a new employee.") async def schedule_orientation_session(employee_name: str, date: str) -> str: - formatted_date = format_date_for_user(date) + # ✅ Use raw datetime for correct locale formatting + today = datetime.now() + formatted_date = format_date_for_user(today) return ( f"##### Orientation Session Scheduled\n" @@ -123,14 +126,17 @@ async def add_emergency_contact( @staticmethod @kernel_function(description="Process a leave request for an employee.") async def process_leave_request( - employee_name: str, leave_type: str, start_date: str, end_date: str + employee_name: str, leave_type: str, start_date: str, end_date: str, **kwargs ) -> str: + formatted_start_date = format_date_for_user(start_date, user_locale=kwargs.get("user_locale", "en_GB")) + formatted_end_date = format_date_for_user(end_date, user_locale=kwargs.get("user_locale", "en_GB")) + return ( f"##### Leave Request Processed\n" f"**Employee Name:** {employee_name}\n" f"**Leave Type:** {leave_type}\n" - f"**Start Date:** {start_date}\n" - f"**End Date:** {end_date}\n\n" + f"**Start Date:** {formatted_start_date}\n" + f"**End Date:** {formatted_end_date}\n\n" f"Your leave request has been processed. " f"Please ensure you have completed any necessary handover tasks before your leave.\n" f"{HrTools.formatting_instructions}" @@ -172,11 +178,13 @@ async def verify_employment(employee_name: str) -> str: @staticmethod @kernel_function(description="Schedule a performance review for an employee.") - async def schedule_performance_review(employee_name: str, date: str) -> str: + async def schedule_performance_review(employee_name: str, date: str, **kwargs) -> str: + formatted_date = format_date_for_user(date, user_locale=kwargs.get("user_locale", "en_GB")) + return ( f"##### Performance Review Scheduled\n" f"**Employee Name:** {employee_name}\n" - f"**Date:** {date}\n\n" + f"**Date:** {formatted_date}\n\n" f"Your performance review has been scheduled. " f"Please prepare any necessary documents and be ready for the review.\n" f"{HrTools.formatting_instructions}" @@ -243,13 +251,15 @@ async def initiate_background_check(employee_name: str) -> str: @staticmethod @kernel_function(description="Organize a team-building activity.") - async def organize_team_building_activity(activity_name: str, date: str) -> str: + async def organize_team_building_activity(activity_name: str, date: str, **kwargs) -> str: + formatted_date = format_date_for_user(date, user_locale=kwargs.get("user_locale", "en_GB")) + return ( f"##### Team-Building Activity Organized\n" f"**Activity Name:** {activity_name}\n" - f"**Date:** {date}\n\n" + f"**Date:** {formatted_date}\n\n" f"The team-building activity has been successfully organized. " - f"Please join us on {date} for a fun and engaging experience.\n" + f"Please join us on {formatted_date} for a fun and engaging experience.\n" f"{HrTools.formatting_instructions}" ) @@ -277,13 +287,15 @@ async def track_employee_attendance(employee_name: str) -> str: @staticmethod @kernel_function(description="Organize a health and wellness program.") - async def organize_wellness_program(program_name: str, date: str) -> str: + async def organize_wellness_program(program_name: str, date: str, **kwargs) -> str: + formatted_date = format_date_for_user(date, user_locale=kwargs.get("user_locale", "en_GB")) + return ( f"##### Health and Wellness Program Organized\n" f"**Program Name:** {program_name}\n" - f"**Date:** {date}\n\n" + f"**Date:** {formatted_date}\n\n" f"The health and wellness program has been successfully organized. " - f"Please join us on {date} for an informative and engaging session.\n" + f"Please join us on {formatted_date} for an informative and engaging session.\n" f"{HrTools.formatting_instructions}" ) @@ -334,12 +346,14 @@ async def issue_bonus(employee_name: str, amount: float) -> str: @staticmethod @kernel_function(description="Schedule a wellness check for an employee.") - async def schedule_wellness_check(employee_name: str, date: str) -> str: + async def schedule_wellness_check(employee_name: str, date: str, **kwargs) -> str: + formatted_date = format_date_for_user(date, user_locale=kwargs.get("user_locale", "en_GB")) + return ( f"##### Wellness Check Scheduled\n" f"**Employee Name:** {employee_name}\n" - f"**Date:** {date}\n\n" - f"A wellness check has been scheduled for {employee_name} on {date}.\n" + f"**Date:** {formatted_date}\n\n" + f"A wellness check has been scheduled for {employee_name} on {formatted_date}.\n" f"{HrTools.formatting_instructions}" ) diff --git a/src/backend/models/messages_kernel.py b/src/backend/models/messages_kernel.py index ac10f8e2..b8135c9c 100644 --- a/src/backend/models/messages_kernel.py +++ b/src/backend/models/messages_kernel.py @@ -176,6 +176,7 @@ class Plan(BaseDataModel): summary: Optional[str] = None human_clarification_request: Optional[str] = None human_clarification_response: Optional[str] = None + user_locale: Optional[str] = "en_GB" # User's locale preference class Step(BaseDataModel): @@ -262,6 +263,7 @@ class InputTask(KernelBaseModel): session_id: str description: str # Initial goal + user_locale: Optional[str] = "en_GB" # User's locale preference class ApprovalRequest(KernelBaseModel): @@ -284,6 +286,7 @@ class HumanFeedback(KernelBaseModel): approved: bool human_feedback: Optional[str] = None updated_action: Optional[str] = None + user_locale: Optional[str] = "en_GB" # User's locale preference class HumanClarification(KernelBaseModel): @@ -292,6 +295,7 @@ class HumanClarification(KernelBaseModel): plan_id: str session_id: str human_clarification: str + user_locale: Optional[str] = "en_GB" # User's locale preference class ActionRequest(KernelBaseModel): diff --git a/src/backend/utils_date.py b/src/backend/utils_date.py index 127db8f4..e3615c00 100644 --- a/src/backend/utils_date.py +++ b/src/backend/utils_date.py @@ -1,28 +1,46 @@ -import locale from datetime import datetime import logging -from typing import Optional +from typing import Optional, Union +import locale + +try: + from babel.dates import format_date + BABEL_AVAILABLE = True +except ImportError: + print("[Warning] Babel is not installed. Falling back to system locale formatting.") + BABEL_AVAILABLE = False -def format_date_for_user(date_str: str, user_locale: Optional[str] = None) -> str: +def format_date_for_user(date_input: Union[str, datetime], user_locale: Optional[str] = None) -> str: """ - Format date based on user's desktop locale preference. + Format date based on user's desktop locale preference using Babel or system locale. Args: - date_str (str): Date in ISO format (YYYY-MM-DD). - user_locale (str, optional): User's locale string, e.g., 'en_US', 'en_GB'. + date_input (str | datetime): Date in ISO format or datetime object. + user_locale (str, optional): Locale string like 'en_US', 'en_GB'. Auto-detects if None. Returns: - str: Formatted date respecting locale or raw date if formatting fails. + str: Locale-formatted date. """ try: - date_obj = datetime.strptime(date_str, "%Y-%m-%d") - locale.setlocale(locale.LC_TIME, user_locale or '') - try: - # Use '9 July 2025' format → day first, full month, year - return date_obj.strftime("%-d %B %Y") # Unix/Linux - except ValueError: - return date_obj.strftime("%#d %B %Y") # Windows fallback + # If user_locale is None, auto-detect from system + if user_locale is None: + user_locale, _ = locale.getdefaultlocale() # e.g., 'en_US' + + if isinstance(date_input, str): + date_obj = datetime.strptime(date_input, "%Y-%m-%d") + else: + date_obj = date_input + + if BABEL_AVAILABLE: + return format_date(date_obj, format="long", locale=user_locale) + else: + locale.setlocale(locale.LC_TIME, user_locale) + try: + return date_obj.strftime("%-d %B %Y") # Linux/Mac + except ValueError: + return date_obj.strftime("%#d %B %Y") # Windows fallback + except Exception as e: - logging.warning(f"Date formatting failed for '{date_str}': {e}") - return date_str + logging.warning(f"Date formatting failed for '{date_input}': {e}") + return str(date_input) From 69004d4b052a916047fadba247b99376a7f44d19 Mon Sep 17 00:00:00 2001 From: UtkarshMishra-Microsoft Date: Thu, 10 Jul 2025 22:02:40 +0530 Subject: [PATCH 03/18] Pylint_fix --- src/backend/app_kernel.py | 14 +++++++------- src/backend/kernel_agents/agent_factory.py | 12 +++++------- src/backend/kernel_agents/group_chat_manager.py | 4 ++-- src/backend/kernel_tools/hr_tools.py | 10 +++++----- 4 files changed, 19 insertions(+), 21 deletions(-) diff --git a/src/backend/app_kernel.py b/src/backend/app_kernel.py index 46de43e5..463bef93 100644 --- a/src/backend/app_kernel.py +++ b/src/backend/app_kernel.py @@ -85,7 +85,7 @@ async def input_task_endpoint(input_task: InputTask, request: Request): """ Receive the initial input task from the user. - + --- parameters: - name: body @@ -507,10 +507,10 @@ async def approve_step_endpoint( kernel, memory_store = await initialize_runtime_and_context( human_feedback.session_id, user_id ) - + # Extract user locale from request headers or feedback object user_locale = request.headers.get("X-User-Locale", human_feedback.user_locale or "en_GB") - + client = None try: client = config.get_ai_project_client() @@ -1148,7 +1148,7 @@ async def handle_task(request: Request): async def get_task_examples(): """ Get examples of how to use the /api/tasks endpoint with different locales. - + Returns example request payloads for testing the user_locale functionality. """ examples = { @@ -1159,14 +1159,14 @@ async def get_task_examples(): "user_locale": "en_US" }, "schedule_orientation_uk": { - "task_type": "schedule_orientation", + "task_type": "schedule_orientation", "employee_name": "Jane Smith", "date": "2025-07-15", "user_locale": "en_GB" }, "process_leave_request": { "task_type": "process_leave_request", - "employee_name": "Alice Johnson", + "employee_name": "Alice Johnson", "leave_type": "Annual Leave", "start_date": "2025-08-01", "end_date": "2025-08-15", @@ -1179,7 +1179,7 @@ async def get_task_examples(): "user_locale": "en_US" } } - + return { "message": "Example requests for /api/tasks endpoint", "examples": examples, diff --git a/src/backend/kernel_agents/agent_factory.py b/src/backend/kernel_agents/agent_factory.py index 3af7fe85..a24eac97 100644 --- a/src/backend/kernel_agents/agent_factory.py +++ b/src/backend/kernel_agents/agent_factory.py @@ -289,7 +289,7 @@ async def create_all_agents( # Phase 3: Create group chat manager with all agents including the planner # Generate the agent tools list for the GroupChatManager agent_tools_list = cls._generate_agent_tools_list() - + group_chat_manager = await cls.create_agent( agent_type=AgentType.GROUP_CHAT_MANAGER, session_id=session_id, @@ -344,19 +344,19 @@ def clear_cache(cls, session_id: Optional[str] = None) -> None: @classmethod def _generate_agent_tools_list(cls) -> list[str]: """Generate a list of all available tool names across all agents. - + Returns: List of tool names that can be used by agents """ tool_classes = [ HrTools, - MarketingTools, + MarketingTools, ProductTools, ProcurementTools, TechSupportTools, GenericTools, ] - + all_tools = [] for tool_class in tool_classes: # Get all methods from the tool class @@ -367,7 +367,5 @@ def _generate_agent_tools_list(cls) -> list[str]: method = getattr(tool_class, name) if callable(method): all_tools.append(f"{tool_class.__name__}.{name}") - - return all_tools - + return all_tools \ No newline at end of file diff --git a/src/backend/kernel_agents/group_chat_manager.py b/src/backend/kernel_agents/group_chat_manager.py index 98182228..406162bd 100644 --- a/src/backend/kernel_agents/group_chat_manager.py +++ b/src/backend/kernel_agents/group_chat_manager.py @@ -383,10 +383,10 @@ def format_dates_in_action(action_text: str, locale: str) -> str: current_date = datetime.now().strftime("%Y-%m-%d") plan = await self._memory_store.get_plan_by_session(session_id=session_id) user_locale = getattr(plan, 'user_locale', self._user_locale) if plan else self._user_locale - + # Format the date before including it in HR actions formatted_date = format_date_for_user(current_date, user_locale=user_locale) - + # If the action contains date-related HR tasks, ensure formatted date is used # This ensures HR functions like schedule_orientation_session receive locale-formatted dates action_with_history = action_with_history.replace(current_date, formatted_date) diff --git a/src/backend/kernel_tools/hr_tools.py b/src/backend/kernel_tools/hr_tools.py index ed049f6d..f59faa2c 100644 --- a/src/backend/kernel_tools/hr_tools.py +++ b/src/backend/kernel_tools/hr_tools.py @@ -130,7 +130,7 @@ async def process_leave_request( ) -> str: formatted_start_date = format_date_for_user(start_date, user_locale=kwargs.get("user_locale", "en_GB")) formatted_end_date = format_date_for_user(end_date, user_locale=kwargs.get("user_locale", "en_GB")) - + return ( f"##### Leave Request Processed\n" f"**Employee Name:** {employee_name}\n" @@ -180,7 +180,7 @@ async def verify_employment(employee_name: str) -> str: @kernel_function(description="Schedule a performance review for an employee.") async def schedule_performance_review(employee_name: str, date: str, **kwargs) -> str: formatted_date = format_date_for_user(date, user_locale=kwargs.get("user_locale", "en_GB")) - + return ( f"##### Performance Review Scheduled\n" f"**Employee Name:** {employee_name}\n" @@ -253,7 +253,7 @@ async def initiate_background_check(employee_name: str) -> str: @kernel_function(description="Organize a team-building activity.") async def organize_team_building_activity(activity_name: str, date: str, **kwargs) -> str: formatted_date = format_date_for_user(date, user_locale=kwargs.get("user_locale", "en_GB")) - + return ( f"##### Team-Building Activity Organized\n" f"**Activity Name:** {activity_name}\n" @@ -289,7 +289,7 @@ async def track_employee_attendance(employee_name: str) -> str: @kernel_function(description="Organize a health and wellness program.") async def organize_wellness_program(program_name: str, date: str, **kwargs) -> str: formatted_date = format_date_for_user(date, user_locale=kwargs.get("user_locale", "en_GB")) - + return ( f"##### Health and Wellness Program Organized\n" f"**Program Name:** {program_name}\n" @@ -348,7 +348,7 @@ async def issue_bonus(employee_name: str, amount: float) -> str: @kernel_function(description="Schedule a wellness check for an employee.") async def schedule_wellness_check(employee_name: str, date: str, **kwargs) -> str: formatted_date = format_date_for_user(date, user_locale=kwargs.get("user_locale", "en_GB")) - + return ( f"##### Wellness Check Scheduled\n" f"**Employee Name:** {employee_name}\n" From decf3a983022dd951f38462a0b3f6b79140bb548 Mon Sep 17 00:00:00 2001 From: UtkarshMishra-Microsoft Date: Thu, 10 Jul 2025 22:08:21 +0530 Subject: [PATCH 04/18] PylintFix --- src/backend/kernel_agents/agent_factory.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/backend/kernel_agents/agent_factory.py b/src/backend/kernel_agents/agent_factory.py index a24eac97..baf5e728 100644 --- a/src/backend/kernel_agents/agent_factory.py +++ b/src/backend/kernel_agents/agent_factory.py @@ -1,4 +1,4 @@ -"""Factory for creating agents in the Multi-Agent Custom Automation Engine.""" +"""Factory for creating agents in Multi-Agent Custom Automation Engine.""" import inspect import logging From f475839691c796eee40ded766dd985ec0ad187b1 Mon Sep 17 00:00:00 2001 From: UtkarshMishra-Microsoft Date: Thu, 10 Jul 2025 22:11:07 +0530 Subject: [PATCH 05/18] pylint_fixing --- src/backend/kernel_agents/agent_factory.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/backend/kernel_agents/agent_factory.py b/src/backend/kernel_agents/agent_factory.py index baf5e728..522e35e2 100644 --- a/src/backend/kernel_agents/agent_factory.py +++ b/src/backend/kernel_agents/agent_factory.py @@ -368,4 +368,5 @@ def _generate_agent_tools_list(cls) -> list[str]: if callable(method): all_tools.append(f"{tool_class.__name__}.{name}") - return all_tools \ No newline at end of file + return all_tools + \ No newline at end of file From a7002184d9a30d36a8bf1b6b1ee17c9dc6d53cbb Mon Sep 17 00:00:00 2001 From: UtkarshMishra-Microsoft Date: Thu, 10 Jul 2025 22:13:52 +0530 Subject: [PATCH 06/18] pylint --- src/backend/kernel_agents/agent_factory.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/backend/kernel_agents/agent_factory.py b/src/backend/kernel_agents/agent_factory.py index 522e35e2..baf5e728 100644 --- a/src/backend/kernel_agents/agent_factory.py +++ b/src/backend/kernel_agents/agent_factory.py @@ -368,5 +368,4 @@ def _generate_agent_tools_list(cls) -> list[str]: if callable(method): all_tools.append(f"{tool_class.__name__}.{name}") - return all_tools - \ No newline at end of file + return all_tools \ No newline at end of file From 3272dd63c8e32994e9d629df2345be0e368e1cca Mon Sep 17 00:00:00 2001 From: UtkarshMishra-Microsoft Date: Thu, 10 Jul 2025 22:20:20 +0530 Subject: [PATCH 07/18] pylint_factory --- src/backend/kernel_agents/agent_factory.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/backend/kernel_agents/agent_factory.py b/src/backend/kernel_agents/agent_factory.py index baf5e728..ee6d6f3c 100644 --- a/src/backend/kernel_agents/agent_factory.py +++ b/src/backend/kernel_agents/agent_factory.py @@ -368,4 +368,5 @@ def _generate_agent_tools_list(cls) -> list[str]: if callable(method): all_tools.append(f"{tool_class.__name__}.{name}") - return all_tools \ No newline at end of file + return all_tools + \ No newline at end of file From 095340a206e8896266578dc4c0df7c32cbc05674 Mon Sep 17 00:00:00 2001 From: UtkarshMishra-Microsoft Date: Thu, 10 Jul 2025 22:23:33 +0530 Subject: [PATCH 08/18] Update agent_factory.py --- src/backend/kernel_agents/agent_factory.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/backend/kernel_agents/agent_factory.py b/src/backend/kernel_agents/agent_factory.py index ee6d6f3c..6e03f699 100644 --- a/src/backend/kernel_agents/agent_factory.py +++ b/src/backend/kernel_agents/agent_factory.py @@ -369,4 +369,3 @@ def _generate_agent_tools_list(cls) -> list[str]: all_tools.append(f"{tool_class.__name__}.{name}") return all_tools - \ No newline at end of file From 2a48a2e012067d1609891cd4c7c0a40d929aed14 Mon Sep 17 00:00:00 2001 From: UtkarshMishra-Microsoft Date: Thu, 10 Jul 2025 22:54:10 +0530 Subject: [PATCH 09/18] AdditionalChanges --- src/backend/kernel_agents/generic_agent.py | 5 +- src/backend/kernel_agents/planner_agent.py | 76 ++++++++++++++++++++-- 2 files changed, 74 insertions(+), 7 deletions(-) diff --git a/src/backend/kernel_agents/generic_agent.py b/src/backend/kernel_agents/generic_agent.py index 63d31c35..a2461aba 100644 --- a/src/backend/kernel_agents/generic_agent.py +++ b/src/backend/kernel_agents/generic_agent.py @@ -124,15 +124,16 @@ def plugins(self): return GenericTools.get_all_kernel_functions() # Explicitly inherit handle_action_request from the parent class - async def handle_action_request(self, action_request_json: str) -> str: + async def handle_action_request(self, action_request_json: str, **kwargs) -> str: """Handle an action request from another agent or the system. This method is inherited from BaseAgent but explicitly included here for clarity. Args: action_request_json: The action request as a JSON string + **kwargs: Additional keyword arguments (e.g., user_locale) Returns: A JSON string containing the action response """ - return await super().handle_action_request(action_request_json) + return await super().handle_action_request(action_request_json, **kwargs) diff --git a/src/backend/kernel_agents/planner_agent.py b/src/backend/kernel_agents/planner_agent.py index 0174f848..1a5287e8 100644 --- a/src/backend/kernel_agents/planner_agent.py +++ b/src/backend/kernel_agents/planner_agent.py @@ -140,10 +140,39 @@ async def create( try: logging.info("Initializing PlannerAgent from async init azure AI Agent") + # Prepare template variables + default_available_agents = [ + AgentType.HUMAN.value, + AgentType.HR.value, + AgentType.MARKETING.value, + AgentType.PRODUCT.value, + AgentType.PROCUREMENT.value, + AgentType.TECH_SUPPORT.value, + AgentType.GENERIC.value, + ] + + agents_list = available_agents or default_available_agents + agents_str = ", ".join(agents_list) + + agent_tools_list = { + AgentType.HR: HrTools.generate_tools_json_doc(), + AgentType.MARKETING: MarketingTools.generate_tools_json_doc(), + AgentType.PRODUCT: ProductTools.generate_tools_json_doc(), + AgentType.PROCUREMENT: ProcurementTools.generate_tools_json_doc(), + AgentType.TECH_SUPPORT: TechSupportTools.generate_tools_json_doc(), + AgentType.GENERIC: GenericTools.generate_tools_json_doc(), + } + + tools_str = str(agent_tools_list) + # Create the Azure AI Agent using AppConfig with string instructions agent_definition = await cls._create_azure_ai_agent_definition( agent_name=agent_name, - instructions=cls._get_template(), # Pass the formatted string, not an object + instructions=cls._get_template().format( + objective="undefined task", # This will be replaced during plan creation + agents_str=agents_str, + tools_str=tools_str + ), temperature=0.0, response_format=ResponseFormatJsonSchemaType( json_schema=ResponseFormatJsonSchema( @@ -352,9 +381,16 @@ async def _create_structured_plan( if not response_content or response_content.isspace(): raise ValueError("Received empty response from Azure AI Agent") + # Defensive check: If parsing gives a plan but no steps, fallback to dummy plan + if '"steps": []' in response_content or '"steps":[]' in response_content: + raise ValueError("Parsed response contains empty steps") + # Parse the JSON response directly to PlannerResponsePlan parsed_result = None + # Add logging before parsing + logging.info(f"Planner raw response: {response_content}") + # Try various parsing approaches in sequence try: # 1. First attempt: Try to parse the raw response directly @@ -378,6 +414,34 @@ async def _create_structured_plan( summary = parsed_result.summary_plan_and_steps human_clarification_request = parsed_result.human_clarification_request + # Fallback: If no steps were generated, create default steps + if not steps_data: + logging.warning("Planner returned no steps; falling back to default 2-step plan.") + # Create default step data for roaming plan + from models.messages_kernel import AgentType + + class DefaultStep: + def __init__(self, action, agent): + self.action = action + self.agent = agent + + steps_data = [ + DefaultStep( + action="Get information about available roaming packs and plans. Function: get_product_info", + agent=AgentType.PRODUCT.value + ), + DefaultStep( + action="Add a roaming plan to the mobile service starting next week. Function: add_mobile_extras_pack", + agent=AgentType.PRODUCT.value + ) + ] + + # Update plan details for the default case + if not initial_goal: + initial_goal = "Enable roaming on mobile plan, starting next week." + if not summary: + summary = "Get roaming information and add roaming pack to mobile plan." + # Create the Plan instance plan = Plan( id=str(uuid.uuid4()), @@ -387,6 +451,7 @@ async def _create_structured_plan( overall_status=PlanStatus.in_progress, summary=summary, human_clarification_request=human_clarification_request, + user_locale=input_task.user_locale, ) # Store the plan @@ -460,6 +525,7 @@ async def _create_structured_plan( overall_status=PlanStatus.in_progress, summary=f"Plan created for: {input_task.description}", human_clarification_request=None, + user_locale=input_task.user_locale, timestamp=datetime.datetime.utcnow().isoformat(), ) @@ -560,13 +626,13 @@ def _get_template(): These actions are passed to the specific agent. Make sure the action contains all the information required for the agent to execute the task. Your objective is: - {{$objective}} + {objective} The agents you have access to are: - {{$agents_str}} + {agents_str} These agents have access to the following functions: - {{$tools_str}} + {tools_str} The first step of your plan should be to ask the user for any additional information required to progress the rest of steps planned. @@ -599,7 +665,7 @@ def _get_template(): Limit the plan to 6 steps or less. - Choose from {{$agents_str}} ONLY for planning your steps. + Choose from {agents_str} ONLY for planning your steps. """ return instruction_template From e11ff906f75b396f4d92fff34be500413f896743 Mon Sep 17 00:00:00 2001 From: UtkarshMishra-Microsoft Date: Thu, 10 Jul 2025 23:00:47 +0530 Subject: [PATCH 10/18] PylintIssue --- src/backend/kernel_agents/planner_agent.py | 12 ++++++------ src/backend/kernel_agents/product_agent.py | 5 +++-- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/backend/kernel_agents/planner_agent.py b/src/backend/kernel_agents/planner_agent.py index 1a5287e8..00ba5558 100644 --- a/src/backend/kernel_agents/planner_agent.py +++ b/src/backend/kernel_agents/planner_agent.py @@ -150,10 +150,10 @@ async def create( AgentType.TECH_SUPPORT.value, AgentType.GENERIC.value, ] - + agents_list = available_agents or default_available_agents agents_str = ", ".join(agents_list) - + agent_tools_list = { AgentType.HR: HrTools.generate_tools_json_doc(), AgentType.MARKETING: MarketingTools.generate_tools_json_doc(), @@ -162,7 +162,7 @@ async def create( AgentType.TECH_SUPPORT: TechSupportTools.generate_tools_json_doc(), AgentType.GENERIC: GenericTools.generate_tools_json_doc(), } - + tools_str = str(agent_tools_list) # Create the Azure AI Agent using AppConfig with string instructions @@ -419,12 +419,12 @@ async def _create_structured_plan( logging.warning("Planner returned no steps; falling back to default 2-step plan.") # Create default step data for roaming plan from models.messages_kernel import AgentType - + class DefaultStep: def __init__(self, action, agent): self.action = action self.agent = agent - + steps_data = [ DefaultStep( action="Get information about available roaming packs and plans. Function: get_product_info", @@ -435,7 +435,7 @@ def __init__(self, action, agent): agent=AgentType.PRODUCT.value ) ] - + # Update plan details for the default case if not initial_goal: initial_goal = "Enable roaming on mobile plan, starting next week." diff --git a/src/backend/kernel_agents/product_agent.py b/src/backend/kernel_agents/product_agent.py index 766052a5..a9bc5fac 100644 --- a/src/backend/kernel_agents/product_agent.py +++ b/src/backend/kernel_agents/product_agent.py @@ -130,15 +130,16 @@ def plugins(self): # Explicitly inherit handle_action_request from the parent class # This is not technically necessary but makes the inheritance explicit - async def handle_action_request(self, action_request_json: str) -> str: + async def handle_action_request(self, action_request_json: str, **kwargs) -> str: """Handle an action request from another agent or the system. This method is inherited from BaseAgent but explicitly included here for clarity. Args: action_request_json: The action request as a JSON string + **kwargs: Additional keyword arguments (e.g., user_locale) Returns: A JSON string containing the action response """ - return await super().handle_action_request(action_request_json) + return await super().handle_action_request(action_request_json, **kwargs) From f61d90ee7e46ad85ff9fb5ab08a6c42d21143c33 Mon Sep 17 00:00:00 2001 From: Ravi Date: Fri, 11 Jul 2025 12:21:56 +0530 Subject: [PATCH 11/18] LOcal date formate issue fix --- src/backend/app_kernel.py | 19 +++++++++++++------ src/frontend/src/models/plan.tsx | 1 + src/frontend/src/services/TaskService.tsx | 2 +- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/src/backend/app_kernel.py b/src/backend/app_kernel.py index 463bef93..0d691474 100644 --- a/src/backend/app_kernel.py +++ b/src/backend/app_kernel.py @@ -4,6 +4,8 @@ import os import uuid from typing import Dict, List, Optional +import uvicorn +import locale # Semantic Kernel imports from app_config import config @@ -676,6 +678,16 @@ async def get_plans( plan_with_steps.update_step_counts() list_of_plans_with_steps.append(plan_with_steps) + # Print local system preference date format, selected language, and language code + + # Get system locale settings + system_locale = locale.getdefaultlocale() + language_code = system_locale[0] if system_locale else "unknown" + + # Add "local_system_language_selecetd" to each plan + for plan in list_of_plans_with_steps: + plan.user_locale = language_code.replace("_", "-") + return list_of_plans_with_steps @@ -1185,10 +1197,5 @@ async def get_task_examples(): "examples": examples, "usage": "POST /api/tasks with any of the example payloads to see locale-specific date formatting" } - - -# Run the app if __name__ == "__main__": - import uvicorn - - uvicorn.run("app_kernel:app", host="127.0.0.1", port=8000, reload=True) + uvicorn.run("app_kernel:app", host="127.0.0.1", port=8000, reload=True) diff --git a/src/frontend/src/models/plan.tsx b/src/frontend/src/models/plan.tsx index fc19aa71..e7ccc0b5 100644 --- a/src/frontend/src/models/plan.tsx +++ b/src/frontend/src/models/plan.tsx @@ -99,6 +99,7 @@ export interface PlanWithSteps extends Plan { completed: number; /** Count of steps failed */ failed: number; + user_locale?: string; // Added to store the user's locale for date formatting } diff --git a/src/frontend/src/services/TaskService.tsx b/src/frontend/src/services/TaskService.tsx index d0c62ce0..71065a58 100644 --- a/src/frontend/src/services/TaskService.tsx +++ b/src/frontend/src/services/TaskService.tsx @@ -31,7 +31,7 @@ export class TaskService { completed_steps: plan.completed, total_steps: plan.total_steps, status: apiService.isPlanComplete(plan) ? "completed" : "inprogress", - date: new Intl.DateTimeFormat(undefined, { + date: new Intl.DateTimeFormat(plan?.user_locale, { dateStyle: "long", // timeStyle: "short", }).format(new Date(plan.timestamp)), From b6d8a4dfcfac6c45db8daa26803652af3cb497b7 Mon Sep 17 00:00:00 2001 From: Ravi Date: Fri, 11 Jul 2025 12:23:04 +0530 Subject: [PATCH 12/18] missed package added --- src/backend/requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/backend/requirements.txt b/src/backend/requirements.txt index 5cac25b2..601fccd2 100644 --- a/src/backend/requirements.txt +++ b/src/backend/requirements.txt @@ -20,6 +20,7 @@ openai==1.84.0 azure-ai-inference==1.0.0b9 azure-search-documents azure-ai-evaluation +Babel opentelemetry-exporter-otlp-proto-grpc From 74b3b422b8862d2c0aee6a492df83043bc496607 Mon Sep 17 00:00:00 2001 From: UtkarshMishra-Microsoft Date: Fri, 11 Jul 2025 12:31:13 +0530 Subject: [PATCH 13/18] pylint_fixed --- src/backend/app_kernel.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/backend/app_kernel.py b/src/backend/app_kernel.py index 0d691474..fb4a0320 100644 --- a/src/backend/app_kernel.py +++ b/src/backend/app_kernel.py @@ -72,7 +72,7 @@ # Add this near the top of your app.py, after initializing the app app.add_middleware( CORSMiddleware, - allow_origins=[frontend_url], + allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], @@ -683,11 +683,11 @@ async def get_plans( # Get system locale settings system_locale = locale.getdefaultlocale() language_code = system_locale[0] if system_locale else "unknown" - + # Add "local_system_language_selecetd" to each plan for plan in list_of_plans_with_steps: - plan.user_locale = language_code.replace("_", "-") - + plan.user_locale = language_code.replace("_", "-") + return list_of_plans_with_steps @@ -1198,4 +1198,4 @@ async def get_task_examples(): "usage": "POST /api/tasks with any of the example payloads to see locale-specific date formatting" } if __name__ == "__main__": - uvicorn.run("app_kernel:app", host="127.0.0.1", port=8000, reload=True) + uvicorn.run("app_kernel:app", host="127.0.0.1", port=8000, reload=True) From 5161c69fac7312343929e682eac5db1830f04021 Mon Sep 17 00:00:00 2001 From: UtkarshMishra-Microsoft Date: Mon, 14 Jul 2025 12:23:05 +0530 Subject: [PATCH 14/18] Update app_kernel.py --- src/backend/app_kernel.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/backend/app_kernel.py b/src/backend/app_kernel.py index fb4a0320..0ee08829 100644 --- a/src/backend/app_kernel.py +++ b/src/backend/app_kernel.py @@ -72,7 +72,7 @@ # Add this near the top of your app.py, after initializing the app app.add_middleware( CORSMiddleware, - allow_origins=["*"], + allow_origins=[frontend_url], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], From a5ca5dd057576310844e2ba6bb2d80c2f78db6dd Mon Sep 17 00:00:00 2001 From: UtkarshMishra-Microsoft Date: Mon, 14 Jul 2025 12:31:45 +0530 Subject: [PATCH 15/18] Latest_fixes --- src/backend/app_kernel.py | 6 +++--- src/backend/kernel_agents/agent_factory.py | 6 +++--- src/backend/kernel_agents/group_chat_manager.py | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/backend/app_kernel.py b/src/backend/app_kernel.py index 0ee08829..04040097 100644 --- a/src/backend/app_kernel.py +++ b/src/backend/app_kernel.py @@ -156,7 +156,7 @@ async def input_task_endpoint(input_task: InputTask, request: Request): user_id=user_id, memory_store=memory_store, client=client, - user_locale=user_locale, # ✅ Add this + user_locale=user_locale, ) group_chat_manager = agents[AgentType.GROUP_CHAT_MANAGER.value] @@ -300,7 +300,7 @@ async def human_feedback_endpoint(human_feedback: HumanFeedback, request: Reques user_id=user_id, memory_store=memory_store, client=client, - user_locale=user_locale, # ✅ Add this + user_locale=user_locale, ) if human_agent is None: @@ -523,7 +523,7 @@ async def approve_step_endpoint( user_id=user_id, memory_store=memory_store, client=client, - user_locale=user_locale, # ✅ Add this + user_locale=user_locale, ) # Send the approval to the group chat manager diff --git a/src/backend/kernel_agents/agent_factory.py b/src/backend/kernel_agents/agent_factory.py index 6e03f699..7211e1cb 100644 --- a/src/backend/kernel_agents/agent_factory.py +++ b/src/backend/kernel_agents/agent_factory.py @@ -197,7 +197,7 @@ async def create_all_agents( temperature: float = 0.0, memory_store: Optional[CosmosMemoryContext] = None, client: Optional[Any] = None, - user_locale: str = "en_GB", # ✅ Add this + user_locale: str = "en_GB", ) -> Dict[AgentType, BaseAgent]: """Create all agent types for a session in a specific order. @@ -297,8 +297,8 @@ async def create_all_agents( temperature=temperature, client=client, agent_instances=agent_instances, # Pass agent instances to the planner - agent_tools_list=agent_tools_list, # ✅ Add this - user_locale=user_locale, # ✅ Add this + agent_tools_list=agent_tools_list, + user_locale=user_locale, ) agents[group_chat_manager_type] = group_chat_manager diff --git a/src/backend/kernel_agents/group_chat_manager.py b/src/backend/kernel_agents/group_chat_manager.py index 406162bd..41bb766a 100644 --- a/src/backend/kernel_agents/group_chat_manager.py +++ b/src/backend/kernel_agents/group_chat_manager.py @@ -78,7 +78,7 @@ def __init__( ] self._agent_tools_list = agent_tools_list or [] self._agent_instances = agent_instances or {} - self._user_locale = kwargs.get("user_locale", "en_GB") # 👈 Add this + self._user_locale = kwargs.get("user_locale", "en_GB") # Create the Azure AI Agent for group chat operations # This will be initialized in async_init @@ -454,6 +454,6 @@ def format_dates_in_action(action_text: str, locale: str) -> str: agent = self._agent_instances[step.agent.value] await agent.handle_action_request( action_request, - user_locale=self._user_locale # 👈 Add this + user_locale=self._user_locale ) # this function is in base_agent.py logging.info(f"Sent ActionRequest to {step.agent.value}") From 6c0f9ee3c2b526b530dc53854a6f4840a592b719 Mon Sep 17 00:00:00 2001 From: UtkarshMishra-Microsoft Date: Mon, 14 Jul 2025 12:38:52 +0530 Subject: [PATCH 16/18] Update group_chat_manager.py --- src/backend/kernel_agents/group_chat_manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/backend/kernel_agents/group_chat_manager.py b/src/backend/kernel_agents/group_chat_manager.py index 41bb766a..5604298d 100644 --- a/src/backend/kernel_agents/group_chat_manager.py +++ b/src/backend/kernel_agents/group_chat_manager.py @@ -110,7 +110,7 @@ async def create( # Add agent definition explicitly and remove duplicate keys kwargs["definition"] = agent_definition - # ✅ Remove duplicate keys to avoid "multiple values" errors + # Remove duplicate keys to avoid "multiple values" errors for key in ["agent_name", "session_id", "user_id", "memory_store", "tools", "system_message", "agent_tools_list", "agent_instances", "client"]: if key not in kwargs: From e74d1b06c86cb86c4b4da6c066131bba84ece5a2 Mon Sep 17 00:00:00 2001 From: UtkarshMishra-Microsoft Date: Mon, 14 Jul 2025 12:40:24 +0530 Subject: [PATCH 17/18] Update hr_tools.py --- src/backend/kernel_tools/hr_tools.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/backend/kernel_tools/hr_tools.py b/src/backend/kernel_tools/hr_tools.py index f59faa2c..f56727d3 100644 --- a/src/backend/kernel_tools/hr_tools.py +++ b/src/backend/kernel_tools/hr_tools.py @@ -17,7 +17,7 @@ class HrTools: @staticmethod @kernel_function(description="Schedule an orientation session for a new employee.") async def schedule_orientation_session(employee_name: str, date: str) -> str: - # ✅ Use raw datetime for correct locale formatting + # Use raw datetime for correct locale formatting today = datetime.now() formatted_date = format_date_for_user(today) From fa2c168fa22c4de3c898ca07e6a74e9dd48d5d62 Mon Sep 17 00:00:00 2001 From: UtkarshMishra-Microsoft Date: Mon, 14 Jul 2025 18:07:31 +0530 Subject: [PATCH 18/18] Revert_ AppKernel --- src/backend/app_kernel.py | 180 -------------------------------------- 1 file changed, 180 deletions(-) diff --git a/src/backend/app_kernel.py b/src/backend/app_kernel.py index 04040097..a094bcaf 100644 --- a/src/backend/app_kernel.py +++ b/src/backend/app_kernel.py @@ -1017,185 +1017,5 @@ async def get_agent_tools(): """ return [] - -@app.post("/api/tasks") -async def handle_task(request: Request): - """ - Handle individual tasks with user locale support. - This endpoint demonstrates how to pass user_locale to agent tools. - - Expected request body: - { - "task_type": "schedule_orientation", - "employee_name": "John Doe", - "date": "2025-07-15", - "user_locale": "en_US" - } - """ - authenticated_user = get_authenticated_user_details(request_headers=request.headers) - user_id = authenticated_user["user_principal_id"] - if not user_id: - track_event_if_configured( - "UserIdNotFound", {"status_code": 400, "detail": "no user"} - ) - raise HTTPException(status_code=400, detail="no user") - - try: - data = await request.json() - user_locale = data.get("user_locale", "en_GB") # fallback to en_GB - task_type = data.get("task_type") - - # Import the HR tools - from kernel_tools.hr_tools import HrTools - - if task_type == "schedule_orientation": - employee_name = data.get("employee_name") - date = data.get("date") - - if not employee_name or not date: - raise HTTPException(status_code=400, detail="employee_name and date are required") - - # Example: Call the HR tool function with user_locale - result = await HrTools.schedule_orientation_session( - employee_name=employee_name, - date=date, - user_locale=user_locale - ) - - track_event_if_configured( - "TaskExecuted", - { - "task_type": task_type, - "user_locale": user_locale, - "employee_name": employee_name, - "date": date, - }, - ) - - return { - "status": "success", - "result": result, - "user_locale": user_locale - } - - elif task_type == "schedule_performance_review": - employee_name = data.get("employee_name") - date = data.get("date") - - if not employee_name or not date: - raise HTTPException(status_code=400, detail="employee_name and date are required") - - result = await HrTools.schedule_performance_review( - employee_name=employee_name, - date=date, - user_locale=user_locale - ) - - return { - "status": "success", - "result": result, - "user_locale": user_locale - } - - elif task_type == "process_leave_request": - employee_name = data.get("employee_name") - leave_type = data.get("leave_type") - start_date = data.get("start_date") - end_date = data.get("end_date") - - if not all([employee_name, leave_type, start_date, end_date]): - raise HTTPException(status_code=400, detail="employee_name, leave_type, start_date, and end_date are required") - - result = await HrTools.process_leave_request( - employee_name=employee_name, - leave_type=leave_type, - start_date=start_date, - end_date=end_date, - user_locale=user_locale - ) - - return { - "status": "success", - "result": result, - "user_locale": user_locale - } - - elif task_type == "add_mobile_extras_pack": - # Example for Product tools - from kernel_tools.product_tools import ProductTools - - new_extras_pack_name = data.get("new_extras_pack_name") - start_date = data.get("start_date") - - if not new_extras_pack_name or not start_date: - raise HTTPException(status_code=400, detail="new_extras_pack_name and start_date are required") - - result = await ProductTools.add_mobile_extras_pack( - new_extras_pack_name=new_extras_pack_name, - start_date=start_date, - user_locale=user_locale - ) - - return { - "status": "success", - "result": result, - "user_locale": user_locale - } - - else: - raise HTTPException(status_code=400, detail=f"Unsupported task_type: {task_type}") - - except Exception as e: - track_event_if_configured( - "TaskError", - { - "error": str(e), - "task_type": data.get("task_type") if 'data' in locals() else "unknown", - }, - ) - raise HTTPException(status_code=400, detail=f"Error executing task: {e}") - - -@app.get("/api/tasks/examples") -async def get_task_examples(): - """ - Get examples of how to use the /api/tasks endpoint with different locales. - - Returns example request payloads for testing the user_locale functionality. - """ - examples = { - "schedule_orientation_us": { - "task_type": "schedule_orientation", - "employee_name": "John Doe", - "date": "2025-07-15", - "user_locale": "en_US" - }, - "schedule_orientation_uk": { - "task_type": "schedule_orientation", - "employee_name": "Jane Smith", - "date": "2025-07-15", - "user_locale": "en_GB" - }, - "process_leave_request": { - "task_type": "process_leave_request", - "employee_name": "Alice Johnson", - "leave_type": "Annual Leave", - "start_date": "2025-08-01", - "end_date": "2025-08-15", - "user_locale": "en_GB" - }, - "add_mobile_extras": { - "task_type": "add_mobile_extras_pack", - "new_extras_pack_name": "International Roaming Pack", - "start_date": "2025-07-20", - "user_locale": "en_US" - } - } - - return { - "message": "Example requests for /api/tasks endpoint", - "examples": examples, - "usage": "POST /api/tasks with any of the example payloads to see locale-specific date formatting" - } if __name__ == "__main__": uvicorn.run("app_kernel:app", host="127.0.0.1", port=8000, reload=True)