@lobehub/chat 1.79.9 → 1.80.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 +77 -0
- package/changelog/v1.json +24 -0
- package/docs/development/basic/feature-development-new.mdx +465 -0
- package/docs/development/basic/feature-development-new.zh-CN.mdx +465 -0
- package/docs/development/database-schema.dbml +2 -0
- package/locales/ar/models.json +9 -0
- package/locales/ar/setting.json +16 -0
- package/locales/bg-BG/models.json +9 -0
- package/locales/bg-BG/setting.json +16 -0
- package/locales/de-DE/models.json +9 -0
- package/locales/de-DE/setting.json +16 -0
- package/locales/en-US/models.json +9 -0
- package/locales/en-US/setting.json +16 -0
- package/locales/es-ES/models.json +9 -0
- package/locales/es-ES/setting.json +16 -0
- package/locales/fa-IR/models.json +9 -0
- package/locales/fa-IR/setting.json +16 -0
- package/locales/fr-FR/models.json +9 -0
- package/locales/fr-FR/setting.json +16 -0
- package/locales/it-IT/models.json +9 -0
- package/locales/it-IT/setting.json +16 -0
- package/locales/ja-JP/models.json +9 -0
- package/locales/ja-JP/setting.json +16 -0
- package/locales/ko-KR/models.json +9 -0
- package/locales/ko-KR/setting.json +16 -0
- package/locales/nl-NL/models.json +9 -0
- package/locales/nl-NL/setting.json +16 -0
- package/locales/pl-PL/models.json +9 -0
- package/locales/pl-PL/setting.json +16 -0
- package/locales/pt-BR/models.json +9 -0
- package/locales/pt-BR/setting.json +16 -0
- package/locales/ru-RU/models.json +9 -0
- package/locales/ru-RU/setting.json +16 -0
- package/locales/tr-TR/models.json +9 -0
- package/locales/tr-TR/setting.json +16 -0
- package/locales/vi-VN/models.json +9 -0
- package/locales/vi-VN/setting.json +16 -0
- package/locales/zh-CN/models.json +9 -0
- package/locales/zh-CN/setting.json +16 -0
- package/locales/zh-TW/models.json +9 -0
- package/locales/zh-TW/setting.json +16 -0
- package/package.json +1 -1
- package/scripts/generate-oidc-jwk.mjs +2 -1
- package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatList/WelcomeChatItem/OpeningQuestions.tsx +78 -0
- package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatList/WelcomeChatItem/WelcomeMessage.tsx +24 -4
- package/src/app/[variants]/(main)/chat/(workspace)/features/AgentSettings/CategoryContent/useCategory.tsx +6 -1
- package/src/app/[variants]/(main)/chat/(workspace)/features/AgentSettings/index.tsx +2 -0
- package/src/config/modelProviders/openai.ts +17 -0
- package/src/const/settings/agent.ts +1 -0
- package/src/const/settings/llm.ts +1 -1
- package/src/database/_deprecated/schemas/session.ts +2 -0
- package/src/database/client/migrations.json +9 -0
- package/src/database/migrations/0021_add_agent_opening_settings.sql +2 -0
- package/src/database/migrations/meta/0021_snapshot.json +4988 -0
- package/src/database/migrations/meta/_journal.json +7 -0
- package/src/database/repositories/dataImporter/deprecated/__tests__/fixtures/messages.json +2 -0
- package/src/database/repositories/dataImporter/deprecated/__tests__/index.test.ts +19 -0
- package/src/database/schemas/agent.ts +3 -0
- package/src/features/AgentSetting/AgentOpening/OpeningMessage.tsx +80 -0
- package/src/features/AgentSetting/AgentOpening/OpeningQuestions.tsx +144 -0
- package/src/features/AgentSetting/AgentOpening/index.tsx +52 -0
- package/src/features/AgentSetting/store/selectors.ts +3 -0
- package/src/locales/default/setting.ts +16 -0
- package/src/migrations/FromV5ToV6/types/v6.ts +2 -0
- package/src/server/routers/lambda/session.ts +8 -1
- package/src/server/routers/tools/search.ts +1 -0
- package/src/services/chat.ts +1 -0
- package/src/services/import/client.test.ts +18 -0
- package/src/services/session/server.test.ts +2 -0
- package/src/store/agent/slices/chat/selectors/__snapshots__/agent.test.ts.snap +2 -1
- package/src/store/agent/slices/chat/selectors/agent.ts +7 -0
- package/src/store/global/initialState.ts +1 -0
- package/src/store/user/slices/modelList/selectors/modelProvider.test.ts +1 -0
- package/src/store/user/slices/settings/selectors/__snapshots__/settings.test.ts.snap +10 -8
- package/src/types/agent/index.ts +12 -0
@@ -0,0 +1,465 @@
|
|
1
|
+
# LobeChat 功能开发完全指南 (新版)
|
2
|
+
|
3
|
+
本文档旨在指导开发者了解如何在 LobeChat 中开发一块完整的功能需求。
|
4
|
+
|
5
|
+
我们将以 [RFC 021 - 自定义助手开场引导](https://github.com/lobehub/lobe-chat/discussions/891) 为例,阐述完整的实现流程。
|
6
|
+
|
7
|
+
## 一、更新 schema
|
8
|
+
|
9
|
+
lobe-chat 使用 postgres 数据库,浏览器端本地数据库使用 [pglite](https://pglite.dev/)(wasm 版本 postgres)。项目还使用了 [drizzle](https://orm.drizzle.team/) ORM 用来操作数据库。
|
10
|
+
|
11
|
+
相比旧方案浏览器端使用 indexDB 来说,浏览器端和 server 端都使用 postgres 好处在于 model 层代码可以完全复用。
|
12
|
+
|
13
|
+
schemas 都统一放在 `src/database/schemas`,我们需要调整 `agents` 表增加两个配置项对应的字段:
|
14
|
+
|
15
|
+
```diff
|
16
|
+
// src/database/schemas/agent.ts
|
17
|
+
export const agents = pgTable(
|
18
|
+
'agents',
|
19
|
+
{
|
20
|
+
id: text('id')
|
21
|
+
.primaryKey()
|
22
|
+
.$defaultFn(() => idGenerator('agents'))
|
23
|
+
.notNull(),
|
24
|
+
avatar: text('avatar'),
|
25
|
+
backgroundColor: text('background_color'),
|
26
|
+
plugins: jsonb('plugins').$type<string[]>().default([]),
|
27
|
+
// ...
|
28
|
+
tts: jsonb('tts').$type<LobeAgentTTSConfig>(),
|
29
|
+
|
30
|
+
+ openingMessage: text('opening_message'),
|
31
|
+
+ openingQuestions: text('opening_questions').array().default([]),
|
32
|
+
|
33
|
+
...timestamps,
|
34
|
+
},
|
35
|
+
(t) => ({
|
36
|
+
// ...
|
37
|
+
// !: update index here
|
38
|
+
}),
|
39
|
+
);
|
40
|
+
|
41
|
+
```
|
42
|
+
|
43
|
+
需要注意的是,有些时候我们可能还需要更新索引,但对于这个需求我们没有相关的性能瓶颈问题,所以不需要更新索引。
|
44
|
+
|
45
|
+
调整完 schema 后我们需要运行 `npm run db:generate,` 使用 drizzle-kit 自带的数据库迁移能力生成对应的用于迁移到最新 schema 的 sql 代码。执行后会产生四个文件:
|
46
|
+
|
47
|
+
- src/database/migrations/meta/\_journal.json:保存每次迁移的相关信息
|
48
|
+
- src/database/migrations/0021\_add\_agent\_opening\_settings.sql:此次迁移的 sql 命令
|
49
|
+
- src/database/client/migrations.json:pglite 使用的此次迁移的 sql 命令
|
50
|
+
- src/database/migrations/meta/0021\_snapshot.json:当前最新的完整数据库快照
|
51
|
+
|
52
|
+
注意脚本默认生成的迁移 sql 文件名不会像 `0021_add_agent_opening_settings.sql` 这样语义清晰,你需要自己手动对它重命名并且更新 `src/database/migrations/meta/_journal.json`。
|
53
|
+
|
54
|
+
以前客户端存储使用 indexDB 数据迁移相对麻烦,现在本地端使用 pglite 之后数据库迁移就是一条命令的事,非常简单快捷,你也可以检查生成的迁移 sql 是否有什么优化空间,手动调整。
|
55
|
+
|
56
|
+
## 二、更新数据模型
|
57
|
+
|
58
|
+
在 `src/types` 下定义了我们项目中使用到的各种数据模型,我们并没有直接使用 drizzle schema 导出的类型,例如 `export type NewAgent = typeof agents.$inferInsert;`,而是根据前端需求和 db schema 定义中对应字段数据类型定义了对应的数据模型。
|
59
|
+
|
60
|
+
数据模型定义都放在 `src/types` 文件夹下,更新 `src/types/agent/index.ts` 中 `LobeAgentConfig` 类型:
|
61
|
+
|
62
|
+
```diff
|
63
|
+
export interface LobeAgentConfig {
|
64
|
+
// ...
|
65
|
+
chatConfig: LobeAgentChatConfig;
|
66
|
+
/**
|
67
|
+
* 角色所使用的语言模型
|
68
|
+
* @default gpt-4o-mini
|
69
|
+
*/
|
70
|
+
model: string;
|
71
|
+
|
72
|
+
+ /**
|
73
|
+
+ * 开场白
|
74
|
+
+ */
|
75
|
+
+ openingMessage?: string;
|
76
|
+
+ /**
|
77
|
+
+ * 开场问题
|
78
|
+
+ */
|
79
|
+
+ openingQuestions?: string[];
|
80
|
+
|
81
|
+
/**
|
82
|
+
* 语言模型参数
|
83
|
+
*/
|
84
|
+
params: LLMParams;
|
85
|
+
/**
|
86
|
+
* 启用的插件
|
87
|
+
*/
|
88
|
+
plugins?: string[];
|
89
|
+
|
90
|
+
/**
|
91
|
+
* 模型供应商
|
92
|
+
*/
|
93
|
+
provider?: string;
|
94
|
+
|
95
|
+
/**
|
96
|
+
* 系统角色
|
97
|
+
*/
|
98
|
+
systemRole: string;
|
99
|
+
|
100
|
+
/**
|
101
|
+
* 语音服务
|
102
|
+
*/
|
103
|
+
tts: LobeAgentTTSConfig;
|
104
|
+
}
|
105
|
+
```
|
106
|
+
|
107
|
+
## 三、Service 实现 / Model 实现
|
108
|
+
|
109
|
+
- `model` 层封装对 DB 的可复用操作
|
110
|
+
- `service` 层实现应用业务逻辑
|
111
|
+
|
112
|
+
在 `src` 目录下都有对应的顶层文件夹。
|
113
|
+
|
114
|
+
我们需要查看是否需要更新其实现,agent 配置在前端被抽象成 session 的配置,在 `src/services/session/server.ts` 中可以看到有个 service 函数 `updateSessionConfig`:
|
115
|
+
|
116
|
+
```typescript
|
117
|
+
export class ServerService implements ISessionService {
|
118
|
+
// ...
|
119
|
+
updateSessionConfig: ISessionService['updateSessionConfig'] = (id, config, signal) => {
|
120
|
+
return lambdaClient.session.updateSessionConfig.mutate({ id, value: config }, { signal });
|
121
|
+
};
|
122
|
+
}
|
123
|
+
```
|
124
|
+
|
125
|
+
跳转 `lambdaClient.session.updateSessionConfig` 实现,发现它只是简单的 **merge** 了新的 config 和旧的 config。
|
126
|
+
|
127
|
+
```typescript
|
128
|
+
export const sessionRouter = router({
|
129
|
+
// ..
|
130
|
+
updateSessionConfig: sessionProcedure
|
131
|
+
.input(
|
132
|
+
z.object({
|
133
|
+
id: z.string(),
|
134
|
+
value: z.object({}).passthrough().partial(),
|
135
|
+
}),
|
136
|
+
)
|
137
|
+
.mutation(async ({ input, ctx }) => {
|
138
|
+
const session = await ctx.sessionModel.findByIdOrSlug(input.id);
|
139
|
+
// ...
|
140
|
+
const mergedValue = merge(session.agent, input.value);
|
141
|
+
return ctx.sessionModel.updateConfig(session.agent.id, mergedValue);
|
142
|
+
}),
|
143
|
+
});
|
144
|
+
```
|
145
|
+
|
146
|
+
可以预想的到,我们前端会增加两个输入,用户修改的时候去调用这个 `updateSessionConfig`,我们只需要把新的字段 `openingMessage` 和 `openingQuestions` 传过来 merge 就好了。
|
147
|
+
|
148
|
+
因此,service 层和 model 层不需要修改。
|
149
|
+
|
150
|
+
## 四、前端实现
|
151
|
+
|
152
|
+
### 数据流 store 实现
|
153
|
+
|
154
|
+
lobe-chat 使用 [zustand](https://zustand.docs.pmnd.rs/getting-started/introduction) 作为全局状态管理框架,对于状态管理的详细实践介绍,可以查阅 [📘 状态管理最佳实践](/zh/docs/development/state-management/state-management-intro)。
|
155
|
+
|
156
|
+
和 agent 相关的 store 有两个:
|
157
|
+
|
158
|
+
- `src/features/AgentSetting/store` 服务于 agent 设置的局部 store
|
159
|
+
- `src/store/agent` 用于获取当前会话 agent 的 store
|
160
|
+
|
161
|
+
后者通过 `src/features/AgentSetting/AgentSettings.tsx` 中 `AgentSettings` 组件的 `onConfigChange` 监听并更新当前会话的 agent 配置。
|
162
|
+
|
163
|
+
#### 更新 AgentSetting/store
|
164
|
+
|
165
|
+
首先我们更新 initialState,阅读 `src/features/AgentSetting/store/initialState.ts` 后得知初始 agent 配置保存在 `src/const/settings/agent.ts` 中的 `DEFAULT_AGENT_CONFIG`:
|
166
|
+
|
167
|
+
```diff
|
168
|
+
export const DEFAULT_AGENT_CONFIG: LobeAgentConfig = {
|
169
|
+
chatConfig: DEFAULT_AGENT_CHAT_CONFIG,
|
170
|
+
model: DEFAULT_MODEL,
|
171
|
+
+ openingQuestions: [],
|
172
|
+
params: {
|
173
|
+
frequency_penalty: 0,
|
174
|
+
presence_penalty: 0,
|
175
|
+
temperature: 1,
|
176
|
+
top_p: 1,
|
177
|
+
},
|
178
|
+
plugins: [],
|
179
|
+
provider: DEFAULT_PROVIDER,
|
180
|
+
systemRole: '',
|
181
|
+
tts: DEFAUTT_AGENT_TTS_CONFIG,
|
182
|
+
};
|
183
|
+
```
|
184
|
+
|
185
|
+
其实你这里不更新都可以,因为 `openingQuestions` 类型本来就是可选的,`openingMessage` 我这里就不更新了。
|
186
|
+
|
187
|
+
因为我们增加了两个新字段,为了方便在 `src/features/AgentSetting/AgentOpening` 文件夹中组件访问和性能优化,我们在 `src/features/AgentSetting/store/selectors.ts` 增加相关的 selectors:
|
188
|
+
|
189
|
+
```diff
|
190
|
+
import { DEFAULT_AGENT_CHAT_CONFIG } from '@/const/settings';
|
191
|
+
import { LobeAgentChatConfig } from '@/types/agent';
|
192
|
+
|
193
|
+
import { Store } from './action';
|
194
|
+
|
195
|
+
const chatConfig = (s: Store): LobeAgentChatConfig =>
|
196
|
+
s.config.chatConfig || DEFAULT_AGENT_CHAT_CONFIG;
|
197
|
+
|
198
|
+
+export const DEFAULT_OPENING_QUESTIONS: string[] = [];
|
199
|
+
export const selectors = {
|
200
|
+
chatConfig,
|
201
|
+
+ openingMessage: (s: Store) => s.config.openingMessage,
|
202
|
+
+ openingQuestions: (s: Store) => s.config.openingQuestions || DEFAULT_OPENING_QUESTIONS,
|
203
|
+
};
|
204
|
+
```
|
205
|
+
|
206
|
+
这里我们就不增加额外的 action 用于更新 agent config 了,因为我观察到已有的其它代码也是直接使用现有代码中统一的 `setChatConfig`:
|
207
|
+
|
208
|
+
```typescript
|
209
|
+
export const store: StateCreator<Store, [['zustand/devtools', never]]> = (set, get) => ({
|
210
|
+
setAgentConfig: (config) => {
|
211
|
+
get().dispatchConfig({ config, type: 'update' });
|
212
|
+
},
|
213
|
+
});
|
214
|
+
```
|
215
|
+
|
216
|
+
#### 更新 store/agent
|
217
|
+
|
218
|
+
在组件 `src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatList/WelcomeChatItem/WelcomeMessage.tsx` 我们使用了这个 store 用于获取当前 agent 配置,用来渲染用户自定义的开场消息和引导性问题。
|
219
|
+
|
220
|
+
这里因为只需要读取两个配置项,我们就简单的加两个 selectors 就好了:
|
221
|
+
|
222
|
+
更新 `src/store/agent/slices/chat/selectors/agent.ts`:
|
223
|
+
|
224
|
+
```diff
|
225
|
+
// ...
|
226
|
+
+const openingQuestions = (s: AgentStoreState) =>
|
227
|
+
+ currentAgentConfig(s).openingQuestions || DEFAULT_OPENING_QUESTIONS;
|
228
|
+
+const openingMessage = (s: AgentStoreState) => currentAgentConfig(s).openingMessage || '';
|
229
|
+
|
230
|
+
export const agentSelectors = {
|
231
|
+
// ...
|
232
|
+
isInboxSession,
|
233
|
+
+ openingMessage,
|
234
|
+
+ openingQuestions,
|
235
|
+
};
|
236
|
+
```
|
237
|
+
|
238
|
+
### UI 实现和 action 绑定
|
239
|
+
|
240
|
+
我们这次要新增一个类别的设置, 在 `src/features/AgentSetting` 中定义了 agent 的各种设置的 UI 组件,这次我们要增加一个设置类型:开场设置。增加一个文件夹 `AgentOpening` 存放开场设置相关的组件。项目使用了:
|
241
|
+
|
242
|
+
- [ant-design](https://ant.design/) 和 [lobe-ui:](https://github.com/lobehub/lobe-ui)组件库
|
243
|
+
- [antd-style](https://ant-design.github.io/antd-style) : css-in-js 方案
|
244
|
+
- [react-layout-kit](https://github.com/arvinxx/react-layout-kit):响应式布局组件
|
245
|
+
- [@ant-design/icons](https://ant.design/components/icon-cn) 和 [lucide](https://lucide.dev/icons/): 图标库
|
246
|
+
- [react-i18next](https://github.com/i18next/react-i18next) 和 [lobe-i18n](https://github.com/lobehub/lobe-cli-toolbox/tree/master/packages/lobe-i18n) :i18n 框架和多语言自动翻译工具
|
247
|
+
|
248
|
+
lobe-chat 是个国际化项目,新加的文案需要更新默认的 `locale` 文件: `src/locales/default/setting.ts` 。
|
249
|
+
|
250
|
+
我们以子组件 `OpeningQuestion.tsx` 为例,组件实现:
|
251
|
+
|
252
|
+
```typescript
|
253
|
+
// src/features/AgentSetting/AgentOpening/OpeningQuestions.tsx
|
254
|
+
'use client';
|
255
|
+
|
256
|
+
import { DeleteOutlined, PlusOutlined } from '@ant-design/icons';
|
257
|
+
import { SortableList } from '@lobehub/ui';
|
258
|
+
import { Button, Empty, Input } from 'antd';
|
259
|
+
import { createStyles } from 'antd-style';
|
260
|
+
import { memo, useCallback, useMemo, useState } from 'react';
|
261
|
+
import { useTranslation } from 'react-i18next';
|
262
|
+
import { Flexbox } from 'react-layout-kit';
|
263
|
+
|
264
|
+
import { useStore } from '../store';
|
265
|
+
import { selectors } from '../store/selectors';
|
266
|
+
|
267
|
+
const useStyles = createStyles(({ css, token }) => ({
|
268
|
+
empty: css`
|
269
|
+
margin-block: 24px;
|
270
|
+
margin-inline: 0;
|
271
|
+
`,
|
272
|
+
questionItemContainer: css`
|
273
|
+
margin-block-end: 8px;
|
274
|
+
padding-block: 2px;
|
275
|
+
padding-inline: 10px 0;
|
276
|
+
background: ${token.colorBgContainer};
|
277
|
+
`,
|
278
|
+
questionItemContent: css`
|
279
|
+
flex: 1;
|
280
|
+
`,
|
281
|
+
questionsList: css`
|
282
|
+
width: 100%;
|
283
|
+
margin-block-start: 16px;
|
284
|
+
`,
|
285
|
+
repeatError: css`
|
286
|
+
margin: 0;
|
287
|
+
color: ${token.colorErrorText};
|
288
|
+
`,
|
289
|
+
}));
|
290
|
+
|
291
|
+
interface QuestionItem {
|
292
|
+
content: string;
|
293
|
+
id: number;
|
294
|
+
}
|
295
|
+
|
296
|
+
const OpeningQuestions = memo(() => {
|
297
|
+
const { t } = useTranslation('setting');
|
298
|
+
const { styles } = useStyles();
|
299
|
+
const [questionInput, setQuestionInput] = useState('');
|
300
|
+
|
301
|
+
// 使用 selector 访问对应配置
|
302
|
+
const openingQuestions = useStore(selectors.openingQuestions);
|
303
|
+
// 使用 action 更新配置
|
304
|
+
const updateConfig = useStore((s) => s.setAgentConfig);
|
305
|
+
const setQuestions = useCallback(
|
306
|
+
(questions: string[]) => {
|
307
|
+
updateConfig({ openingQuestions: questions });
|
308
|
+
},
|
309
|
+
[updateConfig],
|
310
|
+
);
|
311
|
+
|
312
|
+
const addQuestion = useCallback(() => {
|
313
|
+
if (!questionInput.trim()) return;
|
314
|
+
|
315
|
+
setQuestions([...openingQuestions, questionInput.trim()]);
|
316
|
+
setQuestionInput('');
|
317
|
+
}, [openingQuestions, questionInput, setQuestions]);
|
318
|
+
|
319
|
+
const removeQuestion = useCallback(
|
320
|
+
(content: string) => {
|
321
|
+
const newQuestions = [...openingQuestions];
|
322
|
+
const index = newQuestions.indexOf(content);
|
323
|
+
newQuestions.splice(index, 1);
|
324
|
+
setQuestions(newQuestions);
|
325
|
+
},
|
326
|
+
[openingQuestions, setQuestions],
|
327
|
+
);
|
328
|
+
|
329
|
+
// 处理拖拽排序后的逻辑
|
330
|
+
const handleSortEnd = useCallback(
|
331
|
+
(items: QuestionItem[]) => {
|
332
|
+
setQuestions(items.map((item) => item.content));
|
333
|
+
},
|
334
|
+
[setQuestions],
|
335
|
+
);
|
336
|
+
|
337
|
+
const items: QuestionItem[] = useMemo(() => {
|
338
|
+
return openingQuestions.map((item, index) => ({
|
339
|
+
content: item,
|
340
|
+
id: index,
|
341
|
+
}));
|
342
|
+
}, [openingQuestions]);
|
343
|
+
|
344
|
+
const isRepeat = openingQuestions.includes(questionInput.trim());
|
345
|
+
|
346
|
+
return (
|
347
|
+
<Flexbox gap={8}>
|
348
|
+
<Flexbox gap={4}>
|
349
|
+
<Input
|
350
|
+
addonAfter={
|
351
|
+
<Button
|
352
|
+
// don't allow repeat
|
353
|
+
disabled={openingQuestions.includes(questionInput.trim())}
|
354
|
+
icon={<PlusOutlined />}
|
355
|
+
onClick={addQuestion}
|
356
|
+
size="small"
|
357
|
+
type="text"
|
358
|
+
/>
|
359
|
+
}
|
360
|
+
onChange={(e) => setQuestionInput(e.target.value)}
|
361
|
+
onPressEnter={addQuestion}
|
362
|
+
placeholder={t('settingOpening.openingQuestions.placeholder')}
|
363
|
+
value={questionInput}
|
364
|
+
/>
|
365
|
+
|
366
|
+
{isRepeat && (
|
367
|
+
<p className={styles.repeatError}>{t('settingOpening.openingQuestions.repeat')}</p>
|
368
|
+
)}
|
369
|
+
</Flexbox>
|
370
|
+
|
371
|
+
<div className={styles.questionsList}>
|
372
|
+
{openingQuestions.length > 0 ? (
|
373
|
+
<SortableList
|
374
|
+
items={items}
|
375
|
+
onChange={handleSortEnd}
|
376
|
+
renderItem={(item) => (
|
377
|
+
<SortableList.Item className={styles.questionItemContainer} id={item.id}>
|
378
|
+
<SortableList.DragHandle />
|
379
|
+
<div className={styles.questionItemContent}>{item.content}</div>
|
380
|
+
<Button
|
381
|
+
icon={<DeleteOutlined />}
|
382
|
+
onClick={() => removeQuestion(item.content)}
|
383
|
+
type="text"
|
384
|
+
/>
|
385
|
+
</SortableList.Item>
|
386
|
+
)}
|
387
|
+
/>
|
388
|
+
) : (
|
389
|
+
<Empty
|
390
|
+
className={styles.empty}
|
391
|
+
description={t('settingOpening.openingQuestions.empty')}
|
392
|
+
/>
|
393
|
+
)}
|
394
|
+
</div>
|
395
|
+
</Flexbox>
|
396
|
+
);
|
397
|
+
});
|
398
|
+
|
399
|
+
export default OpeningQuestions;
|
400
|
+
```
|
401
|
+
|
402
|
+
同时我们需要将用户设置的开场配置展示出来,这个是在 chat 页面,对应组件在 `src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatList/WelcomeChatItem/WelcomeMessage.tsx`:
|
403
|
+
|
404
|
+
```typescript
|
405
|
+
const WelcomeMessage = () => {
|
406
|
+
const { t } = useTranslation('chat');
|
407
|
+
|
408
|
+
// 获取当前开场配置
|
409
|
+
const openingMessage = useAgentStore(agentSelectors.openingMessage);
|
410
|
+
const openingQuestions = useAgentStore(agentSelectors.openingQuestions);
|
411
|
+
|
412
|
+
const meta = useSessionStore(sessionMetaSelectors.currentAgentMeta, isEqual);
|
413
|
+
const { isAgentEditable } = useServerConfigStore(featureFlagsSelectors);
|
414
|
+
const activeId = useChatStore((s) => s.activeId);
|
415
|
+
|
416
|
+
const agentSystemRoleMsg = t('agentDefaultMessageWithSystemRole', {
|
417
|
+
name: meta.title || t('defaultAgent'),
|
418
|
+
systemRole: meta.description,
|
419
|
+
});
|
420
|
+
|
421
|
+
const agentMsg = t(isAgentEditable ? 'agentDefaultMessage' : 'agentDefaultMessageWithoutEdit', {
|
422
|
+
name: meta.title || t('defaultAgent'),
|
423
|
+
url: `/chat/settings?session=${activeId}`,
|
424
|
+
});
|
425
|
+
|
426
|
+
const message = useMemo(() => {
|
427
|
+
// 用户设置了就用用户设置的
|
428
|
+
if (openingMessage) return openingMessage;
|
429
|
+
return !!meta.description ? agentSystemRoleMsg : agentMsg;
|
430
|
+
}, [openingMessage, agentSystemRoleMsg, agentMsg, meta.description]);
|
431
|
+
|
432
|
+
const chatItem = (
|
433
|
+
<ChatItem
|
434
|
+
avatar={meta}
|
435
|
+
editing={false}
|
436
|
+
message={message}
|
437
|
+
placement={'left'}
|
438
|
+
type={type === 'chat' ? 'block' : 'pure'}
|
439
|
+
/>
|
440
|
+
);
|
441
|
+
|
442
|
+
return openingQuestions.length > 0 ? (
|
443
|
+
<Flexbox>
|
444
|
+
{chatItem}
|
445
|
+
{/* 渲染引导性问题 */}
|
446
|
+
<OpeningQuestions mobile={mobile} questions={openingQuestions} />
|
447
|
+
</Flexbox>
|
448
|
+
) : (
|
449
|
+
chatItem
|
450
|
+
);
|
451
|
+
};
|
452
|
+
export default WelcomeMessage;
|
453
|
+
```
|
454
|
+
|
455
|
+
## 五、测试
|
456
|
+
|
457
|
+
项目使用 vitest 进行单元测试。
|
458
|
+
|
459
|
+
由于我们目前两个新的配置字段都是可选的,所以理论上你不更新测试也能跑通,不过由于我们把前面提到的默认配置 `DEFAULT_AGENT_CONFIG` 增加了 `openingQuestions` 字段,这导致很多测试计算出的配置都是有这个字段的,因此我们还是需要更新一部分测试的快照。
|
460
|
+
|
461
|
+
对于当前这个场景,我建议是本地直接跑下测试,看哪些测试失败了,针对需要更新,例如测试文件 `src/store/agent/slices/chat/selectors/agent.test.ts` 需要执行一下 `npx vitest -u src/store/agent/slices/chat/selectors/agent.test.ts` 更新快照。
|
462
|
+
|
463
|
+
## 总结
|
464
|
+
|
465
|
+
以上就是 LobeChat 开场设置功能的完整实现流程。开发者可以参考本文档进行相关功能的开发和测试。
|
@@ -16,6 +16,8 @@ table agents {
|
|
16
16
|
provider text
|
17
17
|
system_role text
|
18
18
|
tts jsonb
|
19
|
+
opening_message text
|
20
|
+
opening_questions text[] [default: `[]`]
|
19
21
|
accessed_at "timestamp with time zone" [not null, default: `now()`]
|
20
22
|
created_at "timestamp with time zone" [not null, default: `now()`]
|
21
23
|
updated_at "timestamp with time zone" [not null, default: `now()`]
|
package/locales/ar/models.json
CHANGED
@@ -1067,6 +1067,15 @@
|
|
1067
1067
|
"gpt-4-vision-preview": {
|
1068
1068
|
"description": "نموذج GPT-4 Turbo الأحدث يتمتع بقدرات بصرية. الآن، يمكن استخدام الطلبات البصرية باستخدام نمط JSON واستدعاء الوظائف. GPT-4 Turbo هو إصدار معزز يوفر دعمًا فعالًا من حيث التكلفة للمهام متعددة الوسائط. يجد توازنًا بين الدقة والكفاءة، مما يجعله مناسبًا للتطبيقات التي تتطلب تفاعلات في الوقت الحقيقي."
|
1069
1069
|
},
|
1070
|
+
"gpt-4.1": {
|
1071
|
+
"description": "GPT-4.1 هو نموذجنا الرائد للمهام المعقدة. إنه مثالي لحل المشكلات عبر مجالات متعددة."
|
1072
|
+
},
|
1073
|
+
"gpt-4.1-mini": {
|
1074
|
+
"description": "يوفر GPT-4.1 mini توازنًا بين الذكاء والسرعة والتكلفة، مما يجعله نموذجًا جذابًا للعديد من الاستخدامات."
|
1075
|
+
},
|
1076
|
+
"gpt-4.1-nano": {
|
1077
|
+
"description": "يوفر GPT-4.1 mini توازنًا بين الذكاء والسرعة والتكلفة، مما يجعله نموذجًا جذابًا للعديد من الاستخدامات."
|
1078
|
+
},
|
1070
1079
|
"gpt-4.5-preview": {
|
1071
1080
|
"description": "نسخة المعاينة البحثية لـ GPT-4.5، وهي أكبر وأقوى نموذج GPT لدينا حتى الآن. تتمتع بمعرفة واسعة عن العالم وتفهم أفضل لنوايا المستخدم، مما يجعلها بارعة في المهام الإبداعية والتخطيط الذاتي. يمكن لـ GPT-4.5 قبول المدخلات النصية والصورية وتوليد مخرجات نصية (بما في ذلك المخرجات الهيكلية). تدعم ميزات المطورين الأساسية مثل استدعاء الدوال، وواجهة برمجة التطبيقات الجماعية، والمخرجات المتدفقة. تتألق GPT-4.5 بشكل خاص في المهام التي تتطلب التفكير الإبداعي، والتفكير المفتوح، والحوار (مثل الكتابة، والتعلم، أو استكشاف أفكار جديدة). تاريخ انتهاء المعرفة هو أكتوبر 2023."
|
1072
1081
|
},
|
package/locales/ar/setting.json
CHANGED
@@ -6,6 +6,7 @@
|
|
6
6
|
"chat": "تفضيلات الدردشة",
|
7
7
|
"meta": "معلومات المساعد",
|
8
8
|
"modal": "إعدادات النموذج",
|
9
|
+
"opening": "إعداد الافتتاح",
|
9
10
|
"plugin": "إعدادات الإضافة",
|
10
11
|
"prompt": "تعيين الشخصية",
|
11
12
|
"tts": "خدمة النص إلى كلام"
|
@@ -253,6 +254,21 @@
|
|
253
254
|
"title": "مستوى الانفتاح الفكري"
|
254
255
|
}
|
255
256
|
},
|
257
|
+
"settingOpening": {
|
258
|
+
"openingMessage": {
|
259
|
+
"desc": "رسالة الافتتاح عند بدء المحادثة، تستخدم لتعريف وظائف المساعد",
|
260
|
+
"placeholder": "مرحبًا، أنا المساعد المخصص، يمكنك بدء المحادثة معي على الفور، أو يمكنك الذهاب إلى إعدادات المساعد لإكمال معلوماتي.",
|
261
|
+
"title": "رسالة الافتتاح"
|
262
|
+
},
|
263
|
+
"openingQuestions": {
|
264
|
+
"desc": "الأسئلة الإرشادية المعروضة عند بدء المحادثة",
|
265
|
+
"empty": "لا توجد أسئلة حالياً",
|
266
|
+
"placeholder": "أدخل السؤال",
|
267
|
+
"repeat": "السؤال موجود بالفعل",
|
268
|
+
"title": "أسئلة الافتتاح"
|
269
|
+
},
|
270
|
+
"title": "إعداد الافتتاح"
|
271
|
+
},
|
256
272
|
"settingPlugin": {
|
257
273
|
"title": "قائمة الإضافات"
|
258
274
|
},
|
@@ -1067,6 +1067,15 @@
|
|
1067
1067
|
"gpt-4-vision-preview": {
|
1068
1068
|
"description": "Най-новият модел GPT-4 Turbo разполага с визуални функции. Сега визуалните заявки могат да се използват с JSON формат и извиквания на функции. GPT-4 Turbo е подобрена версия, която предлага икономически ефективна поддръжка за мултимодални задачи. Той намира баланс между точност и ефективност, подходящ за приложения, изискващи взаимодействие в реално време."
|
1069
1069
|
},
|
1070
|
+
"gpt-4.1": {
|
1071
|
+
"description": "GPT-4.1 е нашият флагмански модел за сложни задачи. Той е изключително подходящ за решаване на проблеми в различни области."
|
1072
|
+
},
|
1073
|
+
"gpt-4.1-mini": {
|
1074
|
+
"description": "GPT-4.1 mini предлага баланс между интелигентност, скорост и разходи, което го прави привлекателен модел за много случаи на употреба."
|
1075
|
+
},
|
1076
|
+
"gpt-4.1-nano": {
|
1077
|
+
"description": "GPT-4.1 mini предлага баланс между интелигентност, скорост и разходи, което го прави привлекателен модел за много случаи на употреба."
|
1078
|
+
},
|
1070
1079
|
"gpt-4.5-preview": {
|
1071
1080
|
"description": "Изследователската предварителна версия на GPT-4.5, която е нашият най-голям и мощен GPT модел до момента. Тя притежава обширни знания за света и може по-добре да разбира намеренията на потребителите, което я прави изключително ефективна в креативни задачи и автономно планиране. GPT-4.5 приема текстови и изображен вход и генерира текстови изход (включително структурирани изходи). Поддържа ключови функции за разработчици, като извикване на функции, пакетно API и потоков изход. В задачи, изискващи креативно, открито мислене и диалог (като писане, учене или изследване на нови идеи), GPT-4.5 показва особени способности. Крайната дата на знанията е октомври 2023."
|
1072
1081
|
},
|
@@ -6,6 +6,7 @@
|
|
6
6
|
"chat": "Предпочитания за чат",
|
7
7
|
"meta": "Информация за асистента",
|
8
8
|
"modal": "Настройки на модела",
|
9
|
+
"opening": "Настройка на откритие",
|
9
10
|
"plugin": "Настройки на добавката",
|
10
11
|
"prompt": "Настройки на ролята",
|
11
12
|
"tts": "Гласова услуга"
|
@@ -253,6 +254,21 @@
|
|
253
254
|
"title": "Отвореност на мисленето"
|
254
255
|
}
|
255
256
|
},
|
257
|
+
"settingOpening": {
|
258
|
+
"openingMessage": {
|
259
|
+
"desc": "Съобщение за откритие при стартиране на сесия, използвано за представяне на функциите на асистента",
|
260
|
+
"placeholder": "Здравей, аз съм Персонализиран асистент, можеш веднага да започнеш разговор с мен или да отидеш в Настройки на асистента, за да попълниш информацията ми.",
|
261
|
+
"title": "Съобщение за откритие"
|
262
|
+
},
|
263
|
+
"openingQuestions": {
|
264
|
+
"desc": "Водещи въпроси, показвани в началото на сесията",
|
265
|
+
"empty": "Няма въпроси",
|
266
|
+
"placeholder": "Въведете въпрос",
|
267
|
+
"repeat": "Въпросът вече съществува",
|
268
|
+
"title": "Въпроси за откритие"
|
269
|
+
},
|
270
|
+
"title": "Настройка на откритие"
|
271
|
+
},
|
256
272
|
"settingPlugin": {
|
257
273
|
"title": "Списък с плъгини"
|
258
274
|
},
|
@@ -1067,6 +1067,15 @@
|
|
1067
1067
|
"gpt-4-vision-preview": {
|
1068
1068
|
"description": "Das neueste GPT-4 Turbo-Modell verfügt über visuelle Funktionen. Jetzt können visuelle Anfragen im JSON-Format und durch Funktionsaufrufe gestellt werden. GPT-4 Turbo ist eine verbesserte Version, die kosteneffiziente Unterstützung für multimodale Aufgaben bietet. Es findet ein Gleichgewicht zwischen Genauigkeit und Effizienz und eignet sich für Anwendungen, die Echtzeitanpassungen erfordern."
|
1069
1069
|
},
|
1070
|
+
"gpt-4.1": {
|
1071
|
+
"description": "GPT-4.1 ist unser Flaggschiffmodell für komplexe Aufgaben. Es eignet sich hervorragend zur Lösung von Problemen über verschiedene Bereiche hinweg."
|
1072
|
+
},
|
1073
|
+
"gpt-4.1-mini": {
|
1074
|
+
"description": "GPT-4.1 mini bietet ein Gleichgewicht zwischen Intelligenz, Geschwindigkeit und Kosten, was es zu einem attraktiven Modell für viele Anwendungsfälle macht."
|
1075
|
+
},
|
1076
|
+
"gpt-4.1-nano": {
|
1077
|
+
"description": "GPT-4.1 mini bietet ein Gleichgewicht zwischen Intelligenz, Geschwindigkeit und Kosten, was es zu einem attraktiven Modell für viele Anwendungsfälle macht."
|
1078
|
+
},
|
1070
1079
|
"gpt-4.5-preview": {
|
1071
1080
|
"description": "Die Forschungs-Vorschau von GPT-4.5, unserem bisher größten und leistungsstärksten GPT-Modell. Es verfügt über umfangreiches Weltwissen und kann die Absichten der Benutzer besser verstehen, was es in kreativen Aufgaben und autonomer Planung herausragend macht. GPT-4.5 akzeptiert Text- und Bild-Eingaben und generiert Textausgaben (einschließlich strukturierter Ausgaben). Es unterstützt wichtige Entwicklerfunktionen wie Funktionsaufrufe, Batch-APIs und Streaming-Ausgaben. In Aufgaben, die kreatives, offenes Denken und Dialog erfordern (wie Schreiben, Lernen oder das Erkunden neuer Ideen), zeigt GPT-4.5 besonders gute Leistungen. Der Wissensstand ist bis Oktober 2023."
|
1072
1081
|
},
|
@@ -6,6 +6,7 @@
|
|
6
6
|
"chat": "Chat-Präferenz",
|
7
7
|
"meta": "Assistenteninformation",
|
8
8
|
"modal": "Modell-Einstellungen",
|
9
|
+
"opening": "Eröffnungs Einstellungen",
|
9
10
|
"plugin": "Plugin-Einstellungen",
|
10
11
|
"prompt": "Rollenkonfiguration",
|
11
12
|
"tts": "Sprachdienst"
|
@@ -253,6 +254,21 @@
|
|
253
254
|
"title": "Offenheit des Denkens"
|
254
255
|
}
|
255
256
|
},
|
257
|
+
"settingOpening": {
|
258
|
+
"openingMessage": {
|
259
|
+
"desc": "Die Eröffnungsnachricht beim Start des Gesprächs, um die Funktionen des Assistenten vorzustellen",
|
260
|
+
"placeholder": "Hallo, ich bin der benutzerdefinierte Assistent. Du kannst sofort mit mir sprechen oder zu den Assistenteneinstellungen gehen, um meine Informationen zu vervollständigen.",
|
261
|
+
"title": "Eröffnungsnachricht"
|
262
|
+
},
|
263
|
+
"openingQuestions": {
|
264
|
+
"desc": "Leitfragen, die zu Beginn des Gesprächs angezeigt werden",
|
265
|
+
"empty": "Keine Fragen vorhanden",
|
266
|
+
"placeholder": "Bitte Frage eingeben",
|
267
|
+
"repeat": "Frage existiert bereits",
|
268
|
+
"title": "Eröffnungsfragen"
|
269
|
+
},
|
270
|
+
"title": "Eröffnungs Einstellungen"
|
271
|
+
},
|
256
272
|
"settingPlugin": {
|
257
273
|
"title": "Plugin-Liste"
|
258
274
|
},
|
@@ -1067,6 +1067,15 @@
|
|
1067
1067
|
"gpt-4-vision-preview": {
|
1068
1068
|
"description": "The latest GPT-4 Turbo model features visual capabilities. Now, visual requests can be made using JSON format and function calls. GPT-4 Turbo is an enhanced version that provides cost-effective support for multimodal tasks. It strikes a balance between accuracy and efficiency, making it suitable for applications requiring real-time interaction."
|
1069
1069
|
},
|
1070
|
+
"gpt-4.1": {
|
1071
|
+
"description": "GPT-4.1 is our flagship model for complex tasks. It excels at solving problems across various domains."
|
1072
|
+
},
|
1073
|
+
"gpt-4.1-mini": {
|
1074
|
+
"description": "GPT-4.1 mini offers a balance of intelligence, speed, and cost, making it an attractive model for many use cases."
|
1075
|
+
},
|
1076
|
+
"gpt-4.1-nano": {
|
1077
|
+
"description": "GPT-4.1 nano provides a balance of intelligence, speed, and cost, making it an appealing model for numerous applications."
|
1078
|
+
},
|
1070
1079
|
"gpt-4.5-preview": {
|
1071
1080
|
"description": "The research preview of GPT-4.5, our largest and most powerful GPT model to date. It possesses extensive world knowledge and better understands user intent, excelling in creative tasks and autonomous planning. GPT-4.5 accepts both text and image inputs and generates text outputs (including structured outputs). It supports key developer features such as function calling, batch API, and streaming output. GPT-4.5 particularly shines in tasks that require creativity, open-ended thinking, and dialogue, such as writing, learning, or exploring new ideas. Knowledge cutoff date is October 2023."
|
1072
1081
|
},
|
@@ -6,6 +6,7 @@
|
|
6
6
|
"chat": "Chat Preferences",
|
7
7
|
"meta": "Assistant Info",
|
8
8
|
"modal": "Model Settings",
|
9
|
+
"opening": "Opening Settings",
|
9
10
|
"plugin": "Plugin Settings",
|
10
11
|
"prompt": "Role Configuration",
|
11
12
|
"tts": "Voice Service"
|
@@ -253,6 +254,21 @@
|
|
253
254
|
"title": "Openness to Ideas"
|
254
255
|
}
|
255
256
|
},
|
257
|
+
"settingOpening": {
|
258
|
+
"openingMessage": {
|
259
|
+
"desc": "The opening message displayed when the conversation starts, used to introduce the assistant's features",
|
260
|
+
"placeholder": "Hello, I am your Custom Assistant. You can start chatting with me right away, or go to Assistant Settings to complete my information.",
|
261
|
+
"title": "Opening Message"
|
262
|
+
},
|
263
|
+
"openingQuestions": {
|
264
|
+
"desc": "Guiding questions displayed at the beginning of the conversation",
|
265
|
+
"empty": "No questions available",
|
266
|
+
"placeholder": "Please enter a question",
|
267
|
+
"repeat": "Question already exists",
|
268
|
+
"title": "Opening Questions"
|
269
|
+
},
|
270
|
+
"title": "Opening Settings"
|
271
|
+
},
|
256
272
|
"settingPlugin": {
|
257
273
|
"title": "Plugin List"
|
258
274
|
},
|