@lobehub/lobehub 2.0.0-next.48 → 2.0.0-next.49

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 (90) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/README.md +1 -1
  3. package/README.zh-CN.md +1 -1
  4. package/changelog/v1.json +12 -0
  5. package/locales/ar/chat.json +1 -0
  6. package/locales/ar/topic.json +1 -0
  7. package/locales/bg-BG/chat.json +1 -0
  8. package/locales/bg-BG/topic.json +1 -0
  9. package/locales/de-DE/chat.json +1 -0
  10. package/locales/de-DE/topic.json +1 -0
  11. package/locales/en-US/chat.json +1 -0
  12. package/locales/en-US/topic.json +1 -0
  13. package/locales/es-ES/chat.json +1 -0
  14. package/locales/es-ES/topic.json +1 -0
  15. package/locales/fa-IR/chat.json +1 -0
  16. package/locales/fa-IR/topic.json +1 -0
  17. package/locales/fr-FR/chat.json +1 -0
  18. package/locales/fr-FR/topic.json +1 -0
  19. package/locales/it-IT/chat.json +1 -0
  20. package/locales/it-IT/topic.json +1 -0
  21. package/locales/ja-JP/chat.json +1 -0
  22. package/locales/ja-JP/topic.json +1 -0
  23. package/locales/ko-KR/chat.json +1 -0
  24. package/locales/ko-KR/topic.json +1 -0
  25. package/locales/nl-NL/chat.json +1 -0
  26. package/locales/nl-NL/topic.json +1 -0
  27. package/locales/pl-PL/chat.json +1 -0
  28. package/locales/pl-PL/topic.json +1 -0
  29. package/locales/pt-BR/chat.json +1 -0
  30. package/locales/pt-BR/topic.json +1 -0
  31. package/locales/ru-RU/chat.json +1 -0
  32. package/locales/ru-RU/topic.json +1 -0
  33. package/locales/tr-TR/chat.json +1 -0
  34. package/locales/tr-TR/topic.json +1 -0
  35. package/locales/vi-VN/chat.json +1 -0
  36. package/locales/vi-VN/topic.json +1 -0
  37. package/locales/zh-CN/chat.json +1 -0
  38. package/locales/zh-CN/discover.json +1 -1
  39. package/locales/zh-CN/topic.json +1 -0
  40. package/locales/zh-TW/chat.json +1 -0
  41. package/locales/zh-TW/topic.json +1 -0
  42. package/package.json +9 -3
  43. package/packages/agent-runtime/src/core/InterventionChecker.ts +5 -16
  44. package/packages/agent-runtime/src/core/__tests__/InterventionChecker.test.ts +27 -80
  45. package/packages/agent-runtime/src/core/__tests__/runtime.test.ts +32 -13
  46. package/packages/agent-runtime/src/core/runtime.ts +7 -3
  47. package/packages/agent-runtime/src/types/event.ts +2 -1
  48. package/packages/agent-runtime/src/types/generalAgent.ts +1 -0
  49. package/packages/agent-runtime/src/types/instruction.ts +3 -2
  50. package/packages/agent-runtime/src/types/state.ts +3 -1
  51. package/packages/conversation-flow/src/transformation/FlatListBuilder.ts +4 -1
  52. package/packages/database/src/models/message.ts +3 -0
  53. package/packages/obervability-otel/src/node.ts +15 -1
  54. package/packages/types/src/message/common/base.ts +2 -2
  55. package/packages/types/src/message/common/tools.ts +16 -10
  56. package/packages/types/src/message/ui/chat.ts +7 -1
  57. package/packages/types/src/tool/intervention.ts +2 -3
  58. package/packages/types/src/user/settings/tool.ts +15 -28
  59. package/renovate.json +28 -11
  60. package/src/app/[variants]/(main)/chat/components/topic/features/Topic/TopicListContent/TopicItem/TopicContent.tsx +1 -1
  61. package/src/app/[variants]/(main)/chat/session/features/SessionListContent/List/Item/Actions.tsx +1 -1
  62. package/src/features/Conversation/Messages/Group/GroupChildren.tsx +20 -15
  63. package/src/features/Conversation/Messages/Group/GroupContext.tsx +15 -0
  64. package/src/features/Conversation/Messages/Group/Tool/Inspector/BuiltinPluginTitle.tsx +2 -4
  65. package/src/features/Conversation/Messages/Group/Tool/Inspector/ToolTitle.tsx +3 -5
  66. package/src/features/Conversation/Messages/Group/Tool/Inspector/index.tsx +19 -7
  67. package/src/features/Conversation/Messages/Group/Tool/Render/Arguments/index.tsx +14 -12
  68. package/src/features/Conversation/Messages/Group/Tool/Render/Intervention/ApprovalActions.tsx +143 -0
  69. package/src/features/Conversation/Messages/Group/Tool/Render/Intervention/KeyValueEditor.tsx +213 -0
  70. package/src/features/Conversation/Messages/Group/Tool/Render/Intervention/ModeSelector.tsx +134 -0
  71. package/src/features/Conversation/Messages/Group/Tool/Render/Intervention/index.tsx +99 -0
  72. package/src/features/Conversation/Messages/Group/Tool/Render/RejectedResponse.tsx +45 -0
  73. package/src/features/Conversation/Messages/Group/Tool/Render/index.tsx +23 -1
  74. package/src/features/Conversation/Messages/Group/Tool/index.tsx +42 -18
  75. package/src/features/Conversation/Messages/Group/Tools.tsx +3 -1
  76. package/src/locales/default/chat.ts +22 -0
  77. package/src/locales/default/common.ts +1 -0
  78. package/src/locales/default/topic.ts +1 -0
  79. package/src/server/routers/lambda/message.ts +4 -1
  80. package/src/server/services/message/index.ts +13 -0
  81. package/src/services/message/index.ts +17 -2
  82. package/src/store/chat/agents/GeneralChatAgent.ts +141 -24
  83. package/src/store/chat/agents/__tests__/GeneralChatAgent.test.ts +605 -0
  84. package/src/store/chat/agents/createAgentExecutors.ts +144 -26
  85. package/src/store/chat/agents/createToolEngine.ts +22 -0
  86. package/src/store/chat/slices/aiChat/actions/conversationControl.ts +106 -0
  87. package/src/store/chat/slices/aiChat/actions/streamingExecutor.ts +54 -26
  88. package/src/store/chat/slices/message/reducer.ts +2 -1
  89. package/src/store/chat/slices/plugin/actions/optimisticUpdate.ts +26 -1
  90. package/src/store/user/slices/settings/action.ts +15 -0
@@ -0,0 +1,134 @@
1
+ import { Button, Dropdown, Icon, type MenuProps } from '@lobehub/ui';
2
+ import { createStyles } from 'antd-style';
3
+ import { ChevronDown, Hand, ListChecks, Zap } from 'lucide-react';
4
+ import { memo, useCallback, useMemo } from 'react';
5
+ import { useTranslation } from 'react-i18next';
6
+ import { Center } from 'react-layout-kit';
7
+
8
+ import { useUserStore } from '@/store/user';
9
+
10
+ import { ApprovalMode } from './index';
11
+
12
+ const useStyles = createStyles(({ css, token }) => ({
13
+ icon: css`
14
+ border: 1px solid ${token.colorFillTertiary};
15
+ border-radius: ${token.borderRadius}px;
16
+ background: ${token.colorBgElevated};
17
+ `,
18
+ modeButton: css`
19
+ font-size: ${token.fontSizeSM}px;
20
+ color: ${token.colorTextSecondary};
21
+ `,
22
+ modeDesc: css`
23
+ margin-block-start: 2px;
24
+ font-size: 12px;
25
+ line-height: 1.4;
26
+ color: ${token.colorTextDescription};
27
+ `,
28
+ modeItem: css`
29
+ min-width: 160px;
30
+ `,
31
+ modeLabel: css`
32
+ font-size: ${token.fontSize}px;
33
+ font-weight: 500;
34
+ line-height: 1.4;
35
+ color: ${token.colorText};
36
+ `,
37
+ }));
38
+
39
+ const ModeSelector = memo(() => {
40
+ const { t } = useTranslation('chat');
41
+ const { styles } = useStyles();
42
+ const [approvalMode, setSettings] = useUserStore((s) => [
43
+ s.settings.tool?.approvalMode || 'manual',
44
+ s.setSettings,
45
+ ]);
46
+
47
+ const modeLabels = useMemo(
48
+ () => ({
49
+ 'allow-list': t('tool.intervention.mode.allowList'),
50
+ 'auto-run': t('tool.intervention.mode.autoRun'),
51
+ 'manual': t('tool.intervention.mode.manual'),
52
+ }),
53
+ [t],
54
+ );
55
+
56
+ const handleModeChange = useCallback(
57
+ async (mode: ApprovalMode) => {
58
+ await setSettings({ tool: { approvalMode: mode } });
59
+ },
60
+ [setSettings],
61
+ );
62
+
63
+ const menuItems = useMemo<MenuProps['items']>(
64
+ () => [
65
+ {
66
+ icon: (
67
+ <Center className={styles.icon} height={32} width={32}>
68
+ <Icon icon={Zap} />
69
+ </Center>
70
+ ),
71
+ key: 'auto-run',
72
+ label: (
73
+ <div className={styles.modeItem}>
74
+ <div className={styles.modeLabel}>{modeLabels['auto-run']}</div>
75
+ <div className={styles.modeDesc}>{t('tool.intervention.mode.autoRunDesc')}</div>
76
+ </div>
77
+ ),
78
+ onClick: () => handleModeChange('auto-run'),
79
+ },
80
+ {
81
+ icon: (
82
+ <Center className={styles.icon} height={32} width={32}>
83
+ <Icon icon={ListChecks} />
84
+ </Center>
85
+ ),
86
+ key: 'allow-list',
87
+ label: (
88
+ <div className={styles.modeItem}>
89
+ <div className={styles.modeLabel}>{modeLabels['allow-list']}</div>
90
+ <div className={styles.modeDesc}>{t('tool.intervention.mode.allowListDesc')}</div>
91
+ </div>
92
+ ),
93
+ onClick: () => handleModeChange('allow-list'),
94
+ },
95
+ {
96
+ icon: (
97
+ <Center className={styles.icon} height={32} width={32}>
98
+ <Icon icon={Hand} />
99
+ </Center>
100
+ ),
101
+ key: 'manual',
102
+ label: (
103
+ <div className={styles.modeItem}>
104
+ <div className={styles.modeLabel}>{modeLabels.manual}</div>
105
+ <div className={styles.modeDesc}>{t('tool.intervention.mode.manualDesc')}</div>
106
+ </div>
107
+ ),
108
+ onClick: () => handleModeChange('manual'),
109
+ },
110
+ ],
111
+ [modeLabels, handleModeChange, styles, t],
112
+ );
113
+
114
+ return (
115
+ <Dropdown
116
+ // @ts-expect-error activeKey 没在 Dropdown key 里很奇怪
117
+ menu={{ activeKey: approvalMode, items: menuItems }}
118
+ placement="bottomLeft"
119
+ >
120
+ <Button
121
+ className={styles.modeButton}
122
+ color={'default'}
123
+ icon={ChevronDown}
124
+ iconPosition="end"
125
+ size="small"
126
+ variant={'text'}
127
+ >
128
+ {modeLabels[approvalMode]}
129
+ </Button>
130
+ </Dropdown>
131
+ );
132
+ });
133
+
134
+ export default ModeSelector;
@@ -0,0 +1,99 @@
1
+ import { safeParseJSON } from '@lobechat/utils';
2
+ import { ActionIcon } from '@lobehub/ui';
3
+ import { Edit3Icon } from 'lucide-react';
4
+ import { Suspense, memo, useCallback, useState } from 'react';
5
+ import { useTranslation } from 'react-i18next';
6
+ import { Flexbox } from 'react-layout-kit';
7
+
8
+ import { useChatStore } from '@/store/chat';
9
+ import { useUserStore } from '@/store/user';
10
+
11
+ import Arguments from '../Arguments';
12
+ import ApprovalActions from './ApprovalActions';
13
+ import KeyValueEditor from './KeyValueEditor';
14
+ import ModeSelector from './ModeSelector';
15
+
16
+ export type ApprovalMode = 'auto-run' | 'allow-list' | 'manual';
17
+
18
+ interface InterventionProps {
19
+ apiName: string;
20
+ id: string;
21
+ identifier: string;
22
+ requestArgs: string;
23
+ toolCallId: string;
24
+ }
25
+
26
+ const Intervention = memo<InterventionProps>(
27
+ ({ requestArgs, id, identifier, apiName, toolCallId }) => {
28
+ const { t } = useTranslation('chat');
29
+ const approvalMode = useUserStore((s) => s.settings.tool?.approvalMode || 'manual');
30
+ const [isEditing, setIsEditing] = useState(false);
31
+ const [optimisticUpdatePluginArguments] = useChatStore((s) => [
32
+ s.optimisticUpdatePluginArguments,
33
+ ]);
34
+
35
+ const handleCancel = useCallback(() => {
36
+ setIsEditing(false);
37
+ }, []);
38
+
39
+ const handleFinish = useCallback(
40
+ async (editedObject: Record<string, any>) => {
41
+ if (!id) return;
42
+
43
+ try {
44
+ const newArgsString = JSON.stringify(editedObject, null, 2);
45
+
46
+ if (newArgsString !== requestArgs) {
47
+ await optimisticUpdatePluginArguments(id, editedObject, true);
48
+ }
49
+ setIsEditing(false);
50
+ } catch (error) {
51
+ console.error('Error stringifying arguments:', error);
52
+ }
53
+ },
54
+ [requestArgs, id],
55
+ );
56
+
57
+ if (isEditing)
58
+ return (
59
+ <Suspense fallback={<Arguments arguments={requestArgs} />}>
60
+ <KeyValueEditor
61
+ initialValue={safeParseJSON(requestArgs || '')}
62
+ onCancel={handleCancel}
63
+ onFinish={handleFinish}
64
+ />
65
+ </Suspense>
66
+ );
67
+
68
+ return (
69
+ <Flexbox gap={12}>
70
+ <Arguments
71
+ actions={
72
+ <ActionIcon
73
+ icon={Edit3Icon}
74
+ onClick={() => {
75
+ setIsEditing(true);
76
+ }}
77
+ size={'small'}
78
+ title={t('edit', { ns: 'common' })}
79
+ />
80
+ }
81
+ arguments={requestArgs}
82
+ />
83
+
84
+ <Flexbox horizontal justify={'space-between'}>
85
+ <ModeSelector />
86
+ <ApprovalActions
87
+ apiName={apiName}
88
+ approvalMode={approvalMode}
89
+ identifier={identifier}
90
+ messageId={id}
91
+ toolCallId={toolCallId}
92
+ />
93
+ </Flexbox>
94
+ </Flexbox>
95
+ );
96
+ },
97
+ );
98
+
99
+ export default Intervention;
@@ -0,0 +1,45 @@
1
+ import { Icon } from '@lobehub/ui';
2
+ import { createStyles } from 'antd-style';
3
+ import { AlertTriangle } from 'lucide-react';
4
+ import { memo } from 'react';
5
+ import { useTranslation } from 'react-i18next';
6
+ import { Flexbox } from 'react-layout-kit';
7
+
8
+ const useStyles = createStyles(({ css, token }) => ({
9
+ container: css`
10
+ padding-block: 8px;
11
+ padding-inline: 6px;
12
+ `,
13
+ reason: css`
14
+ font-size: 12px;
15
+ color: ${token.colorTextTertiary};
16
+ `,
17
+ title: css`
18
+ font-size: 14px;
19
+ color: ${token.colorTextSecondary};
20
+ `,
21
+ }));
22
+
23
+ interface RejectedResponseProps {
24
+ reason?: string;
25
+ }
26
+
27
+ const RejectedResponse = memo<RejectedResponseProps>(({ reason }) => {
28
+ const { t } = useTranslation('chat');
29
+ const { styles, theme } = useStyles();
30
+
31
+ return (
32
+ <Flexbox className={styles.container} gap={8}>
33
+ <Flexbox align={'center'} gap={8} horizontal>
34
+ <Icon color={theme.colorWarning} icon={AlertTriangle} size={16} />
35
+ <div className={styles.title}>
36
+ {reason
37
+ ? t('tool.intervention.rejectedWithReason', { reason })
38
+ : t('tool.intervention.toolRejected')}
39
+ </div>
40
+ </Flexbox>
41
+ </Flexbox>
42
+ );
43
+ });
44
+
45
+ export default RejectedResponse;
@@ -1,15 +1,18 @@
1
1
  import { LOADING_FLAT } from '@lobechat/const';
2
- import { ChatToolResult } from '@lobechat/types';
2
+ import { ChatToolResult, ToolIntervention } from '@lobechat/types';
3
3
  import { Suspense, memo } from 'react';
4
4
 
5
5
  import CustomRender from './CustomRender';
6
6
  import ErrorResponse from './ErrorResponse';
7
+ import Intervention from './Intervention';
7
8
  import LoadingPlaceholder from './LoadingPlaceholder';
9
+ import RejectedResponse from './RejectedResponse';
8
10
 
9
11
  interface RenderProps {
10
12
  apiName: string;
11
13
  arguments?: string;
12
14
  identifier: string;
15
+ intervention?: ToolIntervention;
13
16
  /**
14
17
  * ContentBlock ID (not the group message ID)
15
18
  */
@@ -18,6 +21,7 @@ interface RenderProps {
18
21
  setShowPluginRender: (show: boolean) => void;
19
22
  showPluginRender: boolean;
20
23
  toolCallId: string;
24
+ toolMessageId?: string;
21
25
  type?: string;
22
26
  }
23
27
 
@@ -38,7 +42,25 @@ const Render = memo<RenderProps>(
38
42
  apiName,
39
43
  result,
40
44
  type,
45
+ intervention,
46
+ toolMessageId,
41
47
  }) => {
48
+ if (toolMessageId && intervention?.status === 'pending') {
49
+ return (
50
+ <Intervention
51
+ apiName={apiName}
52
+ id={toolMessageId}
53
+ identifier={identifier}
54
+ requestArgs={requestArgs || ''}
55
+ toolCallId={toolCallId}
56
+ />
57
+ );
58
+ }
59
+
60
+ if (intervention?.status === 'rejected') {
61
+ return <RejectedResponse reason={intervention.rejectedReason} />;
62
+ }
63
+
42
64
  if (!result) return null;
43
65
 
44
66
  // Handle error state
@@ -1,5 +1,5 @@
1
- import { ChatToolResult } from '@lobechat/types';
2
- import { CSSProperties, memo, useState } from 'react';
1
+ import { ChatToolResult, ToolIntervention } from '@lobechat/types';
2
+ import { CSSProperties, memo, useEffect, useState } from 'react';
3
3
  import { Flexbox } from 'react-layout-kit';
4
4
 
5
5
  import AnimatedCollapsed from '@/components/AnimatedCollapsed';
@@ -10,15 +10,17 @@ import Render from './Render';
10
10
  export interface GroupToolProps {
11
11
  apiName: string;
12
12
  arguments?: string;
13
- id: string;
14
- identifier: string;
15
- index: number;
16
13
  /**
17
14
  * ContentBlock ID (not the group message ID)
18
15
  */
19
- messageId: string;
16
+ assistantMessageId: string;
17
+ id: string;
18
+ identifier: string;
19
+ index: number;
20
+ intervention?: ToolIntervention;
20
21
  result?: ChatToolResult;
21
22
  style?: CSSProperties;
23
+ toolMessageId?: string;
22
24
  type?: string;
23
25
  }
24
26
 
@@ -29,10 +31,29 @@ export interface GroupToolProps {
29
31
  * so we always show the results directly.
30
32
  */
31
33
  const Tool = memo<GroupToolProps>(
32
- ({ arguments: requestArgs, apiName, messageId, id, index, identifier, style, result, type }) => {
34
+ ({
35
+ arguments: requestArgs,
36
+ apiName,
37
+ assistantMessageId,
38
+ id,
39
+ intervention,
40
+ index,
41
+ identifier,
42
+ style,
43
+ result,
44
+ type,
45
+ toolMessageId,
46
+ }) => {
33
47
  // Default to false since group messages are all completed
34
- const [showDetail, setShowDetail] = useState(false);
35
- const [showPluginRender, setShowPluginRender] = useState(false);
48
+
49
+ const [showToolContent, setShowToolDetail] = useState(false);
50
+ const [showCustomPluginUI, setShowCustomPluginUI] = useState(false);
51
+
52
+ useEffect(() => {
53
+ if (intervention?.status === 'pending') {
54
+ setShowToolDetail(true);
55
+ }
56
+ }, [intervention?.status]);
36
57
 
37
58
  return (
38
59
  <Flexbox gap={8} style={style}>
@@ -42,24 +63,27 @@ const Tool = memo<GroupToolProps>(
42
63
  id={id}
43
64
  identifier={identifier}
44
65
  index={index}
45
- messageId={messageId}
66
+ intervention={intervention}
67
+ messageId={assistantMessageId}
46
68
  result={result}
47
- setShowPluginRender={setShowPluginRender}
48
- setShowRender={setShowDetail}
49
- showPluginRender={showPluginRender}
50
- showRender={showDetail}
69
+ setShowPluginRender={setShowCustomPluginUI}
70
+ setShowRender={setShowToolDetail}
71
+ showPluginRender={showCustomPluginUI}
72
+ showRender={showToolContent}
51
73
  type={type}
52
74
  />
53
- <AnimatedCollapsed open={showDetail} width={{ collapsed: 'auto' }}>
75
+ <AnimatedCollapsed open={showToolContent} width={{ collapsed: 'auto' }}>
54
76
  <Render
55
77
  apiName={apiName}
56
78
  arguments={requestArgs}
57
79
  identifier={identifier}
58
- messageId={messageId}
80
+ intervention={intervention}
81
+ messageId={assistantMessageId}
59
82
  result={result}
60
- setShowPluginRender={setShowPluginRender}
61
- showPluginRender={showPluginRender}
83
+ setShowPluginRender={setShowCustomPluginUI}
84
+ showPluginRender={showCustomPluginUI}
62
85
  toolCallId={id}
86
+ toolMessageId={toolMessageId}
63
87
  type={type}
64
88
  />
65
89
  </AnimatedCollapsed>
@@ -32,12 +32,14 @@ export const Tools = memo<ToolsRendererProps>(({ messageId, tools }) => {
32
32
  <Tool
33
33
  apiName={tool.apiName}
34
34
  arguments={tool.arguments}
35
+ assistantMessageId={messageId}
35
36
  id={tool.id}
36
37
  identifier={tool.identifier}
37
38
  index={index}
39
+ intervention={tool.intervention}
38
40
  key={tool.id}
39
- messageId={messageId}
40
41
  result={tool.result}
42
+ toolMessageId={tool.result_msg_id}
41
43
  type={tool.type}
42
44
  />
43
45
  ))}
@@ -266,6 +266,8 @@ export default {
266
266
 
267
267
  noSelectedAgents: '还未选择成员',
268
268
 
269
+ openInNewWindow: '单独打开页面',
270
+
269
271
  owner: '群主',
270
272
 
271
273
  pin: '置顶',
@@ -398,6 +400,26 @@ export default {
398
400
  remained: '剩余',
399
401
  used: '使用',
400
402
  },
403
+ tool: {
404
+ intervention: {
405
+ approve: '批准',
406
+ approveAndRemember: '批准并记住',
407
+ approveOnce: '仅本次批准',
408
+ mode: {
409
+ allowList: '白名单',
410
+ allowListDesc: '仅自动执行已批准的工具',
411
+ autoRun: '自动批准',
412
+ autoRunDesc: '自动批准所有工具执行',
413
+ manual: '手动',
414
+ manualDesc: '每次调用都需要手动批准',
415
+ },
416
+ reject: '拒绝',
417
+ rejectReasonPlaceholder: '输入拒绝原因将帮助 Agent 理解并优化后续行动',
418
+ rejectTitle: '拒绝本次工具调用',
419
+ rejectedWithReason: '本次工具调用被主动拒绝:{{reason}}',
420
+ toolRejected: '本次工具调用被主动拒绝',
421
+ },
422
+ },
401
423
  topic: {
402
424
  checkOpenNewTopic: '是否开启新话题?',
403
425
  checkSaveCurrentMessages: '是否保存当前会话为话题?',
@@ -138,6 +138,7 @@ export default {
138
138
  },
139
139
  },
140
140
  close: '关闭',
141
+ confirm: '确认',
141
142
  contact: '联系我们',
142
143
  copy: '复制',
143
144
  copyFail: '复制失败',
@@ -6,6 +6,7 @@ export default {
6
6
  confirmRemoveUnstarred: '即将删除未收藏话题,删除后将不可恢复,请谨慎操作。',
7
7
  duplicate: '创建副本',
8
8
  export: '导出话题',
9
+ openInNewWindow: '单独打开页面',
9
10
  removeAll: '删除全部话题',
10
11
  removeUnstarred: '删除未收藏话题',
11
12
  },
@@ -179,11 +179,14 @@ export const messageRouter = router({
179
179
  .input(
180
180
  z.object({
181
181
  id: z.string(),
182
+ sessionId: z.string().nullable().optional(),
183
+ topicId: z.string().nullable().optional(),
182
184
  value: UpdateMessagePluginSchema.partial(),
183
185
  }),
184
186
  )
185
187
  .mutation(async ({ input, ctx }) => {
186
- return ctx.messageModel.updateMessagePlugin(input.id, input.value);
188
+ const { id, value, ...options } = input;
189
+ return ctx.messageService.updateMessagePlugin(id, value, options);
187
190
  }),
188
191
 
189
192
  updateMessageRAG: messageProcedure
@@ -150,6 +150,19 @@ export class MessageService {
150
150
  return this.queryWithSuccess(options);
151
151
  }
152
152
 
153
+ /**
154
+ * Update message plugin and return message list
155
+ * Pattern: update + conditional query
156
+ */
157
+ async updateMessagePlugin(
158
+ id: string,
159
+ value: any,
160
+ options: QueryOptions,
161
+ ): Promise<{ messages?: UIChatMessage[], success: boolean; }> {
162
+ await this.messageModel.updateMessagePlugin(id, value);
163
+ return this.queryWithSuccess(options);
164
+ }
165
+
153
166
  /**
154
167
  * Update message and return message list
155
168
  * Pattern: update + conditional query
@@ -6,6 +6,7 @@ import {
6
6
  CreateMessageParams,
7
7
  CreateMessageResult,
8
8
  MessageMetadata,
9
+ MessagePluginItem,
9
10
  ModelRankItem,
10
11
  UIChatMessage,
11
12
  UpdateMessageParams,
@@ -77,8 +78,9 @@ export class MessageService {
77
78
  };
78
79
 
79
80
  updateMessageError = async (id: string, value: ChatMessageError) => {
80
- const error = value.type ? value : { body: value, message: value.message, type: 'ApplicationRuntimeError' };
81
-
81
+ const error = value.type
82
+ ? value
83
+ : { body: value, message: value.message, type: 'ApplicationRuntimeError' };
82
84
 
83
85
  return lambdaClient.message.update.mutate({ id, value: { error } });
84
86
  };
@@ -153,6 +155,19 @@ export class MessageService {
153
155
  });
154
156
  };
155
157
 
158
+ updateMessagePlugin = async (
159
+ id: string,
160
+ value: Partial<Omit<MessagePluginItem, 'id'>>,
161
+ options?: { sessionId?: string | null; topicId?: string | null },
162
+ ): Promise<UpdateMessageResult> => {
163
+ return lambdaClient.message.updateMessagePlugin.mutate({
164
+ id,
165
+ sessionId: options?.sessionId,
166
+ topicId: options?.topicId,
167
+ value,
168
+ });
169
+ };
170
+
156
171
  updateMessageRAG = async (
157
172
  id: string,
158
173
  data: UpdateMessageRAGParams,