* feat(#480): pass refresh=true on Models pull-to-refresh to re-probe custom providers * test(#480): assert refresh flag propagation for default vs pull-to-refresh loads * fix(#480): capture refresh arg via slot instead of brittle coVerify in propagation test --------- Co-authored-by: ronia <ronia@m57>
This commit is contained in:
1 parent
7384b51476
commit
bdf566ba3e
4 files changed
+54
-5
No files matched your search
@@ -430,7 +430,9 @@ interface HermesApiService {
|
|||||||
): Response<Unit>
|
): Response<Unit>
|
||||||
|
|
||||||
@GET("api/model/options")
|
@GET("api/model/options")
|
||||||
suspend fun getModelOptions(): Response<ModelOptionsResponse>
|
suspend fun getModelOptions(
|
||||||
|
@Query("refresh") refresh: Boolean = false,
|
||||||
|
): Response<ModelOptionsResponse>
|
||||||
|
|
||||||
@GET("api/model/auxiliary")
|
@GET("api/model/auxiliary")
|
||||||
suspend fun getAuxiliaryModels(): Response<AuxiliaryModelsResponse>
|
suspend fun getAuxiliaryModels(): Response<AuxiliaryModelsResponse>
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ fun ModelScreen(
|
|||||||
title = { Text(stringResource(R.string.screen_models)) },
|
title = { Text(stringResource(R.string.screen_models)) },
|
||||||
navigationIcon = onOpenDrawer?.let { NavIcon.Menu(it) },
|
navigationIcon = onOpenDrawer?.let { NavIcon.Menu(it) },
|
||||||
isRefreshing = state.isLoading,
|
isRefreshing = state.isLoading,
|
||||||
onRefresh = { viewModel.loadAll() },
|
onRefresh = { viewModel.loadAll(refresh = true) },
|
||||||
) { paddingValues ->
|
) { paddingValues ->
|
||||||
when {
|
when {
|
||||||
state.isLoading && state.providers.isEmpty() -> {
|
state.isLoading && state.providers.isEmpty() -> {
|
||||||
|
|||||||
@@ -76,12 +76,12 @@ class ModelViewModel :
|
|||||||
_uiState.update { it.copy(pinnedModels = AuthManager.getPinnedModels()) }
|
_uiState.update { it.copy(pinnedModels = AuthManager.getPinnedModels()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun loadAll() {
|
fun loadAll(refresh: Boolean = false) {
|
||||||
_uiState.update { it.copy(isLoading = true, errorMessage = null) }
|
_uiState.update { it.copy(isLoading = true, errorMessage = null) }
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
val optionsDeferred =
|
val optionsDeferred =
|
||||||
async(Dispatchers.IO) {
|
async(Dispatchers.IO) {
|
||||||
safeApiCall { ApiClient.hermesApi.getModelOptions() }
|
safeApiCall { ApiClient.hermesApi.getModelOptions(refresh = refresh) }
|
||||||
}
|
}
|
||||||
val activeProfileDeferred =
|
val activeProfileDeferred =
|
||||||
async(Dispatchers.IO) {
|
async(Dispatchers.IO) {
|
||||||
|
|||||||
@@ -1284,7 +1284,7 @@ class E2eIntegrationTest {
|
|||||||
val provider = ModelProvider("ollama", "Ollama", false, true, listOf("llama3"), 1, null, true, null, null)
|
val provider = ModelProvider("ollama", "Ollama", false, true, listOf("llama3"), 1, null, true, null, null)
|
||||||
val profile = ProfileInfo("default", null, true, "llama3", "ollama", null, null, null, null)
|
val profile = ProfileInfo("default", null, true, "llama3", "ollama", null, null, null, null)
|
||||||
|
|
||||||
coEvery { mockApiService.getModelOptions() } returns
|
coEvery { mockApiService.getModelOptions(any()) } returns
|
||||||
Response.success(ModelOptionsResponse(listOf(provider)))
|
Response.success(ModelOptionsResponse(listOf(provider)))
|
||||||
coEvery { mockApiService.getActiveProfile() } returns
|
coEvery { mockApiService.getActiveProfile() } returns
|
||||||
Response.success(ActiveProfileResponse("default", null))
|
Response.success(ActiveProfileResponse("default", null))
|
||||||
@@ -1333,6 +1333,53 @@ class E2eIntegrationTest {
|
|||||||
assertEquals("Successfully set profile model to llama3", viewModel.uiState.value.toastMessage)
|
assertEquals("Successfully set profile model to llama3", viewModel.uiState.value.toastMessage)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testModelOptions_refreshFlagPropagation() =
|
||||||
|
runTest {
|
||||||
|
val provider = ModelProvider("ollama", "Ollama", false, true, listOf("llama3"), 1, null, true, null, null)
|
||||||
|
val profile = ProfileInfo("default", null, true, "llama3", "ollama", null, null, null, null)
|
||||||
|
|
||||||
|
coEvery { mockApiService.getModelOptions(any()) } returns
|
||||||
|
Response.success(ModelOptionsResponse(listOf(provider)))
|
||||||
|
coEvery { mockApiService.getActiveProfile() } returns
|
||||||
|
Response.success(ActiveProfileResponse("default", null))
|
||||||
|
coEvery { mockApiService.getProfiles() } returns Response.success(ProfilesResponse(listOf(profile)))
|
||||||
|
coEvery { mockApiService.getAuxiliaryModels() } returns
|
||||||
|
Response.success(
|
||||||
|
AuxiliaryModelsResponse(
|
||||||
|
tasks = emptyList(),
|
||||||
|
main = MainModelAssignment("openai", "gpt-4"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
coEvery { mockApiService.getMoaModels() } returns
|
||||||
|
Response.success<MoaConfigResponse>(
|
||||||
|
MoaConfigResponse(
|
||||||
|
presets = emptyMap(),
|
||||||
|
default_preset = "",
|
||||||
|
reference_models = emptyList(),
|
||||||
|
aggregator = MoaModelSlot("openai", "gpt-4"),
|
||||||
|
max_tokens = 4096,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
val capturedRefresh = mutableListOf<Boolean>()
|
||||||
|
coEvery { mockApiService.getModelOptions(any()) } answers {
|
||||||
|
capturedRefresh.add(firstArg())
|
||||||
|
Response.success(ModelOptionsResponse(listOf(provider)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default loadAll() must call getModelOptions with refresh = false
|
||||||
|
val viewModel = ModelViewModel()
|
||||||
|
viewModel.loadAll()
|
||||||
|
advanceUntilIdle()
|
||||||
|
|
||||||
|
// Pull-to-refresh path must call getModelOptions with refresh = true
|
||||||
|
viewModel.loadAll(refresh = true)
|
||||||
|
advanceUntilIdle()
|
||||||
|
|
||||||
|
assertEquals(listOf(false, true), capturedRefresh)
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testConnectViewModel_onPairingString_urlFormat() =
|
fun testConnectViewModel_onPairingString_urlFormat() =
|
||||||
runTest {
|
runTest {
|
||||||
|
|||||||
Reference in new issue
Block a user