@lobehub/lobehub 2.0.0-next.256 → 2.0.0-next.258
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/changelog/v1.json +18 -0
- package/package.json +1 -1
- package/packages/builtin-tool-memory/package.json +2 -1
- package/packages/builtin-tool-memory/src/executor/index.ts +2 -30
- package/packages/prompts/src/prompts/userMemory/__snapshots__/formatSearchResults.test.ts.snap +65 -0
- package/packages/prompts/src/prompts/userMemory/formatSearchResults.test.ts +200 -0
- package/packages/prompts/src/prompts/userMemory/formatSearchResults.ts +164 -0
- package/packages/prompts/src/prompts/userMemory/index.ts +2 -0
- package/src/features/Conversation/Messages/AssistantGroup/Tool/Inspector/ToolTitle.tsx +0 -9
- package/src/features/PageEditor/Copilot/index.tsx +15 -1
- package/src/features/RightPanel/index.tsx +11 -2
- package/src/store/chat/slices/portal/initialState.ts +10 -10
- package/src/store/chat/slices/portal/selectors.ts +1 -1
- package/src/store/document/slices/editor/index.ts +1 -1
- package/src/store/global/initialState.ts +2 -0
- package/src/store/global/selectors/systemStatus.ts +2 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,56 @@
|
|
|
2
2
|
|
|
3
3
|
# Changelog
|
|
4
4
|
|
|
5
|
+
## [Version 2.0.0-next.258](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.257...v2.0.0-next.258)
|
|
6
|
+
|
|
7
|
+
<sup>Released on **2026-01-10**</sup>
|
|
8
|
+
|
|
9
|
+
#### 🐛 Bug Fixes
|
|
10
|
+
|
|
11
|
+
- **misc**: Fix memory search context.
|
|
12
|
+
|
|
13
|
+
<br/>
|
|
14
|
+
|
|
15
|
+
<details>
|
|
16
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
17
|
+
|
|
18
|
+
#### What's fixed
|
|
19
|
+
|
|
20
|
+
- **misc**: Fix memory search context, closes [#11393](https://github.com/lobehub/lobe-chat/issues/11393) ([9f51a4c](https://github.com/lobehub/lobe-chat/commit/9f51a4c))
|
|
21
|
+
|
|
22
|
+
</details>
|
|
23
|
+
|
|
24
|
+
<div align="right">
|
|
25
|
+
|
|
26
|
+
[](#readme-top)
|
|
27
|
+
|
|
28
|
+
</div>
|
|
29
|
+
|
|
30
|
+
## [Version 2.0.0-next.257](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.256...v2.0.0-next.257)
|
|
31
|
+
|
|
32
|
+
<sup>Released on **2026-01-10**</sup>
|
|
33
|
+
|
|
34
|
+
#### 💄 Styles
|
|
35
|
+
|
|
36
|
+
- **misc**: Remember page agent panel width.
|
|
37
|
+
|
|
38
|
+
<br/>
|
|
39
|
+
|
|
40
|
+
<details>
|
|
41
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
42
|
+
|
|
43
|
+
#### Styles
|
|
44
|
+
|
|
45
|
+
- **misc**: Remember page agent panel width, closes [#11389](https://github.com/lobehub/lobe-chat/issues/11389) ([801b624](https://github.com/lobehub/lobe-chat/commit/801b624))
|
|
46
|
+
|
|
47
|
+
</details>
|
|
48
|
+
|
|
49
|
+
<div align="right">
|
|
50
|
+
|
|
51
|
+
[](#readme-top)
|
|
52
|
+
|
|
53
|
+
</div>
|
|
54
|
+
|
|
5
55
|
## [Version 2.0.0-next.256](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.255...v2.0.0-next.256)
|
|
6
56
|
|
|
7
57
|
<sup>Released on **2026-01-10**</sup>
|
package/changelog/v1.json
CHANGED
|
@@ -1,4 +1,22 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"children": {
|
|
4
|
+
"fixes": [
|
|
5
|
+
"Fix memory search context."
|
|
6
|
+
]
|
|
7
|
+
},
|
|
8
|
+
"date": "2026-01-10",
|
|
9
|
+
"version": "2.0.0-next.258"
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"children": {
|
|
13
|
+
"improvements": [
|
|
14
|
+
"Remember page agent panel width."
|
|
15
|
+
]
|
|
16
|
+
},
|
|
17
|
+
"date": "2026-01-10",
|
|
18
|
+
"version": "2.0.0-next.257"
|
|
19
|
+
},
|
|
2
20
|
{
|
|
3
21
|
"children": {
|
|
4
22
|
"improvements": [
|
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.258",
|
|
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",
|
|
@@ -6,6 +6,7 @@ import type {
|
|
|
6
6
|
RemoveIdentityActionSchema,
|
|
7
7
|
UpdateIdentityActionSchema,
|
|
8
8
|
} from '@lobechat/memory-user-memory/schemas';
|
|
9
|
+
import { formatMemorySearchResults } from '@lobechat/prompts';
|
|
9
10
|
import { BaseExecutor, type BuiltinToolResult, SearchMemoryParams } from '@lobechat/types';
|
|
10
11
|
import type { z } from 'zod';
|
|
11
12
|
|
|
@@ -14,35 +15,6 @@ import { userMemoryService } from '@/services/userMemory';
|
|
|
14
15
|
import { MemoryIdentifier } from '../manifest';
|
|
15
16
|
import { MemoryApiName } from '../types';
|
|
16
17
|
|
|
17
|
-
/**
|
|
18
|
-
* Format search results into human-readable summary
|
|
19
|
-
*/
|
|
20
|
-
const formatSearchResultsSummary = (result: {
|
|
21
|
-
contexts: unknown[];
|
|
22
|
-
experiences: unknown[];
|
|
23
|
-
preferences: unknown[];
|
|
24
|
-
}): string => {
|
|
25
|
-
const total = result.contexts.length + result.experiences.length + result.preferences.length;
|
|
26
|
-
|
|
27
|
-
if (total === 0) {
|
|
28
|
-
return '🔍 No memories found matching the query.';
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const parts: string[] = [`🔍 Found ${total} memories:`];
|
|
32
|
-
|
|
33
|
-
if (result.contexts.length > 0) {
|
|
34
|
-
parts.push(`- ${result.contexts.length} context memories`);
|
|
35
|
-
}
|
|
36
|
-
if (result.experiences.length > 0) {
|
|
37
|
-
parts.push(`- ${result.experiences.length} experience memories`);
|
|
38
|
-
}
|
|
39
|
-
if (result.preferences.length > 0) {
|
|
40
|
-
parts.push(`- ${result.preferences.length} preference memories`);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
return parts.join('\n');
|
|
44
|
-
};
|
|
45
|
-
|
|
46
18
|
/**
|
|
47
19
|
* Memory Tool Executor
|
|
48
20
|
*
|
|
@@ -62,7 +34,7 @@ class MemoryExecutor extends BaseExecutor<typeof MemoryApiName> {
|
|
|
62
34
|
const result = await userMemoryService.searchMemory(params);
|
|
63
35
|
|
|
64
36
|
return {
|
|
65
|
-
content:
|
|
37
|
+
content: formatMemorySearchResults({ query: params.query, results: result }),
|
|
66
38
|
state: result,
|
|
67
39
|
success: true,
|
|
68
40
|
};
|
package/packages/prompts/src/prompts/userMemory/__snapshots__/formatSearchResults.test.ts.snap
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
|
2
|
+
|
|
3
|
+
exports[`formatMemorySearchResults > should format context memories with full content 1`] = `
|
|
4
|
+
"<memories query="project" total="1">
|
|
5
|
+
<contexts count="1">
|
|
6
|
+
<context id="ctx-1" title="Web App Project" urgency=7 impact=8 type="project" status="in-progress">
|
|
7
|
+
Building a new web application
|
|
8
|
+
<subjects>John (person)</subjects>
|
|
9
|
+
<objects>React (application)</objects>
|
|
10
|
+
</context>
|
|
11
|
+
</contexts>
|
|
12
|
+
</memories>"
|
|
13
|
+
`;
|
|
14
|
+
|
|
15
|
+
exports[`formatMemorySearchResults > should format experience memories with full content 1`] = `
|
|
16
|
+
"<memories query="debugging" total="1">
|
|
17
|
+
<experiences count="1">
|
|
18
|
+
<experience id="exp-1" type="lesson" confidence=9>
|
|
19
|
+
<situation>Debugging complex state issues</situation>
|
|
20
|
+
<keyLearning>Breakpoints save time in complex debugging</keyLearning>
|
|
21
|
+
</experience>
|
|
22
|
+
</experiences>
|
|
23
|
+
</memories>"
|
|
24
|
+
`;
|
|
25
|
+
|
|
26
|
+
exports[`formatMemorySearchResults > should format mixed results with all memory types 1`] = `
|
|
27
|
+
"<memories query="work" total="3">
|
|
28
|
+
<contexts count="1">
|
|
29
|
+
<context id="ctx-1" title="Context Title">
|
|
30
|
+
Context description
|
|
31
|
+
</context>
|
|
32
|
+
</contexts>
|
|
33
|
+
<experiences count="1">
|
|
34
|
+
<experience id="exp-1">
|
|
35
|
+
<situation>Situation</situation>
|
|
36
|
+
<keyLearning>Key learning</keyLearning>
|
|
37
|
+
</experience>
|
|
38
|
+
</experiences>
|
|
39
|
+
<preferences count="1">
|
|
40
|
+
<preference id="pref-1">Directive</preference>
|
|
41
|
+
</preferences>
|
|
42
|
+
</memories>"
|
|
43
|
+
`;
|
|
44
|
+
|
|
45
|
+
exports[`formatMemorySearchResults > should format preference memories with full content 1`] = `
|
|
46
|
+
"<memories query="code style" total="1">
|
|
47
|
+
<preferences count="1">
|
|
48
|
+
<preference id="pref-1" type="coding-standard" priority=10>Always use TypeScript strict mode</preference>
|
|
49
|
+
</preferences>
|
|
50
|
+
</memories>"
|
|
51
|
+
`;
|
|
52
|
+
|
|
53
|
+
exports[`formatMemorySearchResults > should handle null and undefined values gracefully 1`] = `
|
|
54
|
+
"<memories query="test" total="1">
|
|
55
|
+
<contexts count="1">
|
|
56
|
+
<context id="ctx-1"></context>
|
|
57
|
+
</contexts>
|
|
58
|
+
</memories>"
|
|
59
|
+
`;
|
|
60
|
+
|
|
61
|
+
exports[`formatMemorySearchResults > should return empty results message when no memories found 1`] = `
|
|
62
|
+
"<memories query="test query">
|
|
63
|
+
<status>No memories found matching the query.</status>
|
|
64
|
+
</memories>"
|
|
65
|
+
`;
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import { UserMemoryContextObjectType, UserMemoryContextSubjectType } from '@lobechat/types';
|
|
2
|
+
import { describe, expect, it } from 'vitest';
|
|
3
|
+
|
|
4
|
+
import { formatMemorySearchResults } from './formatSearchResults';
|
|
5
|
+
|
|
6
|
+
describe('formatMemorySearchResults', () => {
|
|
7
|
+
it('should return empty results message when no memories found', () => {
|
|
8
|
+
const result = formatMemorySearchResults({
|
|
9
|
+
query: 'test query',
|
|
10
|
+
results: {
|
|
11
|
+
contexts: [],
|
|
12
|
+
experiences: [],
|
|
13
|
+
preferences: [],
|
|
14
|
+
},
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
expect(result).toMatchSnapshot();
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('should format context memories with full content', () => {
|
|
21
|
+
const result = formatMemorySearchResults({
|
|
22
|
+
query: 'project',
|
|
23
|
+
results: {
|
|
24
|
+
contexts: [
|
|
25
|
+
{
|
|
26
|
+
accessedAt: new Date('2024-01-01'),
|
|
27
|
+
associatedObjects: [{ name: 'React', type: UserMemoryContextObjectType.Application }],
|
|
28
|
+
associatedSubjects: [{ name: 'John', type: UserMemoryContextSubjectType.Person }],
|
|
29
|
+
createdAt: new Date('2024-01-01'),
|
|
30
|
+
currentStatus: 'in-progress',
|
|
31
|
+
description: 'Building a new web application',
|
|
32
|
+
id: 'ctx-1',
|
|
33
|
+
metadata: null,
|
|
34
|
+
scoreImpact: 8,
|
|
35
|
+
scoreUrgency: 7,
|
|
36
|
+
tags: ['frontend', 'react'],
|
|
37
|
+
title: 'Web App Project',
|
|
38
|
+
type: 'project',
|
|
39
|
+
updatedAt: new Date('2024-01-01'),
|
|
40
|
+
userMemoryIds: null,
|
|
41
|
+
},
|
|
42
|
+
],
|
|
43
|
+
experiences: [],
|
|
44
|
+
preferences: [],
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
expect(result).toMatchSnapshot();
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('should format experience memories with full content', () => {
|
|
52
|
+
const result = formatMemorySearchResults({
|
|
53
|
+
query: 'debugging',
|
|
54
|
+
results: {
|
|
55
|
+
contexts: [],
|
|
56
|
+
experiences: [
|
|
57
|
+
{
|
|
58
|
+
accessedAt: new Date('2024-01-01'),
|
|
59
|
+
action: 'Used breakpoints instead of console.log',
|
|
60
|
+
createdAt: new Date('2024-01-01'),
|
|
61
|
+
id: 'exp-1',
|
|
62
|
+
keyLearning: 'Breakpoints save time in complex debugging',
|
|
63
|
+
metadata: null,
|
|
64
|
+
possibleOutcome: 'Faster debugging sessions',
|
|
65
|
+
reasoning: 'Console logs clutter the code',
|
|
66
|
+
scoreConfidence: 9,
|
|
67
|
+
situation: 'Debugging complex state issues',
|
|
68
|
+
tags: ['debugging', 'best-practice'],
|
|
69
|
+
type: 'lesson',
|
|
70
|
+
updatedAt: new Date('2024-01-01'),
|
|
71
|
+
userMemoryId: null,
|
|
72
|
+
},
|
|
73
|
+
],
|
|
74
|
+
preferences: [],
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
expect(result).toMatchSnapshot();
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('should format preference memories with full content', () => {
|
|
82
|
+
const result = formatMemorySearchResults({
|
|
83
|
+
query: 'code style',
|
|
84
|
+
results: {
|
|
85
|
+
contexts: [],
|
|
86
|
+
experiences: [],
|
|
87
|
+
preferences: [
|
|
88
|
+
{
|
|
89
|
+
accessedAt: new Date('2024-01-01'),
|
|
90
|
+
conclusionDirectives: 'Always use TypeScript strict mode',
|
|
91
|
+
createdAt: new Date('2024-01-01'),
|
|
92
|
+
id: 'pref-1',
|
|
93
|
+
metadata: null,
|
|
94
|
+
scorePriority: 10,
|
|
95
|
+
suggestions: 'Consider adding eslint rules',
|
|
96
|
+
tags: ['typescript', 'code-quality'],
|
|
97
|
+
type: 'coding-standard',
|
|
98
|
+
updatedAt: new Date('2024-01-01'),
|
|
99
|
+
userMemoryId: null,
|
|
100
|
+
},
|
|
101
|
+
],
|
|
102
|
+
},
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
expect(result).toMatchSnapshot();
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it('should format mixed results with all memory types', () => {
|
|
109
|
+
const result = formatMemorySearchResults({
|
|
110
|
+
query: 'work',
|
|
111
|
+
results: {
|
|
112
|
+
contexts: [
|
|
113
|
+
{
|
|
114
|
+
accessedAt: new Date('2024-01-01'),
|
|
115
|
+
associatedObjects: null,
|
|
116
|
+
associatedSubjects: null,
|
|
117
|
+
createdAt: new Date('2024-01-01'),
|
|
118
|
+
currentStatus: null,
|
|
119
|
+
description: 'Context description',
|
|
120
|
+
id: 'ctx-1',
|
|
121
|
+
metadata: null,
|
|
122
|
+
scoreImpact: null,
|
|
123
|
+
scoreUrgency: null,
|
|
124
|
+
tags: null,
|
|
125
|
+
title: 'Context Title',
|
|
126
|
+
type: null,
|
|
127
|
+
updatedAt: new Date('2024-01-01'),
|
|
128
|
+
userMemoryIds: null,
|
|
129
|
+
},
|
|
130
|
+
],
|
|
131
|
+
experiences: [
|
|
132
|
+
{
|
|
133
|
+
accessedAt: new Date('2024-01-01'),
|
|
134
|
+
action: null,
|
|
135
|
+
createdAt: new Date('2024-01-01'),
|
|
136
|
+
id: 'exp-1',
|
|
137
|
+
keyLearning: 'Key learning',
|
|
138
|
+
metadata: null,
|
|
139
|
+
possibleOutcome: null,
|
|
140
|
+
reasoning: null,
|
|
141
|
+
scoreConfidence: null,
|
|
142
|
+
situation: 'Situation',
|
|
143
|
+
tags: null,
|
|
144
|
+
type: null,
|
|
145
|
+
updatedAt: new Date('2024-01-01'),
|
|
146
|
+
userMemoryId: null,
|
|
147
|
+
},
|
|
148
|
+
],
|
|
149
|
+
preferences: [
|
|
150
|
+
{
|
|
151
|
+
accessedAt: new Date('2024-01-01'),
|
|
152
|
+
conclusionDirectives: 'Directive',
|
|
153
|
+
createdAt: new Date('2024-01-01'),
|
|
154
|
+
id: 'pref-1',
|
|
155
|
+
metadata: null,
|
|
156
|
+
scorePriority: null,
|
|
157
|
+
suggestions: null,
|
|
158
|
+
tags: null,
|
|
159
|
+
type: null,
|
|
160
|
+
updatedAt: new Date('2024-01-01'),
|
|
161
|
+
userMemoryId: null,
|
|
162
|
+
},
|
|
163
|
+
],
|
|
164
|
+
},
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
expect(result).toMatchSnapshot();
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it('should handle null and undefined values gracefully', () => {
|
|
171
|
+
const result = formatMemorySearchResults({
|
|
172
|
+
query: 'test',
|
|
173
|
+
results: {
|
|
174
|
+
contexts: [
|
|
175
|
+
{
|
|
176
|
+
accessedAt: new Date('2024-01-01'),
|
|
177
|
+
associatedObjects: null,
|
|
178
|
+
associatedSubjects: null,
|
|
179
|
+
createdAt: new Date('2024-01-01'),
|
|
180
|
+
currentStatus: null,
|
|
181
|
+
description: null,
|
|
182
|
+
id: 'ctx-1',
|
|
183
|
+
metadata: null,
|
|
184
|
+
scoreImpact: null,
|
|
185
|
+
scoreUrgency: null,
|
|
186
|
+
tags: null,
|
|
187
|
+
title: null,
|
|
188
|
+
type: null,
|
|
189
|
+
updatedAt: new Date('2024-01-01'),
|
|
190
|
+
userMemoryIds: null,
|
|
191
|
+
},
|
|
192
|
+
],
|
|
193
|
+
experiences: [],
|
|
194
|
+
preferences: [],
|
|
195
|
+
},
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
expect(result).toMatchSnapshot();
|
|
199
|
+
});
|
|
200
|
+
});
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import type { SearchMemoryResult } from '@lobechat/types';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Search result item interfaces matching the SearchMemoryResult type
|
|
5
|
+
*/
|
|
6
|
+
type ContextResult = SearchMemoryResult['contexts'][number];
|
|
7
|
+
type ExperienceResult = SearchMemoryResult['experiences'][number];
|
|
8
|
+
type PreferenceResult = SearchMemoryResult['preferences'][number];
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Format a single context memory item for search results
|
|
12
|
+
* Format: attributes for metadata, description as text content
|
|
13
|
+
*/
|
|
14
|
+
const formatContextResult = (item: ContextResult): string => {
|
|
15
|
+
const attrs: string[] = [`id="${item.id}"`];
|
|
16
|
+
|
|
17
|
+
if (item.title) {
|
|
18
|
+
attrs.push(`title="${item.title}"`);
|
|
19
|
+
}
|
|
20
|
+
if (item.scoreUrgency !== null && item.scoreUrgency !== undefined) {
|
|
21
|
+
attrs.push(`urgency=${item.scoreUrgency}`);
|
|
22
|
+
}
|
|
23
|
+
if (item.scoreImpact !== null && item.scoreImpact !== undefined) {
|
|
24
|
+
attrs.push(`impact=${item.scoreImpact}`);
|
|
25
|
+
}
|
|
26
|
+
if (item.type) {
|
|
27
|
+
attrs.push(`type="${item.type}"`);
|
|
28
|
+
}
|
|
29
|
+
if (item.currentStatus) {
|
|
30
|
+
attrs.push(`status="${item.currentStatus}"`);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const children: string[] = [];
|
|
34
|
+
|
|
35
|
+
// Description as main text content
|
|
36
|
+
if (item.description) {
|
|
37
|
+
children.push(` ${item.description}`);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Associated subjects (actors)
|
|
41
|
+
if (item.associatedSubjects && item.associatedSubjects.length > 0) {
|
|
42
|
+
const subjects = item.associatedSubjects
|
|
43
|
+
.filter((s) => s?.name)
|
|
44
|
+
.map((s) => `${s.name}${s.type ? ` (${s.type})` : ''}`)
|
|
45
|
+
.join(', ');
|
|
46
|
+
if (subjects) {
|
|
47
|
+
children.push(` <subjects>${subjects}</subjects>`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Associated objects (resources)
|
|
52
|
+
if (item.associatedObjects && item.associatedObjects.length > 0) {
|
|
53
|
+
const objects = item.associatedObjects
|
|
54
|
+
.filter((o) => o?.name)
|
|
55
|
+
.map((o) => `${o.name}${o.type ? ` (${o.type})` : ''}`)
|
|
56
|
+
.join(', ');
|
|
57
|
+
if (objects) {
|
|
58
|
+
children.push(` <objects>${objects}</objects>`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const content = children.length > 0 ? `\n${children.join('\n')}\n ` : '';
|
|
63
|
+
|
|
64
|
+
return ` <context ${attrs.join(' ')}>${content}</context>`;
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Format a single experience memory item for search results
|
|
69
|
+
* Format: attributes for metadata, situation and keyLearning as child elements
|
|
70
|
+
*/
|
|
71
|
+
const formatExperienceResult = (item: ExperienceResult): string => {
|
|
72
|
+
const attrs: string[] = [`id="${item.id}"`];
|
|
73
|
+
|
|
74
|
+
if (item.type) {
|
|
75
|
+
attrs.push(`type="${item.type}"`);
|
|
76
|
+
}
|
|
77
|
+
if (item.scoreConfidence !== null && item.scoreConfidence !== undefined) {
|
|
78
|
+
attrs.push(`confidence=${item.scoreConfidence}`);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const children: string[] = [];
|
|
82
|
+
|
|
83
|
+
if (item.situation) {
|
|
84
|
+
children.push(` <situation>${item.situation}</situation>`);
|
|
85
|
+
}
|
|
86
|
+
if (item.keyLearning) {
|
|
87
|
+
children.push(` <keyLearning>${item.keyLearning}</keyLearning>`);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const content = children.length > 0 ? `\n${children.join('\n')}\n ` : '';
|
|
91
|
+
|
|
92
|
+
return ` <experience ${attrs.join(' ')}>${content}</experience>`;
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Format a single preference memory item for search results
|
|
97
|
+
* Format: attributes for metadata, directives as text content
|
|
98
|
+
*/
|
|
99
|
+
const formatPreferenceResult = (item: PreferenceResult): string => {
|
|
100
|
+
const attrs: string[] = [`id="${item.id}"`];
|
|
101
|
+
|
|
102
|
+
if (item.type) {
|
|
103
|
+
attrs.push(`type="${item.type}"`);
|
|
104
|
+
}
|
|
105
|
+
if (item.scorePriority !== null && item.scorePriority !== undefined) {
|
|
106
|
+
attrs.push(`priority=${item.scorePriority}`);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const content = item.conclusionDirectives || '';
|
|
110
|
+
|
|
111
|
+
return ` <preference ${attrs.join(' ')}>${content}</preference>`;
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
export interface FormatSearchResultsOptions {
|
|
115
|
+
/** The search query that was used */
|
|
116
|
+
query: string;
|
|
117
|
+
/** The search results to format */
|
|
118
|
+
results: SearchMemoryResult;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Format memory search results as XML for LLM consumption.
|
|
123
|
+
*
|
|
124
|
+
* This function formats the complete search results with all content details,
|
|
125
|
+
* making the retrieved memories directly usable by the LLM for reasoning
|
|
126
|
+
* and response generation.
|
|
127
|
+
*/
|
|
128
|
+
export const formatMemorySearchResults = ({
|
|
129
|
+
query,
|
|
130
|
+
results,
|
|
131
|
+
}: FormatSearchResultsOptions): string => {
|
|
132
|
+
const { contexts, experiences, preferences } = results;
|
|
133
|
+
const total = contexts.length + experiences.length + preferences.length;
|
|
134
|
+
|
|
135
|
+
if (total === 0) {
|
|
136
|
+
return `<memories query="${query}">
|
|
137
|
+
<status>No memories found matching the query.</status>
|
|
138
|
+
</memories>`;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const sections: string[] = [];
|
|
142
|
+
|
|
143
|
+
// Add contexts section
|
|
144
|
+
if (contexts.length > 0) {
|
|
145
|
+
const contextsXml = contexts.map(formatContextResult).join('\n');
|
|
146
|
+
sections.push(`<contexts count="${contexts.length}">\n${contextsXml}\n</contexts>`);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Add experiences section
|
|
150
|
+
if (experiences.length > 0) {
|
|
151
|
+
const experiencesXml = experiences.map(formatExperienceResult).join('\n');
|
|
152
|
+
sections.push(`<experiences count="${experiences.length}">\n${experiencesXml}\n</experiences>`);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Add preferences section
|
|
156
|
+
if (preferences.length > 0) {
|
|
157
|
+
const preferencesXml = preferences.map(formatPreferenceResult).join('\n');
|
|
158
|
+
sections.push(`<preferences count="${preferences.length}">\n${preferencesXml}\n</preferences>`);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return `<memories query="${query}" total="${total}">
|
|
162
|
+
${sections.join('\n')}
|
|
163
|
+
</memories>`;
|
|
164
|
+
};
|
|
@@ -38,15 +38,6 @@ const ToolTitle = memo<ToolTitleProps>(({ identifier, apiName, isLoading, isAbor
|
|
|
38
38
|
const isBuiltinPlugin = builtinToolIdentifiers.includes(identifier);
|
|
39
39
|
const pluginTitle = pluginHelpers.getPluginTitle(pluginMeta) ?? t('unknownPlugin');
|
|
40
40
|
|
|
41
|
-
// Debug logging for LobeHub Skill title issue
|
|
42
|
-
console.log('[ToolTitle Debug]', {
|
|
43
|
-
apiName,
|
|
44
|
-
identifier,
|
|
45
|
-
isBuiltinPlugin,
|
|
46
|
-
pluginMeta,
|
|
47
|
-
pluginTitle,
|
|
48
|
-
});
|
|
49
|
-
|
|
50
41
|
return (
|
|
51
42
|
<div
|
|
52
43
|
className={cx(
|
|
@@ -5,6 +5,8 @@ import { memo } from 'react';
|
|
|
5
5
|
import RightPanel from '@/features/RightPanel';
|
|
6
6
|
import { useAgentStore } from '@/store/agent';
|
|
7
7
|
import { builtinAgentSelectors } from '@/store/agent/selectors';
|
|
8
|
+
import { useGlobalStore } from '@/store/global';
|
|
9
|
+
import { systemStatusSelectors } from '@/store/global/selectors';
|
|
8
10
|
|
|
9
11
|
import Conversation from './Conversation';
|
|
10
12
|
|
|
@@ -13,9 +15,21 @@ import Conversation from './Conversation';
|
|
|
13
15
|
*/
|
|
14
16
|
const Copilot = memo(() => {
|
|
15
17
|
const pageAgentId = useAgentStore(builtinAgentSelectors.pageAgentId);
|
|
18
|
+
const [width, updateSystemStatus] = useGlobalStore((s) => [
|
|
19
|
+
systemStatusSelectors.pageAgentPanelWidth(s),
|
|
20
|
+
s.updateSystemStatus,
|
|
21
|
+
]);
|
|
16
22
|
|
|
17
23
|
return (
|
|
18
|
-
<RightPanel
|
|
24
|
+
<RightPanel
|
|
25
|
+
defaultWidth={width}
|
|
26
|
+
onSizeChange={(size) => {
|
|
27
|
+
if (size?.width) {
|
|
28
|
+
const w = typeof size.width === 'string' ? Number.parseInt(size.width) : size.width;
|
|
29
|
+
if (!!w) updateSystemStatus({ pageAgentPanelWidth: w });
|
|
30
|
+
}
|
|
31
|
+
}}
|
|
32
|
+
>
|
|
19
33
|
<Conversation agentId={pageAgentId} />
|
|
20
34
|
</RightPanel>
|
|
21
35
|
);
|
|
@@ -6,15 +6,21 @@ import Loading from '@/components/Loading/BrandTextLoading';
|
|
|
6
6
|
import { useGlobalStore } from '@/store/global';
|
|
7
7
|
import { systemStatusSelectors } from '@/store/global/selectors';
|
|
8
8
|
|
|
9
|
+
export interface Size {
|
|
10
|
+
height?: string | number;
|
|
11
|
+
width?: string | number;
|
|
12
|
+
}
|
|
13
|
+
|
|
9
14
|
interface RightPanelProps extends Omit<
|
|
10
15
|
DraggablePanelProps,
|
|
11
16
|
'placement' | 'size' | 'onSizeChange' | 'onExpandChange'
|
|
12
17
|
> {
|
|
13
18
|
defaultWidth?: number | string;
|
|
19
|
+
onSizeChange?: (size?: Size) => void;
|
|
14
20
|
}
|
|
15
21
|
|
|
16
22
|
const RightPanel = memo<RightPanelProps>(
|
|
17
|
-
({ maxWidth = 600, minWidth = 300, children, defaultWidth = 360, ...rest }) => {
|
|
23
|
+
({ maxWidth = 600, minWidth = 300, children, defaultWidth = 360, onSizeChange, ...rest }) => {
|
|
18
24
|
const [showRightPanel, toggleRightPanel] = useGlobalStore((s) => [
|
|
19
25
|
systemStatusSelectors.showRightPanel(s),
|
|
20
26
|
s.toggleRightPanel,
|
|
@@ -31,7 +37,10 @@ const RightPanel = memo<RightPanelProps>(
|
|
|
31
37
|
minWidth={minWidth}
|
|
32
38
|
onExpandChange={(expand) => toggleRightPanel(expand)}
|
|
33
39
|
onSizeChange={(_, size) => {
|
|
34
|
-
if (size?.width)
|
|
40
|
+
if (size?.width) {
|
|
41
|
+
setWidth(size.width);
|
|
42
|
+
}
|
|
43
|
+
if (size) onSizeChange?.(size);
|
|
35
44
|
}}
|
|
36
45
|
placement="right"
|
|
37
46
|
size={{
|
|
@@ -16,7 +16,7 @@ export enum PortalViewType {
|
|
|
16
16
|
MessageDetail = 'messageDetail',
|
|
17
17
|
Notebook = 'notebook',
|
|
18
18
|
Thread = 'thread',
|
|
19
|
-
ToolUI = 'toolUI'
|
|
19
|
+
ToolUI = 'toolUI',
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
export interface PortalFile {
|
|
@@ -27,21 +27,21 @@ export interface PortalFile {
|
|
|
27
27
|
|
|
28
28
|
export type PortalViewData =
|
|
29
29
|
| { type: PortalViewType.Home }
|
|
30
|
-
| { artifact: PortalArtifact
|
|
31
|
-
| { documentId: string
|
|
30
|
+
| { artifact: PortalArtifact; type: PortalViewType.Artifact }
|
|
31
|
+
| { documentId: string; type: PortalViewType.Document }
|
|
32
32
|
| { type: PortalViewType.Notebook }
|
|
33
|
-
| { file: PortalFile
|
|
34
|
-
| { messageId: string
|
|
35
|
-
| { identifier: string
|
|
36
|
-
| { startMessageId?: string
|
|
37
|
-
| { agentId: string
|
|
33
|
+
| { file: PortalFile; type: PortalViewType.FilePreview }
|
|
34
|
+
| { messageId: string; type: PortalViewType.MessageDetail }
|
|
35
|
+
| { identifier: string; messageId: string; type: PortalViewType.ToolUI }
|
|
36
|
+
| { startMessageId?: string; threadId?: string; type: PortalViewType.Thread }
|
|
37
|
+
| { agentId: string; type: PortalViewType.GroupThread };
|
|
38
38
|
|
|
39
39
|
// ============== Portal State ==============
|
|
40
40
|
|
|
41
41
|
export interface ChatPortalState {
|
|
42
42
|
// Legacy fields (kept for backward compatibility during migration)
|
|
43
|
-
// TODO: Remove after Phase 3 migration complete
|
|
44
|
-
/** @deprecated Use portalStack instead */
|
|
43
|
+
// TODO: Remove after Phase 3 migration complete
|
|
44
|
+
/** @deprecated Use portalStack instead */
|
|
45
45
|
portalArtifact?: PortalArtifact;
|
|
46
46
|
portalArtifactDisplayMode: ArtifactDisplayMode;
|
|
47
47
|
/** @deprecated Use portalStack instead */
|
|
@@ -109,7 +109,7 @@ const messageDetailId = (s: ChatStoreState): string | undefined => {
|
|
|
109
109
|
// Tool UI / Plugin selectors
|
|
110
110
|
const currentToolUI = (
|
|
111
111
|
s: ChatStoreState,
|
|
112
|
-
): { identifier: string
|
|
112
|
+
): { identifier: string; messageId: string } | undefined => {
|
|
113
113
|
const view = getViewData(s, PortalViewType.ToolUI);
|
|
114
114
|
if (view) {
|
|
115
115
|
return { identifier: view.identifier, messageId: view.messageId };
|
|
@@ -122,6 +122,7 @@ export interface SystemStatus {
|
|
|
122
122
|
*/
|
|
123
123
|
modelSwitchPanelWidth?: number;
|
|
124
124
|
noWideScreen?: boolean;
|
|
125
|
+
pageAgentPanelWidth?: number;
|
|
125
126
|
/**
|
|
126
127
|
* number of pages (documents) to display per page
|
|
127
128
|
*/
|
|
@@ -198,6 +199,7 @@ export const INITIAL_STATUS = {
|
|
|
198
199
|
modelSwitchPanelGroupMode: 'byProvider',
|
|
199
200
|
modelSwitchPanelWidth: 430,
|
|
200
201
|
noWideScreen: true,
|
|
202
|
+
pageAgentPanelWidth: 360,
|
|
201
203
|
pagePageSize: 20,
|
|
202
204
|
portalWidth: 400,
|
|
203
205
|
resourceManagerColumnWidths: {
|
|
@@ -27,6 +27,7 @@ const language = (s: GlobalState) => s.status.language || 'auto';
|
|
|
27
27
|
const modelSwitchPanelGroupMode = (s: GlobalState) =>
|
|
28
28
|
s.status.modelSwitchPanelGroupMode || 'byProvider';
|
|
29
29
|
const modelSwitchPanelWidth = (s: GlobalState) => s.status.modelSwitchPanelWidth || 430;
|
|
30
|
+
const pageAgentPanelWidth = (s: GlobalState) => s.status.pageAgentPanelWidth || 360;
|
|
30
31
|
|
|
31
32
|
const showChatHeader = (s: GlobalState) => !s.status.zenMode;
|
|
32
33
|
const inZenMode = (s: GlobalState) => s.status.zenMode;
|
|
@@ -76,6 +77,7 @@ export const systemStatusSelectors = {
|
|
|
76
77
|
mobileShowTopic,
|
|
77
78
|
modelSwitchPanelGroupMode,
|
|
78
79
|
modelSwitchPanelWidth,
|
|
80
|
+
pageAgentPanelWidth,
|
|
79
81
|
pagePageSize,
|
|
80
82
|
portalWidth,
|
|
81
83
|
sessionGroupKeys,
|