@lobehub/chat 1.12.13 → 1.12.15

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 CHANGED
@@ -2,6 +2,48 @@
2
2
 
3
3
  # Changelog
4
4
 
5
+ ### [Version 1.12.15](https://github.com/lobehub/lobe-chat/compare/v1.12.14...v1.12.15)
6
+
7
+ <sup>Released on **2024-08-24**</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
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
19
+
20
+ </div>
21
+
22
+ ### [Version 1.12.14](https://github.com/lobehub/lobe-chat/compare/v1.12.13...v1.12.14)
23
+
24
+ <sup>Released on **2024-08-24**</sup>
25
+
26
+ #### 🐛 Bug Fixes
27
+
28
+ - **misc**: Fix tts file saving in server mode.
29
+
30
+ <br/>
31
+
32
+ <details>
33
+ <summary><kbd>Improvements and Fixes</kbd></summary>
34
+
35
+ #### What's fixed
36
+
37
+ - **misc**: Fix tts file saving in server mode, closes [#3585](https://github.com/lobehub/lobe-chat/issues/3585) ([ab1cb47](https://github.com/lobehub/lobe-chat/commit/ab1cb47))
38
+
39
+ </details>
40
+
41
+ <div align="right">
42
+
43
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
44
+
45
+ </div>
46
+
5
47
  ### [Version 1.12.13](https://github.com/lobehub/lobe-chat/compare/v1.12.12...v1.12.13)
6
48
 
7
49
  <sup>Released on **2024-08-24**</sup>
package/README.md CHANGED
@@ -268,12 +268,12 @@ Our marketplace is not just a showcase platform but also a collaborative space.
268
268
 
269
269
  | Recent Submits | Description |
270
270
  | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
271
+ | [TypeScript Solution Architect](https://chat-preview.lobehub.com/market?agent=typescript-developer)<br/><sup>By **[swarfte](https://github.com/swarfte)** on **2024-08-24**</sup> | Expert in TypeScript, Node.js, Vue.js 3, Nuxt.js 3, Express.js, React.js, and modern UI libraries.<br/>`type-script` `java-script` `web-development` `coding-standards` `best-practices` |
271
272
  | [Variable Name Conversion Expert](https://chat-preview.lobehub.com/market?agent=variable-name-conversion)<br/><sup>By **[zengyishou](https://github.com/zengyishou)** on **2024-08-21**</sup> | In software development, naming variables is a common yet relatively time-consuming task. This assistant can automatically convert Chinese variable names into English variable names that conform to camelCase, PascalCase, snake_case, kebab-case, and constant naming conventions based on specific naming rules. This not only improves code readability but also alleviates the frustration of variable naming.<br/>`software-development` `variable-naming` `chinese-to-english` `code-standards` `automatic-conversion` |
272
273
  | [Prompt Engineering Expert](https://chat-preview.lobehub.com/market?agent=ai-prompts-assistant)<br/><sup>By **[cyicz123](https://github.com/cyicz123)** on **2024-08-12**</sup> | Specializing in prompt optimization and design<br/>`prompt-engineering` `ai-interaction` `writing` `optimization` `consulting` |
273
274
  | [Commit Message Generator](https://chat-preview.lobehub.com/market?agent=commit-assistant)<br/><sup>By **[cyicz123](https://github.com/cyicz123)** on **2024-08-12**</sup> | Expert at generating precise Git commit messages<br/>`programming` `git` `commit-message` `code-review` |
274
- | [RO-SCIRAW Prompt Word Expert](https://chat-preview.lobehub.com/market?agent=rosciraw)<br/><sup>By **[kirklin](https://github.com/kirklin)** on **2024-08-06**</sup> | The RO-SCIRAW framework, created by Kirk Lin, is a methodology for prompt words that provides a new paradigm for building highly precise and efficient prompt words. Please enter the information for the persona you want to create.<br/>`prompt-word-framework` |
275
275
 
276
- > 📊 Total agents: [<kbd>**315**</kbd> ](https://github.com/lobehub/lobe-chat-agents)
276
+ > 📊 Total agents: [<kbd>**316**</kbd> ](https://github.com/lobehub/lobe-chat-agents)
277
277
 
278
278
  <!-- AGENT LIST -->
279
279
 
package/README.zh-CN.md CHANGED
@@ -256,12 +256,12 @@ LobeChat 的插件生态系统是其核心功能的重要扩展,它极大地
256
256
 
257
257
  | 最近新增 | 助手说明 |
258
258
  | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
259
+ | [TypeScript 解决方案架构师](https://chat-preview.lobehub.com/market?agent=typescript-developer)<br/><sup>By **[swarfte](https://github.com/swarfte)** on **2024-08-24**</sup> | 精通 TypeScript、Node.js、Vue.js 3、Nuxt.js 3、Express.js、React.js 和现代 UI 库。<br/>`类型脚本` `java-script` `网页开发` `编码标准` `最佳实践` |
259
260
  | [开发变量名转换专家](https://chat-preview.lobehub.com/market?agent=variable-name-conversion)<br/><sup>By **[zengyishou](https://github.com/zengyishou)** on **2024-08-21**</sup> | 在软件开发过程中,命名变量是一项常见却相对耗时的任务。本助手能够根据特定的命名规则自动将中文变量名转换为符合小驼峰、大驼峰、下划线、横线以及常量命名规范的英文变量名。这不仅提高了代码的可读性,还解决了变量命名的苦恼。<br/>`软件开发` `变量命名` `中文转英文` `代码规范` `自动转换` |
260
261
  | [提示工程专家](https://chat-preview.lobehub.com/market?agent=ai-prompts-assistant)<br/><sup>By **[cyicz123](https://github.com/cyicz123)** on **2024-08-12**</sup> | 专精 Prompt 优化与设计<br/>`提示工程` `ai交互` `写作` `优化` `咨询` |
261
262
  | [提交信息生成器](https://chat-preview.lobehub.com/market?agent=commit-assistant)<br/><sup>By **[cyicz123](https://github.com/cyicz123)** on **2024-08-12**</sup> | 擅长生成精准的 Git 提交信息<br/>`编程` `git` `提交信息` `代码审查` |
262
- | [RO-SCIRAW 提示词专家](https://chat-preview.lobehub.com/market?agent=rosciraw)<br/><sup>By **[kirklin](https://github.com/kirklin)** on **2024-08-06**</sup> | RO-SCIRAW 框架是由 Kirk Lin 开创的提示词方法论,为构建高度精确和高效的提示词提供了一个全新的范式。请输入你要创建的分身信息。<br/>`提示词框架` |
263
263
 
264
- > 📊 Total agents: [<kbd>**315**</kbd> ](https://github.com/lobehub/lobe-chat-agents)
264
+ > 📊 Total agents: [<kbd>**316**</kbd> ](https://github.com/lobehub/lobe-chat-agents)
265
265
 
266
266
  <!-- AGENT LIST -->
267
267
 
@@ -43,5 +43,8 @@ You can achieve various feature combinations using the above configuration synta
43
43
  | `dalle` | Enables the DALL-E functionality. | Enabled |
44
44
  | `check_updates` | Allows checking for updates. | Enabled |
45
45
  | `welcome_suggest` | Displays welcome suggestions. | Enabled |
46
+ | `market` | Enables the assistant market functionality. | Enabled |
47
+ | `speech_to_text` | Enables speech-to-text functionality. | Enabled |
48
+ | `knowledge_base` | Enables the knowledge base functionality. | Enabled |
46
49
 
47
50
  You can always check the [featureFlags](https://github.com/lobehub/lobe-chat/blob/main/src/config/featureFlags/schema.ts) to get the latest list of feature flags.
@@ -39,5 +39,8 @@ tags:
39
39
  | `dalle` | 启用 DALL-E 功能。 | 开启 |
40
40
  | `check_updates` | 允许检查更新。 | 开启 |
41
41
  | `welcome_suggest` | 显示欢迎建议。 | 开启 |
42
+ | `market` | 启用助手市场功能。 | 开启 |
43
+ | `speech_to_text` | 启用语音转文本功能。 | 开启 |
44
+ | `knowledge_base` | 启用知识库功能。 | 开启 |
42
45
 
43
46
  你可以随时检查 [featureFlags](https://github.com/lobehub/lobe-chat/blob/main/src/config/featureFlags/schema.ts) 以获取最新的特性标志列表。
@@ -151,7 +151,7 @@ echo "Update time: $(date)"
151
151
  echo "Version: $(docker inspect lobehub/lobe-chat:latest | grep 'org.opencontainers.image.version' | awk -F'"' '{print $4}')"
152
152
 
153
153
  # Clean up unused images
154
- docker images | grep 'lobehub/lobe-chat' | grep -v 'latest' | awk '{print $3}' | xargs -r docker rmi > /dev/null 2>&1
154
+ docker images | grep 'lobehub/lobe-chat' | grep -v 'lobehub/lobe-chat-database' | grep -v 'latest' | awk '{print $3}' | xargs -r docker rmi > /dev/null 2>&1
155
155
  echo "Removed old images."
156
156
  ```
157
157
 
@@ -146,7 +146,7 @@ echo "Update time: $(date)"
146
146
  echo "Version: $(docker inspect lobehub/lobe-chat:latest | grep 'org.opencontainers.image.version' | awk -F'"' '{print $4}')"
147
147
 
148
148
  # 清理不再使用的镜像
149
- docker images | grep 'lobehub/lobe-chat' | grep -v 'latest' | awk '{print $3}' | xargs -r docker rmi > /dev/null 2>&1
149
+ docker images | grep 'lobehub/lobe-chat' | grep -v 'lobehub/lobe-chat-database' | grep -v 'latest' | awk '{print $3}' | xargs -r docker rmi > /dev/null 2>&1
150
150
  echo "Removed old images."
151
151
  ```
152
152
 
@@ -121,7 +121,7 @@ echo "Update time: $(date)"
121
121
  echo "Version: $(docker inspect lobehub/lobe-chat:latest | grep 'org.opencontainers.image.version' | awk -F'"' '{print $4}')"
122
122
 
123
123
  # Clean up unused images
124
- docker images | grep 'lobehub/lobe-chat' | grep -v 'latest' | awk '{print $3}' | xargs -r docker rmi > /dev/null 2>&1
124
+ docker images | grep 'lobehub/lobe-chat' | grep -v 'lobehub/lobe-chat-database' | grep -v 'latest' | awk '{print $3}' | xargs -r docker rmi > /dev/null 2>&1
125
125
  echo "Removed old images."
126
126
  ```
127
127
 
@@ -118,7 +118,7 @@ echo "Update time: $(date)"
118
118
  echo "Version: $(docker inspect lobehub/lobe-chat:latest | grep 'org.opencontainers.image.version' | awk -F'"' '{print $4}')"
119
119
 
120
120
  # Clean up unused images
121
- docker images | grep 'lobehub/lobe-chat' | grep -v 'latest' | awk '{print $3}' | xargs -r docker rmi > /dev/null 2>&1
121
+ docker images | grep 'lobehub/lobe-chat' | grep -v 'lobehub/lobe-chat-database' | grep -v 'latest' | awk '{print $3}' | xargs -r docker rmi > /dev/null 2>&1
122
122
  echo "Removed old images."
123
123
  ```
124
124
 
@@ -147,7 +147,7 @@ echo "Update time: $(date)"
147
147
  echo "Version: $(docker inspect lobehub/lobe-chat:latest | grep 'org.opencontainers.image.version' | awk -F'"' '{print $4}')"
148
148
 
149
149
  # Clean up unused images
150
- docker images | grep 'lobehub/lobe-chat' | grep -v 'latest' | awk '{print $3}' | xargs -r docker rmi > /dev/null 2>&1
150
+ docker images | grep 'lobehub/lobe-chat' | grep -v 'lobehub/lobe-chat-database' | grep -v 'latest' | awk '{print $3}' | xargs -r docker rmi > /dev/null 2>&1
151
151
  echo "Removed old images."
152
152
  ```
153
153
 
@@ -147,7 +147,7 @@ echo "Update time: $(date)"
147
147
  echo "Version: $(docker inspect lobehub/lobe-chat:latest | grep 'org.opencontainers.image.version' | awk -F'"' '{print $4}')"
148
148
 
149
149
  # 清理不再使用的镜像
150
- docker images | grep 'lobehub/lobe-chat' | grep -v 'latest' | awk '{print $3}' | xargs -r docker rmi > /dev/null 2>&1
150
+ docker images | grep 'lobehub/lobe-chat' | grep -v 'lobehub/lobe-chat-database' | grep -v 'latest' | awk '{print $3}' | xargs -r docker rmi > /dev/null 2>&1
151
151
  echo "Removed old images."
152
152
  ```
153
153
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/chat",
3
- "version": "1.12.13",
3
+ "version": "1.12.15",
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",
@@ -248,9 +248,10 @@ describe('MessageModel', () => {
248
248
 
249
249
  // 断言结果
250
250
  expect(result[0].extra.translate).toEqual({ content: 'translated', from: 'en', to: 'zh' });
251
- // TODO: 确认是否需要包含 tts 字段
252
251
  expect(result[0].extra.tts).toEqual({
253
- // contentMd5: 'md5', file: 'f1', voice: 'voice1'
252
+ contentMd5: 'md5',
253
+ file: 'f1',
254
+ voice: 'voice1',
254
255
  });
255
256
  });
256
257
 
@@ -89,11 +89,9 @@ export class MessageModel {
89
89
  },
90
90
 
91
91
  ttsId: messageTTS.id,
92
-
93
- // TODO: 确认下如何处理 TTS 的读取
94
- // ttsContentMd5: messageTTS.contentMd5,
95
- // ttsFile: messageTTS.fileId,
96
- // ttsVoice: messageTTS.voice,
92
+ ttsContentMd5: messageTTS.contentMd5,
93
+ ttsFile: messageTTS.fileId,
94
+ ttsVoice: messageTTS.voice,
97
95
  /* eslint-enable */
98
96
  })
99
97
  .from(messages)
@@ -113,7 +111,7 @@ export class MessageModel {
113
111
 
114
112
  const messageIds = result.map((message) => message.id as string);
115
113
 
116
- if (messageIds.length === 0) return result;
114
+ if (messageIds.length === 0) return [];
117
115
 
118
116
  // 2. get relative files
119
117
  const rawRelatedFileList = await serverDB
@@ -166,14 +164,7 @@ export class MessageModel {
166
164
  .where(inArray(messageQueries.messageId, messageIds));
167
165
 
168
166
  return result.map(
169
- ({
170
- model,
171
- provider,
172
- translate,
173
- ttsId,
174
- // ttsFile, ttsId, ttsContentMd5, ttsVoice,
175
- ...item
176
- }) => {
167
+ ({ model, provider, translate, ttsId, ttsFile, ttsContentMd5, ttsVoice, ...item }) => {
177
168
  const messageQuery = messageQueriesList.find((relation) => relation.messageId === item.id);
178
169
  return {
179
170
  ...item,
@@ -185,9 +176,9 @@ export class MessageModel {
185
176
  translate,
186
177
  tts: ttsId
187
178
  ? {
188
- // contentMd5: ttsContentMd5,
189
- // file: ttsFile,
190
- // voice: ttsVoice,
179
+ contentMd5: ttsContentMd5,
180
+ file: ttsFile,
181
+ voice: ttsVoice,
191
182
  }
192
183
  : undefined,
193
184
  },
@@ -8,6 +8,7 @@ import { LangChainLoaderType } from '@/libs/langchain/types';
8
8
 
9
9
  import { CodeLoader } from './code';
10
10
  import { DocxLoader } from './docx';
11
+ import { LatexLoader } from './latex';
11
12
  import { MarkdownLoader } from './markdown';
12
13
  import { PdfLoader } from './pdf';
13
14
  import { PPTXLoader } from './pptx';
@@ -38,6 +39,10 @@ export class ChunkingLoader {
38
39
  return await PPTXLoader(fileBlob);
39
40
  }
40
41
 
42
+ case 'latex': {
43
+ return await LatexLoader(txt);
44
+ }
45
+
41
46
  case 'pdf': {
42
47
  return await PdfLoader(fileBlob);
43
48
  }
@@ -78,6 +83,10 @@ export class ChunkingLoader {
78
83
  return 'pdf';
79
84
  }
80
85
 
86
+ if (filename.endsWith('tex')) {
87
+ return 'latex';
88
+ }
89
+
81
90
  if (filename.endsWith('md') || filename.endsWith('mdx')) {
82
91
  return 'markdown';
83
92
  }
@@ -0,0 +1,205 @@
1
+ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
+
3
+ exports[`LatexLoader > should run 1`] = `
4
+ [
5
+ Document {
6
+ "id": undefined,
7
+ "metadata": {
8
+ "loc": {
9
+ "lines": {
10
+ "from": 1,
11
+ "to": 41,
12
+ },
13
+ },
14
+ },
15
+ "pageContent": "\\documentclass{article}
16
+
17
+
18
+ \\usepackage{graphicx} % Required for inserting images
19
+ \\usepackage{amsmath} % Required for mathematical symbols
20
+ \\usepackage{hyperref} % For hyperlinks
21
+
22
+
23
+ \\title{Sample LaTeX Document}
24
+ \\author{Generated by ChatGPT}
25
+ \\date{\\today}
26
+
27
+
28
+ \\begin{document}
29
+
30
+
31
+ \\maketitle
32
+
33
+
34
+ \\tableofcontents
35
+
36
+
37
+ \\section{Introduction}
38
+ This is a sample LaTeX document that includes various common elements such as sections, lists, tables, figures, and mathematical equations.
39
+
40
+
41
+ \\section{Lists}
42
+ \\subsection{Itemized List}
43
+ \\begin{itemize}
44
+ \\item First item
45
+ \\item Second item
46
+ \\item Third item
47
+ \\end{itemize}
48
+
49
+
50
+ \\subsection{Enumerated List}
51
+ \\begin{enumerate}
52
+ \\item First item
53
+ \\item Second item
54
+ \\item Third item
55
+ \\end{enumerate}",
56
+ },
57
+ Document {
58
+ "id": undefined,
59
+ "metadata": {
60
+ "loc": {
61
+ "lines": {
62
+ "from": 27,
63
+ "to": 61,
64
+ },
65
+ },
66
+ },
67
+ "pageContent": "\\section{Lists}
68
+ \\subsection{Itemized List}
69
+ \\begin{itemize}
70
+ \\item First item
71
+ \\item Second item
72
+ \\item Third item
73
+ \\end{itemize}
74
+
75
+
76
+ \\subsection{Enumerated List}
77
+ \\begin{enumerate}
78
+ \\item First item
79
+ \\item Second item
80
+ \\item Third item
81
+ \\end{enumerate}
82
+
83
+
84
+ \\section{Mathematical Equations}
85
+ Here are some sample mathematical equations:
86
+
87
+
88
+ \\subsection{Inline Equation}
89
+ This is an inline equation: \\( E = mc^2 \\).
90
+
91
+
92
+ \\subsection{Displayed Equations}
93
+ \\begin{equation}
94
+ a^2 + b^2 = c^2
95
+ \\end{equation}
96
+
97
+
98
+ \\begin{align}
99
+ x &= y + z \\\\
100
+ y &= mx + b
101
+ \\end{align}",
102
+ },
103
+ Document {
104
+ "id": undefined,
105
+ "metadata": {
106
+ "loc": {
107
+ "lines": {
108
+ "from": 44,
109
+ "to": 93,
110
+ },
111
+ },
112
+ },
113
+ "pageContent": "\\section{Mathematical Equations}
114
+ Here are some sample mathematical equations:
115
+
116
+
117
+ \\subsection{Inline Equation}
118
+ This is an inline equation: \\( E = mc^2 \\).
119
+
120
+
121
+ \\subsection{Displayed Equations}
122
+ \\begin{equation}
123
+ a^2 + b^2 = c^2
124
+ \\end{equation}
125
+
126
+
127
+ \\begin{align}
128
+ x &= y + z \\\\
129
+ y &= mx + b
130
+ \\end{align}
131
+
132
+
133
+ \\section{Tables}
134
+ Here is a sample table:
135
+
136
+
137
+ \\begin{table}[h!]
138
+ \\centering
139
+ \\begin{tabular}{|c|c|c|}
140
+ \\hline
141
+ Header 1 & Header 2 & Header 3 \\\\
142
+ \\hline
143
+ Data 1 & Data 2 & Data 3 \\\\
144
+ Data 4 & Data 5 & Data 6 \\\\
145
+ Data 7 & Data 8 & Data 9 \\\\
146
+ \\hline
147
+ \\end{tabular}
148
+ \\caption{Sample Table}
149
+ \\label{table:1}
150
+ \\end{table}
151
+
152
+
153
+ \\section{Figures}
154
+ Here is a sample figure:
155
+
156
+
157
+ \\begin{figure}[h!]
158
+ \\centering
159
+ \\includegraphics[width=0.5\\textwidth]{example-image}
160
+ \\caption{Sample Figure}
161
+ \\label{fig:1}
162
+ \\end{figure}",
163
+ },
164
+ Document {
165
+ "id": undefined,
166
+ "metadata": {
167
+ "loc": {
168
+ "lines": {
169
+ "from": 84,
170
+ "to": 112,
171
+ },
172
+ },
173
+ },
174
+ "pageContent": "\\section{Figures}
175
+ Here is a sample figure:
176
+
177
+
178
+ \\begin{figure}[h!]
179
+ \\centering
180
+ \\includegraphics[width=0.5\\textwidth]{example-image}
181
+ \\caption{Sample Figure}
182
+ \\label{fig:1}
183
+ \\end{figure}
184
+
185
+
186
+ \\section{Sections and Subsections}
187
+ This is an example of a section with subsections.
188
+
189
+
190
+ \\subsection{Subsection 1}
191
+ Content of subsection 1.
192
+
193
+
194
+ \\subsection{Subsection 2}
195
+ Content of subsection 2.
196
+
197
+
198
+ \\section{References}
199
+ Here is a reference to the table \\ref{table:1} and the figure \\ref{fig:1}.
200
+
201
+
202
+ \\end{document}",
203
+ },
204
+ ]
205
+ `;
@@ -0,0 +1,112 @@
1
+ \documentclass{article}
2
+
3
+
4
+ \usepackage{graphicx} % Required for inserting images
5
+ \usepackage{amsmath} % Required for mathematical symbols
6
+ \usepackage{hyperref} % For hyperlinks
7
+
8
+
9
+ \title{Sample LaTeX Document}
10
+ \author{Generated by ChatGPT}
11
+ \date{\today}
12
+
13
+
14
+ \begin{document}
15
+
16
+
17
+ \maketitle
18
+
19
+
20
+ \tableofcontents
21
+
22
+
23
+ \section{Introduction}
24
+ This is a sample LaTeX document that includes various common elements such as sections, lists, tables, figures, and mathematical equations.
25
+
26
+
27
+ \section{Lists}
28
+ \subsection{Itemized List}
29
+ \begin{itemize}
30
+ \item First item
31
+ \item Second item
32
+ \item Third item
33
+ \end{itemize}
34
+
35
+
36
+ \subsection{Enumerated List}
37
+ \begin{enumerate}
38
+ \item First item
39
+ \item Second item
40
+ \item Third item
41
+ \end{enumerate}
42
+
43
+
44
+ \section{Mathematical Equations}
45
+ Here are some sample mathematical equations:
46
+
47
+
48
+ \subsection{Inline Equation}
49
+ This is an inline equation: \( E = mc^2 \).
50
+
51
+
52
+ \subsection{Displayed Equations}
53
+ \begin{equation}
54
+ a^2 + b^2 = c^2
55
+ \end{equation}
56
+
57
+
58
+ \begin{align}
59
+ x &= y + z \\
60
+ y &= mx + b
61
+ \end{align}
62
+
63
+
64
+ \section{Tables}
65
+ Here is a sample table:
66
+
67
+
68
+ \begin{table}[h!]
69
+ \centering
70
+ \begin{tabular}{|c|c|c|}
71
+ \hline
72
+ Header 1 & Header 2 & Header 3 \\
73
+ \hline
74
+ Data 1 & Data 2 & Data 3 \\
75
+ Data 4 & Data 5 & Data 6 \\
76
+ Data 7 & Data 8 & Data 9 \\
77
+ \hline
78
+ \end{tabular}
79
+ \caption{Sample Table}
80
+ \label{table:1}
81
+ \end{table}
82
+
83
+
84
+ \section{Figures}
85
+ Here is a sample figure:
86
+
87
+
88
+ \begin{figure}[h!]
89
+ \centering
90
+ \includegraphics[width=0.5\textwidth]{example-image}
91
+ \caption{Sample Figure}
92
+ \label{fig:1}
93
+ \end{figure}
94
+
95
+
96
+ \section{Sections and Subsections}
97
+ This is an example of a section with subsections.
98
+
99
+
100
+ \subsection{Subsection 1}
101
+ Content of subsection 1.
102
+
103
+
104
+ \subsection{Subsection 2}
105
+ Content of subsection 2.
106
+
107
+
108
+ \section{References}
109
+ Here is a reference to the table \ref{table:1} and the figure \ref{fig:1}.
110
+
111
+
112
+ \end{document}
@@ -0,0 +1,16 @@
1
+ // @vitest-environment node
2
+ import * as fs from 'node:fs';
3
+ import { join } from 'node:path';
4
+ import { expect } from 'vitest';
5
+
6
+ import { LatexLoader } from '../index';
7
+
8
+ describe('LatexLoader', () => {
9
+ it('should run', async () => {
10
+ const content = fs.readFileSync(join(__dirname, `./demo.tex`), 'utf-8');
11
+
12
+ const data = await LatexLoader(content);
13
+
14
+ expect(data).toMatchSnapshot();
15
+ });
16
+ });
@@ -0,0 +1,9 @@
1
+ import { LatexTextSplitter } from 'langchain/text_splitter';
2
+
3
+ import { loaderConfig } from '../config';
4
+
5
+ export const LatexLoader = async (text: string) => {
6
+ const splitter = new LatexTextSplitter(loaderConfig);
7
+
8
+ return await splitter.createDocuments([text]);
9
+ };
@@ -1 +1 @@
1
- export type LangChainLoaderType = 'code' | 'ppt' | 'pdf' | 'markdown' | 'doc' | 'text';
1
+ export type LangChainLoaderType = 'code' | 'ppt' | 'pdf' | 'markdown' | 'doc' | 'text' | 'latex';
@@ -150,7 +150,7 @@ export const messageRouter = router({
150
150
  value: z
151
151
  .object({
152
152
  contentMd5: z.string().optional(),
153
- fileId: z.string().optional(),
153
+ file: z.string().optional(),
154
154
  voice: z.string().optional(),
155
155
  })
156
156
  .or(z.literal(false)),
@@ -52,38 +52,6 @@ describe('TTSFileAction', () => {
52
52
  expect(fileService.removeFile).toHaveBeenCalledWith(fileId);
53
53
  });
54
54
 
55
- // Test for uploadTTSFile
56
- it('uploadTTSFile should upload the file and return the file id', async () => {
57
- const testFile = new File(['content'], 'test.mp3', { type: 'audio/mp3' });
58
- const uploadedFileData = {
59
- id: 'new-tts-file-id',
60
- createdAt: testFile.lastModified,
61
- data: await testFile.arrayBuffer(),
62
- fileType: testFile.type,
63
- name: testFile.name,
64
- saveMode: 'local',
65
- size: testFile.size,
66
- };
67
-
68
- // Mock the fileService.uploadFile to resolve with uploadedFileData
69
- vi.spyOn(fileService, 'createFile').mockResolvedValue({ id: uploadedFileData.id, url: '' });
70
-
71
- let fileId;
72
- await act(async () => {
73
- fileId = await useStore.getState().uploadTTSFile(testFile);
74
- });
75
-
76
- expect(fileService.createFile).toHaveBeenCalledWith({
77
- createdAt: testFile.lastModified,
78
- data: await testFile.arrayBuffer(),
79
- fileType: testFile.type,
80
- name: testFile.name,
81
- saveMode: 'local',
82
- size: testFile.size,
83
- });
84
- expect(fileId).toBe(uploadedFileData.id);
85
- });
86
-
87
55
  // Test for uploadTTSByArrayBuffers
88
56
  it('uploadTTSByArrayBuffers should create a file and call uploadTTSFile', async () => {
89
57
  const messageId = 'message-id';
@@ -93,8 +61,8 @@ describe('TTSFileAction', () => {
93
61
 
94
62
  // Spy on uploadTTSFile to simulate a successful upload
95
63
  const uploadTTSFileSpy = vi
96
- .spyOn(useStore.getState(), 'uploadTTSFile')
97
- .mockResolvedValue('new-tts-file-id');
64
+ .spyOn(useStore.getState(), 'uploadWithProgress')
65
+ .mockResolvedValue({ id: 'new-tts-file-id', url: '1' });
98
66
 
99
67
  let fileId;
100
68
  await act(async () => {
@@ -1,12 +1,14 @@
1
- import useSWR, { SWRResponse } from 'swr';
1
+ import { SWRResponse } from 'swr';
2
2
  import { StateCreator } from 'zustand/vanilla';
3
3
 
4
+ import { useClientDataSWR } from '@/libs/swr';
4
5
  import { fileService } from '@/services/file';
5
- import { legacyUploadService } from '@/services/upload_legacy';
6
6
  import { FileItem } from '@/types/files';
7
7
 
8
8
  import { FileStore } from '../../store';
9
9
 
10
+ const FETCH_TTS_FILE = 'fetchTTSFile';
11
+
10
12
  export interface TTSFileAction {
11
13
  removeTTSFile: (id: string) => Promise<void>;
12
14
 
@@ -15,8 +17,6 @@ export interface TTSFileAction {
15
17
  arrayBuffers: ArrayBuffer[],
16
18
  ) => Promise<string | undefined>;
17
19
 
18
- uploadTTSFile: (file: File) => Promise<string | undefined>;
19
-
20
20
  useFetchTTSFile: (id: string | null) => SWRResponse<FileItem>;
21
21
  }
22
22
 
@@ -38,26 +38,11 @@ export const createTTSFileSlice: StateCreator<
38
38
  type: fileType,
39
39
  };
40
40
  const file = new File([blob], fileName, fileOptions);
41
- return get().uploadTTSFile(file);
42
- },
43
- uploadTTSFile: async (file) => {
44
- try {
45
- const res = await legacyUploadService.uploadFile({
46
- createdAt: file.lastModified,
47
- data: await file.arrayBuffer(),
48
- fileType: file.type,
49
- name: file.name,
50
- saveMode: 'local',
51
- size: file.size,
52
- });
53
-
54
- const data = await fileService.createFile(res);
55
-
56
- return data.id;
57
- } catch (error) {
58
- // 提示用户上传失败
59
- console.error('upload error:', error);
60
- }
41
+
42
+ const res = await get().uploadWithProgress({ file });
43
+
44
+ return res?.id;
61
45
  },
62
- useFetchTTSFile: (id) => useSWR(id, fileService.getFile),
46
+ useFetchTTSFile: (id) =>
47
+ useClientDataSWR(!!id ? [FETCH_TTS_FILE, id] : null, () => fileService.getFile(id!)),
63
48
  });
@@ -16,7 +16,7 @@ const serverFileService = new ServerService();
16
16
  interface UploadWithProgressParams {
17
17
  file: File;
18
18
  knowledgeBaseId?: string;
19
- onStatusUpdate: (
19
+ onStatusUpdate?: (
20
20
  data:
21
21
  | {
22
22
  id: string;
@@ -53,7 +53,7 @@ export const createFileUploadSlice: StateCreator<
53
53
  > = (set, get) => ({
54
54
  internal_uploadToClientDB: async ({ file, onStatusUpdate }) => {
55
55
  if (!file.type.startsWith('image')) {
56
- onStatusUpdate({ id: file.name, type: 'removeFile' });
56
+ onStatusUpdate?.({ id: file.name, type: 'removeFile' });
57
57
  message.info({
58
58
  content: t('upload.fileOnlySupportInServerMode', {
59
59
  ext: file.name.split('.').pop(),
@@ -73,7 +73,7 @@ export const createFileUploadSlice: StateCreator<
73
73
  file,
74
74
  );
75
75
 
76
- onStatusUpdate({
76
+ onStatusUpdate?.({
77
77
  id: file.name,
78
78
  type: 'updateFile',
79
79
  value: {
@@ -99,7 +99,7 @@ export const createFileUploadSlice: StateCreator<
99
99
  // 2. if file exist, just skip upload
100
100
  if (checkStatus.isExist) {
101
101
  metadata = checkStatus.metadata as FileMetadata;
102
- onStatusUpdate({
102
+ onStatusUpdate?.({
103
103
  id: file.name,
104
104
  type: 'updateFile',
105
105
  value: { status: 'processing', uploadState: { progress: 100, restTime: 0, speed: 0 } },
@@ -107,7 +107,7 @@ export const createFileUploadSlice: StateCreator<
107
107
  } else {
108
108
  // 2. if file don't exist, need upload files
109
109
  metadata = await uploadService.uploadWithProgress(file, (status, upload) => {
110
- onStatusUpdate({
110
+ onStatusUpdate?.({
111
111
  id: file.name,
112
112
  type: 'updateFile',
113
113
  value: { status: status === 'success' ? 'processing' : status, uploadState: upload },
@@ -140,7 +140,7 @@ export const createFileUploadSlice: StateCreator<
140
140
  knowledgeBaseId,
141
141
  );
142
142
 
143
- onStatusUpdate({
143
+ onStatusUpdate?.({
144
144
  id: file.name,
145
145
  type: 'updateFile',
146
146
  value: {
@@ -1,72 +0,0 @@
1
- import { beforeAll, describe, expect, it, vi } from 'vitest';
2
-
3
- import { DB_File } from '@/database/client/schemas/files';
4
- import { edgeClient } from '@/libs/trpc/client';
5
- import { API_ENDPOINTS } from '@/services/_url';
6
- import { serverConfigSelectors } from '@/store/serverConfig/selectors';
7
- import { createServerConfigStore } from '@/store/serverConfig/store';
8
-
9
- import { legacyUploadService as uploadService } from '../upload_legacy';
10
-
11
- vi.mock('@/store/serverConfig/selectors');
12
- vi.mock('@/libs/trpc/client', () => {
13
- return {
14
- edgeClient: {
15
- upload: {
16
- createS3PreSignedUrl: { mutate: vi.fn() },
17
- },
18
- },
19
- };
20
- });
21
-
22
- beforeAll(() => {
23
- createServerConfigStore();
24
- });
25
-
26
- describe('UploadService', () => {
27
- describe('uploadFile', () => {
28
- it('should upload file to server when enableServer is true', async () => {
29
- // Arrange
30
- const file: DB_File = {
31
- data: new ArrayBuffer(10),
32
- fileType: 'text/plain',
33
- metadata: {},
34
- name: 'test.txt',
35
- saveMode: 'local',
36
- size: 10,
37
- };
38
- const mockCreateS3Url = vi.fn().mockResolvedValue('https://example.com');
39
- vi.mocked(edgeClient.upload.createS3PreSignedUrl.mutate).mockImplementation(mockCreateS3Url);
40
- vi.spyOn(serverConfigSelectors, 'enableUploadFileToServer').mockReturnValue(true);
41
- global.fetch = vi.fn().mockResolvedValue({ ok: true } as Response);
42
-
43
- // Act
44
- const result = await uploadService.uploadFile(file);
45
-
46
- // Assert
47
- expect(mockCreateS3Url).toHaveBeenCalledTimes(1);
48
- expect(fetch).toHaveBeenCalledTimes(1);
49
- expect(result.url).toMatch(/\/\d+\/[a-f0-9-]+\.txt/);
50
- expect(result.saveMode).toBe('url');
51
- });
52
-
53
- it('should save file locally when enableServer is false', async () => {
54
- // Arrange
55
- const file: DB_File = {
56
- data: new ArrayBuffer(10),
57
- fileType: 'text/plain',
58
- metadata: {},
59
- name: 'test.txt',
60
- saveMode: 'local',
61
- size: 10,
62
- };
63
- vi.spyOn(serverConfigSelectors, 'enableUploadFileToServer').mockReturnValue(false);
64
-
65
- // Act
66
- const result = await uploadService.uploadFile(file);
67
-
68
- // Assert
69
- expect(result).toEqual(file);
70
- });
71
- });
72
- });
@@ -1,104 +0,0 @@
1
- import { fileEnv } from '@/config/file';
2
- import { DB_File } from '@/database/client/schemas/files';
3
- import { edgeClient } from '@/libs/trpc/client';
4
- import { API_ENDPOINTS } from '@/services/_url';
5
- import { serverConfigSelectors } from '@/store/serverConfig/selectors';
6
- import compressImage from '@/utils/compressImage';
7
- import { uuid } from '@/utils/uuid';
8
-
9
- class UploadService {
10
- async uploadFile(file: DB_File) {
11
- if (this.enableServer) {
12
- const { data, ...params } = file;
13
- const filename = `${uuid()}.${file.name.split('.').at(-1)}`;
14
-
15
- // 精确到以 h 为单位的 path
16
- const date = (Date.now() / 1000 / 60 / 60).toFixed(0);
17
- const dirname = `${fileEnv.NEXT_PUBLIC_S3_FILE_PATH}/${date}`;
18
- const pathname = `${dirname}/${filename}`;
19
-
20
- const url = await edgeClient.upload.createS3PreSignedUrl.mutate({ pathname });
21
-
22
- const res = await fetch(url, {
23
- body: data,
24
- headers: { 'Content-Type': file.fileType },
25
- method: 'PUT',
26
- });
27
-
28
- if (res.ok) {
29
- return {
30
- ...params,
31
- metadata: { date, dirname: dirname, filename: filename, path: pathname },
32
- name: file.name,
33
- saveMode: 'url',
34
- url: pathname,
35
- } as DB_File;
36
- } else {
37
- throw new Error('Upload Error');
38
- }
39
- }
40
-
41
- // 跳过图片上传测试
42
- const isTestData = file.size === 1;
43
- if (this.isImage(file.fileType) && !isTestData) {
44
- return this.uploadImageFile(file);
45
- }
46
-
47
- // save to local storage
48
- // we may want to save to a remote server later
49
- return file;
50
- }
51
-
52
- /**
53
- * @deprecated
54
- * @param url
55
- * @param file
56
- */
57
- async uploadImageByUrl(url: string, file: Pick<DB_File, 'name' | 'metadata'>) {
58
- const res = await fetch(API_ENDPOINTS.proxy, { body: url, method: 'POST' });
59
- const data = await res.arrayBuffer();
60
- const fileType = res.headers.get('content-type') || 'image/webp';
61
-
62
- return this.uploadFile({
63
- data,
64
- fileType,
65
- metadata: file.metadata,
66
- name: file.name,
67
- saveMode: 'local',
68
- size: data.byteLength,
69
- });
70
- }
71
-
72
- private isImage(fileType: string) {
73
- const imageRegex = /^image\//;
74
- return imageRegex.test(fileType);
75
- }
76
-
77
- private async uploadImageFile(file: DB_File) {
78
- // 加载图片
79
- const url = file.url || URL.createObjectURL(new Blob([file.data!]));
80
-
81
- const img = new Image();
82
- img.src = url;
83
- await (() =>
84
- new Promise((resolve) => {
85
- img.addEventListener('load', resolve);
86
- }))();
87
-
88
- // 压缩图片
89
- const base64String = compressImage({ img, type: file.fileType });
90
- const binaryString = atob(base64String.split('base64,')[1]);
91
- const uint8Array = Uint8Array.from(binaryString, (char) => char.charCodeAt(0));
92
- file.data = uint8Array.buffer;
93
-
94
- return file;
95
- }
96
-
97
- private get enableServer() {
98
- return serverConfigSelectors.enableUploadFileToServer(
99
- window.global_serverConfigStore.getState(),
100
- );
101
- }
102
- }
103
-
104
- export const legacyUploadService = new UploadService();