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
2 changes: 1 addition & 1 deletion src/frontend/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
content="MACAE - Multi-Agent Custom Automation Engine"
/>
<link rel="apple-touch-icon" href="/logo192.png" />
<link rel="manifest" href="/manifest.json" />
<!-- <link rel="manifest" href="/manifest.json" /> -->
<title>Multi-Agent - Custom Automation Engine</title>
</head>
<body>
Expand Down
2 changes: 1 addition & 1 deletion src/frontend/src/api/apiService.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ export class APIService {

if (useCache) {
const cachedPlan = this._cache.get<{ plan_with_steps: PlanWithSteps; messages: PlanMessage[] }>(cacheKey);
//if (cachedPlan) return cachedPlan;
if (cachedPlan) return cachedPlan;

return this._requestTracker.trackRequest(cacheKey, fetcher);
}
Expand Down
8 changes: 7 additions & 1 deletion src/frontend/src/components/content/TaskDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,13 @@ const TaskDetails: React.FC<TaskDetailsProps> = ({
</div>
</div>
<div>
<Body1Strong>{planData.plan.initial_goal}</Body1Strong>
<Tooltip content={planData.plan.initial_goal} relationship={"label"}>
<Body1Strong
className="goal-text"
>
{planData.plan.initial_goal}
</Body1Strong>
</Tooltip>
<br />
<Text size={200}>
{completedCount} of {total} completed
Expand Down
7 changes: 4 additions & 3 deletions src/frontend/src/components/content/TaskList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,12 @@ const TaskList: React.FC<TaskListProps> = ({
<div className="task-name-truncated" title={task.name}>
{task.name}
</div>


{task.date && (
{task.date && task.status == "completed" &&(
<Caption1 className="task-list-task-date">{task.date}</Caption1>
)}
{task.status == "inprogress" &&(
<Caption1 className="task-list-task-date">{`${task?.completed_steps} of ${task?.total_steps} completed`}</Caption1>
)}
</div>
<Menu>
<MenuTrigger>
Expand Down
2 changes: 2 additions & 0 deletions src/frontend/src/models/taskList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ export interface Task {
name: string;
status: 'inprogress' | 'completed';
date?: string;
completed_steps?: number;
total_steps?: number;
}

export interface TaskListProps {
Expand Down
4 changes: 2 additions & 2 deletions src/frontend/src/pages/PlanPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ const PlanPage: React.FC = () => {
}

setError(null);
const data = await PlanDataService.fetchPlanData(planId);
const data = await PlanDataService.fetchPlanData(planId,navigate);
console.log("Fetched plan data:", data);
setPlanData(data);
} catch (err) {
Expand Down Expand Up @@ -146,7 +146,7 @@ const PlanPage: React.FC = () => {


useEffect(() => {
loadPlanData();
loadPlanData(true);
}, [loadPlanData]);

const handleNewTaskButton = () => {
Expand Down
259 changes: 142 additions & 117 deletions src/frontend/src/services/PlanDataService.tsx
Original file line number Diff line number Diff line change
@@ -1,135 +1,160 @@
import { PlanWithSteps, Step, AgentType, ProcessedPlanData, PlanMessage } from '@/models';
import { apiService } from '@/api';

import {
PlanWithSteps,
Step,
AgentType,
ProcessedPlanData,
PlanMessage,
} from "@/models";
import { apiService } from "@/api";

/**
* Service for processing and managing plan data operations
*/
export class PlanDataService { /**
* Fetch plan details by plan ID and process the data
* @param planId Plan ID to fetch
* @returns Promise with processed plan data
*/
static async fetchPlanData(planId: string): Promise<ProcessedPlanData> {
try {
// Use optimized getPlanById method for better performance
const planBody = await apiService.getPlanById(planId);
return this.processPlanData(planBody.plan_with_steps, planBody.messages || []);
} catch (error) {
console.log('Failed to fetch plan data:', error);
throw error;
}
export class PlanDataService {
/**
* Fetch plan details by plan ID and process the data
* @param planId Plan ID to fetch
* @returns Promise with processed plan data
*/
static async fetchPlanData(
planId: string,
useCache: boolean
): Promise<ProcessedPlanData> {
try {
// Use optimized getPlanById method for better performance
const planBody = await apiService.getPlanById(planId, useCache);
return this.processPlanData(
planBody.plan_with_steps,
planBody.messages || []
);
} catch (error) {
console.log("Failed to fetch plan data:", error);
throw error;
}
}

/**
* Process plan data to extract agents, steps, and clarification status
* @param plan PlanWithSteps object to process
* @returns Processed plan data
*/
static processPlanData(plan: PlanWithSteps, messages: PlanMessage[]): ProcessedPlanData {
// Extract unique agents from steps
const uniqueAgents = new Set<AgentType>();
plan.steps.forEach(step => {
if (step.agent) {
uniqueAgents.add(step.agent);
}
});
/**
* Process plan data to extract agents, steps, and clarification status
* @param plan PlanWithSteps object to process
* @returns Processed plan data
*/
static processPlanData(
plan: PlanWithSteps,
messages: PlanMessage[]
): ProcessedPlanData {
// Extract unique agents from steps
const uniqueAgents = new Set<AgentType>();
plan.steps.forEach((step) => {
if (step.agent) {
uniqueAgents.add(step.agent);
}
});

// Convert Set to Array for easier handling
const agents = Array.from(uniqueAgents);
// Convert Set to Array for easier handling
const agents = Array.from(uniqueAgents);

// Get all steps
const steps = plan.steps;
// Get all steps
const steps = plan.steps;

// Check if human_clarification_request is not null
const hasClarificationRequest = plan.human_clarification_request != null && plan.human_clarification_request.trim().length > 0;
const hasClarificationResponse = plan.human_clarification_response != null && plan.human_clarification_response.trim().length > 0;
const enableChat = hasClarificationRequest && !hasClarificationResponse;
const enableStepButtons = (hasClarificationRequest && hasClarificationResponse) || (!hasClarificationRequest && !hasClarificationResponse);
return {
plan,
agents,
steps,
hasClarificationRequest,
hasClarificationResponse,
enableChat,
enableStepButtons,
messages
};
}
// Check if human_clarification_request is not null
const hasClarificationRequest =
plan.human_clarification_request != null &&
plan.human_clarification_request.trim().length > 0;
const hasClarificationResponse =
plan.human_clarification_response != null &&
plan.human_clarification_response.trim().length > 0;
const enableChat = hasClarificationRequest && !hasClarificationResponse;
const enableStepButtons =
(hasClarificationRequest && hasClarificationResponse) ||
(!hasClarificationRequest && !hasClarificationResponse);
return {
plan,
agents,
steps,
hasClarificationRequest,
hasClarificationResponse,
enableChat,
enableStepButtons,
messages,
};
}

/**
* Get steps for a specific agent type
* @param plan Plan with steps
* @param agentType Agent type to filter by
* @returns Array of steps for the specified agent
*/
static getStepsForAgent(plan: PlanWithSteps, agentType: AgentType): Step[] {
return apiService.getStepsForAgent(plan, agentType);
}
/**
* Get steps for a specific agent type
* @param plan Plan with steps
* @param agentType Agent type to filter by
* @returns Array of steps for the specified agent
*/
static getStepsForAgent(plan: PlanWithSteps, agentType: AgentType): Step[] {
return apiService.getStepsForAgent(plan, agentType);
}

/**
* Get steps that are awaiting human feedback
* @param plan Plan with steps
* @returns Array of steps awaiting feedback
*/
static getStepsAwaitingFeedback(plan: PlanWithSteps): Step[] {
return apiService.getStepsAwaitingFeedback(plan);
}
/**
* Get steps that are awaiting human feedback
* @param plan Plan with steps
* @returns Array of steps awaiting feedback
*/
static getStepsAwaitingFeedback(plan: PlanWithSteps): Step[] {
return apiService.getStepsAwaitingFeedback(plan);
}

/**
* Check if plan is complete
* @param plan Plan with steps
* @returns Boolean indicating if plan is complete
*/
static isPlanComplete(plan: PlanWithSteps): boolean {
return apiService.isPlanComplete(plan);
}
/**
* Check if plan is complete
* @param plan Plan with steps
* @returns Boolean indicating if plan is complete
*/
static isPlanComplete(plan: PlanWithSteps): boolean {
return apiService.isPlanComplete(plan);
}

/**
* Get plan completion percentage
* @param plan Plan with steps
* @returns Completion percentage (0-100)
*/
static getPlanCompletionPercentage(plan: PlanWithSteps): number {
return apiService.getPlanCompletionPercentage(plan);
}

/**
* Approve a plan step
* @param step Step to approve
* @returns Promise with API response
*/
static async stepStatus(step: Step, action: boolean): Promise<{ status: string }> {
try {
/**
* Get plan completion percentage
* @param plan Plan with steps
* @returns Completion percentage (0-100)
*/
static getPlanCompletionPercentage(plan: PlanWithSteps): number {
return apiService.getPlanCompletionPercentage(plan);
}


return apiService.stepStatus(
step.plan_id,
step.session_id,
action, // approved
step.id
);
} catch (error) {
console.log('Failed to change step status:', error);
throw error;
}
/**
* Approve a plan step
* @param step Step to approve
* @returns Promise with API response
*/
static async stepStatus(
step: Step,
action: boolean
): Promise<{ status: string }> {
try {
return apiService.stepStatus(
step.plan_id,
step.session_id,
action, // approved
step.id
);
} catch (error) {
console.log("Failed to change step status:", error);
throw error;
}
}


/**
* Submit human clarification for a plan
* @param planId Plan ID
* @param sessionId Session ID
* @param clarification Clarification text
* @returns Promise with API response
*/
static async submitClarification(planId: string, sessionId: string, clarification: string) {
try {
return apiService.submitClarification(planId, sessionId, clarification);
} catch (error) {
console.log('Failed to submit clarification:', error);
throw error;
}
/**
* Submit human clarification for a plan
* @param planId Plan ID
* @param sessionId Session ID
* @param clarification Clarification text
* @returns Promise with API response
*/
static async submitClarification(
planId: string,
sessionId: string,
clarification: string
) {
try {
return apiService.submitClarification(planId, sessionId, clarification);
} catch (error) {
console.log("Failed to submit clarification:", error);
throw error;
}
}
}
2 changes: 2 additions & 0 deletions src/frontend/src/services/TaskService.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ export class TaskService {
const task: Task = {
id: plan.session_id,
name: plan.initial_goal,
completed_steps: plan.completed,
total_steps: plan.total_steps,
status: apiService.isPlanComplete(plan) ? 'completed' : 'inprogress',
date: new Date(plan.timestamp).toLocaleDateString('en-US', {
month: 'short',
Expand Down
10 changes: 10 additions & 0 deletions src/frontend/src/styles/TaskDetails.css
Original file line number Diff line number Diff line change
Expand Up @@ -146,4 +146,14 @@
}
.strikethrough {
text-decoration: line-through;
}

.goal-text {
display: -webkit-box !important;
-webkit-line-clamp: 2 !important;
-webkit-box-orient: vertical !important;
overflow: hidden !important;
text-overflow: ellipsis !important;
white-space: normal !important;
max-width: 300px;
}
Loading