From 373b2fcd78fb17b71274fea15308e9ab9f35a182 Mon Sep 17 00:00:00 2001 From: suyao Date: Mon, 22 Sep 2025 19:24:20 +0800 Subject: [PATCH] feat: abort --- .../home/Inputbar/AgentSessionInputbar.tsx | 65 ++++++++++++++++++- 1 file changed, 62 insertions(+), 3 deletions(-) diff --git a/src/renderer/src/pages/home/Inputbar/AgentSessionInputbar.tsx b/src/renderer/src/pages/home/Inputbar/AgentSessionInputbar.tsx index 71d12a3929..618d70e668 100644 --- a/src/renderer/src/pages/home/Inputbar/AgentSessionInputbar.tsx +++ b/src/renderer/src/pages/home/Inputbar/AgentSessionInputbar.tsx @@ -1,21 +1,28 @@ +import { Tooltip } from '@heroui/react' import { loggerService } from '@logger' +import { ActionIconButton } from '@renderer/components/Buttons' import { QuickPanelView } from '@renderer/components/QuickPanel' import { useSession } from '@renderer/hooks/agents/useSession' +import { selectNewTopicLoading } from '@renderer/hooks/useMessageOperations' import { getModel } from '@renderer/hooks/useModel' import { useSettings } from '@renderer/hooks/useSettings' import { useTimer } from '@renderer/hooks/useTimer' import PasteService from '@renderer/services/PasteService' -import { useAppDispatch } from '@renderer/store' +import { pauseTrace } from '@renderer/services/SpanManagerService' +import { useAppDispatch, useAppSelector } from '@renderer/store' +import { newMessagesActions, selectMessagesForTopic } from '@renderer/store/newMessage' import { sendMessage as dispatchSendMessage } from '@renderer/store/thunk/messageThunk' import type { Assistant, Message, Model, Topic } from '@renderer/types' import { MessageBlock, MessageBlockStatus } from '@renderer/types/newMessage' import { classNames } from '@renderer/utils' +import { abortCompletion } from '@renderer/utils/abortController' import { buildAgentSessionTopicId } from '@renderer/utils/agentSession' import { getSendMessageShortcutLabel, isSendMessageKeyPressed } from '@renderer/utils/input' import { createMainTextBlock, createMessage } from '@renderer/utils/messageUtils/create' import TextArea, { TextAreaRef } from 'antd/es/input/TextArea' import { isEmpty } from 'lodash' -import React, { CSSProperties, FC, useCallback, useEffect, useRef, useState } from 'react' +import { CirclePause } from 'lucide-react' +import React, { CSSProperties, FC, useCallback, useEffect, useMemo, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import styled from 'styled-components' import { v4 as uuid } from 'uuid' @@ -47,6 +54,8 @@ const AgentSessionInputbar: FC = ({ agentId, sessionId }) => { const { setTimeoutTimer } = useTimer() const dispatch = useAppDispatch() const sessionTopicId = buildAgentSessionTopicId(sessionId) + const topicMessages = useAppSelector((state) => selectMessagesForTopic(state, sessionTopicId)) + const loading = useAppSelector((state) => selectNewTopicLoading(state, sessionTopicId)) const focusTextarea = useCallback(() => { textareaRef.current?.focus() @@ -55,6 +64,28 @@ const AgentSessionInputbar: FC = ({ agentId, sessionId }) => { const inputEmpty = isEmpty(text) const sendDisabled = inputEmpty || !apiServer.enabled + const streamingAskIds = useMemo(() => { + if (!topicMessages) { + return [] + } + + const askIdSet = new Set() + for (const message of topicMessages) { + if (!message) continue + if (message.status === 'processing' || message.status === 'pending') { + if (message.askId) { + askIdSet.add(message.askId) + } else if (message.id) { + askIdSet.add(message.id) + } + } + } + + return Array.from(askIdSet) + }, [topicMessages]) + + const canAbort = loading && streamingAskIds.length > 0 + const handleKeyDown = (event: React.KeyboardEvent) => { //to check if the SendMessage key is pressed //other keys should be ignored @@ -97,6 +128,25 @@ const AgentSessionInputbar: FC = ({ agentId, sessionId }) => { } } + const abortAgentSession = useCallback(async () => { + if (!streamingAskIds.length) { + logger.debug('No active agent session streams to abort', { sessionTopicId }) + return + } + + logger.info('Aborting agent session message generation', { + sessionTopicId, + askIds: streamingAskIds + }) + + for (const askId of streamingAskIds) { + abortCompletion(askId) + } + + pauseTrace(sessionTopicId) + dispatch(newMessagesActions.setTopicLoading({ topicId: sessionTopicId, loading: false })) + }, [dispatch, sessionTopicId, streamingAskIds]) + const sendMessage = useCallback(async () => { if (sendDisabled) { return @@ -233,7 +283,16 @@ const AgentSessionInputbar: FC = ({ agentId, sessionId }) => { onBlur={() => setInputFocus(false)} />
- +
+ + {canAbort && ( + + + + + + )} +