@lobehub/lobehub 2.0.0-next.251 → 2.0.0-next.252
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 +25 -0
- package/changelog/v1.json +9 -0
- package/package.json +1 -1
- package/packages/builtin-tool-notebook/src/client/Render/CreateDocument/DocumentCard.tsx +0 -2
- package/packages/database/migrations/meta/_journal.json +1 -1
- package/packages/database/src/models/__tests__/topics/topic.create.test.ts +37 -8
- package/packages/database/src/models/topic.ts +71 -4
- package/packages/database/src/schemas/agentCronJob.ts +1 -2
- package/packages/memory-user-memory/src/extractors/context.ts +1 -4
- package/packages/memory-user-memory/src/extractors/experience.ts +2 -8
- package/packages/memory-user-memory/src/extractors/preference.ts +2 -8
- package/packages/memory-user-memory/src/prompts/gatekeeper.ts +123 -123
- package/packages/memory-user-memory/src/prompts/layers/context.ts +152 -152
- package/packages/memory-user-memory/src/prompts/layers/experience.ts +159 -159
- package/packages/memory-user-memory/src/prompts/layers/identity.ts +213 -213
- package/packages/memory-user-memory/src/prompts/layers/preference.ts +160 -160
- package/packages/memory-user-memory/src/services/extractExecutor.ts +33 -30
- package/packages/memory-user-memory/src/types.ts +10 -8
- package/packages/types/src/topic/topic.ts +9 -0
- package/src/app/[variants]/(main)/chat/_layout/Sidebar/Body.tsx +4 -1
- package/src/app/[variants]/(main)/chat/_layout/Sidebar/Topic/CronTopicList/CronTopicGroup.tsx +74 -0
- package/src/app/[variants]/(main)/chat/_layout/Sidebar/Topic/CronTopicList/CronTopicItem.tsx +40 -0
- package/src/app/[variants]/(main)/chat/_layout/Sidebar/Topic/CronTopicList/index.tsx +140 -0
- package/src/app/[variants]/(main)/chat/_layout/Sidebar/Topic/List/index.tsx +1 -1
- package/src/app/[variants]/(main)/chat/_layout/Sidebar/Topic/TopicListContent/index.tsx +1 -1
- package/src/app/[variants]/(main)/chat/_layout/Sidebar/Topic/index.tsx +1 -1
- package/src/app/[variants]/(main)/chat/cron/[cronId]/index.tsx +664 -0
- package/src/app/[variants]/(main)/chat/profile/features/AgentCronJobs/CronJobCards.tsx +160 -0
- package/src/app/[variants]/(main)/chat/profile/features/AgentCronJobs/CronJobForm.tsx +202 -0
- package/src/app/[variants]/(main)/chat/profile/features/AgentCronJobs/CronJobList.tsx +137 -0
- package/src/app/[variants]/(main)/chat/profile/features/AgentCronJobs/hooks/useAgentCronJobs.ts +138 -0
- package/src/app/[variants]/(main)/chat/profile/features/AgentCronJobs/index.tsx +130 -0
- package/src/app/[variants]/(main)/chat/profile/features/ProfileEditor/index.tsx +33 -3
- package/src/app/[variants]/router/desktopRouter.config.tsx +7 -0
- package/src/hooks/useFetchCronTopics.ts +29 -0
- package/src/hooks/useFetchCronTopicsWithJobInfo.ts +56 -0
- package/src/hooks/useFetchTopics.ts +4 -1
- package/src/locales/default/setting.ts +44 -1
- package/src/server/routers/lambda/agentCronJob.ts +367 -0
- package/src/server/routers/lambda/image/index.test.ts +2 -2
- package/src/server/routers/lambda/index.ts +2 -0
- package/src/server/routers/lambda/topic.ts +15 -3
- package/src/server/services/aiAgent/index.ts +18 -1
- package/src/server/services/memory/userMemory/extract.ts +14 -6
- package/src/services/agentCronJob.ts +95 -0
- package/src/services/topic/index.ts +1 -0
- package/src/store/chat/slices/topic/action.ts +53 -2
- package/src/store/chat/slices/topic/initialState.ts +1 -0
- package/src/store/chat/slices/topic/selectors.ts +14 -6
- package/src/tools/placeholders.ts +1 -4
|
@@ -1,164 +1,164 @@
|
|
|
1
1
|
export const preferencePrompt = [
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
2
|
+
'You are a focused memory extraction assistant specialized in the **preference**',
|
|
3
|
+
'layer. When extracting, ensure all the content is using {{ language }}.',
|
|
4
|
+
'',
|
|
5
|
+
'\\<user_context>',
|
|
6
|
+
'Current user: {{ username }}',
|
|
7
|
+
'Session date: {{ sessionDate }}',
|
|
8
|
+
'Available memory categories: {{ availableCategories }}',
|
|
9
|
+
'Target layer: preference',
|
|
10
|
+
'\\</user_context>',
|
|
11
|
+
'',
|
|
12
|
+
'## Retrieved Memory (Top {{ topK }})',
|
|
13
|
+
'',
|
|
14
|
+
'Use the list below to de-duplicate and decide whether you need to extract',
|
|
15
|
+
'anything. Do not copy these verbatim; use them for comparison.',
|
|
16
|
+
'',
|
|
17
|
+
'{{ retrievedContext }}',
|
|
18
|
+
'',
|
|
19
|
+
'## Your Task',
|
|
20
|
+
'',
|
|
21
|
+
'Extract **ALL** preference layer information from the conversation. Capture user',
|
|
22
|
+
'choices, directives, likes, dislikes, and behavioral guidance for the assistant.',
|
|
23
|
+
'',
|
|
24
|
+
'**CRITICAL**: Return an **array** of memory items. One conversation can include',
|
|
25
|
+
'more than one preference memory. Extract each as a separate item.',
|
|
26
|
+
'',
|
|
27
|
+
'Before extracting, review the retrieved similar memories first (top {{ topK }}',
|
|
28
|
+
'items shown below). Extract items that are NEW or MATERIALLY UPDATED compared to',
|
|
29
|
+
'retrieved entries. Avoid duplicates or near-duplicates. Prefer manual merging',
|
|
30
|
+
'over duplication: if content is already covered with no meaningful new detail,',
|
|
31
|
+
'do not extract it again.',
|
|
32
|
+
'',
|
|
33
|
+
'## Name Handling and Neutrality',
|
|
34
|
+
'',
|
|
35
|
+
'- Always refer to the user with the exact placeholder token "User". Do not',
|
|
36
36
|
" infer, invent, or translate the user's real name.",
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
37
|
+
'- Do not assign gendered terms or honorifics (e.g., "先生 / 女士",',
|
|
38
|
+
' "Mr./Ms."). Keep all references neutral during extraction.',
|
|
39
|
+
'',
|
|
40
|
+
'## Preference vs. Requirement',
|
|
41
|
+
'',
|
|
42
|
+
'- Extract durable, reusable user preferences that guide future assistant',
|
|
43
|
+
' behavior.',
|
|
44
|
+
'- Do NOT extract one-off task requirements, step-by-step implementation plans,',
|
|
45
|
+
' or transient instructions tied to the current task or message.',
|
|
46
|
+
'- Do NOT infer a language preference from the conversation language alone.',
|
|
47
|
+
' Extract language preferences when the user explicitly states a persistent',
|
|
48
|
+
' request (e.g., "Always reply in Chinese").',
|
|
49
|
+
'',
|
|
50
|
+
'## Output Format',
|
|
51
|
+
'',
|
|
52
|
+
'Return structured JSON data according to the provided schema. The output must',
|
|
53
|
+
'pass validation against a strict schema including:',
|
|
54
|
+
'',
|
|
55
|
+
'- Basic fields: title, summary, details, memoryLayer, memoryType,',
|
|
56
|
+
' memoryCategory',
|
|
57
|
+
'- Preference-specific fields in withPreference: extractedLabels,',
|
|
58
|
+
' extractedScopes (array of strings), originContext (trigger, scenario, actor,',
|
|
59
|
+
' applicableWhen, notApplicableWhen), appContext (app, surface, feature,',
|
|
60
|
+
' route), conclusionDirectives, type, suggestions, scorePriority',
|
|
61
|
+
'',
|
|
62
|
+
'`extractedScopes` is a simple string array describing the preference scope.',
|
|
63
|
+
'',
|
|
64
|
+
'## Memory Formatting Guidelines',
|
|
65
|
+
'',
|
|
66
|
+
'> CRITICAL REQUIREMENT: ALL MEMORY ITEMS MUST BE SELF-CONTAINED',
|
|
67
|
+
'',
|
|
68
|
+
'Every memory item you create must be standalone and understandable without',
|
|
69
|
+
'extra context:',
|
|
70
|
+
'',
|
|
71
|
+
'✓ **Required Elements:**',
|
|
72
|
+
'',
|
|
73
|
+
'- Use full names and specific subjects—NEVER use pronouns',
|
|
74
|
+
' (he/she/they/it/this/that)',
|
|
75
|
+
'- Include specific names, places, dates, and complete context',
|
|
76
|
+
'- Preserve the original language from user input—do not translate',
|
|
77
|
+
'- Capture relevant details, emotions, and outcomes',
|
|
78
|
+
'- Ensure each item is comprehensible independently',
|
|
79
|
+
'',
|
|
80
|
+
'✓ **Good Examples:**',
|
|
81
|
+
'',
|
|
82
|
+
'- "When providing technical answers, {{ username }} prefers concise bullet',
|
|
83
|
+
' points and TypeScript code examples; avoid lengthy prose."',
|
|
84
|
+
'- "For daily planning, {{ username }} wants reminders at 08:00 local time and a',
|
|
85
|
+
' single summary message at 21:00 describing completed tasks."',
|
|
86
|
+
'',
|
|
87
|
+
'✗ **Bad Examples:**',
|
|
88
|
+
'',
|
|
89
|
+
'- "She went to a support group" → Missing: who, when, what happened, emotional',
|
|
90
|
+
' outcome',
|
|
91
|
+
'- "They felt happy" → Missing: who, context, cause of emotion',
|
|
92
|
+
'- "The discussion was helpful" → Missing: participants, topic, specific value',
|
|
93
|
+
' gained',
|
|
94
|
+
'- "This made them realize something important" → Vague pronouns and undefined',
|
|
95
|
+
' referents',
|
|
96
|
+
'',
|
|
97
|
+
'## Layer-Specific Extraction Guidance',
|
|
98
|
+
'',
|
|
99
|
+
'Capture actionable rules that guide assistant behavior and decision-making.',
|
|
100
|
+
'Define `extractedScopes` to clarify applicability (time ranges, contexts,',
|
|
101
|
+
'channels). Use `originContext` and `appContext` to describe when and where the',
|
|
102
|
+
'preference applies. Write `conclusionDirectives` as self-contained instructions',
|
|
103
103
|
"from the user's perspective. Use `scorePriority` to mark preferences that should",
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
104
|
+
'override conflicting defaults. Provide `suggestions` for helpful follow-up',
|
|
105
|
+
'actions. Avoid implementation details; focus on what the assistant should do.',
|
|
106
|
+
'',
|
|
107
|
+
'Examples of preference information:',
|
|
108
|
+
'',
|
|
109
|
+
'- "{{ username }} prefers concise, bullet-point responses over long paragraphs',
|
|
110
|
+
' when asking technical questions"',
|
|
111
|
+
'- "{{ username }} likes to receive code examples in TypeScript rather than',
|
|
112
|
+
' JavaScript"',
|
|
113
|
+
'- "{{ username }} prefers morning workouts at 06:00 and dislikes exercising in',
|
|
114
|
+
' the evening"',
|
|
115
|
+
'',
|
|
116
|
+
'Not preferences (do not extract):',
|
|
117
|
+
'',
|
|
118
|
+
'- One-off task instructions (e.g., "帮我把这段话翻译成英文")',
|
|
119
|
+
'- Implementation details or step-by-step plans (e.g.,',
|
|
120
|
+
' "先抓取 API,然后解析 JSON…")',
|
|
121
|
+
'- Language used in the conversation unless explicitly stated as a persistent',
|
|
122
|
+
' preference',
|
|
123
|
+
'',
|
|
124
|
+
'## Memory Type Classifications',
|
|
125
|
+
'',
|
|
126
|
+
'Choose the appropriate memoryType:',
|
|
127
|
+
'',
|
|
128
|
+
'- **activity**: Detailed conversations, interactions, and events with full',
|
|
129
|
+
' contextual narrative',
|
|
130
|
+
'- **event**: Specific time-bound occurrences (dates, milestones, appointments,',
|
|
131
|
+
' meetings)',
|
|
132
|
+
'- **fact**: Factual information, data points, and verifiable knowledge',
|
|
133
|
+
'- **preference**: User choices, likes, dislikes, and behavioral preferences',
|
|
134
|
+
'- **context**: Background information, situational details, environmental',
|
|
135
|
+
' factors',
|
|
136
|
+
'- **location**: Geographic information, places, and spatial context',
|
|
137
|
+
'- **people**: Information about individuals and their relationships',
|
|
138
|
+
'- **topic**: Subject matter, domains of interest, and knowledge areas',
|
|
139
|
+
'- **technology**: Tools, platforms, software, and technical systems',
|
|
140
|
+
'- **other**: Miscellaneous information not fitting other categories',
|
|
141
|
+
'',
|
|
142
|
+
'## Security Considerations',
|
|
143
|
+
'',
|
|
144
|
+
'**NEVER extract or store sensitive information:**',
|
|
145
|
+
'',
|
|
146
|
+
'- Passwords, PINs, or authentication credentials',
|
|
147
|
+
'- API keys, tokens, or secret keys',
|
|
148
|
+
'- Financial data (credit cards, bank accounts, SSN)',
|
|
149
|
+
'- Medical records or protected health information',
|
|
150
|
+
'- Private encryption keys or certificates',
|
|
151
|
+
'',
|
|
152
|
+
'---',
|
|
153
|
+
'',
|
|
154
|
+
'## Final Instructions',
|
|
155
|
+
'',
|
|
156
|
+
'1. Analyze the conversation for preference layer information',
|
|
157
|
+
'2. Extract each distinct preference memory as a separate item',
|
|
158
|
+
'3. Ensure all memories are self-contained (no pronouns, complete context)',
|
|
159
|
+
'4. Return a JSON array conforming to the schema above',
|
|
160
|
+
'5. Return `[]` if you find no preference memories',
|
|
161
|
+
'6. No matter what the language of the retrieved language is, always use {{ language }} for output',
|
|
162
|
+
'',
|
|
163
|
+
'Respond with valid JSON without commentary.',
|
|
164
164
|
].join('\n');
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
layersCallsCounter,
|
|
9
9
|
tracer,
|
|
10
10
|
} from '@lobechat/observability-otel/modules/memory-user-memory';
|
|
11
|
+
import { attributesCommon } from '@lobechat/observability-otel/node';
|
|
11
12
|
import { LayersEnum } from '@lobechat/types';
|
|
12
13
|
|
|
13
14
|
import {
|
|
@@ -20,16 +21,15 @@ import {
|
|
|
20
21
|
import {
|
|
21
22
|
BaseExtractorDependencies,
|
|
22
23
|
ExtractorOptions,
|
|
24
|
+
ExtractorRunOptions,
|
|
23
25
|
GatekeeperDecision,
|
|
26
|
+
GatekeeperOptions,
|
|
24
27
|
MemoryExtractionAgent,
|
|
25
28
|
MemoryExtractionJob,
|
|
26
29
|
MemoryExtractionLLMConfig,
|
|
27
30
|
MemoryExtractionLayerOutputs,
|
|
28
31
|
MemoryExtractionResult,
|
|
29
|
-
GatekeeperOptions,
|
|
30
|
-
ExtractorRunOptions,
|
|
31
32
|
} from '../types';
|
|
32
|
-
import { attributesCommon } from '@lobechat/observability-otel/node';
|
|
33
33
|
|
|
34
34
|
const LAYER_ORDER: LayersEnum[] = [
|
|
35
35
|
'identity' as LayersEnum,
|
|
@@ -60,10 +60,10 @@ export interface MemoryExtractionServiceOptions {
|
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
export interface MemoryExtractionLayerOutputTypes {
|
|
63
|
-
[LayersEnum.Context]: Awaited<ReturnType<ContextExtractor['structuredCall']
|
|
64
|
-
[LayersEnum.Experience]: Awaited<ReturnType<ExperienceExtractor['structuredCall']
|
|
65
|
-
[LayersEnum.Preference]: Awaited<ReturnType<PreferenceExtractor['structuredCall']
|
|
66
|
-
[LayersEnum.Identity]: Awaited<ReturnType<IdentityExtractor['structuredCall']
|
|
63
|
+
[LayersEnum.Context]: Awaited<ReturnType<ContextExtractor['structuredCall']>>;
|
|
64
|
+
[LayersEnum.Experience]: Awaited<ReturnType<ExperienceExtractor['structuredCall']>>;
|
|
65
|
+
[LayersEnum.Preference]: Awaited<ReturnType<PreferenceExtractor['structuredCall']>>;
|
|
66
|
+
[LayersEnum.Identity]: Awaited<ReturnType<IdentityExtractor['structuredCall']>>;
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
export type MemoryExtractionLayerOutputType = {
|
|
@@ -216,7 +216,11 @@ export class MemoryExtractionService<RO> {
|
|
|
216
216
|
const processedLayersCount = {
|
|
217
217
|
context: outputs.context?.data ? outputs.context?.data?.memories?.length : 0,
|
|
218
218
|
experience: outputs.experience?.data ? outputs.experience?.data?.memories?.length : 0,
|
|
219
|
-
identity: outputs.identity?.data
|
|
219
|
+
identity: outputs.identity?.data
|
|
220
|
+
? (outputs.identity?.data?.add?.length || 0) +
|
|
221
|
+
(outputs.identity?.data?.update?.length || 0) +
|
|
222
|
+
(outputs.identity?.data?.remove?.length || 0)
|
|
223
|
+
: 0,
|
|
220
224
|
preference: outputs.preference?.data ? outputs.preference?.data?.memories?.length : 0,
|
|
221
225
|
};
|
|
222
226
|
const processedErrorsCount = {
|
|
@@ -224,7 +228,7 @@ export class MemoryExtractionService<RO> {
|
|
|
224
228
|
experience: outputs.experience?.error ? 1 : 0,
|
|
225
229
|
identity: outputs.identity?.error ? 1 : 0,
|
|
226
230
|
preference: outputs.preference?.error ? 1 : 0,
|
|
227
|
-
}
|
|
231
|
+
};
|
|
228
232
|
|
|
229
233
|
const processedCount = Object.values(processedLayersCount).reduce((a, b) => a + b, 0);
|
|
230
234
|
|
|
@@ -334,9 +338,7 @@ export class MemoryExtractionService<RO> {
|
|
|
334
338
|
|
|
335
339
|
const setLayerOutput = (
|
|
336
340
|
layer: LayersEnum,
|
|
337
|
-
result:
|
|
338
|
-
| { data: MemoryExtractionLayerOutputType }
|
|
339
|
-
| { error: unknown },
|
|
341
|
+
result: { data: MemoryExtractionLayerOutputType } | { error: unknown },
|
|
340
342
|
) => {
|
|
341
343
|
switch (layer) {
|
|
342
344
|
case LayersEnum.Context: {
|
|
@@ -370,24 +372,25 @@ export class MemoryExtractionService<RO> {
|
|
|
370
372
|
};
|
|
371
373
|
|
|
372
374
|
await Promise.all(
|
|
373
|
-
(
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
}
|
|
390
|
-
}
|
|
375
|
+
(
|
|
376
|
+
[
|
|
377
|
+
LayersEnum.Context,
|
|
378
|
+
LayersEnum.Experience,
|
|
379
|
+
LayersEnum.Preference,
|
|
380
|
+
LayersEnum.Identity,
|
|
381
|
+
] as LayersEnum[]
|
|
382
|
+
).map(async (layer) => {
|
|
383
|
+
if (!layers.includes(layer)) {
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
try {
|
|
388
|
+
const result = await this.runLayer(job, layer, options);
|
|
389
|
+
setLayerOutput(layer, { data: result });
|
|
390
|
+
} catch (error) {
|
|
391
|
+
setLayerOutput(layer, { error });
|
|
392
|
+
}
|
|
393
|
+
}),
|
|
391
394
|
);
|
|
392
395
|
|
|
393
396
|
return outputs as MemoryExtractionLayerOutputs;
|
|
@@ -30,12 +30,14 @@ export interface ExtractorOptions extends ExtractorTemplateProps {
|
|
|
30
30
|
additionalMessages?: OpenAIChatMessage[];
|
|
31
31
|
callbacks?: {
|
|
32
32
|
onExtractError?: (agent: MemoryExtractionAgent, error: unknown) => Promise<void> | void;
|
|
33
|
-
onExtractRequest?: (
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
33
|
+
onExtractRequest?: (
|
|
34
|
+
agent: MemoryExtractionAgent,
|
|
35
|
+
request: GenerateObjectPayload,
|
|
36
|
+
) => Promise<void> | void;
|
|
37
|
+
onExtractResponse?: <TOutput>(
|
|
38
|
+
agent: MemoryExtractionAgent,
|
|
39
|
+
response: TOutput,
|
|
40
|
+
) => Promise<void> | void;
|
|
39
41
|
};
|
|
40
42
|
messageIds?: string[];
|
|
41
43
|
sourceId?: string;
|
|
@@ -133,11 +135,11 @@ export type MemoryExtractionLayerOutputs = Partial<{
|
|
|
133
135
|
identity: {
|
|
134
136
|
data?: Awaited<ReturnType<IdentityExtractor['structuredCall']>>;
|
|
135
137
|
error?: unknown;
|
|
136
|
-
}
|
|
138
|
+
};
|
|
137
139
|
preference: {
|
|
138
140
|
data?: Awaited<ReturnType<PreferenceExtractor['structuredCall']>>;
|
|
139
141
|
error?: unknown;
|
|
140
|
-
}
|
|
142
|
+
};
|
|
141
143
|
}>;
|
|
142
144
|
|
|
143
145
|
export interface GatekeeperDecision {
|
|
@@ -34,6 +34,10 @@ export interface TopicUserMemoryExtractRunState {
|
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
export interface ChatTopicMetadata {
|
|
37
|
+
/**
|
|
38
|
+
* Cron job ID that triggered this topic creation (if created by scheduled task)
|
|
39
|
+
*/
|
|
40
|
+
cronJobId?: string;
|
|
37
41
|
model?: string;
|
|
38
42
|
provider?: string;
|
|
39
43
|
userMemoryExtractRunState?: TopicUserMemoryExtractRunState;
|
|
@@ -57,6 +61,7 @@ export interface ChatTopic extends Omit<BaseDataModel, 'meta'> {
|
|
|
57
61
|
metadata?: ChatTopicMetadata;
|
|
58
62
|
sessionId?: string;
|
|
59
63
|
title: string;
|
|
64
|
+
trigger?: string | null;
|
|
60
65
|
}
|
|
61
66
|
|
|
62
67
|
export type ChatTopicMap = Record<string, ChatTopic>;
|
|
@@ -106,6 +111,10 @@ export interface CreateTopicParams {
|
|
|
106
111
|
export interface QueryTopicParams {
|
|
107
112
|
agentId?: string | null;
|
|
108
113
|
current?: number;
|
|
114
|
+
/**
|
|
115
|
+
* Exclude topics by trigger types (e.g. ['cron'])
|
|
116
|
+
*/
|
|
117
|
+
excludeTriggers?: string[];
|
|
109
118
|
/**
|
|
110
119
|
* Group ID to filter topics by
|
|
111
120
|
*/
|
|
@@ -2,15 +2,18 @@ import { Accordion, Flexbox } from '@lobehub/ui';
|
|
|
2
2
|
import React, { memo } from 'react';
|
|
3
3
|
|
|
4
4
|
import Topic from './Topic';
|
|
5
|
+
import CronTopicList from './Topic/CronTopicList';
|
|
5
6
|
|
|
6
7
|
export enum ChatSidebarKey {
|
|
7
|
-
|
|
8
|
+
CronTopics = 'cronTopics',
|
|
9
|
+
Topic = 'topic'
|
|
8
10
|
}
|
|
9
11
|
|
|
10
12
|
const Body = memo(() => {
|
|
11
13
|
return (
|
|
12
14
|
<Flexbox paddingInline={4}>
|
|
13
15
|
<Accordion defaultExpandedKeys={[ChatSidebarKey.Topic]} gap={8}>
|
|
16
|
+
<CronTopicList itemKey={ChatSidebarKey.CronTopics} />
|
|
14
17
|
<Topic itemKey={ChatSidebarKey.Topic} />
|
|
15
18
|
</Accordion>
|
|
16
19
|
</Flexbox>
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { Flexbox, Icon, Text } from '@lobehub/ui';
|
|
4
|
+
import { cssVar } from 'antd-style';
|
|
5
|
+
import { Clock } from 'lucide-react';
|
|
6
|
+
import { type MouseEvent, memo, useCallback } from 'react';
|
|
7
|
+
import { useParams } from 'react-router-dom';
|
|
8
|
+
|
|
9
|
+
import { useRouter } from '@/app/[variants]/(main)/hooks/useRouter';
|
|
10
|
+
import type { AgentCronJob } from '@/database/schemas/agentCronJob';
|
|
11
|
+
|
|
12
|
+
import CronTopicItem from './CronTopicItem';
|
|
13
|
+
|
|
14
|
+
interface CronTopicGroupProps {
|
|
15
|
+
cronJob: AgentCronJob | null;
|
|
16
|
+
cronJobId: string;
|
|
17
|
+
topics: Array<{
|
|
18
|
+
createdAt: Date | string;
|
|
19
|
+
favorite?: boolean | null;
|
|
20
|
+
historySummary?: string | null;
|
|
21
|
+
id: string;
|
|
22
|
+
metadata?: any;
|
|
23
|
+
title?: string | null;
|
|
24
|
+
trigger?: string | null;
|
|
25
|
+
updatedAt: Date | string;
|
|
26
|
+
}>;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const CronTopicGroup = memo<CronTopicGroupProps>(({ cronJob, cronJobId, topics }) => {
|
|
30
|
+
const { aid } = useParams<{ aid?: string }>();
|
|
31
|
+
const router = useRouter();
|
|
32
|
+
const handleOpenCronJob = useCallback(
|
|
33
|
+
(event: MouseEvent) => {
|
|
34
|
+
event.stopPropagation();
|
|
35
|
+
if (!aid) return;
|
|
36
|
+
router.push(`/agent/${aid}/cron/${cronJobId}`);
|
|
37
|
+
},
|
|
38
|
+
[aid, cronJobId, router],
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
const cronJobName = cronJob?.name || `Cron Job ${cronJobId.slice(-8)}`;
|
|
42
|
+
const isEnabled = cronJob?.enabled ?? false;
|
|
43
|
+
|
|
44
|
+
return (
|
|
45
|
+
<Flexbox gap={1}>
|
|
46
|
+
<Flexbox
|
|
47
|
+
align="center"
|
|
48
|
+
gap={6}
|
|
49
|
+
height={24}
|
|
50
|
+
horizontal
|
|
51
|
+
onClick={handleOpenCronJob}
|
|
52
|
+
paddingInline={8}
|
|
53
|
+
style={{ cursor: 'pointer', opacity: isEnabled ? 1 : 0.6, overflow: 'hidden' }}
|
|
54
|
+
>
|
|
55
|
+
<Icon icon={Clock} style={{ color: cssVar.colorTextDescription, opacity: 0.7 }} />
|
|
56
|
+
<Text ellipsis fontSize={12} style={{ flex: 1 }} type={'secondary'} weight={500}>
|
|
57
|
+
{cronJobName}
|
|
58
|
+
</Text>
|
|
59
|
+
{topics.length > 0 && (
|
|
60
|
+
<Text style={{ color: cssVar.colorTextDescription, fontSize: 11 }}>{topics.length}</Text>
|
|
61
|
+
)}
|
|
62
|
+
</Flexbox>
|
|
63
|
+
{topics.length > 0 && (
|
|
64
|
+
<Flexbox gap={1} paddingBlock={1}>
|
|
65
|
+
{topics.map((topic) => (
|
|
66
|
+
<CronTopicItem key={topic.id} topic={topic} />
|
|
67
|
+
))}
|
|
68
|
+
</Flexbox>
|
|
69
|
+
)}
|
|
70
|
+
</Flexbox>
|
|
71
|
+
);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
export default CronTopicGroup;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { memo } from 'react';
|
|
4
|
+
import { useTranslation } from 'react-i18next';
|
|
5
|
+
|
|
6
|
+
import { useChatStore } from '@/store/chat';
|
|
7
|
+
|
|
8
|
+
import TopicItem from '../List/Item';
|
|
9
|
+
|
|
10
|
+
interface CronTopicItemProps {
|
|
11
|
+
topic: {
|
|
12
|
+
createdAt: Date | string;
|
|
13
|
+
favorite?: boolean | null;
|
|
14
|
+
historySummary?: string | null;
|
|
15
|
+
id: string;
|
|
16
|
+
metadata?: any;
|
|
17
|
+
title?: string | null;
|
|
18
|
+
trigger?: string | null;
|
|
19
|
+
updatedAt: Date | string;
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const CronTopicItem = memo<CronTopicItemProps>(({ topic }) => {
|
|
24
|
+
const { t } = useTranslation('topic');
|
|
25
|
+
const [activeTopicId, activeThreadId] = useChatStore((s) => [s.activeTopicId, s.activeThreadId]);
|
|
26
|
+
|
|
27
|
+
const displayTitle = topic.title || topic.historySummary || t('defaultTitle');
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<TopicItem
|
|
31
|
+
active={activeTopicId === topic.id}
|
|
32
|
+
fav={!!topic.favorite}
|
|
33
|
+
id={topic.id}
|
|
34
|
+
threadId={activeThreadId}
|
|
35
|
+
title={displayTitle}
|
|
36
|
+
/>
|
|
37
|
+
);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
export default CronTopicItem;
|