@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.
- package/CHANGELOG.md +42 -0
- package/Dockerfile +4 -1
- package/package.json +3 -2
- package/src/config/modelProviders/anthropic.ts +3 -0
- package/src/config/modelProviders/google.ts +3 -0
- package/src/config/modelProviders/groq.ts +5 -1
- package/src/config/modelProviders/minimax.ts +10 -7
- package/src/config/modelProviders/mistral.ts +1 -0
- package/src/config/modelProviders/moonshot.ts +3 -0
- package/src/config/modelProviders/zhipu.ts +2 -6
- package/src/config/server/provider.ts +1 -1
- package/src/database/client/core/db.ts +32 -0
- package/src/database/client/core/schemas.ts +9 -0
- package/src/database/client/models/__tests__/message.test.ts +2 -2
- package/src/database/client/schemas/message.ts +8 -1
- package/src/features/AgentSetting/store/action.ts +15 -6
- package/src/features/Conversation/Actions/Tool.tsx +16 -0
- package/src/features/Conversation/Actions/index.ts +2 -2
- package/src/features/Conversation/Messages/Assistant/ToolCalls/index.tsx +78 -0
- package/src/features/Conversation/Messages/Assistant/ToolCalls/style.ts +25 -0
- package/src/features/Conversation/Messages/Assistant/index.tsx +47 -0
- package/src/features/Conversation/Messages/Default.tsx +4 -1
- package/src/features/Conversation/{Plugins → Messages/Tool}/Inspector/index.tsx +34 -35
- package/src/features/Conversation/Messages/Tool/index.tsx +44 -0
- package/src/features/Conversation/Messages/index.ts +3 -2
- package/src/features/Conversation/Plugins/Render/StandaloneType/Iframe.tsx +1 -1
- package/src/features/Conversation/components/SkeletonList.tsx +2 -2
- package/src/features/Conversation/index.tsx +2 -3
- package/src/libs/agent-runtime/BaseAI.ts +2 -9
- package/src/libs/agent-runtime/anthropic/index.test.ts +195 -0
- package/src/libs/agent-runtime/anthropic/index.ts +71 -15
- package/src/libs/agent-runtime/azureOpenai/index.ts +12 -13
- package/src/libs/agent-runtime/bedrock/index.ts +24 -18
- package/src/libs/agent-runtime/google/index.test.ts +154 -0
- package/src/libs/agent-runtime/google/index.ts +91 -10
- package/src/libs/agent-runtime/groq/index.test.ts +41 -72
- package/src/libs/agent-runtime/groq/index.ts +7 -0
- package/src/libs/agent-runtime/minimax/index.test.ts +2 -2
- package/src/libs/agent-runtime/minimax/index.ts +14 -37
- package/src/libs/agent-runtime/mistral/index.test.ts +0 -53
- package/src/libs/agent-runtime/mistral/index.ts +1 -0
- package/src/libs/agent-runtime/moonshot/index.test.ts +1 -71
- package/src/libs/agent-runtime/ollama/index.test.ts +197 -0
- package/src/libs/agent-runtime/ollama/index.ts +3 -3
- package/src/libs/agent-runtime/openai/index.test.ts +0 -53
- package/src/libs/agent-runtime/openrouter/index.test.ts +1 -53
- package/src/libs/agent-runtime/perplexity/index.test.ts +0 -71
- package/src/libs/agent-runtime/perplexity/index.ts +2 -3
- package/src/libs/agent-runtime/togetherai/__snapshots__/index.test.ts.snap +886 -0
- package/src/libs/agent-runtime/togetherai/fixtures/models.json +8111 -0
- package/src/libs/agent-runtime/togetherai/index.test.ts +16 -54
- package/src/libs/agent-runtime/types/chat.ts +19 -3
- package/src/libs/agent-runtime/utils/anthropicHelpers.test.ts +120 -1
- package/src/libs/agent-runtime/utils/anthropicHelpers.ts +67 -4
- package/src/libs/agent-runtime/utils/debugStream.test.ts +70 -0
- package/src/libs/agent-runtime/utils/debugStream.ts +39 -9
- package/src/libs/agent-runtime/utils/openaiCompatibleFactory/index.test.ts +521 -0
- package/src/libs/agent-runtime/utils/openaiCompatibleFactory/index.ts +76 -5
- package/src/libs/agent-runtime/utils/response.ts +12 -0
- package/src/libs/agent-runtime/utils/streams/anthropic.test.ts +197 -0
- package/src/libs/agent-runtime/utils/streams/anthropic.ts +91 -0
- package/src/libs/agent-runtime/utils/streams/bedrock/claude.ts +21 -0
- package/src/libs/agent-runtime/utils/streams/bedrock/common.ts +32 -0
- package/src/libs/agent-runtime/utils/streams/bedrock/index.ts +3 -0
- package/src/libs/agent-runtime/utils/streams/bedrock/llama.test.ts +196 -0
- package/src/libs/agent-runtime/utils/streams/bedrock/llama.ts +51 -0
- package/src/libs/agent-runtime/utils/streams/google-ai.test.ts +97 -0
- package/src/libs/agent-runtime/utils/streams/google-ai.ts +68 -0
- package/src/libs/agent-runtime/utils/streams/index.ts +7 -0
- package/src/libs/agent-runtime/utils/streams/minimax.ts +39 -0
- package/src/libs/agent-runtime/utils/streams/ollama.test.ts +77 -0
- package/src/libs/agent-runtime/utils/streams/ollama.ts +38 -0
- package/src/libs/agent-runtime/utils/streams/openai.test.ts +263 -0
- package/src/libs/agent-runtime/utils/streams/openai.ts +79 -0
- package/src/libs/agent-runtime/utils/streams/protocol.ts +100 -0
- package/src/libs/agent-runtime/zeroone/index.test.ts +1 -53
- package/src/libs/agent-runtime/zhipu/index.test.ts +1 -1
- package/src/libs/agent-runtime/zhipu/index.ts +3 -2
- package/src/locales/default/plugin.ts +3 -4
- package/src/migrations/FromV4ToV5/fixtures/from-v1-to-v5-output.json +245 -0
- package/src/migrations/FromV4ToV5/fixtures/function-input-v4.json +96 -0
- package/src/migrations/FromV4ToV5/fixtures/function-output-v5.json +120 -0
- package/src/migrations/FromV4ToV5/index.ts +58 -0
- package/src/migrations/FromV4ToV5/migrations.test.ts +49 -0
- package/src/migrations/FromV4ToV5/types/v4.ts +21 -0
- package/src/migrations/FromV4ToV5/types/v5.ts +27 -0
- package/src/migrations/index.ts +8 -1
- package/src/services/__tests__/chat.test.ts +10 -20
- package/src/services/chat.ts +78 -65
- package/src/store/chat/slices/enchance/action.ts +15 -10
- package/src/store/chat/slices/message/action.test.ts +36 -86
- package/src/store/chat/slices/message/action.ts +70 -79
- package/src/store/chat/slices/message/reducer.ts +18 -1
- package/src/store/chat/slices/message/selectors.test.ts +38 -68
- package/src/store/chat/slices/message/selectors.ts +1 -22
- package/src/store/chat/slices/plugin/action.test.ts +147 -203
- package/src/store/chat/slices/plugin/action.ts +96 -82
- package/src/store/chat/slices/share/action.test.ts +3 -3
- package/src/store/chat/slices/share/action.ts +1 -1
- package/src/store/chat/slices/topic/action.ts +7 -2
- package/src/store/tool/selectors/tool.ts +6 -24
- package/src/store/tool/slices/builtin/action.test.ts +90 -0
- package/src/types/llm.ts +1 -1
- package/src/types/message/index.ts +9 -4
- package/src/types/message/tools.ts +57 -0
- package/src/types/openai/chat.ts +6 -0
- package/src/utils/fetch.test.ts +245 -1
- package/src/utils/fetch.ts +120 -44
- package/src/utils/toolCall.ts +21 -0
- package/src/features/Conversation/Messages/Assistant.tsx +0 -26
- package/src/features/Conversation/Messages/Function.tsx +0 -35
- package/src/libs/agent-runtime/ollama/stream.ts +0 -31
- /package/src/features/Conversation/{Plugins → Messages/Tool}/Inspector/PluginResultJSON.tsx +0 -0
- /package/src/features/Conversation/{Plugins → Messages/Tool}/Inspector/Settings.tsx +0 -0
- /package/src/features/Conversation/{Plugins → Messages/Tool}/Inspector/style.ts +0 -0
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { describe } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import { MigrationData, VersionController } from '@/migrations/VersionController';
|
|
4
|
+
|
|
5
|
+
import { MigrationV1ToV2 } from '../FromV1ToV2';
|
|
6
|
+
import inputV1Data from '../FromV1ToV2/fixtures/input-v1-session.json';
|
|
7
|
+
import { MigrationV2ToV3 } from '../FromV2ToV3';
|
|
8
|
+
import { MigrationV3ToV4 } from '../FromV3ToV4';
|
|
9
|
+
import outputDataFromV1ToV5 from './fixtures/from-v1-to-v5-output.json';
|
|
10
|
+
import functionInputV4 from './fixtures/function-input-v4.json';
|
|
11
|
+
import functionOutputV5 from './fixtures/function-output-v5.json';
|
|
12
|
+
import { MigrationV4ToV5 } from './index';
|
|
13
|
+
|
|
14
|
+
describe('MigrationV4ToV5', () => {
|
|
15
|
+
let migrations;
|
|
16
|
+
let versionController: VersionController<any>;
|
|
17
|
+
|
|
18
|
+
beforeEach(() => {
|
|
19
|
+
migrations = [MigrationV4ToV5];
|
|
20
|
+
versionController = new VersionController(migrations, 5);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
describe('should migrate data correctly from previous versions', () => {
|
|
24
|
+
it('role=function', () => {
|
|
25
|
+
const data: MigrationData = functionInputV4;
|
|
26
|
+
|
|
27
|
+
const migratedData = versionController.migrate(data);
|
|
28
|
+
|
|
29
|
+
expect(migratedData.version).toEqual(functionOutputV5.version);
|
|
30
|
+
expect(migratedData.state.messages).toEqual(functionOutputV5.state.messages);
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('should work correct from v1 to v5', () => {
|
|
35
|
+
const data: MigrationData = inputV1Data;
|
|
36
|
+
|
|
37
|
+
versionController = new VersionController(
|
|
38
|
+
[MigrationV4ToV5, MigrationV3ToV4, MigrationV2ToV3, MigrationV1ToV2],
|
|
39
|
+
5,
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
const migratedData = versionController.migrate(data);
|
|
43
|
+
|
|
44
|
+
expect(migratedData.version).toEqual(outputDataFromV1ToV5.version);
|
|
45
|
+
expect(migratedData.state.messages).toEqual(outputDataFromV1ToV5.state.messages);
|
|
46
|
+
expect(migratedData.state.sessions).toEqual(outputDataFromV1ToV5.state.sessions);
|
|
47
|
+
expect(migratedData.state.topics).toEqual(outputDataFromV1ToV5.state.topics);
|
|
48
|
+
});
|
|
49
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { LobeToolRenderType } from '@/types/tool';
|
|
2
|
+
|
|
3
|
+
export interface V4ChatPluginPayload {
|
|
4
|
+
apiName: string;
|
|
5
|
+
arguments: string;
|
|
6
|
+
identifier: string;
|
|
7
|
+
type: LobeToolRenderType;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface V4Message {
|
|
11
|
+
content: string;
|
|
12
|
+
createdAt: number;
|
|
13
|
+
id: string;
|
|
14
|
+
plugin?: V4ChatPluginPayload;
|
|
15
|
+
role: 'user' | 'system' | 'assistant' | 'function';
|
|
16
|
+
updatedAt: number;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface V4ConfigState {
|
|
20
|
+
messages: V4Message[];
|
|
21
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { LobeToolRenderType } from '@/types/tool';
|
|
2
|
+
|
|
3
|
+
import { V4ChatPluginPayload } from './v4';
|
|
4
|
+
|
|
5
|
+
interface ChatToolPayload {
|
|
6
|
+
apiName: string;
|
|
7
|
+
arguments: string;
|
|
8
|
+
id: string;
|
|
9
|
+
identifier: string;
|
|
10
|
+
type: LobeToolRenderType;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface V5Message {
|
|
14
|
+
content: string;
|
|
15
|
+
createdAt: number;
|
|
16
|
+
id: string;
|
|
17
|
+
parentId?: string;
|
|
18
|
+
plugin?: V4ChatPluginPayload;
|
|
19
|
+
role: 'user' | 'system' | 'assistant' | 'tool';
|
|
20
|
+
tool_call_id?: string;
|
|
21
|
+
tools?: ChatToolPayload[];
|
|
22
|
+
updatedAt: number;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface V5ConfigState {
|
|
26
|
+
messages: V5Message[];
|
|
27
|
+
}
|
package/src/migrations/index.ts
CHANGED
|
@@ -5,12 +5,19 @@ import { ConfigStateAll } from '@/types/exportConfig';
|
|
|
5
5
|
import { MigrationV0ToV1 } from './FromV0ToV1';
|
|
6
6
|
import { MigrationV1ToV2 } from './FromV1ToV2';
|
|
7
7
|
import { MigrationV3ToV4 } from './FromV3ToV4';
|
|
8
|
+
import { MigrationV4ToV5 } from './FromV4ToV5';
|
|
8
9
|
|
|
9
10
|
// Current latest version
|
|
10
|
-
export const CURRENT_CONFIG_VERSION =
|
|
11
|
+
export const CURRENT_CONFIG_VERSION = 5;
|
|
11
12
|
|
|
12
13
|
// Version migrations module
|
|
13
14
|
const ConfigMigrations = [
|
|
15
|
+
/**
|
|
16
|
+
* 2024.05.11
|
|
17
|
+
*
|
|
18
|
+
* role=function to role=tool
|
|
19
|
+
*/
|
|
20
|
+
MigrationV4ToV5,
|
|
14
21
|
/**
|
|
15
22
|
* 2024.04.09
|
|
16
23
|
* settings migrate the `languageModel`
|
|
@@ -126,7 +126,7 @@ describe('ChatService', () => {
|
|
|
126
126
|
it('should include image content when with vision model', async () => {
|
|
127
127
|
const messages = [
|
|
128
128
|
{ content: 'Hello', role: 'user', files: ['file1'] }, // Message with files
|
|
129
|
-
{ content: 'Hi', role: '
|
|
129
|
+
{ content: 'Hi', role: 'tool', plugin: { identifier: 'plugin1', apiName: 'api1' } }, // Message with tool role
|
|
130
130
|
{ content: 'Hey', role: 'assistant' }, // Regular user message
|
|
131
131
|
] as ChatMessage[];
|
|
132
132
|
|
|
@@ -166,8 +166,8 @@ describe('ChatService', () => {
|
|
|
166
166
|
},
|
|
167
167
|
{
|
|
168
168
|
content: 'Hi',
|
|
169
|
-
name: '
|
|
170
|
-
role: '
|
|
169
|
+
name: 'plugin1____api1',
|
|
170
|
+
role: 'tool',
|
|
171
171
|
},
|
|
172
172
|
{
|
|
173
173
|
content: 'Hey',
|
|
@@ -183,7 +183,7 @@ describe('ChatService', () => {
|
|
|
183
183
|
it('should not include image content when default model', async () => {
|
|
184
184
|
const messages = [
|
|
185
185
|
{ content: 'Hello', role: 'user', files: ['file1'] }, // Message with files
|
|
186
|
-
{ content: 'Hi', role: '
|
|
186
|
+
{ content: 'Hi', role: 'tool', plugin: { identifier: 'plugin1', apiName: 'api1' } }, // Message with function role
|
|
187
187
|
{ content: 'Hey', role: 'assistant' }, // Regular user message
|
|
188
188
|
] as ChatMessage[];
|
|
189
189
|
|
|
@@ -212,7 +212,7 @@ describe('ChatService', () => {
|
|
|
212
212
|
{
|
|
213
213
|
messages: [
|
|
214
214
|
{ content: 'Hello', role: 'user' },
|
|
215
|
-
{ content: 'Hi', name: '
|
|
215
|
+
{ content: 'Hi', name: 'plugin1____api1', role: 'tool' },
|
|
216
216
|
{ content: 'Hey', role: 'assistant' },
|
|
217
217
|
],
|
|
218
218
|
model: 'gpt-3.5-turbo',
|
|
@@ -224,7 +224,7 @@ describe('ChatService', () => {
|
|
|
224
224
|
it('should not include image with vision models when can not find the image', async () => {
|
|
225
225
|
const messages = [
|
|
226
226
|
{ content: 'Hello', role: 'user', files: ['file2'] }, // Message with files
|
|
227
|
-
{ content: 'Hi', role: '
|
|
227
|
+
{ content: 'Hi', role: 'tool', plugin: { identifier: 'plugin1', apiName: 'api1' } }, // Message with function role
|
|
228
228
|
{ content: 'Hey', role: 'assistant' }, // Regular user message
|
|
229
229
|
] as ChatMessage[];
|
|
230
230
|
|
|
@@ -248,19 +248,9 @@ describe('ChatService', () => {
|
|
|
248
248
|
expect(getChatCompletionSpy).toHaveBeenCalledWith(
|
|
249
249
|
{
|
|
250
250
|
messages: [
|
|
251
|
-
{
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
},
|
|
255
|
-
{
|
|
256
|
-
content: 'Hi',
|
|
257
|
-
name: 'plugin1',
|
|
258
|
-
role: 'function',
|
|
259
|
-
},
|
|
260
|
-
{
|
|
261
|
-
content: 'Hey',
|
|
262
|
-
role: 'assistant',
|
|
263
|
-
},
|
|
251
|
+
{ content: 'Hello', role: 'user' },
|
|
252
|
+
{ content: 'Hi', name: 'plugin1____api1', role: 'tool' },
|
|
253
|
+
{ content: 'Hey', role: 'assistant' },
|
|
264
254
|
],
|
|
265
255
|
},
|
|
266
256
|
undefined,
|
|
@@ -580,7 +570,7 @@ Get data from users`,
|
|
|
580
570
|
body: JSON.stringify(expectedPayload),
|
|
581
571
|
headers: expect.any(Object),
|
|
582
572
|
method: 'POST',
|
|
583
|
-
signal:
|
|
573
|
+
signal: expect.any(AbortSignal),
|
|
584
574
|
});
|
|
585
575
|
});
|
|
586
576
|
|
package/src/services/chat.ts
CHANGED
|
@@ -21,16 +21,17 @@ import {
|
|
|
21
21
|
userProfileSelectors,
|
|
22
22
|
} from '@/store/user/selectors';
|
|
23
23
|
import { ChatErrorType } from '@/types/fetch';
|
|
24
|
-
import { ChatMessage } from '@/types/message';
|
|
24
|
+
import { ChatMessage, MessageToolCall } from '@/types/message';
|
|
25
25
|
import type { ChatStreamPayload, OpenAIChatMessage } from '@/types/openai/chat';
|
|
26
26
|
import { UserMessageContentPart } from '@/types/openai/chat';
|
|
27
|
-
import { FetchSSEOptions,
|
|
27
|
+
import { FetchSSEOptions, fetchSSE, getMessageError } from '@/utils/fetch';
|
|
28
|
+
import { genToolCallingName } from '@/utils/toolCall';
|
|
28
29
|
import { createTraceHeader, getTraceId } from '@/utils/trace';
|
|
29
30
|
|
|
30
31
|
import { createHeaderWithAuth, getProviderAuthPayload } from './_auth';
|
|
31
32
|
import { API_ENDPOINTS } from './_url';
|
|
32
33
|
|
|
33
|
-
interface FetchOptions {
|
|
34
|
+
interface FetchOptions extends FetchSSEOptions {
|
|
34
35
|
isWelcomeQuestion?: boolean;
|
|
35
36
|
signal?: AbortSignal | undefined;
|
|
36
37
|
trace?: TracePayload;
|
|
@@ -40,23 +41,14 @@ interface GetChatCompletionPayload extends Partial<Omit<ChatStreamPayload, 'mess
|
|
|
40
41
|
messages: ChatMessage[];
|
|
41
42
|
}
|
|
42
43
|
|
|
43
|
-
interface FetchAITaskResultParams {
|
|
44
|
+
interface FetchAITaskResultParams extends FetchSSEOptions {
|
|
44
45
|
abortController?: AbortController;
|
|
45
|
-
/**
|
|
46
|
-
* 错误处理函数
|
|
47
|
-
*/
|
|
48
46
|
onError?: (e: Error, rawError?: any) => void;
|
|
49
|
-
onFinish?: OnFinishHandler;
|
|
50
47
|
/**
|
|
51
48
|
* 加载状态变化处理函数
|
|
52
49
|
* @param loading - 是否处于加载状态
|
|
53
50
|
*/
|
|
54
51
|
onLoadingChange?: (loading: boolean) => void;
|
|
55
|
-
/**
|
|
56
|
-
* 消息处理函数
|
|
57
|
-
* @param text - 消息内容
|
|
58
|
-
*/
|
|
59
|
-
onMessageHandle?: (text: string) => void;
|
|
60
52
|
/**
|
|
61
53
|
* 请求对象
|
|
62
54
|
*/
|
|
@@ -224,20 +216,15 @@ class ChatService {
|
|
|
224
216
|
trace,
|
|
225
217
|
isWelcomeQuestion,
|
|
226
218
|
}: CreateAssistantMessageStream) => {
|
|
227
|
-
await
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
onErrorHandle,
|
|
237
|
-
onFinish,
|
|
238
|
-
onMessageHandle,
|
|
239
|
-
},
|
|
240
|
-
);
|
|
219
|
+
await this.createAssistantMessage(params, {
|
|
220
|
+
isWelcomeQuestion,
|
|
221
|
+
onAbort,
|
|
222
|
+
onErrorHandle,
|
|
223
|
+
onFinish,
|
|
224
|
+
onMessageHandle,
|
|
225
|
+
signal: abortController?.signal,
|
|
226
|
+
trace: this.mapTrace(trace, TraceTagMap.Chat),
|
|
227
|
+
});
|
|
241
228
|
};
|
|
242
229
|
|
|
243
230
|
getChatCompletion = async (params: Partial<ChatStreamPayload>, options?: FetchOptions) => {
|
|
@@ -268,28 +255,33 @@ class ChatService {
|
|
|
268
255
|
const enableFetchOnClient = modelConfigSelectors.isProviderFetchOnClient(provider)(
|
|
269
256
|
useUserStore.getState(),
|
|
270
257
|
);
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
* user which will cause abuse of plugins services
|
|
275
|
-
* 2. This feature will disabled by default
|
|
276
|
-
*/
|
|
258
|
+
|
|
259
|
+
let fetcher: typeof fetch | undefined = undefined;
|
|
260
|
+
|
|
277
261
|
if (enableFetchOnClient) {
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
262
|
+
/**
|
|
263
|
+
* Notes:
|
|
264
|
+
* 1. Browser agent runtime will skip auth check if a key and endpoint provided by
|
|
265
|
+
* user which will cause abuse of plugins services
|
|
266
|
+
* 2. This feature will be disabled by default
|
|
267
|
+
*/
|
|
268
|
+
fetcher = async () => {
|
|
269
|
+
try {
|
|
270
|
+
return await this.fetchOnClient({ payload, provider, signal });
|
|
271
|
+
} catch (e) {
|
|
272
|
+
const {
|
|
273
|
+
errorType = ChatErrorType.BadRequest,
|
|
274
|
+
error: errorContent,
|
|
275
|
+
...res
|
|
276
|
+
} = e as ChatCompletionErrorPayload;
|
|
277
|
+
|
|
278
|
+
const error = errorContent || e;
|
|
279
|
+
// track the error at server side
|
|
280
|
+
console.error(`Route: [${provider}] ${errorType}:`, error);
|
|
281
|
+
|
|
282
|
+
return createErrorResponse(errorType, { error, ...res, provider });
|
|
283
|
+
}
|
|
284
|
+
};
|
|
293
285
|
}
|
|
294
286
|
|
|
295
287
|
const traceHeader = createTraceHeader({ ...options?.trace });
|
|
@@ -299,10 +291,15 @@ class ChatService {
|
|
|
299
291
|
provider,
|
|
300
292
|
});
|
|
301
293
|
|
|
302
|
-
return
|
|
294
|
+
return fetchSSE(API_ENDPOINTS.chat(provider), {
|
|
303
295
|
body: JSON.stringify(payload),
|
|
296
|
+
fetcher: fetcher,
|
|
304
297
|
headers,
|
|
305
298
|
method: 'POST',
|
|
299
|
+
onAbort: options?.onAbort,
|
|
300
|
+
onErrorHandle: options?.onErrorHandle,
|
|
301
|
+
onFinish: options?.onFinish,
|
|
302
|
+
onMessageHandle: options?.onMessageHandle,
|
|
306
303
|
signal,
|
|
307
304
|
});
|
|
308
305
|
};
|
|
@@ -360,20 +357,15 @@ class ChatService {
|
|
|
360
357
|
|
|
361
358
|
onLoadingChange?.(true);
|
|
362
359
|
|
|
363
|
-
const data = await
|
|
364
|
-
() =>
|
|
365
|
-
|
|
366
|
-
signal: abortController?.signal,
|
|
367
|
-
trace: this.mapTrace(trace, TraceTagMap.SystemChain),
|
|
368
|
-
}),
|
|
369
|
-
{
|
|
370
|
-
onErrorHandle: (error) => {
|
|
371
|
-
errorHandle(new Error(error.message), error);
|
|
372
|
-
},
|
|
373
|
-
onFinish,
|
|
374
|
-
onMessageHandle,
|
|
360
|
+
const data = await this.getChatCompletion(params, {
|
|
361
|
+
onErrorHandle: (error) => {
|
|
362
|
+
errorHandle(new Error(error.message), error);
|
|
375
363
|
},
|
|
376
|
-
|
|
364
|
+
onFinish,
|
|
365
|
+
onMessageHandle,
|
|
366
|
+
signal: abortController?.signal,
|
|
367
|
+
trace: this.mapTrace(trace, TraceTagMap.SystemChain),
|
|
368
|
+
}).catch(errorHandle);
|
|
377
369
|
|
|
378
370
|
onLoadingChange?.(false);
|
|
379
371
|
|
|
@@ -424,9 +416,30 @@ class ChatService {
|
|
|
424
416
|
return { content: getContent(m), role: m.role };
|
|
425
417
|
}
|
|
426
418
|
|
|
427
|
-
case '
|
|
428
|
-
|
|
429
|
-
|
|
419
|
+
case 'assistant': {
|
|
420
|
+
return {
|
|
421
|
+
content: m.content,
|
|
422
|
+
role: m.role,
|
|
423
|
+
tool_calls: m.tools?.map(
|
|
424
|
+
(tool): MessageToolCall => ({
|
|
425
|
+
function: {
|
|
426
|
+
arguments: tool.arguments,
|
|
427
|
+
name: genToolCallingName(tool.identifier, tool.apiName, tool.type),
|
|
428
|
+
},
|
|
429
|
+
id: tool.id,
|
|
430
|
+
type: 'function',
|
|
431
|
+
}),
|
|
432
|
+
),
|
|
433
|
+
};
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
case 'tool': {
|
|
437
|
+
return {
|
|
438
|
+
content: m.content,
|
|
439
|
+
name: genToolCallingName(m.plugin!.identifier, m.plugin!.apiName, m.plugin?.type),
|
|
440
|
+
role: m.role,
|
|
441
|
+
tool_call_id: m.tool_call_id,
|
|
442
|
+
};
|
|
430
443
|
}
|
|
431
444
|
|
|
432
445
|
default: {
|
|
@@ -76,16 +76,21 @@ export const chatEnhance: StateCreator<
|
|
|
76
76
|
|
|
77
77
|
// translate to target language
|
|
78
78
|
await chatService.fetchPresetTaskResult({
|
|
79
|
-
onMessageHandle: (
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
79
|
+
onMessageHandle: (chunk) => {
|
|
80
|
+
switch (chunk.type) {
|
|
81
|
+
case 'text': {
|
|
82
|
+
internal_dispatchMessage({
|
|
83
|
+
id,
|
|
84
|
+
key: 'translate',
|
|
85
|
+
type: 'updateMessageExtra',
|
|
86
|
+
value: produce({ content: '', from, to: targetLang }, (draft) => {
|
|
87
|
+
content += chunk.text;
|
|
88
|
+
draft.content += content;
|
|
89
|
+
}),
|
|
90
|
+
});
|
|
91
|
+
break;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
89
94
|
},
|
|
90
95
|
params: chainTranslate(message.content, targetLang),
|
|
91
96
|
trace: get().getCurrentTracePayload({ traceName: TraceNameMap.Translator }),
|
|
@@ -19,6 +19,7 @@ vi.stubGlobal(
|
|
|
19
19
|
vi.fn(() => Promise.resolve(new Response('mock'))),
|
|
20
20
|
);
|
|
21
21
|
|
|
22
|
+
vi.mock('zustand/traditional');
|
|
22
23
|
// Mock service
|
|
23
24
|
vi.mock('@/services/message', () => ({
|
|
24
25
|
messageService: {
|
|
@@ -47,12 +48,6 @@ vi.mock('@/services/chat', async (importOriginal) => {
|
|
|
47
48
|
};
|
|
48
49
|
});
|
|
49
50
|
|
|
50
|
-
vi.mock('@/store/chat/selectors', () => ({
|
|
51
|
-
chatSelectors: {
|
|
52
|
-
currentChats: vi.fn(),
|
|
53
|
-
},
|
|
54
|
-
}));
|
|
55
|
-
|
|
56
51
|
const realCoreProcessMessage = useChatStore.getState().internal_coreProcessMessage;
|
|
57
52
|
const realRefreshMessages = useChatStore.getState().refreshMessages;
|
|
58
53
|
// Mock state
|
|
@@ -86,6 +81,9 @@ describe('chatMessage actions', () => {
|
|
|
86
81
|
const messageId = 'message-id';
|
|
87
82
|
const deleteSpy = vi.spyOn(result.current, 'deleteMessage');
|
|
88
83
|
|
|
84
|
+
act(() => {
|
|
85
|
+
useChatStore.setState({ messages: [{ id: messageId } as ChatMessage] });
|
|
86
|
+
});
|
|
89
87
|
await act(async () => {
|
|
90
88
|
await result.current.deleteMessage(messageId);
|
|
91
89
|
});
|
|
@@ -259,11 +257,6 @@ describe('chatMessage actions', () => {
|
|
|
259
257
|
enableAutoCreateTopic,
|
|
260
258
|
}));
|
|
261
259
|
|
|
262
|
-
// Mock the currentChats selector to return a list that does not reach the threshold
|
|
263
|
-
(chatSelectors.currentChats as Mock).mockReturnValue(
|
|
264
|
-
Array.from({ length: autoCreateTopicThreshold + 1 }, (_, i) => ({ id: `msg-${i}` })),
|
|
265
|
-
);
|
|
266
|
-
|
|
267
260
|
// Mock saveToTopic and switchTopic to simulate not being called
|
|
268
261
|
const saveToTopicMock = vi.fn();
|
|
269
262
|
const switchTopicMock = vi.fn();
|
|
@@ -271,6 +264,10 @@ describe('chatMessage actions', () => {
|
|
|
271
264
|
await act(async () => {
|
|
272
265
|
useChatStore.setState({
|
|
273
266
|
...mockState,
|
|
267
|
+
// Mock the currentChats selector to return a list that does not reach the threshold
|
|
268
|
+
messages: Array.from({ length: autoCreateTopicThreshold + 1 }, (_, i) => ({
|
|
269
|
+
id: `msg-${i}`,
|
|
270
|
+
})) as any,
|
|
274
271
|
activeTopicId: undefined,
|
|
275
272
|
saveToTopic: saveToTopicMock,
|
|
276
273
|
switchTopic: switchTopicMock,
|
|
@@ -298,11 +295,6 @@ describe('chatMessage actions', () => {
|
|
|
298
295
|
// Mock messageService.create to resolve with a message id
|
|
299
296
|
(messageService.createMessage as Mock).mockResolvedValue('new-message-id');
|
|
300
297
|
|
|
301
|
-
// Mock the currentChats selector to return a list that reaches the threshold
|
|
302
|
-
(chatSelectors.currentChats as Mock).mockReturnValue(
|
|
303
|
-
Array.from({ length: autoCreateTopicThreshold }, (_, i) => ({ id: `msg-${i}` })),
|
|
304
|
-
);
|
|
305
|
-
|
|
306
298
|
// Mock saveToTopic to resolve with a topic id and switchTopic to switch to the new topic
|
|
307
299
|
const saveToTopicMock = vi.fn(() => Promise.resolve('new-topic-id'));
|
|
308
300
|
const switchTopicMock = vi.fn();
|
|
@@ -310,6 +302,9 @@ describe('chatMessage actions', () => {
|
|
|
310
302
|
act(() => {
|
|
311
303
|
useChatStore.setState({
|
|
312
304
|
...mockState,
|
|
305
|
+
messages: Array.from({ length: autoCreateTopicThreshold }, (_, i) => ({
|
|
306
|
+
id: `msg-${i}`,
|
|
307
|
+
})) as any,
|
|
313
308
|
activeTopicId: undefined,
|
|
314
309
|
saveToTopic: saveToTopicMock,
|
|
315
310
|
switchTopic: switchTopicMock,
|
|
@@ -339,11 +334,6 @@ describe('chatMessage actions', () => {
|
|
|
339
334
|
enableAutoCreateTopic,
|
|
340
335
|
}));
|
|
341
336
|
|
|
342
|
-
// Mock the currentChats selector to return a list that does not reach the threshold
|
|
343
|
-
(chatSelectors.currentChats as Mock).mockReturnValue(
|
|
344
|
-
Array.from({ length: autoCreateTopicThreshold - 1 }, (_, i) => ({ id: `msg-${i}` })),
|
|
345
|
-
);
|
|
346
|
-
|
|
347
337
|
// Mock saveToTopic and switchTopic to simulate not being called
|
|
348
338
|
const saveToTopicMock = vi.fn();
|
|
349
339
|
const switchTopicMock = vi.fn();
|
|
@@ -351,6 +341,10 @@ describe('chatMessage actions', () => {
|
|
|
351
341
|
await act(async () => {
|
|
352
342
|
useChatStore.setState({
|
|
353
343
|
...mockState,
|
|
344
|
+
// Mock the currentChats selector to return a list that does not reach the threshold
|
|
345
|
+
messages: Array.from({ length: autoCreateTopicThreshold - 2 }, (_, i) => ({
|
|
346
|
+
id: `msg-${i}`,
|
|
347
|
+
})) as any,
|
|
354
348
|
activeTopicId: undefined,
|
|
355
349
|
saveToTopic: saveToTopicMock,
|
|
356
350
|
switchTopic: switchTopicMock,
|
|
@@ -395,12 +389,14 @@ describe('chatMessage actions', () => {
|
|
|
395
389
|
const { result } = renderHook(() => useChatStore());
|
|
396
390
|
const messageId = 'message-id';
|
|
397
391
|
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
392
|
+
act(() => {
|
|
393
|
+
useChatStore.setState({
|
|
394
|
+
// Mock the currentChats selector to return a list that includes the message to be resent
|
|
395
|
+
messages: [
|
|
396
|
+
{ id: messageId, role: 'user', content: 'Resend this message' } as ChatMessage,
|
|
397
|
+
],
|
|
398
|
+
});
|
|
399
|
+
});
|
|
404
400
|
|
|
405
401
|
// Mock the internal_coreProcessMessage function to resolve immediately
|
|
406
402
|
mockState.internal_coreProcessMessage.mockResolvedValue(undefined);
|
|
@@ -421,10 +417,12 @@ describe('chatMessage actions', () => {
|
|
|
421
417
|
const { result } = renderHook(() => useChatStore());
|
|
422
418
|
const messageId = 'non-existing-message-id';
|
|
423
419
|
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
420
|
+
act(() => {
|
|
421
|
+
useChatStore.setState({
|
|
422
|
+
// Mock the currentChats selector to return a list that does not include the message to be resent
|
|
423
|
+
messages: [],
|
|
424
|
+
});
|
|
425
|
+
});
|
|
428
426
|
|
|
429
427
|
await act(async () => {
|
|
430
428
|
await result.current.internal_resendMessage(messageId);
|
|
@@ -461,9 +459,8 @@ describe('chatMessage actions', () => {
|
|
|
461
459
|
|
|
462
460
|
expect(internal_dispatchMessageSpy).toHaveBeenCalledWith({
|
|
463
461
|
id: messageId,
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
value: newContent,
|
|
462
|
+
type: 'updateMessages',
|
|
463
|
+
value: { content: newContent },
|
|
467
464
|
});
|
|
468
465
|
});
|
|
469
466
|
|
|
@@ -645,56 +642,7 @@ describe('chatMessage actions', () => {
|
|
|
645
642
|
messages,
|
|
646
643
|
assistantMessageId,
|
|
647
644
|
);
|
|
648
|
-
expect(response.content).toEqual(aiResponse);
|
|
649
645
|
expect(response.isFunctionCall).toEqual(false);
|
|
650
|
-
expect(response.functionCallAtEnd).toEqual(false);
|
|
651
|
-
expect(response.functionCallContent).toEqual('');
|
|
652
|
-
});
|
|
653
|
-
});
|
|
654
|
-
|
|
655
|
-
it('should handle function call message at start of AI response', async () => {
|
|
656
|
-
const { result } = renderHook(() => useChatStore());
|
|
657
|
-
const messages = [{ id: 'message-id', content: 'Hello', role: 'user' }] as ChatMessage[];
|
|
658
|
-
const assistantMessageId = 'assistant-message-id';
|
|
659
|
-
const aiResponse =
|
|
660
|
-
'{"tool_calls":[{"id":"call_sbca","type":"function","function":{"name":"pluginName____apiName","arguments":{"key":"value"}}}]}';
|
|
661
|
-
|
|
662
|
-
// Mock fetch to resolve with AI response containing function call
|
|
663
|
-
vi.mocked(fetch).mockResolvedValueOnce(new Response(aiResponse));
|
|
664
|
-
|
|
665
|
-
await act(async () => {
|
|
666
|
-
const response = await result.current.internal_fetchAIChatMessage(
|
|
667
|
-
messages,
|
|
668
|
-
assistantMessageId,
|
|
669
|
-
);
|
|
670
|
-
expect(response.content).toEqual(aiResponse);
|
|
671
|
-
expect(response.isFunctionCall).toEqual(true);
|
|
672
|
-
expect(response.functionCallAtEnd).toEqual(false);
|
|
673
|
-
expect(response.functionCallContent).toEqual('');
|
|
674
|
-
});
|
|
675
|
-
});
|
|
676
|
-
|
|
677
|
-
it('should handle function message at end of AI response', async () => {
|
|
678
|
-
const { result } = renderHook(() => useChatStore());
|
|
679
|
-
const messages = [{ id: 'message-id', content: 'Hello', role: 'user' }] as ChatMessage[];
|
|
680
|
-
const assistantMessageId = 'assistant-message-id';
|
|
681
|
-
const aiResponse =
|
|
682
|
-
'Hello, human! {"tool_calls":[{"id":"call_sbca","type":"function","function":{"name":"pluginName____apiName","arguments":{"key":"value"}}}]}';
|
|
683
|
-
|
|
684
|
-
// Mock fetch to resolve with AI response containing function call at end
|
|
685
|
-
vi.mocked(fetch).mockResolvedValue(new Response(aiResponse));
|
|
686
|
-
|
|
687
|
-
await act(async () => {
|
|
688
|
-
const response = await result.current.internal_fetchAIChatMessage(
|
|
689
|
-
messages,
|
|
690
|
-
assistantMessageId,
|
|
691
|
-
);
|
|
692
|
-
expect(response.content).toEqual(aiResponse);
|
|
693
|
-
expect(response.isFunctionCall).toEqual(true);
|
|
694
|
-
expect(response.functionCallAtEnd).toEqual(true);
|
|
695
|
-
expect(response.functionCallContent).toEqual(
|
|
696
|
-
'{"tool_calls":[{"id":"call_sbca","type":"function","function":{"name":"pluginName____apiName","arguments":{"key":"value"}}}]}',
|
|
697
|
-
);
|
|
698
646
|
});
|
|
699
647
|
});
|
|
700
648
|
|
|
@@ -708,9 +656,11 @@ describe('chatMessage actions', () => {
|
|
|
708
656
|
vi.mocked(fetch).mockRejectedValue(new Error(errorMessage));
|
|
709
657
|
|
|
710
658
|
await act(async () => {
|
|
711
|
-
|
|
712
|
-
result.current.internal_fetchAIChatMessage(messages, assistantMessageId),
|
|
713
|
-
).
|
|
659
|
+
expect(
|
|
660
|
+
await result.current.internal_fetchAIChatMessage(messages, assistantMessageId),
|
|
661
|
+
).toEqual({
|
|
662
|
+
isFunctionCall: false,
|
|
663
|
+
});
|
|
714
664
|
});
|
|
715
665
|
});
|
|
716
666
|
});
|