Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ android {

defaultConfig {
applicationId = "dev.chungjungsoo.gptmobile"
minSdk = 28
minSdk = 31
targetSdk = 35
versionCode = 11
versionName = "0.5.3"
Expand Down Expand Up @@ -71,6 +71,9 @@ dependencies {
implementation(libs.androidx.ui.tooling.preview)
implementation(libs.androidx.material3)

// AI Edge SDK
implementation(libs.ai.core)

// SplashScreen
implementation(libs.splashscreen)

Expand Down
5 changes: 5 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@

<uses-permission android:name="android.permission.INTERNET" />

<queries>
<package android:name="com.google.android.aicore" />
<package android:name="com.google.android.as.oss" />
</queries>

<application
android:name=".presentation.GPTMobileApp"
android:allowBackup="true"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,10 @@ object ModelConstants {
"You are to answer my questions precisely. "

const val DEFAULT_PROMPT = "Your task is to answer my questions precisely."

const val CHAT_TITLE_GENERATE_PROMPT =
"Create a title that summarizes the chat. " +
"The output must match the language that the user and the opponent is using, and should be less than 50 letters. " +
"The output should only include the sentence in plain text without bullets or double asterisks. Do not use markdown syntax.\n" +
"[Chat Content]\n"
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ interface ChatRepository {
suspend fun completeOllamaChat(question: Message, history: List<Message>): Flow<ApiState>
suspend fun fetchChatList(): List<ChatRoom>
suspend fun fetchMessages(chatId: Int): List<Message>
fun generateDefaultChatTitle(messages: List<Message>): String?
fun generateAIChatTitle(messages: List<Message>): Flow<ApiState>
suspend fun updateChatTitle(chatRoom: ChatRoom, title: String)
suspend fun saveChat(chatRoom: ChatRoom, messages: List<Message>): ChatRoom
suspend fun deleteChats(chatRooms: List<ChatRoom>)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package dev.chungjungsoo.gptmobile.data.repository

import android.content.Context
import com.aallam.openai.api.chat.ChatCompletionChunk
import com.aallam.openai.api.chat.ChatCompletionRequest
import com.aallam.openai.api.chat.ChatMessage
Expand Down Expand Up @@ -33,11 +34,13 @@ import dev.chungjungsoo.gptmobile.data.network.AnthropicAPI
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onCompletion
import kotlinx.coroutines.flow.onStart

class ChatRepositoryImpl @Inject constructor(
private val appContext: Context,
private val chatRoomDao: ChatRoomDao,
private val messageDao: MessageDao,
private val settingRepository: SettingRepository,
Expand Down Expand Up @@ -175,6 +178,35 @@ class ChatRepositoryImpl @Inject constructor(

override suspend fun fetchMessages(chatId: Int): List<Message> = messageDao.loadMessages(chatId)

override fun generateDefaultChatTitle(messages: List<Message>): String? = messages.sortedBy { it.createdAt }.firstOrNull { it.platformType == null }?.content?.replace('\n', ' ')?.take(50)

override fun generateAIChatTitle(messages: List<Message>): Flow<ApiState> {
if (messages.isEmpty()) {
return flow { "Untitled Chat" }
}

val generationConfig = com.google.ai.edge.aicore.generationConfig {
context = appContext // required
temperature = 1f
maxOutputTokens = 25
}
val model = com.google.ai.edge.aicore.GenerativeModel(generationConfig = generationConfig)

var request = ModelConstants.CHAT_TITLE_GENERATE_PROMPT
messages.sortedBy { it.createdAt }.forEach { message ->
request += when (message.platformType) {
null -> "User: ${message.content}\n\n"
else -> "Assistant: ${message.content}\n\n"
}
}

return model.generateContentStream(request)
.map<com.google.ai.edge.aicore.GenerateContentResponse, ApiState> { response -> ApiState.Success(response.text ?: "") }
.catch { throwable -> emit(ApiState.Error(throwable.message ?: "Unknown error")) }
.onStart { emit(ApiState.Loading) }
.onCompletion { emit(ApiState.Done) }
}

override suspend fun updateChatTitle(chatRoom: ChatRoom, title: String) {
chatRoomDao.editChatRoom(chatRoom.copy(title = title.replace('\n', ' ').take(50)))
}
Expand Down Expand Up @@ -205,6 +237,7 @@ class ChatRepositoryImpl @Inject constructor(
savedMessages.firstOrNull { it.id == m.id } == null
}

chatRoomDao.editChatRoom(chatRoom)
messageDao.deleteMessages(*shouldBeDeleted.toTypedArray())
messageDao.editMessages(*shouldBeUpdated.toTypedArray())
messageDao.addMessages(*shouldBeAdded.toTypedArray())
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package dev.chungjungsoo.gptmobile.di

import android.content.Context
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import dev.chungjungsoo.gptmobile.data.database.dao.ChatRoomDao
import dev.chungjungsoo.gptmobile.data.database.dao.MessageDao
Expand All @@ -19,9 +21,10 @@ object ChatRepositoryModule {
@Provides
@Singleton
fun provideChatRepository(
@ApplicationContext appContext: Context,
chatRoomDao: ChatRoomDao,
messageDao: MessageDao,
settingRepository: SettingRepository,
anthropicAPI: AnthropicAPI
): ChatRepository = ChatRepositoryImpl(chatRoomDao, messageDao, settingRepository, anthropicAPI)
): ChatRepository = ChatRepositoryImpl(appContext, chatRoomDao, messageDao, settingRepository, anthropicAPI)
}
Loading