@opensumi/ide-ai-native 3.8.1-next-1740571693.0 → 3.8.1-next-1740625266.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 +4 -1
- package/lib/browser/ai-core.contribution.d.ts.map +1 -1
- package/lib/browser/ai-core.contribution.js +20 -1
- package/lib/browser/ai-core.contribution.js.map +1 -1
- package/lib/browser/chat/chat-manager.service.d.ts +1 -0
- package/lib/browser/chat/chat-manager.service.d.ts.map +1 -1
- package/lib/browser/chat/chat-manager.service.js +13 -0
- package/lib/browser/chat/chat-manager.service.js.map +1 -1
- package/lib/browser/chat/chat.internal.service.d.ts +1 -0
- package/lib/browser/chat/chat.internal.service.d.ts.map +1 -1
- package/lib/browser/chat/chat.internal.service.js +3 -0
- package/lib/browser/chat/chat.internal.service.js.map +1 -1
- package/lib/browser/components/ChatHistory.d.ts +0 -1
- package/lib/browser/components/ChatHistory.d.ts.map +1 -1
- package/lib/browser/components/ChatHistory.js +14 -14
- package/lib/browser/components/ChatHistory.js.map +1 -1
- package/lib/browser/mcp/base-apply.service.d.ts +31 -40
- package/lib/browser/mcp/base-apply.service.d.ts.map +1 -1
- package/lib/browser/mcp/base-apply.service.js +233 -167
- package/lib/browser/mcp/base-apply.service.js.map +1 -1
- package/lib/browser/mcp/tools/components/EditFile.d.ts.map +1 -1
- package/lib/browser/mcp/tools/components/EditFile.js +55 -41
- package/lib/browser/mcp/tools/components/EditFile.js.map +1 -1
- package/lib/browser/mcp/tools/components/index.module.less +22 -3
- package/lib/browser/mcp/tools/editFile.js +1 -1
- package/lib/browser/mcp/tools/editFile.js.map +1 -1
- package/lib/browser/mcp/tools/handlers/EditFile.d.ts +5 -1
- package/lib/browser/mcp/tools/handlers/EditFile.d.ts.map +1 -1
- package/lib/browser/mcp/tools/handlers/EditFile.js +4 -4
- package/lib/browser/mcp/tools/handlers/EditFile.js.map +1 -1
- package/lib/browser/model/msg-history-manager.d.ts +1 -0
- package/lib/browser/model/msg-history-manager.d.ts.map +1 -1
- package/lib/browser/model/msg-history-manager.js +12 -2
- package/lib/browser/model/msg-history-manager.js.map +1 -1
- package/lib/browser/types.d.ts +1 -1
- package/lib/browser/types.d.ts.map +1 -1
- package/lib/browser/widget/inline-diff/inline-diff-manager.d.ts +6 -0
- package/lib/browser/widget/inline-diff/inline-diff-manager.d.ts.map +1 -0
- package/lib/browser/widget/inline-diff/inline-diff-manager.js +27 -0
- package/lib/browser/widget/inline-diff/inline-diff-manager.js.map +1 -0
- package/lib/browser/widget/inline-diff/inline-diff-widget.module.less +12 -0
- package/lib/browser/widget/inline-stream-diff/inline-stream-diff.handler.d.ts +2 -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 +11 -4
- package/lib/browser/widget/inline-stream-diff/inline-stream-diff.handler.js.map +1 -1
- package/lib/common/types.d.ts +17 -0
- package/lib/common/types.d.ts.map +1 -1
- package/lib/common/types.js.map +1 -1
- package/lib/node/base-language-model.d.ts +1 -1
- package/lib/node/base-language-model.d.ts.map +1 -1
- package/lib/node/base-language-model.js +54 -3
- package/lib/node/base-language-model.js.map +1 -1
- package/package.json +23 -23
- package/src/browser/ai-core.contribution.ts +25 -1
- package/src/browser/chat/chat-manager.service.ts +12 -0
- package/src/browser/chat/chat.internal.service.ts +4 -0
- package/src/browser/components/ChatHistory.tsx +21 -15
- package/src/browser/mcp/base-apply.service.ts +266 -213
- package/src/browser/mcp/tools/components/EditFile.tsx +82 -60
- package/src/browser/mcp/tools/components/index.module.less +22 -3
- package/src/browser/mcp/tools/editFile.ts +2 -2
- package/src/browser/mcp/tools/handlers/EditFile.ts +4 -4
- package/src/browser/model/msg-history-manager.ts +12 -2
- package/src/browser/types.ts +1 -1
- package/src/browser/widget/inline-diff/inline-diff-manager.tsx +38 -0
- package/src/browser/widget/inline-diff/inline-diff-widget.module.less +12 -0
- package/src/browser/widget/inline-stream-diff/inline-stream-diff.handler.tsx +13 -4
- package/src/common/types.ts +20 -0
- package/src/node/base-language-model.ts +63 -1
- /package/lib/browser/components/{chat-history.css → chat-history.module.less} +0 -0
- /package/src/browser/components/{chat-history.css → chat-history.module.less} +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import cls from 'classnames';
|
|
2
|
-
import React, { useEffect, useMemo } from 'react';
|
|
2
|
+
import React, { useEffect, useMemo, useState } from 'react';
|
|
3
3
|
|
|
4
4
|
import { Icon, Popover } from '@opensumi/ide-components';
|
|
5
5
|
import {
|
|
@@ -10,7 +10,6 @@ import {
|
|
|
10
10
|
Uri,
|
|
11
11
|
detectModeId,
|
|
12
12
|
path,
|
|
13
|
-
useAutorun,
|
|
14
13
|
useInjectable,
|
|
15
14
|
} from '@opensumi/ide-core-browser';
|
|
16
15
|
import { Loading } from '@opensumi/ide-core-browser/lib/components/ai-native';
|
|
@@ -18,61 +17,24 @@ import { ILanguageService } from '@opensumi/monaco-editor-core/esm/vs/editor/com
|
|
|
18
17
|
import { IModelService } from '@opensumi/monaco-editor-core/esm/vs/editor/common/services/model';
|
|
19
18
|
import { StandaloneServices } from '@opensumi/monaco-editor-core/esm/vs/editor/standalone/browser/standaloneServices';
|
|
20
19
|
|
|
20
|
+
import { CodeBlockData } from '../../../../common/types';
|
|
21
21
|
import { ChatMarkdown } from '../../../components/ChatMarkdown';
|
|
22
22
|
import { IMCPServerToolComponentProps } from '../../../types';
|
|
23
|
-
import { BaseApplyService
|
|
23
|
+
import { BaseApplyService } from '../../base-apply.service';
|
|
24
24
|
|
|
25
25
|
import styles from './index.module.less';
|
|
26
26
|
|
|
27
|
-
const renderStatus = (codeBlockData: CodeBlockData) => {
|
|
28
|
-
const status = codeBlockData.status;
|
|
29
|
-
switch (status) {
|
|
30
|
-
case 'generating':
|
|
31
|
-
return <Loading />;
|
|
32
|
-
case 'pending':
|
|
33
|
-
return (
|
|
34
|
-
<Popover title={status} id={'edit-file-tool-status-pending'}>
|
|
35
|
-
<Icon iconClass='codicon codicon-circle-large' />
|
|
36
|
-
</Popover>
|
|
37
|
-
);
|
|
38
|
-
case 'success':
|
|
39
|
-
return (
|
|
40
|
-
<Popover title={status} id={'edit-file-tool-status-success'}>
|
|
41
|
-
<Icon iconClass='codicon codicon-check-all' />
|
|
42
|
-
</Popover>
|
|
43
|
-
);
|
|
44
|
-
case 'failed':
|
|
45
|
-
return (
|
|
46
|
-
<Popover title={status} id={'edit-file-tool-status-failed'}>
|
|
47
|
-
<Icon iconClass='codicon codicon-error' color='var(--vscode-input-errorForeground)' />
|
|
48
|
-
</Popover>
|
|
49
|
-
);
|
|
50
|
-
case 'cancelled':
|
|
51
|
-
return (
|
|
52
|
-
<Popover title={status} id={'edit-file-tool-status-cancelled'}>
|
|
53
|
-
<Icon iconClass='codicon codicon-close' color='var(--vscode-input-placeholderForeground)' />
|
|
54
|
-
</Popover>
|
|
55
|
-
);
|
|
56
|
-
default:
|
|
57
|
-
return null;
|
|
58
|
-
}
|
|
59
|
-
};
|
|
60
|
-
|
|
61
27
|
export const EditFileToolComponent = (props: IMCPServerToolComponentProps) => {
|
|
62
28
|
const { args, messageId, toolCallId } = props;
|
|
29
|
+
const [mode, setMode] = useState<'code' | 'diff'>('code');
|
|
63
30
|
const labelService = useInjectable(LabelService);
|
|
64
31
|
const appConfig = useInjectable<AppConfig>(AppConfig);
|
|
65
32
|
const applyService = useInjectable<BaseApplyService>(BaseApplyService);
|
|
66
33
|
const { target_file = '', code_edit, instructions } = args || {};
|
|
67
34
|
const absolutePath = path.join(appConfig.workspaceDir, target_file);
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
useAutorun(applyService.codeBlockMapObservable);
|
|
72
|
-
|
|
73
|
-
if (toolCallId && codeBlockData) {
|
|
74
|
-
applyService.initToolCallId(codeBlockData.id, toolCallId);
|
|
75
|
-
}
|
|
35
|
+
const [codeBlockData, setCodeBlockData] = useState<CodeBlockData | undefined>(
|
|
36
|
+
applyService.getCodeBlock(toolCallId, messageId),
|
|
37
|
+
);
|
|
76
38
|
|
|
77
39
|
const icon = useMemo(() => {
|
|
78
40
|
if (!target_file) {
|
|
@@ -91,40 +53,66 @@ export const EditFileToolComponent = (props: IMCPServerToolComponentProps) => {
|
|
|
91
53
|
return detectedModeId;
|
|
92
54
|
}, [target_file, absolutePath]);
|
|
93
55
|
|
|
56
|
+
useEffect(() => {
|
|
57
|
+
const disposable = applyService.onCodeBlockUpdate((codeBlockData) => {
|
|
58
|
+
setCodeBlockData({ ...codeBlockData });
|
|
59
|
+
});
|
|
60
|
+
return () => {
|
|
61
|
+
disposable.dispose();
|
|
62
|
+
};
|
|
63
|
+
}, []);
|
|
64
|
+
|
|
94
65
|
// 多次迭代时,仅在首处tool组件中展示
|
|
95
|
-
|
|
66
|
+
// FIXME: 这个优化有必要吗?每次都展示也挺好?
|
|
67
|
+
if (!args || !codeBlockData) {
|
|
96
68
|
return null;
|
|
97
69
|
}
|
|
98
70
|
|
|
99
71
|
return [
|
|
100
72
|
instructions && <p>{instructions}</p>,
|
|
101
|
-
<div className={styles['edit-file-tool']} key={
|
|
73
|
+
<div className={styles['edit-file-tool']} key={'edit-file-tool'}>
|
|
102
74
|
<div
|
|
103
75
|
className={cls(styles['edit-file-tool-header'], {
|
|
104
76
|
clickable: codeBlockData.status === 'pending' || codeBlockData.status === 'success',
|
|
105
77
|
})}
|
|
106
78
|
onClick={() => {
|
|
107
79
|
if (codeBlockData.status === 'pending') {
|
|
108
|
-
applyService.
|
|
80
|
+
applyService.renderApplyResult(codeBlockData, codeBlockData.updatedCode!);
|
|
109
81
|
} else if (codeBlockData.status === 'success') {
|
|
110
|
-
applyService.revealApplyPosition(codeBlockData
|
|
82
|
+
applyService.revealApplyPosition(codeBlockData);
|
|
111
83
|
}
|
|
112
84
|
}}
|
|
113
85
|
>
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
86
|
+
<div className={styles.left}>
|
|
87
|
+
{icon && <span className={icon}></span>}
|
|
88
|
+
<span className={styles['edit-file-tool-file-name']}>{target_file}</span>
|
|
89
|
+
{codeBlockData.iterationCount > 1 && (
|
|
90
|
+
<span className={styles['edit-file-tool-iteration-count']}>{codeBlockData.iterationCount}/3</span>
|
|
91
|
+
)}
|
|
92
|
+
{renderStatus(codeBlockData)}
|
|
93
|
+
</div>
|
|
94
|
+
<div className={styles.right}>
|
|
95
|
+
<Popover title={'Show Code'} id={'edit-file-tool-show-code'}>
|
|
96
|
+
<Icon iconClass='codicon codicon-file-code' onClick={() => setMode('code')} />
|
|
97
|
+
</Popover>
|
|
98
|
+
{codeBlockData.applyResult?.diff && (
|
|
99
|
+
<Popover title={'Show Diff'} id={'edit-file-tool-show-diff'}>
|
|
100
|
+
<Icon iconClass='codicon codicon-diff-multiple' onClick={() => setMode('diff')} />
|
|
101
|
+
</Popover>
|
|
102
|
+
)}
|
|
103
|
+
</div>
|
|
120
104
|
</div>
|
|
121
|
-
<ChatMarkdown
|
|
105
|
+
<ChatMarkdown
|
|
106
|
+
markdown={
|
|
107
|
+
mode === 'code'
|
|
108
|
+
? `\`\`\`${languageId || ''}\n${code_edit}\n\`\`\``
|
|
109
|
+
: `\`\`\`diff\n${codeBlockData.applyResult?.diff}\n\`\`\``
|
|
110
|
+
}
|
|
111
|
+
hideInsert={true}
|
|
112
|
+
/>
|
|
122
113
|
</div>,
|
|
123
114
|
codeBlockData.applyResult && codeBlockData.applyResult.diagnosticInfos.length > 0 && (
|
|
124
|
-
<div
|
|
125
|
-
className={styles['edit-file-tool-diagnostic-errors']}
|
|
126
|
-
key={`edit-file-tool-diagnostic-errors-${codeBlockData.id}`}
|
|
127
|
-
>
|
|
115
|
+
<div className={styles['edit-file-tool-diagnostic-errors']} key={'edit-file-tool-diagnostic-errors'}>
|
|
128
116
|
<div className={styles['title']}>Found Lints:</div>
|
|
129
117
|
{codeBlockData.applyResult?.diagnosticInfos.map((info) => (
|
|
130
118
|
<div
|
|
@@ -142,3 +130,37 @@ export const EditFileToolComponent = (props: IMCPServerToolComponentProps) => {
|
|
|
142
130
|
),
|
|
143
131
|
];
|
|
144
132
|
};
|
|
133
|
+
|
|
134
|
+
const renderStatus = (codeBlockData: CodeBlockData) => {
|
|
135
|
+
const status = codeBlockData.status;
|
|
136
|
+
switch (status) {
|
|
137
|
+
case 'generating':
|
|
138
|
+
return <Loading />;
|
|
139
|
+
case 'pending':
|
|
140
|
+
return (
|
|
141
|
+
<Popover title='Pending' id={'edit-file-tool-status-pending'}>
|
|
142
|
+
<Icon iconClass='codicon codicon-circle-large' />
|
|
143
|
+
</Popover>
|
|
144
|
+
);
|
|
145
|
+
case 'success':
|
|
146
|
+
return (
|
|
147
|
+
<Popover title='Success' id={'edit-file-tool-status-success'}>
|
|
148
|
+
<Icon iconClass='codicon codicon-check-all' />
|
|
149
|
+
</Popover>
|
|
150
|
+
);
|
|
151
|
+
case 'failed':
|
|
152
|
+
return (
|
|
153
|
+
<Popover title='Failed' id={'edit-file-tool-status-failed'}>
|
|
154
|
+
<Icon iconClass='codicon codicon-error' style={{ color: 'var(--debugConsole-errorForeground)' }} />
|
|
155
|
+
</Popover>
|
|
156
|
+
);
|
|
157
|
+
case 'cancelled':
|
|
158
|
+
return (
|
|
159
|
+
<Popover title='Cancelled' id={'edit-file-tool-status-cancelled'}>
|
|
160
|
+
<Icon iconClass='codicon codicon-close' style={{ color: 'var(--input-placeholderForeground)' }} />
|
|
161
|
+
</Popover>
|
|
162
|
+
);
|
|
163
|
+
default:
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
166
|
+
};
|
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
.edit-file-tool-header {
|
|
2
2
|
display: flex;
|
|
3
3
|
align-items: center;
|
|
4
|
+
justify-content: space-between;
|
|
4
5
|
padding: 2px 8px;
|
|
5
6
|
border-bottom: 1px solid var(--vscode-commandCenter-inactiveBorder);
|
|
6
7
|
background-color: var(--design-block-background);
|
|
7
8
|
font-size: 10px;
|
|
8
9
|
margin-bottom: -4px;
|
|
9
10
|
border-radius: 8px 8px 0 0;
|
|
10
|
-
|
|
11
|
-
margin-right: 4px;
|
|
12
|
-
}
|
|
11
|
+
white-space: nowrap;
|
|
13
12
|
:global(span.codicon) {
|
|
14
13
|
font-size: 12px;
|
|
15
14
|
}
|
|
@@ -18,6 +17,26 @@
|
|
|
18
17
|
align-items: center;
|
|
19
18
|
justify-content: center;
|
|
20
19
|
}
|
|
20
|
+
.left,
|
|
21
|
+
.right {
|
|
22
|
+
display: flex;
|
|
23
|
+
align-items: center;
|
|
24
|
+
}
|
|
25
|
+
&::after {
|
|
26
|
+
display: none;
|
|
27
|
+
}
|
|
28
|
+
.left {
|
|
29
|
+
> span {
|
|
30
|
+
margin-right: 4px;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
.right > div {
|
|
34
|
+
margin-left: 4px;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
.edit-file-tool-file-name {
|
|
38
|
+
text-overflow: ellipsis;
|
|
39
|
+
overflow: hidden;
|
|
21
40
|
}
|
|
22
41
|
.edit-file-tool {
|
|
23
42
|
border: 1px solid var(--vscode-commandCenter-inactiveBorder);
|
|
@@ -69,8 +69,8 @@ You should specify the following arguments before the others: [target_file]`,
|
|
|
69
69
|
};
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
-
private async handler(args: z.infer<typeof inputSchema
|
|
73
|
-
const result = await this.editFileHandler.handler(args
|
|
72
|
+
private async handler(args: z.infer<typeof inputSchema> & { toolCallId: string }, logger: MCPLogger) {
|
|
73
|
+
const result = await this.editFileHandler.handler(args, args.toolCallId);
|
|
74
74
|
return {
|
|
75
75
|
content: [
|
|
76
76
|
{
|
|
@@ -12,10 +12,10 @@ export class EditFileHandler {
|
|
|
12
12
|
@Autowired(BaseApplyService)
|
|
13
13
|
private applyService: BaseApplyService;
|
|
14
14
|
|
|
15
|
-
async handler(
|
|
16
|
-
|
|
17
|
-
this.applyService.registerCodeBlock(
|
|
18
|
-
const blockData = await this.applyService.apply(
|
|
15
|
+
async handler(params: { targetFile: string; codeEdit: string; instructions?: string }, toolCallId: string) {
|
|
16
|
+
const { targetFile, codeEdit } = params;
|
|
17
|
+
const block = this.applyService.registerCodeBlock(targetFile, codeEdit, toolCallId);
|
|
18
|
+
const blockData = await this.applyService.apply(block);
|
|
19
19
|
return blockData;
|
|
20
20
|
}
|
|
21
21
|
}
|
|
@@ -66,6 +66,11 @@ export class MsgHistoryManager extends Disposable {
|
|
|
66
66
|
return this.startIndex;
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
+
public get lastMessageId(): string | undefined {
|
|
70
|
+
const list = this.messageList;
|
|
71
|
+
return list[list.length - 1]?.id;
|
|
72
|
+
}
|
|
73
|
+
|
|
69
74
|
public getMessages(maxTokens?: number): IHistoryChatMessage[] {
|
|
70
75
|
if (maxTokens && this.totalTokens > maxTokens) {
|
|
71
76
|
while (this.totalTokens > maxTokens) {
|
|
@@ -109,9 +114,14 @@ export class MsgHistoryManager extends Disposable {
|
|
|
109
114
|
return;
|
|
110
115
|
}
|
|
111
116
|
|
|
112
|
-
this.messageAdditionalMap.
|
|
117
|
+
const oldAdditional = this.messageAdditionalMap.get(id) || {};
|
|
118
|
+
const newAdditional = {
|
|
119
|
+
...oldAdditional,
|
|
120
|
+
...additional,
|
|
121
|
+
};
|
|
113
122
|
|
|
114
|
-
this.
|
|
123
|
+
this.messageAdditionalMap.set(id, newAdditional);
|
|
124
|
+
this._onMessageAdditionalChange.fire(newAdditional);
|
|
115
125
|
}
|
|
116
126
|
|
|
117
127
|
public getMessageAdditional(id: string): Record<string, any> {
|
package/src/browser/types.ts
CHANGED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import React, { useEffect, useState } from 'react';
|
|
2
|
+
|
|
3
|
+
import { Button } from '@opensumi/ide-components';
|
|
4
|
+
import { localize, useInjectable } from '@opensumi/ide-core-browser';
|
|
5
|
+
import { IResource } from '@opensumi/ide-editor';
|
|
6
|
+
|
|
7
|
+
import { BaseApplyService } from '../../mcp/base-apply.service';
|
|
8
|
+
|
|
9
|
+
import styles from './inline-diff-widget.module.less';
|
|
10
|
+
|
|
11
|
+
export const InlineDiffManager: React.FC<{ resource: IResource }> = (props) => {
|
|
12
|
+
const applyService = useInjectable<BaseApplyService>(BaseApplyService);
|
|
13
|
+
const [show, setShow] = useState(true);
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
applyService.onCodeBlockUpdate((codeBlock) => {
|
|
16
|
+
setShow(codeBlock.status === 'pending');
|
|
17
|
+
});
|
|
18
|
+
}, []);
|
|
19
|
+
return (
|
|
20
|
+
<div className={styles.inlineDiffManager} style={{ display: show ? 'flex' : 'none' }}>
|
|
21
|
+
<Button
|
|
22
|
+
onClick={() => {
|
|
23
|
+
applyService.processAll(props.resource.uri, 'accept');
|
|
24
|
+
}}
|
|
25
|
+
>
|
|
26
|
+
{localize('aiNative.inlineDiff.acceptAll')}
|
|
27
|
+
</Button>
|
|
28
|
+
<Button
|
|
29
|
+
type='ghost'
|
|
30
|
+
onClick={() => {
|
|
31
|
+
applyService.processAll(props.resource.uri, 'reject');
|
|
32
|
+
}}
|
|
33
|
+
>
|
|
34
|
+
{localize('aiNative.inlineDiff.rejectAll')}
|
|
35
|
+
</Button>
|
|
36
|
+
</div>
|
|
37
|
+
);
|
|
38
|
+
};
|
|
@@ -19,3 +19,15 @@
|
|
|
19
19
|
display: flex;
|
|
20
20
|
position: relative;
|
|
21
21
|
}
|
|
22
|
+
|
|
23
|
+
.inlineDiffManager {
|
|
24
|
+
display: flex;
|
|
25
|
+
padding: 12px 16px;
|
|
26
|
+
justify-content: center;
|
|
27
|
+
position: absolute;
|
|
28
|
+
bottom: 0;
|
|
29
|
+
left: 50%;
|
|
30
|
+
transform: translateX(-50%);
|
|
31
|
+
gap: 12px;
|
|
32
|
+
z-index: 999;
|
|
33
|
+
}
|
|
@@ -52,6 +52,9 @@ export class InlineStreamDiffHandler extends Disposable implements IInlineDiffPr
|
|
|
52
52
|
protected readonly _onDidEditChange = this.registerDispose(new Emitter<void>());
|
|
53
53
|
public readonly onDidEditChange: Event<void> = this._onDidEditChange.event;
|
|
54
54
|
|
|
55
|
+
protected readonly onDiffFinishedEmitter = this.registerDispose(new Emitter<IComputeDiffData>());
|
|
56
|
+
public readonly onDiffFinished: Event<IComputeDiffData> = this.onDiffFinishedEmitter.event;
|
|
57
|
+
|
|
55
58
|
public previewerOptions: IDiffPreviewerOptions;
|
|
56
59
|
|
|
57
60
|
private originalModel: ITextModel;
|
|
@@ -445,6 +448,8 @@ export class InlineStreamDiffHandler extends Disposable implements IInlineDiffPr
|
|
|
445
448
|
this.diffModel.set(currentDiffModel, tx);
|
|
446
449
|
});
|
|
447
450
|
|
|
451
|
+
this.onDiffFinishedEmitter.fire(currentDiffModel);
|
|
452
|
+
|
|
448
453
|
if (this.originalModel.id === this.monacoEditor.getModel()?.id) {
|
|
449
454
|
this.renderDiffEdits(currentDiffModel);
|
|
450
455
|
}
|
|
@@ -471,10 +476,13 @@ export class InlineStreamDiffHandler extends Disposable implements IInlineDiffPr
|
|
|
471
476
|
}
|
|
472
477
|
|
|
473
478
|
public pushRateFinallyDiffStack(diffModel: IComputeDiffData): void {
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
479
|
+
transaction((tx) => {
|
|
480
|
+
this.finallyDiffModel.set(diffModel, tx);
|
|
481
|
+
// 可能存在 rate editor controller 处理完之后接口层流式才结束
|
|
482
|
+
if (this.isEditing === false) {
|
|
483
|
+
this.finallyRender(diffModel);
|
|
484
|
+
}
|
|
485
|
+
});
|
|
478
486
|
}
|
|
479
487
|
|
|
480
488
|
public finallyRender(diffModel: IComputeDiffData): void {
|
|
@@ -487,6 +495,7 @@ export class InlineStreamDiffHandler extends Disposable implements IInlineDiffPr
|
|
|
487
495
|
return;
|
|
488
496
|
}
|
|
489
497
|
|
|
498
|
+
this.onDiffFinishedEmitter.fire(diffModel);
|
|
490
499
|
this.renderPartialEditWidgets(diffModel);
|
|
491
500
|
this.renderDiffEdits(diffModel);
|
|
492
501
|
this.pushStackElement();
|
package/src/common/types.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { IMarker } from '@opensumi/ide-core-browser';
|
|
2
|
+
|
|
1
3
|
export enum NearestCodeBlockType {
|
|
2
4
|
Block = 'block',
|
|
3
5
|
Line = 'line',
|
|
@@ -46,3 +48,21 @@ export interface MCPTool {
|
|
|
46
48
|
inputSchema: any;
|
|
47
49
|
providerName: string;
|
|
48
50
|
}
|
|
51
|
+
|
|
52
|
+
export interface CodeBlockData {
|
|
53
|
+
toolCallId: string;
|
|
54
|
+
codeEdit: string;
|
|
55
|
+
updatedCode?: string;
|
|
56
|
+
relativePath: string;
|
|
57
|
+
status: CodeBlockStatus;
|
|
58
|
+
iterationCount: number;
|
|
59
|
+
createdAt: number;
|
|
60
|
+
version: number;
|
|
61
|
+
instructions?: string;
|
|
62
|
+
applyResult?: {
|
|
63
|
+
diff: string;
|
|
64
|
+
diagnosticInfos: IMarker[];
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export type CodeBlockStatus = 'generating' | 'pending' | 'success' | 'rejected' | 'failed' | 'cancelled';
|
|
@@ -60,6 +60,7 @@ export abstract class BaseLanguageModel {
|
|
|
60
60
|
options.topP,
|
|
61
61
|
options.topK,
|
|
62
62
|
options.providerOptions,
|
|
63
|
+
options.trimTexts,
|
|
63
64
|
cancellationToken,
|
|
64
65
|
);
|
|
65
66
|
}
|
|
@@ -87,6 +88,7 @@ export abstract class BaseLanguageModel {
|
|
|
87
88
|
topP?: number,
|
|
88
89
|
topK?: number,
|
|
89
90
|
providerOptions?: Record<string, any>,
|
|
91
|
+
trimTexts?: [string, string],
|
|
90
92
|
cancellationToken?: CancellationToken,
|
|
91
93
|
): Promise<any> {
|
|
92
94
|
try {
|
|
@@ -120,9 +122,42 @@ export abstract class BaseLanguageModel {
|
|
|
120
122
|
providerOptions,
|
|
121
123
|
});
|
|
122
124
|
|
|
125
|
+
// 状态跟踪变量
|
|
126
|
+
let isFirstChunk = true;
|
|
127
|
+
let bufferedText = '';
|
|
128
|
+
const pendingLines: string[] = [];
|
|
123
129
|
for await (const chunk of stream.fullStream) {
|
|
124
130
|
if (chunk.type === 'text-delta') {
|
|
125
|
-
|
|
131
|
+
if (trimTexts?.length) {
|
|
132
|
+
// 将收到的文本追加到缓冲区
|
|
133
|
+
bufferedText += chunk.textDelta;
|
|
134
|
+
|
|
135
|
+
// 处理第一个文本块的前缀(只处理一次)
|
|
136
|
+
if (isFirstChunk && bufferedText.includes(trimTexts[0])) {
|
|
137
|
+
bufferedText = bufferedText.substring(bufferedText.indexOf(trimTexts[0]) + trimTexts[0].length);
|
|
138
|
+
isFirstChunk = false;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// 检查是否有完整的行,并将它们添加到待发送行队列
|
|
142
|
+
const lines = bufferedText.split('\n');
|
|
143
|
+
|
|
144
|
+
// 最后一个元素可能是不完整的行,保留在缓冲区
|
|
145
|
+
bufferedText = lines.pop() || '';
|
|
146
|
+
|
|
147
|
+
// 将完整的行添加到待发送队列
|
|
148
|
+
if (lines.length > 0) {
|
|
149
|
+
pendingLines.push(...lines);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// 发送除最后几行外的所有行(保留足够的行以处理后缀)
|
|
153
|
+
while (pendingLines.length > 3) {
|
|
154
|
+
// 保留最后3行以确保能完整识别后缀
|
|
155
|
+
const lineToSend = pendingLines.shift() + '\n';
|
|
156
|
+
chatReadableStream.emitData({ kind: 'content', content: lineToSend });
|
|
157
|
+
}
|
|
158
|
+
} else {
|
|
159
|
+
chatReadableStream.emitData({ kind: 'content', content: chunk.textDelta });
|
|
160
|
+
}
|
|
126
161
|
} else if (chunk.type === 'tool-call') {
|
|
127
162
|
chatReadableStream.emitData({
|
|
128
163
|
kind: 'toolCall',
|
|
@@ -169,6 +204,33 @@ export abstract class BaseLanguageModel {
|
|
|
169
204
|
}
|
|
170
205
|
}
|
|
171
206
|
|
|
207
|
+
if (trimTexts?.[1]) {
|
|
208
|
+
// 完成处理所有块后,检查并发送剩余文本
|
|
209
|
+
|
|
210
|
+
// 将剩余缓冲区加入待发送行
|
|
211
|
+
if (bufferedText) {
|
|
212
|
+
pendingLines.push(bufferedText);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// 处理最后一行可能存在的后缀
|
|
216
|
+
if (pendingLines.length > 0) {
|
|
217
|
+
let lastLine = pendingLines[pendingLines.length - 1];
|
|
218
|
+
|
|
219
|
+
if (lastLine.endsWith(trimTexts[1])) {
|
|
220
|
+
// 移除后缀
|
|
221
|
+
lastLine = lastLine.substring(0, lastLine.length - trimTexts[1].length);
|
|
222
|
+
pendingLines[pendingLines.length - 1] = lastLine;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// 发送所有剩余的行
|
|
227
|
+
for (let i = 0; i < pendingLines.length; i++) {
|
|
228
|
+
const isLastLine = i === pendingLines.length - 1;
|
|
229
|
+
const lineToSend = pendingLines[i] + (isLastLine ? '' : '\n');
|
|
230
|
+
chatReadableStream.emitData({ kind: 'content', content: lineToSend });
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
172
234
|
chatReadableStream.end();
|
|
173
235
|
} catch (error) {
|
|
174
236
|
// Use a logger service in production instead of console
|
|
File without changes
|
|
File without changes
|