@lobehub/lobehub 2.0.0-next.160 → 2.0.0-next.162
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/.env.example +10 -0
- package/CHANGELOG.md +42 -0
- package/changelog/v1.json +14 -0
- package/e2e/src/steps/hooks.ts +1 -0
- package/locales/ar/authError.json +40 -0
- package/locales/ar/setting.json +25 -0
- package/locales/bg-BG/authError.json +40 -0
- package/locales/bg-BG/setting.json +25 -0
- package/locales/de-DE/authError.json +40 -0
- package/locales/de-DE/setting.json +25 -0
- package/locales/en-US/authError.json +40 -0
- package/locales/en-US/setting.json +25 -0
- package/locales/es-ES/authError.json +40 -0
- package/locales/es-ES/setting.json +25 -0
- package/locales/fa-IR/authError.json +40 -0
- package/locales/fa-IR/setting.json +25 -0
- package/locales/fr-FR/authError.json +40 -0
- package/locales/fr-FR/setting.json +25 -0
- package/locales/it-IT/authError.json +40 -0
- package/locales/it-IT/setting.json +25 -0
- package/locales/ja-JP/authError.json +40 -0
- package/locales/ja-JP/setting.json +25 -0
- package/locales/ko-KR/authError.json +40 -0
- package/locales/ko-KR/setting.json +25 -0
- package/locales/nl-NL/authError.json +40 -0
- package/locales/nl-NL/setting.json +25 -0
- package/locales/pl-PL/authError.json +40 -0
- package/locales/pl-PL/setting.json +25 -0
- package/locales/pt-BR/authError.json +40 -0
- package/locales/pt-BR/setting.json +25 -0
- package/locales/ru-RU/authError.json +40 -0
- package/locales/ru-RU/setting.json +25 -0
- package/locales/tr-TR/authError.json +40 -0
- package/locales/tr-TR/setting.json +25 -0
- package/locales/vi-VN/authError.json +40 -0
- package/locales/vi-VN/setting.json +25 -0
- package/locales/zh-CN/authError.json +40 -0
- package/locales/zh-CN/setting.json +25 -0
- package/locales/zh-TW/authError.json +40 -0
- package/locales/zh-TW/setting.json +25 -0
- package/next.config.ts +13 -1
- package/package.json +3 -1
- package/packages/const/src/index.ts +1 -0
- package/packages/const/src/klavis.ts +163 -0
- package/packages/database/migrations/meta/_journal.json +1 -1
- package/packages/database/src/core/migrations.json +1 -1
- package/packages/database/src/models/plugin.ts +1 -1
- package/packages/types/src/message/common/tools.ts +9 -0
- package/packages/types/src/serverConfig.ts +1 -0
- package/packages/types/src/tool/plugin.ts +10 -0
- package/src/app/[variants]/(auth)/auth-error/page.tsx +59 -0
- package/src/auth.ts +13 -48
- package/src/config/klavis.ts +41 -0
- package/src/envs/redis.ts +1 -1
- package/src/features/ChatInput/ActionBar/Tools/KlavisServerItem.tsx +351 -0
- package/src/features/ChatInput/ActionBar/Tools/index.tsx +56 -4
- package/src/features/ChatInput/ActionBar/Tools/useControls.tsx +174 -6
- package/src/features/ChatInput/ActionBar/components/ActionDropdown.tsx +3 -1
- package/src/helpers/toolEngineering/index.test.ts +3 -0
- package/src/helpers/toolEngineering/index.ts +13 -2
- package/src/libs/better-auth/utils/config.ts +91 -0
- package/src/libs/klavis/index.ts +36 -0
- package/src/libs/redis/manager.ts +5 -1
- package/src/libs/redis/redis.test.ts +1 -1
- package/src/libs/redis/upstash.test.ts +9 -5
- package/src/libs/redis/upstash.ts +44 -20
- package/src/locales/default/authError.ts +40 -0
- package/src/locales/default/index.ts +2 -0
- package/src/locales/default/setting.ts +25 -0
- package/src/proxy.ts +1 -0
- package/src/server/globalConfig/index.ts +2 -0
- package/src/server/routers/lambda/index.ts +2 -0
- package/src/server/routers/lambda/klavis.ts +249 -0
- package/src/server/routers/tools/index.ts +2 -0
- package/src/server/routers/tools/klavis.ts +80 -0
- package/src/server/services/mcp/index.ts +61 -15
- package/src/services/import/index.test.ts +658 -0
- package/src/services/mcp.test.ts +1 -1
- package/src/store/chat/slices/aiChat/actions/streamingExecutor.ts +2 -3
- package/src/store/chat/slices/plugin/action.test.ts +0 -1
- package/src/store/chat/slices/plugin/actions/internals.ts +22 -2
- package/src/store/chat/slices/plugin/actions/pluginTypes.ts +108 -0
- package/src/store/serverConfig/index.ts +1 -1
- package/src/store/serverConfig/selectors.ts +1 -0
- package/src/store/tool/initialState.ts +4 -1
- package/src/store/tool/selectors/index.ts +1 -0
- package/src/store/tool/slices/builtin/selectors.ts +25 -3
- package/src/store/tool/slices/klavisStore/action.test.ts +512 -0
- package/src/store/tool/slices/klavisStore/action.ts +375 -0
- package/src/store/tool/slices/klavisStore/index.ts +4 -0
- package/src/store/tool/slices/klavisStore/initialState.ts +25 -0
- package/src/store/tool/slices/klavisStore/selectors.test.ts +371 -0
- package/src/store/tool/slices/klavisStore/selectors.ts +123 -0
- package/src/store/tool/slices/klavisStore/types.ts +100 -0
- package/src/store/tool/slices/plugin/selectors.ts +16 -13
- package/src/store/tool/store.ts +4 -1
|
@@ -6,7 +6,7 @@ import { StateCreator } from 'zustand/vanilla';
|
|
|
6
6
|
|
|
7
7
|
import { ChatStore } from '@/store/chat/store';
|
|
8
8
|
import { useToolStore } from '@/store/tool';
|
|
9
|
-
import { pluginSelectors } from '@/store/tool/selectors';
|
|
9
|
+
import { klavisStoreSelectors, pluginSelectors } from '@/store/tool/selectors';
|
|
10
10
|
import { builtinTools } from '@/tools';
|
|
11
11
|
|
|
12
12
|
import { displayMessageSelectors } from '../../message/selectors';
|
|
@@ -40,11 +40,16 @@ export const pluginInternals: StateCreator<
|
|
|
40
40
|
const toolStoreState = useToolStore.getState();
|
|
41
41
|
const manifests: Record<string, LobeChatPluginManifest> = {};
|
|
42
42
|
|
|
43
|
+
// Track source for each identifier
|
|
44
|
+
const sourceMap: Record<string, 'builtin' | 'plugin' | 'mcp' | 'klavis'> = {};
|
|
45
|
+
|
|
43
46
|
// Get all installed plugins
|
|
44
47
|
const installedPlugins = pluginSelectors.installedPlugins(toolStoreState);
|
|
45
48
|
for (const plugin of installedPlugins) {
|
|
46
49
|
if (plugin.manifest) {
|
|
47
50
|
manifests[plugin.identifier] = plugin.manifest as LobeChatPluginManifest;
|
|
51
|
+
// Check if this plugin has MCP params
|
|
52
|
+
sourceMap[plugin.identifier] = plugin.customParams?.mcp ? 'mcp' : 'plugin';
|
|
48
53
|
}
|
|
49
54
|
}
|
|
50
55
|
|
|
@@ -52,10 +57,25 @@ export const pluginInternals: StateCreator<
|
|
|
52
57
|
for (const tool of builtinTools) {
|
|
53
58
|
if (tool.manifest) {
|
|
54
59
|
manifests[tool.identifier] = tool.manifest as LobeChatPluginManifest;
|
|
60
|
+
sourceMap[tool.identifier] = 'builtin';
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Get all Klavis tools
|
|
65
|
+
const klavisTools = klavisStoreSelectors.klavisAsLobeTools(toolStoreState);
|
|
66
|
+
for (const tool of klavisTools) {
|
|
67
|
+
if (tool.manifest) {
|
|
68
|
+
manifests[tool.identifier] = tool.manifest as LobeChatPluginManifest;
|
|
69
|
+
sourceMap[tool.identifier] = 'klavis';
|
|
55
70
|
}
|
|
56
71
|
}
|
|
57
72
|
|
|
58
|
-
|
|
73
|
+
// Resolve tool calls and add source field
|
|
74
|
+
const resolved = toolNameResolver.resolve(toolCalls, manifests);
|
|
75
|
+
return resolved.map((payload) => ({
|
|
76
|
+
...payload,
|
|
77
|
+
source: sourceMap[payload.identifier],
|
|
78
|
+
}));
|
|
59
79
|
},
|
|
60
80
|
|
|
61
81
|
internal_constructToolsCallingContext: (id: string) => {
|
|
@@ -32,6 +32,11 @@ export interface PluginTypesAction {
|
|
|
32
32
|
*/
|
|
33
33
|
invokeDefaultTypePlugin: (id: string, payload: any) => Promise<string | undefined>;
|
|
34
34
|
|
|
35
|
+
/**
|
|
36
|
+
* Invoke Klavis type plugin
|
|
37
|
+
*/
|
|
38
|
+
invokeKlavisTypePlugin: (id: string, payload: ChatToolPayload) => Promise<string | undefined>;
|
|
39
|
+
|
|
35
40
|
/**
|
|
36
41
|
* Invoke markdown type plugin
|
|
37
42
|
*/
|
|
@@ -60,6 +65,11 @@ export const pluginTypes: StateCreator<
|
|
|
60
65
|
PluginTypesAction
|
|
61
66
|
> = (set, get) => ({
|
|
62
67
|
invokeBuiltinTool: async (id, payload) => {
|
|
68
|
+
// Check if this is a Klavis tool by source field
|
|
69
|
+
if (payload.source === 'klavis') {
|
|
70
|
+
return await get().invokeKlavisTypePlugin(id, payload);
|
|
71
|
+
}
|
|
72
|
+
|
|
63
73
|
// run tool api call
|
|
64
74
|
// @ts-ignore
|
|
65
75
|
const { [payload.apiName]: action } = get();
|
|
@@ -82,6 +92,104 @@ export const pluginTypes: StateCreator<
|
|
|
82
92
|
return data;
|
|
83
93
|
},
|
|
84
94
|
|
|
95
|
+
invokeKlavisTypePlugin: async (id, payload) => {
|
|
96
|
+
const {
|
|
97
|
+
optimisticUpdateMessageContent,
|
|
98
|
+
optimisticUpdatePluginState,
|
|
99
|
+
optimisticUpdateMessagePluginError,
|
|
100
|
+
} = get();
|
|
101
|
+
|
|
102
|
+
let data: MCPToolCallResult | undefined;
|
|
103
|
+
|
|
104
|
+
// Get message to extract sessionId/topicId
|
|
105
|
+
const message = dbMessageSelectors.getDbMessageById(id)(get());
|
|
106
|
+
|
|
107
|
+
// Get abort controller from operation
|
|
108
|
+
const operationId = get().messageOperationMap[id];
|
|
109
|
+
const operation = operationId ? get().operations[operationId] : undefined;
|
|
110
|
+
const abortController = operation?.abortController;
|
|
111
|
+
|
|
112
|
+
log(
|
|
113
|
+
'[invokeKlavisTypePlugin] messageId=%s, tool=%s, operationId=%s, aborted=%s',
|
|
114
|
+
id,
|
|
115
|
+
payload.apiName,
|
|
116
|
+
operationId,
|
|
117
|
+
abortController?.signal.aborted,
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
try {
|
|
121
|
+
// payload.identifier 现在是存储用的 identifier(如 'google-calendar')
|
|
122
|
+
const identifier = payload.identifier;
|
|
123
|
+
const klavisServers = useToolStore.getState().servers || [];
|
|
124
|
+
const server = klavisServers.find((s) => s.identifier === identifier);
|
|
125
|
+
|
|
126
|
+
if (!server) {
|
|
127
|
+
throw new Error(`Klavis server not found: ${identifier}`);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Parse arguments
|
|
131
|
+
const args = safeParseJSON(payload.arguments) || {};
|
|
132
|
+
|
|
133
|
+
// Call Klavis tool via store action
|
|
134
|
+
const result = await useToolStore.getState().callKlavisTool({
|
|
135
|
+
serverUrl: server.serverUrl,
|
|
136
|
+
toolArgs: args,
|
|
137
|
+
toolName: payload.apiName,
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
if (!result.success) {
|
|
141
|
+
throw new Error(result.error || 'Klavis tool execution failed');
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// result.data is MCPToolCallProcessedResult from server
|
|
145
|
+
// Convert to MCPToolCallResult format
|
|
146
|
+
const toolResult = result.data;
|
|
147
|
+
if (toolResult) {
|
|
148
|
+
data = {
|
|
149
|
+
content: toolResult.content,
|
|
150
|
+
error: toolResult.state?.isError ? toolResult.state : undefined,
|
|
151
|
+
state: toolResult.state,
|
|
152
|
+
success: toolResult.success,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
} catch (error) {
|
|
156
|
+
console.error('[invokeKlavisTypePlugin] Error:', error);
|
|
157
|
+
|
|
158
|
+
// ignore the aborted request error
|
|
159
|
+
const err = error as Error;
|
|
160
|
+
if (err.message.includes('aborted')) {
|
|
161
|
+
log('[invokeKlavisTypePlugin] Request aborted: messageId=%s, tool=%s', id, payload.apiName);
|
|
162
|
+
} else {
|
|
163
|
+
const result = await messageService.updateMessageError(id, error as any, {
|
|
164
|
+
sessionId: message?.sessionId,
|
|
165
|
+
topicId: message?.topicId,
|
|
166
|
+
});
|
|
167
|
+
if (result?.success && result.messages) {
|
|
168
|
+
get().replaceMessages(result.messages, {
|
|
169
|
+
sessionId: message?.sessionId,
|
|
170
|
+
topicId: message?.topicId,
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// 如果报错则结束了
|
|
177
|
+
if (!data) return;
|
|
178
|
+
|
|
179
|
+
// operationId already declared above, reuse it
|
|
180
|
+
const context = operationId ? { operationId } : undefined;
|
|
181
|
+
|
|
182
|
+
await Promise.all([
|
|
183
|
+
optimisticUpdateMessageContent(id, data.content, undefined, context),
|
|
184
|
+
(async () => {
|
|
185
|
+
if (data.success) await optimisticUpdatePluginState(id, data.state, context);
|
|
186
|
+
else await optimisticUpdateMessagePluginError(id, data.error, context);
|
|
187
|
+
})(),
|
|
188
|
+
]);
|
|
189
|
+
|
|
190
|
+
return data.content;
|
|
191
|
+
},
|
|
192
|
+
|
|
85
193
|
invokeMarkdownTypePlugin: async (id, payload) => {
|
|
86
194
|
const { internal_callPluginApi } = get();
|
|
87
195
|
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { featureFlagsSelectors } from './selectors';
|
|
1
|
+
export { featureFlagsSelectors, serverConfigSelectors } from './selectors';
|
|
2
2
|
export { useServerConfigStore } from './store';
|
|
@@ -3,6 +3,7 @@ import { ServerConfigStore } from './store';
|
|
|
3
3
|
export const featureFlagsSelectors = (s: ServerConfigStore) => s.featureFlags;
|
|
4
4
|
|
|
5
5
|
export const serverConfigSelectors = {
|
|
6
|
+
enableKlavis: (s: ServerConfigStore) => s.serverConfig.enableKlavis || false,
|
|
6
7
|
enableUploadFileToServer: (s: ServerConfigStore) => s.serverConfig.enableUploadFileToServer,
|
|
7
8
|
enabledAccessCode: (s: ServerConfigStore) => !!s.serverConfig?.enabledAccessCode,
|
|
8
9
|
enabledOAuthSSO: (s: ServerConfigStore) => s.serverConfig.enabledOAuthSSO,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { BuiltinToolState, initialBuiltinToolState } from './slices/builtin';
|
|
2
2
|
import { CustomPluginState, initialCustomPluginState } from './slices/customPlugin';
|
|
3
|
+
import { KlavisStoreState, initialKlavisStoreState } from './slices/klavisStore';
|
|
3
4
|
import { MCPStoreState, initialMCPStoreState } from './slices/mcpStore';
|
|
4
5
|
import { PluginState, initialPluginState } from './slices/plugin';
|
|
5
6
|
import { PluginStoreState, initialPluginStoreState } from './slices/oldStore';
|
|
@@ -8,7 +9,8 @@ export type ToolStoreState = PluginState &
|
|
|
8
9
|
CustomPluginState &
|
|
9
10
|
PluginStoreState &
|
|
10
11
|
BuiltinToolState &
|
|
11
|
-
MCPStoreState
|
|
12
|
+
MCPStoreState &
|
|
13
|
+
KlavisStoreState;
|
|
12
14
|
|
|
13
15
|
export const initialState: ToolStoreState = {
|
|
14
16
|
...initialPluginState,
|
|
@@ -16,4 +18,5 @@ export const initialState: ToolStoreState = {
|
|
|
16
18
|
...initialPluginStoreState,
|
|
17
19
|
...initialBuiltinToolState,
|
|
18
20
|
...initialMCPStoreState,
|
|
21
|
+
...initialKlavisStoreState,
|
|
19
22
|
};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { builtinToolSelectors } from '../slices/builtin/selectors';
|
|
2
2
|
export { customPluginSelectors } from '../slices/customPlugin/selectors';
|
|
3
|
+
export { klavisStoreSelectors } from '../slices/klavisStore/selectors';
|
|
3
4
|
export { mcpStoreSelectors } from '../slices/mcpStore/selectors';
|
|
4
5
|
export { pluginStoreSelectors } from '../slices/oldStore/selectors';
|
|
5
6
|
export { pluginSelectors } from '../slices/plugin/selectors';
|
|
@@ -3,9 +3,11 @@ import { LobeToolMeta } from '@lobechat/types';
|
|
|
3
3
|
import { shouldEnableTool } from '@/helpers/toolFilters';
|
|
4
4
|
|
|
5
5
|
import type { ToolStoreState } from '../../initialState';
|
|
6
|
+
import { KlavisServerStatus } from '../klavisStore';
|
|
6
7
|
|
|
7
|
-
const metaList = (s: ToolStoreState): LobeToolMeta[] =>
|
|
8
|
-
|
|
8
|
+
const metaList = (s: ToolStoreState): LobeToolMeta[] => {
|
|
9
|
+
// Get builtin tools meta list
|
|
10
|
+
const builtinMetas = s.builtinTools
|
|
9
11
|
.filter((item) => {
|
|
10
12
|
// Filter hidden tools
|
|
11
13
|
if (item.hidden) return false;
|
|
@@ -19,9 +21,29 @@ const metaList = (s: ToolStoreState): LobeToolMeta[] =>
|
|
|
19
21
|
author: 'LobeHub',
|
|
20
22
|
identifier: t.identifier,
|
|
21
23
|
meta: t.manifest.meta,
|
|
22
|
-
type: 'builtin',
|
|
24
|
+
type: 'builtin' as const,
|
|
23
25
|
}));
|
|
24
26
|
|
|
27
|
+
// Get Klavis servers as builtin tools meta
|
|
28
|
+
const klavisMetas = (s.servers || [])
|
|
29
|
+
.filter((server) => server.status === KlavisServerStatus.CONNECTED && server.tools?.length)
|
|
30
|
+
.map((server) => ({
|
|
31
|
+
author: 'Klavis',
|
|
32
|
+
// 使用 identifier 作为存储标识符(如 'google-calendar')
|
|
33
|
+
identifier: server.identifier,
|
|
34
|
+
meta: {
|
|
35
|
+
avatar: '☁️',
|
|
36
|
+
description: `Klavis MCP Server: ${server.serverName}`,
|
|
37
|
+
tags: ['klavis', 'mcp'],
|
|
38
|
+
// title 仍然使用 serverName 显示友好名称
|
|
39
|
+
title: server.serverName,
|
|
40
|
+
},
|
|
41
|
+
type: 'builtin' as const,
|
|
42
|
+
}));
|
|
43
|
+
|
|
44
|
+
return [...builtinMetas, ...klavisMetas];
|
|
45
|
+
};
|
|
46
|
+
|
|
25
47
|
export const builtinToolSelectors = {
|
|
26
48
|
metaList,
|
|
27
49
|
};
|