From 4b97c0f08bd0dd54c8cd599fb344a6757cb0ad73 Mon Sep 17 00:00:00 2001 From: Francia Riesco Date: Tue, 9 Sep 2025 15:07:16 -0400 Subject: [PATCH 1/4] Refactor team selection display logic Moved team selection UI logic from PlanPanelLeft to conditionally render TeamSelector on the home page and TeamSelected elsewhere. Updated TeamSelected to use local styles and removed unused props. Cleaned up fallback team selection logic in HomePage. --- .../src/components/common/TeamSelected.tsx | 7 +++--- .../src/components/content/PlanPanelLeft.tsx | 24 ++++++++++++++----- src/frontend/src/pages/HomePage.tsx | 14 ----------- 3 files changed, 21 insertions(+), 24 deletions(-) diff --git a/src/frontend/src/components/common/TeamSelected.tsx b/src/frontend/src/components/common/TeamSelected.tsx index 8aedbe7d..881a7133 100644 --- a/src/frontend/src/components/common/TeamSelected.tsx +++ b/src/frontend/src/components/common/TeamSelected.tsx @@ -1,19 +1,18 @@ import { TeamConfig } from "@/models"; import { Body1, Caption1 } from "@fluentui/react-components"; - +import styles from '../../styles/TeamSelector.module.css'; export interface TeamSelectedProps { selectedTeam?: TeamConfig | null; - styles: { [key: string]: string }; } -const TeamSelected: React.FC = ({ selectedTeam, styles }) => { +const TeamSelected: React.FC = ({ selectedTeam }) => { return (
Current Team - {selectedTeam ? selectedTeam.name : 'No team selected'} + {" "} {selectedTeam ? selectedTeam.name : 'No team selected'}
); diff --git a/src/frontend/src/components/content/PlanPanelLeft.tsx b/src/frontend/src/components/content/PlanPanelLeft.tsx index 7e86971d..3cb75676 100644 --- a/src/frontend/src/components/content/PlanPanelLeft.tsx +++ b/src/frontend/src/components/content/PlanPanelLeft.tsx @@ -30,6 +30,8 @@ import PanelUserCard from "../../coral/components/Panels/UserCard"; import { getUserInfoGlobal } from "@/api/config"; import TeamSelector from "../common/TeamSelector"; import { TeamConfig } from "../../models/Team"; +import TeamSelected from "../common/TeamSelected"; +import TeamService from "@/services/TeamService"; const PlanPanelLeft: React.FC = ({ reloadTasks, @@ -172,13 +174,23 @@ const PlanPanelLeft: React.FC = ({ {/* Team Selector right under the toolbar */} +
- + {isHomePage && ( + + )} + + {!isHomePage && ( + + )} +
{ `${initializedTeam.name} team initialized successfully with ${initializedTeam.agents?.length || 0} agents`, "success" ); - } else { - // Fallback: if we can't find the specific team, use HR team or first available - console.log('Specific team not found, using default selection logic'); - const hrTeam = teams.find(team => team.name === "Human Resources Team"); - const defaultTeam = hrTeam || teams[0]; - - if (defaultTeam) { - setSelectedTeam(defaultTeam); - TeamService.storageTeam(defaultTeam); - showToast( - `${defaultTeam.name} team loaded as default`, - "success" - ); - } } } else { From 30974616186be69ac6d71534035f812726e76558 Mon Sep 17 00:00:00 2001 From: Francia Riesco Date: Tue, 9 Sep 2025 15:38:07 -0400 Subject: [PATCH 2/4] Enhance agent and buffer message UI components Updated StreamingAgentMessage to improve message rendering, add bot tags, copy button, and sample data tag for assistant messages. Refined TeamSelected spacing using non-breaking spaces. Changed StreamingBufferMessage to show the last 500 characters of the buffer with a leading ellipsis for long messages. --- .../src/components/common/TeamSelected.tsx | 4 +- .../streaming/StreamingAgentMessage.tsx | 108 +++++++++++++++--- .../streaming/StreamingBufferMessage.tsx | 7 +- src/frontend/src/pages/PlanPage.tsx | 2 +- 4 files changed, 98 insertions(+), 23 deletions(-) diff --git a/src/frontend/src/components/common/TeamSelected.tsx b/src/frontend/src/components/common/TeamSelected.tsx index 881a7133..29609b59 100644 --- a/src/frontend/src/components/common/TeamSelected.tsx +++ b/src/frontend/src/components/common/TeamSelected.tsx @@ -9,10 +9,10 @@ const TeamSelected: React.FC = ({ selectedTeam }) => { return (
- Current Team +   Current Team - {" "} {selectedTeam ? selectedTeam.name : 'No team selected'} +   {selectedTeam ? selectedTeam.name : 'No team selected'}
); diff --git a/src/frontend/src/components/content/streaming/StreamingAgentMessage.tsx b/src/frontend/src/components/content/streaming/StreamingAgentMessage.tsx index 1080cb1d..e84ccba1 100644 --- a/src/frontend/src/components/content/streaming/StreamingAgentMessage.tsx +++ b/src/frontend/src/components/content/streaming/StreamingAgentMessage.tsx @@ -1,7 +1,11 @@ -import { AgentMessageData } from "@/models"; +import { AgentMessageData, AgentMessageType, role } from "@/models"; import ReactMarkdown from "react-markdown"; import remarkGfm from "remark-gfm"; import rehypePrism from "rehype-prism"; +import { Body1, Button, Tag } from "@fluentui/react-components"; +import { TaskService } from "@/services"; +import { Copy } from "@/coral/imports/bundleicons"; +import { DiamondRegular, HeartRegular } from "@fluentui/react-icons"; const StreamingAgentMessage = (agentMessages: AgentMessageData[]) => { @@ -9,27 +13,97 @@ const StreamingAgentMessage = (agentMessages: AgentMessageData[]) => { // Filter out messages with empty content const validMessages = agentMessages.filter(msg => msg.raw_content?.trim()); - + const messages = validMessages; if (!validMessages.length) return null; return ( -
- {validMessages.map((message, index) => ( -
-
- {message.agent}: -
-
- - {message.raw_content.trim()} - + //
+ // {validMessages.map((message, index) => ( + //
+ //
+ // {message.agent}: + //
+ //
+ // + // {message.raw_content.trim()} + // + //
+ //
+ // ))} + //
+ +
+ {messages.map((msg, index) => { + const isHuman = msg.agent_type === AgentMessageType.HUMAN_AGENT; + + return ( +
+ {!isHuman && ( +
+
+ + {TaskService.cleanTextToSpaces(msg.agent)} + + + BOT + +
+
+ )} + + +
+ + {TaskService.cleanHRAgent(msg.raw_content) || ""} + + + {!isHuman && ( +
+
+
+
+ + } + appearance="filled" + size="extra-small" + > + Sample data for demonstration purposes only. + +
+
+ )} +
+
-
- ))} + ); + })}
+ ); }; diff --git a/src/frontend/src/components/content/streaming/StreamingBufferMessage.tsx b/src/frontend/src/components/content/streaming/StreamingBufferMessage.tsx index f5bee37f..5710b891 100644 --- a/src/frontend/src/components/content/streaming/StreamingBufferMessage.tsx +++ b/src/frontend/src/components/content/streaming/StreamingBufferMessage.tsx @@ -12,9 +12,10 @@ const renderBufferMessage = (streamingMessageBuffer: string) => { if (!streamingMessageBuffer || streamingMessageBuffer.trim() === "") return null; - const previewText = streamingMessageBuffer.length > 500 - ? streamingMessageBuffer.substring(0, 500) + "..." - : streamingMessageBuffer; + const start = Math.max(0, streamingMessageBuffer.length - 500); + const previewText = start === 0 + ? streamingMessageBuffer + : "..." + streamingMessageBuffer.substring(start); return (
{ streamingMessages={streamingMessages} wsConnected={wsConnected} onPlanApproval={(approved) => setPlanApproved(approved)} - onPlanProcessing={(showProcessingPlanSpinner) => setShowProcessingPlanSpinner(showProcessingPlanSpinner)} + setShowProcessingPlanSpinner={(showProcessingPlanSpinner) => setShowProcessingPlanSpinner(showProcessingPlanSpinner)} planApprovalRequest={planApprovalRequest} waitingForPlan={waitingForPlan} messagesContainerRef={messagesContainerRef} From fa5b250754993e210d6f06d5bf957de6ebdc9940 Mon Sep 17 00:00:00 2001 From: Francia Riesco Date: Tue, 9 Sep 2025 16:16:44 -0400 Subject: [PATCH 3/4] Refactor agent message content field and add enums Renamed 'raw_content' to 'content' in AgentMessageData across frontend and backend for consistency. Added new enums and fields to backend models, including AgentMessageType, PlanStatus 'canceled', Plan 'approved', and updated DataType values. Updated frontend components and services to use the new 'content' field. --- src/backend/common/models/messages_kernel.py | 19 ++++++++++++-- src/backend/v3/models/messages.py | 1 - src/backend/v3/models/models.py | 5 +++- .../streaming/StreamingAgentMessage.tsx | 26 ++++--------------- src/frontend/src/models/agentMessage.tsx | 2 +- src/frontend/src/pages/PlanPage.tsx | 6 ++--- src/frontend/src/services/PlanDataService.tsx | 10 +++---- 7 files changed, 35 insertions(+), 34 deletions(-) diff --git a/src/backend/common/models/messages_kernel.py b/src/backend/common/models/messages_kernel.py index f27f8cb4..e6b98bef 100644 --- a/src/backend/common/models/messages_kernel.py +++ b/src/backend/common/models/messages_kernel.py @@ -3,8 +3,9 @@ from enum import Enum from typing import Any, Dict, List, Literal, Optional -from semantic_kernel.kernel_pydantic import Field, KernelBaseModel +from semantic_kernel.kernel_pydantic import Field, KernelBaseModel +from dataclasses import dataclass class DataType(str, Enum): """Enumeration of possible data types for documents in the database.""" @@ -16,7 +17,7 @@ class DataType(str, Enum): team_config = "team_config" user_current_team = "user_current_team" m_plan = "m_plan" - m_plan_step = "m_plan_step" + m_plan_message = "m_plan_message" class AgentType(str, Enum): @@ -53,6 +54,7 @@ class PlanStatus(str, Enum): in_progress = "in_progress" completed = "completed" failed = "failed" + canceled = "canceled" class HumanFeedbackStatus(str, Enum): @@ -93,6 +95,7 @@ class AgentMessage(BaseDataModel): step_id: Optional[str] = None + class Session(BaseDataModel): """Represents a user session.""" @@ -119,6 +122,7 @@ class Plan(BaseDataModel): user_id: str initial_goal: str overall_status: PlanStatus = PlanStatus.in_progress + approved: bool = False source: str = AgentType.PLANNER.value summary: Optional[str] = None team_id: Optional[str] = None @@ -250,3 +254,14 @@ class InputTask(KernelBaseModel): class UserLanguage(KernelBaseModel): language: str + + +class AgentMessageType(str, Enum): + HUMAN_AGENT = "Human_Agent", + AI_AGENT = "AI_Agent", + + +class AgentMessageData (BaseDataModel): + agent: str + agent_type: AgentMessageType = AgentMessageType.AI_AGENT + content: str \ No newline at end of file diff --git a/src/backend/v3/models/messages.py b/src/backend/v3/models/messages.py index 489b21dd..3aec8697 100644 --- a/src/backend/v3/models/messages.py +++ b/src/backend/v3/models/messages.py @@ -123,7 +123,6 @@ class ApprovalRequest(KernelBaseModel): agent_name: str - class WebsocketMessageType(str, Enum): """Types of WebSocket messages.""" SYSTEM_MESSAGE = "system_message" diff --git a/src/backend/v3/models/models.py b/src/backend/v3/models/models.py index 7a764a71..19a2b9f3 100644 --- a/src/backend/v3/models/models.py +++ b/src/backend/v3/models/models.py @@ -1,10 +1,12 @@ import uuid from datetime import datetime, timezone from enum import Enum -from typing import List, Optional +from typing import List, Literal, Optional from pydantic import BaseModel, Field +from common.models.messages_kernel import DataType + class PlanStatus(str, Enum): CREATED = "created" @@ -22,6 +24,7 @@ class MStep(BaseModel): class MPlan(BaseModel): """model of a plan""" id: str = Field(default_factory=lambda: str(uuid.uuid4())) + data_type: Literal[DataType.m_plan] = Field(DataType.m_plan, Literal=True) user_id: str = "" team_id: str = "" plan_id: str = "" diff --git a/src/frontend/src/components/content/streaming/StreamingAgentMessage.tsx b/src/frontend/src/components/content/streaming/StreamingAgentMessage.tsx index e84ccba1..efd1f34e 100644 --- a/src/frontend/src/components/content/streaming/StreamingAgentMessage.tsx +++ b/src/frontend/src/components/content/streaming/StreamingAgentMessage.tsx @@ -12,28 +12,12 @@ const StreamingAgentMessage = (agentMessages: AgentMessageData[]) => { if (!agentMessages?.length) return null; // Filter out messages with empty content - const validMessages = agentMessages.filter(msg => msg.raw_content?.trim()); + const validMessages = agentMessages.filter(msg => msg.content?.trim()); const messages = validMessages; if (!validMessages.length) return null; return ( - //
- // {validMessages.map((message, index) => ( - //
- //
- // {message.agent}: - //
- //
- // - // {message.raw_content.trim()} - // - //
- //
- // ))} - //
+
{messages.map((msg, index) => { @@ -68,7 +52,7 @@ const StreamingAgentMessage = (agentMessages: AgentMessageData[]) => { remarkPlugins={[remarkGfm]} rehypePlugins={[rehypePrism]} > - {TaskService.cleanHRAgent(msg.raw_content) || ""} + {TaskService.cleanHRAgent(msg.content) || ""} {!isHuman && ( @@ -77,8 +61,8 @@ const StreamingAgentMessage = (agentMessages: AgentMessageData[]) => {