diff --git a/app/src/main/java/dev/kaizen/app/chat/ChatComponents.kt b/app/src/main/java/dev/kaizen/app/chat/ChatComponents.kt index 6db2d06..45eae75 100644 --- a/app/src/main/java/dev/kaizen/app/chat/ChatComponents.kt +++ b/app/src/main/java/dev/kaizen/app/chat/ChatComponents.kt @@ -151,6 +151,9 @@ fun ModePillsRow( onReasoningChange: (ReasoningPreset) -> Unit, samplingPrefs: SamplingPrefs, onSamplingChange: (SamplingPrefs) -> Unit, + currentModelId: String = "", + webSearchProvider: String = "google", + onWebSearchProviderChange: (String) -> Unit = {}, modifier: Modifier = Modifier, ) { val cs = MaterialTheme.colorScheme @@ -159,6 +162,8 @@ fun ModePillsRow( var showReasoningMenu by remember { mutableStateOf(false) } var showSamplingPopup by remember { mutableStateOf(false) } + var showSearchProviderMenu by remember { mutableStateOf(false) } + val isVertexModel = currentModelId.startsWith("vertex:") Row( modifier @@ -255,6 +260,51 @@ fun ModePillsRow( active = active, onClick = { onToggle(mode.labelRes) }, ) + + // Search provider dropdown — only for Vertex models when search is active + if (mode.labelRes == R.string.mode_search && active && isVertexModel) { + Box { + val providerLabel = stringResource( + if (webSearchProvider == "enterprise") R.string.search_provider_enterprise + else R.string.search_provider_google + ) + ModePill( + icon = KaizenIcons.Globe, + label = providerLabel, + active = true, + showChevron = true, + onClick = { showSearchProviderMenu = true }, + ) + DropdownMenu( + expanded = showSearchProviderMenu, + onDismissRequest = { showSearchProviderMenu = false }, + containerColor = if (isDark) Color(0xFF1A2030) else Color(0xFFF8F9FB), + shadowElevation = 16.dp, + shape = KaizenShapes.md, + ) { + listOf("google" to R.string.search_provider_google, "enterprise" to R.string.search_provider_enterprise).forEach { (value, labelRes) -> + Row( + Modifier + .fillMaxWidth() + .clickable { onWebSearchProviderChange(value); showSearchProviderMenu = false } + .padding(horizontal = 16.dp, vertical = 10.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + Text( + stringResource(labelRes), + fontSize = 13.5.sp, + color = if (value == webSearchProvider) accent.primary else cs.onBackground.copy(alpha = 0.85f), + fontWeight = if (value == webSearchProvider) FontWeight.SemiBold else FontWeight.Normal, + modifier = Modifier.weight(1f), + ) + if (value == webSearchProvider) { + Icon(KaizenIcons.Check, null, tint = accent.primary, modifier = Modifier.size(15.dp)) + } + } + } + } + } + } } } } diff --git a/app/src/main/java/dev/kaizen/app/chat/ChatScreen.kt b/app/src/main/java/dev/kaizen/app/chat/ChatScreen.kt index f01286a..142db93 100644 --- a/app/src/main/java/dev/kaizen/app/chat/ChatScreen.kt +++ b/app/src/main/java/dev/kaizen/app/chat/ChatScreen.kt @@ -114,6 +114,7 @@ fun ChatScreen( var activeMode by remember { mutableStateOf(null) } var reasoningPreset by remember { mutableStateOf(ReasoningPreset.Standard) } var samplingPrefs by remember { mutableStateOf(SamplingPrefs()) } + var webSearchProvider by remember { mutableStateOf("google") } var usedTokens by remember { mutableStateOf(0) } var chatModel by remember { mutableStateOf(null) } var streamJob by remember { mutableStateOf(null) } @@ -584,6 +585,7 @@ fun ChatScreen( preferThroughput = reasoningPreset.throughput, attachments = uploadedAtts.takeIf { it.isNotEmpty() }, webSearch = activeMode == R.string.mode_search, + webSearchProvider = if (activeMode == R.string.mode_search && cfg.model.startsWith("vertex:")) webSearchProvider else null, temperature = samplingPrefs.temperature.takeIf { it.enabled }?.value, topP = samplingPrefs.topP.takeIf { it.enabled }?.value, topK = samplingPrefs.topK.takeIf { it.enabled }?.value?.toInt(), @@ -1055,6 +1057,9 @@ fun ChatScreen( onReasoningChange = { reasoningPreset = it; haptics.tick() }, samplingPrefs = samplingPrefs, onSamplingChange = { samplingPrefs = it }, + currentModelId = session.config?.model ?: "", + webSearchProvider = webSearchProvider, + onWebSearchProviderChange = { webSearchProvider = it; haptics.tick() }, ) Spacer(Modifier.height(if (imeVisible) 6.dp else 10.dp)) ChatInput( diff --git a/app/src/main/java/dev/kaizen/app/net/KaizenApi.kt b/app/src/main/java/dev/kaizen/app/net/KaizenApi.kt index b47e745..0c1b3da 100644 --- a/app/src/main/java/dev/kaizen/app/net/KaizenApi.kt +++ b/app/src/main/java/dev/kaizen/app/net/KaizenApi.kt @@ -37,6 +37,7 @@ import java.util.concurrent.TimeUnit val preferThroughput: Boolean? = null, val attachments: List? = null, val webSearch: Boolean? = null, + val webSearchProvider: String? = null, val temperature: Float? = null, val topP: Float? = null, val topK: Int? = null, @@ -487,6 +488,7 @@ object KaizenApi { preferThroughput: Boolean = false, attachments: List? = null, webSearch: Boolean = false, + webSearchProvider: String? = null, temperature: Float? = null, topP: Float? = null, topK: Int? = null, @@ -499,6 +501,7 @@ object KaizenApi { preferThroughput = if (preferThroughput) true else null, attachments = attachments?.takeIf { it.isNotEmpty() }, webSearch = if (webSearch) true else null, + webSearchProvider = if (webSearch) webSearchProvider else null, temperature = temperature, topP = topP, topK = topK, diff --git a/app/src/main/res/values-en/strings.xml b/app/src/main/res/values-en/strings.xml index b28c744..40121ea 100644 --- a/app/src/main/res/values-en/strings.xml +++ b/app/src/main/res/values-en/strings.xml @@ -48,6 +48,8 @@ Search Video Audio + Google + Enterprise Brainstorm diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 5301c71..5b2d3d4 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -50,6 +50,8 @@ Suche Video Audio + Google + Enterprise Brainstormen