@lobehub/chat 1.77.5 → 1.77.7
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/scripts/pr-comment.js +80 -0
- package/.github/scripts/pr-release-body.js +59 -0
- package/.github/workflows/release-desktop.yml +331 -0
- package/.github/workflows/test.yml +1 -1
- package/CHANGELOG.md +50 -0
- package/changelog/v1.json +18 -0
- package/next.config.ts +16 -11
- package/package.json +92 -89
- package/packages/electron-client-ipc/README.md +48 -0
- package/packages/electron-client-ipc/package.json +7 -0
- package/packages/electron-client-ipc/src/events/devtools.ts +6 -0
- package/packages/electron-client-ipc/src/events/index.ts +13 -0
- package/packages/electron-client-ipc/src/index.ts +2 -0
- package/packages/electron-client-ipc/src/types/dispatch.ts +10 -0
- package/packages/electron-client-ipc/src/types/index.ts +1 -0
- package/packages/electron-server-ipc/README.md +1 -1
- package/pnpm-workspace.yaml +1 -0
- package/scripts/setup-test-postgres-db.sh +21 -0
- package/src/app/desktop/devtools/page.tsx +89 -0
- package/src/app/desktop/layout.tsx +31 -0
- package/src/app/layout.tsx +11 -0
- package/src/app/not-found.tsx +1 -0
- package/src/const/desktop.ts +1 -0
- package/src/const/version.ts +2 -0
- package/src/database/client/db.ts +3 -10
- package/src/database/core/db-adaptor.ts +20 -2
- package/src/database/models/__tests__/aiProvider.test.ts +2 -0
- package/src/database/models/__tests__/message.test.ts +97 -26
- package/src/database/models/__tests__/session.test.ts +2 -0
- package/src/database/models/drizzleMigration.ts +15 -0
- package/src/database/models/message.ts +10 -5
- package/src/database/models/session.ts +8 -0
- package/src/database/models/user.ts +10 -1
- package/src/database/server/index.ts +1 -1
- package/src/features/DevPanel/features/FloatPanel.tsx +23 -6
- package/src/features/User/UserPanel/index.tsx +10 -6
- package/src/libs/trpc/async/index.ts +11 -1
- package/src/libs/trpc/lambda/index.ts +1 -0
- package/src/libs/trpc/lambda/serverDatabase.ts +10 -0
- package/src/libs/trpc/middleware/userAuth.ts +10 -0
- package/src/server/routers/async/file.ts +4 -5
- package/src/server/routers/async/ragEval.ts +3 -4
- package/src/server/routers/lambda/_template.ts +3 -5
- package/src/server/routers/lambda/agent.ts +8 -8
- package/src/server/routers/lambda/aiModel.ts +5 -5
- package/src/server/routers/lambda/aiProvider.test.ts +0 -2
- package/src/server/routers/lambda/aiProvider.ts +5 -5
- package/src/server/routers/lambda/chunk.ts +18 -15
- package/src/server/routers/lambda/exporter.ts +4 -4
- package/src/server/routers/lambda/file.ts +5 -5
- package/src/server/routers/lambda/importer.ts +3 -3
- package/src/server/routers/lambda/knowledgeBase.ts +3 -3
- package/src/server/routers/lambda/message.ts +5 -3
- package/src/server/routers/lambda/plugin.ts +5 -3
- package/src/server/routers/lambda/ragEval.ts +17 -14
- package/src/server/routers/lambda/session.ts +6 -4
- package/src/server/routers/lambda/sessionGroup.ts +3 -3
- package/src/server/routers/lambda/thread.ts +4 -4
- package/src/server/routers/lambda/topic.ts +5 -3
- package/src/server/routers/lambda/user.ts +7 -7
- package/src/server/routers/tools/__tests__/search.test.ts +1 -0
- package/src/server/services/nextAuthUser/index.test.ts +1 -2
- package/src/server/translation.test.ts +72 -52
- package/src/server/translation.ts +2 -11
- package/src/services/electron/devtools.ts +9 -0
- package/src/styles/electron.ts +14 -0
- package/src/tools/web-browsing/Portal/Search/ResultList/SearchItem/index.tsx +3 -8
- package/src/tools/web-browsing/Render/Search/SearchResult/ShowMore.tsx +2 -4
- package/src/types/electron.ts +11 -0
- package/src/utils/electron/dispatch.ts +10 -0
- package/tsconfig.json +6 -6
- package/vitest.config.ts +3 -1
- package/vitest.server.config.ts +7 -3
@@ -1,6 +1,24 @@
|
|
1
|
-
// import { isDesktop } from '@/const/version';
|
2
1
|
import { getDBInstance } from '@/database/core/web-server';
|
2
|
+
import { LobeChatDatabase } from '@/database/type';
|
3
3
|
|
4
|
-
|
4
|
+
/**
|
5
|
+
* 懒加载数据库实例
|
6
|
+
* 避免每次模块导入时都初始化数据库
|
7
|
+
*/
|
8
|
+
let cachedDB: LobeChatDatabase | null = null;
|
9
|
+
|
10
|
+
export const getServerDB = async (): Promise<LobeChatDatabase> => {
|
11
|
+
// 如果已经有缓存的实例,直接返回
|
12
|
+
if (cachedDB) return cachedDB;
|
13
|
+
|
14
|
+
try {
|
15
|
+
// 根据环境选择合适的数据库实例
|
16
|
+
cachedDB = getDBInstance();
|
17
|
+
return cachedDB;
|
18
|
+
} catch (error) {
|
19
|
+
console.error('❌ Failed to initialize database:', error);
|
20
|
+
throw error;
|
21
|
+
}
|
22
|
+
};
|
5
23
|
|
6
24
|
export const serverDB = getDBInstance();
|
@@ -4,6 +4,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
4
4
|
|
5
5
|
import { LobeChatDatabase } from '@/database/type';
|
6
6
|
import { ModelProvider } from '@/libs/agent-runtime';
|
7
|
+
import { sleep } from '@/utils/sleep';
|
7
8
|
|
8
9
|
import { aiProviders, users } from '../../schemas';
|
9
10
|
import { AiProviderModel } from '../aiProvider';
|
@@ -96,6 +97,7 @@ describe('AiProviderModel', () => {
|
|
96
97
|
describe('query', () => {
|
97
98
|
it('should query ai providers for the user', async () => {
|
98
99
|
await aiProviderModel.create({ name: 'AiHubMix', source: 'custom', id: 'aihubmix' });
|
100
|
+
await sleep(10);
|
99
101
|
await aiProviderModel.create({ name: 'AiHubMix', source: 'custom', id: 'aihubmix-2' });
|
100
102
|
|
101
103
|
const userGroups = await aiProviderModel.query();
|
@@ -1660,7 +1660,15 @@ describe('MessageModel', () => {
|
|
1660
1660
|
|
1661
1661
|
describe('getHeatmaps', () => {
|
1662
1662
|
it('should return heatmap data for the last year', async () => {
|
1663
|
-
|
1663
|
+
// 使用固定日期进行测试
|
1664
|
+
vi.useFakeTimers();
|
1665
|
+
const fixedDate = new Date('2023-04-07T13:00:00Z');
|
1666
|
+
vi.setSystemTime(fixedDate);
|
1667
|
+
|
1668
|
+
const today = dayjs(fixedDate);
|
1669
|
+
const twoDaysAgoDate = today.subtract(2, 'day').format('YYYY-MM-DD');
|
1670
|
+
const oneDayAgoDate = today.subtract(1, 'day').format('YYYY-MM-DD');
|
1671
|
+
const todayDate = today.format('YYYY-MM-DD');
|
1664
1672
|
|
1665
1673
|
// 创建测试数据
|
1666
1674
|
await serverDB.insert(messages).values([
|
@@ -1695,31 +1703,39 @@ describe('MessageModel', () => {
|
|
1695
1703
|
expect(result.length).toBeLessThan(368);
|
1696
1704
|
|
1697
1705
|
// 检查两天前的数据
|
1698
|
-
const twoDaysAgo = result.find(
|
1699
|
-
(item) => item.date === today.subtract(2, 'day').format('YYYY-MM-DD'),
|
1700
|
-
);
|
1706
|
+
const twoDaysAgo = result.find((item) => item.date === twoDaysAgoDate);
|
1701
1707
|
expect(twoDaysAgo?.count).toBe(2);
|
1702
1708
|
expect(twoDaysAgo?.level).toBe(1);
|
1703
1709
|
|
1704
1710
|
// 检查一天前的数据
|
1705
|
-
const oneDayAgo = result.find(
|
1706
|
-
(item) => item.date === today.subtract(1, 'day').format('YYYY-MM-DD'),
|
1707
|
-
);
|
1711
|
+
const oneDayAgo = result.find((item) => item.date === oneDayAgoDate);
|
1708
1712
|
expect(oneDayAgo?.count).toBe(1);
|
1709
1713
|
expect(oneDayAgo?.level).toBe(1);
|
1710
1714
|
|
1711
1715
|
// 检查今天的数据
|
1712
|
-
const todayData = result.find((item) => item.date ===
|
1716
|
+
const todayData = result.find((item) => item.date === todayDate);
|
1713
1717
|
expect(todayData?.count).toBe(0);
|
1714
1718
|
expect(todayData?.level).toBe(0);
|
1719
|
+
|
1720
|
+
vi.useRealTimers();
|
1715
1721
|
});
|
1716
1722
|
|
1717
1723
|
it('should calculate correct levels based on message count', async () => {
|
1718
|
-
|
1724
|
+
// 使用固定日期进行测试
|
1725
|
+
vi.useFakeTimers();
|
1726
|
+
const fixedDate = new Date('2023-05-15T12:00:00Z');
|
1727
|
+
vi.setSystemTime(fixedDate);
|
1728
|
+
|
1729
|
+
const today = dayjs(fixedDate);
|
1730
|
+
const fourDaysAgoDate = today.subtract(4, 'day').format('YYYY-MM-DD');
|
1731
|
+
const threeDaysAgoDate = today.subtract(3, 'day').format('YYYY-MM-DD');
|
1732
|
+
const twoDaysAgoDate = today.subtract(2, 'day').format('YYYY-MM-DD');
|
1733
|
+
const oneDayAgoDate = today.subtract(1, 'day').format('YYYY-MM-DD');
|
1734
|
+
const todayDate = today.format('YYYY-MM-DD');
|
1719
1735
|
|
1720
1736
|
// 创建测试数据 - 不同数量的消息以测试不同的等级
|
1721
1737
|
await serverDB.insert(messages).values([
|
1722
|
-
// 1 message - level
|
1738
|
+
// 1 message - level 1
|
1723
1739
|
{
|
1724
1740
|
id: '1',
|
1725
1741
|
userId,
|
@@ -1727,7 +1743,7 @@ describe('MessageModel', () => {
|
|
1727
1743
|
content: 'message 1',
|
1728
1744
|
createdAt: today.subtract(4, 'day').toDate(),
|
1729
1745
|
},
|
1730
|
-
// 6 messages - level
|
1746
|
+
// 6 messages - level 2
|
1731
1747
|
...Array(6)
|
1732
1748
|
.fill(0)
|
1733
1749
|
.map((_, i) => ({
|
@@ -1737,7 +1753,7 @@ describe('MessageModel', () => {
|
|
1737
1753
|
content: `message 2-${i}`,
|
1738
1754
|
createdAt: today.subtract(3, 'day').toDate(),
|
1739
1755
|
})),
|
1740
|
-
// 11 messages - level
|
1756
|
+
// 11 messages - level 3
|
1741
1757
|
...Array(11)
|
1742
1758
|
.fill(0)
|
1743
1759
|
.map((_, i) => ({
|
@@ -1747,7 +1763,7 @@ describe('MessageModel', () => {
|
|
1747
1763
|
content: `message 3-${i}`,
|
1748
1764
|
createdAt: today.subtract(2, 'day').toDate(),
|
1749
1765
|
})),
|
1750
|
-
// 16 messages - level
|
1766
|
+
// 16 messages - level 4
|
1751
1767
|
...Array(16)
|
1752
1768
|
.fill(0)
|
1753
1769
|
.map((_, i) => ({
|
@@ -1773,33 +1789,88 @@ describe('MessageModel', () => {
|
|
1773
1789
|
const result = await messageModel.getHeatmaps();
|
1774
1790
|
|
1775
1791
|
// 检查不同天数的等级
|
1776
|
-
const fourDaysAgo = result.find(
|
1777
|
-
(item) => item.date === today.subtract(4, 'day').format('YYYY-MM-DD'),
|
1778
|
-
);
|
1792
|
+
const fourDaysAgo = result.find((item) => item.date === fourDaysAgoDate);
|
1779
1793
|
expect(fourDaysAgo?.count).toBe(1);
|
1780
1794
|
expect(fourDaysAgo?.level).toBe(1);
|
1781
1795
|
|
1782
|
-
const threeDaysAgo = result.find(
|
1783
|
-
(item) => item.date === today.subtract(3, 'day').format('YYYY-MM-DD'),
|
1784
|
-
);
|
1796
|
+
const threeDaysAgo = result.find((item) => item.date === threeDaysAgoDate);
|
1785
1797
|
expect(threeDaysAgo?.count).toBe(6);
|
1786
1798
|
expect(threeDaysAgo?.level).toBe(2);
|
1787
1799
|
|
1788
|
-
const twoDaysAgo = result.find(
|
1789
|
-
(item) => item.date === today.subtract(2, 'day').format('YYYY-MM-DD'),
|
1790
|
-
);
|
1800
|
+
const twoDaysAgo = result.find((item) => item.date === twoDaysAgoDate);
|
1791
1801
|
expect(twoDaysAgo?.count).toBe(11);
|
1792
1802
|
expect(twoDaysAgo?.level).toBe(3);
|
1793
1803
|
|
1794
|
-
const oneDayAgo = result.find(
|
1795
|
-
(item) => item.date === today.subtract(1, 'day').format('YYYY-MM-DD'),
|
1796
|
-
);
|
1804
|
+
const oneDayAgo = result.find((item) => item.date === oneDayAgoDate);
|
1797
1805
|
expect(oneDayAgo?.count).toBe(16);
|
1798
1806
|
expect(oneDayAgo?.level).toBe(4);
|
1799
1807
|
|
1800
|
-
const todayData = result.find((item) => item.date ===
|
1808
|
+
const todayData = result.find((item) => item.date === todayDate);
|
1801
1809
|
expect(todayData?.count).toBe(21);
|
1802
1810
|
expect(todayData?.level).toBe(4);
|
1811
|
+
|
1812
|
+
vi.useRealTimers();
|
1813
|
+
});
|
1814
|
+
|
1815
|
+
it.skip('should return time count correctly when 19:00 time', async () => {
|
1816
|
+
// 使用固定日期进行测试
|
1817
|
+
vi.useFakeTimers();
|
1818
|
+
const fixedDate = new Date('2025-04-02T19:00:00Z');
|
1819
|
+
vi.setSystemTime(fixedDate);
|
1820
|
+
|
1821
|
+
const today = dayjs(fixedDate);
|
1822
|
+
const twoDaysAgoDate = today.subtract(2, 'day').format('YYYY-MM-DD');
|
1823
|
+
const oneDayAgoDate = today.subtract(1, 'day').format('YYYY-MM-DD');
|
1824
|
+
const todayDate = today.format('YYYY-MM-DD');
|
1825
|
+
|
1826
|
+
// 创建测试数据
|
1827
|
+
await serverDB.insert(messages).values([
|
1828
|
+
{
|
1829
|
+
id: '1',
|
1830
|
+
userId,
|
1831
|
+
role: 'user',
|
1832
|
+
content: 'message 1',
|
1833
|
+
createdAt: today.subtract(2, 'day').toDate(),
|
1834
|
+
},
|
1835
|
+
{
|
1836
|
+
id: '2',
|
1837
|
+
userId,
|
1838
|
+
role: 'user',
|
1839
|
+
content: 'message 2',
|
1840
|
+
createdAt: today.subtract(2, 'day').toDate(),
|
1841
|
+
},
|
1842
|
+
{
|
1843
|
+
id: '3',
|
1844
|
+
userId,
|
1845
|
+
role: 'user',
|
1846
|
+
content: 'message 3',
|
1847
|
+
createdAt: today.subtract(1, 'day').toDate(),
|
1848
|
+
},
|
1849
|
+
]);
|
1850
|
+
|
1851
|
+
// 调用 getHeatmaps 方法
|
1852
|
+
const result = await messageModel.getHeatmaps();
|
1853
|
+
|
1854
|
+
// 断言结果
|
1855
|
+
expect(result.length).toBeGreaterThanOrEqual(366);
|
1856
|
+
expect(result.length).toBeLessThan(368);
|
1857
|
+
|
1858
|
+
// 检查两天前的数据
|
1859
|
+
const twoDaysAgo = result.find((item) => item.date === twoDaysAgoDate);
|
1860
|
+
expect(twoDaysAgo?.count).toBe(2);
|
1861
|
+
expect(twoDaysAgo?.level).toBe(1);
|
1862
|
+
|
1863
|
+
// 检查一天前的数据
|
1864
|
+
const oneDayAgo = result.find((item) => item.date === oneDayAgoDate);
|
1865
|
+
expect(oneDayAgo?.count).toBe(1);
|
1866
|
+
expect(oneDayAgo?.level).toBe(1);
|
1867
|
+
|
1868
|
+
// 检查今天的数据
|
1869
|
+
const todayData = result.find((item) => item.date === todayDate);
|
1870
|
+
expect(todayData?.count).toBe(0);
|
1871
|
+
expect(todayData?.level).toBe(0);
|
1872
|
+
|
1873
|
+
vi.useRealTimers();
|
1803
1874
|
});
|
1804
1875
|
|
1805
1876
|
it('should handle empty data', async () => {
|
@@ -894,7 +894,9 @@ describe('SessionModel', () => {
|
|
894
894
|
await trx.insert(topics).values([
|
895
895
|
{ id: 't1', sessionId: '1', userId },
|
896
896
|
{ id: 't2', sessionId: '1', userId },
|
897
|
+
{ id: 't6', sessionId: '1', userId },
|
897
898
|
{ id: 't3', sessionId: '2', userId },
|
899
|
+
{ id: 't8', sessionId: '2', userId },
|
898
900
|
{ id: 't4', sessionId: '3', userId },
|
899
901
|
]);
|
900
902
|
});
|
@@ -1,3 +1,5 @@
|
|
1
|
+
import { sql } from 'drizzle-orm';
|
2
|
+
|
1
3
|
import { LobeChatDatabase } from '@/database/type';
|
2
4
|
import { MigrationTableItem } from '@/types/clientDB';
|
3
5
|
|
@@ -8,6 +10,19 @@ export class DrizzleMigrationModel {
|
|
8
10
|
this.db = db;
|
9
11
|
}
|
10
12
|
|
13
|
+
getTableCounts = async () => {
|
14
|
+
// 这里使用 pg_tables 系统表查询用户表数量
|
15
|
+
const result = await this.db.execute(
|
16
|
+
sql`
|
17
|
+
SELECT COUNT(*) as table_count
|
18
|
+
FROM information_schema.tables
|
19
|
+
WHERE table_schema = 'public'
|
20
|
+
`,
|
21
|
+
);
|
22
|
+
|
23
|
+
return parseInt((result.rows[0] as any).table_count || '0');
|
24
|
+
};
|
25
|
+
|
11
26
|
getMigrationList = async () => {
|
12
27
|
const res = await this.db.execute(
|
13
28
|
'SELECT * FROM "drizzle"."__drizzle_migrations" ORDER BY "created_at" DESC;',
|
@@ -382,15 +382,20 @@ export class MessageModel {
|
|
382
382
|
.orderBy(desc(sql`heatmaps_date`));
|
383
383
|
|
384
384
|
const heatmapData: HeatmapsProps['data'] = [];
|
385
|
-
let currentDate = startDate;
|
385
|
+
let currentDate = startDate.clone();
|
386
|
+
|
387
|
+
const dateCountMap = new Map<string, number>();
|
388
|
+
for (const item of result) {
|
389
|
+
if (item?.date) {
|
390
|
+
const dateStr = dayjs(item.date as string).format('YYYY-MM-DD');
|
391
|
+
dateCountMap.set(dateStr, Number(item.count) || 0);
|
392
|
+
}
|
393
|
+
}
|
386
394
|
|
387
395
|
while (currentDate.isBefore(endDate) || currentDate.isSame(endDate, 'day')) {
|
388
396
|
const formattedDate = currentDate.format('YYYY-MM-DD');
|
389
|
-
const
|
390
|
-
(r) => r?.date && dayjs(r.date as string).format('YYYY-MM-DD') === formattedDate,
|
391
|
-
);
|
397
|
+
const count = dateCountMap.get(formattedDate) || 0;
|
392
398
|
|
393
|
-
const count = matchingResult ? matchingResult.count : 0;
|
394
399
|
const levelCount = count > 0 ? Math.ceil(count / 5) : 0;
|
395
400
|
const level = levelCount > 4 ? 4 : levelCount;
|
396
401
|
|
@@ -193,6 +193,14 @@ export class SessionModel {
|
|
193
193
|
type: 'agent' | 'group';
|
194
194
|
}): Promise<SessionItem> => {
|
195
195
|
return this.db.transaction(async (trx) => {
|
196
|
+
if (slug) {
|
197
|
+
const existResult = await trx.query.sessions.findFirst({
|
198
|
+
where: and(eq(sessions.slug, slug), eq(sessions.userId, this.userId)),
|
199
|
+
});
|
200
|
+
|
201
|
+
if (existResult) return existResult;
|
202
|
+
}
|
203
|
+
|
196
204
|
const newAgents = await trx
|
197
205
|
.insert(agents)
|
198
206
|
.values({
|
@@ -85,7 +85,13 @@ export class UserModel {
|
|
85
85
|
const state = result[0];
|
86
86
|
|
87
87
|
// Decrypt keyVaults
|
88
|
-
|
88
|
+
let decryptKeyVaults = {};
|
89
|
+
|
90
|
+
try {
|
91
|
+
decryptKeyVaults = await decryptor(state.settingsKeyVaults, this.userId);
|
92
|
+
} catch {
|
93
|
+
/* empty */
|
94
|
+
}
|
89
95
|
|
90
96
|
const settings: DeepPartial<UserSettings> = {
|
91
97
|
defaultAgent: state.settingsDefaultAgent || {},
|
@@ -171,6 +177,9 @@ export class UserModel {
|
|
171
177
|
};
|
172
178
|
|
173
179
|
// Static method
|
180
|
+
static makeSureUserExist = async (db: LobeChatDatabase, userId: string) => {
|
181
|
+
await db.insert(users).values({ id: userId }).onConflictDoNothing();
|
182
|
+
};
|
174
183
|
|
175
184
|
static createUser = async (db: LobeChatDatabase, params: NewUser) => {
|
176
185
|
// if user already exists, skip creation
|
@@ -1 +1 @@
|
|
1
|
-
export { serverDB } from '../core/db-adaptor';
|
1
|
+
export { getServerDB, serverDB } from '../core/db-adaptor';
|
@@ -4,14 +4,16 @@ import { ActionIcon, FluentEmoji, Icon, SideNav } from '@lobehub/ui';
|
|
4
4
|
import { FloatButton } from 'antd';
|
5
5
|
import { createStyles } from 'antd-style';
|
6
6
|
import { BugIcon, BugOff, XIcon } from 'lucide-react';
|
7
|
+
import { usePathname } from 'next/navigation';
|
7
8
|
import { ReactNode, memo, useEffect, useState } from 'react';
|
8
9
|
import { Flexbox } from 'react-layout-kit';
|
9
10
|
import { Rnd } from 'react-rnd';
|
10
11
|
|
11
12
|
import { BRANDING_NAME } from '@/const/branding';
|
13
|
+
import { isDesktop } from '@/const/version';
|
12
14
|
|
13
15
|
// 定义样式
|
14
|
-
const useStyles = createStyles(({ token, css, prefixCls }) => {
|
16
|
+
export const useStyles = createStyles(({ token, css, prefixCls }) => {
|
15
17
|
return {
|
16
18
|
collapsed: css`
|
17
19
|
pointer-events: none;
|
@@ -86,6 +88,7 @@ const CollapsibleFloatPanel = memo<CollapsibleFloatPanelProps>(({ items }) => {
|
|
86
88
|
const [position, setPosition] = useState({ x: 100, y: 100 });
|
87
89
|
const [size, setSize] = useState({ height: minHeight, width: minWidth });
|
88
90
|
|
91
|
+
const pathname = usePathname();
|
89
92
|
useEffect(() => {
|
90
93
|
try {
|
91
94
|
const localStoragePosition = localStorage.getItem('debug-panel-position');
|
@@ -108,11 +111,25 @@ const CollapsibleFloatPanel = memo<CollapsibleFloatPanelProps>(({ items }) => {
|
|
108
111
|
|
109
112
|
return (
|
110
113
|
<>
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
114
|
+
{
|
115
|
+
// desktop devtools 下隐藏
|
116
|
+
pathname !== '/desktop/devtools' && (
|
117
|
+
<FloatButton
|
118
|
+
className={styles.floatButton}
|
119
|
+
icon={<Icon icon={isExpanded ? BugOff : BugIcon} />}
|
120
|
+
onClick={async () => {
|
121
|
+
if (isDesktop) {
|
122
|
+
const { electronDevtoolsService } = await import('@/services/electron/devtools');
|
123
|
+
|
124
|
+
await electronDevtoolsService.openDevtools();
|
125
|
+
|
126
|
+
return;
|
127
|
+
}
|
128
|
+
setIsExpanded(!isExpanded);
|
129
|
+
}}
|
130
|
+
/>
|
131
|
+
)
|
132
|
+
}
|
116
133
|
{isExpanded && (
|
117
134
|
<Rnd
|
118
135
|
bounds="window"
|
@@ -4,16 +4,20 @@ import { Popover } from 'antd';
|
|
4
4
|
import { createStyles } from 'antd-style';
|
5
5
|
import { PropsWithChildren, memo, useState } from 'react';
|
6
6
|
|
7
|
+
import { isDesktop } from '@/const/version';
|
8
|
+
|
7
9
|
import PanelContent from './PanelContent';
|
8
10
|
import UpgradeBadge from './UpgradeBadge';
|
9
11
|
import { useNewVersion } from './useNewVersion';
|
10
12
|
|
11
|
-
const useStyles = createStyles(({ css }) =>
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
13
|
+
const useStyles = createStyles(({ css }) => {
|
14
|
+
return {
|
15
|
+
popover: css`
|
16
|
+
inset-block-start: ${isDesktop ? 24 : 8}px !important;
|
17
|
+
inset-inline-start: 8px !important;
|
18
|
+
`,
|
19
|
+
};
|
20
|
+
});
|
17
21
|
|
18
22
|
const UserPanel = memo<PropsWithChildren>(({ children }) => {
|
19
23
|
const hasNewVersion = useNewVersion();
|
@@ -1,3 +1,5 @@
|
|
1
|
+
import { getServerDB } from '@/database/core/db-adaptor';
|
2
|
+
|
1
3
|
import { asyncAuth } from './asyncAuth';
|
2
4
|
import { asyncTrpc } from './init';
|
3
5
|
|
@@ -5,6 +7,14 @@ export const publicProcedure = asyncTrpc.procedure;
|
|
5
7
|
|
6
8
|
export const asyncRouter = asyncTrpc.router;
|
7
9
|
|
8
|
-
export const asyncAuthedProcedure = asyncTrpc.procedure.use(asyncAuth)
|
10
|
+
export const asyncAuthedProcedure = asyncTrpc.procedure.use(asyncAuth).use(
|
11
|
+
asyncTrpc.middleware(async (opts) => {
|
12
|
+
const serverDB = await getServerDB();
|
13
|
+
|
14
|
+
return opts.next({
|
15
|
+
ctx: { serverDB },
|
16
|
+
});
|
17
|
+
}),
|
18
|
+
);
|
9
19
|
|
10
20
|
export const createAsyncCallerFactory = asyncTrpc.createCallerFactory;
|
@@ -0,0 +1 @@
|
|
1
|
+
export * from './serverDatabase';
|
@@ -0,0 +1,10 @@
|
|
1
|
+
import { getServerDB } from '@/database/core/db-adaptor';
|
2
|
+
import { trpc } from '@/libs/trpc/init';
|
3
|
+
|
4
|
+
export const serverDatabase = trpc.middleware(async (opts) => {
|
5
|
+
const serverDB = await getServerDB();
|
6
|
+
|
7
|
+
return opts.next({
|
8
|
+
ctx: { serverDB },
|
9
|
+
});
|
10
|
+
});
|
@@ -1,11 +1,21 @@
|
|
1
1
|
import { TRPCError } from '@trpc/server';
|
2
2
|
|
3
3
|
import { enableClerk } from '@/const/auth';
|
4
|
+
import { DESKTOP_USER_ID } from '@/const/desktop';
|
5
|
+
import { isDesktop } from '@/const/version';
|
4
6
|
|
5
7
|
import { trpc } from '../init';
|
6
8
|
|
7
9
|
export const userAuth = trpc.middleware(async (opts) => {
|
8
10
|
const { ctx } = opts;
|
11
|
+
|
12
|
+
if (isDesktop) {
|
13
|
+
return opts.next({
|
14
|
+
ctx: {
|
15
|
+
userId: DESKTOP_USER_ID,
|
16
|
+
},
|
17
|
+
});
|
18
|
+
}
|
9
19
|
// `ctx.user` is nullable
|
10
20
|
if (!ctx.userId) {
|
11
21
|
if (enableClerk) {
|
@@ -11,7 +11,6 @@ import { ChunkModel } from '@/database/models/chunk';
|
|
11
11
|
import { EmbeddingModel } from '@/database/models/embedding';
|
12
12
|
import { FileModel } from '@/database/models/file';
|
13
13
|
import { NewChunkItem, NewEmbeddingsItem } from '@/database/schemas';
|
14
|
-
import { serverDB } from '@/database/server';
|
15
14
|
import { asyncAuthedProcedure, asyncRouter as router } from '@/libs/trpc/async';
|
16
15
|
import { getServerDefaultFilesConfig } from '@/server/globalConfig';
|
17
16
|
import { initAgentRuntimeWithUserPayload } from '@/server/modules/AgentRuntime';
|
@@ -31,11 +30,11 @@ const fileProcedure = asyncAuthedProcedure.use(async (opts) => {
|
|
31
30
|
|
32
31
|
return opts.next({
|
33
32
|
ctx: {
|
34
|
-
asyncTaskModel: new AsyncTaskModel(serverDB, ctx.userId),
|
35
|
-
chunkModel: new ChunkModel(serverDB, ctx.userId),
|
33
|
+
asyncTaskModel: new AsyncTaskModel(ctx.serverDB, ctx.userId),
|
34
|
+
chunkModel: new ChunkModel(ctx.serverDB, ctx.userId),
|
36
35
|
chunkService: new ChunkService(ctx.userId),
|
37
|
-
embeddingModel: new EmbeddingModel(serverDB, ctx.userId),
|
38
|
-
fileModel: new FileModel(serverDB, ctx.userId),
|
36
|
+
embeddingModel: new EmbeddingModel(ctx.serverDB, ctx.userId),
|
37
|
+
fileModel: new FileModel(ctx.serverDB, ctx.userId),
|
39
38
|
},
|
40
39
|
});
|
41
40
|
});
|
@@ -7,7 +7,6 @@ import { DEFAULT_EMBEDDING_MODEL, DEFAULT_MODEL } from '@/const/settings';
|
|
7
7
|
import { ChunkModel } from '@/database/models/chunk';
|
8
8
|
import { EmbeddingModel } from '@/database/models/embedding';
|
9
9
|
import { FileModel } from '@/database/models/file';
|
10
|
-
import { serverDB } from '@/database/server';
|
11
10
|
import {
|
12
11
|
EvalDatasetRecordModel,
|
13
12
|
EvalEvaluationModel,
|
@@ -25,13 +24,13 @@ const ragEvalProcedure = asyncAuthedProcedure.use(async (opts) => {
|
|
25
24
|
|
26
25
|
return opts.next({
|
27
26
|
ctx: {
|
28
|
-
chunkModel: new ChunkModel(serverDB, ctx.userId),
|
27
|
+
chunkModel: new ChunkModel(ctx.serverDB, ctx.userId),
|
29
28
|
chunkService: new ChunkService(ctx.userId),
|
30
29
|
datasetRecordModel: new EvalDatasetRecordModel(ctx.userId),
|
31
|
-
embeddingModel: new EmbeddingModel(serverDB, ctx.userId),
|
30
|
+
embeddingModel: new EmbeddingModel(ctx.serverDB, ctx.userId),
|
32
31
|
evalRecordModel: new EvaluationRecordModel(ctx.userId),
|
33
32
|
evaluationModel: new EvalEvaluationModel(ctx.userId),
|
34
|
-
fileModel: new FileModel(serverDB, ctx.userId),
|
33
|
+
fileModel: new FileModel(ctx.serverDB, ctx.userId),
|
35
34
|
},
|
36
35
|
});
|
37
36
|
});
|
@@ -2,16 +2,16 @@ import { z } from 'zod';
|
|
2
2
|
|
3
3
|
import { SessionGroupModel } from '@/database/models/sessionGroup';
|
4
4
|
import { insertSessionGroupSchema } from '@/database/schemas';
|
5
|
-
import { serverDB } from '@/database/server';
|
6
5
|
import { authedProcedure, router } from '@/libs/trpc';
|
6
|
+
import { serverDatabase } from '@/libs/trpc/lambda';
|
7
7
|
import { SessionGroupItem } from '@/types/session';
|
8
8
|
|
9
|
-
const sessionProcedure = authedProcedure.use(async (opts) => {
|
9
|
+
const sessionProcedure = authedProcedure.use(serverDatabase).use(async (opts) => {
|
10
10
|
const { ctx } = opts;
|
11
11
|
|
12
12
|
return opts.next({
|
13
13
|
ctx: {
|
14
|
-
sessionGroupModel: new SessionGroupModel(serverDB, ctx.userId),
|
14
|
+
sessionGroupModel: new SessionGroupModel(ctx.serverDB, ctx.userId),
|
15
15
|
},
|
16
16
|
});
|
17
17
|
});
|
@@ -69,8 +69,6 @@ export const sessionGroupRouter = router({
|
|
69
69
|
}),
|
70
70
|
)
|
71
71
|
.mutation(async ({ input, ctx }) => {
|
72
|
-
console.log('sortMap:', input.sortMap);
|
73
|
-
|
74
72
|
return ctx.sessionGroupModel.updateOrder(input.sortMap);
|
75
73
|
}),
|
76
74
|
});
|
@@ -7,22 +7,22 @@ import { FileModel } from '@/database/models/file';
|
|
7
7
|
import { KnowledgeBaseModel } from '@/database/models/knowledgeBase';
|
8
8
|
import { SessionModel } from '@/database/models/session';
|
9
9
|
import { UserModel } from '@/database/models/user';
|
10
|
-
import { serverDB } from '@/database/server';
|
11
10
|
import { pino } from '@/libs/logger';
|
12
11
|
import { authedProcedure, router } from '@/libs/trpc';
|
12
|
+
import { serverDatabase } from '@/libs/trpc/lambda';
|
13
13
|
import { AgentService } from '@/server/services/agent';
|
14
14
|
import { KnowledgeItem, KnowledgeType } from '@/types/knowledgeBase';
|
15
15
|
|
16
|
-
const agentProcedure = authedProcedure.use(async (opts) => {
|
16
|
+
const agentProcedure = authedProcedure.use(serverDatabase).use(async (opts) => {
|
17
17
|
const { ctx } = opts;
|
18
18
|
|
19
19
|
return opts.next({
|
20
20
|
ctx: {
|
21
|
-
agentModel: new AgentModel(serverDB, ctx.userId),
|
22
|
-
agentService: new AgentService(serverDB, ctx.userId),
|
23
|
-
fileModel: new FileModel(serverDB, ctx.userId),
|
24
|
-
knowledgeBaseModel: new KnowledgeBaseModel(serverDB, ctx.userId),
|
25
|
-
sessionModel: new SessionModel(serverDB, ctx.userId),
|
21
|
+
agentModel: new AgentModel(ctx.serverDB, ctx.userId),
|
22
|
+
agentService: new AgentService(ctx.serverDB, ctx.userId),
|
23
|
+
fileModel: new FileModel(ctx.serverDB, ctx.userId),
|
24
|
+
knowledgeBaseModel: new KnowledgeBaseModel(ctx.serverDB, ctx.userId),
|
25
|
+
sessionModel: new SessionModel(ctx.serverDB, ctx.userId),
|
26
26
|
},
|
27
27
|
});
|
28
28
|
});
|
@@ -90,7 +90,7 @@ export const agentRouter = router({
|
|
90
90
|
// if there is no session for user, create one
|
91
91
|
if (!item) {
|
92
92
|
// if there is no user, return default config
|
93
|
-
const user = await UserModel.findById(serverDB, ctx.userId);
|
93
|
+
const user = await UserModel.findById(ctx.serverDB, ctx.userId);
|
94
94
|
if (!user) return DEFAULT_AGENT_CONFIG;
|
95
95
|
|
96
96
|
const res = await ctx.agentService.createInbox();
|
@@ -3,8 +3,8 @@ import { z } from 'zod';
|
|
3
3
|
import { AiModelModel } from '@/database/models/aiModel';
|
4
4
|
import { UserModel } from '@/database/models/user';
|
5
5
|
import { AiInfraRepos } from '@/database/repositories/aiInfra';
|
6
|
-
import { serverDB } from '@/database/server';
|
7
6
|
import { authedProcedure, router } from '@/libs/trpc';
|
7
|
+
import { serverDatabase } from '@/libs/trpc/lambda';
|
8
8
|
import { getServerGlobalConfig } from '@/server/globalConfig';
|
9
9
|
import { KeyVaultsGateKeeper } from '@/server/modules/KeyVaultsEncrypt';
|
10
10
|
import {
|
@@ -15,7 +15,7 @@ import {
|
|
15
15
|
} from '@/types/aiModel';
|
16
16
|
import { ProviderConfig } from '@/types/user/settings';
|
17
17
|
|
18
|
-
const aiModelProcedure = authedProcedure.use(async (opts) => {
|
18
|
+
const aiModelProcedure = authedProcedure.use(serverDatabase).use(async (opts) => {
|
19
19
|
const { ctx } = opts;
|
20
20
|
|
21
21
|
const gateKeeper = await KeyVaultsGateKeeper.initWithEnvKey();
|
@@ -24,13 +24,13 @@ const aiModelProcedure = authedProcedure.use(async (opts) => {
|
|
24
24
|
return opts.next({
|
25
25
|
ctx: {
|
26
26
|
aiInfraRepos: new AiInfraRepos(
|
27
|
-
serverDB,
|
27
|
+
ctx.serverDB,
|
28
28
|
ctx.userId,
|
29
29
|
aiProvider as Record<string, ProviderConfig>,
|
30
30
|
),
|
31
|
-
aiModelModel: new AiModelModel(serverDB, ctx.userId),
|
31
|
+
aiModelModel: new AiModelModel(ctx.serverDB, ctx.userId),
|
32
32
|
gateKeeper,
|
33
|
-
userModel: new UserModel(serverDB, ctx.userId),
|
33
|
+
userModel: new UserModel(ctx.serverDB, ctx.userId),
|
34
34
|
},
|
35
35
|
});
|
36
36
|
});
|
@@ -1,9 +1,7 @@
|
|
1
1
|
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
2
2
|
|
3
3
|
import { AiProviderModel } from '@/database/models/aiProvider';
|
4
|
-
import { UserModel } from '@/database/models/user';
|
5
4
|
import { AiInfraRepos } from '@/database/repositories/aiInfra';
|
6
|
-
import { serverDB } from '@/database/server';
|
7
5
|
import { getServerGlobalConfig } from '@/server/globalConfig';
|
8
6
|
import { KeyVaultsGateKeeper } from '@/server/modules/KeyVaultsEncrypt';
|
9
7
|
import { AiProviderDetailItem, AiProviderRuntimeState } from '@/types/aiProvider';
|