@lobehub/lobehub 2.0.0-next.35 → 2.0.0-next.37
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 +50 -0
- package/changelog/v1.json +18 -0
- package/next.config.ts +5 -6
- package/package.json +2 -2
- package/packages/agent-runtime/src/core/__tests__/runtime.test.ts +112 -77
- package/packages/agent-runtime/src/core/runtime.ts +63 -18
- package/packages/agent-runtime/src/types/generalAgent.ts +55 -0
- package/packages/agent-runtime/src/types/index.ts +1 -0
- package/packages/agent-runtime/src/types/instruction.ts +10 -3
- package/packages/const/src/user.ts +0 -1
- package/packages/context-engine/src/processors/GroupMessageFlatten.ts +8 -6
- package/packages/context-engine/src/processors/__tests__/GroupMessageFlatten.test.ts +12 -12
- package/packages/conversation-flow/src/__tests__/fixtures/inputs/branch/assistant-group-branches.json +249 -0
- package/packages/conversation-flow/src/__tests__/fixtures/inputs/branch/index.ts +4 -0
- package/packages/conversation-flow/src/__tests__/fixtures/inputs/branch/multi-assistant-group.json +260 -0
- package/packages/conversation-flow/src/__tests__/fixtures/outputs/branch/active-index-1.json +4 -0
- package/packages/conversation-flow/src/__tests__/fixtures/outputs/branch/assistant-group-branches.json +481 -0
- package/packages/conversation-flow/src/__tests__/fixtures/outputs/branch/conversation.json +5 -1
- package/packages/conversation-flow/src/__tests__/fixtures/outputs/branch/index.ts +4 -0
- package/packages/conversation-flow/src/__tests__/fixtures/outputs/branch/multi-assistant-group.json +407 -0
- package/packages/conversation-flow/src/__tests__/fixtures/outputs/branch/nested.json +18 -2
- package/packages/conversation-flow/src/__tests__/fixtures/outputs/complex-scenario.json +25 -3
- package/packages/conversation-flow/src/__tests__/parse.test.ts +12 -0
- package/packages/conversation-flow/src/index.ts +1 -1
- package/packages/conversation-flow/src/transformation/FlatListBuilder.ts +112 -34
- package/packages/conversation-flow/src/types/flatMessageList.ts +0 -12
- package/packages/conversation-flow/src/{types.ts → types/index.ts} +3 -14
- package/packages/database/src/models/__tests__/apiKey.test.ts +444 -0
- package/packages/database/src/models/message.ts +18 -19
- package/packages/types/src/aiChat.ts +2 -0
- package/packages/types/src/importer.ts +2 -2
- package/packages/types/src/message/ui/chat.ts +17 -1
- package/packages/types/src/message/ui/extra.ts +2 -2
- package/packages/types/src/message/ui/params.ts +2 -2
- package/packages/types/src/user/preference.ts +0 -4
- package/packages/utils/src/tokenizer/index.ts +3 -11
- package/src/app/[variants]/(main)/chat/components/conversation/features/ChatInput/Desktop/MessageFromUrl.tsx +3 -3
- package/src/app/[variants]/(main)/chat/components/conversation/features/ChatInput/V1Mobile/index.tsx +1 -1
- package/src/app/[variants]/(main)/chat/components/conversation/features/ChatInput/V1Mobile/useSend.ts +3 -3
- package/src/app/[variants]/(main)/chat/components/conversation/features/ChatInput/useSend.ts +6 -6
- package/src/app/[variants]/(main)/chat/components/conversation/features/ChatList/Content.tsx +5 -3
- package/src/app/[variants]/(main)/chat/components/conversation/features/ChatList/WelcomeChatItem/AgentWelcome/OpeningQuestions.tsx +2 -2
- package/src/app/[variants]/(main)/chat/components/conversation/features/ChatList/WelcomeChatItem/GroupWelcome/GroupUsageSuggest.tsx +2 -2
- package/src/app/[variants]/(main)/labs/page.tsx +0 -9
- package/src/features/ChatInput/ActionBar/STT/browser.tsx +3 -3
- package/src/features/ChatInput/ActionBar/STT/openai.tsx +3 -3
- package/src/features/Conversation/Error/AccessCodeForm.tsx +1 -1
- package/src/features/Conversation/Error/ChatInvalidApiKey.tsx +1 -1
- package/src/features/Conversation/Error/ClerkLogin/index.tsx +1 -1
- package/src/features/Conversation/Error/OAuthForm.tsx +1 -1
- package/src/features/Conversation/Error/index.tsx +0 -5
- package/src/features/Conversation/Messages/Assistant/Actions/index.tsx +13 -10
- package/src/features/Conversation/Messages/Assistant/Extra/index.test.tsx +3 -8
- package/src/features/Conversation/Messages/Assistant/Extra/index.tsx +2 -6
- package/src/features/Conversation/Messages/Assistant/MessageContent.tsx +7 -9
- package/src/features/Conversation/Messages/Assistant/Tool/Inspector/PluginResult.tsx +2 -2
- package/src/features/Conversation/Messages/Assistant/Tool/Inspector/PluginState.tsx +2 -2
- package/src/features/Conversation/Messages/Assistant/Tool/Render/PluginSettings.tsx +4 -1
- package/src/features/Conversation/Messages/Assistant/Tool/Render/index.tsx +2 -3
- package/src/features/Conversation/Messages/Assistant/index.tsx +57 -60
- package/src/features/Conversation/Messages/Default.tsx +1 -0
- package/src/features/Conversation/Messages/Group/Actions/WithContentId.tsx +38 -10
- package/src/features/Conversation/Messages/Group/Actions/index.tsx +1 -1
- package/src/features/Conversation/Messages/Group/ContentBlock.tsx +1 -3
- package/src/features/Conversation/Messages/Group/GroupChildren.tsx +12 -12
- package/src/features/Conversation/Messages/Group/MessageContent.tsx +7 -1
- package/src/features/Conversation/Messages/Group/Tool/Render/PluginSettings.tsx +1 -1
- package/src/features/Conversation/Messages/Group/index.tsx +2 -1
- package/src/features/Conversation/Messages/Supervisor/index.tsx +2 -2
- package/src/features/Conversation/Messages/User/{Actions.tsx → Actions/ActionsBar.tsx} +26 -25
- package/src/features/Conversation/Messages/User/Actions/MessageBranch.tsx +107 -0
- package/src/features/Conversation/Messages/User/Actions/index.tsx +42 -0
- package/src/features/Conversation/Messages/User/index.tsx +43 -44
- package/src/features/Conversation/Messages/index.tsx +3 -3
- package/src/features/Conversation/components/AutoScroll.tsx +3 -3
- package/src/features/Conversation/components/Extras/Usage/UsageDetail/AnimatedNumber.tsx +55 -0
- package/src/features/Conversation/components/Extras/Usage/UsageDetail/index.tsx +5 -2
- package/src/features/Conversation/components/VirtualizedList/index.tsx +29 -20
- package/src/features/Conversation/hooks/useChatListActionsBar.tsx +8 -10
- package/src/features/Portal/Thread/Chat/ChatInput/useSend.ts +3 -3
- package/src/hooks/useHotkeys/chatScope.ts +15 -7
- package/src/libs/trpc/client/lambda.ts +4 -3
- package/src/server/routers/lambda/__tests__/aiChat.test.ts +1 -1
- package/src/server/routers/lambda/__tests__/integration/message.integration.test.ts +0 -26
- package/src/server/routers/lambda/aiChat.ts +3 -2
- package/src/server/routers/lambda/message.ts +8 -16
- package/src/server/services/message/__tests__/index.test.ts +29 -39
- package/src/server/services/message/index.ts +41 -36
- package/src/services/electron/desktopNotification.ts +6 -6
- package/src/services/electron/file.ts +6 -6
- package/src/services/file/ClientS3/index.ts +8 -8
- package/src/services/message/__tests__/metadata-race-condition.test.ts +157 -0
- package/src/services/message/index.ts +21 -15
- package/src/services/upload.ts +11 -11
- package/src/services/utils/abortableRequest.test.ts +161 -0
- package/src/services/utils/abortableRequest.ts +67 -0
- package/src/store/chat/agents/GeneralChatAgent.ts +137 -0
- package/src/store/chat/agents/createAgentExecutors.ts +395 -0
- package/src/store/chat/helpers.test.ts +0 -99
- package/src/store/chat/helpers.ts +0 -11
- package/src/store/chat/slices/aiChat/actions/__tests__/conversationControl.test.ts +332 -0
- package/src/store/chat/slices/aiChat/actions/__tests__/conversationLifecycle.test.ts +257 -0
- package/src/store/chat/slices/aiChat/actions/__tests__/helpers.ts +11 -2
- package/src/store/chat/slices/aiChat/actions/__tests__/rag.test.ts +6 -6
- package/src/store/chat/slices/aiChat/actions/__tests__/streamingExecutor.test.ts +391 -0
- package/src/store/chat/slices/aiChat/actions/__tests__/streamingStates.test.ts +179 -0
- package/src/store/chat/slices/aiChat/actions/conversationControl.ts +157 -0
- package/src/store/chat/slices/aiChat/actions/conversationLifecycle.ts +329 -0
- package/src/store/chat/slices/aiChat/actions/generateAIGroupChat.ts +14 -14
- package/src/store/chat/slices/aiChat/actions/index.ts +12 -6
- package/src/store/chat/slices/aiChat/actions/rag.ts +9 -6
- package/src/store/chat/slices/aiChat/actions/streamingExecutor.ts +604 -0
- package/src/store/chat/slices/aiChat/actions/streamingStates.ts +84 -0
- package/src/store/chat/slices/builtinTool/actions/__tests__/localSystem.test.ts +4 -4
- package/src/store/chat/slices/builtinTool/actions/__tests__/search.test.ts +11 -11
- package/src/store/chat/slices/builtinTool/actions/interpreter.ts +8 -8
- package/src/store/chat/slices/builtinTool/actions/localSystem.ts +2 -2
- package/src/store/chat/slices/builtinTool/actions/search.ts +8 -8
- package/src/store/chat/slices/message/action.test.ts +79 -68
- package/src/store/chat/slices/message/actions/index.ts +39 -0
- package/src/store/chat/slices/message/actions/internals.ts +77 -0
- package/src/store/chat/slices/message/actions/optimisticUpdate.ts +260 -0
- package/src/store/chat/slices/message/actions/publicApi.ts +224 -0
- package/src/store/chat/slices/message/actions/query.ts +120 -0
- package/src/store/chat/slices/message/actions/runtimeState.ts +108 -0
- package/src/store/chat/slices/message/initialState.ts +13 -0
- package/src/store/chat/slices/message/reducer.test.ts +48 -370
- package/src/store/chat/slices/message/reducer.ts +17 -81
- package/src/store/chat/slices/message/selectors/chat.test.ts +13 -50
- package/src/store/chat/slices/message/selectors/chat.ts +78 -242
- package/src/store/chat/slices/message/selectors/dbMessage.ts +140 -0
- package/src/store/chat/slices/message/selectors/displayMessage.ts +301 -0
- package/src/store/chat/slices/message/selectors/messageState.ts +5 -2
- package/src/store/chat/slices/plugin/action.test.ts +62 -64
- package/src/store/chat/slices/plugin/action.ts +34 -28
- package/src/store/chat/slices/thread/action.test.ts +28 -31
- package/src/store/chat/slices/thread/action.ts +13 -10
- package/src/store/chat/slices/thread/selectors/index.ts +8 -6
- package/src/store/chat/slices/topic/reducer.ts +11 -3
- package/src/store/chat/store.ts +1 -1
- package/src/store/user/slices/preference/selectors/labPrefer.ts +0 -3
- package/packages/database/src/models/__tests__/message.grouping.test.ts +0 -812
- package/packages/database/src/utils/__tests__/groupMessages.test.ts +0 -1132
- package/packages/database/src/utils/groupMessages.ts +0 -361
- package/packages/utils/src/tokenizer/client.ts +0 -35
- package/packages/utils/src/tokenizer/estimated.ts +0 -4
- package/packages/utils/src/tokenizer/server.ts +0 -11
- package/packages/utils/src/tokenizer/tokenizer.worker.ts +0 -12
- package/src/app/(backend)/webapi/tokenizer/index.test.ts +0 -32
- package/src/app/(backend)/webapi/tokenizer/route.ts +0 -8
- package/src/features/Conversation/Error/InvalidAccessCode.tsx +0 -79
- package/src/store/chat/slices/aiChat/actions/__tests__/generateAIChat.test.ts +0 -975
- package/src/store/chat/slices/aiChat/actions/__tests__/generateAIChatV2.test.ts +0 -1050
- package/src/store/chat/slices/aiChat/actions/generateAIChat.ts +0 -720
- package/src/store/chat/slices/aiChat/actions/generateAIChatV2.ts +0 -849
- package/src/store/chat/slices/message/action.ts +0 -629
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { memo } from 'react';
|
|
2
|
+
import { Flexbox } from 'react-layout-kit';
|
|
3
|
+
|
|
4
|
+
import { useChatStore } from '@/store/chat';
|
|
5
|
+
import { messageStateSelectors } from '@/store/chat/selectors';
|
|
6
|
+
import { UIChatMessage } from '@/types/index';
|
|
7
|
+
|
|
8
|
+
import { UserActionsBar } from './ActionsBar';
|
|
9
|
+
import MessageBranch from './MessageBranch';
|
|
10
|
+
|
|
11
|
+
interface ActionsProps {
|
|
12
|
+
data: UIChatMessage;
|
|
13
|
+
disableEditing?: boolean;
|
|
14
|
+
id: string;
|
|
15
|
+
index: number;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const Actions = memo<ActionsProps>(({ id, data, index, disableEditing }) => {
|
|
19
|
+
const { branch } = data;
|
|
20
|
+
const [editing] = useChatStore((s) => [messageStateSelectors.isMessageEditing(id)(s)]);
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
!editing && (
|
|
24
|
+
<Flexbox align={'center'} horizontal>
|
|
25
|
+
{!disableEditing && (
|
|
26
|
+
<Flexbox align={'flex-start'} role="menubar">
|
|
27
|
+
<UserActionsBar data={data} id={id} index={index} />
|
|
28
|
+
</Flexbox>
|
|
29
|
+
)}
|
|
30
|
+
{branch && (
|
|
31
|
+
<MessageBranch
|
|
32
|
+
activeBranchIndex={branch.activeBranchIndex}
|
|
33
|
+
count={branch.count}
|
|
34
|
+
messageId={id}
|
|
35
|
+
/>
|
|
36
|
+
)}
|
|
37
|
+
</Flexbox>
|
|
38
|
+
)
|
|
39
|
+
);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
export default Actions;
|
|
@@ -22,7 +22,7 @@ import { useUserStore } from '@/store/user';
|
|
|
22
22
|
import { userProfileSelectors } from '@/store/user/selectors';
|
|
23
23
|
|
|
24
24
|
import { useDoubleClickEdit } from '../../hooks/useDoubleClickEdit';
|
|
25
|
-
import
|
|
25
|
+
import Actions from './Actions';
|
|
26
26
|
import { UserBelowMessage } from './BelowMessage';
|
|
27
27
|
import { UserMessageExtra } from './Extra';
|
|
28
28
|
import { MarkdownRender as UserMarkdownRender } from './MarkdownRender';
|
|
@@ -127,60 +127,59 @@ const UserMessage = memo<UserMessageProps>((props) => {
|
|
|
127
127
|
);
|
|
128
128
|
|
|
129
129
|
return (
|
|
130
|
-
<Flexbox
|
|
131
|
-
className={styles.container}
|
|
132
|
-
direction={placement === 'left' ? 'horizontal' : 'horizontal-reverse'}
|
|
133
|
-
gap={mobile ? 6 : 12}
|
|
134
|
-
>
|
|
135
|
-
<Avatar
|
|
136
|
-
alt={title}
|
|
137
|
-
avatar={{ avatar, title }}
|
|
138
|
-
loading={loading}
|
|
139
|
-
placement={placement}
|
|
140
|
-
size={mobile ? 32 : undefined}
|
|
141
|
-
style={{ marginTop: 6 }}
|
|
142
|
-
/>
|
|
130
|
+
<Flexbox className={styles.container} gap={8}>
|
|
143
131
|
<Flexbox
|
|
144
|
-
|
|
145
|
-
|
|
132
|
+
direction={placement === 'left' ? 'horizontal' : 'horizontal-reverse'}
|
|
133
|
+
gap={mobile ? 6 : 12}
|
|
146
134
|
>
|
|
147
|
-
<
|
|
135
|
+
<Avatar
|
|
136
|
+
alt={title}
|
|
148
137
|
avatar={{ avatar, title }}
|
|
138
|
+
loading={loading}
|
|
149
139
|
placement={placement}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
titleAddon={dmIndicator}
|
|
140
|
+
size={32}
|
|
141
|
+
style={{ marginTop: 6 }}
|
|
153
142
|
/>
|
|
154
143
|
<Flexbox
|
|
155
144
|
align={placement === 'left' ? 'flex-start' : 'flex-end'}
|
|
156
|
-
className={styles.
|
|
157
|
-
direction={placement === 'left' ? 'horizontal' : 'horizontal-reverse'}
|
|
158
|
-
gap={8}
|
|
145
|
+
className={styles.messageContainer}
|
|
159
146
|
>
|
|
160
|
-
<
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
147
|
+
<Title
|
|
148
|
+
avatar={{ avatar, title }}
|
|
149
|
+
placement={placement}
|
|
150
|
+
showTitle={false}
|
|
151
|
+
time={createdAt}
|
|
152
|
+
titleAddon={dmIndicator}
|
|
153
|
+
/>
|
|
154
|
+
<Flexbox
|
|
155
|
+
align={placement === 'left' ? 'flex-start' : 'flex-end'}
|
|
156
|
+
className={styles.messageContent}
|
|
157
|
+
direction={placement === 'left' ? 'horizontal' : 'horizontal-reverse'}
|
|
158
|
+
gap={8}
|
|
159
|
+
>
|
|
160
|
+
<Flexbox flex={1} style={{ maxWidth: '100%', minWidth: 0 }}>
|
|
161
|
+
<MessageContent
|
|
162
|
+
editing={editing}
|
|
163
|
+
id={id}
|
|
164
|
+
markdownProps={markdownProps}
|
|
165
|
+
message={content}
|
|
166
|
+
messageExtra={<UserMessageExtra content={content} extra={extra} id={id} />}
|
|
167
|
+
onDoubleClick={onDoubleClick}
|
|
168
|
+
placement={placement}
|
|
169
|
+
primary
|
|
170
|
+
renderMessage={renderMessage}
|
|
171
|
+
variant={variant}
|
|
172
|
+
/>
|
|
178
173
|
</Flexbox>
|
|
179
|
-
|
|
174
|
+
</Flexbox>
|
|
175
|
+
<UserBelowMessage content={content} id={id} ragQuery={ragQuery} />
|
|
180
176
|
</Flexbox>
|
|
181
|
-
|
|
177
|
+
{mobile && variant === 'bubble' && <BorderSpacing borderSpacing={32} />}
|
|
178
|
+
</Flexbox>
|
|
179
|
+
|
|
180
|
+
<Flexbox direction={'horizontal-reverse'}>
|
|
181
|
+
<Actions data={props} disableEditing={disableEditing} id={id} index={index} />
|
|
182
182
|
</Flexbox>
|
|
183
|
-
{mobile && variant === 'bubble' && <BorderSpacing borderSpacing={32} />}
|
|
184
183
|
</Flexbox>
|
|
185
184
|
);
|
|
186
185
|
});
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
upsertVirtuosoVisibleItem,
|
|
12
12
|
} from '@/features/Conversation/components/VirtualizedList/VirtuosoContext';
|
|
13
13
|
import { useChatStore } from '@/store/chat';
|
|
14
|
-
import {
|
|
14
|
+
import { displayMessageSelectors, messageStateSelectors } from '@/store/chat/selectors';
|
|
15
15
|
|
|
16
16
|
import History from '../components/History';
|
|
17
17
|
import { InPortalThreadContext } from '../context/InPortalThreadContext';
|
|
@@ -56,7 +56,7 @@ const Item = memo<ChatListItemProps>(
|
|
|
56
56
|
const { styles, cx } = useStyles();
|
|
57
57
|
const containerRef = useRef<HTMLDivElement | null>(null);
|
|
58
58
|
|
|
59
|
-
const item = useChatStore(
|
|
59
|
+
const item = useChatStore(displayMessageSelectors.getDisplayMessageById(id), isEqual);
|
|
60
60
|
|
|
61
61
|
const [isMessageLoading] = useChatStore((s) => [messageStateSelectors.isMessageLoading(id)(s)]);
|
|
62
62
|
|
|
@@ -133,7 +133,7 @@ const Item = memo<ChatListItemProps>(
|
|
|
133
133
|
);
|
|
134
134
|
}
|
|
135
135
|
|
|
136
|
-
case '
|
|
136
|
+
case 'assistantGroup': {
|
|
137
137
|
return (
|
|
138
138
|
<GroupMessage
|
|
139
139
|
{...item}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { memo, useEffect } from 'react';
|
|
2
2
|
|
|
3
3
|
import { useChatStore } from '@/store/chat';
|
|
4
|
-
import {
|
|
4
|
+
import { displayMessageSelectors, messageStateSelectors } from '@/store/chat/selectors';
|
|
5
5
|
|
|
6
6
|
import BackBottom from './BackBottom';
|
|
7
7
|
|
|
@@ -12,8 +12,8 @@ interface AutoScrollProps {
|
|
|
12
12
|
}
|
|
13
13
|
const AutoScroll = memo<AutoScrollProps>(({ atBottom, isScrolling, onScrollToBottom }) => {
|
|
14
14
|
const trackVisibility = useChatStore(messageStateSelectors.isAIGenerating);
|
|
15
|
-
const str = useChatStore(
|
|
16
|
-
const reasoningStr = useChatStore(
|
|
15
|
+
const str = useChatStore(displayMessageSelectors.mainAIChatsMessageString);
|
|
16
|
+
const reasoningStr = useChatStore(displayMessageSelectors.mainAILatestMessageReasoningContent);
|
|
17
17
|
|
|
18
18
|
useEffect(() => {
|
|
19
19
|
if (atBottom && trackVisibility && !isScrolling) {
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { memo, useEffect, useRef, useState } from 'react';
|
|
2
|
+
|
|
3
|
+
interface AnimatedNumberProps {
|
|
4
|
+
duration?: number;
|
|
5
|
+
formatter?: (value: number) => string;
|
|
6
|
+
value: number;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const AnimatedNumber = memo<AnimatedNumberProps>(({ value, duration = 3000, formatter }) => {
|
|
10
|
+
const [displayValue, setDisplayValue] = useState(value);
|
|
11
|
+
const frameRef = useRef<number>(undefined);
|
|
12
|
+
const startTimeRef = useRef<number>(undefined);
|
|
13
|
+
const startValueRef = useRef(value);
|
|
14
|
+
|
|
15
|
+
useEffect(() => {
|
|
16
|
+
const startValue = startValueRef.current;
|
|
17
|
+
const diff = value - startValue;
|
|
18
|
+
|
|
19
|
+
if (diff === 0) return;
|
|
20
|
+
|
|
21
|
+
const animate = (currentTime: number) => {
|
|
22
|
+
if (!startTimeRef.current) {
|
|
23
|
+
startTimeRef.current = currentTime;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const elapsed = currentTime - startTimeRef.current;
|
|
27
|
+
const progress = Math.min(elapsed / duration, 1);
|
|
28
|
+
|
|
29
|
+
// 使用 easeOutCubic 缓动函数
|
|
30
|
+
const easeProgress = 1 - (1 - progress) ** 3;
|
|
31
|
+
const current = startValue + diff * easeProgress;
|
|
32
|
+
|
|
33
|
+
setDisplayValue(current);
|
|
34
|
+
|
|
35
|
+
if (progress < 1) {
|
|
36
|
+
frameRef.current = requestAnimationFrame(animate);
|
|
37
|
+
} else {
|
|
38
|
+
startValueRef.current = value;
|
|
39
|
+
startTimeRef.current = undefined;
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
frameRef.current = requestAnimationFrame(animate);
|
|
44
|
+
|
|
45
|
+
return () => {
|
|
46
|
+
if (frameRef.current) {
|
|
47
|
+
cancelAnimationFrame(frameRef.current);
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
}, [value, duration]);
|
|
51
|
+
|
|
52
|
+
return formatter ? formatter(displayValue) : displayValue.toString();
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
export default AnimatedNumber;
|
|
@@ -13,6 +13,7 @@ import { useGlobalStore } from '@/store/global';
|
|
|
13
13
|
import { systemStatusSelectors } from '@/store/global/selectors';
|
|
14
14
|
import { formatNumber, formatShortenNumber } from '@/utils/format';
|
|
15
15
|
|
|
16
|
+
import AnimatedNumber from './AnimatedNumber';
|
|
16
17
|
import ModelCard from './ModelCard';
|
|
17
18
|
import TokenProgress, { TokenProgressItem } from './TokenProgress';
|
|
18
19
|
import { getDetailsToken } from './tokens';
|
|
@@ -116,7 +117,6 @@ const TokenDetail = memo<TokenDetailProps>(({ meta, model, provider }) => {
|
|
|
116
117
|
? detailTokens.totalTokens.credit
|
|
117
118
|
: detailTokens.totalTokens!.token;
|
|
118
119
|
|
|
119
|
-
const shortTotal = (formatShortenNumber(totalCount) as string).toLowerCase?.();
|
|
120
120
|
const detailTotal = formatNumber(totalCount);
|
|
121
121
|
|
|
122
122
|
const averagePricing = formatNumber(
|
|
@@ -215,7 +215,10 @@ const TokenDetail = memo<TokenDetailProps>(({ meta, model, provider }) => {
|
|
|
215
215
|
>
|
|
216
216
|
<Center gap={2} horizontal style={{ cursor: 'default' }}>
|
|
217
217
|
<Icon icon={isShowCredit ? BadgeCent : CoinsIcon} />
|
|
218
|
-
|
|
218
|
+
<AnimatedNumber
|
|
219
|
+
formatter={(value) => (formatShortenNumber(value) as string).toLowerCase?.()}
|
|
220
|
+
value={totalCount}
|
|
221
|
+
/>
|
|
219
222
|
</Center>
|
|
220
223
|
</Popover>
|
|
221
224
|
);
|
|
@@ -1,16 +1,29 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
ReactNode,
|
|
5
|
+
forwardRef,
|
|
6
|
+
memo,
|
|
7
|
+
useCallback,
|
|
8
|
+
useEffect,
|
|
9
|
+
useMemo,
|
|
10
|
+
useRef,
|
|
11
|
+
useState,
|
|
12
|
+
} from 'react';
|
|
4
13
|
import { Flexbox } from 'react-layout-kit';
|
|
5
14
|
import { Virtuoso, VirtuosoHandle } from 'react-virtuoso';
|
|
6
15
|
|
|
7
16
|
import WideScreenContainer from '@/features/Conversation/components/WideScreenContainer';
|
|
8
17
|
import { useChatStore } from '@/store/chat';
|
|
9
|
-
import {
|
|
18
|
+
import { displayMessageSelectors } from '@/store/chat/selectors';
|
|
10
19
|
|
|
11
20
|
import AutoScroll from '../AutoScroll';
|
|
12
21
|
import SkeletonList from '../SkeletonList';
|
|
13
|
-
import {
|
|
22
|
+
import {
|
|
23
|
+
VirtuosoContext,
|
|
24
|
+
resetVirtuosoVisibleItems,
|
|
25
|
+
setVirtuosoGlobalRef,
|
|
26
|
+
} from './VirtuosoContext';
|
|
14
27
|
|
|
15
28
|
interface VirtualizedListProps {
|
|
16
29
|
dataSource: string[];
|
|
@@ -32,10 +45,9 @@ const VirtualizedList = memo<VirtualizedListProps>(({ mobile, dataSource, itemCo
|
|
|
32
45
|
const [atBottom, setAtBottom] = useState(true);
|
|
33
46
|
const [isScrolling, setIsScrolling] = useState(false);
|
|
34
47
|
|
|
35
|
-
const [
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
chatSelectors.isCurrentChatLoaded(s),
|
|
48
|
+
const [isFirstLoading, isCurrentChatLoaded] = useChatStore((s) => [
|
|
49
|
+
displayMessageSelectors.currentChatLoadingState(s),
|
|
50
|
+
displayMessageSelectors.isCurrentDisplayChatLoaded(s),
|
|
39
51
|
]);
|
|
40
52
|
|
|
41
53
|
const getFollowOutput = useCallback(() => {
|
|
@@ -53,9 +65,8 @@ const VirtualizedList = memo<VirtualizedListProps>(({ mobile, dataSource, itemCo
|
|
|
53
65
|
[atBottom],
|
|
54
66
|
);
|
|
55
67
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
}, [id]);
|
|
68
|
+
const components = useMemo(() => ({ List }), []);
|
|
69
|
+
const computeItemKey = useCallback((index: number, item: string) => item, []);
|
|
59
70
|
|
|
60
71
|
useEffect(() => {
|
|
61
72
|
setVirtuosoGlobalRef(virtuosoRef);
|
|
@@ -71,8 +82,8 @@ const VirtualizedList = memo<VirtualizedListProps>(({ mobile, dataSource, itemCo
|
|
|
71
82
|
};
|
|
72
83
|
}, []);
|
|
73
84
|
|
|
74
|
-
// overscan should be
|
|
75
|
-
const overscan = typeof window !== 'undefined' ? window.innerHeight *
|
|
85
|
+
// overscan should be 2 times the height of the window
|
|
86
|
+
const overscan = typeof window !== 'undefined' ? window.innerHeight * 2 : 0;
|
|
76
87
|
|
|
77
88
|
// first time loading or not loaded
|
|
78
89
|
if (isFirstLoading || !isCurrentChatLoaded) return <SkeletonList mobile={mobile} />;
|
|
@@ -81,12 +92,9 @@ const VirtualizedList = memo<VirtualizedListProps>(({ mobile, dataSource, itemCo
|
|
|
81
92
|
<VirtuosoContext value={virtuosoRef}>
|
|
82
93
|
<Virtuoso
|
|
83
94
|
atBottomStateChange={setAtBottom}
|
|
84
|
-
atBottomThreshold={
|
|
85
|
-
components={
|
|
86
|
-
|
|
87
|
-
}}
|
|
88
|
-
|
|
89
|
-
computeItemKey={(_, item) => item}
|
|
95
|
+
atBottomThreshold={200 * (mobile ? 2 : 1)}
|
|
96
|
+
components={components}
|
|
97
|
+
computeItemKey={computeItemKey}
|
|
90
98
|
data={dataSource}
|
|
91
99
|
followOutput={getFollowOutput}
|
|
92
100
|
increaseViewportBy={overscan}
|
|
@@ -108,13 +116,14 @@ const VirtualizedList = memo<VirtualizedListProps>(({ mobile, dataSource, itemCo
|
|
|
108
116
|
atBottom={atBottom}
|
|
109
117
|
isScrolling={isScrolling}
|
|
110
118
|
onScrollToBottom={(type) => {
|
|
119
|
+
const virtuoso = virtuosoRef.current;
|
|
111
120
|
switch (type) {
|
|
112
121
|
case 'auto': {
|
|
113
|
-
|
|
122
|
+
virtuoso?.scrollToIndex({ align: 'end', behavior: 'auto', index: 'LAST' });
|
|
114
123
|
break;
|
|
115
124
|
}
|
|
116
125
|
case 'click': {
|
|
117
|
-
|
|
126
|
+
virtuoso?.scrollToIndex({ align: 'end', behavior: 'smooth', index: 'LAST' });
|
|
118
127
|
break;
|
|
119
128
|
}
|
|
120
129
|
}
|
|
@@ -15,7 +15,6 @@ import {
|
|
|
15
15
|
import { useMemo } from 'react';
|
|
16
16
|
import { useTranslation } from 'react-i18next';
|
|
17
17
|
|
|
18
|
-
import { isDeprecatedEdition } from '@/const/version';
|
|
19
18
|
import { localeOptions } from '@/locales/resources';
|
|
20
19
|
|
|
21
20
|
const translateStyle = css`
|
|
@@ -41,18 +40,16 @@ interface ChatListActionsBar {
|
|
|
41
40
|
|
|
42
41
|
export const useChatListActionsBar = ({
|
|
43
42
|
hasThread,
|
|
44
|
-
|
|
43
|
+
isRegenerating,
|
|
44
|
+
}: { hasThread?: boolean; isRegenerating?: boolean } = {}): ChatListActionsBar => {
|
|
45
45
|
const { t } = useTranslation(['common', 'chat']);
|
|
46
46
|
|
|
47
|
-
return useMemo(
|
|
47
|
+
return useMemo<ChatListActionsBar>(
|
|
48
48
|
() => ({
|
|
49
49
|
branching: {
|
|
50
|
-
disable: isDeprecatedEdition || undefined,
|
|
51
50
|
icon: Split,
|
|
52
51
|
key: 'branching',
|
|
53
|
-
label:
|
|
54
|
-
? t('branching', { defaultValue: 'Create Sub Topic' })
|
|
55
|
-
: t('branchingDisable'),
|
|
52
|
+
label: t('branching', { defaultValue: 'Create Sub Topic' }),
|
|
56
53
|
},
|
|
57
54
|
copy: {
|
|
58
55
|
icon: Copy,
|
|
@@ -61,13 +58,13 @@ export const useChatListActionsBar = ({
|
|
|
61
58
|
},
|
|
62
59
|
del: {
|
|
63
60
|
danger: true,
|
|
64
|
-
|
|
61
|
+
disabled: hasThread,
|
|
65
62
|
icon: Trash,
|
|
66
63
|
key: 'del',
|
|
67
64
|
label: hasThread ? t('messageAction.deleteDisabledByThreads', { ns: 'chat' }) : t('delete'),
|
|
68
65
|
},
|
|
69
66
|
delAndRegenerate: {
|
|
70
|
-
|
|
67
|
+
disabled: hasThread || isRegenerating,
|
|
71
68
|
icon: ListRestart,
|
|
72
69
|
key: 'delAndRegenerate',
|
|
73
70
|
label: t('messageAction.delAndRegenerate', {
|
|
@@ -89,6 +86,7 @@ export const useChatListActionsBar = ({
|
|
|
89
86
|
label: '导出为 PDF',
|
|
90
87
|
},
|
|
91
88
|
regenerate: {
|
|
89
|
+
disabled: isRegenerating,
|
|
92
90
|
icon: RotateCcw,
|
|
93
91
|
key: 'regenerate',
|
|
94
92
|
label: t('regenerate', { defaultValue: 'Regenerate' }),
|
|
@@ -114,6 +112,6 @@ export const useChatListActionsBar = ({
|
|
|
114
112
|
label: t('tts.action', { ns: 'chat' }),
|
|
115
113
|
},
|
|
116
114
|
}),
|
|
117
|
-
[hasThread],
|
|
115
|
+
[hasThread, isRegenerating],
|
|
118
116
|
);
|
|
119
117
|
};
|
|
@@ -17,7 +17,7 @@ export const useSendThreadMessage = () => {
|
|
|
17
17
|
const canNotSend = useChatStore(threadSelectors.isSendButtonDisabledByMessage);
|
|
18
18
|
const generating = useChatStore((s) => threadSelectors.isThreadAIGenerating(s));
|
|
19
19
|
const stop = useChatStore((s) => s.stopGenerateMessage);
|
|
20
|
-
const [sendMessage,
|
|
20
|
+
const [sendMessage, updateMessageInput] = useChatStore((s) => [
|
|
21
21
|
s.sendThreadMessage,
|
|
22
22
|
s.updateThreadInputMessage,
|
|
23
23
|
]);
|
|
@@ -54,11 +54,11 @@ export const useSendThreadMessage = () => {
|
|
|
54
54
|
|
|
55
55
|
if (!shouldContinue) return;
|
|
56
56
|
|
|
57
|
-
|
|
57
|
+
updateMessageInput(inputMessage);
|
|
58
58
|
|
|
59
59
|
sendMessage({ message: inputMessage, ...params });
|
|
60
60
|
|
|
61
|
-
|
|
61
|
+
updateMessageInput('');
|
|
62
62
|
threadInputEditor.clearContent();
|
|
63
63
|
threadInputEditor.focus();
|
|
64
64
|
};
|
|
@@ -7,7 +7,7 @@ import { useClearCurrentMessages } from '@/features/ChatInput/ActionBar/Clear';
|
|
|
7
7
|
import { useOpenChatSettings } from '@/hooks/useInterceptingRoutes';
|
|
8
8
|
import { useActionSWR } from '@/libs/swr';
|
|
9
9
|
import { useChatStore } from '@/store/chat';
|
|
10
|
-
import {
|
|
10
|
+
import { displayMessageSelectors } from '@/store/chat/selectors';
|
|
11
11
|
import { useGlobalStore } from '@/store/global';
|
|
12
12
|
import { systemStatusSelectors } from '@/store/global/selectors';
|
|
13
13
|
import { HotkeyEnum, HotkeyScopeEnum } from '@/types/hotkey';
|
|
@@ -32,14 +32,22 @@ export const useOpenChatSettingsHotkey = () => {
|
|
|
32
32
|
};
|
|
33
33
|
|
|
34
34
|
export const useRegenerateMessageHotkey = () => {
|
|
35
|
-
const
|
|
36
|
-
|
|
35
|
+
const [regenerateUserMessage, regenerateAssistantMessage] = useChatStore((s) => [
|
|
36
|
+
s.regenerateUserMessage,
|
|
37
|
+
s.regenerateAssistantMessage,
|
|
38
|
+
]);
|
|
39
|
+
const lastMessage = useChatStore((s) => displayMessageSelectors.mainAIChats(s).at(-1), isEqual);
|
|
37
40
|
|
|
38
|
-
const disable = !lastMessage
|
|
41
|
+
const disable = !lastMessage;
|
|
39
42
|
|
|
40
43
|
return useHotkeyById(
|
|
41
44
|
HotkeyEnum.RegenerateMessage,
|
|
42
|
-
() =>
|
|
45
|
+
() => {
|
|
46
|
+
if (!lastMessage) return;
|
|
47
|
+
if (lastMessage.role === 'user') return regenerateUserMessage(lastMessage.id);
|
|
48
|
+
|
|
49
|
+
return regenerateAssistantMessage(lastMessage.id);
|
|
50
|
+
},
|
|
43
51
|
{
|
|
44
52
|
enableOnContentEditable: true,
|
|
45
53
|
enabled: !disable,
|
|
@@ -49,7 +57,7 @@ export const useRegenerateMessageHotkey = () => {
|
|
|
49
57
|
|
|
50
58
|
export const useDeleteAndRegenerateMessageHotkey = () => {
|
|
51
59
|
const delAndRegenerateMessage = useChatStore((s) => s.delAndRegenerateMessage);
|
|
52
|
-
const lastMessage = useChatStore(
|
|
60
|
+
const lastMessage = useChatStore((s) => displayMessageSelectors.mainAIChats(s).at(-1), isEqual);
|
|
53
61
|
|
|
54
62
|
const disable = !lastMessage || lastMessage.id === 'default' || lastMessage.role === 'system';
|
|
55
63
|
|
|
@@ -65,7 +73,7 @@ export const useDeleteAndRegenerateMessageHotkey = () => {
|
|
|
65
73
|
|
|
66
74
|
export const useDeleteLastMessageHotkey = () => {
|
|
67
75
|
const deleteMessage = useChatStore((s) => s.deleteMessage);
|
|
68
|
-
const lastMessage = useChatStore(
|
|
76
|
+
const lastMessage = useChatStore((s) => displayMessageSelectors.mainAIChats(s).at(-1), isEqual);
|
|
69
77
|
|
|
70
78
|
const disable = !lastMessage || lastMessage.id === 'default' || lastMessage.role === 'system';
|
|
71
79
|
|
|
@@ -84,7 +84,7 @@ const customHttpBatchLink = httpBatchLink({
|
|
|
84
84
|
// dynamic import to avoid circular dependency
|
|
85
85
|
const { createHeaderWithAuth } = await import('@/services/_auth');
|
|
86
86
|
|
|
87
|
-
let provider: ModelProvider
|
|
87
|
+
let provider: ModelProvider | undefined;
|
|
88
88
|
// for image page, we need to get the provider from the store
|
|
89
89
|
log('Getting provider from store for image page: %s', location.pathname);
|
|
90
90
|
if (location.pathname === '/image') {
|
|
@@ -96,8 +96,9 @@ const customHttpBatchLink = httpBatchLink({
|
|
|
96
96
|
log('Getting provider from store for image page: %s', provider);
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
-
//
|
|
100
|
-
|
|
99
|
+
// Only include provider in JWT for image operations
|
|
100
|
+
// For other operations (like knowledge base embedding), let server use its own config
|
|
101
|
+
const headers = await createHeaderWithAuth(provider ? { provider } : undefined);
|
|
101
102
|
log('Headers: %O', headers);
|
|
102
103
|
return headers;
|
|
103
104
|
},
|
|
@@ -358,32 +358,6 @@ describe('Message Router Integration Tests', () => {
|
|
|
358
358
|
expect(result).toHaveLength(1);
|
|
359
359
|
expect(result[0].id).toBe(msg1.id);
|
|
360
360
|
});
|
|
361
|
-
|
|
362
|
-
it('should support useGroup parameter', async () => {
|
|
363
|
-
const caller = messageRouter.createCaller(createTestContext(userId));
|
|
364
|
-
|
|
365
|
-
// 创建多个消息
|
|
366
|
-
await caller.createMessage({
|
|
367
|
-
content: 'Message 1',
|
|
368
|
-
role: 'assistant',
|
|
369
|
-
sessionId: testSessionId,
|
|
370
|
-
});
|
|
371
|
-
|
|
372
|
-
await caller.createMessage({
|
|
373
|
-
content: 'Message 2',
|
|
374
|
-
role: 'assistant',
|
|
375
|
-
sessionId: testSessionId,
|
|
376
|
-
});
|
|
377
|
-
|
|
378
|
-
// useGroup 参数应该影响消息分组展示
|
|
379
|
-
const result = await caller.getMessages({
|
|
380
|
-
sessionId: testSessionId,
|
|
381
|
-
useGroup: true,
|
|
382
|
-
});
|
|
383
|
-
|
|
384
|
-
expect(result).toBeDefined();
|
|
385
|
-
expect(Array.isArray(result)).toBe(true);
|
|
386
|
-
});
|
|
387
361
|
});
|
|
388
362
|
|
|
389
363
|
describe('removeMessages', () => {
|
|
@@ -96,6 +96,7 @@ export const aiChatRouter = router({
|
|
|
96
96
|
const userMessageItem = await ctx.messageModel.create({
|
|
97
97
|
content: input.newUserMessage.content,
|
|
98
98
|
files: input.newUserMessage.files,
|
|
99
|
+
parentId: input.newUserMessage.parentId,
|
|
99
100
|
role: 'user',
|
|
100
101
|
sessionId: input.sessionId!,
|
|
101
102
|
threadId: input.threadId,
|
|
@@ -113,9 +114,9 @@ export const aiChatRouter = router({
|
|
|
113
114
|
);
|
|
114
115
|
const assistantMessageItem = await ctx.messageModel.create({
|
|
115
116
|
content: LOADING_FLAT,
|
|
116
|
-
|
|
117
|
-
fromProvider: input.newAssistantMessage.provider,
|
|
117
|
+
model: input.newAssistantMessage.model,
|
|
118
118
|
parentId: messageId,
|
|
119
|
+
provider: input.newAssistantMessage.provider,
|
|
119
120
|
role: 'assistant',
|
|
120
121
|
sessionId: input.sessionId!,
|
|
121
122
|
threadId: input.threadId,
|