mirror of
https://github.com/eigent-ai/eigent.git
synced 2026-05-22 03:07:02 +00:00
feat: use proj id for history & replay
This commit is contained in:
parent
407e6822ee
commit
6aeff827aa
7 changed files with 162 additions and 39 deletions
|
|
@ -96,7 +96,7 @@ async def step_solve(options: Chat, request: Request, task_lock: TaskLock):
|
|||
if confirm is not True:
|
||||
yield confirm
|
||||
else:
|
||||
yield sse_json("confirmed", "")
|
||||
yield sse_json("confirmed", {"question": question})
|
||||
(workforce, mcp) = await construct_workforce(options)
|
||||
for new_agent in options.new_agents:
|
||||
workforce.add_single_agent_worker(
|
||||
|
|
|
|||
|
|
@ -28,7 +28,9 @@ def sync_step(func):
|
|||
send_to_api(
|
||||
sync_url,
|
||||
{
|
||||
"task_id": chat.task_id,
|
||||
# TODO: revert to task_id to support multi-task project replay
|
||||
# "task_id": chat.task_id,
|
||||
"task_id": chat.project_id,
|
||||
"step": json_data["step"],
|
||||
"data": json_data["data"],
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { useState, useRef, useEffect, useCallback } from "react";
|
||||
import { useState, useRef, useEffect, useCallback, useMemo } from "react";
|
||||
import { fetchPost, proxyFetchPut, fetchPut, fetchDelete, proxyFetchDelete } from "@/api/http";
|
||||
import BottomBox from "./BottomBox";
|
||||
import { ProjectChatContainer } from "./ProjectChatContainer";
|
||||
|
|
@ -556,12 +556,35 @@ export default function ChatBox(): JSX.Element {
|
|||
console.error(`Can't remove ${task_id} due to ${error}`)
|
||||
}
|
||||
}
|
||||
const getAllChatStoresMemoized = useMemo(() => {
|
||||
const project_id = projectStore.activeProjectId;
|
||||
if(!project_id) return [];
|
||||
|
||||
return projectStore.getAllChatStores(project_id);
|
||||
}, [projectStore, projectStore.activeProjectId, chatStore])
|
||||
|
||||
// Check if any chat store in the project has messages
|
||||
const hasAnyMessages = useMemo(() => {
|
||||
// First check current active chat store
|
||||
if (chatStore.activeTaskId &&
|
||||
(chatStore.tasks[chatStore.activeTaskId].messages.length > 0 ||
|
||||
chatStore.tasks[chatStore.activeTaskId as string]?.hasMessages)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Then check all other chat stores in the project
|
||||
return getAllChatStoresMemoized.some(({chatStore: store}) => {
|
||||
const state = store.getState();
|
||||
return state.activeTaskId &&
|
||||
state.tasks[state.activeTaskId] &&
|
||||
(state.tasks[state.activeTaskId].messages.length > 0 ||
|
||||
state.tasks[state.activeTaskId].hasMessages);
|
||||
});
|
||||
}, [chatStore, getAllChatStoresMemoized]);
|
||||
|
||||
return (
|
||||
<div className="w-full h-full flex flex-col items-center justify-center">
|
||||
{(chatStore.activeTaskId &&
|
||||
chatStore.tasks[chatStore.activeTaskId].messages.length > 0) ||
|
||||
chatStore.tasks[chatStore.activeTaskId as string]?.hasMessages ? (
|
||||
{hasAnyMessages ? (
|
||||
<div className="w-full h-[calc(100vh-54px)] flex flex-col rounded-xl border border-border-disabled border-solid relative shadow-blur-effect overflow-hidden">
|
||||
<div className="absolute inset-0 blur-bg bg-bg-surface-secondary pointer-events-none"></div>
|
||||
|
||||
|
|
|
|||
|
|
@ -148,9 +148,15 @@ export default function HistorySidebar() {
|
|||
fetchHistoryTasks();
|
||||
}, [chatStore.updateCount]);
|
||||
|
||||
const handleReplay = async (taskId: string, question: string) => {
|
||||
const handleReplay = async (projectId: string, question: string, historyId: string) => {
|
||||
close();
|
||||
projectStore.replayProject([taskId], question, projectStore.activeProjectId ?? undefined);
|
||||
/**
|
||||
* TODO(history): For now all replaying is appending to the same instance
|
||||
* of task_id (to be renamed projectId). Later we need to filter task_id from
|
||||
* /api/chat/histories by project_id then feed it here.
|
||||
*/
|
||||
const taskIdsList = [projectId];
|
||||
projectStore.replayProject(taskIdsList, question, projectId, historyId);
|
||||
navigate({ pathname: "/" });
|
||||
};
|
||||
|
||||
|
|
@ -181,16 +187,18 @@ export default function HistorySidebar() {
|
|||
share(taskId);
|
||||
};
|
||||
|
||||
const handleSetActive = (taskId: string, question: string) => {
|
||||
const task = chatStore.tasks[taskId];
|
||||
if (task) {
|
||||
const handleSetActive = (projectId: string, question: string, historyId: string) => {
|
||||
const project = projectStore.getProjectById(projectId);
|
||||
//If project exists
|
||||
if (project) {
|
||||
// if there is record, show result
|
||||
chatStore.setActiveTaskId(taskId);
|
||||
projectStore.setHistoryId(projectId, historyId);
|
||||
projectStore.setActiveProject(projectId)
|
||||
navigate(`/`);
|
||||
close();
|
||||
} else {
|
||||
// if there is no record, execute replay
|
||||
handleReplay(taskId, question);
|
||||
handleReplay(projectId, question, historyId);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -468,7 +476,11 @@ export default function HistorySidebar() {
|
|||
return (
|
||||
<div
|
||||
onClick={() =>
|
||||
handleSetActive(task.task_id, task.question)
|
||||
/**
|
||||
* TODO(history): Update to use project_id field
|
||||
* after update instead.
|
||||
*/
|
||||
handleSetActive(task.task_id, task.question, task.id)
|
||||
}
|
||||
key={task.task_id}
|
||||
className={`${
|
||||
|
|
@ -509,7 +521,11 @@ export default function HistorySidebar() {
|
|||
return (
|
||||
<div
|
||||
onClick={() => {
|
||||
handleSetActive(task.task_id, task.question);
|
||||
/**
|
||||
* TODO(history): Update to use project_id field
|
||||
* after update instead.
|
||||
*/
|
||||
handleSetActive(task.task_id, task.question, task.id);
|
||||
}}
|
||||
key={task.task_id}
|
||||
className={`${
|
||||
|
|
|
|||
|
|
@ -43,21 +43,33 @@ export function SearchHistoryDialog() {
|
|||
}
|
||||
|
||||
const navigate = useNavigate();
|
||||
const handleSetActive = (taskId: string, question: string) => {
|
||||
const task = chatStore.tasks[taskId];
|
||||
if (task) {
|
||||
// if there is a record, show the result
|
||||
chatStore.setActiveTaskId(taskId);
|
||||
const handleSetActive = (projectId: string, question: string, historyId: string) => {
|
||||
const project = projectStore.getProjectById(projectId);
|
||||
//If project exists
|
||||
if (project) {
|
||||
// if there is record, show result
|
||||
projectStore.setHistoryId(projectId, historyId);
|
||||
projectStore.setActiveProject(projectId)
|
||||
navigate(`/`);
|
||||
close();
|
||||
} else {
|
||||
// if there is no record, execute replay
|
||||
handleReplay(taskId, question);
|
||||
handleReplay(projectId, question, historyId);
|
||||
}
|
||||
};
|
||||
const handleReplay = async (taskId: string, question: string) => {
|
||||
projectStore.replayProject([taskId], question, projectStore.activeProjectId ?? undefined);
|
||||
|
||||
const handleReplay = async (projectId: string, question: string, historyId: string) => {
|
||||
close();
|
||||
/**
|
||||
* TODO(history): For now all replaying is appending to the same instance
|
||||
* of task_id (to be renamed projectId). Later we need to filter task_id from
|
||||
* /api/chat/histories by project_id then feed it here.
|
||||
*/
|
||||
const taskIdsList = [projectId];
|
||||
projectStore.replayProject(taskIdsList, question, projectId, historyId);
|
||||
navigate({ pathname: "/" });
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const fetchHistoryTasks = async () => {
|
||||
try {
|
||||
|
|
@ -93,7 +105,11 @@ export function SearchHistoryDialog() {
|
|||
<CommandItem
|
||||
key={task.id}
|
||||
className="cursor-pointer"
|
||||
onSelect={() => handleSetActive(task.task_id, task.question)}
|
||||
/**
|
||||
* TODO(history): Update to use project_id field
|
||||
* after update instead.
|
||||
*/
|
||||
onSelect={() => handleSetActive(task.task_id, task.question, task.id)}
|
||||
>
|
||||
<ScanFace />
|
||||
<div className="overflow-hidden text-ellipsis whitespace-nowrap">
|
||||
|
|
|
|||
|
|
@ -175,20 +175,29 @@ export default function Home() {
|
|||
share(taskId);
|
||||
};
|
||||
|
||||
const handleReplay = async (taskId: string, question: string) => {
|
||||
projectStore.replayProject([taskId], question, projectStore.activeProjectId ?? undefined);
|
||||
const handleReplay = async (projectId: string, question: string, historyId: string) => {
|
||||
/**
|
||||
* TODO(history): For now all replaying is appending to the same instance
|
||||
* of task_id (to be renamed projectId). Later we need to filter task_id from
|
||||
* /api/chat/histories by project_id then feed it here.
|
||||
*/
|
||||
const taskIdsList = [projectId];
|
||||
projectStore.replayProject(taskIdsList, question, projectId, historyId);
|
||||
navigate({ pathname: "/" });
|
||||
};
|
||||
|
||||
const handleSetActive = (taskId: string, question: string) => {
|
||||
const task = chatStore.tasks[taskId];
|
||||
if (task) {
|
||||
// if there is a record, display the result
|
||||
chatStore.setActiveTaskId(taskId);
|
||||
const handleSetActive = (projectId: string, question: string, historyId: string) => {
|
||||
const project = projectStore.getProjectById(projectId);
|
||||
//If project exists
|
||||
if (project) {
|
||||
// if there is record, show result
|
||||
projectStore.setHistoryId(projectId, historyId);
|
||||
projectStore.setActiveProject(projectId)
|
||||
navigate(`/`);
|
||||
close();
|
||||
} else {
|
||||
// if there is no record, execute replay
|
||||
handleReplay(taskId, question);
|
||||
handleReplay(projectId, question, historyId);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -547,7 +556,11 @@ export default function Home() {
|
|||
{historyTasks.map((task) => {
|
||||
return (
|
||||
<div
|
||||
onClick={() => handleSetActive(task.task_id, task.question)}
|
||||
/**
|
||||
* TODO(history): Update to use project_id field
|
||||
* after update instead.
|
||||
*/
|
||||
onClick={() => handleSetActive(task.task_id, task.question, task.id)}
|
||||
key={task.task_id}
|
||||
className={`${
|
||||
chatStore.activeTaskId === task.task_id
|
||||
|
|
@ -589,7 +602,11 @@ export default function Home() {
|
|||
return (
|
||||
<div
|
||||
onClick={() => {
|
||||
handleSetActive(task.task_id, task.question);
|
||||
/**
|
||||
* TODO(history): Update to use project_id field
|
||||
* after update instead.
|
||||
*/
|
||||
handleSetActive(task.task_id, task.question, task.id);
|
||||
}}
|
||||
key={task.task_id}
|
||||
className={`${
|
||||
|
|
|
|||
|
|
@ -193,6 +193,9 @@ const chatStore = (initial?: Partial<ChatStore>) => createStore<ChatStore>()(
|
|||
//Create a new chatStore on Start
|
||||
let newTaskId = taskId;
|
||||
let targetChatStore = { getState: () => get() }; // Default to current store
|
||||
/**
|
||||
* Replay creates its own chatStore for each task with replayProject
|
||||
*/
|
||||
if(project_id && type !== "replay") {
|
||||
console.log("Creating a new Chat Instance for current project on end")
|
||||
const newChatResult = projectStore.appendInitChatStore(project_id);
|
||||
|
|
@ -219,12 +222,13 @@ const chatStore = (initial?: Partial<ChatStore>) => createStore<ChatStore>()(
|
|||
const api = type == 'share' ?
|
||||
`${base_Url}/api/chat/share/playback/${shareToken}?delay_time=${delayTime}`
|
||||
: type == 'replay' ?
|
||||
`${base_Url}/api/chat/steps/playback/${taskId}?delay_time=${delayTime}`
|
||||
`${base_Url}/api/chat/steps/playback/${project_id}?delay_time=${delayTime}`
|
||||
: `${baseURL}/chat`
|
||||
|
||||
const { tasks } = get()
|
||||
let historyId: string | null = null;
|
||||
let historyId: string | null = projectStore.getHistoryId(project_id);
|
||||
let snapshots: any = [];
|
||||
let skipFirstConfirmOnReplay = true;
|
||||
|
||||
// replay or share request
|
||||
if (type) {
|
||||
|
|
@ -304,12 +308,16 @@ const chatStore = (initial?: Partial<ChatStore>) => createStore<ChatStore>()(
|
|||
|
||||
|
||||
// create history
|
||||
if (!type) {
|
||||
if (!type && !historyId) {
|
||||
const authStore = getAuthStore();
|
||||
|
||||
const obj = {
|
||||
"project_id": project_id,
|
||||
"task_id": newTaskId,
|
||||
/**
|
||||
* TODO(history): Currently reusing project_id as the source
|
||||
* of truth per project. Need to update field
|
||||
* name after backend update.
|
||||
*/
|
||||
"task_id": project_id,
|
||||
"user_id": authStore.user_id,
|
||||
"question": messageContent || (targetChatStore.getState().tasks[newTaskId]?.messages[0]?.content ?? ''),
|
||||
"language": systemLanguage,
|
||||
|
|
@ -324,6 +332,12 @@ const chatStore = (initial?: Partial<ChatStore>) => createStore<ChatStore>()(
|
|||
}
|
||||
await proxyFetchPost(`/api/chat/history`, obj).then(res => {
|
||||
historyId = res.id;
|
||||
|
||||
/**Save history id for replay reuse purposes.
|
||||
* TODO(history): Remove historyId handling to support per projectId
|
||||
* instead in history api
|
||||
*/
|
||||
if(project_id && historyId) projectStore.setHistoryId(project_id, historyId);
|
||||
})
|
||||
}
|
||||
const browser_port = await window.ipcRenderer.invoke('get-browser-port');
|
||||
|
|
@ -363,6 +377,41 @@ const chatStore = (initial?: Partial<ChatStore>) => createStore<ChatStore>()(
|
|||
multi_modal_agent: "Multi Modal Agent",
|
||||
social_medium_agent: "Social Media Agent",
|
||||
};
|
||||
|
||||
//Create new chatStore for replay
|
||||
//TODO(history): Remove when per task replay is implemented
|
||||
// waiting for implementing project_id to backend
|
||||
if(type === "replay" && agentMessages.step === "confirmed"
|
||||
&& !skipFirstConfirmOnReplay) {
|
||||
const { question } = agentMessages.data;
|
||||
/**
|
||||
* For Tasks where appended to existing project by
|
||||
* reusing same projectId. Need to create new chatStore
|
||||
* as it has been skipped earlier in startTask.
|
||||
*/
|
||||
if(type && project_id) {
|
||||
const newChatResult = projectStore.appendInitChatStore(project_id);
|
||||
|
||||
if (newChatResult) {
|
||||
newTaskId = newChatResult.taskId;
|
||||
targetChatStore = newChatResult.chatStore;
|
||||
targetChatStore.getState().setIsPending(newTaskId, false);
|
||||
|
||||
//From handleSend if message is given
|
||||
// Add the message to the new chatStore if provided
|
||||
if (question) {
|
||||
targetChatStore.getState().addMessages(newTaskId, {
|
||||
id: generateUniqueId(),
|
||||
role: "user",
|
||||
content: question,
|
||||
attaches: messageAttaches || [],
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//Enable it for the rest of current SSE session
|
||||
skipFirstConfirmOnReplay = false;
|
||||
|
||||
// Dynamic getter function that always returns the current active chat store
|
||||
const getCurrentChatStore = () => {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue