@lobehub/chat 1.0.12 → 1.0.14

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 (123) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/README.md +16 -14
  3. package/README.zh-CN.md +16 -14
  4. package/docs/self-hosting/advanced/feature-flags.zh-CN.mdx +2 -1
  5. package/docs/self-hosting/advanced/model-list.zh-CN.mdx +1 -0
  6. package/docs/self-hosting/advanced/server-database.mdx +4 -3
  7. package/docs/self-hosting/advanced/server-database.zh-CN.mdx +1 -0
  8. package/docs/self-hosting/advanced/settings-url-share.mdx +5 -4
  9. package/docs/self-hosting/advanced/settings-url-share.zh-CN.mdx +5 -4
  10. package/docs/self-hosting/advanced/sso-providers/zitadel.mdx +3 -3
  11. package/docs/self-hosting/advanced/sso-providers/zitadel.zh-CN.mdx +2 -1
  12. package/docs/self-hosting/advanced/upstream-sync.zh-CN.mdx +10 -10
  13. package/docs/self-hosting/examples/ollama.mdx +23 -23
  14. package/docs/self-hosting/examples/ollama.zh-CN.mdx +23 -24
  15. package/docs/self-hosting/platform/docker-compose.zh-CN.mdx +2 -3
  16. package/docs/self-hosting/platform/vercel.mdx +2 -2
  17. package/docs/self-hosting/platform/vercel.zh-CN.mdx +2 -1
  18. package/docs/self-hosting/platform/zeabur.mdx +6 -7
  19. package/docs/self-hosting/platform/zeabur.zh-CN.mdx +6 -7
  20. package/docs/usage/agents/prompt.mdx +1 -1
  21. package/docs/usage/agents/prompt.zh-CN.mdx +1 -1
  22. package/docs/usage/features/auth.mdx +13 -8
  23. package/docs/usage/features/auth.zh-CN.mdx +5 -3
  24. package/docs/usage/features/database.mdx +20 -14
  25. package/docs/usage/features/database.zh-CN.mdx +1 -1
  26. package/docs/usage/features/local-llm.mdx +5 -0
  27. package/docs/usage/features/local-llm.zh-CN.mdx +5 -0
  28. package/docs/usage/features/multi-ai-providers.mdx +5 -0
  29. package/docs/usage/features/multi-ai-providers.zh-CN.mdx +5 -0
  30. package/docs/usage/features/plugin-system.zh-CN.mdx +2 -1
  31. package/docs/usage/features/pwa.mdx +3 -2
  32. package/docs/usage/features/pwa.zh-CN.mdx +3 -2
  33. package/docs/usage/providers/01ai.mdx +86 -0
  34. package/docs/usage/providers/01ai.zh-CN.mdx +85 -0
  35. package/docs/usage/providers/anthropic.mdx +79 -0
  36. package/docs/usage/providers/anthropic.zh-CN.mdx +74 -0
  37. package/docs/usage/providers/azure.mdx +89 -0
  38. package/docs/usage/providers/azure.zh-CN.mdx +82 -0
  39. package/docs/usage/providers/bedrock.mdx +140 -0
  40. package/docs/usage/providers/bedrock.zh-CN.mdx +135 -0
  41. package/docs/usage/providers/deepseek.mdx +91 -0
  42. package/docs/usage/providers/deepseek.zh-CN.mdx +86 -0
  43. package/docs/usage/providers/gemini.mdx +83 -0
  44. package/docs/usage/providers/gemini.zh-CN.mdx +80 -0
  45. package/docs/usage/providers/groq.mdx +1 -3
  46. package/docs/usage/providers/groq.zh-CN.mdx +1 -1
  47. package/docs/usage/providers/minimax.mdx +89 -0
  48. package/docs/usage/providers/minimax.zh-CN.mdx +85 -0
  49. package/docs/usage/providers/mistral.mdx +71 -0
  50. package/docs/usage/providers/mistral.zh-CN.mdx +66 -0
  51. package/docs/usage/providers/moonshot.mdx +70 -0
  52. package/docs/usage/providers/moonshot.zh-CN.mdx +66 -0
  53. package/docs/usage/providers/ollama/gemma.mdx +1 -1
  54. package/docs/usage/providers/ollama/gemma.zh-CN.mdx +1 -1
  55. package/docs/usage/providers/ollama/qwen.mdx +1 -1
  56. package/docs/usage/providers/ollama/qwen.zh-CN.mdx +1 -1
  57. package/docs/usage/providers/ollama.mdx +1 -1
  58. package/docs/usage/providers/ollama.zh-CN.mdx +1 -1
  59. package/docs/usage/providers/openai.mdx +95 -0
  60. package/docs/usage/providers/openai.zh-CN.mdx +87 -0
  61. package/docs/usage/providers/openrouter.mdx +111 -0
  62. package/docs/usage/providers/openrouter.zh-CN.mdx +109 -0
  63. package/docs/usage/providers/perplexity.mdx +64 -0
  64. package/docs/usage/providers/perplexity.zh-CN.mdx +61 -0
  65. package/docs/usage/providers/qwen.mdx +93 -0
  66. package/docs/usage/providers/qwen.zh-CN.mdx +86 -0
  67. package/docs/usage/providers/stepfun.mdx +69 -0
  68. package/docs/usage/providers/stepfun.zh-CN.mdx +64 -0
  69. package/docs/usage/providers/togetherai.mdx +74 -0
  70. package/docs/usage/providers/togetherai.zh-CN.mdx +71 -0
  71. package/docs/usage/providers/zhipu.mdx +69 -0
  72. package/docs/usage/providers/zhipu.zh-CN.mdx +64 -0
  73. package/docs/usage/providers.mdx +36 -0
  74. package/docs/usage/providers.zh-CN.mdx +34 -0
  75. package/docs/usage/start.mdx +2 -0
  76. package/docs/usage/start.zh-CN.mdx +2 -0
  77. package/locales/ar/setting.json +1 -0
  78. package/locales/bg-BG/setting.json +1 -0
  79. package/locales/de-DE/setting.json +1 -0
  80. package/locales/en-US/setting.json +1 -0
  81. package/locales/es-ES/setting.json +1 -0
  82. package/locales/fr-FR/setting.json +1 -0
  83. package/locales/it-IT/setting.json +1 -0
  84. package/locales/ja-JP/setting.json +1 -0
  85. package/locales/ko-KR/setting.json +1 -0
  86. package/locales/nl-NL/setting.json +1 -0
  87. package/locales/pl-PL/setting.json +1 -0
  88. package/locales/pt-BR/setting.json +1 -0
  89. package/locales/ru-RU/setting.json +1 -0
  90. package/locales/tr-TR/setting.json +1 -0
  91. package/locales/vi-VN/setting.json +1 -0
  92. package/locales/zh-CN/setting.json +1 -0
  93. package/locales/zh-TW/setting.json +1 -0
  94. package/package.json +9 -7
  95. package/src/app/(main)/settings/llm/ProviderList/providers.tsx +33 -4
  96. package/src/app/(main)/settings/llm/components/ProviderConfig/index.tsx +41 -11
  97. package/src/app/(main)/settings/llm/index.tsx +1 -2
  98. package/src/features/ChatInput/useSend.ts +2 -1
  99. package/src/features/Conversation/Error/ErrorJsonViewer.tsx +1 -1
  100. package/src/features/Conversation/Messages/Assistant/ToolCalls/index.tsx +4 -3
  101. package/src/features/Conversation/Messages/Tool/index.tsx +3 -5
  102. package/src/features/Conversation/Messages/components/Arguments.tsx +22 -0
  103. package/src/features/Conversation/Messages/hooks/useYamlArguments.ts +14 -0
  104. package/src/libs/agent-runtime/anthropic/index.test.ts +1 -31
  105. package/src/libs/agent-runtime/anthropic/index.ts +9 -63
  106. package/src/libs/agent-runtime/openai/index.test.ts +7 -7
  107. package/src/libs/agent-runtime/openai/index.ts +0 -6
  108. package/src/libs/agent-runtime/utils/anthropicHelpers.ts +2 -2
  109. package/src/libs/agent-runtime/utils/streams/anthropic.test.ts +102 -28
  110. package/src/libs/agent-runtime/utils/streams/anthropic.ts +29 -12
  111. package/src/libs/agent-runtime/utils/streams/protocol.ts +6 -1
  112. package/src/locales/default/setting.ts +1 -0
  113. package/src/services/message/server.ts +4 -0
  114. package/src/services/message/type.ts +1 -1
  115. package/src/store/chat/slices/message/action.ts +48 -48
  116. package/src/store/chat/slices/message/initialState.ts +2 -0
  117. package/src/store/chat/slices/message/selectors.ts +3 -0
  118. package/src/store/chat/slices/plugin/action.test.ts +20 -20
  119. package/src/store/chat/slices/plugin/action.ts +133 -116
  120. package/src/store/chat/utils/index.ts +19 -0
  121. package/src/store/tool/slices/builtin/action.test.ts +8 -11
  122. package/src/store/tool/slices/builtin/action.ts +9 -9
  123. package/src/types/message/index.ts +1 -1
@@ -112,7 +112,7 @@ describe('ChatPluginAction', () => {
112
112
 
113
113
  vi.spyOn(storeState, 'refreshMessages');
114
114
  vi.spyOn(storeState, 'triggerAIMessage').mockResolvedValue(undefined);
115
- vi.spyOn(storeState, 'internal_toggleChatLoading').mockReturnValue(undefined);
115
+ vi.spyOn(storeState, 'internal_togglePluginApiCalling').mockReturnValue(undefined);
116
116
 
117
117
  const runSpy = vi.spyOn(chatService, 'runPluginApi').mockResolvedValue({
118
118
  text: pluginApiResponse,
@@ -125,7 +125,7 @@ describe('ChatPluginAction', () => {
125
125
  await result.current.invokeDefaultTypePlugin(messageId, pluginPayload);
126
126
  });
127
127
 
128
- expect(storeState.internal_toggleChatLoading).toHaveBeenCalledWith(
128
+ expect(storeState.internal_togglePluginApiCalling).toHaveBeenCalledWith(
129
129
  true,
130
130
  messageId,
131
131
  expect.any(String),
@@ -135,7 +135,7 @@ describe('ChatPluginAction', () => {
135
135
  content: pluginApiResponse,
136
136
  });
137
137
  expect(storeState.refreshMessages).toHaveBeenCalled();
138
- expect(storeState.internal_toggleChatLoading).toHaveBeenCalledWith(
138
+ expect(storeState.internal_togglePluginApiCalling).toHaveBeenCalledWith(
139
139
  false,
140
140
  'message-id',
141
141
  'plugin/fetchPlugin/end',
@@ -150,7 +150,7 @@ describe('ChatPluginAction', () => {
150
150
  const storeState = useChatStore.getState();
151
151
  vi.spyOn(storeState, 'refreshMessages');
152
152
  vi.spyOn(storeState, 'triggerAIMessage').mockResolvedValue(undefined);
153
- vi.spyOn(storeState, 'internal_toggleChatLoading').mockReturnValue(undefined);
153
+ vi.spyOn(storeState, 'internal_togglePluginApiCalling').mockReturnValue(undefined);
154
154
 
155
155
  vi.spyOn(chatService, 'runPluginApi').mockRejectedValue(error);
156
156
 
@@ -159,7 +159,7 @@ describe('ChatPluginAction', () => {
159
159
  await result.current.invokeDefaultTypePlugin(messageId, pluginPayload);
160
160
  });
161
161
 
162
- expect(storeState.internal_toggleChatLoading).toHaveBeenCalledWith(
162
+ expect(storeState.internal_togglePluginApiCalling).toHaveBeenCalledWith(
163
163
  true,
164
164
  messageId,
165
165
  expect.any(String),
@@ -167,7 +167,7 @@ describe('ChatPluginAction', () => {
167
167
  expect(chatService.runPluginApi).toHaveBeenCalledWith(pluginPayload, { trace: {} });
168
168
  expect(messageService.updateMessageError).toHaveBeenCalledWith(messageId, error);
169
169
  expect(storeState.refreshMessages).toHaveBeenCalled();
170
- expect(storeState.internal_toggleChatLoading).toHaveBeenCalledWith(
170
+ expect(storeState.internal_togglePluginApiCalling).toHaveBeenCalledWith(
171
171
  false,
172
172
  'message-id',
173
173
  'plugin/fetchPlugin/end',
@@ -463,11 +463,11 @@ describe('ChatPluginAction', () => {
463
463
  const toolResponse = JSON.stringify({ abc: 'data' });
464
464
 
465
465
  useToolStore.setState({
466
- invokeBuiltinTool: vi.fn().mockResolvedValue(toolResponse),
466
+ transformApiArgumentsToAiState: vi.fn().mockResolvedValue(toolResponse),
467
467
  });
468
468
 
469
469
  useChatStore.setState({
470
- internal_toggleChatLoading: vi.fn(),
470
+ internal_togglePluginApiCalling: vi.fn(),
471
471
  internal_updateMessageContent: vi.fn(),
472
472
  text2image: vi.fn(),
473
473
  });
@@ -479,7 +479,7 @@ describe('ChatPluginAction', () => {
479
479
  });
480
480
 
481
481
  // Verify that the builtin tool was invoked with the correct arguments
482
- expect(useToolStore.getState().invokeBuiltinTool).toHaveBeenCalledWith(
482
+ expect(useToolStore.getState().transformApiArgumentsToAiState).toHaveBeenCalledWith(
483
483
  payload.apiName,
484
484
  JSON.parse(payload.arguments),
485
485
  );
@@ -491,12 +491,12 @@ describe('ChatPluginAction', () => {
491
491
  );
492
492
 
493
493
  // Verify that loading was toggled correctly
494
- expect(result.current.internal_toggleChatLoading).toHaveBeenCalledWith(
494
+ expect(result.current.internal_togglePluginApiCalling).toHaveBeenCalledWith(
495
495
  true,
496
496
  messageId,
497
497
  expect.any(String),
498
498
  );
499
- expect(result.current.internal_toggleChatLoading).toHaveBeenCalledWith(false);
499
+ expect(result.current.internal_togglePluginApiCalling).toHaveBeenCalledWith(false);
500
500
  expect(useChatStore.getState().text2image).toHaveBeenCalled();
501
501
  });
502
502
 
@@ -511,12 +511,12 @@ describe('ChatPluginAction', () => {
511
511
 
512
512
  act(() => {
513
513
  useToolStore.setState({
514
- invokeBuiltinTool: vi.fn().mockResolvedValue(toolResponse),
514
+ transformApiArgumentsToAiState: vi.fn().mockResolvedValue(toolResponse),
515
515
  text2image: vi.fn(),
516
516
  });
517
517
 
518
518
  useChatStore.setState({
519
- internal_toggleChatLoading: vi.fn(),
519
+ internal_togglePluginApiCalling: vi.fn(),
520
520
  text2image: vi.fn(),
521
521
  internal_updateMessageContent: vi.fn(),
522
522
  });
@@ -528,7 +528,7 @@ describe('ChatPluginAction', () => {
528
528
  });
529
529
 
530
530
  // Verify that the builtin tool was invoked with the correct arguments
531
- expect(useToolStore.getState().invokeBuiltinTool).toHaveBeenCalledWith(
531
+ expect(useToolStore.getState().transformApiArgumentsToAiState).toHaveBeenCalledWith(
532
532
  payload.apiName,
533
533
  JSON.parse(payload.arguments),
534
534
  );
@@ -540,12 +540,12 @@ describe('ChatPluginAction', () => {
540
540
  );
541
541
 
542
542
  // Verify that loading was toggled correctly
543
- expect(result.current.internal_toggleChatLoading).toHaveBeenCalledWith(
543
+ expect(result.current.internal_togglePluginApiCalling).toHaveBeenCalledWith(
544
544
  true,
545
545
  messageId,
546
546
  expect.any(String),
547
547
  );
548
- expect(result.current.internal_toggleChatLoading).toHaveBeenCalledWith(false);
548
+ expect(result.current.internal_togglePluginApiCalling).toHaveBeenCalledWith(false);
549
549
  expect(useChatStore.getState().text2image).not.toHaveBeenCalled();
550
550
  });
551
551
 
@@ -559,11 +559,11 @@ describe('ChatPluginAction', () => {
559
559
  const error = new Error('Builtin tool failed');
560
560
 
561
561
  useToolStore.setState({
562
- invokeBuiltinTool: vi.fn().mockRejectedValue(error),
562
+ transformApiArgumentsToAiState: vi.fn().mockRejectedValue(error),
563
563
  });
564
564
 
565
565
  useChatStore.setState({
566
- internal_toggleChatLoading: vi.fn(),
566
+ internal_togglePluginApiCalling: vi.fn(),
567
567
  internal_updateMessageContent: vi.fn(),
568
568
  text2image: vi.fn(),
569
569
  refreshMessages: vi.fn(),
@@ -576,12 +576,12 @@ describe('ChatPluginAction', () => {
576
576
  });
577
577
 
578
578
  // Verify that loading was toggled correctly
579
- expect(result.current.internal_toggleChatLoading).toHaveBeenCalledWith(
579
+ expect(result.current.internal_togglePluginApiCalling).toHaveBeenCalledWith(
580
580
  true,
581
581
  messageId,
582
582
  expect.any(String),
583
583
  );
584
- expect(result.current.internal_toggleChatLoading).toHaveBeenCalledWith(false);
584
+ expect(result.current.internal_togglePluginApiCalling).toHaveBeenCalledWith(false);
585
585
 
586
586
  // Verify that the message content was not updated
587
587
  expect(result.current.internal_updateMessageContent).not.toHaveBeenCalled();
@@ -1,3 +1,4 @@
1
+ /* eslint-disable sort-keys-fix/sort-keys-fix, typescript-sort-keys/interface */
1
2
  import { PluginErrorType } from '@lobehub/chat-plugin-sdk';
2
3
  import { t } from 'i18next';
3
4
  import { Md5 } from 'ts-md5';
@@ -25,11 +26,6 @@ export interface ChatPluginAction {
25
26
  triggerAiMessage?: boolean,
26
27
  ) => Promise<void>;
27
28
 
28
- internal_callPluginApi: (id: string, payload: ChatToolPayload) => Promise<string | undefined>;
29
- internal_invokeDifferentTypePlugin: (id: string, payload: ChatToolPayload) => Promise<any>;
30
- internal_transformToolCalls: (toolCalls: MessageToolCall[]) => ChatToolPayload[];
31
- internal_updatePluginError: (id: string, error: any) => Promise<void>;
32
-
33
29
  invokeBuiltinTool: (id: string, payload: ChatToolPayload) => Promise<void>;
34
30
  invokeDefaultTypePlugin: (id: string, payload: any) => Promise<string | undefined>;
35
31
  invokeMarkdownTypePlugin: (id: string, payload: ChatToolPayload) => Promise<void>;
@@ -41,6 +37,16 @@ export interface ChatPluginAction {
41
37
 
42
38
  triggerToolCalls: (id: string) => Promise<void>;
43
39
  updatePluginState: (id: string, key: string, value: any) => Promise<void>;
40
+
41
+ internal_callPluginApi: (id: string, payload: ChatToolPayload) => Promise<string | undefined>;
42
+ internal_invokeDifferentTypePlugin: (id: string, payload: ChatToolPayload) => Promise<any>;
43
+ internal_togglePluginApiCalling: (
44
+ loading: boolean,
45
+ id?: string,
46
+ action?: string,
47
+ ) => AbortController | undefined;
48
+ internal_transformToolCalls: (toolCalls: MessageToolCall[]) => ChatToolPayload[];
49
+ internal_updatePluginError: (id: string, error: any) => Promise<void>;
44
50
  }
45
51
 
46
52
  export const chatPlugin: StateCreator<
@@ -69,129 +75,23 @@ export const chatPlugin: StateCreator<
69
75
 
70
76
  if (triggerAiMessage) await triggerAIMessage({ parentId: id });
71
77
  },
72
- internal_callPluginApi: async (id, payload) => {
73
- const { internal_updateMessageContent, refreshMessages, internal_toggleChatLoading } = get();
74
- let data: string;
75
-
76
- try {
77
- const abortController = internal_toggleChatLoading(
78
- true,
79
- id,
80
- n('fetchPlugin/start') as string,
81
- );
82
-
83
- const message = chatSelectors.getMessageById(id)(get());
84
-
85
- const res = await chatService.runPluginApi(payload, {
86
- signal: abortController?.signal,
87
- trace: { observationId: message?.observationId, traceId: message?.traceId },
88
- });
89
- data = res.text;
90
-
91
- // save traceId
92
- if (res.traceId) {
93
- await messageService.updateMessage(id, { traceId: res.traceId });
94
- }
95
- } catch (error) {
96
- console.log(error);
97
- const err = error as Error;
98
-
99
- // ignore the aborted request error
100
- if (!err.message.includes('The user aborted a request.')) {
101
- await messageService.updateMessageError(id, error as any);
102
- await refreshMessages();
103
- }
104
-
105
- data = '';
106
- }
107
-
108
- internal_toggleChatLoading(false, id, n('fetchPlugin/end') as string);
109
- // 如果报错则结束了
110
- if (!data) return;
111
-
112
- await internal_updateMessageContent(id, data);
113
-
114
- return data;
115
- },
116
-
117
- internal_invokeDifferentTypePlugin: async (id, payload) => {
118
- switch (payload.type) {
119
- case 'standalone': {
120
- return await get().invokeStandaloneTypePlugin(id, payload);
121
- }
122
-
123
- case 'markdown': {
124
- return await get().invokeMarkdownTypePlugin(id, payload);
125
- }
126
-
127
- case 'builtin': {
128
- return await get().invokeBuiltinTool(id, payload);
129
- }
130
-
131
- default: {
132
- return await get().invokeDefaultTypePlugin(id, payload);
133
- }
134
- }
135
- },
136
-
137
- internal_transformToolCalls: (toolCalls) => {
138
- return toolCalls
139
- .map((toolCall): ChatToolPayload | null => {
140
- let payload: ChatToolPayload;
141
-
142
- const [identifier, apiName, type] = toolCall.function.name.split(PLUGIN_SCHEMA_SEPARATOR);
143
-
144
- if (!apiName) return null;
145
-
146
- payload = {
147
- apiName,
148
- arguments: toolCall.function.arguments,
149
- id: toolCall.id,
150
- identifier,
151
- type: (type ?? 'default') as any,
152
- };
153
-
154
- // if the apiName is md5, try to find the correct apiName in the plugins
155
- if (apiName.startsWith(PLUGIN_SCHEMA_API_MD5_PREFIX)) {
156
- const md5 = apiName.replace(PLUGIN_SCHEMA_API_MD5_PREFIX, '');
157
- const manifest = pluginSelectors.getPluginManifestById(identifier)(
158
- useToolStore.getState(),
159
- );
160
-
161
- const api = manifest?.api.find((api) => Md5.hashStr(api.name).toString() === md5);
162
- if (api) {
163
- payload.apiName = api.name;
164
- }
165
- }
166
-
167
- return payload;
168
- })
169
- .filter(Boolean) as ChatToolPayload[];
170
- },
171
-
172
- internal_updatePluginError: async (id, error) => {
173
- const { refreshMessages } = get();
174
-
175
- await messageService.updateMessage(id, { pluginError: error });
176
- await refreshMessages();
177
- },
178
-
179
78
  invokeBuiltinTool: async (id, payload) => {
180
- const { internal_toggleChatLoading, internal_updateMessageContent } = get();
79
+ const { internal_togglePluginApiCalling, internal_updateMessageContent } = get();
181
80
  const params = JSON.parse(payload.arguments);
182
- internal_toggleChatLoading(true, id, n('invokeBuiltinTool') as string);
81
+ internal_togglePluginApiCalling(true, id, n('invokeBuiltinTool') as string);
183
82
  let data;
184
83
  try {
185
- data = await useToolStore.getState().invokeBuiltinTool(payload.apiName, params);
84
+ data = await useToolStore.getState().transformApiArgumentsToAiState(payload.apiName, params);
186
85
  } catch (error) {
187
86
  console.log(error);
188
87
  }
189
- internal_toggleChatLoading(false);
88
+ internal_togglePluginApiCalling(false);
190
89
 
191
90
  if (!data) return;
192
91
 
193
92
  await internal_updateMessageContent(id, data);
194
93
 
94
+ // run tool api call
195
95
  // postToolCalling
196
96
  // @ts-ignore
197
97
  const { [payload.apiName]: action } = get();
@@ -248,6 +148,11 @@ export const chatPlugin: StateCreator<
248
148
  const message = chatSelectors.getMessageById(id)(get());
249
149
  if (!message || message.role !== 'tool' || !message.plugin) return;
250
150
 
151
+ // if there is error content, then clear the error
152
+ if (!!message.error) {
153
+ get().internal_updateMessageError(id, null);
154
+ }
155
+
251
156
  const payload: ChatToolPayload = { ...message.plugin, id: message.tool_call_id! };
252
157
 
253
158
  await get().internal_invokeDifferentTypePlugin(id, payload);
@@ -301,4 +206,116 @@ export const chatPlugin: StateCreator<
301
206
  await messageService.updateMessagePluginState(id, { [key]: value });
302
207
  await refreshMessages();
303
208
  },
209
+
210
+ internal_callPluginApi: async (id, payload) => {
211
+ const { internal_updateMessageContent, refreshMessages, internal_togglePluginApiCalling } =
212
+ get();
213
+ let data: string;
214
+
215
+ try {
216
+ const abortController = internal_togglePluginApiCalling(
217
+ true,
218
+ id,
219
+ n('fetchPlugin/start') as string,
220
+ );
221
+
222
+ const message = chatSelectors.getMessageById(id)(get());
223
+
224
+ const res = await chatService.runPluginApi(payload, {
225
+ signal: abortController?.signal,
226
+ trace: { observationId: message?.observationId, traceId: message?.traceId },
227
+ });
228
+ data = res.text;
229
+
230
+ // save traceId
231
+ if (res.traceId) {
232
+ await messageService.updateMessage(id, { traceId: res.traceId });
233
+ }
234
+ } catch (error) {
235
+ console.log(error);
236
+ const err = error as Error;
237
+
238
+ // ignore the aborted request error
239
+ if (!err.message.includes('The user aborted a request.')) {
240
+ await messageService.updateMessageError(id, error as any);
241
+ await refreshMessages();
242
+ }
243
+
244
+ data = '';
245
+ }
246
+
247
+ internal_togglePluginApiCalling(false, id, n('fetchPlugin/end') as string);
248
+ // 如果报错则结束了
249
+ if (!data) return;
250
+
251
+ await internal_updateMessageContent(id, data);
252
+
253
+ return data;
254
+ },
255
+
256
+ internal_invokeDifferentTypePlugin: async (id, payload) => {
257
+ switch (payload.type) {
258
+ case 'standalone': {
259
+ return await get().invokeStandaloneTypePlugin(id, payload);
260
+ }
261
+
262
+ case 'markdown': {
263
+ return await get().invokeMarkdownTypePlugin(id, payload);
264
+ }
265
+
266
+ case 'builtin': {
267
+ return await get().invokeBuiltinTool(id, payload);
268
+ }
269
+
270
+ default: {
271
+ return await get().invokeDefaultTypePlugin(id, payload);
272
+ }
273
+ }
274
+ },
275
+
276
+ internal_togglePluginApiCalling: (loading, id, action) => {
277
+ return get().internal_toggleLoadingArrays('pluginApiLoadingIds', loading, id, action);
278
+ },
279
+
280
+ internal_transformToolCalls: (toolCalls) => {
281
+ return toolCalls
282
+ .map((toolCall): ChatToolPayload | null => {
283
+ let payload: ChatToolPayload;
284
+
285
+ const [identifier, apiName, type] = toolCall.function.name.split(PLUGIN_SCHEMA_SEPARATOR);
286
+
287
+ if (!apiName) return null;
288
+
289
+ payload = {
290
+ apiName,
291
+ arguments: toolCall.function.arguments,
292
+ id: toolCall.id,
293
+ identifier,
294
+ type: (type ?? 'default') as any,
295
+ };
296
+
297
+ // if the apiName is md5, try to find the correct apiName in the plugins
298
+ if (apiName.startsWith(PLUGIN_SCHEMA_API_MD5_PREFIX)) {
299
+ const md5 = apiName.replace(PLUGIN_SCHEMA_API_MD5_PREFIX, '');
300
+ const manifest = pluginSelectors.getPluginManifestById(identifier)(
301
+ useToolStore.getState(),
302
+ );
303
+
304
+ const api = manifest?.api.find((api) => Md5.hashStr(api.name).toString() === md5);
305
+ if (api) {
306
+ payload.apiName = api.name;
307
+ }
308
+ }
309
+
310
+ return payload;
311
+ })
312
+ .filter(Boolean) as ChatToolPayload[];
313
+ },
314
+ internal_updatePluginError: async (id, error) => {
315
+ const { refreshMessages } = get();
316
+
317
+ get().internal_dispatchMessage({ id, type: 'updateMessages', value: { error } });
318
+ await messageService.updateMessage(id, { pluginError: error });
319
+ await refreshMessages();
320
+ },
304
321
  });
@@ -0,0 +1,19 @@
1
+ import { produce } from 'immer';
2
+
3
+ export const preventLeavingFn = (e: BeforeUnloadEvent) => {
4
+ // set returnValue to trigger alert modal
5
+ // Note: No matter what value is set, the browser will display the standard text
6
+ e.returnValue = '你有正在生成中的请求,确定要离开吗?';
7
+ };
8
+
9
+ export const toggleBooleanList = (ids: string[], id: string, loading: boolean) => {
10
+ return produce(ids, (draft) => {
11
+ if (loading) {
12
+ draft.push(id);
13
+ } else {
14
+ const index = draft.indexOf(id);
15
+
16
+ if (index >= 0) draft.splice(index, 1);
17
+ }
18
+ });
19
+ };
@@ -23,7 +23,7 @@ describe('createBuiltinToolSlice', () => {
23
23
 
24
24
  await act(async () => {
25
25
  // When
26
- const data = await result.current.invokeBuiltinTool(key, params);
26
+ const data = await result.current.transformApiArgumentsToAiState(key, params);
27
27
  expect(data).toBeUndefined();
28
28
  });
29
29
 
@@ -53,7 +53,7 @@ describe('createBuiltinToolSlice', () => {
53
53
  });
54
54
  // When
55
55
  await act(async () => {
56
- await result.current.invokeBuiltinTool(key, params);
56
+ await result.current.transformApiArgumentsToAiState(key, params);
57
57
  });
58
58
 
59
59
  expect(mockFn).toBeCalledWith({
@@ -70,15 +70,12 @@ describe('createBuiltinToolSlice', () => {
70
70
  // When
71
71
  const { result } = renderHook(() => useToolStore());
72
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
- );
73
+ const data = result.current.text2image({
74
+ prompts: ['prompt1', 'prompt2'],
75
+ size: '1024x1024',
76
+ quality: 'standard',
77
+ style: 'vivid',
78
+ });
82
79
 
83
80
  // Then
84
81
  expect(data).toEqual([
@@ -16,9 +16,9 @@ interface Text2ImageParams extends Pick<OpenAIImagePayload, 'quality' | 'style'
16
16
  * 代理行为接口
17
17
  */
18
18
  export interface BuiltinToolAction {
19
- invokeBuiltinTool: (key: string, params: any) => Promise<string | undefined>;
20
- text2image: (params: Text2ImageParams, messageId: string) => DallEImageItem[];
19
+ text2image: (params: Text2ImageParams) => DallEImageItem[];
21
20
  toggleBuiltinToolLoading: (key: string, value: boolean) => void;
21
+ transformApiArgumentsToAiState: (key: string, params: any) => Promise<string | undefined>;
22
22
  }
23
23
 
24
24
  export const createBuiltinToolSlice: StateCreator<
@@ -27,7 +27,13 @@ export const createBuiltinToolSlice: StateCreator<
27
27
  [],
28
28
  BuiltinToolAction
29
29
  > = (set, get) => ({
30
- invokeBuiltinTool: async (key, params) => {
30
+ text2image: ({ prompts, size = '1024x1024' as const, quality = 'standard', style = 'vivid' }) =>
31
+ prompts.map((p) => ({ prompt: p, quality, size, style })),
32
+ toggleBuiltinToolLoading: (key, value) => {
33
+ set({ builtinToolLoading: { [key]: value } }, false, n('toggleBuiltinToolLoading'));
34
+ },
35
+
36
+ transformApiArgumentsToAiState: async (key, params) => {
31
37
  const { builtinToolLoading, toggleBuiltinToolLoading } = get();
32
38
 
33
39
  if (builtinToolLoading[key]) return;
@@ -45,10 +51,4 @@ export const createBuiltinToolSlice: StateCreator<
45
51
 
46
52
  return JSON.stringify(result);
47
53
  },
48
- text2image: ({ prompts, size = '1024x1024' as const, quality = 'standard', style = 'vivid' }) =>
49
- prompts.map((p) => ({ prompt: p, quality, size, style })),
50
-
51
- toggleBuiltinToolLoading: (key, value) => {
52
- set({ builtinToolLoading: { [key]: value } }, false, n('toggleBuiltinToolLoading'));
53
- },
54
54
  });
@@ -32,7 +32,7 @@ export * from './tools';
32
32
 
33
33
  export interface ChatMessage extends BaseDataModel {
34
34
  content: string;
35
- error?: ChatMessageError;
35
+ error?: ChatMessageError | null;
36
36
  // 扩展字段
37
37
  extra?: {
38
38
  fromModel?: string;