mirror of
https://gitee.com/wanwujie/deer-flow
synced 2026-04-23 22:24:46 +08:00
refactor: refine folder structure and rename
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import type { ThreadsClient } from "@langchain/langgraph-sdk/client";
|
||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
|
||||
import type { MessageThread, MessageThreadState } from "../thread";
|
||||
import type { AgentThread, AgentThreadState } from "../threads";
|
||||
|
||||
import { getLangGraphClient } from "./client";
|
||||
|
||||
@@ -13,12 +13,12 @@ export function useThreads(
|
||||
},
|
||||
) {
|
||||
const langGraphClient = getLangGraphClient();
|
||||
return useQuery<MessageThread[]>({
|
||||
return useQuery<AgentThread[]>({
|
||||
queryKey: ["threads", "search", params],
|
||||
queryFn: async () => {
|
||||
const response =
|
||||
await langGraphClient.threads.search<MessageThreadState>(params);
|
||||
return response as MessageThread[];
|
||||
await langGraphClient.threads.search<AgentThreadState>(params);
|
||||
return response as AgentThread[];
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -36,7 +36,7 @@ export function useDeleteThread() {
|
||||
queryKey: ["threads", "search"],
|
||||
exact: false,
|
||||
},
|
||||
(oldData: Array<MessageThread>) => {
|
||||
(oldData: Array<AgentThread>) => {
|
||||
return oldData.filter((t) => t.thread_id !== threadId);
|
||||
},
|
||||
);
|
||||
|
||||
157
frontend/src/core/messages/utils.ts
Normal file
157
frontend/src/core/messages/utils.ts
Normal file
@@ -0,0 +1,157 @@
|
||||
import type { Message } from "@langchain/langgraph-sdk";
|
||||
|
||||
export function groupMessages<T>(
|
||||
messages: Message[],
|
||||
mapper: (
|
||||
groupedMessages: Message[],
|
||||
groupIndex: number,
|
||||
isLastGroup: boolean,
|
||||
) => T,
|
||||
isLoading = false,
|
||||
): T[] {
|
||||
if (messages.length === 0) {
|
||||
return [];
|
||||
}
|
||||
const resultsOfGroups: T[] = [];
|
||||
let currentGroup: Message[] = [];
|
||||
const lastMessage = messages[messages.length - 1]!;
|
||||
const yieldCurrentGroup = () => {
|
||||
if (currentGroup.length > 0) {
|
||||
const resultOfGroup = mapper(
|
||||
currentGroup,
|
||||
resultsOfGroups.length,
|
||||
currentGroup.includes(lastMessage),
|
||||
);
|
||||
if (resultOfGroup !== undefined && resultOfGroup !== null) {
|
||||
resultsOfGroups.push(resultOfGroup);
|
||||
}
|
||||
currentGroup = [];
|
||||
}
|
||||
};
|
||||
let messageIndex = 0;
|
||||
for (const message of messages) {
|
||||
if (message.type === "human") {
|
||||
// Human messages are always shown as a individual group
|
||||
yieldCurrentGroup();
|
||||
currentGroup.push(message);
|
||||
yieldCurrentGroup();
|
||||
} else if (message.type === "tool") {
|
||||
// Tool messages are always shown with the assistant messages that contains the tool calls
|
||||
currentGroup.push(message);
|
||||
} else if (message.type === "ai") {
|
||||
if (
|
||||
hasToolCalls(message) ||
|
||||
(extractTextFromMessage(message) === "" &&
|
||||
extractReasoningContentFromMessage(message) !== "" &&
|
||||
messageIndex === messages.length - 1 &&
|
||||
isLoading)
|
||||
) {
|
||||
// Assistant messages without any content are folded into the previous group
|
||||
// Normally, these are tool calls (with or without thinking)
|
||||
currentGroup.push(message);
|
||||
} else {
|
||||
// Assistant messages with content (text or images) are shown as a group if they have content
|
||||
// No matter whether it has tool calls or not
|
||||
yieldCurrentGroup();
|
||||
currentGroup.push(message);
|
||||
}
|
||||
}
|
||||
messageIndex++;
|
||||
}
|
||||
yieldCurrentGroup();
|
||||
return resultsOfGroups;
|
||||
}
|
||||
|
||||
export function extractTextFromMessage(message: Message) {
|
||||
if (typeof message.content === "string") {
|
||||
return message.content.trim();
|
||||
}
|
||||
if (Array.isArray(message.content)) {
|
||||
return message.content
|
||||
.map((content) => (content.type === "text" ? content.text : ""))
|
||||
.join("\n")
|
||||
.trim();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
export function extractContentFromMessage(message: Message) {
|
||||
if (typeof message.content === "string") {
|
||||
return message.content.trim();
|
||||
}
|
||||
if (Array.isArray(message.content)) {
|
||||
return message.content
|
||||
.map((content) => {
|
||||
switch (content.type) {
|
||||
case "text":
|
||||
return content.text;
|
||||
case "image_url":
|
||||
const imageURL = extractURLFromImageURLContent(content.image_url);
|
||||
return ``;
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
})
|
||||
.join("\n")
|
||||
.trim();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
export function extractReasoningContentFromMessage(message: Message) {
|
||||
if (message.type !== "ai" || !message.additional_kwargs) {
|
||||
return null;
|
||||
}
|
||||
if ("reasoning_content" in message.additional_kwargs) {
|
||||
return message.additional_kwargs.reasoning_content as string | null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export function extractURLFromImageURLContent(
|
||||
content:
|
||||
| string
|
||||
| {
|
||||
url: string;
|
||||
},
|
||||
) {
|
||||
if (typeof content === "string") {
|
||||
return content;
|
||||
}
|
||||
return content.url;
|
||||
}
|
||||
|
||||
export function hasContent(message: Message) {
|
||||
if (typeof message.content === "string") {
|
||||
return message.content.trim().length > 0;
|
||||
}
|
||||
if (Array.isArray(message.content)) {
|
||||
return message.content.length > 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export function hasReasoning(message: Message) {
|
||||
return (
|
||||
message.type === "ai" &&
|
||||
typeof message.additional_kwargs?.reasoning_content === "string"
|
||||
);
|
||||
}
|
||||
|
||||
export function hasToolCalls(message: Message) {
|
||||
return (
|
||||
message.type === "ai" && message.tool_calls && message.tool_calls.length > 0
|
||||
);
|
||||
}
|
||||
|
||||
export function findToolCallResult(toolCallId: string, messages: Message[]) {
|
||||
for (const message of messages) {
|
||||
if (message.type === "tool" && message.tool_call_id === toolCallId) {
|
||||
const content = extractTextFromMessage(message);
|
||||
if (content) {
|
||||
return content;
|
||||
}
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
@@ -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> {}
|
||||
15
frontend/src/core/threads/types.ts
Normal file
15
frontend/src/core/threads/types.ts
Normal 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;
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
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) {
|
||||
return `/workspace/chats/${thread.thread_id}`;
|
||||
}
|
||||
@@ -19,9 +19,9 @@ export function textOfMessage(message: BaseMessage) {
|
||||
return null;
|
||||
}
|
||||
|
||||
export function titleOfThread(thread: MessageThread) {
|
||||
export function titleOfThread(thread: AgentThread) {
|
||||
if (thread.values && "title" in thread.values) {
|
||||
return thread.values.title as string;
|
||||
return thread.values.title;
|
||||
}
|
||||
return "Untitled";
|
||||
}
|
||||
Reference in New Issue
Block a user