@opensumi/ide-ai-native 3.8.3-next-1741767755.0 → 3.8.3-next-1741769014.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/browser/chat/chat-agent.service.d.ts +1 -1
- package/lib/browser/chat/chat-agent.service.d.ts.map +1 -1
- package/lib/browser/chat/chat-agent.service.js +7 -7
- package/lib/browser/chat/chat-agent.service.js.map +1 -1
- package/lib/browser/chat/chat.view.d.ts.map +1 -1
- package/lib/browser/chat/chat.view.js +18 -52
- package/lib/browser/chat/chat.view.js.map +1 -1
- package/lib/browser/components/{chat-context/context-selector.d.ts → ChatContext/ContextSelector.d.ts} +1 -1
- package/lib/browser/components/ChatContext/ContextSelector.d.ts.map +1 -0
- package/lib/browser/components/{chat-context/context-selector.js → ChatContext/ContextSelector.js} +1 -1
- package/lib/browser/components/ChatContext/ContextSelector.js.map +1 -0
- package/lib/browser/components/ChatContext/index.d.ts.map +1 -0
- package/lib/browser/components/{chat-context → ChatContext}/index.js +2 -2
- package/lib/browser/components/ChatContext/index.js.map +1 -0
- package/lib/browser/components/ChatEditor.d.ts +2 -11
- package/lib/browser/components/ChatEditor.d.ts.map +1 -1
- package/lib/browser/components/ChatEditor.js +6 -66
- package/lib/browser/components/ChatEditor.js.map +1 -1
- package/lib/browser/components/components.module.less +0 -25
- package/lib/browser/context/llm-context.service.d.ts +2 -10
- package/lib/browser/context/llm-context.service.d.ts.map +1 -1
- package/lib/browser/context/llm-context.service.js +2 -71
- package/lib/browser/context/llm-context.service.js.map +1 -1
- package/lib/common/llm-context.d.ts +1 -15
- package/lib/common/llm-context.d.ts.map +1 -1
- package/lib/common/llm-context.js.map +1 -1
- package/lib/common/prompts/context-prompt-provider.d.ts +2 -12
- package/lib/common/prompts/context-prompt-provider.d.ts.map +1 -1
- package/lib/common/prompts/context-prompt-provider.js +30 -94
- package/lib/common/prompts/context-prompt-provider.js.map +1 -1
- package/package.json +23 -23
- package/src/browser/chat/chat-agent.service.ts +7 -7
- package/src/browser/chat/chat.view.tsx +21 -72
- package/src/browser/components/{chat-context → ChatContext}/index.tsx +1 -1
- package/src/browser/components/ChatEditor.tsx +9 -126
- package/src/browser/components/components.module.less +0 -25
- package/src/browser/context/llm-context.service.ts +3 -81
- package/src/common/llm-context.ts +1 -16
- package/src/common/prompts/context-prompt-provider.ts +36 -126
- package/lib/browser/components/ChatMentionInput.d.ts +0 -25
- package/lib/browser/components/ChatMentionInput.d.ts.map +0 -1
- package/lib/browser/components/ChatMentionInput.js +0 -221
- package/lib/browser/components/ChatMentionInput.js.map +0 -1
- package/lib/browser/components/chat-context/context-selector.d.ts.map +0 -1
- package/lib/browser/components/chat-context/context-selector.js.map +0 -1
- package/lib/browser/components/chat-context/index.d.ts.map +0 -1
- package/lib/browser/components/chat-context/index.js.map +0 -1
- package/lib/browser/components/mention-input/mention-input.d.ts +0 -5
- package/lib/browser/components/mention-input/mention-input.d.ts.map +0 -1
- package/lib/browser/components/mention-input/mention-input.js +0 -753
- package/lib/browser/components/mention-input/mention-input.js.map +0 -1
- package/lib/browser/components/mention-input/mention-input.module.less +0 -328
- package/lib/browser/components/mention-input/mention-item.d.ts +0 -10
- package/lib/browser/components/mention-input/mention-item.d.ts.map +0 -1
- package/lib/browser/components/mention-input/mention-item.js +0 -16
- package/lib/browser/components/mention-input/mention-item.js.map +0 -1
- package/lib/browser/components/mention-input/mention-panel.d.ts +0 -15
- package/lib/browser/components/mention-input/mention-panel.d.ts.map +0 -1
- package/lib/browser/components/mention-input/mention-panel.js +0 -49
- package/lib/browser/components/mention-input/mention-panel.js.map +0 -1
- package/lib/browser/components/mention-input/types.d.ts +0 -76
- package/lib/browser/components/mention-input/types.d.ts.map +0 -1
- package/lib/browser/components/mention-input/types.js +0 -16
- package/lib/browser/components/mention-input/types.js.map +0 -1
- package/src/browser/components/ChatMentionInput.tsx +0 -268
- package/src/browser/components/mention-input/mention-input.module.less +0 -328
- package/src/browser/components/mention-input/mention-input.tsx +0 -943
- package/src/browser/components/mention-input/mention-item.tsx +0 -24
- package/src/browser/components/mention-input/mention-panel.tsx +0 -89
- package/src/browser/components/mention-input/types.ts +0 -82
- /package/lib/browser/components/{chat-context → ChatContext}/index.d.ts +0 -0
- /package/lib/browser/components/{chat-context → ChatContext}/style.module.less +0 -0
- /package/src/browser/components/{chat-context/context-selector.tsx → ChatContext/ContextSelector.tsx} +0 -0
- /package/src/browser/components/{chat-context → ChatContext}/style.module.less +0 -0
|
@@ -59,7 +59,7 @@ export class ChatAgentService extends Disposable implements IChatAgentService {
|
|
|
59
59
|
private readonly aiReporter: IAIReporter;
|
|
60
60
|
|
|
61
61
|
@Autowired(LLMContextServiceToken)
|
|
62
|
-
protected readonly
|
|
62
|
+
protected readonly contextService: LLMContextService;
|
|
63
63
|
|
|
64
64
|
@Autowired(ChatAgentPromptProvider)
|
|
65
65
|
protected readonly promptProvider: ChatAgentPromptProvider;
|
|
@@ -74,7 +74,7 @@ export class ChatAgentService extends Disposable implements IChatAgentService {
|
|
|
74
74
|
super();
|
|
75
75
|
this.addDispose(this._onDidChangeAgents);
|
|
76
76
|
this.addDispose(
|
|
77
|
-
this.
|
|
77
|
+
this.contextService.onDidContextFilesChangeEvent((event) => {
|
|
78
78
|
if (event.version !== this.contextVersion) {
|
|
79
79
|
this.contextVersion = event.version;
|
|
80
80
|
this.shouldUpdateContext = true;
|
|
@@ -152,9 +152,9 @@ export class ChatAgentService extends Disposable implements IChatAgentService {
|
|
|
152
152
|
if (!this.initialUserMessageMap.has(request.sessionId)) {
|
|
153
153
|
this.initialUserMessageMap.set(request.sessionId, request.message);
|
|
154
154
|
const rawMessage = request.message;
|
|
155
|
-
request.message =
|
|
155
|
+
request.message = this.provideContextMessage(rawMessage, request.sessionId);
|
|
156
156
|
} else if (this.shouldUpdateContext || request.regenerate || history.length === 0) {
|
|
157
|
-
request.message =
|
|
157
|
+
request.message = this.provideContextMessage(request.message, request.sessionId);
|
|
158
158
|
this.shouldUpdateContext = false;
|
|
159
159
|
}
|
|
160
160
|
|
|
@@ -162,9 +162,9 @@ export class ChatAgentService extends Disposable implements IChatAgentService {
|
|
|
162
162
|
return result;
|
|
163
163
|
}
|
|
164
164
|
|
|
165
|
-
private
|
|
166
|
-
const context =
|
|
167
|
-
const fullMessage =
|
|
165
|
+
private provideContextMessage(message: string, sessionId: string) {
|
|
166
|
+
const context = this.contextService.serialize();
|
|
167
|
+
const fullMessage = this.promptProvider.provideContextPrompt(context, message);
|
|
168
168
|
this.aiReporter.send({
|
|
169
169
|
msgType: AIServiceType.Chat,
|
|
170
170
|
actionType: ActionTypeEnum.ContextEnhance,
|
|
@@ -1,14 +1,7 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { MessageList } from 'react-chat-elements';
|
|
3
3
|
|
|
4
|
-
import {
|
|
5
|
-
AINativeConfigService,
|
|
6
|
-
AppConfig,
|
|
7
|
-
LabelService,
|
|
8
|
-
getIcon,
|
|
9
|
-
useInjectable,
|
|
10
|
-
useUpdateOnEvent,
|
|
11
|
-
} from '@opensumi/ide-core-browser';
|
|
4
|
+
import { AppConfig, getIcon, useInjectable, useUpdateOnEvent } from '@opensumi/ide-core-browser';
|
|
12
5
|
import { Popover, PopoverPosition } from '@opensumi/ide-core-browser/lib/components';
|
|
13
6
|
import { EnhanceIcon } from '@opensumi/ide-core-browser/lib/components/ai-native';
|
|
14
7
|
import {
|
|
@@ -21,7 +14,6 @@ import {
|
|
|
21
14
|
ChatMessageRole,
|
|
22
15
|
ChatRenderRegistryToken,
|
|
23
16
|
ChatServiceToken,
|
|
24
|
-
CommandService,
|
|
25
17
|
Disposable,
|
|
26
18
|
DisposableCollection,
|
|
27
19
|
IAIReporter,
|
|
@@ -36,18 +28,16 @@ import {
|
|
|
36
28
|
import { WorkbenchEditorService } from '@opensumi/ide-editor';
|
|
37
29
|
import { IMainLayoutService } from '@opensumi/ide-main-layout';
|
|
38
30
|
import { IMessageService } from '@opensumi/ide-overlay';
|
|
39
|
-
import 'react-chat-elements/dist/main.css';
|
|
40
|
-
import { IWorkspaceService } from '@opensumi/ide-workspace';
|
|
41
31
|
|
|
32
|
+
import 'react-chat-elements/dist/main.css';
|
|
42
33
|
import { AI_CHAT_VIEW_ID, IChatAgentService, IChatInternalService, IChatMessageStructure } from '../../common';
|
|
43
|
-
import { LLMContextService, LLMContextServiceToken } from '../../common/llm-context';
|
|
44
34
|
import { CodeBlockData } from '../../common/types';
|
|
45
35
|
import { FileChange, FileListDisplay } from '../components/ChangeList';
|
|
36
|
+
import { ChatContext } from '../components/ChatContext';
|
|
46
37
|
import { CodeBlockWrapperInput } from '../components/ChatEditor';
|
|
47
38
|
import ChatHistory, { IChatHistoryItem } from '../components/ChatHistory';
|
|
48
39
|
import { ChatInput } from '../components/ChatInput';
|
|
49
40
|
import { ChatMarkdown } from '../components/ChatMarkdown';
|
|
50
|
-
import { ChatMentionInput } from '../components/ChatMentionInput';
|
|
51
41
|
import { ChatNotify, ChatReply } from '../components/ChatReply';
|
|
52
42
|
import { SlashCustomRender } from '../components/SlashCustomRender';
|
|
53
43
|
import { MessageData, createMessageByAI, createMessageByUser } from '../components/utils';
|
|
@@ -115,8 +105,6 @@ export const AIChatView = () => {
|
|
|
115
105
|
const chatFeatureRegistry = useInjectable<ChatFeatureRegistry>(ChatFeatureRegistryToken);
|
|
116
106
|
const chatRenderRegistry = useInjectable<ChatRenderRegistry>(ChatRenderRegistryToken);
|
|
117
107
|
const mcpServerRegistry = useInjectable<IMCPServerRegistry>(TokenMCPServerRegistry);
|
|
118
|
-
const aiNativeConfigService = useInjectable<AINativeConfigService>(AINativeConfigService);
|
|
119
|
-
const llmContextService = useInjectable<LLMContextService>(LLMContextServiceToken);
|
|
120
108
|
|
|
121
109
|
const layoutService = useInjectable<IMainLayoutService>(IMainLayoutService);
|
|
122
110
|
const msgHistoryManager = aiChatService.sessionModel.history;
|
|
@@ -126,9 +114,6 @@ export const AIChatView = () => {
|
|
|
126
114
|
const editorService = useInjectable<WorkbenchEditorService>(WorkbenchEditorService);
|
|
127
115
|
const appConfig = useInjectable<AppConfig>(AppConfig);
|
|
128
116
|
const applyService = useInjectable<BaseApplyService>(BaseApplyService);
|
|
129
|
-
const labelService = useInjectable<LabelService>(LabelService);
|
|
130
|
-
const workspaceService = useInjectable<IWorkspaceService>(IWorkspaceService);
|
|
131
|
-
const commandService = useInjectable<CommandService>(CommandService);
|
|
132
117
|
const [shortcutCommands, setShortcutCommands] = React.useState<ChatSlashCommandItemModel[]>([]);
|
|
133
118
|
|
|
134
119
|
const [changeList, setChangeList] = React.useState<FileChange[]>(getFileChanges(applyService.getSessionCodeBlocks()));
|
|
@@ -199,9 +184,6 @@ export const AIChatView = () => {
|
|
|
199
184
|
if (chatRenderRegistry.chatInputRender) {
|
|
200
185
|
return chatRenderRegistry.chatInputRender;
|
|
201
186
|
}
|
|
202
|
-
if (aiNativeConfigService.capabilities.supportsMCP) {
|
|
203
|
-
return ChatMentionInput;
|
|
204
|
-
}
|
|
205
187
|
return ChatInput;
|
|
206
188
|
}, [chatRenderRegistry.chatInputRender]);
|
|
207
189
|
|
|
@@ -280,7 +262,7 @@ export const AIChatView = () => {
|
|
|
280
262
|
if (loading) {
|
|
281
263
|
return;
|
|
282
264
|
}
|
|
283
|
-
await handleSend(message
|
|
265
|
+
await handleSend(message);
|
|
284
266
|
} else {
|
|
285
267
|
if (message.agentId) {
|
|
286
268
|
setAgentId(message.agentId);
|
|
@@ -367,9 +349,6 @@ export const AIChatView = () => {
|
|
|
367
349
|
text={message}
|
|
368
350
|
agentId={visibleAgentId}
|
|
369
351
|
command={command}
|
|
370
|
-
labelService={labelService}
|
|
371
|
-
workspaceService={workspaceService}
|
|
372
|
-
commandService={commandService}
|
|
373
352
|
/>
|
|
374
353
|
),
|
|
375
354
|
},
|
|
@@ -475,15 +454,7 @@ export const AIChatView = () => {
|
|
|
475
454
|
text: ChatUserRoleRender ? (
|
|
476
455
|
<ChatUserRoleRender content={message} agentId={visibleAgentId} command={command} />
|
|
477
456
|
) : (
|
|
478
|
-
<CodeBlockWrapperInput
|
|
479
|
-
labelService={labelService}
|
|
480
|
-
relationId={relationId}
|
|
481
|
-
text={message}
|
|
482
|
-
agentId={visibleAgentId}
|
|
483
|
-
command={command}
|
|
484
|
-
workspaceService={workspaceService}
|
|
485
|
-
commandService={commandService}
|
|
486
|
-
/>
|
|
457
|
+
<CodeBlockWrapperInput relationId={relationId} text={message} agentId={visibleAgentId} command={command} />
|
|
487
458
|
),
|
|
488
459
|
},
|
|
489
460
|
styles.chat_message_code,
|
|
@@ -667,44 +638,11 @@ export const AIChatView = () => {
|
|
|
667
638
|
);
|
|
668
639
|
|
|
669
640
|
const handleSend = React.useCallback(
|
|
670
|
-
async (
|
|
671
|
-
const reportExtra =
|
|
672
|
-
actionSource: ActionSourceEnum.Chat,
|
|
673
|
-
actionType: ActionTypeEnum.Send,
|
|
674
|
-
};
|
|
675
|
-
agentId = agentId ? agentId : ChatProxyService.AGENT_ID;
|
|
676
|
-
// 提取并替换 {{@file:xxx}} 中的文件内容
|
|
677
|
-
let processedContent = message;
|
|
678
|
-
const filePattern = /\{\{@file:(.*?)\}\}/g;
|
|
679
|
-
const fileMatches = message.match(filePattern);
|
|
680
|
-
let isCleanContext = false;
|
|
681
|
-
if (fileMatches) {
|
|
682
|
-
for (const match of fileMatches) {
|
|
683
|
-
const filePath = match.replace(/\{\{@file:(.*?)\}\}/, '$1');
|
|
684
|
-
if (filePath && !isCleanContext) {
|
|
685
|
-
isCleanContext = true;
|
|
686
|
-
llmContextService.cleanFileContext();
|
|
687
|
-
}
|
|
688
|
-
const fileUri = new URI(filePath);
|
|
689
|
-
llmContextService.addFileToContext(fileUri, undefined, true);
|
|
690
|
-
// 获取文件内容
|
|
691
|
-
// 替换占位符,后续支持自定义渲染时可替换为自定义渲染标签
|
|
692
|
-
processedContent = processedContent.replace(match, `\`<attached_file>${fileUri.displayName}\``);
|
|
693
|
-
}
|
|
694
|
-
}
|
|
641
|
+
async (value: IChatMessageStructure) => {
|
|
642
|
+
const { message, command, reportExtra } = value;
|
|
695
643
|
|
|
696
|
-
const
|
|
697
|
-
|
|
698
|
-
if (folderMatches) {
|
|
699
|
-
for (const match of folderMatches) {
|
|
700
|
-
const folderPath = match.replace(/\{\{@folder:(.*?)\}\}/, '$1');
|
|
701
|
-
const folderUri = new URI(folderPath);
|
|
702
|
-
llmContextService.addFolderToContext(folderUri);
|
|
703
|
-
// 替换占位符,后续支持自定义渲染时可替换为自定义渲染标签
|
|
704
|
-
processedContent = processedContent.replace(match, `\`<attached_folder>${folderUri.displayName}\``);
|
|
705
|
-
}
|
|
706
|
-
}
|
|
707
|
-
return handleAgentReply({ message: processedContent, agentId, command, reportExtra });
|
|
644
|
+
const agentId = value.agentId ? value.agentId : ChatProxyService.AGENT_ID;
|
|
645
|
+
return handleAgentReply({ message, agentId, command, reportExtra });
|
|
708
646
|
},
|
|
709
647
|
[handleAgentReply],
|
|
710
648
|
);
|
|
@@ -821,6 +759,7 @@ export const AIChatView = () => {
|
|
|
821
759
|
</div>
|
|
822
760
|
) : null}
|
|
823
761
|
<div className={styles.chat_input_wrap}>
|
|
762
|
+
<ChatContext />
|
|
824
763
|
<div className={styles.header_operate}>
|
|
825
764
|
<div className={styles.header_operate_left}>
|
|
826
765
|
{shortcutCommands.map((command) => (
|
|
@@ -851,7 +790,17 @@ export const AIChatView = () => {
|
|
|
851
790
|
/>
|
|
852
791
|
)}
|
|
853
792
|
<ChatInputWrapperRender
|
|
854
|
-
onSend={
|
|
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
|
+
}
|
|
855
804
|
disabled={loading}
|
|
856
805
|
enableOptions={true}
|
|
857
806
|
theme={theme}
|
|
@@ -15,7 +15,7 @@ import { WorkbenchEditorService } from '@opensumi/ide-editor/lib/browser/types';
|
|
|
15
15
|
|
|
16
16
|
import { FileContext, LLMContextService, LLMContextServiceToken } from '../../../common/llm-context';
|
|
17
17
|
|
|
18
|
-
import { ContextSelector } from './
|
|
18
|
+
import { ContextSelector } from './ContextSelector';
|
|
19
19
|
import styles from './style.module.less';
|
|
20
20
|
|
|
21
21
|
const getCollapsedHeight = () => ({ height: 0, opacity: 0 });
|
|
@@ -2,24 +2,14 @@ import capitalize from 'lodash/capitalize';
|
|
|
2
2
|
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
|
3
3
|
import Highlight from 'react-highlight';
|
|
4
4
|
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
FILE_COMMANDS,
|
|
8
|
-
IClipboardService,
|
|
9
|
-
LabelService,
|
|
10
|
-
getIcon,
|
|
11
|
-
useInjectable,
|
|
12
|
-
uuid,
|
|
13
|
-
} from '@opensumi/ide-core-browser';
|
|
14
|
-
import { Icon, Popover } from '@opensumi/ide-core-browser/lib/components';
|
|
5
|
+
import { IClipboardService, getIcon, useInjectable, uuid } from '@opensumi/ide-core-browser';
|
|
6
|
+
import { Popover } from '@opensumi/ide-core-browser/lib/components';
|
|
15
7
|
import { EnhanceIcon } from '@opensumi/ide-core-browser/lib/components/ai-native';
|
|
16
8
|
import {
|
|
17
9
|
ActionSourceEnum,
|
|
18
10
|
ActionTypeEnum,
|
|
19
11
|
ChatFeatureRegistryToken,
|
|
20
|
-
CommandService,
|
|
21
12
|
IAIReporter,
|
|
22
|
-
URI,
|
|
23
13
|
localize,
|
|
24
14
|
runWhenIdle,
|
|
25
15
|
} from '@opensumi/ide-core-common';
|
|
@@ -32,9 +22,6 @@ import { ChatFeatureRegistry } from '../chat/chat.feature.registry';
|
|
|
32
22
|
|
|
33
23
|
import styles from './components.module.less';
|
|
34
24
|
import { highLightLanguageSupport } from './highLight';
|
|
35
|
-
import { MentionType } from './mention-input/types';
|
|
36
|
-
|
|
37
|
-
import type { IWorkspaceService } from '@opensumi/ide-workspace';
|
|
38
25
|
|
|
39
26
|
import './highlightTheme.less';
|
|
40
27
|
|
|
@@ -152,56 +139,16 @@ const CodeBlock = ({
|
|
|
152
139
|
renderText,
|
|
153
140
|
agentId = '',
|
|
154
141
|
command = '',
|
|
155
|
-
labelService,
|
|
156
|
-
commandService,
|
|
157
|
-
workspaceService,
|
|
158
142
|
}: {
|
|
159
143
|
content?: string;
|
|
160
144
|
relationId: string;
|
|
161
145
|
renderText?: (t: string) => React.ReactNode;
|
|
162
146
|
agentId?: string;
|
|
163
147
|
command?: string;
|
|
164
|
-
labelService?: LabelService;
|
|
165
|
-
commandService?: CommandService;
|
|
166
|
-
workspaceService?: IWorkspaceService;
|
|
167
148
|
}) => {
|
|
168
149
|
const rgInlineCode = /`([^`]+)`/g;
|
|
169
150
|
const rgBlockCode = /```([^]+?)```/g;
|
|
170
151
|
const rgBlockCodeBefore = /```([^]+)?/g;
|
|
171
|
-
const rgAttachedFile = /<attached_file>(.*)/g;
|
|
172
|
-
const rgAttachedFolder = /<attached_folder>(.*)/g;
|
|
173
|
-
const handleAttachmentClick = useCallback(
|
|
174
|
-
async (text: string, type: MentionType) => {
|
|
175
|
-
const roots = await workspaceService?.roots;
|
|
176
|
-
let uri;
|
|
177
|
-
if (!roots) {
|
|
178
|
-
return;
|
|
179
|
-
}
|
|
180
|
-
for (const root of roots) {
|
|
181
|
-
uri = new URI(root.uri).resolve(text);
|
|
182
|
-
try {
|
|
183
|
-
await commandService?.executeCommand(FILE_COMMANDS.LOCATION.id, uri);
|
|
184
|
-
if (type === MentionType.FILE) {
|
|
185
|
-
await commandService?.executeCommand(EDITOR_COMMANDS.OPEN_RESOURCE.id, uri);
|
|
186
|
-
}
|
|
187
|
-
break;
|
|
188
|
-
} catch {
|
|
189
|
-
continue;
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
},
|
|
193
|
-
[commandService, workspaceService],
|
|
194
|
-
);
|
|
195
|
-
const renderAttachment = (text: string, isFolder = false, key: string) => (
|
|
196
|
-
<span
|
|
197
|
-
className={styles.attachment}
|
|
198
|
-
key={key}
|
|
199
|
-
onClick={() => handleAttachmentClick(text, isFolder ? MentionType.FOLDER : MentionType.FILE)}
|
|
200
|
-
>
|
|
201
|
-
<Icon iconClass={isFolder ? getIcon('folder') : labelService?.getIcon(new URI(text || 'file'))} />
|
|
202
|
-
<span className={styles.attachment_text}>{text}</span>
|
|
203
|
-
</span>
|
|
204
|
-
);
|
|
205
152
|
|
|
206
153
|
const renderCodeEditor = (content: string) => {
|
|
207
154
|
const language = content.split('\n')[0].trim().toLowerCase();
|
|
@@ -246,47 +193,11 @@ const CodeBlock = ({
|
|
|
246
193
|
renderedContent.push(text);
|
|
247
194
|
}
|
|
248
195
|
} else {
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
let lastIndex = 0;
|
|
255
|
-
const fragments: (string | React.ReactNode)[] = [];
|
|
256
|
-
|
|
257
|
-
// 处理文件标记
|
|
258
|
-
fileMatches.forEach((match, matchIndex) => {
|
|
259
|
-
if (match.index !== undefined) {
|
|
260
|
-
const spanText = processedText.slice(lastIndex, match.index);
|
|
261
|
-
if (spanText) {
|
|
262
|
-
fragments.push(<span key={`${index}-${matchIndex}`}>{spanText}</span>);
|
|
263
|
-
}
|
|
264
|
-
fragments.push(renderAttachment(match[1], false, `${index}-tag-${matchIndex}`));
|
|
265
|
-
lastIndex = match.index + match[0].length;
|
|
266
|
-
}
|
|
267
|
-
});
|
|
268
|
-
|
|
269
|
-
// 处理文件夹标记
|
|
270
|
-
folderMatches.forEach((match, matchIndex) => {
|
|
271
|
-
if (match.index !== undefined) {
|
|
272
|
-
const spanText = processedText.slice(lastIndex, match.index);
|
|
273
|
-
if (spanText) {
|
|
274
|
-
fragments.push(<span key={`${index}-${matchIndex}`}>{spanText}</span>);
|
|
275
|
-
}
|
|
276
|
-
fragments.push(renderAttachment(match[1], true, `${index}-tag-${matchIndex}`));
|
|
277
|
-
lastIndex = match.index + match[0].length;
|
|
278
|
-
}
|
|
279
|
-
});
|
|
280
|
-
|
|
281
|
-
fragments.push(processedText.slice(lastIndex));
|
|
282
|
-
renderedContent.push(...fragments);
|
|
283
|
-
} else {
|
|
284
|
-
renderedContent.push(
|
|
285
|
-
<span className={styles.code_inline} key={index}>
|
|
286
|
-
{text}
|
|
287
|
-
</span>,
|
|
288
|
-
);
|
|
289
|
-
}
|
|
196
|
+
renderedContent.push(
|
|
197
|
+
<span className={styles.code_inline} key={index}>
|
|
198
|
+
{text}
|
|
199
|
+
</span>,
|
|
200
|
+
);
|
|
290
201
|
}
|
|
291
202
|
});
|
|
292
203
|
} else {
|
|
@@ -305,29 +216,15 @@ export const CodeBlockWrapper = ({
|
|
|
305
216
|
renderText,
|
|
306
217
|
relationId,
|
|
307
218
|
agentId,
|
|
308
|
-
labelService,
|
|
309
|
-
commandService,
|
|
310
|
-
workspaceService,
|
|
311
219
|
}: {
|
|
312
220
|
text?: string;
|
|
313
221
|
relationId: string;
|
|
314
222
|
renderText?: (t: string) => React.ReactNode;
|
|
315
223
|
agentId?: string;
|
|
316
|
-
labelService?: LabelService;
|
|
317
|
-
commandService?: CommandService;
|
|
318
|
-
workspaceService?: IWorkspaceService;
|
|
319
224
|
}) => (
|
|
320
225
|
<div className={styles.ai_chat_code_wrapper}>
|
|
321
226
|
<div className={styles.render_text}>
|
|
322
|
-
<CodeBlock
|
|
323
|
-
content={text}
|
|
324
|
-
labelService={labelService}
|
|
325
|
-
renderText={renderText}
|
|
326
|
-
relationId={relationId}
|
|
327
|
-
agentId={agentId}
|
|
328
|
-
commandService={commandService}
|
|
329
|
-
workspaceService={workspaceService}
|
|
330
|
-
/>
|
|
227
|
+
<CodeBlock content={text} renderText={renderText} relationId={relationId} agentId={agentId} />
|
|
331
228
|
</div>
|
|
332
229
|
</div>
|
|
333
230
|
);
|
|
@@ -337,17 +234,11 @@ export const CodeBlockWrapperInput = ({
|
|
|
337
234
|
relationId,
|
|
338
235
|
agentId,
|
|
339
236
|
command,
|
|
340
|
-
labelService,
|
|
341
|
-
workspaceService,
|
|
342
|
-
commandService,
|
|
343
237
|
}: {
|
|
344
238
|
text: string;
|
|
345
239
|
relationId: string;
|
|
346
240
|
agentId?: string;
|
|
347
241
|
command?: string;
|
|
348
|
-
labelService?: LabelService;
|
|
349
|
-
workspaceService?: IWorkspaceService;
|
|
350
|
-
commandService?: CommandService;
|
|
351
242
|
}) => {
|
|
352
243
|
const chatFeatureRegistry = useInjectable<ChatFeatureRegistry>(ChatFeatureRegistryToken);
|
|
353
244
|
const [tag, setTag] = useState<string>('');
|
|
@@ -380,15 +271,7 @@ export const CodeBlockWrapperInput = ({
|
|
|
380
271
|
</div>
|
|
381
272
|
)}
|
|
382
273
|
{command && <div className={styles.tag}>/ {command}</div>}
|
|
383
|
-
<CodeBlock
|
|
384
|
-
content={txt}
|
|
385
|
-
labelService={labelService}
|
|
386
|
-
relationId={relationId}
|
|
387
|
-
agentId={agentId}
|
|
388
|
-
command={command}
|
|
389
|
-
workspaceService={workspaceService}
|
|
390
|
-
commandService={commandService}
|
|
391
|
-
/>
|
|
274
|
+
<CodeBlock content={txt} relationId={relationId} agentId={agentId} command={command} />
|
|
392
275
|
</div>
|
|
393
276
|
</div>
|
|
394
277
|
);
|
|
@@ -519,7 +519,6 @@
|
|
|
519
519
|
display: flex;
|
|
520
520
|
font-size: 11px;
|
|
521
521
|
align-items: center;
|
|
522
|
-
min-width: 150px;
|
|
523
522
|
}
|
|
524
523
|
|
|
525
524
|
.mcp_desc {
|
|
@@ -566,27 +565,3 @@
|
|
|
566
565
|
color: var(--descriptionForeground);
|
|
567
566
|
}
|
|
568
567
|
}
|
|
569
|
-
|
|
570
|
-
.attachment {
|
|
571
|
-
display: inline-flex;
|
|
572
|
-
align-items: center;
|
|
573
|
-
padding: 0 4px;
|
|
574
|
-
margin: 0 2px;
|
|
575
|
-
background: var(--badge-background);
|
|
576
|
-
color: var(--badge-foreground);
|
|
577
|
-
border-radius: 3px;
|
|
578
|
-
vertical-align: middle;
|
|
579
|
-
font-size: 12px;
|
|
580
|
-
cursor: pointer;
|
|
581
|
-
:global {
|
|
582
|
-
.kt-icon {
|
|
583
|
-
margin-right: 3px;
|
|
584
|
-
}
|
|
585
|
-
}
|
|
586
|
-
}
|
|
587
|
-
|
|
588
|
-
.attachment_text {
|
|
589
|
-
line-height: 20px;
|
|
590
|
-
vertical-align: middle;
|
|
591
|
-
font-size: 12px;
|
|
592
|
-
}
|
|
@@ -10,7 +10,6 @@ import {
|
|
|
10
10
|
IEditorDocumentModelService,
|
|
11
11
|
} from '@opensumi/ide-editor/lib/browser/doc-model/types';
|
|
12
12
|
import { EditorSelectionChangeEvent } from '@opensumi/ide-editor/lib/browser/types';
|
|
13
|
-
import { FileType, IFileServiceClient } from '@opensumi/ide-file-service';
|
|
14
13
|
import { IMarkerService } from '@opensumi/ide-markers/lib/common/types';
|
|
15
14
|
import { Range } from '@opensumi/ide-monaco';
|
|
16
15
|
|
|
@@ -27,18 +26,13 @@ export class LLMContextServiceImpl extends WithEventBus implements LLMContextSer
|
|
|
27
26
|
@Autowired(IMarkerService)
|
|
28
27
|
protected readonly markerService: IMarkerService;
|
|
29
28
|
|
|
30
|
-
@Autowired(IFileServiceClient)
|
|
31
|
-
protected readonly fileService: IFileServiceClient;
|
|
32
|
-
|
|
33
29
|
private isAutoCollecting = false;
|
|
34
30
|
|
|
35
31
|
private contextVersion = 0;
|
|
36
32
|
|
|
37
33
|
private readonly maxAttachFilesLimit = 10;
|
|
38
|
-
private readonly maxAttachFoldersLimit = 10;
|
|
39
34
|
private readonly maxViewFilesLimit = 20;
|
|
40
|
-
private attachedFiles: FileContext[] = [];
|
|
41
|
-
private attachedFolders: FileContext[] = [];
|
|
35
|
+
private readonly attachedFiles: FileContext[] = [];
|
|
42
36
|
private readonly recentlyViewFiles: FileContext[] = [];
|
|
43
37
|
private readonly onDidContextFilesChangeEmitter = new Emitter<{
|
|
44
38
|
viewed: FileContext[];
|
|
@@ -59,18 +53,6 @@ export class LLMContextServiceImpl extends WithEventBus implements LLMContextSer
|
|
|
59
53
|
}
|
|
60
54
|
}
|
|
61
55
|
|
|
62
|
-
private addFolderToList(folder: FileContext, list: FileContext[], maxLimit: number) {
|
|
63
|
-
const existingIndex = list.findIndex((f) => f.uri.toString() === folder.uri.toString());
|
|
64
|
-
if (existingIndex > -1) {
|
|
65
|
-
list.splice(existingIndex, 1);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
list.push(folder);
|
|
69
|
-
if (list.length > maxLimit) {
|
|
70
|
-
list.shift();
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
56
|
addFileToContext(uri: URI, selection?: [number, number], isManual = false): void {
|
|
75
57
|
if (!uri) {
|
|
76
58
|
return;
|
|
@@ -88,24 +70,12 @@ export class LLMContextServiceImpl extends WithEventBus implements LLMContextSer
|
|
|
88
70
|
this.notifyContextChange();
|
|
89
71
|
}
|
|
90
72
|
|
|
91
|
-
addFolderToContext(uri: URI): void {
|
|
92
|
-
if (!uri) {
|
|
93
|
-
return;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
const file = { uri };
|
|
97
|
-
|
|
98
|
-
this.addFolderToList(file, this.attachedFolders, this.maxAttachFoldersLimit);
|
|
99
|
-
this.notifyContextChange();
|
|
100
|
-
}
|
|
101
|
-
|
|
102
73
|
private notifyContextChange(): void {
|
|
103
74
|
this.onDidContextFilesChangeEmitter.fire(this.getAllContextFiles());
|
|
104
75
|
}
|
|
105
76
|
|
|
106
77
|
cleanFileContext() {
|
|
107
|
-
this.attachedFiles =
|
|
108
|
-
this.attachedFolders = [];
|
|
78
|
+
this.attachedFiles.length = 0;
|
|
109
79
|
this.notifyContextChange();
|
|
110
80
|
}
|
|
111
81
|
|
|
@@ -113,7 +83,6 @@ export class LLMContextServiceImpl extends WithEventBus implements LLMContextSer
|
|
|
113
83
|
return {
|
|
114
84
|
viewed: this.recentlyViewFiles,
|
|
115
85
|
attached: this.attachedFiles,
|
|
116
|
-
attachedFolders: this.attachedFolders,
|
|
117
86
|
version: this.contextVersion++,
|
|
118
87
|
};
|
|
119
88
|
}
|
|
@@ -191,63 +160,16 @@ export class LLMContextServiceImpl extends WithEventBus implements LLMContextSer
|
|
|
191
160
|
this.dispose();
|
|
192
161
|
}
|
|
193
162
|
|
|
194
|
-
|
|
163
|
+
serialize(): SerializedContext {
|
|
195
164
|
const files = this.getAllContextFiles();
|
|
196
165
|
const workspaceRoot = URI.file(this.appConfig.workspaceDir);
|
|
197
166
|
|
|
198
167
|
return {
|
|
199
168
|
recentlyViewFiles: this.serializeRecentlyViewFiles(files.viewed, workspaceRoot),
|
|
200
169
|
attachedFiles: this.serializeAttachedFiles(files.attached, workspaceRoot),
|
|
201
|
-
attachedFolders: await this.serializeAttachedFolders(files.attachedFolders, workspaceRoot),
|
|
202
170
|
};
|
|
203
171
|
}
|
|
204
172
|
|
|
205
|
-
private async serializeAttachedFolders(folders: FileContext[], workspaceRoot: URI): Promise<string[]> {
|
|
206
|
-
// 去重
|
|
207
|
-
const folderPath = Array.from(new Set(folders.map((folder) => folder.uri.toString())));
|
|
208
|
-
return Promise.all(
|
|
209
|
-
folderPath.map(async (folder) => {
|
|
210
|
-
const folderUri = new URI(folder);
|
|
211
|
-
const root = workspaceRoot.relative(folderUri)?.toString() || '/';
|
|
212
|
-
return `\`\`\`\n${root}\n${(await this.getPartiaFolderStructure(folderUri.codeUri.fsPath))
|
|
213
|
-
.map((line) => `- ${line}`)
|
|
214
|
-
.join('\n')}\n\`\`\`\n`;
|
|
215
|
-
}),
|
|
216
|
-
);
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
private async getPartiaFolderStructure(folder: string, level = 2): Promise<string[]> {
|
|
220
|
-
const result: string[] = [];
|
|
221
|
-
const stat = await this.fileService.getFileStat(folder);
|
|
222
|
-
|
|
223
|
-
for (const child of stat?.children || []) {
|
|
224
|
-
const relativePath = new URI(folder).relative(new URI(child.uri))!.toString();
|
|
225
|
-
|
|
226
|
-
if (child.isSymbolicLink) {
|
|
227
|
-
// 处理软链接
|
|
228
|
-
const target = await this.fileService.getFileStat(child.realUri || child.uri);
|
|
229
|
-
if (target) {
|
|
230
|
-
result.push(`${relativePath} -> ${target} (symbolic link)`);
|
|
231
|
-
} else {
|
|
232
|
-
result.push(`${relativePath} (broken symbolic link)`);
|
|
233
|
-
}
|
|
234
|
-
continue;
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
if (child.type === FileType.Directory) {
|
|
238
|
-
result.push(`${relativePath}/`);
|
|
239
|
-
if (level > 1) {
|
|
240
|
-
const subDirStructure = await this.getPartiaFolderStructure(child.uri, level - 1);
|
|
241
|
-
result.push(...subDirStructure.map((subEntry) => `${relativePath}/${subEntry}`));
|
|
242
|
-
}
|
|
243
|
-
} else if (child.type === FileType.File) {
|
|
244
|
-
result.push(relativePath);
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
return result;
|
|
249
|
-
}
|
|
250
|
-
|
|
251
173
|
private serializeRecentlyViewFiles(files: FileContext[], workspaceRoot: URI): string[] {
|
|
252
174
|
return files
|
|
253
175
|
.map((file) => workspaceRoot.relative(file.uri)?.toString() || file.uri.parent.toString())
|
|
@@ -1,14 +1,8 @@
|
|
|
1
1
|
import { Event, URI } from '@opensumi/ide-core-common/lib/utils';
|
|
2
2
|
|
|
3
3
|
export interface LLMContextService {
|
|
4
|
-
/**
|
|
5
|
-
* 开始自动收集
|
|
6
|
-
*/
|
|
7
4
|
startAutoCollection(): void;
|
|
8
5
|
|
|
9
|
-
/**
|
|
10
|
-
* 停止自动收集
|
|
11
|
-
*/
|
|
12
6
|
stopAutoCollection(): void;
|
|
13
7
|
|
|
14
8
|
/**
|
|
@@ -16,19 +10,11 @@ export interface LLMContextService {
|
|
|
16
10
|
*/
|
|
17
11
|
addFileToContext(uri: URI, selection?: [number, number], isManual?: boolean): void;
|
|
18
12
|
|
|
19
|
-
/**
|
|
20
|
-
* 添加文件夹到 context 中
|
|
21
|
-
*/
|
|
22
|
-
addFolderToContext(uri: URI, isManual?: boolean): void;
|
|
23
|
-
|
|
24
13
|
/**
|
|
25
14
|
* 清除上下文
|
|
26
15
|
*/
|
|
27
16
|
cleanFileContext(): void;
|
|
28
17
|
|
|
29
|
-
/**
|
|
30
|
-
* 上下文文件变化事件
|
|
31
|
-
*/
|
|
32
18
|
onDidContextFilesChangeEvent: Event<{ viewed: FileContext[]; attached: FileContext[]; version: number }>;
|
|
33
19
|
|
|
34
20
|
/**
|
|
@@ -38,7 +24,7 @@ export interface LLMContextService {
|
|
|
38
24
|
removeFileFromContext(uri: URI, isManual?: boolean): void;
|
|
39
25
|
|
|
40
26
|
/** 导出为可序列化格式 */
|
|
41
|
-
serialize():
|
|
27
|
+
serialize(): SerializedContext;
|
|
42
28
|
}
|
|
43
29
|
|
|
44
30
|
export interface FileContext {
|
|
@@ -58,5 +44,4 @@ export interface AttachFileContext {
|
|
|
58
44
|
export interface SerializedContext {
|
|
59
45
|
recentlyViewFiles: string[];
|
|
60
46
|
attachedFiles: Array<AttachFileContext>;
|
|
61
|
-
attachedFolders: string[];
|
|
62
47
|
}
|