Skip to content

Commit a53dcaf

Browse files
authored
Refactor GeminiConfig (#5)
There are many config entries that were hardcoded, exposing all of them through the old way would cause lots of code repetition, with this, the new GeminiConfig has all the config entries with sane defaults.
1 parent 3e23b4e commit a53dcaf

6 files changed

Lines changed: 106 additions & 88 deletions

File tree

audio/src/main/scala/com/alexitc/geminilive4s/GeminiService.scala

Lines changed: 7 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -189,71 +189,21 @@ object GeminiService {
189189

190190
def make(
191191
apiKey: String,
192-
promptSettings: GeminiPromptSettings,
193-
functions: List[GeminiFunction],
194-
customApiVersion: Option[GeminiCustomApi] = None
192+
config: GeminiConfig
195193
): fs2.Stream[IO, GeminiService] = {
196194
val wakeUpMessage =
197-
if (promptSettings.language.string.startsWith("es")) "Hola" else "Hello"
195+
if (config.language.string.startsWith("es")) "Hola"
196+
else "Hello"
197+
198198
for {
199199
gemini <- fs2.Stream.resource(
200200
GeminiIO.make(
201201
apiKey,
202-
promptSettings,
203-
functions.map(_.declaration),
204-
makeGeminiConfig,
205-
customApiVersion
202+
config,
203+
GeminiLiveConfigBuilder.make(config)
206204
)
207205
)
208-
} yield new GeminiService(gemini, functions, wakeUpMessage)
209-
}
210-
211-
def makeGeminiConfig(
212-
configParams: GeminiConfigParams
213-
): genai.types.LiveConnectConfig = {
214-
import configParams.*
215-
216-
val tool = genai.types.Tool
217-
.builder()
218-
.functionDeclarations(functionDefs*)
219-
.build()
220-
221-
genai.types.LiveConnectConfig
222-
.builder()
223-
.inputAudioTranscription(
224-
genai.types.AudioTranscriptionConfig.builder().build()
225-
)
226-
.outputAudioTranscription(
227-
genai.types.AudioTranscriptionConfig.builder().build()
228-
)
229-
.responseModalities(genai.types.Modality.Known.AUDIO)
230-
.systemInstruction(
231-
genai.types.Content
232-
.builder()
233-
.parts(genai.types.Part.builder().text(prompt))
234-
.build()
235-
)
236-
.speechConfig(
237-
genai.types.SpeechConfig
238-
.builder()
239-
.voiceConfig(
240-
genai.types.VoiceConfig
241-
.builder()
242-
.prebuiltVoiceConfig(
243-
genai.types.PrebuiltVoiceConfig
244-
.builder()
245-
.voiceName(voiceName.string)
246-
)
247-
)
248-
.languageCode(voiceLanguage.string)
249-
)
250-
.tools(tool)
251-
.temperature(0.7f)
252-
// .enableAffectiveDialog(true) // not supported by a all models
253-
// .proactivity(
254-
// genai.types.ProactivityConfig.builder().proactiveAudio(true).build()
255-
// )
256-
.build()
206+
} yield new GeminiService(gemini, config.functions, wakeUpMessage)
257207
}
258208

259209
private case class TaggedMessage(

audio/src/main/scala/com/alexitc/geminilive4s/internal/GeminiIO.scala

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -73,13 +73,11 @@ private[geminilive4s] object GeminiIO {
7373

7474
def make(
7575
apiKey: String,
76-
promptSettings: GeminiPromptSettings,
77-
functionDefs: List[FunctionDeclaration],
78-
makeGeminiConfig: GeminiConfigParams => LiveConnectConfig,
79-
customApiVersion: Option[GeminiCustomApi]
76+
config: GeminiConfig,
77+
liveConfig: LiveConnectConfig
8078
): Resource[IO, GeminiIO] = {
8179
def acquire(dispatcher: Dispatcher[IO]) = for {
82-
client <- customApiVersion match {
80+
client <- config.customApiVersion match {
8381
case None =>
8482
IO(Client.builder().apiKey(apiKey).build())
8583
case Some(version) =>
@@ -93,14 +91,7 @@ private[geminilive4s] object GeminiIO {
9391
.build()
9492
)
9593
}
96-
configParams = GeminiConfigParams(
97-
prompt = promptSettings.prompt,
98-
voiceLanguage = promptSettings.language,
99-
voiceName = promptSettings.voiceName,
100-
functionDefs = functionDefs
101-
)
102-
config = makeGeminiConfig(configParams)
103-
session <- makeSession(client, config, promptSettings.model)
94+
session <- makeSession(client, liveConfig, config.model)
10495

10596
_ <- IO.println("✅ Connected to Gemini Live API")
10697
} yield GeminiIO(session, dispatcher)
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package com.alexitc.geminilive4s.internal
2+
3+
import com.alexitc.geminilive4s.models.GeminiConfig
4+
import com.google.genai.types.*
5+
6+
object GeminiLiveConfigBuilder {
7+
def make(params: GeminiConfig): LiveConnectConfig = {
8+
def transform(when: Boolean)(
9+
f: LiveConnectConfig.Builder => LiveConnectConfig.Builder
10+
)(builder: LiveConnectConfig.Builder): LiveConnectConfig.Builder = {
11+
if (when) f(builder) else builder
12+
}
13+
14+
val options = List(
15+
transform(params.disableAutomaticActivityDetection)(builder =>
16+
builder // TODO: Enable when the app supports handling this
17+
// _.realtimeInputConfig(
18+
// RealtimeInputConfig
19+
// .builder()
20+
// .automaticActivityDetection(
21+
// AutomaticActivityDetection.builder().disabled(true).build()
22+
// )
23+
// .build()
24+
// )
25+
),
26+
transform(params.inputAudioTranscription)(
27+
_.inputAudioTranscription(AudioTranscriptionConfig.builder().build())
28+
),
29+
transform(params.outputAudioTranscription)(
30+
_.outputAudioTranscription(AudioTranscriptionConfig.builder().build())
31+
),
32+
transform(params.enableAffectiveDialog)(_.enableAffectiveDialog(true)),
33+
transform(params.proactivity)(
34+
_.proactivity(ProactivityConfig.builder().proactiveAudio(true).build())
35+
)
36+
)
37+
38+
val functionDefs = params.functions.map(_.declaration)
39+
val tool = Tool
40+
.builder()
41+
.functionDeclarations(functionDefs*)
42+
.build()
43+
44+
val base = LiveConnectConfig
45+
.builder()
46+
.responseModalities(Modality.Known.AUDIO)
47+
.systemInstruction(
48+
Content
49+
.builder()
50+
.parts(Part.builder().text(params.prompt))
51+
.build()
52+
)
53+
.speechConfig(
54+
SpeechConfig
55+
.builder()
56+
.voiceConfig(
57+
VoiceConfig
58+
.builder()
59+
.prebuiltVoiceConfig(
60+
PrebuiltVoiceConfig.builder().voiceName(params.voiceName.string)
61+
)
62+
)
63+
.languageCode(params.language.string)
64+
)
65+
.tools(tool)
66+
.temperature(params.temperature)
67+
68+
options
69+
.foldLeft(base) { case (builder, apply) => apply(builder) }
70+
.build()
71+
}
72+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package com.alexitc.geminilive4s.models
2+
3+
case class GeminiConfig(
4+
prompt: String,
5+
functions: List[GeminiFunction],
6+
language: GeminiLanguage = GeminiLanguage.EnglishUS,
7+
voiceName: GeminiVoice = GeminiVoice.random,
8+
model: GeminiModel = GeminiModel.FlashPreview,
9+
temperature: Float = 0.7f,
10+
// Required when using experimental models
11+
customApiVersion: Option[GeminiCustomApi] = None,
12+
// When enabled, Gemini waits for messages to confirm when the voice starts/stops
13+
// NOTE: This is not implemented yet
14+
disableAutomaticActivityDetection: Boolean = false,
15+
// When enabled, Gemini transcribes the input voice
16+
inputAudioTranscription: Boolean = false,
17+
// When enabled, Gemini transcribes the output voice
18+
outputAudioTranscription: Boolean = false,
19+
// Only a few models support this
20+
enableAffectiveDialog: Boolean = false,
21+
// Only a few models support this
22+
proactivity: Boolean = false
23+
)

audio/src/main/scala/com/alexitc/geminilive4s/models/GeminiConfigParams.scala

Lines changed: 0 additions & 10 deletions
This file was deleted.

audio/src/main/scala/com/alexitc/geminilive4s/models/GeminiPromptSettings.scala

Lines changed: 0 additions & 8 deletions
This file was deleted.

0 commit comments

Comments
 (0)