@lobehub/chat 0.21.0 → 0.22.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 +25 -0
- package/package.json +1 -1
- package/src/const/fetch.ts +2 -0
- package/src/pages/api/openai.api.ts +3 -1
- package/src/pages/api/openai.ts +26 -14
- package/src/pages/api/plugins.api.ts +27 -16
- package/src/pages/setting/SettingForm.tsx +2 -2
- package/src/services/chatModel.ts +9 -1
- package/src/store/settings/initialState.ts +2 -2
- package/src/types/exportConfig.ts +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,31 @@
|
|
|
2
2
|
|
|
3
3
|
# Changelog
|
|
4
4
|
|
|
5
|
+
## [Version 0.22.0](https://github.com/lobehub/lobe-chat/compare/v0.21.0...v0.22.0)
|
|
6
|
+
|
|
7
|
+
<sup>Released on **2023-07-25**</sup>
|
|
8
|
+
|
|
9
|
+
#### ✨ Features
|
|
10
|
+
|
|
11
|
+
- **misc**: 支持使用自定义 OpenAI Key.
|
|
12
|
+
|
|
13
|
+
<br/>
|
|
14
|
+
|
|
15
|
+
<details>
|
|
16
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
17
|
+
|
|
18
|
+
#### What's improved
|
|
19
|
+
|
|
20
|
+
- **misc**: 支持使用自定义 OpenAI Key, closes [#20](https://github.com/lobehub/lobe-chat/issues/20) ([fb454a0](https://github.com/lobehub/lobe-chat/commit/fb454a0))
|
|
21
|
+
|
|
22
|
+
</details>
|
|
23
|
+
|
|
24
|
+
<div align="right">
|
|
25
|
+
|
|
26
|
+
[](#readme-top)
|
|
27
|
+
|
|
28
|
+
</div>
|
|
29
|
+
|
|
5
30
|
## [Version 0.21.0](https://github.com/lobehub/lobe-chat/compare/v0.20.0...v0.21.0)
|
|
6
31
|
|
|
7
32
|
<sup>Released on **2023-07-25**</sup>
|
package/package.json
CHANGED
package/src/const/fetch.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { StreamingTextResponse } from 'ai';
|
|
2
2
|
|
|
3
|
+
import { OPENAI_API_KEY_HEADER_KEY } from '@/const/fetch';
|
|
3
4
|
import { OpenAIStreamPayload } from '@/types/openai';
|
|
4
5
|
|
|
5
6
|
import { createChatCompletion } from './openai';
|
|
@@ -8,8 +9,9 @@ export const runtime = 'edge';
|
|
|
8
9
|
|
|
9
10
|
export default async function handler(req: Request) {
|
|
10
11
|
const payload = (await req.json()) as OpenAIStreamPayload;
|
|
12
|
+
const apiKey = req.headers.get(OPENAI_API_KEY_HEADER_KEY);
|
|
11
13
|
|
|
12
|
-
const stream = await createChatCompletion(payload);
|
|
14
|
+
const stream = await createChatCompletion({ OPENAI_API_KEY: apiKey, payload });
|
|
13
15
|
|
|
14
16
|
return new StreamingTextResponse(stream);
|
|
15
17
|
}
|
package/src/pages/api/openai.ts
CHANGED
|
@@ -9,20 +9,30 @@ import pluginList from '../../plugins';
|
|
|
9
9
|
const isDev = process.env.NODE_ENV === 'development';
|
|
10
10
|
const OPENAI_PROXY_URL = process.env.OPENAI_PROXY_URL;
|
|
11
11
|
|
|
12
|
-
//
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
config,
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
payload: OpenAIStreamPayload
|
|
24
|
-
|
|
25
|
-
|
|
12
|
+
// 创建 OpenAI 实例
|
|
13
|
+
export const createOpenAI = (OPENAI_API_KEY: string | null) => {
|
|
14
|
+
const config = new Configuration({
|
|
15
|
+
apiKey: OPENAI_API_KEY ?? process.env.OPENAI_API_KEY,
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
return new OpenAIApi(config, isDev && OPENAI_PROXY_URL ? OPENAI_PROXY_URL : undefined);
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
interface CreateChatCompletionOptions {
|
|
22
|
+
OPENAI_API_KEY: string | null;
|
|
23
|
+
callbacks?: (payload: OpenAIStreamPayload) => OpenAIStreamCallbacks;
|
|
24
|
+
payload: OpenAIStreamPayload;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const createChatCompletion = async ({
|
|
28
|
+
payload,
|
|
29
|
+
callbacks,
|
|
30
|
+
OPENAI_API_KEY,
|
|
31
|
+
}: CreateChatCompletionOptions) => {
|
|
32
|
+
// ============ 0.创建 OpenAI 实例 ============ //
|
|
33
|
+
|
|
34
|
+
const openai = createOpenAI(OPENAI_API_KEY);
|
|
35
|
+
|
|
26
36
|
const { messages, plugins: enabledPlugins, ...params } = payload;
|
|
27
37
|
|
|
28
38
|
// ============ 1. 前置处理 functions ============ //
|
|
@@ -42,6 +52,8 @@ export const createChatCompletion = async (
|
|
|
42
52
|
// ============ 2. 前置处理 messages ============ //
|
|
43
53
|
const formatMessages = messages.map((m) => ({ content: m.content, role: m.role }));
|
|
44
54
|
|
|
55
|
+
// ============ 3. 发送请求 ============ //
|
|
56
|
+
|
|
45
57
|
const requestParams = { functions, messages: formatMessages, stream: true, ...params };
|
|
46
58
|
|
|
47
59
|
const response = await openai.createChatCompletion(requestParams);
|
|
@@ -1,36 +1,47 @@
|
|
|
1
1
|
import { StreamingTextResponse } from 'ai';
|
|
2
2
|
import { ChatCompletionRequestMessage } from 'openai-edge';
|
|
3
3
|
|
|
4
|
+
import { OPENAI_API_KEY_HEADER_KEY } from '@/const/fetch';
|
|
4
5
|
import { OpenAIStreamPayload } from '@/types/openai';
|
|
5
6
|
|
|
6
7
|
import pluginList from '../../plugins';
|
|
7
|
-
import { createChatCompletion,
|
|
8
|
+
import { createChatCompletion, createOpenAI } from './openai';
|
|
8
9
|
|
|
9
10
|
export const runtime = 'edge';
|
|
10
11
|
|
|
11
12
|
export default async function handler(req: Request) {
|
|
12
13
|
const payload = (await req.json()) as OpenAIStreamPayload;
|
|
14
|
+
const apiKey = req.headers.get(OPENAI_API_KEY_HEADER_KEY);
|
|
13
15
|
|
|
14
|
-
const
|
|
15
|
-
experimental_onFunctionCall: async ({ name, arguments: args }, createFunctionCallMessages) => {
|
|
16
|
-
console.log(`检测到 functionCall: ${name}`);
|
|
16
|
+
const openai = createOpenAI(apiKey);
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
const stream = await createChatCompletion({
|
|
19
|
+
OPENAI_API_KEY: apiKey,
|
|
20
|
+
callbacks: (payload) => ({
|
|
21
|
+
experimental_onFunctionCall: async (
|
|
22
|
+
{ name, arguments: args },
|
|
23
|
+
createFunctionCallMessages,
|
|
24
|
+
) => {
|
|
25
|
+
console.log(`检测到 functionCall: ${name}`);
|
|
19
26
|
|
|
20
|
-
|
|
21
|
-
const result = await func.runner(args as any);
|
|
27
|
+
const func = pluginList.find((f) => f.name === name);
|
|
22
28
|
|
|
23
|
-
|
|
29
|
+
if (func) {
|
|
30
|
+
const result = await func.runner(args as any);
|
|
24
31
|
|
|
25
|
-
|
|
32
|
+
console.log(`[${name}]`, args, `result:`, JSON.stringify(result, null, 2));
|
|
26
33
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
+
const newMessages = createFunctionCallMessages(result) as ChatCompletionRequestMessage[];
|
|
35
|
+
|
|
36
|
+
return openai.createChatCompletion({
|
|
37
|
+
...payload,
|
|
38
|
+
messages: [...payload.messages, ...newMessages],
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
}),
|
|
43
|
+
payload,
|
|
44
|
+
});
|
|
34
45
|
|
|
35
46
|
return new StreamingTextResponse(stream);
|
|
36
47
|
}
|
|
@@ -107,7 +107,7 @@ const SettingForm = memo(() => {
|
|
|
107
107
|
children: <Input.Password placeholder={t('settingOpenAI.token.placeholder')} />,
|
|
108
108
|
desc: t('settingOpenAI.token.desc'),
|
|
109
109
|
label: t('settingOpenAI.token.title'),
|
|
110
|
-
name: '
|
|
110
|
+
name: 'OPENAI_API_KEY',
|
|
111
111
|
},
|
|
112
112
|
{
|
|
113
113
|
children: <Input placeholder={t('settingOpenAI.endpoint.placeholder')} />,
|
|
@@ -236,7 +236,7 @@ const SettingForm = memo(() => {
|
|
|
236
236
|
children: <Input.Password placeholder={t('settingSystem.accessCode.placeholder')} />,
|
|
237
237
|
desc: t('settingSystem.accessCode.desc'),
|
|
238
238
|
label: t('settingSystem.accessCode.title'),
|
|
239
|
-
name: '
|
|
239
|
+
name: 'password',
|
|
240
240
|
},
|
|
241
241
|
{
|
|
242
242
|
children: (
|
|
@@ -1,16 +1,23 @@
|
|
|
1
1
|
import { merge } from 'lodash-es';
|
|
2
2
|
|
|
3
|
+
import { OPENAI_API_KEY_HEADER_KEY } from '@/const/fetch';
|
|
3
4
|
import { initialLobeAgentConfig } from '@/store/session';
|
|
5
|
+
import { useSettings } from '@/store/settings';
|
|
4
6
|
import type { OpenAIStreamPayload } from '@/types/openai';
|
|
5
7
|
|
|
6
8
|
import { URLS } from './url';
|
|
7
9
|
|
|
10
|
+
interface FetchChatModelOptions {
|
|
11
|
+
signal?: AbortSignal | undefined;
|
|
12
|
+
withPlugin?: boolean;
|
|
13
|
+
}
|
|
14
|
+
|
|
8
15
|
/**
|
|
9
16
|
* 专门用于对话的 fetch
|
|
10
17
|
*/
|
|
11
18
|
export const fetchChatModel = (
|
|
12
19
|
params: Partial<OpenAIStreamPayload>,
|
|
13
|
-
options?:
|
|
20
|
+
options?: FetchChatModelOptions,
|
|
14
21
|
) => {
|
|
15
22
|
const payload = merge(
|
|
16
23
|
{
|
|
@@ -25,6 +32,7 @@ export const fetchChatModel = (
|
|
|
25
32
|
body: JSON.stringify(payload),
|
|
26
33
|
headers: {
|
|
27
34
|
'Content-Type': 'application/json',
|
|
35
|
+
[OPENAI_API_KEY_HEADER_KEY]: useSettings.getState().settings.OPENAI_API_KEY || '',
|
|
28
36
|
},
|
|
29
37
|
method: 'POST',
|
|
30
38
|
signal: options?.signal,
|
|
@@ -4,7 +4,7 @@ import { LanguageModel } from '@/types/llm';
|
|
|
4
4
|
export type SidebarTabKey = 'chat' | 'market';
|
|
5
5
|
|
|
6
6
|
export const DEFAULT_SETTINGS: ConfigSettings = {
|
|
7
|
-
|
|
7
|
+
OPENAI_API_KEY: '',
|
|
8
8
|
avatar: '',
|
|
9
9
|
compressThreshold: 24,
|
|
10
10
|
enableCompressThreshold: false,
|
|
@@ -18,11 +18,11 @@ export const DEFAULT_SETTINGS: ConfigSettings = {
|
|
|
18
18
|
maxTokens: 2000,
|
|
19
19
|
model: LanguageModel.GPT3_5,
|
|
20
20
|
neutralColor: '',
|
|
21
|
+
password: '',
|
|
21
22
|
presencePenalty: 0,
|
|
22
23
|
primaryColor: '',
|
|
23
24
|
temperature: 0.5,
|
|
24
25
|
themeMode: 'auto',
|
|
25
|
-
token: '',
|
|
26
26
|
topP: 1,
|
|
27
27
|
};
|
|
28
28
|
|
|
@@ -9,7 +9,7 @@ import { Locales } from './locale';
|
|
|
9
9
|
* 配置设置
|
|
10
10
|
*/
|
|
11
11
|
export interface ConfigSettings {
|
|
12
|
-
|
|
12
|
+
OPENAI_API_KEY: string;
|
|
13
13
|
avatar: string;
|
|
14
14
|
compressThreshold: number;
|
|
15
15
|
enableCompressThreshold: boolean;
|
|
@@ -23,11 +23,11 @@ export interface ConfigSettings {
|
|
|
23
23
|
maxTokens: number;
|
|
24
24
|
model: LanguageModel;
|
|
25
25
|
neutralColor: NeutralColors | '';
|
|
26
|
+
password: string;
|
|
26
27
|
presencePenalty: number;
|
|
27
28
|
primaryColor: PrimaryColors | '';
|
|
28
29
|
temperature: number;
|
|
29
30
|
themeMode: ThemeMode;
|
|
30
|
-
token: string;
|
|
31
31
|
topP: number;
|
|
32
32
|
}
|
|
33
33
|
|