@opensumi/ide-ai-native 3.8.1-next-1740965430.0 → 3.8.1-next-1741061006.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 +0 -8
- package/lib/browser/chat/chat-agent.service.d.ts.map +1 -1
- package/lib/browser/chat/chat-agent.service.js +0 -34
- package/lib/browser/chat/chat-agent.service.js.map +1 -1
- package/lib/browser/chat/chat-model.d.ts.map +1 -1
- package/lib/browser/chat/chat-model.js +2 -3
- package/lib/browser/chat/chat-model.js.map +1 -1
- package/lib/browser/chat/chat-proxy.service.d.ts.map +1 -1
- package/lib/browser/chat/chat-proxy.service.js +1 -1
- package/lib/browser/chat/chat-proxy.service.js.map +1 -1
- package/lib/browser/chat/chat.module.less +2 -1
- package/lib/browser/chat/chat.view.d.ts.map +1 -1
- package/lib/browser/chat/chat.view.js +60 -9
- package/lib/browser/chat/chat.view.js.map +1 -1
- package/lib/browser/components/ChatContext/index.js +2 -2
- package/lib/browser/components/ChatContext/index.js.map +1 -1
- package/lib/browser/components/ChatInput.d.ts.map +1 -1
- package/lib/browser/components/ChatInput.js +1 -25
- package/lib/browser/components/ChatInput.js.map +1 -1
- package/lib/browser/components/ChatToolRender.d.ts.map +1 -1
- package/lib/browser/components/ChatToolRender.js +3 -2
- package/lib/browser/components/ChatToolRender.js.map +1 -1
- package/lib/browser/components/chat-history.module.less +1 -1
- package/lib/browser/components/components.module.less +0 -20
- package/lib/browser/context/llm-context.service.d.ts +5 -18
- package/lib/browser/context/llm-context.service.d.ts.map +1 -1
- package/lib/browser/context/llm-context.service.js +47 -80
- package/lib/browser/context/llm-context.service.js.map +1 -1
- package/lib/browser/contrib/intelligent-completions/intelligent-completions.controller.js +1 -1
- package/lib/browser/contrib/intelligent-completions/intelligent-completions.controller.js.map +1 -1
- package/lib/browser/contrib/intelligent-completions/view/default.d.ts.map +1 -1
- package/lib/browser/contrib/intelligent-completions/view/default.js +6 -0
- package/lib/browser/contrib/intelligent-completions/view/default.js.map +1 -1
- package/lib/browser/layout/layout.module.less +4 -4
- package/lib/browser/mcp/tools/components/index.module.less +0 -1
- package/lib/browser/mcp/tools/createNewFileWithText.d.ts.map +1 -1
- package/lib/browser/mcp/tools/createNewFileWithText.js +0 -1
- package/lib/browser/mcp/tools/createNewFileWithText.js.map +1 -1
- package/lib/browser/mcp/tools/getDiagnosticsByPath.d.ts.map +1 -1
- package/lib/browser/mcp/tools/getDiagnosticsByPath.js +0 -1
- package/lib/browser/mcp/tools/getDiagnosticsByPath.js.map +1 -1
- package/lib/browser/mcp/tools/handlers/RunCommand.d.ts.map +1 -1
- package/lib/browser/mcp/tools/handlers/RunCommand.js +0 -2
- package/lib/browser/mcp/tools/handlers/RunCommand.js.map +1 -1
- package/lib/browser/mcp/tools/runTerminalCmd.d.ts.map +1 -1
- package/lib/browser/mcp/tools/runTerminalCmd.js +0 -1
- package/lib/browser/mcp/tools/runTerminalCmd.js.map +1 -1
- package/lib/browser/preferences/schema.d.ts.map +1 -1
- package/lib/browser/preferences/schema.js +1 -0
- package/lib/browser/preferences/schema.js.map +1 -1
- package/lib/common/llm-context.d.ts +9 -13
- 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 +3 -4
- package/lib/common/prompts/context-prompt-provider.d.ts.map +1 -1
- package/lib/common/prompts/context-prompt-provider.js +22 -33
- package/lib/common/prompts/context-prompt-provider.js.map +1 -1
- package/package.json +23 -23
- package/src/browser/chat/chat-agent.service.ts +3 -42
- package/src/browser/chat/chat-model.ts +6 -11
- package/src/browser/chat/chat-proxy.service.ts +1 -2
- package/src/browser/chat/chat.module.less +2 -1
- package/src/browser/chat/chat.view.tsx +95 -10
- package/src/browser/components/ChatContext/index.tsx +2 -2
- package/src/browser/components/ChatInput.tsx +3 -67
- package/src/browser/components/ChatToolRender.tsx +2 -1
- package/src/browser/components/chat-history.module.less +1 -1
- package/src/browser/components/components.module.less +0 -20
- package/src/browser/context/llm-context.service.ts +54 -93
- package/src/browser/contrib/intelligent-completions/intelligent-completions.controller.ts +1 -1
- package/src/browser/contrib/intelligent-completions/view/default.ts +7 -0
- package/src/browser/layout/layout.module.less +4 -4
- package/src/browser/mcp/tools/components/index.module.less +0 -1
- package/src/browser/mcp/tools/createNewFileWithText.ts +0 -1
- package/src/browser/mcp/tools/getDiagnosticsByPath.ts +0 -1
- package/src/browser/mcp/tools/handlers/RunCommand.ts +0 -2
- package/src/browser/mcp/tools/runTerminalCmd.ts +0 -1
- package/src/browser/preferences/schema.ts +1 -0
- package/src/common/llm-context.ts +4 -10
- package/src/common/prompts/context-prompt-provider.ts +29 -38
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
AINativeConfigService,
|
|
6
6
|
CommandService,
|
|
7
7
|
getIcon,
|
|
8
|
+
useEventEffect,
|
|
8
9
|
useInjectable,
|
|
9
10
|
useUpdateOnEvent,
|
|
10
11
|
} from '@opensumi/ide-core-browser';
|
|
@@ -41,6 +42,8 @@ import {
|
|
|
41
42
|
IChatMessageStructure,
|
|
42
43
|
TokenMCPServerProxyService,
|
|
43
44
|
} from '../../common';
|
|
45
|
+
import { LLMContextService, LLMContextServiceToken } from '../../common/llm-context';
|
|
46
|
+
import { ChatAgentPromptProvider } from '../../common/prompts/context-prompt-provider';
|
|
44
47
|
import { ChatContext } from '../components/ChatContext';
|
|
45
48
|
import { CodeBlockWrapperInput } from '../components/ChatEditor';
|
|
46
49
|
import ChatHistory, { IChatHistoryItem } from '../components/ChatHistory';
|
|
@@ -79,11 +82,18 @@ export const AIChatView = () => {
|
|
|
79
82
|
const chatAgentService = useInjectable<IChatAgentService>(IChatAgentService);
|
|
80
83
|
const chatFeatureRegistry = useInjectable<ChatFeatureRegistry>(ChatFeatureRegistryToken);
|
|
81
84
|
const chatRenderRegistry = useInjectable<ChatRenderRegistry>(ChatRenderRegistryToken);
|
|
85
|
+
const contextService = useInjectable<LLMContextService>(LLMContextServiceToken);
|
|
86
|
+
const promptProvider = useInjectable<ChatAgentPromptProvider>(ChatAgentPromptProvider);
|
|
87
|
+
const mcpServerProxyService = useInjectable<MCPServerProxyService>(TokenMCPServerProxyService);
|
|
82
88
|
|
|
83
89
|
const layoutService = useInjectable<IMainLayoutService>(IMainLayoutService);
|
|
84
90
|
const msgHistoryManager = aiChatService.sessionModel.history;
|
|
85
91
|
const containerRef = React.useRef<HTMLDivElement>(null);
|
|
92
|
+
const autoScroll = React.useRef<boolean>(true);
|
|
86
93
|
const chatInputRef = React.useRef<{ setInputValue: (v: string) => void } | null>(null);
|
|
94
|
+
const dialogService = useInjectable<IDialogService>(IDialogService);
|
|
95
|
+
const aiNativeConfigService = useInjectable<AINativeConfigService>(AINativeConfigService);
|
|
96
|
+
const commandService = useInjectable<CommandService>(CommandService);
|
|
87
97
|
|
|
88
98
|
const [shortcutCommands, setShortcutCommands] = React.useState<ChatSlashCommandItemModel[]>([]);
|
|
89
99
|
|
|
@@ -105,6 +115,8 @@ export const AIChatView = () => {
|
|
|
105
115
|
const [defaultAgentId, setDefaultAgentId] = React.useState<string>('');
|
|
106
116
|
const [command, setCommand] = React.useState('');
|
|
107
117
|
const [theme, setTheme] = React.useState<string | null>(null);
|
|
118
|
+
const [mcpToolsCount, setMcpToolsCount] = React.useState<number>(0);
|
|
119
|
+
const [mcpServersCount, setMcpServersCount] = React.useState<number>(0);
|
|
108
120
|
|
|
109
121
|
React.useEffect(() => {
|
|
110
122
|
const featureSlashCommands = chatFeatureRegistry.getAllShortcutSlashCommand();
|
|
@@ -154,8 +166,29 @@ export const AIChatView = () => {
|
|
|
154
166
|
[],
|
|
155
167
|
);
|
|
156
168
|
|
|
169
|
+
const onDidWheel = React.useCallback(
|
|
170
|
+
(e: WheelEvent) => {
|
|
171
|
+
// 向上滚动
|
|
172
|
+
if (e.deltaY < 0) {
|
|
173
|
+
autoScroll.current = false;
|
|
174
|
+
} else {
|
|
175
|
+
autoScroll.current = true;
|
|
176
|
+
}
|
|
177
|
+
},
|
|
178
|
+
[autoScroll],
|
|
179
|
+
);
|
|
180
|
+
|
|
181
|
+
React.useEffect(() => {
|
|
182
|
+
if (containerRef.current) {
|
|
183
|
+
containerRef.current.addEventListener('wheel', onDidWheel);
|
|
184
|
+
return () => {
|
|
185
|
+
containerRef.current?.removeEventListener('wheel', onDidWheel);
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
}, [autoScroll]);
|
|
189
|
+
|
|
157
190
|
const scrollToBottom = React.useCallback(() => {
|
|
158
|
-
if (containerRef && containerRef.current) {
|
|
191
|
+
if (containerRef && containerRef.current && autoScroll.current) {
|
|
159
192
|
const lastElement = containerRef.current.lastElementChild;
|
|
160
193
|
if (lastElement) {
|
|
161
194
|
lastElement.scrollIntoView({ behavior: 'smooth', block: 'end' });
|
|
@@ -165,7 +198,7 @@ export const AIChatView = () => {
|
|
|
165
198
|
containerRef.current.classList.add(SCROLL_CLASSNAME);
|
|
166
199
|
}
|
|
167
200
|
}
|
|
168
|
-
}, [containerRef]);
|
|
201
|
+
}, [containerRef, autoScroll]);
|
|
169
202
|
|
|
170
203
|
const handleDispatchMessage = React.useCallback(
|
|
171
204
|
(dispatch: TDispatchAction) => {
|
|
@@ -187,7 +220,7 @@ export const AIChatView = () => {
|
|
|
187
220
|
disposer.addDispose(
|
|
188
221
|
chatApiService.onScrollToBottom(() => {
|
|
189
222
|
requestAnimationFrame(() => {
|
|
190
|
-
scrollToBottom();
|
|
223
|
+
// scrollToBottom();
|
|
191
224
|
});
|
|
192
225
|
}),
|
|
193
226
|
);
|
|
@@ -504,7 +537,10 @@ export const AIChatView = () => {
|
|
|
504
537
|
const { message, agentId, command, reportExtra } = value;
|
|
505
538
|
const { actionType, actionSource } = reportExtra || {};
|
|
506
539
|
|
|
507
|
-
const
|
|
540
|
+
const context = contextService.serialize();
|
|
541
|
+
const fullMessage = await promptProvider.provideContextPrompt(context, message);
|
|
542
|
+
|
|
543
|
+
const request = aiChatService.createRequest(fullMessage, agentId!, command);
|
|
508
544
|
if (!request) {
|
|
509
545
|
return;
|
|
510
546
|
}
|
|
@@ -653,6 +689,32 @@ export const AIChatView = () => {
|
|
|
653
689
|
};
|
|
654
690
|
}, [aiChatService.sessionModel]);
|
|
655
691
|
|
|
692
|
+
useEventEffect(
|
|
693
|
+
mcpServerProxyService.onChangeMCPServers,
|
|
694
|
+
() => {
|
|
695
|
+
mcpServerProxyService.getAllMCPTools().then((tools) => {
|
|
696
|
+
setMcpToolsCount(tools.length);
|
|
697
|
+
});
|
|
698
|
+
mcpServerProxyService.$getServers().then((servers) => {
|
|
699
|
+
setMcpServersCount(servers.length);
|
|
700
|
+
});
|
|
701
|
+
},
|
|
702
|
+
[mcpServerProxyService],
|
|
703
|
+
);
|
|
704
|
+
|
|
705
|
+
const handleShowMCPTools = React.useCallback(async () => {
|
|
706
|
+
const tools = await mcpServerProxyService.getAllMCPTools();
|
|
707
|
+
dialogService.open({
|
|
708
|
+
message: <MCPToolsDialog tools={tools} />,
|
|
709
|
+
type: MessageType.Empty,
|
|
710
|
+
buttons: ['关闭'],
|
|
711
|
+
});
|
|
712
|
+
}, [mcpServerProxyService, dialogService]);
|
|
713
|
+
|
|
714
|
+
const handleShowMCPConfig = React.useCallback(() => {
|
|
715
|
+
commandService.executeCommand(OPEN_MCP_CONFIG_COMMAND.id);
|
|
716
|
+
}, [commandService]);
|
|
717
|
+
|
|
656
718
|
return (
|
|
657
719
|
<div id={styles.ai_chat_view}>
|
|
658
720
|
<div className={styles.header_container}>
|
|
@@ -692,6 +754,18 @@ export const AIChatView = () => {
|
|
|
692
754
|
</Popover>
|
|
693
755
|
))}
|
|
694
756
|
</div>
|
|
757
|
+
<div className={styles.header_operate_right}>
|
|
758
|
+
{aiNativeConfigService.capabilities.supportsMCP && (
|
|
759
|
+
<>
|
|
760
|
+
<div className={styles.tag} onClick={handleShowMCPConfig}>
|
|
761
|
+
{`MCP Servers: ${mcpServersCount}`}
|
|
762
|
+
</div>
|
|
763
|
+
<div className={styles.tag} onClick={handleShowMCPTools}>
|
|
764
|
+
{`MCP Tools: ${mcpToolsCount}`}
|
|
765
|
+
</div>
|
|
766
|
+
</>
|
|
767
|
+
)}
|
|
768
|
+
</div>
|
|
695
769
|
</div>
|
|
696
770
|
<ChatInputWrapperRender
|
|
697
771
|
onSend={(value, agentId, command) =>
|
|
@@ -734,8 +808,6 @@ export function DefaultChatViewHeader({
|
|
|
734
808
|
const aiNativeConfigService = useInjectable<AINativeConfigService>(AINativeConfigService);
|
|
735
809
|
const mcpServerProxyService = useInjectable<MCPServerProxyService>(TokenMCPServerProxyService);
|
|
736
810
|
const aiChatService = useInjectable<ChatInternalService>(IChatInternalService);
|
|
737
|
-
const commandService = useInjectable<CommandService>(CommandService);
|
|
738
|
-
|
|
739
811
|
const [historyList, setHistoryList] = React.useState<IChatHistoryItem[]>([]);
|
|
740
812
|
const [currentTitle, setCurrentTitle] = React.useState<string>('');
|
|
741
813
|
const handleNewChat = React.useCallback(() => {
|
|
@@ -756,10 +828,6 @@ export function DefaultChatViewHeader({
|
|
|
756
828
|
[aiChatService],
|
|
757
829
|
);
|
|
758
830
|
|
|
759
|
-
const handleShowMCPConfig = React.useCallback(() => {
|
|
760
|
-
commandService.executeCommand(OPEN_MCP_CONFIG_COMMAND.id);
|
|
761
|
-
}, [commandService]);
|
|
762
|
-
|
|
763
831
|
const handleShowMCPTools = React.useCallback(async () => {
|
|
764
832
|
const tools = await mcpServerProxyService.getAllMCPTools();
|
|
765
833
|
dialogService.open({
|
|
@@ -848,6 +916,23 @@ export function DefaultChatViewHeader({
|
|
|
848
916
|
ariaLabel={localize('aiNative.operate.clear.title')}
|
|
849
917
|
/>
|
|
850
918
|
</Popover>
|
|
919
|
+
{aiNativeConfigService.capabilities.supportsMCP && (
|
|
920
|
+
<Popover
|
|
921
|
+
overlayClassName={styles.popover_icon}
|
|
922
|
+
id={'ai-chat-header-tools'}
|
|
923
|
+
position={PopoverPosition.left}
|
|
924
|
+
title={localize('aiNative.operate.tools.title')}
|
|
925
|
+
>
|
|
926
|
+
<EnhanceIcon
|
|
927
|
+
wrapperClassName={styles.action_btn}
|
|
928
|
+
className={getIcon('menubar-tool')}
|
|
929
|
+
onClick={handleShowMCPTools}
|
|
930
|
+
tabIndex={0}
|
|
931
|
+
role='button'
|
|
932
|
+
ariaLabel={localize('aiNative.operate.tools.title')}
|
|
933
|
+
/>
|
|
934
|
+
</Popover>
|
|
935
|
+
)}
|
|
851
936
|
<Popover
|
|
852
937
|
overlayClassName={styles.popover_icon}
|
|
853
938
|
id={'ai-chat-header-close'}
|
|
@@ -39,7 +39,7 @@ export const ChatContext = memo(() => {
|
|
|
39
39
|
50,
|
|
40
40
|
)((files) => {
|
|
41
41
|
if (files) {
|
|
42
|
-
updateAddedFiles(
|
|
42
|
+
updateAddedFiles(files);
|
|
43
43
|
}
|
|
44
44
|
}, contextService);
|
|
45
45
|
|
|
@@ -57,7 +57,7 @@ export const ChatContext = memo(() => {
|
|
|
57
57
|
}, []);
|
|
58
58
|
|
|
59
59
|
const onDidDeselect = useCallback((uri: URI) => {
|
|
60
|
-
contextService.removeFileFromContext(uri
|
|
60
|
+
contextService.removeFileFromContext(uri);
|
|
61
61
|
}, []);
|
|
62
62
|
|
|
63
63
|
const onDidClickFile = useCallback((uri: URI) => {
|
|
@@ -1,29 +1,18 @@
|
|
|
1
1
|
import cls from 'classnames';
|
|
2
2
|
import React, { useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react';
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import { useInjectable, useLatest } from '@opensumi/ide-core-browser';
|
|
5
5
|
import { Icon, Popover, PopoverPosition, getIcon } from '@opensumi/ide-core-browser/lib/components';
|
|
6
6
|
import { EnhanceIcon } from '@opensumi/ide-core-browser/lib/components/ai-native';
|
|
7
7
|
import { InteractiveInput } from '@opensumi/ide-core-browser/lib/components/ai-native/interactive-input/index';
|
|
8
|
-
import {
|
|
9
|
-
ChatAgentViewServiceToken,
|
|
10
|
-
ChatFeatureRegistryToken,
|
|
11
|
-
MessageType,
|
|
12
|
-
localize,
|
|
13
|
-
runWhenIdle,
|
|
14
|
-
} from '@opensumi/ide-core-common';
|
|
15
|
-
import { CommandService } from '@opensumi/ide-core-common/lib/command';
|
|
8
|
+
import { ChatAgentViewServiceToken, ChatFeatureRegistryToken, localize, runWhenIdle } from '@opensumi/ide-core-common';
|
|
16
9
|
import { MonacoCommandRegistry } from '@opensumi/ide-editor/lib/browser/monaco-contrib/command/command.service';
|
|
17
|
-
import { IDialogService } from '@opensumi/ide-overlay';
|
|
18
10
|
|
|
19
|
-
import { AT_SIGN_SYMBOL, IChatAgentService, SLASH_SYMBOL
|
|
11
|
+
import { AT_SIGN_SYMBOL, IChatAgentService, SLASH_SYMBOL } from '../../common';
|
|
20
12
|
import { ChatAgentViewService } from '../chat/chat-agent.view.service';
|
|
21
13
|
import { ChatSlashCommandItemModel } from '../chat/chat-model';
|
|
22
14
|
import { ChatProxyService } from '../chat/chat-proxy.service';
|
|
23
15
|
import { ChatFeatureRegistry } from '../chat/chat.feature.registry';
|
|
24
|
-
import { OPEN_MCP_CONFIG_COMMAND } from '../mcp/config/mcp-config.commands';
|
|
25
|
-
import { MCPServerProxyService } from '../mcp/mcp-server-proxy.service';
|
|
26
|
-
import { MCPToolsDialog } from '../mcp/mcp-tools-dialog.view';
|
|
27
16
|
import { IChatSlashCommandItem } from '../types';
|
|
28
17
|
|
|
29
18
|
import styles from './components.module.less';
|
|
@@ -205,29 +194,12 @@ export const ChatInput = React.forwardRef((props: IChatInputProps, ref) => {
|
|
|
205
194
|
const [isExpand, setIsExpand] = useState(false);
|
|
206
195
|
const [placeholder, setPlaceHolder] = useState(localize('aiNative.chat.input.placeholder.default'));
|
|
207
196
|
|
|
208
|
-
const dialogService = useInjectable<IDialogService>(IDialogService);
|
|
209
|
-
const aiNativeConfigService = useInjectable<AINativeConfigService>(AINativeConfigService);
|
|
210
|
-
const mcpServerProxyService = useInjectable<MCPServerProxyService>(TokenMCPServerProxyService);
|
|
211
197
|
const monacoCommandRegistry = useInjectable<MonacoCommandRegistry>(MonacoCommandRegistry);
|
|
212
198
|
const chatAgentService = useInjectable<IChatAgentService>(IChatAgentService);
|
|
213
199
|
const chatFeatureRegistry = useInjectable<ChatFeatureRegistry>(ChatFeatureRegistryToken);
|
|
214
|
-
const commandService = useInjectable<CommandService>(CommandService);
|
|
215
200
|
|
|
216
201
|
const currentAgentIdRef = useLatest(agentId);
|
|
217
202
|
|
|
218
|
-
const handleShowMCPConfig = React.useCallback(() => {
|
|
219
|
-
commandService.executeCommand(OPEN_MCP_CONFIG_COMMAND.id);
|
|
220
|
-
}, [commandService]);
|
|
221
|
-
|
|
222
|
-
const handleShowMCPTools = React.useCallback(async () => {
|
|
223
|
-
const tools = await mcpServerProxyService.getAllMCPTools();
|
|
224
|
-
dialogService.open({
|
|
225
|
-
message: <MCPToolsDialog tools={tools} />,
|
|
226
|
-
type: MessageType.Empty,
|
|
227
|
-
buttons: ['关闭'],
|
|
228
|
-
});
|
|
229
|
-
}, [mcpServerProxyService, dialogService]);
|
|
230
|
-
|
|
231
203
|
useImperativeHandle(ref, () => ({
|
|
232
204
|
setInputValue: (v: string) => {
|
|
233
205
|
setValue(v);
|
|
@@ -489,42 +461,6 @@ export const ChatInput = React.forwardRef((props: IChatInputProps, ref) => {
|
|
|
489
461
|
height={inputHeight}
|
|
490
462
|
popoverPosition={PopoverPosition.left}
|
|
491
463
|
/>
|
|
492
|
-
<div className={styles.chat_input_footer}>
|
|
493
|
-
{aiNativeConfigService.capabilities.supportsMCP && (
|
|
494
|
-
<div className={styles.mcp_desc}>
|
|
495
|
-
<Popover
|
|
496
|
-
overlayClassName={styles.popover_icon}
|
|
497
|
-
id={'ai-chat-header-mcp-server'}
|
|
498
|
-
position={PopoverPosition.left}
|
|
499
|
-
title={'MCP Server'}
|
|
500
|
-
>
|
|
501
|
-
<EnhanceIcon
|
|
502
|
-
wrapperClassName={styles.action_btn}
|
|
503
|
-
className={'codicon codicon-server'}
|
|
504
|
-
onClick={handleShowMCPConfig}
|
|
505
|
-
tabIndex={0}
|
|
506
|
-
role='button'
|
|
507
|
-
ariaLabel={'MCP Server'}
|
|
508
|
-
/>
|
|
509
|
-
</Popover>
|
|
510
|
-
<Popover
|
|
511
|
-
overlayClassName={styles.popover_icon}
|
|
512
|
-
id={'ai-chat-header-tools'}
|
|
513
|
-
position={PopoverPosition.left}
|
|
514
|
-
title={localize('aiNative.operate.tools.title')}
|
|
515
|
-
>
|
|
516
|
-
<EnhanceIcon
|
|
517
|
-
wrapperClassName={styles.action_btn}
|
|
518
|
-
className={getIcon('menubar-tool')}
|
|
519
|
-
onClick={handleShowMCPTools}
|
|
520
|
-
tabIndex={0}
|
|
521
|
-
role='button'
|
|
522
|
-
ariaLabel={localize('aiNative.operate.tools.title')}
|
|
523
|
-
/>
|
|
524
|
-
</Popover>
|
|
525
|
-
</div>
|
|
526
|
-
)}
|
|
527
|
-
</div>
|
|
528
464
|
</div>
|
|
529
465
|
);
|
|
530
466
|
});
|
|
@@ -67,12 +67,13 @@ export const ChatToolRender = (props: { value: IChatToolContent['content']; mess
|
|
|
67
67
|
<div className={styles['chat-tool-render']}>
|
|
68
68
|
<div className={styles['tool-header']} onClick={toggleExpand}>
|
|
69
69
|
<div className={styles['tool-name']}>
|
|
70
|
-
<
|
|
70
|
+
<span className={cls(styles['expand-icon'], { [styles.expanded]: isExpanded })}>▶</span>
|
|
71
71
|
{label}
|
|
72
72
|
</div>
|
|
73
73
|
{value.state && (
|
|
74
74
|
<div className={styles['tool-state']}>
|
|
75
75
|
<span className={styles['state-icon']}>{stateInfo.icon}</span>
|
|
76
|
+
<span className={styles['state-label']}>{stateInfo.label}</span>
|
|
76
77
|
</div>
|
|
77
78
|
)}
|
|
78
79
|
</div>
|
|
@@ -96,7 +96,6 @@
|
|
|
96
96
|
.chat_input_container {
|
|
97
97
|
position: relative;
|
|
98
98
|
border-radius: 9px;
|
|
99
|
-
padding: 10px 0px 0px 0px;
|
|
100
99
|
border: 1px solid var(--kt-input-border);
|
|
101
100
|
background-color: var(--design-chatInput-background);
|
|
102
101
|
&.active {
|
|
@@ -506,22 +505,3 @@
|
|
|
506
505
|
}
|
|
507
506
|
}
|
|
508
507
|
}
|
|
509
|
-
|
|
510
|
-
.chat_input_footer {
|
|
511
|
-
padding: 0px 10px;
|
|
512
|
-
display: flex;
|
|
513
|
-
align-items: center;
|
|
514
|
-
font-size: 12px;
|
|
515
|
-
|
|
516
|
-
.model_selector {
|
|
517
|
-
display: flex;
|
|
518
|
-
font-size: 11px;
|
|
519
|
-
align-items: center;
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
.mcp_desc {
|
|
523
|
-
padding: 5px 0px;
|
|
524
|
-
display: flex;
|
|
525
|
-
align-items: center;
|
|
526
|
-
}
|
|
527
|
-
}
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
import { EditorSelectionChangeEvent } from '@opensumi/ide-editor/lib/browser/types';
|
|
13
13
|
import { IMarkerService } from '@opensumi/ide-markers/lib/common/types';
|
|
14
14
|
|
|
15
|
-
import {
|
|
15
|
+
import { FileContext, LLMContextService, SerializedContext } from '../../common/llm-context';
|
|
16
16
|
|
|
17
17
|
@Injectable()
|
|
18
18
|
export class LLMContextServiceImpl extends WithEventBus implements LLMContextService {
|
|
@@ -27,68 +27,40 @@ export class LLMContextServiceImpl extends WithEventBus implements LLMContextSer
|
|
|
27
27
|
|
|
28
28
|
private isAutoCollecting = false;
|
|
29
29
|
|
|
30
|
-
private
|
|
30
|
+
private contextFiles: FileContext[] = [];
|
|
31
31
|
|
|
32
|
-
private
|
|
33
|
-
private readonly maxViewFilesLimit = 20;
|
|
34
|
-
private readonly attachedFiles: FileContext[] = [];
|
|
35
|
-
private readonly recentlyViewFiles: FileContext[] = [];
|
|
36
|
-
private readonly onDidContextFilesChangeEmitter = new Emitter<{ viewed: FileContext[]; attached: FileContext[]; version: number }>();
|
|
37
|
-
onDidContextFilesChangeEvent = this.onDidContextFilesChangeEmitter.event;
|
|
38
|
-
|
|
39
|
-
private addFileToList(file: FileContext, list: FileContext[], maxLimit: number) {
|
|
40
|
-
const existingIndex = list.findIndex((f) => f.uri.toString() === file.uri.toString());
|
|
41
|
-
if (existingIndex > -1) {
|
|
42
|
-
list.splice(existingIndex, 1);
|
|
43
|
-
}
|
|
32
|
+
private maxFiles: number = 10; // 上下文的最大长度限制
|
|
44
33
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
list.shift();
|
|
48
|
-
}
|
|
49
|
-
}
|
|
34
|
+
private onDidContextFilesChangeEmitter = new Emitter<FileContext[]>();
|
|
35
|
+
onDidContextFilesChangeEvent = this.onDidContextFilesChangeEmitter.event;
|
|
50
36
|
|
|
51
|
-
addFileToContext(uri: URI, selection?: [number, number], isManual =
|
|
52
|
-
|
|
53
|
-
return;
|
|
54
|
-
}
|
|
37
|
+
addFileToContext(uri: URI, selection?: [number, number], isManual = true): void {
|
|
38
|
+
this.removeFileFromContext(uri);
|
|
55
39
|
|
|
56
|
-
|
|
57
|
-
const targetList = isManual ? this.attachedFiles : this.recentlyViewFiles;
|
|
58
|
-
const maxLimit = isManual ? this.maxAttachFilesLimit : this.maxViewFilesLimit;
|
|
40
|
+
this.contextFiles.push({ uri, selection, isManual });
|
|
59
41
|
|
|
60
|
-
if (
|
|
61
|
-
this.
|
|
42
|
+
if (this.contextFiles.length > this.maxFiles) {
|
|
43
|
+
this.contextFiles.shift();
|
|
62
44
|
}
|
|
63
45
|
|
|
64
|
-
this.addFileToList(file, targetList, maxLimit);
|
|
65
|
-
this.notifyContextChange();
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
private notifyContextChange(): void {
|
|
69
46
|
this.onDidContextFilesChangeEmitter.fire(this.getAllContextFiles());
|
|
70
47
|
}
|
|
71
48
|
|
|
72
49
|
cleanFileContext() {
|
|
73
|
-
this.
|
|
74
|
-
this.
|
|
50
|
+
this.contextFiles = [];
|
|
51
|
+
this.onDidContextFilesChangeEmitter.fire(this.getAllContextFiles());
|
|
75
52
|
}
|
|
76
53
|
|
|
77
54
|
private getAllContextFiles() {
|
|
78
|
-
return
|
|
79
|
-
viewed: this.recentlyViewFiles,
|
|
80
|
-
attached: this.attachedFiles,
|
|
81
|
-
version: this.contextVersion++,
|
|
82
|
-
};
|
|
55
|
+
return [...this.contextFiles];
|
|
83
56
|
}
|
|
84
57
|
|
|
85
|
-
removeFileFromContext(uri: URI
|
|
86
|
-
const
|
|
87
|
-
const index = targetList.findIndex((file) => file.uri.toString() === uri.toString());
|
|
58
|
+
removeFileFromContext(uri: URI): void {
|
|
59
|
+
const index = this.contextFiles.findIndex((file) => file.uri.toString() === uri.toString());
|
|
88
60
|
if (index > -1) {
|
|
89
|
-
|
|
61
|
+
this.contextFiles.splice(index, 1);
|
|
62
|
+
this.onDidContextFilesChangeEmitter.fire(this.getAllContextFiles());
|
|
90
63
|
}
|
|
91
|
-
this.notifyContextChange();
|
|
92
64
|
}
|
|
93
65
|
|
|
94
66
|
startAutoCollection(): void {
|
|
@@ -106,7 +78,8 @@ export class LLMContextServiceImpl extends WithEventBus implements LLMContextSer
|
|
|
106
78
|
if (event.payload.uri.scheme !== 'file') {
|
|
107
79
|
return;
|
|
108
80
|
}
|
|
109
|
-
|
|
81
|
+
// FIXME: 暂时不自动添加
|
|
82
|
+
// this.addFileToContext(event.payload.uri);
|
|
110
83
|
}),
|
|
111
84
|
);
|
|
112
85
|
|
|
@@ -115,8 +88,6 @@ export class LLMContextServiceImpl extends WithEventBus implements LLMContextSer
|
|
|
115
88
|
if (event.payload.scheme !== 'file') {
|
|
116
89
|
return;
|
|
117
90
|
}
|
|
118
|
-
|
|
119
|
-
this.removeFileFromContext(event.payload, false);
|
|
120
91
|
}),
|
|
121
92
|
);
|
|
122
93
|
|
|
@@ -138,12 +109,11 @@ export class LLMContextServiceImpl extends WithEventBus implements LLMContextSer
|
|
|
138
109
|
].sort() as [number, number];
|
|
139
110
|
|
|
140
111
|
if (selection[0] === selection[1]) {
|
|
141
|
-
this.addFileToContext(event.payload.editorUri, undefined
|
|
112
|
+
this.addFileToContext(event.payload.editorUri, undefined);
|
|
142
113
|
} else {
|
|
143
114
|
this.addFileToContext(
|
|
144
115
|
event.payload.editorUri,
|
|
145
116
|
selection.sort((a, b) => a - b),
|
|
146
|
-
false,
|
|
147
117
|
);
|
|
148
118
|
}
|
|
149
119
|
}
|
|
@@ -157,51 +127,42 @@ export class LLMContextServiceImpl extends WithEventBus implements LLMContextSer
|
|
|
157
127
|
|
|
158
128
|
serialize(): SerializedContext {
|
|
159
129
|
const files = this.getAllContextFiles();
|
|
160
|
-
const
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
return files
|
|
170
|
-
.map((file) => workspaceRoot.relative(file.uri)?.toString() || file.uri.parent.toString())
|
|
130
|
+
const recentlyViewFiles = files
|
|
131
|
+
.filter((v) => !v.selection)
|
|
132
|
+
.map((file) => {
|
|
133
|
+
const relativePath = URI.file(this.appConfig.workspaceDir).relative(file.uri);
|
|
134
|
+
if (relativePath) {
|
|
135
|
+
return relativePath.toString();
|
|
136
|
+
}
|
|
137
|
+
return file.uri.parent.toString();
|
|
138
|
+
})
|
|
171
139
|
.filter(Boolean);
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
private serializeAttachedFiles(files: FileContext[], workspaceRoot: URI): AttachFileContext[] {
|
|
175
|
-
return files
|
|
176
|
-
.map((file) => this.serializeAttachedFile(file, workspaceRoot))
|
|
177
|
-
.filter(Boolean) as unknown as AttachFileContext[];
|
|
178
|
-
}
|
|
179
140
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
return this.markerService
|
|
200
|
-
.getManager()
|
|
201
|
-
.getMarkers({
|
|
202
|
-
resource: uri.toString(),
|
|
203
|
-
severities: MarkerSeverity.Error,
|
|
141
|
+
const attachedFiles = files
|
|
142
|
+
.filter((v) => v.selection)
|
|
143
|
+
.map((file) => {
|
|
144
|
+
const ref = this.docModelManager.getModelReference(file.uri);
|
|
145
|
+
const content = ref!.instance.getText();
|
|
146
|
+
const lineErrors = this.markerService
|
|
147
|
+
.getManager()
|
|
148
|
+
.getMarkers({
|
|
149
|
+
resource: file.uri.toString(),
|
|
150
|
+
severities: MarkerSeverity.Error,
|
|
151
|
+
})
|
|
152
|
+
.map((marker) => marker.message);
|
|
153
|
+
|
|
154
|
+
return {
|
|
155
|
+
content,
|
|
156
|
+
lineErrors,
|
|
157
|
+
path: URI.file(this.appConfig.workspaceDir).relative(file.uri)!.toString(),
|
|
158
|
+
language: ref?.instance.languageId!,
|
|
159
|
+
};
|
|
204
160
|
})
|
|
205
|
-
.
|
|
161
|
+
.filter(Boolean);
|
|
162
|
+
|
|
163
|
+
return {
|
|
164
|
+
recentlyViewFiles,
|
|
165
|
+
attachedFiles,
|
|
166
|
+
};
|
|
206
167
|
}
|
|
207
168
|
}
|
|
@@ -290,7 +290,7 @@ export class IntelligentCompletionsController extends BaseAIMonacoEditorControll
|
|
|
290
290
|
const range = completionModel.items[0].range;
|
|
291
291
|
if (position.lineNumber < range.startLineNumber || position.lineNumber > range.endLineNumber) {
|
|
292
292
|
runWhenIdle(() => {
|
|
293
|
-
this.
|
|
293
|
+
this.hide();
|
|
294
294
|
});
|
|
295
295
|
}
|
|
296
296
|
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { AI_CODE_EDITS_COMMANDS } from '@opensumi/ide-core-browser/lib/ai-native/command';
|
|
2
|
+
import { CommandService } from '@opensumi/ide-core-common';
|
|
1
3
|
import {
|
|
2
4
|
InlineCompletionContext,
|
|
3
5
|
InlineCompletionTriggerKind,
|
|
@@ -27,6 +29,7 @@ import {
|
|
|
27
29
|
|
|
28
30
|
import { CodeEditsResultValue, ICodeEdit, ICodeEditsResult } from '../index';
|
|
29
31
|
|
|
32
|
+
|
|
30
33
|
import { BaseCodeEditsView } from './base';
|
|
31
34
|
|
|
32
35
|
/**
|
|
@@ -115,6 +118,10 @@ export class DefaultCodeEditsView extends BaseCodeEditsView {
|
|
|
115
118
|
return completionModel;
|
|
116
119
|
},
|
|
117
120
|
freeInlineCompletions: () => [],
|
|
121
|
+
handleRejection: (completions, item) => {
|
|
122
|
+
const commandService: CommandService = this.injector.get(CommandService);
|
|
123
|
+
commandService.executeCommand(AI_CODE_EDITS_COMMANDS.DISCARD.id);
|
|
124
|
+
},
|
|
118
125
|
} as InlineCompletionsProvider<ICodeEditsResult<ICodeEdit>>,
|
|
119
126
|
],
|
|
120
127
|
} as unknown as LanguageFeatureRegistry<InlineCompletionsProvider>,
|
|
@@ -14,16 +14,16 @@
|
|
|
14
14
|
padding: 8px 12px;
|
|
15
15
|
min-width: initial;
|
|
16
16
|
margin: 0;
|
|
17
|
+
|
|
18
|
+
::after {
|
|
19
|
+
content: '';
|
|
20
|
+
}
|
|
17
21
|
}
|
|
18
22
|
|
|
19
23
|
.rce-mbox-text {
|
|
20
24
|
line-height: 18px;
|
|
21
25
|
width: inherit;
|
|
22
26
|
font-size: 12px;
|
|
23
|
-
|
|
24
|
-
&::after {
|
|
25
|
-
display: none;
|
|
26
|
-
}
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
.rce-smsg {
|
|
@@ -27,7 +27,6 @@ export class CreateNewFileWithTextTool implements MCPServerContribution {
|
|
|
27
27
|
getToolDefinition(): MCPToolDefinition {
|
|
28
28
|
return {
|
|
29
29
|
name: 'create_new_file_with_text',
|
|
30
|
-
label: 'Create File',
|
|
31
30
|
description:
|
|
32
31
|
'Creates a new file at the specified path within the project directory and populates it with the provided text. ' +
|
|
33
32
|
'Use this tool to generate new files in your project structure. ' +
|
|
@@ -29,7 +29,6 @@ export class GetDiagnosticsByPathTool implements MCPServerContribution {
|
|
|
29
29
|
getToolDefinition(): MCPToolDefinition {
|
|
30
30
|
return {
|
|
31
31
|
name: 'get_diagnostics_by_path',
|
|
32
|
-
label: 'Get Diagnostics',
|
|
33
32
|
description:
|
|
34
33
|
'Retrieves diagnostic information (errors, warnings, etc.) from a specific file in the project. ' +
|
|
35
34
|
'Use this tool to get information about problems in any project file. ' +
|