Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions src/backend/v3/api/router.py
Original file line number Diff line number Diff line change
Expand Up @@ -424,6 +424,36 @@ async def user_clarification(
)
# Set the approval in the orchestration config
if user_id and human_feedback.request_id:
### validate rai
if human_feedback.answer != None or human_feedback.answer !="":
if not await rai_success(human_feedback.answer, False):
track_event_if_configured(
"RAI failed",
{
"status": "Plan Clarification ",
"description": human_feedback.answer,
"request_id": human_feedback.request_id,
},
)
raise HTTPException(
status_code=400,
detail={
"error_type": "RAI_VALIDATION_FAILED",
"message": "Content Safety Check Failed",
"description": "Your request contains content that doesn't meet our safety guidelines. Please modify your request to ensure it's appropriate and try again.",
"suggestions": [
"Remove any potentially harmful, inappropriate, or unsafe content",
"Use more professional and constructive language",
"Focus on legitimate business or educational objectives",
"Ensure your request complies with content policies",
],
"user_action": "Please revise your request and try again",
},
)




if (
orchestration_config
and human_feedback.request_id in orchestration_config.clarifications
Expand Down
2 changes: 2 additions & 0 deletions src/backend/v3/models/messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ class UserClarificationResponse:
"""Response for user clarification from the frontend."""
request_id: str
answer: str = ""
plan_id: str = ""
m_plan_id: str = ""

@dataclass(slots=True)
class FinalResultMessage:
Expand Down
16 changes: 9 additions & 7 deletions src/frontend/src/api/apiService.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -290,14 +290,16 @@ export class APIService {
* @returns Promise with response object
*/
async submitClarification(
planId: string,
sessionId: string,
clarification: string
request_id: string = "",
answer: string = "",
plan_id: string = "",
m_plan_id: string = ""
): Promise<{ status: string; session_id: string }> {
const clarificationData: HumanClarification = {
plan_id: planId,
session_id: sessionId,
human_clarification: clarification
request_id,
answer,
plan_id,
m_plan_id
};

const response = await apiClient.post(
Expand All @@ -306,7 +308,7 @@ export class APIService {
);

// Invalidate cached data
this._cache.invalidate(new RegExp(`^(plan|steps)_${planId}`));
this._cache.invalidate(new RegExp(`^(plan|steps)_${plan_id}`));
this._cache.invalidate(new RegExp(`^plans_`));

return response;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,8 @@ import ReactMarkdown from "react-markdown";
import remarkGfm from "remark-gfm";
import rehypePrism from "rehype-prism";

interface StreamingAgentMessageProps {
agentMessages: AgentMessageData[];
}

const StreamingAgentMessage = ({ agentMessages }: StreamingAgentMessageProps) => {
const StreamingAgentMessage = (agentMessages: AgentMessageData[]) => {
if (!agentMessages?.length) return null;

// Filter out messages with empty content
Expand Down
5 changes: 4 additions & 1 deletion src/frontend/src/models/agentMessage.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { Agent } from 'http';
import { BaseModel } from './plan';
import { AgentMessageType, AgentType } from './enums';

/**
* Represents a message from an agent
Expand All @@ -20,9 +22,10 @@ export interface AgentMessage extends BaseModel {

export interface AgentMessageData {
agent: string;
agent_type: AgentMessageType;
timestamp: number;
steps: any[]; // intentionally always empty
next_steps: []; // intentionally always empty
raw_content: string;
raw_data: string;
}
}
5 changes: 5 additions & 0 deletions src/frontend/src/models/enums.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -250,4 +250,9 @@ export enum WebsocketMessageType {
USER_CLARIFICATION_REQUEST = "user_clarification_request",
USER_CLARIFICATION_RESPONSE = "user_clarification_response",
FINAL_RESULT_MESSAGE = "final_result_message"
}

export enum AgentMessageType {
HUMAN_AGENT = "Human_Agent",
AI_AGENT = "AI_Agent",
}
14 changes: 9 additions & 5 deletions src/frontend/src/models/messages.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,10 @@ export interface HumanFeedback {
* Message containing human clarification on a plan
*/
export interface HumanClarification {
/** Plan identifier */
request_id: string;
answer: string;
plan_id: string;
/** Session identifier */
session_id: string;
/** Clarification from human */
human_clarification: string;
m_plan_id: string;
}

/**
Expand Down Expand Up @@ -168,4 +166,10 @@ export interface ParsedPlanApprovalRequest {
plan_id: string;
parsedData: MPlanData;
rawData: string;
}

export interface ParsedUserClarification {
type: WebsocketMessageType.USER_CLARIFICATION_REQUEST;
question: string;
request_id: string;
}
83 changes: 68 additions & 15 deletions src/frontend/src/pages/PlanPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React, { useCallback, useEffect, useRef, useState, useMemo } from "react"
import { useParams, useNavigate } from "react-router-dom";
import { Spinner, Text } from "@fluentui/react-components";
import { PlanDataService } from "../services/PlanDataService";
import { ProcessedPlanData, PlanWithSteps, WebsocketMessageType, MPlanData, AgentMessageData } from "../models";
import { ProcessedPlanData, PlanWithSteps, WebsocketMessageType, MPlanData, AgentMessageData, AgentMessageType, ParsedUserClarification } from "../models";
import PlanChat from "../components/content/PlanChat";
import PlanPanelRight from "../components/content/PlanPanelRight";
import PlanPanelLeft from "../components/content/PlanPanelLeft";
Expand Down Expand Up @@ -42,8 +42,9 @@ const PlanPage: React.FC = () => {
const [planData, setPlanData] = useState<ProcessedPlanData | any>(null);
const [allPlans, setAllPlans] = useState<ProcessedPlanData[]>([]);
const [loading, setLoading] = useState<boolean>(true);
const [submittingChatDisableInput, setSubmitting] = useState<boolean>(false);
const [submittingChatDisableInput, setSubmittingChatDisableInput] = useState<boolean>(true);
const [error, setError] = useState<Error | null>(null);
const [clarificationMessage, setClarificationMessage] = useState<ParsedUserClarification | null>(null);

const [planApprovalRequest, setPlanApprovalRequest] = useState<MPlanData | null>(null);
const [reloadLeftList, setReloadLeftList] = useState(true);
Expand All @@ -52,8 +53,7 @@ const PlanPage: React.FC = () => {
const [wsConnected, setWsConnected] = useState(false);
const [streamingMessages, setStreamingMessages] = useState<StreamingPlanUpdate[]>([]);
const [streamingMessageBuffer, setStreamingMessageBuffer] = useState<string>("");
// RAI Error state
const [raiError, setRAIError] = useState<RAIErrorData | null>(null);


const [agentMessages, setAgentMessages] = useState<AgentMessageData[]>([]);
// Team config state
Expand Down Expand Up @@ -137,6 +137,19 @@ const PlanPage: React.FC = () => {
useEffect(() => {
const unsubscribe = webSocketService.on(WebsocketMessageType.USER_CLARIFICATION_REQUEST, (clarificationMessage: any) => {
console.log('📋 Clarification Message', clarificationMessage);
const agentMessageData = {
agent: 'ProxyAgent',
agent_type: AgentMessageType.AI_AGENT,
timestamp: clarificationMessage.timestamp || Date.now(),
steps: [], // intentionally always empty
next_steps: [], // intentionally always empty
raw_content: clarificationMessage.data.question || '',
raw_data: clarificationMessage.data || '',
} as AgentMessageData;
console.log('✅ Parsed clarification message:', agentMessageData);
setClarificationMessage(clarificationMessage.data as ParsedUserClarification | null);
setAgentMessages(prev => [...prev, agentMessageData]);
setSubmittingChatDisableInput(false);
scrollToBottom();

});
Expand All @@ -154,6 +167,31 @@ const PlanPage: React.FC = () => {
return () => unsubscribe();
}, [scrollToBottom]);


//WebsocketMessageType.FINAL_RESULT_MESSAGE
useEffect(() => {
const unsubscribe = webSocketService.on(WebsocketMessageType.FINAL_RESULT_MESSAGE, (finalMessage: any) => {
console.log('📋 Final Result Message', finalMessage);
const agentMessageData = {
agent: 'ProxyAgent',
agent_type: AgentMessageType.AI_AGENT,
timestamp: Date.now(),
steps: [], // intentionally always empty
next_steps: [], // intentionally always empty
raw_content: finalMessage.content || '',
raw_data: finalMessage || '',
} as AgentMessageData;
console.log('✅ Parsed final result message:', agentMessageData);
setAgentMessages(prev => [...prev, agentMessageData]);
setSubmittingChatDisableInput(true);
scrollToBottom();

});

return () => unsubscribe();
}, [scrollToBottom]);


//WebsocketMessageType.AGENT_MESSAGE
useEffect(() => {
const unsubscribe = webSocketService.on(WebsocketMessageType.AGENT_MESSAGE, (agentMessage: any) => {
Expand Down Expand Up @@ -325,24 +363,39 @@ const PlanPage: React.FC = () => {
return;
}
setInput("");
setRAIError(null); // Clear any previous RAI errors

if (!planData?.plan) return;
setSubmitting(true);
setSubmittingChatDisableInput(true);
let id = showToast("Submitting clarification", "progress");

try {
// Use legacy method for non-v3 backends
await PlanDataService.submitClarification(
planData.plan.id,
planData.plan.session_id,
chatInput
);

const response = await PlanDataService.submitClarification({
request_id: clarificationMessage?.request_id || "",
answer: chatInput,
plan_id: planData?.plan.id,
m_plan_id: planApprovalRequest?.id || ""
});

console.log("Clarification submitted successfully:", response);
setInput("");
dismissToast(id);
showToast("Clarification submitted successfully", "success");
await loadPlanData(false);
setClarificationMessage(null);
const agentMessageData = {
agent: 'You',
agent_type: AgentMessageType.HUMAN_AGENT,
timestamp: Date.now(),
steps: [], // intentionally always empty
next_steps: [], // intentionally always empty
raw_content: chatInput || '',
raw_data: chatInput || '',
} as AgentMessageData;

setAgentMessages(prev => [...prev, agentMessageData]);
setSubmittingChatDisableInput(true);
scrollToBottom();

} catch (error: any) {
dismissToast(id);

Expand Down Expand Up @@ -373,7 +426,7 @@ const PlanPage: React.FC = () => {
],
user_action: 'Please modify your input and try again.'
};
setRAIError(raiErrorData);

} else {
// Handle other types of errors
showToast(
Expand All @@ -385,7 +438,7 @@ const PlanPage: React.FC = () => {
);
}
} finally {
setSubmitting(false);
setSubmittingChatDisableInput(false);
}
},
[planData?.plan, showToast, dismissToast, loadPlanData]
Expand Down
Loading
Loading