fix: duplicate chat content (#916)

* style: add dark drop shadow to images

* docs: add release note

* style: add dark

* fix: duplicate chat content

* docs: add release note

* chore: update history list
This commit is contained in:
BiggerRain
2025-10-11 10:33:09 +08:00
committed by GitHub
parent a9a4b5319c
commit 4e58bc4b2c
12 changed files with 252 additions and 124 deletions

View File

@@ -19,6 +19,7 @@ feat: support opening logs from about page #915
### 🐛 Bug fix ### 🐛 Bug fix
fix: automatic update of service list #913 fix: automatic update of service list #913
fix: duplicate chat content #916
### ✈️ Improvements ### ✈️ Improvements

View File

@@ -41,6 +41,7 @@ interface ChatAIProps {
startPage?: StartPage; startPage?: StartPage;
formatUrl?: (data: any) => string; formatUrl?: (data: any) => string;
instanceId?: string; instanceId?: string;
getChatHistoryChatPage?: () => void;
} }
export interface SendMessageParams { export interface SendMessageParams {
@@ -52,6 +53,7 @@ export interface ChatAIRef {
init: (params: SendMessageParams) => void; init: (params: SendMessageParams) => void;
cancelChat: () => void; cancelChat: () => void;
clearChat: () => void; clearChat: () => void;
onSelectChat: (chat: Chat) => void;
} }
const ChatAI = memo( const ChatAI = memo(
@@ -73,6 +75,7 @@ const ChatAI = memo(
startPage, startPage,
formatUrl, formatUrl,
instanceId, instanceId,
getChatHistoryChatPage,
}, },
ref ref
) => { ) => {
@@ -80,6 +83,7 @@ const ChatAI = memo(
init: init, init: init,
cancelChat: () => cancelChat(activeChat), cancelChat: () => cancelChat(activeChat),
clearChat: clearChat, clearChat: clearChat,
onSelectChat: onSelectChat,
})); }));
const curChatEnd = useChatStore((state) => state.curChatEnd); const curChatEnd = useChatStore((state) => state.curChatEnd);
@@ -193,7 +197,8 @@ const ChatAI = memo(
isDeepThinkActive, isDeepThinkActive,
isMCPActive, isMCPActive,
changeInput, changeInput,
showChatHistory showChatHistory,
getChatHistoryChatPage,
); );
const { dealMsg } = useMessageHandler( const { dealMsg } = useMessageHandler(

View File

@@ -5,7 +5,6 @@ import { ChatMessage } from "@/components/ChatMessage";
import { Greetings } from "./Greetings"; import { Greetings } from "./Greetings";
import AttachmentList from "@/components/Assistant/AttachmentList"; import AttachmentList from "@/components/Assistant/AttachmentList";
import { useChatScroll } from "@/hooks/useChatScroll"; import { useChatScroll } from "@/hooks/useChatScroll";
import type { Chat, IChunkData } from "@/types/chat"; import type { Chat, IChunkData } from "@/types/chat";
import { useConnectStore } from "@/stores/connectStore"; import { useConnectStore } from "@/stores/connectStore";
// import SessionFile from "./SessionFile"; // import SessionFile from "./SessionFile";
@@ -45,20 +44,23 @@ export const ChatContent = ({
handleSendMessage, handleSendMessage,
formatUrl, formatUrl,
}: ChatContentProps) => { }: ChatContentProps) => {
const { currentSessionId, setCurrentSessionId } = useConnectStore();
const { t } = useTranslation(); const { t } = useTranslation();
const { uploadAttachments } = useChatStore(); const currentSessionId = useConnectStore((state) => state.currentSessionId);
const setCurrentSessionId = useConnectStore(
(state) => state.setCurrentSessionId
);
const visibleStartPage = useConnectStore((state) => state.visibleStartPage);
const uploadAttachments = useChatStore((state) => state.uploadAttachments);
const curChatEnd = useChatStore((state) => state.curChatEnd);
const messagesEndRef = useRef<HTMLDivElement>(null); const messagesEndRef = useRef<HTMLDivElement>(null);
const { scrollToBottom } = useChatScroll(messagesEndRef); const { scrollToBottom } = useChatScroll(messagesEndRef);
const scrollRef = useRef<HTMLDivElement>(null); const scrollRef = useRef<HTMLDivElement>(null);
const [isAtBottom, setIsAtBottom] = useState(true); const [isAtBottom, setIsAtBottom] = useState(true);
const visibleStartPage = useConnectStore((state) => state.visibleStartPage);
const curChatEnd = useChatStore((state) => state.curChatEnd);
useEffect(() => { useEffect(() => {
setIsAtBottom(true); setIsAtBottom(true);

View File

@@ -139,7 +139,7 @@ const ExtensionStore = ({ extensionId }: { extensionId?: string }) => {
} }
); );
console.log("search_extension", result); // console.log("search_extension", result);
setList(result ?? []); setList(result ?? []);

View File

@@ -121,7 +121,7 @@ export default function ChatInput({
const handleSubmit = useCallback(() => { const handleSubmit = useCallback(() => {
const trimmedValue = inputValue.trim(); const trimmedValue = inputValue.trim();
console.log("handleSubmit", trimmedValue, disabled); // console.log("handleSubmit", trimmedValue, disabled);
if ((trimmedValue || !isEmpty(uploadAttachments)) && !disabled) { if ((trimmedValue || !isEmpty(uploadAttachments)) && !disabled) {
changeInput(""); changeInput("");

View File

@@ -29,7 +29,8 @@ export function useChatActions(
isDeepThinkActive?: boolean, isDeepThinkActive?: boolean,
isMCPActive?: boolean, isMCPActive?: boolean,
changeInput?: (val: string) => void, changeInput?: (val: string) => void,
showChatHistory?: boolean showChatHistory?: boolean,
getChatHistoryChatPage?: () => void,
) { ) {
const isCurrentLogin = useAuthStore((state) => state.isCurrentLogin); const isCurrentLogin = useAuthStore((state) => state.isCurrentLogin);
@@ -197,17 +198,17 @@ export function useChatActions(
) { ) {
try { try {
const response = JSON.parse(msg); const response = JSON.parse(msg);
console.log("first", response); // console.log("first", response);
let updatedChat: Chat; let updatedChat: Chat;
if (Array.isArray(response)) { if (Array.isArray(response)) {
curIdRef.current = response[0]?._id; curIdRef.current = response[0]?._id;
curSessionIdRef.current = response[0]?._source?.session_id; curSessionIdRef.current = response[0]?._source?.session_id;
console.log( // console.log(
"curIdRef-curSessionIdRef-Array", // "curIdRef-curSessionIdRef-Array",
curIdRef.current, // curIdRef.current,
curSessionIdRef.current // curSessionIdRef.current
); // );
updatedChat = { updatedChat = {
...updatedChatRef.current, ...updatedChatRef.current,
messages: [ messages: [
@@ -215,16 +216,16 @@ export function useChatActions(
...(response || []), ...(response || []),
], ],
}; };
console.log("array", updatedChat, updatedChatRef.current?.messages); // console.log("array", updatedChat, updatedChatRef.current?.messages);
} else { } else {
const newChat: Chat = response; const newChat: Chat = response;
curIdRef.current = response?.payload?.id; curIdRef.current = response?.payload?.id;
curSessionIdRef.current = response?.payload?.session_id; curSessionIdRef.current = response?.payload?.session_id;
console.log( // console.log(
"curIdRef-curSessionIdRef", // "curIdRef-curSessionIdRef",
curIdRef.current, // curIdRef.current,
curSessionIdRef.current // curSessionIdRef.current
); // );
newChat._source = { newChat._source = {
...response?.payload, ...response?.payload,
@@ -252,7 +253,7 @@ export function useChatActions(
async (timestamp: number) => { async (timestamp: number) => {
cleanupListeners(); cleanupListeners();
console.log("setupListeners", clientId, timestamp); // console.log("setupListeners", clientId, timestamp);
const unlisten_chat_message = await platformAdapter.listenEvent( const unlisten_chat_message = await platformAdapter.listenEvent(
`chat-stream-${clientId}-${timestamp}`, `chat-stream-${clientId}-${timestamp}`,
(event) => { (event) => {
@@ -300,12 +301,45 @@ export function useChatActions(
[setupListeners] [setupListeners]
); );
const getChatHistory = useCallback(async () => {
let response: any;
if (isTauri) {
if (await unrequitable()) {
return setChats([]);
}
response = await platformAdapter.commands("chat_history", {
serverId: currentService?.id,
from: 0,
size: 100,
query: keyword,
});
response = response ? JSON.parse(response) : null;
} else {
const [_error, res] = await Get(`/chat/_history`, {
from: 0,
size: 100,
});
response = res;
}
const hits = response?.hits?.hits || [];
setChats(hits);
}, [
currentService?.id,
keyword,
isTauri,
currentService?.enabled,
isCurrentLogin,
]);
const createNewChat = useCallback( const createNewChat = useCallback(
async (params?: SendMessageParams) => { async (params?: SendMessageParams) => {
const { message, attachments } = params || {}; const { message, attachments } = params || {};
console.log("message", message); // console.log("message", message);
console.log("attachments", attachments); // console.log("attachments", attachments);
if (!message && isEmpty(attachments)) return; if (!message && isEmpty(attachments)) return;
@@ -325,7 +359,7 @@ export function useChatActions(
if (isTauri) { if (isTauri) {
if (!currentService?.id) return; if (!currentService?.id) return;
console.log("chat_create", clientId, timestamp); // console.log("chat_create", clientId, timestamp);
await platformAdapter.commands("chat_create", { await platformAdapter.commands("chat_create", {
serverId: currentService?.id, serverId: currentService?.id,
@@ -334,7 +368,7 @@ export function useChatActions(
queryParams, queryParams,
clientId: `chat-stream-${clientId}-${timestamp}`, clientId: `chat-stream-${clientId}-${timestamp}`,
}); });
console.log("_create end", message); // console.log("_create end", message);
resetChatState(); resetChatState();
} else { } else {
await streamPost({ await streamPost({
@@ -342,12 +376,17 @@ export function useChatActions(
body: { message }, body: { message },
queryParams, queryParams,
onMessage: (line) => { onMessage: (line) => {
console.log("⏳", line); // console.log("⏳", line);
handleChatCreateStreamMessage(line); handleChatCreateStreamMessage(line);
// append to chat box // append to chat box
}, },
}); });
} }
// console.log("showChatHistory", showChatHistory);
if (showChatHistory) {
getChatHistoryChatPage ? getChatHistoryChatPage() : getChatHistory();
}
}, },
[ [
isTauri, isTauri,
@@ -360,6 +399,8 @@ export function useChatActions(
currentAssistant, currentAssistant,
chatClose, chatClose,
clientId, clientId,
showChatHistory,
getChatHistory,
] ]
); );
@@ -386,7 +427,7 @@ export function useChatActions(
if (isTauri) { if (isTauri) {
if (!currentService?.id) return; if (!currentService?.id) return;
console.log("chat_chat", clientId, timestamp); // console.log("chat_chat", clientId, timestamp);
await platformAdapter.commands("chat_chat", { await platformAdapter.commands("chat_chat", {
serverId: currentService?.id, serverId: currentService?.id,
sessionId: newChat?._id, sessionId: newChat?._id,
@@ -395,7 +436,7 @@ export function useChatActions(
attachments, attachments,
clientId: `chat-stream-${clientId}-${timestamp}`, clientId: `chat-stream-${clientId}-${timestamp}`,
}); });
console.log("chat_chat end", message, clientId); // console.log("chat_chat end", message, clientId);
resetChatState(); resetChatState();
} else { } else {
await streamPost({ await streamPost({
@@ -403,7 +444,7 @@ export function useChatActions(
body: { message }, body: { message },
queryParams, queryParams,
onMessage: (line) => { onMessage: (line) => {
console.log("line", line); // console.log("line", line);
handleChatCreateStreamMessage(line); handleChatCreateStreamMessage(line);
// append to chat box // append to chat box
}, },
@@ -468,39 +509,6 @@ export function useChatActions(
[currentService?.id, isTauri] [currentService?.id, isTauri]
); );
const getChatHistory = useCallback(async () => {
let response: any;
if (isTauri) {
if (await unrequitable()) {
return setChats([]);
}
response = await platformAdapter.commands("chat_history", {
serverId: currentService?.id,
from: 0,
size: 100,
query: keyword,
});
response = response ? JSON.parse(response) : null;
} else {
const [_error, res] = await Get(`/chat/_history`, {
from: 0,
size: 100,
});
response = res;
}
const hits = response?.hits?.hits || [];
setChats(hits);
}, [
currentService?.id,
keyword,
isTauri,
currentService?.enabled,
isCurrentLogin,
]);
useEffect(() => { useEffect(() => {
if (showChatHistory) { if (showChatHistory) {
getChatHistory(); getChatHistory();

160
src/hooks/useChatPanel.ts Normal file
View File

@@ -0,0 +1,160 @@
import { useCallback, useState } from "react";
import type { Chat } from "@/types/chat";
import { useConnectStore } from "@/stores/connectStore";
import platformAdapter from "@/utils/platformAdapter";
import { unrequitable } from "@/utils";
export function useChatPanel() {
const {
assistantList,
setCurrentAssistant,
setVisibleStartPage,
currentService,
} = useConnectStore();
const [chats, setChats] = useState<Chat[]>([]);
const [activeChat, setActiveChat] = useState<Chat | undefined>();
const [keyword, setKeyword] = useState("");
const getChatHistory = useCallback(async () => {
try {
if (await unrequitable()) {
return setChats([]);
}
let response: any = await platformAdapter.commands("chat_history", {
serverId: currentService?.id,
from: 0,
size: 100,
query: keyword,
});
response = response ? JSON.parse(response) : null;
const hits = response?.hits?.hits || [];
setChats(hits);
} catch (error) {
console.error("chat_history:", error);
}
}, [keyword, currentService?.id]);
const chatHistory = useCallback(
async (chat: Chat) => {
try {
let response: any = await platformAdapter.commands(
"session_chat_history",
{
serverId: currentService?.id,
sessionId: chat?._id || "",
from: 0,
size: 500,
}
);
response = response ? JSON.parse(response) : null;
const hits = response?.hits?.hits || [];
// set current assistant based on last message
const lastAssistantId = hits[hits.length - 1]?._source?.assistant_id;
const matchedAssistant = assistantList?.find(
(assistant) => assistant._id === lastAssistantId
);
if (matchedAssistant) {
setCurrentAssistant(matchedAssistant);
}
const updatedChat: Chat = {
...chat,
messages: hits,
};
setActiveChat(updatedChat);
} catch (error) {
console.error("session_chat_history:", error);
}
},
[assistantList, currentService?.id, setCurrentAssistant]
);
const onSelectChat = useCallback(
async (chat: any) => {
try {
let response: any = await platformAdapter.commands(
"open_session_chat",
{
serverId: currentService?.id,
sessionId: chat?._id,
}
);
response = response ? JSON.parse(response) : null;
chatHistory(response);
setVisibleStartPage(false);
} catch (error) {
console.error("open_session_chat:", error);
}
},
[currentService?.id, chatHistory, setVisibleStartPage]
);
const deleteChat = useCallback(
async (chatId: string) => {
if (!currentService?.id) return;
await platformAdapter.commands(
"delete_session_chat",
currentService.id,
chatId
);
setChats((prev) => prev.filter((chat) => chat._id !== chatId));
if (activeChat?._id === chatId) {
const remainingChats = chats.filter((chat) => chat._id !== chatId);
setActiveChat(remainingChats[0]);
}
},
[currentService?.id, activeChat?._id, chats]
);
const handleSearch = useCallback((kw: string) => {
setKeyword(kw);
}, []);
const handleRename = useCallback(
(chatId: string, title: string) => {
if (!currentService?.id) return;
setChats((prev) => {
const updatedChats = prev.map((item) => {
if (item._id !== chatId) return item;
return { ...item, _source: { ...item._source, title } };
});
return updatedChats;
});
if (activeChat?._id === chatId) {
setActiveChat((prev) => {
if (!prev) return prev;
return { ...prev, _source: { ...prev._source, title } };
});
}
platformAdapter.commands("update_session_chat", {
serverId: currentService.id,
sessionId: chatId,
title,
});
},
[currentService?.id, activeChat?._id]
);
return {
chats,
setChats,
activeChat,
setActiveChat,
keyword,
setKeyword,
getChatHistory,
chatHistory,
onSelectChat,
deleteChat,
handleSearch,
handleRename,
};
}

View File

@@ -35,7 +35,7 @@ export function useMessageHandler(
} }
messageTimeoutRef.current = setTimeout(() => { messageTimeoutRef.current = setTimeout(() => {
console.log("AI response timeout"); // console.log("AI response timeout");
setTimedoutShow(true); setTimedoutShow(true);
onCancel(); onCancel();
}, (connectionTimeout ?? 120) * 1000); }, (connectionTimeout ?? 120) * 1000);
@@ -108,7 +108,7 @@ export function useMessageHandler(
clearTimeout(messageTimeoutRef.current); clearTimeout(messageTimeoutRef.current);
} }
setCurChatEnd(true); setCurChatEnd(true);
console.log("AI finished output"); // console.log("AI finished output");
return; return;
} }
} catch (error) { } catch (error) {

View File

@@ -198,7 +198,7 @@ export function useSearch() {
} }
} }
console.log("_suggest", searchInput, response); //console.log("_suggest", searchInput, response);
if (timerRef.current) { if (timerRef.current) {
clearTimeout(timerRef.current); clearTimeout(timerRef.current);

View File

@@ -68,7 +68,7 @@ export default function useSettingsWindow() {
useEffect(() => { useEffect(() => {
const unlisten = platformAdapter.listenEvent("open_settings", async (event) => { const unlisten = platformAdapter.listenEvent("open_settings", async (event) => {
console.log("open_settings event received:", event); //console.log("open_settings event received:", event);
const tab = event.payload as string | ""; const tab = event.payload as string | "";
platformAdapter.emitEvent("tab_index", tab); platformAdapter.emitEvent("tab_index", tab);

View File

@@ -52,7 +52,7 @@ export const useStreamChat = (options: Options) => {
unlistenRef.current = await platformAdapter.listenEvent( unlistenRef.current = await platformAdapter.listenEvent(
clientId, clientId,
({ payload }) => { ({ payload }) => {
console.log(clientId, JSON.parse(payload)); //console.log(clientId, JSON.parse(payload));
const chunkData = JSON.parse(payload); const chunkData = JSON.parse(payload);

View File

@@ -35,12 +35,7 @@ export default function StandaloneChat({}: StandaloneChatProps) {
setIsTauri(true); setIsTauri(true);
}, []); }, []);
const { const currentService = useConnectStore((state) => state.currentService);
setCurrentAssistant,
assistantList,
setVisibleStartPage,
currentService,
} = useConnectStore();
const chatAIRef = useRef<ChatAIRef>(null); const chatAIRef = useRef<ChatAIRef>(null);
@@ -77,7 +72,7 @@ export default function StandaloneChat({}: StandaloneChatProps) {
query: keyword, query: keyword,
}); });
response = response ? JSON.parse(response) : null; response = response ? JSON.parse(response) : null;
console.log("_history", response); // console.log("_history", response);
const hits = response?.hits?.hits || []; const hits = response?.hits?.hits || [];
setChats(hits); setChats(hits);
} catch (error) { } catch (error) {
@@ -112,39 +107,6 @@ export default function StandaloneChat({}: StandaloneChatProps) {
chatAIRef.current?.init(params); chatAIRef.current?.init(params);
}; };
const chatHistory = async (chat: typeChat) => {
try {
let response: any = await platformAdapter.commands(
"session_chat_history",
{
serverId: currentService?.id,
sessionId: chat?._id || "",
from: 0,
size: 500,
}
);
response = response ? JSON.parse(response) : null;
console.log("id_history", response);
const hits = response?.hits?.hits || [];
// set current assistant
const lastAssistantId = hits[hits.length - 1]?._source?.assistant_id;
const matchedAssistant = assistantList?.find(
(assistant) => assistant._id === lastAssistantId
);
if (matchedAssistant) {
setCurrentAssistant(matchedAssistant);
}
//
const updatedChat: typeChat = {
...chat,
messages: hits,
};
setActiveChat(updatedChat);
} catch (error) {
console.error("session_chat_history:", error);
}
};
const chatClose = async () => { const chatClose = async () => {
if (!activeChat?._id) return; if (!activeChat?._id) return;
try { try {
@@ -153,26 +115,14 @@ export default function StandaloneChat({}: StandaloneChatProps) {
sessionId: activeChat?._id, sessionId: activeChat?._id,
}); });
response = response ? JSON.parse(response) : null; response = response ? JSON.parse(response) : null;
console.log("_close", response); // console.log("_close", response);
} catch (error) { } catch (error) {
console.error("close_session_chat:", error); console.error("close_session_chat:", error);
} }
}; };
const onSelectChat = async (chat: any) => { const onSelectChat = async (chat: any) => {
chatClose(); chatAIRef.current?.onSelectChat(chat);
try {
let response: any = await platformAdapter.commands("open_session_chat", {
serverId: currentService?.id,
sessionId: chat?._id,
});
response = response ? JSON.parse(response) : null;
console.log("_open", response);
chatHistory(response);
setVisibleStartPage(false);
} catch (error) {
console.error("open_session_chat:", error);
}
}; };
const cancelChat = async () => { const cancelChat = async () => {
@@ -317,6 +267,8 @@ export default function StandaloneChat({}: StandaloneChatProps) {
isChatPage={isChatPage} isChatPage={isChatPage}
getFileUrl={getFileUrl} getFileUrl={getFileUrl}
changeInput={setInput} changeInput={setInput}
showChatHistory={true}
getChatHistoryChatPage={getChatHistory}
/> />
</div> </div>