@lobehub/chat 0.161.25 → 0.162.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 +50 -0
- package/locales/ar/setting.json +5 -0
- package/locales/bg-BG/setting.json +5 -0
- package/locales/de-DE/setting.json +5 -0
- package/locales/en-US/setting.json +5 -0
- package/locales/es-ES/setting.json +5 -0
- package/locales/fr-FR/setting.json +5 -0
- package/locales/it-IT/setting.json +5 -0
- package/locales/ja-JP/setting.json +5 -0
- package/locales/ko-KR/setting.json +5 -0
- package/locales/nl-NL/setting.json +5 -0
- package/locales/pl-PL/setting.json +5 -0
- package/locales/pt-BR/setting.json +5 -0
- package/locales/ru-RU/setting.json +5 -0
- package/locales/tr-TR/setting.json +5 -0
- package/locales/vi-VN/setting.json +5 -0
- package/locales/zh-CN/setting.json +6 -1
- package/locales/zh-TW/setting.json +5 -0
- package/package.json +1 -1
- package/src/app/(main)/settings/system-agent/features/Topic.tsx +56 -0
- package/src/app/(main)/settings/system-agent/features/Translation.tsx +6 -8
- package/src/app/(main)/settings/system-agent/features/useSync.ts +23 -0
- package/src/app/(main)/settings/system-agent/index.tsx +7 -1
- package/src/app/api/middleware/auth/utils.test.ts +150 -0
- package/src/app/api/middleware/auth/utils.ts +1 -1
- package/src/chains/__tests__/summaryTitle.test.ts +0 -61
- package/src/chains/summaryTitle.ts +12 -24
- package/src/const/settings/common.ts +0 -1
- package/src/const/settings/systemAgent.ts +4 -3
- package/src/features/PluginDetailModal/APIs.tsx +1 -2
- package/src/hooks/useTokenCount.test.ts +5 -2
- package/src/locales/default/setting.ts +6 -1
- package/src/store/chat/slices/topic/action.ts +8 -2
- package/src/store/session/slices/session/action.test.ts +2 -2
- package/src/store/user/slices/settings/action.test.ts +6 -3
- package/src/store/user/slices/settings/action.ts +6 -11
- package/src/store/user/slices/settings/selectors/systemAgent.ts +2 -0
- package/src/types/user/settings/general.ts +0 -4
- package/src/types/user/settings/systemAgent.ts +3 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,56 @@
|
|
|
2
2
|
|
|
3
3
|
# Changelog
|
|
4
4
|
|
|
5
|
+
### [Version 0.162.1](https://github.com/lobehub/lobe-chat/compare/v0.162.0...v0.162.1)
|
|
6
|
+
|
|
7
|
+
<sup>Released on **2024-05-27**</sup>
|
|
8
|
+
|
|
9
|
+
#### 💄 Styles
|
|
10
|
+
|
|
11
|
+
- **misc**: Improve the display effect of plug-in API name and description.
|
|
12
|
+
|
|
13
|
+
<br/>
|
|
14
|
+
|
|
15
|
+
<details>
|
|
16
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
17
|
+
|
|
18
|
+
#### Styles
|
|
19
|
+
|
|
20
|
+
- **misc**: Improve the display effect of plug-in API name and description, closes [#2678](https://github.com/lobehub/lobe-chat/issues/2678) ([19cd0b9](https://github.com/lobehub/lobe-chat/commit/19cd0b9))
|
|
21
|
+
|
|
22
|
+
</details>
|
|
23
|
+
|
|
24
|
+
<div align="right">
|
|
25
|
+
|
|
26
|
+
[](#readme-top)
|
|
27
|
+
|
|
28
|
+
</div>
|
|
29
|
+
|
|
30
|
+
## [Version 0.162.0](https://github.com/lobehub/lobe-chat/compare/v0.161.25...v0.162.0)
|
|
31
|
+
|
|
32
|
+
<sup>Released on **2024-05-27**</sup>
|
|
33
|
+
|
|
34
|
+
#### ✨ Features
|
|
35
|
+
|
|
36
|
+
- **misc**: Support topic agent.
|
|
37
|
+
|
|
38
|
+
<br/>
|
|
39
|
+
|
|
40
|
+
<details>
|
|
41
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
42
|
+
|
|
43
|
+
#### What's improved
|
|
44
|
+
|
|
45
|
+
- **misc**: Support topic agent, closes [#2683](https://github.com/lobehub/lobe-chat/issues/2683) ([56865fe](https://github.com/lobehub/lobe-chat/commit/56865fe))
|
|
46
|
+
|
|
47
|
+
</details>
|
|
48
|
+
|
|
49
|
+
<div align="right">
|
|
50
|
+
|
|
51
|
+
[](#readme-top)
|
|
52
|
+
|
|
53
|
+
</div>
|
|
54
|
+
|
|
5
55
|
### [Version 0.161.25](https://github.com/lobehub/lobe-chat/compare/v0.161.24...v0.161.25)
|
|
6
56
|
|
|
7
57
|
<sup>Released on **2024-05-27**</sup>
|
package/locales/ar/setting.json
CHANGED
|
@@ -369,6 +369,11 @@
|
|
|
369
369
|
},
|
|
370
370
|
"systemAgent": {
|
|
371
371
|
"title": "مساعد النظام",
|
|
372
|
+
"topic": {
|
|
373
|
+
"label": "نموذج تسمية الموضوع",
|
|
374
|
+
"modelDesc": "يحدد النموذج المستخدم لإعادة تسمية الموضوع تلقائيًا",
|
|
375
|
+
"title": "إعادة تسمية الموضوع"
|
|
376
|
+
},
|
|
372
377
|
"translation": {
|
|
373
378
|
"label": "نموذج الترجمة",
|
|
374
379
|
"modelDesc": "النموذج المحدد للاستخدام في الترجمة",
|
|
@@ -369,6 +369,11 @@
|
|
|
369
369
|
},
|
|
370
370
|
"systemAgent": {
|
|
371
371
|
"title": "Системен асистент",
|
|
372
|
+
"topic": {
|
|
373
|
+
"label": "Модел за именуване на теми",
|
|
374
|
+
"modelDesc": "Модел, определен за автоматично преименуване на теми",
|
|
375
|
+
"title": "Автоматично именуване на теми"
|
|
376
|
+
},
|
|
372
377
|
"translation": {
|
|
373
378
|
"label": "Модел за превод",
|
|
374
379
|
"modelDesc": "Определя модела, използван за превод",
|
|
@@ -369,6 +369,11 @@
|
|
|
369
369
|
},
|
|
370
370
|
"systemAgent": {
|
|
371
371
|
"title": "Systemassistent",
|
|
372
|
+
"topic": {
|
|
373
|
+
"label": "Themenbenennungsmodell",
|
|
374
|
+
"modelDesc": "Das Modell, das für die automatische Umbenennung von Themen verwendet wird",
|
|
375
|
+
"title": "Automatische Themenbenennung"
|
|
376
|
+
},
|
|
372
377
|
"translation": {
|
|
373
378
|
"label": "Übersetzungsmodell",
|
|
374
379
|
"modelDesc": "Das für die Übersetzung verwendete Modell",
|
|
@@ -369,6 +369,11 @@
|
|
|
369
369
|
},
|
|
370
370
|
"systemAgent": {
|
|
371
371
|
"title": "System Agents",
|
|
372
|
+
"topic": {
|
|
373
|
+
"label": "Topic Naming Model",
|
|
374
|
+
"modelDesc": "Model designated for automatic topic renaming",
|
|
375
|
+
"title": "Automatic Topic Naming"
|
|
376
|
+
},
|
|
372
377
|
"translation": {
|
|
373
378
|
"label": "Translation Agent",
|
|
374
379
|
"modelDesc": "Specific model for translate message",
|
|
@@ -369,6 +369,11 @@
|
|
|
369
369
|
},
|
|
370
370
|
"systemAgent": {
|
|
371
371
|
"title": "Asistente del sistema",
|
|
372
|
+
"topic": {
|
|
373
|
+
"label": "Modelo de nombramiento de temas",
|
|
374
|
+
"modelDesc": "Modelo designado para el renombramiento automático de temas",
|
|
375
|
+
"title": "Renombramiento automático de temas"
|
|
376
|
+
},
|
|
372
377
|
"translation": {
|
|
373
378
|
"label": "Modelo de traducción",
|
|
374
379
|
"modelDesc": "Especifica el modelo a utilizar para la traducción",
|
|
@@ -369,6 +369,11 @@
|
|
|
369
369
|
},
|
|
370
370
|
"systemAgent": {
|
|
371
371
|
"title": "Assistant système",
|
|
372
|
+
"topic": {
|
|
373
|
+
"label": "Modèle de nommage des sujets",
|
|
374
|
+
"modelDesc": "Modèle spécifié pour le renommage automatique des sujets",
|
|
375
|
+
"title": "Renommage automatique des sujets"
|
|
376
|
+
},
|
|
372
377
|
"translation": {
|
|
373
378
|
"label": "Modèle de traduction",
|
|
374
379
|
"modelDesc": "Modèle spécifié pour la traduction",
|
|
@@ -369,6 +369,11 @@
|
|
|
369
369
|
},
|
|
370
370
|
"systemAgent": {
|
|
371
371
|
"title": "Assistente di sistema",
|
|
372
|
+
"topic": {
|
|
373
|
+
"label": "Modello di denominazione degli argomenti",
|
|
374
|
+
"modelDesc": "Modello designato per la ridenominazione automatica degli argomenti",
|
|
375
|
+
"title": "Ridenominazione automatica degli argomenti"
|
|
376
|
+
},
|
|
372
377
|
"translation": {
|
|
373
378
|
"label": "Modello di traduzione",
|
|
374
379
|
"modelDesc": "Modello specificato per la traduzione",
|
|
@@ -369,6 +369,11 @@
|
|
|
369
369
|
},
|
|
370
370
|
"systemAgent": {
|
|
371
371
|
"title": "システムアシスタント",
|
|
372
|
+
"topic": {
|
|
373
|
+
"label": "トピックネーミングモデル",
|
|
374
|
+
"modelDesc": "トピックの自動リネームに使用されるモデルを指定します",
|
|
375
|
+
"title": "トピックの自動リネーム"
|
|
376
|
+
},
|
|
372
377
|
"translation": {
|
|
373
378
|
"label": "翻訳モデル",
|
|
374
379
|
"modelDesc": "翻訳に使用するモデルを指定します",
|
|
@@ -369,6 +369,11 @@
|
|
|
369
369
|
},
|
|
370
370
|
"systemAgent": {
|
|
371
371
|
"title": "Systeemassistent",
|
|
372
|
+
"topic": {
|
|
373
|
+
"label": "Onderwerp Naamgevingsmodel",
|
|
374
|
+
"modelDesc": "Specificeer het model dat wordt gebruikt voor automatische hernoeming van onderwerpen",
|
|
375
|
+
"title": "Automatische Onderwerpnaamgeving"
|
|
376
|
+
},
|
|
372
377
|
"translation": {
|
|
373
378
|
"label": "Vertaalmodel",
|
|
374
379
|
"modelDesc": "Specificeer het model voor vertaling",
|
|
@@ -369,6 +369,11 @@
|
|
|
369
369
|
},
|
|
370
370
|
"systemAgent": {
|
|
371
371
|
"title": "Asystent Systemowy",
|
|
372
|
+
"topic": {
|
|
373
|
+
"label": "Model nazewnictwa tematów",
|
|
374
|
+
"modelDesc": "Określa model używany do automatycznego zmieniania nazw tematów",
|
|
375
|
+
"title": "Automatyczne nadawanie nazw tematom"
|
|
376
|
+
},
|
|
372
377
|
"translation": {
|
|
373
378
|
"label": "Model Tłumaczenia",
|
|
374
379
|
"modelDesc": "Określ model używany do tłumaczenia",
|
|
@@ -369,6 +369,11 @@
|
|
|
369
369
|
},
|
|
370
370
|
"systemAgent": {
|
|
371
371
|
"title": "Assistente do Sistema",
|
|
372
|
+
"topic": {
|
|
373
|
+
"label": "Modelo de Nomeação de Tópicos",
|
|
374
|
+
"modelDesc": "Especifica o modelo usado para renomeação automática de tópicos",
|
|
375
|
+
"title": "Renomeação Automática de Tópicos"
|
|
376
|
+
},
|
|
372
377
|
"translation": {
|
|
373
378
|
"label": "Modelo de Tradução",
|
|
374
379
|
"modelDesc": "Especifica o modelo usado para tradução",
|
|
@@ -369,6 +369,11 @@
|
|
|
369
369
|
},
|
|
370
370
|
"systemAgent": {
|
|
371
371
|
"title": "Системный агент",
|
|
372
|
+
"topic": {
|
|
373
|
+
"label": "Модель именования тем",
|
|
374
|
+
"modelDesc": "Модель, используемая для автоматического переименования тем",
|
|
375
|
+
"title": "Автоматическое именование тем"
|
|
376
|
+
},
|
|
372
377
|
"translation": {
|
|
373
378
|
"label": "Модель перевода",
|
|
374
379
|
"modelDesc": "Модель, используемая для перевода",
|
|
@@ -369,6 +369,11 @@
|
|
|
369
369
|
},
|
|
370
370
|
"systemAgent": {
|
|
371
371
|
"title": "Sistem Asistanı",
|
|
372
|
+
"topic": {
|
|
373
|
+
"label": "Konu Adlandırma Modeli",
|
|
374
|
+
"modelDesc": "Konuların otomatik olarak yeniden adlandırılması için belirlenen model",
|
|
375
|
+
"title": "Konu Otomatik Adlandırma"
|
|
376
|
+
},
|
|
372
377
|
"translation": {
|
|
373
378
|
"label": "Çeviri Modeli",
|
|
374
379
|
"modelDesc": "Çeviri için belirlenen model",
|
|
@@ -369,6 +369,11 @@
|
|
|
369
369
|
},
|
|
370
370
|
"systemAgent": {
|
|
371
371
|
"title": "Trợ lý hệ thống",
|
|
372
|
+
"topic": {
|
|
373
|
+
"label": "Mô hình đặt tên chủ đề",
|
|
374
|
+
"modelDesc": "Mô hình được chỉ định để tự động đặt tên chủ đề",
|
|
375
|
+
"title": "Tự động đặt tên chủ đề"
|
|
376
|
+
},
|
|
372
377
|
"translation": {
|
|
373
378
|
"label": "Mô hình dịch",
|
|
374
379
|
"modelDesc": "Chọn mô hình để dịch",
|
|
@@ -369,10 +369,15 @@
|
|
|
369
369
|
},
|
|
370
370
|
"systemAgent": {
|
|
371
371
|
"title": "系统助手",
|
|
372
|
+
"topic": {
|
|
373
|
+
"label": "话题命名模型",
|
|
374
|
+
"modelDesc": "指定用于话题自动重命名的模型",
|
|
375
|
+
"title": "话题自动命名"
|
|
376
|
+
},
|
|
372
377
|
"translation": {
|
|
373
378
|
"label": "翻译模型",
|
|
374
379
|
"modelDesc": "指定用于翻译的模型",
|
|
375
|
-
"title": "
|
|
380
|
+
"title": "消息内容翻译"
|
|
376
381
|
}
|
|
377
382
|
},
|
|
378
383
|
"tab": {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lobehub/chat",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.162.1",
|
|
4
4
|
"description": "Lobe Chat - an open-source, high-performance chatbot framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"framework",
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { Form, type ItemGroup } from '@lobehub/ui';
|
|
4
|
+
import { Form as AntForm } from 'antd';
|
|
5
|
+
import isEqual from 'fast-deep-equal';
|
|
6
|
+
import { memo } from 'react';
|
|
7
|
+
import { useTranslation } from 'react-i18next';
|
|
8
|
+
|
|
9
|
+
import { FORM_STYLE } from '@/const/layoutTokens';
|
|
10
|
+
import ModelSelect from '@/features/ModelSelect';
|
|
11
|
+
import { useUserStore } from '@/store/user';
|
|
12
|
+
import { settingsSelectors } from '@/store/user/selectors';
|
|
13
|
+
|
|
14
|
+
import { useSyncSystemAgent } from './useSync';
|
|
15
|
+
|
|
16
|
+
type SettingItemGroup = ItemGroup;
|
|
17
|
+
|
|
18
|
+
const Topic = memo(() => {
|
|
19
|
+
const { t } = useTranslation('setting');
|
|
20
|
+
const [form] = AntForm.useForm();
|
|
21
|
+
|
|
22
|
+
const settings = useUserStore(settingsSelectors.currentSystemAgent, isEqual);
|
|
23
|
+
const [updateSystemAgent] = useUserStore((s) => [s.updateSystemAgent]);
|
|
24
|
+
|
|
25
|
+
const systemAgentSettings: SettingItemGroup = {
|
|
26
|
+
children: [
|
|
27
|
+
{
|
|
28
|
+
children: (
|
|
29
|
+
<ModelSelect
|
|
30
|
+
onChange={(props) => {
|
|
31
|
+
updateSystemAgent('topic', props);
|
|
32
|
+
}}
|
|
33
|
+
/>
|
|
34
|
+
),
|
|
35
|
+
desc: t('systemAgent.topic.modelDesc'),
|
|
36
|
+
label: t('systemAgent.topic.label'),
|
|
37
|
+
name: ['topic', 'model'],
|
|
38
|
+
},
|
|
39
|
+
],
|
|
40
|
+
title: t('systemAgent.topic.title'),
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
useSyncSystemAgent(form);
|
|
44
|
+
|
|
45
|
+
return (
|
|
46
|
+
<Form
|
|
47
|
+
form={form}
|
|
48
|
+
initialValues={settings}
|
|
49
|
+
items={[systemAgentSettings]}
|
|
50
|
+
variant={'pure'}
|
|
51
|
+
{...FORM_STYLE}
|
|
52
|
+
/>
|
|
53
|
+
);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
export default Topic;
|
|
@@ -11,9 +11,7 @@ import ModelSelect from '@/features/ModelSelect';
|
|
|
11
11
|
import { useUserStore } from '@/store/user';
|
|
12
12
|
import { settingsSelectors } from '@/store/user/selectors';
|
|
13
13
|
|
|
14
|
-
import {
|
|
15
|
-
|
|
16
|
-
const SYSTEM_AGENT_SETTING_KEY = 'systemAgent';
|
|
14
|
+
import { useSyncSystemAgent } from './useSync';
|
|
17
15
|
|
|
18
16
|
type SettingItemGroup = ItemGroup;
|
|
19
17
|
|
|
@@ -21,8 +19,8 @@ const Translation = memo(() => {
|
|
|
21
19
|
const { t } = useTranslation('setting');
|
|
22
20
|
const [form] = AntForm.useForm();
|
|
23
21
|
|
|
24
|
-
const settings = useUserStore(settingsSelectors.
|
|
25
|
-
const [
|
|
22
|
+
const settings = useUserStore(settingsSelectors.currentSystemAgent, isEqual);
|
|
23
|
+
const [updateSystemAgent] = useUserStore((s) => [s.updateSystemAgent]);
|
|
26
24
|
|
|
27
25
|
const systemAgentSettings: SettingItemGroup = {
|
|
28
26
|
children: [
|
|
@@ -30,19 +28,19 @@ const Translation = memo(() => {
|
|
|
30
28
|
children: (
|
|
31
29
|
<ModelSelect
|
|
32
30
|
onChange={(props) => {
|
|
33
|
-
|
|
31
|
+
updateSystemAgent('translation', props);
|
|
34
32
|
}}
|
|
35
33
|
/>
|
|
36
34
|
),
|
|
37
35
|
desc: t('systemAgent.translation.modelDesc'),
|
|
38
36
|
label: t('systemAgent.translation.label'),
|
|
39
|
-
name: [
|
|
37
|
+
name: ['translation', 'model'],
|
|
40
38
|
},
|
|
41
39
|
],
|
|
42
40
|
title: t('systemAgent.translation.title'),
|
|
43
41
|
};
|
|
44
42
|
|
|
45
|
-
|
|
43
|
+
useSyncSystemAgent(form);
|
|
46
44
|
|
|
47
45
|
return (
|
|
48
46
|
<Form
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { FormInstance } from 'antd';
|
|
2
|
+
import { useLayoutEffect } from 'react';
|
|
3
|
+
|
|
4
|
+
import { useUserStore } from '@/store/user';
|
|
5
|
+
|
|
6
|
+
export const useSyncSystemAgent = (form: FormInstance) => {
|
|
7
|
+
useLayoutEffect(() => {
|
|
8
|
+
// set the first time
|
|
9
|
+
form.setFieldsValue(useUserStore.getState().settings.systemAgent);
|
|
10
|
+
|
|
11
|
+
// sync with later updated settings
|
|
12
|
+
const unsubscribe = useUserStore.subscribe(
|
|
13
|
+
(s) => s.settings.systemAgent,
|
|
14
|
+
(settings) => {
|
|
15
|
+
form.setFieldsValue(settings);
|
|
16
|
+
},
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
return () => {
|
|
20
|
+
unsubscribe();
|
|
21
|
+
};
|
|
22
|
+
}, []);
|
|
23
|
+
};
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { type AuthObject } from '@clerk/backend/internal';
|
|
2
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
3
|
+
|
|
4
|
+
import { getAppConfig } from '@/config/app';
|
|
5
|
+
import { NON_HTTP_PREFIX } from '@/const/auth';
|
|
6
|
+
|
|
7
|
+
import { checkAuthMethod, getJWTPayload } from './utils';
|
|
8
|
+
|
|
9
|
+
let enableClerkMock = false;
|
|
10
|
+
let enableNextAuthMock = false;
|
|
11
|
+
|
|
12
|
+
vi.mock('@/const/auth', async (importOriginal) => {
|
|
13
|
+
const data = await importOriginal();
|
|
14
|
+
|
|
15
|
+
return {
|
|
16
|
+
...(data as any),
|
|
17
|
+
get enableClerk() {
|
|
18
|
+
return enableClerkMock;
|
|
19
|
+
},
|
|
20
|
+
get enableNextAuth() {
|
|
21
|
+
return enableNextAuthMock;
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
vi.mock('@/config/app', () => ({
|
|
27
|
+
getAppConfig: vi.fn(),
|
|
28
|
+
}));
|
|
29
|
+
|
|
30
|
+
describe('getJWTPayload', () => {
|
|
31
|
+
it('should parse JWT payload for non-HTTPS token', async () => {
|
|
32
|
+
const token = `${NON_HTTP_PREFIX}.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ`;
|
|
33
|
+
const payload = await getJWTPayload(token);
|
|
34
|
+
expect(payload).toEqual({
|
|
35
|
+
sub: '1234567890',
|
|
36
|
+
name: 'John Doe',
|
|
37
|
+
iat: 1516239022,
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('should verify and parse JWT payload for HTTPS token', async () => {
|
|
42
|
+
const token =
|
|
43
|
+
'eyJhbGciOiJIUzI1NiJ9.eyJhY2Nlc3NDb2RlIjoiIiwidXNlcklkIjoiMDAxMzYyYzMtNDhjNS00NjM1LWJkM2ItODM3YmZmZjU4ZmMwIiwiYXBpS2V5IjoiYWJjIiwiZW5kcG9pbnQiOiJhYmMiLCJpYXQiOjE3MTY4MDIyMjUsImV4cCI6MTAwMDAwMDAwMDE3MTY4MDIwMDB9.FF0FxsE8Cajs-_hv5GD0TNUDwvekAkI9l_LL_IOPdGQ';
|
|
44
|
+
const payload = await getJWTPayload(token);
|
|
45
|
+
expect(payload).toEqual({
|
|
46
|
+
accessCode: '',
|
|
47
|
+
apiKey: 'abc',
|
|
48
|
+
endpoint: 'abc',
|
|
49
|
+
exp: 10000000001716802000,
|
|
50
|
+
iat: 1716802225,
|
|
51
|
+
userId: '001362c3-48c5-4635-bd3b-837bfff58fc0',
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('should not verify success and parse JWT payload for dated token', async () => {
|
|
56
|
+
const token =
|
|
57
|
+
'eyJhbGciOiJIUzI1NiJ9.eyJhY2Nlc3NDb2RlIjoiIiwidXNlcklkIjoiYWY3M2JhODktZjFhMy00YjliLWEwM2QtZGViZmZlMzE4NmQxIiwiYXBpS2V5IjoiYWJjIiwiZW5kcG9pbnQiOiJhYmMiLCJpYXQiOjE3MTY3OTk5ODAsImV4cCI6MTcxNjgwMDA4MH0.8AGFsLcwyrQG82kVUYOGFXHIwihm2n16ctyArKW9100';
|
|
58
|
+
try {
|
|
59
|
+
await getJWTPayload(token);
|
|
60
|
+
} catch (e) {
|
|
61
|
+
expect(e).toEqual(new TypeError('"exp" claim timestamp check failed'));
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
describe('checkAuthMethod', () => {
|
|
67
|
+
beforeEach(() => {
|
|
68
|
+
vi.mocked(getAppConfig).mockReturnValue({
|
|
69
|
+
ACCESS_CODES: ['validAccessCode'],
|
|
70
|
+
} as any);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('should pass with valid Clerk auth', () => {
|
|
74
|
+
enableClerkMock = true;
|
|
75
|
+
expect(() =>
|
|
76
|
+
checkAuthMethod({
|
|
77
|
+
clerkAuth: { userId: 'someUserId' } as AuthObject,
|
|
78
|
+
}),
|
|
79
|
+
).not.toThrow();
|
|
80
|
+
|
|
81
|
+
enableClerkMock = false;
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('should throw error with invalid Clerk auth', () => {
|
|
85
|
+
enableClerkMock = true;
|
|
86
|
+
try {
|
|
87
|
+
checkAuthMethod({
|
|
88
|
+
clerkAuth: {} as any,
|
|
89
|
+
});
|
|
90
|
+
} catch (e) {
|
|
91
|
+
expect(e).toEqual({ errorType: 'InvalidClerkUser' });
|
|
92
|
+
}
|
|
93
|
+
enableClerkMock = false;
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('should pass with valid Next auth', () => {
|
|
97
|
+
enableNextAuthMock = true;
|
|
98
|
+
expect(() =>
|
|
99
|
+
checkAuthMethod({
|
|
100
|
+
nextAuthAuthorized: true,
|
|
101
|
+
}),
|
|
102
|
+
).not.toThrow();
|
|
103
|
+
|
|
104
|
+
enableNextAuthMock = false;
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it('should pass with valid API key', () => {
|
|
108
|
+
expect(() =>
|
|
109
|
+
checkAuthMethod({
|
|
110
|
+
apiKey: 'someApiKey',
|
|
111
|
+
}),
|
|
112
|
+
).not.toThrow();
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it('should pass with no access code required', () => {
|
|
116
|
+
vi.mocked(getAppConfig).mockReturnValueOnce({
|
|
117
|
+
ACCESS_CODES: [],
|
|
118
|
+
} as any);
|
|
119
|
+
|
|
120
|
+
expect(() => checkAuthMethod({})).not.toThrow();
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it('should pass with valid access code', () => {
|
|
124
|
+
expect(() =>
|
|
125
|
+
checkAuthMethod({
|
|
126
|
+
accessCode: 'validAccessCode',
|
|
127
|
+
}),
|
|
128
|
+
).not.toThrow();
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it('should throw error with invalid access code', () => {
|
|
132
|
+
try {
|
|
133
|
+
checkAuthMethod({
|
|
134
|
+
accessCode: 'invalidAccessCode',
|
|
135
|
+
});
|
|
136
|
+
} catch (e) {
|
|
137
|
+
expect(e).toEqual({
|
|
138
|
+
errorType: 'InvalidAccessCode',
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
try {
|
|
143
|
+
checkAuthMethod({});
|
|
144
|
+
} catch (e) {
|
|
145
|
+
expect(e).toEqual({
|
|
146
|
+
errorType: 'InvalidAccessCode',
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
});
|
|
@@ -21,52 +21,6 @@ vi.mock('@/store/chat/helpers', () => ({
|
|
|
21
21
|
}));
|
|
22
22
|
|
|
23
23
|
describe('chainSummaryTitle', () => {
|
|
24
|
-
it('should create a payload with system and user messages and select the appropriate model based on token count', async () => {
|
|
25
|
-
// Arrange
|
|
26
|
-
const messages: OpenAIChatMessage[] = [
|
|
27
|
-
{ content: 'Hello, how can I assist you?', role: 'assistant' },
|
|
28
|
-
{ content: 'I need help with my account.', role: 'user' },
|
|
29
|
-
];
|
|
30
|
-
const currentLanguage = 'en-US';
|
|
31
|
-
const tokenCount = 17000; // Arbitrary token count above the GPT-3.5 limit
|
|
32
|
-
(globalHelpers.getCurrentLanguage as Mock).mockReturnValue(currentLanguage);
|
|
33
|
-
(chatHelpers.getMessagesTokenCount as Mock).mockResolvedValue(tokenCount);
|
|
34
|
-
|
|
35
|
-
// Act
|
|
36
|
-
const result = await chainSummaryTitle(messages);
|
|
37
|
-
|
|
38
|
-
// Assert
|
|
39
|
-
expect(result).toEqual({
|
|
40
|
-
messages: [
|
|
41
|
-
{
|
|
42
|
-
content: '你是一名擅长会话的助理,你需要将用户的会话总结为 10 个字以内的标题',
|
|
43
|
-
role: 'system',
|
|
44
|
-
},
|
|
45
|
-
{
|
|
46
|
-
content: `assistant: Hello, how can I assist you?\nuser: I need help with my account.
|
|
47
|
-
|
|
48
|
-
请总结上述对话为10个字以内的标题,不需要包含标点符号,输出语言语种为:${currentLanguage}`,
|
|
49
|
-
role: 'user',
|
|
50
|
-
},
|
|
51
|
-
],
|
|
52
|
-
model: 'gpt-4-turbo-preview',
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
// Verify that getMessagesTokenCount was called with the correct messages
|
|
56
|
-
expect(chatHelpers.getMessagesTokenCount).toHaveBeenCalledWith([
|
|
57
|
-
{
|
|
58
|
-
content: '你是一名擅长会话的助理,你需要将用户的会话总结为 10 个字以内的标题',
|
|
59
|
-
role: 'system',
|
|
60
|
-
},
|
|
61
|
-
{
|
|
62
|
-
content: `assistant: Hello, how can I assist you?\nuser: I need help with my account.
|
|
63
|
-
|
|
64
|
-
请总结上述对话为10个字以内的标题,不需要包含标点符号,输出语言语种为:${currentLanguage}`,
|
|
65
|
-
role: 'user',
|
|
66
|
-
},
|
|
67
|
-
]);
|
|
68
|
-
});
|
|
69
|
-
|
|
70
24
|
it('should use the default model if the token count is below the GPT-3.5 limit', async () => {
|
|
71
25
|
// Arrange
|
|
72
26
|
const messages: OpenAIChatMessage[] = [
|
|
@@ -95,21 +49,6 @@ describe('chainSummaryTitle', () => {
|
|
|
95
49
|
role: 'user',
|
|
96
50
|
},
|
|
97
51
|
],
|
|
98
|
-
// No model specified since the token count is below the limit
|
|
99
52
|
});
|
|
100
|
-
|
|
101
|
-
// Verify that getMessagesTokenCount was called with the correct messages
|
|
102
|
-
expect(chatHelpers.getMessagesTokenCount).toHaveBeenCalledWith([
|
|
103
|
-
{
|
|
104
|
-
content: '你是一名擅长会话的助理,你需要将用户的会话总结为 10 个字以内的标题',
|
|
105
|
-
role: 'system',
|
|
106
|
-
},
|
|
107
|
-
{
|
|
108
|
-
content: `assistant: Hello, how can I assist you?\nuser: I need help with my account.
|
|
109
|
-
|
|
110
|
-
请总结上述对话为10个字以内的标题,不需要包含标点符号,输出语言语种为:${currentLanguage}`,
|
|
111
|
-
role: 'user',
|
|
112
|
-
},
|
|
113
|
-
]);
|
|
114
53
|
});
|
|
115
54
|
});
|
|
@@ -1,33 +1,21 @@
|
|
|
1
|
-
import { chatHelpers } from '@/store/chat/helpers';
|
|
2
1
|
import { globalHelpers } from '@/store/user/helpers';
|
|
3
2
|
import { ChatStreamPayload, OpenAIChatMessage } from '@/types/openai/chat';
|
|
4
3
|
|
|
5
|
-
export const chainSummaryTitle =
|
|
6
|
-
messages: OpenAIChatMessage[],
|
|
7
|
-
): Promise<Partial<ChatStreamPayload>> => {
|
|
4
|
+
export const chainSummaryTitle = (messages: OpenAIChatMessage[]): Partial<ChatStreamPayload> => {
|
|
8
5
|
const lang = globalHelpers.getCurrentLanguage();
|
|
9
6
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
7
|
+
return {
|
|
8
|
+
messages: [
|
|
9
|
+
{
|
|
10
|
+
content: '你是一名擅长会话的助理,你需要将用户的会话总结为 10 个字以内的标题',
|
|
11
|
+
role: 'system',
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
content: `${messages.map((message) => `${message.role}: ${message.content}`).join('\n')}
|
|
17
15
|
|
|
18
16
|
请总结上述对话为10个字以内的标题,不需要包含标点符号,输出语言语种为:${lang}`,
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
// 如果超过 16k,则使用 GPT-4-turbo 模型
|
|
23
|
-
const tokens = await chatHelpers.getMessagesTokenCount(finalMessages);
|
|
24
|
-
let model: string | undefined = undefined;
|
|
25
|
-
if (tokens > 16_000) {
|
|
26
|
-
model = 'gpt-4-turbo-preview';
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
return {
|
|
30
|
-
messages: finalMessages,
|
|
31
|
-
model,
|
|
17
|
+
role: 'user',
|
|
18
|
+
},
|
|
19
|
+
],
|
|
32
20
|
};
|
|
33
21
|
};
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { SystemAgentItem, UserSystemAgentConfig } from '@/types/user/settings';
|
|
2
2
|
|
|
3
3
|
import { DEFAULT_MODEL, DEFAULT_PROVIDER } from './llm';
|
|
4
4
|
|
|
5
|
-
export const
|
|
5
|
+
export const DEFAULT_SYSTEM_AGENT_ITEM: SystemAgentItem = {
|
|
6
6
|
model: DEFAULT_MODEL,
|
|
7
7
|
provider: DEFAULT_PROVIDER,
|
|
8
8
|
};
|
|
9
9
|
|
|
10
10
|
export const DEFAULT_SYSTEM_AGENT_CONFIG: UserSystemAgentConfig = {
|
|
11
|
-
|
|
11
|
+
topic: DEFAULT_SYSTEM_AGENT_ITEM,
|
|
12
|
+
translation: DEFAULT_SYSTEM_AGENT_ITEM,
|
|
12
13
|
};
|
|
@@ -22,13 +22,11 @@ const APIs = memo<{
|
|
|
22
22
|
columns={[
|
|
23
23
|
{
|
|
24
24
|
dataIndex: 'name',
|
|
25
|
-
ellipsis: true,
|
|
26
25
|
render: (name: string) => <code>{name}</code>,
|
|
27
26
|
title: t('detailModal.info.name'),
|
|
28
27
|
},
|
|
29
28
|
{
|
|
30
29
|
dataIndex: 'description',
|
|
31
|
-
ellipsis: true,
|
|
32
30
|
title: t('detailModal.info.description'),
|
|
33
31
|
},
|
|
34
32
|
]}
|
|
@@ -36,6 +34,7 @@ const APIs = memo<{
|
|
|
36
34
|
pagination={false}
|
|
37
35
|
rowKey={'name'}
|
|
38
36
|
size={'small'}
|
|
37
|
+
tableLayout="fixed"
|
|
39
38
|
/>
|
|
40
39
|
</Flexbox>
|
|
41
40
|
);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { renderHook, waitFor } from '@testing-library/react';
|
|
2
2
|
import { describe, expect, it, vi } from 'vitest';
|
|
3
3
|
|
|
4
4
|
import * as tokenizers from '@/utils/tokenizer';
|
|
@@ -6,7 +6,8 @@ import * as tokenizers from '@/utils/tokenizer';
|
|
|
6
6
|
import { useTokenCount } from './useTokenCount';
|
|
7
7
|
|
|
8
8
|
describe('useTokenCount', () => {
|
|
9
|
-
|
|
9
|
+
// TODO: need to be fixed in the future
|
|
10
|
+
it.skip('should return token count for given input', async () => {
|
|
10
11
|
const { result } = renderHook(() => useTokenCount('test input'));
|
|
11
12
|
|
|
12
13
|
expect(result.current).toBe(0);
|
|
@@ -21,6 +22,8 @@ describe('useTokenCount', () => {
|
|
|
21
22
|
|
|
22
23
|
expect(result.current).toBe(0);
|
|
23
24
|
await waitFor(() => expect(result.current).toBe(0));
|
|
25
|
+
|
|
26
|
+
mockEncodeAsync.mockClear();
|
|
24
27
|
});
|
|
25
28
|
|
|
26
29
|
it('should handle empty input', async () => {
|
|
@@ -373,10 +373,15 @@ export default {
|
|
|
373
373
|
},
|
|
374
374
|
systemAgent: {
|
|
375
375
|
title: '系统助手',
|
|
376
|
+
topic: {
|
|
377
|
+
label: '话题命名模型',
|
|
378
|
+
modelDesc: '指定用于话题自动重命名的模型',
|
|
379
|
+
title: '话题自动命名',
|
|
380
|
+
},
|
|
376
381
|
translation: {
|
|
377
382
|
label: '翻译模型',
|
|
378
383
|
modelDesc: '指定用于翻译的模型',
|
|
379
|
-
title: '
|
|
384
|
+
title: '消息内容翻译',
|
|
380
385
|
},
|
|
381
386
|
},
|
|
382
387
|
tab: {
|
|
@@ -16,8 +16,11 @@ import { messageService } from '@/services/message';
|
|
|
16
16
|
import { topicService } from '@/services/topic';
|
|
17
17
|
import { CreateTopicParams } from '@/services/topic/type';
|
|
18
18
|
import type { ChatStore } from '@/store/chat';
|
|
19
|
+
import { useUserStore } from '@/store/user';
|
|
20
|
+
import { systemAgentSelectors } from '@/store/user/selectors';
|
|
19
21
|
import { ChatMessage } from '@/types/message';
|
|
20
22
|
import { ChatTopic } from '@/types/topic';
|
|
23
|
+
import { merge } from '@/utils/merge';
|
|
21
24
|
import { setNamespace } from '@/utils/storeDebug';
|
|
22
25
|
|
|
23
26
|
import { chatSelectors } from '../message/selectors';
|
|
@@ -140,7 +143,10 @@ export const chatTopic: StateCreator<
|
|
|
140
143
|
|
|
141
144
|
let output = '';
|
|
142
145
|
|
|
143
|
-
//
|
|
146
|
+
// Get current agent for topic
|
|
147
|
+
const topicConfig = systemAgentSelectors.topic(useUserStore.getState());
|
|
148
|
+
|
|
149
|
+
// Automatically summarize the topic title
|
|
144
150
|
await chatService.fetchPresetTaskResult({
|
|
145
151
|
onError: () => {
|
|
146
152
|
internal_updateTopicTitleInSummary(topicId, topic.title);
|
|
@@ -160,7 +166,7 @@ export const chatTopic: StateCreator<
|
|
|
160
166
|
|
|
161
167
|
internal_updateTopicTitleInSummary(topicId, output);
|
|
162
168
|
},
|
|
163
|
-
params:
|
|
169
|
+
params: merge(topicConfig, chainSummaryTitle(messages)),
|
|
164
170
|
trace: get().getCurrentTracePayload({ traceName: TraceNameMap.SummaryTopicTitle, topicId }),
|
|
165
171
|
});
|
|
166
172
|
},
|
|
@@ -148,7 +148,7 @@ describe('SessionAction', () => {
|
|
|
148
148
|
});
|
|
149
149
|
|
|
150
150
|
describe('pinSession', () => {
|
|
151
|
-
it
|
|
151
|
+
it('should pin a session when pinned is true', async () => {
|
|
152
152
|
const { result } = renderHook(() => useSessionStore());
|
|
153
153
|
const sessionId = 'session-id-to-pin';
|
|
154
154
|
|
|
@@ -160,7 +160,7 @@ describe('SessionAction', () => {
|
|
|
160
160
|
expect(mockRefresh).toHaveBeenCalled();
|
|
161
161
|
});
|
|
162
162
|
|
|
163
|
-
it
|
|
163
|
+
it('should unpin a session when pinned is false', async () => {
|
|
164
164
|
const { result } = renderHook(() => useSessionStore());
|
|
165
165
|
const sessionId = 'session-id-to-unpin';
|
|
166
166
|
|
|
@@ -115,10 +115,10 @@ describe('SettingsAction', () => {
|
|
|
115
115
|
});
|
|
116
116
|
});
|
|
117
117
|
|
|
118
|
-
describe('
|
|
118
|
+
describe('updateSystemAgent', () => {
|
|
119
119
|
it('should set partial settings', async () => {
|
|
120
120
|
const { result } = renderHook(() => useUserStore());
|
|
121
|
-
const systemAgentSettings:
|
|
121
|
+
const systemAgentSettings: DeepPartial<UserSettings> = {
|
|
122
122
|
systemAgent: {
|
|
123
123
|
translation: {
|
|
124
124
|
model: 'testmodel',
|
|
@@ -129,7 +129,10 @@ describe('SettingsAction', () => {
|
|
|
129
129
|
|
|
130
130
|
// Perform the action
|
|
131
131
|
await act(async () => {
|
|
132
|
-
await result.current.
|
|
132
|
+
await result.current.updateSystemAgent('translation', {
|
|
133
|
+
provider: 'provider',
|
|
134
|
+
model: 'testmodel',
|
|
135
|
+
});
|
|
133
136
|
});
|
|
134
137
|
|
|
135
138
|
// Assert that updateUserSettings was called with the correct settings
|
|
@@ -16,12 +16,12 @@ export interface UserSettingsAction {
|
|
|
16
16
|
importAppSettings: (settings: UserSettings) => Promise<void>;
|
|
17
17
|
resetSettings: () => Promise<void>;
|
|
18
18
|
setSettings: (settings: DeepPartial<UserSettings>) => Promise<void>;
|
|
19
|
-
setTranslationSystemAgent: (provider: string, model: string) => Promise<void>;
|
|
20
19
|
switchLocale: (locale: LocaleMode) => Promise<void>;
|
|
21
20
|
switchThemeMode: (themeMode: ThemeMode) => Promise<void>;
|
|
22
21
|
updateDefaultAgent: (agent: DeepPartial<LobeAgentSettings>) => Promise<void>;
|
|
23
22
|
updateGeneralConfig: (settings: Partial<UserGeneralConfig>) => Promise<void>;
|
|
24
23
|
updateKeyVaults: (settings: Partial<UserKeyVaults>) => Promise<void>;
|
|
24
|
+
updateSystemAgent: (key: string, value: { model: string; provider: string }) => Promise<void>;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
export const createSettingsSlice: StateCreator<
|
|
@@ -51,16 +51,6 @@ export const createSettingsSlice: StateCreator<
|
|
|
51
51
|
await userService.updateUserSettings(diffs);
|
|
52
52
|
await get().refreshUserState();
|
|
53
53
|
},
|
|
54
|
-
setTranslationSystemAgent: async (provider, model) => {
|
|
55
|
-
await get().setSettings({
|
|
56
|
-
systemAgent: {
|
|
57
|
-
translation: {
|
|
58
|
-
model: model,
|
|
59
|
-
provider: provider,
|
|
60
|
-
},
|
|
61
|
-
},
|
|
62
|
-
});
|
|
63
|
-
},
|
|
64
54
|
switchLocale: async (locale) => {
|
|
65
55
|
await get().updateGeneralConfig({ language: locale });
|
|
66
56
|
|
|
@@ -78,4 +68,9 @@ export const createSettingsSlice: StateCreator<
|
|
|
78
68
|
updateKeyVaults: async (keyVaults) => {
|
|
79
69
|
await get().setSettings({ keyVaults });
|
|
80
70
|
},
|
|
71
|
+
updateSystemAgent: async (key, { provider, model }) => {
|
|
72
|
+
await get().setSettings({
|
|
73
|
+
systemAgent: { [key]: { model, provider } },
|
|
74
|
+
});
|
|
75
|
+
},
|
|
81
76
|
});
|
|
@@ -8,7 +8,9 @@ const currentSystemAgent = (s: UserStore) =>
|
|
|
8
8
|
merge(DEFAULT_SYSTEM_AGENT_CONFIG, currentSettings(s).systemAgent);
|
|
9
9
|
|
|
10
10
|
const translation = (s: UserStore) => currentSystemAgent(s).translation;
|
|
11
|
+
const topic = (s: UserStore) => currentSystemAgent(s).topic;
|
|
11
12
|
|
|
12
13
|
export const systemAgentSelectors = {
|
|
14
|
+
topic,
|
|
13
15
|
translation,
|
|
14
16
|
};
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
export interface
|
|
1
|
+
export interface SystemAgentItem {
|
|
2
2
|
model: string;
|
|
3
3
|
provider: string;
|
|
4
4
|
}
|
|
5
5
|
|
|
6
6
|
export interface UserSystemAgentConfig {
|
|
7
|
-
|
|
7
|
+
topic: SystemAgentItem;
|
|
8
|
+
translation: SystemAgentItem;
|
|
8
9
|
}
|