@opensumi/ide-ai-native 3.8.1-next-1741080291.0 → 3.8.1-next-1741092802.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 (110) hide show
  1. package/lib/browser/ai-core.contribution.d.ts.map +1 -1
  2. package/lib/browser/ai-core.contribution.js +22 -22
  3. package/lib/browser/ai-core.contribution.js.map +1 -1
  4. package/lib/browser/chat/chat-agent.service.d.ts +8 -0
  5. package/lib/browser/chat/chat-agent.service.d.ts.map +1 -1
  6. package/lib/browser/chat/chat-agent.service.js +32 -5
  7. package/lib/browser/chat/chat-agent.service.js.map +1 -1
  8. package/lib/browser/chat/chat-model.d.ts.map +1 -1
  9. package/lib/browser/chat/chat-model.js +3 -2
  10. package/lib/browser/chat/chat-model.js.map +1 -1
  11. package/lib/browser/chat/chat-proxy.service.d.ts.map +1 -1
  12. package/lib/browser/chat/chat-proxy.service.js +3 -1
  13. package/lib/browser/chat/chat-proxy.service.js.map +1 -1
  14. package/lib/browser/chat/chat.module.less +1 -2
  15. package/lib/browser/chat/chat.view.d.ts.map +1 -1
  16. package/lib/browser/chat/chat.view.js +6 -38
  17. package/lib/browser/chat/chat.view.js.map +1 -1
  18. package/lib/browser/components/ChatContext/index.js +2 -2
  19. package/lib/browser/components/ChatContext/index.js.map +1 -1
  20. package/lib/browser/components/ChatInput.d.ts.map +1 -1
  21. package/lib/browser/components/ChatInput.js +25 -1
  22. package/lib/browser/components/ChatInput.js.map +1 -1
  23. package/lib/browser/components/ChatToolRender.d.ts.map +1 -1
  24. package/lib/browser/components/ChatToolRender.js +2 -3
  25. package/lib/browser/components/ChatToolRender.js.map +1 -1
  26. package/lib/browser/components/chat-history.module.less +1 -1
  27. package/lib/browser/components/components.module.less +20 -0
  28. package/lib/browser/context/llm-context.service.d.ts +18 -5
  29. package/lib/browser/context/llm-context.service.d.ts.map +1 -1
  30. package/lib/browser/context/llm-context.service.js +80 -47
  31. package/lib/browser/context/llm-context.service.js.map +1 -1
  32. package/lib/browser/layout/layout.module.less +4 -4
  33. package/lib/browser/mcp/tools/components/ExpandableFileList.d.ts +13 -0
  34. package/lib/browser/mcp/tools/components/ExpandableFileList.d.ts.map +1 -0
  35. package/lib/browser/mcp/tools/components/{SearchResult.js → ExpandableFileList.js} +29 -19
  36. package/lib/browser/mcp/tools/components/ExpandableFileList.js.map +1 -0
  37. package/lib/browser/mcp/tools/components/index.module.less +1 -0
  38. package/lib/browser/mcp/tools/createNewFileWithText.d.ts.map +1 -1
  39. package/lib/browser/mcp/tools/createNewFileWithText.js +1 -0
  40. package/lib/browser/mcp/tools/createNewFileWithText.js.map +1 -1
  41. package/lib/browser/mcp/tools/fileSearch.d.ts +1 -0
  42. package/lib/browser/mcp/tools/fileSearch.d.ts.map +1 -1
  43. package/lib/browser/mcp/tools/fileSearch.js +14 -5
  44. package/lib/browser/mcp/tools/fileSearch.js.map +1 -1
  45. package/lib/browser/mcp/tools/getDiagnosticsByPath.d.ts.map +1 -1
  46. package/lib/browser/mcp/tools/getDiagnosticsByPath.js +1 -0
  47. package/lib/browser/mcp/tools/getDiagnosticsByPath.js.map +1 -1
  48. package/lib/browser/mcp/tools/grepSearch.d.ts.map +1 -1
  49. package/lib/browser/mcp/tools/grepSearch.js +6 -3
  50. package/lib/browser/mcp/tools/grepSearch.js.map +1 -1
  51. package/lib/browser/mcp/tools/handlers/ListDir.d.ts +1 -0
  52. package/lib/browser/mcp/tools/handlers/ListDir.d.ts.map +1 -1
  53. package/lib/browser/mcp/tools/handlers/ListDir.js +3 -0
  54. package/lib/browser/mcp/tools/handlers/ListDir.js.map +1 -1
  55. package/lib/browser/mcp/tools/handlers/RunCommand.d.ts.map +1 -1
  56. package/lib/browser/mcp/tools/handlers/RunCommand.js +2 -0
  57. package/lib/browser/mcp/tools/handlers/RunCommand.js.map +1 -1
  58. package/lib/browser/mcp/tools/listDir.d.ts +1 -0
  59. package/lib/browser/mcp/tools/listDir.d.ts.map +1 -1
  60. package/lib/browser/mcp/tools/listDir.js +35 -4
  61. package/lib/browser/mcp/tools/listDir.js.map +1 -1
  62. package/lib/browser/mcp/tools/runTerminalCmd.d.ts.map +1 -1
  63. package/lib/browser/mcp/tools/runTerminalCmd.js +1 -0
  64. package/lib/browser/mcp/tools/runTerminalCmd.js.map +1 -1
  65. package/lib/browser/preferences/schema.d.ts.map +1 -1
  66. package/lib/browser/preferences/schema.js +0 -1
  67. package/lib/browser/preferences/schema.js.map +1 -1
  68. package/lib/common/llm-context.d.ts +13 -9
  69. package/lib/common/llm-context.d.ts.map +1 -1
  70. package/lib/common/llm-context.js.map +1 -1
  71. package/lib/common/prompts/context-prompt-provider.d.ts +4 -3
  72. package/lib/common/prompts/context-prompt-provider.d.ts.map +1 -1
  73. package/lib/common/prompts/context-prompt-provider.js +33 -22
  74. package/lib/common/prompts/context-prompt-provider.js.map +1 -1
  75. package/lib/node/base-language-model.d.ts +1 -1
  76. package/lib/node/base-language-model.d.ts.map +1 -1
  77. package/lib/node/base-language-model.js +3 -3
  78. package/lib/node/base-language-model.js.map +1 -1
  79. package/package.json +23 -23
  80. package/src/browser/ai-core.contribution.ts +27 -26
  81. package/src/browser/chat/chat-agent.service.ts +38 -7
  82. package/src/browser/chat/chat-model.ts +11 -6
  83. package/src/browser/chat/chat-proxy.service.ts +4 -2
  84. package/src/browser/chat/chat.module.less +1 -2
  85. package/src/browser/chat/chat.view.tsx +7 -70
  86. package/src/browser/components/ChatContext/index.tsx +2 -2
  87. package/src/browser/components/ChatInput.tsx +67 -3
  88. package/src/browser/components/ChatToolRender.tsx +1 -2
  89. package/src/browser/components/chat-history.module.less +1 -1
  90. package/src/browser/components/components.module.less +20 -0
  91. package/src/browser/context/llm-context.service.ts +93 -54
  92. package/src/browser/layout/layout.module.less +4 -4
  93. package/src/browser/mcp/tools/components/ExpandableFileList.tsx +133 -0
  94. package/src/browser/mcp/tools/components/index.module.less +1 -0
  95. package/src/browser/mcp/tools/createNewFileWithText.ts +1 -0
  96. package/src/browser/mcp/tools/fileSearch.ts +14 -4
  97. package/src/browser/mcp/tools/getDiagnosticsByPath.ts +1 -0
  98. package/src/browser/mcp/tools/grepSearch.ts +6 -3
  99. package/src/browser/mcp/tools/handlers/ListDir.ts +4 -0
  100. package/src/browser/mcp/tools/handlers/RunCommand.ts +2 -0
  101. package/src/browser/mcp/tools/listDir.ts +36 -5
  102. package/src/browser/mcp/tools/runTerminalCmd.ts +1 -0
  103. package/src/browser/preferences/schema.ts +0 -1
  104. package/src/common/llm-context.ts +10 -4
  105. package/src/common/prompts/context-prompt-provider.ts +38 -29
  106. package/src/node/base-language-model.ts +3 -1
  107. package/lib/browser/mcp/tools/components/SearchResult.d.ts +0 -11
  108. package/lib/browser/mcp/tools/components/SearchResult.d.ts.map +0 -1
  109. package/lib/browser/mcp/tools/components/SearchResult.js.map +0 -1
  110. package/src/browser/mcp/tools/components/SearchResult.tsx +0 -92
@@ -279,7 +279,6 @@
279
279
  }
280
280
  }
281
281
 
282
-
283
282
  .chat_tips_container {
284
283
  display: flex;
285
284
  align-items: center;
@@ -290,6 +289,6 @@
290
289
  }
291
290
 
292
291
  .chat_history {
293
- width: calc(100% - 60px);
292
+ width: calc(100% - 40px);
294
293
  color: var(--design-text-foreground);
295
294
  }
@@ -5,7 +5,6 @@ import {
5
5
  AINativeConfigService,
6
6
  CommandService,
7
7
  getIcon,
8
- useEventEffect,
9
8
  useInjectable,
10
9
  useUpdateOnEvent,
11
10
  } from '@opensumi/ide-core-browser';
@@ -42,8 +41,6 @@ import {
42
41
  IChatMessageStructure,
43
42
  TokenMCPServerProxyService,
44
43
  } from '../../common';
45
- import { LLMContextService, LLMContextServiceToken } from '../../common/llm-context';
46
- import { ChatAgentPromptProvider } from '../../common/prompts/context-prompt-provider';
47
44
  import { ChatContext } from '../components/ChatContext';
48
45
  import { CodeBlockWrapperInput } from '../components/ChatEditor';
49
46
  import ChatHistory, { IChatHistoryItem } from '../components/ChatHistory';
@@ -82,18 +79,12 @@ export const AIChatView = () => {
82
79
  const chatAgentService = useInjectable<IChatAgentService>(IChatAgentService);
83
80
  const chatFeatureRegistry = useInjectable<ChatFeatureRegistry>(ChatFeatureRegistryToken);
84
81
  const chatRenderRegistry = useInjectable<ChatRenderRegistry>(ChatRenderRegistryToken);
85
- const contextService = useInjectable<LLMContextService>(LLMContextServiceToken);
86
- const promptProvider = useInjectable<ChatAgentPromptProvider>(ChatAgentPromptProvider);
87
- const mcpServerProxyService = useInjectable<MCPServerProxyService>(TokenMCPServerProxyService);
88
82
 
89
83
  const layoutService = useInjectable<IMainLayoutService>(IMainLayoutService);
90
84
  const msgHistoryManager = aiChatService.sessionModel.history;
91
85
  const containerRef = React.useRef<HTMLDivElement>(null);
92
86
  const autoScroll = React.useRef<boolean>(true);
93
87
  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);
97
88
 
98
89
  const [shortcutCommands, setShortcutCommands] = React.useState<ChatSlashCommandItemModel[]>([]);
99
90
 
@@ -115,8 +106,6 @@ export const AIChatView = () => {
115
106
  const [defaultAgentId, setDefaultAgentId] = React.useState<string>('');
116
107
  const [command, setCommand] = React.useState('');
117
108
  const [theme, setTheme] = React.useState<string | null>(null);
118
- const [mcpToolsCount, setMcpToolsCount] = React.useState<number>(0);
119
- const [mcpServersCount, setMcpServersCount] = React.useState<number>(0);
120
109
 
121
110
  React.useEffect(() => {
122
111
  const featureSlashCommands = chatFeatureRegistry.getAllShortcutSlashCommand();
@@ -537,10 +526,7 @@ export const AIChatView = () => {
537
526
  const { message, agentId, command, reportExtra } = value;
538
527
  const { actionType, actionSource } = reportExtra || {};
539
528
 
540
- const context = contextService.serialize();
541
- const fullMessage = await promptProvider.provideContextPrompt(context, message);
542
-
543
- const request = aiChatService.createRequest(fullMessage, agentId!, command);
529
+ const request = aiChatService.createRequest(message, agentId!, command);
544
530
  if (!request) {
545
531
  return;
546
532
  }
@@ -689,32 +675,6 @@ export const AIChatView = () => {
689
675
  };
690
676
  }, [aiChatService.sessionModel]);
691
677
 
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
-
718
678
  return (
719
679
  <div id={styles.ai_chat_view}>
720
680
  <div className={styles.header_container}>
@@ -754,18 +714,6 @@ export const AIChatView = () => {
754
714
  </Popover>
755
715
  ))}
756
716
  </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>
769
717
  </div>
770
718
  <ChatInputWrapperRender
771
719
  onSend={(value, agentId, command) =>
@@ -808,6 +756,8 @@ export function DefaultChatViewHeader({
808
756
  const aiNativeConfigService = useInjectable<AINativeConfigService>(AINativeConfigService);
809
757
  const mcpServerProxyService = useInjectable<MCPServerProxyService>(TokenMCPServerProxyService);
810
758
  const aiChatService = useInjectable<ChatInternalService>(IChatInternalService);
759
+ const commandService = useInjectable<CommandService>(CommandService);
760
+
811
761
  const [historyList, setHistoryList] = React.useState<IChatHistoryItem[]>([]);
812
762
  const [currentTitle, setCurrentTitle] = React.useState<string>('');
813
763
  const handleNewChat = React.useCallback(() => {
@@ -828,6 +778,10 @@ export function DefaultChatViewHeader({
828
778
  [aiChatService],
829
779
  );
830
780
 
781
+ const handleShowMCPConfig = React.useCallback(() => {
782
+ commandService.executeCommand(OPEN_MCP_CONFIG_COMMAND.id);
783
+ }, [commandService]);
784
+
831
785
  const handleShowMCPTools = React.useCallback(async () => {
832
786
  const tools = await mcpServerProxyService.getAllMCPTools();
833
787
  dialogService.open({
@@ -916,23 +870,6 @@ export function DefaultChatViewHeader({
916
870
  ariaLabel={localize('aiNative.operate.clear.title')}
917
871
  />
918
872
  </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
- )}
936
873
  <Popover
937
874
  overlayClassName={styles.popover_icon}
938
875
  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);
42
+ updateAddedFiles([...files.attached]);
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, true);
61
61
  }, []);
62
62
 
63
63
  const onDidClickFile = useCallback((uri: URI) => {
@@ -1,18 +1,29 @@
1
1
  import cls from 'classnames';
2
2
  import React, { useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react';
3
3
 
4
- import { useInjectable, useLatest } from '@opensumi/ide-core-browser';
4
+ import { AINativeConfigService, 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 { ChatAgentViewServiceToken, ChatFeatureRegistryToken, localize, runWhenIdle } from '@opensumi/ide-core-common';
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';
9
16
  import { MonacoCommandRegistry } from '@opensumi/ide-editor/lib/browser/monaco-contrib/command/command.service';
17
+ import { IDialogService } from '@opensumi/ide-overlay';
10
18
 
11
- import { AT_SIGN_SYMBOL, IChatAgentService, SLASH_SYMBOL } from '../../common';
19
+ import { AT_SIGN_SYMBOL, IChatAgentService, SLASH_SYMBOL, TokenMCPServerProxyService } from '../../common';
12
20
  import { ChatAgentViewService } from '../chat/chat-agent.view.service';
13
21
  import { ChatSlashCommandItemModel } from '../chat/chat-model';
14
22
  import { ChatProxyService } from '../chat/chat-proxy.service';
15
23
  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';
16
27
  import { IChatSlashCommandItem } from '../types';
17
28
 
18
29
  import styles from './components.module.less';
@@ -194,12 +205,29 @@ export const ChatInput = React.forwardRef((props: IChatInputProps, ref) => {
194
205
  const [isExpand, setIsExpand] = useState(false);
195
206
  const [placeholder, setPlaceHolder] = useState(localize('aiNative.chat.input.placeholder.default'));
196
207
 
208
+ const dialogService = useInjectable<IDialogService>(IDialogService);
209
+ const aiNativeConfigService = useInjectable<AINativeConfigService>(AINativeConfigService);
210
+ const mcpServerProxyService = useInjectable<MCPServerProxyService>(TokenMCPServerProxyService);
197
211
  const monacoCommandRegistry = useInjectable<MonacoCommandRegistry>(MonacoCommandRegistry);
198
212
  const chatAgentService = useInjectable<IChatAgentService>(IChatAgentService);
199
213
  const chatFeatureRegistry = useInjectable<ChatFeatureRegistry>(ChatFeatureRegistryToken);
214
+ const commandService = useInjectable<CommandService>(CommandService);
200
215
 
201
216
  const currentAgentIdRef = useLatest(agentId);
202
217
 
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: [localize('dialog.file.close')],
228
+ });
229
+ }, [mcpServerProxyService, dialogService]);
230
+
203
231
  useImperativeHandle(ref, () => ({
204
232
  setInputValue: (v: string) => {
205
233
  setValue(v);
@@ -461,6 +489,42 @@ export const ChatInput = React.forwardRef((props: IChatInputProps, ref) => {
461
489
  height={inputHeight}
462
490
  popoverPosition={PopoverPosition.left}
463
491
  />
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>
464
528
  </div>
465
529
  );
466
530
  });
@@ -67,13 +67,12 @@ 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
- <span className={cls(styles['expand-icon'], { [styles.expanded]: isExpanded })}>▶</span>
70
+ <Icon iconClass={`codicon codicon-triangle-${isExpanded ? 'down' : 'right'}`} />
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>
77
76
  </div>
78
77
  )}
79
78
  </div>
@@ -3,7 +3,7 @@
3
3
  align-items: center;
4
4
  justify-content: space-between;
5
5
  font-size: 13px;
6
- padding: 0 4px 0 12px;
6
+ padding: 0 0 0 12px;
7
7
  color: var(--editor-foreground);
8
8
  text-overflow: ellipsis;
9
9
  white-space: nowrap;
@@ -96,6 +96,7 @@
96
96
  .chat_input_container {
97
97
  position: relative;
98
98
  border-radius: 9px;
99
+ padding: 10px 0px 0px 0px;
99
100
  border: 1px solid var(--kt-input-border);
100
101
  background-color: var(--design-chatInput-background);
101
102
  &.active {
@@ -505,3 +506,22 @@
505
506
  }
506
507
  }
507
508
  }
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 { FileContext, LLMContextService, SerializedContext } from '../../common/llm-context';
15
+ import { AttachFileContext, FileContext, LLMContextService, SerializedContext } from '../../common/llm-context';
16
16
 
17
17
  @Injectable()
18
18
  export class LLMContextServiceImpl extends WithEventBus implements LLMContextService {
@@ -27,40 +27,68 @@ export class LLMContextServiceImpl extends WithEventBus implements LLMContextSer
27
27
 
28
28
  private isAutoCollecting = false;
29
29
 
30
- private contextFiles: FileContext[] = [];
30
+ private contextVersion = 0;
31
31
 
32
- private maxFiles: number = 10; // 上下文的最大长度限制
33
-
34
- private onDidContextFilesChangeEmitter = new Emitter<FileContext[]>();
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 }>();
35
37
  onDidContextFilesChangeEvent = this.onDidContextFilesChangeEmitter.event;
36
38
 
37
- addFileToContext(uri: URI, selection?: [number, number], isManual = true): void {
38
- this.removeFileFromContext(uri);
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
+ }
44
+
45
+ list.push(file);
46
+ if (list.length > maxLimit) {
47
+ list.shift();
48
+ }
49
+ }
50
+
51
+ addFileToContext(uri: URI, selection?: [number, number], isManual = false): void {
52
+ if (!uri) {
53
+ return;
54
+ }
39
55
 
40
- this.contextFiles.push({ uri, selection, isManual });
56
+ const file = { uri, selection };
57
+ const targetList = isManual ? this.attachedFiles : this.recentlyViewFiles;
58
+ const maxLimit = isManual ? this.maxAttachFilesLimit : this.maxViewFilesLimit;
41
59
 
42
- if (this.contextFiles.length > this.maxFiles) {
43
- this.contextFiles.shift();
60
+ if (isManual) {
61
+ this.docModelManager.createModelReference(uri);
44
62
  }
45
63
 
64
+ this.addFileToList(file, targetList, maxLimit);
65
+ this.notifyContextChange();
66
+ }
67
+
68
+ private notifyContextChange(): void {
46
69
  this.onDidContextFilesChangeEmitter.fire(this.getAllContextFiles());
47
70
  }
48
71
 
49
72
  cleanFileContext() {
50
- this.contextFiles = [];
51
- this.onDidContextFilesChangeEmitter.fire(this.getAllContextFiles());
73
+ this.attachedFiles.length = 0;
74
+ this.notifyContextChange();
52
75
  }
53
76
 
54
77
  private getAllContextFiles() {
55
- return [...this.contextFiles];
78
+ return {
79
+ viewed: this.recentlyViewFiles,
80
+ attached: this.attachedFiles,
81
+ version: this.contextVersion++,
82
+ };
56
83
  }
57
84
 
58
- removeFileFromContext(uri: URI): void {
59
- const index = this.contextFiles.findIndex((file) => file.uri.toString() === uri.toString());
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());
60
88
  if (index > -1) {
61
- this.contextFiles.splice(index, 1);
62
- this.onDidContextFilesChangeEmitter.fire(this.getAllContextFiles());
89
+ targetList.splice(index, 1);
63
90
  }
91
+ this.notifyContextChange();
64
92
  }
65
93
 
66
94
  startAutoCollection(): void {
@@ -78,8 +106,7 @@ export class LLMContextServiceImpl extends WithEventBus implements LLMContextSer
78
106
  if (event.payload.uri.scheme !== 'file') {
79
107
  return;
80
108
  }
81
- // FIXME: 暂时不自动添加
82
- // this.addFileToContext(event.payload.uri);
109
+ this.addFileToContext(event.payload.uri, undefined, false);
83
110
  }),
84
111
  );
85
112
 
@@ -88,6 +115,8 @@ export class LLMContextServiceImpl extends WithEventBus implements LLMContextSer
88
115
  if (event.payload.scheme !== 'file') {
89
116
  return;
90
117
  }
118
+
119
+ this.removeFileFromContext(event.payload, false);
91
120
  }),
92
121
  );
93
122
 
@@ -109,11 +138,12 @@ export class LLMContextServiceImpl extends WithEventBus implements LLMContextSer
109
138
  ].sort() as [number, number];
110
139
 
111
140
  if (selection[0] === selection[1]) {
112
- this.addFileToContext(event.payload.editorUri, undefined);
141
+ this.addFileToContext(event.payload.editorUri, undefined, false);
113
142
  } else {
114
143
  this.addFileToContext(
115
144
  event.payload.editorUri,
116
145
  selection.sort((a, b) => a - b),
146
+ false,
117
147
  );
118
148
  }
119
149
  }
@@ -127,42 +157,51 @@ export class LLMContextServiceImpl extends WithEventBus implements LLMContextSer
127
157
 
128
158
  serialize(): SerializedContext {
129
159
  const files = this.getAllContextFiles();
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
- })
139
- .filter(Boolean);
140
-
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
- };
160
- })
161
- .filter(Boolean);
160
+ const workspaceRoot = URI.file(this.appConfig.workspaceDir);
162
161
 
163
162
  return {
164
- recentlyViewFiles,
165
- attachedFiles,
163
+ recentlyViewFiles: this.serializeRecentlyViewFiles(files.viewed, workspaceRoot),
164
+ attachedFiles: this.serializeAttachedFiles(files.attached, workspaceRoot),
166
165
  };
167
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())
171
+ .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
+
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,
204
+ })
205
+ .map((marker) => marker.message);
206
+ }
168
207
  }
@@ -14,16 +14,16 @@
14
14
  padding: 8px 12px;
15
15
  min-width: initial;
16
16
  margin: 0;
17
-
18
- ::after {
19
- content: '';
20
- }
21
17
  }
22
18
 
23
19
  .rce-mbox-text {
24
20
  line-height: 18px;
25
21
  width: inherit;
26
22
  font-size: 12px;
23
+
24
+ &::after {
25
+ display: none;
26
+ }
27
27
  }
28
28
 
29
29
  .rce-smsg {