@lobehub/lobehub 2.0.0-next.56 → 2.0.0-next.58

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,56 @@
2
2
 
3
3
  # Changelog
4
4
 
5
+ ## [Version 2.0.0-next.58](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.57...v2.0.0-next.58)
6
+
7
+ <sup>Released on **2025-11-14**</sup>
8
+
9
+ #### ✨ Features
10
+
11
+ - **misc**: Support DeepSeek Interleaved thinking.
12
+
13
+ <br/>
14
+
15
+ <details>
16
+ <summary><kbd>Improvements and Fixes</kbd></summary>
17
+
18
+ #### What's improved
19
+
20
+ - **misc**: Support DeepSeek Interleaved thinking, closes [#10219](https://github.com/lobehub/lobe-chat/issues/10219) ([3736a85](https://github.com/lobehub/lobe-chat/commit/3736a85))
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
+
30
+ ## [Version 2.0.0-next.57](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.56...v2.0.0-next.57)
31
+
32
+ <sup>Released on **2025-11-14**</sup>
33
+
34
+ #### 💄 Styles
35
+
36
+ - **misc**: Revert background style.
37
+
38
+ <br/>
39
+
40
+ <details>
41
+ <summary><kbd>Improvements and Fixes</kbd></summary>
42
+
43
+ #### Styles
44
+
45
+ - **misc**: Revert background style, closes [#10218](https://github.com/lobehub/lobe-chat/issues/10218) ([97b0413](https://github.com/lobehub/lobe-chat/commit/97b0413))
46
+
47
+ </details>
48
+
49
+ <div align="right">
50
+
51
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
52
+
53
+ </div>
54
+
5
55
  ## [Version 2.0.0-next.56](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.55...v2.0.0-next.56)
6
56
 
7
57
  <sup>Released on **2025-11-14**</sup>
package/changelog/v1.json CHANGED
@@ -1,4 +1,22 @@
1
1
  [
2
+ {
3
+ "children": {
4
+ "features": [
5
+ "Support DeepSeek Interleaved thinking."
6
+ ]
7
+ },
8
+ "date": "2025-11-14",
9
+ "version": "2.0.0-next.58"
10
+ },
11
+ {
12
+ "children": {
13
+ "improvements": [
14
+ "Revert background style."
15
+ ]
16
+ },
17
+ "date": "2025-11-14",
18
+ "version": "2.0.0-next.57"
19
+ },
2
20
  {
3
21
  "children": {
4
22
  "features": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/lobehub",
3
- "version": "2.0.0-next.56",
3
+ "version": "2.0.0-next.58",
4
4
  "description": "LobeHub - an open-source,comprehensive AI Agent 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",
@@ -171,7 +171,7 @@ describe('convertOpenAIMessages', () => {
171
171
  expect((result[0] as any).reasoning).toBeUndefined();
172
172
  });
173
173
 
174
- it('should filter out reasoning_content field from messages', async () => {
174
+ it('should preserve reasoning_content field from messages (for DeepSeek compatibility)', async () => {
175
175
  const messages = [
176
176
  {
177
177
  role: 'assistant',
@@ -184,14 +184,14 @@ describe('convertOpenAIMessages', () => {
184
184
  const result = await convertOpenAIMessages(messages);
185
185
 
186
186
  expect(result).toEqual([
187
- { role: 'assistant', content: 'Hello' },
187
+ { role: 'assistant', content: 'Hello', reasoning_content: 'some reasoning content' },
188
188
  { role: 'user', content: 'Hi' },
189
189
  ]);
190
- // Ensure reasoning_content field is removed
191
- expect((result[0] as any).reasoning_content).toBeUndefined();
190
+ // Ensure reasoning_content field is preserved
191
+ expect((result[0] as any).reasoning_content).toBe('some reasoning content');
192
192
  });
193
193
 
194
- it('should filter out both reasoning and reasoning_content fields from messages', async () => {
194
+ it('should filter out reasoning but preserve reasoning_content field', async () => {
195
195
  const messages = [
196
196
  {
197
197
  role: 'assistant',
@@ -203,10 +203,12 @@ describe('convertOpenAIMessages', () => {
203
203
 
204
204
  const result = await convertOpenAIMessages(messages);
205
205
 
206
- expect(result).toEqual([{ role: 'assistant', content: 'Hello' }]);
207
- // Ensure both fields are removed
206
+ expect(result).toEqual([
207
+ { role: 'assistant', content: 'Hello', reasoning_content: 'some reasoning content' },
208
+ ]);
209
+ // Ensure reasoning object is removed but reasoning_content is preserved
208
210
  expect((result[0] as any).reasoning).toBeUndefined();
209
- expect((result[0] as any).reasoning_content).toBeUndefined();
211
+ expect((result[0] as any).reasoning_content).toBe('some reasoning content');
210
212
  });
211
213
  });
212
214
 
@@ -49,6 +49,9 @@ export const convertOpenAIMessages = async (messages: OpenAI.ChatCompletionMessa
49
49
  if (msg.tool_call_id !== undefined) result.tool_call_id = msg.tool_call_id;
50
50
  if (msg.function_call !== undefined) result.function_call = msg.function_call;
51
51
 
52
+ // it's compatible for DeepSeek
53
+ if (msg.reasoning_content !== undefined) result.reasoning_content = msg.reasoning_content;
54
+
52
55
  return result;
53
56
  }),
54
57
  )) as OpenAI.ChatCompletionMessageParam[];
@@ -20,6 +20,92 @@ testProvider({
20
20
  });
21
21
 
22
22
  describe('LobeDeepSeekAI - custom features', () => {
23
+ describe('chatCompletion.handlePayload', () => {
24
+ it('should transform reasoning object to reasoning_content string', () => {
25
+ const payload = {
26
+ messages: [
27
+ { role: 'user', content: 'Hello' },
28
+ {
29
+ role: 'assistant',
30
+ content: 'Hi there',
31
+ reasoning: { content: 'Let me think...', duration: 1000 },
32
+ },
33
+ { role: 'user', content: 'How are you?' },
34
+ ],
35
+ model: 'deepseek-r1',
36
+ };
37
+
38
+ const result = params.chatCompletion!.handlePayload!(payload as any);
39
+
40
+ expect(result.messages).toEqual([
41
+ { role: 'user', content: 'Hello' },
42
+ {
43
+ role: 'assistant',
44
+ content: 'Hi there',
45
+ reasoning_content: 'Let me think...',
46
+ },
47
+ { role: 'user', content: 'How are you?' },
48
+ ]);
49
+ });
50
+
51
+ it('should not modify messages without reasoning field', () => {
52
+ const payload = {
53
+ messages: [
54
+ { role: 'user', content: 'Hello' },
55
+ { role: 'assistant', content: 'Hi there' },
56
+ ],
57
+ model: 'deepseek-chat',
58
+ };
59
+
60
+ const result = params.chatCompletion!.handlePayload!(payload as any);
61
+
62
+ expect(result.messages).toEqual(payload.messages);
63
+ });
64
+
65
+ it('should handle empty reasoning content', () => {
66
+ const payload = {
67
+ messages: [
68
+ {
69
+ role: 'assistant',
70
+ content: 'Response',
71
+ reasoning: { duration: 1000 },
72
+ },
73
+ ],
74
+ model: 'deepseek-r1',
75
+ };
76
+
77
+ const result = params.chatCompletion!.handlePayload!(payload as any);
78
+
79
+ expect(result.messages[0]).toEqual({
80
+ role: 'assistant',
81
+ content: 'Response',
82
+ });
83
+ });
84
+
85
+ it('should set stream to true by default', () => {
86
+ const payload = {
87
+ messages: [{ role: 'user', content: 'Hello' }],
88
+ model: 'deepseek-chat',
89
+ };
90
+
91
+ const result = params.chatCompletion!.handlePayload!(payload as any);
92
+
93
+ expect(result.stream).toBe(true);
94
+ });
95
+
96
+ it('should preserve existing stream value', () => {
97
+ const payload = {
98
+ messages: [{ role: 'user', content: 'Hello' }],
99
+ model: 'deepseek-chat',
100
+ stream: false,
101
+ };
102
+
103
+ const result = params.chatCompletion!.handlePayload!(payload as any);
104
+
105
+ expect(result.stream).toBe(false);
106
+ });
107
+ });
108
+
23
109
  describe('Debug Configuration', () => {
24
110
  it('should disable debug by default', () => {
25
111
  delete process.env.DEBUG_DEEPSEEK_CHAT_COMPLETION;
@@ -12,6 +12,30 @@ export interface DeepSeekModelCard {
12
12
 
13
13
  export const params = {
14
14
  baseURL: 'https://api.deepseek.com/v1',
15
+ chatCompletion: {
16
+ handlePayload: (payload) => {
17
+ // Transform reasoning object to reasoning_content string for multi-turn conversations
18
+ const messages = payload.messages.map((message: any) => {
19
+ // Only transform if message has reasoning.content
20
+ if (message.reasoning?.content) {
21
+ const { reasoning, ...rest } = message;
22
+ return {
23
+ ...rest,
24
+ reasoning_content: reasoning.content,
25
+ };
26
+ }
27
+ // If message has reasoning but no content, remove reasoning field entirely
28
+ delete message.reasoning;
29
+ return message;
30
+ });
31
+
32
+ return {
33
+ ...payload,
34
+ messages,
35
+ stream: payload.stream ?? true,
36
+ } as any;
37
+ },
38
+ },
15
39
  debug: {
16
40
  chatCompletion: () => process.env.DEBUG_DEEPSEEK_CHAT_COMPLETION === '1',
17
41
  },
@@ -1,4 +1,3 @@
1
- import { useTheme } from 'antd-style';
2
1
  import { Suspense, memo } from 'react';
3
2
  import { Flexbox } from 'react-layout-kit';
4
3
 
@@ -19,38 +18,30 @@ interface WorkspaceLayoutProps {
19
18
  mobile?: boolean;
20
19
  }
21
20
 
22
- const DesktopWorkspace = memo(() => {
23
- const theme = useTheme();
24
-
25
- return (
26
- <>
27
- <ChatHeaderDesktop />
28
- <Flexbox
29
- height={'100%'}
30
- horizontal
31
- style={{ overflow: 'hidden', position: 'relative' }}
32
- width={'100%'}
33
- >
34
- <Flexbox
35
- height={'100%'}
36
- style={{ background: theme.colorBgContainer, overflow: 'hidden', position: 'relative' }}
37
- width={'100%'}
38
- >
39
- <ConversationArea mobile={false} />
40
- </Flexbox>
41
- <Portal>
42
- <Suspense fallback={<BrandTextLoading />}>
43
- <PortalPanel mobile={false} />
44
- </Suspense>
45
- </Portal>
46
- <TopicPanel>
47
- <TopicSidebar mobile={false} />
48
- </TopicPanel>
21
+ const DesktopWorkspace = memo(() => (
22
+ <>
23
+ <ChatHeaderDesktop />
24
+ <Flexbox
25
+ height={'100%'}
26
+ horizontal
27
+ style={{ overflow: 'hidden', position: 'relative' }}
28
+ width={'100%'}
29
+ >
30
+ <Flexbox height={'100%'} style={{ overflow: 'hidden', position: 'relative' }} width={'100%'}>
31
+ <ConversationArea mobile={false} />
49
32
  </Flexbox>
50
- <MainInterfaceTracker />
51
- </>
52
- );
53
- });
33
+ <Portal>
34
+ <Suspense fallback={<BrandTextLoading />}>
35
+ <PortalPanel mobile={false} />
36
+ </Suspense>
37
+ </Portal>
38
+ <TopicPanel>
39
+ <TopicSidebar mobile={false} />
40
+ </TopicPanel>
41
+ </Flexbox>
42
+ <MainInterfaceTracker />
43
+ </>
44
+ ));
54
45
 
55
46
  DesktopWorkspace.displayName = 'DesktopWorkspace';
56
47
 
@@ -1,6 +1,6 @@
1
1
  import { UIChatMessage } from '@lobechat/types';
2
2
  import { Tag } from '@lobehub/ui';
3
- import { createStyles, useResponsive } from 'antd-style';
3
+ import { useResponsive } from 'antd-style';
4
4
  import isEqual from 'fast-deep-equal';
5
5
  import { ReactNode, memo, useCallback, useMemo } from 'react';
6
6
  import { useTranslation } from 'react-i18next';
@@ -45,13 +45,6 @@ const remarkPlugins = markdownElements
45
45
  .map((element) => element.remarkPlugin)
46
46
  .filter(Boolean);
47
47
 
48
- const useUserStyles = createStyles(({ css, token }) => ({
49
- messageContainer: css`
50
- border: none;
51
- background: ${token.colorFillTertiary};
52
- `,
53
- }));
54
-
55
48
  const UserMessage = memo<UserMessageProps>(({ id, disableEditing, index }) => {
56
49
  const item = useChatStore(
57
50
  displayMessageSelectors.getDisplayMessageById(id),
@@ -63,8 +56,6 @@ const UserMessage = memo<UserMessageProps>(({ id, disableEditing, index }) => {
63
56
  const { t } = useTranslation('chat');
64
57
  const { mobile } = useResponsive();
65
58
  const avatar = useUserAvatar();
66
- const { styles: userStyles } = useUserStyles();
67
-
68
59
  const title = useUserStore(userProfileSelectors.displayUserName);
69
60
 
70
61
  const displayMode = useAgentStore(agentChatConfigSelectors.displayMode);
@@ -174,7 +165,6 @@ const UserMessage = memo<UserMessageProps>(({ id, disableEditing, index }) => {
174
165
  >
175
166
  <Flexbox flex={1} style={{ maxWidth: '100%', minWidth: 0 }}>
176
167
  <MessageContent
177
- className={userStyles.messageContainer}
178
168
  editing={editing}
179
169
  id={id}
180
170
  markdownProps={markdownProps}