mirror of
https://gitee.com/wanwujie/deer-flow
synced 2026-04-06 07:20:21 +08:00
feat: rename 'model' to 'model_name'
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { type HumanMessage } from "@langchain/core/messages";
|
||||
import { useStream } from "@langchain/langgraph-sdk/react";
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
import { useParams, useRouter } from "next/navigation";
|
||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||
@@ -16,14 +15,12 @@ import {
|
||||
WorkspaceHeader,
|
||||
WorkspaceFooter,
|
||||
} from "@/components/workspace/workspace-container";
|
||||
import { getLangGraphClient } from "@/core/api";
|
||||
import { useLocalSettings } from "@/core/settings";
|
||||
import type { AgentThread, AgentThreadState } from "@/core/threads";
|
||||
import { type AgentThread } from "@/core/threads";
|
||||
import { useThreadStream } from "@/core/threads/hooks";
|
||||
import { titleOfThread } from "@/core/threads/utils";
|
||||
import { uuid } from "@/core/utils/uuid";
|
||||
|
||||
const langGraphClient = getLangGraphClient();
|
||||
|
||||
export default function ChatPage() {
|
||||
const router = useRouter();
|
||||
const queryClient = useQueryClient();
|
||||
@@ -42,15 +39,9 @@ export default function ChatPage() {
|
||||
setThreadId(uuid());
|
||||
}
|
||||
}, [threadIdFromPath]);
|
||||
const thread = useStream<AgentThreadState>({
|
||||
client: langGraphClient,
|
||||
assistantId: "lead_agent",
|
||||
threadId: !isNewThread ? threadId : undefined,
|
||||
reconnectOnMount: true,
|
||||
fetchStateHistory: true,
|
||||
onFinish() {
|
||||
void queryClient.invalidateQueries({ queryKey: ["threads", "search"] });
|
||||
},
|
||||
const thread = useThreadStream({
|
||||
isNewThread,
|
||||
threadId,
|
||||
});
|
||||
const handleSubmit = useCallback(
|
||||
async (message: PromptInputMessage) => {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as React from "react"
|
||||
import * as React from "react";
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
function Input({ className, type, ...props }: React.ComponentProps<"input">) {
|
||||
return (
|
||||
@@ -11,11 +11,11 @@ function Input({ className, type, ...props }: React.ComponentProps<"input">) {
|
||||
"file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
||||
"focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
|
||||
"aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
||||
className
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export { Input }
|
||||
export { Input };
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
import type { ChatStatus } from "ai";
|
||||
import { CheckIcon, LightbulbIcon, LightbulbOffIcon } from "lucide-react";
|
||||
import {
|
||||
BoxIcon,
|
||||
CheckIcon,
|
||||
LightbulbIcon,
|
||||
LightbulbOffIcon,
|
||||
} from "lucide-react";
|
||||
import { useCallback, useMemo, useState, type ComponentProps } from "react";
|
||||
|
||||
import {
|
||||
@@ -20,10 +25,8 @@ import {
|
||||
ModelSelectorInput,
|
||||
ModelSelectorItem,
|
||||
ModelSelectorList,
|
||||
ModelSelectorLogo,
|
||||
ModelSelectorName,
|
||||
ModelSelectorTrigger,
|
||||
type ModelSelectorLogoProps,
|
||||
} from "../ai-elements/model-selector";
|
||||
|
||||
import { Tooltip } from "./tooltip";
|
||||
@@ -56,14 +59,14 @@ export function InputBox({
|
||||
}) {
|
||||
const [modelDialogOpen, setModelDialogOpen] = useState(false);
|
||||
const selectedModel = useMemo(
|
||||
() => AVAILABLE_MODELS.find((m) => m.name === context.model),
|
||||
[context.model],
|
||||
() => AVAILABLE_MODELS.find((m) => m.name === context.model_name),
|
||||
[context.model_name],
|
||||
);
|
||||
const handleModelSelect = useCallback(
|
||||
(model: string) => {
|
||||
(model_name: string) => {
|
||||
onContextChange?.({
|
||||
...context,
|
||||
model,
|
||||
model_name,
|
||||
});
|
||||
setModelDialogOpen(false);
|
||||
},
|
||||
@@ -140,13 +143,8 @@ export function InputBox({
|
||||
>
|
||||
<ModelSelectorTrigger asChild>
|
||||
<PromptInputButton>
|
||||
<ModelSelectorLogo
|
||||
className="size-4"
|
||||
provider={
|
||||
selectedModel?.provider as ModelSelectorLogoProps["provider"]
|
||||
}
|
||||
/>
|
||||
<ModelSelectorName>
|
||||
<BoxIcon className="size-4" />
|
||||
<ModelSelectorName className="font-light">
|
||||
{selectedModel?.displayName}
|
||||
</ModelSelectorName>
|
||||
</PromptInputButton>
|
||||
@@ -160,12 +158,8 @@ export function InputBox({
|
||||
value={m.name}
|
||||
onSelect={() => handleModelSelect(m.name)}
|
||||
>
|
||||
<ModelSelectorLogo
|
||||
className="size-4"
|
||||
provider={m.provider}
|
||||
/>
|
||||
<ModelSelectorName>{m.displayName}</ModelSelectorName>
|
||||
{m.name === context.model ? (
|
||||
{m.name === context.model_name ? (
|
||||
<CheckIcon className="ml-auto size-4" />
|
||||
) : (
|
||||
<div className="ml-auto size-4" />
|
||||
|
||||
@@ -20,7 +20,7 @@ import {
|
||||
SidebarMenuButton,
|
||||
SidebarMenuItem,
|
||||
} from "@/components/ui/sidebar";
|
||||
import { useDeleteThread, useThreads } from "@/core/api";
|
||||
import { useDeleteThread, useThreads } from "@/core/threads/hooks";
|
||||
import { pathOfThread, titleOfThread } from "@/core/threads/utils";
|
||||
|
||||
export function RecentChatList() {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import { Client as LangGraphClient } from "@langchain/langgraph-sdk/client";
|
||||
|
||||
let _singleton: LangGraphClient | null = null;
|
||||
export function getLangGraphClient(): LangGraphClient {
|
||||
export function getAPIClient(): LangGraphClient {
|
||||
let url: URL | null = null;
|
||||
if (typeof window === "undefined") {
|
||||
url = new URL("/api/langgraph", "http://localhost:3000");
|
||||
@@ -1,45 +0,0 @@
|
||||
import type { ThreadsClient } from "@langchain/langgraph-sdk/client";
|
||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
|
||||
import type { AgentThread, AgentThreadState } from "../threads";
|
||||
|
||||
import { getLangGraphClient } from "./client";
|
||||
|
||||
export function useThreads(
|
||||
params: Parameters<ThreadsClient["search"]>[0] = {
|
||||
limit: 50,
|
||||
sortBy: "updated_at",
|
||||
sortOrder: "desc",
|
||||
},
|
||||
) {
|
||||
const langGraphClient = getLangGraphClient();
|
||||
return useQuery<AgentThread[]>({
|
||||
queryKey: ["threads", "search", params],
|
||||
queryFn: async () => {
|
||||
const response =
|
||||
await langGraphClient.threads.search<AgentThreadState>(params);
|
||||
return response as AgentThread[];
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useDeleteThread() {
|
||||
const queryClient = useQueryClient();
|
||||
const langGraphClient = getLangGraphClient();
|
||||
return useMutation({
|
||||
mutationFn: async ({ threadId }: { threadId: string }) => {
|
||||
await langGraphClient.threads.delete(threadId);
|
||||
},
|
||||
onSuccess(_, { threadId }) {
|
||||
queryClient.setQueriesData(
|
||||
{
|
||||
queryKey: ["threads", "search"],
|
||||
exact: false,
|
||||
},
|
||||
(oldData: Array<AgentThread>) => {
|
||||
return oldData.filter((t) => t.thread_id !== threadId);
|
||||
},
|
||||
);
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -1,2 +1 @@
|
||||
export * from "./client";
|
||||
export * from "./hooks";
|
||||
export * from "./api-client";
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { AgentThreadContext } from "../threads";
|
||||
|
||||
export const DEFAULT_LOCAL_SETTINGS: LocalSettings = {
|
||||
context: {
|
||||
model: "deepseek-v3.2",
|
||||
model_name: "deepseek-v3.2",
|
||||
thinking_enabled: true,
|
||||
},
|
||||
};
|
||||
|
||||
119
frontend/src/core/threads/hooks.ts
Normal file
119
frontend/src/core/threads/hooks.ts
Normal file
@@ -0,0 +1,119 @@
|
||||
import type { HumanMessage } from "@langchain/core/messages";
|
||||
import type { ThreadsClient } from "@langchain/langgraph-sdk/client";
|
||||
import { useStream, type UseStream } from "@langchain/langgraph-sdk/react";
|
||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import { useCallback } from "react";
|
||||
|
||||
import type { PromptInputMessage } from "@/components/ai-elements/prompt-input";
|
||||
|
||||
import { getAPIClient } from "../api";
|
||||
|
||||
import type {
|
||||
AgentThread,
|
||||
AgentThreadContext,
|
||||
AgentThreadState,
|
||||
} from "./types";
|
||||
|
||||
export function useThreadStream({
|
||||
threadId,
|
||||
isNewThread,
|
||||
}: {
|
||||
isNewThread: boolean;
|
||||
threadId: string | null | undefined;
|
||||
}) {
|
||||
const queryClient = useQueryClient();
|
||||
return useStream<AgentThreadState>({
|
||||
client: getAPIClient(),
|
||||
assistantId: "lead_agent",
|
||||
threadId: isNewThread ? undefined : threadId,
|
||||
reconnectOnMount: true,
|
||||
fetchStateHistory: true,
|
||||
onFinish() {
|
||||
void queryClient.invalidateQueries({ queryKey: ["threads", "search"] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useSubmitThread({
|
||||
threadId,
|
||||
thread,
|
||||
threadContext,
|
||||
isNewThread,
|
||||
message,
|
||||
}: {
|
||||
isNewThread: boolean;
|
||||
threadId: string;
|
||||
thread: UseStream<AgentThreadState>;
|
||||
threadContext: AgentThreadContext;
|
||||
message: PromptInputMessage;
|
||||
}) {
|
||||
const queryClient = useQueryClient();
|
||||
const text = message.text.trim();
|
||||
const callback = useCallback(async () => {
|
||||
await thread.submit(
|
||||
{
|
||||
messages: [
|
||||
{
|
||||
type: "human",
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text,
|
||||
},
|
||||
],
|
||||
},
|
||||
] as HumanMessage[],
|
||||
},
|
||||
{
|
||||
threadId: isNewThread ? threadId : undefined,
|
||||
streamSubgraphs: true,
|
||||
streamResumable: true,
|
||||
context: {
|
||||
...threadContext,
|
||||
thread_id: threadId,
|
||||
},
|
||||
},
|
||||
);
|
||||
void queryClient.invalidateQueries({ queryKey: ["threads", "search"] });
|
||||
}, [queryClient, thread, threadContext, threadId, isNewThread, text]);
|
||||
return callback;
|
||||
}
|
||||
|
||||
export function useThreads(
|
||||
params: Parameters<ThreadsClient["search"]>[0] = {
|
||||
limit: 50,
|
||||
sortBy: "updated_at",
|
||||
sortOrder: "desc",
|
||||
},
|
||||
) {
|
||||
const langGraphClient = getAPIClient();
|
||||
return useQuery<AgentThread[]>({
|
||||
queryKey: ["threads", "search", params],
|
||||
queryFn: async () => {
|
||||
const response =
|
||||
await langGraphClient.threads.search<AgentThreadState>(params);
|
||||
return response as AgentThread[];
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useDeleteThread() {
|
||||
const queryClient = useQueryClient();
|
||||
const langGraphClient = getAPIClient();
|
||||
return useMutation({
|
||||
mutationFn: async ({ threadId }: { threadId: string }) => {
|
||||
await langGraphClient.threads.delete(threadId);
|
||||
},
|
||||
onSuccess(_, { threadId }) {
|
||||
queryClient.setQueriesData(
|
||||
{
|
||||
queryKey: ["threads", "search"],
|
||||
exact: false,
|
||||
},
|
||||
(oldData: Array<AgentThread>) => {
|
||||
return oldData.filter((t) => t.thread_id !== threadId);
|
||||
},
|
||||
);
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -10,6 +10,6 @@ export interface AgentThread extends Thread<AgentThreadState> {}
|
||||
|
||||
export interface AgentThreadContext extends Record<string, unknown> {
|
||||
thread_id: string;
|
||||
model: string;
|
||||
model_name: string;
|
||||
thinking_enabled: boolean;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user