|
| 1 | +import cls from 'classnames'; |
1 | 2 | import React, {
|
2 | 3 | Fragment,
|
3 | 4 | ReactNode,
|
@@ -40,6 +41,7 @@ import {
|
40 | 41 | IChatResponseProgressFileTreeData,
|
41 | 42 | IChatToolContent,
|
42 | 43 | URI,
|
| 44 | + localize, |
43 | 45 | } from '@opensumi/ide-core-common';
|
44 | 46 | import { IIconService } from '@opensumi/ide-theme';
|
45 | 47 | import { IMarkdownString, MarkdownString } from '@opensumi/monaco-editor-core/esm/vs/base/common/htmlContent';
|
@@ -224,6 +226,28 @@ export const ChatReply = (props: IChatReplyProps) => {
|
224 | 226 | const chatApiService = useInjectable<ChatService>(ChatServiceToken);
|
225 | 227 | const chatAgentService = useInjectable<IChatAgentService>(IChatAgentService);
|
226 | 228 | const chatRenderRegistry = useInjectable<ChatRenderRegistry>(ChatRenderRegistryToken);
|
| 229 | + const [collapseThinkingIndexSet, setCollapseThinkingIndexSet] = useState<Set<number>>( |
| 230 | + !request.response.isComplete |
| 231 | + ? new Set() |
| 232 | + : new Set( |
| 233 | + request.response.responseContents |
| 234 | + .map((item, index) => (item.kind === 'reasoning' ? index : -1)) |
| 235 | + .filter((item) => item !== -1), |
| 236 | + ), |
| 237 | + ); |
| 238 | + |
| 239 | + useEffect(() => { |
| 240 | + if (request.response.isComplete) { |
| 241 | + setCollapseThinkingIndexSet( |
| 242 | + new Set( |
| 243 | + request.response.responseContents |
| 244 | + .map((item, index) => (item.kind === 'reasoning' ? index : -1)) |
| 245 | + .filter((item) => item !== -1), |
| 246 | + ), |
| 247 | + ); |
| 248 | + } |
| 249 | + }, [request.response.isComplete]); |
| 250 | + |
227 | 251 | useEffect(() => {
|
228 | 252 | const disposableCollection = new DisposableCollection();
|
229 | 253 |
|
@@ -263,23 +287,6 @@ export const ChatReply = (props: IChatReplyProps) => {
|
263 | 287 | onRegenerate?.();
|
264 | 288 | }, [onRegenerate]);
|
265 | 289 |
|
266 |
| - const onStop = () => { |
267 |
| - if (onDone) { |
268 |
| - onDone(); |
269 |
| - } |
270 |
| - aiReporter.end(relationId, { |
271 |
| - assistantMessage: request.response.responseText, |
272 |
| - replytime: Date.now() - startTime, |
273 |
| - success: false, |
274 |
| - isStop: true, |
275 |
| - command, |
276 |
| - agentId, |
277 |
| - messageId: msgId, |
278 |
| - sessionId: aiChatService.sessionModel.sessionId, |
279 |
| - }); |
280 |
| - aiChatService.cancelRequest(); |
281 |
| - }; |
282 |
| - |
283 | 290 | const renderMarkdown = useCallback(
|
284 | 291 | (markdown: IMarkdownString) => {
|
285 | 292 | if (chatRenderRegistry.chatAIRoleRender) {
|
@@ -313,12 +320,48 @@ export const ChatReply = (props: IChatReplyProps) => {
|
313 | 320 | node = <ComponentRender component={item.component} value={item.value} messageId={msgId} />;
|
314 | 321 | } else if (item.kind === 'toolCall') {
|
315 | 322 | node = <ToolCallRender toolCall={item.content} messageId={msgId} />;
|
| 323 | + } else if (item.kind === 'reasoning') { |
| 324 | + // 思考中必然为最后一条 |
| 325 | + const isThinking = index === request.response.responseContents.length - 1 && !request.response.isComplete; |
| 326 | + node = ( |
| 327 | + <div className={cls(styles.reasoning, { [styles.thinking]: isThinking })}> |
| 328 | + <Button |
| 329 | + size='small' |
| 330 | + type='secondary' |
| 331 | + className={styles.thinking} |
| 332 | + onClick={() => { |
| 333 | + if (isThinking) { |
| 334 | + return; |
| 335 | + } |
| 336 | + if (collapseThinkingIndexSet.has(index)) { |
| 337 | + collapseThinkingIndexSet.delete(index); |
| 338 | + } else { |
| 339 | + collapseThinkingIndexSet.add(index); |
| 340 | + } |
| 341 | + setCollapseThinkingIndexSet(new Set(collapseThinkingIndexSet)); |
| 342 | + }} |
| 343 | + > |
| 344 | + <Icon iconClass='codicon codicon-sparkle' /> |
| 345 | + {localize('aiNative.chat.thinking')} |
| 346 | + {isThinking ? ( |
| 347 | + <Loading /> |
| 348 | + ) : collapseThinkingIndexSet.has(index) ? ( |
| 349 | + <Icon iconClass='codicon codicon-chevron-right' /> |
| 350 | + ) : ( |
| 351 | + <Icon iconClass='codicon codicon-chevron-down' /> |
| 352 | + )} |
| 353 | + </Button> |
| 354 | + {!collapseThinkingIndexSet.has(index) ? ( |
| 355 | + <div className={styles.reasoning_content}>{renderMarkdown(new MarkdownString(item.content))}</div> |
| 356 | + ) : null} |
| 357 | + </div> |
| 358 | + ); |
316 | 359 | } else {
|
317 | 360 | node = renderMarkdown(item.content);
|
318 | 361 | }
|
319 | 362 | return <Fragment key={`${item.kind}-${index}`}>{node}</Fragment>;
|
320 | 363 | }),
|
321 |
| - [request.response.responseContents], |
| 364 | + [request.response.responseContents, collapseThinkingIndexSet], |
322 | 365 | );
|
323 | 366 |
|
324 | 367 | const followupNode = React.useMemo(() => {
|
|
0 commit comments