mirror of
https://gitee.com/wanwujie/deer-flow
synced 2026-04-03 06:12:14 +08:00
Refactor hooks and improve error handling in chat functionality (#962)
* refactor: update useThreadChat and useThreadStream hooks for improved state management * fix: improve error handling in agent configuration loading and enhance chat page functionality * fix: enhance error handling in agent configuration loading * Update frontend/src/app/workspace/agents/[agent_name]/chats/[thread_id]/page.tsx Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -265,7 +265,7 @@ def make_lead_agent(config: RunnableConfig):
|
||||
is_bootstrap = config.get("configurable", {}).get("is_bootstrap", False)
|
||||
agent_name = config.get("configurable", {}).get("agent_name")
|
||||
|
||||
agent_config = load_agent_config(agent_name)
|
||||
agent_config = load_agent_config(agent_name) if not is_bootstrap else None
|
||||
# Custom agent model or fallback to global/default model resolution
|
||||
agent_model_name = agent_config.model if agent_config and agent_config.model else _resolve_model_name()
|
||||
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
"use client";
|
||||
|
||||
import { BotIcon } from "lucide-react";
|
||||
import { useParams } from "next/navigation";
|
||||
import { BotIcon, PlusSquare } from "lucide-react";
|
||||
import { useParams, useRouter } from "next/navigation";
|
||||
import { useCallback } from "react";
|
||||
|
||||
import type { PromptInputMessage } from "@/components/ai-elements/prompt-input";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { AgentWelcome } from "@/components/workspace/agent-welcome";
|
||||
import { ArtifactTrigger } from "@/components/workspace/artifacts";
|
||||
import { ChatBox, useThreadChat } from "@/components/workspace/chats";
|
||||
@@ -13,6 +14,7 @@ import { MessageList } from "@/components/workspace/messages";
|
||||
import { ThreadContext } from "@/components/workspace/messages/context";
|
||||
import { ThreadTitle } from "@/components/workspace/thread-title";
|
||||
import { TodoList } from "@/components/workspace/todo-list";
|
||||
import { Tooltip } from "@/components/workspace/tooltip";
|
||||
import { useAgent } from "@/core/agents";
|
||||
import { useI18n } from "@/core/i18n/hooks";
|
||||
import { useNotification } from "@/core/notification/hooks";
|
||||
@@ -25,10 +27,10 @@ import { cn } from "@/lib/utils";
|
||||
export default function AgentChatPage() {
|
||||
const { t } = useI18n();
|
||||
const [settings, setSettings] = useLocalSettings();
|
||||
const router = useRouter();
|
||||
|
||||
const { agent_name, thread_id: threadIdFromPath } = useParams<{
|
||||
const { agent_name } = useParams<{
|
||||
agent_name: string;
|
||||
thread_id: string;
|
||||
}>();
|
||||
|
||||
const { agent } = useAgent(agent_name);
|
||||
@@ -37,7 +39,7 @@ export default function AgentChatPage() {
|
||||
|
||||
const { showNotification } = useNotification();
|
||||
const [thread, sendMessage] = useThreadStream({
|
||||
threadId: threadIdFromPath !== "new" ? threadIdFromPath : undefined,
|
||||
threadId: isNewThread ? undefined : threadId,
|
||||
context: { ...settings.context, agent_name: agent_name },
|
||||
onStart: () => {
|
||||
setIsNewThread(false);
|
||||
@@ -99,7 +101,18 @@ export default function AgentChatPage() {
|
||||
<div className="flex w-full items-center text-sm font-medium">
|
||||
<ThreadTitle threadId={threadId} thread={thread} />
|
||||
</div>
|
||||
<div>
|
||||
<div className="mr-4 flex items-center">
|
||||
<Tooltip content={t.agents.newChat}>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="secondary"
|
||||
onClick={() => {
|
||||
router.push(`/workspace/agents/${agent_name}/chats/new`);
|
||||
}}
|
||||
>
|
||||
<PlusSquare /> {t.agents.newChat}
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<ArtifactTrigger />
|
||||
</div>
|
||||
</header>
|
||||
|
||||
@@ -43,6 +43,7 @@ export default function NewAgentPage() {
|
||||
const threadId = useMemo(() => uuid(), []);
|
||||
|
||||
const [thread, sendMessage] = useThreadStream({
|
||||
threadId: step === "chat" ? threadId : undefined,
|
||||
context: {
|
||||
mode: "flash",
|
||||
is_bootstrap: true,
|
||||
|
||||
@@ -10,7 +10,7 @@ export const ArtifactTrigger = () => {
|
||||
const { t } = useI18n();
|
||||
const { artifacts, setOpen: setArtifactsOpen } = useArtifacts();
|
||||
|
||||
if (artifacts?.length === 0) {
|
||||
if (!artifacts || artifacts.length === 0) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
|
||||
@@ -1,21 +1,29 @@
|
||||
"use client";
|
||||
|
||||
import { useParams, useSearchParams } from "next/navigation";
|
||||
import { useMemo, useState } from "react";
|
||||
import { useParams, usePathname, useSearchParams } from "next/navigation";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
import { uuid } from "@/core/utils/uuid";
|
||||
|
||||
export function useThreadChat() {
|
||||
const { thread_id: threadIdFromPath } = useParams<{ thread_id: string }>();
|
||||
const pathname = usePathname();
|
||||
|
||||
const searchParams = useSearchParams();
|
||||
const threadId = useMemo(
|
||||
() => (threadIdFromPath === "new" ? uuid() : threadIdFromPath),
|
||||
[threadIdFromPath],
|
||||
);
|
||||
const [threadId, setThreadId] = useState(() => {
|
||||
return threadIdFromPath === "new" ? uuid() : threadIdFromPath;
|
||||
});
|
||||
|
||||
const [isNewThread, setIsNewThread] = useState(
|
||||
() => threadIdFromPath === "new",
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (pathname.endsWith("/new")) {
|
||||
setIsNewThread(true);
|
||||
setThreadId(uuid());
|
||||
}
|
||||
}, [pathname]);
|
||||
const isMock = searchParams.get("mock") === "true";
|
||||
return { threadId, isNewThread, setIsNewThread, isMock };
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import type { AIMessage } from "@langchain/langgraph-sdk";
|
||||
import type { ThreadsClient } from "@langchain/langgraph-sdk/client";
|
||||
import { useStream, type UseStream } from "@langchain/langgraph-sdk/react";
|
||||
import { useStream } from "@langchain/langgraph-sdk/react";
|
||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import { useCallback, useState } from "react";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { toast } from "sonner";
|
||||
|
||||
import type { PromptInputMessage } from "@/components/ai-elements/prompt-input";
|
||||
@@ -37,6 +37,13 @@ export function useThreadStream({
|
||||
onToolEnd,
|
||||
}: ThreadStreamOptions) {
|
||||
const [_threadId, setThreadId] = useState<string | null>(threadId ?? null);
|
||||
|
||||
useEffect(() => {
|
||||
if (_threadId && _threadId !== threadId) {
|
||||
setThreadId(threadId ?? null);
|
||||
}
|
||||
}, [threadId, _threadId]);
|
||||
|
||||
const queryClient = useQueryClient();
|
||||
const updateSubtask = useUpdateSubtask();
|
||||
const thread = useStream<AgentThreadState>({
|
||||
@@ -74,27 +81,7 @@ export function useThreadStream({
|
||||
},
|
||||
onFinish(state) {
|
||||
onFinish?.(state.values);
|
||||
// void queryClient.invalidateQueries({ queryKey: ["threads", "search"] });
|
||||
queryClient.setQueriesData(
|
||||
{
|
||||
queryKey: ["threads", "search"],
|
||||
exact: false,
|
||||
},
|
||||
(oldData: Array<AgentThread>) => {
|
||||
return oldData.map((t) => {
|
||||
if (t.thread_id === threadId) {
|
||||
return {
|
||||
...t,
|
||||
values: {
|
||||
...t.values,
|
||||
title: state.values.title,
|
||||
},
|
||||
};
|
||||
}
|
||||
return t;
|
||||
});
|
||||
},
|
||||
);
|
||||
void queryClient.invalidateQueries({ queryKey: ["threads", "search"] });
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user