@lobehub/chat 1.47.22 → 1.48.0
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 +4 -0
- package/locales/ar/components.json +1 -0
- package/locales/ar/models.json +6 -0
- package/locales/bg-BG/chat.json +4 -0
- package/locales/bg-BG/components.json +1 -0
- package/locales/bg-BG/models.json +6 -0
- package/locales/de-DE/chat.json +4 -0
- package/locales/de-DE/components.json +1 -0
- package/locales/de-DE/models.json +6 -0
- package/locales/en-US/chat.json +4 -0
- package/locales/en-US/components.json +1 -0
- package/locales/en-US/models.json +6 -0
- package/locales/es-ES/chat.json +4 -0
- package/locales/es-ES/components.json +1 -0
- package/locales/es-ES/models.json +6 -0
- package/locales/fa-IR/chat.json +4 -0
- package/locales/fa-IR/components.json +1 -0
- package/locales/fa-IR/models.json +6 -0
- package/locales/fr-FR/chat.json +4 -0
- package/locales/fr-FR/components.json +1 -0
- package/locales/fr-FR/models.json +6 -0
- package/locales/it-IT/chat.json +4 -0
- package/locales/it-IT/components.json +1 -0
- package/locales/it-IT/models.json +6 -0
- package/locales/ja-JP/chat.json +4 -0
- package/locales/ja-JP/components.json +1 -0
- package/locales/ja-JP/models.json +6 -0
- package/locales/ko-KR/chat.json +4 -0
- package/locales/ko-KR/components.json +1 -0
- package/locales/ko-KR/models.json +6 -0
- package/locales/nl-NL/chat.json +4 -0
- package/locales/nl-NL/components.json +1 -0
- package/locales/nl-NL/models.json +6 -0
- package/locales/pl-PL/chat.json +4 -0
- package/locales/pl-PL/components.json +1 -0
- package/locales/pl-PL/models.json +6 -0
- package/locales/pt-BR/chat.json +4 -0
- package/locales/pt-BR/components.json +1 -0
- package/locales/pt-BR/models.json +6 -0
- package/locales/ru-RU/chat.json +4 -0
- package/locales/ru-RU/components.json +1 -0
- package/locales/ru-RU/models.json +6 -0
- package/locales/tr-TR/chat.json +4 -0
- package/locales/tr-TR/components.json +1 -0
- package/locales/tr-TR/models.json +6 -0
- package/locales/vi-VN/chat.json +4 -0
- package/locales/vi-VN/components.json +1 -0
- package/locales/vi-VN/models.json +6 -0
- package/locales/zh-CN/chat.json +4 -0
- package/locales/zh-CN/components.json +1 -0
- package/locales/zh-CN/modelProvider.json +2 -2
- package/locales/zh-CN/models.json +7 -1
- package/locales/zh-TW/chat.json +4 -0
- package/locales/zh-TW/components.json +1 -0
- package/locales/zh-TW/models.json +6 -0
- package/package.json +1 -1
- package/src/components/ModelSelect/index.tsx +16 -1
- package/src/config/aiModels/deepseek.ts +3 -0
- package/src/config/aiModels/hunyuan.ts +132 -12
- package/src/config/aiModels/qwen.ts +19 -2
- package/src/config/modelProviders/hunyuan.ts +2 -0
- package/src/database/client/migrations.json +13 -2
- package/src/database/migrations/0014_add_message_reasoning.sql +1 -0
- package/src/database/migrations/meta/0014_snapshot.json +3961 -0
- package/src/database/migrations/meta/_journal.json +7 -0
- package/src/database/schemas/message.ts +2 -3
- package/src/database/server/models/__tests__/message.test.ts +5 -4
- package/src/database/server/models/message.ts +35 -13
- package/src/database/server/models/topic.ts +3 -2
- package/src/features/Conversation/Messages/Assistant/Reasoning/index.tsx +123 -0
- package/src/features/Conversation/Messages/Assistant/index.tsx +8 -1
- package/src/features/Conversation/components/MarkdownElements/LobeThinking/index.ts +2 -2
- package/src/libs/agent-runtime/deepseek/index.ts +1 -1
- package/src/libs/agent-runtime/google/index.ts +7 -5
- package/src/libs/agent-runtime/hunyuan/index.ts +24 -0
- package/src/libs/agent-runtime/qwen/index.ts +8 -3
- package/src/libs/agent-runtime/stepfun/index.ts +7 -1
- package/src/libs/agent-runtime/utils/streams/openai.test.ts +203 -0
- package/src/libs/agent-runtime/utils/streams/openai.ts +8 -1
- package/src/libs/agent-runtime/utils/streams/protocol.ts +1 -1
- package/src/locales/default/chat.ts +4 -0
- package/src/locales/default/components.ts +1 -0
- package/src/server/routers/lambda/message.ts +4 -2
- package/src/services/message/client.test.ts +1 -1
- package/src/services/message/type.ts +1 -1
- package/src/store/chat/selectors.ts +1 -0
- package/src/store/chat/slices/aiChat/actions/generateAIChat.ts +60 -14
- package/src/store/chat/slices/aiChat/initialState.ts +5 -0
- package/src/store/chat/slices/aiChat/selectors.ts +9 -0
- package/src/store/chat/slices/message/action.ts +4 -1
- package/src/types/aiModel.ts +5 -14
- package/src/types/message/base.ts +59 -0
- package/src/types/message/chat.ts +136 -0
- package/src/types/message/index.ts +2 -135
- package/src/utils/fetch/__tests__/fetchSSE.test.ts +34 -0
- package/src/utils/fetch/fetchSSE.ts +38 -3
@@ -98,6 +98,13 @@
|
|
98
98
|
"when": 1735834653361,
|
99
99
|
"tag": "0013_add_ai_infra",
|
100
100
|
"breakpoints": true
|
101
|
+
},
|
102
|
+
{
|
103
|
+
"idx": 14,
|
104
|
+
"version": "7",
|
105
|
+
"when": 1737609172353,
|
106
|
+
"tag": "0014_add_message_reasoning",
|
107
|
+
"breakpoints": true
|
101
108
|
}
|
102
109
|
],
|
103
110
|
"version": "6"
|
@@ -13,6 +13,7 @@ import {
|
|
13
13
|
import { createSelectSchema } from 'drizzle-zod';
|
14
14
|
|
15
15
|
import { idGenerator } from '@/database/utils/idGenerator';
|
16
|
+
import { ModelReasoning } from '@/types/message';
|
16
17
|
|
17
18
|
import { timestamps } from './_helpers';
|
18
19
|
import { agents } from './agent';
|
@@ -32,6 +33,7 @@ export const messages = pgTable(
|
|
32
33
|
|
33
34
|
role: text('role', { enum: ['user', 'system', 'assistant', 'tool'] }).notNull(),
|
34
35
|
content: text('content'),
|
36
|
+
reasoning: jsonb('reasoning').$type<ModelReasoning>(),
|
35
37
|
|
36
38
|
model: text('model'),
|
37
39
|
provider: text('provider'),
|
@@ -71,9 +73,6 @@ export const messages = pgTable(
|
|
71
73
|
}),
|
72
74
|
);
|
73
75
|
|
74
|
-
export type NewMessage = typeof messages.$inferInsert;
|
75
|
-
export type MessageItem = typeof messages.$inferSelect;
|
76
|
-
|
77
76
|
// if the message container a plugin
|
78
77
|
export const messagePlugins = pgTable('message_plugins', {
|
79
78
|
id: text('id')
|
@@ -3,6 +3,7 @@ import { eq } from 'drizzle-orm/expressions';
|
|
3
3
|
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
4
4
|
|
5
5
|
import { getTestDBInstance } from '@/database/server/core/dbForTest';
|
6
|
+
import { MessageItem } from '@/types/message';
|
6
7
|
import { uuid } from '@/utils/uuid';
|
7
8
|
|
8
9
|
import {
|
@@ -253,8 +254,8 @@ describe('MessageModel', () => {
|
|
253
254
|
const result = await messageModel.query();
|
254
255
|
|
255
256
|
// 断言结果
|
256
|
-
expect(result[0].extra
|
257
|
-
expect(result[0].extra
|
257
|
+
expect(result[0].extra!.translate).toEqual({ content: 'translated', from: 'en', to: 'zh' });
|
258
|
+
expect(result[0].extra!.tts).toEqual({
|
258
259
|
contentMd5: 'md5',
|
259
260
|
file: 'f1',
|
260
261
|
voice: 'voice1',
|
@@ -345,7 +346,7 @@ describe('MessageModel', () => {
|
|
345
346
|
|
346
347
|
expect(result).toHaveLength(1);
|
347
348
|
expect(result[0].chunksList).toHaveLength(1);
|
348
|
-
expect(result[0].chunksList[0]).toMatchObject({
|
349
|
+
expect(result[0].chunksList![0]).toMatchObject({
|
349
350
|
text: 'chunk content',
|
350
351
|
similarity: 0.95,
|
351
352
|
});
|
@@ -655,7 +656,7 @@ describe('MessageModel', () => {
|
|
655
656
|
const newMessages = [
|
656
657
|
{ id: '1', role: 'user', content: 'message 1' },
|
657
658
|
{ id: '2', role: 'assistant', content: 'message 2' },
|
658
|
-
];
|
659
|
+
] as MessageItem[];
|
659
660
|
|
660
661
|
// 调用 batchCreateMessages 方法
|
661
662
|
await messageModel.batchCreate(newMessages);
|
@@ -14,16 +14,18 @@ import { idGenerator } from '@/database/utils/idGenerator';
|
|
14
14
|
import {
|
15
15
|
ChatFileItem,
|
16
16
|
ChatImageItem,
|
17
|
+
ChatMessage,
|
17
18
|
ChatTTS,
|
18
19
|
ChatToolPayload,
|
20
|
+
ChatTranslate,
|
19
21
|
CreateMessageParams,
|
22
|
+
MessageItem,
|
20
23
|
ModelRankItem,
|
21
24
|
} from '@/types/message';
|
22
25
|
import { merge } from '@/utils/merge';
|
23
26
|
import { today } from '@/utils/time';
|
24
27
|
|
25
28
|
import {
|
26
|
-
MessageItem,
|
27
29
|
MessagePluginItem,
|
28
30
|
NewMessageQuery,
|
29
31
|
chunks,
|
@@ -61,7 +63,7 @@ export class MessageModel {
|
|
61
63
|
options: {
|
62
64
|
postProcessUrl?: (path: string | null, file: { fileType: string }) => Promise<string>;
|
63
65
|
} = {},
|
64
|
-
)
|
66
|
+
) => {
|
65
67
|
const offset = current * pageSize;
|
66
68
|
|
67
69
|
// 1. get basic messages
|
@@ -71,6 +73,7 @@ export class MessageModel {
|
|
71
73
|
id: messages.id,
|
72
74
|
role: messages.role,
|
73
75
|
content: messages.content,
|
76
|
+
reasoning: messages.reasoning,
|
74
77
|
error: messages.error,
|
75
78
|
|
76
79
|
model: messages.model,
|
@@ -220,10 +223,11 @@ export class MessageModel {
|
|
220
223
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
221
224
|
.map<ChatImageItem>(({ id, url, name }) => ({ alt: name!, id, url })),
|
222
225
|
|
226
|
+
meta: {},
|
223
227
|
ragQuery: messageQuery?.rewriteQuery,
|
224
228
|
ragQueryId: messageQuery?.id,
|
225
229
|
ragRawQuery: messageQuery?.userQuery,
|
226
|
-
};
|
230
|
+
} as unknown as ChatMessage;
|
227
231
|
},
|
228
232
|
);
|
229
233
|
};
|
@@ -252,27 +256,33 @@ export class MessageModel {
|
|
252
256
|
return result[0];
|
253
257
|
};
|
254
258
|
|
255
|
-
queryAll = async ()
|
256
|
-
|
259
|
+
queryAll = async () => {
|
260
|
+
const result = await this.db
|
257
261
|
.select()
|
258
262
|
.from(messages)
|
259
263
|
.orderBy(messages.createdAt)
|
260
264
|
.where(eq(messages.userId, this.userId));
|
265
|
+
|
266
|
+
return result as MessageItem[];
|
261
267
|
};
|
262
268
|
|
263
|
-
queryBySessionId = async (sessionId?: string | null)
|
264
|
-
|
269
|
+
queryBySessionId = async (sessionId?: string | null) => {
|
270
|
+
const result = await this.db.query.messages.findMany({
|
265
271
|
orderBy: [asc(messages.createdAt)],
|
266
272
|
where: and(eq(messages.userId, this.userId), this.matchSession(sessionId)),
|
267
273
|
});
|
274
|
+
|
275
|
+
return result as MessageItem[];
|
268
276
|
};
|
269
277
|
|
270
|
-
queryByKeyword = async (keyword: string)
|
278
|
+
queryByKeyword = async (keyword: string) => {
|
271
279
|
if (!keyword) return [];
|
272
|
-
|
280
|
+
const result = await this.db.query.messages.findMany({
|
273
281
|
orderBy: [desc(messages.createdAt)],
|
274
282
|
where: and(eq(messages.userId, this.userId), like(messages.content, `%${keyword}%`)),
|
275
283
|
});
|
284
|
+
|
285
|
+
return result as MessageItem[];
|
276
286
|
};
|
277
287
|
|
278
288
|
count = async (params?: {
|
@@ -414,6 +424,8 @@ export class MessageModel {
|
|
414
424
|
pluginState,
|
415
425
|
fileChunks,
|
416
426
|
ragQueryId,
|
427
|
+
updatedAt,
|
428
|
+
createdAt,
|
417
429
|
...message
|
418
430
|
}: CreateMessageParams,
|
419
431
|
id: string = this.genId(),
|
@@ -423,9 +435,12 @@ export class MessageModel {
|
|
423
435
|
.insert(messages)
|
424
436
|
.values({
|
425
437
|
...message,
|
438
|
+
// TODO: remove this when the client is updated
|
439
|
+
createdAt: createdAt ? new Date(createdAt) : undefined,
|
426
440
|
id,
|
427
441
|
model: fromModel,
|
428
442
|
provider: fromProvider,
|
443
|
+
updatedAt: updatedAt ? new Date(updatedAt) : undefined,
|
429
444
|
userId: this.userId,
|
430
445
|
})
|
431
446
|
.returning()) as MessageItem[];
|
@@ -466,7 +481,8 @@ export class MessageModel {
|
|
466
481
|
|
467
482
|
batchCreate = async (newMessages: MessageItem[]) => {
|
468
483
|
const messagesToInsert = newMessages.map((m) => {
|
469
|
-
|
484
|
+
// TODO: need a better way to handle this
|
485
|
+
return { ...m, role: m.role as any, userId: this.userId };
|
470
486
|
});
|
471
487
|
|
472
488
|
return this.db.insert(messages).values(messagesToInsert);
|
@@ -482,7 +498,11 @@ export class MessageModel {
|
|
482
498
|
update = async (id: string, message: Partial<MessageItem>) => {
|
483
499
|
return this.db
|
484
500
|
.update(messages)
|
485
|
-
.set(
|
501
|
+
.set({
|
502
|
+
...message,
|
503
|
+
// TODO: need a better way to handle this
|
504
|
+
role: message.role as any,
|
505
|
+
})
|
486
506
|
.where(and(eq(messages.id, id), eq(messages.userId, this.userId)));
|
487
507
|
};
|
488
508
|
|
@@ -507,7 +527,7 @@ export class MessageModel {
|
|
507
527
|
return this.db.update(messagePlugins).set(value).where(eq(messagePlugins.id, id));
|
508
528
|
};
|
509
529
|
|
510
|
-
updateTranslate = async (id: string, translate: Partial<
|
530
|
+
updateTranslate = async (id: string, translate: Partial<ChatTranslate>) => {
|
511
531
|
const result = await this.db.query.messageTranslates.findFirst({
|
512
532
|
where: and(eq(messageTranslates.id, id)),
|
513
533
|
});
|
@@ -555,7 +575,9 @@ export class MessageModel {
|
|
555
575
|
if (message.length === 0) return;
|
556
576
|
|
557
577
|
// 2. 检查 message 是否包含 tools
|
558
|
-
const toolCallIds = message[0].tools
|
578
|
+
const toolCallIds = (message[0].tools as ChatToolPayload[])
|
579
|
+
?.map((tool) => tool.id)
|
580
|
+
.filter(Boolean);
|
559
581
|
|
560
582
|
let relatedMessageIds: string[] = [];
|
561
583
|
|
@@ -9,9 +9,10 @@ import {
|
|
9
9
|
genWhere,
|
10
10
|
} from '@/database/utils/genWhere';
|
11
11
|
import { idGenerator } from '@/database/utils/idGenerator';
|
12
|
+
import { MessageItem } from '@/types/message';
|
12
13
|
import { TopicRankItem } from '@/types/topic';
|
13
14
|
|
14
|
-
import {
|
15
|
+
import { TopicItem, messages, topics } from '../../schemas';
|
15
16
|
|
16
17
|
export interface CreateTopicParams {
|
17
18
|
favorite?: boolean;
|
@@ -244,7 +245,7 @@ export class TopicModel {
|
|
244
245
|
id: idGenerator('messages'),
|
245
246
|
topicId: duplicatedTopic.id,
|
246
247
|
})
|
247
|
-
.returning()) as
|
248
|
+
.returning()) as MessageItem[];
|
248
249
|
|
249
250
|
return result[0];
|
250
251
|
}),
|
@@ -0,0 +1,123 @@
|
|
1
|
+
import { Icon, Markdown } from '@lobehub/ui';
|
2
|
+
import { createStyles } from 'antd-style';
|
3
|
+
import { AtomIcon, ChevronDown, ChevronRight } from 'lucide-react';
|
4
|
+
import { rgba } from 'polished';
|
5
|
+
import { memo, useEffect, useState } from 'react';
|
6
|
+
import { useTranslation } from 'react-i18next';
|
7
|
+
import { Flexbox } from 'react-layout-kit';
|
8
|
+
|
9
|
+
import { useChatStore } from '@/store/chat';
|
10
|
+
import { aiChatSelectors } from '@/store/chat/selectors';
|
11
|
+
|
12
|
+
const useStyles = createStyles(({ css, token, isDarkMode }) => ({
|
13
|
+
container: css`
|
14
|
+
cursor: pointer;
|
15
|
+
|
16
|
+
width: fit-content;
|
17
|
+
padding-block: 4px;
|
18
|
+
padding-inline: 8px;
|
19
|
+
border-radius: 6px;
|
20
|
+
|
21
|
+
color: ${token.colorTextTertiary};
|
22
|
+
|
23
|
+
&:hover {
|
24
|
+
background: ${isDarkMode ? token.colorFillQuaternary : token.colorFillTertiary};
|
25
|
+
}
|
26
|
+
`,
|
27
|
+
expand: css`
|
28
|
+
background: ${isDarkMode ? token.colorFillQuaternary : token.colorFillTertiary} !important;
|
29
|
+
`,
|
30
|
+
shinyText: css`
|
31
|
+
color: ${rgba(token.colorText, 0.45)};
|
32
|
+
|
33
|
+
background: linear-gradient(
|
34
|
+
120deg,
|
35
|
+
${rgba(token.colorTextBase, 0)} 40%,
|
36
|
+
${token.colorTextSecondary} 50%,
|
37
|
+
${rgba(token.colorTextBase, 0)} 60%
|
38
|
+
);
|
39
|
+
background-clip: text;
|
40
|
+
background-size: 200% 100%;
|
41
|
+
|
42
|
+
animation: shine 1.5s linear infinite;
|
43
|
+
|
44
|
+
@keyframes shine {
|
45
|
+
0% {
|
46
|
+
background-position: 100%;
|
47
|
+
}
|
48
|
+
|
49
|
+
100% {
|
50
|
+
background-position: -100%;
|
51
|
+
}
|
52
|
+
}
|
53
|
+
`,
|
54
|
+
title: css`
|
55
|
+
overflow: hidden;
|
56
|
+
display: -webkit-box;
|
57
|
+
-webkit-box-orient: vertical;
|
58
|
+
-webkit-line-clamp: 1;
|
59
|
+
|
60
|
+
font-size: 12px;
|
61
|
+
text-overflow: ellipsis;
|
62
|
+
`,
|
63
|
+
}));
|
64
|
+
|
65
|
+
interface ThinkingProps {
|
66
|
+
content?: string;
|
67
|
+
duration?: number;
|
68
|
+
id: string;
|
69
|
+
}
|
70
|
+
|
71
|
+
const Thinking = memo<ThinkingProps>(({ content = '', duration, id }) => {
|
72
|
+
const { t } = useTranslation('chat');
|
73
|
+
const { styles, cx } = useStyles();
|
74
|
+
|
75
|
+
const [showDetail, setShowDetail] = useState(false);
|
76
|
+
|
77
|
+
const isReasoning = useChatStore(aiChatSelectors.isMessageInReasoning(id));
|
78
|
+
|
79
|
+
useEffect(() => {
|
80
|
+
if (isReasoning && !content) {
|
81
|
+
setShowDetail(true);
|
82
|
+
}
|
83
|
+
|
84
|
+
if (!isReasoning) {
|
85
|
+
setShowDetail(false);
|
86
|
+
}
|
87
|
+
}, [isReasoning, content]);
|
88
|
+
|
89
|
+
return (
|
90
|
+
<Flexbox
|
91
|
+
className={cx(styles.container, showDetail && styles.expand)}
|
92
|
+
gap={16}
|
93
|
+
onClick={() => {
|
94
|
+
setShowDetail(!showDetail);
|
95
|
+
}}
|
96
|
+
>
|
97
|
+
<Flexbox distribution={'space-between'} flex={1} horizontal>
|
98
|
+
{isReasoning ? (
|
99
|
+
<Flexbox gap={8} horizontal>
|
100
|
+
<Icon icon={AtomIcon} />
|
101
|
+
<Flexbox className={styles.shinyText} horizontal>
|
102
|
+
{t('reasoning.thinking')}
|
103
|
+
</Flexbox>
|
104
|
+
</Flexbox>
|
105
|
+
) : (
|
106
|
+
<Flexbox gap={8} horizontal>
|
107
|
+
<Icon icon={AtomIcon} />
|
108
|
+
{t('reasoning.thought', { duration: ((duration || 0) / 1000).toFixed(1) })}
|
109
|
+
</Flexbox>
|
110
|
+
)}
|
111
|
+
<Icon icon={showDetail ? ChevronDown : ChevronRight} />
|
112
|
+
</Flexbox>
|
113
|
+
|
114
|
+
{showDetail && (
|
115
|
+
<Flexbox>
|
116
|
+
<Markdown variant={'chat'}>{content}</Markdown>
|
117
|
+
</Flexbox>
|
118
|
+
)}
|
119
|
+
</Flexbox>
|
120
|
+
);
|
121
|
+
});
|
122
|
+
|
123
|
+
export default Thinking;
|
@@ -3,13 +3,15 @@ import { ReactNode, Suspense, memo, useContext } from 'react';
|
|
3
3
|
import { Flexbox } from 'react-layout-kit';
|
4
4
|
|
5
5
|
import { LOADING_FLAT } from '@/const/message';
|
6
|
-
import { InPortalThreadContext } from '@/features/Conversation/components/ChatItem/InPortalThreadContext';
|
7
6
|
import { useChatStore } from '@/store/chat';
|
8
7
|
import { chatSelectors } from '@/store/chat/selectors';
|
8
|
+
import { aiChatSelectors } from '@/store/chat/slices/aiChat/selectors';
|
9
9
|
import { ChatMessage } from '@/types/message';
|
10
10
|
|
11
|
+
import { InPortalThreadContext } from '../../components/ChatItem/InPortalThreadContext';
|
11
12
|
import { DefaultMessage } from '../Default';
|
12
13
|
import FileChunks from './FileChunks';
|
14
|
+
import Thinking from './Reasoning';
|
13
15
|
import ToolCall from './ToolCallItem';
|
14
16
|
|
15
17
|
export const AssistantMessage = memo<
|
@@ -23,6 +25,10 @@ export const AssistantMessage = memo<
|
|
23
25
|
const inThread = useContext(InPortalThreadContext);
|
24
26
|
const isToolCallGenerating = generating && (content === LOADING_FLAT || !content) && !!tools;
|
25
27
|
|
28
|
+
const isReasoning = useChatStore(aiChatSelectors.isMessageInReasoning(id));
|
29
|
+
|
30
|
+
const showReasoning = !!props.reasoning || (!props.reasoning && isReasoning);
|
31
|
+
|
26
32
|
return editing ? (
|
27
33
|
<DefaultMessage
|
28
34
|
content={content}
|
@@ -33,6 +39,7 @@ export const AssistantMessage = memo<
|
|
33
39
|
) : (
|
34
40
|
<Flexbox gap={8} id={id}>
|
35
41
|
{!!chunksList && chunksList.length > 0 && <FileChunks data={chunksList} />}
|
42
|
+
{showReasoning && <Thinking {...props.reasoning} id={id} />}
|
36
43
|
{content && (
|
37
44
|
<DefaultMessage
|
38
45
|
addIdOnDOM={false}
|
@@ -3,10 +3,10 @@ import { ARTIFACT_THINKING_TAG } from '@/const/plugin';
|
|
3
3
|
import Component from './Render';
|
4
4
|
import rehypePlugin from './rehypePlugin';
|
5
5
|
|
6
|
-
const
|
6
|
+
const LobeThinkingElement = {
|
7
7
|
Component,
|
8
8
|
rehypePlugin,
|
9
9
|
tag: ARTIFACT_THINKING_TAG,
|
10
10
|
};
|
11
11
|
|
12
|
-
export default
|
12
|
+
export default LobeThinkingElement;
|
@@ -40,7 +40,7 @@ export const LobeDeepSeekAI = LobeOpenAICompatibleFactory({
|
|
40
40
|
|
41
41
|
return {
|
42
42
|
enabled: LOBE_DEFAULT_MODEL_LIST.find((m) => model.id.endsWith(m.id))?.enabled || false,
|
43
|
-
functionCall:
|
43
|
+
functionCall: !model.id.toLowerCase().includes('deepseek-reasoner'),
|
44
44
|
id: model.id,
|
45
45
|
};
|
46
46
|
},
|
@@ -8,6 +8,8 @@ import {
|
|
8
8
|
SchemaType,
|
9
9
|
} from '@google/generative-ai';
|
10
10
|
|
11
|
+
import { LOBE_DEFAULT_MODEL_LIST } from '@/config/aiModels';
|
12
|
+
import type { ChatModelCard } from '@/types/llm';
|
11
13
|
import { imageUrlToBase64 } from '@/utils/imageToBase64';
|
12
14
|
import { safeParseJSON } from '@/utils/safeParseJSON';
|
13
15
|
|
@@ -27,9 +29,6 @@ import { StreamingResponse } from '../utils/response';
|
|
27
29
|
import { GoogleGenerativeAIStream, convertIterableToStream } from '../utils/streams';
|
28
30
|
import { parseDataUri } from '../utils/uriParser';
|
29
31
|
|
30
|
-
import { LOBE_DEFAULT_MODEL_LIST } from '@/config/aiModels';
|
31
|
-
import type { ChatModelCard } from '@/types/llm';
|
32
|
-
|
33
32
|
export interface GoogleModelCard {
|
34
33
|
displayName: string;
|
35
34
|
inputTokenLimit: number;
|
@@ -143,7 +142,7 @@ export class LobeGoogleAI implements LobeRuntimeAI {
|
|
143
142
|
method: 'GET',
|
144
143
|
});
|
145
144
|
const json = await response.json();
|
146
|
-
|
145
|
+
|
147
146
|
const modelList: GoogleModelCard[] = json['models'];
|
148
147
|
|
149
148
|
return modelList
|
@@ -156,7 +155,10 @@ export class LobeGoogleAI implements LobeRuntimeAI {
|
|
156
155
|
enabled: LOBE_DEFAULT_MODEL_LIST.find((m) => modelName.endsWith(m.id))?.enabled || false,
|
157
156
|
functionCall: modelName.toLowerCase().includes('gemini'),
|
158
157
|
id: modelName,
|
159
|
-
vision:
|
158
|
+
vision:
|
159
|
+
modelName.toLowerCase().includes('vision') ||
|
160
|
+
(modelName.toLowerCase().includes('gemini') &&
|
161
|
+
!modelName.toLowerCase().includes('gemini-1.0')),
|
160
162
|
};
|
161
163
|
})
|
162
164
|
.filter(Boolean) as ChatModelCard[];
|
@@ -1,10 +1,34 @@
|
|
1
1
|
import { ModelProvider } from '../types';
|
2
2
|
import { LobeOpenAICompatibleFactory } from '../utils/openaiCompatibleFactory';
|
3
3
|
|
4
|
+
import { LOBE_DEFAULT_MODEL_LIST } from '@/config/aiModels';
|
5
|
+
|
6
|
+
export interface HunyuanModelCard {
|
7
|
+
id: string;
|
8
|
+
}
|
9
|
+
|
4
10
|
export const LobeHunyuanAI = LobeOpenAICompatibleFactory({
|
5
11
|
baseURL: 'https://api.hunyuan.cloud.tencent.com/v1',
|
6
12
|
debug: {
|
7
13
|
chatCompletion: () => process.env.DEBUG_HUNYUAN_CHAT_COMPLETION === '1',
|
8
14
|
},
|
15
|
+
models: {
|
16
|
+
transformModel: (m) => {
|
17
|
+
const functionCallKeywords = [
|
18
|
+
'hunyuan-functioncall',
|
19
|
+
'hunyuan-turbo',
|
20
|
+
'hunyuan-pro',
|
21
|
+
];
|
22
|
+
|
23
|
+
const model = m as unknown as HunyuanModelCard;
|
24
|
+
|
25
|
+
return {
|
26
|
+
enabled: LOBE_DEFAULT_MODEL_LIST.find((m) => model.id.endsWith(m.id))?.enabled || false,
|
27
|
+
functionCall: functionCallKeywords.some(keyword => model.id.toLowerCase().includes(keyword)) && !model.id.toLowerCase().includes('vision'),
|
28
|
+
id: model.id,
|
29
|
+
vision: model.id.toLowerCase().includes('vision'),
|
30
|
+
};
|
31
|
+
},
|
32
|
+
},
|
9
33
|
provider: ModelProvider.Hunyuan,
|
10
34
|
});
|
@@ -49,7 +49,7 @@ export const LobeQwenAI = LobeOpenAICompatibleFactory({
|
|
49
49
|
: undefined,
|
50
50
|
stream: !payload.tools,
|
51
51
|
temperature: (temperature !== undefined && temperature >= 0 && temperature < 2) ? temperature : undefined,
|
52
|
-
...(model.startsWith('qwen-vl') ? {
|
52
|
+
...(model.startsWith('qvq') || model.startsWith('qwen-vl') ? {
|
53
53
|
top_p: (top_p !== undefined && top_p > 0 && top_p <= 1) ? top_p : undefined,
|
54
54
|
} : {
|
55
55
|
top_p: (top_p !== undefined && top_p > 0 && top_p < 1) ? top_p : undefined,
|
@@ -67,7 +67,7 @@ export const LobeQwenAI = LobeOpenAICompatibleFactory({
|
|
67
67
|
debug: {
|
68
68
|
chatCompletion: () => process.env.DEBUG_QWEN_CHAT_COMPLETION === '1',
|
69
69
|
},
|
70
|
-
|
70
|
+
models: {
|
71
71
|
transformModel: (m) => {
|
72
72
|
const functionCallKeywords = [
|
73
73
|
'qwen-max',
|
@@ -76,13 +76,18 @@ export const LobeQwenAI = LobeOpenAICompatibleFactory({
|
|
76
76
|
'qwen2.5',
|
77
77
|
];
|
78
78
|
|
79
|
+
const visionKeywords = [
|
80
|
+
'qvq',
|
81
|
+
'vl',
|
82
|
+
];
|
83
|
+
|
79
84
|
const model = m as unknown as QwenModelCard;
|
80
85
|
|
81
86
|
return {
|
82
87
|
enabled: LOBE_DEFAULT_MODEL_LIST.find((m) => model.id.endsWith(m.id))?.enabled || false,
|
83
88
|
functionCall: functionCallKeywords.some(keyword => model.id.toLowerCase().includes(keyword)),
|
84
89
|
id: model.id,
|
85
|
-
vision: model.id.toLowerCase().includes(
|
90
|
+
vision: visionKeywords.some(keyword => model.id.toLowerCase().includes(keyword)),
|
86
91
|
};
|
87
92
|
},
|
88
93
|
},
|
@@ -25,7 +25,13 @@ export const LobeStepfunAI = LobeOpenAICompatibleFactory({
|
|
25
25
|
// ref: https://platform.stepfun.com/docs/llm/modeloverview
|
26
26
|
const functionCallKeywords = [
|
27
27
|
'step-1-',
|
28
|
+
'step-1o-',
|
29
|
+
'step-1v-',
|
28
30
|
'step-2-',
|
31
|
+
];
|
32
|
+
|
33
|
+
const visionKeywords = [
|
34
|
+
'step-1o-',
|
29
35
|
'step-1v-',
|
30
36
|
];
|
31
37
|
|
@@ -35,7 +41,7 @@ export const LobeStepfunAI = LobeOpenAICompatibleFactory({
|
|
35
41
|
enabled: LOBE_DEFAULT_MODEL_LIST.find((m) => model.id.endsWith(m.id))?.enabled || false,
|
36
42
|
functionCall: functionCallKeywords.some(keyword => model.id.toLowerCase().includes(keyword)),
|
37
43
|
id: model.id,
|
38
|
-
vision: model.id.toLowerCase().includes(
|
44
|
+
vision: visionKeywords.some(keyword => model.id.toLowerCase().includes(keyword)),
|
39
45
|
};
|
40
46
|
},
|
41
47
|
},
|