@lobehub/lobehub 2.0.0-next.360 → 2.0.0-next.362
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/CHANGELOG.md +50 -0
- package/Dockerfile +2 -1
- package/changelog/v1.json +14 -0
- package/locales/en-US/chat.json +3 -1
- package/locales/zh-CN/chat.json +2 -0
- package/package.json +1 -1
- package/packages/const/src/userMemory.ts +1 -0
- package/packages/context-engine/src/base/BaseEveryUserContentProvider.ts +204 -0
- package/packages/context-engine/src/base/BaseLastUserContentProvider.ts +1 -8
- package/packages/context-engine/src/base/__tests__/BaseEveryUserContentProvider.test.ts +354 -0
- package/packages/context-engine/src/base/constants.ts +20 -0
- package/packages/context-engine/src/engine/messages/MessagesEngine.ts +27 -23
- package/packages/context-engine/src/engine/messages/__tests__/MessagesEngine.test.ts +364 -0
- package/packages/context-engine/src/providers/PageEditorContextInjector.ts +17 -13
- package/packages/context-engine/src/providers/PageSelectionsInjector.ts +65 -0
- package/packages/context-engine/src/providers/__tests__/PageSelectionsInjector.test.ts +333 -0
- package/packages/context-engine/src/providers/index.ts +3 -1
- package/packages/database/src/models/userMemory/model.ts +178 -3
- package/packages/database/src/models/userMemory/sources/benchmarkLoCoMo.ts +1 -1
- package/packages/memory-user-memory/package.json +2 -1
- package/packages/memory-user-memory/promptfoo/evals/activity/basic/buildMessages.ts +40 -0
- package/packages/memory-user-memory/promptfoo/evals/activity/basic/eval.yaml +13 -0
- package/packages/memory-user-memory/promptfoo/evals/activity/basic/prompt.ts +5 -0
- package/packages/memory-user-memory/promptfoo/evals/activity/basic/tests/cases.ts +106 -0
- package/packages/memory-user-memory/promptfoo/evals/activity/locomo/buildMessages.ts +104 -0
- package/packages/memory-user-memory/promptfoo/evals/activity/locomo/eval.yaml +13 -0
- package/packages/memory-user-memory/promptfoo/evals/activity/locomo/prompt.ts +5 -0
- package/packages/memory-user-memory/promptfoo/evals/activity/locomo/tests/benchmark-locomo-payload-conv-26.json +149 -0
- package/packages/memory-user-memory/promptfoo/evals/activity/locomo/tests/cases.ts +72 -0
- package/packages/memory-user-memory/promptfoo/response-formats/activity.json +370 -0
- package/packages/memory-user-memory/promptfoo/response-formats/experience.json +14 -0
- package/packages/memory-user-memory/promptfoo/response-formats/identity.json +281 -255
- package/packages/memory-user-memory/promptfooconfig.yaml +1 -0
- package/packages/memory-user-memory/scripts/generate-response-formats.ts +26 -2
- package/packages/memory-user-memory/src/extractors/activity.ts +44 -0
- package/packages/memory-user-memory/src/extractors/gatekeeper.test.ts +2 -1
- package/packages/memory-user-memory/src/extractors/gatekeeper.ts +2 -1
- package/packages/memory-user-memory/src/extractors/index.ts +1 -0
- package/packages/memory-user-memory/src/prompts/gatekeeper.ts +3 -3
- package/packages/memory-user-memory/src/prompts/index.ts +7 -1
- package/packages/memory-user-memory/src/prompts/layers/activity.ts +90 -0
- package/packages/memory-user-memory/src/prompts/layers/index.ts +1 -0
- package/packages/memory-user-memory/src/providers/existingUserMemory.test.ts +25 -1
- package/packages/memory-user-memory/src/providers/existingUserMemory.ts +113 -0
- package/packages/memory-user-memory/src/schemas/activity.ts +315 -0
- package/packages/memory-user-memory/src/schemas/experience.ts +5 -5
- package/packages/memory-user-memory/src/schemas/gatekeeper.ts +1 -0
- package/packages/memory-user-memory/src/schemas/index.ts +1 -0
- package/packages/memory-user-memory/src/services/extractExecutor.ts +29 -0
- package/packages/memory-user-memory/src/types.ts +7 -0
- package/packages/prompts/src/agents/index.ts +1 -0
- package/packages/prompts/src/agents/pageSelectionContext.ts +28 -0
- package/packages/types/src/aiChat.ts +4 -0
- package/packages/types/src/message/common/index.ts +1 -0
- package/packages/types/src/message/common/metadata.ts +8 -0
- package/packages/types/src/message/common/pageSelection.ts +36 -0
- package/packages/types/src/message/ui/params.ts +16 -0
- package/packages/types/src/serverConfig.ts +1 -1
- package/packages/types/src/userMemory/layers.ts +52 -0
- package/packages/types/src/userMemory/list.ts +20 -2
- package/packages/types/src/userMemory/shared.ts +22 -1
- package/packages/types/src/userMemory/trace.ts +1 -0
- package/packages/types/src/util.ts +9 -1
- package/scripts/prebuild.mts +1 -0
- package/src/features/ChatInput/Desktop/ContextContainer/ContextList.tsx +1 -1
- package/src/features/Conversation/ChatInput/index.tsx +9 -1
- package/src/features/Conversation/Messages/User/components/MessageContent.tsx +7 -1
- package/src/features/Conversation/Messages/User/components/PageSelections.tsx +62 -0
- package/src/features/PageEditor/EditorCanvas/useAskCopilotItem.tsx +5 -1
- package/src/libs/next/proxy/define-config.ts +1 -0
- package/src/locales/default/chat.ts +3 -2
- package/src/server/globalConfig/parseMemoryExtractionConfig.ts +7 -1
- package/src/server/routers/lambda/aiChat.ts +7 -0
- package/src/server/services/memory/userMemory/__tests__/extract.runtime.test.ts +2 -0
- package/src/server/services/memory/userMemory/extract.ts +108 -7
- package/src/store/chat/slices/aiChat/actions/conversationLifecycle.ts +5 -19
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,56 @@
|
|
|
2
2
|
|
|
3
3
|
# Changelog
|
|
4
4
|
|
|
5
|
+
## [Version 2.0.0-next.362](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.361...v2.0.0-next.362)
|
|
6
|
+
|
|
7
|
+
<sup>Released on **2026-01-24**</sup>
|
|
8
|
+
|
|
9
|
+
#### 🐛 Bug Fixes
|
|
10
|
+
|
|
11
|
+
- **misc**: Fix page selection not display correctly.
|
|
12
|
+
|
|
13
|
+
<br/>
|
|
14
|
+
|
|
15
|
+
<details>
|
|
16
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
17
|
+
|
|
18
|
+
#### What's fixed
|
|
19
|
+
|
|
20
|
+
- **misc**: Fix page selection not display correctly, closes [#11765](https://github.com/lobehub/lobe-chat/issues/11765) ([7ae5f68](https://github.com/lobehub/lobe-chat/commit/7ae5f68))
|
|
21
|
+
|
|
22
|
+
</details>
|
|
23
|
+
|
|
24
|
+
<div align="right">
|
|
25
|
+
|
|
26
|
+
[](#readme-top)
|
|
27
|
+
|
|
28
|
+
</div>
|
|
29
|
+
|
|
30
|
+
## [Version 2.0.0-next.361](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.360...v2.0.0-next.361)
|
|
31
|
+
|
|
32
|
+
<sup>Released on **2026-01-24**</sup>
|
|
33
|
+
|
|
34
|
+
#### ✨ Features
|
|
35
|
+
|
|
36
|
+
- **userMemories**: Added memory layer activity.
|
|
37
|
+
|
|
38
|
+
<br/>
|
|
39
|
+
|
|
40
|
+
<details>
|
|
41
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
42
|
+
|
|
43
|
+
#### What's improved
|
|
44
|
+
|
|
45
|
+
- **userMemories**: Added memory layer activity, closes [#11747](https://github.com/lobehub/lobe-chat/issues/11747) ([2021b1c](https://github.com/lobehub/lobe-chat/commit/2021b1c))
|
|
46
|
+
|
|
47
|
+
</details>
|
|
48
|
+
|
|
49
|
+
<div align="right">
|
|
50
|
+
|
|
51
|
+
[](#readme-top)
|
|
52
|
+
|
|
53
|
+
</div>
|
|
54
|
+
|
|
5
55
|
## [Version 2.0.0-next.360](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.359...v2.0.0-next.360)
|
|
6
56
|
|
|
7
57
|
<sup>Released on **2026-01-24**</sup>
|
package/Dockerfile
CHANGED
|
@@ -47,7 +47,8 @@ ENV NEXT_PUBLIC_BASE_PATH="${NEXT_PUBLIC_BASE_PATH}" \
|
|
|
47
47
|
ENV APP_URL="http://app.com" \
|
|
48
48
|
DATABASE_DRIVER="node" \
|
|
49
49
|
DATABASE_URL="postgres://postgres:password@localhost:5432/postgres" \
|
|
50
|
-
KEY_VAULTS_SECRET="use-for-build"
|
|
50
|
+
KEY_VAULTS_SECRET="use-for-build" \
|
|
51
|
+
AUTH_SECRET="use-for-build"
|
|
51
52
|
|
|
52
53
|
# Sentry
|
|
53
54
|
ENV NEXT_PUBLIC_SENTRY_DSN="${NEXT_PUBLIC_SENTRY_DSN}" \
|
package/changelog/v1.json
CHANGED
|
@@ -1,4 +1,18 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"children": {
|
|
4
|
+
"fixes": [
|
|
5
|
+
"Fix page selection not display correctly."
|
|
6
|
+
]
|
|
7
|
+
},
|
|
8
|
+
"date": "2026-01-24",
|
|
9
|
+
"version": "2.0.0-next.362"
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"children": {},
|
|
13
|
+
"date": "2026-01-24",
|
|
14
|
+
"version": "2.0.0-next.361"
|
|
15
|
+
},
|
|
2
16
|
{
|
|
3
17
|
"children": {
|
|
4
18
|
"fixes": [
|
package/locales/en-US/chat.json
CHANGED
|
@@ -208,7 +208,9 @@
|
|
|
208
208
|
"operation.sendMessage": "Sending message",
|
|
209
209
|
"owner": "Group owner",
|
|
210
210
|
"pageCopilot.title": "Page Agent",
|
|
211
|
-
"pageCopilot.welcome": "**Clearer, sharper writing**\n\nDraft, rewrite, or polish—tell me your intent and I
|
|
211
|
+
"pageCopilot.welcome": "**Clearer, sharper writing**\n\nDraft, rewrite, or polish—tell me your intent and I'll refine the rest.",
|
|
212
|
+
"pageSelection.lines": "Lines {{start}}-{{end}}",
|
|
213
|
+
"pageSelection.reference": "Selected Text",
|
|
212
214
|
"pin": "Pin",
|
|
213
215
|
"pinOff": "Unpin",
|
|
214
216
|
"prompts.summaryExpert": "As a summary expert, please summarize the following content based on the system prompts above:",
|
package/locales/zh-CN/chat.json
CHANGED
|
@@ -209,6 +209,8 @@
|
|
|
209
209
|
"owner": "群主",
|
|
210
210
|
"pageCopilot.title": "文稿助理",
|
|
211
211
|
"pageCopilot.welcome": "**让文字更清晰、更到位**\n\n起草、改写、润色都可以。你把意图说清楚,其余交给我打磨",
|
|
212
|
+
"pageSelection.lines": "第 {{start}}-{{end}} 行",
|
|
213
|
+
"pageSelection.reference": "选中文本",
|
|
212
214
|
"pin": "置顶",
|
|
213
215
|
"pinOff": "取消置顶",
|
|
214
216
|
"prompts.summaryExpert": "作为一名总结专家,请结合以上系统提示词,将以下内容进行总结:",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lobehub/lobehub",
|
|
3
|
-
"version": "2.0.0-next.
|
|
3
|
+
"version": "2.0.0-next.362",
|
|
4
4
|
"description": "LobeHub - an open-source,comprehensive AI Agent framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"framework",
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import type { Message, PipelineContext, ProcessorOptions } from '../types';
|
|
2
|
+
import { BaseProcessor } from './BaseProcessor';
|
|
3
|
+
import { CONTEXT_INSTRUCTION, SYSTEM_CONTEXT_END, SYSTEM_CONTEXT_START } from './constants';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Base Provider for appending content to every user message
|
|
7
|
+
* Used for injecting context that should be attached to each user message individually
|
|
8
|
+
* (e.g., page selections that are specific to each message)
|
|
9
|
+
*
|
|
10
|
+
* Features:
|
|
11
|
+
* - Iterates through all user messages
|
|
12
|
+
* - For each message, calls buildContentForMessage to get content to inject
|
|
13
|
+
* - Wraps content with SYSTEM CONTEXT markers (or reuses existing wrapper)
|
|
14
|
+
* - Runs BEFORE BaseLastUserContentProvider so that the last user message
|
|
15
|
+
* can reuse the SYSTEM CONTEXT wrapper created here
|
|
16
|
+
*/
|
|
17
|
+
export abstract class BaseEveryUserContentProvider extends BaseProcessor {
|
|
18
|
+
constructor(options: ProcessorOptions = {}) {
|
|
19
|
+
super(options);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Build the content to inject for a specific user message
|
|
24
|
+
* Subclasses must implement this method
|
|
25
|
+
* @param message - The user message to build content for
|
|
26
|
+
* @param index - The index of the message in the messages array
|
|
27
|
+
* @param isLastUser - Whether this is the last user message
|
|
28
|
+
* @returns Object with content and contextType, or null to skip injection for this message
|
|
29
|
+
*/
|
|
30
|
+
protected abstract buildContentForMessage(
|
|
31
|
+
message: Message,
|
|
32
|
+
index: number,
|
|
33
|
+
isLastUser: boolean,
|
|
34
|
+
): { content: string; contextType: string } | null;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Get the text content from a message (handles both string and array content)
|
|
38
|
+
*/
|
|
39
|
+
private getTextContent(content: string | any[]): string {
|
|
40
|
+
if (typeof content === 'string') {
|
|
41
|
+
return content;
|
|
42
|
+
}
|
|
43
|
+
if (Array.isArray(content)) {
|
|
44
|
+
const lastTextPart = content.findLast((part: any) => part.type === 'text');
|
|
45
|
+
return lastTextPart?.text || '';
|
|
46
|
+
}
|
|
47
|
+
return '';
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Check if the content already has a system context wrapper
|
|
52
|
+
*/
|
|
53
|
+
protected hasSystemContextWrapper(content: string | any[]): boolean {
|
|
54
|
+
const textContent = this.getTextContent(content);
|
|
55
|
+
return textContent.includes(SYSTEM_CONTEXT_START) && textContent.includes(SYSTEM_CONTEXT_END);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Wrap content with system context markers
|
|
60
|
+
*/
|
|
61
|
+
protected wrapWithSystemContext(content: string, contextType: string): string {
|
|
62
|
+
return `${SYSTEM_CONTEXT_START}
|
|
63
|
+
${CONTEXT_INSTRUCTION}
|
|
64
|
+
<${contextType}>
|
|
65
|
+
${content}
|
|
66
|
+
</${contextType}>
|
|
67
|
+
${SYSTEM_CONTEXT_END}`;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Insert content into existing system context wrapper (before the END marker)
|
|
72
|
+
*/
|
|
73
|
+
private insertIntoExistingWrapper(existingContent: string, newContextBlock: string): string {
|
|
74
|
+
const endMarkerIndex = existingContent.lastIndexOf(SYSTEM_CONTEXT_END);
|
|
75
|
+
if (endMarkerIndex === -1) {
|
|
76
|
+
return existingContent + '\n\n' + newContextBlock;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const beforeEnd = existingContent.slice(0, endMarkerIndex);
|
|
80
|
+
const afterEnd = existingContent.slice(endMarkerIndex);
|
|
81
|
+
|
|
82
|
+
return beforeEnd + newContextBlock + '\n' + afterEnd;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Create a context block without the full wrapper (for inserting into existing wrapper)
|
|
87
|
+
*/
|
|
88
|
+
protected createContextBlock(content: string, contextType: string): string {
|
|
89
|
+
return `<${contextType}>
|
|
90
|
+
${content}
|
|
91
|
+
</${contextType}>`;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Append content to a message with SYSTEM CONTEXT wrapper
|
|
96
|
+
*/
|
|
97
|
+
protected appendToMessage(message: Message, content: string, contextType: string): Message {
|
|
98
|
+
const currentContent = message.content;
|
|
99
|
+
|
|
100
|
+
// Handle string content
|
|
101
|
+
if (typeof currentContent === 'string') {
|
|
102
|
+
let newContent: string;
|
|
103
|
+
|
|
104
|
+
if (this.hasSystemContextWrapper(currentContent)) {
|
|
105
|
+
// Insert into existing wrapper
|
|
106
|
+
const contextBlock = this.createContextBlock(content, contextType);
|
|
107
|
+
newContent = this.insertIntoExistingWrapper(currentContent, contextBlock);
|
|
108
|
+
} else {
|
|
109
|
+
// Create new wrapper
|
|
110
|
+
newContent = currentContent + '\n\n' + this.wrapWithSystemContext(content, contextType);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return {
|
|
114
|
+
...message,
|
|
115
|
+
content: newContent,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Handle array content (multimodal messages)
|
|
120
|
+
if (Array.isArray(currentContent)) {
|
|
121
|
+
const lastTextIndex = currentContent.findLastIndex((part: any) => part.type === 'text');
|
|
122
|
+
|
|
123
|
+
if (lastTextIndex !== -1) {
|
|
124
|
+
const newContent = [...currentContent];
|
|
125
|
+
const existingText = newContent[lastTextIndex].text;
|
|
126
|
+
let updatedText: string;
|
|
127
|
+
|
|
128
|
+
if (this.hasSystemContextWrapper(existingText)) {
|
|
129
|
+
// Insert into existing wrapper
|
|
130
|
+
const contextBlock = this.createContextBlock(content, contextType);
|
|
131
|
+
updatedText = this.insertIntoExistingWrapper(existingText, contextBlock);
|
|
132
|
+
} else {
|
|
133
|
+
// Create new wrapper
|
|
134
|
+
updatedText = existingText + '\n\n' + this.wrapWithSystemContext(content, contextType);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
newContent[lastTextIndex] = {
|
|
138
|
+
...newContent[lastTextIndex],
|
|
139
|
+
text: updatedText,
|
|
140
|
+
};
|
|
141
|
+
return {
|
|
142
|
+
...message,
|
|
143
|
+
content: newContent,
|
|
144
|
+
};
|
|
145
|
+
} else {
|
|
146
|
+
// No text part found, add a new one with wrapper
|
|
147
|
+
return {
|
|
148
|
+
...message,
|
|
149
|
+
content: [
|
|
150
|
+
...currentContent,
|
|
151
|
+
{ text: this.wrapWithSystemContext(content, contextType), type: 'text' },
|
|
152
|
+
],
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return message;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Find the index of the last user message
|
|
162
|
+
*/
|
|
163
|
+
protected findLastUserMessageIndex(messages: Message[]): number {
|
|
164
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
165
|
+
if (messages[i].role === 'user') {
|
|
166
|
+
return i;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return -1;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Process the context by injecting content to every user message
|
|
174
|
+
*/
|
|
175
|
+
protected async doProcess(context: PipelineContext): Promise<PipelineContext> {
|
|
176
|
+
const clonedContext = this.cloneContext(context);
|
|
177
|
+
const lastUserIndex = this.findLastUserMessageIndex(clonedContext.messages);
|
|
178
|
+
let injectCount = 0;
|
|
179
|
+
|
|
180
|
+
// Iterate through all messages
|
|
181
|
+
for (let i = 0; i < clonedContext.messages.length; i++) {
|
|
182
|
+
const message = clonedContext.messages[i];
|
|
183
|
+
|
|
184
|
+
// Only process user messages
|
|
185
|
+
if (message.role !== 'user') continue;
|
|
186
|
+
|
|
187
|
+
const isLastUser = i === lastUserIndex;
|
|
188
|
+
const result = this.buildContentForMessage(message, i, isLastUser);
|
|
189
|
+
|
|
190
|
+
if (!result) continue;
|
|
191
|
+
|
|
192
|
+
// Append to this user message with SYSTEM CONTEXT wrapper
|
|
193
|
+
clonedContext.messages[i] = this.appendToMessage(message, result.content, result.contextType);
|
|
194
|
+
injectCount++;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Update metadata with injection count
|
|
198
|
+
if (injectCount > 0) {
|
|
199
|
+
clonedContext.metadata[`${this.name}InjectedCount`] = injectCount;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
return this.markAsExecuted(clonedContext);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
@@ -1,13 +1,6 @@
|
|
|
1
1
|
import type { Message, PipelineContext, ProcessorOptions } from '../types';
|
|
2
2
|
import { BaseProcessor } from './BaseProcessor';
|
|
3
|
-
|
|
4
|
-
const SYSTEM_CONTEXT_START = '<!-- SYSTEM CONTEXT (NOT PART OF USER QUERY) -->';
|
|
5
|
-
const SYSTEM_CONTEXT_END = '<!-- END SYSTEM CONTEXT -->';
|
|
6
|
-
const CONTEXT_INSTRUCTION = `<context.instruction>following part contains context information injected by the system. Please follow these instructions:
|
|
7
|
-
|
|
8
|
-
1. Always prioritize handling user-visible content.
|
|
9
|
-
2. the context is only required when user's queries rely on it.
|
|
10
|
-
</context.instruction>`;
|
|
3
|
+
import { CONTEXT_INSTRUCTION, SYSTEM_CONTEXT_END, SYSTEM_CONTEXT_START } from './constants';
|
|
11
4
|
|
|
12
5
|
/**
|
|
13
6
|
* Base Provider for appending content to the last user message
|