@lobehub/chat 1.26.11 → 1.26.12
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 +25 -0
- package/package.json +1 -1
- package/src/services/__tests__/chat.test.ts +170 -96
- package/src/services/chat.ts +2 -1
package/CHANGELOG.md
CHANGED
@@ -2,6 +2,31 @@
|
|
2
2
|
|
3
3
|
# Changelog
|
4
4
|
|
5
|
+
### [Version 1.26.12](https://github.com/lobehub/lobe-chat/compare/v1.26.11...v1.26.12)
|
6
|
+
|
7
|
+
<sup>Released on **2024-10-30**</sup>
|
8
|
+
|
9
|
+
#### 🐛 Bug Fixes
|
10
|
+
|
11
|
+
- **misc**: Fix file image prompts in client mode.
|
12
|
+
|
13
|
+
<br/>
|
14
|
+
|
15
|
+
<details>
|
16
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
17
|
+
|
18
|
+
#### What's fixed
|
19
|
+
|
20
|
+
- **misc**: Fix file image prompts in client mode, closes [#4548](https://github.com/lobehub/lobe-chat/issues/4548) ([1b66639](https://github.com/lobehub/lobe-chat/commit/1b66639))
|
21
|
+
|
22
|
+
</details>
|
23
|
+
|
24
|
+
<div align="right">
|
25
|
+
|
26
|
+
[](#readme-top)
|
27
|
+
|
28
|
+
</div>
|
29
|
+
|
5
30
|
### [Version 1.26.11](https://github.com/lobehub/lobe-chat/compare/v1.26.10...v1.26.11)
|
6
31
|
|
7
32
|
<sup>Released on **2024-10-29**</sup>
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@lobehub/chat",
|
3
|
-
"version": "1.26.
|
3
|
+
"version": "1.26.12",
|
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",
|
@@ -2,8 +2,10 @@ import { LobeChatPluginManifest } from '@lobehub/chat-plugin-sdk';
|
|
2
2
|
import { act } from '@testing-library/react';
|
3
3
|
import { merge } from 'lodash-es';
|
4
4
|
import OpenAI from 'openai';
|
5
|
-
import { describe, expect, it, vi } from 'vitest';
|
5
|
+
import { beforeAll, beforeEach, describe, expect, it, vi } from 'vitest';
|
6
6
|
|
7
|
+
import { getAppConfig } from '@/config/app';
|
8
|
+
import { getServerDBConfig } from '@/config/db';
|
7
9
|
import { DEFAULT_AGENT_CONFIG } from '@/const/settings';
|
8
10
|
import {
|
9
11
|
LobeAnthropicAI,
|
@@ -53,6 +55,15 @@ vi.mock('@/utils/fetch', async (importOriginal) => {
|
|
53
55
|
return { ...(module as any), getMessageError: vi.fn() };
|
54
56
|
});
|
55
57
|
|
58
|
+
beforeEach(() => {
|
59
|
+
// 清除所有模块的缓存
|
60
|
+
vi.resetModules();
|
61
|
+
// 默认设置 isServerMode 为 false
|
62
|
+
vi.mock('@/const/version', () => ({
|
63
|
+
isServerMode: false,
|
64
|
+
}));
|
65
|
+
});
|
66
|
+
|
56
67
|
// mock auth
|
57
68
|
vi.mock('../_auth', () => ({
|
58
69
|
createHeaderWithAuth: vi.fn().mockResolvedValue({}),
|
@@ -126,92 +137,6 @@ describe('ChatService', () => {
|
|
126
137
|
);
|
127
138
|
});
|
128
139
|
|
129
|
-
describe('handle with files content', () => {
|
130
|
-
it('should includes files', async () => {
|
131
|
-
const messages = [
|
132
|
-
{
|
133
|
-
content: 'Hello',
|
134
|
-
role: 'user',
|
135
|
-
imageList: [
|
136
|
-
{
|
137
|
-
id: 'imagecx1',
|
138
|
-
url: 'http://example.com/xxx0asd-dsd.png',
|
139
|
-
alt: 'ttt.png',
|
140
|
-
},
|
141
|
-
],
|
142
|
-
fileList: [
|
143
|
-
{
|
144
|
-
fileType: 'plain/txt',
|
145
|
-
size: 100000,
|
146
|
-
id: 'file1',
|
147
|
-
url: 'http://abc.com/abc.txt',
|
148
|
-
name: 'abc.png',
|
149
|
-
},
|
150
|
-
{
|
151
|
-
id: 'file_oKMve9qySLMI',
|
152
|
-
name: '2402.16667v1.pdf',
|
153
|
-
type: 'application/pdf',
|
154
|
-
size: 11256078,
|
155
|
-
url: 'https://xxx.com/ppp/480497/5826c2b8-fde0-4de1-a54b-a224d5e3d898.pdf',
|
156
|
-
},
|
157
|
-
],
|
158
|
-
}, // Message with files
|
159
|
-
{ content: 'Hi', role: 'tool', plugin: { identifier: 'plugin1', apiName: 'api1' } }, // Message with tool role
|
160
|
-
{ content: 'Hey', role: 'assistant' }, // Regular user message
|
161
|
-
] as ChatMessage[];
|
162
|
-
|
163
|
-
const getChatCompletionSpy = vi.spyOn(chatService, 'getChatCompletion');
|
164
|
-
await chatService.createAssistantMessage({
|
165
|
-
messages,
|
166
|
-
plugins: [],
|
167
|
-
model: 'gpt-4o',
|
168
|
-
});
|
169
|
-
|
170
|
-
expect(getChatCompletionSpy).toHaveBeenCalledWith(
|
171
|
-
{
|
172
|
-
messages: [
|
173
|
-
{
|
174
|
-
content: [
|
175
|
-
{
|
176
|
-
text: `Hello
|
177
|
-
|
178
|
-
<files_info>
|
179
|
-
<images>
|
180
|
-
<images_docstring>here are user upload images you can refer to</images_docstring>
|
181
|
-
<image name="ttt.png" url="http://example.com/xxx0asd-dsd.png"></image>
|
182
|
-
</images>
|
183
|
-
<files>
|
184
|
-
<files_docstring>here are user upload files you can refer to</files_docstring>
|
185
|
-
<file id="file1" name="abc.png" type="plain/txt" size="100000" url="http://abc.com/abc.txt"></file>
|
186
|
-
<file id="file_oKMve9qySLMI" name="2402.16667v1.pdf" type="undefined" size="11256078" url="https://xxx.com/ppp/480497/5826c2b8-fde0-4de1-a54b-a224d5e3d898.pdf"></file>
|
187
|
-
</files>
|
188
|
-
</files_info>`,
|
189
|
-
type: 'text',
|
190
|
-
},
|
191
|
-
{
|
192
|
-
image_url: { detail: 'auto', url: 'http://example.com/xxx0asd-dsd.png' },
|
193
|
-
type: 'image_url',
|
194
|
-
},
|
195
|
-
],
|
196
|
-
role: 'user',
|
197
|
-
},
|
198
|
-
{
|
199
|
-
content: 'Hi',
|
200
|
-
name: 'plugin1____api1',
|
201
|
-
role: 'tool',
|
202
|
-
},
|
203
|
-
{
|
204
|
-
content: 'Hey',
|
205
|
-
role: 'assistant',
|
206
|
-
},
|
207
|
-
],
|
208
|
-
model: 'gpt-4o',
|
209
|
-
},
|
210
|
-
undefined,
|
211
|
-
);
|
212
|
-
});
|
213
|
-
});
|
214
|
-
|
215
140
|
describe('should handle content correctly for vision models', () => {
|
216
141
|
it('should include image content when with vision model', async () => {
|
217
142
|
const messages = [
|
@@ -243,15 +168,7 @@ describe('ChatService', () => {
|
|
243
168
|
{
|
244
169
|
content: [
|
245
170
|
{
|
246
|
-
text:
|
247
|
-
|
248
|
-
<files_info>
|
249
|
-
<images>
|
250
|
-
<images_docstring>here are user upload images you can refer to</images_docstring>
|
251
|
-
<image name="abc.png" url="http://example.com/image.jpg"></image>
|
252
|
-
</images>
|
253
|
-
|
254
|
-
</files_info>`,
|
171
|
+
text: 'Hello',
|
255
172
|
type: 'text',
|
256
173
|
},
|
257
174
|
{
|
@@ -829,6 +746,163 @@ describe('ChatService', () => {
|
|
829
746
|
},
|
830
747
|
]);
|
831
748
|
});
|
749
|
+
|
750
|
+
describe('handle with files content in server mode', () => {
|
751
|
+
it('should includes files', async () => {
|
752
|
+
// 重新模拟模块,设置 isServerMode 为 true
|
753
|
+
vi.doMock('@/const/version', () => ({
|
754
|
+
isServerMode: true,
|
755
|
+
}));
|
756
|
+
|
757
|
+
// 需要在修改模拟后重新导入相关模块
|
758
|
+
const { chatService } = await import('../chat');
|
759
|
+
|
760
|
+
const messages = [
|
761
|
+
{
|
762
|
+
content: 'Hello',
|
763
|
+
role: 'user',
|
764
|
+
imageList: [
|
765
|
+
{
|
766
|
+
id: 'imagecx1',
|
767
|
+
url: 'http://example.com/xxx0asd-dsd.png',
|
768
|
+
alt: 'ttt.png',
|
769
|
+
},
|
770
|
+
],
|
771
|
+
fileList: [
|
772
|
+
{
|
773
|
+
fileType: 'plain/txt',
|
774
|
+
size: 100000,
|
775
|
+
id: 'file1',
|
776
|
+
url: 'http://abc.com/abc.txt',
|
777
|
+
name: 'abc.png',
|
778
|
+
},
|
779
|
+
{
|
780
|
+
id: 'file_oKMve9qySLMI',
|
781
|
+
name: '2402.16667v1.pdf',
|
782
|
+
type: 'application/pdf',
|
783
|
+
size: 11256078,
|
784
|
+
url: 'https://xxx.com/ppp/480497/5826c2b8-fde0-4de1-a54b-a224d5e3d898.pdf',
|
785
|
+
},
|
786
|
+
],
|
787
|
+
}, // Message with files
|
788
|
+
{ content: 'Hi', role: 'tool', plugin: { identifier: 'plugin1', apiName: 'api1' } }, // Message with tool role
|
789
|
+
{ content: 'Hey', role: 'assistant' }, // Regular user message
|
790
|
+
] as ChatMessage[];
|
791
|
+
|
792
|
+
const output = chatService['processMessages']({
|
793
|
+
messages,
|
794
|
+
model: 'gpt-4o',
|
795
|
+
});
|
796
|
+
|
797
|
+
expect(output).toEqual([
|
798
|
+
{
|
799
|
+
content: [
|
800
|
+
{
|
801
|
+
text: `Hello
|
802
|
+
|
803
|
+
<files_info>
|
804
|
+
<images>
|
805
|
+
<images_docstring>here are user upload images you can refer to</images_docstring>
|
806
|
+
<image name="ttt.png" url="http://example.com/xxx0asd-dsd.png"></image>
|
807
|
+
</images>
|
808
|
+
<files>
|
809
|
+
<files_docstring>here are user upload files you can refer to</files_docstring>
|
810
|
+
<file id="file1" name="abc.png" type="plain/txt" size="100000" url="http://abc.com/abc.txt"></file>
|
811
|
+
<file id="file_oKMve9qySLMI" name="2402.16667v1.pdf" type="undefined" size="11256078" url="https://xxx.com/ppp/480497/5826c2b8-fde0-4de1-a54b-a224d5e3d898.pdf"></file>
|
812
|
+
</files>
|
813
|
+
</files_info>`,
|
814
|
+
type: 'text',
|
815
|
+
},
|
816
|
+
{
|
817
|
+
image_url: { detail: 'auto', url: 'http://example.com/xxx0asd-dsd.png' },
|
818
|
+
type: 'image_url',
|
819
|
+
},
|
820
|
+
],
|
821
|
+
role: 'user',
|
822
|
+
},
|
823
|
+
{
|
824
|
+
content: 'Hi',
|
825
|
+
name: 'plugin1____api1',
|
826
|
+
role: 'tool',
|
827
|
+
},
|
828
|
+
{
|
829
|
+
content: 'Hey',
|
830
|
+
role: 'assistant',
|
831
|
+
},
|
832
|
+
]);
|
833
|
+
});
|
834
|
+
});
|
835
|
+
|
836
|
+
it('should include image files in server mode', async () => {
|
837
|
+
// 重新模拟模块,设置 isServerMode 为 true
|
838
|
+
vi.doMock('@/const/version', () => ({
|
839
|
+
isServerMode: true,
|
840
|
+
}));
|
841
|
+
|
842
|
+
// 需要在修改模拟后重新导入相关模块
|
843
|
+
const { chatService } = await import('../chat');
|
844
|
+
const messages = [
|
845
|
+
{
|
846
|
+
content: 'Hello',
|
847
|
+
role: 'user',
|
848
|
+
imageList: [
|
849
|
+
{
|
850
|
+
id: 'file1',
|
851
|
+
url: 'http://example.com/image.jpg',
|
852
|
+
alt: 'abc.png',
|
853
|
+
},
|
854
|
+
],
|
855
|
+
}, // Message with files
|
856
|
+
{ content: 'Hi', role: 'tool', plugin: { identifier: 'plugin1', apiName: 'api1' } }, // Message with tool role
|
857
|
+
{ content: 'Hey', role: 'assistant' }, // Regular user message
|
858
|
+
] as ChatMessage[];
|
859
|
+
|
860
|
+
const getChatCompletionSpy = vi.spyOn(chatService, 'getChatCompletion');
|
861
|
+
await chatService.createAssistantMessage({
|
862
|
+
messages,
|
863
|
+
plugins: [],
|
864
|
+
model: 'gpt-4-vision-preview',
|
865
|
+
});
|
866
|
+
|
867
|
+
expect(getChatCompletionSpy).toHaveBeenCalledWith(
|
868
|
+
{
|
869
|
+
messages: [
|
870
|
+
{
|
871
|
+
content: [
|
872
|
+
{
|
873
|
+
text: `Hello
|
874
|
+
|
875
|
+
<files_info>
|
876
|
+
<images>
|
877
|
+
<images_docstring>here are user upload images you can refer to</images_docstring>
|
878
|
+
<image name="abc.png" url="http://example.com/image.jpg"></image>
|
879
|
+
</images>
|
880
|
+
|
881
|
+
</files_info>`,
|
882
|
+
type: 'text',
|
883
|
+
},
|
884
|
+
{
|
885
|
+
image_url: { detail: 'auto', url: 'http://example.com/image.jpg' },
|
886
|
+
type: 'image_url',
|
887
|
+
},
|
888
|
+
],
|
889
|
+
role: 'user',
|
890
|
+
},
|
891
|
+
{
|
892
|
+
content: 'Hi',
|
893
|
+
name: 'plugin1____api1',
|
894
|
+
role: 'tool',
|
895
|
+
},
|
896
|
+
{
|
897
|
+
content: 'Hey',
|
898
|
+
role: 'assistant',
|
899
|
+
},
|
900
|
+
],
|
901
|
+
model: 'gpt-4-vision-preview',
|
902
|
+
},
|
903
|
+
undefined,
|
904
|
+
);
|
905
|
+
});
|
832
906
|
});
|
833
907
|
});
|
834
908
|
|
package/src/services/chat.ts
CHANGED
@@ -7,6 +7,7 @@ import { INBOX_GUIDE_SYSTEMROLE } from '@/const/guide';
|
|
7
7
|
import { INBOX_SESSION_ID } from '@/const/session';
|
8
8
|
import { DEFAULT_AGENT_CONFIG } from '@/const/settings';
|
9
9
|
import { TracePayload, TraceTagMap } from '@/const/trace';
|
10
|
+
import { isServerMode } from '@/const/version';
|
10
11
|
import { AgentRuntime, ChatCompletionErrorPayload, ModelProvider } from '@/libs/agent-runtime';
|
11
12
|
import { filesPrompts } from '@/prompts/files';
|
12
13
|
import { useSessionStore } from '@/store/session';
|
@@ -420,7 +421,7 @@ class ChatService {
|
|
420
421
|
|
421
422
|
const imageList = m.imageList || [];
|
422
423
|
|
423
|
-
const filesContext = filesPrompts({ fileList: m.fileList, imageList });
|
424
|
+
const filesContext = isServerMode ? filesPrompts({ fileList: m.fileList, imageList }) : '';
|
424
425
|
return [
|
425
426
|
{ text: (m.content + '\n\n' + filesContext).trim(), type: 'text' },
|
426
427
|
...imageList.map(
|