-
-
Notifications
You must be signed in to change notification settings - Fork 10.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Pglite Client DB Feedback | 客户端 Pglite DB 功能反馈 #5131
Comments
目前我们的演示站点 // 定义常量
const LOBE_CHAT_LOCAL_DB_NAME = 'LOBE_CHAT_DB';
const V2DB_LASET_SCHEMA_VERSION = 7;
const STORE_NAMES = ['messages', 'sessionGroups', 'sessions', 'topics', 'files', 'plugins', 'users'];
class V2DBReader {
constructor(storeNames) {
this.dbName = LOBE_CHAT_LOCAL_DB_NAME;
this.storeNames = storeNames;
}
async readAllData() {
try {
const db = await this.openDB();
const results = await Promise.all(
this.storeNames.map((storeName) => this.readStore(db, storeName))
);
const migrationData = this.storeNames.reduce((acc, storeName, index) => {
acc[storeName] = results[index];
return acc;
}, {});
db.close();
return migrationData;
} catch (error) {
console.error('读取数据库失败:', error);
throw error;
}
}
convertToImportData(data) {
// 转换 messages
const messages = data.messages.map((msg) => ({
content: msg.content,
createdAt: msg.createdAt,
error: msg.error || msg.pluginError,
extra: {
fromModel: msg.fromModel,
fromProvider: msg.fromProvider,
translate: msg.translate,
tts: msg.tts,
},
files: msg.files,
id: msg.id,
observationId: msg.observationId,
parentId: msg.parentId,
plugin: msg.plugin,
pluginState: msg.pluginState,
quotaId: msg.quotaId,
role: msg.role,
sessionId: msg.sessionId,
tool_call_id: msg.tool_call_id,
tools: msg.tools,
topicId: msg.topicId,
traceId: msg.traceId,
updatedAt: msg.updatedAt,
}));
// 转换 sessionGroups
const sessionGroups = data.sessionGroups.map((group) => ({
createdAt: group.createdAt,
id: group.id,
name: group.name,
sort: group.sort || null,
updatedAt: group.updatedAt,
}));
// 转换 sessions
const sessions = data.sessions.map((session) => ({
config: session.config,
createdAt: new Date(session.createdAt).toString(),
group: session.group,
id: session.id,
meta: session.meta,
pinned: session.pinned ? true : undefined,
type: session.type || 'agent',
updatedAt: new Date(session.updatedAt).toString(),
}));
// 转换 topics
const topics = data.topics.map((topic) => ({
...topic,
favorite: topic.favorite ? true : undefined,
}));
return {
state: {
messages,
sessionGroups,
sessions,
topics,
},
version: V2DB_LASET_SCHEMA_VERSION,
};
}
openDB() {
return new Promise((resolve, reject) => {
const request = indexedDB.open(this.dbName);
request.onerror = () => reject(request.error);
request.onsuccess = () => resolve(request.result);
});
}
readStore(db, storeName) {
return new Promise((resolve, reject) => {
try {
const transaction = db.transaction(storeName, 'readonly');
const store = transaction.objectStore(storeName);
const request = store.getAll();
request.onerror = () => reject(request.error);
request.onsuccess = () => resolve(request.result);
} catch (error) {
reject(error);
}
});
}
}
// 下载文件的辅助函数
function downloadJSON(data, filename) {
const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = filename || 'lobe-chat-backup.json';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
}
// 主函数
async function exportLobeChat() {
try {
console.log('开始导出数据...');
const reader = new V2DBReader(STORE_NAMES);
const rawData = await reader.readAllData();
console.log('原始数据读取完成:', rawData);
const importData = reader.convertToImportData(rawData);
console.log('数据转换完成:', importData);
// 下载文件
downloadJSON(importData, `lobe-chat-backup-${new Date().toISOString()}.json`);
console.log('数据导出完成!');
} catch (error) {
console.error('导出失败:', error);
}
}
// 执行导出
exportLobeChat(); |
Currently our demo site // define constants
const LOBE_CHAT_LOCAL_DB_NAME = 'LOBE_CHAT_DB';
const V2DB_LASET_SCHEMA_VERSION = 7;
const STORE_NAMES = ['messages', 'sessionGroups', 'sessions', 'topics', 'files', 'plugins', 'users'];
class V2DBReader {
constructor(storeNames) {
this.dbName = LOBE_CHAT_LOCAL_DB_NAME;
this.storeNames = storeNames;
}
async readAllData() {
try {
const db = await this.openDB();
const results = await Promise.all(
this.storeNames.map((storeName) => this.readStore(db, storeName))
);
const migrationData = this.storeNames.reduce((acc, storeName, index) => {
acc[storeName] = results[index];
return acc;
}, {});
db.close();
return migrationData;
} catch (error) {
console.error('Failed to read database:', error);
throw error;
}
}
convertToImportData(data) {
// Convert messages
const messages = data.messages.map((msg) => ({
content: msg.content,
createdAt: msg.createdAt,
error: msg.error || msg.pluginError,
extra: {
fromModel: msg.fromModel,
fromProvider: msg.fromProvider,
translate: msg.translate,
tts: msg.tts,
},
files: msg.files,
id: msg.id,
observationId: msg.observationId,
parentId: msg.parentId,
plugin: msg.plugin,
pluginState: msg.pluginState,
quotaId: msg.quotaId,
role: msg.role,
sessionId: msg.sessionId,
tool_call_id: msg.tool_call_id,
tools: msg.tools,
topicId: msg.topicId,
traceId: msg.traceId,
updatedAt: msg.updatedAt,
}));
// Convert sessionGroups
const sessionGroups = data.sessionGroups.map((group) => ({
createdAt: group.createdAt,
id: group.id,
name: group.name,
sort: group.sort || null,
updatedAt: group.updatedAt,
}));
//Convert sessions
const sessions = data.sessions.map((session) => ({
config: session.config,
createdAt: new Date(session.createdAt).toString(),
group: session.group,
id: session.id,
meta: session.meta,
pinned: session.pinned ? true : undefined,
type: session.type || 'agent',
updatedAt: new Date(session.updatedAt).toString(),
}));
//Convert topics
const topics = data.topics.map((topic) => ({
...topic,
favorite: topic.favorite ? true : undefined,
}));
return {
state: {
messages,
sessionGroups,
sessions,
topics,
},
version: V2DB_LASET_SCHEMA_VERSION,
};
}
openDB() {
return new Promise((resolve, reject) => {
const request = indexedDB.open(this.dbName);
request.onerror = () => reject(request.error);
request.onsuccess = () => resolve(request.result);
});
}
readStore(db, storeName) {
return new Promise((resolve, reject) => {
try {
const transaction = db.transaction(storeName, 'readonly');
const store = transaction.objectStore(storeName);
const request = store.getAll();
request.onerror = () => reject(request.error);
request.onsuccess = () => resolve(request.result);
} catch (error) {
reject(error);
}
});
}
}
//Auxiliary function for downloading files
function downloadJSON(data, filename) {
const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = filename || 'lobe-chat-backup.json';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
}
// main function
async function exportLobeChat() {
try {
console.log('Start exporting data...');
const reader = new V2DBReader(STORE_NAMES);
const rawData = await reader.readAllData();
console.log('raw data reading completed:', rawData);
const importData = reader.convertToImportData(rawData);
console.log('Data conversion completed:', importData);
// Download file
downloadJSON(importData, `lobe-chat-backup-${new Date().toISOString()}.json`);
console.log('Data export completed!');
} catch (error) {
console.error('Export failed:', error);
}
}
//Execute export
exportLobeChat(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Looking to the future, LobeChat introduces a brand new Client DB engine in v1.37.0: PGlite. It is the wasm version of our server-side database Postgres, capable of running in the browser with a relatively small footprint while providing database capabilities that are fully consistent with Postgres, which means:
The implementation of the database on both the client and server sides will only need to be done once, significantly reducing future development costs, and most features will be able to be updated synchronously to both the client and server versions in the future;
With the help of a CRDT collaboration engine like electric-sql, we will be able to truly integrate a unified client/server in the future, achieving a local-first, on-demand synchronization ultimate form.
However, any forward-looking innovation comes at a cost. While we are very optimistic about the potential of PGlite, it is still a very early project at this point in time, and there are currently no large-scale user projects that are truly using it in production scenarios. As a pioneering explorer of open-source AI projects, LobeChat hopes to explore the possibilities of using PGlite together with the community and to discover new best practices for applications in the AI era!
How to use
This change has no impact on users using the server-side DB.
To avoid affecting the existing client DB users, we have added a new environment variable to enable pglite. You can enable the use of pglite by adding
NEXT_PUBLIC_CLIENT_DB=pglite
during the application build.Important
Note: Currently, automatic data migration has not been implemented. Please manually back up the existing data before adding variables.
We will enable this engine by default in the major version of LobeChat V2 and remove all existing implementations of dexie.
面向未来,LobeChat 在 v1.37.0 中提供了一种全新的 Client DB 引擎: PGlite。它是我们服务端数据库 Postgres 的 wasm 版本,可以以相对较小的体积在浏览器中运行,并获得与 Postgres 完全一致的数据库能力,这意味着:
但是,任何面向未来的创新都是有代价的,虽然我们非常看好 PGlite 的潜力,但 PGlite 在当前的时间点下仍然是一个非常早期的项目,目前也没有一个大规模用户的项目真正在生产场景下使用它。而 LobeChat 作为开源 AI 项目的先锋探索者,我们希望与社区一起探索使用 PGlite 的可能性,一起来探索 AI 时代新的应用最佳实践!
如何使用
为了不影响原有客户端 DB 用户的使用,我们新增了一个环境变量用于启用 pglite,你可以通过在应用构建时添加
NEXT_PUBLIC_CLIENT_DB=pglite
来开启 pglite 的使用。Important
注意事项:目前没有实现数据的自动迁移,请在添加变量前手动备份原有的数据。
我们将会在 LobeChat V2 大版本中默认开启该引擎,并移除原有的 dexie 的所有实现。
The text was updated successfully, but these errors were encountered: