@lobehub/chat 0.161.23 → 0.161.25
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/package.json +2 -2
- package/src/app/api/chat/[provider]/route.test.ts +2 -1
- package/src/app/api/chat/[provider]/route.ts +7 -8
- package/src/const/auth.ts +6 -0
- package/src/libs/agent-runtime/types/chat.ts +4 -0
- package/src/libs/agent-runtime/utils/openaiCompatibleFactory/index.test.ts +10 -7
- package/src/libs/agent-runtime/utils/openaiCompatibleFactory/index.ts +8 -5
- package/src/libs/trpc/client.ts +2 -1
- package/src/services/_auth.ts +7 -2
- package/src/store/chat/slices/topic/action.test.ts +12 -6
- package/src/store/chat/slices/topic/action.ts +17 -18
- package/src/store/chat/slices/topic/initialState.ts +0 -2
- package/src/store/chat/slices/topic/selectors.test.ts +1 -1
- package/src/store/chat/slices/topic/selectors.ts +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,56 @@
|
|
|
2
2
|
|
|
3
3
|
# Changelog
|
|
4
4
|
|
|
5
|
+
### [Version 0.161.25](https://github.com/lobehub/lobe-chat/compare/v0.161.24...v0.161.25)
|
|
6
|
+
|
|
7
|
+
<sup>Released on **2024-05-27**</sup>
|
|
8
|
+
|
|
9
|
+
#### 🐛 Bug Fixes
|
|
10
|
+
|
|
11
|
+
- **misc**: Fix trpc/edge path error when setting `NEXT_PUBLIC_BASE_PATH`.
|
|
12
|
+
|
|
13
|
+
<br/>
|
|
14
|
+
|
|
15
|
+
<details>
|
|
16
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
17
|
+
|
|
18
|
+
#### What's fixed
|
|
19
|
+
|
|
20
|
+
- **misc**: Fix trpc/edge path error when setting `NEXT_PUBLIC_BASE_PATH`, closes [#2681](https://github.com/lobehub/lobe-chat/issues/2681) ([622b390](https://github.com/lobehub/lobe-chat/commit/622b390))
|
|
21
|
+
|
|
22
|
+
</details>
|
|
23
|
+
|
|
24
|
+
<div align="right">
|
|
25
|
+
|
|
26
|
+
[](#readme-top)
|
|
27
|
+
|
|
28
|
+
</div>
|
|
29
|
+
|
|
30
|
+
### [Version 0.161.24](https://github.com/lobehub/lobe-chat/compare/v0.161.23...v0.161.24)
|
|
31
|
+
|
|
32
|
+
<sup>Released on **2024-05-27**</sup>
|
|
33
|
+
|
|
34
|
+
#### 🐛 Bug Fixes
|
|
35
|
+
|
|
36
|
+
- **misc**: Fix the missing user id in chat compeletition and fix remove unstarred topic not working.
|
|
37
|
+
|
|
38
|
+
<br/>
|
|
39
|
+
|
|
40
|
+
<details>
|
|
41
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
42
|
+
|
|
43
|
+
#### What's fixed
|
|
44
|
+
|
|
45
|
+
- **misc**: Fix the missing user id in chat compeletition and fix remove unstarred topic not working, closes [#2677](https://github.com/lobehub/lobe-chat/issues/2677) ([c9fb2de](https://github.com/lobehub/lobe-chat/commit/c9fb2de))
|
|
46
|
+
|
|
47
|
+
</details>
|
|
48
|
+
|
|
49
|
+
<div align="right">
|
|
50
|
+
|
|
51
|
+
[](#readme-top)
|
|
52
|
+
|
|
53
|
+
</div>
|
|
54
|
+
|
|
5
55
|
### [Version 0.161.23](https://github.com/lobehub/lobe-chat/compare/v0.161.22...v0.161.23)
|
|
6
56
|
|
|
7
57
|
<sup>Released on **2024-05-27**</sup>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lobehub/chat",
|
|
3
|
-
"version": "0.161.
|
|
3
|
+
"version": "0.161.25",
|
|
4
4
|
"description": "Lobe Chat - an open-source, high-performance chatbot 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",
|
|
@@ -82,7 +82,7 @@
|
|
|
82
82
|
},
|
|
83
83
|
"dependencies": {
|
|
84
84
|
"@ant-design/icons": "^5.3.7",
|
|
85
|
-
"@anthropic-ai/sdk": "^0.
|
|
85
|
+
"@anthropic-ai/sdk": "^0.21.0",
|
|
86
86
|
"@auth/core": "0.28.0",
|
|
87
87
|
"@aws-sdk/client-bedrock-runtime": "^3.583.0",
|
|
88
88
|
"@aws-sdk/client-s3": "^3.583.0",
|
|
@@ -159,6 +159,7 @@ describe('POST handler', () => {
|
|
|
159
159
|
accessCode: 'test-access-code',
|
|
160
160
|
apiKey: 'test-api-key',
|
|
161
161
|
azureApiVersion: 'v1',
|
|
162
|
+
userId: 'abc',
|
|
162
163
|
});
|
|
163
164
|
|
|
164
165
|
const mockParams = { provider: 'test-provider' };
|
|
@@ -176,7 +177,7 @@ describe('POST handler', () => {
|
|
|
176
177
|
const response = await POST(request as unknown as Request, { params: mockParams });
|
|
177
178
|
|
|
178
179
|
expect(response).toEqual(mockChatResponse);
|
|
179
|
-
expect(AgentRuntime.prototype.chat).toHaveBeenCalledWith(mockChatPayload);
|
|
180
|
+
expect(AgentRuntime.prototype.chat).toHaveBeenCalledWith(mockChatPayload, { user: 'abc' });
|
|
180
181
|
});
|
|
181
182
|
|
|
182
183
|
it('should return an error response when chat completion fails', async () => {
|
|
@@ -25,17 +25,16 @@ export const POST = checkAuth(async (req: Request, { params, jwtPayload }) => {
|
|
|
25
25
|
|
|
26
26
|
const tracePayload = getTracePayload(req);
|
|
27
27
|
|
|
28
|
+
let traceOptions = {};
|
|
28
29
|
// If user enable trace
|
|
29
30
|
if (tracePayload?.enabled) {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
trace: tracePayload,
|
|
35
|
-
}),
|
|
36
|
-
);
|
|
31
|
+
traceOptions = createTraceOptions(data, {
|
|
32
|
+
provider,
|
|
33
|
+
trace: tracePayload,
|
|
34
|
+
});
|
|
37
35
|
}
|
|
38
|
-
|
|
36
|
+
|
|
37
|
+
return await agentRuntime.chat(data, { user: jwtPayload.userId, ...traceOptions });
|
|
39
38
|
} catch (e) {
|
|
40
39
|
const {
|
|
41
40
|
errorType = ChatErrorType.InternalServerError,
|
package/src/const/auth.ts
CHANGED
|
@@ -35,5 +35,11 @@ export interface JWTPayload {
|
|
|
35
35
|
awsAccessKeyId?: string;
|
|
36
36
|
awsRegion?: string;
|
|
37
37
|
awsSecretAccessKey?: string;
|
|
38
|
+
/**
|
|
39
|
+
* user id
|
|
40
|
+
* in client db mode it's a uuid
|
|
41
|
+
* in server db mode it's a user id
|
|
42
|
+
*/
|
|
43
|
+
userId?: string;
|
|
38
44
|
}
|
|
39
45
|
/* eslint-enable */
|
|
@@ -95,6 +95,10 @@ export interface ChatCompetitionOptions {
|
|
|
95
95
|
callback?: ChatStreamCallbacks;
|
|
96
96
|
headers?: Record<string, any>;
|
|
97
97
|
signal?: AbortSignal;
|
|
98
|
+
/**
|
|
99
|
+
* userId for the chat completion
|
|
100
|
+
*/
|
|
101
|
+
user?: string;
|
|
98
102
|
}
|
|
99
103
|
|
|
100
104
|
export interface ChatCompletionFunctions {
|
|
@@ -195,18 +195,21 @@ describe('LobeOpenAICompatibleFactory', () => {
|
|
|
195
195
|
});
|
|
196
196
|
|
|
197
197
|
describe('handlePayload option', () => {
|
|
198
|
-
it('should
|
|
198
|
+
it('should add user in payload correctly', async () => {
|
|
199
199
|
const mockCreateMethod = vi.spyOn(instance['client'].chat.completions, 'create');
|
|
200
200
|
|
|
201
|
-
await instance.chat(
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
201
|
+
await instance.chat(
|
|
202
|
+
{
|
|
203
|
+
messages: [{ content: 'Hello', role: 'user' }],
|
|
204
|
+
model: 'mistralai/mistral-7b-instruct:free',
|
|
205
|
+
temperature: 0,
|
|
206
|
+
},
|
|
207
|
+
{ user: 'abc' },
|
|
208
|
+
);
|
|
206
209
|
|
|
207
210
|
expect(mockCreateMethod).toHaveBeenCalledWith(
|
|
208
211
|
expect.objectContaining({
|
|
209
|
-
|
|
212
|
+
user: 'abc',
|
|
210
213
|
}),
|
|
211
214
|
expect.anything(),
|
|
212
215
|
);
|
|
@@ -79,11 +79,14 @@ export const LobeOpenAICompatibleFactory = ({
|
|
|
79
79
|
stream: payload.stream ?? true,
|
|
80
80
|
} as OpenAI.ChatCompletionCreateParamsStreaming);
|
|
81
81
|
|
|
82
|
-
const response = await this.client.chat.completions.create(
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
82
|
+
const response = await this.client.chat.completions.create(
|
|
83
|
+
{ ...postPayload, user: options?.user },
|
|
84
|
+
{
|
|
85
|
+
// https://github.com/lobehub/lobe-chat/pull/318
|
|
86
|
+
headers: { Accept: '*/*' },
|
|
87
|
+
signal: options?.signal,
|
|
88
|
+
},
|
|
89
|
+
);
|
|
87
90
|
|
|
88
91
|
if (postPayload.stream) {
|
|
89
92
|
const [prod, useForDebug] = response.tee();
|
package/src/libs/trpc/client.ts
CHANGED
|
@@ -3,13 +3,14 @@ import superjson from 'superjson';
|
|
|
3
3
|
|
|
4
4
|
import type { EdgeRouter } from '@/server/routers';
|
|
5
5
|
import { createHeaderWithAuth } from '@/services/_auth';
|
|
6
|
+
import { withBasePath } from '@/utils/basePath';
|
|
6
7
|
|
|
7
8
|
export const edgeClient = createTRPCClient<EdgeRouter>({
|
|
8
9
|
links: [
|
|
9
10
|
httpBatchLink({
|
|
10
11
|
headers: async () => createHeaderWithAuth(),
|
|
11
12
|
transformer: superjson,
|
|
12
|
-
url: '/trpc/edge',
|
|
13
|
+
url: withBasePath('/trpc/edge'),
|
|
13
14
|
}),
|
|
14
15
|
],
|
|
15
16
|
});
|
package/src/services/_auth.ts
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import { JWTPayload, LOBE_CHAT_AUTH_HEADER } from '@/const/auth';
|
|
2
2
|
import { ModelProvider } from '@/libs/agent-runtime';
|
|
3
3
|
import { useUserStore } from '@/store/user';
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
keyVaultsConfigSelectors,
|
|
6
|
+
settingsSelectors,
|
|
7
|
+
userProfileSelectors,
|
|
8
|
+
} from '@/store/user/selectors';
|
|
5
9
|
import { GlobalLLMProviderKey } from '@/types/user/settings';
|
|
6
10
|
import { createJWT } from '@/utils/jwt';
|
|
7
11
|
|
|
@@ -48,8 +52,9 @@ export const getProviderAuthPayload = (provider: string) => {
|
|
|
48
52
|
|
|
49
53
|
const createAuthTokenWithPayload = async (payload = {}) => {
|
|
50
54
|
const accessCode = settingsSelectors.password(useUserStore.getState());
|
|
55
|
+
const userId = userProfileSelectors.userId(useUserStore.getState());
|
|
51
56
|
|
|
52
|
-
return await createJWT<JWTPayload>({ accessCode, ...payload });
|
|
57
|
+
return await createJWT<JWTPayload>({ accessCode, userId, ...payload });
|
|
53
58
|
};
|
|
54
59
|
|
|
55
60
|
interface AuthParams {
|
|
@@ -384,11 +384,14 @@ describe('topic action', () => {
|
|
|
384
384
|
// Set up mock state with unstarred topics
|
|
385
385
|
await act(async () => {
|
|
386
386
|
useChatStore.setState({
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
387
|
+
activeId: 'abc',
|
|
388
|
+
topicMaps: {
|
|
389
|
+
abc: [
|
|
390
|
+
{ id: 'topic-1', favorite: false },
|
|
391
|
+
{ id: 'topic-2', favorite: true },
|
|
392
|
+
{ id: 'topic-3', favorite: false },
|
|
393
|
+
] as ChatTopic[],
|
|
394
|
+
},
|
|
392
395
|
});
|
|
393
396
|
});
|
|
394
397
|
const refreshTopicSpy = vi.spyOn(result.current, 'refreshTopic');
|
|
@@ -431,7 +434,10 @@ describe('topic action', () => {
|
|
|
431
434
|
});
|
|
432
435
|
|
|
433
436
|
// Mock the `updateTopicTitleInSummary` and `refreshTopic` for spying
|
|
434
|
-
const updateTopicTitleInSummarySpy = vi.spyOn(
|
|
437
|
+
const updateTopicTitleInSummarySpy = vi.spyOn(
|
|
438
|
+
result.current,
|
|
439
|
+
'internal_updateTopicTitleInSummary',
|
|
440
|
+
);
|
|
435
441
|
const refreshTopicSpy = vi.spyOn(result.current, 'refreshTopic');
|
|
436
442
|
|
|
437
443
|
// Mock the `chatService.fetchPresetTaskResult` to simulate the AI response
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
// DON'T REMOVE THE FIRST LINE
|
|
4
4
|
import isEqual from 'fast-deep-equal';
|
|
5
5
|
import { t } from 'i18next';
|
|
6
|
-
import { produce } from 'immer';
|
|
7
6
|
import useSWR, { SWRResponse, mutate } from 'swr';
|
|
8
7
|
import { StateCreator } from 'zustand/vanilla';
|
|
9
8
|
|
|
@@ -37,7 +36,7 @@ export interface ChatTopicAction {
|
|
|
37
36
|
removeAllTopics: () => Promise<void>;
|
|
38
37
|
removeSessionTopics: () => Promise<void>;
|
|
39
38
|
removeTopic: (id: string) => Promise<void>;
|
|
40
|
-
removeUnstarredTopic: () => void
|
|
39
|
+
removeUnstarredTopic: () => Promise<void>;
|
|
41
40
|
saveToTopic: () => Promise<string | undefined>;
|
|
42
41
|
createTopic: () => Promise<string | undefined>;
|
|
43
42
|
|
|
@@ -45,11 +44,11 @@ export interface ChatTopicAction {
|
|
|
45
44
|
duplicateTopic: (id: string) => Promise<void>;
|
|
46
45
|
summaryTopicTitle: (topicId: string, messages: ChatMessage[]) => Promise<void>;
|
|
47
46
|
switchTopic: (id?: string, skipRefreshMessage?: boolean) => Promise<void>;
|
|
48
|
-
updateTopicTitleInSummary: (id: string, title: string) => void;
|
|
49
47
|
updateTopicTitle: (id: string, title: string) => Promise<void>;
|
|
50
48
|
useFetchTopics: (sessionId: string) => SWRResponse<ChatTopic[]>;
|
|
51
49
|
useSearchTopics: (keywords?: string, sessionId?: string) => SWRResponse<ChatTopic[]>;
|
|
52
50
|
|
|
51
|
+
internal_updateTopicTitleInSummary: (id: string, title: string) => void;
|
|
53
52
|
internal_updateTopicLoading: (id: string, loading: boolean) => void;
|
|
54
53
|
internal_createTopic: (params: CreateTopicParams) => Promise<string>;
|
|
55
54
|
internal_updateTopic: (id: string, data: Partial<ChatTopic>) => Promise<void>;
|
|
@@ -133,18 +132,18 @@ export const chatTopic: StateCreator<
|
|
|
133
132
|
},
|
|
134
133
|
// update
|
|
135
134
|
summaryTopicTitle: async (topicId, messages) => {
|
|
136
|
-
const {
|
|
135
|
+
const { internal_updateTopicTitleInSummary, internal_updateTopicLoading } = get();
|
|
137
136
|
const topic = topicSelectors.getTopicById(topicId)(get());
|
|
138
137
|
if (!topic) return;
|
|
139
138
|
|
|
140
|
-
|
|
139
|
+
internal_updateTopicTitleInSummary(topicId, LOADING_FLAT);
|
|
141
140
|
|
|
142
141
|
let output = '';
|
|
143
142
|
|
|
144
143
|
// 自动总结话题标题
|
|
145
144
|
await chatService.fetchPresetTaskResult({
|
|
146
145
|
onError: () => {
|
|
147
|
-
|
|
146
|
+
internal_updateTopicTitleInSummary(topicId, topic.title);
|
|
148
147
|
},
|
|
149
148
|
onFinish: async (text) => {
|
|
150
149
|
await get().internal_updateTopic(topicId, { title: text });
|
|
@@ -159,7 +158,7 @@ export const chatTopic: StateCreator<
|
|
|
159
158
|
}
|
|
160
159
|
}
|
|
161
160
|
|
|
162
|
-
|
|
161
|
+
internal_updateTopicTitleInSummary(topicId, output);
|
|
163
162
|
},
|
|
164
163
|
params: await chainSummaryTitle(messages),
|
|
165
164
|
trace: get().getCurrentTracePayload({ traceName: TraceNameMap.SummaryTopicTitle, topicId }),
|
|
@@ -264,15 +263,11 @@ export const chatTopic: StateCreator<
|
|
|
264
263
|
},
|
|
265
264
|
|
|
266
265
|
// Internal process method of the topics
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
topic.title = title;
|
|
273
|
-
});
|
|
274
|
-
|
|
275
|
-
set({ topics }, false, n(`updateTopicTitleInSummary`, { id, title }));
|
|
266
|
+
internal_updateTopicTitleInSummary: (id, title) => {
|
|
267
|
+
get().internal_dispatchTopic(
|
|
268
|
+
{ type: 'updateTopic', id, value: { title } },
|
|
269
|
+
'updateTopicTitleInSummary',
|
|
270
|
+
);
|
|
276
271
|
},
|
|
277
272
|
refreshTopic: async () => {
|
|
278
273
|
return mutate([SWR_USE_FETCH_TOPIC, get().activeId]);
|
|
@@ -317,8 +312,12 @@ export const chatTopic: StateCreator<
|
|
|
317
312
|
},
|
|
318
313
|
|
|
319
314
|
internal_dispatchTopic: (payload, action) => {
|
|
320
|
-
const nextTopics = topicReducer(get()
|
|
315
|
+
const nextTopics = topicReducer(topicSelectors.currentTopics(get()), payload);
|
|
316
|
+
const nextMap = { ...get().topicMaps, [get().activeId]: nextTopics };
|
|
317
|
+
|
|
318
|
+
// no need to update map if is the same
|
|
319
|
+
if (isEqual(nextMap, get().topicMaps)) return;
|
|
321
320
|
|
|
322
|
-
set({
|
|
321
|
+
set({ topicMaps: nextMap }, false, action ?? n(`dispatchTopic/${payload.type}`));
|
|
323
322
|
},
|
|
324
323
|
});
|
|
@@ -9,7 +9,6 @@ export interface ChatTopicState {
|
|
|
9
9
|
topicMaps: Record<string, ChatTopic[]>;
|
|
10
10
|
topicRenamingId?: string;
|
|
11
11
|
topicSearchKeywords: string;
|
|
12
|
-
topics: ChatTopic[];
|
|
13
12
|
/**
|
|
14
13
|
* whether topics have fetched
|
|
15
14
|
*/
|
|
@@ -23,6 +22,5 @@ export const initialTopicState: ChatTopicState = {
|
|
|
23
22
|
topicLoadingIds: [],
|
|
24
23
|
topicMaps: {},
|
|
25
24
|
topicSearchKeywords: '',
|
|
26
|
-
topics: [],
|
|
27
25
|
topicsInit: false,
|
|
28
26
|
};
|
|
@@ -58,7 +58,7 @@ describe('topicSelectors', () => {
|
|
|
58
58
|
|
|
59
59
|
describe('currentUnFavTopics', () => {
|
|
60
60
|
it('should return all unfavorited topics', () => {
|
|
61
|
-
const state = merge(initialStore, {
|
|
61
|
+
const state = merge(initialStore, { topicMaps, activeId: 'test' });
|
|
62
62
|
const topics = topicSelectors.currentUnFavTopics(state);
|
|
63
63
|
expect(topics).toEqual([topicMaps.test[1]]);
|
|
64
64
|
});
|
|
@@ -12,7 +12,8 @@ const searchTopics = (s: ChatStore): ChatTopic[] => s.searchTopics;
|
|
|
12
12
|
const displayTopics = (s: ChatStore): ChatTopic[] | undefined =>
|
|
13
13
|
s.isSearchingTopic ? searchTopics(s) : currentTopics(s);
|
|
14
14
|
|
|
15
|
-
const currentUnFavTopics = (s: ChatStore): ChatTopic[] =>
|
|
15
|
+
const currentUnFavTopics = (s: ChatStore): ChatTopic[] =>
|
|
16
|
+
currentTopics(s)?.filter((s) => !s.favorite) || [];
|
|
16
17
|
|
|
17
18
|
const currentTopicLength = (s: ChatStore): number => currentTopics(s)?.length || 0;
|
|
18
19
|
|