diff --git a/app/src/main/java/com/m57/hermescontrol/ui/chat/ChatViewModel.kt b/app/src/main/java/com/m57/hermescontrol/ui/chat/ChatViewModel.kt index 5a7b873e..0bce0c20 100644 --- a/app/src/main/java/com/m57/hermescontrol/ui/chat/ChatViewModel.kt +++ b/app/src/main/java/com/m57/hermescontrol/ui/chat/ChatViewModel.kt @@ -4,6 +4,7 @@ import android.app.Application import android.util.Log import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.viewModelScope +import com.google.gson.Gson import com.m57.hermescontrol.data.local.AuthManager import com.m57.hermescontrol.data.local.HermesDatabase import com.m57.hermescontrol.data.local.toEntity @@ -371,10 +372,11 @@ class ChatViewModel( var orphanToPersist: ChatMessage? = null val sessionId = _uiState.value.currentSessionId + val contentJson = event.data?.let { Gson().toJson(it) } ?: "" val toolMessage = ChatMessage( role = MessageRole.TOOL, - content = event.data.toString(), + content = contentJson, toolName = event.name, toolStatus = ToolStatus.RUNNING, ) @@ -418,10 +420,11 @@ class ChatViewModel( it.toolStatus == ToolStatus.RUNNING } if (toolIdx >= 0) { + val contentJson = event.data?.let { Gson().toJson(it) } ?: "" val updated = messages[toolIdx].copy( toolStatus = ToolStatus.COMPLETED, - content = event.data.toString(), + content = contentJson, ) messages[toolIdx] = updated completedTool = updated diff --git a/app/src/test/java/com/m57/hermescontrol/ui/chat/ChatViewModelTest.kt b/app/src/test/java/com/m57/hermescontrol/ui/chat/ChatViewModelTest.kt index afb79907..23d9223c 100644 --- a/app/src/test/java/com/m57/hermescontrol/ui/chat/ChatViewModelTest.kt +++ b/app/src/test/java/com/m57/hermescontrol/ui/chat/ChatViewModelTest.kt @@ -562,4 +562,52 @@ class ChatViewModelTest { assertEquals("Second response segment", state.streamingMessage?.content) assertTrue(state.streamingMessage?.isStreaming == true) } + + @Test + fun testToolExecution_serializesDataAsJson() = + runTest { + val viewModel = ChatViewModel(app, startCleanup = false) + advanceUntilIdle() + + // Setup active session + var createReqId = "" + every { HermesWsClient.send(WsMethods.SESSION_CREATE, any(), any()) } answers { + createReqId = "create-req-tool-json-test" + val onSent = arg<((String) -> Unit)?>(2) + onSent?.invoke(createReqId) + createReqId + } + mockEventsFlow.emit(WsEvent.GatewayReady(null)) + advanceUntilIdle() + mockEventsFlow.emit(WsEvent.RpcResult(createReqId, mapOf("session_id" to "session-123"))) + advanceUntilIdle() + + // Start tool call + mockEventsFlow.emit( + WsEvent.ToolStart( + name = "calculator", + data = + mapOf( + "input" to "2+2", + "nested" to mapOf("key" to "value"), + ), + ), + ) + advanceUntilIdle() + + var state = viewModel.uiState.value + assertEquals(2, state.messages.size) + assertEquals(MessageRole.TOOL, state.messages[1].role) + assertEquals("{\"input\":\"2+2\",\"nested\":{\"key\":\"value\"}}", state.messages[1].content) + assertEquals(ToolStatus.RUNNING, state.messages[1].toolStatus) + + // Complete tool call + mockEventsFlow.emit(WsEvent.ToolComplete("calculator", mapOf("result" to "4", "exit_code" to 0))) + advanceUntilIdle() + + state = viewModel.uiState.value + assertEquals(2, state.messages.size) + assertEquals("{\"result\":\"4\",\"exit_code\":0}", state.messages[1].content) + assertEquals(ToolStatus.COMPLETED, state.messages[1].toolStatus) + } }