@lobehub/lobehub 2.0.0-next.8 → 2.0.0-next.9
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/desktop-pr-build.yml +8 -8
- package/.github/workflows/docker.yml +17 -16
- package/.github/workflows/e2e.yml +3 -3
- package/.github/workflows/release-desktop-beta.yml +8 -8
- package/.github/workflows/release.yml +1 -1
- package/.github/workflows/test.yml +4 -4
- package/CHANGELOG.md +25 -0
- package/changelog/v1.json +9 -0
- package/package.json +1 -1
- package/packages/const/src/index.ts +0 -1
- package/packages/const/src/url.ts +1 -4
- package/packages/context-engine/src/index.ts +1 -6
- package/packages/context-engine/src/processors/GroupMessageFlatten.ts +12 -2
- package/packages/context-engine/src/processors/__tests__/GroupMessageFlatten.test.ts +73 -9
- package/packages/context-engine/src/providers/index.ts +0 -2
- package/packages/database/package.json +1 -1
- package/packages/database/src/models/__tests__/message.grouping.test.ts +812 -0
- package/packages/database/src/models/__tests__/message.test.ts +322 -170
- package/packages/database/src/models/message.ts +62 -24
- package/packages/database/src/utils/__tests__/groupMessages.test.ts +145 -2
- package/packages/database/src/utils/groupMessages.ts +7 -5
- package/packages/types/src/message/common/base.ts +13 -0
- package/packages/types/src/message/common/image.ts +8 -0
- package/packages/types/src/message/common/metadata.ts +39 -0
- package/packages/types/src/message/common/tools.ts +10 -0
- package/packages/types/src/message/db/params.ts +47 -1
- package/packages/types/src/message/ui/chat.ts +4 -1
- package/packages/types/src/search.ts +16 -0
- package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/V1Mobile/index.tsx +2 -2
- package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/V1Mobile/useSend.ts +6 -4
- package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/useSend.ts +15 -10
- package/src/app/[variants]/(main)/chat/@session/features/SessionListContent/List/Item/index.tsx +4 -2
- package/src/components/Thinking/index.tsx +4 -3
- package/src/features/AgentSetting/AgentPlugin/index.tsx +2 -2
- package/src/features/ChatInput/ActionBar/STT/browser.tsx +2 -2
- package/src/features/ChatInput/ActionBar/STT/openai.tsx +2 -2
- package/src/features/ChatInput/ActionBar/Tools/useControls.tsx +1 -3
- package/src/features/Conversation/Error/ErrorJsonViewer.tsx +4 -3
- package/src/features/Conversation/Error/OllamaBizError/index.tsx +7 -2
- package/src/features/Conversation/Error/index.tsx +15 -5
- package/src/features/Conversation/MarkdownElements/LobeArtifact/Render/index.tsx +2 -2
- package/src/features/Conversation/Messages/Assistant/Extra/index.tsx +2 -2
- package/src/features/Conversation/Messages/Assistant/MessageContent.tsx +5 -3
- package/src/features/Conversation/Messages/Assistant/Tool/Inspector/BuiltinPluginTitle.tsx +2 -2
- package/src/features/Conversation/Messages/Assistant/Tool/Inspector/ToolTitle.tsx +4 -2
- package/src/features/Conversation/Messages/Assistant/Tool/Render/CustomRender.tsx +2 -2
- package/src/features/Conversation/Messages/Assistant/Tool/Render/index.tsx +2 -2
- package/src/features/Conversation/Messages/Assistant/Tool/index.tsx +2 -2
- package/src/features/Conversation/Messages/Assistant/index.tsx +4 -4
- package/src/features/Conversation/Messages/Default.tsx +2 -2
- package/src/features/Conversation/Messages/User/Extra.tsx +2 -2
- package/src/features/Conversation/Messages/User/index.tsx +4 -4
- package/src/features/Conversation/Messages/index.tsx +3 -3
- package/src/features/Conversation/components/AutoScroll.tsx +2 -2
- package/src/features/Conversation/components/Extras/Usage/UsageDetail/index.tsx +9 -6
- package/src/features/PluginTag/index.tsx +1 -3
- package/src/features/PluginsUI/Render/BuiltinType/index.test.tsx +37 -28
- package/src/features/Portal/Artifacts/Body/index.tsx +2 -2
- package/src/server/modules/ModelRuntime/trace.ts +11 -4
- package/src/server/routers/lambda/message.ts +14 -3
- package/src/services/chat/chat.test.ts +1 -40
- package/src/services/chat/contextEngineering.test.ts +0 -30
- package/src/services/chat/contextEngineering.ts +1 -12
- package/src/services/chat/index.ts +2 -7
- package/src/services/chat/types.ts +1 -1
- package/src/services/message/_deprecated.ts +1 -1
- package/src/services/message/client.ts +8 -2
- package/src/services/message/server.ts +7 -2
- package/src/services/message/type.ts +6 -1
- package/src/store/chat/helpers.test.ts +99 -0
- package/src/store/chat/helpers.ts +21 -2
- package/src/store/chat/selectors.ts +1 -1
- package/src/store/chat/slices/aiChat/actions/generateAIChat.ts +3 -3
- package/src/store/chat/slices/builtinTool/actions/index.ts +1 -4
- package/src/store/chat/slices/message/action.test.ts +5 -1
- package/src/store/chat/slices/message/action.ts +102 -14
- package/src/store/chat/slices/message/reducer.test.ts +363 -5
- package/src/store/chat/slices/message/reducer.ts +87 -3
- package/src/store/chat/slices/message/{selectors.test.ts → selectors/chat.test.ts} +266 -30
- package/src/store/chat/slices/message/{selectors.ts → selectors/chat.ts} +29 -79
- package/src/store/chat/slices/message/selectors/index.ts +2 -0
- package/src/store/chat/slices/message/selectors/messageState.test.ts +36 -0
- package/src/store/chat/slices/message/selectors/messageState.ts +80 -0
- package/src/store/chat/slices/plugin/action.test.ts +34 -132
- package/src/store/chat/slices/plugin/action.ts +1 -44
- package/src/store/tool/selectors/tool.test.ts +1 -1
- package/src/store/tool/selectors/tool.ts +6 -8
- package/src/store/tool/slices/builtin/action.test.ts +83 -35
- package/src/store/tool/slices/builtin/action.ts +0 -9
- package/src/store/tool/slices/builtin/selectors.test.ts +4 -30
- package/src/store/tool/slices/builtin/selectors.ts +15 -21
- package/src/tools/index.ts +0 -6
- package/src/tools/renders.ts +0 -3
- package/src/tools/web-browsing/Portal/Search/Footer.tsx +2 -2
- package/packages/const/src/guide.ts +0 -89
- package/packages/context-engine/src/providers/InboxGuide.ts +0 -102
- package/packages/context-engine/src/providers/__tests__/InboxGuideProvider.test.ts +0 -121
- package/src/services/chat/__snapshots__/chat.test.ts.snap +0 -110
- package/src/store/chat/slices/builtinTool/actions/__tests__/dalle.test.ts +0 -121
- package/src/store/chat/slices/builtinTool/actions/dalle.ts +0 -124
- package/src/tools/dalle/Render/GalleyGrid.tsx +0 -60
- package/src/tools/dalle/Render/Item/EditMode.tsx +0 -66
- package/src/tools/dalle/Render/Item/Error.tsx +0 -49
- package/src/tools/dalle/Render/Item/Image.tsx +0 -44
- package/src/tools/dalle/Render/Item/ImageFileItem.tsx +0 -57
- package/src/tools/dalle/Render/Item/index.tsx +0 -88
- package/src/tools/dalle/Render/ToolBar.tsx +0 -56
- package/src/tools/dalle/Render/index.tsx +0 -52
- package/src/tools/dalle/index.ts +0 -92
|
@@ -11,7 +11,7 @@ import InfoTooltip from '@/components/InfoTooltip';
|
|
|
11
11
|
import { aiModelSelectors, useAiInfraStore } from '@/store/aiInfra';
|
|
12
12
|
import { useGlobalStore } from '@/store/global';
|
|
13
13
|
import { systemStatusSelectors } from '@/store/global/selectors';
|
|
14
|
-
import { formatNumber } from '@/utils/format';
|
|
14
|
+
import { formatNumber, formatShortenNumber } from '@/utils/format';
|
|
15
15
|
|
|
16
16
|
import ModelCard from './ModelCard';
|
|
17
17
|
import TokenProgress, { TokenProgressItem } from './TokenProgress';
|
|
@@ -111,10 +111,13 @@ const TokenDetail = memo<TokenDetailProps>(({ meta, model, provider }) => {
|
|
|
111
111
|
},
|
|
112
112
|
].filter(Boolean) as TokenProgressItem[];
|
|
113
113
|
|
|
114
|
-
const
|
|
114
|
+
const totalCount =
|
|
115
115
|
isShowCredit && !!detailTokens.totalTokens
|
|
116
|
-
?
|
|
117
|
-
:
|
|
116
|
+
? detailTokens.totalTokens.credit
|
|
117
|
+
: detailTokens.totalTokens!.token;
|
|
118
|
+
|
|
119
|
+
const shortTotal = (formatShortenNumber(totalCount) as string).toLowerCase?.();
|
|
120
|
+
const detailTotal = formatNumber(totalCount);
|
|
118
121
|
|
|
119
122
|
const averagePricing = formatNumber(
|
|
120
123
|
detailTokens.totalTokens!.credit / detailTokens.totalTokens!.token,
|
|
@@ -171,7 +174,7 @@ const TokenDetail = memo<TokenDetailProps>(({ meta, model, provider }) => {
|
|
|
171
174
|
<div style={{ color: theme.colorTextSecondary }}>
|
|
172
175
|
{t('messages.tokenDetails.total')}
|
|
173
176
|
</div>
|
|
174
|
-
<div style={{ fontWeight: 500 }}>{
|
|
177
|
+
<div style={{ fontWeight: 500 }}>{detailTotal}</div>
|
|
175
178
|
</Flexbox>
|
|
176
179
|
{isShowCredit && (
|
|
177
180
|
<Flexbox align={'center'} gap={4} horizontal justify={'space-between'}>
|
|
@@ -212,7 +215,7 @@ const TokenDetail = memo<TokenDetailProps>(({ meta, model, provider }) => {
|
|
|
212
215
|
>
|
|
213
216
|
<Center gap={2} horizontal style={{ cursor: 'default' }}>
|
|
214
217
|
<Icon icon={isShowCredit ? BadgeCent : CoinsIcon} />
|
|
215
|
-
{
|
|
218
|
+
{shortTotal}
|
|
216
219
|
</Center>
|
|
217
220
|
</Popover>
|
|
218
221
|
);
|
|
@@ -7,7 +7,6 @@ import { memo } from 'react';
|
|
|
7
7
|
import { Center } from 'react-layout-kit';
|
|
8
8
|
|
|
9
9
|
import Avatar from '@/components/Plugins/PluginAvatar';
|
|
10
|
-
import { featureFlagsSelectors, useServerConfigStore } from '@/store/serverConfig';
|
|
11
10
|
import { pluginHelpers, useToolStore } from '@/store/tool';
|
|
12
11
|
import { toolSelectors } from '@/store/tool/selectors';
|
|
13
12
|
|
|
@@ -18,8 +17,7 @@ export interface PluginTagProps {
|
|
|
18
17
|
}
|
|
19
18
|
|
|
20
19
|
const PluginTag = memo<PluginTagProps>(({ plugins }) => {
|
|
21
|
-
const
|
|
22
|
-
const list = useToolStore(toolSelectors.metaList(showDalle), isEqual);
|
|
20
|
+
const list = useToolStore(toolSelectors.metaList, isEqual);
|
|
23
21
|
|
|
24
22
|
const displayPlugin = useToolStore(toolSelectors.getMetaById(plugins[0]), isEqual);
|
|
25
23
|
|
|
@@ -1,22 +1,19 @@
|
|
|
1
1
|
import { render, screen } from '@testing-library/react';
|
|
2
2
|
import { describe, expect, it, vi } from 'vitest';
|
|
3
3
|
|
|
4
|
-
import { DalleManifest } from '@/tools/dalle';
|
|
5
|
-
import { BuiltinToolsRenders } from '@/tools/renders';
|
|
6
|
-
|
|
7
4
|
import BuiltinType from './index';
|
|
8
5
|
|
|
9
|
-
// Mock
|
|
6
|
+
// Mock renders module
|
|
10
7
|
vi.mock('@/tools/renders', () => ({
|
|
11
8
|
BuiltinToolsRenders: {
|
|
12
|
-
|
|
13
|
-
|
|
9
|
+
'lobe-web-browsing': vi.fn(({ content }) => <div>WebBrowsingRender: {content}</div>),
|
|
10
|
+
'lobe-code-interpreter': vi.fn(({ content }) => <div>CodeInterpreterRender: {content}</div>),
|
|
14
11
|
},
|
|
15
12
|
}));
|
|
16
13
|
|
|
17
|
-
// Mock
|
|
18
|
-
vi.mock('../
|
|
19
|
-
|
|
14
|
+
// Mock useParseContent hook
|
|
15
|
+
vi.mock('../useParseContent', () => ({
|
|
16
|
+
useParseContent: vi.fn((content) => ({ data: content })),
|
|
20
17
|
}));
|
|
21
18
|
|
|
22
19
|
describe('BuiltinType', () => {
|
|
@@ -25,29 +22,41 @@ describe('BuiltinType', () => {
|
|
|
25
22
|
expect(container).toBeEmptyDOMElement();
|
|
26
23
|
});
|
|
27
24
|
|
|
28
|
-
it('should not render anything if content is not JSON and no identifier', () => {
|
|
29
|
-
const { container } = render(<BuiltinType content="..." id="123" />);
|
|
30
|
-
expect(container).toBeEmptyDOMElement();
|
|
31
|
-
});
|
|
32
|
-
|
|
33
25
|
it('should not render anything if identifier is unknown', () => {
|
|
34
26
|
const { container } = render(<BuiltinType content="{}" id="123" identifier="unknown" />);
|
|
35
27
|
expect(container).toBeEmptyDOMElement();
|
|
36
28
|
});
|
|
37
29
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
}
|
|
30
|
+
it('should render the correct renderer for web browsing', () => {
|
|
31
|
+
const content = '{"query":"test"}';
|
|
32
|
+
render(<BuiltinType content={content} id="123" identifier="lobe-web-browsing" />);
|
|
33
|
+
expect(screen.getByText(`WebBrowsingRender: ${content}`)).toBeInTheDocument();
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('should render the correct renderer for code interpreter', () => {
|
|
37
|
+
const content = '{"code":"print(1)"}';
|
|
38
|
+
render(<BuiltinType content={content} id="123" identifier="lobe-code-interpreter" />);
|
|
39
|
+
expect(screen.getByText(`CodeInterpreterRender: ${content}`)).toBeInTheDocument();
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('should pass correct props to renderer', () => {
|
|
43
|
+
const content = '{"test":"data"}';
|
|
44
|
+
const args = '{"arg":"value"}';
|
|
45
|
+
const pluginState = { state: 'value' };
|
|
46
|
+
const pluginError = { error: 'test' };
|
|
47
|
+
|
|
48
|
+
render(
|
|
49
|
+
<BuiltinType
|
|
50
|
+
content={content}
|
|
51
|
+
id="msg-123"
|
|
52
|
+
identifier="lobe-web-browsing"
|
|
53
|
+
arguments={args}
|
|
54
|
+
pluginState={pluginState}
|
|
55
|
+
pluginError={pluginError}
|
|
56
|
+
apiName="testApi"
|
|
57
|
+
/>,
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
expect(screen.getByText(`WebBrowsingRender: ${content}`)).toBeInTheDocument();
|
|
52
61
|
});
|
|
53
62
|
});
|
|
@@ -3,7 +3,7 @@ import { memo, useEffect, useMemo } from 'react';
|
|
|
3
3
|
import { Flexbox } from 'react-layout-kit';
|
|
4
4
|
|
|
5
5
|
import { useChatStore } from '@/store/chat';
|
|
6
|
-
import { chatPortalSelectors,
|
|
6
|
+
import { chatPortalSelectors, messageStateSelectors } from '@/store/chat/selectors';
|
|
7
7
|
import { ArtifactDisplayMode } from '@/store/chat/slices/portal/initialState';
|
|
8
8
|
import { ArtifactType } from '@/types/artifact';
|
|
9
9
|
|
|
@@ -24,7 +24,7 @@ const ArtifactsUI = memo(() => {
|
|
|
24
24
|
return [
|
|
25
25
|
messageId,
|
|
26
26
|
s.portalArtifactDisplayMode,
|
|
27
|
-
|
|
27
|
+
messageStateSelectors.isMessageGenerating(messageId)(s),
|
|
28
28
|
chatPortalSelectors.artifactType(s),
|
|
29
29
|
chatPortalSelectors.artifactCode(messageId)(s),
|
|
30
30
|
chatPortalSelectors.artifactCodeLanguage(s),
|
|
@@ -42,6 +42,16 @@ export const createTraceOptions = (
|
|
|
42
42
|
startTime: new Date(),
|
|
43
43
|
});
|
|
44
44
|
|
|
45
|
+
const headers = new Headers();
|
|
46
|
+
|
|
47
|
+
if (trace?.id) {
|
|
48
|
+
headers.set(LOBE_CHAT_TRACE_ID, trace.id);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (generation?.id) {
|
|
52
|
+
headers.set(LOBE_CHAT_OBSERVATION_ID, generation.id);
|
|
53
|
+
}
|
|
54
|
+
|
|
45
55
|
return {
|
|
46
56
|
callback: {
|
|
47
57
|
onCompletion: async ({ text, thinking, usage, grounding, toolsCalling }) => {
|
|
@@ -94,9 +104,6 @@ export const createTraceOptions = (
|
|
|
94
104
|
});
|
|
95
105
|
},
|
|
96
106
|
} as ChatStreamCallbacks,
|
|
97
|
-
headers:
|
|
98
|
-
[LOBE_CHAT_OBSERVATION_ID]: generation?.id,
|
|
99
|
-
[LOBE_CHAT_TRACE_ID]: trace?.id,
|
|
100
|
-
},
|
|
107
|
+
headers: headers,
|
|
101
108
|
};
|
|
102
109
|
};
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
BatchTaskResult,
|
|
3
|
+
UIChatMessage,
|
|
4
|
+
UpdateMessageParamsSchema,
|
|
5
|
+
UpdateMessageRAGParamsSchema,
|
|
6
|
+
} from '@lobechat/types';
|
|
2
7
|
import { z } from 'zod';
|
|
3
8
|
|
|
4
9
|
import { MessageModel } from '@/database/models/message';
|
|
@@ -180,11 +185,17 @@ export const messageRouter = router({
|
|
|
180
185
|
.input(
|
|
181
186
|
z.object({
|
|
182
187
|
id: z.string(),
|
|
183
|
-
|
|
188
|
+
sessionId: z.string().nullable().optional(),
|
|
189
|
+
topicId: z.string().nullable().optional(),
|
|
190
|
+
value: UpdateMessageParamsSchema,
|
|
184
191
|
}),
|
|
185
192
|
)
|
|
186
193
|
.mutation(async ({ input, ctx }) => {
|
|
187
|
-
return ctx.messageModel.update(input.id, input.value
|
|
194
|
+
return ctx.messageModel.update(input.id, input.value as any, {
|
|
195
|
+
postProcessUrl: (path) => ctx.fileService.getFullFileUrl(path),
|
|
196
|
+
sessionId: input.sessionId,
|
|
197
|
+
topicId: input.topicId,
|
|
198
|
+
});
|
|
188
199
|
}),
|
|
189
200
|
|
|
190
201
|
updateMessagePlugin: messageProcedure
|
|
@@ -8,18 +8,12 @@ import { type Mock, afterEach, beforeEach, describe, expect, it, vi } from 'vite
|
|
|
8
8
|
|
|
9
9
|
import { DEFAULT_USER_AVATAR } from '@/const/meta';
|
|
10
10
|
import { DEFAULT_AGENT_CONFIG } from '@/const/settings';
|
|
11
|
-
import * as isCanUseFCModule from '@/helpers/isCanUseFC';
|
|
12
11
|
import * as toolEngineeringModule from '@/helpers/toolEngineering';
|
|
13
|
-
import { agentChatConfigSelectors
|
|
12
|
+
import { agentChatConfigSelectors } from '@/store/agent/selectors';
|
|
14
13
|
import { aiModelSelectors } from '@/store/aiInfra';
|
|
15
14
|
import { useToolStore } from '@/store/tool';
|
|
16
|
-
import { toolSelectors } from '@/store/tool/selectors';
|
|
17
|
-
import { modelProviderSelectors } from '@/store/user/selectors';
|
|
18
|
-
import { DalleManifest } from '@/tools/dalle';
|
|
19
15
|
import { WebBrowsingManifest } from '@/tools/web-browsing';
|
|
20
16
|
|
|
21
|
-
import { API_ENDPOINTS } from '../_url';
|
|
22
|
-
import * as helpers from './helper';
|
|
23
17
|
import { chatService } from './index';
|
|
24
18
|
|
|
25
19
|
// Mocking external dependencies
|
|
@@ -831,39 +825,6 @@ describe('ChatService', () => {
|
|
|
831
825
|
undefined,
|
|
832
826
|
);
|
|
833
827
|
});
|
|
834
|
-
|
|
835
|
-
it('work with dalle3', async () => {
|
|
836
|
-
const getChatCompletionSpy = vi.spyOn(chatService, 'getChatCompletion');
|
|
837
|
-
const messages = [
|
|
838
|
-
{
|
|
839
|
-
role: 'user',
|
|
840
|
-
content: 'https://vercel.com/ 请分析 chatGPT 关键词\n\n',
|
|
841
|
-
sessionId: 'inbox',
|
|
842
|
-
createdAt: 1702723964330,
|
|
843
|
-
id: 'vyQvEw6V',
|
|
844
|
-
updatedAt: 1702723964330,
|
|
845
|
-
extra: {},
|
|
846
|
-
meta: {
|
|
847
|
-
avatar: DEFAULT_USER_AVATAR,
|
|
848
|
-
},
|
|
849
|
-
},
|
|
850
|
-
] as UIChatMessage[];
|
|
851
|
-
|
|
852
|
-
await chatService.createAssistantMessage({
|
|
853
|
-
messages,
|
|
854
|
-
model: 'gpt-3.5-turbo-1106',
|
|
855
|
-
top_p: 1,
|
|
856
|
-
plugins: [DalleManifest.identifier],
|
|
857
|
-
});
|
|
858
|
-
|
|
859
|
-
// Assert that getChatCompletionSpy was called with the expected arguments
|
|
860
|
-
expect(getChatCompletionSpy).toHaveBeenCalled();
|
|
861
|
-
|
|
862
|
-
const calls = getChatCompletionSpy.mock.lastCall;
|
|
863
|
-
// Take a snapshot of the first call's first argument
|
|
864
|
-
expect(calls![0]).toMatchSnapshot();
|
|
865
|
-
expect(calls![1]).toBeUndefined();
|
|
866
|
-
});
|
|
867
828
|
});
|
|
868
829
|
|
|
869
830
|
describe('search functionality', () => {
|
|
@@ -248,36 +248,6 @@ describe('contextEngineering', () => {
|
|
|
248
248
|
]);
|
|
249
249
|
});
|
|
250
250
|
|
|
251
|
-
it('should inject INBOX_GUIDE_SYSTEM_ROLE for welcome questions in inbox session', async () => {
|
|
252
|
-
// Don't mock INBOX_GUIDE_SYSTEMROLE, use the real one
|
|
253
|
-
const messages: UIChatMessage[] = [
|
|
254
|
-
{
|
|
255
|
-
role: 'user',
|
|
256
|
-
content: 'Hello, this is my first question',
|
|
257
|
-
createdAt: Date.now(),
|
|
258
|
-
id: 'test-welcome',
|
|
259
|
-
meta: {},
|
|
260
|
-
updatedAt: Date.now(),
|
|
261
|
-
},
|
|
262
|
-
];
|
|
263
|
-
|
|
264
|
-
const result = await contextEngineering({
|
|
265
|
-
messages,
|
|
266
|
-
model: 'gpt-4',
|
|
267
|
-
provider: 'openai',
|
|
268
|
-
isWelcomeQuestion: true,
|
|
269
|
-
sessionId: 'inbox',
|
|
270
|
-
});
|
|
271
|
-
|
|
272
|
-
// Should have system message with inbox guide content
|
|
273
|
-
const systemMessage = result.find((msg) => msg.role === 'system');
|
|
274
|
-
expect(systemMessage).toBeDefined();
|
|
275
|
-
// Check for characteristic content of the actual INBOX_GUIDE_SYSTEMROLE
|
|
276
|
-
expect(systemMessage!.content).toContain('LobeChat Support Assistant');
|
|
277
|
-
expect(systemMessage!.content).toContain('LobeHub');
|
|
278
|
-
expect(Object.keys(systemMessage!).length).toEqual(2);
|
|
279
|
-
});
|
|
280
|
-
|
|
281
251
|
it('should inject historySummary into system message when provided', async () => {
|
|
282
252
|
const historySummary = 'Previous conversation summary: User discussed AI topics.';
|
|
283
253
|
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { isDesktop, isServerMode } from '@lobechat/const';
|
|
2
2
|
import {
|
|
3
3
|
ContextEngine,
|
|
4
4
|
HistorySummaryProvider,
|
|
5
5
|
HistoryTruncateProcessor,
|
|
6
|
-
InboxGuideProvider,
|
|
7
6
|
InputTemplateProcessor,
|
|
8
7
|
MessageCleanupProcessor,
|
|
9
8
|
MessageContentProcessor,
|
|
@@ -48,8 +47,6 @@ export const contextEngineering = async ({
|
|
|
48
47
|
enableHistoryCount,
|
|
49
48
|
historyCount,
|
|
50
49
|
historySummary,
|
|
51
|
-
sessionId,
|
|
52
|
-
isWelcomeQuestion,
|
|
53
50
|
}: ContextEngineeringContext): Promise<OpenAIChatMessage[]> => {
|
|
54
51
|
const toolNameResolver = new ToolNameResolver();
|
|
55
52
|
|
|
@@ -63,14 +60,6 @@ export const contextEngineering = async ({
|
|
|
63
60
|
// 2. System role injection (agent's system role)
|
|
64
61
|
new SystemRoleInjector({ systemRole }),
|
|
65
62
|
|
|
66
|
-
// 3. Inbox guide system role injection
|
|
67
|
-
new InboxGuideProvider({
|
|
68
|
-
inboxGuideSystemRole: INBOX_GUIDE_SYSTEMROLE,
|
|
69
|
-
inboxSessionId: INBOX_SESSION_ID,
|
|
70
|
-
isWelcomeQuestion: isWelcomeQuestion,
|
|
71
|
-
sessionId: sessionId,
|
|
72
|
-
}),
|
|
73
|
-
|
|
74
63
|
// 4. Tool system role injection
|
|
75
64
|
new ToolSystemRoleProvider({
|
|
76
65
|
getToolSystemRoles: (tools) => toolSelectors.enabledSystemRoles(tools)(getToolStoreState()),
|
|
@@ -66,7 +66,6 @@ interface FetchAITaskResultParams extends FetchSSEOptions {
|
|
|
66
66
|
interface CreateAssistantMessageStream extends FetchSSEOptions {
|
|
67
67
|
abortController?: AbortController;
|
|
68
68
|
historySummary?: string;
|
|
69
|
-
isWelcomeQuestion?: boolean;
|
|
70
69
|
params: GetChatCompletionPayload;
|
|
71
70
|
trace?: TracePayload;
|
|
72
71
|
}
|
|
@@ -113,9 +112,7 @@ class ChatService {
|
|
|
113
112
|
enableHistoryCount: agentChatConfigSelectors.enableHistoryCount(agentStoreState),
|
|
114
113
|
// include user messages
|
|
115
114
|
historyCount: agentChatConfigSelectors.historyCount(agentStoreState) + 2,
|
|
116
|
-
historySummary: options?.historySummary,
|
|
117
115
|
inputTemplate: chatConfig.inputTemplate,
|
|
118
|
-
isWelcomeQuestion: options?.isWelcomeQuestion,
|
|
119
116
|
messages,
|
|
120
117
|
model: payload.model,
|
|
121
118
|
provider: payload.provider!,
|
|
@@ -217,12 +214,10 @@ class ChatService {
|
|
|
217
214
|
onErrorHandle,
|
|
218
215
|
onFinish,
|
|
219
216
|
trace,
|
|
220
|
-
isWelcomeQuestion,
|
|
221
217
|
historySummary,
|
|
222
218
|
}: CreateAssistantMessageStream) => {
|
|
223
219
|
await this.createAssistantMessage(params, {
|
|
224
220
|
historySummary,
|
|
225
|
-
isWelcomeQuestion,
|
|
226
221
|
onAbort,
|
|
227
222
|
onErrorHandle,
|
|
228
223
|
onFinish,
|
|
@@ -404,7 +399,7 @@ class ChatService {
|
|
|
404
399
|
onLoadingChange?.(true);
|
|
405
400
|
|
|
406
401
|
try {
|
|
407
|
-
const
|
|
402
|
+
const llmMessages = await contextEngineering({
|
|
408
403
|
messages: params.messages as any,
|
|
409
404
|
model: params.model!,
|
|
410
405
|
provider: params.provider!,
|
|
@@ -421,7 +416,7 @@ class ChatService {
|
|
|
421
416
|
// remove plugins
|
|
422
417
|
delete params.plugins;
|
|
423
418
|
await this.getChatCompletion(
|
|
424
|
-
{ ...params, messages:
|
|
419
|
+
{ ...params, messages: llmMessages, tools },
|
|
425
420
|
{
|
|
426
421
|
onErrorHandle: (error) => {
|
|
427
422
|
errorHandle(new Error(error.message), error);
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { TracePayload } from '@lobechat/types';
|
|
2
|
+
|
|
2
3
|
import { FetchSSEOptions } from '@/utils/fetch';
|
|
3
4
|
|
|
4
5
|
export interface FetchOptions extends FetchSSEOptions {
|
|
5
6
|
historySummary?: string;
|
|
6
|
-
isWelcomeQuestion?: boolean;
|
|
7
7
|
signal?: AbortSignal | undefined;
|
|
8
8
|
trace?: TracePayload;
|
|
9
9
|
}
|
|
@@ -102,7 +102,7 @@ export class ClientService implements IMessageService {
|
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
// @ts-ignore
|
|
105
|
-
async updateMessage(id: string, message: Partial<DB_Message>) {
|
|
105
|
+
async updateMessage(id: string, message: Partial<DB_Message>): Promise<any> {
|
|
106
106
|
return MessageModel.update(id, message);
|
|
107
107
|
}
|
|
108
108
|
|
|
@@ -45,6 +45,7 @@ export class ClientService extends BaseClientService implements IMessageService
|
|
|
45
45
|
topicId,
|
|
46
46
|
},
|
|
47
47
|
{
|
|
48
|
+
groupAssistantMessages: false,
|
|
48
49
|
postProcessUrl: this.postProcessUrl,
|
|
49
50
|
},
|
|
50
51
|
);
|
|
@@ -60,6 +61,7 @@ export class ClientService extends BaseClientService implements IMessageService
|
|
|
60
61
|
topicId,
|
|
61
62
|
},
|
|
62
63
|
{
|
|
64
|
+
groupAssistantMessages: false,
|
|
63
65
|
postProcessUrl: this.postProcessUrl,
|
|
64
66
|
},
|
|
65
67
|
);
|
|
@@ -99,8 +101,12 @@ export class ClientService extends BaseClientService implements IMessageService
|
|
|
99
101
|
return this.messageModel.update(id, { error });
|
|
100
102
|
};
|
|
101
103
|
|
|
102
|
-
updateMessage: IMessageService['updateMessage'] = async (id, message) => {
|
|
103
|
-
return this.messageModel.update(id, message
|
|
104
|
+
updateMessage: IMessageService['updateMessage'] = async (id, message, options) => {
|
|
105
|
+
return this.messageModel.update(id, message, {
|
|
106
|
+
postProcessUrl: this.postProcessUrl,
|
|
107
|
+
sessionId: options?.sessionId,
|
|
108
|
+
topicId: options?.topicId,
|
|
109
|
+
});
|
|
104
110
|
};
|
|
105
111
|
|
|
106
112
|
updateMessageTTS: IMessageService['updateMessageTTS'] = async (id, tts) => {
|
|
@@ -81,8 +81,13 @@ export class ServerService implements IMessageService {
|
|
|
81
81
|
return lambdaClient.message.updateMessagePlugin.mutate({ id, value: { arguments: args } });
|
|
82
82
|
};
|
|
83
83
|
|
|
84
|
-
updateMessage: IMessageService['updateMessage'] = async (id, value) => {
|
|
85
|
-
return lambdaClient.message.update.mutate({
|
|
84
|
+
updateMessage: IMessageService['updateMessage'] = async (id, value, options) => {
|
|
85
|
+
return lambdaClient.message.update.mutate({
|
|
86
|
+
id,
|
|
87
|
+
sessionId: options?.sessionId,
|
|
88
|
+
topicId: options?.topicId,
|
|
89
|
+
value,
|
|
90
|
+
});
|
|
86
91
|
};
|
|
87
92
|
|
|
88
93
|
updateMessageTranslate: IMessageService['updateMessageTranslate'] = async (id, translate) => {
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
UIChatMessage,
|
|
11
11
|
UpdateMessageParams,
|
|
12
12
|
UpdateMessageRAGParams,
|
|
13
|
+
UpdateMessageResult,
|
|
13
14
|
} from '@lobechat/types';
|
|
14
15
|
import type { HeatmapsProps } from '@lobehub/charts';
|
|
15
16
|
|
|
@@ -37,7 +38,11 @@ export interface IMessageService {
|
|
|
37
38
|
rankModels(): Promise<ModelRankItem[]>;
|
|
38
39
|
getHeatmaps(): Promise<HeatmapsProps['data']>;
|
|
39
40
|
updateMessageError(id: string, error: ChatMessageError): Promise<any>;
|
|
40
|
-
updateMessage(
|
|
41
|
+
updateMessage(
|
|
42
|
+
id: string,
|
|
43
|
+
message: Partial<UpdateMessageParams>,
|
|
44
|
+
options?: { sessionId?: string | null; topicId?: string | null },
|
|
45
|
+
): Promise<UpdateMessageResult>;
|
|
41
46
|
updateMessageTTS(id: string, tts: Partial<ChatTTS> | false): Promise<any>;
|
|
42
47
|
updateMessageTranslate(id: string, translate: Partial<ChatTranslate> | false): Promise<any>;
|
|
43
48
|
updateMessagePluginState(id: string, value: Record<string, any>): Promise<any>;
|
|
@@ -64,6 +64,105 @@ describe('chatHelpers', () => {
|
|
|
64
64
|
const message = chatHelpers.getMessageById([], '1');
|
|
65
65
|
expect(message).toBeUndefined();
|
|
66
66
|
});
|
|
67
|
+
|
|
68
|
+
it('finds a block within a group message', () => {
|
|
69
|
+
const messagesWithGroup = [
|
|
70
|
+
{ id: '1', content: 'Hello' },
|
|
71
|
+
{
|
|
72
|
+
id: 'group1',
|
|
73
|
+
role: 'group',
|
|
74
|
+
content: '',
|
|
75
|
+
children: [
|
|
76
|
+
{ id: 'block1', content: 'First block' },
|
|
77
|
+
{ id: 'block2', content: 'Second block' },
|
|
78
|
+
],
|
|
79
|
+
},
|
|
80
|
+
] as UIChatMessage[];
|
|
81
|
+
|
|
82
|
+
const block = chatHelpers.getMessageById(messagesWithGroup, 'block1');
|
|
83
|
+
expect(block).toBeDefined();
|
|
84
|
+
expect(block?.id).toBe('block1');
|
|
85
|
+
expect(block?.content).toBe('First block');
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('returns block with parentId set to group message id', () => {
|
|
89
|
+
const messagesWithGroup = [
|
|
90
|
+
{
|
|
91
|
+
id: 'group1',
|
|
92
|
+
role: 'group',
|
|
93
|
+
content: '',
|
|
94
|
+
children: [{ id: 'block1', content: 'Block content' }],
|
|
95
|
+
},
|
|
96
|
+
] as UIChatMessage[];
|
|
97
|
+
|
|
98
|
+
const block = chatHelpers.getMessageById(messagesWithGroup, 'block1');
|
|
99
|
+
expect(block).toBeDefined();
|
|
100
|
+
expect(block?.parentId).toBe('group1');
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it('searches across multiple group messages', () => {
|
|
104
|
+
const messagesWithGroups = [
|
|
105
|
+
{
|
|
106
|
+
id: 'group1',
|
|
107
|
+
role: 'group',
|
|
108
|
+
content: '',
|
|
109
|
+
children: [{ id: 'block1', content: 'First group block' }],
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
id: 'group2',
|
|
113
|
+
role: 'group',
|
|
114
|
+
content: '',
|
|
115
|
+
children: [{ id: 'block2', content: 'Second group block' }],
|
|
116
|
+
},
|
|
117
|
+
] as UIChatMessage[];
|
|
118
|
+
|
|
119
|
+
const block = chatHelpers.getMessageById(messagesWithGroups, 'block2');
|
|
120
|
+
expect(block).toBeDefined();
|
|
121
|
+
expect(block?.id).toBe('block2');
|
|
122
|
+
expect(block?.parentId).toBe('group2');
|
|
123
|
+
expect(block?.content).toBe('Second group block');
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it('prioritizes top-level message over block with same id', () => {
|
|
127
|
+
const messagesWithConflict = [
|
|
128
|
+
{ id: 'duplicate', content: 'Top-level message', role: 'user' },
|
|
129
|
+
{
|
|
130
|
+
id: 'group1',
|
|
131
|
+
role: 'group',
|
|
132
|
+
content: '',
|
|
133
|
+
children: [{ id: 'duplicate', content: 'Block message' }],
|
|
134
|
+
},
|
|
135
|
+
] as UIChatMessage[];
|
|
136
|
+
|
|
137
|
+
const message = chatHelpers.getMessageById(messagesWithConflict, 'duplicate');
|
|
138
|
+
expect(message).toBeDefined();
|
|
139
|
+
expect(message?.content).toBe('Top-level message');
|
|
140
|
+
expect(message?.role).toBe('user');
|
|
141
|
+
expect(message?.parentId).toBeUndefined();
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it('returns undefined when block is not found in any group', () => {
|
|
145
|
+
const messagesWithGroup = [
|
|
146
|
+
{
|
|
147
|
+
id: 'group1',
|
|
148
|
+
role: 'group',
|
|
149
|
+
content: '',
|
|
150
|
+
children: [{ id: 'block1', content: 'Block content' }],
|
|
151
|
+
},
|
|
152
|
+
] as UIChatMessage[];
|
|
153
|
+
|
|
154
|
+
const block = chatHelpers.getMessageById(messagesWithGroup, 'nonexistent');
|
|
155
|
+
expect(block).toBeUndefined();
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it('handles group message without children', () => {
|
|
159
|
+
const messagesWithEmptyGroup = [
|
|
160
|
+
{ id: 'group1', role: 'group', content: '' },
|
|
161
|
+
] as UIChatMessage[];
|
|
162
|
+
|
|
163
|
+
const block = chatHelpers.getMessageById(messagesWithEmptyGroup, 'block1');
|
|
164
|
+
expect(block).toBeUndefined();
|
|
165
|
+
});
|
|
67
166
|
});
|
|
68
167
|
|
|
69
168
|
describe('getSlicedMessages', () => {
|
|
@@ -5,8 +5,27 @@ import { encodeAsync } from '@/utils/tokenizer';
|
|
|
5
5
|
export const getMessagesTokenCount = async (messages: OpenAIChatMessage[]) =>
|
|
6
6
|
encodeAsync(messages.map((m) => m.content).join(''));
|
|
7
7
|
|
|
8
|
-
export const getMessageById = (
|
|
9
|
-
messages
|
|
8
|
+
export const getMessageById = (
|
|
9
|
+
messages: UIChatMessage[],
|
|
10
|
+
id: string,
|
|
11
|
+
): UIChatMessage | undefined => {
|
|
12
|
+
// First try to find in top-level messages
|
|
13
|
+
const directMatch = messages.find((m) => m.id === id);
|
|
14
|
+
if (directMatch) return directMatch;
|
|
15
|
+
|
|
16
|
+
// If not found, search in group message children (blocks)
|
|
17
|
+
for (const message of messages) {
|
|
18
|
+
if (message.role === 'group' && message.children) {
|
|
19
|
+
const blockMatch = message.children.find((block) => block.id === id);
|
|
20
|
+
if (blockMatch) {
|
|
21
|
+
// Return the block with parentId set to group message ID
|
|
22
|
+
return { ...blockMatch, parentId: message.id } as UIChatMessage;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return undefined;
|
|
28
|
+
};
|
|
10
29
|
|
|
11
30
|
const getSlicedMessages = (
|
|
12
31
|
messages: UIChatMessage[],
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { aiChatSelectors } from './slices/aiChat/selectors';
|
|
2
2
|
export { chatToolSelectors } from './slices/builtinTool/selectors';
|
|
3
|
-
export
|
|
3
|
+
export * from './slices/message/selectors';
|
|
4
4
|
export * from './slices/portal/selectors';
|
|
5
5
|
export { threadSelectors } from './slices/thread/selectors';
|
|
6
6
|
export { topicSelectors } from './slices/topic/selectors';
|