feat: auto start after 30s if not confirmed (#193)

This commit is contained in:
Wendong-Fan 2025-08-19 22:04:17 +08:00 committed by GitHub
commit c8a0a21ef2
2 changed files with 87 additions and 61 deletions

View file

@ -1,17 +1,10 @@
import { useState, useRef, useEffect, useCallback } from "react"; import { useState, useRef, useEffect, useCallback } from "react";
import { fetchPost } from "@/api/http"; import { fetchPost } from "@/api/http";
import { Button } from "@/components/ui/button";
import { BottomInput } from "./BottomInput"; import { BottomInput } from "./BottomInput";
import { TaskCard } from "./TaskCard"; import { TaskCard } from "./TaskCard";
import { MessageCard } from "./MessageCard"; import { MessageCard } from "./MessageCard";
import { TypeCardSkeleton } from "./TypeCardSkeleton"; import { TypeCardSkeleton } from "./TypeCardSkeleton";
import { import { FileText, TriangleAlert } from "lucide-react";
Smartphone,
Workflow,
CircleDollarSign,
FileText,
TriangleAlert,
} from "lucide-react";
import { generateUniqueId } from "@/lib"; import { generateUniqueId } from "@/lib";
import { useChatStore } from "@/store/chatStore"; import { useChatStore } from "@/store/chatStore";
import { proxyFetchGet } from "@/api/http"; import { proxyFetchGet } from "@/api/http";
@ -31,7 +24,6 @@ export default function ChatBox(): JSX.Element {
const [privacyDialogOpen, setPrivacyDialogOpen] = useState(false); const [privacyDialogOpen, setPrivacyDialogOpen] = useState(false);
const { modelType } = useAuthStore(); const { modelType } = useAuthStore();
const [useCloudModelInDev, setUseCloudModelInDev] = useState(false); const [useCloudModelInDev, setUseCloudModelInDev] = useState(false);
useEffect(() => { useEffect(() => {
if ( if (
import.meta.env.VITE_USE_LOCAL_PROXY === "true" && import.meta.env.VITE_USE_LOCAL_PROXY === "true" &&
@ -200,26 +192,23 @@ export default function ChatBox(): JSX.Element {
}, [scrollContainerRef.current?.scrollHeight]); }, [scrollContainerRef.current?.scrollHeight]);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const handleConfirmTask = async () => { const handleConfirmTask = async (taskId?: string) => {
const taskId = chatStore.activeTaskId; const _taskId = taskId || chatStore.activeTaskId;
if (!taskId) return; if (!_taskId) return;
setLoading(true); setLoading(true);
await chatStore.handleConfirmTask(taskId); await chatStore.handleConfirmTask(_taskId);
setLoading(false); setLoading(false);
}; };
const [hasSubTask, setHasSubTask] = useState(false); const [hasSubTask, setHasSubTask] = useState(false);
useEffect(() => { useEffect(() => {
setHasSubTask( const _hasSubTask = chatStore.tasks[
chatStore.tasks[chatStore.activeTaskId as string]?.messages?.find( chatStore.activeTaskId as string
(message) => { ]?.messages?.find((message) => message.step === "to_sub_tasks")
return message.step === "to_sub_tasks"; ? true
} : false;
) setHasSubTask(_hasSubTask);
? true
: false
);
}, [chatStore?.tasks[chatStore.activeTaskId as string]?.messages]); }, [chatStore?.tasks[chatStore.activeTaskId as string]?.messages]);
useEffect(() => { useEffect(() => {
@ -437,13 +426,18 @@ export default function ChatBox(): JSX.Element {
chatStore.tasks[chatStore.activeTaskId].summaryTask || chatStore.tasks[chatStore.activeTaskId].summaryTask ||
"" ""
} }
onAddTask={() => chatStore.addTaskInfo()} onAddTask={() => {
onUpdateTask={(taskIndex, content) => chatStore.setIsTaskEdit(chatStore.activeTaskId as string, true);
chatStore.updateTaskInfo(taskIndex, content) chatStore.addTaskInfo();
} }}
onDeleteTask={(taskIndex) => onUpdateTask={(taskIndex, content) => {
chatStore.deleteTaskInfo(taskIndex) chatStore.setIsTaskEdit(chatStore.activeTaskId as string, true);
} chatStore.updateTaskInfo(taskIndex, content);
}}
onDeleteTask={(taskIndex) => {
chatStore.setIsTaskEdit(chatStore.activeTaskId as string, true);
chatStore.deleteTaskInfo(taskIndex);
}}
/> />
); );
} }
@ -582,37 +576,39 @@ export default function ChatBox(): JSX.Element {
</div> </div>
) )
)} )}
{!useCloudModelInDev && privacy && (hasSearchKey || modelType === "cloud") && ( {!useCloudModelInDev &&
<div className="mr-2 flex flex-col items-center gap-2"> privacy &&
{[ (hasSearchKey || modelType === "cloud") && (
{ <div className="mr-2 flex flex-col items-center gap-2">
label: "Palm Springs Tennis Trip Planner", {[
message: {
"I am two tennis fans and want to go see the tennis tournament in palm springs. l live in SF - please prepare a detailed itinerary with flights, hotels, things to do for 3 days - around the time semifinal/finals are happening. We like hiking, vegan food and spas. Our budget is $5K. The itinerary should be a detailed timeline of time, activity, cost, other details and if applicable a link to buy tickets/make reservations etc. for the item. Some preferences 1.Spa access would be nice but not necessary 2. When you finnish this task, please generate a html report about this trip.", label: "Palm Springs Tennis Trip Planner",
}, message:
{ "I am two tennis fans and want to go see the tennis tournament in palm springs. l live in SF - please prepare a detailed itinerary with flights, hotels, things to do for 3 days - around the time semifinal/finals are happening. We like hiking, vegan food and spas. Our budget is $5K. The itinerary should be a detailed timeline of time, activity, cost, other details and if applicable a link to buy tickets/make reservations etc. for the item. Some preferences 1.Spa access would be nice but not necessary 2. When you finnish this task, please generate a html report about this trip.",
label: "Bank Transfer CSV Analysis and Visualization", },
message: {
"Create a mock bank transfer CSV file include 10 columns and 10 rows. Read the generated CSV file and summarize the data, generate a chart to visualize relevant trends or insights from the data.", label: "Bank Transfer CSV Analysis and Visualization",
}, message:
{ "Create a mock bank transfer CSV file include 10 columns and 10 rows. Read the generated CSV file and summarize the data, generate a chart to visualize relevant trends or insights from the data.",
label: "Find Duplicate Files in Downloads Folder", },
message: {
"Help me find duplicate files by content, size, and format in my downloads folder.", label: "Find Duplicate Files in Downloads Folder",
}, message:
].map(({ label, message }) => ( "Help me find duplicate files by content, size, and format in my downloads folder.",
<div },
key={label} ].map(({ label, message }) => (
className="cursor-pointer px-sm py-xs rounded-md bg-input-bg-default opacity-70 hover:opacity-100 text-xs font-medium leading-none text-button-tertiery-text-default transition-all duration-300" <div
onClick={() => { key={label}
setMessage(message); className="cursor-pointer px-sm py-xs rounded-md bg-input-bg-default opacity-70 hover:opacity-100 text-xs font-medium leading-none text-button-tertiery-text-default transition-all duration-300"
}} onClick={() => {
> setMessage(message);
<span>{label}</span> }}
</div> >
))} <span>{label}</span>
</div> </div>
)} ))}
</div>
)}
</div> </div>
</div> </div>
</div> </div>

View file

@ -38,6 +38,7 @@ interface Task {
snapshots: any[]; snapshots: any[];
snapshotsTemp: any[]; snapshotsTemp: any[];
isTakeControl: boolean; isTakeControl: boolean;
isTaskEdit: boolean;
} }
interface ChatStore { interface ChatStore {
@ -91,6 +92,7 @@ interface ChatStore {
setSnapshots: (taskId: string, snapshots: any[]) => void, setSnapshots: (taskId: string, snapshots: any[]) => void,
setIsTakeControl: (taskId: string, isTakeControl: boolean) => void, setIsTakeControl: (taskId: string, isTakeControl: boolean) => void,
setSnapshotsTemp: (taskId: string, snapshot: any) => void, setSnapshotsTemp: (taskId: string, snapshot: any) => void,
setIsTaskEdit: (taskId: string, isTaskEdit: boolean) => void,
} }
@ -137,7 +139,8 @@ const chatStore = create<ChatStore>()(
selectedFile: null, selectedFile: null,
snapshots: [], snapshots: [],
snapshotsTemp: [], snapshotsTemp: [],
isTakeControl: false isTakeControl: false,
isTaskEdit: false
}, },
} }
})) }))
@ -326,9 +329,24 @@ const chatStore = create<ChatStore>()(
// if (tasks[taskId].status === 'finished') return // if (tasks[taskId].status === 'finished') return
if (agentMessages.step === "to_sub_tasks") { if (agentMessages.step === "to_sub_tasks") {
const messages = [...tasks[taskId].messages] const messages = [...tasks[taskId].messages]
const toSubTaskIndex = messages.findLastIndex((message: Message) => message.step === 'to_sub_tasks') const toSubTaskIndex = messages.findLastIndex((message: Message) => message.step === 'to_sub_tasks')
if (toSubTaskIndex === -1) { if (toSubTaskIndex === -1) {
// 30 seconds auto confirm
setTimeout(() => {
const { tasks, handleConfirmTask, setIsTaskEdit } = get();
const message = tasks[taskId].messages.findLast((item) => item.step === "to_sub_tasks");
const isConfirm = message?.isConfirm || false;
const isTakeControl =
tasks[taskId].isTakeControl;
if (!isConfirm && !isTakeControl && !tasks[taskId].isTaskEdit) {
handleConfirmTask(taskId);
}
setIsTaskEdit(taskId, false);
}, 30000);
const newNoticeMessage: Message = { const newNoticeMessage: Message = {
id: generateUniqueId(), id: generateUniqueId(),
role: "agent", role: "agent",
@ -1586,6 +1604,18 @@ const chatStore = create<ChatStore>()(
} }
}) })
}, },
setIsTaskEdit(taskId: string, isTaskEdit: boolean) {
set((state) => ({
...state,
tasks: {
...state.tasks,
[taskId]: {
...state.tasks[taskId],
isTaskEdit
},
},
}))
},
}) })
); );