@opensumi/ide-ai-native 3.8.3-next-1741747748.0 → 3.8.3-next-1741763229.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/browser/chat/chat-agent.service.d.ts +1 -1
- package/lib/browser/chat/chat-agent.service.d.ts.map +1 -1
- package/lib/browser/chat/chat-agent.service.js +4 -4
- package/lib/browser/chat/chat-agent.service.js.map +1 -1
- package/lib/browser/chat/chat.view.d.ts.map +1 -1
- package/lib/browser/chat/chat.view.js +5 -4
- package/lib/browser/chat/chat.view.js.map +1 -1
- package/lib/browser/components/ChatEditor.d.ts +5 -2
- package/lib/browser/components/ChatEditor.d.ts.map +1 -1
- package/lib/browser/components/ChatEditor.js +45 -6
- package/lib/browser/components/ChatEditor.js.map +1 -1
- package/lib/browser/components/ChatMentionInput.d.ts.map +1 -1
- package/lib/browser/components/ChatMentionInput.js +1 -1
- package/lib/browser/components/ChatMentionInput.js.map +1 -1
- package/lib/browser/components/components.module.less +18 -0
- package/lib/browser/components/mention-input/mention-input.d.ts.map +1 -1
- package/lib/browser/components/mention-input/mention-input.js +22 -3
- package/lib/browser/components/mention-input/mention-input.js.map +1 -1
- package/lib/browser/components/mention-input/mention-input.module.less +19 -1
- package/lib/browser/components/mention-input/types.d.ts +2 -0
- package/lib/browser/components/mention-input/types.d.ts.map +1 -1
- package/lib/browser/components/mention-input/types.js.map +1 -1
- package/lib/browser/context/llm-context.service.d.ts.map +1 -1
- package/lib/browser/context/llm-context.service.js +1 -1
- package/lib/browser/context/llm-context.service.js.map +1 -1
- package/lib/common/prompts/context-prompt-provider.d.ts +12 -2
- package/lib/common/prompts/context-prompt-provider.d.ts.map +1 -1
- package/lib/common/prompts/context-prompt-provider.js +90 -29
- package/lib/common/prompts/context-prompt-provider.js.map +1 -1
- package/package.json +23 -23
- package/src/browser/chat/chat-agent.service.ts +4 -4
- package/src/browser/chat/chat.view.tsx +19 -4
- package/src/browser/components/ChatEditor.tsx +72 -9
- package/src/browser/components/ChatMentionInput.tsx +1 -0
- package/src/browser/components/components.module.less +18 -0
- package/src/browser/components/mention-input/mention-input.module.less +19 -1
- package/src/browser/components/mention-input/mention-input.tsx +32 -2
- package/src/browser/components/mention-input/types.ts +3 -0
- package/src/browser/context/llm-context.service.ts +1 -3
- package/src/common/prompts/context-prompt-provider.ts +122 -35
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { MessageList } from 'react-chat-elements';
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
AINativeConfigService,
|
|
6
|
+
AppConfig,
|
|
7
|
+
LabelService,
|
|
8
|
+
getIcon,
|
|
9
|
+
useInjectable,
|
|
10
|
+
useUpdateOnEvent,
|
|
11
|
+
} from '@opensumi/ide-core-browser';
|
|
5
12
|
import { Popover, PopoverPosition } from '@opensumi/ide-core-browser/lib/components';
|
|
6
13
|
import { EnhanceIcon } from '@opensumi/ide-core-browser/lib/components/ai-native';
|
|
7
14
|
import {
|
|
@@ -117,6 +124,7 @@ export const AIChatView = () => {
|
|
|
117
124
|
const editorService = useInjectable<WorkbenchEditorService>(WorkbenchEditorService);
|
|
118
125
|
const appConfig = useInjectable<AppConfig>(AppConfig);
|
|
119
126
|
const applyService = useInjectable<BaseApplyService>(BaseApplyService);
|
|
127
|
+
const labelService = useInjectable<LabelService>(LabelService);
|
|
120
128
|
const [shortcutCommands, setShortcutCommands] = React.useState<ChatSlashCommandItemModel[]>([]);
|
|
121
129
|
|
|
122
130
|
const [changeList, setChangeList] = React.useState<FileChange[]>(getFileChanges(applyService.getSessionCodeBlocks()));
|
|
@@ -355,6 +363,7 @@ export const AIChatView = () => {
|
|
|
355
363
|
text={message}
|
|
356
364
|
agentId={visibleAgentId}
|
|
357
365
|
command={command}
|
|
366
|
+
labelService={labelService}
|
|
358
367
|
/>
|
|
359
368
|
),
|
|
360
369
|
},
|
|
@@ -460,7 +469,13 @@ export const AIChatView = () => {
|
|
|
460
469
|
text: ChatUserRoleRender ? (
|
|
461
470
|
<ChatUserRoleRender content={message} agentId={visibleAgentId} command={command} />
|
|
462
471
|
) : (
|
|
463
|
-
<CodeBlockWrapperInput
|
|
472
|
+
<CodeBlockWrapperInput
|
|
473
|
+
labelService={labelService}
|
|
474
|
+
relationId={relationId}
|
|
475
|
+
text={message}
|
|
476
|
+
agentId={visibleAgentId}
|
|
477
|
+
command={command}
|
|
478
|
+
/>
|
|
464
479
|
),
|
|
465
480
|
},
|
|
466
481
|
styles.chat_message_code,
|
|
@@ -666,7 +681,7 @@ export const AIChatView = () => {
|
|
|
666
681
|
llmContextService.addFileToContext(fileUri, undefined, true);
|
|
667
682
|
// 获取文件内容
|
|
668
683
|
// 替换占位符,后续支持自定义渲染时可替换为自定义渲染标签
|
|
669
|
-
processedContent = processedContent.replace(match,
|
|
684
|
+
processedContent = processedContent.replace(match, `\`<attached_file>${fileUri.displayName}\``);
|
|
670
685
|
}
|
|
671
686
|
}
|
|
672
687
|
|
|
@@ -678,7 +693,7 @@ export const AIChatView = () => {
|
|
|
678
693
|
const folderUri = new URI(folderPath);
|
|
679
694
|
llmContextService.addFolderToContext(folderUri);
|
|
680
695
|
// 替换占位符,后续支持自定义渲染时可替换为自定义渲染标签
|
|
681
|
-
processedContent = processedContent.replace(match,
|
|
696
|
+
processedContent = processedContent.replace(match, `\`<attached_folder>${folderUri.displayName}\``);
|
|
682
697
|
}
|
|
683
698
|
}
|
|
684
699
|
return handleAgentReply({ message: processedContent, agentId, command, reportExtra });
|
|
@@ -2,14 +2,15 @@ import capitalize from 'lodash/capitalize';
|
|
|
2
2
|
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
|
3
3
|
import Highlight from 'react-highlight';
|
|
4
4
|
|
|
5
|
-
import { IClipboardService, getIcon, useInjectable, uuid } from '@opensumi/ide-core-browser';
|
|
6
|
-
import { Popover } from '@opensumi/ide-core-browser/lib/components';
|
|
5
|
+
import { IClipboardService, LabelService, getIcon, useInjectable, uuid } from '@opensumi/ide-core-browser';
|
|
6
|
+
import { Icon, Popover } from '@opensumi/ide-core-browser/lib/components';
|
|
7
7
|
import { EnhanceIcon } from '@opensumi/ide-core-browser/lib/components/ai-native';
|
|
8
8
|
import {
|
|
9
9
|
ActionSourceEnum,
|
|
10
10
|
ActionTypeEnum,
|
|
11
11
|
ChatFeatureRegistryToken,
|
|
12
12
|
IAIReporter,
|
|
13
|
+
URI,
|
|
13
14
|
localize,
|
|
14
15
|
runWhenIdle,
|
|
15
16
|
} from '@opensumi/ide-core-common';
|
|
@@ -139,16 +140,26 @@ const CodeBlock = ({
|
|
|
139
140
|
renderText,
|
|
140
141
|
agentId = '',
|
|
141
142
|
command = '',
|
|
143
|
+
labelService,
|
|
142
144
|
}: {
|
|
143
145
|
content?: string;
|
|
144
146
|
relationId: string;
|
|
145
147
|
renderText?: (t: string) => React.ReactNode;
|
|
146
148
|
agentId?: string;
|
|
147
149
|
command?: string;
|
|
150
|
+
labelService?: LabelService;
|
|
148
151
|
}) => {
|
|
149
152
|
const rgInlineCode = /`([^`]+)`/g;
|
|
150
153
|
const rgBlockCode = /```([^]+?)```/g;
|
|
151
154
|
const rgBlockCodeBefore = /```([^]+)?/g;
|
|
155
|
+
const rgAttachedFile = /<attached_file>(.*)/g;
|
|
156
|
+
const rgAttachedFolder = /<attached_folder>(.*)/g;
|
|
157
|
+
const renderAttachment = (text: string, isFolder = false, key: string) => (
|
|
158
|
+
<span className={styles.attachment} key={key}>
|
|
159
|
+
<Icon iconClass={isFolder ? getIcon('folder') : labelService?.getIcon(new URI(text || 'file'))} />
|
|
160
|
+
<span className={styles.attachment_text}>{text}</span>
|
|
161
|
+
</span>
|
|
162
|
+
);
|
|
152
163
|
|
|
153
164
|
const renderCodeEditor = (content: string) => {
|
|
154
165
|
const language = content.split('\n')[0].trim().toLowerCase();
|
|
@@ -193,11 +204,47 @@ const CodeBlock = ({
|
|
|
193
204
|
renderedContent.push(text);
|
|
194
205
|
}
|
|
195
206
|
} else {
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
)
|
|
207
|
+
// 处理文件和文件夹标记
|
|
208
|
+
const processedText = text;
|
|
209
|
+
const fileMatches = [...text.matchAll(rgAttachedFile)];
|
|
210
|
+
const folderMatches = [...text.matchAll(rgAttachedFolder)];
|
|
211
|
+
if (fileMatches.length || folderMatches.length) {
|
|
212
|
+
let lastIndex = 0;
|
|
213
|
+
const fragments: (string | React.ReactNode)[] = [];
|
|
214
|
+
|
|
215
|
+
// 处理文件标记
|
|
216
|
+
fileMatches.forEach((match, matchIndex) => {
|
|
217
|
+
if (match.index !== undefined) {
|
|
218
|
+
const spanText = processedText.slice(lastIndex, match.index);
|
|
219
|
+
if (spanText) {
|
|
220
|
+
fragments.push(<span key={`${index}-${matchIndex}`}>{spanText}</span>);
|
|
221
|
+
}
|
|
222
|
+
fragments.push(renderAttachment(match[1], false, `${index}-tag-${matchIndex}`));
|
|
223
|
+
lastIndex = match.index + match[0].length;
|
|
224
|
+
}
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
// 处理文件夹标记
|
|
228
|
+
folderMatches.forEach((match, matchIndex) => {
|
|
229
|
+
if (match.index !== undefined) {
|
|
230
|
+
const spanText = processedText.slice(lastIndex, match.index);
|
|
231
|
+
if (spanText) {
|
|
232
|
+
fragments.push(<span key={`${index}-${matchIndex}`}>{spanText}</span>);
|
|
233
|
+
}
|
|
234
|
+
fragments.push(renderAttachment(match[1], true, `${index}-tag-${matchIndex}`));
|
|
235
|
+
lastIndex = match.index + match[0].length;
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
fragments.push(processedText.slice(lastIndex));
|
|
240
|
+
renderedContent.push(...fragments);
|
|
241
|
+
} else {
|
|
242
|
+
renderedContent.push(
|
|
243
|
+
<span className={styles.code_inline} key={index}>
|
|
244
|
+
{text}
|
|
245
|
+
</span>,
|
|
246
|
+
);
|
|
247
|
+
}
|
|
201
248
|
}
|
|
202
249
|
});
|
|
203
250
|
} else {
|
|
@@ -216,15 +263,23 @@ export const CodeBlockWrapper = ({
|
|
|
216
263
|
renderText,
|
|
217
264
|
relationId,
|
|
218
265
|
agentId,
|
|
266
|
+
labelService,
|
|
219
267
|
}: {
|
|
220
268
|
text?: string;
|
|
221
269
|
relationId: string;
|
|
222
270
|
renderText?: (t: string) => React.ReactNode;
|
|
223
271
|
agentId?: string;
|
|
272
|
+
labelService?: LabelService;
|
|
224
273
|
}) => (
|
|
225
274
|
<div className={styles.ai_chat_code_wrapper}>
|
|
226
275
|
<div className={styles.render_text}>
|
|
227
|
-
<CodeBlock
|
|
276
|
+
<CodeBlock
|
|
277
|
+
content={text}
|
|
278
|
+
labelService={labelService}
|
|
279
|
+
renderText={renderText}
|
|
280
|
+
relationId={relationId}
|
|
281
|
+
agentId={agentId}
|
|
282
|
+
/>
|
|
228
283
|
</div>
|
|
229
284
|
</div>
|
|
230
285
|
);
|
|
@@ -234,11 +289,13 @@ export const CodeBlockWrapperInput = ({
|
|
|
234
289
|
relationId,
|
|
235
290
|
agentId,
|
|
236
291
|
command,
|
|
292
|
+
labelService,
|
|
237
293
|
}: {
|
|
238
294
|
text: string;
|
|
239
295
|
relationId: string;
|
|
240
296
|
agentId?: string;
|
|
241
297
|
command?: string;
|
|
298
|
+
labelService?: LabelService;
|
|
242
299
|
}) => {
|
|
243
300
|
const chatFeatureRegistry = useInjectable<ChatFeatureRegistry>(ChatFeatureRegistryToken);
|
|
244
301
|
const [tag, setTag] = useState<string>('');
|
|
@@ -271,7 +328,13 @@ export const CodeBlockWrapperInput = ({
|
|
|
271
328
|
</div>
|
|
272
329
|
)}
|
|
273
330
|
{command && <div className={styles.tag}>/ {command}</div>}
|
|
274
|
-
<CodeBlock
|
|
331
|
+
<CodeBlock
|
|
332
|
+
content={txt}
|
|
333
|
+
labelService={labelService}
|
|
334
|
+
relationId={relationId}
|
|
335
|
+
agentId={agentId}
|
|
336
|
+
command={command}
|
|
337
|
+
/>
|
|
275
338
|
</div>
|
|
276
339
|
</div>
|
|
277
340
|
);
|
|
@@ -259,6 +259,7 @@ export const ChatMentionInput = React.forwardRef((props: IChatMentionInputProps)
|
|
|
259
259
|
onSend={handleSend}
|
|
260
260
|
onStop={handleStop}
|
|
261
261
|
loading={disabled}
|
|
262
|
+
labelService={labelService}
|
|
262
263
|
placeholder={localize('aiNative.chat.input.placeholder.default')}
|
|
263
264
|
footerConfig={defaultMentionInputFooterOptions}
|
|
264
265
|
/>
|
|
@@ -566,3 +566,21 @@
|
|
|
566
566
|
color: var(--descriptionForeground);
|
|
567
567
|
}
|
|
568
568
|
}
|
|
569
|
+
|
|
570
|
+
.attachment {
|
|
571
|
+
display: inline-flex;
|
|
572
|
+
align-items: center;
|
|
573
|
+
padding: 0 4px;
|
|
574
|
+
margin: 0 2px;
|
|
575
|
+
background: var(--badge-background);
|
|
576
|
+
color: var(--badge-foreground);
|
|
577
|
+
border-radius: 3px;
|
|
578
|
+
vertical-align: middle;
|
|
579
|
+
font-size: 12px;
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
.attachment_text {
|
|
583
|
+
line-height: 20px;
|
|
584
|
+
vertical-align: middle;
|
|
585
|
+
font-size: 12px;
|
|
586
|
+
}
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
width: 100%;
|
|
4
4
|
margin: 0 auto;
|
|
5
5
|
border-radius: 4px;
|
|
6
|
+
|
|
6
7
|
.model_selector {
|
|
7
8
|
margin-right: 5px;
|
|
8
9
|
}
|
|
@@ -55,8 +56,10 @@
|
|
|
55
56
|
span {
|
|
56
57
|
color: var(--design-text-foreground);
|
|
57
58
|
}
|
|
59
|
+
|
|
58
60
|
&:hover {
|
|
59
61
|
background-color: var(--badge-background);
|
|
62
|
+
|
|
60
63
|
span {
|
|
61
64
|
color: var(--badge-foreground);
|
|
62
65
|
}
|
|
@@ -75,6 +78,7 @@
|
|
|
75
78
|
justify-content: flex-start;
|
|
76
79
|
align-items: center;
|
|
77
80
|
flex-direction: row;
|
|
81
|
+
|
|
78
82
|
.left_control {
|
|
79
83
|
display: flex;
|
|
80
84
|
align-items: center;
|
|
@@ -82,17 +86,21 @@
|
|
|
82
86
|
flex-direction: row;
|
|
83
87
|
flex: 1;
|
|
84
88
|
}
|
|
89
|
+
|
|
85
90
|
.right_control {
|
|
86
91
|
display: flex;
|
|
87
92
|
align-items: center;
|
|
88
93
|
justify-content: flex-end;
|
|
89
94
|
flex-direction: row;
|
|
95
|
+
|
|
90
96
|
.send_logo,
|
|
91
97
|
.stop_logo {
|
|
92
98
|
background-color: var(--badge-background);
|
|
93
99
|
color: var(--badge-foreground);
|
|
100
|
+
|
|
94
101
|
&:hover {
|
|
95
102
|
background-color: var(--kt-primaryButton-background);
|
|
103
|
+
|
|
96
104
|
.send_logo_icon,
|
|
97
105
|
.stop_logo_icon {
|
|
98
106
|
color: var(--kt-primaryButton-foreground);
|
|
@@ -155,6 +163,7 @@
|
|
|
155
163
|
color: var(--foreground);
|
|
156
164
|
border-radius: 4px;
|
|
157
165
|
margin-bottom: 5px;
|
|
166
|
+
|
|
158
167
|
&:last-child {
|
|
159
168
|
margin-bottom: 0;
|
|
160
169
|
}
|
|
@@ -242,9 +251,17 @@
|
|
|
242
251
|
border-radius: 4px;
|
|
243
252
|
padding: 0 4px;
|
|
244
253
|
margin: 0 3px;
|
|
245
|
-
display: inline-block;
|
|
246
254
|
user-select: all;
|
|
247
255
|
cursor: default;
|
|
256
|
+
display: inline-flex;
|
|
257
|
+
align-items: center;
|
|
258
|
+
justify-content: center;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
.mention_icon {
|
|
262
|
+
display: inline-flex;
|
|
263
|
+
align-items: center;
|
|
264
|
+
justify-content: center;
|
|
248
265
|
}
|
|
249
266
|
|
|
250
267
|
.empty_state {
|
|
@@ -296,6 +313,7 @@
|
|
|
296
313
|
0% {
|
|
297
314
|
background-position: 100% 0;
|
|
298
315
|
}
|
|
316
|
+
|
|
299
317
|
100% {
|
|
300
318
|
background-position: -100% 0;
|
|
301
319
|
}
|
|
@@ -3,6 +3,7 @@ import * as React from 'react';
|
|
|
3
3
|
|
|
4
4
|
import { Popover, PopoverPosition, Select, getIcon } from '@opensumi/ide-core-browser/lib/components';
|
|
5
5
|
import { EnhanceIcon } from '@opensumi/ide-core-browser/lib/components/ai-native';
|
|
6
|
+
import { URI } from '@opensumi/ide-utils';
|
|
6
7
|
|
|
7
8
|
import styles from './mention-input.module.less';
|
|
8
9
|
import { MentionPanel } from './mention-panel';
|
|
@@ -17,6 +18,7 @@ export const MentionInput: React.FC<MentionInputProps> = ({
|
|
|
17
18
|
loading = false,
|
|
18
19
|
mentionKeyword = MENTION_KEYWORD,
|
|
19
20
|
onSelectionChange,
|
|
21
|
+
labelService,
|
|
20
22
|
placeholder = 'Ask anything, @ to mention',
|
|
21
23
|
footerConfig = {
|
|
22
24
|
buttons: [],
|
|
@@ -577,7 +579,21 @@ export const MentionInput: React.FC<MentionInputProps> = ({
|
|
|
577
579
|
mentionTag.dataset.type = item.type;
|
|
578
580
|
mentionTag.dataset.contextId = item.contextId || '';
|
|
579
581
|
mentionTag.contentEditable = 'false';
|
|
580
|
-
|
|
582
|
+
|
|
583
|
+
// 为 file 和 folder 类型添加图标
|
|
584
|
+
if (item.type === 'file' || item.type === 'folder') {
|
|
585
|
+
// 创建图标容器
|
|
586
|
+
const iconSpan = document.createElement('span');
|
|
587
|
+
iconSpan.className = cls(
|
|
588
|
+
styles.mention_icon,
|
|
589
|
+
item.type === 'file' ? labelService?.getIcon(new URI(item.text)) : getIcon('folder'),
|
|
590
|
+
);
|
|
591
|
+
mentionTag.appendChild(iconSpan);
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
// 创建文本内容容器
|
|
595
|
+
const textSpan = document.createTextNode(item.text);
|
|
596
|
+
mentionTag.appendChild(textSpan);
|
|
581
597
|
|
|
582
598
|
// 创建一个范围从 @type: 开始到当前光标
|
|
583
599
|
const tempRange = document.createRange();
|
|
@@ -666,7 +682,21 @@ export const MentionInput: React.FC<MentionInputProps> = ({
|
|
|
666
682
|
mentionTag.dataset.type = item.type;
|
|
667
683
|
mentionTag.dataset.contextId = item.contextId || '';
|
|
668
684
|
mentionTag.contentEditable = 'false';
|
|
669
|
-
|
|
685
|
+
|
|
686
|
+
// 为 file 和 folder 类型添加图标
|
|
687
|
+
if (item.type === 'file' || item.type === 'folder') {
|
|
688
|
+
// 创建图标容器
|
|
689
|
+
const iconSpan = document.createElement('span');
|
|
690
|
+
iconSpan.className = cls(
|
|
691
|
+
styles.mention_icon,
|
|
692
|
+
item.type === 'file' ? labelService?.getIcon(new URI(item.text)) : getIcon('folder'),
|
|
693
|
+
);
|
|
694
|
+
mentionTag.appendChild(iconSpan);
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
// 创建文本内容容器
|
|
698
|
+
const textSpan = document.createTextNode(item.text);
|
|
699
|
+
mentionTag.appendChild(textSpan);
|
|
670
700
|
|
|
671
701
|
// 定位到 @ 符号的位置
|
|
672
702
|
let charIndex = 0;
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { LabelService } from '@opensumi/ide-core-browser';
|
|
2
|
+
|
|
1
3
|
export interface MentionItem {
|
|
2
4
|
id: string;
|
|
3
5
|
type: string;
|
|
@@ -74,6 +76,7 @@ export interface MentionInputProps {
|
|
|
74
76
|
onSelectionChange?: (value: string) => void;
|
|
75
77
|
footerConfig?: FooterConfig; // 新增配置项
|
|
76
78
|
mentionKeyword?: string;
|
|
79
|
+
labelService?: LabelService;
|
|
77
80
|
}
|
|
78
81
|
|
|
79
82
|
export const MENTION_KEYWORD = '@';
|
|
@@ -209,9 +209,7 @@ export class LLMContextServiceImpl extends WithEventBus implements LLMContextSer
|
|
|
209
209
|
folderPath.map(async (folder) => {
|
|
210
210
|
const folderUri = new URI(folder);
|
|
211
211
|
const root = workspaceRoot.relative(folderUri)?.toString() || '/';
|
|
212
|
-
return
|
|
213
|
-
await this.getPartiaFolderStructure(folderUri.codeUri.fsPath)
|
|
214
|
-
)
|
|
212
|
+
return `\`\`\`\n${root}\n${(await this.getPartiaFolderStructure(folderUri.codeUri.fsPath))
|
|
215
213
|
.map((line) => `- ${line}`)
|
|
216
214
|
.join('\n')}\n\`\`\`\n`;
|
|
217
215
|
}),
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Autowired, Injectable } from '@opensumi/di';
|
|
2
2
|
import { WorkbenchEditorService } from '@opensumi/ide-editor/lib/common/editor';
|
|
3
|
+
import { IWorkspaceService } from '@opensumi/ide-workspace';
|
|
3
4
|
|
|
4
5
|
import { SerializedContext } from '../llm-context';
|
|
5
6
|
|
|
@@ -10,7 +11,7 @@ export interface ChatAgentPromptProvider {
|
|
|
10
11
|
* 提供上下文提示
|
|
11
12
|
* @param context 上下文
|
|
12
13
|
*/
|
|
13
|
-
provideContextPrompt(context: SerializedContext, userMessage: string): string
|
|
14
|
+
provideContextPrompt(context: SerializedContext, userMessage: string): Promise<string>;
|
|
14
15
|
}
|
|
15
16
|
|
|
16
17
|
@Injectable()
|
|
@@ -18,45 +19,131 @@ export class DefaultChatAgentPromptProvider implements ChatAgentPromptProvider {
|
|
|
18
19
|
@Autowired(WorkbenchEditorService)
|
|
19
20
|
protected readonly workbenchEditorService: WorkbenchEditorService;
|
|
20
21
|
|
|
21
|
-
|
|
22
|
+
@Autowired(IWorkspaceService)
|
|
23
|
+
protected readonly workspaceService: IWorkspaceService;
|
|
24
|
+
|
|
25
|
+
async provideContextPrompt(context: SerializedContext, userMessage: string) {
|
|
26
|
+
const currentFileInfo = await this.getCurrentFileInfo();
|
|
27
|
+
|
|
28
|
+
return this.buildPromptTemplate({
|
|
29
|
+
recentFiles: this.buildRecentFilesSection(context.recentlyViewFiles),
|
|
30
|
+
attachedFiles: this.buildAttachedFilesSection(context.attachedFiles),
|
|
31
|
+
attachedFolders: this.buildAttachedFoldersSection(context.attachedFolders),
|
|
32
|
+
currentFile: currentFileInfo,
|
|
33
|
+
userMessage,
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
private async getCurrentFileInfo() {
|
|
22
38
|
const editor = this.workbenchEditorService.currentEditor;
|
|
23
39
|
const currentModel = editor?.currentDocumentModel;
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
40
|
+
|
|
41
|
+
if (!currentModel?.uri) {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const currentPath =
|
|
46
|
+
(await this.workspaceService.asRelativePath(currentModel.uri))?.path || currentModel.uri.codeUri.fsPath;
|
|
47
|
+
|
|
48
|
+
return {
|
|
49
|
+
path: currentPath,
|
|
50
|
+
languageId: currentModel.languageId,
|
|
51
|
+
content: currentModel.getText(),
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
private buildPromptTemplate({
|
|
56
|
+
recentFiles,
|
|
57
|
+
attachedFiles,
|
|
58
|
+
attachedFolders,
|
|
59
|
+
currentFile,
|
|
60
|
+
userMessage,
|
|
61
|
+
}: {
|
|
62
|
+
recentFiles: string;
|
|
63
|
+
attachedFiles: string;
|
|
64
|
+
attachedFolders: string;
|
|
65
|
+
currentFile: { path: string; languageId: string; content: string } | null;
|
|
66
|
+
userMessage: string;
|
|
67
|
+
}) {
|
|
68
|
+
const sections = [
|
|
69
|
+
'<additional_data>',
|
|
70
|
+
'Below are some potentially helpful/relevant pieces of information for figuring out to respond',
|
|
71
|
+
recentFiles,
|
|
72
|
+
attachedFiles,
|
|
73
|
+
attachedFolders,
|
|
74
|
+
this.buildCurrentFileSection(currentFile),
|
|
75
|
+
'</additional_data>',
|
|
76
|
+
'<user_query>',
|
|
77
|
+
userMessage,
|
|
78
|
+
'</user_query>',
|
|
79
|
+
].filter(Boolean);
|
|
80
|
+
|
|
81
|
+
return sections.join('\n');
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
private buildRecentFilesSection(files: string[]): string {
|
|
85
|
+
if (!files.length) {
|
|
86
|
+
return '';
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return `<recently_viewed_files>
|
|
90
|
+
${files.map((file, idx) => ` ${idx + 1}: ${file}`).join('\n')}
|
|
91
|
+
</recently_viewed_files>`;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
private buildAttachedFilesSection(files: { path: string; content: string; lineErrors: string[] }[]): string {
|
|
95
|
+
if (!files.length) {
|
|
96
|
+
return '';
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const fileContents = files
|
|
100
|
+
.map((file) => {
|
|
101
|
+
const sections = [
|
|
102
|
+
this.buildFileContentSection(file),
|
|
103
|
+
file.lineErrors.length ? this.buildLineErrorsSection(file.lineErrors) : '',
|
|
104
|
+
].filter(Boolean);
|
|
105
|
+
|
|
106
|
+
return sections.join('\n');
|
|
107
|
+
})
|
|
108
|
+
.filter(Boolean)
|
|
109
|
+
.join('\n');
|
|
110
|
+
|
|
111
|
+
return `<attached_files>\n${fileContents}\n</attached_files>`;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
private buildFileContentSection(file: { path: string; content: string }): string {
|
|
115
|
+
return `<file_contents>
|
|
34
116
|
\`\`\`${file.path}
|
|
35
117
|
${file.content}
|
|
36
118
|
\`\`\`
|
|
37
|
-
</file_contents
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
119
|
+
</file_contents>`;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
private buildLineErrorsSection(errors: string[]): string {
|
|
123
|
+
if (!errors.length) {
|
|
124
|
+
return '';
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return `<linter_errors>\n${errors.join('\n')}\n</linter_errors>`;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
private buildAttachedFoldersSection(folders: string[]): string {
|
|
131
|
+
if (!folders.length) {
|
|
132
|
+
return '';
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return `<attached_folders>\n${folders.join('\n')}</attached_folders>`;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
private buildCurrentFileSection(fileInfo: { path: string; languageId: string; content: string } | null): string {
|
|
139
|
+
if (!fileInfo) {
|
|
140
|
+
return '';
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return `<current_opened_file>
|
|
144
|
+
\`\`\`${fileInfo.languageId} ${fileInfo.path}
|
|
145
|
+
${fileInfo.content}
|
|
53
146
|
\`\`\`
|
|
54
|
-
</current_opened_file
|
|
55
|
-
: ''
|
|
56
|
-
}
|
|
57
|
-
</additional_data>
|
|
58
|
-
<user_query>
|
|
59
|
-
${userMessage}
|
|
60
|
-
</user_query>`;
|
|
147
|
+
</current_opened_file>`;
|
|
61
148
|
}
|
|
62
149
|
}
|