refactor: refine folder structure and rename

This commit is contained in:
Henry Li
2026-01-16 09:13:02 +08:00
parent 7680a5adba
commit fe7504daed
13 changed files with 65 additions and 37 deletions

View File

@@ -17,8 +17,12 @@ import {
WorkspaceFooter, WorkspaceFooter,
} from "@/components/workspace/workspace-container"; } from "@/components/workspace/workspace-container";
import { getLangGraphClient } from "@/core/api"; import { getLangGraphClient } from "@/core/api";
import type { MessageThread, MessageThreadState } from "@/core/thread"; import type {
import { titleOfThread } from "@/core/thread/utils"; AgentThread,
AgentThreadContext,
AgentThreadState,
} from "@/core/threads";
import { titleOfThread } from "@/core/threads/utils";
import { uuid } from "@/core/utils/uuid"; import { uuid } from "@/core/utils/uuid";
const langGraphClient = getLangGraphClient(); const langGraphClient = getLangGraphClient();
@@ -32,6 +36,11 @@ export default function ChatPage() {
[threadIdFromPath], [threadIdFromPath],
); );
const [threadId, setThreadId] = useState<string | null>(null); const [threadId, setThreadId] = useState<string | null>(null);
const [threadContext, setThreadContext] = useState<AgentThreadContext>({
thread_id: "",
model: "deepseek-v3.2",
thinking_enabled: true,
});
useEffect(() => { useEffect(() => {
if (threadIdFromPath !== "new") { if (threadIdFromPath !== "new") {
setThreadId(threadIdFromPath); setThreadId(threadIdFromPath);
@@ -39,7 +48,7 @@ export default function ChatPage() {
setThreadId(uuid()); setThreadId(uuid());
} }
}, [threadIdFromPath]); }, [threadIdFromPath]);
const thread = useStream<MessageThreadState>({ const thread = useStream<AgentThreadState>({
client: langGraphClient, client: langGraphClient,
assistantId: "lead_agent", assistantId: "lead_agent",
threadId: !isNewThread ? threadId : undefined, threadId: !isNewThread ? threadId : undefined,
@@ -74,15 +83,14 @@ export default function ChatPage() {
streamSubgraphs: true, streamSubgraphs: true,
streamResumable: true, streamResumable: true,
context: { context: {
...threadContext,
thread_id: threadId!, thread_id: threadId!,
model: "deepseek-v3.2",
thinking_enabled: true,
}, },
}, },
); );
void queryClient.invalidateQueries({ queryKey: ["threads", "search"] }); void queryClient.invalidateQueries({ queryKey: ["threads", "search"] });
}, },
[isNewThread, queryClient, router, thread, threadId], [isNewThread, queryClient, router, thread, threadContext, threadId],
); );
const handleStop = useCallback(async () => { const handleStop = useCallback(async () => {
await thread.stop(); await thread.stop();
@@ -93,7 +101,7 @@ export default function ChatPage() {
<BreadcrumbItem className="hidden md:block"> <BreadcrumbItem className="hidden md:block">
{isNewThread {isNewThread
? "New" ? "New"
: titleOfThread(thread as unknown as MessageThread)} : titleOfThread(thread as unknown as AgentThread)}
</BreadcrumbItem> </BreadcrumbItem>
</WorkspaceHeader> </WorkspaceHeader>
<WorkspaceBody> <WorkspaceBody>

View File

@@ -4,13 +4,13 @@ import { useStream } from "@langchain/langgraph-sdk/react";
import { useParams } from "next/navigation"; import { useParams } from "next/navigation";
import { getLangGraphClient } from "@/core/api"; import { getLangGraphClient } from "@/core/api";
import type { MessageThreadState } from "@/core/thread"; import type { AgentThreadState } from "@/core/threads";
const apiClient = getLangGraphClient(); const apiClient = getLangGraphClient();
export default function TestPage() { export default function TestPage() {
const { thread_id: threadId } = useParams<{ thread_id: string }>(); const { thread_id: threadId } = useParams<{ thread_id: string }>();
const thread = useStream<MessageThreadState>({ const thread = useStream<AgentThreadState>({
client: apiClient, client: apiClient,
assistantId: "lead_agent", assistantId: "lead_agent",
threadId, threadId,

View File

@@ -1,9 +1,11 @@
import type { ChatStatus } from "ai"; import type { ChatStatus } from "ai";
import { LightbulbIcon, LightbulbOffIcon } from "lucide-react";
import { useCallback, type ComponentProps } from "react"; import { useCallback, type ComponentProps } from "react";
import { import {
PromptInput, PromptInput,
PromptInputBody, PromptInputBody,
PromptInputButton,
PromptInputFooter, PromptInputFooter,
PromptInputSubmit, PromptInputSubmit,
PromptInputTextarea, PromptInputTextarea,
@@ -11,6 +13,8 @@ import {
} from "@/components/ai-elements/prompt-input"; } from "@/components/ai-elements/prompt-input";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import { Tooltip } from "./tooltip";
export function InputBox({ export function InputBox({
className, className,
autoFocus, autoFocus,
@@ -57,7 +61,13 @@ export function InputBox({
/> />
</PromptInputBody> </PromptInputBody>
<PromptInputFooter className="flex"> <PromptInputFooter className="flex">
<div></div> <div>
<Tooltip content="">
<PromptInputButton>
<LightbulbOffIcon className="size-4" />
</PromptInputButton>
</Tooltip>
</div>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<PromptInputSubmit <PromptInputSubmit
className="rounded-full" className="rounded-full"

View File

@@ -21,14 +21,13 @@ import {
ChainOfThoughtStep, ChainOfThoughtStep,
} from "@/components/ai-elements/chain-of-thought"; } from "@/components/ai-elements/chain-of-thought";
import { MessageResponse } from "@/components/ai-elements/message"; import { MessageResponse } from "@/components/ai-elements/message";
import { useRehypeSplitWordsIntoSpans } from "@/core/rehype";
import { extractTitleFromMarkdown } from "@/core/utils/markdown";
import { cn } from "@/lib/utils";
import { import {
extractReasoningContentFromMessage, extractReasoningContentFromMessage,
findToolCallResult, findToolCallResult,
} from "./utils"; } from "@/core/messages/utils";
import { useRehypeSplitWordsIntoSpans } from "@/core/rehype";
import { extractTitleFromMarkdown } from "@/core/utils/markdown";
import { cn } from "@/lib/utils";
export function MessageGroup({ export function MessageGroup({
className, className,

View File

@@ -6,11 +6,15 @@ import {
MessageContent as AIElementMessageContent, MessageContent as AIElementMessageContent,
MessageResponse as AIElementMessageResponse, MessageResponse as AIElementMessageResponse,
} from "@/components/ai-elements/message"; } from "@/components/ai-elements/message";
import {
extractContentFromMessage,
hasReasoning,
hasToolCalls,
} from "@/core/messages/utils";
import { useRehypeSplitWordsIntoSpans } from "@/core/rehype"; import { useRehypeSplitWordsIntoSpans } from "@/core/rehype";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import { MessageGroup } from "./message-group"; import { MessageGroup } from "./message-group";
import { extractContentFromMessage, hasReasoning, hasToolCalls } from "./utils";
export function MessageListItem({ export function MessageListItem({
className, className,

View File

@@ -5,7 +5,8 @@ import {
ConversationContent, ConversationContent,
ConversationScrollButton, ConversationScrollButton,
} from "@/components/ai-elements/conversation"; } from "@/components/ai-elements/conversation";
import type { MessageThreadState } from "@/core/thread"; import { groupMessages, hasContent } from "@/core/messages/utils";
import type { AgentThreadState } from "@/core/threads";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import { StreamingIndicator } from "../streaming-indicator"; import { StreamingIndicator } from "../streaming-indicator";
@@ -13,14 +14,13 @@ import { StreamingIndicator } from "../streaming-indicator";
import { MessageGroup } from "./message-group"; import { MessageGroup } from "./message-group";
import { MessageListItem } from "./message-list-item"; import { MessageListItem } from "./message-list-item";
import { MessageListSkeleton } from "./skeleton"; import { MessageListSkeleton } from "./skeleton";
import { groupMessages, hasContent } from "./utils";
export function MessageList({ export function MessageList({
className, className,
thread, thread,
}: { }: {
className?: string; className?: string;
thread: UseStream<MessageThreadState>; thread: UseStream<AgentThreadState>;
}) { }) {
if (thread.isThreadLoading) { if (thread.isThreadLoading) {
return <MessageListSkeleton />; return <MessageListSkeleton />;

View File

@@ -21,7 +21,7 @@ import {
SidebarMenuItem, SidebarMenuItem,
} from "@/components/ui/sidebar"; } from "@/components/ui/sidebar";
import { useDeleteThread, useThreads } from "@/core/api"; import { useDeleteThread, useThreads } from "@/core/api";
import { pathOfThread, titleOfThread } from "@/core/thread/utils"; import { pathOfThread, titleOfThread } from "@/core/threads/utils";
export function RecentChatList() { export function RecentChatList() {
const router = useRouter(); const router = useRouter();

View File

@@ -1,7 +1,7 @@
import type { ThreadsClient } from "@langchain/langgraph-sdk/client"; import type { ThreadsClient } from "@langchain/langgraph-sdk/client";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import type { MessageThread, MessageThreadState } from "../thread"; import type { AgentThread, AgentThreadState } from "../threads";
import { getLangGraphClient } from "./client"; import { getLangGraphClient } from "./client";
@@ -13,12 +13,12 @@ export function useThreads(
}, },
) { ) {
const langGraphClient = getLangGraphClient(); const langGraphClient = getLangGraphClient();
return useQuery<MessageThread[]>({ return useQuery<AgentThread[]>({
queryKey: ["threads", "search", params], queryKey: ["threads", "search", params],
queryFn: async () => { queryFn: async () => {
const response = const response =
await langGraphClient.threads.search<MessageThreadState>(params); await langGraphClient.threads.search<AgentThreadState>(params);
return response as MessageThread[]; return response as AgentThread[];
}, },
}); });
} }
@@ -36,7 +36,7 @@ export function useDeleteThread() {
queryKey: ["threads", "search"], queryKey: ["threads", "search"],
exact: false, exact: false,
}, },
(oldData: Array<MessageThread>) => { (oldData: Array<AgentThread>) => {
return oldData.filter((t) => t.thread_id !== threadId); return oldData.filter((t) => t.thread_id !== threadId);
}, },
); );

View File

@@ -1,8 +0,0 @@
import { type BaseMessage } from "@langchain/core/messages";
import type { Thread } from "@langchain/langgraph-sdk";
export interface MessageThreadState extends Record<string, unknown> {
messages: BaseMessage[];
}
export interface MessageThread extends Thread<MessageThreadState> {}

View File

@@ -0,0 +1,15 @@
import { type BaseMessage } from "@langchain/core/messages";
import type { Thread } from "@langchain/langgraph-sdk";
export interface AgentThreadState extends Record<string, unknown> {
title: string;
messages: BaseMessage[];
}
export interface AgentThread extends Thread<AgentThreadState> {}
export interface AgentThreadContext extends Record<string, unknown> {
thread_id: string;
model: string;
thinking_enabled: boolean;
}

View File

@@ -1,8 +1,8 @@
import type { BaseMessage } from "@langchain/core/messages"; import type { BaseMessage } from "@langchain/core/messages";
import type { MessageThread } from "./types"; import type { AgentThread } from "./types";
export function pathOfThread(thread: MessageThread, includeAssistantId = true) { export function pathOfThread(thread: AgentThread, includeAssistantId = true) {
if (includeAssistantId) { if (includeAssistantId) {
return `/workspace/chats/${thread.thread_id}`; return `/workspace/chats/${thread.thread_id}`;
} }
@@ -19,9 +19,9 @@ export function textOfMessage(message: BaseMessage) {
return null; return null;
} }
export function titleOfThread(thread: MessageThread) { export function titleOfThread(thread: AgentThread) {
if (thread.values && "title" in thread.values) { if (thread.values && "title" in thread.values) {
return thread.values.title as string; return thread.values.title;
} }
return "Untitled"; return "Untitled";
} }