@lobehub/lobehub 2.0.0-next.38 → 2.0.0-next.39
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 +17 -0
- package/apps/desktop/src/main/modules/networkProxy/__tests__/dispatcher.test.ts +401 -0
- package/apps/desktop/src/main/modules/networkProxy/__tests__/tester.test.ts +531 -0
- package/apps/desktop/src/main/modules/networkProxy/__tests__/urlBuilder.test.ts +349 -0
- package/apps/desktop/src/main/modules/networkProxy/__tests__/validator.test.ts +492 -0
- package/changelog/v1.json +5 -0
- package/locales/ar/auth.json +45 -1
- package/locales/bg-BG/auth.json +45 -1
- package/locales/de-DE/auth.json +45 -1
- package/locales/en-US/auth.json +45 -1
- package/locales/es-ES/auth.json +45 -1
- package/locales/fa-IR/auth.json +45 -1
- package/locales/fr-FR/auth.json +45 -1
- package/locales/it-IT/auth.json +45 -1
- package/locales/ja-JP/auth.json +45 -1
- package/locales/ko-KR/auth.json +45 -1
- package/locales/nl-NL/auth.json +45 -1
- package/locales/pl-PL/auth.json +45 -1
- package/locales/pt-BR/auth.json +45 -1
- package/locales/ru-RU/auth.json +45 -1
- package/locales/tr-TR/auth.json +45 -1
- package/locales/vi-VN/auth.json +45 -1
- package/locales/zh-CN/auth.json +45 -1
- package/locales/zh-TW/auth.json +45 -1
- package/package.json +1 -1
- package/packages/context-engine/src/processors/MessageCleanup.ts +1 -0
- package/packages/context-engine/src/processors/__tests__/MessageCleanup.test.ts +28 -0
- package/packages/obervability-otel/package.json +3 -1
- package/packages/obervability-otel/src/api.ts +2 -0
- package/packages/obervability-otel/src/trpc/convention.ts +16 -0
- package/packages/obervability-otel/src/trpc/index.test.ts +38 -0
- package/packages/obervability-otel/src/trpc/index.ts +62 -0
- package/packages/obervability-otel/src/trpc/metrics.ts +31 -0
- package/packages/types/src/usage/usageRecord.ts +54 -0
- package/src/app/[variants]/(main)/profile/hooks/useCategory.tsx +10 -1
- package/src/app/[variants]/(main)/profile/usage/Client.tsx +114 -0
- package/src/app/[variants]/(main)/profile/usage/features/UsageCards/ActiveModels/ModelTable.tsx +175 -0
- package/src/app/[variants]/(main)/profile/usage/features/UsageCards/ActiveModels/index.tsx +126 -0
- package/src/app/[variants]/(main)/profile/usage/features/UsageCards/MonthSpend.tsx +53 -0
- package/src/app/[variants]/(main)/profile/usage/features/UsageCards/TodaySpend.tsx +67 -0
- package/src/app/[variants]/(main)/profile/usage/features/UsageCards/index.tsx +19 -0
- package/src/app/[variants]/(main)/profile/usage/features/UsageTable.tsx +145 -0
- package/src/app/[variants]/(main)/profile/usage/features/UsageTrends.tsx +107 -0
- package/src/app/[variants]/(main)/profile/usage/features/components/UsageBarChart.tsx +48 -0
- package/src/app/[variants]/(main)/profile/usage/page.tsx +23 -0
- package/src/features/Conversation/Messages/Assistant/Tool/Render/CustomRender.tsx +3 -3
- package/src/features/Conversation/Messages/Group/Actions/WithoutContentId.tsx +37 -14
- package/src/features/Conversation/Messages/Group/Error/index.tsx +1 -1
- package/src/features/Conversation/Messages/Group/GroupChildren.tsx +13 -35
- package/src/features/Conversation/Messages/Group/GroupItem.tsx +43 -0
- package/src/features/Conversation/Messages/Group/Tool/Inspector/index.tsx +1 -2
- package/src/features/Conversation/Messages/Group/Tool/Render/CustomRender.tsx +1 -1
- package/src/features/Conversation/Messages/Group/Tool/index.tsx +0 -2
- package/src/features/Conversation/Messages/Group/index.tsx +7 -2
- package/src/features/Conversation/hooks/useChatListActionsBar.tsx +21 -7
- package/src/features/PluginsUI/Render/BuiltinType/index.tsx +1 -1
- package/src/features/PluginsUI/Render/MCPType/index.tsx +52 -0
- package/src/features/PluginsUI/Render/StandaloneType/Iframe.tsx +2 -2
- package/src/features/PluginsUI/Render/index.tsx +17 -0
- package/src/libs/mcp/client.ts +3 -2
- package/src/libs/mcp/types.ts +71 -0
- package/src/libs/trpc/lambda/index.ts +5 -2
- package/src/libs/trpc/middleware/openTelemetry.ts +141 -0
- package/src/locales/default/auth.ts +44 -0
- package/src/locales/default/chat.ts +1 -0
- package/src/server/routers/desktop/mcp.ts +1 -3
- package/src/server/routers/lambda/index.ts +2 -0
- package/src/server/routers/lambda/usage.ts +36 -0
- package/src/server/routers/tools/mcp.ts +1 -3
- package/src/server/services/mcp/index.test.ts +28 -15
- package/src/server/services/mcp/index.ts +29 -18
- package/src/server/services/usage/index.test.ts +310 -0
- package/src/server/services/usage/index.ts +164 -0
- package/src/services/chat/contextEngineering.test.ts +4 -0
- package/src/services/mcp.test.ts +7 -1
- package/src/services/mcp.ts +13 -12
- package/src/services/usage.ts +13 -0
- package/src/store/chat/agents/createAgentExecutors.ts +2 -3
- package/src/store/chat/slices/aiChat/actions/conversationLifecycle.ts +40 -1
- package/src/store/chat/slices/aiChat/actions/streamingExecutor.ts +13 -5
- package/src/store/chat/slices/builtinTool/actions/__tests__/localSystem.test.ts +3 -3
- package/src/store/chat/slices/builtinTool/actions/__tests__/search.test.ts +6 -6
- package/src/store/chat/slices/builtinTool/actions/interpreter.ts +2 -2
- package/src/store/chat/slices/builtinTool/actions/localSystem.ts +2 -2
- package/src/store/chat/slices/builtinTool/actions/search.ts +6 -6
- package/src/store/chat/slices/message/actions/publicApi.ts +19 -1
- package/src/store/chat/slices/message/initialState.ts +5 -0
- package/src/store/chat/slices/message/selectors/chat.test.ts +22 -602
- package/src/store/chat/slices/message/selectors/chat.ts +0 -2
- package/src/store/chat/slices/message/selectors/dbMessage.test.ts +51 -0
- package/src/store/chat/slices/message/selectors/displayMessage.test.ts +818 -0
- package/src/store/chat/slices/message/selectors/displayMessage.ts +52 -1
- package/src/store/chat/slices/message/selectors/messageState.ts +2 -0
- package/src/store/chat/slices/plugin/action.test.ts +4 -4
- package/src/store/chat/slices/plugin/actions/index.ts +39 -0
- package/src/store/chat/slices/plugin/actions/internals.ts +83 -0
- package/src/store/chat/slices/plugin/actions/optimisticUpdate.ts +188 -0
- package/src/store/chat/slices/plugin/actions/pluginTypes.ts +213 -0
- package/src/store/chat/slices/plugin/actions/publicApi.ts +115 -0
- package/src/store/chat/slices/plugin/actions/workflow.ts +121 -0
- package/src/store/chat/store.ts +1 -1
- package/src/store/global/initialState.ts +1 -0
- package/src/store/chat/slices/plugin/action.ts +0 -539
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { UIChatMessage } from '@lobechat/types';
|
|
2
|
-
import { ActionIconGroup, type ActionIconGroupEvent
|
|
2
|
+
import { ActionIconGroup, type ActionIconGroupEvent } from '@lobehub/ui';
|
|
3
3
|
import { useSearchParams } from 'next/navigation';
|
|
4
|
-
import { memo, useCallback, useContext
|
|
4
|
+
import { memo, useCallback, useContext } from 'react';
|
|
5
5
|
|
|
6
6
|
import { useChatStore } from '@/store/chat';
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
import {
|
|
8
|
+
displayMessageSelectors,
|
|
9
|
+
messageStateSelectors,
|
|
10
|
+
threadSelectors,
|
|
11
|
+
} from '@/store/chat/selectors';
|
|
10
12
|
|
|
11
13
|
import { InPortalThreadContext } from '../../../context/InPortalThreadContext';
|
|
12
14
|
import { useChatListActionsBar } from '../../../hooks/useChatListActionsBar';
|
|
@@ -17,28 +19,38 @@ interface GroupActionsProps {
|
|
|
17
19
|
}
|
|
18
20
|
|
|
19
21
|
const WithoutContentId = memo<GroupActionsProps>(({ id, data }) => {
|
|
20
|
-
const [
|
|
21
|
-
!!s.activeThreadId,
|
|
22
|
+
const [hasThread, lastBlockId] = useChatStore((s) => [
|
|
22
23
|
threadSelectors.hasThreadBySourceMsgId(id)(s),
|
|
24
|
+
displayMessageSelectors.findLastMessageId(id)(s),
|
|
23
25
|
]);
|
|
24
|
-
const isGroupSession = useSessionStore(sessionSelectors.isCurrentSessionGroupSession);
|
|
25
26
|
|
|
26
|
-
const
|
|
27
|
+
const isContinuing = useChatStore((s) =>
|
|
28
|
+
lastBlockId ? messageStateSelectors.isMessageContinuing(lastBlockId)(s) : false,
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
const { delAndRegenerate, del, continueGeneration } = useChatListActionsBar({
|
|
32
|
+
hasThread,
|
|
33
|
+
isContinuing,
|
|
34
|
+
});
|
|
27
35
|
|
|
28
36
|
const inPortalThread = useContext(InPortalThreadContext);
|
|
29
|
-
const inThread = isThreadMode || inPortalThread;
|
|
37
|
+
// const inThread = isThreadMode || inPortalThread;
|
|
30
38
|
|
|
31
|
-
const items =
|
|
32
|
-
return [delAndRegenerate, del].filter(Boolean) as ActionIconGroupItemType[];
|
|
33
|
-
}, [inThread, isGroupSession]);
|
|
39
|
+
const items = [continueGeneration, delAndRegenerate, del];
|
|
34
40
|
|
|
35
41
|
const searchParams = useSearchParams();
|
|
36
42
|
const topic = searchParams.get('topic');
|
|
37
43
|
|
|
38
|
-
const [
|
|
44
|
+
const [
|
|
45
|
+
deleteMessage,
|
|
46
|
+
delAndRegenerateMessage,
|
|
47
|
+
delAndResendThreadMessage,
|
|
48
|
+
continueGenerationMessage,
|
|
49
|
+
] = useChatStore((s) => [
|
|
39
50
|
s.deleteMessage,
|
|
40
51
|
s.delAndRegenerateMessage,
|
|
41
52
|
s.delAndResendThreadMessage,
|
|
53
|
+
s.continueGenerationMessage,
|
|
42
54
|
]);
|
|
43
55
|
|
|
44
56
|
const onActionClick = useCallback(
|
|
@@ -46,6 +58,17 @@ const WithoutContentId = memo<GroupActionsProps>(({ id, data }) => {
|
|
|
46
58
|
if (!data) return;
|
|
47
59
|
|
|
48
60
|
switch (action.key) {
|
|
61
|
+
case 'continueGeneration': {
|
|
62
|
+
const lastMessageId = displayMessageSelectors.findLastMessageId(id)(
|
|
63
|
+
useChatStore.getState(),
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
if (!lastMessageId) return;
|
|
67
|
+
|
|
68
|
+
continueGenerationMessage(lastMessageId, id);
|
|
69
|
+
break;
|
|
70
|
+
}
|
|
71
|
+
|
|
49
72
|
case 'del': {
|
|
50
73
|
deleteMessage(id);
|
|
51
74
|
break;
|
|
@@ -17,7 +17,7 @@ const ErrorContent = memo<ErrorContentProps>(({ error, id }) => {
|
|
|
17
17
|
const { t } = useTranslation('common');
|
|
18
18
|
const errorProps = useErrorContent(error);
|
|
19
19
|
|
|
20
|
-
const [deleteMessage] = useChatStore((s) => [s.
|
|
20
|
+
const [deleteMessage] = useChatStore((s) => [s.deleteDBMessage]);
|
|
21
21
|
const message = <ErrorMessageExtra block data={{ error, id }} />;
|
|
22
22
|
|
|
23
23
|
if (!error?.message) {
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
import { AssistantContentBlock } from '@lobechat/types';
|
|
2
2
|
import { createStyles } from 'antd-style';
|
|
3
|
-
import
|
|
3
|
+
import isEqual from 'fast-deep-equal';
|
|
4
|
+
import { memo } from 'react';
|
|
4
5
|
import { Flexbox } from 'react-layout-kit';
|
|
5
6
|
|
|
6
|
-
import
|
|
7
|
-
import { useChatStore } from '@/store/chat';
|
|
8
|
-
|
|
9
|
-
import { ContentBlock } from './ContentBlock';
|
|
7
|
+
import GroupItem from './GroupItem';
|
|
10
8
|
|
|
11
9
|
const useStyles = createStyles(({ css }) => {
|
|
12
10
|
return {
|
|
@@ -30,44 +28,24 @@ const GroupChildren = memo<GroupChildrenProps>(
|
|
|
30
28
|
({ blocks, contentId, disableEditing, messageIndex, id }) => {
|
|
31
29
|
const { styles } = useStyles();
|
|
32
30
|
|
|
33
|
-
const [toggleMessageEditing] = useChatStore((s) => [s.toggleMessageEditing]);
|
|
34
|
-
const virtuosoRef = use(VirtuosoContext);
|
|
35
|
-
|
|
36
31
|
return (
|
|
37
32
|
<Flexbox className={styles.container} gap={8}>
|
|
38
33
|
{blocks.map((item, index) => {
|
|
39
|
-
return
|
|
40
|
-
<
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
behavior: 'auto',
|
|
49
|
-
index: messageIndex,
|
|
50
|
-
});
|
|
51
|
-
}}
|
|
52
|
-
>
|
|
53
|
-
<ContentBlock index={index} {...item} />
|
|
54
|
-
</Flexbox>
|
|
55
|
-
) : (
|
|
56
|
-
// <motion.div
|
|
57
|
-
// animate={{ height: 'auto', opacity: 1 }}
|
|
58
|
-
// exit={{ height: 0, opacity: 0 }}
|
|
59
|
-
// initial={{ height: 0, opacity: 0 }}
|
|
60
|
-
// key={`${id}_${index}`}
|
|
61
|
-
// style={{ overflow: 'hidden' }}
|
|
62
|
-
// transition={{ duration: 0.3, ease: 'easeInOut' }}
|
|
63
|
-
// >
|
|
64
|
-
<ContentBlock index={index} key={`${id}_${index}`} {...item} />
|
|
65
|
-
// </motion.div>
|
|
34
|
+
return (
|
|
35
|
+
<GroupItem
|
|
36
|
+
{...item}
|
|
37
|
+
contentId={contentId}
|
|
38
|
+
disableEditing={disableEditing}
|
|
39
|
+
index={index}
|
|
40
|
+
key={`${id}_${index}`}
|
|
41
|
+
messageIndex={messageIndex}
|
|
42
|
+
/>
|
|
66
43
|
);
|
|
67
44
|
})}
|
|
68
45
|
</Flexbox>
|
|
69
46
|
);
|
|
70
47
|
},
|
|
48
|
+
isEqual,
|
|
71
49
|
);
|
|
72
50
|
|
|
73
51
|
export default GroupChildren;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { AssistantContentBlock } from '@lobechat/types';
|
|
2
|
+
import isEqual from 'fast-deep-equal';
|
|
3
|
+
import { memo, use } from 'react';
|
|
4
|
+
import { Flexbox } from 'react-layout-kit';
|
|
5
|
+
|
|
6
|
+
import { ContentBlock } from '@/features/Conversation/Messages/Group/ContentBlock';
|
|
7
|
+
import { VirtuosoContext } from '@/features/Conversation/components/VirtualizedList/VirtuosoContext';
|
|
8
|
+
import { useChatStore } from '@/store/chat';
|
|
9
|
+
|
|
10
|
+
interface GroupItemProps extends AssistantContentBlock {
|
|
11
|
+
contentId?: string;
|
|
12
|
+
disableEditing?: boolean;
|
|
13
|
+
index: number;
|
|
14
|
+
messageIndex: number;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const GroupItem = memo<GroupItemProps>(
|
|
18
|
+
({ contentId, messageIndex, index, disableEditing, error, ...item }) => {
|
|
19
|
+
const [toggleMessageEditing] = useChatStore((s) => [s.toggleMessageEditing]);
|
|
20
|
+
const virtuosoRef = use(VirtuosoContext);
|
|
21
|
+
|
|
22
|
+
return item.id === contentId ? (
|
|
23
|
+
<Flexbox
|
|
24
|
+
onDoubleClick={(e) => {
|
|
25
|
+
if (disableEditing || error || !e.altKey) return;
|
|
26
|
+
|
|
27
|
+
toggleMessageEditing(item.id, true);
|
|
28
|
+
virtuosoRef?.current?.scrollIntoView({
|
|
29
|
+
align: 'start',
|
|
30
|
+
behavior: 'auto',
|
|
31
|
+
index: messageIndex,
|
|
32
|
+
});
|
|
33
|
+
}}
|
|
34
|
+
>
|
|
35
|
+
<ContentBlock index={index} {...item} />
|
|
36
|
+
</Flexbox>
|
|
37
|
+
) : (
|
|
38
|
+
<ContentBlock index={index} {...item} />
|
|
39
|
+
);
|
|
40
|
+
},
|
|
41
|
+
isEqual,
|
|
42
|
+
);
|
|
43
|
+
export default GroupItem;
|
|
@@ -90,7 +90,6 @@ const Inspectors = memo<InspectorProps>(
|
|
|
90
90
|
setShowRender,
|
|
91
91
|
showPluginRender,
|
|
92
92
|
setShowPluginRender,
|
|
93
|
-
hidePluginUI = false,
|
|
94
93
|
type,
|
|
95
94
|
}) => {
|
|
96
95
|
const { t } = useTranslation('plugin');
|
|
@@ -127,7 +126,7 @@ const Inspectors = memo<InspectorProps>(
|
|
|
127
126
|
</Flexbox>
|
|
128
127
|
<Flexbox align={'center'} gap={8} horizontal>
|
|
129
128
|
<Flexbox className={styles.actions} horizontal>
|
|
130
|
-
{showRender &&
|
|
129
|
+
{showRender && (
|
|
131
130
|
<ActionIcon
|
|
132
131
|
icon={showPluginRender ? LogsIcon : LayoutPanelTop}
|
|
133
132
|
onClick={() => {
|
|
@@ -30,7 +30,7 @@ const CustomRender = memo<CustomRenderProps>(
|
|
|
30
30
|
// Determine if plugin UI should be shown based on plugin type
|
|
31
31
|
useEffect(() => {
|
|
32
32
|
if (!plugin?.type) return;
|
|
33
|
-
setShowPluginRender(
|
|
33
|
+
setShowPluginRender(plugin.type !== 'default');
|
|
34
34
|
}, [plugin?.type, setShowPluginRender]);
|
|
35
35
|
|
|
36
36
|
// Parse and display result content
|
|
@@ -15,7 +15,10 @@ import { useOpenChatSettings } from '@/hooks/useInterceptingRoutes';
|
|
|
15
15
|
import { useAgentStore } from '@/store/agent';
|
|
16
16
|
import { agentChatConfigSelectors } from '@/store/agent/selectors';
|
|
17
17
|
import { useChatStore } from '@/store/chat';
|
|
18
|
-
import {
|
|
18
|
+
import {
|
|
19
|
+
displayMessageSelectors,
|
|
20
|
+
messageStateSelectors,
|
|
21
|
+
} from '@/store/chat/slices/message/selectors';
|
|
19
22
|
import { useGlobalStore } from '@/store/global';
|
|
20
23
|
import { useSessionStore } from '@/store/session';
|
|
21
24
|
import { sessionSelectors } from '@/store/session/selectors';
|
|
@@ -64,7 +67,9 @@ const GroupMessage = memo<GroupMessageProps>((props) => {
|
|
|
64
67
|
const [isInbox] = useSessionStore((s) => [sessionSelectors.isInboxSession(s)]);
|
|
65
68
|
const [toggleSystemRole] = useGlobalStore((s) => [s.toggleSystemRole]);
|
|
66
69
|
const openChatSettings = useOpenChatSettings();
|
|
67
|
-
const lastAssistantMsg = useChatStore(
|
|
70
|
+
const lastAssistantMsg = useChatStore(
|
|
71
|
+
displayMessageSelectors.getGroupLatestMessageWithoutTools(id),
|
|
72
|
+
);
|
|
68
73
|
|
|
69
74
|
const contentId = lastAssistantMsg?.id;
|
|
70
75
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { ActionIconGroupItemType } from '@lobehub/ui';
|
|
2
2
|
import { css, cx } from 'antd-style';
|
|
3
3
|
import {
|
|
4
|
+
ArrowDownFromLine,
|
|
4
5
|
Copy,
|
|
5
6
|
DownloadIcon,
|
|
6
7
|
Edit,
|
|
@@ -26,6 +27,7 @@ const translateStyle = css`
|
|
|
26
27
|
|
|
27
28
|
interface ChatListActionsBar {
|
|
28
29
|
branching: ActionIconGroupItemType;
|
|
30
|
+
continueGeneration: ActionIconGroupItemType;
|
|
29
31
|
copy: ActionIconGroupItemType;
|
|
30
32
|
del: ActionIconGroupItemType;
|
|
31
33
|
delAndRegenerate: ActionIconGroupItemType;
|
|
@@ -40,8 +42,13 @@ interface ChatListActionsBar {
|
|
|
40
42
|
|
|
41
43
|
export const useChatListActionsBar = ({
|
|
42
44
|
hasThread,
|
|
45
|
+
isContinuing,
|
|
43
46
|
isRegenerating,
|
|
44
|
-
}: {
|
|
47
|
+
}: {
|
|
48
|
+
hasThread?: boolean;
|
|
49
|
+
isContinuing?: boolean;
|
|
50
|
+
isRegenerating?: boolean;
|
|
51
|
+
} = {}): ChatListActionsBar => {
|
|
45
52
|
const { t } = useTranslation(['common', 'chat']);
|
|
46
53
|
|
|
47
54
|
return useMemo<ChatListActionsBar>(
|
|
@@ -49,7 +56,14 @@ export const useChatListActionsBar = ({
|
|
|
49
56
|
branching: {
|
|
50
57
|
icon: Split,
|
|
51
58
|
key: 'branching',
|
|
52
|
-
label: t('branching'
|
|
59
|
+
label: t('branching'),
|
|
60
|
+
},
|
|
61
|
+
continueGeneration: {
|
|
62
|
+
disabled: isContinuing,
|
|
63
|
+
icon: ArrowDownFromLine,
|
|
64
|
+
key: 'continueGeneration',
|
|
65
|
+
label: t('messageAction.continueGeneration', { ns: 'chat' }),
|
|
66
|
+
spin: isContinuing,
|
|
53
67
|
},
|
|
54
68
|
copy: {
|
|
55
69
|
icon: Copy,
|
|
@@ -68,7 +82,6 @@ export const useChatListActionsBar = ({
|
|
|
68
82
|
icon: ListRestart,
|
|
69
83
|
key: 'delAndRegenerate',
|
|
70
84
|
label: t('messageAction.delAndRegenerate', {
|
|
71
|
-
defaultValue: 'Delete and regenerate',
|
|
72
85
|
ns: 'chat',
|
|
73
86
|
}),
|
|
74
87
|
},
|
|
@@ -78,7 +91,7 @@ export const useChatListActionsBar = ({
|
|
|
78
91
|
edit: {
|
|
79
92
|
icon: Edit,
|
|
80
93
|
key: 'edit',
|
|
81
|
-
label: t('edit'
|
|
94
|
+
label: t('edit'),
|
|
82
95
|
},
|
|
83
96
|
export: {
|
|
84
97
|
icon: DownloadIcon,
|
|
@@ -89,12 +102,13 @@ export const useChatListActionsBar = ({
|
|
|
89
102
|
disabled: isRegenerating,
|
|
90
103
|
icon: RotateCcw,
|
|
91
104
|
key: 'regenerate',
|
|
92
|
-
label: t('regenerate'
|
|
105
|
+
label: t('regenerate'),
|
|
106
|
+
spin: isRegenerating,
|
|
93
107
|
},
|
|
94
108
|
share: {
|
|
95
109
|
icon: Share2,
|
|
96
110
|
key: 'share',
|
|
97
|
-
label: t('share'
|
|
111
|
+
label: t('share'),
|
|
98
112
|
},
|
|
99
113
|
translate: {
|
|
100
114
|
children: localeOptions.map((i) => ({
|
|
@@ -112,6 +126,6 @@ export const useChatListActionsBar = ({
|
|
|
112
126
|
label: t('tts.action', { ns: 'chat' }),
|
|
113
127
|
},
|
|
114
128
|
}),
|
|
115
|
-
[hasThread, isRegenerating],
|
|
129
|
+
[hasThread, isContinuing, isRegenerating],
|
|
116
130
|
);
|
|
117
131
|
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { Markdown } from '@lobehub/ui';
|
|
2
|
+
import { memo } from 'react';
|
|
3
|
+
import { Flexbox } from 'react-layout-kit';
|
|
4
|
+
|
|
5
|
+
import Arguments from '@/features/Conversation/Messages/Group/Tool/Render/Arguments';
|
|
6
|
+
import { ToolCallResult } from '@/libs/mcp';
|
|
7
|
+
|
|
8
|
+
export interface MCPTypeProps {
|
|
9
|
+
apiName?: string;
|
|
10
|
+
arguments?: string;
|
|
11
|
+
content: string;
|
|
12
|
+
id: string;
|
|
13
|
+
identifier?: string;
|
|
14
|
+
loading?: boolean;
|
|
15
|
+
pluginError?: any;
|
|
16
|
+
pluginState?: ToolCallResult;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const MCPType = memo<MCPTypeProps>(({ pluginState, arguments: args }) => {
|
|
20
|
+
if (!pluginState) return;
|
|
21
|
+
|
|
22
|
+
const { content } = pluginState;
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<Flexbox gap={8} style={{ maxHeight: 400, overflow: 'scroll', padding: 8, width: '100%' }}>
|
|
26
|
+
<div>
|
|
27
|
+
<Arguments arguments={args} />
|
|
28
|
+
</div>
|
|
29
|
+
<Flexbox>
|
|
30
|
+
<Flexbox>
|
|
31
|
+
{content.map((item) => {
|
|
32
|
+
switch (item.type) {
|
|
33
|
+
case 'text': {
|
|
34
|
+
return (
|
|
35
|
+
<Markdown key={item.text} variant={'chat'}>
|
|
36
|
+
{item.text}
|
|
37
|
+
</Markdown>
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
default: {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
})}
|
|
46
|
+
</Flexbox>
|
|
47
|
+
</Flexbox>
|
|
48
|
+
</Flexbox>
|
|
49
|
+
);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
export default MCPType;
|
|
@@ -94,9 +94,9 @@ const IFrameRender = memo<IFrameRenderProps>(({ url, id, payload, width = 600, h
|
|
|
94
94
|
});
|
|
95
95
|
|
|
96
96
|
// when plugin update state, we should update it to the message pluginState key
|
|
97
|
-
const
|
|
97
|
+
const optimisticUpdatePluginState = useChatStore((s) => s.optimisticUpdatePluginState);
|
|
98
98
|
useOnPluginStateUpdate((key, value) => {
|
|
99
|
-
|
|
99
|
+
optimisticUpdatePluginState(id, { [key]: value });
|
|
100
100
|
});
|
|
101
101
|
|
|
102
102
|
// when plugin wants to get plugin settings, send it to plugin
|
|
@@ -4,6 +4,7 @@ import { memo } from 'react';
|
|
|
4
4
|
|
|
5
5
|
import BuiltinType from './BuiltinType';
|
|
6
6
|
import DefaultType from './DefaultType';
|
|
7
|
+
import MCP from './MCPType';
|
|
7
8
|
import Markdown from './MarkdownType';
|
|
8
9
|
import Standalone from './StandaloneType';
|
|
9
10
|
|
|
@@ -51,6 +52,22 @@ const PluginRender = memo<PluginRenderProps>(
|
|
|
51
52
|
);
|
|
52
53
|
}
|
|
53
54
|
|
|
55
|
+
// @ts-expect-error need to update types
|
|
56
|
+
case 'mcp': {
|
|
57
|
+
return (
|
|
58
|
+
<MCP
|
|
59
|
+
apiName={payload?.apiName}
|
|
60
|
+
arguments={argumentsStr}
|
|
61
|
+
content={content}
|
|
62
|
+
id={id}
|
|
63
|
+
identifier={identifier}
|
|
64
|
+
loading={loading}
|
|
65
|
+
pluginError={pluginError}
|
|
66
|
+
pluginState={pluginState}
|
|
67
|
+
/>
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
54
71
|
case 'markdown': {
|
|
55
72
|
return <Markdown content={content} loading={loading} />;
|
|
56
73
|
}
|
package/src/libs/mcp/client.ts
CHANGED
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
McpPrompt,
|
|
16
16
|
McpResource,
|
|
17
17
|
McpTool,
|
|
18
|
+
ToolCallResult,
|
|
18
19
|
createMCPError,
|
|
19
20
|
} from './types';
|
|
20
21
|
|
|
@@ -374,12 +375,12 @@ export class MCPClient {
|
|
|
374
375
|
return manifest;
|
|
375
376
|
}
|
|
376
377
|
|
|
377
|
-
async callTool(toolName: string, args: any) {
|
|
378
|
+
async callTool(toolName: string, args: any): Promise<ToolCallResult> {
|
|
378
379
|
log('Calling tool: %s with args: %O, timeout: %O', toolName, args, MCP_TOOL_TIMEOUT);
|
|
379
380
|
const result = await this.mcp.callTool({ arguments: args, name: toolName }, undefined, {
|
|
380
381
|
timeout: MCP_TOOL_TIMEOUT,
|
|
381
382
|
});
|
|
382
383
|
log('Tool call result: %O', result);
|
|
383
|
-
return result;
|
|
384
|
+
return result as ToolCallResult;
|
|
384
385
|
}
|
|
385
386
|
}
|
package/src/libs/mcp/types.ts
CHANGED
|
@@ -30,6 +30,77 @@ export interface McpPrompt {
|
|
|
30
30
|
name: string;
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
/**
|
|
34
|
+
* MCP Tool Call Result Types
|
|
35
|
+
*/
|
|
36
|
+
export interface TextContent {
|
|
37
|
+
_meta?: any;
|
|
38
|
+
text: string;
|
|
39
|
+
type: 'text';
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface ImageContent {
|
|
43
|
+
_meta?: any;
|
|
44
|
+
data: string;
|
|
45
|
+
// base64
|
|
46
|
+
mimeType: string;
|
|
47
|
+
type: 'image';
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface AudioContent {
|
|
51
|
+
_meta?: any;
|
|
52
|
+
data: string;
|
|
53
|
+
// base64
|
|
54
|
+
mimeType: string;
|
|
55
|
+
type: 'audio';
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export interface ResourceContent {
|
|
59
|
+
_meta?: any;
|
|
60
|
+
resource: {
|
|
61
|
+
_meta?: any;
|
|
62
|
+
blob?: string;
|
|
63
|
+
mimeType?: string;
|
|
64
|
+
text?: string;
|
|
65
|
+
uri: string;
|
|
66
|
+
};
|
|
67
|
+
type: 'resource';
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export interface ResourceLinkContent {
|
|
71
|
+
_meta?: any;
|
|
72
|
+
description?: string;
|
|
73
|
+
icons?: Array<{
|
|
74
|
+
mimeType?: string;
|
|
75
|
+
sizes?: string[];
|
|
76
|
+
src: string;
|
|
77
|
+
}>;
|
|
78
|
+
name: string;
|
|
79
|
+
title?: string;
|
|
80
|
+
type: 'resource_link';
|
|
81
|
+
uri: string;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export type ToolCallContent =
|
|
85
|
+
| TextContent
|
|
86
|
+
| ImageContent
|
|
87
|
+
| AudioContent
|
|
88
|
+
| ResourceContent
|
|
89
|
+
| ResourceLinkContent;
|
|
90
|
+
|
|
91
|
+
export interface ToolCallResult {
|
|
92
|
+
content: ToolCallContent[];
|
|
93
|
+
isError?: boolean;
|
|
94
|
+
structuredContent?: any;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export interface MCPToolCallResult {
|
|
98
|
+
content: string;
|
|
99
|
+
error?: any;
|
|
100
|
+
state: ToolCallResult;
|
|
101
|
+
success: boolean;
|
|
102
|
+
}
|
|
103
|
+
|
|
33
104
|
/**
|
|
34
105
|
* MCP 认证配置接口
|
|
35
106
|
* 支持第一阶段的手动配置和未来的 OAuth 2.1 自动化流程
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
import { DESKTOP_USER_ID } from '@/const/desktop';
|
|
11
11
|
import { isDesktop } from '@/const/version';
|
|
12
12
|
|
|
13
|
+
import { openTelemetry } from '../middleware/openTelemetry';
|
|
13
14
|
import { userAuth } from '../middleware/userAuth';
|
|
14
15
|
import { trpc } from './init';
|
|
15
16
|
import { oidcAuth } from './middleware/oidcAuth';
|
|
@@ -24,14 +25,16 @@ export const router = trpc.router;
|
|
|
24
25
|
* Create an unprotected procedure
|
|
25
26
|
* @link https://trpc.io/docs/v11/procedures
|
|
26
27
|
**/
|
|
27
|
-
|
|
28
|
+
const baseProcedure = trpc.procedure.use(openTelemetry);
|
|
29
|
+
|
|
30
|
+
export const publicProcedure = baseProcedure.use(({ next, ctx }) => {
|
|
28
31
|
return next({
|
|
29
32
|
ctx: { ...ctx, userId: isDesktop ? DESKTOP_USER_ID : ctx.userId },
|
|
30
33
|
});
|
|
31
34
|
});
|
|
32
35
|
|
|
33
36
|
// procedure that asserts that the user is logged in
|
|
34
|
-
export const authedProcedure =
|
|
37
|
+
export const authedProcedure = baseProcedure.use(oidcAuth).use(userAuth);
|
|
35
38
|
|
|
36
39
|
/**
|
|
37
40
|
* Create a server-side caller
|