fix(model): MoA row shows 'not loaded' on null temperatures (#1421)
This commit is contained in:
3 files changed
+45
-5
No files matched your search
@@ -25,14 +25,20 @@ data class MainModelAssignment(
|
|||||||
data class MoaModelSlot(
|
data class MoaModelSlot(
|
||||||
val provider: String,
|
val provider: String,
|
||||||
val model: String,
|
val model: String,
|
||||||
|
val reasoning_effort: String? = null,
|
||||||
|
val enabled: Boolean = true,
|
||||||
)
|
)
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class MoaConfigPreset(
|
data class MoaConfigPreset(
|
||||||
val reference_models: List<MoaModelSlot>,
|
val reference_models: List<MoaModelSlot>,
|
||||||
val aggregator: MoaModelSlot,
|
val aggregator: MoaModelSlot,
|
||||||
val reference_temperature: Double = 0.7,
|
// null = omitted from API calls (provider default); the backend sends null when unset.
|
||||||
val aggregator_temperature: Double = 0.3,
|
val reference_temperature: Double? = null,
|
||||||
|
val aggregator_temperature: Double? = null,
|
||||||
|
val reference_timeout: Double? = null,
|
||||||
|
val degraded_reference_policy: String = "loud",
|
||||||
|
val fanout: String? = null,
|
||||||
val max_tokens: Int = 4096,
|
val max_tokens: Int = 4096,
|
||||||
val enabled: Boolean = true,
|
val enabled: Boolean = true,
|
||||||
)
|
)
|
||||||
@@ -44,8 +50,12 @@ data class MoaConfigResponse(
|
|||||||
val presets: Map<String, MoaConfigPreset> = emptyMap(),
|
val presets: Map<String, MoaConfigPreset> = emptyMap(),
|
||||||
val reference_models: List<MoaModelSlot> = emptyList(),
|
val reference_models: List<MoaModelSlot> = emptyList(),
|
||||||
val aggregator: MoaModelSlot = MoaModelSlot("", ""),
|
val aggregator: MoaModelSlot = MoaModelSlot("", ""),
|
||||||
val reference_temperature: Double = 0.7,
|
// null = omitted from API calls (provider default); the backend sends null when unset.
|
||||||
val aggregator_temperature: Double = 0.3,
|
val reference_temperature: Double? = null,
|
||||||
|
val aggregator_temperature: Double? = null,
|
||||||
|
val reference_timeout: Double? = null,
|
||||||
|
val degraded_reference_policy: String = "loud",
|
||||||
|
val fanout: String? = null,
|
||||||
val max_tokens: Int = 4096,
|
val max_tokens: Int = 4096,
|
||||||
val enabled: Boolean = true,
|
val enabled: Boolean = true,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import androidx.compose.runtime.snapshotFlow
|
|||||||
import androidx.compose.ui.platform.ClipEntry
|
import androidx.compose.ui.platform.ClipEntry
|
||||||
import androidx.compose.ui.platform.LocalClipboard
|
import androidx.compose.ui.platform.LocalClipboard
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.lifecycle.Lifecycle
|
import androidx.lifecycle.Lifecycle
|
||||||
import androidx.lifecycle.LifecycleEventObserver
|
import androidx.lifecycle.LifecycleEventObserver
|
||||||
@@ -146,7 +147,7 @@ fun ChatLifecycleEffects(
|
|||||||
|
|
||||||
// Show error as snackbar
|
// Show error as snackbar
|
||||||
val clipboard = LocalClipboard.current
|
val clipboard = LocalClipboard.current
|
||||||
val copyLabel = context.getString(R.string.action_copy_error)
|
val copyLabel = stringResource(R.string.action_copy_error)
|
||||||
// Own scope + immediate clear: a later `errorMessage = null` elsewhere must not cancel a visible popup.
|
// Own scope + immediate clear: a later `errorMessage = null` elsewhere must not cancel a visible popup.
|
||||||
val snackbarScope = rememberCoroutineScope()
|
val snackbarScope = rememberCoroutineScope()
|
||||||
LaunchedEffect(errorMessage) {
|
LaunchedEffect(errorMessage) {
|
||||||
|
|||||||
@@ -622,4 +622,33 @@ class ModelSerializationTest {
|
|||||||
json.decodeFromString<ToggleSkillRequest>(jsonInput)
|
json.decodeFromString<ToggleSkillRequest>(jsonInput)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun moaConfigDecodesNullTemperaturesAndNewFields() {
|
||||||
|
val payload =
|
||||||
|
"""
|
||||||
|
{"default_preset":"default","active_preset":"","privacy_filter":"",
|
||||||
|
"presets":{"default":{"enabled":true,
|
||||||
|
"reference_models":[{"provider":"openrouter","model":"x/y","enabled":false,"reasoning_effort":"high"}],
|
||||||
|
"aggregator":{"provider":"openrouter","model":"a/b"},
|
||||||
|
"reference_temperature":null,"aggregator_temperature":null,
|
||||||
|
"reference_timeout":null,"degraded_reference_policy":"silent","fanout":"every_n:3"}},
|
||||||
|
"reference_models":[{"provider":"openrouter","model":"x/y","enabled":true}],
|
||||||
|
"aggregator":{"provider":"openrouter","model":"a/b"},
|
||||||
|
"reference_temperature":null,"aggregator_temperature":null,
|
||||||
|
"reference_timeout":null,"degraded_reference_policy":"silent","fanout":"every_n:3","enabled":true}
|
||||||
|
""".trimIndent()
|
||||||
|
val cfg = json.decodeFromString<MoaConfigResponse>(payload)
|
||||||
|
assertNull(cfg.reference_temperature)
|
||||||
|
assertNull(cfg.aggregator_temperature)
|
||||||
|
assertEquals("every_n:3", cfg.fanout)
|
||||||
|
val preset = cfg.presets.getValue("default")
|
||||||
|
assertEquals("silent", preset.degraded_reference_policy)
|
||||||
|
assertEquals(false, preset.reference_models.first().enabled)
|
||||||
|
assertEquals("high", preset.reference_models.first().reasoning_effort)
|
||||||
|
// Round-trip keeps the non-default knobs so a save doesn't reset them.
|
||||||
|
val back = json.encodeToString(cfg)
|
||||||
|
assertTrue(back.contains("every_n:3"))
|
||||||
|
assertTrue(back.contains("silent"))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user