@lobehub/lobehub 2.0.0-next.65 → 2.0.0-next.66
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/.github/workflows/claude-translator.yml +1 -0
- package/CHANGELOG.md +25 -0
- package/changelog/v1.json +9 -0
- package/locales/ar/chat.json +3 -0
- package/locales/bg-BG/chat.json +3 -0
- package/locales/de-DE/chat.json +3 -0
- package/locales/en-US/chat.json +3 -0
- package/locales/es-ES/chat.json +3 -0
- package/locales/fa-IR/chat.json +3 -0
- package/locales/fr-FR/chat.json +3 -0
- package/locales/it-IT/chat.json +3 -0
- package/locales/ja-JP/chat.json +3 -0
- package/locales/ko-KR/chat.json +3 -0
- package/locales/nl-NL/chat.json +3 -0
- package/locales/pl-PL/chat.json +3 -0
- package/locales/pt-BR/chat.json +3 -0
- package/locales/ru-RU/chat.json +3 -0
- package/locales/tr-TR/chat.json +3 -0
- package/locales/vi-VN/chat.json +3 -0
- package/locales/zh-CN/chat.json +3 -0
- package/locales/zh-TW/chat.json +3 -0
- package/package.json +5 -5
- package/packages/conversation-flow/src/__tests__/fixtures/index.ts +4 -8
- package/packages/conversation-flow/src/__tests__/fixtures/inputs/{assistant-with-tools.json → assistantGroup/assistant-with-tools.json} +2 -1
- package/packages/conversation-flow/src/__tests__/fixtures/inputs/assistantGroup/index.ts +8 -0
- package/packages/conversation-flow/src/__tests__/fixtures/inputs/index.ts +2 -4
- package/packages/conversation-flow/src/__tests__/fixtures/outputs/{assistant-with-tools.json → assistantGroup/assistant-with-tools.json} +8 -8
- package/packages/conversation-flow/src/__tests__/fixtures/outputs/assistantGroup/index.ts +8 -0
- package/packages/conversation-flow/src/__tests__/parse.test.ts +6 -6
- package/packages/conversation-flow/src/parse.ts +45 -1
- package/packages/conversation-flow/src/transformation/FlatListBuilder.ts +64 -0
- package/packages/database/package.json +2 -2
- package/packages/obervability-otel/package.json +1 -1
- package/packages/types/src/message/common/metadata.ts +8 -1
- package/packages/types/src/message/ui/chat.ts +1 -0
- package/src/app/(backend)/market/agent/[[...segments]]/route.ts +1 -1
- package/src/app/(backend)/market/oidc/[[...segments]]/route.ts +1 -1
- package/src/app/market-auth-callback/layout.tsx +27 -3
- package/src/features/ChatInput/ActionBar/Token/TokenTag.tsx +2 -2
- package/src/features/Conversation/Messages/Assistant/Actions/index.tsx +15 -1
- package/src/features/Conversation/Messages/Assistant/CollapsedMessage.tsx +37 -0
- package/src/features/Conversation/Messages/Assistant/MessageContent.tsx +16 -9
- package/src/features/Conversation/Messages/Group/Actions/WithContentId.tsx +28 -6
- package/src/features/Conversation/Messages/Group/CollapsedMessage.tsx +37 -0
- package/src/features/Conversation/Messages/Group/{GroupChildren.tsx → Group.tsx} +18 -4
- package/src/features/Conversation/Messages/Group/index.tsx +4 -6
- package/src/features/Conversation/hooks/useChatListActionsBar.tsx +14 -0
- package/src/layout/AuthProvider/MarketAuth/MarketAuthProvider.tsx +1 -1
- package/src/libs/mcp/__tests__/index.test.ts +6 -6
- package/src/locales/default/chat.ts +3 -0
- package/src/store/chat/slices/message/actions/publicApi.ts +17 -0
- package/src/store/chat/slices/message/selectors/displayMessage.ts +1 -1
- package/src/store/chat/slices/message/selectors/messageState.ts +7 -0
- package/src/store/chat/slices/translate/action.test.ts +26 -32
- package/src/store/chat/slices/translate/action.ts +3 -3
- /package/packages/conversation-flow/src/__tests__/fixtures/inputs/{complex-scenario.json → assistantGroup/tools-with-branches.json} +0 -0
- /package/packages/conversation-flow/src/__tests__/fixtures/outputs/{complex-scenario.json → assistantGroup/tools-with-branches.json} +0 -0
- /package/src/features/Conversation/Messages/Group/{GroupContext.tsx → GroupContext.ts} +0 -0
|
@@ -6,6 +6,8 @@ import {
|
|
|
6
6
|
DownloadIcon,
|
|
7
7
|
Edit,
|
|
8
8
|
LanguagesIcon,
|
|
9
|
+
ListChevronsDownUp,
|
|
10
|
+
ListChevronsUpDown,
|
|
9
11
|
ListRestart,
|
|
10
12
|
Play,
|
|
11
13
|
RotateCcw,
|
|
@@ -27,12 +29,14 @@ const translateStyle = css`
|
|
|
27
29
|
|
|
28
30
|
interface ChatListActionsBar {
|
|
29
31
|
branching: ActionIconGroupItemType;
|
|
32
|
+
collapse: ActionIconGroupItemType;
|
|
30
33
|
continueGeneration: ActionIconGroupItemType;
|
|
31
34
|
copy: ActionIconGroupItemType;
|
|
32
35
|
del: ActionIconGroupItemType;
|
|
33
36
|
delAndRegenerate: ActionIconGroupItemType;
|
|
34
37
|
divider: { type: 'divider' };
|
|
35
38
|
edit: ActionIconGroupItemType;
|
|
39
|
+
expand: ActionIconGroupItemType;
|
|
36
40
|
export: ActionIconGroupItemType;
|
|
37
41
|
regenerate: ActionIconGroupItemType;
|
|
38
42
|
share: ActionIconGroupItemType;
|
|
@@ -58,6 +62,11 @@ export const useChatListActionsBar = ({
|
|
|
58
62
|
key: 'branching',
|
|
59
63
|
label: t('branching'),
|
|
60
64
|
},
|
|
65
|
+
collapse: {
|
|
66
|
+
icon: ListChevronsDownUp,
|
|
67
|
+
key: 'collapse',
|
|
68
|
+
label: t('messageAction.collapse', { ns: 'chat' }),
|
|
69
|
+
},
|
|
61
70
|
continueGeneration: {
|
|
62
71
|
disabled: isContinuing,
|
|
63
72
|
icon: ArrowDownFromLine,
|
|
@@ -93,6 +102,11 @@ export const useChatListActionsBar = ({
|
|
|
93
102
|
key: 'edit',
|
|
94
103
|
label: t('edit'),
|
|
95
104
|
},
|
|
105
|
+
expand: {
|
|
106
|
+
icon: ListChevronsUpDown,
|
|
107
|
+
key: 'expand',
|
|
108
|
+
label: t('messageAction.expand', { ns: 'chat' }),
|
|
109
|
+
},
|
|
96
110
|
export: {
|
|
97
111
|
icon: DownloadIcon,
|
|
98
112
|
key: 'export',
|
|
@@ -106,7 +106,7 @@ export const MarketAuthProvider = ({ children, isDesktop }: MarketAuthProviderPr
|
|
|
106
106
|
// 初始化 OIDC 客户端(仅在客户端)
|
|
107
107
|
useEffect(() => {
|
|
108
108
|
if (typeof window !== 'undefined') {
|
|
109
|
-
const baseUrl = process.env.NEXT_PUBLIC_MARKET_BASE_URL || '
|
|
109
|
+
const baseUrl = process.env.NEXT_PUBLIC_MARKET_BASE_URL || 'https://market.lobehub.com';
|
|
110
110
|
const desktopRedirectUri = new URL(MARKET_OIDC_ENDPOINTS.desktopCallback, baseUrl).toString();
|
|
111
111
|
|
|
112
112
|
// 桌面端使用 Market 手动维护的 Web 回调,Web 端使用当前域名
|
|
@@ -21,16 +21,16 @@ describe('MCPClient', () => {
|
|
|
21
21
|
await mcpClient.initialize();
|
|
22
22
|
// Add a small delay to allow the server process to fully start (optional, but can help)
|
|
23
23
|
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
24
|
-
});
|
|
24
|
+
}, 30000);
|
|
25
25
|
|
|
26
26
|
afterEach(async () => {
|
|
27
27
|
// Assume SDK client/transport handles process termination gracefully
|
|
28
28
|
// If processes leak, more explicit cleanup might be needed here
|
|
29
|
-
});
|
|
29
|
+
}, 30000);
|
|
30
30
|
|
|
31
31
|
it('should create and initialize an instance with stdio transport', () => {
|
|
32
32
|
expect(mcpClient).toBeInstanceOf(MCPClient);
|
|
33
|
-
});
|
|
33
|
+
}, 30000);
|
|
34
34
|
|
|
35
35
|
it('should list tools via stdio', async () => {
|
|
36
36
|
const result = await mcpClient.listTools();
|
|
@@ -40,7 +40,7 @@ describe('MCPClient', () => {
|
|
|
40
40
|
|
|
41
41
|
// Expect the tools defined in mock-sdk-server.ts
|
|
42
42
|
expect(result).toMatchSnapshot();
|
|
43
|
-
});
|
|
43
|
+
}, 30000);
|
|
44
44
|
|
|
45
45
|
it('should call the "echo" tool via stdio', async () => {
|
|
46
46
|
const toolName = 'echo';
|
|
@@ -52,7 +52,7 @@ describe('MCPClient', () => {
|
|
|
52
52
|
|
|
53
53
|
const result = await mcpClient.callTool(toolName, toolArgs);
|
|
54
54
|
expect(result).toEqual(expectedResult);
|
|
55
|
-
});
|
|
55
|
+
}, 30000);
|
|
56
56
|
|
|
57
57
|
it('should call the "add" tool via stdio', async () => {
|
|
58
58
|
const toolName = 'add';
|
|
@@ -62,7 +62,7 @@ describe('MCPClient', () => {
|
|
|
62
62
|
expect(result).toEqual({
|
|
63
63
|
content: [{ type: 'text', text: 'The sum is: 12' }],
|
|
64
64
|
});
|
|
65
|
-
});
|
|
65
|
+
}, 30000);
|
|
66
66
|
});
|
|
67
67
|
|
|
68
68
|
// Error Handling tests remain the same...
|
|
@@ -18,6 +18,7 @@ export default {
|
|
|
18
18
|
availableAgents: '可用助手',
|
|
19
19
|
backToBottom: '跳转至当前',
|
|
20
20
|
chatList: {
|
|
21
|
+
expandMessage: '展开消息',
|
|
21
22
|
longMessageDetail: '查看详情',
|
|
22
23
|
},
|
|
23
24
|
clearCurrentMessages: '清空当前会话消息',
|
|
@@ -188,9 +189,11 @@ export default {
|
|
|
188
189
|
},
|
|
189
190
|
|
|
190
191
|
messageAction: {
|
|
192
|
+
collapse: '收起消息',
|
|
191
193
|
continueGeneration: '继续生成',
|
|
192
194
|
delAndRegenerate: '删除并重新生成',
|
|
193
195
|
deleteDisabledByThreads: '存在子话题,不能删除',
|
|
196
|
+
expand: '展开消息',
|
|
194
197
|
regenerate: '重新生成',
|
|
195
198
|
},
|
|
196
199
|
|
|
@@ -43,6 +43,10 @@ export interface MessagePublicApiAction {
|
|
|
43
43
|
updateMessageInput: (message: string) => void;
|
|
44
44
|
modifyMessageContent: (id: string, content: string) => Promise<void>;
|
|
45
45
|
toggleMessageEditing: (id: string, editing: boolean) => void;
|
|
46
|
+
/**
|
|
47
|
+
* Toggle message collapsed state
|
|
48
|
+
*/
|
|
49
|
+
toggleMessageCollapsed: (id: string, collapsed?: boolean) => Promise<void>;
|
|
46
50
|
|
|
47
51
|
// ===== Others ===== //
|
|
48
52
|
copyMessage: (id: string, content: string) => Promise<void>;
|
|
@@ -241,4 +245,17 @@ export const messagePublicApi: StateCreator<
|
|
|
241
245
|
|
|
242
246
|
await get().optimisticUpdateMessageContent(id, content);
|
|
243
247
|
},
|
|
248
|
+
|
|
249
|
+
toggleMessageCollapsed: async (id, collapsed) => {
|
|
250
|
+
const message = displayMessageSelectors.getDisplayMessageById(id)(get());
|
|
251
|
+
if (!message) return;
|
|
252
|
+
|
|
253
|
+
// 如果没有传入 collapsed,则取反当前状态
|
|
254
|
+
const nextCollapsed = collapsed ?? !message.metadata?.collapsed;
|
|
255
|
+
|
|
256
|
+
// 直接调用现有的 optimisticUpdateMessageMetadata
|
|
257
|
+
await get().optimisticUpdateMessageMetadata(id, {
|
|
258
|
+
collapsed: nextCollapsed,
|
|
259
|
+
});
|
|
260
|
+
},
|
|
244
261
|
});
|
|
@@ -85,7 +85,7 @@ const activeDisplayMessages = (s: ChatStoreState): UIChatMessage[] => {
|
|
|
85
85
|
/**
|
|
86
86
|
* Get display message by ID (searches in messagesMap including assistantGroup children)
|
|
87
87
|
*/
|
|
88
|
-
const getDisplayMessageById = (id: string) => (s: ChatStoreState) =>
|
|
88
|
+
export const getDisplayMessageById = (id: string) => (s: ChatStoreState) =>
|
|
89
89
|
chatHelpers.getMessageById(activeDisplayMessages(s), id);
|
|
90
90
|
|
|
91
91
|
const lastDisplayMessageId = (s: ChatStoreState) => {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { ChatStoreState } from '../../../initialState';
|
|
2
2
|
import { mainDisplayChatIDs } from './chat';
|
|
3
3
|
import { getDbMessageByToolCallId } from './dbMessage';
|
|
4
|
+
import { getDisplayMessageById } from './displayMessage';
|
|
4
5
|
|
|
5
6
|
const isMessageEditing = (id: string) => (s: ChatStoreState) => s.messageEditingIds.includes(id);
|
|
6
7
|
const isMessageLoading = (id: string) => (s: ChatStoreState) => s.messageLoadingIds.includes(id);
|
|
@@ -13,6 +14,11 @@ const isMessageInRAGFlow = (id: string) => (s: ChatStoreState) =>
|
|
|
13
14
|
const isMessageInChatReasoning = (id: string) => (s: ChatStoreState) =>
|
|
14
15
|
s.reasoningLoadingIds.includes(id);
|
|
15
16
|
|
|
17
|
+
const isMessageCollapsed = (id: string) => (s: ChatStoreState) => {
|
|
18
|
+
const message = getDisplayMessageById(id)(s);
|
|
19
|
+
return message?.metadata?.collapsed ?? false;
|
|
20
|
+
};
|
|
21
|
+
|
|
16
22
|
const isPluginApiInvoking = (id: string) => (s: ChatStoreState) =>
|
|
17
23
|
s.pluginApiLoadingIds.includes(id);
|
|
18
24
|
|
|
@@ -71,6 +77,7 @@ export const messageStateSelectors = {
|
|
|
71
77
|
isHasMessageLoading,
|
|
72
78
|
isInRAGFlow,
|
|
73
79
|
isInToolsCalling,
|
|
80
|
+
isMessageCollapsed,
|
|
74
81
|
isMessageContinuing,
|
|
75
82
|
isMessageEditing,
|
|
76
83
|
isMessageGenerating,
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { chainLangDetect } from '@lobechat/prompts';
|
|
2
|
-
import { chainTranslate } from '@lobechat/prompts';
|
|
3
1
|
import { act, renderHook } from '@testing-library/react';
|
|
4
2
|
import { Mock, afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
5
3
|
|
|
@@ -9,7 +7,7 @@ import { messageMapKey } from '@/store/chat/utils/messageMapKey';
|
|
|
9
7
|
|
|
10
8
|
import { useChatStore } from '../../store';
|
|
11
9
|
|
|
12
|
-
// Mock messageService
|
|
10
|
+
// Mock messageService and chatService
|
|
13
11
|
vi.mock('@/services/message', () => ({
|
|
14
12
|
messageService: {
|
|
15
13
|
updateMessageTTS: vi.fn(),
|
|
@@ -24,27 +22,20 @@ vi.mock('@/services/chat', () => ({
|
|
|
24
22
|
},
|
|
25
23
|
}));
|
|
26
24
|
|
|
27
|
-
vi.mock('@/
|
|
28
|
-
|
|
29
|
-
}))
|
|
30
|
-
|
|
31
|
-
vi.mock('@/chains/translate', () => ({
|
|
32
|
-
chainTranslate: vi.fn(),
|
|
25
|
+
vi.mock('@/store/user', () => ({
|
|
26
|
+
useUserStore: {
|
|
27
|
+
getState: vi.fn(() => ({})),
|
|
28
|
+
},
|
|
33
29
|
}));
|
|
34
30
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
31
|
+
vi.mock('@/store/user/selectors', () => ({
|
|
32
|
+
systemAgentSelectors: {
|
|
33
|
+
translation: vi.fn(() => ({})),
|
|
34
|
+
},
|
|
38
35
|
}));
|
|
39
36
|
|
|
40
37
|
beforeEach(() => {
|
|
41
38
|
vi.clearAllMocks();
|
|
42
|
-
useChatStore.setState(
|
|
43
|
-
{
|
|
44
|
-
// ... 初始状态
|
|
45
|
-
},
|
|
46
|
-
false,
|
|
47
|
-
);
|
|
48
39
|
});
|
|
49
40
|
|
|
50
41
|
afterEach(() => {
|
|
@@ -53,26 +44,26 @@ afterEach(() => {
|
|
|
53
44
|
|
|
54
45
|
describe('ChatEnhanceAction', () => {
|
|
55
46
|
describe('translateMessage', () => {
|
|
56
|
-
it('should translate a message to the target language
|
|
57
|
-
const { result } = renderHook(() => useChatStore());
|
|
47
|
+
it('should translate a message to the target language', async () => {
|
|
58
48
|
const messageId = 'message-id';
|
|
59
49
|
const targetLang = 'zh-CN';
|
|
60
50
|
const messageContent = 'Hello World';
|
|
61
51
|
const detectedLang = 'en-US';
|
|
52
|
+
const translatedText = '你好世界';
|
|
62
53
|
|
|
54
|
+
// Setup initial state
|
|
63
55
|
act(() => {
|
|
64
56
|
useChatStore.setState({
|
|
65
57
|
activeId: 'session',
|
|
66
|
-
|
|
58
|
+
dbMessagesMap: {
|
|
67
59
|
[messageMapKey('session')]: [
|
|
68
60
|
{
|
|
69
61
|
id: messageId,
|
|
70
62
|
content: messageContent,
|
|
71
63
|
createdAt: Date.now(),
|
|
72
64
|
updatedAt: Date.now(),
|
|
73
|
-
role: '
|
|
74
|
-
sessionId: '
|
|
75
|
-
topicId: 'test',
|
|
65
|
+
role: 'assistant',
|
|
66
|
+
sessionId: 'session',
|
|
76
67
|
meta: {},
|
|
77
68
|
},
|
|
78
69
|
],
|
|
@@ -80,21 +71,24 @@ describe('ChatEnhanceAction', () => {
|
|
|
80
71
|
});
|
|
81
72
|
});
|
|
82
73
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
}
|
|
87
|
-
if (params === chainTranslate(messageContent, targetLang)) {
|
|
88
|
-
return Promise.resolve('Hola Mundo');
|
|
89
|
-
}
|
|
90
|
-
return Promise.resolve(undefined);
|
|
74
|
+
// First call for language detection
|
|
75
|
+
(chatService.fetchPresetTaskResult as Mock).mockImplementationOnce(async ({ onFinish }) => {
|
|
76
|
+
if (onFinish) await onFinish(detectedLang);
|
|
91
77
|
});
|
|
92
78
|
|
|
79
|
+
// Second call for translation
|
|
80
|
+
(chatService.fetchPresetTaskResult as Mock).mockImplementationOnce(async ({ onFinish }) => {
|
|
81
|
+
if (onFinish) await onFinish(translatedText);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
const { result } = renderHook(() => useChatStore());
|
|
85
|
+
|
|
93
86
|
await act(async () => {
|
|
94
87
|
await result.current.translateMessage(messageId, targetLang);
|
|
95
88
|
});
|
|
96
89
|
|
|
97
90
|
expect(messageService.updateMessageTranslate).toHaveBeenCalled();
|
|
91
|
+
expect(chatService.fetchPresetTaskResult).toHaveBeenCalledTimes(2);
|
|
98
92
|
});
|
|
99
93
|
});
|
|
100
94
|
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import { chainLangDetect, chainTranslate } from '@lobechat/prompts';
|
|
2
2
|
import { ChatTranslate, TraceNameMap, TracePayload } from '@lobechat/types';
|
|
3
|
+
import { merge } from '@lobechat/utils';
|
|
3
4
|
import { produce } from 'immer';
|
|
4
5
|
import { StateCreator } from 'zustand/vanilla';
|
|
5
6
|
|
|
6
7
|
import { supportLocales } from '@/locales/resources';
|
|
7
8
|
import { chatService } from '@/services/chat';
|
|
8
9
|
import { messageService } from '@/services/message';
|
|
9
|
-
import {
|
|
10
|
+
import { dbMessageSelectors } from '@/store/chat/selectors';
|
|
10
11
|
import { ChatStore } from '@/store/chat/store';
|
|
11
12
|
import { useUserStore } from '@/store/user';
|
|
12
13
|
import { systemAgentSelectors } from '@/store/user/selectors';
|
|
13
|
-
import { merge } from '@/utils/merge';
|
|
14
14
|
import { setNamespace } from '@/utils/storeDebug';
|
|
15
15
|
|
|
16
16
|
const n = setNamespace('enhance');
|
|
@@ -43,7 +43,7 @@ export const chatTranslate: StateCreator<
|
|
|
43
43
|
translateMessage: async (id, targetLang) => {
|
|
44
44
|
const { internal_toggleChatLoading, updateMessageTranslate, internal_dispatchMessage } = get();
|
|
45
45
|
|
|
46
|
-
const message =
|
|
46
|
+
const message = dbMessageSelectors.getDbMessageById(id)(get());
|
|
47
47
|
if (!message) return;
|
|
48
48
|
|
|
49
49
|
// Get current agent for translation
|
|
File without changes
|
|
File without changes
|
|
File without changes
|