@lobehub/chat 0.156.1 → 0.157.0

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 (115) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/Dockerfile +4 -1
  3. package/package.json +3 -2
  4. package/src/config/modelProviders/anthropic.ts +3 -0
  5. package/src/config/modelProviders/google.ts +3 -0
  6. package/src/config/modelProviders/groq.ts +5 -1
  7. package/src/config/modelProviders/minimax.ts +10 -7
  8. package/src/config/modelProviders/mistral.ts +1 -0
  9. package/src/config/modelProviders/moonshot.ts +3 -0
  10. package/src/config/modelProviders/zhipu.ts +2 -6
  11. package/src/config/server/provider.ts +1 -1
  12. package/src/database/client/core/db.ts +32 -0
  13. package/src/database/client/core/schemas.ts +9 -0
  14. package/src/database/client/models/__tests__/message.test.ts +2 -2
  15. package/src/database/client/schemas/message.ts +8 -1
  16. package/src/features/AgentSetting/store/action.ts +15 -6
  17. package/src/features/Conversation/Actions/Tool.tsx +16 -0
  18. package/src/features/Conversation/Actions/index.ts +2 -2
  19. package/src/features/Conversation/Messages/Assistant/ToolCalls/index.tsx +78 -0
  20. package/src/features/Conversation/Messages/Assistant/ToolCalls/style.ts +25 -0
  21. package/src/features/Conversation/Messages/Assistant/index.tsx +47 -0
  22. package/src/features/Conversation/Messages/Default.tsx +4 -1
  23. package/src/features/Conversation/{Plugins → Messages/Tool}/Inspector/index.tsx +34 -35
  24. package/src/features/Conversation/Messages/Tool/index.tsx +44 -0
  25. package/src/features/Conversation/Messages/index.ts +3 -2
  26. package/src/features/Conversation/Plugins/Render/StandaloneType/Iframe.tsx +1 -1
  27. package/src/features/Conversation/components/SkeletonList.tsx +2 -2
  28. package/src/features/Conversation/index.tsx +2 -3
  29. package/src/libs/agent-runtime/BaseAI.ts +2 -9
  30. package/src/libs/agent-runtime/anthropic/index.test.ts +195 -0
  31. package/src/libs/agent-runtime/anthropic/index.ts +71 -15
  32. package/src/libs/agent-runtime/azureOpenai/index.ts +12 -13
  33. package/src/libs/agent-runtime/bedrock/index.ts +24 -18
  34. package/src/libs/agent-runtime/google/index.test.ts +154 -0
  35. package/src/libs/agent-runtime/google/index.ts +91 -10
  36. package/src/libs/agent-runtime/groq/index.test.ts +41 -72
  37. package/src/libs/agent-runtime/groq/index.ts +7 -0
  38. package/src/libs/agent-runtime/minimax/index.test.ts +2 -2
  39. package/src/libs/agent-runtime/minimax/index.ts +14 -37
  40. package/src/libs/agent-runtime/mistral/index.test.ts +0 -53
  41. package/src/libs/agent-runtime/mistral/index.ts +1 -0
  42. package/src/libs/agent-runtime/moonshot/index.test.ts +1 -71
  43. package/src/libs/agent-runtime/ollama/index.test.ts +197 -0
  44. package/src/libs/agent-runtime/ollama/index.ts +3 -3
  45. package/src/libs/agent-runtime/openai/index.test.ts +0 -53
  46. package/src/libs/agent-runtime/openrouter/index.test.ts +1 -53
  47. package/src/libs/agent-runtime/perplexity/index.test.ts +0 -71
  48. package/src/libs/agent-runtime/perplexity/index.ts +2 -3
  49. package/src/libs/agent-runtime/togetherai/__snapshots__/index.test.ts.snap +886 -0
  50. package/src/libs/agent-runtime/togetherai/fixtures/models.json +8111 -0
  51. package/src/libs/agent-runtime/togetherai/index.test.ts +16 -54
  52. package/src/libs/agent-runtime/types/chat.ts +19 -3
  53. package/src/libs/agent-runtime/utils/anthropicHelpers.test.ts +120 -1
  54. package/src/libs/agent-runtime/utils/anthropicHelpers.ts +67 -4
  55. package/src/libs/agent-runtime/utils/debugStream.test.ts +70 -0
  56. package/src/libs/agent-runtime/utils/debugStream.ts +39 -9
  57. package/src/libs/agent-runtime/utils/openaiCompatibleFactory/index.test.ts +521 -0
  58. package/src/libs/agent-runtime/utils/openaiCompatibleFactory/index.ts +76 -5
  59. package/src/libs/agent-runtime/utils/response.ts +12 -0
  60. package/src/libs/agent-runtime/utils/streams/anthropic.test.ts +197 -0
  61. package/src/libs/agent-runtime/utils/streams/anthropic.ts +91 -0
  62. package/src/libs/agent-runtime/utils/streams/bedrock/claude.ts +21 -0
  63. package/src/libs/agent-runtime/utils/streams/bedrock/common.ts +32 -0
  64. package/src/libs/agent-runtime/utils/streams/bedrock/index.ts +3 -0
  65. package/src/libs/agent-runtime/utils/streams/bedrock/llama.test.ts +196 -0
  66. package/src/libs/agent-runtime/utils/streams/bedrock/llama.ts +51 -0
  67. package/src/libs/agent-runtime/utils/streams/google-ai.test.ts +97 -0
  68. package/src/libs/agent-runtime/utils/streams/google-ai.ts +68 -0
  69. package/src/libs/agent-runtime/utils/streams/index.ts +7 -0
  70. package/src/libs/agent-runtime/utils/streams/minimax.ts +39 -0
  71. package/src/libs/agent-runtime/utils/streams/ollama.test.ts +77 -0
  72. package/src/libs/agent-runtime/utils/streams/ollama.ts +38 -0
  73. package/src/libs/agent-runtime/utils/streams/openai.test.ts +263 -0
  74. package/src/libs/agent-runtime/utils/streams/openai.ts +79 -0
  75. package/src/libs/agent-runtime/utils/streams/protocol.ts +100 -0
  76. package/src/libs/agent-runtime/zeroone/index.test.ts +1 -53
  77. package/src/libs/agent-runtime/zhipu/index.test.ts +1 -1
  78. package/src/libs/agent-runtime/zhipu/index.ts +3 -2
  79. package/src/locales/default/plugin.ts +3 -4
  80. package/src/migrations/FromV4ToV5/fixtures/from-v1-to-v5-output.json +245 -0
  81. package/src/migrations/FromV4ToV5/fixtures/function-input-v4.json +96 -0
  82. package/src/migrations/FromV4ToV5/fixtures/function-output-v5.json +120 -0
  83. package/src/migrations/FromV4ToV5/index.ts +58 -0
  84. package/src/migrations/FromV4ToV5/migrations.test.ts +49 -0
  85. package/src/migrations/FromV4ToV5/types/v4.ts +21 -0
  86. package/src/migrations/FromV4ToV5/types/v5.ts +27 -0
  87. package/src/migrations/index.ts +8 -1
  88. package/src/services/__tests__/chat.test.ts +10 -20
  89. package/src/services/chat.ts +78 -65
  90. package/src/store/chat/slices/enchance/action.ts +15 -10
  91. package/src/store/chat/slices/message/action.test.ts +36 -86
  92. package/src/store/chat/slices/message/action.ts +70 -79
  93. package/src/store/chat/slices/message/reducer.ts +18 -1
  94. package/src/store/chat/slices/message/selectors.test.ts +38 -68
  95. package/src/store/chat/slices/message/selectors.ts +1 -22
  96. package/src/store/chat/slices/plugin/action.test.ts +147 -203
  97. package/src/store/chat/slices/plugin/action.ts +96 -82
  98. package/src/store/chat/slices/share/action.test.ts +3 -3
  99. package/src/store/chat/slices/share/action.ts +1 -1
  100. package/src/store/chat/slices/topic/action.ts +7 -2
  101. package/src/store/tool/selectors/tool.ts +6 -24
  102. package/src/store/tool/slices/builtin/action.test.ts +90 -0
  103. package/src/types/llm.ts +1 -1
  104. package/src/types/message/index.ts +9 -4
  105. package/src/types/message/tools.ts +57 -0
  106. package/src/types/openai/chat.ts +6 -0
  107. package/src/utils/fetch.test.ts +245 -1
  108. package/src/utils/fetch.ts +120 -44
  109. package/src/utils/toolCall.ts +21 -0
  110. package/src/features/Conversation/Messages/Assistant.tsx +0 -26
  111. package/src/features/Conversation/Messages/Function.tsx +0 -35
  112. package/src/libs/agent-runtime/ollama/stream.ts +0 -31
  113. /package/src/features/Conversation/{Plugins → Messages/Tool}/Inspector/PluginResultJSON.tsx +0 -0
  114. /package/src/features/Conversation/{Plugins → Messages/Tool}/Inspector/Settings.tsx +0 -0
  115. /package/src/features/Conversation/{Plugins → Messages/Tool}/Inspector/style.ts +0 -0
@@ -3,14 +3,14 @@ import { t } from 'i18next';
3
3
  import { Md5 } from 'ts-md5';
4
4
  import { StateCreator } from 'zustand/vanilla';
5
5
 
6
+ import { LOADING_FLAT } from '@/const/message';
6
7
  import { PLUGIN_SCHEMA_API_MD5_PREFIX, PLUGIN_SCHEMA_SEPARATOR } from '@/const/plugin';
7
8
  import { chatService } from '@/services/chat';
8
9
  import { CreateMessageParams, messageService } from '@/services/message';
9
10
  import { ChatStore } from '@/store/chat/store';
10
11
  import { useToolStore } from '@/store/tool';
11
12
  import { pluginSelectors } from '@/store/tool/selectors';
12
- import { ChatPluginPayload } from '@/types/message';
13
- import { OpenAIToolCall } from '@/types/openai/functionCall';
13
+ import { ChatToolPayload, MessageToolCall } from '@/types/message';
14
14
  import { setNamespace } from '@/utils/storeDebug';
15
15
 
16
16
  import { chatSelectors } from '../../slices/message/selectors';
@@ -24,13 +24,15 @@ export interface ChatPluginAction {
24
24
  content: string,
25
25
  triggerAiMessage?: boolean,
26
26
  ) => Promise<void>;
27
- invokeBuiltinTool: (id: string, payload: ChatPluginPayload) => Promise<void>;
28
- invokeDefaultTypePlugin: (id: string, payload: any) => Promise<void>;
29
- invokeMarkdownTypePlugin: (id: string, payload: ChatPluginPayload) => Promise<void>;
30
- invokeStandaloneTypePlugin: (id: string, payload: ChatPluginPayload) => Promise<void>;
31
- runPluginApi: (id: string, payload: ChatPluginPayload) => Promise<string | undefined>;
32
- triggerAIMessage: (id: string, traceId?: string) => Promise<void>;
33
- triggerFunctionCall: (id: string) => Promise<void>;
27
+ internal_transformToolCalls: (toolCalls: MessageToolCall[]) => ChatToolPayload[];
28
+ invokeBuiltinTool: (id: string, payload: ChatToolPayload) => Promise<void>;
29
+ invokeDefaultTypePlugin: (id: string, payload: any) => Promise<string | undefined>;
30
+ invokeMarkdownTypePlugin: (id: string, payload: ChatToolPayload) => Promise<void>;
31
+ invokeStandaloneTypePlugin: (id: string, payload: ChatToolPayload) => Promise<void>;
32
+ runPluginApi: (id: string, payload: ChatToolPayload) => Promise<string | undefined>;
33
+ triggerAIMessage: (params: { parentId?: string; traceId?: string }) => Promise<void>;
34
+ triggerToolCalls: (id: string) => Promise<void>;
35
+
34
36
  updatePluginState: (id: string, key: string, value: any) => Promise<void>;
35
37
  }
36
38
 
@@ -58,7 +60,42 @@ export const chatPlugin: StateCreator<
58
60
 
59
61
  await internal_updateMessageContent(id, content);
60
62
 
61
- if (triggerAiMessage) await triggerAIMessage(id);
63
+ if (triggerAiMessage) await triggerAIMessage({ parentId: id });
64
+ },
65
+
66
+ internal_transformToolCalls: (toolCalls) => {
67
+ return toolCalls
68
+ .map((toolCall): ChatToolPayload | null => {
69
+ let payload: ChatToolPayload;
70
+
71
+ const [identifier, apiName, type] = toolCall.function.name.split(PLUGIN_SCHEMA_SEPARATOR);
72
+
73
+ if (!apiName) return null;
74
+
75
+ payload = {
76
+ apiName,
77
+ arguments: toolCall.function.arguments,
78
+ id: toolCall.id,
79
+ identifier,
80
+ type: (type ?? 'default') as any,
81
+ };
82
+
83
+ // if the apiName is md5, try to find the correct apiName in the plugins
84
+ if (apiName.startsWith(PLUGIN_SCHEMA_API_MD5_PREFIX)) {
85
+ const md5 = apiName.replace(PLUGIN_SCHEMA_API_MD5_PREFIX, '');
86
+ const manifest = pluginSelectors.getPluginManifestById(identifier)(
87
+ useToolStore.getState(),
88
+ );
89
+
90
+ const api = manifest?.api.find((api) => Md5.hashStr(api.name).toString() === md5);
91
+ if (api) {
92
+ payload.apiName = api.name;
93
+ }
94
+ }
95
+
96
+ return payload;
97
+ })
98
+ .filter(Boolean) as ChatToolPayload[];
62
99
  },
63
100
 
64
101
  invokeBuiltinTool: async (id, payload) => {
@@ -94,14 +131,13 @@ export const chatPlugin: StateCreator<
94
131
  },
95
132
 
96
133
  invokeDefaultTypePlugin: async (id, payload) => {
97
- const { runPluginApi, triggerAIMessage } = get();
134
+ const { runPluginApi } = get();
98
135
 
99
136
  const data = await runPluginApi(id, payload);
100
137
 
101
138
  if (!data) return;
102
- const traceId = chatSelectors.getTraceIdByMessageId(id)(get());
103
139
 
104
- await triggerAIMessage(id, traceId);
140
+ return data;
105
141
  },
106
142
 
107
143
  invokeMarkdownTypePlugin: async (id, payload) => {
@@ -175,95 +211,73 @@ export const chatPlugin: StateCreator<
175
211
  return data;
176
212
  },
177
213
 
178
- triggerAIMessage: async (id, traceId) => {
214
+ triggerAIMessage: async ({ parentId, traceId }) => {
179
215
  const { internal_coreProcessMessage } = get();
180
216
  const chats = chatSelectors.currentChats(get());
181
- await internal_coreProcessMessage(chats, id, { traceId });
217
+ await internal_coreProcessMessage(chats, parentId ?? chats.at(-1)!.id, { traceId });
182
218
  },
183
219
 
184
- triggerFunctionCall: async (id) => {
185
- const message = chatSelectors.getMessageById(id)(get());
186
- if (!message) return;
220
+ triggerToolCalls: async (assistantId) => {
221
+ const message = chatSelectors.getMessageById(assistantId)(get());
222
+ if (!message || !message.tools) return;
187
223
 
188
224
  const {
189
225
  invokeDefaultTypePlugin,
190
226
  invokeMarkdownTypePlugin,
191
227
  invokeStandaloneTypePlugin,
192
228
  invokeBuiltinTool,
193
- refreshMessages,
194
- internal_resendMessage,
195
- deleteMessage,
229
+ triggerAIMessage,
196
230
  } = get();
197
231
 
198
- let payload = { apiName: '', identifier: '' } as ChatPluginPayload;
199
-
200
- // 识别到内容是 function_call 的情况下
201
- // function_call 转换为 plugin request payload
202
- if (message.content) {
203
- const { tool_calls } = JSON.parse(message.content) as {
204
- tool_calls: OpenAIToolCall[];
232
+ let shouldCreateMessage = false;
233
+ let latestToolId = '';
234
+ const messagePools = message.tools.map(async (payload) => {
235
+ const toolMessage: CreateMessageParams = {
236
+ content: LOADING_FLAT,
237
+ parentId: assistantId,
238
+ plugin: payload,
239
+ role: 'tool',
240
+ sessionId: get().activeId,
241
+ tool_call_id: payload.id,
242
+ topicId: get().activeTopicId, // if there is activeTopicId,then add it to topicId
205
243
  };
206
244
 
207
- const function_call = tool_calls[0].function;
208
-
209
- const [identifier, apiName, type] = function_call.name.split(PLUGIN_SCHEMA_SEPARATOR);
210
-
211
- payload = {
212
- apiName,
213
- arguments: function_call.arguments,
214
- identifier,
215
- type: (type ?? 'default') as any,
216
- };
217
-
218
- // fix https://github.com/lobehub/lobe-chat/issues/1094, remove and retry after experiencing plugin illusion
219
- if (!apiName) {
220
- internal_resendMessage(id);
221
- deleteMessage(id);
222
- return;
223
- }
224
-
225
- // if the apiName is md5, try to find the correct apiName in the plugins
226
- if (apiName.startsWith(PLUGIN_SCHEMA_API_MD5_PREFIX)) {
227
- const md5 = apiName.replace(PLUGIN_SCHEMA_API_MD5_PREFIX, '');
228
- const manifest = pluginSelectors.getPluginManifestById(identifier)(useToolStore.getState());
229
-
230
- const api = manifest?.api.find((api) => Md5.hashStr(api.name).toString() === md5);
231
- if (!api) return;
232
- payload.apiName = api.name;
245
+ const id = await get().internal_createMessage(toolMessage);
246
+
247
+ switch (payload.type) {
248
+ case 'standalone': {
249
+ await invokeStandaloneTypePlugin(id, payload);
250
+ break;
251
+ }
252
+
253
+ case 'markdown': {
254
+ await invokeMarkdownTypePlugin(id, payload);
255
+ break;
256
+ }
257
+
258
+ case 'builtin': {
259
+ await invokeBuiltinTool(id, payload);
260
+ break;
261
+ }
262
+
263
+ default: {
264
+ const data = await invokeDefaultTypePlugin(id, payload);
265
+ if (data) {
266
+ shouldCreateMessage = true;
267
+ latestToolId = id;
268
+ }
269
+ }
233
270
  }
234
- } else {
235
- if (message.plugin) payload = message.plugin;
236
- }
237
-
238
- if (!payload.apiName) return;
239
-
240
- await messageService.updateMessage(id, {
241
- content: !!message.content ? '' : undefined,
242
- plugin: payload,
243
- role: 'function',
244
271
  });
245
- await refreshMessages();
246
272
 
247
- switch (payload.type) {
248
- case 'standalone': {
249
- await invokeStandaloneTypePlugin(id, payload);
250
- break;
251
- }
273
+ await Promise.all(messagePools);
252
274
 
253
- case 'markdown': {
254
- await invokeMarkdownTypePlugin(id, payload);
255
- break;
256
- }
275
+ // only default type tool calls should trigger AI message
276
+ if (!shouldCreateMessage) return;
257
277
 
258
- case 'builtin': {
259
- await invokeBuiltinTool(id, payload);
260
- break;
261
- }
278
+ const traceId = chatSelectors.getTraceIdByMessageId(latestToolId)(get());
262
279
 
263
- default: {
264
- await invokeDefaultTypePlugin(id, payload);
265
- }
266
- }
280
+ await triggerAIMessage({ traceId });
267
281
  },
268
282
 
269
283
  updatePluginState: async (id, key, value) => {
@@ -85,7 +85,7 @@ describe('shareSlice actions', () => {
85
85
  it('should include plugin information when withPluginInfo is true', async () => {
86
86
  // 模拟带有插件信息的消息
87
87
  const pluginMessage = {
88
- role: 'function',
88
+ role: 'tool',
89
89
  content: 'plugin content',
90
90
  plugin: {
91
91
  type: 'default',
@@ -118,7 +118,7 @@ describe('shareSlice actions', () => {
118
118
 
119
119
  it('should not include plugin information when withPluginInfo is false', async () => {
120
120
  const pluginMessage = {
121
- role: 'function',
121
+ role: 'tool',
122
122
  content: 'plugin content',
123
123
  plugin: {
124
124
  type: 'default',
@@ -154,7 +154,7 @@ describe('shareSlice actions', () => {
154
154
  { role: 'user', content: 'user message', id: '1' },
155
155
  { role: 'assistant', content: 'assistant message', id: '2' },
156
156
  {
157
- role: 'function',
157
+ role: 'tool',
158
158
  content: 'plugin content',
159
159
  plugin: {
160
160
  type: 'default',
@@ -81,7 +81,7 @@ export const chatShare: StateCreator<ChatStore, [['zustand/devtools', never]], [
81
81
  draft.push({ from: 'gpt', value: i.content });
82
82
  break;
83
83
  }
84
- case 'function': {
84
+ case 'tool': {
85
85
  if (withPluginInfo)
86
86
  draft.push(
87
87
  PLUGIN_INFO({
@@ -134,8 +134,13 @@ export const chatTopic: StateCreator<
134
134
  onLoadingChange: (loading) => {
135
135
  internal_updateTopicLoading(topicId, loading);
136
136
  },
137
- onMessageHandle: (x) => {
138
- output += x;
137
+ onMessageHandle: (chunk) => {
138
+ switch (chunk.type) {
139
+ case 'text': {
140
+ output += chunk.text;
141
+ }
142
+ }
143
+
139
144
  updateTopicTitleInSummary(topicId, output);
140
145
  },
141
146
  params: await chainSummaryTitle(messages),
@@ -1,35 +1,16 @@
1
1
  import { LobeChatPluginManifest } from '@lobehub/chat-plugin-sdk';
2
2
  import { uniqBy } from 'lodash-es';
3
- import { Md5 } from 'ts-md5';
4
3
 
5
- import { PLUGIN_SCHEMA_API_MD5_PREFIX, PLUGIN_SCHEMA_SEPARATOR } from '@/const/plugin';
6
4
  import { MetaData } from '@/types/meta';
7
5
  import { ChatCompletionTool } from '@/types/openai/chat';
8
6
  import { LobeToolMeta } from '@/types/tool/tool';
7
+ import { genToolCallingName } from '@/utils/toolCall';
9
8
 
10
9
  import { pluginHelpers } from '../helpers';
11
10
  import { ToolStoreState } from '../initialState';
12
11
  import { builtinToolSelectors } from '../slices/builtin/selectors';
13
12
  import { pluginSelectors } from '../slices/plugin/selectors';
14
13
 
15
- const getAPIName = (identifier: string, name: string, type?: string) => {
16
- const pluginType = type && type !== 'default' ? `${PLUGIN_SCHEMA_SEPARATOR + type}` : '';
17
-
18
- // 将插件的 identifier 作为前缀,避免重复
19
- let apiName = identifier + PLUGIN_SCHEMA_SEPARATOR + name + pluginType;
20
-
21
- // OpenAI GPT function_call name can't be longer than 64 characters
22
- // So we need to use md5 to shorten the name
23
- // and then find the correct apiName in response by md5
24
- if (apiName.length >= 64) {
25
- const md5Content = PLUGIN_SCHEMA_API_MD5_PREFIX + Md5.hashStr(name).toString();
26
-
27
- apiName = identifier + PLUGIN_SCHEMA_SEPARATOR + md5Content + pluginType;
28
- }
29
-
30
- return apiName;
31
- };
32
-
33
14
  const enabledSchema =
34
15
  (tools: string[] = []) =>
35
16
  (s: ToolStoreState): ChatCompletionTool[] => {
@@ -41,7 +22,7 @@ const enabledSchema =
41
22
  .flatMap((manifest) =>
42
23
  manifest.api.map((m) => ({
43
24
  description: m.description,
44
- name: getAPIName(manifest.identifier, m.name, manifest.type),
25
+ name: genToolCallingName(manifest.identifier, m.name, manifest.type),
45
26
  parameters: m.parameters,
46
27
  })),
47
28
  );
@@ -67,9 +48,10 @@ const enabledSystemRoles =
67
48
 
68
49
  const methods = manifest.api
69
50
  .map((m) =>
70
- [`#### ${getAPIName(manifest.identifier, m.name, manifest.type)}`, m.description].join(
71
- '\n\n',
72
- ),
51
+ [
52
+ `#### ${genToolCallingName(manifest.identifier, m.name, manifest.type)}`,
53
+ m.description,
54
+ ].join('\n\n'),
73
55
  )
74
56
  .join('\n\n');
75
57
 
@@ -0,0 +1,90 @@
1
+ import { act, renderHook } from '@testing-library/react';
2
+ import { describe, expect, it, vi } from 'vitest';
3
+
4
+ import { useToolStore } from '../../store';
5
+
6
+ vi.mock('zustand/traditional');
7
+
8
+ describe('createBuiltinToolSlice', () => {
9
+ describe('invokeBuiltinTool', () => {
10
+ it('should return if the tool is already loading', async () => {
11
+ // Given
12
+ const key = 'text2image';
13
+ const params = {};
14
+
15
+ const mockFn = vi.fn();
16
+ const { result } = renderHook(() => useToolStore());
17
+
18
+ act(() => {
19
+ useToolStore.setState({
20
+ text2image: mockFn,
21
+ });
22
+ });
23
+
24
+ await act(async () => {
25
+ // When
26
+ const data = await result.current.invokeBuiltinTool(key, params);
27
+ expect(data).toBeUndefined();
28
+ });
29
+
30
+ // Then
31
+ expect(mockFn).toHaveBeenCalled();
32
+ });
33
+
34
+ it('should invoke the specified tool action and return the stringified result', async () => {
35
+ // Given
36
+ const key = 'text2image';
37
+
38
+ const mockFn = vi.fn();
39
+ const { result } = renderHook(() => useToolStore());
40
+
41
+ const params = {
42
+ prompts: ['test prompt'],
43
+ size: '512x512',
44
+ quality: 'standard',
45
+ style: 'vivid',
46
+ };
47
+
48
+ act(() => {
49
+ useToolStore.setState({
50
+ builtinToolLoading: { [key]: false },
51
+ text2image: mockFn,
52
+ });
53
+ });
54
+ // When
55
+ await act(async () => {
56
+ await result.current.invokeBuiltinTool(key, params);
57
+ });
58
+
59
+ expect(mockFn).toBeCalledWith({
60
+ prompts: ['test prompt'],
61
+ quality: 'standard',
62
+ size: '512x512',
63
+ style: 'vivid',
64
+ });
65
+ });
66
+ });
67
+
68
+ describe('text2image', () => {
69
+ it('should map the prompts to DallEImageItem objects', () => {
70
+ // When
71
+ const { result } = renderHook(() => useToolStore());
72
+
73
+ const data = result.current.text2image(
74
+ {
75
+ prompts: ['prompt1', 'prompt2'],
76
+ size: '1024x1024',
77
+ quality: 'standard',
78
+ style: 'vivid',
79
+ },
80
+ 'a',
81
+ );
82
+
83
+ // Then
84
+ expect(data).toEqual([
85
+ { prompt: 'prompt1', quality: 'standard', size: '1024x1024', style: 'vivid' },
86
+ { prompt: 'prompt2', quality: 'standard', size: '1024x1024', style: 'vivid' },
87
+ ]);
88
+ });
89
+ });
90
+ });
package/src/types/llm.ts CHANGED
@@ -75,7 +75,7 @@ export interface LLMParams {
75
75
  top_p?: number;
76
76
  }
77
77
 
78
- export type LLMRoleType = 'user' | 'system' | 'assistant' | 'function';
78
+ export type LLMRoleType = 'user' | 'system' | 'assistant' | 'tool';
79
79
 
80
80
  export interface LLMMessage {
81
81
  content: string;
@@ -3,11 +3,12 @@ import { IPluginErrorType } from '@lobehub/chat-plugin-sdk';
3
3
  import { ILobeAgentRuntimeErrorType } from '@/libs/agent-runtime';
4
4
  import { ErrorType } from '@/types/fetch';
5
5
 
6
- import { LLMRoleType } from '../llm';
7
6
  import { BaseDataModel } from '../meta';
8
- import { ChatPluginPayload } from './tools';
7
+ import { ChatPluginPayload, ChatToolPayload } from './tools';
9
8
  import { Translate } from './translate';
10
9
 
10
+ export type MessageRoleType = 'user' | 'system' | 'assistant' | 'tool';
11
+
11
12
  /**
12
13
  * 聊天消息错误对象
13
14
  */
@@ -51,9 +52,10 @@ export interface ChatMessage extends BaseDataModel {
51
52
  * parent message id
52
53
  */
53
54
  parentId?: string;
54
- plugin?: ChatPluginPayload;
55
55
 
56
+ plugin?: ChatPluginPayload;
56
57
  pluginState?: any;
58
+
57
59
  /**
58
60
  * quoted other message's id
59
61
  */
@@ -61,9 +63,12 @@ export interface ChatMessage extends BaseDataModel {
61
63
  /**
62
64
  * message role type
63
65
  */
64
- role: LLMRoleType;
66
+ role: MessageRoleType;
65
67
  sessionId?: string;
66
68
 
69
+ tool_call_id?: string;
70
+ tools?: ChatToolPayload[];
71
+
67
72
  /**
68
73
  * 保存到主题的消息
69
74
  */
@@ -1,3 +1,6 @@
1
+ import { DeepPartial } from 'utility-types';
2
+ import { z } from 'zod';
3
+
1
4
  import { LobeToolRenderType } from '@/types/tool';
2
5
 
3
6
  export interface ChatPluginPayload {
@@ -6,3 +9,57 @@ export interface ChatPluginPayload {
6
9
  identifier: string;
7
10
  type: LobeToolRenderType;
8
11
  }
12
+
13
+ export interface ChatToolPayload {
14
+ apiName: string;
15
+ arguments: string;
16
+ id: string;
17
+ identifier: string;
18
+ type: LobeToolRenderType;
19
+ }
20
+
21
+ /**
22
+ * The function that the model called.
23
+ */
24
+ export interface ToolFunction {
25
+ /**
26
+ * The arguments to call the function with, as generated by the model in JSON
27
+ * format. Note that the model does not always generate valid JSON, and may
28
+ * hallucinate parameters not defined by your function schema. Validate the
29
+ * arguments in your code before calling your function.
30
+ */
31
+ arguments: string;
32
+
33
+ /**
34
+ * The name of the function to call.
35
+ */
36
+ name: string;
37
+ }
38
+
39
+ export interface MessageToolCall {
40
+ /**
41
+ * The function that the model called.
42
+ */
43
+ function: ToolFunction;
44
+
45
+ /**
46
+ * The ID of the tool call.
47
+ */
48
+ id: string;
49
+
50
+ /**
51
+ * The type of the tool. Currently, only `function` is supported.
52
+ */
53
+ type: 'function' | string;
54
+ }
55
+
56
+ export type MessageToolCallChunk = DeepPartial<MessageToolCall> & { index: number };
57
+
58
+ export const MessageToolCallSchema = z.object({
59
+ function: z.object({
60
+ arguments: z.string(),
61
+ name: z.string(),
62
+ }),
63
+ id: z.string(),
64
+ type: z.string(),
65
+ });
@@ -1,4 +1,5 @@
1
1
  import { LLMRoleType } from '@/types/llm';
2
+ import { MessageToolCall } from '@/types/message';
2
3
 
3
4
  import { OpenAIFunctionCall } from './functionCall';
4
5
 
@@ -23,6 +24,9 @@ export interface OpenAIChatMessage {
23
24
  */
24
25
  content: string | UserMessageContentPart[];
25
26
 
27
+ /**
28
+ * @deprecated
29
+ */
26
30
  function_call?: OpenAIFunctionCall;
27
31
  name?: string;
28
32
  /**
@@ -30,6 +34,8 @@ export interface OpenAIChatMessage {
30
34
  * @description 消息发送者的角色
31
35
  */
32
36
  role: LLMRoleType;
37
+ tool_call_id?: string;
38
+ tool_calls?: MessageToolCall[];
33
39
  }
34
40
 
35
41
  /**