@lobehub/chat 0.147.20 → 0.147.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +42 -0
- package/README.md +8 -8
- package/README.zh-CN.md +8 -8
- package/locales/ar/chat.json +3 -0
- package/locales/bg-BG/chat.json +3 -0
- package/locales/de-DE/chat.json +3 -0
- package/locales/en-US/chat.json +3 -0
- package/locales/es-ES/chat.json +3 -0
- package/locales/fr-FR/chat.json +3 -0
- package/locales/it-IT/chat.json +3 -0
- package/locales/ja-JP/chat.json +3 -0
- package/locales/ko-KR/chat.json +3 -0
- package/locales/nl-NL/chat.json +3 -0
- package/locales/pl-PL/chat.json +3 -0
- package/locales/pt-BR/chat.json +3 -0
- package/locales/ru-RU/chat.json +3 -0
- package/locales/tr-TR/chat.json +3 -0
- package/locales/vi-VN/chat.json +3 -0
- package/locales/zh-CN/chat.json +4 -1
- package/locales/zh-TW/chat.json +3 -0
- package/package.json +2 -2
- package/public/favicon-32x32.ico +0 -0
- package/public/favicon.ico +0 -0
- package/public/icons/apple-touch-icon.png +0 -0
- package/src/app/chat/(desktop)/features/ChatInput/Footer/DragUpload.tsx +3 -3
- package/src/app/chat/features/TopicListContent/Topic/TopicContent.tsx +2 -2
- package/src/app/metadata.ts +3 -5
- package/src/features/ChatInput/ActionBar/FileUpload.tsx +3 -3
- package/src/features/ChatInput/Topic/index.tsx +6 -2
- package/src/features/Conversation/components/ChatItem/index.tsx +8 -3
- package/src/libs/swr/index.ts +9 -0
- package/src/locales/default/chat.ts +5 -2
- package/src/store/chat/slices/message/action.ts +80 -42
- package/src/store/chat/slices/message/initialState.ts +1 -1
- package/src/store/chat/slices/message/reducer.ts +32 -1
- package/src/store/chat/slices/topic/action.test.ts +25 -2
- package/src/store/chat/slices/topic/action.ts +24 -7
- package/src/store/chat/slices/topic/reducer.test.ts +141 -0
- package/src/store/chat/slices/topic/reducer.ts +67 -0
- package/src/store/session/slices/session/action.ts +4 -5
- package/vercel.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,48 @@
|
|
|
2
2
|
|
|
3
3
|
# Changelog
|
|
4
4
|
|
|
5
|
+
### [Version 0.147.22](https://github.com/lobehub/lobe-chat/compare/v0.147.21...v0.147.22)
|
|
6
|
+
|
|
7
|
+
<sup>Released on **2024-04-19**</sup>
|
|
8
|
+
|
|
9
|
+
<br/>
|
|
10
|
+
|
|
11
|
+
<details>
|
|
12
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
13
|
+
|
|
14
|
+
</details>
|
|
15
|
+
|
|
16
|
+
<div align="right">
|
|
17
|
+
|
|
18
|
+
[](#readme-top)
|
|
19
|
+
|
|
20
|
+
</div>
|
|
21
|
+
|
|
22
|
+
### [Version 0.147.21](https://github.com/lobehub/lobe-chat/compare/v0.147.20...v0.147.21)
|
|
23
|
+
|
|
24
|
+
<sup>Released on **2024-04-19**</sup>
|
|
25
|
+
|
|
26
|
+
#### 💄 Styles
|
|
27
|
+
|
|
28
|
+
- **misc**: Optimized file upload buttons and prompts.
|
|
29
|
+
|
|
30
|
+
<br/>
|
|
31
|
+
|
|
32
|
+
<details>
|
|
33
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
34
|
+
|
|
35
|
+
#### Styles
|
|
36
|
+
|
|
37
|
+
- **misc**: Optimized file upload buttons and prompts, closes [#2050](https://github.com/lobehub/lobe-chat/issues/2050) ([c23087e](https://github.com/lobehub/lobe-chat/commit/c23087e))
|
|
38
|
+
|
|
39
|
+
</details>
|
|
40
|
+
|
|
41
|
+
<div align="right">
|
|
42
|
+
|
|
43
|
+
[](#readme-top)
|
|
44
|
+
|
|
45
|
+
</div>
|
|
46
|
+
|
|
5
47
|
### [Version 0.147.20](https://github.com/lobehub/lobe-chat/compare/v0.147.19...v0.147.20)
|
|
6
48
|
|
|
7
49
|
<sup>Released on **2024-04-18**</sup>
|
package/README.md
CHANGED
|
@@ -260,14 +260,14 @@ Our marketplace is not just a showcase platform but also a collaborative space.
|
|
|
260
260
|
|
|
261
261
|
<!-- AGENT LIST -->
|
|
262
262
|
|
|
263
|
-
| Recent Submits
|
|
264
|
-
|
|
|
265
|
-
| [
|
|
266
|
-
| [
|
|
267
|
-
| [
|
|
268
|
-
| [
|
|
269
|
-
|
|
270
|
-
> 📊 Total agents: [<kbd>**
|
|
263
|
+
| Recent Submits | Description |
|
|
264
|
+
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
265
|
+
| [Chinese Academic Paper Editor](https://chat-preview.lobehub.com/market?agent=chinese-paper-polishing)<br/><sup>By **[y22emc2](https://github.com/y22emc2)** on **2024-04-15**</sup> | As an assistant for improving Chinese academic paper writing, your task is to enhance the provided text in terms of spelling, grammar, clarity, conciseness, and overall readability, to improve the text's academic standards and literary quality. This includes breaking down long sentences, reducing repetition, and providing improvement suggestions. Please provide the corrected version of the text first, then list the modifications in the markdown table below, along with the reasons for the changes.<br/>`academic-paper-writing` `proofreading` `text-editing` |
|
|
266
|
+
| [Biology Professor](https://chat-preview.lobehub.com/market?agent=bio-professor)<br/><sup>By **[luxiangze](https://github.com/luxiangze)** on **2024-04-13**</sup> | As a biology professor, you will receive questions and concepts about biology. Please explain these questions and concepts in specific and concise language, and try to use practical examples to illustrate them to help your audience better understand. Please ensure that your explanations are accurate and clear, and try to encourage creative and flexible answers. Respond in Chinese.<br/>`biology` |
|
|
267
|
+
| [Master of Fortune Telling](https://chat-preview.lobehub.com/market?agent=fortune-teller)<br/><sup>By **[kamilkenrich](https://github.com/kamilkenrich)** on **2024-04-13**</sup> | Specializes in numerology, divination, astrology, and blood type analysis<br/>`numerology` `divination` `astrology` `psychology` `blood-type` `constellation` |
|
|
268
|
+
| [High School Science Study Assistant](https://chat-preview.lobehub.com/market?agent=highschool-master)<br/><sup>By **[cnliucheng](https://github.com/cnliucheng)** on **2024-04-13**</sup> | I am a study assistant AI specially designed for Chinese high school students. Whether you encounter difficulties in physics, chemistry, mathematics, or biology, I can provide detailed answers and explanations for you. Not only that, I can also recommend suitable practice questions based on your learning progress to help you consolidate knowledge and improve learning efficiency. Additionally, I will use LaTeX format as much as possible to present the solution process and formulas.<br/>`high-school-study` `science-assistant` `problem-solving` `learning-progress` `la-te-x` |
|
|
269
|
+
|
|
270
|
+
> 📊 Total agents: [<kbd>**240**</kbd> ](https://github.com/lobehub/lobe-chat-agents)
|
|
271
271
|
|
|
272
272
|
<!-- AGENT LIST -->
|
|
273
273
|
|
package/README.zh-CN.md
CHANGED
|
@@ -248,14 +248,14 @@ LobeChat 的插件生态系统是其核心功能的重要扩展,它极大地
|
|
|
248
248
|
|
|
249
249
|
<!-- AGENT LIST -->
|
|
250
250
|
|
|
251
|
-
| 最近新增
|
|
252
|
-
|
|
|
253
|
-
| [
|
|
254
|
-
| [
|
|
255
|
-
| [
|
|
256
|
-
| [
|
|
257
|
-
|
|
258
|
-
> 📊 Total agents: [<kbd>**
|
|
251
|
+
| 最近新增 | 助手说明 |
|
|
252
|
+
| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
253
|
+
| [中文论文编辑师](https://chat-preview.lobehub.com/market?agent=chinese-paper-polishing)<br/><sup>By **[y22emc2](https://github.com/y22emc2)** on **2024-04-15**</sup> | 作为一名中文学术论文写作改进助理,你的任务是改进所提供文本的拼写、语法、清晰、简洁和整体可读性,提高文本的学术规范性、文学性,同时分解长句,减少重复,并提供改进建议。请先提供文本的更正版本,然后在 markdown 表格中列出修改的内容,并给出修改的理由。<br/>`学术论文写作` `校对` `文本编辑` |
|
|
254
|
+
| [生物学教授](https://chat-preview.lobehub.com/market?agent=bio-professor)<br/><sup>By **[luxiangze](https://github.com/luxiangze)** on **2024-04-13**</sup> | 作为一位生物学教授,您将收到关于生物学的问题和概念,请用具体而简明的语言解释这些问题和概念,并尽量使用实际例子进行说明,以帮助您的听众更好地理解。请确保您的解释准确而清晰,并试图鼓励创造性和灵活性的答案。用中文回答<br/>`生物` |
|
|
255
|
+
| [命理大师](https://chat-preview.lobehub.com/market?agent=fortune-teller)<br/><sup>By **[kamilkenrich](https://github.com/kamilkenrich)** on **2024-04-13**</sup> | 擅长命理学、占卜预测、占星术和血型学分析<br/>`命理学、占卜、占星学、心理学、血型、星座` |
|
|
256
|
+
| [高中理科学习助手](https://chat-preview.lobehub.com/market?agent=highschool-master)<br/><sup>By **[cnliucheng](https://github.com/cnliucheng)** on **2024-04-13**</sup> | 我是一个专门为中国高中生设计的学习辅助 AI。无论你在物理、化学、数学还是生物上遇到难题,我都能为你提供详细的解答和解析。不仅如此,我还能根据你的学习进度,推荐合适的练习题,帮助我巩固知识,提升学习效率。同时,我会尽可能使用 LaTeX 格式来呈现解题过程和公式<br/>`高中学习` `理科辅助` `题目解答` `学习进度` `la-te-x` |
|
|
257
|
+
|
|
258
|
+
> 📊 Total agents: [<kbd>**240**</kbd> ](https://github.com/lobehub/lobe-chat-agents)
|
|
259
259
|
|
|
260
260
|
<!-- AGENT LIST -->
|
|
261
261
|
|
package/locales/ar/chat.json
CHANGED
|
@@ -118,9 +118,12 @@
|
|
|
118
118
|
},
|
|
119
119
|
"updateAgent": "تحديث معلومات المساعد",
|
|
120
120
|
"upload": {
|
|
121
|
+
"actionFiletip": "تحميل المستند",
|
|
121
122
|
"actionTooltip": "تحميل الصورة",
|
|
122
123
|
"disabled": "النموذج الحالي لا يدعم التعرف على الرؤية، يرجى تغيير النموذج المستخدم",
|
|
123
124
|
"dragDesc": "اسحب الملفات إلى هنا، يدعم تحميل عدة صور. اضغط على Shift لإرسال الصور مباشرة",
|
|
125
|
+
"dragFileDesc": "اسحب الصور والملفات هنا لدعم تحميل صور وملفات متعددة. اضغط باستمرار على Shift لإرسال صورة أو ملف مباشرة",
|
|
126
|
+
"dragFileTitle": "تحميل المستند",
|
|
124
127
|
"dragTitle": "تحميل الصورة"
|
|
125
128
|
}
|
|
126
129
|
}
|
package/locales/bg-BG/chat.json
CHANGED
|
@@ -118,9 +118,12 @@
|
|
|
118
118
|
},
|
|
119
119
|
"updateAgent": "Актуализирай информацията за агента",
|
|
120
120
|
"upload": {
|
|
121
|
+
"actionFiletip": "Загрузите файл",
|
|
121
122
|
"actionTooltip": "Качи изображение",
|
|
122
123
|
"disabled": "Текущият модел не поддържа визуално разпознаване. Моля, превключи моделите, за да използваш тази функция.",
|
|
123
124
|
"dragDesc": "Плъзни и пусни файлове тук, поддържа се качване на няколко изображения. Задръж Shift, за да изпратиш изображения директно.",
|
|
125
|
+
"dragFileDesc": "Перетащите сюда изображения и файлы, чтобы можно было загрузить несколько изображений и файлов. Удерживайте нажатой клавишу Shift, чтобы отправить изображение или файл напрямую.",
|
|
126
|
+
"dragFileTitle": "Загрузите файл",
|
|
124
127
|
"dragTitle": "Качи изображение"
|
|
125
128
|
}
|
|
126
129
|
}
|
package/locales/de-DE/chat.json
CHANGED
|
@@ -118,9 +118,12 @@
|
|
|
118
118
|
},
|
|
119
119
|
"updateAgent": "Assistenteninformationen aktualisieren",
|
|
120
120
|
"upload": {
|
|
121
|
+
"actionFiletip": "Laden Sie die Datei hoch",
|
|
121
122
|
"actionTooltip": "Bild hochladen",
|
|
122
123
|
"disabled": "Das aktuelle Modell unterstützt keine visuelle Erkennung. Bitte wechseln Sie das Modell, um es zu verwenden.",
|
|
123
124
|
"dragDesc": "Dateien hierher ziehen, um mehrere Bilder hochzuladen. Halte die Umschalttaste gedrückt, um Bilder direkt zu senden.",
|
|
125
|
+
"dragFileDesc": "Ziehen Sie Bilder und Dateien hierher, um das Hochladen mehrerer Bilder und Dateien zu unterstützen. Halten Sie die Umschalttaste gedrückt, um ein Bild oder eine Datei direkt zu senden.",
|
|
126
|
+
"dragFileTitle": "Laden Sie die Datei hoch",
|
|
124
127
|
"dragTitle": "Bild hochladen"
|
|
125
128
|
}
|
|
126
129
|
}
|
package/locales/en-US/chat.json
CHANGED
|
@@ -118,9 +118,12 @@
|
|
|
118
118
|
},
|
|
119
119
|
"updateAgent": "Update Agent Information",
|
|
120
120
|
"upload": {
|
|
121
|
+
"actionFiletip": "Update File",
|
|
121
122
|
"actionTooltip": "Upload Image",
|
|
122
123
|
"disabled": "The current model does not support visual recognition. Please switch models to use this feature.",
|
|
123
124
|
"dragDesc": "Drag and drop files here, support uploading multiple images. Hold down Shift to send images directly.",
|
|
125
|
+
"dragFileDesc": "Drag pictures and files here to support uploading multiple pictures and files. Hold down Shift to send an image or file directly.",
|
|
126
|
+
"dragFileTitle": "Update File",
|
|
124
127
|
"dragTitle": "Upload Image"
|
|
125
128
|
}
|
|
126
129
|
}
|
package/locales/es-ES/chat.json
CHANGED
|
@@ -118,9 +118,12 @@
|
|
|
118
118
|
},
|
|
119
119
|
"updateAgent": "Actualizar información del asistente",
|
|
120
120
|
"upload": {
|
|
121
|
+
"actionFiletip": "Sube el archivo",
|
|
121
122
|
"actionTooltip": "Subir imagen",
|
|
122
123
|
"disabled": "El modelo actual no admite reconocimiento visual. Por favor, cambia de modelo para usar esta función",
|
|
123
124
|
"dragDesc": "Arrastra y suelta archivos aquí, admite la carga de varias imágenes. Mantén presionada la tecla Shift para enviar las imágenes directamente.",
|
|
125
|
+
"dragFileDesc": "Arrastre imágenes y archivos aquí para admitir la carga de varias imágenes y archivos. Mantenga presionada la tecla Mayús para enviar una imagen o un archivo directamente.",
|
|
126
|
+
"dragFileTitle": "Sube el archivo",
|
|
124
127
|
"dragTitle": "Subir imagen"
|
|
125
128
|
}
|
|
126
129
|
}
|
package/locales/fr-FR/chat.json
CHANGED
|
@@ -118,9 +118,12 @@
|
|
|
118
118
|
},
|
|
119
119
|
"updateAgent": "Mettre à jour les informations de l'agent",
|
|
120
120
|
"upload": {
|
|
121
|
+
"actionFiletip": "Télécharger le fichier",
|
|
121
122
|
"actionTooltip": "Télécharger une image",
|
|
122
123
|
"disabled": "Le modèle actuel ne prend pas en charge la reconnaissance visuelle. Veuillez changer de modèle pour utiliser cette fonctionnalité.",
|
|
123
124
|
"dragDesc": "Faites glisser les fichiers ici pour télécharger plusieurs images. Maintenez la touche Maj enfoncée pour envoyer directement les images.",
|
|
125
|
+
"dragFileDesc": "Faites glisser les images et les fichiers ici pour prendre en charge le téléchargement de plusieurs images et fichiers. Maintenez la touche Shift enfoncée pour envoyer directement une image ou un fichier.",
|
|
126
|
+
"dragFileTitle": "Télécharger le fichier",
|
|
124
127
|
"dragTitle": "Télécharger une image"
|
|
125
128
|
}
|
|
126
129
|
}
|
package/locales/it-IT/chat.json
CHANGED
|
@@ -118,9 +118,12 @@
|
|
|
118
118
|
},
|
|
119
119
|
"updateAgent": "Aggiorna informazioni assistente",
|
|
120
120
|
"upload": {
|
|
121
|
+
"actionFiletip": "Carica il file",
|
|
121
122
|
"actionTooltip": "Carica immagine",
|
|
122
123
|
"disabled": "Il modello attuale non supporta il riconoscimento visivo, si prega di cambiare modello prima di utilizzarlo",
|
|
123
124
|
"dragDesc": "Trascina i file qui, supporta il caricamento di più immagini. Tieni premuto Shift per inviare direttamente le immagini",
|
|
125
|
+
"dragFileDesc": "Trascina qui immagini e file per supportare il caricamento di più immagini e file. Tieni premuto Maiusc per inviare direttamente un'immagine o un file",
|
|
126
|
+
"dragFileTitle": "Carica il file",
|
|
124
127
|
"dragTitle": "Carica immagine"
|
|
125
128
|
}
|
|
126
129
|
}
|
package/locales/ja-JP/chat.json
CHANGED
|
@@ -118,9 +118,12 @@
|
|
|
118
118
|
},
|
|
119
119
|
"updateAgent": "エージェント情報を更新",
|
|
120
120
|
"upload": {
|
|
121
|
+
"actionFiletip": "ファイルをアップロードする",
|
|
121
122
|
"actionTooltip": "画像をアップロード",
|
|
122
123
|
"disabled": "現在のモデルはビジュアル認識をサポートしていません。モデルを切り替えて使用してください",
|
|
123
124
|
"dragDesc": "ファイルをここにドラッグしてください。複数の画像をアップロードできます。Shift キーを押しながら画像を直接送信することもできます。",
|
|
125
|
+
"dragFileDesc": "複数の写真やファイルのアップロードをサポートするには、ここに写真やファイルをドラッグします。 Shift キーを押しながら画像またはファイルを直接送信します。",
|
|
126
|
+
"dragFileTitle": "ファイルをアップロードする",
|
|
124
127
|
"dragTitle": "画像をアップロード"
|
|
125
128
|
}
|
|
126
129
|
}
|
package/locales/ko-KR/chat.json
CHANGED
|
@@ -118,9 +118,12 @@
|
|
|
118
118
|
},
|
|
119
119
|
"updateAgent": "도우미 정보 업데이트",
|
|
120
120
|
"upload": {
|
|
121
|
+
"actionFiletip": "파일 업로드",
|
|
121
122
|
"actionTooltip": "이미지 업로드",
|
|
122
123
|
"disabled": "현재 모델은 시각 인식을 지원하지 않습니다. 모델을 전환한 후 사용해주세요.",
|
|
123
124
|
"dragDesc": "여기로 파일을 끌어다 놓거나 여러 이미지를 업로드할 수 있습니다. Shift를 누른 채로 이미지를 직접 보내기",
|
|
125
|
+
"dragFileDesc": "여러 사진과 파일 업로드를 지원하려면 여기로 사진과 파일을 드래그하세요. 이미지나 파일을 직접 보내려면 Shift 키를 누르세요",
|
|
126
|
+
"dragFileTitle": "파일 업로드",
|
|
124
127
|
"dragTitle": "이미지 업로드"
|
|
125
128
|
}
|
|
126
129
|
}
|
package/locales/nl-NL/chat.json
CHANGED
|
@@ -118,9 +118,12 @@
|
|
|
118
118
|
},
|
|
119
119
|
"updateAgent": "Assistentgegevens bijwerken",
|
|
120
120
|
"upload": {
|
|
121
|
+
"actionFiletip": "Upload het bestand",
|
|
121
122
|
"actionTooltip": "Upload afbeelding",
|
|
122
123
|
"disabled": "Het huidige model ondersteunt geen visuele herkenning. Schakel over naar een ander model om dit te gebruiken.",
|
|
123
124
|
"dragDesc": "Sleep bestanden hierheen om meerdere afbeeldingen te uploaden. Houd Shift ingedrukt om afbeeldingen direct te verzenden",
|
|
125
|
+
"dragFileDesc": "Sleep afbeeldingen en bestanden hierheen om het uploaden van meerdere afbeeldingen en bestanden te ondersteunen. Houd Shift ingedrukt om een afbeelding of bestand rechtstreeks te verzenden",
|
|
126
|
+
"dragFileTitle": "Upload het bestand",
|
|
124
127
|
"dragTitle": "Upload afbeelding"
|
|
125
128
|
}
|
|
126
129
|
}
|
package/locales/pl-PL/chat.json
CHANGED
|
@@ -118,9 +118,12 @@
|
|
|
118
118
|
},
|
|
119
119
|
"updateAgent": "Aktualizuj informacje asystenta",
|
|
120
120
|
"upload": {
|
|
121
|
+
"actionFiletip": "Prześlij plik",
|
|
121
122
|
"actionTooltip": "Prześlij obraz",
|
|
122
123
|
"disabled": "Obecny model nie obsługuje rozpoznawania wizyjnego. Proszę przełączyć model.",
|
|
123
124
|
"dragDesc": "Przeciągnij pliki tutaj, obsługiwane jest przesyłanie wielu obrazów. Naciśnij klawisz Shift, aby wysłać obraz bezpośrednio",
|
|
125
|
+
"dragFileDesc": "Przeciągnij zdjęcia i pliki tutaj, aby umożliwić przesyłanie wielu zdjęć i plików. Przytrzymaj klawisz Shift, aby bezpośrednio wysłać obraz lub plik",
|
|
126
|
+
"dragFileTitle": "Prześlij plik",
|
|
124
127
|
"dragTitle": "Prześlij obraz"
|
|
125
128
|
}
|
|
126
129
|
}
|
package/locales/pt-BR/chat.json
CHANGED
|
@@ -118,9 +118,12 @@
|
|
|
118
118
|
},
|
|
119
119
|
"updateAgent": "Atualizar Informações do Assistente",
|
|
120
120
|
"upload": {
|
|
121
|
+
"actionFiletip": "Enviar arquivo",
|
|
121
122
|
"actionTooltip": "Enviar Imagem",
|
|
122
123
|
"disabled": "O modelo atual não suporta reconhecimento visual. Por favor, altere o modelo antes de usar.",
|
|
123
124
|
"dragDesc": "Arraste os arquivos aqui, suporta o envio de várias imagens. Pressione Shift para enviar as imagens diretamente.",
|
|
125
|
+
"dragFileDesc": "Arraste fotos e arquivos aqui para suportar o upload de várias fotos e arquivos. Mantenha pressionada a tecla Shift para enviar uma imagem ou arquivo diretamente.",
|
|
126
|
+
"dragFileTitle": "Enviar arquivo",
|
|
124
127
|
"dragTitle": "Enviar Imagem"
|
|
125
128
|
}
|
|
126
129
|
}
|
package/locales/ru-RU/chat.json
CHANGED
|
@@ -118,9 +118,12 @@
|
|
|
118
118
|
},
|
|
119
119
|
"updateAgent": "Обновить информацию помощника",
|
|
120
120
|
"upload": {
|
|
121
|
+
"actionFiletip": "Загрузите файл",
|
|
121
122
|
"actionTooltip": "Загрузить изображение",
|
|
122
123
|
"disabled": "Текущая модель не поддерживает визуальное распознавание. Пожалуйста, выберите другую модель.",
|
|
123
124
|
"dragDesc": "Перетащите файлы сюда, поддерживается загрузка нескольких изображений. Удерживайте Shift для отправки изображений",
|
|
125
|
+
"dragFileDesc": "Перетащите сюда изображения и файлы, чтобы можно было загрузить несколько изображений и файлов. Удерживайте нажатой клавишу Shift, чтобы отправить изображение или файл напрямую",
|
|
126
|
+
"dragFileTitle": "Загрузите файл",
|
|
124
127
|
"dragTitle": "Загрузить изображение"
|
|
125
128
|
}
|
|
126
129
|
}
|
package/locales/tr-TR/chat.json
CHANGED
|
@@ -118,9 +118,12 @@
|
|
|
118
118
|
},
|
|
119
119
|
"updateAgent": "Asistan Bilgilerini Güncelle",
|
|
120
120
|
"upload": {
|
|
121
|
+
"actionFiletip": "Dosyayı Yükle",
|
|
121
122
|
"actionTooltip": "Resim Yükle",
|
|
122
123
|
"disabled": "Geçerli model görüntü tanıma desteğini desteklemiyor, lütfen modeli değiştirerek kullanın",
|
|
123
124
|
"dragDesc": "Dosyaları buraya sürükleyip bırakın, birden fazla resim yüklemeyi destekler. Resimleri doğrudan göndermek için Shift tuşunu basılı tutun.",
|
|
125
|
+
"dragFileDesc": "Birden fazla resim ve dosya yüklemeyi desteklemek için resimleri ve dosyaları buraya sürükleyin. Bir görüntüyü veya dosyayı doğrudan göndermek için Shift tuşunu basılı tutun.",
|
|
126
|
+
"dragFileTitle": "Dosyayı Yükle",
|
|
124
127
|
"dragTitle": "Resim Yükle"
|
|
125
128
|
}
|
|
126
129
|
}
|
package/locales/vi-VN/chat.json
CHANGED
|
@@ -118,9 +118,12 @@
|
|
|
118
118
|
},
|
|
119
119
|
"updateAgent": "Cập nhật thông tin trợ lý",
|
|
120
120
|
"upload": {
|
|
121
|
+
"actionFiletip": "Tải lên tập tin",
|
|
121
122
|
"actionTooltip": "Tải lên hình ảnh",
|
|
122
123
|
"disabled": "Mô hình hiện tại không hỗ trợ nhận diện hình ảnh, vui lòng chuyển đổi mô hình trước khi sử dụng",
|
|
123
124
|
"dragDesc": "Kéo và thả tệp vào đây, hỗ trợ tải lên nhiều hình ảnh. Giữ phím Shift để gửi hình ảnh trực tiếp",
|
|
125
|
+
"dragFileDesc": "Kéo ảnh và tệp vào đây để hỗ trợ tải lên nhiều ảnh và tệp. Giữ phím Shift để gửi hình ảnh hoặc tập tin trực tiếp",
|
|
126
|
+
"dragFileTitle": "Tải lên tập tin",
|
|
124
127
|
"dragTitle": "Tải lên hình ảnh"
|
|
125
128
|
}
|
|
126
129
|
}
|
package/locales/zh-CN/chat.json
CHANGED
|
@@ -118,9 +118,12 @@
|
|
|
118
118
|
},
|
|
119
119
|
"updateAgent": "更新助理信息",
|
|
120
120
|
"upload": {
|
|
121
|
+
"actionFiletip": "上传文件",
|
|
121
122
|
"actionTooltip": "上传图片",
|
|
122
|
-
"disabled": "
|
|
123
|
+
"disabled": "当前模型不支持视觉识别和文件分析,请切换模型后使用",
|
|
123
124
|
"dragDesc": "拖拽文件到这里,支持上传多个图片。按住 Shift 直接发送图片",
|
|
125
|
+
"dragFileDesc": "拖拽图片和文件到这里,支持上传多个图片和文件。按住 Shift 直接发送图片或文件",
|
|
126
|
+
"dragFileTitle": "上传文件",
|
|
124
127
|
"dragTitle": "上传图片"
|
|
125
128
|
}
|
|
126
129
|
}
|
package/locales/zh-TW/chat.json
CHANGED
|
@@ -118,9 +118,12 @@
|
|
|
118
118
|
},
|
|
119
119
|
"updateAgent": "更新助理信息",
|
|
120
120
|
"upload": {
|
|
121
|
+
"actionFiletip": "上傳文件",
|
|
121
122
|
"actionTooltip": "上傳圖片",
|
|
122
123
|
"disabled": "當前模型不支援視覺識別,請切換模型後使用",
|
|
123
124
|
"dragDesc": "拖拽文件到這裡,支持上傳多個圖片。按住 Shift 直接發送圖片",
|
|
125
|
+
"dragFileDesc": "拖曳圖片和文件至此,支援上傳多張圖片和文件。按住 Shift 直接傳送圖片或文件",
|
|
126
|
+
"dragFileTitle": "上傳文件",
|
|
124
127
|
"dragTitle": "上傳圖片"
|
|
125
128
|
}
|
|
126
129
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lobehub/chat",
|
|
3
|
-
"version": "0.147.
|
|
3
|
+
"version": "0.147.22",
|
|
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",
|
|
@@ -86,7 +86,7 @@
|
|
|
86
86
|
"@aws-sdk/client-bedrock-runtime": "^3.549.0",
|
|
87
87
|
"@azure/openai": "^1.0.0-beta.12",
|
|
88
88
|
"@cfworker/json-schema": "^1.12.8",
|
|
89
|
-
"@google/generative-ai": "^0.
|
|
89
|
+
"@google/generative-ai": "^0.7.0",
|
|
90
90
|
"@icons-pack/react-simple-icons": "^9.4.0",
|
|
91
91
|
"@lobehub/chat-plugin-sdk": "latest",
|
|
92
92
|
"@lobehub/chat-plugins-gateway": "latest",
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -151,7 +151,7 @@ const DragUpload = memo(() => {
|
|
|
151
151
|
window.removeEventListener('drop', handleDrop);
|
|
152
152
|
window.removeEventListener('paste', handlePaste);
|
|
153
153
|
};
|
|
154
|
-
}, [handleDrop, handlePaste]);
|
|
154
|
+
}, [handleDragEnter, handleDragOver, handleDragLeave, handleDrop, handlePaste]);
|
|
155
155
|
|
|
156
156
|
return (
|
|
157
157
|
isDragging && (
|
|
@@ -164,8 +164,8 @@ const DragUpload = memo(() => {
|
|
|
164
164
|
<Icon icon={FileText} size={{ fontSize: 64, strokeWidth: 1 }} />
|
|
165
165
|
</Flexbox>
|
|
166
166
|
<Flexbox align={'center'} gap={8} style={{ textAlign: 'center' }}>
|
|
167
|
-
<Flexbox className={styles.title}>{t('upload.dragTitle')}</Flexbox>
|
|
168
|
-
<Flexbox className={styles.desc}>{t('upload.dragDesc')}</Flexbox>
|
|
167
|
+
<Flexbox className={styles.title}>{t(enabledFiles ? 'upload.dragFileTitle' : 'upload.dragTitle')}</Flexbox>
|
|
168
|
+
<Flexbox className={styles.desc}>{t(enabledFiles ? 'upload.dragFileDesc' : 'upload.dragDesc')}</Flexbox>
|
|
169
169
|
</Flexbox>
|
|
170
170
|
</Center>
|
|
171
171
|
</div>
|
|
@@ -114,8 +114,8 @@ const TopicContent = memo<TopicContentProps>(({ id, title, fav, showMore }) => {
|
|
|
114
114
|
modal.confirm({
|
|
115
115
|
centered: true,
|
|
116
116
|
okButtonProps: { danger: true },
|
|
117
|
-
onOk: () => {
|
|
118
|
-
removeTopic(id);
|
|
117
|
+
onOk: async () => {
|
|
118
|
+
await removeTopic(id);
|
|
119
119
|
},
|
|
120
120
|
title: t('topic.confirmRemoveTopic', { ns: 'chat' }),
|
|
121
121
|
});
|
package/src/app/metadata.ts
CHANGED
|
@@ -22,11 +22,9 @@ const metadata: Metadata = {
|
|
|
22
22
|
},
|
|
23
23
|
description,
|
|
24
24
|
icons: {
|
|
25
|
-
apple:
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
shortcut:
|
|
29
|
-
'https://registry.npmmirror.com/@lobehub/assets-favicons/latest/files/assets/favicon.ico',
|
|
25
|
+
apple:'icons/apple-touch-icon.png',
|
|
26
|
+
icon:'favicon.ico',
|
|
27
|
+
shortcut:'favicon-32x32.ico',
|
|
30
28
|
},
|
|
31
29
|
manifest: noManifest ? undefined : '/manifest.json',
|
|
32
30
|
metadataBase: new URL(SITE_URL),
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ActionIcon, Icon } from '@lobehub/ui';
|
|
2
2
|
import { Upload } from 'antd';
|
|
3
3
|
import { useTheme } from 'antd-style';
|
|
4
|
-
import { LucideImage, LucideLoader2 } from 'lucide-react';
|
|
4
|
+
import { LucideImage, FileUp, LucideLoader2 } from 'lucide-react';
|
|
5
5
|
import { memo, useState } from 'react';
|
|
6
6
|
import { useTranslation } from 'react-i18next';
|
|
7
7
|
import { Center } from 'react-layout-kit';
|
|
@@ -51,9 +51,9 @@ const FileUpload = memo(() => {
|
|
|
51
51
|
) : (
|
|
52
52
|
<ActionIcon
|
|
53
53
|
disable={!canUpload}
|
|
54
|
-
icon={LucideImage}
|
|
54
|
+
icon={enabledFiles ? FileUp : LucideImage}
|
|
55
55
|
placement={'bottom'}
|
|
56
|
-
title={t(canUpload ? 'upload.actionTooltip' : 'upload.disabled')}
|
|
56
|
+
title={t(canUpload ? (enabledFiles ? 'upload.actionFiletip' : 'upload.actionTooltip') : 'upload.disabled')}
|
|
57
57
|
/>
|
|
58
58
|
)}
|
|
59
59
|
</Upload>
|
|
@@ -7,6 +7,7 @@ import { useTranslation } from 'react-i18next';
|
|
|
7
7
|
|
|
8
8
|
import HotKeys from '@/components/HotKeys';
|
|
9
9
|
import { PREFIX_KEY, SAVE_TOPIC_KEY } from '@/const/hotkeys';
|
|
10
|
+
import { useActionSWR } from '@/libs/swr';
|
|
10
11
|
import { useChatStore } from '@/store/chat';
|
|
11
12
|
|
|
12
13
|
const SaveTopic = memo<{ mobile?: boolean }>(({ mobile }) => {
|
|
@@ -16,20 +17,23 @@ const SaveTopic = memo<{ mobile?: boolean }>(({ mobile }) => {
|
|
|
16
17
|
s.openNewTopicOrSaveTopic,
|
|
17
18
|
]);
|
|
18
19
|
|
|
20
|
+
const { mutate, isValidating } = useActionSWR('openNewTopicOrSaveTopic', openNewTopicOrSaveTopic);
|
|
21
|
+
|
|
19
22
|
const icon = hasTopic ? LucideMessageSquarePlus : LucideGalleryVerticalEnd;
|
|
20
23
|
const Render = mobile ? ActionIcon : Button;
|
|
21
24
|
const iconRender: any = mobile ? icon : <Icon icon={icon} />;
|
|
22
25
|
const desc = t(hasTopic ? 'topic.openNewTopic' : 'topic.saveCurrentMessages');
|
|
23
26
|
|
|
24
27
|
const hotkeys = [PREFIX_KEY, SAVE_TOPIC_KEY].join('+');
|
|
25
|
-
|
|
28
|
+
|
|
29
|
+
useHotkeys(hotkeys, () => mutate(), {
|
|
26
30
|
enableOnFormTags: true,
|
|
27
31
|
preventDefault: true,
|
|
28
32
|
});
|
|
29
33
|
|
|
30
34
|
return (
|
|
31
35
|
<Tooltip title={<HotKeys desc={desc} keys={hotkeys} />}>
|
|
32
|
-
<Render aria-label={desc} icon={iconRender} onClick={
|
|
36
|
+
<Render aria-label={desc} icon={iconRender} loading={isValidating} onClick={() => mutate()} />
|
|
33
37
|
</Tooltip>
|
|
34
38
|
);
|
|
35
39
|
});
|
|
@@ -19,6 +19,9 @@ import ActionsBar from './ActionsBar';
|
|
|
19
19
|
import HistoryDivider from './HistoryDivider';
|
|
20
20
|
|
|
21
21
|
const useStyles = createStyles(({ css, prefixCls }) => ({
|
|
22
|
+
loading: css`
|
|
23
|
+
opacity: 0.6;
|
|
24
|
+
`,
|
|
22
25
|
message: css`
|
|
23
26
|
// prevent the textarea too long
|
|
24
27
|
.${prefixCls}-input {
|
|
@@ -35,7 +38,7 @@ export interface ChatListItemProps {
|
|
|
35
38
|
const Item = memo<ChatListItemProps>(({ index, id }) => {
|
|
36
39
|
const fontSize = useGlobalStore((s) => settingsSelectors.currentSettings(s).fontSize);
|
|
37
40
|
const { t } = useTranslation('common');
|
|
38
|
-
const { styles } = useStyles();
|
|
41
|
+
const { styles, cx } = useStyles();
|
|
39
42
|
const [editing, setEditing] = useState(false);
|
|
40
43
|
const [type = 'chat'] = useSessionStore((s) => {
|
|
41
44
|
const config = agentSelectors.currentAgentConfig(s);
|
|
@@ -54,10 +57,12 @@ const Item = memo<ChatListItemProps>(({ index, id }) => {
|
|
|
54
57
|
const historyLength = useChatStore((s) => chatSelectors.currentChats(s).length);
|
|
55
58
|
|
|
56
59
|
const [loading, updateMessageContent] = useChatStore((s) => [
|
|
57
|
-
s.chatLoadingId === id,
|
|
60
|
+
s.chatLoadingId === id || s.messageLoadingIds.includes(id),
|
|
58
61
|
s.modifyMessageContent,
|
|
59
62
|
]);
|
|
60
63
|
|
|
64
|
+
const [isMessageLoading] = useChatStore((s) => [s.messageLoadingIds.includes(id)]);
|
|
65
|
+
|
|
61
66
|
const onAvatarsClick = useAvatarsClick();
|
|
62
67
|
|
|
63
68
|
const RenderMessage = useCallback(
|
|
@@ -110,7 +115,7 @@ const Item = memo<ChatListItemProps>(({ index, id }) => {
|
|
|
110
115
|
<ChatItem
|
|
111
116
|
actions={<ActionsBar index={index} setEditing={setEditing} />}
|
|
112
117
|
avatar={item.meta}
|
|
113
|
-
className={styles.message}
|
|
118
|
+
className={cx(styles.message, isMessageLoading && styles.loading)}
|
|
114
119
|
editing={editing}
|
|
115
120
|
error={error}
|
|
116
121
|
errorMessage={<ErrorMessageExtra data={item} />}
|
package/src/libs/swr/index.ts
CHANGED
|
@@ -32,3 +32,12 @@ export const useActionSWR: SWRHook = (key, fetch, config) =>
|
|
|
32
32
|
revalidateOnReconnect: false,
|
|
33
33
|
...config,
|
|
34
34
|
});
|
|
35
|
+
|
|
36
|
+
export interface SWRRefreshParams<T, A = (...args: any[]) => any> {
|
|
37
|
+
action: A;
|
|
38
|
+
optimisticData?: (data: T | undefined) => T;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export type SWRefreshMethod<T> = <A extends (...args: any[]) => Promise<any>>(
|
|
42
|
+
params?: SWRRefreshParams<T, A>,
|
|
43
|
+
) => ReturnType<A>;
|
|
@@ -120,9 +120,12 @@ export default {
|
|
|
120
120
|
},
|
|
121
121
|
updateAgent: '更新助理信息',
|
|
122
122
|
upload: {
|
|
123
|
+
actionFiletip: '上传文件',
|
|
123
124
|
actionTooltip: '上传图片',
|
|
124
|
-
disabled: '
|
|
125
|
+
disabled: '当前模型不支持视觉识别和文件分析,请切换模型后使用',
|
|
125
126
|
dragDesc: '拖拽文件到这里,支持上传多个图片。按住 Shift 直接发送图片',
|
|
126
|
-
|
|
127
|
+
dragFileDesc: '拖拽图片和文件到这里,支持上传多个图片和文件。按住 Shift 直接发送图片或文件',
|
|
128
|
+
dragFileTitle: '上传文件',
|
|
129
|
+
dragTitle: '上传图片'
|
|
127
130
|
},
|
|
128
131
|
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/* eslint-disable sort-keys-fix/sort-keys-fix, typescript-sort-keys/interface */
|
|
2
2
|
// Disable the auto sort key eslint rule to make the code more logic and readable
|
|
3
3
|
import { copyToClipboard } from '@lobehub/ui';
|
|
4
|
+
import { produce } from 'immer';
|
|
4
5
|
import { template } from 'lodash-es';
|
|
5
6
|
import { SWRResponse, mutate } from 'swr';
|
|
6
7
|
import { StateCreator } from 'zustand/vanilla';
|
|
@@ -19,6 +20,7 @@ import { agentSelectors } from '@/store/session/selectors';
|
|
|
19
20
|
import { ChatMessage } from '@/types/message';
|
|
20
21
|
import { TraceEventPayloads } from '@/types/trace';
|
|
21
22
|
import { setNamespace } from '@/utils/storeDebug';
|
|
23
|
+
import { nanoid } from '@/utils/uuid';
|
|
22
24
|
|
|
23
25
|
import { chatSelectors } from '../../selectors';
|
|
24
26
|
import { MessageDispatch, messagesReducer } from './reducer';
|
|
@@ -97,6 +99,7 @@ export interface ChatMessageAction {
|
|
|
97
99
|
id?: string,
|
|
98
100
|
action?: string,
|
|
99
101
|
) => AbortController | undefined;
|
|
102
|
+
toggleMessageLoading: (loading: boolean, id: string) => void;
|
|
100
103
|
refreshMessages: () => Promise<void>;
|
|
101
104
|
// TODO: 后续 smoothMessage 实现考虑落到 sse 这一层
|
|
102
105
|
createSmoothMessage: (id: string) => {
|
|
@@ -111,6 +114,7 @@ export interface ChatMessageAction {
|
|
|
111
114
|
* @param content
|
|
112
115
|
*/
|
|
113
116
|
internalUpdateMessageContent: (id: string, content: string) => Promise<void>;
|
|
117
|
+
internalCreateMessage: (params: CreateMessageParams) => Promise<string>;
|
|
114
118
|
internalResendMessage: (id: string, traceId?: string) => Promise<void>;
|
|
115
119
|
internalTraceMessage: (id: string, payload: TraceEventPayloads) => Promise<void>;
|
|
116
120
|
}
|
|
@@ -130,6 +134,7 @@ export const chatMessage: StateCreator<
|
|
|
130
134
|
ChatMessageAction
|
|
131
135
|
> = (set, get) => ({
|
|
132
136
|
deleteMessage: async (id) => {
|
|
137
|
+
get().dispatchMessage({ type: 'deleteMessage', id });
|
|
133
138
|
await messageService.removeMessage(id);
|
|
134
139
|
await get().refreshMessages();
|
|
135
140
|
},
|
|
@@ -167,43 +172,6 @@ export const chatMessage: StateCreator<
|
|
|
167
172
|
await messageService.removeAllMessages();
|
|
168
173
|
await refreshMessages();
|
|
169
174
|
},
|
|
170
|
-
internalResendMessage: async (messageId, traceId) => {
|
|
171
|
-
// 1. 构造所有相关的历史记录
|
|
172
|
-
const chats = chatSelectors.currentChats(get());
|
|
173
|
-
|
|
174
|
-
const currentIndex = chats.findIndex((c) => c.id === messageId);
|
|
175
|
-
if (currentIndex < 0) return;
|
|
176
|
-
|
|
177
|
-
const currentMessage = chats[currentIndex];
|
|
178
|
-
|
|
179
|
-
let contextMessages: ChatMessage[] = [];
|
|
180
|
-
|
|
181
|
-
switch (currentMessage.role) {
|
|
182
|
-
case 'function':
|
|
183
|
-
case 'user': {
|
|
184
|
-
contextMessages = chats.slice(0, currentIndex + 1);
|
|
185
|
-
break;
|
|
186
|
-
}
|
|
187
|
-
case 'assistant': {
|
|
188
|
-
// 消息是 AI 发出的因此需要找到它的 user 消息
|
|
189
|
-
const userId = currentMessage.parentId;
|
|
190
|
-
const userIndex = chats.findIndex((c) => c.id === userId);
|
|
191
|
-
// 如果消息没有 parentId,那么同 user/function 模式
|
|
192
|
-
contextMessages = chats.slice(0, userIndex < 0 ? currentIndex + 1 : userIndex + 1);
|
|
193
|
-
break;
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
if (contextMessages.length <= 0) return;
|
|
198
|
-
|
|
199
|
-
const { coreProcessMessage } = get();
|
|
200
|
-
|
|
201
|
-
const latestMsg = contextMessages.filter((s) => s.role === 'user').at(-1);
|
|
202
|
-
|
|
203
|
-
if (!latestMsg) return;
|
|
204
|
-
|
|
205
|
-
await coreProcessMessage(contextMessages, latestMsg.id, traceId);
|
|
206
|
-
},
|
|
207
175
|
sendMessage: async ({ message, files, onlyAddUserMessage }) => {
|
|
208
176
|
const { coreProcessMessage, activeTopicId, activeId } = get();
|
|
209
177
|
if (!activeId) return;
|
|
@@ -223,8 +191,7 @@ export const chatMessage: StateCreator<
|
|
|
223
191
|
topicId: activeTopicId,
|
|
224
192
|
};
|
|
225
193
|
|
|
226
|
-
const id = await
|
|
227
|
-
await get().refreshMessages();
|
|
194
|
+
const id = await get().internalCreateMessage(newMessage);
|
|
228
195
|
|
|
229
196
|
// if only add user message, then stop
|
|
230
197
|
if (onlyAddUserMessage) return;
|
|
@@ -315,8 +282,7 @@ export const chatMessage: StateCreator<
|
|
|
315
282
|
topicId: activeTopicId, // if there is activeTopicId,then add it to topicId
|
|
316
283
|
};
|
|
317
284
|
|
|
318
|
-
const mid = await
|
|
319
|
-
await refreshMessages();
|
|
285
|
+
const mid = await get().internalCreateMessage(assistantMessage);
|
|
320
286
|
|
|
321
287
|
// 2. fetch the AI response
|
|
322
288
|
const { isFunctionCall, content, functionCallAtEnd, functionCallContent, traceId } =
|
|
@@ -344,7 +310,7 @@ export const chatMessage: StateCreator<
|
|
|
344
310
|
traceId,
|
|
345
311
|
};
|
|
346
312
|
|
|
347
|
-
functionId = await
|
|
313
|
+
functionId = await get().internalCreateMessage(functionMessage);
|
|
348
314
|
}
|
|
349
315
|
|
|
350
316
|
await refreshMessages();
|
|
@@ -533,6 +499,62 @@ export const chatMessage: StateCreator<
|
|
|
533
499
|
window.removeEventListener('beforeunload', preventLeavingFn);
|
|
534
500
|
}
|
|
535
501
|
},
|
|
502
|
+
toggleMessageLoading: (loading, id) => {
|
|
503
|
+
set(
|
|
504
|
+
{
|
|
505
|
+
messageLoadingIds: produce(get().messageLoadingIds, (draft) => {
|
|
506
|
+
if (loading) {
|
|
507
|
+
draft.push(id);
|
|
508
|
+
} else {
|
|
509
|
+
const index = draft.indexOf(id);
|
|
510
|
+
|
|
511
|
+
if (index >= 0) draft.splice(index, 1);
|
|
512
|
+
}
|
|
513
|
+
}),
|
|
514
|
+
},
|
|
515
|
+
false,
|
|
516
|
+
'toggleMessageLoading',
|
|
517
|
+
);
|
|
518
|
+
},
|
|
519
|
+
|
|
520
|
+
internalResendMessage: async (messageId, traceId) => {
|
|
521
|
+
// 1. 构造所有相关的历史记录
|
|
522
|
+
const chats = chatSelectors.currentChats(get());
|
|
523
|
+
|
|
524
|
+
const currentIndex = chats.findIndex((c) => c.id === messageId);
|
|
525
|
+
if (currentIndex < 0) return;
|
|
526
|
+
|
|
527
|
+
const currentMessage = chats[currentIndex];
|
|
528
|
+
|
|
529
|
+
let contextMessages: ChatMessage[] = [];
|
|
530
|
+
|
|
531
|
+
switch (currentMessage.role) {
|
|
532
|
+
case 'function':
|
|
533
|
+
case 'user': {
|
|
534
|
+
contextMessages = chats.slice(0, currentIndex + 1);
|
|
535
|
+
break;
|
|
536
|
+
}
|
|
537
|
+
case 'assistant': {
|
|
538
|
+
// 消息是 AI 发出的因此需要找到它的 user 消息
|
|
539
|
+
const userId = currentMessage.parentId;
|
|
540
|
+
const userIndex = chats.findIndex((c) => c.id === userId);
|
|
541
|
+
// 如果消息没有 parentId,那么同 user/function 模式
|
|
542
|
+
contextMessages = chats.slice(0, userIndex < 0 ? currentIndex + 1 : userIndex + 1);
|
|
543
|
+
break;
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
if (contextMessages.length <= 0) return;
|
|
548
|
+
|
|
549
|
+
const { coreProcessMessage } = get();
|
|
550
|
+
|
|
551
|
+
const latestMsg = contextMessages.filter((s) => s.role === 'user').at(-1);
|
|
552
|
+
|
|
553
|
+
if (!latestMsg) return;
|
|
554
|
+
|
|
555
|
+
await coreProcessMessage(contextMessages, latestMsg.id, traceId);
|
|
556
|
+
},
|
|
557
|
+
|
|
536
558
|
internalUpdateMessageContent: async (id, content) => {
|
|
537
559
|
const { dispatchMessage, refreshMessages } = get();
|
|
538
560
|
|
|
@@ -545,6 +567,22 @@ export const chatMessage: StateCreator<
|
|
|
545
567
|
await refreshMessages();
|
|
546
568
|
},
|
|
547
569
|
|
|
570
|
+
internalCreateMessage: async (message) => {
|
|
571
|
+
const { dispatchMessage, refreshMessages, toggleMessageLoading } = get();
|
|
572
|
+
|
|
573
|
+
// use optimistic update to avoid the slow waiting
|
|
574
|
+
const tempId = 'tmp_' + nanoid();
|
|
575
|
+
dispatchMessage({ type: 'createMessage', id: tempId, value: message });
|
|
576
|
+
|
|
577
|
+
toggleMessageLoading(true, tempId);
|
|
578
|
+
const id = await messageService.createMessage(message);
|
|
579
|
+
|
|
580
|
+
await refreshMessages();
|
|
581
|
+
toggleMessageLoading(false, tempId);
|
|
582
|
+
|
|
583
|
+
return id;
|
|
584
|
+
},
|
|
585
|
+
|
|
548
586
|
createSmoothMessage: (id) => {
|
|
549
587
|
const { dispatchMessage } = get();
|
|
550
588
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import isEqual from 'fast-deep-equal';
|
|
2
2
|
import { produce } from 'immer';
|
|
3
3
|
|
|
4
|
+
import { CreateMessageParams } from '@/services/message';
|
|
4
5
|
import { ChatMessage } from '@/types/message';
|
|
5
6
|
import { merge } from '@/utils/merge';
|
|
6
7
|
|
|
@@ -10,6 +11,15 @@ interface UpdateMessage {
|
|
|
10
11
|
type: 'updateMessage';
|
|
11
12
|
value: ChatMessage[keyof ChatMessage];
|
|
12
13
|
}
|
|
14
|
+
interface CreateMessage {
|
|
15
|
+
id: string;
|
|
16
|
+
type: 'createMessage';
|
|
17
|
+
value: CreateMessageParams;
|
|
18
|
+
}
|
|
19
|
+
interface DeleteMessage {
|
|
20
|
+
id: string;
|
|
21
|
+
type: 'deleteMessage';
|
|
22
|
+
}
|
|
13
23
|
|
|
14
24
|
interface UpdatePluginState {
|
|
15
25
|
id: string;
|
|
@@ -24,7 +34,12 @@ interface UpdateMessageExtra {
|
|
|
24
34
|
value: any;
|
|
25
35
|
}
|
|
26
36
|
|
|
27
|
-
export type MessageDispatch =
|
|
37
|
+
export type MessageDispatch =
|
|
38
|
+
| CreateMessage
|
|
39
|
+
| UpdateMessage
|
|
40
|
+
| UpdatePluginState
|
|
41
|
+
| UpdateMessageExtra
|
|
42
|
+
| DeleteMessage;
|
|
28
43
|
|
|
29
44
|
export const messagesReducer = (state: ChatMessage[], payload: MessageDispatch): ChatMessage[] => {
|
|
30
45
|
switch (payload.type) {
|
|
@@ -76,6 +91,22 @@ export const messagesReducer = (state: ChatMessage[], payload: MessageDispatch):
|
|
|
76
91
|
});
|
|
77
92
|
}
|
|
78
93
|
|
|
94
|
+
case 'createMessage': {
|
|
95
|
+
return produce(state, (draftState) => {
|
|
96
|
+
const { value, id } = payload;
|
|
97
|
+
|
|
98
|
+
draftState.push({ ...value, createdAt: Date.now(), id, meta: {}, updatedAt: Date.now() });
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
case 'deleteMessage': {
|
|
102
|
+
return produce(state, (draft) => {
|
|
103
|
+
const { id } = payload;
|
|
104
|
+
|
|
105
|
+
const index = draft.findIndex((m) => m.id === id);
|
|
106
|
+
|
|
107
|
+
if (index >= 0) draft.splice(index, 1);
|
|
108
|
+
});
|
|
109
|
+
}
|
|
79
110
|
default: {
|
|
80
111
|
throw new Error('暂未实现的 type,请检查 reducer');
|
|
81
112
|
}
|
|
@@ -149,7 +149,9 @@ describe('topic action', () => {
|
|
|
149
149
|
});
|
|
150
150
|
|
|
151
151
|
// Check if mutate has been called with the active session ID
|
|
152
|
-
expect(mutate).toHaveBeenCalledWith(['SWR_USE_FETCH_TOPIC', activeId]
|
|
152
|
+
expect(mutate).toHaveBeenCalledWith(['SWR_USE_FETCH_TOPIC', activeId], undefined, {
|
|
153
|
+
populateCache: false,
|
|
154
|
+
});
|
|
153
155
|
});
|
|
154
156
|
|
|
155
157
|
it('should handle errors during refreshing topics', async () => {
|
|
@@ -314,7 +316,7 @@ describe('topic action', () => {
|
|
|
314
316
|
const activeId = 'test-session-id';
|
|
315
317
|
|
|
316
318
|
await act(async () => {
|
|
317
|
-
useChatStore.setState({ activeId });
|
|
319
|
+
useChatStore.setState({ activeId, activeTopicId: topicId });
|
|
318
320
|
});
|
|
319
321
|
|
|
320
322
|
const refreshTopicSpy = vi.spyOn(result.current, 'refreshTopic');
|
|
@@ -329,6 +331,27 @@ describe('topic action', () => {
|
|
|
329
331
|
expect(refreshTopicSpy).toHaveBeenCalled();
|
|
330
332
|
expect(switchTopicSpy).toHaveBeenCalled();
|
|
331
333
|
});
|
|
334
|
+
it('should remove a specific topic and its messages, then not refresh the topic list', async () => {
|
|
335
|
+
const topicId = 'topic-1';
|
|
336
|
+
const { result } = renderHook(() => useChatStore());
|
|
337
|
+
const activeId = 'test-session-id';
|
|
338
|
+
|
|
339
|
+
await act(async () => {
|
|
340
|
+
useChatStore.setState({ activeId });
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
const refreshTopicSpy = vi.spyOn(result.current, 'refreshTopic');
|
|
344
|
+
const switchTopicSpy = vi.spyOn(result.current, 'switchTopic');
|
|
345
|
+
|
|
346
|
+
await act(async () => {
|
|
347
|
+
await result.current.removeTopic(topicId);
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
expect(messageService.removeMessages).toHaveBeenCalledWith(activeId, topicId);
|
|
351
|
+
expect(topicService.removeTopic).toHaveBeenCalledWith(topicId);
|
|
352
|
+
expect(refreshTopicSpy).toHaveBeenCalled();
|
|
353
|
+
expect(switchTopicSpy).not.toHaveBeenCalled();
|
|
354
|
+
});
|
|
332
355
|
});
|
|
333
356
|
describe('removeUnstarredTopic', () => {
|
|
334
357
|
it('should remove unstarred topics and refresh the topic list', async () => {
|
|
@@ -9,11 +9,11 @@ import { StateCreator } from 'zustand/vanilla';
|
|
|
9
9
|
import { chainSummaryTitle } from '@/chains/summaryTitle';
|
|
10
10
|
import { LOADING_FLAT } from '@/const/message';
|
|
11
11
|
import { TraceNameMap } from '@/const/trace';
|
|
12
|
-
import { useClientDataSWR } from '@/libs/swr';
|
|
12
|
+
import { SWRefreshMethod, useClientDataSWR } from '@/libs/swr';
|
|
13
13
|
import { chatService } from '@/services/chat';
|
|
14
14
|
import { messageService } from '@/services/message';
|
|
15
15
|
import { topicService } from '@/services/topic';
|
|
16
|
-
import { ChatStore } from '@/store/chat';
|
|
16
|
+
import type { ChatStore } from '@/store/chat';
|
|
17
17
|
import { ChatMessage } from '@/types/message';
|
|
18
18
|
import { ChatTopic } from '@/types/topic';
|
|
19
19
|
import { setNamespace } from '@/utils/storeDebug';
|
|
@@ -29,7 +29,7 @@ const SWR_USE_SEARCH_TOPIC = 'SWR_USE_SEARCH_TOPIC';
|
|
|
29
29
|
export interface ChatTopicAction {
|
|
30
30
|
favoriteTopic: (id: string, favState: boolean) => Promise<void>;
|
|
31
31
|
openNewTopicOrSaveTopic: () => Promise<void>;
|
|
32
|
-
refreshTopic:
|
|
32
|
+
refreshTopic: SWRefreshMethod<ChatTopic[]>;
|
|
33
33
|
removeAllTopics: () => Promise<void>;
|
|
34
34
|
removeSessionTopics: () => Promise<void>;
|
|
35
35
|
removeTopic: (id: string) => Promise<void>;
|
|
@@ -78,6 +78,17 @@ export const chatTopic: StateCreator<
|
|
|
78
78
|
messages: messages.map((m) => m.id),
|
|
79
79
|
});
|
|
80
80
|
await refreshTopic();
|
|
81
|
+
// TODO: 优化为乐观更新
|
|
82
|
+
// const params: CreateTopicParams = {
|
|
83
|
+
// sessionId: activeId,
|
|
84
|
+
// title: t('topic.defaultTitle', { ns: 'chat' }),
|
|
85
|
+
// messages: messages.map((m) => m.id),
|
|
86
|
+
// };
|
|
87
|
+
|
|
88
|
+
// const topicId = await refreshTopic({
|
|
89
|
+
// action: async () => topicService.createTopic(params),
|
|
90
|
+
// optimisticData: (data) => topicReducer(data, { type: 'addTopic', value: params }),
|
|
91
|
+
// });
|
|
81
92
|
|
|
82
93
|
// 2. auto summary topic Title
|
|
83
94
|
// we don't need to wait for summary, just let it run async
|
|
@@ -189,9 +200,10 @@ export const chatTopic: StateCreator<
|
|
|
189
200
|
await refreshTopic();
|
|
190
201
|
},
|
|
191
202
|
removeTopic: async (id) => {
|
|
192
|
-
const { activeId, switchTopic, refreshTopic } = get();
|
|
203
|
+
const { activeId, activeTopicId, switchTopic, refreshTopic } = get();
|
|
193
204
|
|
|
194
205
|
// remove messages in the topic
|
|
206
|
+
// TODO: Need to remove because server service don't need to call it
|
|
195
207
|
await messageService.removeMessages(activeId, id);
|
|
196
208
|
|
|
197
209
|
// remove topic
|
|
@@ -199,7 +211,7 @@ export const chatTopic: StateCreator<
|
|
|
199
211
|
await refreshTopic();
|
|
200
212
|
|
|
201
213
|
// switch bach to default topic
|
|
202
|
-
switchTopic();
|
|
214
|
+
if (activeTopicId === id) switchTopic();
|
|
203
215
|
},
|
|
204
216
|
removeUnstarredTopic: async () => {
|
|
205
217
|
const { refreshTopic, switchTopic } = get();
|
|
@@ -226,7 +238,12 @@ export const chatTopic: StateCreator<
|
|
|
226
238
|
updateTopicLoading: (id) => {
|
|
227
239
|
set({ topicLoadingId: id }, false, n('updateTopicLoading'));
|
|
228
240
|
},
|
|
229
|
-
|
|
230
|
-
|
|
241
|
+
// TODO: I don't know why this ts error, so have to ignore it
|
|
242
|
+
// @ts-ignore
|
|
243
|
+
refreshTopic: async (params) => {
|
|
244
|
+
return mutate([SWR_USE_FETCH_TOPIC, get().activeId], params?.action, {
|
|
245
|
+
optimisticData: params?.optimisticData,
|
|
246
|
+
populateCache: false,
|
|
247
|
+
});
|
|
231
248
|
},
|
|
232
249
|
});
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { produce } from 'immer';
|
|
2
|
+
import { expect } from 'vitest';
|
|
3
|
+
|
|
4
|
+
import { ChatTopic } from '@/types/topic';
|
|
5
|
+
|
|
6
|
+
import { ChatTopicDispatch, topicReducer } from './reducer';
|
|
7
|
+
|
|
8
|
+
describe('topicReducer', () => {
|
|
9
|
+
let state: ChatTopic[];
|
|
10
|
+
|
|
11
|
+
beforeEach(() => {
|
|
12
|
+
state = [];
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
describe('addTopic', () => {
|
|
16
|
+
it('should add a new ChatTopic object to state', () => {
|
|
17
|
+
const payload: ChatTopicDispatch = {
|
|
18
|
+
type: 'addTopic',
|
|
19
|
+
value: {
|
|
20
|
+
title: 'Test Topic',
|
|
21
|
+
sessionId: '',
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const newState = topicReducer(state, payload);
|
|
26
|
+
|
|
27
|
+
expect(newState[0].id).toBeDefined();
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
describe('updateTopic', () => {
|
|
32
|
+
it('should update the ChatTopic object in state', () => {
|
|
33
|
+
const topic: ChatTopic = {
|
|
34
|
+
id: '1',
|
|
35
|
+
title: 'Test Topic',
|
|
36
|
+
createdAt: Date.now(),
|
|
37
|
+
updatedAt: Date.now(),
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
state.push(topic);
|
|
41
|
+
|
|
42
|
+
const payload: ChatTopicDispatch = {
|
|
43
|
+
type: 'updateTopic',
|
|
44
|
+
id: '1',
|
|
45
|
+
key: 'title',
|
|
46
|
+
value: 'Updated Topic',
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const newState = topicReducer(state, payload);
|
|
50
|
+
|
|
51
|
+
expect(newState[0].title).toBe('Updated Topic');
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('should update the ChatTopic object with correct properties', () => {
|
|
55
|
+
const topic: ChatTopic = {
|
|
56
|
+
id: '1',
|
|
57
|
+
title: 'Test Topic',
|
|
58
|
+
createdAt: Date.now() - 1,
|
|
59
|
+
updatedAt: Date.now() - 1, // 设定比当前时间前面一点
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
state.push(topic);
|
|
63
|
+
|
|
64
|
+
const payload: ChatTopicDispatch = {
|
|
65
|
+
type: 'updateTopic',
|
|
66
|
+
id: '1',
|
|
67
|
+
key: 'title',
|
|
68
|
+
value: 'Updated Topic',
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const newState = topicReducer(state, payload);
|
|
72
|
+
|
|
73
|
+
expect(newState[0].updatedAt).toBeGreaterThan(topic.updatedAt);
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
describe('deleteTopic', () => {
|
|
78
|
+
it('should delete the specified ChatTopic object from state', () => {
|
|
79
|
+
const topic: ChatTopic = {
|
|
80
|
+
id: '1',
|
|
81
|
+
title: 'Test Topic',
|
|
82
|
+
createdAt: Date.now(),
|
|
83
|
+
updatedAt: Date.now(),
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
state.push(topic);
|
|
87
|
+
|
|
88
|
+
const payload: ChatTopicDispatch = {
|
|
89
|
+
type: 'deleteTopic',
|
|
90
|
+
id: '1',
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
const newState = topicReducer(state, payload);
|
|
94
|
+
|
|
95
|
+
expect(newState).toEqual([]);
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
describe('default', () => {
|
|
100
|
+
it('should return the original state object', () => {
|
|
101
|
+
const payload = {
|
|
102
|
+
type: 'unknown',
|
|
103
|
+
} as unknown as ChatTopicDispatch;
|
|
104
|
+
|
|
105
|
+
const newState = topicReducer(state, payload);
|
|
106
|
+
|
|
107
|
+
expect(newState).toBe(state);
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
describe('produce', () => {
|
|
112
|
+
it('should generate immutable state object', () => {
|
|
113
|
+
const payload: ChatTopicDispatch = {
|
|
114
|
+
type: 'addTopic',
|
|
115
|
+
value: {
|
|
116
|
+
title: 'Test Topic',
|
|
117
|
+
sessionId: '1',
|
|
118
|
+
},
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
const newState = topicReducer(state, payload);
|
|
122
|
+
|
|
123
|
+
expect(newState).not.toBe(state);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it('should not modify the original state object', () => {
|
|
127
|
+
const payload: ChatTopicDispatch = {
|
|
128
|
+
type: 'addTopic',
|
|
129
|
+
value: {
|
|
130
|
+
title: 'Test Topic',
|
|
131
|
+
|
|
132
|
+
sessionId: '123',
|
|
133
|
+
},
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
const newState = topicReducer(state, payload);
|
|
137
|
+
|
|
138
|
+
expect(state).toEqual([]);
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
});
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { produce } from 'immer';
|
|
2
|
+
|
|
3
|
+
import { CreateTopicParams } from '@/services/topic/type';
|
|
4
|
+
import { ChatTopic } from '@/types/topic';
|
|
5
|
+
|
|
6
|
+
interface AddChatTopicAction {
|
|
7
|
+
type: 'addTopic';
|
|
8
|
+
value: CreateTopicParams;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
interface UpdateChatTopicAction {
|
|
12
|
+
id: string;
|
|
13
|
+
key: keyof ChatTopic;
|
|
14
|
+
type: 'updateTopic';
|
|
15
|
+
value: any;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface DeleteChatTopicAction {
|
|
19
|
+
id: string;
|
|
20
|
+
type: 'deleteTopic';
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export type ChatTopicDispatch = AddChatTopicAction | UpdateChatTopicAction | DeleteChatTopicAction;
|
|
24
|
+
|
|
25
|
+
export const topicReducer = (state: ChatTopic[] = [], payload: ChatTopicDispatch): ChatTopic[] => {
|
|
26
|
+
switch (payload.type) {
|
|
27
|
+
case 'addTopic': {
|
|
28
|
+
return produce(state, (draftState) => {
|
|
29
|
+
draftState.unshift({
|
|
30
|
+
...payload.value,
|
|
31
|
+
createdAt: Date.now(),
|
|
32
|
+
id: Date.now().toString(),
|
|
33
|
+
sessionId: payload.value.sessionId ? payload.value.sessionId : undefined,
|
|
34
|
+
updatedAt: Date.now(),
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
case 'updateTopic': {
|
|
40
|
+
return produce(state, (draftState) => {
|
|
41
|
+
const { key, value, id } = payload;
|
|
42
|
+
const topicIndex = draftState.findIndex((topic) => topic.id === id);
|
|
43
|
+
|
|
44
|
+
if (topicIndex !== -1) {
|
|
45
|
+
const updatedTopic = { ...draftState[topicIndex] };
|
|
46
|
+
// @ts-ignore
|
|
47
|
+
updatedTopic[key] = value;
|
|
48
|
+
draftState[topicIndex] = updatedTopic;
|
|
49
|
+
updatedTopic.updatedAt = Date.now();
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
case 'deleteTopic': {
|
|
55
|
+
return produce(state, (draftState) => {
|
|
56
|
+
const topicIndex = draftState.findIndex((topic) => topic.id === payload.id);
|
|
57
|
+
if (topicIndex !== -1) {
|
|
58
|
+
draftState.splice(topicIndex, 1);
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
default: {
|
|
64
|
+
return state;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
};
|
|
@@ -6,7 +6,7 @@ import { StateCreator } from 'zustand/vanilla';
|
|
|
6
6
|
|
|
7
7
|
import { message } from '@/components/AntdStaticMethods';
|
|
8
8
|
import { INBOX_SESSION_ID } from '@/const/session';
|
|
9
|
-
import { useClientDataSWR } from '@/libs/swr';
|
|
9
|
+
import { SWRRefreshParams, useClientDataSWR } from '@/libs/swr';
|
|
10
10
|
import { sessionService } from '@/services/session';
|
|
11
11
|
import { useGlobalStore } from '@/store/global';
|
|
12
12
|
import { settingsSelectors } from '@/store/global/selectors';
|
|
@@ -51,10 +51,7 @@ export interface SessionAction {
|
|
|
51
51
|
/**
|
|
52
52
|
* re-fetch the data
|
|
53
53
|
*/
|
|
54
|
-
refreshSessions: (params?:
|
|
55
|
-
action: () => Promise<void>;
|
|
56
|
-
optimisticData?: (data: ChatSessionList) => ChatSessionList;
|
|
57
|
-
}) => Promise<void>;
|
|
54
|
+
refreshSessions: (params?: SWRRefreshParams<ChatSessionList>) => Promise<void>;
|
|
58
55
|
|
|
59
56
|
/**
|
|
60
57
|
* remove session
|
|
@@ -145,6 +142,8 @@ export const createSessionSlice: StateCreator<
|
|
|
145
142
|
},
|
|
146
143
|
// 乐观更新
|
|
147
144
|
optimisticData: produce((draft) => {
|
|
145
|
+
if (!draft) return;
|
|
146
|
+
|
|
148
147
|
const session = draft.all.find((i) => i.id === sessionId);
|
|
149
148
|
if (!session) return;
|
|
150
149
|
|
package/vercel.json
CHANGED