@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.
Files changed (103) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/apps/desktop/src/main/modules/networkProxy/__tests__/dispatcher.test.ts +401 -0
  3. package/apps/desktop/src/main/modules/networkProxy/__tests__/tester.test.ts +531 -0
  4. package/apps/desktop/src/main/modules/networkProxy/__tests__/urlBuilder.test.ts +349 -0
  5. package/apps/desktop/src/main/modules/networkProxy/__tests__/validator.test.ts +492 -0
  6. package/changelog/v1.json +5 -0
  7. package/locales/ar/auth.json +45 -1
  8. package/locales/bg-BG/auth.json +45 -1
  9. package/locales/de-DE/auth.json +45 -1
  10. package/locales/en-US/auth.json +45 -1
  11. package/locales/es-ES/auth.json +45 -1
  12. package/locales/fa-IR/auth.json +45 -1
  13. package/locales/fr-FR/auth.json +45 -1
  14. package/locales/it-IT/auth.json +45 -1
  15. package/locales/ja-JP/auth.json +45 -1
  16. package/locales/ko-KR/auth.json +45 -1
  17. package/locales/nl-NL/auth.json +45 -1
  18. package/locales/pl-PL/auth.json +45 -1
  19. package/locales/pt-BR/auth.json +45 -1
  20. package/locales/ru-RU/auth.json +45 -1
  21. package/locales/tr-TR/auth.json +45 -1
  22. package/locales/vi-VN/auth.json +45 -1
  23. package/locales/zh-CN/auth.json +45 -1
  24. package/locales/zh-TW/auth.json +45 -1
  25. package/package.json +1 -1
  26. package/packages/context-engine/src/processors/MessageCleanup.ts +1 -0
  27. package/packages/context-engine/src/processors/__tests__/MessageCleanup.test.ts +28 -0
  28. package/packages/obervability-otel/package.json +3 -1
  29. package/packages/obervability-otel/src/api.ts +2 -0
  30. package/packages/obervability-otel/src/trpc/convention.ts +16 -0
  31. package/packages/obervability-otel/src/trpc/index.test.ts +38 -0
  32. package/packages/obervability-otel/src/trpc/index.ts +62 -0
  33. package/packages/obervability-otel/src/trpc/metrics.ts +31 -0
  34. package/packages/types/src/usage/usageRecord.ts +54 -0
  35. package/src/app/[variants]/(main)/profile/hooks/useCategory.tsx +10 -1
  36. package/src/app/[variants]/(main)/profile/usage/Client.tsx +114 -0
  37. package/src/app/[variants]/(main)/profile/usage/features/UsageCards/ActiveModels/ModelTable.tsx +175 -0
  38. package/src/app/[variants]/(main)/profile/usage/features/UsageCards/ActiveModels/index.tsx +126 -0
  39. package/src/app/[variants]/(main)/profile/usage/features/UsageCards/MonthSpend.tsx +53 -0
  40. package/src/app/[variants]/(main)/profile/usage/features/UsageCards/TodaySpend.tsx +67 -0
  41. package/src/app/[variants]/(main)/profile/usage/features/UsageCards/index.tsx +19 -0
  42. package/src/app/[variants]/(main)/profile/usage/features/UsageTable.tsx +145 -0
  43. package/src/app/[variants]/(main)/profile/usage/features/UsageTrends.tsx +107 -0
  44. package/src/app/[variants]/(main)/profile/usage/features/components/UsageBarChart.tsx +48 -0
  45. package/src/app/[variants]/(main)/profile/usage/page.tsx +23 -0
  46. package/src/features/Conversation/Messages/Assistant/Tool/Render/CustomRender.tsx +3 -3
  47. package/src/features/Conversation/Messages/Group/Actions/WithoutContentId.tsx +37 -14
  48. package/src/features/Conversation/Messages/Group/Error/index.tsx +1 -1
  49. package/src/features/Conversation/Messages/Group/GroupChildren.tsx +13 -35
  50. package/src/features/Conversation/Messages/Group/GroupItem.tsx +43 -0
  51. package/src/features/Conversation/Messages/Group/Tool/Inspector/index.tsx +1 -2
  52. package/src/features/Conversation/Messages/Group/Tool/Render/CustomRender.tsx +1 -1
  53. package/src/features/Conversation/Messages/Group/Tool/index.tsx +0 -2
  54. package/src/features/Conversation/Messages/Group/index.tsx +7 -2
  55. package/src/features/Conversation/hooks/useChatListActionsBar.tsx +21 -7
  56. package/src/features/PluginsUI/Render/BuiltinType/index.tsx +1 -1
  57. package/src/features/PluginsUI/Render/MCPType/index.tsx +52 -0
  58. package/src/features/PluginsUI/Render/StandaloneType/Iframe.tsx +2 -2
  59. package/src/features/PluginsUI/Render/index.tsx +17 -0
  60. package/src/libs/mcp/client.ts +3 -2
  61. package/src/libs/mcp/types.ts +71 -0
  62. package/src/libs/trpc/lambda/index.ts +5 -2
  63. package/src/libs/trpc/middleware/openTelemetry.ts +141 -0
  64. package/src/locales/default/auth.ts +44 -0
  65. package/src/locales/default/chat.ts +1 -0
  66. package/src/server/routers/desktop/mcp.ts +1 -3
  67. package/src/server/routers/lambda/index.ts +2 -0
  68. package/src/server/routers/lambda/usage.ts +36 -0
  69. package/src/server/routers/tools/mcp.ts +1 -3
  70. package/src/server/services/mcp/index.test.ts +28 -15
  71. package/src/server/services/mcp/index.ts +29 -18
  72. package/src/server/services/usage/index.test.ts +310 -0
  73. package/src/server/services/usage/index.ts +164 -0
  74. package/src/services/chat/contextEngineering.test.ts +4 -0
  75. package/src/services/mcp.test.ts +7 -1
  76. package/src/services/mcp.ts +13 -12
  77. package/src/services/usage.ts +13 -0
  78. package/src/store/chat/agents/createAgentExecutors.ts +2 -3
  79. package/src/store/chat/slices/aiChat/actions/conversationLifecycle.ts +40 -1
  80. package/src/store/chat/slices/aiChat/actions/streamingExecutor.ts +13 -5
  81. package/src/store/chat/slices/builtinTool/actions/__tests__/localSystem.test.ts +3 -3
  82. package/src/store/chat/slices/builtinTool/actions/__tests__/search.test.ts +6 -6
  83. package/src/store/chat/slices/builtinTool/actions/interpreter.ts +2 -2
  84. package/src/store/chat/slices/builtinTool/actions/localSystem.ts +2 -2
  85. package/src/store/chat/slices/builtinTool/actions/search.ts +6 -6
  86. package/src/store/chat/slices/message/actions/publicApi.ts +19 -1
  87. package/src/store/chat/slices/message/initialState.ts +5 -0
  88. package/src/store/chat/slices/message/selectors/chat.test.ts +22 -602
  89. package/src/store/chat/slices/message/selectors/chat.ts +0 -2
  90. package/src/store/chat/slices/message/selectors/dbMessage.test.ts +51 -0
  91. package/src/store/chat/slices/message/selectors/displayMessage.test.ts +818 -0
  92. package/src/store/chat/slices/message/selectors/displayMessage.ts +52 -1
  93. package/src/store/chat/slices/message/selectors/messageState.ts +2 -0
  94. package/src/store/chat/slices/plugin/action.test.ts +4 -4
  95. package/src/store/chat/slices/plugin/actions/index.ts +39 -0
  96. package/src/store/chat/slices/plugin/actions/internals.ts +83 -0
  97. package/src/store/chat/slices/plugin/actions/optimisticUpdate.ts +188 -0
  98. package/src/store/chat/slices/plugin/actions/pluginTypes.ts +213 -0
  99. package/src/store/chat/slices/plugin/actions/publicApi.ts +115 -0
  100. package/src/store/chat/slices/plugin/actions/workflow.ts +121 -0
  101. package/src/store/chat/store.ts +1 -1
  102. package/src/store/global/initialState.ts +1 -0
  103. 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, ActionIconGroupItemType } from '@lobehub/ui';
2
+ import { ActionIconGroup, type ActionIconGroupEvent } from '@lobehub/ui';
3
3
  import { useSearchParams } from 'next/navigation';
4
- import { memo, useCallback, useContext, useMemo } from 'react';
4
+ import { memo, useCallback, useContext } from 'react';
5
5
 
6
6
  import { useChatStore } from '@/store/chat';
7
- import { threadSelectors } from '@/store/chat/selectors';
8
- import { useSessionStore } from '@/store/session';
9
- import { sessionSelectors } from '@/store/session/selectors';
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 [isThreadMode, hasThread] = useChatStore((s) => [
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 { delAndRegenerate, del } = useChatListActionsBar({ hasThread });
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 = useMemo(() => {
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 [deleteMessage, delAndRegenerateMessage, delAndResendThreadMessage] = useChatStore((s) => [
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.deleteMessage]);
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 { memo, use } from 'react';
3
+ import isEqual from 'fast-deep-equal';
4
+ import { memo } from 'react';
4
5
  import { Flexbox } from 'react-layout-kit';
5
6
 
6
- import { VirtuosoContext } from '@/features/Conversation/components/VirtualizedList/VirtuosoContext';
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 item.id === contentId ? (
40
- <Flexbox
41
- key={index}
42
- onDoubleClick={(e) => {
43
- if (disableEditing || item.error || !e.altKey) return;
44
-
45
- toggleMessageEditing(item.id, true);
46
- virtuosoRef?.current?.scrollIntoView({
47
- align: 'start',
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 && !hidePluginUI && (
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(!['default', 'mcp'].includes(plugin.type));
33
+ setShowPluginRender(plugin.type !== 'default');
34
34
  }, [plugin?.type, setShowPluginRender]);
35
35
 
36
36
  // Parse and display result content
@@ -39,8 +39,6 @@ const Tool = memo<GroupToolProps>(
39
39
  <Inspectors
40
40
  apiName={apiName}
41
41
  arguments={requestArgs}
42
- // mcp don't have ui render
43
- hidePluginUI={type === 'mcp'}
44
42
  id={id}
45
43
  identifier={identifier}
46
44
  index={index}
@@ -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 { chatSelectors, messageStateSelectors } from '@/store/chat/slices/message/selectors';
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(chatSelectors.getGroupLatestMessageWithoutTools(id));
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
- }: { hasThread?: boolean; isRegenerating?: boolean } = {}): ChatListActionsBar => {
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', { defaultValue: 'Create Sub Topic' }),
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', { defaultValue: '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', { defaultValue: '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', { defaultValue: '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
  };
@@ -1,7 +1,7 @@
1
+ import { safeParseJSON } from '@lobechat/utils';
1
2
  import { memo } from 'react';
2
3
 
3
4
  import { BuiltinToolsRenders } from '@/tools/renders';
4
- import { safeParseJSON } from '@/utils/safeParseJSON';
5
5
 
6
6
  import { useParseContent } from '../useParseContent';
7
7
 
@@ -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 updatePluginState = useChatStore((s) => s.updatePluginState);
97
+ const optimisticUpdatePluginState = useChatStore((s) => s.optimisticUpdatePluginState);
98
98
  useOnPluginStateUpdate((key, value) => {
99
- updatePluginState(id, { [key]: value });
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
  }
@@ -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
  }
@@ -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
- export const publicProcedure = trpc.procedure.use(({ next, ctx }) => {
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 = trpc.procedure.use(oidcAuth).use(userAuth);
37
+ export const authedProcedure = baseProcedure.use(oidcAuth).use(userAuth);
35
38
 
36
39
  /**
37
40
  * Create a server-side caller