@lobehub/lobehub 2.0.0-next.64 → 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 +50 -0
- package/changelog/v1.json +18 -0
- package/locales/ar/chat.json +3 -0
- package/locales/ar/plugin.json +5 -0
- package/locales/bg-BG/chat.json +3 -0
- package/locales/bg-BG/plugin.json +5 -0
- package/locales/de-DE/chat.json +3 -0
- package/locales/de-DE/plugin.json +5 -0
- package/locales/en-US/chat.json +3 -0
- package/locales/es-ES/chat.json +3 -0
- package/locales/es-ES/plugin.json +5 -0
- package/locales/fa-IR/chat.json +3 -0
- package/locales/fa-IR/plugin.json +5 -0
- package/locales/fr-FR/chat.json +3 -0
- package/locales/fr-FR/plugin.json +5 -0
- package/locales/it-IT/chat.json +3 -0
- package/locales/it-IT/plugin.json +5 -0
- package/locales/ja-JP/chat.json +3 -0
- package/locales/ja-JP/plugin.json +5 -0
- package/locales/ko-KR/chat.json +3 -0
- package/locales/ko-KR/plugin.json +5 -0
- package/locales/nl-NL/chat.json +3 -0
- package/locales/nl-NL/plugin.json +5 -0
- package/locales/pl-PL/chat.json +3 -0
- package/locales/pl-PL/plugin.json +5 -0
- package/locales/pt-BR/chat.json +3 -0
- package/locales/pt-BR/plugin.json +5 -0
- package/locales/ru-RU/chat.json +3 -0
- package/locales/ru-RU/plugin.json +5 -0
- package/locales/tr-TR/chat.json +3 -0
- package/locales/tr-TR/plugin.json +5 -0
- package/locales/vi-VN/chat.json +3 -0
- package/locales/vi-VN/plugin.json +5 -0
- package/locales/zh-CN/chat.json +3 -0
- package/locales/zh-CN/plugin.json +2 -2
- package/locales/zh-TW/chat.json +3 -0
- package/locales/zh-TW/plugin.json +5 -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
|
@@ -253,6 +253,7 @@
|
|
|
253
253
|
},
|
|
254
254
|
"localSystem": {
|
|
255
255
|
"apiName": {
|
|
256
|
+
"editLocalFile": "编辑文件",
|
|
256
257
|
"getCommandOutput": "获取代码输出",
|
|
257
258
|
"globLocalFiles": "匹配搜索文件",
|
|
258
259
|
"grepContent": "搜索内容",
|
|
@@ -263,8 +264,7 @@
|
|
|
263
264
|
"renameLocalFile": "重命名",
|
|
264
265
|
"runCommand": "执行代码",
|
|
265
266
|
"searchLocalFiles": "搜索文件",
|
|
266
|
-
"writeLocalFile": "写入文件"
|
|
267
|
-
"editLocalFile": "编辑文件"
|
|
267
|
+
"writeLocalFile": "写入文件"
|
|
268
268
|
},
|
|
269
269
|
"title": "本地系统"
|
|
270
270
|
},
|
package/locales/zh-TW/chat.json
CHANGED
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
"availableAgents": "可用助理",
|
|
18
18
|
"backToBottom": "返回底部",
|
|
19
19
|
"chatList": {
|
|
20
|
+
"expandMessage": "展開訊息",
|
|
20
21
|
"longMessageDetail": "查看詳情"
|
|
21
22
|
},
|
|
22
23
|
"clearCurrentMessages": "清空當前對話",
|
|
@@ -173,9 +174,11 @@
|
|
|
173
174
|
"title": "提及成員"
|
|
174
175
|
},
|
|
175
176
|
"messageAction": {
|
|
177
|
+
"collapse": "收起訊息",
|
|
176
178
|
"continueGeneration": "繼續生成",
|
|
177
179
|
"delAndRegenerate": "刪除並重新生成",
|
|
178
180
|
"deleteDisabledByThreads": "存在子話題,無法刪除",
|
|
181
|
+
"expand": "展開訊息",
|
|
179
182
|
"regenerate": "重新生成"
|
|
180
183
|
},
|
|
181
184
|
"messages": {
|
|
@@ -253,6 +253,11 @@
|
|
|
253
253
|
},
|
|
254
254
|
"localSystem": {
|
|
255
255
|
"apiName": {
|
|
256
|
+
"editLocalFile": "編輯檔案",
|
|
257
|
+
"getCommandOutput": "取得程式輸出",
|
|
258
|
+
"globLocalFiles": "匹配搜尋檔案",
|
|
259
|
+
"grepContent": "搜尋內容",
|
|
260
|
+
"killCommand": "終止程式執行",
|
|
256
261
|
"listLocalFiles": "查看檔案列表",
|
|
257
262
|
"moveLocalFiles": "移動檔案",
|
|
258
263
|
"readLocalFile": "讀取檔案內容",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lobehub/lobehub",
|
|
3
|
-
"version": "2.0.0-next.
|
|
3
|
+
"version": "2.0.0-next.66",
|
|
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",
|
|
@@ -130,8 +130,8 @@
|
|
|
130
130
|
"@ant-design/pro-components": "^2.8.10",
|
|
131
131
|
"@anthropic-ai/sdk": "^0.67.1",
|
|
132
132
|
"@auth/core": "^0.40.0",
|
|
133
|
-
"@aws-sdk/client-s3": "~3.
|
|
134
|
-
"@aws-sdk/s3-request-presigner": "~3.
|
|
133
|
+
"@aws-sdk/client-s3": "~3.932.0",
|
|
134
|
+
"@aws-sdk/s3-request-presigner": "~3.932.0",
|
|
135
135
|
"@azure-rest/ai-inference": "1.0.0-beta.5",
|
|
136
136
|
"@azure/core-auth": "^1.10.1",
|
|
137
137
|
"@cfworker/json-schema": "^4.1.1",
|
|
@@ -225,7 +225,7 @@
|
|
|
225
225
|
"langfuse": "^3.38.6",
|
|
226
226
|
"langfuse-core": "^3.38.6",
|
|
227
227
|
"lodash-es": "^4.17.21",
|
|
228
|
-
"lucide-react": "^0.
|
|
228
|
+
"lucide-react": "^0.553.0",
|
|
229
229
|
"mammoth": "^1.11.0",
|
|
230
230
|
"markdown-to-txt": "^2.0.1",
|
|
231
231
|
"marked": "^16.4.2",
|
|
@@ -242,7 +242,7 @@
|
|
|
242
242
|
"nuqs": "^2.7.3",
|
|
243
243
|
"officeparser": "5.1.1",
|
|
244
244
|
"oidc-provider": "^9.5.2",
|
|
245
|
-
"ollama": "^0.6.
|
|
245
|
+
"ollama": "^0.6.3",
|
|
246
246
|
"openai": "^4.104.0",
|
|
247
247
|
"openapi-fetch": "^0.14.1",
|
|
248
248
|
"partial-json": "^0.1.7",
|
|
@@ -1,17 +1,15 @@
|
|
|
1
1
|
import type { Message, ParseResult } from '../../types';
|
|
2
2
|
// Input fixtures
|
|
3
3
|
import assistantChainWithFollowupInput from './inputs/assistant-chain-with-followup.json';
|
|
4
|
-
import
|
|
4
|
+
import { assistantGroup as assistantGroupInputs } from './inputs/assistantGroup';
|
|
5
5
|
import { branch as branchInputs } from './inputs/branch';
|
|
6
6
|
import { compare as compareInputs } from './inputs/compare';
|
|
7
|
-
import complexScenarioInput from './inputs/complex-scenario.json';
|
|
8
7
|
import linearConversationInput from './inputs/linear-conversation.json';
|
|
9
8
|
// Output fixtures
|
|
10
9
|
import assistantChainWithFollowupOutput from './outputs/assistant-chain-with-followup.json';
|
|
11
|
-
import
|
|
10
|
+
import { assistantGroup as assistantGroupOutputs } from './outputs/assistantGroup';
|
|
12
11
|
import { branch as branchOutputs } from './outputs/branch';
|
|
13
12
|
import { compare as compareOutputs } from './outputs/compare';
|
|
14
|
-
import complexScenarioOutput from './outputs/complex-scenario.json';
|
|
15
13
|
import linearConversationOutput from './outputs/linear-conversation.json';
|
|
16
14
|
|
|
17
15
|
/**
|
|
@@ -28,10 +26,9 @@ export interface SerializedParseResult {
|
|
|
28
26
|
*/
|
|
29
27
|
export const inputs = {
|
|
30
28
|
assistantChainWithFollowup: assistantChainWithFollowupInput as Message[],
|
|
31
|
-
|
|
29
|
+
assistantGroup: assistantGroupInputs,
|
|
32
30
|
branch: branchInputs,
|
|
33
31
|
compare: compareInputs,
|
|
34
|
-
complexScenario: complexScenarioInput as Message[],
|
|
35
32
|
linearConversation: linearConversationInput as Message[],
|
|
36
33
|
};
|
|
37
34
|
|
|
@@ -40,9 +37,8 @@ export const inputs = {
|
|
|
40
37
|
*/
|
|
41
38
|
export const outputs = {
|
|
42
39
|
assistantChainWithFollowup: assistantChainWithFollowupOutput as unknown as SerializedParseResult,
|
|
43
|
-
|
|
40
|
+
assistantGroup: assistantGroupOutputs,
|
|
44
41
|
branch: branchOutputs,
|
|
45
42
|
compare: compareOutputs,
|
|
46
|
-
complexScenario: complexScenarioOutput as unknown as SerializedParseResult,
|
|
47
43
|
linearConversation: linearConversationOutput as unknown as SerializedParseResult,
|
|
48
44
|
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Message } from '../../../../types';
|
|
2
|
+
import assistantWithTools from './assistant-with-tools.json';
|
|
3
|
+
import toolsWithBranches from './tools-with-branches.json';
|
|
4
|
+
|
|
5
|
+
export const assistantGroup = {
|
|
6
|
+
assistantWithTools: assistantWithTools as Message[],
|
|
7
|
+
toolsWithBranches: toolsWithBranches as Message[],
|
|
8
|
+
};
|
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
import type { Message } from '../../../types';
|
|
2
|
-
import
|
|
2
|
+
import { assistantGroup } from './assistantGroup';
|
|
3
3
|
import { branch } from './branch';
|
|
4
4
|
import { compare } from './compare';
|
|
5
|
-
import complexScenario from './complex-scenario.json';
|
|
6
5
|
import linearConversation from './linear-conversation.json';
|
|
7
6
|
|
|
8
7
|
export const inputs = {
|
|
9
|
-
|
|
8
|
+
assistantGroup,
|
|
10
9
|
branch,
|
|
11
10
|
compare,
|
|
12
|
-
complexScenario: complexScenario as Message[],
|
|
13
11
|
linearConversation: linearConversation as Message[],
|
|
14
12
|
};
|
|
@@ -9,18 +9,12 @@
|
|
|
9
9
|
{
|
|
10
10
|
"id": "msg-102",
|
|
11
11
|
"type": "message",
|
|
12
|
-
"tools": [
|
|
13
|
-
"msg-103",
|
|
14
|
-
"msg-104"
|
|
15
|
-
]
|
|
12
|
+
"tools": ["msg-103", "msg-104"]
|
|
16
13
|
},
|
|
17
14
|
{
|
|
18
15
|
"id": "msg-105",
|
|
19
16
|
"type": "message",
|
|
20
|
-
"tools": [
|
|
21
|
-
"msg-106",
|
|
22
|
-
"msg-107"
|
|
23
|
-
]
|
|
17
|
+
"tools": ["msg-106", "msg-107"]
|
|
24
18
|
},
|
|
25
19
|
{
|
|
26
20
|
"id": "msg-108",
|
|
@@ -98,6 +92,9 @@
|
|
|
98
92
|
"totalInputTokens": 28,
|
|
99
93
|
"totalOutputTokens": 45,
|
|
100
94
|
"totalTokens": 73
|
|
95
|
+
},
|
|
96
|
+
"metadata": {
|
|
97
|
+
"collapsed": true
|
|
101
98
|
}
|
|
102
99
|
},
|
|
103
100
|
{
|
|
@@ -178,6 +175,9 @@
|
|
|
178
175
|
"totalOutputTokens": 199,
|
|
179
176
|
"totalTokens": 628,
|
|
180
177
|
"cost": 0.0018839999999999998
|
|
178
|
+
},
|
|
179
|
+
"metadata": {
|
|
180
|
+
"collapsed": true
|
|
181
181
|
}
|
|
182
182
|
}
|
|
183
183
|
],
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { SerializedParseResult } from '../..';
|
|
2
|
+
import assistantWithTools from './assistant-with-tools.json';
|
|
3
|
+
import toolsWithBranches from './tools-with-branches.json';
|
|
4
|
+
|
|
5
|
+
export const assistantGroup = {
|
|
6
|
+
assistantWithTools: assistantWithTools as unknown as SerializedParseResult,
|
|
7
|
+
toolsWithBranches: toolsWithBranches as unknown as SerializedParseResult,
|
|
8
|
+
};
|
|
@@ -22,9 +22,9 @@ describe('parse', () => {
|
|
|
22
22
|
|
|
23
23
|
describe('Tool Usage', () => {
|
|
24
24
|
it('should parse assistant with tools correctly', () => {
|
|
25
|
-
const result = parse(inputs.assistantWithTools);
|
|
25
|
+
const result = parse(inputs.assistantGroup.assistantWithTools);
|
|
26
26
|
|
|
27
|
-
expect(serializeParseResult(result)).toEqual(outputs.assistantWithTools);
|
|
27
|
+
expect(serializeParseResult(result)).toEqual(outputs.assistantGroup.assistantWithTools);
|
|
28
28
|
});
|
|
29
29
|
|
|
30
30
|
it('should include follow-up messages after assistant chain', () => {
|
|
@@ -99,11 +99,11 @@ describe('parse', () => {
|
|
|
99
99
|
});
|
|
100
100
|
});
|
|
101
101
|
|
|
102
|
-
describe('
|
|
103
|
-
it('should handle
|
|
104
|
-
const result = parse(inputs.
|
|
102
|
+
describe('Assistant Group Scenarios', () => {
|
|
103
|
+
it('should handle tools with assistant branches correctly', () => {
|
|
104
|
+
const result = parse(inputs.assistantGroup.toolsWithBranches);
|
|
105
105
|
|
|
106
|
-
expect(serializeParseResult(result)).toEqual(outputs.
|
|
106
|
+
expect(serializeParseResult(result)).toEqual(outputs.assistantGroup.toolsWithBranches);
|
|
107
107
|
});
|
|
108
108
|
});
|
|
109
109
|
|
|
@@ -40,9 +40,53 @@ export function parse(messages: Message[], messageGroups?: MessageGroupMetadata[
|
|
|
40
40
|
const flatList = transformer.flatten(messages);
|
|
41
41
|
|
|
42
42
|
// Convert messageMap from Map to plain object for serialization
|
|
43
|
+
// Clean up metadata for assistant messages with tools
|
|
43
44
|
const messageMapObj: Record<string, Message> = {};
|
|
45
|
+
const usagePerformanceFields = new Set([
|
|
46
|
+
'acceptedPredictionTokens',
|
|
47
|
+
'cost',
|
|
48
|
+
'duration',
|
|
49
|
+
'inputAudioTokens',
|
|
50
|
+
'inputCacheMissTokens',
|
|
51
|
+
'inputCachedTokens',
|
|
52
|
+
'inputCitationTokens',
|
|
53
|
+
'inputImageTokens',
|
|
54
|
+
'inputTextTokens',
|
|
55
|
+
'inputWriteCacheTokens',
|
|
56
|
+
'latency',
|
|
57
|
+
'outputAudioTokens',
|
|
58
|
+
'outputImageTokens',
|
|
59
|
+
'outputReasoningTokens',
|
|
60
|
+
'outputTextTokens',
|
|
61
|
+
'rejectedPredictionTokens',
|
|
62
|
+
'totalInputTokens',
|
|
63
|
+
'totalOutputTokens',
|
|
64
|
+
'totalTokens',
|
|
65
|
+
'tps',
|
|
66
|
+
'ttft',
|
|
67
|
+
]);
|
|
68
|
+
|
|
44
69
|
helperMaps.messageMap.forEach((message, id) => {
|
|
45
|
-
|
|
70
|
+
// For assistant messages with tools, clean metadata to keep only usage/performance fields
|
|
71
|
+
if (
|
|
72
|
+
message.role === 'assistant' &&
|
|
73
|
+
message.tools &&
|
|
74
|
+
message.tools.length > 0 &&
|
|
75
|
+
message.metadata
|
|
76
|
+
) {
|
|
77
|
+
const cleanedMetadata: Record<string, any> = {};
|
|
78
|
+
Object.entries(message.metadata).forEach(([key, value]) => {
|
|
79
|
+
if (usagePerformanceFields.has(key)) {
|
|
80
|
+
cleanedMetadata[key] = value;
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
messageMapObj[id] = {
|
|
84
|
+
...message,
|
|
85
|
+
metadata: Object.keys(cleanedMetadata).length > 0 ? cleanedMetadata : undefined,
|
|
86
|
+
};
|
|
87
|
+
} else {
|
|
88
|
+
messageMapObj[id] = message;
|
|
89
|
+
}
|
|
46
90
|
});
|
|
47
91
|
|
|
48
92
|
return {
|
|
@@ -445,6 +445,40 @@ export class FlatListBuilder {
|
|
|
445
445
|
const msgUsage = assistant.usage || metaUsage;
|
|
446
446
|
const msgPerformance = assistant.performance || metaPerformance;
|
|
447
447
|
|
|
448
|
+
// Extract non-usage/performance metadata fields
|
|
449
|
+
const otherMetadata: Record<string, any> = {};
|
|
450
|
+
if (assistant.metadata) {
|
|
451
|
+
const usagePerformanceFields = new Set([
|
|
452
|
+
'acceptedPredictionTokens',
|
|
453
|
+
'cost',
|
|
454
|
+
'duration',
|
|
455
|
+
'inputAudioTokens',
|
|
456
|
+
'inputCacheMissTokens',
|
|
457
|
+
'inputCachedTokens',
|
|
458
|
+
'inputCitationTokens',
|
|
459
|
+
'inputImageTokens',
|
|
460
|
+
'inputTextTokens',
|
|
461
|
+
'inputWriteCacheTokens',
|
|
462
|
+
'latency',
|
|
463
|
+
'outputAudioTokens',
|
|
464
|
+
'outputImageTokens',
|
|
465
|
+
'outputReasoningTokens',
|
|
466
|
+
'outputTextTokens',
|
|
467
|
+
'rejectedPredictionTokens',
|
|
468
|
+
'totalInputTokens',
|
|
469
|
+
'totalOutputTokens',
|
|
470
|
+
'totalTokens',
|
|
471
|
+
'tps',
|
|
472
|
+
'ttft',
|
|
473
|
+
]);
|
|
474
|
+
|
|
475
|
+
Object.entries(assistant.metadata).forEach(([key, value]) => {
|
|
476
|
+
if (!usagePerformanceFields.has(key)) {
|
|
477
|
+
otherMetadata[key] = value;
|
|
478
|
+
}
|
|
479
|
+
});
|
|
480
|
+
}
|
|
481
|
+
|
|
448
482
|
const childBlock: AssistantContentBlock = {
|
|
449
483
|
content: assistant.content || '',
|
|
450
484
|
id: assistant.id,
|
|
@@ -457,12 +491,37 @@ export class FlatListBuilder {
|
|
|
457
491
|
if (assistant.reasoning) childBlock.reasoning = assistant.reasoning;
|
|
458
492
|
if (toolsWithResults.length > 0) childBlock.tools = toolsWithResults;
|
|
459
493
|
if (msgUsage) childBlock.usage = msgUsage;
|
|
494
|
+
if (Object.keys(otherMetadata).length > 0) {
|
|
495
|
+
childBlock.metadata = otherMetadata;
|
|
496
|
+
}
|
|
460
497
|
|
|
461
498
|
children.push(childBlock);
|
|
462
499
|
}
|
|
463
500
|
|
|
464
501
|
const aggregated = this.messageTransformer.aggregateMetadata(children);
|
|
465
502
|
|
|
503
|
+
// Collect all non-usage/performance metadata from all children
|
|
504
|
+
const groupMetadata: Record<string, any> = {};
|
|
505
|
+
children.forEach((child) => {
|
|
506
|
+
if ((child as any).metadata) {
|
|
507
|
+
Object.assign(groupMetadata, (child as any).metadata);
|
|
508
|
+
}
|
|
509
|
+
});
|
|
510
|
+
|
|
511
|
+
// If there's group-level metadata, apply it to first child and remove from others
|
|
512
|
+
if (Object.keys(groupMetadata).length > 0 && children.length > 0) {
|
|
513
|
+
// Ensure first child has the group metadata
|
|
514
|
+
if (!(children[0] as any).metadata) {
|
|
515
|
+
(children[0] as any).metadata = {};
|
|
516
|
+
}
|
|
517
|
+
Object.assign((children[0] as any).metadata, groupMetadata);
|
|
518
|
+
|
|
519
|
+
// Remove metadata from subsequent children (keep only in first child)
|
|
520
|
+
for (let i = 1; i < children.length; i++) {
|
|
521
|
+
delete (children[i] as any).metadata;
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
|
|
466
525
|
const result: Message = {
|
|
467
526
|
...firstAssistant,
|
|
468
527
|
children,
|
|
@@ -480,6 +539,11 @@ export class FlatListBuilder {
|
|
|
480
539
|
if (aggregated.performance) result.performance = aggregated.performance;
|
|
481
540
|
if (aggregated.usage) result.usage = aggregated.usage;
|
|
482
541
|
|
|
542
|
+
// Add group-level metadata if it exists
|
|
543
|
+
if (Object.keys(groupMetadata).length > 0) {
|
|
544
|
+
result.metadata = groupMetadata;
|
|
545
|
+
}
|
|
546
|
+
|
|
483
547
|
return result;
|
|
484
548
|
}
|
|
485
549
|
|
|
@@ -75,7 +75,9 @@ export const ModelPerformanceSchema = z.object({
|
|
|
75
75
|
latency: z.number().optional(),
|
|
76
76
|
});
|
|
77
77
|
|
|
78
|
-
export const MessageMetadataSchema = ModelUsageSchema.merge(ModelPerformanceSchema)
|
|
78
|
+
export const MessageMetadataSchema = ModelUsageSchema.merge(ModelPerformanceSchema).extend({
|
|
79
|
+
collapsed: z.boolean().optional(),
|
|
80
|
+
});
|
|
79
81
|
|
|
80
82
|
export interface ModelUsage extends ModelTokensUsage {
|
|
81
83
|
/**
|
|
@@ -106,5 +108,10 @@ export interface ModelPerformance {
|
|
|
106
108
|
export interface MessageMetadata extends ModelUsage, ModelPerformance {
|
|
107
109
|
activeBranchIndex?: number;
|
|
108
110
|
activeColumn?: boolean;
|
|
111
|
+
/**
|
|
112
|
+
* 消息折叠状态
|
|
113
|
+
* true: 折叠, false/undefined: 展开
|
|
114
|
+
*/
|
|
115
|
+
collapsed?: boolean;
|
|
109
116
|
compare?: boolean;
|
|
110
117
|
}
|
|
@@ -40,6 +40,7 @@ export interface AssistantContentBlock {
|
|
|
40
40
|
error?: ChatMessageError | null;
|
|
41
41
|
id: string;
|
|
42
42
|
imageList?: ChatImageItem[];
|
|
43
|
+
metadata?: Record<string, any>;
|
|
43
44
|
performance?: ModelPerformance;
|
|
44
45
|
reasoning?: ModelReasoning;
|
|
45
46
|
tools?: ChatToolPayloadWithResult[];
|
|
@@ -7,7 +7,7 @@ type RouteContext = {
|
|
|
7
7
|
}>;
|
|
8
8
|
};
|
|
9
9
|
|
|
10
|
-
const MARKET_BASE_URL = process.env.NEXT_PUBLIC_MARKET_BASE_URL || '
|
|
10
|
+
const MARKET_BASE_URL = process.env.NEXT_PUBLIC_MARKET_BASE_URL || 'https://market.lobehub.com';
|
|
11
11
|
|
|
12
12
|
const extractAccessToken = (req: NextRequest) => {
|
|
13
13
|
const authorization = req.headers.get('authorization');
|
|
@@ -7,7 +7,7 @@ type RouteContext = {
|
|
|
7
7
|
}>;
|
|
8
8
|
};
|
|
9
9
|
|
|
10
|
-
const MARKET_BASE_URL = process.env.NEXT_PUBLIC_MARKET_BASE_URL || '
|
|
10
|
+
const MARKET_BASE_URL = process.env.NEXT_PUBLIC_MARKET_BASE_URL || 'https://market.lobehub.com';
|
|
11
11
|
const ALLOWED_ENDPOINTS = new Set(['handoff', 'token', 'userinfo']);
|
|
12
12
|
|
|
13
13
|
const ensureEndpoint = (segments?: string[]) => {
|
|
@@ -1,13 +1,37 @@
|
|
|
1
|
+
import { cookies, headers } from 'next/headers';
|
|
1
2
|
import { ReactNode } from 'react';
|
|
3
|
+
import { isRtlLang } from 'rtl-detect';
|
|
4
|
+
import { NuqsAdapter } from 'nuqs/adapters/next/app';
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
import { DEFAULT_LANG, LOBE_LOCALE_COOKIE } from '@/const/locale';
|
|
8
|
+
import GlobalLayout from '@/layout/GlobalProvider';
|
|
9
|
+
import { Locales } from '@/locales/resources';
|
|
10
|
+
import { parseBrowserLanguage } from '@/utils/locale';
|
|
2
11
|
|
|
3
12
|
interface RootLayoutProps {
|
|
4
13
|
children: ReactNode;
|
|
5
14
|
}
|
|
6
15
|
|
|
7
|
-
const RootLayout = ({ children }: RootLayoutProps) => {
|
|
16
|
+
const RootLayout = async ({ children }: RootLayoutProps) => {
|
|
17
|
+
// 获取 locale:优先级为 cookie > 浏览器语言 > 默认语言
|
|
18
|
+
const cookieStore = await cookies();
|
|
19
|
+
const headersList = await headers();
|
|
20
|
+
const cookieLocale = cookieStore.get(LOBE_LOCALE_COOKIE)?.value as Locales | undefined;
|
|
21
|
+
const browserLanguage = parseBrowserLanguage(headersList, DEFAULT_LANG);
|
|
22
|
+
const locale = (cookieLocale || browserLanguage || DEFAULT_LANG) as Locales;
|
|
23
|
+
|
|
24
|
+
const direction = isRtlLang(locale) ? 'rtl' : 'ltr';
|
|
25
|
+
|
|
8
26
|
return (
|
|
9
|
-
<html lang=
|
|
10
|
-
<body>
|
|
27
|
+
<html dir={direction} lang={locale} suppressHydrationWarning>
|
|
28
|
+
<body>
|
|
29
|
+
<NuqsAdapter>
|
|
30
|
+
<GlobalLayout appearance="auto" isMobile={false} locale={locale}>
|
|
31
|
+
{children}
|
|
32
|
+
</GlobalLayout>
|
|
33
|
+
</NuqsAdapter>
|
|
34
|
+
</body>
|
|
11
35
|
</html>
|
|
12
36
|
);
|
|
13
37
|
};
|
|
@@ -13,7 +13,7 @@ import { useTokenCount } from '@/hooks/useTokenCount';
|
|
|
13
13
|
import { useAgentStore } from '@/store/agent';
|
|
14
14
|
import { agentChatConfigSelectors, agentSelectors } from '@/store/agent/selectors';
|
|
15
15
|
import { useChatStore } from '@/store/chat';
|
|
16
|
-
import {
|
|
16
|
+
import { dbMessageSelectors, topicSelectors } from '@/store/chat/selectors';
|
|
17
17
|
import { useToolStore } from '@/store/tool';
|
|
18
18
|
import { toolSelectors } from '@/store/tool/selectors';
|
|
19
19
|
|
|
@@ -77,7 +77,7 @@ const Token = memo<TokenTagProps>(({ total: messageString }) => {
|
|
|
77
77
|
const inputTokenCount = useTokenCount(input);
|
|
78
78
|
|
|
79
79
|
const chatsString = useMemo(() => {
|
|
80
|
-
const chats =
|
|
80
|
+
const chats = dbMessageSelectors.activeDbMessages(useChatStore.getState());
|
|
81
81
|
return chats.map((chat) => chat.content).join('');
|
|
82
82
|
}, [messageString, historyCount, enableHistoryCount]);
|
|
83
83
|
|
|
@@ -23,10 +23,11 @@ interface AssistantActionsProps {
|
|
|
23
23
|
}
|
|
24
24
|
export const AssistantActionsBar = memo<AssistantActionsProps>(({ id, data, index }) => {
|
|
25
25
|
const { error, tools } = data;
|
|
26
|
-
const [isThreadMode, hasThread, isRegenerating] = useChatStore((s) => [
|
|
26
|
+
const [isThreadMode, hasThread, isRegenerating, isCollapsed] = useChatStore((s) => [
|
|
27
27
|
!!s.activeThreadId,
|
|
28
28
|
threadSelectors.hasThreadBySourceMsgId(id)(s),
|
|
29
29
|
messageStateSelectors.isMessageRegenerating(id)(s),
|
|
30
|
+
messageStateSelectors.isMessageCollapsed(id)(s),
|
|
30
31
|
]);
|
|
31
32
|
const isGroupSession = useSessionStore(sessionSelectors.isCurrentSessionGroupSession);
|
|
32
33
|
const [showShareModal, setShareModal] = useState(false);
|
|
@@ -43,6 +44,8 @@ export const AssistantActionsBar = memo<AssistantActionsProps>(({ id, data, inde
|
|
|
43
44
|
share,
|
|
44
45
|
tts,
|
|
45
46
|
translate,
|
|
47
|
+
collapse,
|
|
48
|
+
expand,
|
|
46
49
|
} = useChatListActionsBar({ hasThread, isRegenerating });
|
|
47
50
|
|
|
48
51
|
const hasTools = !!tools;
|
|
@@ -72,6 +75,7 @@ export const AssistantActionsBar = memo<AssistantActionsProps>(({ id, data, inde
|
|
|
72
75
|
resendThreadMessage,
|
|
73
76
|
delAndResendThreadMessage,
|
|
74
77
|
toggleMessageEditing,
|
|
78
|
+
toggleMessageCollapsed,
|
|
75
79
|
] = useChatStore((s) => [
|
|
76
80
|
s.deleteMessage,
|
|
77
81
|
s.regenerateAssistantMessage,
|
|
@@ -83,6 +87,7 @@ export const AssistantActionsBar = memo<AssistantActionsProps>(({ id, data, inde
|
|
|
83
87
|
s.resendThreadMessage,
|
|
84
88
|
s.delAndResendThreadMessage,
|
|
85
89
|
s.toggleMessageEditing,
|
|
90
|
+
s.toggleMessageCollapsed,
|
|
86
91
|
]);
|
|
87
92
|
const { message } = App.useApp();
|
|
88
93
|
const virtuosoRef = use(VirtuosoContext);
|
|
@@ -142,6 +147,12 @@ export const AssistantActionsBar = memo<AssistantActionsProps>(({ id, data, inde
|
|
|
142
147
|
break;
|
|
143
148
|
}
|
|
144
149
|
|
|
150
|
+
case 'collapse':
|
|
151
|
+
case 'expand': {
|
|
152
|
+
toggleMessageCollapsed(id);
|
|
153
|
+
break;
|
|
154
|
+
}
|
|
155
|
+
|
|
145
156
|
// case 'export': {
|
|
146
157
|
// setModal(true);
|
|
147
158
|
// break;
|
|
@@ -166,6 +177,8 @@ export const AssistantActionsBar = memo<AssistantActionsProps>(({ id, data, inde
|
|
|
166
177
|
|
|
167
178
|
if (error) return <ErrorActionsBar onActionClick={onActionClick} />;
|
|
168
179
|
|
|
180
|
+
const collapseAction = isCollapsed ? expand : collapse;
|
|
181
|
+
|
|
169
182
|
return (
|
|
170
183
|
<>
|
|
171
184
|
<ActionIconGroup
|
|
@@ -174,6 +187,7 @@ export const AssistantActionsBar = memo<AssistantActionsProps>(({ id, data, inde
|
|
|
174
187
|
items: [
|
|
175
188
|
edit,
|
|
176
189
|
copy,
|
|
190
|
+
collapseAction,
|
|
177
191
|
divider,
|
|
178
192
|
tts,
|
|
179
193
|
translate,
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { Button, Markdown, MaskShadow } from '@lobehub/ui';
|
|
2
|
+
import { memo } from 'react';
|
|
3
|
+
import { useTranslation } from 'react-i18next';
|
|
4
|
+
import { Flexbox } from 'react-layout-kit';
|
|
5
|
+
|
|
6
|
+
import { useChatStore } from '@/store/chat';
|
|
7
|
+
|
|
8
|
+
interface CollapsedMessageProps {
|
|
9
|
+
content: string;
|
|
10
|
+
id: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const CollapsedMessage = memo<CollapsedMessageProps>(({ id, content }) => {
|
|
14
|
+
const { t } = useTranslation('chat');
|
|
15
|
+
const toggleMessageCollapsed = useChatStore((s) => s.toggleMessageCollapsed);
|
|
16
|
+
|
|
17
|
+
return (
|
|
18
|
+
<Flexbox>
|
|
19
|
+
<MaskShadow>
|
|
20
|
+
<Markdown variant={'chat'}>{content?.slice(0, 100)}</Markdown>
|
|
21
|
+
</MaskShadow>
|
|
22
|
+
<Flexbox padding={4}>
|
|
23
|
+
<Button
|
|
24
|
+
block
|
|
25
|
+
color={'default'}
|
|
26
|
+
onClick={() => {
|
|
27
|
+
toggleMessageCollapsed(id, false);
|
|
28
|
+
}}
|
|
29
|
+
size={'small'}
|
|
30
|
+
variant={'filled'}
|
|
31
|
+
>
|
|
32
|
+
{t('chatList.expandMessage')}
|
|
33
|
+
</Button>
|
|
34
|
+
</Flexbox>
|
|
35
|
+
</Flexbox>
|
|
36
|
+
);
|
|
37
|
+
});
|