Skip to content

Commit accb7ba

Browse files
authored
Merge pull request #17 from DevelopersBreach/shreyas/migrate-openai-to-gemini
Switch AI integration from OpenAI to Gemini.
2 parents 49cdd62 + 15fc888 commit accb7ba

File tree

18 files changed

+269
-213
lines changed

18 files changed

+269
-213
lines changed

.github/workflows/main.yml

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ jobs:
1010
build:
1111
runs-on: ubuntu-latest
1212
env:
13-
OPEN_API_KEY: ${{ secrets.OPEN_API_KEY }}
13+
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
1414

1515
steps:
1616
- name: Checkout
@@ -23,9 +23,18 @@ jobs:
2323
java-version: 17
2424
cache: gradle
2525

26+
- name: Set up Android SDK
27+
uses: android-actions/setup-android@v2
28+
with:
29+
api-level: 33
30+
build-tools: "33.0.2"
31+
2632
- name: Grant execute permission for gradlew
2733
run: chmod +x gradlew
2834

35+
- name: Create local.properties
36+
run: echo "sdk.dir=$ANDROID_SDK_ROOT" > local.properties
37+
2938
- name: Cache Gradle dependencies
3039
uses: actions/cache@v3
3140
with:

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ This is an educational project developed by students. Expect rapid changes, expe
6565
- [ ] Introduce common @Preview annotations for reusable Composable previews.
6666
- [ ] Implement section-wise scroll behavior on the `Detail Screen` for better navigation.
6767
- [ ] Add inline code formatting support for syntax display on the `Detail Screen`.
68-
- [ ] Replace the search icon on the `Topic Card` to improve visual consistency.
68+
- [x] Replace the search icon on the `Topic Card` to improve visual consistency.
6969
- [ ] Implement caching on the `Detail Screen` to store previously viewed topic data.
70-
- [ ] Switch AI integration from OpenAI to Gemini.
70+
- [x] Switch AI integration from OpenAI to Gemini.
7171
- [ ] Refactor network layer for cleaner architecture.

build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,6 @@ plugins {
77
alias(libs.plugins.composeCompiler) apply false
88
alias(libs.plugins.kotlinMultiplatform) apply false
99
alias(libs.plugins.serialization) apply false
10+
alias(libs.plugins.googleSecrets) apply false
1011
alias(libs.plugins.jetbrainsKotlinJvm) apply false
1112
}

composeApp/build.gradle.kts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ plugins {
1414
alias(libs.plugins.composeCompiler)
1515
alias(libs.plugins.serialization)
1616
alias(libs.plugins.ktlint)
17+
alias(libs.plugins.googleSecrets)
1718
}
1819

1920
ktlint {
@@ -75,6 +76,7 @@ kotlin {
7576
implementation(libs.androidx.activity.compose)
7677
implementation(libs.koin.android)
7778
implementation(libs.koin.androidx.compose)
79+
implementation(libs.generativeai)
7880
}
7981
commonMain.dependencies {
8082
implementation(compose.runtime)
@@ -177,14 +179,14 @@ fun ApplicationDefaultConfig.setupBuildConfigFields(
177179
)
178180
}
179181

180-
if (secret("OPEN_API_KEY").isEmpty()) {
181-
error("OPEN_API_KEY not set in local.properties")
182+
if (secret("GEMINI_API_KEY").isEmpty()) {
183+
error("GEMINI_API_KEY not set in local.properties")
182184
}
183185

184186
buildConfigField(
185187
type = "String",
186-
name = "OPEN_API_KEY",
187-
value = "\"${secret("OPEN_API_KEY")}\"",
188+
name = "GEMINI_API_KEY",
189+
value = "\"${secret("GEMINI_API_KEY")}\"",
188190
)
189191
}
190192

composeApp/src/androidMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/MainActivity.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import androidx.activity.ComponentActivity
55
import androidx.activity.compose.setContent
66
import androidx.compose.runtime.Composable
77
import androidx.compose.ui.tooling.preview.Preview
8-
import com.developersbreach.kotlindictionarymultiplatform.di.appModule
8+
import com.developersbreach.kotlindictionarymultiplatform.di.appModules
99
import org.koin.android.ext.koin.androidContext
1010
import org.koin.core.context.startKoin
1111

@@ -17,7 +17,7 @@ class MainActivity : ComponentActivity() {
1717

1818
startKoin {
1919
androidContext(this@MainActivity)
20-
modules(appModule)
20+
appModules()
2121
}
2222

2323
setContent {

composeApp/src/androidMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/Platform.android.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,5 @@ actual fun getPlatform(): Platform {
1111
}
1212

1313
actual fun getOpenApiKey(): String {
14-
return BuildConfig.OPEN_API_KEY
14+
return BuildConfig.GEMINI_API_KEY
1515
}

composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/core/network/KtorHttpClient.kt

Lines changed: 0 additions & 136 deletions
This file was deleted.
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package com.developersbreach.kotlindictionarymultiplatform.core.network.api
2+
3+
import com.developersbreach.kotlindictionarymultiplatform.Log
4+
import com.developersbreach.kotlindictionarymultiplatform.core.network.parser.GeminiJsonParser
5+
import com.developersbreach.kotlindictionarymultiplatform.core.network.request.GeminiPromptBuilder
6+
import com.developersbreach.kotlindictionarymultiplatform.data.detail.model.KotlinTopicDetails
7+
import io.ktor.client.HttpClient
8+
import io.ktor.client.request.post
9+
import io.ktor.client.request.setBody
10+
import io.ktor.client.statement.bodyAsText
11+
import io.ktor.http.ContentType
12+
import io.ktor.http.contentType
13+
import kotlinx.serialization.json.Json
14+
15+
class GeminiApiService(
16+
private val client: HttpClient,
17+
private val json: Json,
18+
) {
19+
suspend fun generateTopicDetails(
20+
topicId: String,
21+
apiKey: String,
22+
): KotlinTopicDetails {
23+
val prompt = GeminiPromptBuilder.buildRequest(topicId)
24+
val requestBody = GeminiPromptBuilder.buildRequestBody(prompt)
25+
26+
val response = client.post(
27+
"https://generativelanguage.googleapis.com/v1/models/gemini-2.0-flash:generateContent?key=$apiKey",
28+
) {
29+
contentType(ContentType.Application.Json)
30+
setBody(requestBody)
31+
}
32+
33+
val responseBody = response.bodyAsText()
34+
Log.i("GeminiRawResponse", responseBody)
35+
36+
val cleanJson = GeminiJsonParser.extractCleanJson(responseBody, json)
37+
Log.i("CleanJson", cleanJson)
38+
39+
return json.decodeFromString(KotlinTopicDetails.serializer(), cleanJson)
40+
}
41+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package com.developersbreach.kotlindictionarymultiplatform.core.network.parser
2+
3+
import kotlinx.serialization.json.Json
4+
import kotlinx.serialization.json.jsonObject
5+
import kotlinx.serialization.json.jsonArray
6+
import kotlinx.serialization.json.jsonPrimitive
7+
8+
object GeminiJsonParser {
9+
10+
fun extractCleanJson(
11+
rawResponse: String,
12+
json: Json,
13+
): String {
14+
val root = json.parseToJsonElement(rawResponse).jsonObject
15+
val text = root["candidates"]
16+
?.jsonArray?.firstOrNull()
17+
?.jsonObject?.get("content")
18+
?.jsonObject?.get("parts")
19+
?.jsonArray?.firstOrNull()
20+
?.jsonObject?.get("text")
21+
?.jsonPrimitive?.content
22+
?: error("Malformed Gemini response")
23+
24+
return text
25+
.removePrefix("```json\n")
26+
.removePrefix("```json")
27+
.removePrefix("json\n")
28+
.removePrefix("json")
29+
.removeSuffix("```")
30+
.trim()
31+
}
32+
}

0 commit comments

Comments
 (0)