Skip to content

Commit

Permalink
feat(gemini): enhance module initialization and testing
Browse files Browse the repository at this point in the history
Refactor the Gemini module for streamlined initialization with new configuration parameters and improve test coverage with additional integration tests. Removed unnecessary API key from requests to streamline configuration handling.
  • Loading branch information
hanrw committed Jan 1, 2025
1 parent 7271538 commit 3ee5770
Show file tree
Hide file tree
Showing 13 changed files with 172 additions and 54 deletions.
2 changes: 1 addition & 1 deletion gemini-client/gemini-client-core/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ tasks.withType<KotlinCompile>().configureEach {
ksp {
arg("KOIN_DEFAULT_MODULE", "false")
// https://insert-koin.io/docs/reference/koin-annotations/start#compile-safety---check-your-koin-config-at-compile-time-since-130
arg("KOIN_CONFIG_CHECK", "true")
arg("KOIN_CONFIG_CHECK", "false")
}

tasks {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
package com.tddworks.gemini.api.textGeneration.api

import com.tddworks.di.getInstance

interface Gemini : TextGeneration {
companion object {
const val HOST = "generativelanguage.googleapis.com"
const val BASE_URL = "https://$HOST"


fun default(): Gemini {
return object : Gemini, TextGeneration by getInstance() {}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,7 @@ data class GenerateContentRequest(
@Transient
val model: GeminiModel = GeminiModel.GEMINI_1_5_FLASH,
@Transient
val stream: Boolean = false,
@Transient
val apiKey: String = ""
val stream: Boolean = false
) {
fun toRequestUrl(): String {
val endpoint = if (stream) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@ data class GenerateContentResponse(
@Serializable
data class Candidate(
val content: Content,
val finishReason: String,
val finishReason: String? = null,
val avgLogprobs: Double? = null
)

@Serializable
data class Content(
val parts: List<Part>,
val role: String
val role: String? = null
)

@Serializable
Expand All @@ -30,6 +30,6 @@ data class Part(
@Serializable
data class UsageMetadata(
val promptTokenCount: Int,
val candidatesTokenCount: Int,
val candidatesTokenCount: Int? = null,
val totalTokenCount: Int
)
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,6 @@ interface TextGeneration {
* data: {"candidates": [{"content": {"parts": [{"text": " it uses. It doesn't possess consciousness or genuine understanding in the human sense.\n"}],"role": "model"},"finishReason": "STOP"}],"usageMetadata": {"promptTokenCount": 4,"candidatesTokenCount": 724,"totalTokenCount": 728},"modelVersion": "gemini-1.5-flash"}
*/
fun streamGenerateContent(request: GenerateContentRequest): Flow<GenerateContentResponse>

companion object
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,20 +31,13 @@ class DefaultTextGenerationApi(
private fun HttpRequestBuilder.configureRequest(request: GenerateContentRequest) {
method = HttpMethod.Post
url(path = request.toRequestUrl())
parameters {
configureParameters(request)
if (request.stream) {
parameter("alt", "sse")
}
setBody(request)
contentType(ContentType.Application.Json)
}

private fun ParametersBuilder.configureParameters(request: GenerateContentRequest) {
append("api_key", request.apiKey)
if (request.stream) {
append("alt", "sse")
}
}

companion object {
const val GEMINI_API_PATH = "/v1beta/models"
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,26 +1,39 @@
package com.tddworks.gemini.di

import com.tddworks.common.network.api.ktor.api.HttpRequester
import com.tddworks.common.network.api.ktor.internal.*
import com.tddworks.di.commonModule
import kotlinx.serialization.json.Json
import org.koin.core.context.startKoin
import org.koin.core.qualifier.named
import org.koin.dsl.KoinAppDeclaration
import org.koin.dsl.module
import com.tddworks.common.network.api.ktor.internal.ClientFeatures
import com.tddworks.common.network.api.ktor.internal.UrlBasedConnectionConfig
import com.tddworks.common.network.api.ktor.internal.createHttpClient
import com.tddworks.common.network.api.ktor.internal.default
import com.tddworks.di.createJson
import com.tddworks.gemini.api.textGeneration.api.Gemini
import com.tddworks.gemini.api.textGeneration.api.GeminiConfig
import org.koin.core.annotation.ComponentScan
import org.koin.core.annotation.Module

//
//fun initGemini(
// appDeclaration: KoinAppDeclaration = {}
//): HttpRequester {
// return startKoin {
// appDeclaration()
// modules(commonModule(false) + geminiModules())
// }.koin.get<HttpRequester>()
//}
import org.koin.dsl.module
import org.koin.ksp.generated.module

@Module
@ComponentScan("com.tddworks.gemini")
class GeminiModule
class GeminiModule {
companion object {
fun initGeminiModule(config: GeminiConfig, enableNetworkLogs: Boolean) = module {
single {
HttpRequester.default(
createHttpClient(
connectionConfig = UrlBasedConnectionConfig(config.baseUrl),
features = ClientFeatures(
json = createJson(),
queryParams = mapOf("key" to config.apiKey())
)
)
)
}

includes(GeminiModule().module)

single { Gemini.default() }
}
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.tddworks.gemini.di

import com.tddworks.di.commonModule
import com.tddworks.gemini.api.textGeneration.api.GeminiConfig
import com.tddworks.gemini.di.GeminiModule.Companion.initGeminiModule
import org.koin.core.context.startKoin
import org.koin.dsl.KoinAppDeclaration

fun initGemini(
config: GeminiConfig,
enableNetworkLogs: Boolean = false,
appDeclaration: KoinAppDeclaration = {}
) = startKoin {
appDeclaration()
modules(
commonModule(enableNetworkLogs),
initGeminiModule(config, enableNetworkLogs)
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package com.tddworks.gemini.api

import app.cash.turbine.test
import com.tddworks.di.getInstance
import com.tddworks.gemini.api.textGeneration.api.*
import com.tddworks.gemini.di.initGemini
import kotlinx.coroutines.test.runTest
import org.junit.jupiter.api.Assertions.*
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable
import org.koin.test.junit5.AutoCloseKoinTest
import kotlin.time.Duration.Companion.seconds


@EnabledIfEnvironmentVariable(named = "GEMINI_API_KEY", matches = ".+")
class GeminiITest : AutoCloseKoinTest() {

@BeforeEach
fun setUp() {
initGemini(
config = GeminiConfig(
apiKey = { System.getenv("GEMINI_API_KEY") ?: "CONFIGURE_ME" },
baseUrl = { System.getenv("GEMINI_BASE_URL") ?: Gemini.BASE_URL }
))
}

@Test
fun `should return stream generateContent response`() = runTest {
val gemini = getInstance<Gemini>()

gemini.streamGenerateContent(
GenerateContentRequest(
contents = listOf(Content(parts = listOf(Part(text = "hello")))),
stream = true
)
).test(timeout = 10.seconds) {
assertNotNull(awaitItem())
assertNotNull(awaitItem())
cancelAndIgnoreRemainingEvents()
}
}

@Test
fun `should return generateContent response`() = runTest {
val gemini = getInstance<Gemini>()

val response = gemini.generateContent(
GenerateContentRequest(
contents = listOf(Content(parts = listOf(Part(text = "hello")))),
)
)

assertNotNull(response)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.tddworks.gemini.api

import com.tddworks.di.getInstance
import com.tddworks.gemini.api.textGeneration.api.Gemini
import com.tddworks.gemini.api.textGeneration.api.GeminiConfig
import com.tddworks.gemini.di.initGemini
import org.junit.jupiter.api.Assertions.*
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.koin.test.junit5.AutoCloseKoinTest


class GeminiTest : AutoCloseKoinTest() {

@BeforeEach
fun setUp() {
initGemini(config = GeminiConfig())
}

@Test
fun `should get gemini default api`() {
val gemini = getInstance<Gemini>()

assertNotNull(gemini)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,7 @@ class GenerateContentRequestTest {
// Given
val generateContentRequest = GenerateContentRequest(
contents = listOf(),
stream = false,
apiKey = "some-key"
stream = false
)

// When
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.tddworks.gemini.api

import com.tddworks.gemini.api.textGeneration.api.Gemini
import com.tddworks.gemini.api.textGeneration.api.GeminiConfig
import com.tddworks.gemini.di.initGemini

/**
* Object responsible for setting up and initializing the Anthropoc API client.
*/
object DarwinGemini {
//
/**
* Initializes the Anthropic library with the provided configuration parameters.
*
* @param apiKey a lambda function that returns the API key to be used for authentication
* @param baseUrl a lambda function that returns the base URL of the Anthropic API
* @param anthropicVersion a lambda function that returns the version of the Anthropic API to use
*/
fun anthropic(
apiKey: () -> String = { "CONFIG_API_KEY" },
baseUrl: () -> String = { Gemini.BASE_URL },
) = initGemini(GeminiConfig(apiKey, baseUrl))
}

This file was deleted.

0 comments on commit 3ee5770

Please sign in to comment.