@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.
Files changed (80) hide show
  1. package/lib/browser/chat/chat-agent.service.d.ts +0 -8
  2. package/lib/browser/chat/chat-agent.service.d.ts.map +1 -1
  3. package/lib/browser/chat/chat-agent.service.js +0 -34
  4. package/lib/browser/chat/chat-agent.service.js.map +1 -1
  5. package/lib/browser/chat/chat-model.d.ts.map +1 -1
  6. package/lib/browser/chat/chat-model.js +2 -3
  7. package/lib/browser/chat/chat-model.js.map +1 -1
  8. package/lib/browser/chat/chat-proxy.service.d.ts.map +1 -1
  9. package/lib/browser/chat/chat-proxy.service.js +1 -1
  10. package/lib/browser/chat/chat-proxy.service.js.map +1 -1
  11. package/lib/browser/chat/chat.module.less +2 -1
  12. package/lib/browser/chat/chat.view.d.ts.map +1 -1
  13. package/lib/browser/chat/chat.view.js +60 -9
  14. package/lib/browser/chat/chat.view.js.map +1 -1
  15. package/lib/browser/components/ChatContext/index.js +2 -2
  16. package/lib/browser/components/ChatContext/index.js.map +1 -1
  17. package/lib/browser/components/ChatInput.d.ts.map +1 -1
  18. package/lib/browser/components/ChatInput.js +1 -25
  19. package/lib/browser/components/ChatInput.js.map +1 -1
  20. package/lib/browser/components/ChatToolRender.d.ts.map +1 -1
  21. package/lib/browser/components/ChatToolRender.js +3 -2
  22. package/lib/browser/components/ChatToolRender.js.map +1 -1
  23. package/lib/browser/components/chat-history.module.less +1 -1
  24. package/lib/browser/components/components.module.less +0 -20
  25. package/lib/browser/context/llm-context.service.d.ts +5 -18
  26. package/lib/browser/context/llm-context.service.d.ts.map +1 -1
  27. package/lib/browser/context/llm-context.service.js +47 -80
  28. package/lib/browser/context/llm-context.service.js.map +1 -1
  29. package/lib/browser/contrib/intelligent-completions/intelligent-completions.controller.js +1 -1
  30. package/lib/browser/contrib/intelligent-completions/intelligent-completions.controller.js.map +1 -1
  31. package/lib/browser/contrib/intelligent-completions/view/default.d.ts.map +1 -1
  32. package/lib/browser/contrib/intelligent-completions/view/default.js +6 -0
  33. package/lib/browser/contrib/intelligent-completions/view/default.js.map +1 -1
  34. package/lib/browser/layout/layout.module.less +4 -4
  35. package/lib/browser/mcp/tools/components/index.module.less +0 -1
  36. package/lib/browser/mcp/tools/createNewFileWithText.d.ts.map +1 -1
  37. package/lib/browser/mcp/tools/createNewFileWithText.js +0 -1
  38. package/lib/browser/mcp/tools/createNewFileWithText.js.map +1 -1
  39. package/lib/browser/mcp/tools/getDiagnosticsByPath.d.ts.map +1 -1
  40. package/lib/browser/mcp/tools/getDiagnosticsByPath.js +0 -1
  41. package/lib/browser/mcp/tools/getDiagnosticsByPath.js.map +1 -1
  42. package/lib/browser/mcp/tools/handlers/RunCommand.d.ts.map +1 -1
  43. package/lib/browser/mcp/tools/handlers/RunCommand.js +0 -2
  44. package/lib/browser/mcp/tools/handlers/RunCommand.js.map +1 -1
  45. package/lib/browser/mcp/tools/runTerminalCmd.d.ts.map +1 -1
  46. package/lib/browser/mcp/tools/runTerminalCmd.js +0 -1
  47. package/lib/browser/mcp/tools/runTerminalCmd.js.map +1 -1
  48. package/lib/browser/preferences/schema.d.ts.map +1 -1
  49. package/lib/browser/preferences/schema.js +1 -0
  50. package/lib/browser/preferences/schema.js.map +1 -1
  51. package/lib/common/llm-context.d.ts +9 -13
  52. package/lib/common/llm-context.d.ts.map +1 -1
  53. package/lib/common/llm-context.js.map +1 -1
  54. package/lib/common/prompts/context-prompt-provider.d.ts +3 -4
  55. package/lib/common/prompts/context-prompt-provider.d.ts.map +1 -1
  56. package/lib/common/prompts/context-prompt-provider.js +22 -33
  57. package/lib/common/prompts/context-prompt-provider.js.map +1 -1
  58. package/package.json +23 -23
  59. package/src/browser/chat/chat-agent.service.ts +3 -42
  60. package/src/browser/chat/chat-model.ts +6 -11
  61. package/src/browser/chat/chat-proxy.service.ts +1 -2
  62. package/src/browser/chat/chat.module.less +2 -1
  63. package/src/browser/chat/chat.view.tsx +95 -10
  64. package/src/browser/components/ChatContext/index.tsx +2 -2
  65. package/src/browser/components/ChatInput.tsx +3 -67
  66. package/src/browser/components/ChatToolRender.tsx +2 -1
  67. package/src/browser/components/chat-history.module.less +1 -1
  68. package/src/browser/components/components.module.less +0 -20
  69. package/src/browser/context/llm-context.service.ts +54 -93
  70. package/src/browser/contrib/intelligent-completions/intelligent-completions.controller.ts +1 -1
  71. package/src/browser/contrib/intelligent-completions/view/default.ts +7 -0
  72. package/src/browser/layout/layout.module.less +4 -4
  73. package/src/browser/mcp/tools/components/index.module.less +0 -1
  74. package/src/browser/mcp/tools/createNewFileWithText.ts +0 -1
  75. package/src/browser/mcp/tools/getDiagnosticsByPath.ts +0 -1
  76. package/src/browser/mcp/tools/handlers/RunCommand.ts +0 -2
  77. package/src/browser/mcp/tools/runTerminalCmd.ts +0 -1
  78. package/src/browser/preferences/schema.ts +1 -0
  79. package/src/common/llm-context.ts +4 -10
  80. 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 request = aiChatService.createRequest(message, agentId!, command);
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([...files.attached]);
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, true);
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 { AINativeConfigService, useInjectable, useLatest } from '@opensumi/ide-core-browser';
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, TokenMCPServerProxyService } from '../../common';
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
- <Icon iconClass={`codicon codicon-triangle-${isExpanded ? 'down' : 'right'}`} />
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>
@@ -3,7 +3,7 @@
3
3
  align-items: center;
4
4
  justify-content: space-between;
5
5
  font-size: 13px;
6
- padding: 0 0 0 12px;
6
+ padding: 0 4px 0 12px;
7
7
  color: var(--editor-foreground);
8
8
  text-overflow: ellipsis;
9
9
  white-space: nowrap;
@@ -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 { AttachFileContext, FileContext, LLMContextService, SerializedContext } from '../../common/llm-context';
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 contextVersion = 0;
30
+ private contextFiles: FileContext[] = [];
31
31
 
32
- private readonly maxAttachFilesLimit = 10;
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
- list.push(file);
46
- if (list.length > maxLimit) {
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 = false): void {
52
- if (!uri) {
53
- return;
54
- }
37
+ addFileToContext(uri: URI, selection?: [number, number], isManual = true): void {
38
+ this.removeFileFromContext(uri);
55
39
 
56
- const file = { uri, selection };
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 (isManual) {
61
- this.docModelManager.createModelReference(uri);
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.attachedFiles.length = 0;
74
- this.notifyContextChange();
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, isManual = false): void {
86
- const targetList = isManual ? this.attachedFiles : this.recentlyViewFiles;
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
- targetList.splice(index, 1);
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
- this.addFileToContext(event.payload.uri, undefined, false);
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, false);
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 workspaceRoot = URI.file(this.appConfig.workspaceDir);
161
-
162
- return {
163
- recentlyViewFiles: this.serializeRecentlyViewFiles(files.viewed, workspaceRoot),
164
- attachedFiles: this.serializeAttachedFiles(files.attached, workspaceRoot),
165
- };
166
- }
167
-
168
- private serializeRecentlyViewFiles(files: FileContext[], workspaceRoot: URI): string[] {
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
- private serializeAttachedFile(file: FileContext, workspaceRoot: URI) {
181
- try {
182
- const ref = this.docModelManager.getModelReference(file.uri);
183
- if (!ref) {
184
- return null;
185
- }
186
-
187
- return {
188
- content: ref.instance.getText(),
189
- lineErrors: this.getFileErrors(file.uri),
190
- path: workspaceRoot.relative(file.uri)!.toString(),
191
- language: ref.instance.languageId!,
192
- };
193
- } catch (e) {
194
- return null;
195
- }
196
- }
197
-
198
- private getFileErrors(uri: URI): string[] {
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
- .map((marker) => marker.message);
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.discard.get();
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 {
@@ -154,7 +154,6 @@
154
154
  background-color: var(--design-chatInput-background);
155
155
  padding: 10px;
156
156
  border-radius: 4px;
157
- margin: 10px 0px;
158
157
 
159
158
  .command_title {
160
159
  display: flex;
@@ -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. ' +