@opensumi/ide-ai-native 3.7.2-next-1740064823.0 → 3.7.2-next-1740107209.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/ai-core.contribution.d.ts.map +1 -1
- package/lib/browser/ai-core.contribution.js +4 -0
- package/lib/browser/ai-core.contribution.js.map +1 -1
- package/lib/browser/chat/chat-agent.service.d.ts.map +1 -1
- package/lib/browser/chat/chat-agent.service.js +6 -0
- 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 +5 -23
- package/lib/browser/chat/chat-model.js.map +1 -1
- package/lib/browser/chat/chat-proxy.service.d.ts +1 -0
- package/lib/browser/chat/chat-proxy.service.d.ts.map +1 -1
- package/lib/browser/chat/chat-proxy.service.js +8 -1
- package/lib/browser/chat/chat-proxy.service.js.map +1 -1
- package/lib/browser/chat/chat.internal.service.d.ts +4 -0
- package/lib/browser/chat/chat.internal.service.d.ts.map +1 -1
- package/lib/browser/chat/chat.internal.service.js +10 -1
- package/lib/browser/chat/chat.internal.service.js.map +1 -1
- package/lib/browser/chat/chat.view.d.ts.map +1 -1
- package/lib/browser/chat/chat.view.js +2 -2
- package/lib/browser/chat/chat.view.js.map +1 -1
- package/lib/browser/components/ChatEditor.d.ts +1 -0
- package/lib/browser/components/ChatEditor.d.ts.map +1 -1
- package/lib/browser/components/ChatEditor.js +3 -3
- package/lib/browser/components/ChatEditor.js.map +1 -1
- package/lib/browser/components/ChatMarkdown.d.ts +1 -0
- package/lib/browser/components/ChatMarkdown.d.ts.map +1 -1
- package/lib/browser/components/ChatMarkdown.js +2 -2
- package/lib/browser/components/ChatMarkdown.js.map +1 -1
- package/lib/browser/components/ChatReply.d.ts.map +1 -1
- package/lib/browser/components/ChatReply.js +6 -8
- package/lib/browser/components/ChatReply.js.map +1 -1
- package/lib/browser/components/ChatToolRender.d.ts +2 -1
- package/lib/browser/components/ChatToolRender.d.ts.map +1 -1
- package/lib/browser/components/ChatToolRender.js +40 -19
- package/lib/browser/components/ChatToolRender.js.map +1 -1
- package/lib/browser/components/components.module.less +3 -2
- package/lib/browser/index.d.ts.map +1 -1
- package/lib/browser/index.js +7 -0
- package/lib/browser/index.js.map +1 -1
- package/lib/browser/mcp/base-apply.service.d.ts +67 -0
- package/lib/browser/mcp/base-apply.service.d.ts.map +1 -0
- package/lib/browser/mcp/base-apply.service.js +290 -0
- package/lib/browser/mcp/base-apply.service.js.map +1 -0
- package/lib/browser/mcp/mcp-server-proxy.service.d.ts +6 -1
- package/lib/browser/mcp/mcp-server-proxy.service.d.ts.map +1 -1
- package/lib/browser/mcp/mcp-server-proxy.service.js +2 -1
- package/lib/browser/mcp/mcp-server-proxy.service.js.map +1 -1
- package/lib/browser/mcp/mcp-server.feature.registry.d.ts +5 -1
- package/lib/browser/mcp/mcp-server.feature.registry.d.ts.map +1 -1
- package/lib/browser/mcp/mcp-server.feature.registry.js +15 -0
- package/lib/browser/mcp/mcp-server.feature.registry.js.map +1 -1
- package/lib/browser/mcp/tools/components/EditFile.d.ts +3 -0
- package/lib/browser/mcp/tools/components/EditFile.d.ts.map +1 -0
- package/lib/browser/mcp/tools/components/EditFile.js +101 -0
- package/lib/browser/mcp/tools/components/EditFile.js.map +1 -0
- package/lib/browser/mcp/tools/components/index.module.less +67 -0
- package/lib/browser/mcp/tools/createNewFileWithText.d.ts.map +1 -1
- package/lib/browser/mcp/tools/createNewFileWithText.js +1 -2
- package/lib/browser/mcp/tools/createNewFileWithText.js.map +1 -1
- package/lib/browser/mcp/tools/editFile.d.ts +8 -0
- package/lib/browser/mcp/tools/editFile.d.ts.map +1 -0
- package/lib/browser/mcp/tools/editFile.js +95 -0
- package/lib/browser/mcp/tools/editFile.js.map +1 -0
- package/lib/browser/mcp/tools/findFilesByNameSubstring.d.ts.map +1 -1
- package/lib/browser/mcp/tools/findFilesByNameSubstring.js +1 -2
- package/lib/browser/mcp/tools/findFilesByNameSubstring.js.map +1 -1
- package/lib/browser/mcp/tools/getCurrentFilePath.d.ts.map +1 -1
- package/lib/browser/mcp/tools/getCurrentFilePath.js +1 -2
- package/lib/browser/mcp/tools/getCurrentFilePath.js.map +1 -1
- package/lib/browser/mcp/tools/getDiagnosticsByPath.d.ts.map +1 -1
- package/lib/browser/mcp/tools/getDiagnosticsByPath.js +1 -2
- package/lib/browser/mcp/tools/getDiagnosticsByPath.js.map +1 -1
- package/lib/browser/mcp/tools/getFileTextByPath.d.ts.map +1 -1
- package/lib/browser/mcp/tools/getFileTextByPath.js +1 -2
- package/lib/browser/mcp/tools/getFileTextByPath.js.map +1 -1
- package/lib/browser/mcp/tools/getOpenEditorFileDiagnostics.d.ts.map +1 -1
- package/lib/browser/mcp/tools/getOpenEditorFileDiagnostics.js +1 -2
- package/lib/browser/mcp/tools/getOpenEditorFileDiagnostics.js.map +1 -1
- package/lib/browser/mcp/tools/getOpenEditorFileText.d.ts.map +1 -1
- package/lib/browser/mcp/tools/getOpenEditorFileText.js +1 -2
- package/lib/browser/mcp/tools/getOpenEditorFileText.js.map +1 -1
- package/lib/browser/mcp/tools/getSelectedText.d.ts.map +1 -1
- package/lib/browser/mcp/tools/getSelectedText.js +1 -2
- package/lib/browser/mcp/tools/getSelectedText.js.map +1 -1
- package/lib/browser/mcp/tools/handlers/EditFile.d.ts +10 -0
- package/lib/browser/mcp/tools/handlers/EditFile.d.ts.map +1 -0
- package/lib/browser/mcp/tools/handlers/EditFile.js +28 -0
- package/lib/browser/mcp/tools/handlers/EditFile.js.map +1 -0
- package/lib/browser/mcp/tools/handlers/ReadFile.d.ts +6 -0
- package/lib/browser/mcp/tools/handlers/ReadFile.d.ts.map +1 -1
- package/lib/browser/mcp/tools/handlers/ReadFile.js +14 -0
- package/lib/browser/mcp/tools/handlers/ReadFile.js.map +1 -1
- package/lib/browser/mcp/tools/handlers/utils.d.ts +2 -0
- package/lib/browser/mcp/tools/handlers/utils.d.ts.map +1 -0
- package/lib/browser/mcp/tools/handlers/utils.js +7 -0
- package/lib/browser/mcp/tools/handlers/utils.js.map +1 -0
- package/lib/browser/mcp/tools/listDir.d.ts.map +1 -1
- package/lib/browser/mcp/tools/listDir.js +2 -4
- package/lib/browser/mcp/tools/listDir.js.map +1 -1
- package/lib/browser/mcp/tools/readFile.d.ts.map +1 -1
- package/lib/browser/mcp/tools/readFile.js +2 -4
- package/lib/browser/mcp/tools/readFile.js.map +1 -1
- package/lib/browser/mcp/tools/replaceOpenEditorFile.d.ts.map +1 -1
- package/lib/browser/mcp/tools/replaceOpenEditorFile.js +6 -5
- package/lib/browser/mcp/tools/replaceOpenEditorFile.js.map +1 -1
- package/lib/browser/mcp/tools/replaceOpenEditorFileByDiffPreviewer.d.ts.map +1 -1
- package/lib/browser/mcp/tools/replaceOpenEditorFileByDiffPreviewer.js +1 -2
- package/lib/browser/mcp/tools/replaceOpenEditorFileByDiffPreviewer.js.map +1 -1
- package/lib/browser/mcp/tools/runTerminalCmd.d.ts.map +1 -1
- package/lib/browser/mcp/tools/runTerminalCmd.js +1 -2
- 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 +5 -0
- package/lib/browser/preferences/schema.js.map +1 -1
- package/lib/browser/types.d.ts +14 -10
- package/lib/browser/types.d.ts.map +1 -1
- package/lib/browser/types.js +1 -2
- package/lib/browser/types.js.map +1 -1
- package/lib/browser/widget/inline-stream-diff/inline-stream-diff.handler.d.ts +1 -0
- package/lib/browser/widget/inline-stream-diff/inline-stream-diff.handler.d.ts.map +1 -1
- package/lib/browser/widget/inline-stream-diff/inline-stream-diff.handler.js +3 -0
- package/lib/browser/widget/inline-stream-diff/inline-stream-diff.handler.js.map +1 -1
- package/lib/browser/widget/inline-stream-diff/live-preview.component.d.ts +4 -0
- package/lib/browser/widget/inline-stream-diff/live-preview.component.d.ts.map +1 -1
- package/lib/browser/widget/inline-stream-diff/live-preview.component.js.map +1 -1
- package/lib/browser/widget/inline-stream-diff/live-preview.decoration.d.ts.map +1 -1
- package/lib/browser/widget/inline-stream-diff/live-preview.decoration.js +1 -0
- package/lib/browser/widget/inline-stream-diff/live-preview.decoration.js.map +1 -1
- package/lib/common/index.d.ts +1 -0
- package/lib/common/index.d.ts.map +1 -1
- package/lib/common/index.js.map +1 -1
- package/lib/common/prompts/context-prompt-provider.d.ts +14 -0
- package/lib/common/prompts/context-prompt-provider.d.ts.map +1 -0
- package/lib/common/prompts/context-prompt-provider.js +38 -0
- package/lib/common/prompts/context-prompt-provider.js.map +1 -0
- package/lib/common/utils.d.ts +1 -0
- package/lib/common/utils.d.ts.map +1 -1
- package/lib/common/utils.js +3 -1
- package/lib/common/utils.js.map +1 -1
- package/lib/node/base-language-model.d.ts +2 -2
- package/lib/node/base-language-model.d.ts.map +1 -1
- package/lib/node/base-language-model.js +8 -4
- package/lib/node/base-language-model.js.map +1 -1
- package/lib/node/mcp-server-manager-impl.d.ts.map +1 -1
- package/lib/node/mcp-server-manager-impl.js +2 -1
- package/lib/node/mcp-server-manager-impl.js.map +1 -1
- package/lib/node/openai/openai-language-model.d.ts +1 -1
- package/lib/node/openai/openai-language-model.d.ts.map +1 -1
- package/lib/node/openai/openai-language-model.js +2 -2
- package/lib/node/openai/openai-language-model.js.map +1 -1
- package/package.json +23 -25
- package/src/browser/ai-core.contribution.ts +4 -0
- package/src/browser/chat/chat-agent.service.ts +7 -0
- package/src/browser/chat/chat-model.ts +0 -2
- package/src/browser/chat/chat-proxy.service.ts +12 -2
- package/src/browser/chat/chat.internal.service.ts +12 -1
- package/src/browser/chat/chat.view.tsx +2 -1
- package/src/browser/components/ChatEditor.tsx +13 -10
- package/src/browser/components/ChatMarkdown.tsx +3 -1
- package/src/browser/components/ChatReply.tsx +8 -15
- package/src/browser/components/ChatToolRender.tsx +41 -20
- package/src/browser/components/components.module.less +3 -2
- package/src/browser/index.ts +7 -0
- package/src/browser/mcp/base-apply.service.ts +349 -0
- package/src/browser/mcp/mcp-server-proxy.service.ts +4 -2
- package/src/browser/mcp/mcp-server.feature.registry.ts +25 -3
- package/src/browser/mcp/tools/components/EditFile.tsx +144 -0
- package/src/browser/mcp/tools/components/index.module.less +67 -0
- package/src/browser/mcp/tools/createNewFileWithText.ts +1 -2
- package/src/browser/mcp/tools/editFile.ts +100 -0
- package/src/browser/mcp/tools/findFilesByNameSubstring.ts +1 -2
- package/src/browser/mcp/tools/getCurrentFilePath.ts +1 -2
- package/src/browser/mcp/tools/getDiagnosticsByPath.ts +1 -2
- package/src/browser/mcp/tools/getFileTextByPath.ts +1 -2
- package/src/browser/mcp/tools/getOpenEditorFileDiagnostics.ts +1 -2
- package/src/browser/mcp/tools/getOpenEditorFileText.ts +1 -2
- package/src/browser/mcp/tools/getSelectedText.ts +1 -2
- package/src/browser/mcp/tools/handlers/EditFile.ts +21 -0
- package/src/browser/mcp/tools/handlers/ReadFile.ts +19 -1
- package/src/browser/mcp/tools/handlers/utils.ts +3 -0
- package/src/browser/mcp/tools/listDir.ts +2 -4
- package/src/browser/mcp/tools/readFile.ts +2 -4
- package/src/browser/mcp/tools/replaceOpenEditorFile.ts +8 -7
- package/src/browser/mcp/tools/replaceOpenEditorFileByDiffPreviewer.ts +1 -2
- package/src/browser/mcp/tools/runTerminalCmd.ts +1 -2
- package/src/browser/preferences/schema.ts +5 -0
- package/src/browser/types.ts +15 -11
- package/src/browser/widget/inline-stream-diff/inline-stream-diff.handler.tsx +4 -0
- package/src/browser/widget/inline-stream-diff/live-preview.component.tsx +4 -0
- package/src/browser/widget/inline-stream-diff/live-preview.decoration.tsx +1 -0
- package/src/common/index.ts +1 -0
- package/src/common/prompts/context-prompt-provider.ts +46 -0
- package/src/common/utils.ts +2 -0
- package/src/node/base-language-model.ts +17 -4
- package/src/node/mcp-server-manager-impl.ts +2 -1
- package/src/node/openai/openai-language-model.ts +2 -2
|
@@ -40,6 +40,7 @@ import {
|
|
|
40
40
|
TokenMCPServerProxyService,
|
|
41
41
|
} from '../../common';
|
|
42
42
|
import { LLMContextService, LLMContextServiceToken } from '../../common/llm-context';
|
|
43
|
+
import { ChatAgentPromptProvider } from '../../common/prompts/context-prompt-provider';
|
|
43
44
|
import { ChatContext } from '../components/ChatContext';
|
|
44
45
|
import { CodeBlockWrapperInput } from '../components/ChatEditor';
|
|
45
46
|
import { ChatInput } from '../components/ChatInput';
|
|
@@ -50,7 +51,7 @@ import { MessageData, createMessageByAI, createMessageByUser } from '../componen
|
|
|
50
51
|
import { WelcomeMessage } from '../components/WelcomeMsg';
|
|
51
52
|
import { MCPServerProxyService } from '../mcp/mcp-server-proxy.service';
|
|
52
53
|
import { MCPToolsDialog } from '../mcp/mcp-tools-dialog.view';
|
|
53
|
-
import {
|
|
54
|
+
import { ChatViewHeaderRender, TSlashCommandCustomRender } from '../types';
|
|
54
55
|
|
|
55
56
|
import { ChatRequestModel, ChatSlashCommandItemModel } from './chat-model';
|
|
56
57
|
import { ChatProxyService } from './chat-proxy.service';
|
|
@@ -31,9 +31,10 @@ interface Props {
|
|
|
31
31
|
language?: string;
|
|
32
32
|
agentId?: string;
|
|
33
33
|
command?: string;
|
|
34
|
+
hideInsert?: boolean;
|
|
34
35
|
}
|
|
35
36
|
export const CodeEditorWithHighlight = (props: Props) => {
|
|
36
|
-
const { input, language, relationId, agentId, command } = props;
|
|
37
|
+
const { input, language, relationId, agentId, command, hideInsert } = props;
|
|
37
38
|
const ref = React.useRef<HTMLDivElement | null>(null);
|
|
38
39
|
const monacoCommandRegistry = useInjectable<MonacoCommandRegistry>(MonacoCommandRegistry);
|
|
39
40
|
const clipboardService = useInjectable<IClipboardService>(IClipboardService);
|
|
@@ -101,15 +102,17 @@ export const CodeEditorWithHighlight = (props: Props) => {
|
|
|
101
102
|
return (
|
|
102
103
|
<div className={styles.monaco_wrapper}>
|
|
103
104
|
<div className={styles.action_toolbar}>
|
|
104
|
-
|
|
105
|
-
<
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
105
|
+
{!hideInsert && (
|
|
106
|
+
<Popover id={`ai-chat-inser-${useUUID}`} title={localize('aiNative.chat.code.insert')}>
|
|
107
|
+
<EnhanceIcon
|
|
108
|
+
className={getIcon('insert')}
|
|
109
|
+
onClick={() => handleInsert()}
|
|
110
|
+
tabIndex={0}
|
|
111
|
+
role='button'
|
|
112
|
+
ariaLabel={localize('aiNative.chat.code.insert')}
|
|
113
|
+
/>
|
|
114
|
+
</Popover>
|
|
115
|
+
)}
|
|
113
116
|
<Popover
|
|
114
117
|
id={`ai-chat-copy-${useUUID}`}
|
|
115
118
|
title={localize(isCoping ? 'aiNative.chat.code.copy.success' : 'aiNative.chat.code.copy')}
|
|
@@ -17,6 +17,7 @@ interface MarkdownProps {
|
|
|
17
17
|
className?: string;
|
|
18
18
|
fillInIncompleteTokens?: boolean; // 补齐不完整的 token,如代码块或表格
|
|
19
19
|
markedOptions?: IMarkedOptions;
|
|
20
|
+
hideInsert?: boolean;
|
|
20
21
|
}
|
|
21
22
|
|
|
22
23
|
export const ChatMarkdown = (props: MarkdownProps) => {
|
|
@@ -42,13 +43,14 @@ export const ChatMarkdown = (props: MarkdownProps) => {
|
|
|
42
43
|
<div className={styles.code}>
|
|
43
44
|
<ConfigProvider value={appConfig}>
|
|
44
45
|
<div className={styles.code_block}>
|
|
45
|
-
<div className={styles.code_language}>{language}</div>
|
|
46
|
+
<div className={cls(styles.code_language, 'language-badge')}>{language}</div>
|
|
46
47
|
<CodeEditorWithHighlight
|
|
47
48
|
input={code as string}
|
|
48
49
|
language={language}
|
|
49
50
|
relationId={props.relationId || ''}
|
|
50
51
|
agentId={props.agentId}
|
|
51
52
|
command={props.command}
|
|
53
|
+
hideInsert={props.hideInsert}
|
|
52
54
|
/>
|
|
53
55
|
</div>
|
|
54
56
|
</ConfigProvider>
|
|
@@ -149,8 +149,8 @@ const TreeRenderer = (props: { treeData: IChatResponseProgressFileTreeData }) =>
|
|
|
149
149
|
);
|
|
150
150
|
};
|
|
151
151
|
|
|
152
|
-
const ToolCallRender = (props: { toolCall: IChatToolContent['content'] }) => {
|
|
153
|
-
const { toolCall } = props;
|
|
152
|
+
const ToolCallRender = (props: { toolCall: IChatToolContent['content']; messageId?: string }) => {
|
|
153
|
+
const { toolCall, messageId } = props;
|
|
154
154
|
const chatAgentViewService = useInjectable<IChatAgentViewService>(ChatAgentViewServiceToken);
|
|
155
155
|
const [node, setNode] = useState<React.JSX.Element | null>(null);
|
|
156
156
|
|
|
@@ -158,7 +158,7 @@ const ToolCallRender = (props: { toolCall: IChatToolContent['content'] }) => {
|
|
|
158
158
|
const config = chatAgentViewService.getChatComponent('toolCall');
|
|
159
159
|
if (config) {
|
|
160
160
|
const { component: Component, initialProps } = config;
|
|
161
|
-
setNode(<Component {...initialProps} value={toolCall} />);
|
|
161
|
+
setNode(<Component {...initialProps} value={toolCall} messageId={messageId} />);
|
|
162
162
|
return;
|
|
163
163
|
}
|
|
164
164
|
setNode(
|
|
@@ -169,14 +169,14 @@ const ToolCallRender = (props: { toolCall: IChatToolContent['content'] }) => {
|
|
|
169
169
|
);
|
|
170
170
|
const deferred = chatAgentViewService.getChatComponentDeferred('toolCall')!;
|
|
171
171
|
deferred.promise.then(({ component: Component, initialProps }) => {
|
|
172
|
-
setNode(<Component {...initialProps} value={toolCall} />);
|
|
172
|
+
setNode(<Component {...initialProps} value={toolCall} messageId={messageId} />);
|
|
173
173
|
});
|
|
174
174
|
}, [toolCall.state]);
|
|
175
175
|
|
|
176
176
|
return node;
|
|
177
177
|
};
|
|
178
178
|
|
|
179
|
-
const ComponentRender = (props: { component: string; value?: unknown }) => {
|
|
179
|
+
const ComponentRender = (props: { component: string; value?: unknown; messageId?: string }) => {
|
|
180
180
|
const chatAgentViewService = useInjectable<IChatAgentViewService>(ChatAgentViewServiceToken);
|
|
181
181
|
const [node, setNode] = useState<React.JSX.Element | null>(null);
|
|
182
182
|
|
|
@@ -184,7 +184,7 @@ const ComponentRender = (props: { component: string; value?: unknown }) => {
|
|
|
184
184
|
const config = chatAgentViewService.getChatComponent(props.component);
|
|
185
185
|
if (config) {
|
|
186
186
|
const { component: Component, initialProps } = config;
|
|
187
|
-
setNode(<Component {...initialProps} value={props.value} />);
|
|
187
|
+
setNode(<Component {...initialProps} value={props.value} messageId={props.messageId} />);
|
|
188
188
|
return;
|
|
189
189
|
}
|
|
190
190
|
setNode(
|
|
@@ -224,7 +224,6 @@ export const ChatReply = (props: IChatReplyProps) => {
|
|
|
224
224
|
const chatApiService = useInjectable<ChatService>(ChatServiceToken);
|
|
225
225
|
const chatAgentService = useInjectable<IChatAgentService>(IChatAgentService);
|
|
226
226
|
const chatRenderRegistry = useInjectable<ChatRenderRegistry>(ChatRenderRegistryToken);
|
|
227
|
-
|
|
228
227
|
useEffect(() => {
|
|
229
228
|
const disposableCollection = new DisposableCollection();
|
|
230
229
|
|
|
@@ -298,12 +297,6 @@ export const ChatReply = (props: IChatReplyProps) => {
|
|
|
298
297
|
</div>
|
|
299
298
|
);
|
|
300
299
|
|
|
301
|
-
const renderComponent = (componentId: string, value: unknown) => (
|
|
302
|
-
<ComponentRender component={componentId} value={value} />
|
|
303
|
-
);
|
|
304
|
-
|
|
305
|
-
const renderToolCall = (toolCall: IChatToolContent['content']) => <ToolCallRender toolCall={toolCall} />;
|
|
306
|
-
|
|
307
300
|
const contentNode = React.useMemo(
|
|
308
301
|
() =>
|
|
309
302
|
request.response.responseContents.map((item, index) => {
|
|
@@ -313,9 +306,9 @@ export const ChatReply = (props: IChatReplyProps) => {
|
|
|
313
306
|
} else if (item.kind === 'treeData') {
|
|
314
307
|
node = renderTreeData(item.treeData);
|
|
315
308
|
} else if (item.kind === 'component') {
|
|
316
|
-
node =
|
|
309
|
+
node = <ComponentRender component={item.component} value={item.value} messageId={msgId} />;
|
|
317
310
|
} else if (item.kind === 'toolCall') {
|
|
318
|
-
node =
|
|
311
|
+
node = <ToolCallRender toolCall={item.content} messageId={msgId} />;
|
|
319
312
|
} else {
|
|
320
313
|
node = renderMarkdown(item.content);
|
|
321
314
|
}
|
|
@@ -1,20 +1,27 @@
|
|
|
1
1
|
import cls from 'classnames';
|
|
2
2
|
import React, { useState } from 'react';
|
|
3
3
|
|
|
4
|
+
import { useInjectable } from '@opensumi/ide-core-browser';
|
|
4
5
|
import { Icon } from '@opensumi/ide-core-browser/lib/components';
|
|
5
6
|
import { Loading } from '@opensumi/ide-core-browser/lib/components/ai-native';
|
|
6
7
|
import { IChatToolContent, uuid } from '@opensumi/ide-core-common';
|
|
7
8
|
|
|
9
|
+
import { IMCPServerRegistry, TokenMCPServerRegistry } from '../types';
|
|
10
|
+
|
|
8
11
|
import { CodeEditorWithHighlight } from './ChatEditor';
|
|
9
12
|
import styles from './ChatToolRender.module.less';
|
|
10
13
|
|
|
11
|
-
export const ChatToolRender = (props: { value: IChatToolContent['content'] }) => {
|
|
12
|
-
const { value } = props;
|
|
14
|
+
export const ChatToolRender = (props: { value: IChatToolContent['content']; messageId?: string }) => {
|
|
15
|
+
const { value, messageId } = props;
|
|
13
16
|
const [isExpanded, setIsExpanded] = useState(false);
|
|
17
|
+
const mcpServerFeatureRegistry = useInjectable<IMCPServerRegistry>(TokenMCPServerRegistry);
|
|
14
18
|
|
|
15
19
|
if (!value || !value.function || !value.id) {
|
|
16
20
|
return null;
|
|
17
21
|
}
|
|
22
|
+
const label = mcpServerFeatureRegistry.getMCPTool(value.function.name)?.label || value.function.name;
|
|
23
|
+
|
|
24
|
+
const ToolComponent = mcpServerFeatureRegistry.getToolComponent(value.function.name);
|
|
18
25
|
|
|
19
26
|
const getStateInfo = (state?: string): { label: string; icon: React.ReactNode } => {
|
|
20
27
|
switch (state) {
|
|
@@ -22,11 +29,22 @@ export const ChatToolRender = (props: { value: IChatToolContent['content'] }) =>
|
|
|
22
29
|
case 'streaming':
|
|
23
30
|
return { label: 'Generating', icon: <Loading /> };
|
|
24
31
|
case 'complete':
|
|
25
|
-
return { label: 'Complete', icon: <Icon iconClass=
|
|
32
|
+
return { label: 'Complete', icon: <Icon iconClass='codicon codicon-check' /> };
|
|
26
33
|
case 'result':
|
|
27
|
-
return { label: 'Result Ready', icon: <Icon iconClass=
|
|
34
|
+
return { label: 'Result Ready', icon: <Icon iconClass='codicon codicon-check-all' /> };
|
|
28
35
|
default:
|
|
29
|
-
return { label: state || 'Unknown', icon: <Icon iconClass=
|
|
36
|
+
return { label: state || 'Unknown', icon: <Icon iconClass='codicon codicon-question' /> };
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
const getParsedArgs = () => {
|
|
40
|
+
try {
|
|
41
|
+
// TODO: 流式输出中function_call的参数还不完整,需要等待complete状态
|
|
42
|
+
if (value.state !== 'complete' && value.state !== 'result') {
|
|
43
|
+
return {};
|
|
44
|
+
}
|
|
45
|
+
return JSON.parse(value.function?.arguments || '{}');
|
|
46
|
+
} catch (error) {
|
|
47
|
+
return {};
|
|
30
48
|
}
|
|
31
49
|
};
|
|
32
50
|
|
|
@@ -36,12 +54,12 @@ export const ChatToolRender = (props: { value: IChatToolContent['content'] }) =>
|
|
|
36
54
|
|
|
37
55
|
const stateInfo = getStateInfo(value.state);
|
|
38
56
|
|
|
39
|
-
return
|
|
40
|
-
<div className={styles['chat-tool-render']}>
|
|
57
|
+
return [
|
|
58
|
+
<div className={styles['chat-tool-render']} key='chat-tool-render'>
|
|
41
59
|
<div className={styles['tool-header']} onClick={toggleExpand}>
|
|
42
60
|
<div className={styles['tool-name']}>
|
|
43
61
|
<span className={cls(styles['expand-icon'], { [styles.expanded]: isExpanded })}>▶</span>
|
|
44
|
-
{
|
|
62
|
+
{label}
|
|
45
63
|
</div>
|
|
46
64
|
{value.state && (
|
|
47
65
|
<div className={styles['tool-state']}>
|
|
@@ -54,24 +72,27 @@ export const ChatToolRender = (props: { value: IChatToolContent['content'] }) =>
|
|
|
54
72
|
{value?.function?.arguments && (
|
|
55
73
|
<div className={styles['tool-arguments']}>
|
|
56
74
|
<div className={styles['section-label']}>Arguments</div>
|
|
57
|
-
<CodeEditorWithHighlight
|
|
58
|
-
input={value?.function?.arguments}
|
|
59
|
-
language={'json'}
|
|
60
|
-
relationId={uuid(4)}
|
|
61
|
-
/>
|
|
75
|
+
<CodeEditorWithHighlight input={value?.function?.arguments} language={'json'} relationId={uuid(4)} />
|
|
62
76
|
</div>
|
|
63
77
|
)}
|
|
64
78
|
{value?.result && (
|
|
65
79
|
<div className={styles['tool-result']}>
|
|
66
80
|
<div className={styles['section-label']}>Result</div>
|
|
67
|
-
<CodeEditorWithHighlight
|
|
68
|
-
input={value.result}
|
|
69
|
-
language={'json'}
|
|
70
|
-
relationId={uuid(4)}
|
|
71
|
-
/>
|
|
81
|
+
<CodeEditorWithHighlight input={value.result} language={'json'} relationId={uuid(4)} />
|
|
72
82
|
</div>
|
|
73
83
|
)}
|
|
74
84
|
</div>
|
|
75
|
-
</div
|
|
76
|
-
|
|
85
|
+
</div>,
|
|
86
|
+
ToolComponent && (
|
|
87
|
+
<ToolComponent
|
|
88
|
+
key='tool-component'
|
|
89
|
+
state={value.state}
|
|
90
|
+
args={getParsedArgs()}
|
|
91
|
+
result={value.result}
|
|
92
|
+
index={value.index}
|
|
93
|
+
messageId={messageId}
|
|
94
|
+
toolCallId={value.id}
|
|
95
|
+
/>
|
|
96
|
+
),
|
|
97
|
+
];
|
|
77
98
|
};
|
|
@@ -253,7 +253,7 @@
|
|
|
253
253
|
.editor {
|
|
254
254
|
border-radius: 8px;
|
|
255
255
|
font-size: 12px;
|
|
256
|
-
padding:
|
|
256
|
+
padding: 28px 8px 8px 8px;
|
|
257
257
|
line-height: 18px;
|
|
258
258
|
&::-webkit-scrollbar {
|
|
259
259
|
width: auto;
|
|
@@ -265,7 +265,7 @@
|
|
|
265
265
|
display: flex;
|
|
266
266
|
position: absolute;
|
|
267
267
|
right: 8px;
|
|
268
|
-
top:
|
|
268
|
+
top: 2px;
|
|
269
269
|
z-index: 100;
|
|
270
270
|
height: 20px;
|
|
271
271
|
align-items: center;
|
|
@@ -300,6 +300,7 @@
|
|
|
300
300
|
background-color: var(--design-language-background);
|
|
301
301
|
border-radius: 8px 0px 8px 0;
|
|
302
302
|
color: var(--design-text-foreground);
|
|
303
|
+
font-size: 12px;
|
|
303
304
|
}
|
|
304
305
|
}
|
|
305
306
|
|
package/src/browser/index.ts
CHANGED
|
@@ -30,6 +30,7 @@ import {
|
|
|
30
30
|
} from '../common';
|
|
31
31
|
import { LLMContextServiceToken } from '../common/llm-context';
|
|
32
32
|
import { MCPServerManager, MCPServerManagerPath } from '../common/mcp-server-manager';
|
|
33
|
+
import { ChatAgentPromptProvider, DefaultChatAgentPromptProvider } from '../common/prompts/context-prompt-provider';
|
|
33
34
|
|
|
34
35
|
import { AINativeBrowserContribution } from './ai-core.contribution';
|
|
35
36
|
import { ChatAgentService } from './chat/chat-agent.service';
|
|
@@ -57,6 +58,7 @@ import { LanguageParserService } from './languages/service';
|
|
|
57
58
|
import { MCPServerProxyService } from './mcp/mcp-server-proxy.service';
|
|
58
59
|
import { MCPServerRegistry } from './mcp/mcp-server.feature.registry';
|
|
59
60
|
import { CreateNewFileWithTextTool } from './mcp/tools/createNewFileWithText';
|
|
61
|
+
import { EditFileTool } from './mcp/tools/editFile';
|
|
60
62
|
import { FindFilesByNameSubstringTool } from './mcp/tools/findFilesByNameSubstring';
|
|
61
63
|
import { GetCurrentFilePathTool } from './mcp/tools/getCurrentFilePath';
|
|
62
64
|
import { GetDiagnosticsByPathTool } from './mcp/tools/getDiagnosticsByPath';
|
|
@@ -97,6 +99,7 @@ export class AINativeModule extends BrowserModule {
|
|
|
97
99
|
// MCP Server Contributions START
|
|
98
100
|
ListDirTool,
|
|
99
101
|
ReadFileTool,
|
|
102
|
+
EditFileTool,
|
|
100
103
|
CreateNewFileWithTextTool,
|
|
101
104
|
GetSelectedTextTool,
|
|
102
105
|
GetOpenEditorFileDiagnosticsTool,
|
|
@@ -196,6 +199,10 @@ export class AINativeModule extends BrowserModule {
|
|
|
196
199
|
token: InlineDiffService,
|
|
197
200
|
useClass: InlineDiffService,
|
|
198
201
|
},
|
|
202
|
+
{
|
|
203
|
+
token: ChatAgentPromptProvider,
|
|
204
|
+
useValue: DefaultChatAgentPromptProvider,
|
|
205
|
+
},
|
|
199
206
|
];
|
|
200
207
|
|
|
201
208
|
backServices = [
|
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
import { createPatch } from 'diff';
|
|
2
|
+
|
|
3
|
+
import { Autowired } from '@opensumi/di';
|
|
4
|
+
import { AppConfig, ChatMessageRole, IMarker, MarkerSeverity, OnEvent, WithEventBus } from '@opensumi/ide-core-browser';
|
|
5
|
+
import { WorkbenchEditorService } from '@opensumi/ide-editor';
|
|
6
|
+
import { EditorGroupCloseEvent } from '@opensumi/ide-editor/lib/browser';
|
|
7
|
+
import { IMarkerService } from '@opensumi/ide-markers';
|
|
8
|
+
import { Position, Range, Selection, SelectionDirection } from '@opensumi/ide-monaco';
|
|
9
|
+
import { observableValue, transaction } from '@opensumi/ide-monaco/lib/common/observable';
|
|
10
|
+
import { Deferred, URI, path } from '@opensumi/ide-utils';
|
|
11
|
+
|
|
12
|
+
import { IChatInternalService } from '../../common';
|
|
13
|
+
import { ChatInternalService } from '../chat/chat.internal.service';
|
|
14
|
+
import {
|
|
15
|
+
BaseInlineDiffPreviewer,
|
|
16
|
+
InlineDiffController,
|
|
17
|
+
InlineDiffService,
|
|
18
|
+
LiveInlineDiffPreviewer,
|
|
19
|
+
} from '../widget/inline-diff';
|
|
20
|
+
import { InlineStreamDiffHandler } from '../widget/inline-stream-diff/inline-stream-diff.handler';
|
|
21
|
+
|
|
22
|
+
import { FileHandler } from './tools/handlers/ReadFile';
|
|
23
|
+
|
|
24
|
+
// 提供代码块的唯一索引,迭代轮次,生成状态管理(包括取消),关联文件位置这些信息的记录,后续并行 apply 的支持
|
|
25
|
+
export abstract class BaseApplyService extends WithEventBus {
|
|
26
|
+
@Autowired(FileHandler)
|
|
27
|
+
protected fileHandler: FileHandler;
|
|
28
|
+
|
|
29
|
+
@Autowired(IChatInternalService)
|
|
30
|
+
protected chatInternalService: ChatInternalService;
|
|
31
|
+
|
|
32
|
+
@Autowired(AppConfig)
|
|
33
|
+
protected appConfig: AppConfig;
|
|
34
|
+
|
|
35
|
+
@Autowired(WorkbenchEditorService)
|
|
36
|
+
protected readonly editorService: WorkbenchEditorService;
|
|
37
|
+
|
|
38
|
+
@Autowired(InlineDiffService)
|
|
39
|
+
private readonly inlineDiffService: InlineDiffService;
|
|
40
|
+
|
|
41
|
+
@Autowired(IMarkerService)
|
|
42
|
+
private readonly markerService: IMarkerService;
|
|
43
|
+
|
|
44
|
+
constructor() {
|
|
45
|
+
super();
|
|
46
|
+
this.addDispose(
|
|
47
|
+
this.chatInternalService.onCancelRequest(() => {
|
|
48
|
+
this.cancelAllApply();
|
|
49
|
+
}),
|
|
50
|
+
);
|
|
51
|
+
this.addDispose(
|
|
52
|
+
this.chatInternalService.onRegenerateRequest(() => {
|
|
53
|
+
const messages = this.chatInternalService.sessionModel.history.getMessages();
|
|
54
|
+
const messageId = messages[messages.length - 1].id;
|
|
55
|
+
messageId && this.disposeApplyForMessage(messageId);
|
|
56
|
+
}),
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
public readonly codeBlockMapObservable = observableValue<Map<string, CodeBlockData>>(this, new Map());
|
|
61
|
+
|
|
62
|
+
private activePreviewer: BaseInlineDiffPreviewer<InlineStreamDiffHandler> | undefined;
|
|
63
|
+
|
|
64
|
+
private pendingApplyParams:
|
|
65
|
+
| {
|
|
66
|
+
relativePath: string;
|
|
67
|
+
newContent: string;
|
|
68
|
+
range?: Range;
|
|
69
|
+
}
|
|
70
|
+
| undefined;
|
|
71
|
+
|
|
72
|
+
@OnEvent(EditorGroupCloseEvent)
|
|
73
|
+
onEditorGroupClose(event: EditorGroupCloseEvent) {
|
|
74
|
+
if (this.activePreviewer?.getNode()?.uri.path.toString() === event.payload.resource.uri.path.toString()) {
|
|
75
|
+
this.activePreviewer.dispose();
|
|
76
|
+
this.activePreviewer = undefined;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Get the code block data by relative or absolute path of the last assistant message
|
|
82
|
+
*/
|
|
83
|
+
getCodeBlock(relativeOrAbsolutePath: string, messageId?: string): CodeBlockData | undefined {
|
|
84
|
+
if (!relativeOrAbsolutePath) {
|
|
85
|
+
return undefined;
|
|
86
|
+
}
|
|
87
|
+
const blockId = this.generateBlockId(relativeOrAbsolutePath, messageId);
|
|
88
|
+
return this.codeBlockMapObservable.get().get(blockId);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
getCodeBlockById(id: string): CodeBlockData | undefined {
|
|
92
|
+
return this.codeBlockMapObservable.get().get(id);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
protected updateCodeBlock(codeBlock: CodeBlockData) {
|
|
96
|
+
const codeBlockMap = new Map(this.codeBlockMapObservable.get());
|
|
97
|
+
codeBlockMap.set(codeBlock.id, codeBlock);
|
|
98
|
+
transaction((tx) => {
|
|
99
|
+
this.codeBlockMapObservable.set(codeBlockMap, tx);
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Register a new code block and return its unique ID
|
|
105
|
+
*/
|
|
106
|
+
registerCodeBlock(relativePath: string, content: string): string {
|
|
107
|
+
const blockId = this.generateBlockId(relativePath);
|
|
108
|
+
|
|
109
|
+
if (!this.codeBlockMapObservable.get().has(blockId)) {
|
|
110
|
+
this.codeBlockMapObservable.get().set(blockId, {
|
|
111
|
+
id: blockId,
|
|
112
|
+
content,
|
|
113
|
+
relativePath,
|
|
114
|
+
status: 'generating',
|
|
115
|
+
iterationCount: 0,
|
|
116
|
+
createdAt: Date.now(),
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return blockId;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
initToolCallId(blockId: string, toolCallId: string): void {
|
|
124
|
+
const blockData = this.getCodeBlockById(blockId);
|
|
125
|
+
if (blockData && !blockData.initToolCallId) {
|
|
126
|
+
blockData.initToolCallId = toolCallId;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Apply changes of a code block
|
|
132
|
+
*/
|
|
133
|
+
async apply(relativePath: string, newContent: string, instructions?: string): Promise<CodeBlockData> {
|
|
134
|
+
const blockData = this.getCodeBlock(relativePath);
|
|
135
|
+
if (!blockData) {
|
|
136
|
+
throw new Error('Code block not found');
|
|
137
|
+
}
|
|
138
|
+
try {
|
|
139
|
+
if (++blockData.iterationCount > 3) {
|
|
140
|
+
throw new Error('Max iteration count exceeded');
|
|
141
|
+
}
|
|
142
|
+
blockData.status = 'generating';
|
|
143
|
+
blockData.content = newContent;
|
|
144
|
+
this.updateCodeBlock(blockData);
|
|
145
|
+
const applyDiffResult = await this.doApply(relativePath, newContent, instructions);
|
|
146
|
+
blockData.applyResult = applyDiffResult;
|
|
147
|
+
this.updateCodeBlock(blockData);
|
|
148
|
+
return blockData;
|
|
149
|
+
} catch (err) {
|
|
150
|
+
blockData.status = 'failed';
|
|
151
|
+
this.updateCodeBlock(blockData);
|
|
152
|
+
throw err;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
async reRenderPendingApply() {
|
|
157
|
+
if (!this.pendingApplyParams) {
|
|
158
|
+
throw new Error('No pending apply params');
|
|
159
|
+
}
|
|
160
|
+
const result = await this.renderApplyResult(
|
|
161
|
+
this.pendingApplyParams.relativePath,
|
|
162
|
+
this.pendingApplyParams.newContent,
|
|
163
|
+
this.pendingApplyParams.range,
|
|
164
|
+
);
|
|
165
|
+
if (result) {
|
|
166
|
+
const blockData = this.getCodeBlock(this.pendingApplyParams.relativePath)!;
|
|
167
|
+
blockData.applyResult = result;
|
|
168
|
+
this.updateCodeBlock(blockData);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
async renderApplyResult(
|
|
173
|
+
relativePath: string,
|
|
174
|
+
newContent: string,
|
|
175
|
+
range?: Range,
|
|
176
|
+
): Promise<{ diff: string; diagnosticInfos: IMarker[] } | undefined> {
|
|
177
|
+
// 用户可能会关闭编辑器,所以需要缓存参数
|
|
178
|
+
this.pendingApplyParams = {
|
|
179
|
+
relativePath,
|
|
180
|
+
newContent,
|
|
181
|
+
range,
|
|
182
|
+
};
|
|
183
|
+
const blockData = this.getCodeBlock(relativePath);
|
|
184
|
+
if (!blockData) {
|
|
185
|
+
throw new Error('Code block not found');
|
|
186
|
+
}
|
|
187
|
+
const openResult = await this.editorService.open(URI.file(path.join(this.appConfig.workspaceDir, relativePath)));
|
|
188
|
+
if (!openResult) {
|
|
189
|
+
throw new Error('Failed to open editor');
|
|
190
|
+
}
|
|
191
|
+
const editor = openResult.group.codeEditor.monacoEditor;
|
|
192
|
+
const inlineDiffController = InlineDiffController.get(editor)!;
|
|
193
|
+
blockData.status = 'pending';
|
|
194
|
+
this.updateCodeBlock(blockData);
|
|
195
|
+
|
|
196
|
+
range = range || editor.getModel()?.getFullModelRange()!;
|
|
197
|
+
// Create diff previewer
|
|
198
|
+
const previewer = inlineDiffController.createDiffPreviewer(
|
|
199
|
+
editor,
|
|
200
|
+
Selection.fromRange(range, SelectionDirection.LTR),
|
|
201
|
+
{
|
|
202
|
+
disposeWhenEditorClosed: true,
|
|
203
|
+
renderRemovedWidgetImmediately: true,
|
|
204
|
+
},
|
|
205
|
+
) as LiveInlineDiffPreviewer;
|
|
206
|
+
this.activePreviewer = previewer;
|
|
207
|
+
|
|
208
|
+
const fullOriginalContent = editor.getModel()!.getValue();
|
|
209
|
+
const savedContent = editor.getModel()!.getValueInRange(range);
|
|
210
|
+
const deferred = new Deferred<{ diff: string; diagnosticInfos: IMarker[] }>();
|
|
211
|
+
if (newContent === savedContent) {
|
|
212
|
+
blockData.status = 'success';
|
|
213
|
+
deferred.resolve();
|
|
214
|
+
} else {
|
|
215
|
+
previewer.setValue(newContent);
|
|
216
|
+
this.addDispose(
|
|
217
|
+
this.inlineDiffService.onPartialEdit((event) => {
|
|
218
|
+
// TODO 支持自动保存
|
|
219
|
+
if (event.totalPartialEditCount === event.resolvedPartialEditCount) {
|
|
220
|
+
if (event.acceptPartialEditCount > 0) {
|
|
221
|
+
blockData.status = 'success';
|
|
222
|
+
const appliedResult = editor.getModel()!.getValue();
|
|
223
|
+
const diffResult = createPatch(relativePath, fullOriginalContent, appliedResult)
|
|
224
|
+
.split('\n')
|
|
225
|
+
.slice(4)
|
|
226
|
+
.join('\n');
|
|
227
|
+
const rangesFromDiffHunk = diffResult
|
|
228
|
+
.split('\n')
|
|
229
|
+
.map((line) => {
|
|
230
|
+
if (line.startsWith('@@')) {
|
|
231
|
+
const [, , , start, end] = line.match(/@@ -(\d+),(\d+) \+(\d+),(\d+) @@/)!;
|
|
232
|
+
return new Range(parseInt(start, 10), 0, parseInt(end, 10), 0);
|
|
233
|
+
}
|
|
234
|
+
return null;
|
|
235
|
+
})
|
|
236
|
+
.filter((range) => range !== null);
|
|
237
|
+
const diagnosticInfos = this.getdiagnosticInfos(editor.getModel()!.uri.toString(), rangesFromDiffHunk);
|
|
238
|
+
// 移除开头的几个固定信息,避免浪费 tokens
|
|
239
|
+
deferred.resolve({
|
|
240
|
+
diff: diffResult,
|
|
241
|
+
diagnosticInfos,
|
|
242
|
+
});
|
|
243
|
+
} else {
|
|
244
|
+
// 用户全部取消
|
|
245
|
+
blockData.status = 'cancelled';
|
|
246
|
+
deferred.resolve();
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}),
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
return deferred.promise;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Cancel an ongoing apply operation
|
|
257
|
+
*/
|
|
258
|
+
cancelApply(relativePath: string): void {
|
|
259
|
+
const blockData = this.getCodeBlock(relativePath);
|
|
260
|
+
if (blockData && (blockData.status === 'generating' || blockData.status === 'pending')) {
|
|
261
|
+
if (this.activePreviewer) {
|
|
262
|
+
this.activePreviewer.getNode()?.livePreviewDiffDecorationModel.discardUnProcessed();
|
|
263
|
+
this.activePreviewer.dispose();
|
|
264
|
+
}
|
|
265
|
+
blockData.status = 'cancelled';
|
|
266
|
+
this.updateCodeBlock(blockData);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
cancelAllApply(): void {
|
|
271
|
+
this.codeBlockMapObservable.get().forEach((blockData) => {
|
|
272
|
+
if (blockData.status === 'generating' || blockData.status === 'pending') {
|
|
273
|
+
this.cancelApply(blockData.relativePath);
|
|
274
|
+
}
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
disposeApplyForMessage(messageId: string): void {
|
|
279
|
+
this.codeBlockMapObservable.get().forEach((blockData) => {
|
|
280
|
+
if (blockData.id.endsWith(':' + messageId)) {
|
|
281
|
+
if (blockData.status === 'generating') {
|
|
282
|
+
this.cancelApply(blockData.relativePath);
|
|
283
|
+
}
|
|
284
|
+
this.codeBlockMapObservable.get().delete(blockData.id);
|
|
285
|
+
}
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
revealApplyPosition(blockId: string): void {
|
|
290
|
+
const blockData = this.codeBlockMapObservable.get().get(blockId);
|
|
291
|
+
if (blockData) {
|
|
292
|
+
const hunkInfo = blockData.applyResult?.diff.split('\n').find((line) => line.startsWith('@@'));
|
|
293
|
+
let startLine = 0;
|
|
294
|
+
let endLine = 0;
|
|
295
|
+
if (hunkInfo) {
|
|
296
|
+
// 取改动后的区间
|
|
297
|
+
const [, , , start, end] = hunkInfo.match(/@@ -(\d+),(\d+) \+(\d+),(\d+) @@/)!;
|
|
298
|
+
startLine = parseInt(start, 10) - 1;
|
|
299
|
+
endLine = parseInt(end, 10) - 1;
|
|
300
|
+
}
|
|
301
|
+
this.editorService.open(URI.file(path.join(this.appConfig.workspaceDir, blockData.relativePath)));
|
|
302
|
+
const editor = this.editorService.currentEditor;
|
|
303
|
+
if (editor) {
|
|
304
|
+
editor.setSelection(new Selection(startLine, 0, endLine, 0));
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
protected abstract doApply(
|
|
310
|
+
relativePath: string,
|
|
311
|
+
newContent: string,
|
|
312
|
+
instructions?: string,
|
|
313
|
+
): Promise<{ diff: string; diagnosticInfos: IMarker[] } | undefined>;
|
|
314
|
+
|
|
315
|
+
protected generateBlockId(absoluteOrRelativePath: string, messageId?: string): string {
|
|
316
|
+
if (!absoluteOrRelativePath.startsWith('/')) {
|
|
317
|
+
absoluteOrRelativePath = path.join(this.appConfig.workspaceDir, absoluteOrRelativePath);
|
|
318
|
+
}
|
|
319
|
+
const sessionId = this.chatInternalService.sessionModel.sessionId;
|
|
320
|
+
const messages = this.chatInternalService.sessionModel.history.getMessages();
|
|
321
|
+
messageId = messageId || messages[messages.length - 1].id;
|
|
322
|
+
return `${sessionId}:${absoluteOrRelativePath}:${messageId || '-'}`;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
protected getdiagnosticInfos(uri: string, ranges: Range[]) {
|
|
326
|
+
const markers = this.markerService.getManager().getMarkers({ resource: uri });
|
|
327
|
+
return markers.filter(
|
|
328
|
+
(marker) =>
|
|
329
|
+
marker.severity >= MarkerSeverity.Warning &&
|
|
330
|
+
ranges.some((range) => range.containsPosition(new Position(marker.startLineNumber, marker.startColumn))),
|
|
331
|
+
);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
export interface CodeBlockData {
|
|
336
|
+
id: string;
|
|
337
|
+
initToolCallId?: string;
|
|
338
|
+
content: string;
|
|
339
|
+
relativePath: string;
|
|
340
|
+
status: CodeBlockStatus;
|
|
341
|
+
iterationCount: number;
|
|
342
|
+
createdAt: number;
|
|
343
|
+
applyResult?: {
|
|
344
|
+
diff: string;
|
|
345
|
+
diagnosticInfos: IMarker[];
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
export type CodeBlockStatus = 'generating' | 'pending' | 'success' | 'rejected' | 'failed' | 'cancelled';
|