1
1
import * as React from 'react' ;
2
2
import { MessageList } from 'react-chat-elements' ;
3
3
4
- import { AppConfig , getIcon , useInjectable , useUpdateOnEvent } from '@opensumi/ide-core-browser' ;
4
+ import {
5
+ AINativeConfigService ,
6
+ AppConfig ,
7
+ LabelService ,
8
+ getIcon ,
9
+ useInjectable ,
10
+ useUpdateOnEvent ,
11
+ } from '@opensumi/ide-core-browser' ;
5
12
import { Popover , PopoverPosition } from '@opensumi/ide-core-browser/lib/components' ;
6
13
import { EnhanceIcon } from '@opensumi/ide-core-browser/lib/components/ai-native' ;
7
14
import {
@@ -14,6 +21,7 @@ import {
14
21
ChatMessageRole ,
15
22
ChatRenderRegistryToken ,
16
23
ChatServiceToken ,
24
+ CommandService ,
17
25
Disposable ,
18
26
DisposableCollection ,
19
27
IAIReporter ,
@@ -28,16 +36,19 @@ import {
28
36
import { WorkbenchEditorService } from '@opensumi/ide-editor' ;
29
37
import { IMainLayoutService } from '@opensumi/ide-main-layout' ;
30
38
import { IMessageService } from '@opensumi/ide-overlay' ;
31
-
32
39
import 'react-chat-elements/dist/main.css' ;
40
+ import { IWorkspaceService } from '@opensumi/ide-workspace' ;
41
+
33
42
import { AI_CHAT_VIEW_ID , IChatAgentService , IChatInternalService , IChatMessageStructure } from '../../common' ;
43
+ import { LLMContextService , LLMContextServiceToken } from '../../common/llm-context' ;
34
44
import { CodeBlockData } from '../../common/types' ;
45
+ import { cleanAttachedTextWrapper } from '../../common/utils' ;
35
46
import { FileChange , FileListDisplay } from '../components/ChangeList' ;
36
- import { ChatContext } from '../components/ChatContext' ;
37
47
import { CodeBlockWrapperInput } from '../components/ChatEditor' ;
38
48
import ChatHistory , { IChatHistoryItem } from '../components/ChatHistory' ;
39
49
import { ChatInput } from '../components/ChatInput' ;
40
50
import { ChatMarkdown } from '../components/ChatMarkdown' ;
51
+ import { ChatMentionInput } from '../components/ChatMentionInput' ;
41
52
import { ChatNotify , ChatReply } from '../components/ChatReply' ;
42
53
import { SlashCustomRender } from '../components/SlashCustomRender' ;
43
54
import { MessageData , createMessageByAI , createMessageByUser } from '../components/utils' ;
@@ -105,6 +116,8 @@ export const AIChatView = () => {
105
116
const chatFeatureRegistry = useInjectable < ChatFeatureRegistry > ( ChatFeatureRegistryToken ) ;
106
117
const chatRenderRegistry = useInjectable < ChatRenderRegistry > ( ChatRenderRegistryToken ) ;
107
118
const mcpServerRegistry = useInjectable < IMCPServerRegistry > ( TokenMCPServerRegistry ) ;
119
+ const aiNativeConfigService = useInjectable < AINativeConfigService > ( AINativeConfigService ) ;
120
+ const llmContextService = useInjectable < LLMContextService > ( LLMContextServiceToken ) ;
108
121
109
122
const layoutService = useInjectable < IMainLayoutService > ( IMainLayoutService ) ;
110
123
const msgHistoryManager = aiChatService . sessionModel . history ;
@@ -114,6 +127,9 @@ export const AIChatView = () => {
114
127
const editorService = useInjectable < WorkbenchEditorService > ( WorkbenchEditorService ) ;
115
128
const appConfig = useInjectable < AppConfig > ( AppConfig ) ;
116
129
const applyService = useInjectable < BaseApplyService > ( BaseApplyService ) ;
130
+ const labelService = useInjectable < LabelService > ( LabelService ) ;
131
+ const workspaceService = useInjectable < IWorkspaceService > ( IWorkspaceService ) ;
132
+ const commandService = useInjectable < CommandService > ( CommandService ) ;
117
133
const [ shortcutCommands , setShortcutCommands ] = React . useState < ChatSlashCommandItemModel [ ] > ( [ ] ) ;
118
134
119
135
const [ changeList , setChangeList ] = React . useState < FileChange [ ] > ( getFileChanges ( applyService . getSessionCodeBlocks ( ) ) ) ;
@@ -184,6 +200,9 @@ export const AIChatView = () => {
184
200
if ( chatRenderRegistry . chatInputRender ) {
185
201
return chatRenderRegistry . chatInputRender ;
186
202
}
203
+ if ( aiNativeConfigService . capabilities . supportsMCP ) {
204
+ return ChatMentionInput ;
205
+ }
187
206
return ChatInput ;
188
207
} , [ chatRenderRegistry . chatInputRender ] ) ;
189
208
@@ -262,7 +281,7 @@ export const AIChatView = () => {
262
281
if ( loading ) {
263
282
return ;
264
283
}
265
- await handleSend ( message ) ;
284
+ await handleSend ( message . message , message . agentId , message . command ) ;
266
285
} else {
267
286
if ( message . agentId ) {
268
287
setAgentId ( message . agentId ) ;
@@ -349,6 +368,9 @@ export const AIChatView = () => {
349
368
text = { message }
350
369
agentId = { visibleAgentId }
351
370
command = { command }
371
+ labelService = { labelService }
372
+ workspaceService = { workspaceService }
373
+ commandService = { commandService }
352
374
/>
353
375
) ,
354
376
} ,
@@ -454,7 +476,15 @@ export const AIChatView = () => {
454
476
text : ChatUserRoleRender ? (
455
477
< ChatUserRoleRender content = { message } agentId = { visibleAgentId } command = { command } />
456
478
) : (
457
- < CodeBlockWrapperInput relationId = { relationId } text = { message } agentId = { visibleAgentId } command = { command } />
479
+ < CodeBlockWrapperInput
480
+ labelService = { labelService }
481
+ relationId = { relationId }
482
+ text = { message }
483
+ agentId = { visibleAgentId }
484
+ command = { command }
485
+ workspaceService = { workspaceService }
486
+ commandService = { commandService }
487
+ />
458
488
) ,
459
489
} ,
460
490
styles . chat_message_code ,
@@ -634,15 +664,50 @@ export const AIChatView = () => {
634
664
msgId,
635
665
} ) ;
636
666
} ,
637
- [ chatRenderRegistry , chatRenderRegistry . chatUserRoleRender , msgHistoryManager , scrollToBottom ] ,
667
+ [ chatRenderRegistry , chatRenderRegistry . chatUserRoleRender , msgHistoryManager , scrollToBottom , loading ] ,
638
668
) ;
639
669
640
670
const handleSend = React . useCallback (
641
- async ( value : IChatMessageStructure ) => {
642
- const { message, command, reportExtra } = value ;
671
+ async ( message : string , agentId ?: string , command ?: string ) => {
672
+ const reportExtra = {
673
+ actionSource : ActionSourceEnum . Chat ,
674
+ actionType : ActionTypeEnum . Send ,
675
+ } ;
676
+ agentId = agentId ? agentId : ChatProxyService . AGENT_ID ;
677
+ // 提取并替换 {{@file :xxx}} 中的文件内容
678
+ let processedContent = message ;
679
+ const filePattern = / \{ \{ @ f i l e : ( .* ?) \} \} / g;
680
+ const fileMatches = message . match ( filePattern ) ;
681
+ let isCleanContext = false ;
682
+ if ( fileMatches ) {
683
+ for ( const match of fileMatches ) {
684
+ const filePath = match . replace ( / \{ \{ @ f i l e : ( .* ?) \} \} / , '$1' ) ;
685
+ if ( filePath && ! isCleanContext ) {
686
+ isCleanContext = true ;
687
+ llmContextService . cleanFileContext ( ) ;
688
+ }
689
+ const fileUri = new URI ( filePath ) ;
690
+ llmContextService . addFileToContext ( fileUri , undefined , true ) ;
691
+ const relativePath = ( await workspaceService . asRelativePath ( fileUri ) ) ?. path || fileUri . displayName ;
692
+ // 获取文件内容
693
+ // 替换占位符,后续支持自定义渲染时可替换为自定义渲染标签
694
+ processedContent = processedContent . replace ( match , `\`<attached_file>${ relativePath } \`` ) ;
695
+ }
696
+ }
643
697
644
- const agentId = value . agentId ? value . agentId : ChatProxyService . AGENT_ID ;
645
- return handleAgentReply ( { message, agentId, command, reportExtra } ) ;
698
+ const folderPattern = / \{ \{ @ f o l d e r : ( .* ?) \} \} / g;
699
+ const folderMatches = processedContent . match ( folderPattern ) ;
700
+ if ( folderMatches ) {
701
+ for ( const match of folderMatches ) {
702
+ const folderPath = match . replace ( / \{ \{ @ f o l d e r : ( .* ?) \} \} / , '$1' ) ;
703
+ const folderUri = new URI ( folderPath ) ;
704
+ llmContextService . addFolderToContext ( folderUri ) ;
705
+ const relativePath = ( await workspaceService . asRelativePath ( folderUri ) ) ?. path || folderUri . displayName ;
706
+ // 替换占位符,后续支持自定义渲染时可替换为自定义渲染标签
707
+ processedContent = processedContent . replace ( match , `\`<attached_folder>${ relativePath } \`` ) ;
708
+ }
709
+ }
710
+ return handleAgentReply ( { message : processedContent , agentId, command, reportExtra } ) ;
646
711
} ,
647
712
[ handleAgentReply ] ,
648
713
) ;
@@ -759,7 +824,6 @@ export const AIChatView = () => {
759
824
</ div >
760
825
) : null }
761
826
< div className = { styles . chat_input_wrap } >
762
- < ChatContext />
763
827
< div className = { styles . header_operate } >
764
828
< div className = { styles . header_operate_left } >
765
829
{ shortcutCommands . map ( ( command ) => (
@@ -790,17 +854,7 @@ export const AIChatView = () => {
790
854
/>
791
855
) }
792
856
< ChatInputWrapperRender
793
- onSend = { ( value , agentId , command ) =>
794
- handleSend ( {
795
- message : value ,
796
- agentId,
797
- command,
798
- reportExtra : {
799
- actionSource : ActionSourceEnum . Chat ,
800
- actionType : ActionTypeEnum . Send ,
801
- } ,
802
- } )
803
- }
857
+ onSend = { handleSend }
804
858
disabled = { loading }
805
859
enableOptions = { true }
806
860
theme = { theme }
@@ -857,12 +911,15 @@ export function DefaultChatViewHeader({
857
911
const getHistoryList = ( ) => {
858
912
const currentMessages = aiChatService . sessionModel . history . getMessages ( ) ;
859
913
const latestUserMessage = currentMessages . findLast ( ( m ) => m . role === ChatMessageRole . User ) ;
860
- setCurrentTitle ( latestUserMessage ? latestUserMessage . content . slice ( 0 , MAX_TITLE_LENGTH ) : '' ) ;
914
+ setCurrentTitle (
915
+ latestUserMessage ? cleanAttachedTextWrapper ( latestUserMessage . content ) . slice ( 0 , MAX_TITLE_LENGTH ) : '' ,
916
+ ) ;
861
917
setHistoryList (
862
918
aiChatService . getSessions ( ) . map ( ( session ) => {
863
919
const history = session . history ;
864
920
const messages = history . getMessages ( ) ;
865
- const title = messages . length > 0 ? messages [ 0 ] . content . slice ( 0 , MAX_TITLE_LENGTH ) : '' ;
921
+ const title =
922
+ messages . length > 0 ? cleanAttachedTextWrapper ( messages [ 0 ] . content ) . slice ( 0 , MAX_TITLE_LENGTH ) : '' ;
866
923
const updatedAt = messages . length > 0 ? messages [ messages . length - 1 ] . replyStartTime || 0 : 0 ;
867
924
// const loading = session.requests[session.requests.length - 1]?.response.isComplete;
868
925
return {
0 commit comments