@lobehub/chat 1.20.8 → 1.21.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.
- package/CHANGELOG.md +42 -0
- package/Dockerfile +5 -2
- package/Dockerfile.database +5 -2
- package/locales/ar/error.json +1 -0
- package/locales/ar/modelProvider.json +20 -0
- package/locales/ar/models.json +73 -4
- package/locales/ar/providers.json +6 -0
- package/locales/bg-BG/error.json +1 -0
- package/locales/bg-BG/modelProvider.json +20 -0
- package/locales/bg-BG/models.json +73 -4
- package/locales/bg-BG/providers.json +6 -0
- package/locales/de-DE/error.json +1 -0
- package/locales/de-DE/modelProvider.json +20 -0
- package/locales/de-DE/models.json +73 -4
- package/locales/de-DE/providers.json +6 -0
- package/locales/en-US/error.json +1 -0
- package/locales/en-US/modelProvider.json +20 -0
- package/locales/en-US/models.json +73 -4
- package/locales/en-US/providers.json +6 -0
- package/locales/es-ES/error.json +1 -0
- package/locales/es-ES/modelProvider.json +20 -0
- package/locales/es-ES/models.json +73 -4
- package/locales/es-ES/providers.json +6 -0
- package/locales/fr-FR/error.json +1 -0
- package/locales/fr-FR/modelProvider.json +20 -0
- package/locales/fr-FR/models.json +73 -4
- package/locales/fr-FR/providers.json +6 -0
- package/locales/it-IT/error.json +1 -0
- package/locales/it-IT/modelProvider.json +20 -0
- package/locales/it-IT/models.json +73 -4
- package/locales/it-IT/providers.json +6 -0
- package/locales/ja-JP/error.json +1 -0
- package/locales/ja-JP/modelProvider.json +20 -0
- package/locales/ja-JP/models.json +73 -4
- package/locales/ja-JP/providers.json +6 -0
- package/locales/ko-KR/error.json +1 -0
- package/locales/ko-KR/modelProvider.json +20 -0
- package/locales/ko-KR/models.json +73 -4
- package/locales/ko-KR/providers.json +6 -0
- package/locales/nl-NL/error.json +1 -0
- package/locales/nl-NL/modelProvider.json +20 -0
- package/locales/nl-NL/models.json +73 -4
- package/locales/nl-NL/providers.json +6 -0
- package/locales/pl-PL/error.json +1 -0
- package/locales/pl-PL/modelProvider.json +20 -0
- package/locales/pl-PL/models.json +73 -4
- package/locales/pl-PL/providers.json +6 -0
- package/locales/pt-BR/error.json +1 -0
- package/locales/pt-BR/modelProvider.json +20 -0
- package/locales/pt-BR/models.json +73 -4
- package/locales/pt-BR/providers.json +6 -0
- package/locales/ru-RU/error.json +1 -0
- package/locales/ru-RU/modelProvider.json +20 -0
- package/locales/ru-RU/models.json +73 -4
- package/locales/ru-RU/providers.json +6 -0
- package/locales/tr-TR/error.json +1 -0
- package/locales/tr-TR/modelProvider.json +20 -0
- package/locales/tr-TR/models.json +73 -4
- package/locales/tr-TR/providers.json +6 -0
- package/locales/vi-VN/error.json +1 -0
- package/locales/vi-VN/modelProvider.json +20 -0
- package/locales/vi-VN/models.json +73 -4
- package/locales/vi-VN/providers.json +6 -0
- package/locales/zh-CN/error.json +1 -0
- package/locales/zh-CN/modelProvider.json +20 -0
- package/locales/zh-CN/models.json +76 -7
- package/locales/zh-CN/providers.json +6 -0
- package/locales/zh-TW/error.json +1 -0
- package/locales/zh-TW/modelProvider.json +20 -0
- package/locales/zh-TW/models.json +73 -4
- package/locales/zh-TW/providers.json +6 -0
- package/package.json +3 -2
- package/scripts/serverLauncher/startServer.js +10 -81
- package/src/app/(main)/settings/llm/ProviderList/Wenxin/index.tsx +46 -0
- package/src/app/(main)/settings/llm/ProviderList/providers.tsx +4 -1
- package/src/app/api/chat/agentRuntime.test.ts +21 -0
- package/src/app/api/chat/wenxin/route.test.ts +27 -0
- package/src/app/api/chat/wenxin/route.ts +30 -0
- package/src/app/api/errorResponse.ts +4 -0
- package/src/config/llm.ts +8 -0
- package/src/config/modelProviders/index.ts +4 -0
- package/src/config/modelProviders/wenxin.ts +159 -0
- package/src/const/auth.ts +4 -0
- package/src/const/settings/llm.ts +5 -0
- package/src/features/Conversation/Error/APIKeyForm/Wenxin.tsx +49 -0
- package/src/features/Conversation/Error/APIKeyForm/index.tsx +3 -0
- package/src/features/Conversation/Error/index.tsx +1 -0
- package/src/libs/agent-runtime/AgentRuntime.test.ts +1 -0
- package/src/libs/agent-runtime/error.ts +1 -0
- package/src/libs/agent-runtime/types/type.ts +1 -0
- package/src/libs/agent-runtime/utils/streams/wenxin.test.ts +149 -0
- package/src/libs/agent-runtime/utils/streams/wenxin.ts +46 -0
- package/src/libs/agent-runtime/wenxin/index.ts +106 -0
- package/src/libs/agent-runtime/wenxin/type.ts +84 -0
- package/src/locales/default/error.ts +2 -0
- package/src/locales/default/modelProvider.ts +20 -0
- package/src/server/globalConfig/index.ts +4 -1
- package/src/services/_auth.ts +14 -0
- package/src/store/user/slices/modelList/selectors/keyVaults.ts +2 -0
- package/src/types/user/settings/keyVaults.ts +6 -0
@@ -0,0 +1,149 @@
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest';
|
2
|
+
|
3
|
+
import * as uuidModule from '@/utils/uuid';
|
4
|
+
|
5
|
+
import { ChatResp } from '../../wenxin/type';
|
6
|
+
import { WenxinResultToStream, WenxinStream } from './wenxin';
|
7
|
+
|
8
|
+
const dataStream = [
|
9
|
+
{
|
10
|
+
id: 'as-vb0m37ti8y',
|
11
|
+
object: 'chat.completion',
|
12
|
+
created: 1709089502,
|
13
|
+
sentence_id: 0,
|
14
|
+
is_end: false,
|
15
|
+
is_truncated: false,
|
16
|
+
result: '当然可以,',
|
17
|
+
need_clear_history: false,
|
18
|
+
finish_reason: 'normal',
|
19
|
+
usage: { prompt_tokens: 5, completion_tokens: 2, total_tokens: 7 },
|
20
|
+
},
|
21
|
+
{
|
22
|
+
id: 'as-vb0m37ti8y',
|
23
|
+
object: 'chat.completion',
|
24
|
+
created: 1709089504,
|
25
|
+
sentence_id: 1,
|
26
|
+
is_end: false,
|
27
|
+
is_truncated: false,
|
28
|
+
result:
|
29
|
+
'以下是一些建议的自驾游路线,它们涵盖了各种不同的风景和文化体验:\n\n1. **西安-敦煌历史文化之旅**:\n\n\n\t* 路线:西安',
|
30
|
+
need_clear_history: false,
|
31
|
+
finish_reason: 'normal',
|
32
|
+
usage: { prompt_tokens: 5, completion_tokens: 2, total_tokens: 7 },
|
33
|
+
},
|
34
|
+
{
|
35
|
+
id: 'as-vb0m37ti8y',
|
36
|
+
object: 'chat.completion',
|
37
|
+
created: 1709089506,
|
38
|
+
sentence_id: 2,
|
39
|
+
is_end: false,
|
40
|
+
is_truncated: false,
|
41
|
+
result: ' - 天水 - 兰州 - 嘉峪关 - 敦煌\n\t* 特点:此路线让您领略到中国西北的丰富历史文化。',
|
42
|
+
need_clear_history: false,
|
43
|
+
finish_reason: 'normal',
|
44
|
+
usage: { prompt_tokens: 5, completion_tokens: 2, total_tokens: 7 },
|
45
|
+
},
|
46
|
+
{
|
47
|
+
id: 'as-vb0m37ti8y',
|
48
|
+
object: 'chat.completion',
|
49
|
+
created: 1709089508,
|
50
|
+
sentence_id: 3,
|
51
|
+
is_end: false,
|
52
|
+
is_truncated: false,
|
53
|
+
result: '您可以参观西安的兵马俑、大雁塔,体验兰州的黄河风情,以及在敦煌欣赏壮丽的莫高窟。',
|
54
|
+
need_clear_history: false,
|
55
|
+
finish_reason: 'normal',
|
56
|
+
usage: { prompt_tokens: 5, completion_tokens: 2, total_tokens: 7 },
|
57
|
+
},
|
58
|
+
{
|
59
|
+
id: 'as-vb0m37ti8y',
|
60
|
+
object: 'chat.completion',
|
61
|
+
created: 1709089511,
|
62
|
+
sentence_id: 4,
|
63
|
+
is_end: false,
|
64
|
+
is_truncated: false,
|
65
|
+
result: '\n2. **海南环岛热带风情游**:\n\n\n\t* 路线:海口 - 三亚 - 陵水 - 万宁 - 文昌 - 海',
|
66
|
+
need_clear_history: false,
|
67
|
+
finish_reason: 'normal',
|
68
|
+
usage: { prompt_tokens: 5, completion_tokens: 2, total_tokens: 7 },
|
69
|
+
},
|
70
|
+
{
|
71
|
+
id: 'as-vb0m37ti8y',
|
72
|
+
object: 'chat.completion',
|
73
|
+
created: 1709089512,
|
74
|
+
sentence_id: 5,
|
75
|
+
is_end: false,
|
76
|
+
is_truncated: false,
|
77
|
+
result:
|
78
|
+
'口\n\t* 特点:海南岛是中国唯一的黎族聚居区,这里有独特的热带风情、美丽的海滩和丰富的水果。',
|
79
|
+
need_clear_history: false,
|
80
|
+
finish_reason: 'normal',
|
81
|
+
usage: { prompt_tokens: 5, completion_tokens: 153, total_tokens: 158 },
|
82
|
+
},
|
83
|
+
];
|
84
|
+
|
85
|
+
describe('WenxinStream', () => {
|
86
|
+
it('should transform Wenxin stream to protocol stream', async () => {
|
87
|
+
vi.spyOn(uuidModule, 'nanoid').mockReturnValueOnce('1');
|
88
|
+
|
89
|
+
const mockWenxinStream: AsyncIterable<ChatResp> = {
|
90
|
+
// @ts-ignore
|
91
|
+
async *[Symbol.asyncIterator]() {
|
92
|
+
for (const item of dataStream) {
|
93
|
+
yield item;
|
94
|
+
}
|
95
|
+
},
|
96
|
+
};
|
97
|
+
|
98
|
+
const stream = WenxinResultToStream(mockWenxinStream);
|
99
|
+
|
100
|
+
const onStartMock = vi.fn();
|
101
|
+
const onTextMock = vi.fn();
|
102
|
+
const onTokenMock = vi.fn();
|
103
|
+
const onCompletionMock = vi.fn();
|
104
|
+
|
105
|
+
const protocolStream = WenxinStream(stream, {
|
106
|
+
onStart: onStartMock,
|
107
|
+
onText: onTextMock,
|
108
|
+
onToken: onTokenMock,
|
109
|
+
onCompletion: onCompletionMock,
|
110
|
+
});
|
111
|
+
|
112
|
+
const decoder = new TextDecoder();
|
113
|
+
const chunks = [];
|
114
|
+
|
115
|
+
// @ts-ignore
|
116
|
+
for await (const chunk of protocolStream) {
|
117
|
+
chunks.push(decoder.decode(chunk, { stream: true }));
|
118
|
+
}
|
119
|
+
|
120
|
+
expect(chunks).toEqual(
|
121
|
+
[
|
122
|
+
'id: as-vb0m37ti8y',
|
123
|
+
'event: text',
|
124
|
+
`data: "当然可以,"\n`,
|
125
|
+
'id: as-vb0m37ti8y',
|
126
|
+
'event: text',
|
127
|
+
`data: "以下是一些建议的自驾游路线,它们涵盖了各种不同的风景和文化体验:\\n\\n1. **西安-敦煌历史文化之旅**:\\n\\n\\n\\t* 路线:西安"\n`,
|
128
|
+
'id: as-vb0m37ti8y',
|
129
|
+
'event: text',
|
130
|
+
`data: " - 天水 - 兰州 - 嘉峪关 - 敦煌\\n\\t* 特点:此路线让您领略到中国西北的丰富历史文化。"\n`,
|
131
|
+
'id: as-vb0m37ti8y',
|
132
|
+
'event: text',
|
133
|
+
`data: "您可以参观西安的兵马俑、大雁塔,体验兰州的黄河风情,以及在敦煌欣赏壮丽的莫高窟。"\n`,
|
134
|
+
'id: as-vb0m37ti8y',
|
135
|
+
'event: text',
|
136
|
+
`data: "\\n2. **海南环岛热带风情游**:\\n\\n\\n\\t* 路线:海口 - 三亚 - 陵水 - 万宁 - 文昌 - 海"\n`,
|
137
|
+
'id: as-vb0m37ti8y',
|
138
|
+
'event: text',
|
139
|
+
`data: "口\\n\\t* 特点:海南岛是中国唯一的黎族聚居区,这里有独特的热带风情、美丽的海滩和丰富的水果。"\n`,
|
140
|
+
].map((item) => `${item}\n`),
|
141
|
+
);
|
142
|
+
|
143
|
+
expect(onStartMock).toHaveBeenCalledTimes(1);
|
144
|
+
expect(onTextMock).toHaveBeenNthCalledWith(1, '"当然可以,"');
|
145
|
+
expect(onTextMock).toHaveBeenNthCalledWith(2, '"以下是一些建议的自驾游路线,它们涵盖了各种不同的风景和文化体验:\\n\\n1. **西安-敦煌历史文化之旅**:\\n\\n\\n\\t* 路线:西安"');
|
146
|
+
expect(onTokenMock).toHaveBeenCalledTimes(6);
|
147
|
+
expect(onCompletionMock).toHaveBeenCalledTimes(1);
|
148
|
+
});
|
149
|
+
});
|
@@ -0,0 +1,46 @@
|
|
1
|
+
import { readableFromAsyncIterable } from 'ai';
|
2
|
+
|
3
|
+
import { ChatStreamCallbacks } from '@/libs/agent-runtime';
|
4
|
+
import { nanoid } from '@/utils/uuid';
|
5
|
+
|
6
|
+
import { ChatResp } from '../../wenxin/type';
|
7
|
+
import {
|
8
|
+
StreamProtocolChunk,
|
9
|
+
StreamStack,
|
10
|
+
chatStreamable,
|
11
|
+
createCallbacksTransformer,
|
12
|
+
createSSEProtocolTransformer,
|
13
|
+
} from './protocol';
|
14
|
+
|
15
|
+
const transformERNIEBotStream = (chunk: ChatResp): StreamProtocolChunk => {
|
16
|
+
const finished = chunk.is_end;
|
17
|
+
if (finished) {
|
18
|
+
return { data: chunk.finish_reason || 'stop', id: chunk.id, type: 'stop' };
|
19
|
+
}
|
20
|
+
|
21
|
+
if (chunk.result) {
|
22
|
+
return { data: chunk.result, id: chunk.id, type: 'text' };
|
23
|
+
}
|
24
|
+
|
25
|
+
return {
|
26
|
+
data: chunk,
|
27
|
+
id: chunk.id,
|
28
|
+
type: 'data',
|
29
|
+
};
|
30
|
+
};
|
31
|
+
|
32
|
+
export const WenxinResultToStream = (stream: AsyncIterable<ChatResp>) => {
|
33
|
+
// make the response to the streamable format
|
34
|
+
return readableFromAsyncIterable(chatStreamable(stream));
|
35
|
+
};
|
36
|
+
|
37
|
+
export const WenxinStream = (
|
38
|
+
rawStream: ReadableStream<ChatResp>,
|
39
|
+
callbacks?: ChatStreamCallbacks,
|
40
|
+
) => {
|
41
|
+
const streamStack: StreamStack = { id: 'chat_' + nanoid() };
|
42
|
+
|
43
|
+
return rawStream
|
44
|
+
.pipeThrough(createSSEProtocolTransformer(transformERNIEBotStream, streamStack))
|
45
|
+
.pipeThrough(createCallbacksTransformer(callbacks));
|
46
|
+
};
|
@@ -0,0 +1,106 @@
|
|
1
|
+
import { ChatCompletion } from '@baiducloud/qianfan';
|
2
|
+
|
3
|
+
// 如果引入了这个类型,那么在跑 type-check 的 tsc 检查中就会抛错,大无语
|
4
|
+
// import type QianFanClient from '@baiducloud/qianfan/src/ChatCompletion/index';
|
5
|
+
import { safeParseJSON } from '@/utils/safeParseJSON';
|
6
|
+
|
7
|
+
import { LobeRuntimeAI } from '../BaseAI';
|
8
|
+
import { AgentRuntimeErrorType } from '../error';
|
9
|
+
import { ChatCompetitionOptions, ChatStreamPayload } from '../types';
|
10
|
+
import { AgentRuntimeError } from '../utils/createError';
|
11
|
+
import { debugStream } from '../utils/debugStream';
|
12
|
+
import { StreamingResponse } from '../utils/response';
|
13
|
+
import { WenxinResultToStream, WenxinStream } from '../utils/streams/wenxin';
|
14
|
+
import { ChatResp } from './type';
|
15
|
+
|
16
|
+
interface ChatErrorCode {
|
17
|
+
error_code: number;
|
18
|
+
error_msg: string;
|
19
|
+
}
|
20
|
+
|
21
|
+
export interface LobeWenxinAIParams {
|
22
|
+
accessKey?: string;
|
23
|
+
baseURL?: string;
|
24
|
+
secretKey?: string;
|
25
|
+
}
|
26
|
+
|
27
|
+
export class LobeWenxinAI implements LobeRuntimeAI {
|
28
|
+
private client: any;
|
29
|
+
baseURL?: string;
|
30
|
+
|
31
|
+
constructor({ accessKey, baseURL, secretKey }: LobeWenxinAIParams = {}) {
|
32
|
+
if (!accessKey || !secretKey)
|
33
|
+
throw AgentRuntimeError.createError(AgentRuntimeErrorType.InvalidProviderAPIKey);
|
34
|
+
|
35
|
+
this.client = new ChatCompletion({
|
36
|
+
QIANFAN_ACCESS_KEY: accessKey,
|
37
|
+
QIANFAN_SECRET_KEY: secretKey,
|
38
|
+
});
|
39
|
+
this.baseURL = baseURL;
|
40
|
+
}
|
41
|
+
|
42
|
+
async chat(payload: ChatStreamPayload, options?: ChatCompetitionOptions) {
|
43
|
+
try {
|
44
|
+
const result = await this.client.chat(
|
45
|
+
{ messages: payload.messages as any, stream: true, user_id: options?.user },
|
46
|
+
payload.model,
|
47
|
+
);
|
48
|
+
|
49
|
+
const wenxinStream = WenxinResultToStream(result as AsyncIterable<ChatResp>);
|
50
|
+
|
51
|
+
const [prod, useForDebug] = wenxinStream.tee();
|
52
|
+
|
53
|
+
if (process.env.DEBUG_WENXIN_CHAT_COMPLETION === '1') {
|
54
|
+
debugStream(useForDebug).catch();
|
55
|
+
}
|
56
|
+
|
57
|
+
const stream = WenxinStream(prod, options?.callback);
|
58
|
+
|
59
|
+
// Respond with the stream
|
60
|
+
return StreamingResponse(stream, { headers: options?.headers });
|
61
|
+
} catch (e) {
|
62
|
+
const err = e as Error;
|
63
|
+
|
64
|
+
const error: ChatErrorCode | undefined = safeParseJSON(err.message);
|
65
|
+
|
66
|
+
if (!error) {
|
67
|
+
throw AgentRuntimeError.createError(AgentRuntimeErrorType.AgentRuntimeError, {
|
68
|
+
message: err.message,
|
69
|
+
name: err.name,
|
70
|
+
});
|
71
|
+
}
|
72
|
+
|
73
|
+
// 文心一言错误码
|
74
|
+
// https://cloud.baidu.com/doc/WENXINWORKSHOP/s/tlmyncueh
|
75
|
+
switch (error.error_code) {
|
76
|
+
// Invalid API key or access key
|
77
|
+
case 100:
|
78
|
+
case 13:
|
79
|
+
case 14: {
|
80
|
+
throw AgentRuntimeError.createError(AgentRuntimeErrorType.InvalidProviderAPIKey, error);
|
81
|
+
}
|
82
|
+
|
83
|
+
// quota limit
|
84
|
+
case 4:
|
85
|
+
case 17:
|
86
|
+
case 18:
|
87
|
+
case 19:
|
88
|
+
case 336_501:
|
89
|
+
case 336_502:
|
90
|
+
case 336_503:
|
91
|
+
case 336_504:
|
92
|
+
case 336_505:
|
93
|
+
case 336_507: {
|
94
|
+
throw AgentRuntimeError.createError(AgentRuntimeErrorType.QuotaLimitReached, {
|
95
|
+
errorCode: error.error_code,
|
96
|
+
message: `${error.error_msg} | you can visit https://cloud.baidu.com/doc/WENXINWORKSHOP/s/tlmyncueh for more information about the error code`,
|
97
|
+
});
|
98
|
+
}
|
99
|
+
}
|
100
|
+
|
101
|
+
throw AgentRuntimeError.createError(AgentRuntimeErrorType.ProviderBizError, error);
|
102
|
+
}
|
103
|
+
}
|
104
|
+
}
|
105
|
+
|
106
|
+
export default LobeWenxinAI;
|
@@ -0,0 +1,84 @@
|
|
1
|
+
/**
|
2
|
+
* token 用量基类
|
3
|
+
*/
|
4
|
+
export interface TokenUsage {
|
5
|
+
/**
|
6
|
+
* 回答tokens数
|
7
|
+
*/
|
8
|
+
completion_tokens?: number;
|
9
|
+
/**
|
10
|
+
* 问题tokens数
|
11
|
+
*/
|
12
|
+
prompt_tokens: number;
|
13
|
+
/**
|
14
|
+
* tokens总数
|
15
|
+
*/
|
16
|
+
total_tokens: number;
|
17
|
+
}
|
18
|
+
|
19
|
+
/**
|
20
|
+
* 响应基类
|
21
|
+
*/
|
22
|
+
export interface RespBase {
|
23
|
+
/**
|
24
|
+
* 时间戳
|
25
|
+
*/
|
26
|
+
created: number;
|
27
|
+
/**
|
28
|
+
* 本轮对话的id
|
29
|
+
*/
|
30
|
+
id: string;
|
31
|
+
/**
|
32
|
+
* 表示当前子句是否是最后一句。只有在流式接口模式下会返回该字段
|
33
|
+
*/
|
34
|
+
is_end?: boolean;
|
35
|
+
/**
|
36
|
+
* 1:表示输入内容无安全风险
|
37
|
+
* 0:表示输入内容有安全风险
|
38
|
+
*/
|
39
|
+
is_safe?: number;
|
40
|
+
/**
|
41
|
+
* 回包类型。
|
42
|
+
*
|
43
|
+
* chat.completion:多轮对话返回
|
44
|
+
*/
|
45
|
+
object: string;
|
46
|
+
/**
|
47
|
+
* 对话返回结果
|
48
|
+
*/
|
49
|
+
result: string;
|
50
|
+
/**
|
51
|
+
* 表示当前子句的序号。只有在流式接口模式下会返回该字段
|
52
|
+
*/
|
53
|
+
sentence_id?: number;
|
54
|
+
/**
|
55
|
+
* token统计信息,token数 = 汉字数+单词数*1.3 (仅为估算逻辑)
|
56
|
+
*/
|
57
|
+
usage: TokenUsage;
|
58
|
+
}
|
59
|
+
|
60
|
+
export interface ChatResp extends RespBase {
|
61
|
+
/**
|
62
|
+
* 当 need_clear_history 为 true 时,此字段会告知第几轮对话有敏感信息,如果是当前问题,ban_round=-1
|
63
|
+
*/
|
64
|
+
ban_round: number;
|
65
|
+
/**
|
66
|
+
* 输出内容标识,说明:
|
67
|
+
* · normal:输出内容完全由大模型生成,未触发截断、替换
|
68
|
+
* · stop:输出结果命中入参stop中指定的字段后被截断
|
69
|
+
* · length:达到了最大的token数,根据EB返回结果is_truncated来截断
|
70
|
+
* · content_filter:输出内容被截断、兜底、替换为**等
|
71
|
+
*/
|
72
|
+
finish_reason: string;
|
73
|
+
/**
|
74
|
+
* 当前生成的结果是否被截断
|
75
|
+
*/
|
76
|
+
is_truncated?: boolean;
|
77
|
+
/**
|
78
|
+
* 表示用户输入是否存在安全,是否关闭当前会话,清理历史会话信息
|
79
|
+
*
|
80
|
+
* true:是,表示用户输入存在安全风险,建议关闭当前会话,清理历史会话信息
|
81
|
+
* false:否,表示用户输入无安全风险
|
82
|
+
*/
|
83
|
+
need_clear_history: boolean;
|
84
|
+
}
|
@@ -79,6 +79,8 @@ export default {
|
|
79
79
|
InvalidClerkUser: '很抱歉,你当前尚未登录,请先登录或注册账号后继续操作',
|
80
80
|
LocationNotSupportError:
|
81
81
|
'很抱歉,你的所在地区不支持此模型服务,可能是由于区域限制或服务未开通。请确认当前地区是否支持使用此服务,或尝试使用切换到其他地区后重试。',
|
82
|
+
QuotaLimitReached:
|
83
|
+
'很抱歉,当前 Token 用量或请求次数已达该秘钥的配额(quota)上限,请增加该秘钥的配额或稍后再试',
|
82
84
|
|
83
85
|
InvalidProviderAPIKey: '{{provider}} API Key 不正确或为空,请检查 {{provider}} API Key 后重试',
|
84
86
|
ProviderBizError: '请求 {{provider}} 服务出错,请根据以下信息排查或重试',
|
@@ -115,6 +115,26 @@ export default {
|
|
115
115
|
title: '下载指定的 Ollama 模型',
|
116
116
|
},
|
117
117
|
},
|
118
|
+
wenxin: {
|
119
|
+
accessKey: {
|
120
|
+
desc: '填入百度千帆平台的 Access Key',
|
121
|
+
placeholder: 'Qianfan Access Key',
|
122
|
+
title: 'Access Key',
|
123
|
+
},
|
124
|
+
checker: {
|
125
|
+
desc: '测试 AccessKey / SecretAccess 是否填写正确',
|
126
|
+
},
|
127
|
+
secretKey: {
|
128
|
+
desc: '填入百度千帆平台 Secret Key',
|
129
|
+
placeholder: 'Qianfan Secret Key',
|
130
|
+
title: 'Secret Key',
|
131
|
+
},
|
132
|
+
unlock: {
|
133
|
+
customRegion: '自定义服务区域',
|
134
|
+
description: '输入你的 AccessKey / SecretKey 即可开始会话。应用不会记录你的鉴权配置',
|
135
|
+
title: '使用自定义文心一言鉴权信息',
|
136
|
+
},
|
137
|
+
},
|
118
138
|
zeroone: {
|
119
139
|
title: '01.AI 零一万物',
|
120
140
|
},
|
@@ -52,7 +52,7 @@ export const getServerGlobalConfig = () => {
|
|
52
52
|
|
53
53
|
ENABLED_HUNYUAN,
|
54
54
|
HUNYUAN_MODEL_LIST,
|
55
|
-
|
55
|
+
|
56
56
|
ENABLED_DEEPSEEK,
|
57
57
|
ENABLED_PERPLEXITY,
|
58
58
|
ENABLED_ANTHROPIC,
|
@@ -96,6 +96,8 @@ export const getServerGlobalConfig = () => {
|
|
96
96
|
|
97
97
|
ENABLED_FIREWORKSAI,
|
98
98
|
FIREWORKSAI_MODEL_LIST,
|
99
|
+
|
100
|
+
ENABLED_WENXIN,
|
99
101
|
} = getLLMConfig();
|
100
102
|
|
101
103
|
const config: GlobalServerConfig = {
|
@@ -239,6 +241,7 @@ export const getServerGlobalConfig = () => {
|
|
239
241
|
}),
|
240
242
|
},
|
241
243
|
upstage: { enabled: ENABLED_UPSTAGE },
|
244
|
+
wenxin: { enabled: ENABLED_WENXIN },
|
242
245
|
zeroone: {
|
243
246
|
enabled: ENABLED_ZEROONE,
|
244
247
|
enabledModels: extractEnabledModels(ZEROONE_MODEL_LIST),
|
package/src/services/_auth.ts
CHANGED
@@ -25,6 +25,20 @@ export const getProviderAuthPayload = (provider: string) => {
|
|
25
25
|
};
|
26
26
|
}
|
27
27
|
|
28
|
+
case ModelProvider.Wenxin: {
|
29
|
+
const { secretKey, accessKey } = keyVaultsConfigSelectors.wenxinConfig(
|
30
|
+
useUserStore.getState(),
|
31
|
+
);
|
32
|
+
|
33
|
+
const apiKey = (accessKey || '') + (secretKey || '');
|
34
|
+
|
35
|
+
return {
|
36
|
+
apiKey,
|
37
|
+
wenxinAccessKey: accessKey,
|
38
|
+
wenxinSecretKey: secretKey,
|
39
|
+
};
|
40
|
+
}
|
41
|
+
|
28
42
|
case ModelProvider.Azure: {
|
29
43
|
const azure = keyVaultsConfigSelectors.azureConfig(useUserStore.getState());
|
30
44
|
|
@@ -14,6 +14,7 @@ export const keyVaultsSettings = (s: UserStore): UserKeyVaults =>
|
|
14
14
|
|
15
15
|
const openAIConfig = (s: UserStore) => keyVaultsSettings(s).openai || {};
|
16
16
|
const bedrockConfig = (s: UserStore) => keyVaultsSettings(s).bedrock || {};
|
17
|
+
const wenxinConfig = (s: UserStore) => keyVaultsSettings(s).wenxin || {};
|
17
18
|
const ollamaConfig = (s: UserStore) => keyVaultsSettings(s).ollama || {};
|
18
19
|
const azureConfig = (s: UserStore) => keyVaultsSettings(s).azure || {};
|
19
20
|
const getVaultByProvider = (provider: GlobalLLMProviderKey) => (s: UserStore) =>
|
@@ -42,4 +43,5 @@ export const keyVaultsConfigSelectors = {
|
|
42
43
|
ollamaConfig,
|
43
44
|
openAIConfig,
|
44
45
|
password,
|
46
|
+
wenxinConfig,
|
45
47
|
};
|
@@ -16,6 +16,11 @@ export interface AWSBedrockKeyVault {
|
|
16
16
|
sessionToken?: string;
|
17
17
|
}
|
18
18
|
|
19
|
+
export interface WenxinKeyVault {
|
20
|
+
accessKey?: string;
|
21
|
+
secretKey?: string;
|
22
|
+
}
|
23
|
+
|
19
24
|
export interface UserKeyVaults {
|
20
25
|
ai21?: OpenAICompatibleKeyVault;
|
21
26
|
ai360?: OpenAICompatibleKeyVault;
|
@@ -46,6 +51,7 @@ export interface UserKeyVaults {
|
|
46
51
|
taichu?: OpenAICompatibleKeyVault;
|
47
52
|
togetherai?: OpenAICompatibleKeyVault;
|
48
53
|
upstage?: OpenAICompatibleKeyVault;
|
54
|
+
wenxin?: WenxinKeyVault;
|
49
55
|
zeroone?: OpenAICompatibleKeyVault;
|
50
56
|
zhipu?: OpenAICompatibleKeyVault;
|
51
57
|
}
|