@lobehub/chat 1.62.7 → 1.62.8
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/.nvmrc +1 -1
- package/CHANGELOG.md +25 -0
- package/changelog/v1.json +9 -0
- package/docker-compose/local/docker-compose.yml +1 -0
- package/package.json +1 -1
- package/src/features/Conversation/Error/index.tsx +8 -1
- package/src/features/Conversation/components/MarkdownElements/LobeThinking/Render.tsx +2 -11
- package/src/features/Conversation/components/MarkdownElements/LobeThinking/index.ts +2 -2
- package/src/features/Conversation/components/MarkdownElements/utils.ts +6 -0
- package/src/libs/agent-runtime/utils/openaiCompatibleFactory/index.ts +14 -12
- package/src/prompts/files/index.test.ts +27 -6
- package/src/prompts/files/index.ts +9 -2
- package/src/services/__tests__/chat.test.ts +16 -2
- package/src/store/chat/slices/aiChat/actions/__tests__/generateAIChat.test.ts +1 -1
- package/src/features/Conversation/components/MarkdownElements/LobeThinking/rehypePlugin.test.ts +0 -124
- package/src/features/Conversation/components/MarkdownElements/LobeThinking/rehypePlugin.ts +0 -51
package/.nvmrc
CHANGED
@@ -1 +1 @@
|
|
1
|
-
lts/
|
1
|
+
lts/jod
|
package/CHANGELOG.md
CHANGED
@@ -2,6 +2,31 @@
|
|
2
2
|
|
3
3
|
# Changelog
|
4
4
|
|
5
|
+
### [Version 1.62.8](https://github.com/lobehub/lobe-chat/compare/v1.62.7...v1.62.8)
|
6
|
+
|
7
|
+
<sup>Released on **2025-02-22**</sup>
|
8
|
+
|
9
|
+
#### 🐛 Bug Fixes
|
10
|
+
|
11
|
+
- **misc**: Fix image prompts with some user cases.
|
12
|
+
|
13
|
+
<br/>
|
14
|
+
|
15
|
+
<details>
|
16
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
17
|
+
|
18
|
+
#### What's fixed
|
19
|
+
|
20
|
+
- **misc**: Fix image prompts with some user cases, closes [#6406](https://github.com/lobehub/lobe-chat/issues/6406) ([e9df49d](https://github.com/lobehub/lobe-chat/commit/e9df49d))
|
21
|
+
|
22
|
+
</details>
|
23
|
+
|
24
|
+
<div align="right">
|
25
|
+
|
26
|
+
[](#readme-top)
|
27
|
+
|
28
|
+
</div>
|
29
|
+
|
5
30
|
### [Version 1.62.7](https://github.com/lobehub/lobe-chat/compare/v1.62.6...v1.62.7)
|
6
31
|
|
7
32
|
<sup>Released on **2025-02-21**</sup>
|
package/changelog/v1.json
CHANGED
@@ -66,6 +66,7 @@ services:
|
|
66
66
|
postgresql:
|
67
67
|
condition: service_healthy
|
68
68
|
environment:
|
69
|
+
httpport: ${CASDOOR_PORT}
|
69
70
|
RUNNING_IN_DOCKER: 'true'
|
70
71
|
driverName: 'postgres'
|
71
72
|
dataSourceName: 'user=postgres password=${POSTGRES_PASSWORD} host=postgresql port=5432 sslmode=disable dbname=casdoor'
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@lobehub/chat",
|
3
|
-
"version": "1.62.
|
3
|
+
"version": "1.62.8",
|
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",
|
@@ -14,6 +14,7 @@ import ClerkLogin from './ClerkLogin';
|
|
14
14
|
import ErrorJsonViewer from './ErrorJsonViewer';
|
15
15
|
import InvalidAPIKey from './InvalidAPIKey';
|
16
16
|
import InvalidAccessCode from './InvalidAccessCode';
|
17
|
+
import { ErrorActionContainer } from './style';
|
17
18
|
|
18
19
|
const loading = () => <Skeleton active />;
|
19
20
|
|
@@ -120,7 +121,13 @@ const ErrorMessageExtra = memo<{ data: ChatMessage }>(({ data }) => {
|
|
120
121
|
});
|
121
122
|
|
122
123
|
export default memo<{ data: ChatMessage }>(({ data }) => (
|
123
|
-
<Suspense
|
124
|
+
<Suspense
|
125
|
+
fallback={
|
126
|
+
<ErrorActionContainer>
|
127
|
+
<Skeleton active style={{ width: '100%' }} />
|
128
|
+
</ErrorActionContainer>
|
129
|
+
}
|
130
|
+
>
|
124
131
|
<ErrorMessageExtra data={data} />
|
125
132
|
</Suspense>
|
126
133
|
));
|
@@ -6,21 +6,12 @@ import { useChatStore } from '@/store/chat';
|
|
6
6
|
import { chatSelectors } from '@/store/chat/selectors';
|
7
7
|
|
8
8
|
import { MarkdownElementProps } from '../type';
|
9
|
-
|
10
|
-
/**
|
11
|
-
* Replace all line breaks in the matched `lobeArtifact` tag with an empty string
|
12
|
-
*/
|
13
|
-
export const isLobeThinkingClosed = (input: string = '') => {
|
14
|
-
const openTag = `<${ARTIFACT_THINKING_TAG}>`;
|
15
|
-
const closeTag = `</${ARTIFACT_THINKING_TAG}>`;
|
16
|
-
|
17
|
-
return input.includes(openTag) && input.includes(closeTag);
|
18
|
-
};
|
9
|
+
import { isTagClosed } from '../utils';
|
19
10
|
|
20
11
|
const Render = memo<MarkdownElementProps>(({ children, id }) => {
|
21
12
|
const [isGenerating] = useChatStore((s) => {
|
22
13
|
const message = chatSelectors.getMessageById(id)(s);
|
23
|
-
return [!
|
14
|
+
return [!isTagClosed(ARTIFACT_THINKING_TAG, message?.content)];
|
24
15
|
});
|
25
16
|
|
26
17
|
return (
|
@@ -1,12 +1,12 @@
|
|
1
1
|
import { ARTIFACT_THINKING_TAG } from '@/const/plugin';
|
2
2
|
|
3
|
+
import { createRemarkCustomTagPlugin } from '../remarkPlugins/createRemarkCustomTagPlugin';
|
3
4
|
import { MarkdownElement } from '../type';
|
4
5
|
import Component from './Render';
|
5
|
-
import rehypePlugin from './rehypePlugin';
|
6
6
|
|
7
7
|
const LobeThinkingElement: MarkdownElement = {
|
8
8
|
Component,
|
9
|
-
|
9
|
+
remarkPlugin: createRemarkCustomTagPlugin(ARTIFACT_THINKING_TAG),
|
10
10
|
tag: ARTIFACT_THINKING_TAG,
|
11
11
|
};
|
12
12
|
|
@@ -211,21 +211,23 @@ export const LobeOpenAICompatibleFactory = <T extends Record<string, any> = any>
|
|
211
211
|
callbacks: options?.callback,
|
212
212
|
provider,
|
213
213
|
};
|
214
|
+
|
214
215
|
if (customClient?.createChatCompletionStream) {
|
215
216
|
response = customClient.createChatCompletionStream(this.client, payload, this) as any;
|
216
217
|
} else {
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
},
|
228
|
-
|
218
|
+
const finalPayload = {
|
219
|
+
...postPayload,
|
220
|
+
messages,
|
221
|
+
...(chatCompletion?.noUserId ? {} : { user: options?.user }),
|
222
|
+
};
|
223
|
+
if (debug?.chatCompletion?.()) {
|
224
|
+
console.log('[requestPayload]:', JSON.stringify(finalPayload, null, 2));
|
225
|
+
}
|
226
|
+
response = await this.client.chat.completions.create(finalPayload, {
|
227
|
+
// https://github.com/lobehub/lobe-chat/pull/318
|
228
|
+
headers: { Accept: '*/*', ...options?.requestHeaders },
|
229
|
+
signal: options?.signal,
|
230
|
+
});
|
229
231
|
}
|
230
232
|
|
231
233
|
if (postPayload.stream) {
|
@@ -27,13 +27,20 @@ describe('filesPrompts', () => {
|
|
27
27
|
});
|
28
28
|
|
29
29
|
expect(result).toEqual(
|
30
|
-
|
30
|
+
`<!-- SYSTEM CONTEXT (NOT PART OF USER QUERY) -->
|
31
|
+
<context.instruction>following part contains context information injected by the system. Please follow these instructions:
|
32
|
+
|
33
|
+
1. Always prioritize handling user-visible content.
|
34
|
+
2. the context is only required when user's queries rely on it.
|
35
|
+
</context.instruction>
|
36
|
+
<files_info>
|
31
37
|
<images>
|
32
38
|
<images_docstring>here are user upload images you can refer to</images_docstring>
|
33
39
|
<image name="test image" url="https://example.com/image.jpg"></image>
|
34
40
|
</images>
|
35
41
|
|
36
|
-
</files_info
|
42
|
+
</files_info>
|
43
|
+
<!-- END SYSTEM CONTEXT -->`,
|
37
44
|
);
|
38
45
|
});
|
39
46
|
|
@@ -44,13 +51,20 @@ describe('filesPrompts', () => {
|
|
44
51
|
});
|
45
52
|
|
46
53
|
expect(result).toEqual(
|
47
|
-
|
54
|
+
`<!-- SYSTEM CONTEXT (NOT PART OF USER QUERY) -->
|
55
|
+
<context.instruction>following part contains context information injected by the system. Please follow these instructions:
|
56
|
+
|
57
|
+
1. Always prioritize handling user-visible content.
|
58
|
+
2. the context is only required when user's queries rely on it.
|
59
|
+
</context.instruction>
|
60
|
+
<files_info>
|
48
61
|
|
49
62
|
<files>
|
50
63
|
<files_docstring>here are user upload files you can refer to</files_docstring>
|
51
64
|
<file id="file-1" name="test.pdf" type="application/pdf" size="1024" url="https://example.com/test.pdf"></file>
|
52
65
|
</files>
|
53
|
-
</files_info
|
66
|
+
</files_info>
|
67
|
+
<!-- END SYSTEM CONTEXT -->`,
|
54
68
|
);
|
55
69
|
});
|
56
70
|
|
@@ -61,7 +75,13 @@ describe('filesPrompts', () => {
|
|
61
75
|
});
|
62
76
|
|
63
77
|
expect(result).toEqual(
|
64
|
-
|
78
|
+
`<!-- SYSTEM CONTEXT (NOT PART OF USER QUERY) -->
|
79
|
+
<context.instruction>following part contains context information injected by the system. Please follow these instructions:
|
80
|
+
|
81
|
+
1. Always prioritize handling user-visible content.
|
82
|
+
2. the context is only required when user's queries rely on it.
|
83
|
+
</context.instruction>
|
84
|
+
<files_info>
|
65
85
|
<images>
|
66
86
|
<images_docstring>here are user upload images you can refer to</images_docstring>
|
67
87
|
<image name="test image" url="https://example.com/image.jpg"></image>
|
@@ -70,7 +90,8 @@ describe('filesPrompts', () => {
|
|
70
90
|
<files_docstring>here are user upload files you can refer to</files_docstring>
|
71
91
|
<file id="file-1" name="test.pdf" type="application/pdf" size="1024" url="https://example.com/test.pdf"></file>
|
72
92
|
</files>
|
73
|
-
</files_info
|
93
|
+
</files_info>
|
94
|
+
<!-- END SYSTEM CONTEXT -->`,
|
74
95
|
);
|
75
96
|
});
|
76
97
|
|
@@ -12,10 +12,17 @@ export const filesPrompts = ({
|
|
12
12
|
}) => {
|
13
13
|
if (imageList.length === 0 && (fileList || []).length === 0) return '';
|
14
14
|
|
15
|
-
const prompt =
|
15
|
+
const prompt = `<!-- SYSTEM CONTEXT (NOT PART OF USER QUERY) -->
|
16
|
+
<context.instruction>following part contains context information injected by the system. Please follow these instructions:
|
17
|
+
|
18
|
+
1. Always prioritize handling user-visible content.
|
19
|
+
2. the context is only required when user's queries rely on it.
|
20
|
+
</context.instruction>
|
21
|
+
<files_info>
|
16
22
|
${imagesPrompts(imageList)}
|
17
23
|
${fileList ? filePrompts(fileList) : ''}
|
18
|
-
</files_info
|
24
|
+
</files_info>
|
25
|
+
<!-- END SYSTEM CONTEXT -->`;
|
19
26
|
|
20
27
|
return prompt.trim();
|
21
28
|
};
|
@@ -800,6 +800,12 @@ describe('ChatService', () => {
|
|
800
800
|
{
|
801
801
|
text: `Hello
|
802
802
|
|
803
|
+
<!-- SYSTEM CONTEXT (NOT PART OF USER QUERY) -->
|
804
|
+
<context.instruction>following part contains context information injected by the system. Please follow these instructions:
|
805
|
+
|
806
|
+
1. Always prioritize handling user-visible content.
|
807
|
+
2. the context is only required when user's queries rely on it.
|
808
|
+
</context.instruction>
|
803
809
|
<files_info>
|
804
810
|
<images>
|
805
811
|
<images_docstring>here are user upload images you can refer to</images_docstring>
|
@@ -810,7 +816,8 @@ describe('ChatService', () => {
|
|
810
816
|
<file id="file1" name="abc.png" type="plain/txt" size="100000" url="http://abc.com/abc.txt"></file>
|
811
817
|
<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
818
|
</files>
|
813
|
-
</files_info
|
819
|
+
</files_info>
|
820
|
+
<!-- END SYSTEM CONTEXT -->`,
|
814
821
|
type: 'text',
|
815
822
|
},
|
816
823
|
{
|
@@ -867,13 +874,20 @@ describe('ChatService', () => {
|
|
867
874
|
{
|
868
875
|
text: `Hello
|
869
876
|
|
877
|
+
<!-- SYSTEM CONTEXT (NOT PART OF USER QUERY) -->
|
878
|
+
<context.instruction>following part contains context information injected by the system. Please follow these instructions:
|
879
|
+
|
880
|
+
1. Always prioritize handling user-visible content.
|
881
|
+
2. the context is only required when user's queries rely on it.
|
882
|
+
</context.instruction>
|
870
883
|
<files_info>
|
871
884
|
<images>
|
872
885
|
<images_docstring>here are user upload images you can refer to</images_docstring>
|
873
886
|
<image name="abc.png" url="http://example.com/image.jpg"></image>
|
874
887
|
</images>
|
875
888
|
|
876
|
-
</files_info
|
889
|
+
</files_info>
|
890
|
+
<!-- END SYSTEM CONTEXT -->`,
|
877
891
|
type: 'text',
|
878
892
|
},
|
879
893
|
{
|
@@ -925,7 +925,7 @@ describe('chatMessage actions', () => {
|
|
925
925
|
});
|
926
926
|
|
927
927
|
const state = useChatStore.getState();
|
928
|
-
expect(state.chatLoadingIdsAbortController).
|
928
|
+
expect(state.chatLoadingIdsAbortController).toStrictEqual(abortController);
|
929
929
|
});
|
930
930
|
});
|
931
931
|
|
package/src/features/Conversation/components/MarkdownElements/LobeThinking/rehypePlugin.test.ts
DELETED
@@ -1,124 +0,0 @@
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
2
|
-
|
3
|
-
import rehypePlugin from './rehypePlugin';
|
4
|
-
|
5
|
-
describe('rehypePlugin', () => {
|
6
|
-
it('should transform <lobeThinking> tags within paragraphs', () => {
|
7
|
-
const tree = {
|
8
|
-
type: 'root',
|
9
|
-
children: [
|
10
|
-
{
|
11
|
-
type: 'element',
|
12
|
-
tagName: 'p',
|
13
|
-
children: [
|
14
|
-
{ type: 'text', value: 'Before ' },
|
15
|
-
{ type: 'raw', value: '<lobeThinking>' },
|
16
|
-
{ type: 'text', value: 'Thinking content' },
|
17
|
-
{ type: 'raw', value: '</lobeThinking>' },
|
18
|
-
{ type: 'text', value: ' After' },
|
19
|
-
],
|
20
|
-
},
|
21
|
-
],
|
22
|
-
};
|
23
|
-
|
24
|
-
const expectedTree = {
|
25
|
-
type: 'root',
|
26
|
-
children: [
|
27
|
-
{
|
28
|
-
type: 'element',
|
29
|
-
tagName: 'lobeThinking',
|
30
|
-
properties: {},
|
31
|
-
children: [{ type: 'text', value: 'Thinking content' }],
|
32
|
-
},
|
33
|
-
],
|
34
|
-
};
|
35
|
-
|
36
|
-
const plugin = rehypePlugin();
|
37
|
-
plugin(tree);
|
38
|
-
|
39
|
-
expect(tree).toEqual(expectedTree);
|
40
|
-
});
|
41
|
-
|
42
|
-
it('should not transform when only closing tag is present', () => {
|
43
|
-
const tree = {
|
44
|
-
type: 'root',
|
45
|
-
children: [
|
46
|
-
{
|
47
|
-
type: 'element',
|
48
|
-
tagName: 'p',
|
49
|
-
children: [
|
50
|
-
{ type: 'text', value: 'Thinking content' },
|
51
|
-
{ type: 'raw', value: '</lobeThinking>' },
|
52
|
-
{ type: 'text', value: ' After' },
|
53
|
-
],
|
54
|
-
},
|
55
|
-
],
|
56
|
-
};
|
57
|
-
|
58
|
-
const originalTree = JSON.parse(JSON.stringify(tree));
|
59
|
-
|
60
|
-
const plugin = rehypePlugin();
|
61
|
-
plugin(tree);
|
62
|
-
|
63
|
-
expect(tree).toEqual(originalTree);
|
64
|
-
});
|
65
|
-
|
66
|
-
it('should handle multiple paragraphs and transformations', () => {
|
67
|
-
const tree = {
|
68
|
-
type: 'root',
|
69
|
-
children: [
|
70
|
-
{
|
71
|
-
type: 'element',
|
72
|
-
tagName: 'p',
|
73
|
-
children: [{ type: 'text', value: 'Normal paragraph' }],
|
74
|
-
},
|
75
|
-
{
|
76
|
-
type: 'element',
|
77
|
-
tagName: 'p',
|
78
|
-
children: [
|
79
|
-
{ type: 'raw', value: '<lobeThinking>' },
|
80
|
-
{ type: 'text', value: 'First thinking' },
|
81
|
-
{ type: 'raw', value: '</lobeThinking>' },
|
82
|
-
],
|
83
|
-
},
|
84
|
-
{
|
85
|
-
type: 'element',
|
86
|
-
tagName: 'p',
|
87
|
-
children: [
|
88
|
-
{ type: 'raw', value: '<lobeThinking>' },
|
89
|
-
{ type: 'text', value: 'Second thinking' },
|
90
|
-
{ type: 'raw', value: '</lobeThinking>' },
|
91
|
-
],
|
92
|
-
},
|
93
|
-
],
|
94
|
-
};
|
95
|
-
|
96
|
-
const expectedTree = {
|
97
|
-
type: 'root',
|
98
|
-
children: [
|
99
|
-
{
|
100
|
-
type: 'element',
|
101
|
-
tagName: 'p',
|
102
|
-
children: [{ type: 'text', value: 'Normal paragraph' }],
|
103
|
-
},
|
104
|
-
{
|
105
|
-
type: 'element',
|
106
|
-
tagName: 'lobeThinking',
|
107
|
-
properties: {},
|
108
|
-
children: [{ type: 'text', value: 'First thinking' }],
|
109
|
-
},
|
110
|
-
{
|
111
|
-
type: 'element',
|
112
|
-
tagName: 'lobeThinking',
|
113
|
-
properties: {},
|
114
|
-
children: [{ type: 'text', value: 'Second thinking' }],
|
115
|
-
},
|
116
|
-
],
|
117
|
-
};
|
118
|
-
|
119
|
-
const plugin = rehypePlugin();
|
120
|
-
plugin(tree);
|
121
|
-
|
122
|
-
expect(tree).toEqual(expectedTree);
|
123
|
-
});
|
124
|
-
});
|
@@ -1,51 +0,0 @@
|
|
1
|
-
import type { Node } from 'unist';
|
2
|
-
import { visit } from 'unist-util-visit';
|
3
|
-
|
4
|
-
import { ARTIFACT_THINKING_TAG } from '@/const/plugin';
|
5
|
-
|
6
|
-
// eslint-disable-next-line unicorn/consistent-function-scoping
|
7
|
-
const rehypePlugin = () => (tree: Node) => {
|
8
|
-
visit(tree, 'element', (node: any, index, parent) => {
|
9
|
-
if (node.type === 'element' && node.tagName === 'p') {
|
10
|
-
const children = node.children || [];
|
11
|
-
const openTagIndex = children.findIndex(
|
12
|
-
(child: any) => child.type === 'raw' && child.value === `<${ARTIFACT_THINKING_TAG}>`,
|
13
|
-
);
|
14
|
-
const closeTagIndex = children.findIndex(
|
15
|
-
(child: any) => child.type === 'raw' && child.value === `</${ARTIFACT_THINKING_TAG}>`,
|
16
|
-
);
|
17
|
-
|
18
|
-
if (openTagIndex !== -1) {
|
19
|
-
// 有闭合标签的情况
|
20
|
-
if (closeTagIndex !== -1 && closeTagIndex > openTagIndex) {
|
21
|
-
const content = children.slice(openTagIndex + 1, closeTagIndex);
|
22
|
-
const lobeThinkingNode = {
|
23
|
-
children: content,
|
24
|
-
properties: {},
|
25
|
-
tagName: ARTIFACT_THINKING_TAG,
|
26
|
-
type: 'element',
|
27
|
-
};
|
28
|
-
|
29
|
-
// Replace the entire paragraph with our new lobeThinking node
|
30
|
-
parent.children.splice(index, 1, lobeThinkingNode);
|
31
|
-
return index; // Skip processing the newly inserted node
|
32
|
-
} else {
|
33
|
-
// 无闭合标签的情况
|
34
|
-
const content = children.slice(openTagIndex + 1);
|
35
|
-
const lobeThinkingNode = {
|
36
|
-
children: content,
|
37
|
-
properties: {},
|
38
|
-
tagName: ARTIFACT_THINKING_TAG,
|
39
|
-
type: 'element',
|
40
|
-
};
|
41
|
-
|
42
|
-
// Replace the entire paragraph with our new lobeThinking node
|
43
|
-
parent.children.splice(index, 1, lobeThinkingNode);
|
44
|
-
return index; // Skip processing the newly inserted node
|
45
|
-
}
|
46
|
-
}
|
47
|
-
}
|
48
|
-
});
|
49
|
-
};
|
50
|
-
|
51
|
-
export default rehypePlugin;
|