mirror of
https://github.com/eigent-ai/eigent.git
synced 2026-05-31 21:39:28 +00:00
enhance: refine the task trigger flow in replay, and normal flow
This commit is contained in:
parent
559615d4f0
commit
b71d5decc4
4 changed files with 180 additions and 114 deletions
|
|
@ -119,7 +119,11 @@ export default function ChatBox(): JSX.Element {
|
|||
|
||||
const navigate = useNavigate();
|
||||
|
||||
const handleSend = async (messageStr?: string, taskId?: string) => {
|
||||
const handleSend = async (
|
||||
messageStr?: string,
|
||||
taskId?: string,
|
||||
executionId?: string
|
||||
) => {
|
||||
const _taskId = taskId || chatStore.activeTaskId;
|
||||
if (message.trim() === '' && !messageStr) return;
|
||||
const tempMessageContent = messageStr || message;
|
||||
|
|
@ -254,7 +258,8 @@ export default function ChatBox(): JSX.Element {
|
|||
undefined,
|
||||
undefined,
|
||||
tempMessageContent,
|
||||
attachesToSend
|
||||
attachesToSend,
|
||||
executionId
|
||||
);
|
||||
} catch (err: any) {
|
||||
console.error('Failed to start task:', err);
|
||||
|
|
@ -274,6 +279,7 @@ export default function ChatBox(): JSX.Element {
|
|||
//Generate nextId in case new chatStore is created to sync with the backend beforehand
|
||||
const nextTaskId = generateUniqueId();
|
||||
chatStore.setNextTaskId(nextTaskId);
|
||||
chatStore.setNextExecutionId(taskId as string, executionId);
|
||||
|
||||
// Use improve endpoint (POST /chat/{id}) - {id} is project_id
|
||||
// This reuses the existing SSE connection and step_solve loop
|
||||
|
|
@ -329,7 +335,8 @@ export default function ChatBox(): JSX.Element {
|
|||
undefined,
|
||||
undefined,
|
||||
tempMessageContent,
|
||||
attachesToSend
|
||||
attachesToSend,
|
||||
executionId
|
||||
);
|
||||
chatStore.setHasWaitComfirm(_taskId as string, true);
|
||||
} catch (err: any) {
|
||||
|
|
@ -784,7 +791,7 @@ export default function ChatBox(): JSX.Element {
|
|||
// Dequeue the task from triggerTaskStore (marks as running)
|
||||
taskToProcess = triggerTaskStoreState.dequeueTask();
|
||||
if (!taskToProcess) return;
|
||||
|
||||
|
||||
// Skip if we've already started processing this trigger task
|
||||
if (processedTriggerTaskRef.current === taskToProcess.id) {
|
||||
console.log(
|
||||
|
|
@ -803,8 +810,7 @@ export default function ChatBox(): JSX.Element {
|
|||
// Mark this trigger task as being processed to prevent duplicate sends
|
||||
processedTriggerTaskRef.current = taskToProcess.id;
|
||||
|
||||
// Register execution mapping for this task
|
||||
// The executionId from the queued task will be used for tracking
|
||||
// Register execution mapping for this task (for external tracking)
|
||||
if (taskToProcess.executionId && projectStore.activeProjectId) {
|
||||
const targetTaskId = chatStore.nextTaskId || taskId;
|
||||
|
||||
|
|
@ -817,10 +823,10 @@ export default function ChatBox(): JSX.Element {
|
|||
}
|
||||
|
||||
// Process the queued message via handleSend
|
||||
// Use the formattedMessage from the trigger task
|
||||
// Pass executionId so startTask can set it on the new task
|
||||
const messageContent =
|
||||
taskToProcess.formattedMessage || taskToProcess.taskPrompt;
|
||||
handleSend(messageContent);
|
||||
handleSend(messageContent, undefined, taskToProcess.executionId);
|
||||
}, [
|
||||
projectStore.activeProjectId,
|
||||
triggerTaskStoreState.taskQueue,
|
||||
|
|
@ -1088,7 +1094,7 @@ export default function ChatBox(): JSX.Element {
|
|||
href="https://www.eigent.ai/terms-of-use"
|
||||
target="_blank"
|
||||
className="text-text-information underline"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
onClick={(e) => e.stopPropagation()} rel="noreferrer"
|
||||
>
|
||||
{t('layout.terms-of-use')}
|
||||
</a>{' '}
|
||||
|
|
@ -1097,7 +1103,7 @@ export default function ChatBox(): JSX.Element {
|
|||
href="https://www.eigent.ai/privacy-policy"
|
||||
target="_blank"
|
||||
className="text-text-information underline"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
onClick={(e) => e.stopPropagation()} rel="noreferrer"
|
||||
>
|
||||
{t('layout.privacy-policy')}
|
||||
</a>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { proxyFetchGet } from '@/api/http';
|
||||
import { useProjectStore } from '@/store/projectStore';
|
||||
import { ProjectType, useProjectStore } from '@/store/projectStore';
|
||||
import { useTriggerStore } from '@/store/triggerStore';
|
||||
import {
|
||||
TriggeredTask,
|
||||
|
|
@ -87,7 +87,14 @@ export function useTriggerTaskExecutor() {
|
|||
});
|
||||
|
||||
// Use replayProject to load the project from history
|
||||
store.replayProject(taskIdsList, question, projectId, historyId);
|
||||
// store.replayProject(taskIdsList, question, projectId, historyId);
|
||||
store.createProject(
|
||||
`Trigger Project ${question}`,
|
||||
`No tasks to replay`,
|
||||
projectId,
|
||||
ProjectType.NORMAL,
|
||||
historyId
|
||||
);
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ import {
|
|||
import { showCreditsToast } from '@/components/Toast/creditsToast';
|
||||
import { showStorageToast } from '@/components/Toast/storageToast';
|
||||
import { generateUniqueId, uploadLog } from '@/lib';
|
||||
import { proxyUpdateTriggerExecution } from '@/service/triggerApi';
|
||||
import { ExecutionStatus } from '@/types';
|
||||
import {
|
||||
AgentMessageStatus,
|
||||
|
|
@ -77,6 +78,9 @@ interface Task {
|
|||
isContextExceeded?: boolean;
|
||||
// Streaming decompose text - stored separately to avoid frequent re-renders
|
||||
streamingDecomposeText: string;
|
||||
// Trigger execution ID for tracking trigger task completion
|
||||
executionId?: string;
|
||||
nextExecutionId?: string;
|
||||
}
|
||||
|
||||
export interface ChatStore {
|
||||
|
|
@ -96,7 +100,8 @@ export interface ChatStore {
|
|||
shareToken?: string,
|
||||
delayTime?: number,
|
||||
messageContent?: string,
|
||||
messageAttaches?: File[]
|
||||
messageAttaches?: File[],
|
||||
executionId?: string
|
||||
) => Promise<void>;
|
||||
handleConfirmTask: (
|
||||
project_id: string,
|
||||
|
|
@ -170,6 +175,11 @@ export interface ChatStore {
|
|||
setNextTaskId: (taskId: string | null) => void;
|
||||
setStreamingDecomposeText: (taskId: string, text: string) => void;
|
||||
clearStreamingDecomposeText: (taskId: string) => void;
|
||||
setExecutionId: (taskId: string, executionId: string | undefined) => void;
|
||||
setNextExecutionId: (
|
||||
taskId: string,
|
||||
nextExecutionId: string | undefined
|
||||
) => void;
|
||||
}
|
||||
|
||||
export type VanillaChatStore = {
|
||||
|
|
@ -228,23 +238,95 @@ const ttftTracking: Record<
|
|||
{ confirmedAt: number; firstTokenLogged: boolean }
|
||||
> = {};
|
||||
|
||||
// Helper function to update trigger execution status using triggerTaskStore
|
||||
// Track which executionIds have already been reported to prevent duplicate updates
|
||||
const reportedExecutionIds = new Set<string>();
|
||||
|
||||
// Helper function to update trigger execution status using executionId from task
|
||||
const updateTriggerExecutionStatus = async (
|
||||
_projectStore: any,
|
||||
_project_id: string | null | undefined,
|
||||
chatStoreState: ChatStore,
|
||||
projectId: string | null | undefined,
|
||||
currentTaskId: string,
|
||||
status: import('@/types').ExecutionStatus,
|
||||
tokens: number,
|
||||
errorMessage?: string
|
||||
) => {
|
||||
// Use triggerTaskStore for reliable execution status tracking
|
||||
const triggerTaskStore = useTriggerTaskStore.getState();
|
||||
await triggerTaskStore.updateExecutionStatus(
|
||||
console.log('[updateTriggerExecutionStatus] Called with:', {
|
||||
projectId,
|
||||
currentTaskId,
|
||||
status,
|
||||
tokens,
|
||||
errorMessage
|
||||
);
|
||||
});
|
||||
|
||||
// Get executionId directly from the task
|
||||
const executionId = chatStoreState.tasks[currentTaskId]?.executionId;
|
||||
|
||||
if (!executionId) {
|
||||
// No executionId means this is not a trigger-initiated task, skip silently
|
||||
console.log(
|
||||
'[updateTriggerExecutionStatus] No executionId found for task:',
|
||||
currentTaskId,
|
||||
'- skipping (not a trigger-initiated task)'
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if this execution has already been reported
|
||||
if (reportedExecutionIds.has(executionId)) {
|
||||
console.log(
|
||||
'[updateTriggerExecutionStatus] Execution already reported:',
|
||||
executionId
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Mark as reported to prevent duplicate updates
|
||||
reportedExecutionIds.add(executionId);
|
||||
|
||||
// Call the API to update execution status
|
||||
await proxyUpdateTriggerExecution(
|
||||
executionId,
|
||||
{
|
||||
status,
|
||||
completed_at: new Date().toISOString(),
|
||||
...(errorMessage && { error_message: errorMessage }),
|
||||
tokens_used: tokens,
|
||||
},
|
||||
{ projectId: projectId || undefined }
|
||||
);
|
||||
|
||||
console.log(
|
||||
'[updateTriggerExecutionStatus] Execution status updated:',
|
||||
executionId,
|
||||
'->',
|
||||
status
|
||||
);
|
||||
|
||||
// Complete or fail the current trigger task in triggerTaskStore
|
||||
const triggerTaskStore = useTriggerTaskStore.getState();
|
||||
const currentTask = triggerTaskStore.currentTask;
|
||||
|
||||
if (currentTask && currentTask.executionId === executionId) {
|
||||
if (status === ExecutionStatus.Completed) {
|
||||
triggerTaskStore.completeTask(currentTask.id);
|
||||
} else if (
|
||||
status === ExecutionStatus.Failed ||
|
||||
status === ExecutionStatus.Cancelled
|
||||
) {
|
||||
triggerTaskStore.failTask(
|
||||
currentTask.id,
|
||||
errorMessage || 'Task failed'
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn(
|
||||
`[updateTriggerExecutionStatus] Failed to update execution status to ${status}:`,
|
||||
err
|
||||
);
|
||||
// Remove from reported set so it can be retried
|
||||
reportedExecutionIds.delete(executionId);
|
||||
}
|
||||
};
|
||||
|
||||
const chatStore = (initial?: Partial<ChatStore>) =>
|
||||
|
|
@ -292,6 +374,7 @@ const chatStore = (initial?: Partial<ChatStore>) =>
|
|||
isTakeControl: false,
|
||||
isTaskEdit: false,
|
||||
streamingDecomposeText: '',
|
||||
executionId: undefined,
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
|
@ -426,7 +509,8 @@ const chatStore = (initial?: Partial<ChatStore>) =>
|
|||
shareToken?: string,
|
||||
delayTime?: number,
|
||||
messageContent?: string,
|
||||
messageAttaches?: File[]
|
||||
messageAttaches?: File[],
|
||||
executionId?: string
|
||||
) => {
|
||||
// ✅ Wait for backend to be ready before starting task (except for replay/share)
|
||||
if (!type || type === 'normal') {
|
||||
|
|
@ -483,6 +567,11 @@ const chatStore = (initial?: Partial<ChatStore>) =>
|
|||
targetChatStore = newChatResult.chatStore;
|
||||
targetChatStore.getState().setIsPending(newTaskId, true);
|
||||
|
||||
// Set executionId if this is a trigger-initiated task
|
||||
if (executionId) {
|
||||
targetChatStore.getState().setExecutionId(newTaskId, executionId);
|
||||
}
|
||||
|
||||
//From handleSend if message is given
|
||||
// Add the message to the new chatStore if provided
|
||||
if (messageContent) {
|
||||
|
|
@ -850,6 +939,16 @@ const chatStore = (initial?: Partial<ChatStore>) =>
|
|||
updateLockedReferences(newChatStore, newTaskId);
|
||||
newChatStore.getState().setIsPending(newTaskId, false);
|
||||
|
||||
// If nextExecutionId exists, pass it to new task
|
||||
if (previousChatStore.tasks[currentTaskId]?.nextExecutionId) {
|
||||
newChatStore
|
||||
.getState()
|
||||
.setExecutionId(
|
||||
newTaskId,
|
||||
previousChatStore.tasks[currentTaskId]?.nextExecutionId
|
||||
);
|
||||
}
|
||||
|
||||
if (type === 'replay') {
|
||||
newChatStore
|
||||
.getState()
|
||||
|
|
@ -918,7 +1017,7 @@ const chatStore = (initial?: Partial<ChatStore>) =>
|
|||
const currentTaskId = getCurrentTaskId();
|
||||
// Update trigger execution status to Completed for connection closed by server
|
||||
updateTriggerExecutionStatus(
|
||||
projectStore,
|
||||
getCurrentChatStore(),
|
||||
project_id,
|
||||
currentTaskId,
|
||||
ExecutionStatus.Running,
|
||||
|
|
@ -1268,6 +1367,17 @@ const chatStore = (initial?: Partial<ChatStore>) =>
|
|||
step: AgentStep.WAIT_CONFIRM,
|
||||
isConfirm: false,
|
||||
});
|
||||
|
||||
// Update trigger execution status to Completed for simple question/answer flow
|
||||
// This handles cases where the task ends with wait_confirm instead of the end step
|
||||
updateTriggerExecutionStatus(
|
||||
currentChatStore,
|
||||
project_id,
|
||||
currentTaskId,
|
||||
ExecutionStatus.Completed,
|
||||
currentChatStore.tasks[currentTaskId]?.tokens || 0
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
// Task State
|
||||
|
|
@ -1465,7 +1575,7 @@ const chatStore = (initial?: Partial<ChatStore>) =>
|
|||
) {
|
||||
// This is a quick reply - update trigger execution status to Completed
|
||||
updateTriggerExecutionStatus(
|
||||
projectStore,
|
||||
getCurrentChatStore(),
|
||||
project_id,
|
||||
currentTaskId,
|
||||
ExecutionStatus.Completed,
|
||||
|
|
@ -1984,7 +2094,7 @@ const chatStore = (initial?: Partial<ChatStore>) =>
|
|||
uploadLog(currentTaskId, type);
|
||||
// Update trigger execution status to Failed on error
|
||||
updateTriggerExecutionStatus(
|
||||
projectStore,
|
||||
getCurrentChatStore(),
|
||||
project_id,
|
||||
currentTaskId,
|
||||
ExecutionStatus.Failed,
|
||||
|
|
@ -2296,7 +2406,7 @@ const chatStore = (initial?: Partial<ChatStore>) =>
|
|||
|
||||
// Update trigger execution status to Completed
|
||||
updateTriggerExecutionStatus(
|
||||
projectStore,
|
||||
getCurrentChatStore(),
|
||||
project_id,
|
||||
currentTaskId,
|
||||
ExecutionStatus.Completed,
|
||||
|
|
@ -2420,7 +2530,7 @@ const chatStore = (initial?: Partial<ChatStore>) =>
|
|||
const currentTaskId = getCurrentTaskId();
|
||||
// Update trigger execution status to Completed for connection closed by server
|
||||
updateTriggerExecutionStatus(
|
||||
projectStore,
|
||||
getCurrentChatStore(),
|
||||
project_id,
|
||||
currentTaskId,
|
||||
ExecutionStatus.Cancelled,
|
||||
|
|
@ -3235,6 +3345,36 @@ const chatStore = (initial?: Partial<ChatStore>) =>
|
|||
};
|
||||
});
|
||||
},
|
||||
setExecutionId: (taskId, executionId) => {
|
||||
set((state) => {
|
||||
if (!state.tasks[taskId]) return state;
|
||||
return {
|
||||
...state,
|
||||
tasks: {
|
||||
...state.tasks,
|
||||
[taskId]: {
|
||||
...state.tasks[taskId],
|
||||
executionId,
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
},
|
||||
setNextExecutionId: (taskId, nextExecutionId) => {
|
||||
set((state) => {
|
||||
if (!state.tasks[taskId]) return state;
|
||||
return {
|
||||
...state,
|
||||
tasks: {
|
||||
...state.tasks,
|
||||
[taskId]: {
|
||||
...state.tasks[taskId],
|
||||
nextExecutionId,
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
},
|
||||
}));
|
||||
|
||||
const filterMessage = (message: AgentMessage) => {
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@
|
|||
// limitations under the License.
|
||||
// ========= Copyright 2025-2026 @ Eigent.ai All Rights Reserved. =========
|
||||
|
||||
import { proxyUpdateTriggerExecution } from '@/service/triggerApi';
|
||||
import { ExecutionStatus, TriggerType } from '@/types';
|
||||
import { create } from 'zustand';
|
||||
|
||||
|
|
@ -224,15 +223,6 @@ interface TriggerTaskStore {
|
|||
getExecutionMapping: (chatTaskId: string) => ExecutionMapping | undefined;
|
||||
/** Remove execution mapping */
|
||||
removeExecutionMapping: (chatTaskId: string) => void;
|
||||
/** Update execution status on the backend */
|
||||
updateExecutionStatus: (
|
||||
chatTaskId: string,
|
||||
status: ExecutionStatus,
|
||||
tokens: number,
|
||||
errorMessage?: string
|
||||
) => Promise<void>;
|
||||
/** Check if a task has been reported */
|
||||
isExecutionReported: (chatTaskId: string) => boolean;
|
||||
}
|
||||
|
||||
export const useTriggerTaskStore = create<TriggerTaskStore>((set, get) => ({
|
||||
|
|
@ -443,81 +433,4 @@ export const useTriggerTaskStore = create<TriggerTaskStore>((set, get) => ({
|
|||
return { executionMappings: newMappings };
|
||||
});
|
||||
},
|
||||
|
||||
updateExecutionStatus: async (
|
||||
chatTaskId: string,
|
||||
status: ExecutionStatus,
|
||||
tokens: number,
|
||||
errorMessage?: string
|
||||
) => {
|
||||
const { executionMappings, removeExecutionMapping, completeTask, failTask } = get();
|
||||
const mapping = executionMappings.get(chatTaskId);
|
||||
|
||||
if (!mapping) {
|
||||
console.warn(
|
||||
'[TriggerTaskStore] No execution mapping found for chat task:',
|
||||
chatTaskId
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mapping.reported) {
|
||||
console.log(
|
||||
'[TriggerTaskStore] Execution already reported for chat task:',
|
||||
chatTaskId
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await proxyUpdateTriggerExecution(
|
||||
mapping.executionId,
|
||||
{
|
||||
status,
|
||||
completed_at: new Date().toISOString(),
|
||||
...(errorMessage && { error_message: errorMessage }),
|
||||
tokens_used: tokens,
|
||||
},
|
||||
{ projectId: mapping.projectId }
|
||||
);
|
||||
|
||||
// Mark as reported
|
||||
set((state) => {
|
||||
const newMappings = new Map(state.executionMappings);
|
||||
const updatedMapping = newMappings.get(chatTaskId);
|
||||
if (updatedMapping) {
|
||||
updatedMapping.reported = true;
|
||||
}
|
||||
return { executionMappings: newMappings };
|
||||
});
|
||||
|
||||
console.log(
|
||||
'[TriggerTaskStore] Execution status updated:',
|
||||
mapping.executionId,
|
||||
'->',
|
||||
status
|
||||
);
|
||||
|
||||
// Complete or fail the trigger task based on status
|
||||
// This moves the task from currentTask to taskHistory
|
||||
if (status === ExecutionStatus.Completed) {
|
||||
completeTask(mapping.triggerTaskId);
|
||||
} else if (status === ExecutionStatus.Failed || status === ExecutionStatus.Cancelled) {
|
||||
failTask(mapping.triggerTaskId, errorMessage || 'Task failed');
|
||||
}
|
||||
|
||||
// Clean up mapping after successful update
|
||||
removeExecutionMapping(chatTaskId);
|
||||
} catch (err) {
|
||||
console.warn(
|
||||
`[TriggerTaskStore] Failed to update execution status to ${status}:`,
|
||||
err
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
isExecutionReported: (chatTaskId: string) => {
|
||||
const mapping = get().executionMappings.get(chatTaskId);
|
||||
return mapping?.reported ?? false;
|
||||
},
|
||||
}));
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue