Skip to content

Commit 3957246

Browse files
Fix UI model sync, sliders, persistence, and instance kill switches
1 parent 5761192 commit 3957246

4 files changed

Lines changed: 61 additions & 27 deletions

File tree

app/src/main/kotlin/com/google/ai/sample/GenerativeAiViewModelFactory.kt

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,8 +137,12 @@ object GenerativeAiViewModelFactory {
137137
private var currentModel: ModelOption = ModelOption.GPT_5_1_CODEX_MAX
138138
private var currentBackend: InferenceBackend = InferenceBackend.GPU
139139

140-
fun setModel(modelOption: ModelOption) {
140+
fun setModel(modelOption: ModelOption, context: Context? = null) {
141141
currentModel = modelOption
142+
if (context != null) {
143+
val prefs = context.getSharedPreferences("inference_prefs", Context.MODE_PRIVATE)
144+
prefs.edit().putString("selected_model", modelOption.name).apply()
145+
}
142146
}
143147

144148
fun getCurrentModel(): ModelOption {
@@ -164,4 +168,14 @@ object GenerativeAiViewModelFactory {
164168
InferenceBackend.GPU
165169
}
166170
}
171+
172+
fun loadModelPreference(context: Context) {
173+
val prefs = context.getSharedPreferences("inference_prefs", Context.MODE_PRIVATE)
174+
val modelNameStr = prefs.getString("selected_model", ModelOption.GPT_5_1_CODEX_MAX.name)
175+
currentModel = try {
176+
ModelOption.valueOf(modelNameStr ?: ModelOption.GPT_5_1_CODEX_MAX.name)
177+
} catch (e: IllegalArgumentException) {
178+
ModelOption.GPT_5_1_CODEX_MAX
179+
}
180+
}
167181
}

app/src/main/kotlin/com/google/ai/sample/MainActivity.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -412,6 +412,9 @@ class MainActivity : ComponentActivity() {
412412
Log.d(TAG, "onCreate: Calling setupBillingClient.")
413413
setupBillingClient()
414414

415+
Log.d(TAG, "onCreate: Loading Model Preference.")
416+
GenerativeAiViewModelFactory.loadModelPreference(this)
417+
415418
Log.d(TAG, "onCreate: Calling TrialManager.initializeTrialStateFlagsIfNecessary.")
416419
TrialManager.initializeTrialStateFlagsIfNecessary(this)
417420

app/src/main/kotlin/com/google/ai/sample/MenuScreen.kt

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ fun MenuScreen(
181181
showDownloadDialog = true
182182
} else {
183183
selectedModel = modelOption
184-
GenerativeAiViewModelFactory.setModel(modelOption)
184+
GenerativeAiViewModelFactory.setModel(modelOption, context)
185185

186186
// Task 15: Initialize offline model upon selection
187187
val mainActivity = context as? MainActivity
@@ -190,7 +190,7 @@ fun MenuScreen(
190190
} else if (modelOption == ModelOption.HUMAN_EXPERT) {
191191
if (isPurchased) {
192192
selectedModel = modelOption
193-
GenerativeAiViewModelFactory.setModel(modelOption)
193+
GenerativeAiViewModelFactory.setModel(modelOption, context)
194194
if (wasOfflineModel) {
195195
// Task 19: Close offline model to free RAM
196196
val mainActivity = context as? MainActivity
@@ -201,7 +201,7 @@ fun MenuScreen(
201201
}
202202
} else {
203203
selectedModel = modelOption
204-
GenerativeAiViewModelFactory.setModel(modelOption)
204+
GenerativeAiViewModelFactory.setModel(modelOption, context)
205205
if (wasOfflineModel) {
206206
// Task 19: Close offline model to free RAM
207207
val mainActivity = context as? MainActivity
@@ -261,10 +261,9 @@ fun MenuScreen(
261261
onClick = {
262262
GenerativeAiViewModelFactory.setBackend(InferenceBackend.GPU, context)
263263
currentBackend.value = InferenceBackend.GPU
264-
// Re-initialize model with new backend
265264
val mainActivity = context as? MainActivity
266-
mainActivity?.getPhotoReasoningViewModel()?.reinitializeOfflineModel(context)
267-
Toast.makeText(context, "GPU selected – Model loaded into RAM and processed on GPU", Toast.LENGTH_SHORT).show()
265+
mainActivity?.getPhotoReasoningViewModel()?.closeOfflineModel()
266+
Toast.makeText(context, "GPU selected – Model stopped. Will load on next generation", Toast.LENGTH_SHORT).show()
268267
},
269268
modifier = Modifier.weight(1f),
270269
colors = if (currentBackend.value == InferenceBackend.GPU)
@@ -280,12 +279,9 @@ fun MenuScreen(
280279
onClick = {
281280
GenerativeAiViewModelFactory.setBackend(InferenceBackend.CPU, context)
282281
currentBackend.value = InferenceBackend.CPU
283-
// Re-initialize model with new backend
284282
val mainActivity = context as? MainActivity
285-
// Explicitly kill the model from GPU to free RAM before reloading
286283
mainActivity?.getPhotoReasoningViewModel()?.closeOfflineModel()
287-
mainActivity?.getPhotoReasoningViewModel()?.reinitializeOfflineModel(context)
288-
Toast.makeText(context, "CPU selected – Model reloading", Toast.LENGTH_SHORT).show()
284+
Toast.makeText(context, "CPU selected – Model stopped. Will load on next generation", Toast.LENGTH_SHORT).show()
289285
},
290286
modifier = Modifier.weight(1f),
291287
colors = if (currentBackend.value == InferenceBackend.CPU)
@@ -322,6 +318,9 @@ fun MenuScreen(
322318
)
323319
)
324320
}
321+
var tempSlider by remember(selectedModel) { mutableStateOf(genSettings.value.temperature) }
322+
var topPSlider by remember(selectedModel) { mutableStateOf(genSettings.value.topP) }
323+
var topKSlider by remember(selectedModel) { mutableStateOf(genSettings.value.topK.toFloat()) }
325324

326325
Card(
327326
modifier = Modifier
@@ -342,15 +341,16 @@ fun MenuScreen(
342341

343342
// Temperature Slider (0.0 - 2.0)
344343
Text(
345-
text = "Temperature: ${"%.2f".format(genSettings.value.temperature)}",
344+
text = "Temperature: ${"%.2f".format(tempSlider)}",
346345
style = MaterialTheme.typography.bodyMedium
347346
)
348347
androidx.compose.material3.Slider(
349-
value = genSettings.value.temperature,
348+
value = tempSlider,
350349
onValueChange = { newVal ->
351-
genSettings.value = genSettings.value.copy(temperature = newVal)
350+
tempSlider = newVal
352351
},
353352
onValueChangeFinished = {
353+
genSettings.value = genSettings.value.copy(temperature = tempSlider)
354354
com.google.ai.sample.util.GenerationSettingsPreferences.saveSettings(
355355
context, selectedModel.modelName, genSettings.value
356356
)
@@ -364,15 +364,16 @@ fun MenuScreen(
364364

365365
// TopP Slider (0.0 - 1.0)
366366
Text(
367-
text = "Top P: ${"%.2f".format(genSettings.value.topP)}",
367+
text = "Top P: ${"%.2f".format(topPSlider)}",
368368
style = MaterialTheme.typography.bodyMedium
369369
)
370370
androidx.compose.material3.Slider(
371-
value = genSettings.value.topP,
371+
value = topPSlider,
372372
onValueChange = { newVal ->
373-
genSettings.value = genSettings.value.copy(topP = newVal)
373+
topPSlider = newVal
374374
},
375375
onValueChangeFinished = {
376+
genSettings.value = genSettings.value.copy(topP = topPSlider)
376377
com.google.ai.sample.util.GenerationSettingsPreferences.saveSettings(
377378
context, selectedModel.modelName, genSettings.value
378379
)
@@ -386,15 +387,16 @@ fun MenuScreen(
386387

387388
// TopK Slider (0 - 100)
388389
Text(
389-
text = "Top K: ${genSettings.value.topK}",
390+
text = "Top K: ${Math.round(topKSlider)}",
390391
style = MaterialTheme.typography.bodyMedium
391392
)
392393
androidx.compose.material3.Slider(
393-
value = genSettings.value.topK.toFloat(),
394+
value = topKSlider,
394395
onValueChange = { newVal ->
395-
genSettings.value = genSettings.value.copy(topK = Math.round(newVal))
396+
topKSlider = newVal
396397
},
397398
onValueChangeFinished = {
399+
genSettings.value = genSettings.value.copy(topK = Math.round(topKSlider))
398400
com.google.ai.sample.util.GenerationSettingsPreferences.saveSettings(
399401
context, selectedModel.modelName, genSettings.value
400402
)
@@ -725,7 +727,7 @@ fun MenuScreen(
725727
// Set model only after download is completed (Point 17)
726728
downloadDialogModel?.let {
727729
selectedModel = it
728-
GenerativeAiViewModelFactory.setModel(it)
730+
GenerativeAiViewModelFactory.setModel(it, context)
729731
}
730732
showDownloadDialog = false
731733
}) { Text("Close") }

app/src/main/kotlin/com/google/ai/sample/feature/multimodal/PhotoReasoningViewModel.kt

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -822,6 +822,25 @@ class PhotoReasoningViewModel(
822822
_uiState.value = PhotoReasoningUiState.Error("Application not ready")
823823
return
824824
}
825+
826+
// Task 3: Sync model before API call
827+
val apiKeyManager = ApiKeyManager.getInstance(context)
828+
val currentKey = apiKeyManager.getCurrentApiKey(currentModel.apiProvider)
829+
if (currentKey != null && currentModel != ModelOption.GEMMA_3N_E4B_IT && currentModel != ModelOption.HUMAN_EXPERT) {
830+
val genSettings = com.google.ai.sample.util.GenerationSettingsPreferences.loadSettings(context, currentModel.modelName)
831+
val config = com.google.ai.client.generativeai.type.generationConfig {
832+
temperature = genSettings.temperature
833+
topP = genSettings.topP
834+
topK = genSettings.topK
835+
}
836+
generativeModel = GenerativeModel(
837+
modelName = currentModel.modelName,
838+
apiKey = currentKey,
839+
generationConfig = config
840+
)
841+
_modelNameState.value = currentModel.modelName
842+
}
843+
825844
ensureInitialized(context)
826845
currentRetryAttempt = 0
827846
performReasoning(userInput, selectedImages, screenInfoForPrompt, imageUrisForChat)
@@ -1059,13 +1078,9 @@ class PhotoReasoningViewModel(
10591078
liveApiManager?.close()
10601079
}
10611080

1062-
// Close offline model ONLY if no inference/commands are running
1063-
val isReasoningActive = currentReasoningJob?.isActive == true
1064-
val isCommandProcessingActive = commandProcessingJob?.isActive == true
1065-
if (!isReasoningActive && !isCommandProcessingActive && com.google.ai.sample.GenerativeAiViewModelFactory.getCurrentModel() == ModelOption.GEMMA_3N_E4B_IT) {
1081+
// Close offline model instance to force stop generation or just release RAM
1082+
if (com.google.ai.sample.GenerativeAiViewModelFactory.getCurrentModel() == ModelOption.GEMMA_3N_E4B_IT) {
10661083
closeOfflineModel()
1067-
_uiState.value = PhotoReasoningUiState.Initial
1068-
return // We are done here, just closed
10691084
}
10701085

10711086
// Rest of the existing onStopClicked code

0 commit comments

Comments
 (0)