@lobehub/chat 0.10.1 → 0.11.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 +68 -0
- package/README.md +1 -3
- package/next.config.mjs +4 -3
- package/package.json +1 -2
- package/public/manifest.json +3 -6
- package/src/locales/default/common.ts +1 -0
- package/src/pages/_app.page.tsx +6 -8
- package/src/pages/api/openai.api.ts +28 -4
- package/src/pages/api/plugins/index.ts +3 -0
- package/src/pages/api/plugins/weather/index.ts +20 -0
- package/src/pages/api/plugins/weather/runner.ts +30 -0
- package/src/pages/chat/SessionList/List/SessionItem.tsx +52 -20
- package/src/pages/chat/[id]/Config/ReadMode.tsx +6 -5
- package/src/services/url.ts +0 -1
- package/src/pages/api/LangChainStream.ts +0 -95
- package/src/pages/api/chain.api.ts +0 -17
- package/src/services/langChain.ts +0 -18
- package/src/types/langchain.ts +0 -34
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,74 @@
|
|
|
2
2
|
|
|
3
3
|
# Changelog
|
|
4
4
|
|
|
5
|
+
## [Version 0.11.0](https://github.com/lobehub/lobe-chat/compare/v0.10.2...v0.11.0)
|
|
6
|
+
|
|
7
|
+
<sup>Released on **2023-07-23**</sup>
|
|
8
|
+
|
|
9
|
+
#### ♻ Code Refactoring
|
|
10
|
+
|
|
11
|
+
- **misc**: Remove langchain, 优化代码.
|
|
12
|
+
|
|
13
|
+
#### ✨ Features
|
|
14
|
+
|
|
15
|
+
- **misc**: 支持查询天气.
|
|
16
|
+
|
|
17
|
+
#### 💄 Styles
|
|
18
|
+
|
|
19
|
+
- **misc**: Update manifest, 增加国际化文案.
|
|
20
|
+
|
|
21
|
+
<br/>
|
|
22
|
+
|
|
23
|
+
<details>
|
|
24
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
25
|
+
|
|
26
|
+
#### Code refactoring
|
|
27
|
+
|
|
28
|
+
- **misc**: Remove langchain ([7b0f96c](https://github.com/lobehub/lobe-chat/commit/7b0f96c))
|
|
29
|
+
- **misc**: 优化代码 ([6a8f7df](https://github.com/lobehub/lobe-chat/commit/6a8f7df))
|
|
30
|
+
|
|
31
|
+
#### What's improved
|
|
32
|
+
|
|
33
|
+
- **misc**: 支持查询天气 ([34bf285](https://github.com/lobehub/lobe-chat/commit/34bf285))
|
|
34
|
+
|
|
35
|
+
#### Styles
|
|
36
|
+
|
|
37
|
+
- **misc**: Update manifest ([ea9e8de](https://github.com/lobehub/lobe-chat/commit/ea9e8de))
|
|
38
|
+
- **misc**: 增加国际化文案 ([f5e8d7c](https://github.com/lobehub/lobe-chat/commit/f5e8d7c))
|
|
39
|
+
|
|
40
|
+
</details>
|
|
41
|
+
|
|
42
|
+
<div align="right">
|
|
43
|
+
|
|
44
|
+
[](#readme-top)
|
|
45
|
+
|
|
46
|
+
</div>
|
|
47
|
+
|
|
48
|
+
### [Version 0.10.2](https://github.com/lobehub/lobe-chat/compare/v0.10.1...v0.10.2)
|
|
49
|
+
|
|
50
|
+
<sup>Released on **2023-07-23**</sup>
|
|
51
|
+
|
|
52
|
+
#### 💄 Styles
|
|
53
|
+
|
|
54
|
+
- **misc**: 优化模型在 list 中的展示逻辑.
|
|
55
|
+
|
|
56
|
+
<br/>
|
|
57
|
+
|
|
58
|
+
<details>
|
|
59
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
60
|
+
|
|
61
|
+
#### Styles
|
|
62
|
+
|
|
63
|
+
- **misc**: 优化模型在 list 中的展示逻辑 ([4bdf3c5](https://github.com/lobehub/lobe-chat/commit/4bdf3c5))
|
|
64
|
+
|
|
65
|
+
</details>
|
|
66
|
+
|
|
67
|
+
<div align="right">
|
|
68
|
+
|
|
69
|
+
[](#readme-top)
|
|
70
|
+
|
|
71
|
+
</div>
|
|
72
|
+
|
|
5
73
|
### [Version 0.10.1](https://github.com/lobehub/lobe-chat/compare/v0.10.0...v0.10.1)
|
|
6
74
|
|
|
7
75
|
<sup>Released on **2023-07-22**</sup>
|
package/README.md
CHANGED
|
@@ -8,9 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
<h1>Lobe Chat</h1>
|
|
10
10
|
|
|
11
|
-
Lobe Chat is an open-source chatbot client using
|
|
12
|
-
|
|
13
|
-
Typescript and Next.js
|
|
11
|
+
Lobe Chat is an open-source chatbot client using OpenAI and Next.js
|
|
14
12
|
|
|
15
13
|
[Changelog](./CHANGELOG.md) · [Report Bug][issues-url] · [Request Feature][issues-url]
|
|
16
14
|
|
package/next.config.mjs
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import nextPWA from 'next-pwa';
|
|
2
2
|
|
|
3
|
+
const isProd = process.env.NODE_ENV === 'production';
|
|
4
|
+
const API_END_PORT_URL = process.env.API_END_PORT_URL || '';
|
|
5
|
+
|
|
3
6
|
const withPWA = nextPWA({
|
|
4
7
|
dest: 'public',
|
|
5
8
|
register: true,
|
|
6
9
|
skipWaiting: true,
|
|
7
|
-
disable: process.env.NODE_ENV === 'development',
|
|
8
10
|
});
|
|
9
11
|
|
|
10
|
-
const API_END_PORT_URL = process.env.API_END_PORT_URL || '';
|
|
11
12
|
/** @type {import('next').NextConfig} */
|
|
12
13
|
const nextConfig = {
|
|
13
14
|
reactStrictMode: true,
|
|
@@ -37,4 +38,4 @@ const nextConfig = {
|
|
|
37
38
|
},
|
|
38
39
|
};
|
|
39
40
|
|
|
40
|
-
export default withPWA(nextConfig);
|
|
41
|
+
export default isProd ? withPWA(nextConfig) : nextConfig;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lobehub/chat",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"description": "Lobe Chat is an open-source chatbot client using LangChain, Typescript and Next.js",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"chatbot",
|
|
@@ -76,7 +76,6 @@
|
|
|
76
76
|
"gpt-tokenizer": "^2",
|
|
77
77
|
"i18next": "^23",
|
|
78
78
|
"immer": "^10",
|
|
79
|
-
"langchain": "latest",
|
|
80
79
|
"lodash-es": "^4",
|
|
81
80
|
"lucide-react": "latest",
|
|
82
81
|
"nanoid": "^4",
|
package/public/manifest.json
CHANGED
|
@@ -5,20 +5,17 @@
|
|
|
5
5
|
{
|
|
6
6
|
"src": "icons/icon-72x72.png",
|
|
7
7
|
"sizes": "72x72",
|
|
8
|
-
"type": "image/png"
|
|
9
|
-
"purpose": "any maskable"
|
|
8
|
+
"type": "image/png"
|
|
10
9
|
},
|
|
11
10
|
{
|
|
12
11
|
"src": "icons/icon-96x96.png",
|
|
13
12
|
"sizes": "96x96",
|
|
14
|
-
"type": "image/png"
|
|
15
|
-
"purpose": "any maskable"
|
|
13
|
+
"type": "image/png"
|
|
16
14
|
},
|
|
17
15
|
{
|
|
18
16
|
"src": "icons/icon-192x192.png",
|
|
19
17
|
"sizes": "192x192",
|
|
20
|
-
"type": "image/png"
|
|
21
|
-
"purpose": "any maskable"
|
|
18
|
+
"type": "image/png"
|
|
22
19
|
}
|
|
23
20
|
],
|
|
24
21
|
"name": "LobeChat",
|
package/src/pages/_app.page.tsx
CHANGED
|
@@ -3,11 +3,9 @@ import type { AppProps } from 'next/app';
|
|
|
3
3
|
|
|
4
4
|
import Layout from '@/layout';
|
|
5
5
|
|
|
6
|
-
export default ({ Component, pageProps }: AppProps) =>
|
|
7
|
-
|
|
8
|
-
<
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
);
|
|
13
|
-
};
|
|
6
|
+
export default ({ Component, pageProps }: AppProps) => (
|
|
7
|
+
<Layout>
|
|
8
|
+
<Component {...pageProps} />
|
|
9
|
+
<Analytics />
|
|
10
|
+
</Layout>
|
|
11
|
+
);
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
import { OpenAIStream, StreamingTextResponse } from 'ai';
|
|
2
2
|
import { Configuration, OpenAIApi } from 'openai-edge';
|
|
3
|
+
import { ChatCompletionFunctions, ChatCompletionRequestMessage } from 'openai-edge/types/api';
|
|
3
4
|
|
|
4
5
|
import { OpenAIStreamPayload } from '@/types/openai';
|
|
5
6
|
|
|
7
|
+
import { plugins } from './plugins';
|
|
8
|
+
|
|
9
|
+
export const runtime = 'edge';
|
|
10
|
+
|
|
6
11
|
const isDev = process.env.NODE_ENV === 'development';
|
|
7
12
|
const OPENAI_PROXY_URL = process.env.OPENAI_PROXY_URL;
|
|
8
13
|
|
|
@@ -13,19 +18,38 @@ const config = new Configuration({
|
|
|
13
18
|
|
|
14
19
|
const openai = new OpenAIApi(config, isDev && OPENAI_PROXY_URL ? OPENAI_PROXY_URL : undefined);
|
|
15
20
|
|
|
16
|
-
|
|
21
|
+
const functions: ChatCompletionFunctions[] = plugins.map((f) => f.schema);
|
|
17
22
|
|
|
18
23
|
export default async function handler(req: Request) {
|
|
19
24
|
// Extract the `messages` from the body of the request
|
|
20
25
|
const { messages, ...params } = (await req.json()) as OpenAIStreamPayload;
|
|
21
26
|
|
|
22
|
-
|
|
27
|
+
const formatMessages = messages.map((m) => ({ content: m.content, role: m.role }));
|
|
28
|
+
|
|
23
29
|
const response = await openai.createChatCompletion({
|
|
30
|
+
functions,
|
|
31
|
+
messages: formatMessages,
|
|
24
32
|
stream: true,
|
|
25
33
|
...params,
|
|
26
|
-
messages: messages.map((m) => ({ content: m.content, role: m.role })),
|
|
27
34
|
});
|
|
28
35
|
|
|
29
|
-
const stream = OpenAIStream(response
|
|
36
|
+
const stream = OpenAIStream(response, {
|
|
37
|
+
experimental_onFunctionCall: async ({ name, arguments: args }, createFunctionCallMessages) => {
|
|
38
|
+
const func = plugins.find((f) => f.name === name);
|
|
39
|
+
|
|
40
|
+
if (func) {
|
|
41
|
+
const result = await func.runner(args as any);
|
|
42
|
+
|
|
43
|
+
const newMessages = createFunctionCallMessages(result) as ChatCompletionRequestMessage[];
|
|
44
|
+
|
|
45
|
+
return openai.createChatCompletion({
|
|
46
|
+
functions,
|
|
47
|
+
messages: [...formatMessages, ...newMessages],
|
|
48
|
+
stream: true,
|
|
49
|
+
...params,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
});
|
|
30
54
|
return new StreamingTextResponse(stream);
|
|
31
55
|
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import runner from './runner';
|
|
2
|
+
|
|
3
|
+
const schema = {
|
|
4
|
+
description: '获取当前天气情况',
|
|
5
|
+
name: 'fetchWeather',
|
|
6
|
+
parameters: {
|
|
7
|
+
properties: {
|
|
8
|
+
city: {
|
|
9
|
+
description: '城市名称',
|
|
10
|
+
type: 'string',
|
|
11
|
+
},
|
|
12
|
+
},
|
|
13
|
+
required: ['city'],
|
|
14
|
+
type: 'object',
|
|
15
|
+
},
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const getWeather = { name: 'fetchWeather', runner, schema };
|
|
19
|
+
|
|
20
|
+
export default getWeather;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
const weatherBaseURL = 'https://restapi.amap.com/v3/weather/weatherInfo';
|
|
2
|
+
|
|
3
|
+
const citySearchURL = 'https://restapi.amap.com/v3/config/district';
|
|
4
|
+
|
|
5
|
+
const KEY = process.env.GAODE_WEATHER_KEY;
|
|
6
|
+
|
|
7
|
+
interface WeatherParams {
|
|
8
|
+
city: string;
|
|
9
|
+
extensions?: 'base' | 'all';
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const fetchCityCode = async (keywords: string): Promise<string> => {
|
|
13
|
+
const URL = `${citySearchURL}?keywords=${keywords}&subdistrict=0&extensions=base&key=${KEY}`;
|
|
14
|
+
const res = await fetch(URL);
|
|
15
|
+
|
|
16
|
+
const data = await res.json();
|
|
17
|
+
|
|
18
|
+
return data.districts[0].adcode;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const fetchWeather = async ({ city, extensions = 'all' }: WeatherParams) => {
|
|
22
|
+
const cityCode = await fetchCityCode(city);
|
|
23
|
+
|
|
24
|
+
const URL = `${weatherBaseURL}?city=${cityCode}&extensions=${extensions}&key=${KEY}`;
|
|
25
|
+
const res = await fetch(URL);
|
|
26
|
+
|
|
27
|
+
return await res.json();
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export default fetchWeather;
|
|
@@ -6,8 +6,9 @@ import { useTranslation } from 'react-i18next';
|
|
|
6
6
|
import { Flexbox } from 'react-layout-kit';
|
|
7
7
|
import { shallow } from 'zustand/shallow';
|
|
8
8
|
|
|
9
|
-
import { sessionSelectors, useSessionStore } from '@/store/session';
|
|
9
|
+
import { chatSelectors, sessionSelectors, useSessionStore } from '@/store/session';
|
|
10
10
|
import { DEFAULT_TITLE } from '@/store/session/slices/agentConfig';
|
|
11
|
+
import { useSettings } from '@/store/settings';
|
|
11
12
|
|
|
12
13
|
import { useStyles } from './style';
|
|
13
14
|
|
|
@@ -22,22 +23,37 @@ interface SessionItemProps {
|
|
|
22
23
|
const SessionItem: FC<SessionItemProps> = memo(({ id, active = true, loading }) => {
|
|
23
24
|
const { t } = useTranslation('common');
|
|
24
25
|
const { styles, theme, cx } = useStyles();
|
|
25
|
-
const [
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
26
|
+
const [defaultModel] = useSettings((s) => [s.settings.model], shallow);
|
|
27
|
+
|
|
28
|
+
const [
|
|
29
|
+
title,
|
|
30
|
+
description,
|
|
31
|
+
systemRole,
|
|
32
|
+
avatar,
|
|
33
|
+
avatarBackground,
|
|
34
|
+
updateAt,
|
|
35
|
+
model,
|
|
36
|
+
chatLength,
|
|
37
|
+
removeSession,
|
|
38
|
+
] = useSessionStore((s) => {
|
|
39
|
+
const session = sessionSelectors.getSessionById(id)(s);
|
|
40
|
+
const meta = session.meta;
|
|
41
|
+
const systemRole = session.config.systemRole;
|
|
42
|
+
return [
|
|
43
|
+
meta.title,
|
|
44
|
+
meta.description,
|
|
45
|
+
systemRole,
|
|
46
|
+
sessionSelectors.getAgentAvatar(meta),
|
|
47
|
+
meta.backgroundColor,
|
|
48
|
+
session?.updateAt,
|
|
49
|
+
session.config.model,
|
|
50
|
+
chatSelectors.currentChats(s).length,
|
|
51
|
+
s.removeSession,
|
|
52
|
+
];
|
|
53
|
+
}, shallow);
|
|
54
|
+
|
|
55
|
+
const showModel = model !== defaultModel;
|
|
56
|
+
const showChatLength = chatLength > 0;
|
|
41
57
|
|
|
42
58
|
return (
|
|
43
59
|
<Flexbox className={styles.container} style={{ position: 'relative' }}>
|
|
@@ -57,9 +73,25 @@ const SessionItem: FC<SessionItemProps> = memo(({ id, active = true, loading })
|
|
|
57
73
|
description={
|
|
58
74
|
<Flexbox gap={4}>
|
|
59
75
|
<Flexbox>{description || systemRole}</Flexbox>
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
76
|
+
|
|
77
|
+
{!(showModel || showChatLength) ? undefined : (
|
|
78
|
+
<Flexbox horizontal>
|
|
79
|
+
{showModel && (
|
|
80
|
+
<Tag bordered={false} style={{ color: theme.colorTextSecondary }}>
|
|
81
|
+
{model}
|
|
82
|
+
</Tag>
|
|
83
|
+
)}
|
|
84
|
+
{/*{showChatLength && (*/}
|
|
85
|
+
{/* <Tag*/}
|
|
86
|
+
{/* bordered={false}*/}
|
|
87
|
+
{/* style={{ color: theme.colorTextSecondary, display: 'flex', gap: 4 }}*/}
|
|
88
|
+
{/* >*/}
|
|
89
|
+
{/* <Icon icon={LucideMessageCircle} />*/}
|
|
90
|
+
{/* {chatLength}*/}
|
|
91
|
+
{/* </Tag>*/}
|
|
92
|
+
{/*)}*/}
|
|
93
|
+
</Flexbox>
|
|
94
|
+
)}
|
|
63
95
|
</Flexbox>
|
|
64
96
|
}
|
|
65
97
|
loading={loading}
|
|
@@ -3,6 +3,7 @@ import { createStyles } from 'antd-style';
|
|
|
3
3
|
import isEqual from 'fast-deep-equal';
|
|
4
4
|
import { LucideBrain, LucideThermometer, WholeWord } from 'lucide-react';
|
|
5
5
|
import { memo } from 'react';
|
|
6
|
+
import { useTranslation } from 'react-i18next';
|
|
6
7
|
import { Center, Flexbox } from 'react-layout-kit';
|
|
7
8
|
import { shallow } from 'zustand/shallow';
|
|
8
9
|
|
|
@@ -31,26 +32,26 @@ const ReadMode = memo(() => {
|
|
|
31
32
|
const title = useSessionStore(agentSelectors.currentAgentTitle, shallow);
|
|
32
33
|
const model = useSessionStore(agentSelectors.currentAgentModel, shallow);
|
|
33
34
|
|
|
35
|
+
const { t } = useTranslation('common');
|
|
34
36
|
return (
|
|
35
|
-
<Center gap={12}
|
|
37
|
+
<Center gap={12} paddingBlock={16} style={{ marginTop: 8 }}>
|
|
36
38
|
<Avatar avatar={avatar} size={100} />
|
|
37
39
|
<Flexbox className={styles.title}>{title || DEFAULT_TITLE}</Flexbox>
|
|
38
40
|
<Flexbox className={styles.model}>{model}</Flexbox>
|
|
39
41
|
<Flexbox className={styles.desc}>{session.meta.description}</Flexbox>
|
|
40
42
|
|
|
41
43
|
<Flexbox flex={1} gap={12} width={'100%'}>
|
|
42
|
-
<ConfigCell icon={LucideBrain} label={'
|
|
43
|
-
|
|
44
|
+
<ConfigCell icon={LucideBrain} label={t('agentPrompt')} />
|
|
44
45
|
<ConfigCellGroup
|
|
45
46
|
items={[
|
|
46
47
|
{
|
|
47
48
|
icon: LucideThermometer,
|
|
48
|
-
label: '
|
|
49
|
+
label: t('modelTemperature'),
|
|
49
50
|
value: session.config.params.temperature,
|
|
50
51
|
},
|
|
51
52
|
{
|
|
52
53
|
icon: WholeWord,
|
|
53
|
-
label: '
|
|
54
|
+
label: t('agentMaxToken'),
|
|
54
55
|
value: session.config.params.max_tokens,
|
|
55
56
|
},
|
|
56
57
|
]}
|
package/src/services/url.ts
CHANGED
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
import { LLMChain } from 'langchain/chains';
|
|
2
|
-
import { ChatOpenAI } from 'langchain/chat_models/openai';
|
|
3
|
-
import {
|
|
4
|
-
AIMessagePromptTemplate,
|
|
5
|
-
ChatPromptTemplate,
|
|
6
|
-
HumanMessagePromptTemplate,
|
|
7
|
-
SystemMessagePromptTemplate,
|
|
8
|
-
} from 'langchain/prompts';
|
|
9
|
-
|
|
10
|
-
import { LangChainParams } from '@/types/langchain';
|
|
11
|
-
|
|
12
|
-
const isDev = process.env.NODE_ENV === 'development';
|
|
13
|
-
const OPENAI_PROXY_URL = process.env.OPENAI_PROXY_URL;
|
|
14
|
-
|
|
15
|
-
export function LangChainStream(payload: LangChainParams) {
|
|
16
|
-
const { prompts, vars, llm } = payload;
|
|
17
|
-
|
|
18
|
-
// 将 payload 中的消息转换为 ChatOpenAI 所需的 HumanChatMessage、SystemChatMessage 和 AIChatMessage 类型
|
|
19
|
-
const chatPrompt = ChatPromptTemplate.fromPromptMessages(
|
|
20
|
-
prompts.map((m) => {
|
|
21
|
-
switch (m.role) {
|
|
22
|
-
default:
|
|
23
|
-
case 'user': {
|
|
24
|
-
return HumanMessagePromptTemplate.fromTemplate(m.content);
|
|
25
|
-
}
|
|
26
|
-
case 'system': {
|
|
27
|
-
return SystemMessagePromptTemplate.fromTemplate(m.content);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
case 'assistant': {
|
|
31
|
-
return AIMessagePromptTemplate.fromTemplate(m.content);
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
}),
|
|
35
|
-
);
|
|
36
|
-
|
|
37
|
-
// 使用 TextEncoder 将字符串转换为字节数组,以便在 ReadableStream 中发送
|
|
38
|
-
const encoder = new TextEncoder();
|
|
39
|
-
|
|
40
|
-
// 初始化换行符计数器
|
|
41
|
-
|
|
42
|
-
return new ReadableStream({
|
|
43
|
-
async start(controller) {
|
|
44
|
-
let newlineCounter = 0;
|
|
45
|
-
|
|
46
|
-
const chat = new ChatOpenAI(
|
|
47
|
-
{
|
|
48
|
-
streaming: true,
|
|
49
|
-
...llm,
|
|
50
|
-
|
|
51
|
-
callbacks: [
|
|
52
|
-
{
|
|
53
|
-
handleLLMNewToken(token) {
|
|
54
|
-
// 如果 message 是换行符,且 newlineCounter 小于 2,那么跳过该换行符
|
|
55
|
-
if (newlineCounter < 2 && token === '\n') {
|
|
56
|
-
return;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// 将 message 编码为字节并添加到流中
|
|
60
|
-
const queue = encoder.encode(token);
|
|
61
|
-
controller.enqueue(queue);
|
|
62
|
-
newlineCounter++;
|
|
63
|
-
},
|
|
64
|
-
},
|
|
65
|
-
],
|
|
66
|
-
// 暂时设定不重试 ,后续看是否需要支持重试
|
|
67
|
-
maxRetries: 0,
|
|
68
|
-
},
|
|
69
|
-
isDev && OPENAI_PROXY_URL ? { basePath: OPENAI_PROXY_URL } : undefined,
|
|
70
|
-
);
|
|
71
|
-
|
|
72
|
-
const chain = new LLMChain({
|
|
73
|
-
callbacks: [
|
|
74
|
-
{
|
|
75
|
-
handleChainError(err: Error): Promise<void> | void {
|
|
76
|
-
console.log(err.message);
|
|
77
|
-
},
|
|
78
|
-
},
|
|
79
|
-
],
|
|
80
|
-
llm: chat,
|
|
81
|
-
prompt: chatPrompt,
|
|
82
|
-
verbose: true,
|
|
83
|
-
});
|
|
84
|
-
try {
|
|
85
|
-
// 使用转换后的聊天消息作为输入开始聊天
|
|
86
|
-
await chain.call(vars);
|
|
87
|
-
// 完成后,关闭流
|
|
88
|
-
controller.close();
|
|
89
|
-
} catch (error) {
|
|
90
|
-
// 如果在执行过程中发生错误,向流发送错误
|
|
91
|
-
controller.error(error);
|
|
92
|
-
}
|
|
93
|
-
},
|
|
94
|
-
});
|
|
95
|
-
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { LangChainParams } from '@/types/langchain';
|
|
2
|
-
|
|
3
|
-
import { LangChainStream } from './LangChainStream';
|
|
4
|
-
|
|
5
|
-
if (!process.env.OPENAI_API_KEY) {
|
|
6
|
-
throw new Error('Missing env var from OpenAI');
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export const config = {
|
|
10
|
-
runtime: 'edge',
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
export default async function handler(request: Request) {
|
|
14
|
-
const payload = (await request.json()) as LangChainParams;
|
|
15
|
-
|
|
16
|
-
return new Response(LangChainStream(payload));
|
|
17
|
-
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { URLS } from '@/services/url';
|
|
2
|
-
import { LangChainParams } from '@/types/langchain';
|
|
3
|
-
import { fetchAIFactory } from '@/utils/fetch';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* 专门用于 FlowChain 的 fetch
|
|
7
|
-
*/
|
|
8
|
-
export const fetchLangChain = fetchAIFactory(
|
|
9
|
-
(params: LangChainParams, signal?: AbortSignal | undefined) =>
|
|
10
|
-
fetch(URLS.chain, {
|
|
11
|
-
body: JSON.stringify(params),
|
|
12
|
-
headers: {
|
|
13
|
-
'Content-Type': 'application/json',
|
|
14
|
-
},
|
|
15
|
-
method: 'POST',
|
|
16
|
-
signal,
|
|
17
|
-
}),
|
|
18
|
-
);
|
package/src/types/langchain.ts
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import { ChatMessage } from '@lobehub/ui';
|
|
2
|
-
|
|
3
|
-
export interface LangChainParams {
|
|
4
|
-
llm: {
|
|
5
|
-
/**
|
|
6
|
-
* 控制生成文本中的惩罚系数,用于减少重复性
|
|
7
|
-
*/
|
|
8
|
-
frequency_penalty?: number;
|
|
9
|
-
/**
|
|
10
|
-
* 生成文本的最大长度
|
|
11
|
-
*/
|
|
12
|
-
max_tokens?: number;
|
|
13
|
-
model: string;
|
|
14
|
-
/**
|
|
15
|
-
* 控制生成文本中的惩罚系数,用于减少主题的变化
|
|
16
|
-
*/
|
|
17
|
-
presence_penalty?: number;
|
|
18
|
-
/**
|
|
19
|
-
* 生成文本的随机度量,用于控制文本的创造性和多样性
|
|
20
|
-
* @default 0.6
|
|
21
|
-
*/
|
|
22
|
-
temperature: number;
|
|
23
|
-
/**
|
|
24
|
-
* 控制生成文本中最高概率的单个令牌
|
|
25
|
-
*/
|
|
26
|
-
top_p?: number;
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* 聊天信息列表
|
|
31
|
-
*/
|
|
32
|
-
prompts: ChatMessage[];
|
|
33
|
-
vars: Record<string, string>;
|
|
34
|
-
}
|