@lobehub/lobehub 2.0.0-next.293 → 2.0.0-next.295
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/.github/workflows/release-desktop-beta.yml +6 -6
- package/.github/workflows/release-desktop-stable.yml +11 -11
- package/CHANGELOG.md +52 -0
- package/apps/desktop/electron.vite.config.ts +0 -1
- package/apps/desktop/src/main/__mocks__/node-mac-permissions.ts +0 -1
- package/apps/desktop/src/main/__mocks__/setup.ts +0 -1
- package/apps/desktop/src/main/controllers/McpCtr.ts +50 -18
- package/apps/desktop/src/main/controllers/__tests__/SystemCtr.test.ts +1 -4
- package/apps/desktop/src/main/libs/mcp/client.ts +54 -2
- package/apps/desktop/tsconfig.json +4 -10
- package/changelog/v1.json +14 -0
- package/e2e/scripts/setup.ts +45 -32
- package/package.json +1 -1
- package/packages/database/src/models/__tests__/knowledgeBase.test.ts +1 -1
- package/packages/database/src/repositories/knowledge/index.ts +1 -4
- package/packages/types/src/discover/assistants.ts +2 -2
- package/scripts/migrate-spa-navigation.ts +129 -0
- package/src/app/(backend)/api/workflows/memory-user-memory/pipelines/chat-topic/process-topics/route.ts +112 -109
- package/src/app/(backend)/api/workflows/memory-user-memory/pipelines/chat-topic/process-user-topics/route.ts +125 -113
- package/src/app/(backend)/api/workflows/memory-user-memory/pipelines/chat-topic/process-users/route.ts +74 -65
- package/src/app/[variants]/(auth)/auth-error/page.tsx +1 -1
- package/src/app/[variants]/(auth)/login/[[...login]]/page.tsx +1 -1
- package/src/app/[variants]/(auth)/next-auth/error/AuthErrorPage.tsx +1 -1
- package/src/app/[variants]/(auth)/next-auth/signin/AuthSignInBox.tsx +1 -1
- package/src/app/[variants]/(auth)/oauth/callback/error/page.tsx +1 -1
- package/src/app/[variants]/(auth)/oauth/callback/success/page.tsx +1 -1
- package/src/app/[variants]/(auth)/oauth/consent/[uid]/page.tsx +1 -1
- package/src/app/[variants]/(auth)/reset-password/layout.tsx +1 -1
- package/src/app/[variants]/(auth)/reset-password/page.tsx +2 -2
- package/src/app/[variants]/(auth)/signin/layout.tsx +1 -1
- package/src/app/[variants]/(auth)/signin/useSignIn.ts +1 -1
- package/src/app/[variants]/(auth)/signup/[[...signup]]/BetterAuthSignUpForm.tsx +2 -2
- package/src/app/[variants]/(auth)/signup/[[...signup]]/page.tsx +1 -1
- package/src/app/[variants]/(auth)/signup/[[...signup]]/useSignUp.tsx +1 -1
- package/src/app/[variants]/(auth)/verify-email/layout.tsx +1 -1
- package/src/app/[variants]/(auth)/verify-email/page.tsx +2 -2
- package/src/app/[variants]/(main)/_layout/index.tsx +1 -1
- package/src/app/[variants]/(main)/agent/_layout/AgentIdSync.tsx +12 -1
- package/src/app/[variants]/(main)/agent/_layout/Sidebar/Cron/CronTopicGroup.tsx +1 -1
- package/src/app/[variants]/(main)/agent/_layout/Sidebar/Header/AddTopicButon.tsx +1 -1
- package/src/app/[variants]/(main)/agent/_layout/Sidebar/Header/Nav.tsx +1 -1
- package/src/app/[variants]/(main)/agent/_layout/Sidebar/Topic/AllTopicsDrawer/index.tsx +1 -1
- package/src/app/[variants]/(main)/agent/_layout/Sidebar/Topic/hooks/useThreadNavigation.ts +1 -1
- package/src/app/[variants]/(main)/agent/_layout/Sidebar/Topic/hooks/useTopicNavigation.ts +1 -1
- package/src/app/[variants]/(main)/agent/features/TelemetryNotification.tsx +2 -3
- package/src/app/[variants]/(main)/community/(detail)/assistant/features/Details/Nav.tsx +9 -9
- package/src/app/[variants]/(main)/community/(detail)/assistant/features/Details/Versions/index.tsx +2 -3
- package/src/app/[variants]/(main)/community/(detail)/features/MakedownRender.tsx +1 -2
- package/src/app/[variants]/(main)/community/(detail)/features/ShareButton.tsx +2 -3
- package/src/app/[variants]/(main)/community/(detail)/features/Toc/Heading.tsx +2 -3
- package/src/app/[variants]/(main)/community/(detail)/mcp/features/Details/Versions/index.tsx +2 -2
- package/src/app/[variants]/(main)/community/(detail)/model/features/Details/Nav.tsx +12 -11
- package/src/app/[variants]/(main)/community/(detail)/model/features/Details/Parameter/ParameterItem.tsx +2 -3
- package/src/app/[variants]/(main)/community/(detail)/provider/features/Details/Nav.tsx +11 -10
- package/src/app/[variants]/(main)/community/(detail)/provider/features/Header.tsx +10 -9
- package/src/app/[variants]/(main)/community/(list)/(home)/index.tsx +1 -1
- package/src/app/[variants]/(main)/community/(list)/assistant/features/Category/useCategory.tsx +1 -1
- package/src/app/[variants]/(main)/community/(list)/features/SortButton/index.tsx +2 -3
- package/src/app/[variants]/(main)/community/_layout/Sidebar/Header/Nav.tsx +1 -1
- package/src/app/[variants]/(main)/community/features/CreateButton/Inner.tsx +1 -1
- package/src/app/[variants]/(main)/community/features/CreateButton/index.tsx +1 -1
- package/src/app/[variants]/(main)/community/features/Search.tsx +1 -2
- package/src/app/[variants]/(main)/community/features/Title.tsx +5 -5
- package/src/app/[variants]/(main)/group/_layout/GroupIdSync.tsx +12 -1
- package/src/app/[variants]/(main)/group/_layout/Sidebar/Header/Nav.tsx +1 -1
- package/src/app/[variants]/(main)/group/_layout/Sidebar/Topic/AllTopicsDrawer/index.tsx +1 -1
- package/src/app/[variants]/(main)/group/_layout/Sidebar/Topic/hooks/useThreadNavigation.ts +1 -1
- package/src/app/[variants]/(main)/group/features/Conversation/Header/ShareButton/index.tsx +1 -1
- package/src/app/[variants]/(main)/group/features/TelemetryNotification.tsx +2 -3
- package/src/app/[variants]/(main)/home/_layout/Body/Agent/AllAgentsDrawer/index.tsx +1 -1
- package/src/app/[variants]/(main)/hooks/useActiveTabKey.ts +6 -11
- package/src/app/[variants]/(main)/image/NotSupportClient.tsx +4 -3
- package/src/app/[variants]/(main)/image/_layout/ConfigPanel/components/ImageUpload.tsx +1 -1
- package/src/app/[variants]/(main)/image/_layout/ConfigPanel/components/MultiImagesUpload/ImageManageModal.tsx +1 -1
- package/src/app/[variants]/(main)/image/_layout/ConfigPanel/components/MultiImagesUpload/index.tsx +1 -1
- package/src/app/[variants]/(main)/memory/(home)/features/RoleTagCloud/index.tsx +1 -1
- package/src/app/[variants]/(main)/memory/_layout/Sidebar/Header/Nav.tsx +1 -1
- package/src/app/[variants]/(main)/memory/features/SourceLink.tsx +1 -1
- package/src/app/[variants]/(main)/page/_layout/Body/AllPagesDrawer/index.tsx +1 -1
- package/src/app/[variants]/(main)/settings/about/features/ItemCard.tsx +2 -3
- package/src/app/[variants]/(main)/settings/about/features/ItemLink.tsx +2 -3
- package/src/app/[variants]/(main)/settings/about/features/Version.tsx +6 -7
- package/src/app/[variants]/(main)/settings/features/SettingsContent.tsx +1 -1
- package/src/app/[variants]/(main)/settings/features/UpgradeAlert.tsx +4 -4
- package/src/app/[variants]/(main)/settings/provider/(list)/Footer.tsx +2 -2
- package/src/app/[variants]/(main)/settings/provider/detail/index.tsx +1 -1
- package/src/app/[variants]/(main)/settings/provider/detail/ollama/CheckError.tsx +1 -1
- package/src/app/[variants]/(main)/settings/provider/features/ProviderConfig/index.tsx +12 -6
- package/src/app/[variants]/(main)/settings/security/index.tsx +1 -1
- package/src/app/[variants]/(main)/settings/stats/features/overview/ShareButton/ShareModal.tsx +1 -1
- package/src/app/[variants]/(main)/settings/stats/features/rankings/AssistantsRank.tsx +1 -1
- package/src/app/[variants]/(main)/settings/stats/features/rankings/TopicsRank.tsx +1 -1
- package/src/app/[variants]/(mobile)/_layout/index.tsx +1 -1
- package/src/app/[variants]/(mobile)/chat/settings/features/AgentInfoDescription/index.tsx +1 -1
- package/src/app/[variants]/(mobile)/chat/settings/features/SettingButton.tsx +1 -1
- package/src/app/[variants]/(mobile)/router/index.tsx +1 -1
- package/src/app/[variants]/page.tsx +1 -1
- package/src/app/[variants]/router/index.tsx +1 -1
- package/src/components/404/index.tsx +4 -4
- package/src/components/Analytics/index.tsx +1 -1
- package/src/components/BrandWatermark/index.tsx +4 -4
- package/src/components/Branding/ProductLogo/Custom.tsx +1 -1
- package/src/components/Error/index.tsx +3 -4
- package/src/components/GoBack/index.tsx +2 -2
- package/src/components/LabsModal/LabCard.tsx +1 -1
- package/src/components/Link.tsx +25 -5
- package/src/components/OllamaSetupGuide/index.tsx +5 -4
- package/src/components/WebFavicon/index.tsx +1 -1
- package/src/components/client/ClientResponsiveContent/index.tsx +1 -1
- package/src/components/client/ClientResponsiveLayout.tsx +1 -1
- package/src/components/mdx/Image.tsx +1 -1
- package/src/components/mdx/Link.tsx +26 -9
- package/src/features/AlertBanner/CloudBanner.tsx +2 -3
- package/src/features/ChatInput/ActionBar/Model/ControlsForm.tsx +8 -7
- package/src/features/ChatInput/ActionBar/Params/Controls.tsx +1 -3
- package/src/features/ChatInput/ActionBar/Token/index.tsx +1 -1
- package/src/features/ChatInput/Mobile/index.tsx +1 -1
- package/src/features/Conversation/ChatItem/components/MessageContent/index.tsx +1 -1
- package/src/features/Conversation/Error/OllamaBizError/index.tsx +1 -1
- package/src/features/Conversation/Error/OllamaSetupGuide/Desktop.tsx +1 -2
- package/src/features/Conversation/Error/index.tsx +1 -1
- package/src/features/Conversation/Messages/AssistantGroup/Tool/Actions/Settings.tsx +1 -1
- package/src/features/Conversation/Messages/AssistantGroup/Tool/index.tsx +1 -1
- package/src/features/Conversation/Messages/AssistantGroup/index.tsx +1 -1
- package/src/features/Conversation/Messages/Tool/Tool/index.tsx +1 -1
- package/src/features/Conversation/Messages/components/SearchGrounding.tsx +1 -1
- package/src/features/DataImporter/Error.tsx +3 -3
- package/src/features/DevPanel/CacheViewer/cacheProvider.tsx +2 -1
- package/src/features/DevPanel/MetadataViewer/Og.tsx +1 -1
- package/src/features/DevPanel/features/FloatPanel.tsx +1 -1
- package/src/features/DevPanel/features/Table/TooltipContent.tsx +3 -4
- package/src/features/DevPanel/index.tsx +1 -1
- package/src/features/EditorCanvas/InlineToolbar.tsx +1 -6
- package/src/features/FileViewer/NotSupport/index.tsx +2 -3
- package/src/features/Follow/index.tsx +8 -9
- package/src/features/LibraryModal/AddFilesToKnowledgeBase/SelectForm.tsx +2 -2
- package/src/features/MCP/MCPInstallProgress/InstallError/ErrorDetails.tsx +61 -83
- package/src/features/MCP/Scores.tsx +1 -1
- package/src/features/MCPPluginDetail/Nav.tsx +8 -8
- package/src/features/MCPPluginDetail/Overview/TagList.tsx +1 -1
- package/src/features/MobileTabBar/index.tsx +2 -1
- package/src/features/OllamaSetupGuide/Desktop.tsx +1 -2
- package/src/features/PWAInstall/Install.tsx +1 -1
- package/src/features/PWAInstall/index.tsx +1 -1
- package/src/features/PluginDevModal/MCPManifestForm/index.tsx +30 -3
- package/src/features/PluginStore/McpList/index.tsx +1 -1
- package/src/features/PluginStore/PluginList/Detail/Header.tsx +6 -6
- package/src/features/PluginsUI/Render/DefaultType/index.tsx +1 -1
- package/src/features/Portal/Artifacts/Body/Renderer/index.tsx +1 -1
- package/src/features/ResourceManager/components/ChunkDrawer/index.tsx +1 -1
- package/src/features/ResourceManager/components/Explorer/ListView/Skeleton.tsx +26 -26
- package/src/features/ResourceManager/components/Explorer/ToolBar/BatchActionsDropdown.tsx +147 -149
- package/src/features/ResourceManager/index.tsx +1 -1
- package/src/features/Setting/Footer.tsx +4 -5
- package/src/features/User/UserPanel/PanelContent.tsx +1 -1
- package/src/hooks/useActiveTabKey.ts +6 -3
- package/src/hooks/useIsSingleMode.test.ts +10 -24
- package/src/hooks/useIsSingleMode.ts +4 -2
- package/src/hooks/useIsSubSlug.ts +2 -1
- package/src/hooks/useQuery.ts +5 -5
- package/src/layout/GlobalProvider/AppTheme.tsx +2 -2
- package/src/layout/GlobalProvider/StyleRegistry.tsx +1 -1
- package/src/layout/GlobalProvider/useUserStateRedirect.ts +13 -25
- package/src/libs/mcp/types.ts +31 -0
- package/src/libs/next/Image.tsx +13 -0
- package/src/libs/next/Link.tsx +13 -0
- package/src/libs/next/dynamic.tsx +13 -0
- package/src/libs/next/index.ts +22 -0
- package/src/libs/next/navigation.ts +22 -0
- package/src/libs/router/Link.tsx +30 -0
- package/src/libs/router/index.ts +18 -0
- package/src/libs/router/navigation.ts +72 -0
- package/src/server/modules/AgentRuntime/AgentStateManager.ts +5 -1
- package/src/store/chat/slices/portal/selectors.test.ts +5 -15
- package/src/store/page/index.ts +1 -1
- package/src/store/page/slices/crud/index.ts +1 -1
- package/src/store/tool/slices/mcpStore/action.ts +26 -11
- package/src/app/[variants]/(main)/hooks/usePathname.ts +0 -10
- package/src/app/[variants]/(main)/hooks/useQuery.ts +0 -12
- package/src/app/[variants]/(main)/hooks/useRouter.ts +0 -22
- package/src/app/[variants]/(main)/hooks/useSearchParams.ts +0 -11
|
@@ -1,5 +1,9 @@
|
|
|
1
|
+
import { MemorySourceType } from '@lobechat/types';
|
|
2
|
+
import { Client } from '@upstash/qstash';
|
|
1
3
|
import { serve } from '@upstash/workflow/nextjs';
|
|
4
|
+
|
|
2
5
|
import type { ListTopicsForMemoryExtractorCursor } from '@/database/models/topic';
|
|
6
|
+
import { parseMemoryExtractionConfig } from '@/server/globalConfig/parseMemoryExtractionConfig';
|
|
3
7
|
import {
|
|
4
8
|
MemoryExtractionExecutor,
|
|
5
9
|
type MemoryExtractionPayloadInput,
|
|
@@ -8,135 +12,143 @@ import {
|
|
|
8
12
|
normalizeMemoryExtractionPayload,
|
|
9
13
|
} from '@/server/services/memory/userMemory/extract';
|
|
10
14
|
import { forEachBatchSequential } from '@/server/services/memory/userMemory/topicBatching';
|
|
11
|
-
import { MemorySourceType } from '@lobechat/types';
|
|
12
|
-
import { parseMemoryExtractionConfig } from '@/server/globalConfig/parseMemoryExtractionConfig';
|
|
13
|
-
import { Client } from '@upstash/qstash'
|
|
14
15
|
|
|
15
16
|
const TOPIC_PAGE_SIZE = 50;
|
|
16
17
|
const TOPIC_BATCH_SIZE = 4;
|
|
17
18
|
|
|
18
19
|
const { upstashWorkflowExtraHeaders } = parseMemoryExtractionConfig();
|
|
19
20
|
|
|
20
|
-
export const { POST } = serve<MemoryExtractionPayloadInput>(
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
const executor = await MemoryExtractionExecutor.create();
|
|
30
|
-
|
|
31
|
-
const scheduleNextPage = async (userId: string, cursorCreatedAt: Date, cursorId: string) => {
|
|
32
|
-
await MemoryExtractionWorkflowService.triggerProcessUserTopics({
|
|
33
|
-
...buildWorkflowPayloadInput({
|
|
34
|
-
...params,
|
|
35
|
-
topicCursor: {
|
|
36
|
-
createdAt: cursorCreatedAt.toISOString(),
|
|
37
|
-
id: cursorId,
|
|
38
|
-
userId,
|
|
39
|
-
},
|
|
40
|
-
topicIds: [],
|
|
41
|
-
userId,
|
|
42
|
-
userIds: [userId],
|
|
43
|
-
}),
|
|
44
|
-
}, { extraHeaders: upstashWorkflowExtraHeaders });
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
for (const userId of params.userIds) {
|
|
48
|
-
const topicCursor =
|
|
49
|
-
params.topicCursor && params.topicCursor.userId === userId
|
|
50
|
-
? {
|
|
51
|
-
createdAt: new Date(params.topicCursor.createdAt),
|
|
52
|
-
id: params.topicCursor.id,
|
|
53
|
-
}
|
|
54
|
-
: undefined;
|
|
21
|
+
export const { POST } = serve<MemoryExtractionPayloadInput>(
|
|
22
|
+
async (context) => {
|
|
23
|
+
const params = normalizeMemoryExtractionPayload(context.requestPayload || {});
|
|
24
|
+
if (!params.userIds.length) {
|
|
25
|
+
return { message: 'No user ids provided for topic processing.' };
|
|
26
|
+
}
|
|
27
|
+
if (!params.sources.includes(MemorySourceType.ChatTopic)) {
|
|
28
|
+
return { message: 'No supported sources requested, skip topic processing.' };
|
|
29
|
+
}
|
|
55
30
|
|
|
56
|
-
const
|
|
57
|
-
params.topicIds && params.topicIds.length > 0
|
|
58
|
-
? await context.run(
|
|
59
|
-
`memory:user-memory:extract:users:${userId}:filter-topic-ids`,
|
|
60
|
-
async () => {
|
|
61
|
-
const filtered = await executor.filterTopicIdsForUser(userId, params.topicIds);
|
|
62
|
-
return filtered.length > 0 ? filtered : undefined;
|
|
63
|
-
},
|
|
64
|
-
)
|
|
65
|
-
: undefined;
|
|
31
|
+
const executor = await MemoryExtractionExecutor.create();
|
|
66
32
|
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
cursor: topicCursor,
|
|
76
|
-
forceAll: params.forceAll,
|
|
77
|
-
forceTopics: params.forceTopics,
|
|
78
|
-
from: params.from,
|
|
79
|
-
to: params.to,
|
|
33
|
+
const scheduleNextPage = async (userId: string, cursorCreatedAt: Date, cursorId: string) => {
|
|
34
|
+
await MemoryExtractionWorkflowService.triggerProcessUserTopics(
|
|
35
|
+
{
|
|
36
|
+
...buildWorkflowPayloadInput({
|
|
37
|
+
...params,
|
|
38
|
+
topicCursor: {
|
|
39
|
+
createdAt: cursorCreatedAt.toISOString(),
|
|
40
|
+
id: cursorId,
|
|
80
41
|
userId,
|
|
81
42
|
},
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
43
|
+
topicIds: [],
|
|
44
|
+
userId,
|
|
45
|
+
userIds: [userId],
|
|
46
|
+
}),
|
|
47
|
+
},
|
|
48
|
+
{ extraHeaders: upstashWorkflowExtraHeaders },
|
|
49
|
+
);
|
|
50
|
+
};
|
|
85
51
|
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
52
|
+
for (const userId of params.userIds) {
|
|
53
|
+
const topicCursor =
|
|
54
|
+
params.topicCursor && params.topicCursor.userId === userId
|
|
55
|
+
? {
|
|
56
|
+
createdAt: new Date(params.topicCursor.createdAt),
|
|
57
|
+
id: params.topicCursor.id,
|
|
58
|
+
}
|
|
59
|
+
: undefined;
|
|
90
60
|
|
|
91
|
-
|
|
61
|
+
const topicsFromPayload =
|
|
62
|
+
params.topicIds && params.topicIds.length > 0
|
|
63
|
+
? await context.run(
|
|
64
|
+
`memory:user-memory:extract:users:${userId}:filter-topic-ids`,
|
|
65
|
+
async () => {
|
|
66
|
+
const filtered = await executor.filterTopicIdsForUser(userId, params.topicIds);
|
|
67
|
+
return filtered.length > 0 ? filtered : undefined;
|
|
68
|
+
},
|
|
69
|
+
)
|
|
70
|
+
: undefined;
|
|
92
71
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
`memory:user-memory:extract:users:${userId}:process-topics-batch:${batchIndex}`,
|
|
72
|
+
const topicBatch = await context.run<{
|
|
73
|
+
cursor?: ListTopicsForMemoryExtractorCursor;
|
|
74
|
+
ids: string[];
|
|
75
|
+
}>(
|
|
76
|
+
`memory:user-memory:extract:users:${userId}:list-topics:${topicCursor?.id || 'root'}`,
|
|
99
77
|
() =>
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
78
|
+
topicsFromPayload && topicsFromPayload.length > 0
|
|
79
|
+
? Promise.resolve({ ids: topicsFromPayload })
|
|
80
|
+
: executor.getTopicsForUser(
|
|
81
|
+
{
|
|
82
|
+
cursor: topicCursor,
|
|
83
|
+
forceAll: params.forceAll,
|
|
84
|
+
forceTopics: params.forceTopics,
|
|
85
|
+
from: params.from,
|
|
86
|
+
to: params.to,
|
|
87
|
+
userId,
|
|
88
|
+
},
|
|
89
|
+
TOPIC_PAGE_SIZE,
|
|
90
|
+
),
|
|
107
91
|
);
|
|
108
|
-
});
|
|
109
92
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
// NOTICE: Upstash Workflow only supports serializable data into plain JSON,
|
|
115
|
-
// this causes the Date object to be converted into string when passed as parameter from
|
|
116
|
-
// context to child workflow. So we need to convert it back to Date object here.
|
|
117
|
-
const createdAt = new Date(cursor.createdAt);
|
|
118
|
-
if (Number.isNaN(createdAt.getTime())) {
|
|
119
|
-
throw new Error('Invalid cursor date when scheduling next topic page');
|
|
120
|
-
}
|
|
93
|
+
const ids = topicBatch.ids;
|
|
94
|
+
if (!ids.length) {
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
121
97
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
)
|
|
98
|
+
const cursor = 'cursor' in topicBatch ? topicBatch.cursor : undefined;
|
|
99
|
+
|
|
100
|
+
await forEachBatchSequential(ids, TOPIC_BATCH_SIZE, async (topicIds, batchIndex) => {
|
|
101
|
+
// NOTICE: We trigger via QStash instead of context.invoke because invoke only swaps the last path
|
|
102
|
+
// segment with the workflowId. If we invoked directly from /process-user-topics, child workflow
|
|
103
|
+
// URLs would inherit that base and lose the desired /process-topics/workflows prefix.
|
|
104
|
+
await context.run(
|
|
105
|
+
`memory:user-memory:extract:users:${userId}:process-topics-batch:${batchIndex}`,
|
|
106
|
+
() =>
|
|
107
|
+
MemoryExtractionWorkflowService.triggerProcessTopics(
|
|
108
|
+
{
|
|
109
|
+
...buildWorkflowPayloadInput(params),
|
|
110
|
+
topicCursor: undefined,
|
|
111
|
+
topicIds,
|
|
112
|
+
userId,
|
|
113
|
+
userIds: [userId],
|
|
114
|
+
},
|
|
115
|
+
{ extraHeaders: upstashWorkflowExtraHeaders },
|
|
116
|
+
),
|
|
117
|
+
);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
if (!topicsFromPayload && cursor) {
|
|
121
|
+
await context.run(
|
|
122
|
+
`memory:user-memory:extract:users:${userId}:topics:${cursor.id}:schedule-next-batch`,
|
|
123
|
+
() => {
|
|
124
|
+
// NOTICE: Upstash Workflow only supports serializable data into plain JSON,
|
|
125
|
+
// this causes the Date object to be converted into string when passed as parameter from
|
|
126
|
+
// context to child workflow. So we need to convert it back to Date object here.
|
|
127
|
+
const createdAt = new Date(cursor.createdAt);
|
|
128
|
+
if (Number.isNaN(createdAt.getTime())) {
|
|
129
|
+
throw new Error('Invalid cursor date when scheduling next topic page');
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
scheduleNextPage(userId, createdAt, cursor.id);
|
|
133
|
+
},
|
|
134
|
+
);
|
|
135
|
+
}
|
|
125
136
|
}
|
|
126
|
-
}
|
|
127
137
|
|
|
128
|
-
|
|
129
|
-
},
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
})
|
|
138
|
+
return { processedUsers: params.userIds.length };
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
// NOTICE(@nekomeowww): Here as scenarios like Vercel Deployment Protection,
|
|
142
|
+
// intermediate context.run(...) won't offer customizable headers like context.trigger(...) / client.trigger(...)
|
|
143
|
+
// for passing additional headers, we have to provide a custom QStash client with the required headers here.
|
|
144
|
+
//
|
|
145
|
+
// Refer to the doc for more details:
|
|
146
|
+
// https://upstash.com/docs/workflow/troubleshooting/vercel#step-2-pass-header-when-triggering
|
|
147
|
+
qstashClient: new Client({
|
|
148
|
+
headers: {
|
|
149
|
+
...upstashWorkflowExtraHeaders,
|
|
150
|
+
},
|
|
151
|
+
token: process.env.QSTASH_TOKEN!,
|
|
152
|
+
}),
|
|
153
|
+
},
|
|
154
|
+
);
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import { Client } from '@upstash/qstash';
|
|
1
2
|
import { serve } from '@upstash/workflow/nextjs';
|
|
2
3
|
import { chunk } from 'es-toolkit/compat';
|
|
3
|
-
import { Client } from '@upstash/qstash'
|
|
4
4
|
|
|
5
|
+
import { parseMemoryExtractionConfig } from '@/server/globalConfig/parseMemoryExtractionConfig';
|
|
5
6
|
import {
|
|
6
7
|
MemoryExtractionExecutor,
|
|
7
8
|
type MemoryExtractionPayloadInput,
|
|
@@ -9,82 +10,90 @@ import {
|
|
|
9
10
|
buildWorkflowPayloadInput,
|
|
10
11
|
normalizeMemoryExtractionPayload,
|
|
11
12
|
} from '@/server/services/memory/userMemory/extract';
|
|
12
|
-
import { parseMemoryExtractionConfig } from '@/server/globalConfig/parseMemoryExtractionConfig';
|
|
13
13
|
|
|
14
14
|
const USER_PAGE_SIZE = 50;
|
|
15
15
|
const USER_BATCH_SIZE = 10;
|
|
16
16
|
|
|
17
17
|
const { upstashWorkflowExtraHeaders } = parseMemoryExtractionConfig();
|
|
18
18
|
|
|
19
|
-
export const { POST } = serve<MemoryExtractionPayloadInput>(
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
19
|
+
export const { POST } = serve<MemoryExtractionPayloadInput>(
|
|
20
|
+
async (context) => {
|
|
21
|
+
const params = normalizeMemoryExtractionPayload(context.requestPayload || {});
|
|
22
|
+
if (params.sources.length === 0) {
|
|
23
|
+
return { message: 'No sources provided, skip memory extraction.' };
|
|
24
|
+
}
|
|
24
25
|
|
|
25
|
-
|
|
26
|
+
const executor = await MemoryExtractionExecutor.create();
|
|
26
27
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
28
|
+
// NOTICE: Upstash Workflow only supports serializable data into plain JSON,
|
|
29
|
+
// this causes the Date object to be converted into string when passed as parameter from
|
|
30
|
+
// context to child workflow. So we need to convert it back to Date object here.
|
|
31
|
+
const userCursor = params.userCursor
|
|
32
|
+
? { createdAt: new Date(params.userCursor.createdAt), id: params.userCursor.id }
|
|
33
|
+
: undefined;
|
|
33
34
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
35
|
+
const userBatch = await context.run('memory:user-memory:extract:get-users', () =>
|
|
36
|
+
params.userIds.length > 0
|
|
37
|
+
? { ids: params.userIds }
|
|
38
|
+
: executor.getUsers(USER_PAGE_SIZE, userCursor),
|
|
39
|
+
);
|
|
39
40
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
41
|
+
const ids = userBatch.ids;
|
|
42
|
+
if (ids.length === 0) {
|
|
43
|
+
return { message: 'No users to process for memory extraction.' };
|
|
44
|
+
}
|
|
44
45
|
|
|
45
|
-
|
|
46
|
+
const cursor = 'cursor' in userBatch ? userBatch.cursor : undefined;
|
|
46
47
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
48
|
+
const batches = chunk(ids, USER_BATCH_SIZE);
|
|
49
|
+
await Promise.all(
|
|
50
|
+
batches.map((userIds) =>
|
|
51
|
+
context.run(`memory:user-memory:extract:users:process-topic-batches`, () =>
|
|
52
|
+
MemoryExtractionWorkflowService.triggerProcessUserTopics(
|
|
53
|
+
{
|
|
54
|
+
...buildWorkflowPayloadInput(params),
|
|
55
|
+
topicCursor: undefined,
|
|
56
|
+
userId: userIds[0],
|
|
57
|
+
userIds,
|
|
58
|
+
},
|
|
59
|
+
{ extraHeaders: upstashWorkflowExtraHeaders },
|
|
60
|
+
),
|
|
61
|
+
),
|
|
57
62
|
),
|
|
58
|
-
),
|
|
59
|
-
);
|
|
60
|
-
|
|
61
|
-
if (params.userIds.length === 0 && cursor) {
|
|
62
|
-
await context.run('memory:user-memory:extract:users:schedule-next-user-batch', () =>
|
|
63
|
-
MemoryExtractionWorkflowService.triggerProcessUsers({
|
|
64
|
-
...buildWorkflowPayloadInput({
|
|
65
|
-
...params,
|
|
66
|
-
userCursor: { createdAt: cursor.createdAt.toISOString(), id: cursor.id },
|
|
67
|
-
}),
|
|
68
|
-
}, { extraHeaders: upstashWorkflowExtraHeaders }),
|
|
69
63
|
);
|
|
70
|
-
}
|
|
71
64
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
}
|
|
65
|
+
if (params.userIds.length === 0 && cursor) {
|
|
66
|
+
await context.run('memory:user-memory:extract:users:schedule-next-user-batch', () =>
|
|
67
|
+
MemoryExtractionWorkflowService.triggerProcessUsers(
|
|
68
|
+
{
|
|
69
|
+
...buildWorkflowPayloadInput({
|
|
70
|
+
...params,
|
|
71
|
+
userCursor: { createdAt: cursor.createdAt.toISOString(), id: cursor.id },
|
|
72
|
+
}),
|
|
73
|
+
},
|
|
74
|
+
{ extraHeaders: upstashWorkflowExtraHeaders },
|
|
75
|
+
),
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return {
|
|
80
|
+
batches: batches.length,
|
|
81
|
+
nextCursor: cursor ? cursor.id : null,
|
|
82
|
+
processedUsers: ids.length,
|
|
83
|
+
};
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
// NOTICE(@nekomeowww): Here as scenarios like Vercel Deployment Protection,
|
|
87
|
+
// intermediate context.run(...) won't offer customizable headers like context.trigger(...) / client.trigger(...)
|
|
88
|
+
// for passing additional headers, we have to provide a custom QStash client with the required headers here.
|
|
89
|
+
//
|
|
90
|
+
// Refer to the doc for more details:
|
|
91
|
+
// https://upstash.com/docs/workflow/troubleshooting/vercel#step-2-pass-header-when-triggering
|
|
92
|
+
qstashClient: new Client({
|
|
93
|
+
headers: {
|
|
94
|
+
...upstashWorkflowExtraHeaders,
|
|
95
|
+
},
|
|
96
|
+
token: process.env.QSTASH_TOKEN!,
|
|
97
|
+
}),
|
|
98
|
+
},
|
|
99
|
+
);
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { SiDiscord } from '@icons-pack/react-simple-icons';
|
|
4
4
|
import { Alert, Button, Flexbox, Icon } from '@lobehub/ui';
|
|
5
|
-
import Link from 'next/
|
|
5
|
+
import Link from '@/libs/next/Link';
|
|
6
6
|
import { parseAsString, useQueryState } from 'nuqs';
|
|
7
7
|
import { memo } from 'react';
|
|
8
8
|
import { useTranslation } from 'react-i18next';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { SignIn } from '@clerk/nextjs';
|
|
2
2
|
import { BRANDING_NAME } from '@lobechat/business-const';
|
|
3
|
-
import { notFound } from 'next/navigation';
|
|
3
|
+
import { notFound } from '@/libs/next/navigation';
|
|
4
4
|
|
|
5
5
|
import { enableClerk } from '@/envs/auth';
|
|
6
6
|
import { metadataModule } from '@/server/metadata';
|
|
@@ -8,7 +8,7 @@ import { Col, Flex, Row } from 'antd';
|
|
|
8
8
|
import { createStaticStyles } from 'antd-style';
|
|
9
9
|
import { AuthError } from 'next-auth';
|
|
10
10
|
import { signIn } from 'next-auth/react';
|
|
11
|
-
import { useRouter, useSearchParams } from 'next/navigation';
|
|
11
|
+
import { useRouter, useSearchParams } from '@/libs/next/navigation';
|
|
12
12
|
import { memo, useState } from 'react';
|
|
13
13
|
import { useTranslation } from 'react-i18next';
|
|
14
14
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { Button, Flexbox, FluentEmoji, Highlighter, Text } from '@lobehub/ui';
|
|
4
4
|
import { Result } from 'antd';
|
|
5
|
-
import Link from 'next/
|
|
5
|
+
import Link from '@/libs/next/Link';
|
|
6
6
|
import { parseAsString, useQueryState } from 'nuqs';
|
|
7
7
|
import React, { memo } from 'react';
|
|
8
8
|
import { useTranslation } from 'react-i18next';
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { FluentEmoji, Text } from '@lobehub/ui';
|
|
4
4
|
import { Result } from 'antd';
|
|
5
|
-
import { useSearchParams } from 'next/navigation';
|
|
5
|
+
import { useSearchParams } from '@/libs/next/navigation';
|
|
6
6
|
import React, { memo, useEffect, useState } from 'react';
|
|
7
7
|
import { useTranslation } from 'react-i18next';
|
|
8
8
|
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
import { Button } from '@lobehub/ui';
|
|
4
4
|
import { ChevronLeftIcon } from 'lucide-react';
|
|
5
|
-
import Link from 'next/
|
|
6
|
-
import { useRouter, useSearchParams } from 'next/navigation';
|
|
5
|
+
import Link from '@/libs/next/Link';
|
|
6
|
+
import { useRouter, useSearchParams } from '@/libs/next/navigation';
|
|
7
7
|
import { useTranslation } from 'react-i18next';
|
|
8
8
|
|
|
9
9
|
import AuthCard from '../../../../features/AuthCard';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ENABLE_BUSINESS_FEATURES } from '@lobechat/business-const';
|
|
2
2
|
import { Form } from 'antd';
|
|
3
|
-
import { useRouter, useSearchParams } from 'next/navigation';
|
|
3
|
+
import { useRouter, useSearchParams } from '@/libs/next/navigation';
|
|
4
4
|
import { useEffect, useState } from 'react';
|
|
5
5
|
import { useTranslation } from 'react-i18next';
|
|
6
6
|
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
import { Button, Icon, Text } from '@lobehub/ui';
|
|
4
4
|
import { Form, Input } from 'antd';
|
|
5
5
|
import { Lock, Mail } from 'lucide-react';
|
|
6
|
-
import Link from 'next/
|
|
7
|
-
import { useSearchParams } from 'next/navigation';
|
|
6
|
+
import Link from '@/libs/next/Link';
|
|
7
|
+
import { useSearchParams } from '@/libs/next/navigation';
|
|
8
8
|
import { useEffect } from 'react';
|
|
9
9
|
import { useTranslation } from 'react-i18next';
|
|
10
10
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ENABLE_BUSINESS_FEATURES } from '@lobechat/business-const';
|
|
2
2
|
import { form } from 'motion/react-m';
|
|
3
|
-
import { useRouter, useSearchParams } from 'next/navigation';
|
|
3
|
+
import { useRouter, useSearchParams } from '@/libs/next/navigation';
|
|
4
4
|
import { useState } from 'react';
|
|
5
5
|
import { useTranslation } from 'react-i18next';
|
|
6
6
|
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
import { Button } from '@lobehub/ui';
|
|
4
4
|
import { ChevronLeftIcon } from 'lucide-react';
|
|
5
|
-
import Link from 'next/
|
|
6
|
-
import { useSearchParams } from 'next/navigation';
|
|
5
|
+
import Link from '@/libs/next/Link';
|
|
6
|
+
import { useSearchParams } from '@/libs/next/navigation';
|
|
7
7
|
import { useTranslation } from 'react-i18next';
|
|
8
8
|
|
|
9
9
|
import AuthCard from '../../../../features/AuthCard';
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
import { TITLE_BAR_HEIGHT } from '@lobechat/desktop-bridge';
|
|
4
4
|
import { Flexbox } from '@lobehub/ui';
|
|
5
5
|
import { cx } from 'antd-style';
|
|
6
|
-
import dynamic from 'next/dynamic';
|
|
7
6
|
import { type FC, Suspense, lazy } from 'react';
|
|
8
7
|
import { HotkeysProvider } from 'react-hotkeys-hook';
|
|
9
8
|
import { Outlet } from 'react-router-dom';
|
|
@@ -20,6 +19,7 @@ import { useFeedbackModal } from '@/hooks/useFeedbackModal';
|
|
|
20
19
|
import { usePlatform } from '@/hooks/usePlatform';
|
|
21
20
|
import { MarketAuthProvider } from '@/layout/AuthProvider/MarketAuth';
|
|
22
21
|
import CmdkLazy from '@/layout/GlobalProvider/CmdkLazy';
|
|
22
|
+
import dynamic from '@/libs/next/dynamic';
|
|
23
23
|
import { featureFlagsSelectors, useServerConfigStore } from '@/store/serverConfig';
|
|
24
24
|
import { HotkeyScopeEnum } from '@/types/hotkey';
|
|
25
25
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { useUnmount } from 'ahooks';
|
|
1
|
+
import { usePrevious, useUnmount } from 'ahooks';
|
|
2
|
+
import { useEffect } from 'react';
|
|
2
3
|
import { useParams } from 'react-router-dom';
|
|
3
4
|
import { createStoreUpdater } from 'zustand-utils';
|
|
4
5
|
|
|
@@ -9,10 +10,20 @@ const AgentIdSync = () => {
|
|
|
9
10
|
const useStoreUpdater = createStoreUpdater(useAgentStore);
|
|
10
11
|
const useChatStoreUpdater = createStoreUpdater(useChatStore);
|
|
11
12
|
const params = useParams<{ aid?: string }>();
|
|
13
|
+
const prevAgentId = usePrevious(params.aid);
|
|
12
14
|
|
|
13
15
|
useStoreUpdater('activeAgentId', params.aid);
|
|
14
16
|
useChatStoreUpdater('activeAgentId', params.aid ?? '');
|
|
15
17
|
|
|
18
|
+
// Reset activeTopicId when switching to a different agent
|
|
19
|
+
// This prevents messages from being saved to the wrong topic bucket
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
// Only reset topic when switching between agents (not on initial mount)
|
|
22
|
+
if (prevAgentId !== undefined && prevAgentId !== params.aid) {
|
|
23
|
+
useChatStore.getState().switchTopic(null, { skipRefreshMessage: true });
|
|
24
|
+
}
|
|
25
|
+
}, [params.aid, prevAgentId]);
|
|
26
|
+
|
|
16
27
|
// Clear activeAgentId when unmounting (leaving chat page)
|
|
17
28
|
useUnmount(() => {
|
|
18
29
|
useAgentStore.setState({ activeAgentId: undefined });
|
|
@@ -6,8 +6,8 @@ import { memo, useCallback } from 'react';
|
|
|
6
6
|
import { useTranslation } from 'react-i18next';
|
|
7
7
|
import { useParams } from 'react-router-dom';
|
|
8
8
|
|
|
9
|
-
import { useRouter } from '@/app/[variants]/(main)/hooks/useRouter';
|
|
10
9
|
import type { AgentCronJob } from '@/database/schemas/agentCronJob';
|
|
10
|
+
import { useRouter } from '@/libs/router/navigation';
|
|
11
11
|
|
|
12
12
|
import Actions from './Actions';
|
|
13
13
|
import CronTopicItem from './CronTopicItem';
|