feat(chat): integrate fast mode toggle into generation controls dropdown (#1093)
This commit is contained in:
11 files changed
+388
-19
No files matched your search
@@ -40,7 +40,7 @@ class ChatModelSwitchDelegateTest {
|
||||
slug = "openai",
|
||||
name = "OpenAI",
|
||||
models = listOf("gpt-4o"),
|
||||
capabilities = mapOf("gpt-4o" to ModelCapabilities(reasoning = true)),
|
||||
capabilities = mapOf("gpt-4o" to ModelCapabilities(fast = true, reasoning = true)),
|
||||
),
|
||||
),
|
||||
)
|
||||
@@ -138,4 +138,73 @@ class ChatModelSwitchDelegateTest {
|
||||
assertNull(uiState.value.modelSwitchConfirmMessage)
|
||||
assertEquals("anthropic/claude-3", uiState.value.currentSessionModel)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun toggleFastMode_sendsConfigSetFast_andUpdatesOnAck() =
|
||||
testScope.runTest {
|
||||
delegate.preloadModelOptions()
|
||||
advanceUntilIdle()
|
||||
|
||||
// Switch to model with fast capability
|
||||
delegate.sendSlashModel("openai", "gpt-4o")
|
||||
advanceUntilIdle()
|
||||
sentMethods.clear()
|
||||
sentParams.clear()
|
||||
|
||||
assertTrue(uiState.value.currentModelCapabilities?.fast == true)
|
||||
assertFalse(uiState.value.fastMode)
|
||||
assertFalse(uiState.value.isFastModeChanging)
|
||||
|
||||
delegate.toggleFastMode()
|
||||
advanceUntilIdle()
|
||||
assertTrue(uiState.value.isFastModeChanging)
|
||||
assertFalse(uiState.value.fastMode) // Still unconfirmed!
|
||||
assertEquals(listOf(WsMethods.CONFIG_SET), sentMethods)
|
||||
assertEquals("fast", sentParams.first()["key"])
|
||||
assertEquals("fast", sentParams.first()["value"])
|
||||
assertEquals("runtime-1", sentParams.first()["session_id"])
|
||||
|
||||
// Backend acknowledges
|
||||
delegate.handleConfigSetResult("req-1", mapOf("key" to "fast", "value" to "fast"))
|
||||
assertTrue(uiState.value.fastMode)
|
||||
assertFalse(uiState.value.isFastModeChanging)
|
||||
|
||||
// Toggle off
|
||||
delegate.toggleFastMode()
|
||||
advanceUntilIdle()
|
||||
assertTrue(uiState.value.isFastModeChanging)
|
||||
assertEquals("normal", sentParams.last()["value"])
|
||||
|
||||
delegate.handleConfigSetResult("req-2", mapOf("key" to "fast", "value" to "normal"))
|
||||
assertFalse(uiState.value.fastMode)
|
||||
assertFalse(uiState.value.isFastModeChanging)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun toggleFastMode_whenRejectedByBackend_clearsChangingAndLatchesUnavailable() =
|
||||
testScope.runTest {
|
||||
delegate.preloadModelOptions()
|
||||
advanceUntilIdle()
|
||||
delegate.sendSlashModel("openai", "gpt-4o")
|
||||
advanceUntilIdle()
|
||||
sentMethods.clear()
|
||||
sentParams.clear()
|
||||
assertTrue(uiState.value.currentModelCapabilities?.fast == true)
|
||||
|
||||
delegate.toggleFastMode()
|
||||
advanceUntilIdle()
|
||||
assertTrue(uiState.value.isFastModeChanging)
|
||||
|
||||
// Backend returns error indicating fast mode not available
|
||||
delegate.handleConfigSetError("req-1", mapOf("message" to "fast mode is not available for this model"))
|
||||
assertFalse(uiState.value.isFastModeChanging)
|
||||
assertFalse(uiState.value.fastMode)
|
||||
assertEquals(false, uiState.value.currentModelCapabilities?.fast)
|
||||
|
||||
// Further toggle attempts are no-ops
|
||||
val sentCount = sentMethods.size
|
||||
delegate.toggleFastMode()
|
||||
advanceUntilIdle()
|
||||
assertEquals(sentCount, sentMethods.size)
|
||||
}
|
||||
}
|
||||
@@ -2951,6 +2951,92 @@ class ChatViewModelTest {
|
||||
assertEquals("nous/tencent/hy3:free", viewModel.uiState.value.currentSessionModel)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSessionInfo_hydratesFastModeState() =
|
||||
runTest {
|
||||
stubEmptySessionRests("session-a")
|
||||
val (viewModel, _) = createViewModelWithSession()
|
||||
advanceUntilIdle()
|
||||
|
||||
assertFalse(viewModel.uiState.value.fastMode)
|
||||
|
||||
// Fast = true in session.info
|
||||
mockEventsFlow.emit(
|
||||
WsEvent.SessionInfo(
|
||||
mapOf("fast" to true, "service_tier" to "priority"),
|
||||
),
|
||||
)
|
||||
advanceUntilIdle()
|
||||
assertTrue(viewModel.uiState.value.fastMode)
|
||||
assertFalse(viewModel.uiState.value.isFastModeChanging)
|
||||
|
||||
// Fast = false / normal in session.info
|
||||
mockEventsFlow.emit(
|
||||
WsEvent.SessionInfo(
|
||||
mapOf("fast" to false, "service_tier" to "normal"),
|
||||
),
|
||||
)
|
||||
advanceUntilIdle()
|
||||
assertFalse(viewModel.uiState.value.fastMode)
|
||||
|
||||
// Partial session.info without fast/service_tier does NOT clear state
|
||||
mockEventsFlow.emit(
|
||||
WsEvent.SessionInfo(
|
||||
mapOf("fast" to true),
|
||||
),
|
||||
)
|
||||
advanceUntilIdle()
|
||||
assertTrue(viewModel.uiState.value.fastMode)
|
||||
|
||||
mockEventsFlow.emit(
|
||||
WsEvent.SessionInfo(
|
||||
mapOf("terminal_backend" to "tmux"),
|
||||
),
|
||||
)
|
||||
advanceUntilIdle()
|
||||
assertTrue(viewModel.uiState.value.fastMode) // preserved!
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSessionResume_hydratesFastModeState() =
|
||||
runTest {
|
||||
stubEmptySessionRests("session-b")
|
||||
val (viewModel, _) = createViewModelWithSession()
|
||||
var resumeReqId = ""
|
||||
every { HermesWsClient.send(WsMethods.SESSION_RESUME, any(), any()) } answers {
|
||||
val reqId = "resume-req-1"
|
||||
resumeReqId = reqId
|
||||
arg<((String) -> Unit)?>(2)?.invoke(reqId)
|
||||
reqId
|
||||
}
|
||||
|
||||
viewModel.switchSession("session-b")
|
||||
runCurrent()
|
||||
|
||||
// Simulate SESSION_RESUME response with fast=true in info
|
||||
mockEventsFlow.emit(
|
||||
WsEvent.RpcResult(
|
||||
id = resumeReqId,
|
||||
result =
|
||||
mapOf(
|
||||
"session_id" to "sess-runtime-1",
|
||||
"resumed" to "session-b",
|
||||
"info" to
|
||||
mapOf(
|
||||
"model" to "gpt-4o",
|
||||
"provider" to "openai",
|
||||
"fast" to true,
|
||||
"service_tier" to "priority",
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
advanceUntilIdle()
|
||||
|
||||
assertTrue(viewModel.uiState.value.fastMode)
|
||||
assertFalse(viewModel.uiState.value.isFastModeChanging)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testResumeNotFound_recoversWithNewSession() =
|
||||
runTest {
|
||||
|
||||
Reference in new issue
Block a user