fix: remove chat message on GUI (#5114)

* fix: remove chat message on GUI

* Update web-app/src/containers/ThreadContent.tsx

Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>

* chore: fix message removal sequence

* chore: add comment

* Update web-app/src/containers/ThreadContent.tsx

Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>

---------

Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>
This commit is contained in:
Louis 2025-05-27 15:13:47 +07:00 committed by GitHub
parent bbfc754fa4
commit c6ce193256
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 51 additions and 29 deletions

View File

@ -24,10 +24,6 @@ import { useModelProvider } from '@/hooks/useModelProvider'
import { useAppState } from '@/hooks/useAppState'
import { MovingBorder } from './MovingBorder'
import { MCPTool } from '@/types/completion'
import { listen } from '@tauri-apps/api/event'
import { SystemEvent } from '@/types/events'
import { getTools } from '@/services/mcp'
import { useChat } from '@/hooks/useChat'
import DropdownModelProvider from '@/containers/DropdownModelProvider'
import { ModelLoader } from '@/containers/loaders/ModelLoader'
@ -46,8 +42,7 @@ const ChatInput = ({
const textareaRef = useRef<HTMLTextAreaElement>(null)
const [isFocused, setIsFocused] = useState(false)
const [rows, setRows] = useState(1)
const { streamingContent, updateTools, abortControllers, loadingModel } =
useAppState()
const { streamingContent, abortControllers, loadingModel } = useAppState()
const { prompt, setPrompt } = usePrompt()
const { currentThreadId } = useThreads()
const { t } = useTranslation()
@ -102,22 +97,6 @@ const ChatInput = ({
}
}, [])
useEffect(() => {
function setTools() {
getTools().then((data: MCPTool[]) => {
updateTools(data)
})
}
setTools()
let unsubscribe = () => {}
listen(SystemEvent.MCP_UPDATE, setTools).then((unsub) => {
// Unsubscribe from the event when the component unmounts
unsubscribe = unsub
})
return unsubscribe
}, [updateTools])
// Focus when component mounts
useEffect(() => {
if (textareaRef.current) {

View File

@ -132,12 +132,25 @@ export const ThreadContent = memo(
}, [deleteMessage, getMessages, item, sendMessage])
const removeMessage = useCallback(() => {
if (item.role === 'assistant' || item.role === 'tool') {
const threadMessages = getMessages(item.thread_id)
if (
item.index !== undefined &&
(item.role === 'assistant' || item.role === 'tool')
) {
const threadMessages = getMessages(item.thread_id).slice(
0,
item.index + 1
)
let toSendMessage = threadMessages.pop()
while (toSendMessage && toSendMessage?.role !== 'user') {
deleteMessage(toSendMessage.thread_id, toSendMessage.id ?? '')
toSendMessage = threadMessages.pop()
// Stop deletion when encountering an assistant message that isnt a tool call
if (
toSendMessage &&
toSendMessage.role === 'assistant' &&
!('tool_calls' in (toSendMessage.metadata ?? {}))
)
break
}
} else {
deleteMessage(item.thread_id, item.id)

View File

@ -1,4 +1,4 @@
import { useCallback, useMemo } from 'react'
import { useCallback, useEffect, useMemo } from 'react'
import { usePrompt } from './usePrompt'
import { useModelProvider } from './useModelProvider'
import { useThreads } from './useThreads'
@ -21,18 +21,28 @@ import { CompletionMessagesBuilder } from '@/lib/messages'
import { ChatCompletionMessageToolCall } from 'openai/resources'
import { useAssistant } from './useAssistant'
import { toast } from 'sonner'
import { getTools } from '@/services/mcp'
import { MCPTool } from '@/types/completion'
import { listen } from '@tauri-apps/api/event'
import { SystemEvent } from '@/types/events'
export const useChat = () => {
const { prompt, setPrompt } = usePrompt()
const { tools, updateTokenSpeed, resetTokenSpeed } = useAppState()
const {
tools,
updateTokenSpeed,
resetTokenSpeed,
updateTools,
updateStreamingContent,
updateLoadingModel,
setAbortController,
} = useAppState()
const { currentAssistant } = useAssistant()
const { getProviderByName, selectedModel, selectedProvider } =
useModelProvider()
const { getCurrentThread: retrieveThread, createThread } = useThreads()
const { updateStreamingContent, updateLoadingModel, setAbortController } =
useAppState()
const { getMessages, addMessage } = useMessages()
const router = useRouter()
@ -40,6 +50,22 @@ export const useChat = () => {
return getProviderByName(selectedProvider)
}, [selectedProvider, getProviderByName])
useEffect(() => {
function setTools() {
getTools().then((data: MCPTool[]) => {
updateTools(data)
})
}
setTools()
let unsubscribe = () => {}
listen(SystemEvent.MCP_UPDATE, setTools).then((unsub) => {
// Unsubscribe from the event when the component unmounts
unsubscribe = unsub
})
return unsubscribe
}, [updateTools])
const getCurrentThread = useCallback(async () => {
let currentThread = retrieveThread()
if (!currentThread) {

View File

@ -199,7 +199,11 @@ function ThreadDetail() {
showAssistant={
item.role === 'assistant' &&
(index === 0 ||
messages[index - 1].role !== 'assistant')
messages[index - 1]?.role !== 'assistant' ||
!(
messages[index - 1]?.metadata &&
'tool_calls' in (messages[index - 1].metadata ?? {})
))
}
index={index}
/>