-
Notifications
You must be signed in to change notification settings - Fork 2
/
db.ts
129 lines (119 loc) · 3.78 KB
/
db.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
// db.ts
import Dexie, { Table } from 'dexie';
import { ACDoc, Workspace, AgentWorkspace } from './src/state';
import { toastifyInfo } from './src/components/Toast';
export interface AcaiMemoryVector {
content: string;
embedding: number[];
metadata: Record<string, any>;
}
export interface Knowledge {
id: string;
workspaceId: string;
fileId: string;
fileName: string;
fileType: 'pdf' | 'md' | 'txt';
fullText: string;
createdAt: string;
lastModified: string;
memoryVectors: AcaiMemoryVector[];
summary?: string;
title?: string;
tags?: string[];
}
export interface ACFile {
id: string;
workspaceId: string;
file: File;
fileType: 'pdf' | 'md' | 'txt' | 'html';
fileName: string;
createdAt: string;
lastModified: string;
}
function getCanonicalComparableSchema(db: Dexie) {
return JSON.stringify(
db.tables
.map(({ name, schema }) => ({
name,
schema: [
schema.primKey.src,
...schema.indexes.map((idx) => idx.src).sort(),
].join(','),
}))
.sort((a, b) => (a.name < b.name ? 1 : -1)),
);
}
async function isDeclaredSchemaSameAsInstalled(db: Dexie) {
const declaredSchema = getCanonicalComparableSchema(db);
const dynDb = await new Dexie(db.name).open();
const installedSchema = getCanonicalComparableSchema(dynDb);
return declaredSchema === installedSchema;
}
function checkAndOpenDb() {
indexedDB
.databases()
.then((databases) => {
const dbExists = databases.some((dbInfo) => dbInfo.name === db.name);
if (dbExists) {
isDeclaredSchemaSameAsInstalled(db).then((same) => {
if (!same) {
console.log('Schema mismatch, deleting database');
// delete db in indexedDB
const req = indexedDB.deleteDatabase(db.name);
req.onsuccess = () => {
console.log('Deleted database successfully');
db.open().catch((err) => {
console.error(err.stack || err);
});
toastifyInfo(
'Your db schema was out of date, we deleted it and created a new one for you. Please refresh the page',
);
};
} else {
db.open().catch((err) => {
console.error(err.stack || err);
});
}
});
} else {
db.open().catch((err) => {
console.error(err.stack || err);
});
}
})
.catch((err) => {
console.error('Error checking for existing databases: ', err);
});
}
export class AcaiDexie extends Dexie {
// 'embeddings' is added by dexie when declaring the stores()
// We just tell the typing system this is the case
knowledge!: Table<Knowledge>;
workspaces!: Table<Workspace>;
docs!: Table<ACDoc>;
agents!: Table<AgentWorkspace>;
files!: Table<ACFile>;
constructor() {
super('acaiDb');
this.version(1).stores({
knowledge:
'++id, workspaceId, file, fileType, fullText, createdAt, lastModified, memoryVectors, summary, title, tags',
});
this.version(2).stores({
workspaces: '++id, name, createdAt, lastUpdated, private',
docs: '++id, workspaceId, title, filetype, content, isContext, systemNote, createdAt, lastUpdated, autoSave, canEdit',
});
this.version(3).stores({
agents:
'++id, loading, agentMode, agentName, workspaceId, customPrompt, recentChatHistory, openAIChatModel, returnRagResults, customAgentVectorSearch, agentLogs, memory, agentTools',
files: '++id, workspaceId, file, createdAt, lastModified',
});
this.knowledge = this.table('knowledge');
this.workspaces = this.table('workspaces');
this.agents = this.table('agents');
this.docs = this.table('docs');
this.files = this.table('files');
}
}
export const db = new AcaiDexie();
checkAndOpenDb();