@opensumi/ide-ai-native 3.9.1-next-1749171169.0 → 3.9.1-next-1749181695.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-manager.service.d.ts +0 -1
- package/lib/browser/chat/chat-manager.service.d.ts.map +1 -1
- package/lib/browser/chat/chat-manager.service.js +3 -9
- package/lib/browser/chat/chat-manager.service.js.map +1 -1
- package/lib/browser/chat/chat-model.d.ts +1 -8
- package/lib/browser/chat/chat-model.d.ts.map +1 -1
- package/lib/browser/chat/chat-model.js +69 -105
- package/lib/browser/chat/chat-model.js.map +1 -1
- package/lib/browser/chat/chat.view.d.ts.map +1 -1
- package/lib/browser/chat/chat.view.js +16 -32
- package/lib/browser/chat/chat.view.js.map +1 -1
- package/lib/browser/components/ChatMentionInput.d.ts.map +1 -1
- package/lib/browser/components/ChatMentionInput.js +25 -3
- package/lib/browser/components/ChatMentionInput.js.map +1 -1
- package/lib/browser/components/ChatReply.js +2 -2
- package/lib/browser/components/ChatReply.js.map +1 -1
- package/lib/browser/components/components.module.less +30 -8
- package/lib/browser/components/mention-input/mention-input.d.ts.map +1 -1
- package/lib/browser/components/mention-input/mention-input.js +30 -36
- package/lib/browser/components/mention-input/mention-input.js.map +1 -1
- package/lib/browser/components/mention-input/mention-input.module.less +0 -1
- package/lib/browser/components/mention-input/mention-select.d.ts +28 -0
- package/lib/browser/components/mention-input/mention-select.d.ts.map +1 -0
- package/lib/browser/components/mention-input/mention-select.js +136 -0
- package/lib/browser/components/mention-input/mention-select.js.map +1 -0
- package/lib/browser/components/mention-input/mention-select.module.less +297 -0
- package/lib/browser/components/mention-input/types.d.ts +13 -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/model/msg-history-manager.d.ts +1 -41
- package/lib/browser/model/msg-history-manager.d.ts.map +1 -1
- package/lib/browser/model/msg-history-manager.js +2 -105
- package/lib/browser/model/msg-history-manager.js.map +1 -1
- package/lib/browser/rules/rules.module.less +1 -0
- package/lib/browser/rules/rules.view.js +1 -1
- package/lib/browser/rules/rules.view.js.map +1 -1
- package/lib/browser/types.d.ts +0 -4
- package/lib/browser/types.d.ts.map +1 -1
- package/lib/browser/types.js.map +1 -1
- package/lib/common/model.d.ts +1 -0
- package/lib/common/model.d.ts.map +1 -1
- package/lib/common/model.js.map +1 -1
- package/lib/node/anthropic/anthropic-language-model.d.ts +1 -1
- package/lib/node/base-language-model.js +2 -1
- package/lib/node/base-language-model.js.map +1 -1
- package/lib/node/deepseek/deepseek-language-model.d.ts +1 -1
- package/package.json +26 -26
- package/src/browser/chat/chat-manager.service.ts +7 -16
- package/src/browser/chat/chat-model.ts +66 -124
- package/src/browser/chat/chat.view.tsx +14 -46
- package/src/browser/components/ChatMentionInput.tsx +37 -4
- package/src/browser/components/ChatReply.tsx +4 -4
- package/src/browser/components/components.module.less +30 -8
- package/src/browser/components/mention-input/mention-input.module.less +0 -1
- package/src/browser/components/mention-input/mention-input.tsx +38 -52
- package/src/browser/components/mention-input/mention-select.module.less +297 -0
- package/src/browser/components/mention-input/mention-select.tsx +256 -0
- package/src/browser/components/mention-input/types.ts +14 -0
- package/src/browser/model/msg-history-manager.ts +2 -145
- package/src/browser/rules/rules.module.less +1 -0
- package/src/browser/rules/rules.view.tsx +1 -1
- package/src/browser/types.ts +0 -6
- package/src/common/model.ts +1 -0
- package/src/node/base-language-model.ts +1 -1
- package/lib/common/MDC_PARSER_README.md +0 -171
- package/src/common/MDC_PARSER_README.md +0 -171
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
import cls from 'classnames';
|
|
2
|
+
import React, { useEffect, useRef, useState } from 'react';
|
|
3
|
+
|
|
4
|
+
import { ClickOutside } from '@opensumi/ide-components/lib/click-outside';
|
|
5
|
+
import { Icon, getIcon } from '@opensumi/ide-core-browser/lib/components';
|
|
6
|
+
|
|
7
|
+
import styles from './mention-select.module.less';
|
|
8
|
+
|
|
9
|
+
export interface ExtendedModelOption {
|
|
10
|
+
label: string;
|
|
11
|
+
value: string;
|
|
12
|
+
icon?: string;
|
|
13
|
+
iconClass?: string;
|
|
14
|
+
tags?: string[];
|
|
15
|
+
features?: string[];
|
|
16
|
+
description?: string;
|
|
17
|
+
disabled?: boolean;
|
|
18
|
+
badge?: string;
|
|
19
|
+
badgeColor?: string;
|
|
20
|
+
selected?: boolean;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface MentionSelectProps {
|
|
24
|
+
options: ExtendedModelOption[];
|
|
25
|
+
value?: string;
|
|
26
|
+
onChange?: (value: string) => void;
|
|
27
|
+
placeholder?: string;
|
|
28
|
+
disabled?: boolean;
|
|
29
|
+
className?: string;
|
|
30
|
+
size?: 'small' | 'medium' | 'large';
|
|
31
|
+
showThinking?: boolean;
|
|
32
|
+
thinkingEnabled?: boolean;
|
|
33
|
+
onThinkingChange?: (enabled: boolean) => void;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const ThinkingToggle: React.FC<{
|
|
37
|
+
enabled: boolean;
|
|
38
|
+
onChange: (enabled: boolean) => void;
|
|
39
|
+
}> = ({ enabled, onChange }) => (
|
|
40
|
+
<div className={styles.thinking_toggle} onClick={() => onChange(!enabled)}>
|
|
41
|
+
<Icon
|
|
42
|
+
iconClass={getIcon(enabled ? 'check' : 'circle-outline')}
|
|
43
|
+
className={cls(styles.thinking_icon, {
|
|
44
|
+
[styles.enabled]: enabled,
|
|
45
|
+
})}
|
|
46
|
+
/>
|
|
47
|
+
<span className={styles.thinking_label}>Thinking</span>
|
|
48
|
+
</div>
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
export const MentionSelect: React.FC<MentionSelectProps> = ({
|
|
52
|
+
options,
|
|
53
|
+
value,
|
|
54
|
+
onChange,
|
|
55
|
+
placeholder,
|
|
56
|
+
disabled = false,
|
|
57
|
+
className,
|
|
58
|
+
size = 'small',
|
|
59
|
+
showThinking = false,
|
|
60
|
+
thinkingEnabled = false,
|
|
61
|
+
onThinkingChange,
|
|
62
|
+
}) => {
|
|
63
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
64
|
+
const [activeIndex, setActiveIndex] = useState(-1);
|
|
65
|
+
const [dropdownDirection, setDropdownDirection] = useState<'up' | 'down'>('up');
|
|
66
|
+
const selectRef = useRef<HTMLDivElement>(null);
|
|
67
|
+
const dropdownRef = useRef<HTMLDivElement>(null);
|
|
68
|
+
|
|
69
|
+
const selectedOption = options.find((option) => option.selected) || options.find((option) => option.value === value);
|
|
70
|
+
|
|
71
|
+
const handleToggle = () => {
|
|
72
|
+
if (!disabled) {
|
|
73
|
+
setIsOpen(!isOpen);
|
|
74
|
+
setActiveIndex(-1);
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const handleSelect = (option: ExtendedModelOption) => {
|
|
79
|
+
if (!option.disabled) {
|
|
80
|
+
onChange?.(option.value);
|
|
81
|
+
setIsOpen(false);
|
|
82
|
+
setActiveIndex(-1);
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const handleKeyDown = (e: React.KeyboardEvent) => {
|
|
87
|
+
if (disabled) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
switch (e.key) {
|
|
92
|
+
case 'Enter':
|
|
93
|
+
case ' ':
|
|
94
|
+
e.preventDefault();
|
|
95
|
+
if (!isOpen) {
|
|
96
|
+
setIsOpen(true);
|
|
97
|
+
} else if (activeIndex >= 0) {
|
|
98
|
+
handleSelect(options[activeIndex]);
|
|
99
|
+
}
|
|
100
|
+
break;
|
|
101
|
+
case 'Escape':
|
|
102
|
+
e.preventDefault();
|
|
103
|
+
setIsOpen(false);
|
|
104
|
+
setActiveIndex(-1);
|
|
105
|
+
break;
|
|
106
|
+
case 'ArrowDown':
|
|
107
|
+
e.preventDefault();
|
|
108
|
+
if (!isOpen) {
|
|
109
|
+
setIsOpen(true);
|
|
110
|
+
} else {
|
|
111
|
+
setActiveIndex((prev) => (prev < options.length - 1 ? prev + 1 : 0));
|
|
112
|
+
}
|
|
113
|
+
break;
|
|
114
|
+
case 'ArrowUp':
|
|
115
|
+
e.preventDefault();
|
|
116
|
+
if (isOpen) {
|
|
117
|
+
setActiveIndex((prev) => (prev > 0 ? prev - 1 : options.length - 1));
|
|
118
|
+
}
|
|
119
|
+
break;
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
const handleClickOutside = () => {
|
|
124
|
+
setIsOpen(false);
|
|
125
|
+
setActiveIndex(-1);
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
useEffect(() => {
|
|
129
|
+
if (isOpen && selectRef.current) {
|
|
130
|
+
const selectRect = selectRef.current.getBoundingClientRect();
|
|
131
|
+
const viewportHeight = window.innerHeight;
|
|
132
|
+
const dropdownHeight = Math.min(400, options.length * 60);
|
|
133
|
+
|
|
134
|
+
const spaceAbove = selectRect.top;
|
|
135
|
+
const spaceBelow = viewportHeight - selectRect.bottom;
|
|
136
|
+
|
|
137
|
+
if (spaceAbove < dropdownHeight && spaceBelow > spaceAbove) {
|
|
138
|
+
setDropdownDirection('down');
|
|
139
|
+
} else {
|
|
140
|
+
setDropdownDirection('up');
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}, [isOpen, options.length]);
|
|
144
|
+
|
|
145
|
+
useEffect(() => {
|
|
146
|
+
if (isOpen && activeIndex >= 0 && dropdownRef.current) {
|
|
147
|
+
const activeElement = dropdownRef.current.children[activeIndex] as HTMLElement;
|
|
148
|
+
if (activeElement) {
|
|
149
|
+
activeElement.scrollIntoView({
|
|
150
|
+
behavior: 'smooth',
|
|
151
|
+
block: 'nearest',
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}, [isOpen, activeIndex]);
|
|
156
|
+
|
|
157
|
+
return (
|
|
158
|
+
<ClickOutside onOutsideClick={handleClickOutside}>
|
|
159
|
+
<div
|
|
160
|
+
ref={selectRef}
|
|
161
|
+
className={cls(
|
|
162
|
+
styles.mention_select,
|
|
163
|
+
styles[`size_${size}`],
|
|
164
|
+
{
|
|
165
|
+
[styles.disabled]: disabled,
|
|
166
|
+
[styles.open]: isOpen,
|
|
167
|
+
[styles.dropdown_up]: dropdownDirection === 'up',
|
|
168
|
+
[styles.dropdown_down]: dropdownDirection === 'down',
|
|
169
|
+
},
|
|
170
|
+
className,
|
|
171
|
+
)}
|
|
172
|
+
onClick={handleToggle}
|
|
173
|
+
onKeyDown={handleKeyDown}
|
|
174
|
+
tabIndex={disabled ? -1 : 0}
|
|
175
|
+
role='combobox'
|
|
176
|
+
aria-expanded={isOpen}
|
|
177
|
+
aria-haspopup='listbox'
|
|
178
|
+
>
|
|
179
|
+
<div className={styles.select_trigger}>
|
|
180
|
+
<div className={styles.select_content}>
|
|
181
|
+
{selectedOption ? (
|
|
182
|
+
<div className={styles.selected_option}>
|
|
183
|
+
<span className={styles.option_label}>{selectedOption.label}</span>
|
|
184
|
+
{selectedOption.badge && (
|
|
185
|
+
<span className={styles.option_badge} style={{ backgroundColor: selectedOption.badgeColor }}>
|
|
186
|
+
{selectedOption.badge}
|
|
187
|
+
</span>
|
|
188
|
+
)}
|
|
189
|
+
</div>
|
|
190
|
+
) : (
|
|
191
|
+
<span className={styles.placeholder}>{placeholder}</span>
|
|
192
|
+
)}
|
|
193
|
+
</div>
|
|
194
|
+
<Icon
|
|
195
|
+
iconClass={getIcon('down-arrow')}
|
|
196
|
+
className={cls(styles.dropdown_arrow, {
|
|
197
|
+
[styles.open]: isOpen,
|
|
198
|
+
})}
|
|
199
|
+
/>
|
|
200
|
+
</div>
|
|
201
|
+
|
|
202
|
+
{isOpen && (
|
|
203
|
+
<div ref={dropdownRef} className={styles.dropdown} role='listbox'>
|
|
204
|
+
{showThinking && onThinkingChange && (
|
|
205
|
+
<div className={styles.thinking_section}>
|
|
206
|
+
<ThinkingToggle enabled={thinkingEnabled} onChange={onThinkingChange} />
|
|
207
|
+
<div className={styles.divider} />
|
|
208
|
+
</div>
|
|
209
|
+
)}
|
|
210
|
+
|
|
211
|
+
{options.map((option, index) => (
|
|
212
|
+
<div
|
|
213
|
+
key={option.value}
|
|
214
|
+
className={cls(styles.option, {
|
|
215
|
+
[styles.active]: index === activeIndex,
|
|
216
|
+
[styles.selected]: option.selected || option.value === value,
|
|
217
|
+
[styles.disabled]: option.disabled,
|
|
218
|
+
})}
|
|
219
|
+
onClick={() => handleSelect(option)}
|
|
220
|
+
role='option'
|
|
221
|
+
aria-selected={option.selected || option.value === value}
|
|
222
|
+
>
|
|
223
|
+
<div className={styles.option_main}>
|
|
224
|
+
<div className={styles.option_header}>
|
|
225
|
+
<div className={styles.option_title}>
|
|
226
|
+
{option.icon && <Icon icon={option.icon} className={styles.option_icon} />}
|
|
227
|
+
{option.iconClass && <Icon iconClass={option.iconClass} className={styles.option_icon} />}
|
|
228
|
+
<span className={styles.option_label}>{option.label}</span>
|
|
229
|
+
{option.badge && (
|
|
230
|
+
<span className={styles.option_badge} style={{ backgroundColor: option.badgeColor }}>
|
|
231
|
+
{option.badge}
|
|
232
|
+
</span>
|
|
233
|
+
)}
|
|
234
|
+
</div>
|
|
235
|
+
</div>
|
|
236
|
+
|
|
237
|
+
{option.description && <div className={styles.option_description}>{option.description}</div>}
|
|
238
|
+
|
|
239
|
+
{option.tags && option.tags.length > 0 && (
|
|
240
|
+
<div className={styles.option_tags}>
|
|
241
|
+
{option.tags.map((tag, idx) => (
|
|
242
|
+
<span key={idx} className={styles.tag}>
|
|
243
|
+
{tag}
|
|
244
|
+
</span>
|
|
245
|
+
))}
|
|
246
|
+
</div>
|
|
247
|
+
)}
|
|
248
|
+
</div>
|
|
249
|
+
</div>
|
|
250
|
+
))}
|
|
251
|
+
</div>
|
|
252
|
+
)}
|
|
253
|
+
</div>
|
|
254
|
+
</ClickOutside>
|
|
255
|
+
);
|
|
256
|
+
};
|
|
@@ -46,6 +46,17 @@ export interface MentionState {
|
|
|
46
46
|
interface ModelOption {
|
|
47
47
|
label: string;
|
|
48
48
|
value: string;
|
|
49
|
+
icon?: string;
|
|
50
|
+
iconClass?: string;
|
|
51
|
+
tags?: string[];
|
|
52
|
+
description?: string;
|
|
53
|
+
badge?: string;
|
|
54
|
+
badgeColor?: string;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface ExtendedModelOption extends ModelOption {
|
|
58
|
+
disabled?: boolean;
|
|
59
|
+
selected?: boolean; // 由外部控制选中状态
|
|
49
60
|
}
|
|
50
61
|
|
|
51
62
|
export interface ExtendedModelOption {
|
|
@@ -89,6 +100,9 @@ export interface FooterConfig {
|
|
|
89
100
|
buttons?: FooterButton[];
|
|
90
101
|
showModelSelector?: boolean;
|
|
91
102
|
disableModelSelector?: boolean;
|
|
103
|
+
showThinking?: boolean;
|
|
104
|
+
thinkingEnabled?: boolean;
|
|
105
|
+
onThinkingChange?: (enabled: boolean) => void;
|
|
92
106
|
}
|
|
93
107
|
|
|
94
108
|
export interface MentionInputProps {
|
|
@@ -1,34 +1,11 @@
|
|
|
1
1
|
import { Disposable, Emitter, Event, uuid } from '@opensumi/ide-core-common';
|
|
2
2
|
import { ChatMessageRole, IHistoryChatMessage } from '@opensumi/ide-core-common/lib/types/ai-native';
|
|
3
3
|
|
|
4
|
-
import { ChatFeatureRegistry } from '../chat/chat.feature.registry';
|
|
5
|
-
|
|
6
4
|
type IExcludeMessage = Omit<IHistoryChatMessage, 'id' | 'order'>;
|
|
7
5
|
|
|
8
|
-
interface IMemoryConfig {
|
|
9
|
-
shortTermSize: number; // 最近消息窗口大小(10条)
|
|
10
|
-
bufferSize: number; // 缓冲区大小(5条)
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
interface IMemorySummary {
|
|
14
|
-
content: string;
|
|
15
|
-
timestamp: number;
|
|
16
|
-
messageIds: string[];
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
interface IToolCallInfo {
|
|
20
|
-
id: string;
|
|
21
|
-
name: string;
|
|
22
|
-
args: Record<string, any>;
|
|
23
|
-
result: any;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
6
|
export class MsgHistoryManager extends Disposable {
|
|
27
7
|
private messageMap: Map<string, IHistoryChatMessage> = new Map();
|
|
28
8
|
private messageAdditionalMap: Map<string, Record<string, any>> = new Map();
|
|
29
|
-
private memorySummaries: IMemorySummary[] = [];
|
|
30
|
-
private toolCallMap: Map<string, IToolCallInfo> = new Map();
|
|
31
|
-
private isCompressing = false; // 添加压缩锁,防止重复压缩
|
|
32
9
|
|
|
33
10
|
private readonly _onMessageChange = new Emitter<IHistoryChatMessage[]>();
|
|
34
11
|
public readonly onMessageChange: Event<IHistoryChatMessage[]> = this._onMessageChange.event;
|
|
@@ -36,34 +13,11 @@ export class MsgHistoryManager extends Disposable {
|
|
|
36
13
|
private readonly _onMessageAdditionalChange = new Emitter<Record<string, any>>();
|
|
37
14
|
public readonly onMessageAdditionalChange: Event<Record<string, any>> = this._onMessageAdditionalChange.event;
|
|
38
15
|
|
|
39
|
-
|
|
40
|
-
shortTermSize: 10, // 保留最近10条消息
|
|
41
|
-
bufferSize: 5, // 缓冲区大小5条(作为最近10条中最早的5条)
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
public get config(): IMemoryConfig {
|
|
45
|
-
return { ...this.memoryConfig };
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
constructor(
|
|
49
|
-
private chatFeatureRegistry: ChatFeatureRegistry,
|
|
50
|
-
data?: {
|
|
51
|
-
additional: Record<string, any>;
|
|
52
|
-
messages: IHistoryChatMessage[];
|
|
53
|
-
toolCalls?: Record<string, IToolCallInfo>;
|
|
54
|
-
memorySummaries?: IMemorySummary[];
|
|
55
|
-
},
|
|
56
|
-
) {
|
|
16
|
+
constructor(data?: { additional: Record<string, any>; messages: IHistoryChatMessage[] }) {
|
|
57
17
|
super();
|
|
58
18
|
if (data) {
|
|
59
19
|
this.messageMap = new Map(data.messages.map((item) => [item.id, item]));
|
|
60
20
|
this.messageAdditionalMap = new Map(Object.entries(data.additional));
|
|
61
|
-
if (data.toolCalls) {
|
|
62
|
-
this.toolCallMap = new Map(Object.entries(data.toolCalls));
|
|
63
|
-
}
|
|
64
|
-
if (data.memorySummaries) {
|
|
65
|
-
this.memorySummaries = data.memorySummaries;
|
|
66
|
-
}
|
|
67
21
|
}
|
|
68
22
|
}
|
|
69
23
|
|
|
@@ -79,81 +33,6 @@ export class MsgHistoryManager extends Disposable {
|
|
|
79
33
|
public clearMessages() {
|
|
80
34
|
this.messageMap.clear();
|
|
81
35
|
this.messageAdditionalMap.clear();
|
|
82
|
-
this.memorySummaries = [];
|
|
83
|
-
this.toolCallMap.clear();
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* 压缩历史消息的方法:
|
|
88
|
-
* 1. 保持最新的10条消息不变
|
|
89
|
-
* 2. 当累积了5条超出限制的消息时,对这5条(最早的消息)进行总结
|
|
90
|
-
* 3. 总结完成后删除这5条消息
|
|
91
|
-
* 4. 重复这个过程,确保消息数量始终在可控范围内
|
|
92
|
-
*/
|
|
93
|
-
private async compressMemory() {
|
|
94
|
-
// 如果正在压缩中,直接返回
|
|
95
|
-
if (this.isCompressing) {
|
|
96
|
-
return;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
const messages = this.messageList;
|
|
100
|
-
// 只有当消息总数超过10条时才进行压缩
|
|
101
|
-
if (messages.length <= this.memoryConfig.shortTermSize) {
|
|
102
|
-
return;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
try {
|
|
106
|
-
this.isCompressing = true;
|
|
107
|
-
|
|
108
|
-
// 1. 计算超出10条限制的消息数量
|
|
109
|
-
const excessCount = messages.length - this.memoryConfig.shortTermSize;
|
|
110
|
-
|
|
111
|
-
// 当累积了5条或更多超出限制的消息时,进行总结
|
|
112
|
-
if (excessCount >= this.memoryConfig.bufferSize) {
|
|
113
|
-
// 2. 对最早的5条消息进行总结(取前5条,因为 messageList 是按从旧到新排序)
|
|
114
|
-
const messagesToSummarize = messages.slice(0, this.memoryConfig.bufferSize);
|
|
115
|
-
|
|
116
|
-
const summaryProvider = this.chatFeatureRegistry.getMessageSummaryProvider?.();
|
|
117
|
-
if (summaryProvider) {
|
|
118
|
-
const messageContents = messagesToSummarize.map((msg) => ({
|
|
119
|
-
role: msg.role,
|
|
120
|
-
content: msg.content,
|
|
121
|
-
}));
|
|
122
|
-
|
|
123
|
-
const summary = await summaryProvider.generateMemorizedMessage(messageContents);
|
|
124
|
-
if (summary) {
|
|
125
|
-
// 添加新的记忆总结
|
|
126
|
-
this.memorySummaries.push({
|
|
127
|
-
content: summary,
|
|
128
|
-
timestamp: Date.now(),
|
|
129
|
-
messageIds: messagesToSummarize.map((msg) => msg.id),
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
// 标记消息为已总结
|
|
133
|
-
for (const msg of messagesToSummarize) {
|
|
134
|
-
const existingMsg = this.messageMap.get(msg.id);
|
|
135
|
-
if (existingMsg) {
|
|
136
|
-
this.messageMap.set(msg.id, {
|
|
137
|
-
...existingMsg,
|
|
138
|
-
isSummarized: true,
|
|
139
|
-
});
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
} finally {
|
|
146
|
-
this.isCompressing = false;
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
public addToolCall(toolCall: IToolCallInfo): void {
|
|
151
|
-
this.toolCallMap.set(toolCall.id, toolCall);
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
public getToolCall(id: string): IToolCallInfo | undefined {
|
|
155
|
-
const toolCall = this.toolCallMap.get(id);
|
|
156
|
-
return toolCall;
|
|
157
36
|
}
|
|
158
37
|
|
|
159
38
|
private doAddMessage(message: IExcludeMessage): string {
|
|
@@ -169,19 +48,11 @@ export class MsgHistoryManager extends Disposable {
|
|
|
169
48
|
|
|
170
49
|
this.messageMap.set(id, msg);
|
|
171
50
|
|
|
172
|
-
// 在添加新消息后尝试压缩记忆
|
|
173
|
-
this.compressMemory().catch((error) => {
|
|
174
|
-
// eslint-disable-next-line no-console
|
|
175
|
-
console.error('[MsgHistoryManager] Error compressing memory', error);
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
// 无论压缩是否完成,都触发消息变更事件
|
|
179
51
|
this._onMessageChange.fire(this.getMessages());
|
|
180
52
|
return id;
|
|
181
53
|
}
|
|
182
54
|
|
|
183
55
|
private get messageList(): IHistoryChatMessage[] {
|
|
184
|
-
// 按 order 升序排序,保持消息的原始顺序
|
|
185
56
|
return Array.from(this.messageMap.values()).sort((a, b) => a.order - b.order);
|
|
186
57
|
}
|
|
187
58
|
|
|
@@ -194,17 +65,6 @@ export class MsgHistoryManager extends Disposable {
|
|
|
194
65
|
return this.messageList;
|
|
195
66
|
}
|
|
196
67
|
|
|
197
|
-
public getMemorySummaries(): IMemorySummary[] {
|
|
198
|
-
return this.memorySummaries;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
public setMemoryConfig(config: Partial<IMemoryConfig>) {
|
|
202
|
-
this.memoryConfig = {
|
|
203
|
-
...this.memoryConfig,
|
|
204
|
-
...config,
|
|
205
|
-
};
|
|
206
|
-
}
|
|
207
|
-
|
|
208
68
|
public addUserMessage(
|
|
209
69
|
message: Required<Pick<IExcludeMessage, 'agentId' | 'agentCommand' | 'content' | 'relationId' | 'images'>>,
|
|
210
70
|
): string {
|
|
@@ -258,12 +118,9 @@ export class MsgHistoryManager extends Disposable {
|
|
|
258
118
|
}
|
|
259
119
|
|
|
260
120
|
toJSON() {
|
|
261
|
-
|
|
121
|
+
return {
|
|
262
122
|
messages: this.getMessages(),
|
|
263
123
|
additional: Object.fromEntries(this.messageAdditionalMap.entries()),
|
|
264
|
-
memorySummaries: this.memorySummaries,
|
|
265
|
-
toolCalls: Object.fromEntries(this.toolCallMap.entries()),
|
|
266
124
|
};
|
|
267
|
-
return data;
|
|
268
125
|
}
|
|
269
126
|
}
|
|
@@ -37,7 +37,7 @@ export const RulesView: React.FC = () => {
|
|
|
37
37
|
[rulesService],
|
|
38
38
|
);
|
|
39
39
|
|
|
40
|
-
const getFileNameFromPath = (path: string) => path.split('/').pop() || path;
|
|
40
|
+
const getFileNameFromPath = (path: string) => decodeURIComponent(path.split('/').pop() || path);
|
|
41
41
|
|
|
42
42
|
const hasWarning = (rule: ProjectRule) => !rule.description && !rule.alwaysApply && !rule.globs;
|
|
43
43
|
|
package/src/browser/types.ts
CHANGED
|
@@ -308,12 +308,6 @@ export interface IMessageSummaryProvider {
|
|
|
308
308
|
content: string;
|
|
309
309
|
}>,
|
|
310
310
|
): Promise<string | undefined>;
|
|
311
|
-
generateMemorizedMessage(
|
|
312
|
-
messages: Array<{
|
|
313
|
-
role: ChatMessageRole;
|
|
314
|
-
content: string;
|
|
315
|
-
}>,
|
|
316
|
-
): Promise<string | undefined>;
|
|
317
311
|
}
|
|
318
312
|
|
|
319
313
|
export const AINativeCoreContribution = Symbol('AINativeCoreContribution');
|
package/src/common/model.ts
CHANGED
|
@@ -116,7 +116,7 @@ export abstract class BaseLanguageModel {
|
|
|
116
116
|
messages,
|
|
117
117
|
abortSignal: abortController.signal,
|
|
118
118
|
experimental_toolCallStreaming: true,
|
|
119
|
-
maxSteps:
|
|
119
|
+
maxSteps: modelInfo?.maxSteps ?? 25,
|
|
120
120
|
maxTokens,
|
|
121
121
|
system: systemPrompt,
|
|
122
122
|
providerOptions,
|
|
@@ -1,171 +0,0 @@
|
|
|
1
|
-
# MDC 文件解析器
|
|
2
|
-
|
|
3
|
-
MDC (Markdown with YAML Frontmatter) 解析器用于解析和序列化带有 YAML frontmatter 的 Markdown 文件。
|
|
4
|
-
|
|
5
|
-
## 文件格式
|
|
6
|
-
|
|
7
|
-
MDC 文件格式如下:
|
|
8
|
-
|
|
9
|
-
```
|
|
10
|
-
---
|
|
11
|
-
description:
|
|
12
|
-
globs:
|
|
13
|
-
alwaysApply: false
|
|
14
|
-
---
|
|
15
|
-
icejs is a react-like framework
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
其中:
|
|
19
|
-
|
|
20
|
-
- `---` 分隔符之间的部分是 YAML frontmatter
|
|
21
|
-
- 分隔符之后的部分是 Markdown 内容
|
|
22
|
-
|
|
23
|
-
## API 接口
|
|
24
|
-
|
|
25
|
-
### 类型定义
|
|
26
|
-
|
|
27
|
-
```typescript
|
|
28
|
-
interface IMDCFrontmatter {
|
|
29
|
-
description?: string;
|
|
30
|
-
globs?: string | string[];
|
|
31
|
-
alwaysApply?: boolean;
|
|
32
|
-
[key: string]: any;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
interface IMDCParseResult {
|
|
36
|
-
frontmatter: IMDCFrontmatter;
|
|
37
|
-
content: string;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
interface IMDCContent {
|
|
41
|
-
frontmatter: IMDCFrontmatter;
|
|
42
|
-
content: string;
|
|
43
|
-
}
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
### 主要函数
|
|
47
|
-
|
|
48
|
-
#### `parseMDC(rawContent: string): IMDCParseResult`
|
|
49
|
-
|
|
50
|
-
解析 MDC 文件内容,返回解析后的 frontmatter 和内容。
|
|
51
|
-
|
|
52
|
-
**示例:**
|
|
53
|
-
|
|
54
|
-
```typescript
|
|
55
|
-
const content = `---
|
|
56
|
-
description: "React规则"
|
|
57
|
-
globs: ["*.tsx", "*.jsx"]
|
|
58
|
-
alwaysApply: true
|
|
59
|
-
---
|
|
60
|
-
使用React hooks和函数组件`;
|
|
61
|
-
|
|
62
|
-
const result = parseMDC(content);
|
|
63
|
-
// result.frontmatter = { description: "React规则", globs: ["*.tsx", "*.jsx"], alwaysApply: true }
|
|
64
|
-
// result.content = "使用React hooks和函数组件"
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
#### `serializeMDC(mdcContent: IMDCContent): string`
|
|
68
|
-
|
|
69
|
-
将 MDC 内容对象序列化为字符串格式。
|
|
70
|
-
|
|
71
|
-
**示例:**
|
|
72
|
-
|
|
73
|
-
```typescript
|
|
74
|
-
const mdcContent = {
|
|
75
|
-
frontmatter: {
|
|
76
|
-
description: 'TypeScript规则',
|
|
77
|
-
globs: ['*.ts'],
|
|
78
|
-
alwaysApply: false,
|
|
79
|
-
},
|
|
80
|
-
content: '使用严格的类型检查',
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
const serialized = serializeMDC(mdcContent);
|
|
84
|
-
// 输出:
|
|
85
|
-
// ---
|
|
86
|
-
// description: TypeScript规则
|
|
87
|
-
// globs:
|
|
88
|
-
// - "*.ts"
|
|
89
|
-
// alwaysApply: false
|
|
90
|
-
// ---
|
|
91
|
-
// 使用严格的类型检查
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
#### `updateMDCFrontmatter(rawContent: string, newFrontmatter: Partial<IMDCFrontmatter>): string`
|
|
95
|
-
|
|
96
|
-
更新现有 MDC 内容的 frontmatter。
|
|
97
|
-
|
|
98
|
-
**示例:**
|
|
99
|
-
|
|
100
|
-
```typescript
|
|
101
|
-
const original = `---
|
|
102
|
-
description:
|
|
103
|
-
alwaysApply: false
|
|
104
|
-
---
|
|
105
|
-
原始内容`;
|
|
106
|
-
|
|
107
|
-
const updated = updateMDCFrontmatter(original, {
|
|
108
|
-
description: '更新后的描述',
|
|
109
|
-
globs: ['*.js'],
|
|
110
|
-
});
|
|
111
|
-
// 更新 frontmatter,保持原始内容不变
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
#### `extractMDCContent(rawContent: string): string`
|
|
115
|
-
|
|
116
|
-
从 MDC 内容中提取纯文本内容(不包含 frontmatter)。
|
|
117
|
-
|
|
118
|
-
**示例:**
|
|
119
|
-
|
|
120
|
-
```typescript
|
|
121
|
-
const content = extractMDCContent(mdcContent);
|
|
122
|
-
// 只返回 Markdown 内容部分
|
|
123
|
-
```
|
|
124
|
-
|
|
125
|
-
#### `validateMDCFrontmatter(frontmatter: any): boolean`
|
|
126
|
-
|
|
127
|
-
验证 frontmatter 对象是否符合规范。
|
|
128
|
-
|
|
129
|
-
#### `createDefaultMDCFrontmatter(): IMDCFrontmatter`
|
|
130
|
-
|
|
131
|
-
创建默认的 frontmatter 对象。
|
|
132
|
-
|
|
133
|
-
## 在 RulesService 中的使用
|
|
134
|
-
|
|
135
|
-
`RulesService` 类已经集成了 MDC 解析功能:
|
|
136
|
-
|
|
137
|
-
```typescript
|
|
138
|
-
// 解析全局规则
|
|
139
|
-
const mdcRules = rulesService.parseGlobalRulesAsMDC();
|
|
140
|
-
if (mdcRules) {
|
|
141
|
-
console.log('规则描述:', mdcRules.frontmatter.description);
|
|
142
|
-
console.log('适用文件:', mdcRules.frontmatter.globs);
|
|
143
|
-
console.log('规则内容:', mdcRules.content);
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
// 更新全局规则
|
|
147
|
-
const newMDCContent = {
|
|
148
|
-
frontmatter: {
|
|
149
|
-
description: '新的编码规范',
|
|
150
|
-
globs: ['src/**/*.ts'],
|
|
151
|
-
alwaysApply: true,
|
|
152
|
-
},
|
|
153
|
-
content: '遵循 TypeScript 最佳实践',
|
|
154
|
-
};
|
|
155
|
-
rulesService.updateGlobalRulesWithMDC(newMDCContent);
|
|
156
|
-
```
|
|
157
|
-
|
|
158
|
-
## 错误处理
|
|
159
|
-
|
|
160
|
-
解析器对错误具有良好的容错性:
|
|
161
|
-
|
|
162
|
-
- 如果 YAML frontmatter 格式错误,将返回空的 frontmatter 对象
|
|
163
|
-
- 如果没有 frontmatter,整个内容将作为 content 返回
|
|
164
|
-
- 序列化失败时,只返回内容部分
|
|
165
|
-
|
|
166
|
-
## 注意事项
|
|
167
|
-
|
|
168
|
-
1. frontmatter 中的 `globs` 字段可以是字符串或字符串数组
|
|
169
|
-
2. 所有 frontmatter 字段都是可选的
|
|
170
|
-
3. frontmatter 支持扩展,可以包含任意额外字段
|
|
171
|
-
4. 内容部分会自动去除首尾空白字符
|