@lobehub/lobehub 2.0.0-next.264 → 2.0.0-next.266
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/manual-build-desktop.yml +16 -37
- package/CHANGELOG.md +52 -0
- package/apps/desktop/native-deps.config.mjs +19 -3
- package/apps/desktop/src/main/controllers/__tests__/SystemCtr.test.ts +13 -0
- package/apps/desktop/src/main/utils/permissions.ts +86 -22
- package/changelog/v1.json +18 -0
- package/locales/ar/chat.json +1 -0
- package/locales/ar/modelProvider.json +20 -0
- package/locales/ar/models.json +33 -10
- package/locales/ar/plugin.json +1 -0
- package/locales/ar/providers.json +1 -0
- package/locales/ar/setting.json +2 -0
- package/locales/bg-BG/chat.json +1 -0
- package/locales/bg-BG/modelProvider.json +20 -0
- package/locales/bg-BG/models.json +27 -7
- package/locales/bg-BG/plugin.json +1 -0
- package/locales/bg-BG/providers.json +1 -0
- package/locales/bg-BG/setting.json +2 -0
- package/locales/de-DE/chat.json +1 -0
- package/locales/de-DE/modelProvider.json +20 -0
- package/locales/de-DE/models.json +44 -10
- package/locales/de-DE/plugin.json +1 -0
- package/locales/de-DE/providers.json +1 -0
- package/locales/de-DE/setting.json +2 -0
- package/locales/en-US/chat.json +1 -0
- package/locales/en-US/modelProvider.json +20 -0
- package/locales/en-US/models.json +10 -10
- package/locales/en-US/providers.json +1 -0
- package/locales/en-US/setting.json +2 -1
- package/locales/es-ES/chat.json +1 -0
- package/locales/es-ES/modelProvider.json +20 -0
- package/locales/es-ES/models.json +53 -10
- package/locales/es-ES/plugin.json +1 -0
- package/locales/es-ES/providers.json +1 -0
- package/locales/es-ES/setting.json +2 -0
- package/locales/fa-IR/chat.json +1 -0
- package/locales/fa-IR/modelProvider.json +20 -0
- package/locales/fa-IR/models.json +33 -10
- package/locales/fa-IR/plugin.json +1 -0
- package/locales/fa-IR/providers.json +1 -0
- package/locales/fa-IR/setting.json +2 -0
- package/locales/fr-FR/chat.json +1 -0
- package/locales/fr-FR/modelProvider.json +20 -0
- package/locales/fr-FR/models.json +27 -7
- package/locales/fr-FR/plugin.json +1 -0
- package/locales/fr-FR/providers.json +1 -0
- package/locales/fr-FR/setting.json +2 -0
- package/locales/it-IT/chat.json +1 -0
- package/locales/it-IT/modelProvider.json +20 -0
- package/locales/it-IT/models.json +10 -10
- package/locales/it-IT/plugin.json +1 -0
- package/locales/it-IT/providers.json +1 -0
- package/locales/it-IT/setting.json +2 -0
- package/locales/ja-JP/chat.json +1 -0
- package/locales/ja-JP/modelProvider.json +20 -0
- package/locales/ja-JP/models.json +5 -10
- package/locales/ja-JP/plugin.json +1 -0
- package/locales/ja-JP/providers.json +1 -0
- package/locales/ja-JP/setting.json +2 -0
- package/locales/ko-KR/chat.json +1 -0
- package/locales/ko-KR/modelProvider.json +20 -0
- package/locales/ko-KR/models.json +36 -10
- package/locales/ko-KR/plugin.json +1 -0
- package/locales/ko-KR/providers.json +1 -0
- package/locales/ko-KR/setting.json +2 -0
- package/locales/nl-NL/chat.json +1 -0
- package/locales/nl-NL/modelProvider.json +20 -0
- package/locales/nl-NL/models.json +35 -4
- package/locales/nl-NL/plugin.json +1 -0
- package/locales/nl-NL/providers.json +1 -0
- package/locales/nl-NL/setting.json +2 -0
- package/locales/pl-PL/chat.json +1 -0
- package/locales/pl-PL/modelProvider.json +20 -0
- package/locales/pl-PL/models.json +37 -7
- package/locales/pl-PL/plugin.json +1 -0
- package/locales/pl-PL/providers.json +1 -0
- package/locales/pl-PL/setting.json +2 -0
- package/locales/pt-BR/chat.json +1 -0
- package/locales/pt-BR/modelProvider.json +20 -0
- package/locales/pt-BR/models.json +51 -9
- package/locales/pt-BR/plugin.json +1 -0
- package/locales/pt-BR/providers.json +1 -0
- package/locales/pt-BR/setting.json +2 -0
- package/locales/ru-RU/chat.json +1 -0
- package/locales/ru-RU/modelProvider.json +20 -0
- package/locales/ru-RU/models.json +48 -7
- package/locales/ru-RU/plugin.json +1 -0
- package/locales/ru-RU/providers.json +1 -0
- package/locales/ru-RU/setting.json +2 -0
- package/locales/tr-TR/chat.json +1 -0
- package/locales/tr-TR/modelProvider.json +20 -0
- package/locales/tr-TR/models.json +48 -7
- package/locales/tr-TR/plugin.json +1 -0
- package/locales/tr-TR/providers.json +1 -0
- package/locales/tr-TR/setting.json +2 -0
- package/locales/vi-VN/chat.json +1 -0
- package/locales/vi-VN/modelProvider.json +20 -0
- package/locales/vi-VN/models.json +5 -5
- package/locales/vi-VN/plugin.json +1 -0
- package/locales/vi-VN/providers.json +1 -0
- package/locales/vi-VN/setting.json +2 -0
- package/locales/zh-CN/modelProvider.json +20 -20
- package/locales/zh-CN/models.json +49 -8
- package/locales/zh-CN/providers.json +1 -0
- package/locales/zh-CN/setting.json +2 -1
- package/locales/zh-TW/chat.json +1 -0
- package/locales/zh-TW/modelProvider.json +20 -0
- package/locales/zh-TW/models.json +29 -10
- package/locales/zh-TW/plugin.json +1 -0
- package/locales/zh-TW/providers.json +1 -0
- package/locales/zh-TW/setting.json +2 -0
- package/package.json +2 -2
- package/packages/database/src/models/__tests__/agent.test.ts +165 -4
- package/packages/database/src/models/agent.ts +46 -0
- package/packages/database/src/repositories/agentGroup/index.test.ts +498 -0
- package/packages/database/src/repositories/agentGroup/index.ts +150 -0
- package/packages/database/src/repositories/home/__tests__/index.test.ts +113 -1
- package/packages/database/src/repositories/home/index.ts +48 -67
- package/pnpm-workspace.yaml +1 -0
- package/src/app/[variants]/(main)/agent/_layout/Sidebar/Body.tsx +1 -1
- package/src/app/[variants]/(main)/agent/_layout/Sidebar/Cron/CronTopicGroup.tsx +84 -0
- package/src/app/[variants]/(main)/agent/_layout/Sidebar/{Topic/CronTopicList → Cron}/CronTopicItem.tsx +1 -1
- package/src/app/[variants]/(main)/agent/_layout/Sidebar/{Topic/CronTopicList → Cron}/index.tsx +23 -33
- package/src/app/[variants]/(main)/agent/_layout/Sidebar/Topic/List/Item/Editing.tsx +12 -49
- package/src/app/[variants]/(main)/agent/_layout/Sidebar/Topic/List/index.tsx +3 -1
- package/src/app/[variants]/(main)/agent/_layout/Sidebar/Topic/TopicListContent/ThreadList/ThreadItem/Editing.tsx +12 -40
- package/src/app/[variants]/(main)/agent/_layout/Sidebar/Topic/hooks/useTopicNavigation.ts +5 -1
- package/src/app/[variants]/(main)/agent/features/Conversation/MainChatInput/index.tsx +2 -2
- package/src/app/[variants]/(main)/agent/profile/features/AgentCronJobs/CronJobCards.tsx +1 -1
- package/src/app/[variants]/(main)/agent/profile/features/AgentCronJobs/CronJobForm.tsx +1 -1
- package/src/app/[variants]/(main)/group/_layout/Sidebar/AddGroupMemberModal/AvailableAgentList.tsx +0 -1
- package/src/app/[variants]/(main)/group/_layout/Sidebar/AddGroupMemberModal/index.tsx +5 -1
- package/src/app/[variants]/(main)/home/_layout/Body/Agent/List/AgentGroupItem/index.tsx +2 -6
- package/src/app/[variants]/(main)/home/_layout/Body/Agent/List/AgentGroupItem/useDropdownMenu.tsx +100 -0
- package/src/app/[variants]/(main)/home/_layout/Body/Agent/List/AgentItem/index.tsx +2 -4
- package/src/app/[variants]/(main)/home/_layout/Body/Agent/List/AgentItem/useDropdownMenu.tsx +149 -0
- package/src/app/[variants]/(main)/home/_layout/hooks/index.ts +0 -1
- package/src/app/[variants]/(main)/home/features/InputArea/index.tsx +1 -1
- package/src/components/InlineRename/index.tsx +121 -0
- package/src/features/ChatInput/InputEditor/index.tsx +1 -0
- package/src/features/EditorCanvas/DiffAllToolbar.tsx +1 -1
- package/src/features/NavPanel/components/NavItem.tsx +1 -1
- package/src/locales/default/setting.ts +2 -0
- package/src/server/routers/lambda/agent.ts +15 -0
- package/src/server/routers/lambda/agentGroup.ts +16 -0
- package/src/services/agent.ts +11 -0
- package/src/services/chatGroup/index.ts +11 -0
- package/src/store/agent/slices/cron/action.ts +108 -0
- package/src/store/agent/slices/cron/index.ts +1 -0
- package/src/store/agent/store.ts +3 -0
- package/src/store/home/slices/sidebarUI/action.test.ts +23 -22
- package/src/store/home/slices/sidebarUI/action.ts +37 -9
- package/src/app/[variants]/(main)/agent/_layout/Sidebar/Topic/CronTopicList/CronTopicGroup.tsx +0 -74
- package/src/app/[variants]/(main)/group/features/ChangelogModal.tsx +0 -11
- package/src/app/[variants]/(main)/home/_layout/Body/Agent/List/Item/useDropdownMenu.tsx +0 -62
- package/src/app/[variants]/(main)/home/_layout/hooks/useSessionItemMenuItems.tsx +0 -238
- package/src/hooks/useFetchCronTopicsWithJobInfo.ts +0 -56
|
@@ -3,7 +3,9 @@ import { INBOX_SESSION_ID } from '@lobechat/const';
|
|
|
3
3
|
import { eq } from 'drizzle-orm';
|
|
4
4
|
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
5
5
|
|
|
6
|
+
import { getTestDB } from '../../core/getTestDB';
|
|
6
7
|
import {
|
|
8
|
+
NewAgent,
|
|
7
9
|
agents,
|
|
8
10
|
agentsFiles,
|
|
9
11
|
agentsKnowledgeBases,
|
|
@@ -11,12 +13,12 @@ import {
|
|
|
11
13
|
documents,
|
|
12
14
|
files,
|
|
13
15
|
knowledgeBases,
|
|
16
|
+
sessionGroups,
|
|
14
17
|
sessions,
|
|
15
18
|
users,
|
|
16
19
|
} from '../../schemas';
|
|
17
20
|
import { LobeChatDatabase } from '../../type';
|
|
18
21
|
import { AgentModel } from '../agent';
|
|
19
|
-
import { getTestDB } from '../../core/getTestDB';
|
|
20
22
|
|
|
21
23
|
const serverDB: LobeChatDatabase = await getTestDB();
|
|
22
24
|
|
|
@@ -290,9 +292,7 @@ describe('AgentModel', () => {
|
|
|
290
292
|
// Create agent and session for user2
|
|
291
293
|
await serverDB.insert(agents).values({ id: agentId, userId: userId2 });
|
|
292
294
|
await serverDB.insert(sessions).values({ id: sessionId, userId: userId2 });
|
|
293
|
-
await serverDB
|
|
294
|
-
.insert(agentsToSessions)
|
|
295
|
-
.values({ agentId, sessionId, userId: userId2 });
|
|
295
|
+
await serverDB.insert(agentsToSessions).values({ agentId, sessionId, userId: userId2 });
|
|
296
296
|
|
|
297
297
|
// Try to access with user1's model
|
|
298
298
|
const result = await agentModel.findBySessionId(sessionId);
|
|
@@ -1390,6 +1390,167 @@ describe('AgentModel', () => {
|
|
|
1390
1390
|
});
|
|
1391
1391
|
});
|
|
1392
1392
|
|
|
1393
|
+
describe('duplicate', () => {
|
|
1394
|
+
it('should duplicate an agent with all config fields', async () => {
|
|
1395
|
+
// Create source agent with full config
|
|
1396
|
+
const [sourceAgent] = await serverDB
|
|
1397
|
+
.insert(agents)
|
|
1398
|
+
.values({
|
|
1399
|
+
userId,
|
|
1400
|
+
title: 'Original Agent',
|
|
1401
|
+
description: 'Original description',
|
|
1402
|
+
tags: ['tag1', 'tag2'],
|
|
1403
|
+
avatar: 'avatar-url',
|
|
1404
|
+
backgroundColor: '#ffffff',
|
|
1405
|
+
plugins: ['plugin1'],
|
|
1406
|
+
model: 'gpt-4',
|
|
1407
|
+
provider: 'openai',
|
|
1408
|
+
systemRole: 'You are helpful',
|
|
1409
|
+
openingMessage: 'Hello!',
|
|
1410
|
+
openingQuestions: ['Q1', 'Q2'],
|
|
1411
|
+
chatConfig: { historyCount: 10 },
|
|
1412
|
+
fewShots: [{ role: 'user', content: 'test' }],
|
|
1413
|
+
params: { temperature: 0.7 },
|
|
1414
|
+
tts: { showAllLocaleVoice: true },
|
|
1415
|
+
} as NewAgent)
|
|
1416
|
+
.returning();
|
|
1417
|
+
|
|
1418
|
+
const result = await agentModel.duplicate(sourceAgent.id);
|
|
1419
|
+
|
|
1420
|
+
expect(result).toBeDefined();
|
|
1421
|
+
expect(result?.agentId).toBeDefined();
|
|
1422
|
+
expect(result?.agentId).not.toBe(sourceAgent.id);
|
|
1423
|
+
|
|
1424
|
+
// Verify the duplicated agent
|
|
1425
|
+
const duplicatedAgent = await serverDB.query.agents.findFirst({
|
|
1426
|
+
where: eq(agents.id, result!.agentId),
|
|
1427
|
+
});
|
|
1428
|
+
|
|
1429
|
+
expect(duplicatedAgent).toEqual(
|
|
1430
|
+
expect.objectContaining({
|
|
1431
|
+
// Should be copied
|
|
1432
|
+
title: 'Original Agent (Copy)',
|
|
1433
|
+
description: 'Original description',
|
|
1434
|
+
tags: ['tag1', 'tag2'],
|
|
1435
|
+
avatar: 'avatar-url',
|
|
1436
|
+
backgroundColor: '#ffffff',
|
|
1437
|
+
plugins: ['plugin1'],
|
|
1438
|
+
model: 'gpt-4',
|
|
1439
|
+
provider: 'openai',
|
|
1440
|
+
systemRole: 'You are helpful',
|
|
1441
|
+
openingMessage: 'Hello!',
|
|
1442
|
+
openingQuestions: ['Q1', 'Q2'],
|
|
1443
|
+
chatConfig: { historyCount: 10 },
|
|
1444
|
+
fewShots: [{ role: 'user', content: 'test' }],
|
|
1445
|
+
params: { temperature: 0.7 },
|
|
1446
|
+
tts: { showAllLocaleVoice: true },
|
|
1447
|
+
sessionGroupId: null,
|
|
1448
|
+
userId,
|
|
1449
|
+
// Should NOT be copied (new values)
|
|
1450
|
+
virtual: false,
|
|
1451
|
+
pinned: null,
|
|
1452
|
+
clientId: null,
|
|
1453
|
+
editorData: null,
|
|
1454
|
+
marketIdentifier: null,
|
|
1455
|
+
}),
|
|
1456
|
+
);
|
|
1457
|
+
|
|
1458
|
+
// Verify these are NOT copied from source
|
|
1459
|
+
expect(duplicatedAgent?.id).not.toBe(sourceAgent.id);
|
|
1460
|
+
expect(duplicatedAgent?.slug).not.toBe(sourceAgent.slug);
|
|
1461
|
+
});
|
|
1462
|
+
|
|
1463
|
+
it('should use provided title when duplicating', async () => {
|
|
1464
|
+
const [sourceAgent] = await serverDB
|
|
1465
|
+
.insert(agents)
|
|
1466
|
+
.values({ userId, title: 'Original' })
|
|
1467
|
+
.returning();
|
|
1468
|
+
|
|
1469
|
+
const result = await agentModel.duplicate(sourceAgent.id, 'Custom Title');
|
|
1470
|
+
|
|
1471
|
+
const duplicatedAgent = await serverDB.query.agents.findFirst({
|
|
1472
|
+
where: eq(agents.id, result!.agentId),
|
|
1473
|
+
});
|
|
1474
|
+
|
|
1475
|
+
expect(duplicatedAgent?.title).toBe('Custom Title');
|
|
1476
|
+
});
|
|
1477
|
+
|
|
1478
|
+
it('should return null for non-existent agent', async () => {
|
|
1479
|
+
const result = await agentModel.duplicate('non-existent-id');
|
|
1480
|
+
|
|
1481
|
+
expect(result).toBeNull();
|
|
1482
|
+
});
|
|
1483
|
+
|
|
1484
|
+
it('should not duplicate another user agent', async () => {
|
|
1485
|
+
const [sourceAgent] = await serverDB
|
|
1486
|
+
.insert(agents)
|
|
1487
|
+
.values({ userId: userId2, title: 'User2 Agent' })
|
|
1488
|
+
.returning();
|
|
1489
|
+
|
|
1490
|
+
const result = await agentModel.duplicate(sourceAgent.id);
|
|
1491
|
+
|
|
1492
|
+
expect(result).toBeNull();
|
|
1493
|
+
});
|
|
1494
|
+
|
|
1495
|
+
it('should not copy marketIdentifier, slug, or id', async () => {
|
|
1496
|
+
const [sourceAgent] = await serverDB
|
|
1497
|
+
.insert(agents)
|
|
1498
|
+
.values({
|
|
1499
|
+
userId,
|
|
1500
|
+
title: 'Original',
|
|
1501
|
+
slug: 'original-slug',
|
|
1502
|
+
marketIdentifier: 'market-123',
|
|
1503
|
+
})
|
|
1504
|
+
.returning();
|
|
1505
|
+
|
|
1506
|
+
const result = await agentModel.duplicate(sourceAgent.id);
|
|
1507
|
+
|
|
1508
|
+
const duplicatedAgent = await serverDB.query.agents.findFirst({
|
|
1509
|
+
where: eq(agents.id, result!.agentId),
|
|
1510
|
+
});
|
|
1511
|
+
|
|
1512
|
+
expect(duplicatedAgent?.id).not.toBe(sourceAgent.id);
|
|
1513
|
+
expect(duplicatedAgent?.slug).not.toBe('original-slug');
|
|
1514
|
+
expect(duplicatedAgent?.marketIdentifier).toBeNull();
|
|
1515
|
+
});
|
|
1516
|
+
|
|
1517
|
+
it('should preserve sessionGroupId when duplicating', async () => {
|
|
1518
|
+
// Create a session group
|
|
1519
|
+
const [sessionGroup] = await serverDB
|
|
1520
|
+
.insert(sessionGroups)
|
|
1521
|
+
.values({ userId, name: 'Test Group' })
|
|
1522
|
+
.returning();
|
|
1523
|
+
|
|
1524
|
+
const [sourceAgent] = await serverDB
|
|
1525
|
+
.insert(agents)
|
|
1526
|
+
.values({ userId, title: 'Agent in Group', sessionGroupId: sessionGroup.id })
|
|
1527
|
+
.returning();
|
|
1528
|
+
|
|
1529
|
+
const result = await agentModel.duplicate(sourceAgent.id);
|
|
1530
|
+
|
|
1531
|
+
const duplicatedAgent = await serverDB.query.agents.findFirst({
|
|
1532
|
+
where: eq(agents.id, result!.agentId),
|
|
1533
|
+
});
|
|
1534
|
+
|
|
1535
|
+
expect(duplicatedAgent?.sessionGroupId).toBe(sessionGroup.id);
|
|
1536
|
+
});
|
|
1537
|
+
|
|
1538
|
+
it('should handle agent with null title', async () => {
|
|
1539
|
+
const [sourceAgent] = await serverDB
|
|
1540
|
+
.insert(agents)
|
|
1541
|
+
.values({ userId, title: null })
|
|
1542
|
+
.returning();
|
|
1543
|
+
|
|
1544
|
+
const result = await agentModel.duplicate(sourceAgent.id);
|
|
1545
|
+
|
|
1546
|
+
const duplicatedAgent = await serverDB.query.agents.findFirst({
|
|
1547
|
+
where: eq(agents.id, result!.agentId),
|
|
1548
|
+
});
|
|
1549
|
+
|
|
1550
|
+
expect(duplicatedAgent?.title).toBe('Copy');
|
|
1551
|
+
});
|
|
1552
|
+
});
|
|
1553
|
+
|
|
1393
1554
|
describe('queryAgents', () => {
|
|
1394
1555
|
it('should return non-virtual agents for the user', async () => {
|
|
1395
1556
|
// Create non-virtual agents
|
|
@@ -452,6 +452,52 @@ export class AgentModel {
|
|
|
452
452
|
return result[0];
|
|
453
453
|
};
|
|
454
454
|
|
|
455
|
+
/**
|
|
456
|
+
* Duplicate an agent.
|
|
457
|
+
* Returns the new agent ID.
|
|
458
|
+
*/
|
|
459
|
+
duplicate = async (agentId: string, newTitle?: string): Promise<{ agentId: string } | null> => {
|
|
460
|
+
// Get the source agent
|
|
461
|
+
const sourceAgent = await this.db.query.agents.findFirst({
|
|
462
|
+
where: and(eq(agents.id, agentId), eq(agents.userId, this.userId)),
|
|
463
|
+
});
|
|
464
|
+
|
|
465
|
+
if (!sourceAgent) return null;
|
|
466
|
+
|
|
467
|
+
// Create new agent with explicit include fields
|
|
468
|
+
const [newAgent] = await this.db
|
|
469
|
+
.insert(agents)
|
|
470
|
+
.values({
|
|
471
|
+
avatar: sourceAgent.avatar,
|
|
472
|
+
backgroundColor: sourceAgent.backgroundColor,
|
|
473
|
+
chatConfig: sourceAgent.chatConfig,
|
|
474
|
+
description: sourceAgent.description,
|
|
475
|
+
fewShots: sourceAgent.fewShots,
|
|
476
|
+
model: sourceAgent.model,
|
|
477
|
+
openingMessage: sourceAgent.openingMessage,
|
|
478
|
+
openingQuestions: sourceAgent.openingQuestions,
|
|
479
|
+
params: sourceAgent.params,
|
|
480
|
+
pinned: sourceAgent.pinned,
|
|
481
|
+
// Config
|
|
482
|
+
plugins: sourceAgent.plugins,
|
|
483
|
+
provider: sourceAgent.provider,
|
|
484
|
+
|
|
485
|
+
// Session group
|
|
486
|
+
sessionGroupId: sourceAgent.sessionGroupId,
|
|
487
|
+
systemRole: sourceAgent.systemRole,
|
|
488
|
+
|
|
489
|
+
tags: sourceAgent.tags,
|
|
490
|
+
// Metadata
|
|
491
|
+
title: newTitle || (sourceAgent.title ? `${sourceAgent.title} (Copy)` : 'Copy'),
|
|
492
|
+
tts: sourceAgent.tts,
|
|
493
|
+
// User
|
|
494
|
+
userId: this.userId,
|
|
495
|
+
})
|
|
496
|
+
.returning();
|
|
497
|
+
|
|
498
|
+
return { agentId: newAgent.id };
|
|
499
|
+
};
|
|
500
|
+
|
|
455
501
|
/**
|
|
456
502
|
* Get a builtin agent by slug, creating it if it doesn't exist.
|
|
457
503
|
* Builtin agents are standalone agents not bound to sessions.
|