@lobehub/lobehub 2.0.0-next.335 → 2.0.0-next.337
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 +59 -0
- package/changelog/v1.json +21 -0
- package/package.json +1 -1
- package/packages/builtin-tool-agent-builder/src/manifest.ts +0 -2
- package/packages/builtin-tool-group-management/src/manifest.ts +54 -53
- package/packages/builtin-tool-group-management/src/systemRole.ts +43 -111
- package/packages/builtin-tool-memory/src/client/Render/AddPreferenceMemory/index.tsx +17 -0
- package/packages/builtin-tool-memory/src/client/Render/index.ts +2 -0
- package/packages/builtin-tool-memory/src/client/Streaming/AddPreferenceMemory/index.tsx +17 -0
- package/packages/builtin-tool-memory/src/client/Streaming/index.ts +4 -3
- package/packages/builtin-tool-memory/src/client/components/PreferenceMemoryCard.tsx +357 -0
- package/packages/builtin-tool-memory/src/client/components/index.ts +1 -0
- package/packages/builtin-tool-memory/src/executor/index.ts +3 -3
- package/packages/builtin-tool-memory/src/systemRole.ts +1 -0
- package/packages/context-engine/src/engine/tools/ToolArgumentsRepairer.ts +129 -0
- package/packages/context-engine/src/engine/tools/__tests__/ToolArgumentsRepairer.test.ts +186 -0
- package/packages/context-engine/src/engine/tools/index.ts +3 -0
- package/packages/conversation-flow/src/__tests__/fixtures/inputs/tasks/index.ts +2 -0
- package/packages/conversation-flow/src/__tests__/fixtures/inputs/tasks/with-assistant-group.json +156 -0
- package/packages/conversation-flow/src/__tests__/parse.test.ts +22 -0
- package/packages/conversation-flow/src/transformation/FlatListBuilder.ts +88 -11
- package/packages/database/src/models/userMemory/model.ts +1 -1
- package/packages/memory-user-memory/src/extractors/context.test.ts +0 -1
- package/packages/memory-user-memory/src/extractors/experience.test.ts +0 -1
- package/packages/memory-user-memory/src/extractors/identity.test.ts +0 -1
- package/packages/memory-user-memory/src/extractors/preference.test.ts +0 -1
- package/packages/memory-user-memory/src/schemas/context.ts +0 -2
- package/packages/memory-user-memory/src/schemas/experience.ts +0 -2
- package/packages/memory-user-memory/src/schemas/identity.ts +1 -2
- package/packages/memory-user-memory/src/schemas/preference.ts +0 -2
- package/packages/types/src/openai/chat.ts +0 -4
- package/src/app/[variants]/(main)/community/(detail)/user/features/DetailProvider.tsx +5 -1
- package/src/app/[variants]/(main)/community/(detail)/user/features/UserAgentCard.tsx +8 -8
- package/src/app/[variants]/(main)/community/(detail)/user/features/UserGroupCard.tsx +142 -15
- package/src/app/[variants]/(main)/community/(detail)/user/features/useUserDetail.ts +45 -20
- package/src/server/routers/lambda/market/agentGroup.ts +179 -1
- package/src/server/routers/lambda/userMemories/tools.ts +5 -4
- package/src/server/routers/lambda/userMemories.ts +4 -4
- package/src/server/services/discover/index.ts +4 -0
- package/src/server/services/memory/userMemory/extract.ts +3 -3
- package/src/services/chat/chat.test.ts +109 -104
- package/src/services/chat/index.ts +13 -32
- package/src/services/chat/mecha/agentConfigResolver.test.ts +113 -0
- package/src/services/chat/mecha/agentConfigResolver.ts +15 -5
- package/src/services/marketApi.ts +14 -0
- package/src/store/chat/agents/__tests__/createAgentExecutors/helpers/testExecutor.ts +13 -0
- package/src/store/chat/agents/createAgentExecutors.ts +13 -1
- package/src/store/chat/slices/aiChat/actions/__tests__/conversationControl.test.ts +5 -1
- package/src/store/chat/slices/aiChat/actions/__tests__/fixtures.ts +14 -0
- package/src/store/chat/slices/aiChat/actions/__tests__/streamingExecutor.test.ts +131 -7
- package/src/store/chat/slices/aiChat/actions/streamingExecutor.ts +61 -62
- package/src/store/chat/slices/plugin/action.test.ts +71 -0
- package/src/store/chat/slices/plugin/actions/internals.ts +14 -5
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import { ToolArgumentsRepairer } from '../ToolArgumentsRepairer';
|
|
4
|
+
import type { LobeToolManifest } from '../types';
|
|
5
|
+
|
|
6
|
+
describe('ToolArgumentsRepairer', () => {
|
|
7
|
+
describe('repair - basic functionality', () => {
|
|
8
|
+
it('should return original parsed data when no schema provided', () => {
|
|
9
|
+
const repairer = new ToolArgumentsRepairer();
|
|
10
|
+
const parsed = { foo: 'bar' };
|
|
11
|
+
|
|
12
|
+
const result = repairer.repair(parsed);
|
|
13
|
+
|
|
14
|
+
expect(result).toEqual(parsed);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('should return original parsed data when schema has no required fields', () => {
|
|
18
|
+
const repairer = new ToolArgumentsRepairer();
|
|
19
|
+
const parsed = { foo: 'bar' };
|
|
20
|
+
const schema = { type: 'object', properties: { foo: { type: 'string' } } };
|
|
21
|
+
|
|
22
|
+
const result = repairer.repair(parsed, schema);
|
|
23
|
+
|
|
24
|
+
expect(result).toEqual(parsed);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('should return original parsed data when all required fields are present', () => {
|
|
28
|
+
const repairer = new ToolArgumentsRepairer();
|
|
29
|
+
const parsed = { description: 'test', instruction: 'do something' };
|
|
30
|
+
const schema = {
|
|
31
|
+
type: 'object',
|
|
32
|
+
required: ['description', 'instruction'],
|
|
33
|
+
properties: {
|
|
34
|
+
description: { type: 'string' },
|
|
35
|
+
instruction: { type: 'string' },
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const result = repairer.repair(parsed, schema);
|
|
40
|
+
|
|
41
|
+
expect(result).toEqual(parsed);
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
describe('repair - malformed JSON from LLM', () => {
|
|
46
|
+
it('should repair malformed JSON with escaped string issue', () => {
|
|
47
|
+
const repairer = new ToolArgumentsRepairer();
|
|
48
|
+
|
|
49
|
+
// This is the malformed data from haiku-4.5 model
|
|
50
|
+
// The entire JSON got stuffed into the "description" field with escaped quotes
|
|
51
|
+
const malformedParsed = {
|
|
52
|
+
description:
|
|
53
|
+
'Synthesize all 10 batch analyses into 10 most important themes for product builders", "instruction": "You have access to 10 batch analysis files", "runInClient": true, "timeout": 120000}',
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const schema = {
|
|
57
|
+
type: 'object',
|
|
58
|
+
required: ['description', 'instruction'],
|
|
59
|
+
properties: {
|
|
60
|
+
description: { type: 'string' },
|
|
61
|
+
instruction: { type: 'string' },
|
|
62
|
+
runInClient: { type: 'boolean' },
|
|
63
|
+
timeout: { type: 'number' },
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const result = repairer.repair(malformedParsed, schema);
|
|
68
|
+
|
|
69
|
+
expect(result).toHaveProperty('description');
|
|
70
|
+
expect(result).toHaveProperty('instruction');
|
|
71
|
+
expect(result).toHaveProperty('runInClient', true);
|
|
72
|
+
expect(result).toHaveProperty('timeout', 120000);
|
|
73
|
+
expect(result.description).toBe(
|
|
74
|
+
'Synthesize all 10 batch analyses into 10 most important themes for product builders',
|
|
75
|
+
);
|
|
76
|
+
expect(result.instruction).toBe('You have access to 10 batch analysis files');
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('should return original data if repair fails', () => {
|
|
80
|
+
const repairer = new ToolArgumentsRepairer();
|
|
81
|
+
|
|
82
|
+
// Invalid malformed data that cannot be repaired
|
|
83
|
+
const malformedParsed = {
|
|
84
|
+
description: 'some text without proper escape pattern',
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const schema = {
|
|
88
|
+
type: 'object',
|
|
89
|
+
required: ['description', 'instruction'],
|
|
90
|
+
properties: {
|
|
91
|
+
description: { type: 'string' },
|
|
92
|
+
instruction: { type: 'string' },
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
const result = repairer.repair(malformedParsed, schema);
|
|
97
|
+
|
|
98
|
+
// Should return original since pattern doesn't match
|
|
99
|
+
expect(result).toEqual(malformedParsed);
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
describe('parse - integrated parsing and repair', () => {
|
|
104
|
+
it('should parse and repair arguments using manifest schema', () => {
|
|
105
|
+
const manifest: LobeToolManifest = {
|
|
106
|
+
identifier: 'lobe-gtd',
|
|
107
|
+
api: [
|
|
108
|
+
{
|
|
109
|
+
name: 'execTask',
|
|
110
|
+
description: 'Execute async task',
|
|
111
|
+
parameters: {
|
|
112
|
+
type: 'object',
|
|
113
|
+
required: ['description', 'instruction'],
|
|
114
|
+
properties: {
|
|
115
|
+
description: { type: 'string' },
|
|
116
|
+
instruction: { type: 'string' },
|
|
117
|
+
runInClient: { type: 'boolean' },
|
|
118
|
+
timeout: { type: 'number' },
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
],
|
|
123
|
+
type: 'builtin',
|
|
124
|
+
} as unknown as LobeToolManifest;
|
|
125
|
+
|
|
126
|
+
const repairer = new ToolArgumentsRepairer(manifest);
|
|
127
|
+
|
|
128
|
+
// Malformed arguments string
|
|
129
|
+
const malformedArguments = JSON.stringify({
|
|
130
|
+
description:
|
|
131
|
+
'Test task", "instruction": "Do something important", "runInClient": true, "timeout": 60000}',
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
const result = repairer.parse('execTask', malformedArguments);
|
|
135
|
+
|
|
136
|
+
expect(result).toHaveProperty('description', 'Test task');
|
|
137
|
+
expect(result).toHaveProperty('instruction', 'Do something important');
|
|
138
|
+
expect(result).toHaveProperty('runInClient', true);
|
|
139
|
+
expect(result).toHaveProperty('timeout', 60000);
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it('should handle normal arguments without repair needed', () => {
|
|
143
|
+
const manifest: LobeToolManifest = {
|
|
144
|
+
identifier: 'test-tool',
|
|
145
|
+
api: [
|
|
146
|
+
{
|
|
147
|
+
name: 'testApi',
|
|
148
|
+
description: 'Test API',
|
|
149
|
+
parameters: {
|
|
150
|
+
type: 'object',
|
|
151
|
+
required: ['name'],
|
|
152
|
+
properties: {
|
|
153
|
+
name: { type: 'string' },
|
|
154
|
+
},
|
|
155
|
+
},
|
|
156
|
+
},
|
|
157
|
+
],
|
|
158
|
+
type: 'default',
|
|
159
|
+
} as unknown as LobeToolManifest;
|
|
160
|
+
|
|
161
|
+
const repairer = new ToolArgumentsRepairer(manifest);
|
|
162
|
+
const normalArguments = JSON.stringify({ name: 'test value' });
|
|
163
|
+
|
|
164
|
+
const result = repairer.parse('testApi', normalArguments);
|
|
165
|
+
|
|
166
|
+
expect(result).toEqual({ name: 'test value' });
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it('should handle no manifest gracefully', () => {
|
|
170
|
+
const repairer = new ToolArgumentsRepairer();
|
|
171
|
+
const arguments_ = JSON.stringify({ foo: 'bar' });
|
|
172
|
+
|
|
173
|
+
const result = repairer.parse('unknownApi', arguments_);
|
|
174
|
+
|
|
175
|
+
expect(result).toEqual({ foo: 'bar' });
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
it('should handle invalid JSON gracefully', () => {
|
|
179
|
+
const repairer = new ToolArgumentsRepairer();
|
|
180
|
+
|
|
181
|
+
const result = repairer.parse('test', 'invalid json');
|
|
182
|
+
|
|
183
|
+
expect(result).toEqual({});
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
});
|
|
@@ -4,6 +4,9 @@ export { ToolsEngine } from './ToolsEngine';
|
|
|
4
4
|
// Tool Name Resolver
|
|
5
5
|
export { ToolNameResolver } from './ToolNameResolver';
|
|
6
6
|
|
|
7
|
+
// Tool Arguments Repairer
|
|
8
|
+
export { ToolArgumentsRepairer, type ToolParameterSchema } from './ToolArgumentsRepairer';
|
|
9
|
+
|
|
7
10
|
// Types and interfaces
|
|
8
11
|
export type {
|
|
9
12
|
FunctionCallChecker,
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import type { Message } from '../../../../types';
|
|
2
2
|
import multiTasksWithSummary from './multi-tasks-with-summary.json';
|
|
3
3
|
import simple from './simple.json';
|
|
4
|
+
import withAssistantGroup from './with-assistant-group.json';
|
|
4
5
|
import withSummary from './with-summary.json';
|
|
5
6
|
|
|
6
7
|
export const tasks = {
|
|
7
8
|
multiTasksWithSummary: multiTasksWithSummary as Message[],
|
|
8
9
|
simple: simple as Message[],
|
|
10
|
+
withAssistantGroup: withAssistantGroup as Message[],
|
|
9
11
|
withSummary: withSummary as Message[],
|
|
10
12
|
};
|
package/packages/conversation-flow/src/__tests__/fixtures/inputs/tasks/with-assistant-group.json
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"id": "msg-user-1",
|
|
4
|
+
"role": "user",
|
|
5
|
+
"content": "Create three parallel tasks to research AI topics.",
|
|
6
|
+
"parentId": null,
|
|
7
|
+
"createdAt": 1735526559382,
|
|
8
|
+
"updatedAt": 1735526559382
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"id": "msg-assistant-1",
|
|
12
|
+
"role": "assistant",
|
|
13
|
+
"content": "Creating three parallel research tasks.",
|
|
14
|
+
"parentId": "msg-user-1",
|
|
15
|
+
"createdAt": 1735526560163,
|
|
16
|
+
"updatedAt": 1735526585550,
|
|
17
|
+
"model": "gpt-4",
|
|
18
|
+
"provider": "openai",
|
|
19
|
+
"tools": [
|
|
20
|
+
{
|
|
21
|
+
"id": "call_exec_tasks_1",
|
|
22
|
+
"type": "builtin",
|
|
23
|
+
"apiName": "execTasks",
|
|
24
|
+
"arguments": "{\"tasks\": [{\"description\": \"Research LLM architectures\"}, {\"description\": \"Research AI agents\"}, {\"description\": \"Research RAG systems\"}]}",
|
|
25
|
+
"identifier": "lobe-gtd"
|
|
26
|
+
}
|
|
27
|
+
]
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"id": "msg-tool-1",
|
|
31
|
+
"role": "tool",
|
|
32
|
+
"content": "Triggered 3 async tasks",
|
|
33
|
+
"parentId": "msg-assistant-1",
|
|
34
|
+
"tool_call_id": "call_exec_tasks_1",
|
|
35
|
+
"createdAt": 1735526588116,
|
|
36
|
+
"updatedAt": 1735526591337,
|
|
37
|
+
"pluginState": {
|
|
38
|
+
"type": "execTasks",
|
|
39
|
+
"tasks": [
|
|
40
|
+
{ "description": "Research LLM architectures" },
|
|
41
|
+
{ "description": "Research AI agents" },
|
|
42
|
+
{ "description": "Research RAG systems" }
|
|
43
|
+
],
|
|
44
|
+
"parentMessageId": "msg-tool-1"
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"id": "msg-task-llm",
|
|
49
|
+
"role": "task",
|
|
50
|
+
"content": "# LLM Architectures\n\nTransformer-based models dominate...",
|
|
51
|
+
"parentId": "msg-tool-1",
|
|
52
|
+
"createdAt": 1735526594643,
|
|
53
|
+
"updatedAt": 1735526756262,
|
|
54
|
+
"taskDetail": {
|
|
55
|
+
"duration": 120000,
|
|
56
|
+
"status": "completed",
|
|
57
|
+
"threadId": "thd_llm",
|
|
58
|
+
"title": "Research LLM architectures",
|
|
59
|
+
"totalCost": 0.015,
|
|
60
|
+
"totalMessages": 15,
|
|
61
|
+
"totalTokens": 100000
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
"id": "msg-task-agents",
|
|
66
|
+
"role": "task",
|
|
67
|
+
"content": "# AI Agents\n\nAutonomous agents using LLMs...",
|
|
68
|
+
"parentId": "msg-tool-1",
|
|
69
|
+
"createdAt": 1735526595647,
|
|
70
|
+
"updatedAt": 1735526792430,
|
|
71
|
+
"taskDetail": {
|
|
72
|
+
"duration": 150000,
|
|
73
|
+
"status": "completed",
|
|
74
|
+
"threadId": "thd_agents",
|
|
75
|
+
"title": "Research AI agents",
|
|
76
|
+
"totalCost": 0.018,
|
|
77
|
+
"totalMessages": 20,
|
|
78
|
+
"totalTokens": 120000
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
"id": "msg-task-rag",
|
|
83
|
+
"role": "task",
|
|
84
|
+
"content": "# RAG Systems\n\nRetrieval Augmented Generation combines...",
|
|
85
|
+
"parentId": "msg-tool-1",
|
|
86
|
+
"createdAt": 1735526596000,
|
|
87
|
+
"updatedAt": 1735526800000,
|
|
88
|
+
"taskDetail": {
|
|
89
|
+
"duration": 130000,
|
|
90
|
+
"status": "completed",
|
|
91
|
+
"threadId": "thd_rag",
|
|
92
|
+
"title": "Research RAG systems",
|
|
93
|
+
"totalCost": 0.016,
|
|
94
|
+
"totalMessages": 18,
|
|
95
|
+
"totalTokens": 110000
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
"id": "msg-assistant-after-task",
|
|
100
|
+
"role": "assistant",
|
|
101
|
+
"content": "All tasks completed. Let me check the analysis files created.",
|
|
102
|
+
"parentId": "msg-task-rag",
|
|
103
|
+
"createdAt": 1735526810000,
|
|
104
|
+
"updatedAt": 1735526815000,
|
|
105
|
+
"agentId": "agent-main",
|
|
106
|
+
"model": "gpt-4",
|
|
107
|
+
"provider": "openai",
|
|
108
|
+
"tools": [
|
|
109
|
+
{
|
|
110
|
+
"id": "call_list_files",
|
|
111
|
+
"type": "builtin",
|
|
112
|
+
"apiName": "listFiles",
|
|
113
|
+
"arguments": "{\"path\": \"/analysis\"}",
|
|
114
|
+
"identifier": "lobe-local-system"
|
|
115
|
+
}
|
|
116
|
+
],
|
|
117
|
+
"metadata": {
|
|
118
|
+
"totalInputTokens": 100,
|
|
119
|
+
"totalOutputTokens": 50,
|
|
120
|
+
"totalTokens": 150,
|
|
121
|
+
"cost": 0.001
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
"id": "msg-tool-list-files",
|
|
126
|
+
"role": "tool",
|
|
127
|
+
"content": "./ANALYSIS_1.md\n./ANALYSIS_2.md\n./ANALYSIS_3.md",
|
|
128
|
+
"parentId": "msg-assistant-after-task",
|
|
129
|
+
"tool_call_id": "call_list_files",
|
|
130
|
+
"createdAt": 1735526816000,
|
|
131
|
+
"updatedAt": 1735526817000,
|
|
132
|
+
"pluginState": {
|
|
133
|
+
"result": {
|
|
134
|
+
"output": "./ANALYSIS_1.md\n./ANALYSIS_2.md\n./ANALYSIS_3.md",
|
|
135
|
+
"success": true
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
"id": "msg-assistant-final",
|
|
141
|
+
"role": "assistant",
|
|
142
|
+
"content": "I found 3 analysis files. Let me summarize the findings for you.",
|
|
143
|
+
"parentId": "msg-tool-list-files",
|
|
144
|
+
"createdAt": 1735526820000,
|
|
145
|
+
"updatedAt": 1735526825000,
|
|
146
|
+
"agentId": "agent-main",
|
|
147
|
+
"model": "gpt-4",
|
|
148
|
+
"provider": "openai",
|
|
149
|
+
"metadata": {
|
|
150
|
+
"totalInputTokens": 200,
|
|
151
|
+
"totalOutputTokens": 80,
|
|
152
|
+
"totalTokens": 280,
|
|
153
|
+
"cost": 0.002
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
]
|
|
@@ -219,6 +219,28 @@ describe('parse', () => {
|
|
|
219
219
|
expect(result.flatList[3].id).toBe('msg-assistant-summary');
|
|
220
220
|
expect(result.flatList[3].content).toContain('All 10 tasks completed');
|
|
221
221
|
});
|
|
222
|
+
|
|
223
|
+
it('should merge assistant with tools after task into AssistantGroup', () => {
|
|
224
|
+
const result = parse(inputs.tasks.withAssistantGroup);
|
|
225
|
+
|
|
226
|
+
// The critical assertions:
|
|
227
|
+
// 1. flatList should have 4 items: user, assistantGroup(+tool), tasks(3 tasks), assistantGroup(with tool chain)
|
|
228
|
+
expect(result.flatList).toHaveLength(4);
|
|
229
|
+
expect(result.flatList[0].role).toBe('user');
|
|
230
|
+
expect(result.flatList[1].role).toBe('assistantGroup');
|
|
231
|
+
expect(result.flatList[2].role).toBe('tasks');
|
|
232
|
+
expect(result.flatList[3].role).toBe('assistantGroup');
|
|
233
|
+
|
|
234
|
+
// 2. The last assistantGroup should contain the full chain:
|
|
235
|
+
// - msg-assistant-after-task (with tool)
|
|
236
|
+
// - msg-assistant-final (without tool)
|
|
237
|
+
const lastGroup = result.flatList[3] as any;
|
|
238
|
+
expect(lastGroup.children).toHaveLength(2);
|
|
239
|
+
expect(lastGroup.children[0].id).toBe('msg-assistant-after-task');
|
|
240
|
+
expect(lastGroup.children[0].tools).toBeDefined();
|
|
241
|
+
expect(lastGroup.children[0].tools[0].result_msg_id).toBe('msg-tool-list-files');
|
|
242
|
+
expect(lastGroup.children[1].id).toBe('msg-assistant-final');
|
|
243
|
+
});
|
|
222
244
|
});
|
|
223
245
|
|
|
224
246
|
describe('Performance', () => {
|
|
@@ -107,9 +107,18 @@ export class FlatListBuilder {
|
|
|
107
107
|
if (!processedIds.has(nonTaskChildId)) {
|
|
108
108
|
const nonTaskChild = this.messageMap.get(nonTaskChildId);
|
|
109
109
|
if (nonTaskChild) {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
110
|
+
// Check if it's an AssistantGroup (assistant with tools)
|
|
111
|
+
if (
|
|
112
|
+
nonTaskChild.role === 'assistant' &&
|
|
113
|
+
nonTaskChild.tools &&
|
|
114
|
+
nonTaskChild.tools.length > 0
|
|
115
|
+
) {
|
|
116
|
+
this.processAssistantGroup(nonTaskChild, flatList, processedIds, allMessages);
|
|
117
|
+
} else {
|
|
118
|
+
flatList.push(nonTaskChild);
|
|
119
|
+
processedIds.add(nonTaskChildId);
|
|
120
|
+
this.buildFlatListRecursive(nonTaskChildId, flatList, processedIds, allMessages);
|
|
121
|
+
}
|
|
113
122
|
}
|
|
114
123
|
}
|
|
115
124
|
}
|
|
@@ -121,14 +130,28 @@ export class FlatListBuilder {
|
|
|
121
130
|
if (!processedIds.has(taskGrandchildId)) {
|
|
122
131
|
const taskGrandchild = this.messageMap.get(taskGrandchildId);
|
|
123
132
|
if (taskGrandchild && taskGrandchild.role !== 'task') {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
133
|
+
// Check if it's an AssistantGroup (assistant with tools)
|
|
134
|
+
if (
|
|
135
|
+
taskGrandchild.role === 'assistant' &&
|
|
136
|
+
taskGrandchild.tools &&
|
|
137
|
+
taskGrandchild.tools.length > 0
|
|
138
|
+
) {
|
|
139
|
+
this.processAssistantGroup(
|
|
140
|
+
taskGrandchild,
|
|
141
|
+
flatList,
|
|
142
|
+
processedIds,
|
|
143
|
+
allMessages,
|
|
144
|
+
);
|
|
145
|
+
} else {
|
|
146
|
+
flatList.push(taskGrandchild);
|
|
147
|
+
processedIds.add(taskGrandchildId);
|
|
148
|
+
this.buildFlatListRecursive(
|
|
149
|
+
taskGrandchildId,
|
|
150
|
+
flatList,
|
|
151
|
+
processedIds,
|
|
152
|
+
allMessages,
|
|
153
|
+
);
|
|
154
|
+
}
|
|
132
155
|
}
|
|
133
156
|
}
|
|
134
157
|
}
|
|
@@ -427,6 +450,60 @@ export class FlatListBuilder {
|
|
|
427
450
|
}
|
|
428
451
|
}
|
|
429
452
|
|
|
453
|
+
/**
|
|
454
|
+
* Process an assistant message with tools into an AssistantGroup
|
|
455
|
+
* Extracted to avoid code duplication in task children handling
|
|
456
|
+
*/
|
|
457
|
+
private processAssistantGroup(
|
|
458
|
+
message: Message,
|
|
459
|
+
flatList: Message[],
|
|
460
|
+
processedIds: Set<string>,
|
|
461
|
+
allMessages: Message[],
|
|
462
|
+
): void {
|
|
463
|
+
// Collect the entire assistant group chain
|
|
464
|
+
const assistantChain: Message[] = [];
|
|
465
|
+
const allToolMessages: Message[] = [];
|
|
466
|
+
this.messageCollector.collectAssistantChain(
|
|
467
|
+
message,
|
|
468
|
+
allMessages,
|
|
469
|
+
assistantChain,
|
|
470
|
+
allToolMessages,
|
|
471
|
+
processedIds,
|
|
472
|
+
);
|
|
473
|
+
|
|
474
|
+
// Create assistantGroup virtual message
|
|
475
|
+
const groupMessage = this.createAssistantGroupMessage(
|
|
476
|
+
assistantChain[0],
|
|
477
|
+
assistantChain,
|
|
478
|
+
allToolMessages,
|
|
479
|
+
);
|
|
480
|
+
flatList.push(groupMessage);
|
|
481
|
+
|
|
482
|
+
// Mark all as processed
|
|
483
|
+
assistantChain.forEach((m) => processedIds.add(m.id));
|
|
484
|
+
allToolMessages.forEach((m) => processedIds.add(m.id));
|
|
485
|
+
|
|
486
|
+
// Continue after the assistant chain
|
|
487
|
+
// Priority 1: If last assistant has non-tool children, continue from it
|
|
488
|
+
// Priority 2: Otherwise continue from tools (for cases where user replies to tool)
|
|
489
|
+
const lastAssistant = assistantChain.at(-1);
|
|
490
|
+
const toolIds = new Set(allToolMessages.map((t) => t.id));
|
|
491
|
+
|
|
492
|
+
const lastAssistantNonToolChildren = lastAssistant
|
|
493
|
+
? this.childrenMap.get(lastAssistant.id)?.filter((childId) => !toolIds.has(childId))
|
|
494
|
+
: undefined;
|
|
495
|
+
|
|
496
|
+
if (lastAssistantNonToolChildren && lastAssistantNonToolChildren.length > 0 && lastAssistant) {
|
|
497
|
+
// Follow-up messages exist after the last assistant (not tools)
|
|
498
|
+
this.buildFlatListRecursive(lastAssistant.id, flatList, processedIds, allMessages);
|
|
499
|
+
} else {
|
|
500
|
+
// No non-tool children of last assistant, check tools for children
|
|
501
|
+
for (const toolMsg of allToolMessages) {
|
|
502
|
+
this.buildFlatListRecursive(toolMsg.id, flatList, processedIds, allMessages);
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
|
|
430
507
|
/**
|
|
431
508
|
* Check if message has compare mode in metadata
|
|
432
509
|
*/
|
|
@@ -39,7 +39,6 @@ describe('ContextExtractor', () => {
|
|
|
39
39
|
const memoryItem = memories.items;
|
|
40
40
|
|
|
41
41
|
expect(memories.type).toBe('array');
|
|
42
|
-
expect(memoryItem.properties.memoryLayer.const).toBe('context');
|
|
43
42
|
// memoryCategory is a plain string in schema, not an enum
|
|
44
43
|
expect(memoryItem.properties.memoryCategory.type).toBe('string');
|
|
45
44
|
expect(memoryItem.properties.memoryType.enum).toEqual(memoryTypeValues);
|
|
@@ -39,7 +39,6 @@ describe('ExperienceExtractor', () => {
|
|
|
39
39
|
const memoryItem = memories.items;
|
|
40
40
|
|
|
41
41
|
expect(memories.type).toBe('array');
|
|
42
|
-
expect(memoryItem.properties.memoryLayer.const).toBe('experience');
|
|
43
42
|
// memoryCategory is a plain string in schema, not an enum
|
|
44
43
|
expect(memoryItem.properties.memoryCategory.type).toBe('string');
|
|
45
44
|
expect(memoryItem.properties.memoryType.enum).toEqual(memoryTypeValues);
|
|
@@ -39,7 +39,6 @@ describe('PreferenceExtractor', () => {
|
|
|
39
39
|
const memoryItem = memories.items;
|
|
40
40
|
|
|
41
41
|
expect(memories.type).toBe('array');
|
|
42
|
-
expect(memoryItem.properties.memoryLayer.const).toBe('preference');
|
|
43
42
|
// memoryCategory is a plain string in schema, not an enum
|
|
44
43
|
expect(memoryItem.properties.memoryCategory.type).toBe('string');
|
|
45
44
|
expect(memoryItem.properties.memoryType.enum).toEqual(memoryTypeValues);
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ContextStatusEnum,
|
|
3
|
-
LayersEnum,
|
|
4
3
|
UserMemoryContextObjectType,
|
|
5
4
|
UserMemoryContextSubjectType,
|
|
6
5
|
} from '@lobechat/types';
|
|
@@ -79,7 +78,6 @@ export const WithContextSchema = z.object({
|
|
|
79
78
|
export const ContextMemoryItemSchema = z.object({
|
|
80
79
|
details: z.string().describe('Optional detailed information'),
|
|
81
80
|
memoryCategory: z.string().describe('Memory category'),
|
|
82
|
-
memoryLayer: z.literal(LayersEnum.Context).describe('Memory layer'),
|
|
83
81
|
memoryType: MemoryTypeSchema.describe('Memory type'),
|
|
84
82
|
summary: z.string().describe('Concise overview of this specific memory'),
|
|
85
83
|
tags: z.array(z.string()).describe('User defined tags that summarize the context facets'),
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { LayersEnum } from '@lobechat/types';
|
|
2
1
|
import { z } from 'zod';
|
|
3
2
|
|
|
4
3
|
import { MemoryTypeSchema } from './common';
|
|
@@ -37,7 +36,6 @@ export const WithExperienceSchema = z.object({
|
|
|
37
36
|
export const ExperienceMemoryItemSchema = z.object({
|
|
38
37
|
details: z.string().describe('Optional detailed information'),
|
|
39
38
|
memoryCategory: z.string().describe('Memory category'),
|
|
40
|
-
memoryLayer: z.literal(LayersEnum.Experience).describe('Memory layer'),
|
|
41
39
|
memoryType: MemoryTypeSchema.describe('Memory type'),
|
|
42
40
|
summary: z.string().describe('Concise overview of this specific memory'),
|
|
43
41
|
tags: z.array(z.string()).describe('Model generated tags that summarize the experience facets'),
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { MergeStrategyEnum } from '@lobechat/types';
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
|
|
4
4
|
import { MemoryTypeSchema } from './common';
|
|
@@ -43,7 +43,6 @@ export const AddIdentityActionSchema = z
|
|
|
43
43
|
.object({
|
|
44
44
|
details: z.union([z.string(), z.null()]).describe('Optional detailed information'),
|
|
45
45
|
memoryCategory: z.string().describe('Memory category'),
|
|
46
|
-
memoryLayer: z.literal(LayersEnum.Identity).describe('Memory layer'),
|
|
47
46
|
memoryType: MemoryTypeSchema.describe('Memory type'),
|
|
48
47
|
summary: z.string().describe('Concise overview of this specific memory'),
|
|
49
48
|
tags: z.array(z.string()).describe('Model generated tags that summarize the identity facets'),
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { LayersEnum } from '@lobechat/types';
|
|
2
1
|
import { z } from 'zod';
|
|
3
2
|
|
|
4
3
|
import { MemoryTypeSchema } from './common';
|
|
@@ -66,7 +65,6 @@ export const WithPreferenceSchema = z.object({
|
|
|
66
65
|
export const PreferenceMemoryItemSchema = z.object({
|
|
67
66
|
details: z.string().describe('Optional detailed information'),
|
|
68
67
|
memoryCategory: z.string().describe('Memory category'),
|
|
69
|
-
memoryLayer: z.literal(LayersEnum.Preference).describe('Memory layer'),
|
|
70
68
|
memoryType: MemoryTypeSchema.describe('Memory type'),
|
|
71
69
|
summary: z.string().describe('Concise overview of this specific memory'),
|
|
72
70
|
tags: z.array(z.string()).describe('Model generated tags that summarize the preference facets'),
|
|
@@ -86,10 +86,6 @@ export interface ChatStreamPayload {
|
|
|
86
86
|
* @title Number of texts to return
|
|
87
87
|
*/
|
|
88
88
|
n?: number;
|
|
89
|
-
/**
|
|
90
|
-
* List of enabled plugins
|
|
91
|
-
*/
|
|
92
|
-
plugins?: string[];
|
|
93
89
|
/**
|
|
94
90
|
* @title Penalty coefficient in generated text to reduce topic changes
|
|
95
91
|
* @default 0
|
|
@@ -19,7 +19,11 @@ export interface UserDetailContextConfig {
|
|
|
19
19
|
isOwner: boolean;
|
|
20
20
|
mobile?: boolean;
|
|
21
21
|
onEditProfile?: (onSuccess?: (profile: MarketUserProfile) => void) => void;
|
|
22
|
-
onStatusChange?: (
|
|
22
|
+
onStatusChange?: (
|
|
23
|
+
identifier: string,
|
|
24
|
+
action: 'publish' | 'unpublish' | 'deprecate',
|
|
25
|
+
type?: 'agent' | 'group',
|
|
26
|
+
) => void;
|
|
23
27
|
totalInstalls: number;
|
|
24
28
|
user: DiscoverUserInfo;
|
|
25
29
|
}
|