@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 CHANGED
@@ -1 +1 @@
1
- lts/iron
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
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#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
@@ -1,4 +1,13 @@
1
1
  [
2
+ {
3
+ "children": {
4
+ "fixes": [
5
+ "Fix image prompts with some user cases."
6
+ ]
7
+ },
8
+ "date": "2025-02-22",
9
+ "version": "1.62.8"
10
+ },
2
11
  {
3
12
  "children": {
4
13
  "improvements": [
@@ -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.7",
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 fallback={<Skeleton active style={{ width: '100%' }} />}>
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 [!isLobeThinkingClosed(message?.content)];
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
- rehypePlugin,
9
+ remarkPlugin: createRemarkCustomTagPlugin(ARTIFACT_THINKING_TAG),
10
10
  tag: ARTIFACT_THINKING_TAG,
11
11
  };
12
12
 
@@ -0,0 +1,6 @@
1
+ export const isTagClosed = (tag: string, input: string = '') => {
2
+ const openTag = `<${tag}>`;
3
+ const closeTag = `</${tag}>`;
4
+
5
+ return input.includes(openTag) && input.includes(closeTag);
6
+ };
@@ -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
- response = await this.client.chat.completions.create(
218
- {
219
- ...postPayload,
220
- messages,
221
- ...(chatCompletion?.noUserId ? {} : { user: options?.user }),
222
- },
223
- {
224
- // https://github.com/lobehub/lobe-chat/pull/318
225
- headers: { Accept: '*/*', ...options?.requestHeaders },
226
- signal: options?.signal,
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
- `<files_info>
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
- `<files_info>
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
- `<files_info>
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 = `<files_info>
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).toEqual(abortController);
928
+ expect(state.chatLoadingIdsAbortController).toStrictEqual(abortController);
929
929
  });
930
930
  });
931
931
 
@@ -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;