@lobehub/chat 0.156.2 → 0.157.1

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 (155) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/package.json +32 -31
  3. package/src/app/api/chat/[provider]/route.test.ts +2 -2
  4. package/src/app/api/chat/[provider]/route.ts +1 -1
  5. package/src/app/api/chat/models/[provider]/route.ts +1 -1
  6. package/src/app/api/config.test.ts +1 -51
  7. package/src/app/api/openai/createBizOpenAI/auth.test.ts +52 -0
  8. package/src/app/api/openai/createBizOpenAI/index.ts +1 -1
  9. package/src/app/api/plugin/gateway/route.ts +1 -1
  10. package/src/app/api/text-to-image/[provider]/route.ts +61 -0
  11. package/src/components/GalleyGrid/index.tsx +2 -2
  12. package/src/config/modelProviders/anthropic.ts +3 -0
  13. package/src/config/modelProviders/google.ts +3 -0
  14. package/src/config/modelProviders/groq.ts +5 -1
  15. package/src/config/modelProviders/minimax.ts +10 -7
  16. package/src/config/modelProviders/mistral.ts +1 -0
  17. package/src/config/modelProviders/moonshot.ts +3 -0
  18. package/src/config/modelProviders/zhipu.ts +2 -6
  19. package/src/config/server/provider.ts +1 -1
  20. package/src/database/client/core/db.ts +32 -0
  21. package/src/database/client/core/schemas.ts +9 -0
  22. package/src/database/client/models/__tests__/message.test.ts +2 -2
  23. package/src/database/client/schemas/message.ts +10 -1
  24. package/src/features/AgentSetting/store/action.ts +15 -6
  25. package/src/features/Conversation/Actions/Assistant.tsx +3 -2
  26. package/src/features/Conversation/Actions/Tool.tsx +28 -0
  27. package/src/features/Conversation/Actions/index.ts +2 -2
  28. package/src/features/Conversation/Messages/Assistant/ToolCalls/index.tsx +73 -0
  29. package/src/features/Conversation/Messages/Assistant/ToolCalls/style.ts +25 -0
  30. package/src/features/Conversation/Messages/Assistant/index.tsx +51 -0
  31. package/src/features/Conversation/Messages/Default.tsx +4 -1
  32. package/src/features/Conversation/{Plugins → Messages/Tool}/Inspector/index.tsx +35 -36
  33. package/src/features/Conversation/Messages/Tool/index.tsx +44 -0
  34. package/src/features/Conversation/Messages/index.ts +3 -2
  35. package/src/features/Conversation/Plugins/Render/StandaloneType/Iframe.tsx +1 -1
  36. package/src/features/Conversation/Plugins/Render/index.tsx +11 -2
  37. package/src/features/Conversation/components/SkeletonList.tsx +2 -2
  38. package/src/features/Conversation/index.tsx +2 -3
  39. package/src/hooks/useTokenCount.test.ts +38 -0
  40. package/src/hooks/useTokenCount.ts +1 -2
  41. package/src/libs/agent-runtime/AgentRuntime.ts +9 -1
  42. package/src/libs/agent-runtime/BaseAI.ts +5 -9
  43. package/src/libs/agent-runtime/anthropic/index.test.ts +195 -0
  44. package/src/libs/agent-runtime/anthropic/index.ts +71 -15
  45. package/src/libs/agent-runtime/azureOpenai/index.ts +6 -5
  46. package/src/libs/agent-runtime/bedrock/index.ts +24 -18
  47. package/src/libs/agent-runtime/google/index.test.ts +154 -0
  48. package/src/libs/agent-runtime/google/index.ts +91 -10
  49. package/src/libs/agent-runtime/groq/index.test.ts +41 -72
  50. package/src/libs/agent-runtime/groq/index.ts +7 -0
  51. package/src/libs/agent-runtime/minimax/index.test.ts +2 -2
  52. package/src/libs/agent-runtime/minimax/index.ts +14 -37
  53. package/src/libs/agent-runtime/mistral/index.test.ts +0 -53
  54. package/src/libs/agent-runtime/mistral/index.ts +1 -0
  55. package/src/libs/agent-runtime/moonshot/index.test.ts +1 -71
  56. package/src/libs/agent-runtime/ollama/index.test.ts +197 -0
  57. package/src/libs/agent-runtime/ollama/index.ts +3 -3
  58. package/src/libs/agent-runtime/openai/index.test.ts +0 -53
  59. package/src/libs/agent-runtime/openrouter/index.test.ts +1 -53
  60. package/src/libs/agent-runtime/perplexity/index.test.ts +0 -71
  61. package/src/libs/agent-runtime/perplexity/index.ts +2 -3
  62. package/src/libs/agent-runtime/togetherai/__snapshots__/index.test.ts.snap +886 -0
  63. package/src/libs/agent-runtime/togetherai/fixtures/models.json +8111 -0
  64. package/src/libs/agent-runtime/togetherai/index.test.ts +16 -54
  65. package/src/libs/agent-runtime/types/chat.ts +19 -3
  66. package/src/libs/agent-runtime/types/index.ts +1 -0
  67. package/src/libs/agent-runtime/types/textToImage.ts +34 -0
  68. package/src/libs/agent-runtime/utils/anthropicHelpers.test.ts +120 -1
  69. package/src/libs/agent-runtime/utils/anthropicHelpers.ts +67 -4
  70. package/src/libs/agent-runtime/utils/createError.ts +1 -0
  71. package/src/libs/agent-runtime/utils/debugStream.test.ts +70 -0
  72. package/src/libs/agent-runtime/utils/debugStream.ts +39 -9
  73. package/src/libs/agent-runtime/utils/openaiCompatibleFactory/index.test.ts +521 -0
  74. package/src/libs/agent-runtime/utils/openaiCompatibleFactory/index.ts +127 -5
  75. package/src/libs/agent-runtime/utils/response.ts +12 -0
  76. package/src/libs/agent-runtime/utils/streams/anthropic.test.ts +197 -0
  77. package/src/libs/agent-runtime/utils/streams/anthropic.ts +91 -0
  78. package/src/libs/agent-runtime/utils/streams/bedrock/claude.ts +21 -0
  79. package/src/libs/agent-runtime/utils/streams/bedrock/common.ts +32 -0
  80. package/src/libs/agent-runtime/utils/streams/bedrock/index.ts +3 -0
  81. package/src/libs/agent-runtime/utils/streams/bedrock/llama.test.ts +196 -0
  82. package/src/libs/agent-runtime/utils/streams/bedrock/llama.ts +51 -0
  83. package/src/libs/agent-runtime/utils/streams/google-ai.test.ts +97 -0
  84. package/src/libs/agent-runtime/utils/streams/google-ai.ts +68 -0
  85. package/src/libs/agent-runtime/utils/streams/index.ts +7 -0
  86. package/src/libs/agent-runtime/utils/streams/minimax.ts +39 -0
  87. package/src/libs/agent-runtime/utils/streams/ollama.test.ts +77 -0
  88. package/src/libs/agent-runtime/utils/streams/ollama.ts +38 -0
  89. package/src/libs/agent-runtime/utils/streams/openai.test.ts +263 -0
  90. package/src/libs/agent-runtime/utils/streams/openai.ts +79 -0
  91. package/src/libs/agent-runtime/utils/streams/protocol.ts +100 -0
  92. package/src/libs/agent-runtime/zeroone/index.test.ts +1 -53
  93. package/src/libs/agent-runtime/zhipu/index.test.ts +1 -1
  94. package/src/libs/agent-runtime/zhipu/index.ts +3 -2
  95. package/src/locales/default/plugin.ts +3 -4
  96. package/src/locales/default/tool.ts +1 -0
  97. package/src/migrations/FromV4ToV5/fixtures/from-v1-to-v5-output.json +245 -0
  98. package/src/migrations/FromV4ToV5/fixtures/function-input-v4.json +96 -0
  99. package/src/migrations/FromV4ToV5/fixtures/function-output-v5.json +120 -0
  100. package/src/migrations/FromV4ToV5/index.ts +58 -0
  101. package/src/migrations/FromV4ToV5/migrations.test.ts +49 -0
  102. package/src/migrations/FromV4ToV5/types/v4.ts +21 -0
  103. package/src/migrations/FromV4ToV5/types/v5.ts +27 -0
  104. package/src/migrations/index.ts +8 -1
  105. package/src/services/__tests__/chat.test.ts +10 -20
  106. package/src/services/_url.ts +1 -1
  107. package/src/services/chat.ts +78 -65
  108. package/src/services/{imageGeneration.ts → textToImage.ts} +11 -2
  109. package/src/store/chat/initialState.ts +1 -1
  110. package/src/store/chat/selectors.ts +1 -1
  111. package/src/store/chat/slices/{tool → builtinTool}/action.test.ts +1 -1
  112. package/src/store/chat/slices/{tool → builtinTool}/action.ts +16 -4
  113. package/src/store/chat/slices/enchance/action.ts +25 -21
  114. package/src/store/chat/slices/message/action.test.ts +36 -86
  115. package/src/store/chat/slices/message/action.ts +98 -169
  116. package/src/store/chat/slices/message/initialState.ts +5 -0
  117. package/src/store/chat/slices/message/reducer.ts +18 -1
  118. package/src/store/chat/slices/message/selectors.test.ts +38 -68
  119. package/src/store/chat/slices/message/selectors.ts +9 -22
  120. package/src/store/chat/slices/plugin/action.test.ts +148 -204
  121. package/src/store/chat/slices/plugin/action.ts +163 -134
  122. package/src/store/chat/slices/share/action.test.ts +3 -3
  123. package/src/store/chat/slices/share/action.ts +1 -1
  124. package/src/store/chat/slices/topic/action.ts +7 -2
  125. package/src/store/chat/store.ts +2 -2
  126. package/src/store/tool/selectors/tool.ts +6 -24
  127. package/src/store/tool/slices/builtin/action.test.ts +90 -0
  128. package/src/store/tool/slices/store/action.test.ts +6 -2
  129. package/src/store/tool/slices/store/action.ts +3 -1
  130. package/src/tools/dalle/Render/Item/Error.tsx +50 -0
  131. package/src/tools/dalle/Render/Item/Image.tsx +44 -0
  132. package/src/tools/dalle/Render/{Item.tsx → Item/index.tsx} +20 -29
  133. package/src/types/llm.ts +1 -1
  134. package/src/types/message/index.ts +9 -4
  135. package/src/types/message/tools.ts +57 -0
  136. package/src/types/openai/chat.ts +6 -0
  137. package/src/utils/fetch.test.ts +450 -1
  138. package/src/utils/fetch.ts +338 -39
  139. package/src/utils/toolCall.ts +21 -0
  140. package/src/app/api/openai/images/createImageGeneration.ts +0 -26
  141. package/src/app/api/openai/images/route.ts +0 -16
  142. package/src/features/Conversation/Actions/Function.tsx +0 -17
  143. package/src/features/Conversation/Messages/Assistant.tsx +0 -26
  144. package/src/features/Conversation/Messages/Function.tsx +0 -35
  145. package/src/libs/agent-runtime/ollama/stream.ts +0 -31
  146. /package/src/app/api/{chat → middleware}/auth/index.test.ts +0 -0
  147. /package/src/app/api/{chat → middleware}/auth/index.ts +0 -0
  148. /package/src/app/api/{chat → middleware}/auth/utils.ts +0 -0
  149. /package/src/app/api/{auth.ts → openai/createBizOpenAI/auth.ts} +0 -0
  150. /package/src/features/Conversation/{Plugins → Messages/Tool}/Inspector/PluginResultJSON.tsx +0 -0
  151. /package/src/features/Conversation/{Plugins → Messages/Tool}/Inspector/Settings.tsx +0 -0
  152. /package/src/features/Conversation/{Plugins → Messages/Tool}/Inspector/style.ts +0 -0
  153. /package/src/store/chat/slices/{tool → builtinTool}/initialState.ts +0 -0
  154. /package/src/store/chat/slices/{tool → builtinTool}/selectors.ts +0 -0
  155. /package/src/tools/dalle/Render/{EditMode.tsx → Item/EditMode.tsx} +0 -0
@@ -19,6 +19,7 @@ import {
19
19
  dbSchemaV5,
20
20
  dbSchemaV6,
21
21
  dbSchemaV7,
22
+ dbSchemaV9,
22
23
  } from './schemas';
23
24
  import { DBModel, LOBE_CHAT_LOCAL_DB_NAME } from './types/db';
24
25
 
@@ -67,6 +68,10 @@ export class BrowserDB extends Dexie {
67
68
  .stores(dbSchemaV7)
68
69
  .upgrade((trans) => this.upgradeToV8(trans));
69
70
 
71
+ this.version(9)
72
+ .stores(dbSchemaV9)
73
+ .upgrade((trans) => this.upgradeToV9(trans));
74
+
70
75
  this.files = this.table('files');
71
76
  this.sessions = this.table('sessions');
72
77
  this.messages = this.table('messages');
@@ -153,6 +158,33 @@ export class BrowserDB extends Dexie {
153
158
  }
154
159
  });
155
160
  };
161
+
162
+ upgradeToV9 = async (trans: Transaction) => {
163
+ const messages = trans.table('messages');
164
+ await messages.toCollection().modify(async (message: DBModel<DB_Message>) => {
165
+ if ((message.role as string) === 'function') {
166
+ const origin = Object.assign({}, message);
167
+
168
+ const toolCallId = `tool_call_${message.id}`;
169
+ const assistantMessageId = `tool_calls_${message.id}`;
170
+
171
+ message.role = 'tool';
172
+ message.tool_call_id = toolCallId;
173
+ message.parentId = assistantMessageId;
174
+
175
+ await messages.add({
176
+ ...origin,
177
+ content: '',
178
+ createdAt: message.createdAt - 10,
179
+ error: undefined,
180
+ id: assistantMessageId,
181
+ role: 'assistant',
182
+ tools: [{ ...message.plugin!, id: toolCallId }],
183
+ updatedAt: message.updatedAt - 10,
184
+ } as DBModel<DB_Message>);
185
+ }
186
+ });
187
+ };
156
188
  }
157
189
 
158
190
  export const browserDB = new BrowserDB();
@@ -76,3 +76,12 @@ export const dbSchemaV7 = {
76
76
  plugins:
77
77
  '&identifier, id, type, manifest.type, manifest.meta.title, manifest.meta.description, manifest.meta.author, createdAt, updatedAt',
78
78
  };
79
+ // ************************************** //
80
+ // ******* Version 9 - 2024-03-14 ******* //
81
+ // ************************************** //
82
+ // - Added id to `plugins` table
83
+ export const dbSchemaV9 = {
84
+ ...dbSchemaV7,
85
+ messages:
86
+ '&id, role, content, fromModel, favorite, tool_call_id, plugin.identifier, plugin.apiName, translate.content, createdAt, updatedAt, sessionId, topicId, quotaId, parentId, [sessionId+topicId], traceId',
87
+ };
@@ -264,14 +264,14 @@ describe('MessageModel', () => {
264
264
  it('should update a role and plugins', async () => {
265
265
  const createdMessage = await MessageModel.create(messageData);
266
266
  const updateData = {
267
- role: 'function' as const,
267
+ role: 'tool' as const,
268
268
  plugin: { apiName: 'a', identifier: 'b', arguments: 'abc' },
269
269
  };
270
270
 
271
271
  await MessageModel.update(createdMessage.id, updateData);
272
272
  const updatedMessage = await MessageModel.findById(createdMessage.id);
273
273
 
274
- expect(updatedMessage).toHaveProperty('role', 'function');
274
+ expect(updatedMessage).toHaveProperty('role', 'tool');
275
275
  });
276
276
  });
277
277
 
@@ -14,15 +14,24 @@ const PluginSchema = z.object({
14
14
  type: z.enum(['default', 'markdown', 'standalone', 'builtin']).default('default'),
15
15
  });
16
16
 
17
+ const ToolCallSchema = PluginSchema.extend({
18
+ id: z.string(),
19
+ });
20
+
17
21
  export const DB_MessageSchema = z.object({
18
- role: z.enum(['user', 'system', 'assistant', 'function']),
22
+ role: z.enum(['user', 'system', 'assistant', 'tool']),
19
23
  content: z.string(),
20
24
  files: z.array(z.string()).optional(),
21
25
  favorite: z.number().int().min(0).max(1).optional(),
22
26
  error: z.any().optional(),
23
27
 
28
+ tools: z.array(ToolCallSchema).optional(),
29
+ tool_call_id: z.string().optional(),
30
+
24
31
  plugin: PluginSchema.optional(),
25
32
  pluginState: z.any().optional(),
33
+ pluginError: z.any().optional(),
34
+
26
35
  fromModel: z.string().optional(),
27
36
  fromProvider: z.string().optional(),
28
37
  translate: TranslateSchema.optional().or(z.literal(false)),
@@ -8,6 +8,7 @@ import { TraceNameMap, TracePayload, TraceTopicType } from '@/const/trace';
8
8
  import { chatService } from '@/services/chat';
9
9
  import { LobeAgentConfig } from '@/types/agent';
10
10
  import { MetaData } from '@/types/meta';
11
+ import { MessageTextChunk } from '@/utils/fetch';
11
12
  import { setNamespace } from '@/utils/storeDebug';
12
13
 
13
14
  import { SessionLoadingState } from '../store/initialState';
@@ -246,17 +247,25 @@ export const store: StateCreator<Store, [['zustand/devtools', never]]> = (set, g
246
247
 
247
248
  streamUpdateMetaArray: (key: keyof MetaData) => {
248
249
  let value = '';
249
- return (text: string) => {
250
- value += text;
251
- get().dispatchMeta({ type: 'update', value: { [key]: value.split(',') } });
250
+ return (chunk: MessageTextChunk) => {
251
+ switch (chunk.type) {
252
+ case 'text': {
253
+ value += chunk.text;
254
+ get().dispatchMeta({ type: 'update', value: { [key]: value.split(',') } });
255
+ }
256
+ }
252
257
  };
253
258
  },
254
259
 
255
260
  streamUpdateMetaString: (key: keyof MetaData) => {
256
261
  let value = '';
257
- return (text: string) => {
258
- value += text;
259
- get().dispatchMeta({ type: 'update', value: { [key]: value } });
262
+ return (chunk: MessageTextChunk) => {
263
+ switch (chunk.type) {
264
+ case 'text': {
265
+ value += chunk.text;
266
+ get().dispatchMeta({ type: 'update', value: { [key]: value } });
267
+ }
268
+ }
260
269
  };
261
270
  },
262
271
 
@@ -6,9 +6,10 @@ import { RenderAction } from '../types';
6
6
  import { ErrorActionsBar } from './Error';
7
7
  import { useCustomActions } from './customAction';
8
8
 
9
- export const AssistantActionsBar: RenderAction = memo(({ id, onActionClick, error }) => {
9
+ export const AssistantActionsBar: RenderAction = memo(({ id, onActionClick, error, tools }) => {
10
10
  const { regenerate, edit, delAndRegenerate, copy, divider, del } = useChatListActionsBar();
11
11
  const { translate, tts } = useCustomActions();
12
+ const hasTools = !!tools;
12
13
 
13
14
  if (id === 'default') return;
14
15
 
@@ -27,7 +28,7 @@ export const AssistantActionsBar: RenderAction = memo(({ id, onActionClick, erro
27
28
  delAndRegenerate,
28
29
  del,
29
30
  ]}
30
- items={[edit, copy]}
31
+ items={[hasTools ? delAndRegenerate : edit, copy]}
31
32
  onActionClick={onActionClick}
32
33
  type="ghost"
33
34
  />
@@ -0,0 +1,28 @@
1
+ import { ActionIconGroup } from '@lobehub/ui';
2
+ import { memo } from 'react';
3
+
4
+ import { useChatStore } from '@/store/chat';
5
+
6
+ import { useChatListActionsBar } from '../hooks/useChatListActionsBar';
7
+ import { RenderAction } from '../types';
8
+
9
+ export const ToolActionsBar: RenderAction = memo(({ id }) => {
10
+ const { regenerate } = useChatListActionsBar();
11
+ const [reInvokeToolMessage] = useChatStore((s) => [s.reInvokeToolMessage]);
12
+
13
+ return (
14
+ <ActionIconGroup
15
+ // dropdownMenu={[regenerate]}
16
+ items={[regenerate]}
17
+ onActionClick={(event) => {
18
+ switch (event.key) {
19
+ case 'regenerate': {
20
+ reInvokeToolMessage(id);
21
+ break;
22
+ }
23
+ }
24
+ }}
25
+ type="ghost"
26
+ />
27
+ );
28
+ });
@@ -8,13 +8,13 @@ import { LLMRoleType } from '@/types/llm';
8
8
  import { OnActionsClick, RenderAction } from '../types';
9
9
  import { AssistantActionsBar } from './Assistant';
10
10
  import { DefaultActionsBar } from './Fallback';
11
- import { FunctionActionsBar } from './Function';
11
+ import { ToolActionsBar } from './Tool';
12
12
  import { UserActionsBar } from './User';
13
13
 
14
14
  export const renderActions: Record<LLMRoleType, RenderAction> = {
15
15
  assistant: AssistantActionsBar,
16
- function: FunctionActionsBar,
17
16
  system: DefaultActionsBar,
17
+ tool: ToolActionsBar,
18
18
  user: UserActionsBar,
19
19
  };
20
20
 
@@ -0,0 +1,73 @@
1
+ import { Avatar, Highlighter, Icon } from '@lobehub/ui';
2
+ import isEqual from 'fast-deep-equal';
3
+ import { Loader2, LucideChevronDown, LucideChevronRight, LucideToyBrick } from 'lucide-react';
4
+ import { CSSProperties, memo, useState } from 'react';
5
+ import { useTranslation } from 'react-i18next';
6
+ import { Center, Flexbox } from 'react-layout-kit';
7
+
8
+ import { useChatStore } from '@/store/chat';
9
+ import { chatSelectors } from '@/store/chat/selectors';
10
+ import { pluginHelpers, useToolStore } from '@/store/tool';
11
+ import { toolSelectors } from '@/store/tool/selectors';
12
+
13
+ import { useStyles } from './style';
14
+
15
+ export interface InspectorProps {
16
+ arguments?: string;
17
+ identifier: string;
18
+ index: number;
19
+ messageId: string;
20
+ style: CSSProperties;
21
+ }
22
+
23
+ const CallItem = memo<InspectorProps>(
24
+ ({ arguments: requestArgs = '{}', messageId, index, identifier, style }) => {
25
+ const { t } = useTranslation('plugin');
26
+ const { styles } = useStyles();
27
+ const [open, setOpen] = useState(false);
28
+ const loading = useChatStore(chatSelectors.isToolCallStreaming(messageId, index));
29
+
30
+ const pluginMeta = useToolStore(toolSelectors.getMetaById(identifier), isEqual);
31
+
32
+ const pluginAvatar = pluginHelpers.getPluginAvatar(pluginMeta);
33
+
34
+ const pluginTitle = pluginHelpers.getPluginTitle(pluginMeta) ?? t('unknownPlugin');
35
+
36
+ const avatar = pluginAvatar ? (
37
+ <Avatar alt={pluginTitle} avatar={pluginAvatar} size={32} />
38
+ ) : (
39
+ <Icon icon={LucideToyBrick} />
40
+ );
41
+
42
+ return (
43
+ <Flexbox gap={8} style={style}>
44
+ <Flexbox
45
+ align={'center'}
46
+ className={styles.container}
47
+ distribution={'space-between'}
48
+ gap={8}
49
+ height={32}
50
+ horizontal
51
+ onClick={() => {
52
+ setOpen(!open);
53
+ }}
54
+ >
55
+ <Flexbox align={'center'} gap={8} horizontal>
56
+ {loading ? (
57
+ <Center height={30} width={24}>
58
+ <Icon icon={Loader2} spin />
59
+ </Center>
60
+ ) : (
61
+ avatar
62
+ )}
63
+ {pluginTitle}
64
+ </Flexbox>
65
+ <Icon icon={open ? LucideChevronDown : LucideChevronRight} />
66
+ </Flexbox>
67
+ {(open || loading) && <Highlighter language={'json'}>{requestArgs}</Highlighter>}
68
+ </Flexbox>
69
+ );
70
+ },
71
+ );
72
+
73
+ export default CallItem;
@@ -0,0 +1,25 @@
1
+ import { createStyles } from 'antd-style';
2
+
3
+ export const useStyles = createStyles(({ css, token }) => ({
4
+ container: css`
5
+ cursor: pointer;
6
+
7
+ width: fit-content;
8
+ padding-inline: 4px 6px;
9
+
10
+ color: ${token.colorText};
11
+
12
+ background: ${token.colorFillTertiary};
13
+ border-radius: 8px;
14
+
15
+ &:hover {
16
+ background: ${token.colorFillSecondary};
17
+ }
18
+ `,
19
+ plugin: css`
20
+ display: flex;
21
+ gap: 4px;
22
+ align-items: center;
23
+ width: fit-content;
24
+ `,
25
+ }));
@@ -0,0 +1,51 @@
1
+ import { ReactNode, memo } from 'react';
2
+ import { Flexbox } from 'react-layout-kit';
3
+
4
+ import { LOADING_FLAT } from '@/const/message';
5
+ import { useChatStore } from '@/store/chat';
6
+ import { chatSelectors } from '@/store/chat/selectors';
7
+ import { ChatMessage } from '@/types/message';
8
+
9
+ import { DefaultMessage } from '../Default';
10
+ import ToolCall from './ToolCalls';
11
+
12
+ export const AssistantMessage = memo<
13
+ ChatMessage & {
14
+ editableContent: ReactNode;
15
+ }
16
+ >(({ id, tools, content, ...props }) => {
17
+ const editing = useChatStore(chatSelectors.isMessageEditing(id));
18
+ const generating = useChatStore(chatSelectors.isMessageGenerating(id));
19
+
20
+ const isToolCallGenerating = generating && (content === LOADING_FLAT || !content) && !!tools;
21
+
22
+ return (
23
+ <Flexbox gap={8} id={id}>
24
+ {(content || editing) && (
25
+ <DefaultMessage
26
+ content={content}
27
+ // we have id above, so don't need to pass it again
28
+ id={undefined as any}
29
+ isToolCallGenerating={isToolCallGenerating}
30
+ {...props}
31
+ />
32
+ )}
33
+ {!editing && tools && (
34
+ <Flexbox gap={8} horizontal>
35
+ {tools.map((toolCall, index) => (
36
+ <ToolCall
37
+ arguments={toolCall.arguments}
38
+ identifier={toolCall.identifier}
39
+ index={index}
40
+ key={toolCall.id}
41
+ messageId={id}
42
+ style={{
43
+ maxWidth: `max(${100 / tools.length}%, 300px)`,
44
+ }}
45
+ />
46
+ ))}
47
+ </Flexbox>
48
+ )}
49
+ </Flexbox>
50
+ );
51
+ });
@@ -8,8 +8,11 @@ import BubblesLoading from '../components/BubblesLoading';
8
8
  export const DefaultMessage = memo<
9
9
  ChatMessage & {
10
10
  editableContent: ReactNode;
11
+ isToolCallGenerating?: boolean;
11
12
  }
12
- >(({ id, editableContent, content }) => {
13
+ >(({ id, editableContent, content, isToolCallGenerating }) => {
14
+ if (isToolCallGenerating) return;
15
+
13
16
  if (content === LOADING_FLAT) return <BubblesLoading />;
14
17
 
15
18
  return <div id={id}>{editableContent}</div>;
@@ -1,13 +1,12 @@
1
1
  import { Loading3QuartersOutlined } from '@ant-design/icons';
2
- import { LobePluginType } from '@lobehub/chat-plugin-sdk';
3
- import { ActionIcon, Avatar, Highlighter, Icon } from '@lobehub/ui';
2
+ import { ActionIcon, Avatar, Highlighter, Icon, Tag } from '@lobehub/ui';
4
3
  import { Tabs } from 'antd';
5
4
  import isEqual from 'fast-deep-equal';
6
5
  import {
7
6
  LucideBug,
8
7
  LucideBugOff,
9
8
  LucideChevronDown,
10
- LucideChevronUp,
9
+ LucideChevronRight,
11
10
  LucideToyBrick,
12
11
  } from 'lucide-react';
13
12
  import { memo, useState } from 'react';
@@ -16,6 +15,7 @@ import { Flexbox } from 'react-layout-kit';
16
15
 
17
16
  import { pluginHelpers, useToolStore } from '@/store/tool';
18
17
  import { pluginSelectors, toolSelectors } from '@/store/tool/selectors';
18
+ import { ChatPluginPayload } from '@/types/message';
19
19
 
20
20
  import PluginResult from './PluginResultJSON';
21
21
  import Settings from './Settings';
@@ -23,46 +23,44 @@ import { useStyles } from './style';
23
23
 
24
24
  export interface InspectorProps {
25
25
  arguments?: string;
26
- command?: any;
27
26
  content: string;
28
- id?: string;
27
+ identifier?: string;
29
28
  loading?: boolean;
29
+ payload?: ChatPluginPayload;
30
30
  setShow?: (showRender: boolean) => void;
31
31
  showRender?: boolean;
32
- type?: LobePluginType;
33
32
  }
34
33
 
35
34
  const Inspector = memo<InspectorProps>(
36
35
  ({
37
36
  arguments: requestArgs = '{}',
38
- command,
37
+ payload,
39
38
  showRender,
40
39
  loading,
41
40
  setShow,
42
41
  content,
43
- id = 'unknown',
44
- // type,
42
+ identifier = 'unknown',
45
43
  }) => {
46
44
  const { t } = useTranslation('plugin');
47
45
  const { styles } = useStyles();
48
46
  const [open, setOpen] = useState(false);
49
47
 
50
- const pluginMeta = useToolStore(toolSelectors.getMetaById(id), isEqual);
48
+ const pluginMeta = useToolStore(toolSelectors.getMetaById(identifier), isEqual);
51
49
 
52
- const showRightAction = useToolStore(pluginSelectors.isPluginHasUI(id));
50
+ const showRightAction = useToolStore(pluginSelectors.isPluginHasUI(identifier));
53
51
  const pluginAvatar = pluginHelpers.getPluginAvatar(pluginMeta);
54
52
 
55
- const pluginTitle = pluginHelpers.getPluginTitle(pluginMeta) ?? t('plugins.loading');
53
+ const pluginTitle = pluginHelpers.getPluginTitle(pluginMeta) ?? t('unknownPlugin');
56
54
 
57
55
  const avatar = pluginAvatar ? (
58
- <Avatar avatar={pluginAvatar} size={32} />
56
+ <Avatar alt={pluginTitle} avatar={pluginAvatar} size={32} />
59
57
  ) : (
60
58
  <Icon icon={LucideToyBrick} />
61
59
  );
62
60
 
63
61
  let args, params;
64
62
  try {
65
- args = JSON.stringify(command, null, 2);
63
+ args = JSON.stringify(payload, null, 2);
66
64
  params = JSON.stringify(JSON.parse(requestArgs), null, 2);
67
65
  } catch {
68
66
  args = '';
@@ -81,29 +79,30 @@ const Inspector = memo<InspectorProps>(
81
79
  setShow?.(!showRender);
82
80
  }}
83
81
  >
84
- {loading ? (
85
- <div>
86
- <Loading3QuartersOutlined spin />
87
- </div>
88
- ) : (
89
- avatar
90
- )}
91
- {pluginTitle}
92
- {showRightAction && <Icon icon={showRender ? LucideChevronUp : LucideChevronDown} />}
93
- </Flexbox>
94
- {
95
- <Flexbox horizontal>
96
- {/*{type === 'standalone' && <ActionIcon icon={LucideOrbit} />}*/}
97
- <ActionIcon
98
- icon={open ? LucideBugOff : LucideBug}
99
- onClick={() => {
100
- setOpen(!open);
101
- }}
102
- title={t(open ? 'debug.off' : 'debug.on')}
103
- />
104
- <Settings id={id} />
82
+ <Flexbox align={'center'} gap={8} horizontal>
83
+ {loading ? (
84
+ <div>
85
+ <Loading3QuartersOutlined spin />
86
+ </div>
87
+ ) : (
88
+ avatar
89
+ )}
90
+ <div>{pluginTitle}</div>
91
+ <Tag>{payload?.apiName}</Tag>
105
92
  </Flexbox>
106
- }
93
+ {showRightAction && <Icon icon={showRender ? LucideChevronDown : LucideChevronRight} />}
94
+ </Flexbox>
95
+
96
+ <Flexbox horizontal>
97
+ <ActionIcon
98
+ icon={open ? LucideBugOff : LucideBug}
99
+ onClick={() => {
100
+ setOpen(!open);
101
+ }}
102
+ title={t(open ? 'debug.off' : 'debug.on')}
103
+ />
104
+ <Settings id={identifier} />
105
+ </Flexbox>
107
106
  </Flexbox>
108
107
  {open && (
109
108
  <Tabs
@@ -0,0 +1,44 @@
1
+ import { Snippet } from '@lobehub/ui';
2
+ import { memo, useState } from 'react';
3
+ import { Flexbox } from 'react-layout-kit';
4
+
5
+ import { useChatStore } from '@/store/chat';
6
+ import { chatSelectors } from '@/store/chat/selectors';
7
+ import { ChatMessage } from '@/types/message';
8
+
9
+ import PluginRender from '../../Plugins/Render';
10
+ import Inspector from './Inspector';
11
+
12
+ export const ToolMessage = memo<ChatMessage>(({ id, content, plugin }) => {
13
+ const loading = useChatStore(chatSelectors.isMessageGenerating(id));
14
+
15
+ const [showRender, setShow] = useState(plugin?.type !== 'default');
16
+
17
+ return (
18
+ <Flexbox gap={12} id={id} width={'100%'}>
19
+ <Inspector
20
+ arguments={plugin?.arguments}
21
+ content={content}
22
+ identifier={plugin?.identifier}
23
+ loading={loading}
24
+ payload={plugin}
25
+ setShow={setShow}
26
+ showRender={showRender}
27
+ />
28
+ {showRender || loading ? (
29
+ <PluginRender
30
+ content={content}
31
+ id={id}
32
+ identifier={plugin?.identifier}
33
+ loading={loading}
34
+ payload={plugin}
35
+ type={plugin?.type}
36
+ />
37
+ ) : (
38
+ <Flexbox>
39
+ <Snippet>{plugin?.arguments || ''}</Snippet>
40
+ </Flexbox>
41
+ )}
42
+ </Flexbox>
43
+ );
44
+ });
@@ -6,13 +6,14 @@ import { sessionSelectors } from '@/store/session/selectors';
6
6
  import { OnAvatarsClick, RenderMessage } from '../types';
7
7
  import { AssistantMessage } from './Assistant';
8
8
  import { DefaultMessage } from './Default';
9
- import { FunctionMessage } from './Function';
9
+ import { ToolMessage } from './Tool';
10
10
  import { UserMessage } from './User';
11
11
 
12
12
  export const renderMessages: Record<string, RenderMessage> = {
13
13
  assistant: AssistantMessage,
14
14
  default: DefaultMessage,
15
- function: FunctionMessage,
15
+ function: DefaultMessage,
16
+ tool: ToolMessage,
16
17
  user: UserMessage,
17
18
  };
18
19
 
@@ -126,7 +126,7 @@ const IFrameRender = memo<IFrameRenderProps>(({ url, id, payload, width = 600, h
126
126
  // we need to know which message to trigger
127
127
  if (messageId !== id) return;
128
128
 
129
- triggerAIMessage(id);
129
+ triggerAIMessage({ parentId: id });
130
130
  });
131
131
 
132
132
  // when plugin want to create an assistant message
@@ -1,12 +1,21 @@
1
1
  import { PluginRequestPayload } from '@lobehub/chat-plugin-sdk';
2
+ import { Skeleton } from 'antd';
3
+ import dynamic from 'next/dynamic';
2
4
  import { memo } from 'react';
3
5
 
4
6
  import { LobeToolRenderType } from '@/types/tool';
5
7
 
6
- import BuiltinType from '././BuiltinType';
7
8
  import DefaultType from './DefaultType';
8
9
  import Markdown from './MarkdownType';
9
- import Standalone from './StandaloneType';
10
+
11
+ const loading = () => (
12
+ <Skeleton.Node active style={{ width: '100%' }}>
13
+ {' '}
14
+ </Skeleton.Node>
15
+ );
16
+
17
+ const Standalone = dynamic(() => import('./StandaloneType'), { loading });
18
+ const BuiltinType = dynamic(() => import('./BuiltinType'), { loading });
10
19
 
11
20
  export interface PluginRenderProps {
12
21
  content: string;
@@ -34,14 +34,14 @@ const SkeletonList = memo<SkeletonListProps>(({ mobile }) => {
34
34
  <Skeleton
35
35
  active
36
36
  avatar={{ size: mobile ? 32 : 40 }}
37
- className={styles.message}
37
+ className={cx(styles.message, styles.user)}
38
38
  paragraph={{ width: mobile ? ['80%', '40%'] : ['50%', '30%'] }}
39
39
  title={false}
40
40
  />
41
41
  <Skeleton
42
42
  active
43
43
  avatar={{ size: mobile ? 32 : 40 }}
44
- className={cx(styles.message, styles.user)}
44
+ className={styles.message}
45
45
  paragraph={{ width: mobile ? ['80%', '40%'] : ['50%', '30%'] }}
46
46
  title={false}
47
47
  />
@@ -1,9 +1,8 @@
1
- import { Suspense, lazy } from 'react';
1
+ import { Suspense } from 'react';
2
2
  import { Flexbox } from 'react-layout-kit';
3
3
 
4
4
  import SkeletonList from './components/SkeletonList';
5
-
6
- const ChatList = lazy(() => import('./components/VirtualizedList'));
5
+ import ChatList from './components/VirtualizedList';
7
6
 
8
7
  interface ConversationProps {
9
8
  mobile?: boolean;