Skip to content

Commit

Permalink
Add Google Gemini and make chat feature public
Browse files Browse the repository at this point in the history
Tested-by: Pranav Purwar <[email protected]>
Signed-off-by: PranavPurwar <[email protected]>
  • Loading branch information
PranavPurwar committed Jan 5, 2024
1 parent 65df5e9 commit 55397db
Show file tree
Hide file tree
Showing 21 changed files with 305 additions and 89 deletions.
6 changes: 6 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,15 @@ android {

defaultConfig {
val commit = getGitCommit()
val GEMINI_API_KEY = "AIzaSyB-axwh7qrTGngrI2qNLgN5YAjCFJ-w0R8"

applicationId = "org.cosmicide"
minSdk = 26
targetSdk = 34
versionCode = 24
versionName = "2.0.4"
buildConfigField("String", "GIT_COMMIT", "\"$commit\"")
buildConfigField("String", "GEMINI_API_KEY", "\"$GEMINI_API_KEY\"")
}

signingConfigs {
Expand Down Expand Up @@ -203,6 +206,9 @@ dependencies {
implementation("org.lsposed.hiddenapibypass:hiddenapibypass:4.3")
implementation("org.slf4j:slf4j-simple:2.1.0-alpha1")

implementation("com.google.ai.client.generativeai:generativeai:0.1.1")


val shizukuVersion = "13.1.5"
implementation("dev.rikka.shizuku:api:$shizukuVersion")

Expand Down
46 changes: 44 additions & 2 deletions app/src/main/kotlin/org/cosmicide/adapter/ConversationAdapter.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ package org.cosmicide.adapter
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.google.ai.client.generativeai.type.GenerateContentResponse
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.cosmicide.databinding.ConversationItemReceivedBinding
import org.cosmicide.databinding.ConversationItemSentBinding
import org.cosmicide.util.CommonUtils
Expand All @@ -18,14 +24,31 @@ class ConversationAdapter :

private val conversations = mutableListOf<Conversation>()

data class Conversation(val text: String, val author: String = "assistant")
data class Conversation(
var text: String = "",
val author: String = "assistant",
val flow: Flow<GenerateContentResponse>? = null,
var finished: Boolean = false
) {
init {
if (flow != null) {
CoroutineScope(Dispatchers.IO).launch {
flow.collect {
if (finished) return@collect
text = it.text!!
}
}
}
}
}

companion object {
const val VIEW_TYPE_SENT = 1
const val VIEW_TYPE_RECEIVED = 2
}

fun add(conversation: Conversation) {
conversations.last().finished = true
conversations += conversation
notifyItemInserted(conversations.lastIndex)
}
Expand Down Expand Up @@ -94,8 +117,27 @@ class ConversationAdapter :
BindableViewHolder<Conversation, ConversationItemReceivedBinding>(itemBinding) {

override fun bind(data: Conversation) {
val scope = CoroutineScope(Dispatchers.IO)

scope.launch {
if (data.text.isNotEmpty()) {
withContext(Dispatchers.Main) {
stream(data.text)
}
}
data.flow!!.collect {
if (data.finished) return@collect

withContext(Dispatchers.Main) {
stream(it.text!!)
}
}
}
}

fun stream(text: String) {
binding.message.apply {
CommonUtils.getMarkwon().setMarkdown(this, data.text)
CommonUtils.getMarkwon().setMarkdown(this, binding.message.text.toString() + text)
}
}
}
Expand Down
73 changes: 63 additions & 10 deletions app/src/main/kotlin/org/cosmicide/chat/ChatProvider.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,74 @@
package org.cosmicide.chat

import android.util.Log
import com.google.gson.Gson
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody
import java.util.concurrent.TimeUnit
import com.google.ai.client.generativeai.GenerativeModel
import com.google.ai.client.generativeai.type.BlockThreshold
import com.google.ai.client.generativeai.type.GenerateContentResponse
import com.google.ai.client.generativeai.type.HarmCategory
import com.google.ai.client.generativeai.type.SafetySetting
import com.google.ai.client.generativeai.type.generationConfig
import kotlinx.coroutines.flow.Flow
import org.cosmicide.BuildConfig
import org.cosmicide.common.Prefs

object ChatProvider {

private val client = OkHttpClient()
private val safetySettings = listOf(
SafetySetting(HarmCategory.SEXUALLY_EXPLICIT, BlockThreshold.NONE), // should we block this?
SafetySetting(HarmCategory.HATE_SPEECH, BlockThreshold.NONE),
SafetySetting(HarmCategory.DANGEROUS_CONTENT, BlockThreshold.NONE),
SafetySetting(HarmCategory.HARASSMENT, BlockThreshold.NONE),
)

private val gson = Gson()
private var generativeModel = GenerativeModel(
apiKey = BuildConfig.GEMINI_API_KEY,
modelName = "gemini-pro",
safetySettings = safetySettings,
generationConfig = generationConfig {
temperature = Prefs.temperature
topP = Prefs.topP
topK = Prefs.topK
maxOutputTokens = Prefs.maxTokens
}
)

private var chat = generativeModel.startChat()

data class Message(val role: String, val content: String)
@JvmStatic
fun regenerateModel(
temp: Float = Prefs.temperature,
top_p: Float = Prefs.topP,
top_k: Int = Prefs.topK,
maxTokens: Int = Prefs.maxTokens
) {
Log.d(
"ChatProvider",
"regenerateModel: temperature ${Prefs.temperature}, topP ${Prefs.topP}, topK ${Prefs.topK}, maxTokens ${Prefs.maxTokens}"
)

GenerativeModel(
apiKey = BuildConfig.GEMINI_API_KEY,
modelName = "gemini-pro",
safetySettings = safetySettings,
generationConfig = generationConfig {
temperature = temp
topP = top_p
topK = top_k
maxOutputTokens = maxTokens
}
).let {
generativeModel = it
chat = it.startChat()
}
}

@JvmStatic
fun generate(model: String, conversation: List<Map<String, String>>): String {
fun generate(conversation: List<Map<String, String>>): Flow<GenerateContentResponse> {
return chat.sendMessageStream(conversation.last()["text"]!!)


/*
// json format
// {
// "messages": [
Expand Down Expand Up @@ -56,5 +107,7 @@ object ChatProvider {
e.printStackTrace()
"Error: ${e.message}"
}
*/
}
}
49 changes: 5 additions & 44 deletions app/src/main/kotlin/org/cosmicide/fragment/ChatFragment.kt
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ import org.cosmicide.rewrite.common.BaseBindingFragment
class ChatFragment : BaseBindingFragment<FragmentChatBinding>() {

private val conversationAdapter = ConversationAdapter()
private var model = Models.GPT_35_TURBO

override fun getViewBinding() = FragmentChatBinding.inflate(layoutInflater)

Expand All @@ -47,7 +46,7 @@ class ChatFragment : BaseBindingFragment<FragmentChatBinding>() {
private fun setupUI(context: Context) {
initToolbar()
initBackground(context)
binding.toolbar.title = model.name
binding.toolbar.title = "Gemini Pro"
}

private fun initToolbar() {
Expand All @@ -61,26 +60,7 @@ class ChatFragment : BaseBindingFragment<FragmentChatBinding>() {
binding.recyclerview.invalidate()
return@setOnMenuItemClickListener false
}
val modelName = when (it.itemId) {
R.id.model_gpt35turbo -> {
model = Models.GPT_35_TURBO
"GPT-3.5 Turbo"
}

R.id.model_gpt3516k -> {
model = Models.GPT_3516K
"GPT-3.5 16k"
}

R.id.model_gpt35turbo0613 -> {
model = Models.GPT_35_TURBO_0613
"GPT-3.5 Turbo 0613"
}

else -> return@setOnMenuItemClickListener false
}

binding.toolbar.title = modelName
true
}
}
Expand All @@ -95,24 +75,11 @@ class ChatFragment : BaseBindingFragment<FragmentChatBinding>() {
conversationAdapter.add(conversation)
binding.messageText.setText("")
lifecycleScope.launch(Dispatchers.IO) {
val reply = when (model) {
Models.GPT_35_TURBO_0613 -> ChatProvider.generate(
"gpt-3.5-turbo-0613",
conversationAdapter.getConversations()
)

Models.GPT_35_TURBO -> ChatProvider.generate(
"gpt-3.5-turbo",
conversationAdapter.getConversations()
)

Models.GPT_3516K -> ChatProvider.generate(
"gpt-3.5-16k",
conversationAdapter.getConversations()
)
val reply = ChatProvider.generate(
conversationAdapter.getConversations()
)

}
val response = ConversationAdapter.Conversation(reply)
val response = ConversationAdapter.Conversation(flow = reply)
withContext(Dispatchers.Main) {
conversationAdapter.add(response)
binding.recyclerview.scrollToPosition(conversationAdapter.itemCount - 1)
Expand Down Expand Up @@ -160,11 +127,5 @@ class ChatFragment : BaseBindingFragment<FragmentChatBinding>() {
}
}

enum class Models {
GPT_35_TURBO,
GPT_3516K,
GPT_35_TURBO_0613,
}

private val Int.dp: Int
get() = (Resources.getSystem().displayMetrics.density * this + 0.5f).toInt()
Original file line number Diff line number Diff line change
Expand Up @@ -111,4 +111,4 @@ class NewProjectFragment : BaseBindingFragment<FragmentNewProjectBinding>() {
val content = language.classFileContent(name = "Main", packageName = packageName)
writeText(content)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@ class PluginListFragment : BaseBindingFragment<FragmentPluginListBinding>() {
.setTitle("Delete plugin")
.setMessage("Are you sure you want to delete ${plugin.name}?")
.setPositiveButton("Yes") { _, _ ->
Analytics.logEvent("plugin_delete", mapOf("plugin" to plugin.name))
lifecycleScope.launch {
FileUtil.pluginDir.resolve(plugin.name).deleteRecursively()
val plugins = getPlugins()
Expand All @@ -77,7 +76,6 @@ class PluginListFragment : BaseBindingFragment<FragmentPluginListBinding>() {
}

override fun onPluginInstall(plugin: Plugin) {
Analytics.logEvent("plugin_install", mapOf("plugin" to plugin.name))
binding.progressBar.visibility = View.VISIBLE
Snackbar.make(
binding.root,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,4 +110,4 @@ class PluginsFragment : BaseBindingFragment<FragmentPluginsBinding>() {
return plugins
}
}
}
}
36 changes: 35 additions & 1 deletion app/src/main/kotlin/org/cosmicide/fragment/ProjectFragment.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import android.os.Bundle
import android.view.View
import android.widget.EditText
import android.widget.TextView
import android.widget.Toast
import androidx.activity.result.ActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.activity.result.contract.ActivityResultContracts.CreateDocument
Expand Down Expand Up @@ -115,6 +116,7 @@ class ProjectFragment : BaseBindingFragment<FragmentProjectBinding>(),

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
askClientName()
askForAnalyticsPermission()

setOnClickListeners()
Expand Down Expand Up @@ -331,6 +333,38 @@ class ProjectFragment : BaseBindingFragment<FragmentProjectBinding>(),
}.show()
}

private fun askClientName() {
val prefs = PreferenceManager.getDefaultSharedPreferences(requireContext())

val enteredName = prefs.getString("client_name", null)
if (enteredName != null) return

val editText = EditText(requireContext()).apply {
hint = "Enter your name"
}

MaterialAlertDialogBuilder(requireContext()).apply {
setView(editText)
setTitle("Enter your name")
setPositiveButton("Okay") { _, _ ->
val name = editText.text.toString()

if (name.isEmpty()) {
Toast.makeText(requireContext(), "Name cannot be empty", Toast.LENGTH_SHORT)
.show()
askClientName()
return@setPositiveButton
}

prefs.edit(commit = true) {
putString("client_name", name).apply()
}
}
show()
}
}


private fun askForAnalyticsPermission() {
val prefs = PreferenceManager.getDefaultSharedPreferences(requireContext())
if (prefs.getBoolean("analytics_preference_asked", false)) return
Expand Down Expand Up @@ -376,4 +410,4 @@ class ProjectFragment : BaseBindingFragment<FragmentProjectBinding>(),
setTransition(androidx.fragment.app.FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
}
}
}
}
Loading

0 comments on commit 55397db

Please sign in to comment.