@lobehub/lobehub 2.0.0-next.40 → 2.0.0-next.42
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/locales/ar/chat.json +1 -0
- package/locales/bg-BG/chat.json +1 -0
- package/locales/de-DE/chat.json +1 -0
- package/locales/en-US/chat.json +1 -0
- package/locales/es-ES/chat.json +1 -0
- package/locales/fa-IR/chat.json +1 -0
- package/locales/fr-FR/chat.json +1 -0
- package/locales/it-IT/chat.json +1 -0
- package/locales/ja-JP/chat.json +1 -0
- package/locales/ko-KR/chat.json +1 -0
- package/locales/nl-NL/chat.json +1 -0
- package/locales/pl-PL/chat.json +1 -0
- package/locales/pt-BR/chat.json +1 -0
- package/locales/ru-RU/chat.json +1 -0
- package/locales/tr-TR/chat.json +1 -0
- package/locales/vi-VN/chat.json +1 -0
- package/locales/zh-CN/chat.json +1 -0
- package/locales/zh-TW/chat.json +1 -0
- package/package.json +1 -1
- package/packages/database/src/models/__tests__/messages/message.create.test.ts +549 -0
- package/packages/database/src/models/__tests__/messages/message.delete.test.ts +481 -0
- package/packages/database/src/models/__tests__/messages/message.query.test.ts +1187 -0
- package/packages/database/src/models/__tests__/messages/message.stats.test.ts +633 -0
- package/packages/database/src/models/__tests__/messages/message.update.test.ts +757 -0
- package/packages/database/src/models/message.ts +5 -55
- package/packages/utils/src/clientIP.ts +6 -6
- package/packages/utils/src/compressImage.ts +3 -3
- package/packages/utils/src/fetch/fetchSSE.ts +15 -15
- package/packages/utils/src/format.ts +2 -2
- package/packages/utils/src/merge.ts +3 -3
- package/packages/utils/src/parseModels.ts +3 -3
- package/packages/utils/src/sanitizeUTF8.ts +4 -4
- package/packages/utils/src/toolManifest.ts +4 -4
- package/packages/utils/src/trace.test.ts +359 -0
- package/packages/utils/src/uriParser.ts +4 -4
- package/src/features/ChatItem/components/Title.tsx +20 -16
- package/src/features/Conversation/Messages/Assistant/index.tsx +3 -2
- package/src/features/Conversation/Messages/Group/index.tsx +10 -3
- package/src/server/services/message/index.ts +14 -4
- package/src/store/chat/slices/aiChat/actions/conversationLifecycle.ts +8 -2
- package/src/store/chat/slices/aiChat/actions/streamingExecutor.ts +1 -4
- package/src/store/chat/slices/message/actions/optimisticUpdate.ts +1 -1
- package/packages/database/src/models/__tests__/message.test.ts +0 -2632
|
@@ -14,7 +14,6 @@ import {
|
|
|
14
14
|
UIChatMessage,
|
|
15
15
|
UpdateMessageParams,
|
|
16
16
|
UpdateMessageRAGParams,
|
|
17
|
-
UpdateMessageResult,
|
|
18
17
|
} from '@lobechat/types';
|
|
19
18
|
import type { HeatmapsProps } from '@lobehub/charts';
|
|
20
19
|
import dayjs from 'dayjs';
|
|
@@ -213,7 +212,7 @@ export class MessageModel {
|
|
|
213
212
|
.from(messageQueries)
|
|
214
213
|
.where(inArray(messageQueries.messageId, messageIds));
|
|
215
214
|
|
|
216
|
-
|
|
215
|
+
return result.map(
|
|
217
216
|
({ model, provider, translate, ttsId, ttsFile, ttsContentMd5, ttsVoice, ...item }) => {
|
|
218
217
|
const messageQuery = messageQueriesList.find((relation) => relation.messageId === item.id);
|
|
219
218
|
return {
|
|
@@ -222,7 +221,7 @@ export class MessageModel {
|
|
|
222
221
|
.filter((relation) => relation.messageId === item.id)
|
|
223
222
|
.map((c) => ({
|
|
224
223
|
...c,
|
|
225
|
-
similarity: Number(c.similarity)
|
|
224
|
+
similarity: c.similarity === null ? undefined : Number(c.similarity),
|
|
226
225
|
})),
|
|
227
226
|
|
|
228
227
|
extra: {
|
|
@@ -267,8 +266,6 @@ export class MessageModel {
|
|
|
267
266
|
} as unknown as UIChatMessage;
|
|
268
267
|
},
|
|
269
268
|
);
|
|
270
|
-
|
|
271
|
-
return mappedMessages;
|
|
272
269
|
};
|
|
273
270
|
|
|
274
271
|
findById = async (id: string) => {
|
|
@@ -424,7 +421,7 @@ export class MessageModel {
|
|
|
424
421
|
for (const item of result) {
|
|
425
422
|
if (item?.date) {
|
|
426
423
|
const dateStr = dayjs(item.date as string).format('YYYY-MM-DD');
|
|
427
|
-
dateCountMap.set(dateStr,
|
|
424
|
+
dateCountMap.set(dateStr, item.count);
|
|
428
425
|
}
|
|
429
426
|
}
|
|
430
427
|
|
|
@@ -550,13 +547,7 @@ export class MessageModel {
|
|
|
550
547
|
update = async (
|
|
551
548
|
id: string,
|
|
552
549
|
{ imageList, ...message }: Partial<UpdateMessageParams>,
|
|
553
|
-
|
|
554
|
-
groupAssistantMessages?: boolean;
|
|
555
|
-
postProcessUrl?: (path: string | null, file: { fileType: string }) => Promise<string>;
|
|
556
|
-
sessionId?: string | null;
|
|
557
|
-
topicId?: string | null;
|
|
558
|
-
},
|
|
559
|
-
): Promise<UpdateMessageResult> => {
|
|
550
|
+
): Promise<{ success: boolean }> => {
|
|
560
551
|
try {
|
|
561
552
|
await this.db.transaction(async (trx) => {
|
|
562
553
|
// 1. insert message files
|
|
@@ -574,22 +565,6 @@ export class MessageModel {
|
|
|
574
565
|
.where(and(eq(messages.id, id), eq(messages.userId, this.userId)));
|
|
575
566
|
});
|
|
576
567
|
|
|
577
|
-
// if sessionId or topicId provided, return the updated message list
|
|
578
|
-
if (options?.sessionId !== undefined || options?.topicId !== undefined) {
|
|
579
|
-
const messageList = await this.query(
|
|
580
|
-
{
|
|
581
|
-
sessionId: options.sessionId,
|
|
582
|
-
topicId: options.topicId,
|
|
583
|
-
},
|
|
584
|
-
{
|
|
585
|
-
groupAssistantMessages: options.groupAssistantMessages ?? false,
|
|
586
|
-
postProcessUrl: options.postProcessUrl,
|
|
587
|
-
},
|
|
588
|
-
);
|
|
589
|
-
|
|
590
|
-
return { messages: messageList, success: true };
|
|
591
|
-
}
|
|
592
|
-
|
|
593
568
|
return { success: true };
|
|
594
569
|
} catch (error) {
|
|
595
570
|
console.error('Update message error:', error);
|
|
@@ -610,16 +585,7 @@ export class MessageModel {
|
|
|
610
585
|
.where(and(eq(messages.userId, this.userId), eq(messages.id, id)));
|
|
611
586
|
};
|
|
612
587
|
|
|
613
|
-
updatePluginState = async (
|
|
614
|
-
id: string,
|
|
615
|
-
state: Record<string, any>,
|
|
616
|
-
options?: {
|
|
617
|
-
groupAssistantMessages?: boolean;
|
|
618
|
-
postProcessUrl?: (path: string | null, file: { fileType: string }) => Promise<string>;
|
|
619
|
-
sessionId?: string | null;
|
|
620
|
-
topicId?: string | null;
|
|
621
|
-
},
|
|
622
|
-
): Promise<UpdateMessageResult> => {
|
|
588
|
+
updatePluginState = async (id: string, state: Record<string, any>): Promise<void> => {
|
|
623
589
|
const item = await this.db.query.messagePlugins.findFirst({
|
|
624
590
|
where: eq(messagePlugins.id, id),
|
|
625
591
|
});
|
|
@@ -629,22 +595,6 @@ export class MessageModel {
|
|
|
629
595
|
.update(messagePlugins)
|
|
630
596
|
.set({ state: merge(item.state || {}, state) })
|
|
631
597
|
.where(eq(messagePlugins.id, id));
|
|
632
|
-
|
|
633
|
-
// Return updated messages if sessionId or topicId is provided
|
|
634
|
-
if (options?.sessionId !== undefined || options?.topicId !== undefined) {
|
|
635
|
-
const messageList = await this.query(
|
|
636
|
-
{
|
|
637
|
-
sessionId: options.sessionId,
|
|
638
|
-
topicId: options.topicId,
|
|
639
|
-
},
|
|
640
|
-
{
|
|
641
|
-
groupAssistantMessages: options.groupAssistantMessages ?? false,
|
|
642
|
-
postProcessUrl: options.postProcessUrl,
|
|
643
|
-
},
|
|
644
|
-
);
|
|
645
|
-
return { messages: messageList, success: true };
|
|
646
|
-
}
|
|
647
|
-
return { success: true };
|
|
648
598
|
};
|
|
649
599
|
|
|
650
600
|
updateMessagePlugin = async (id: string, value: Partial<MessagePluginItem>) => {
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* @param headers HTTP
|
|
2
|
+
* Get client IP address
|
|
3
|
+
* @param headers HTTP request headers
|
|
4
4
|
*/
|
|
5
5
|
export const getClientIP = (headers: Headers): string => {
|
|
6
|
-
//
|
|
6
|
+
// Check various IP headers in priority order
|
|
7
7
|
const ipHeaders = [
|
|
8
8
|
'cf-connecting-ip', // Cloudflare
|
|
9
9
|
'x-real-ip', // Nginx proxy
|
|
10
|
-
'x-forwarded-for', //
|
|
10
|
+
'x-forwarded-for', // Standard proxy header
|
|
11
11
|
'x-client-ip', // Apache
|
|
12
12
|
'true-client-ip', // Akamai and Cloudflare
|
|
13
|
-
'x-cluster-client-ip', //
|
|
13
|
+
'x-cluster-client-ip', // Load balancer
|
|
14
14
|
'forwarded', // RFC 7239
|
|
15
15
|
'fastly-client-ip', // Fastly CDN
|
|
16
16
|
'x-forwarded', // General forward
|
|
@@ -21,7 +21,7 @@ export const getClientIP = (headers: Headers): string => {
|
|
|
21
21
|
const value = headers.get(header);
|
|
22
22
|
if (!value) continue;
|
|
23
23
|
|
|
24
|
-
//
|
|
24
|
+
// Handle cases where multiple IPs may be present (e.g., x-forwarded-for)
|
|
25
25
|
if (header.toLowerCase() === 'x-forwarded-for') {
|
|
26
26
|
const firstIP = value.split(',')[0].trim();
|
|
27
27
|
if (firstIP) return firstIP;
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
const compressImage = ({ img, type = 'image/webp' }: { img: HTMLImageElement; type?: string }) => {
|
|
2
|
-
//
|
|
2
|
+
// Set maximum width and height
|
|
3
3
|
const maxWidth = 2160;
|
|
4
4
|
const maxHeight = 2160;
|
|
5
5
|
let width = img.width;
|
|
6
6
|
let height = img.height;
|
|
7
7
|
|
|
8
8
|
if (width > height && width > maxWidth) {
|
|
9
|
-
//
|
|
9
|
+
// If image width is greater than height and exceeds maximum width limit
|
|
10
10
|
width = maxWidth;
|
|
11
11
|
height = Math.round((maxWidth / img.width) * img.height);
|
|
12
12
|
} else if (height > width && height > maxHeight) {
|
|
13
|
-
//
|
|
13
|
+
// If image height is greater than width and exceeds maximum height limit
|
|
14
14
|
height = maxHeight;
|
|
15
15
|
width = Math.round((maxHeight / img.height) * img.width);
|
|
16
16
|
}
|
|
@@ -91,7 +91,7 @@ export interface FetchSSEOptions {
|
|
|
91
91
|
responseAnimation?: ResponseAnimation;
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
-
const START_ANIMATION_SPEED = 10; //
|
|
94
|
+
const START_ANIMATION_SPEED = 10; // Default starting speed
|
|
95
95
|
|
|
96
96
|
const createSmoothMessage = (params: {
|
|
97
97
|
onTextUpdate: (delta: string, text: string) => void;
|
|
@@ -106,7 +106,7 @@ const createSmoothMessage = (params: {
|
|
|
106
106
|
let lastFrameTime = 0;
|
|
107
107
|
let accumulatedTime = 0;
|
|
108
108
|
let currentSpeed = startSpeed;
|
|
109
|
-
let lastQueueLength = 0; //
|
|
109
|
+
let lastQueueLength = 0; // Record the queue length from the previous frame
|
|
110
110
|
|
|
111
111
|
const stopAnimation = () => {
|
|
112
112
|
isAnimationActive = false;
|
|
@@ -127,7 +127,7 @@ const createSmoothMessage = (params: {
|
|
|
127
127
|
lastFrameTime = performance.now();
|
|
128
128
|
accumulatedTime = 0;
|
|
129
129
|
currentSpeed = speed;
|
|
130
|
-
lastQueueLength = 0; //
|
|
130
|
+
lastQueueLength = 0; // Reset previous frame queue length
|
|
131
131
|
|
|
132
132
|
const updateText = (timestamp: number) => {
|
|
133
133
|
if (!isAnimationActive) {
|
|
@@ -144,9 +144,9 @@ const createSmoothMessage = (params: {
|
|
|
144
144
|
|
|
145
145
|
let charsToProcess = 0;
|
|
146
146
|
if (outputQueue.length > 0) {
|
|
147
|
-
//
|
|
147
|
+
// Smoother speed adjustment
|
|
148
148
|
const targetSpeed = Math.max(speed, outputQueue.length);
|
|
149
|
-
//
|
|
149
|
+
// Adjust speed change rate based on queue length changes
|
|
150
150
|
const speedChangeRate = Math.abs(outputQueue.length - lastQueueLength) * 0.0008 + 0.005;
|
|
151
151
|
currentSpeed += (targetSpeed - currentSpeed) * speedChangeRate;
|
|
152
152
|
|
|
@@ -157,7 +157,7 @@ const createSmoothMessage = (params: {
|
|
|
157
157
|
accumulatedTime -= (charsToProcess * 1000) / currentSpeed;
|
|
158
158
|
|
|
159
159
|
let actualChars = Math.min(charsToProcess, outputQueue.length);
|
|
160
|
-
// actualChars = Math.min(speed, actualChars); //
|
|
160
|
+
// actualChars = Math.min(speed, actualChars); // Speed upper limit
|
|
161
161
|
|
|
162
162
|
// if (actualChars * 2 < outputQueue.length && /[\dA-Za-z]/.test(outputQueue[actualChars])) {
|
|
163
163
|
// actualChars *= 2;
|
|
@@ -168,7 +168,7 @@ const createSmoothMessage = (params: {
|
|
|
168
168
|
params.onTextUpdate(charsToAdd, buffer);
|
|
169
169
|
}
|
|
170
170
|
|
|
171
|
-
lastQueueLength = outputQueue.length; //
|
|
171
|
+
lastQueueLength = outputQueue.length; // Update previous frame queue length
|
|
172
172
|
|
|
173
173
|
if (outputQueue.length > 0 && isAnimationActive) {
|
|
174
174
|
animationFrameId = requestAnimationFrame(updateText);
|
|
@@ -219,7 +219,7 @@ export const fetchSSE = async (url: string, options: RequestInit & FetchSSEOptio
|
|
|
219
219
|
const shouldSkipTextProcessing = text === 'none';
|
|
220
220
|
const textSmoothing = text === 'smooth';
|
|
221
221
|
|
|
222
|
-
//
|
|
222
|
+
// Add text buffer and timer related variables
|
|
223
223
|
let textBuffer = '';
|
|
224
224
|
let bufferTimer: ReturnType<typeof setTimeout> | null = null;
|
|
225
225
|
const BUFFER_INTERVAL = 300; // 300ms
|
|
@@ -254,7 +254,7 @@ export const fetchSSE = async (url: string, options: RequestInit & FetchSSEOptio
|
|
|
254
254
|
let thinkingBuffer = '';
|
|
255
255
|
let thinkingBufferTimer: ReturnType<typeof setTimeout> | null = null;
|
|
256
256
|
|
|
257
|
-
//
|
|
257
|
+
// Create a function to handle buffer flushing
|
|
258
258
|
const flushThinkingBuffer = () => {
|
|
259
259
|
if (thinkingBuffer) {
|
|
260
260
|
options.onMessageHandle?.({ text: thinkingBuffer, type: 'reasoning' });
|
|
@@ -349,10 +349,10 @@ export const fetchSSE = async (url: string, options: RequestInit & FetchSSEOptio
|
|
|
349
349
|
} else {
|
|
350
350
|
output += data;
|
|
351
351
|
|
|
352
|
-
//
|
|
352
|
+
// Use buffer mechanism
|
|
353
353
|
textBuffer += data;
|
|
354
354
|
|
|
355
|
-
//
|
|
355
|
+
// If timer not set yet, create one
|
|
356
356
|
if (!bufferTimer) {
|
|
357
357
|
bufferTimer = setTimeout(() => {
|
|
358
358
|
flushTextBuffer();
|
|
@@ -395,10 +395,10 @@ export const fetchSSE = async (url: string, options: RequestInit & FetchSSEOptio
|
|
|
395
395
|
} else {
|
|
396
396
|
thinking += data;
|
|
397
397
|
|
|
398
|
-
//
|
|
398
|
+
// Use buffer mechanism
|
|
399
399
|
thinkingBuffer += data;
|
|
400
400
|
|
|
401
|
-
//
|
|
401
|
+
// If timer not set yet, create one
|
|
402
402
|
if (!thinkingBufferTimer) {
|
|
403
403
|
thinkingBufferTimer = setTimeout(() => {
|
|
404
404
|
flushThinkingBuffer();
|
|
@@ -421,7 +421,7 @@ export const fetchSSE = async (url: string, options: RequestInit & FetchSSEOptio
|
|
|
421
421
|
},
|
|
422
422
|
onopen: async (res) => {
|
|
423
423
|
response = res.clone();
|
|
424
|
-
//
|
|
424
|
+
// If not ok, it means there is a request error
|
|
425
425
|
if (!response.ok) {
|
|
426
426
|
throw await getMessageError(res);
|
|
427
427
|
}
|
|
@@ -434,7 +434,7 @@ export const fetchSSE = async (url: string, options: RequestInit & FetchSSEOptio
|
|
|
434
434
|
if (response) {
|
|
435
435
|
textController.stopAnimation();
|
|
436
436
|
|
|
437
|
-
//
|
|
437
|
+
// Ensure all buffered data is processed
|
|
438
438
|
if (bufferTimer) {
|
|
439
439
|
clearTimeout(bufferTimer);
|
|
440
440
|
flushTextBuffer();
|
|
@@ -64,10 +64,10 @@ export const formatShortenNumber = (num: any) => {
|
|
|
64
64
|
if (!num && num !== 0) return '--';
|
|
65
65
|
if (!isNumber(num)) return num;
|
|
66
66
|
|
|
67
|
-
//
|
|
67
|
+
// Use Intl.NumberFormat to add thousand separators
|
|
68
68
|
const formattedWithComma = new Intl.NumberFormat('en-US').format(num);
|
|
69
69
|
|
|
70
|
-
//
|
|
70
|
+
// Format as K or M
|
|
71
71
|
if (num >= 1_000_000) {
|
|
72
72
|
return (num / 1_000_000).toFixed(1) + 'M';
|
|
73
73
|
} else if (num >= 10_000) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { merge as _merge, isEmpty, mergeWith } from 'lodash-es';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
4
|
+
* Merge objects, directly replace if it's an array
|
|
5
5
|
* @param target
|
|
6
6
|
* @param source
|
|
7
7
|
*/
|
|
@@ -24,7 +24,7 @@ export const mergeArrayById = <T extends MergeableItem>(defaultItems: T[], userI
|
|
|
24
24
|
// Create a map of default items for faster lookup
|
|
25
25
|
const defaultItemsMap = new Map(defaultItems.map((item) => [item.id, item]));
|
|
26
26
|
|
|
27
|
-
//
|
|
27
|
+
// Use Map to store merged results, so duplicate IDs naturally overwrite previous entries
|
|
28
28
|
const mergedItemsMap = new Map<string, T>();
|
|
29
29
|
|
|
30
30
|
// Process user items with default metadata
|
|
@@ -51,7 +51,7 @@ export const mergeArrayById = <T extends MergeableItem>(defaultItems: T[], userI
|
|
|
51
51
|
mergedItemsMap.set(userItem.id, mergedItem);
|
|
52
52
|
});
|
|
53
53
|
|
|
54
|
-
//
|
|
54
|
+
// Add items that only exist in default configuration
|
|
55
55
|
defaultItems.forEach((item) => {
|
|
56
56
|
if (!mergedItemsMap.has(item.id)) {
|
|
57
57
|
mergedItemsMap.set(item.id, item);
|
|
@@ -138,16 +138,16 @@ export const transformToAiModelList = async ({
|
|
|
138
138
|
const modelConfig = await parseModelString(providerId, modelString, withDeploymentName);
|
|
139
139
|
let chatModels = modelConfig.removeAll ? [] : defaultModels;
|
|
140
140
|
|
|
141
|
-
//
|
|
141
|
+
// Handle removal logic
|
|
142
142
|
if (!modelConfig.removeAll) {
|
|
143
143
|
chatModels = chatModels.filter((m) => !modelConfig.removed.includes(m.id));
|
|
144
144
|
}
|
|
145
145
|
|
|
146
|
-
//
|
|
146
|
+
// Asynchronously load configuration
|
|
147
147
|
const { LOBE_DEFAULT_MODEL_LIST } = await import('model-bank');
|
|
148
148
|
|
|
149
149
|
return produce(chatModels, (draft) => {
|
|
150
|
-
//
|
|
150
|
+
// Handle add or replace logic
|
|
151
151
|
for (const toAddModel of modelConfig.add) {
|
|
152
152
|
// first try to find the model in LOBE_DEFAULT_MODEL_LIST to confirm if it is a known model
|
|
153
153
|
let knownModel = LOBE_DEFAULT_MODEL_LIST.find(
|
|
@@ -3,12 +3,12 @@
|
|
|
3
3
|
* @param str
|
|
4
4
|
*/
|
|
5
5
|
export const sanitizeUTF8 = (str: string) => {
|
|
6
|
-
//
|
|
6
|
+
// Remove replacement character (0xFFFD) and other illegal characters
|
|
7
7
|
return (
|
|
8
8
|
str
|
|
9
|
-
.replaceAll('�', '') //
|
|
9
|
+
.replaceAll('�', '') // Remove Unicode replacement character
|
|
10
10
|
// eslint-disable-next-line no-control-regex
|
|
11
|
-
.replaceAll(/[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F-\u009F]/g, '') //
|
|
11
|
+
.replaceAll(/[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F-\u009F]/g, '') // Remove control characters
|
|
12
12
|
.replaceAll(/[\uD800-\uDFFF]/g, '')
|
|
13
|
-
); //
|
|
13
|
+
); // Remove unpaired surrogate code points
|
|
14
14
|
};
|
|
@@ -4,7 +4,7 @@ import { LobeChatPluginManifest, pluginManifestSchema } from '@lobehub/chat-plug
|
|
|
4
4
|
import { API_ENDPOINTS } from '@/services/_url';
|
|
5
5
|
|
|
6
6
|
const fetchJSON = async <T = any>(url: string, proxy = false): Promise<T> => {
|
|
7
|
-
// 2.
|
|
7
|
+
// 2. Send request
|
|
8
8
|
let res: Response;
|
|
9
9
|
try {
|
|
10
10
|
res = await (proxy ? fetch(API_ENDPOINTS.proxy, { body: url, method: 'POST' }) : fetch(url));
|
|
@@ -80,12 +80,12 @@ export const getToolManifest = async (
|
|
|
80
80
|
url?: string,
|
|
81
81
|
useProxy: boolean = false,
|
|
82
82
|
): Promise<LobeChatPluginManifest> => {
|
|
83
|
-
// 1.
|
|
83
|
+
// 1. Validate plugin
|
|
84
84
|
if (!url) {
|
|
85
85
|
throw new TypeError('noManifest');
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
-
// 2.
|
|
88
|
+
// 2. Send request
|
|
89
89
|
let data = await fetchJSON<LobeChatPluginManifest>(url, useProxy);
|
|
90
90
|
|
|
91
91
|
// @ts-ignore
|
|
@@ -94,7 +94,7 @@ export const getToolManifest = async (
|
|
|
94
94
|
if (data['description_for_model']) {
|
|
95
95
|
data = convertOpenAIManifestToLobeManifest(data as any);
|
|
96
96
|
}
|
|
97
|
-
// 3.
|
|
97
|
+
// 3. Validate plugin file format specification
|
|
98
98
|
const parser = pluginManifestSchema.safeParse(data);
|
|
99
99
|
|
|
100
100
|
if (!parser.success) {
|