Skip to content

Commit 0d04492

Browse files
committed
core: combine all Room databases into one to aid schema migration
Currently, each table was contained within its own database instance. In the scenario of migrating the schema, the schema version of each database had to be incremented. Also, one table in one database seems like a bad design. With this commit, there's one database containing all tables.
1 parent a427753 commit 0d04492

File tree

13 files changed

+266
-321
lines changed

13 files changed

+266
-321
lines changed
Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
package io.shubham0204.smollmandroid.data
2+
3+
import android.content.Context
4+
import androidx.room.Database
5+
import androidx.room.Room
6+
import androidx.room.RoomDatabase
7+
import androidx.room.TypeConverters
8+
import kotlinx.coroutines.Dispatchers
9+
import kotlinx.coroutines.flow.Flow
10+
import kotlinx.coroutines.runBlocking
11+
import org.koin.core.annotation.Single
12+
import java.util.Date
13+
14+
@Database(
15+
entities = [Chat::class, ChatMessage::class, LLMModel::class, Task::class],
16+
version = 1,
17+
)
18+
@TypeConverters(Converters::class)
19+
abstract class AppRoomDatabase : RoomDatabase() {
20+
abstract fun chatsDao(): ChatsDao
21+
22+
abstract fun chatMessagesDao(): ChatMessageDao
23+
24+
abstract fun llmModelDao(): LLMModelDao
25+
26+
abstract fun taskDao(): TaskDao
27+
}
28+
29+
@Single
30+
class AppDB(
31+
context: Context,
32+
) {
33+
private val db =
34+
Room
35+
.databaseBuilder(
36+
context,
37+
AppRoomDatabase::class.java,
38+
"app-database",
39+
).build()
40+
41+
/** Get all chats from the database sorted by dateUsed in descending order. */
42+
fun getChats(): Flow<List<Chat>> = db.chatsDao().getChats()
43+
44+
fun loadDefaultChat(): Chat {
45+
val defaultChat =
46+
if (getChatsCount() == 0L) {
47+
addChat("Untitled")
48+
getRecentlyUsedChat()!!
49+
} else {
50+
// Given that chatsDB has at least one chat
51+
// chatsDB.getRecentlyUsedChat() will never return null
52+
getRecentlyUsedChat()!!
53+
}
54+
return defaultChat
55+
}
56+
57+
/**
58+
* Get the most recently used chat from the database. This function might return null, if there
59+
* are no chats in the database.
60+
*/
61+
fun getRecentlyUsedChat(): Chat? =
62+
runBlocking(Dispatchers.IO) {
63+
db.chatsDao().getRecentlyUsedChat()
64+
}
65+
66+
/**
67+
* Adds a new chat to the database initialized with given
68+
* arguments and returns the new Chat object
69+
*/
70+
fun addChat(
71+
chatName: String,
72+
chatTemplate: String = "",
73+
systemPrompt: String = "You are a helpful assistant.",
74+
llmModelId: Long = -1,
75+
isTask: Boolean = false,
76+
): Chat =
77+
runBlocking(Dispatchers.IO) {
78+
val newChat =
79+
Chat(
80+
name = chatName,
81+
systemPrompt = systemPrompt,
82+
dateCreated = Date(),
83+
dateUsed = Date(),
84+
llmModelId = llmModelId,
85+
contextSize = 2048,
86+
chatTemplate = chatTemplate,
87+
isTask = isTask,
88+
)
89+
val newChatId = db.chatsDao().insertChat(newChat)
90+
newChat.copy(id = newChatId)
91+
}
92+
93+
/** Update the chat in the database. */
94+
fun updateChat(modifiedChat: Chat) =
95+
runBlocking(Dispatchers.IO) {
96+
db.chatsDao().updateChat(modifiedChat)
97+
}
98+
99+
fun deleteChat(chat: Chat) =
100+
runBlocking(Dispatchers.IO) {
101+
db.chatsDao().deleteChat(chat.id)
102+
}
103+
104+
fun getChatsCount(): Long =
105+
runBlocking(Dispatchers.IO) {
106+
db.chatsDao().getChatsCount()
107+
}
108+
109+
// Chat Messages
110+
111+
fun getMessages(chatId: Long): Flow<List<ChatMessage>> = db.chatMessagesDao().getMessages(chatId)
112+
113+
fun getMessagesForModel(chatId: Long): List<ChatMessage> =
114+
runBlocking(Dispatchers.IO) {
115+
db.chatMessagesDao().getMessagesForModel(chatId)
116+
}
117+
118+
fun addUserMessage(
119+
chatId: Long,
120+
message: String,
121+
) = runBlocking(Dispatchers.IO) {
122+
db
123+
.chatMessagesDao()
124+
.insertMessage(ChatMessage(chatId = chatId, message = message, isUserMessage = true))
125+
}
126+
127+
fun addAssistantMessage(
128+
chatId: Long,
129+
message: String,
130+
) = runBlocking(Dispatchers.IO) {
131+
db
132+
.chatMessagesDao()
133+
.insertMessage(ChatMessage(chatId = chatId, message = message, isUserMessage = false))
134+
}
135+
136+
fun deleteMessage(messageId: Long) =
137+
runBlocking(Dispatchers.IO) {
138+
db.chatMessagesDao().deleteMessage(messageId)
139+
}
140+
141+
fun deleteMessages(chatId: Long) =
142+
runBlocking(Dispatchers.IO) {
143+
db.chatMessagesDao().deleteMessages(chatId)
144+
}
145+
146+
// Models
147+
148+
fun addModel(
149+
name: String,
150+
url: String,
151+
path: String,
152+
contextSize: Int,
153+
chatTemplate: String,
154+
) = runBlocking(Dispatchers.IO) {
155+
db.llmModelDao().insertModels(
156+
LLMModel(
157+
name = name,
158+
url = url,
159+
path = path,
160+
contextSize = contextSize,
161+
chatTemplate = chatTemplate,
162+
),
163+
)
164+
}
165+
166+
fun getModel(id: Long): LLMModel? =
167+
runBlocking(Dispatchers.IO) {
168+
try {
169+
db.llmModelDao().getModel(id)
170+
} catch (_: IllegalArgumentException) {
171+
null
172+
}
173+
}
174+
175+
fun getModels(): Flow<List<LLMModel>> = runBlocking(Dispatchers.IO) { db.llmModelDao().getAllModels() }
176+
177+
fun getModelsList(): List<LLMModel> = runBlocking(Dispatchers.IO) { db.llmModelDao().getAllModelsList() }
178+
179+
fun deleteModel(id: Long) =
180+
runBlocking(Dispatchers.IO) {
181+
db.llmModelDao().deleteModel(id)
182+
}
183+
184+
// Tasks
185+
186+
fun getTask(taskId: Long): Task =
187+
runBlocking(Dispatchers.IO) {
188+
db.taskDao().getTask(taskId)
189+
}
190+
191+
fun getTasks(): Flow<List<Task>> = db.taskDao().getTasks()
192+
193+
fun addTask(
194+
name: String,
195+
systemPrompt: String,
196+
modelId: Long,
197+
) = runBlocking(Dispatchers.IO) {
198+
db.taskDao().insertTask(Task(name = name, systemPrompt = systemPrompt, modelId = modelId))
199+
}
200+
201+
fun deleteTask(taskId: Long) =
202+
runBlocking(Dispatchers.IO) {
203+
db.taskDao().deleteTask(taskId)
204+
}
205+
206+
fun updateTask(task: Task) =
207+
runBlocking(Dispatchers.IO) {
208+
db.taskDao().updateTask(task)
209+
}
210+
}

app/src/main/java/io/shubham0204/smollmandroid/data/ChatsDB.kt

Lines changed: 0 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -16,23 +16,14 @@
1616

1717
package io.shubham0204.smollmandroid.data
1818

19-
import android.content.Context
2019
import android.util.Log
2120
import androidx.room.Dao
22-
import androidx.room.Database
2321
import androidx.room.Entity
2422
import androidx.room.Insert
2523
import androidx.room.PrimaryKey
2624
import androidx.room.Query
27-
import androidx.room.Room
28-
import androidx.room.RoomDatabase
29-
import androidx.room.TypeConverters
3025
import androidx.room.Update
31-
import kotlinx.coroutines.Dispatchers
32-
import kotlinx.coroutines.ExperimentalCoroutinesApi
3326
import kotlinx.coroutines.flow.Flow
34-
import kotlinx.coroutines.runBlocking
35-
import org.koin.core.annotation.Single
3627
import java.util.Date
3728

3829
private const val LOGTAG = "[ChatDB-Kt]"
@@ -116,92 +107,3 @@ interface ChatsDao {
116107
@Query("SELECT COUNT(*) FROM Chat")
117108
suspend fun getChatsCount(): Long
118109
}
119-
120-
@Database(entities = [Chat::class], version = 1)
121-
@TypeConverters(Converters::class)
122-
abstract class ChatsDatabase : RoomDatabase() {
123-
abstract fun chatsDao(): ChatsDao
124-
}
125-
126-
@Single
127-
class ChatsDB(
128-
context: Context,
129-
) {
130-
private val db =
131-
Room
132-
.databaseBuilder(
133-
context,
134-
ChatsDatabase::class.java,
135-
"chats-database",
136-
).build()
137-
138-
/** Get all chats from the database sorted by dateUsed in descending order. */
139-
@OptIn(ExperimentalCoroutinesApi::class)
140-
fun getChats(): Flow<List<Chat>> = db.chatsDao().getChats()
141-
142-
fun loadDefaultChat(): Chat {
143-
val defaultChat =
144-
if (getChatsCount() == 0L) {
145-
addChat("Untitled")
146-
getRecentlyUsedChat()!!
147-
} else {
148-
// Given that chatsDB has at least one chat
149-
// chatsDB.getRecentlyUsedChat() will never return null
150-
getRecentlyUsedChat()!!
151-
}
152-
LOGD("Default chat is $defaultChat")
153-
return defaultChat
154-
}
155-
156-
/**
157-
* Get the most recently used chat from the database. This function might return null, if there
158-
* are no chats in the database.
159-
*/
160-
fun getRecentlyUsedChat(): Chat? =
161-
runBlocking(Dispatchers.IO) {
162-
db.chatsDao().getRecentlyUsedChat()
163-
}
164-
165-
/**
166-
* Adds a new chat to the database initialized with given
167-
* arguments and returns the new Chat object
168-
*/
169-
fun addChat(
170-
chatName: String,
171-
chatTemplate: String = "",
172-
systemPrompt: String = "You are a helpful assistant.",
173-
llmModelId: Long = -1,
174-
isTask: Boolean = false,
175-
): Chat =
176-
runBlocking(Dispatchers.IO) {
177-
val newChat =
178-
Chat(
179-
name = chatName,
180-
systemPrompt = systemPrompt,
181-
dateCreated = Date(),
182-
dateUsed = Date(),
183-
llmModelId = llmModelId,
184-
contextSize = 2048,
185-
chatTemplate = chatTemplate,
186-
isTask = isTask,
187-
)
188-
val newChatId = db.chatsDao().insertChat(newChat)
189-
newChat.copy(id = newChatId)
190-
}
191-
192-
/** Update the chat in the database. */
193-
fun updateChat(modifiedChat: Chat) =
194-
runBlocking(Dispatchers.IO) {
195-
db.chatsDao().updateChat(modifiedChat)
196-
}
197-
198-
fun deleteChat(chat: Chat) =
199-
runBlocking(Dispatchers.IO) {
200-
db.chatsDao().deleteChat(chat.id)
201-
}
202-
203-
fun getChatsCount(): Long =
204-
runBlocking(Dispatchers.IO) {
205-
db.chatsDao().getChatsCount()
206-
}
207-
}

0 commit comments

Comments
 (0)