fix(chat): searchable tool/reasoning rows and live markdown (#1361)
This commit is contained in:
19 files changed
+454
-146
No files matched your search
@@ -34,15 +34,25 @@ class ChatSearchControllerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `excludes tool rows even when the payload contains the query`() {
|
||||
fun `finds visible tool name without indexing hidden raw payload`() {
|
||||
val messages =
|
||||
listOf(
|
||||
message(MessageRole.USER, "check the logs"),
|
||||
message(MessageRole.TOOL, "{\"result\": \"check the logs: all green\"}"),
|
||||
message(MessageRole.ASSISTANT, "all good"),
|
||||
message(MessageRole.TOOL, "unrendered private payload").copy(toolName = "terminal"),
|
||||
)
|
||||
|
||||
assertEquals(listOf(SearchMatch(0, 0)), hits(controller.findMatches(messages, "check the logs")))
|
||||
assertEquals(listOf(SearchMatch(1, 0, SearchTarget.TOOL)), hits(controller.findMatches(messages, "terminal")))
|
||||
assertEquals(emptyList<SearchMatch>(), hits(controller.findMatches(messages, "private payload")))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `system marker carried as user does not yield an invisible hit`() {
|
||||
val marker =
|
||||
message(
|
||||
MessageRole.USER,
|
||||
"You've reached the maximum number of tool-calling iterations allowed.",
|
||||
)
|
||||
assertEquals(emptyList<SearchMatch>(), hits(controller.findMatches(listOf(marker), "maximum")))
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -57,7 +67,7 @@ class ChatSearchControllerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `excludes reasoning text that never made it to visible content`() {
|
||||
fun `finds reasoning text independently of assistant prose`() {
|
||||
val messages =
|
||||
listOf(
|
||||
message(
|
||||
@@ -67,8 +77,10 @@ class ChatSearchControllerTest {
|
||||
),
|
||||
)
|
||||
|
||||
// Only the visible content is searchable — reasoning is invisible.
|
||||
assertEquals(emptyList<SearchMatch>(), hits(controller.findMatches(messages, "deployment")))
|
||||
assertEquals(
|
||||
listOf(SearchMatch(0, 19, SearchTarget.REASONING)),
|
||||
hits(controller.findMatches(messages, "deployment")),
|
||||
)
|
||||
assertEquals(listOf(SearchMatch(0, 12)), hits(controller.findMatches(messages, "answer")))
|
||||
}
|
||||
|
||||
|
||||
@@ -139,6 +139,39 @@ class ChatSearchDelegateTest {
|
||||
assertEquals(0, delegate.searchState.currentIndex)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `search tracks target kind while navigating reasoning tool and prose`() =
|
||||
runTest {
|
||||
val uiState =
|
||||
MutableStateFlow(
|
||||
ChatUiState(
|
||||
messages =
|
||||
listOf(
|
||||
ChatMessage("m0", MessageRole.ASSISTANT, "answer", reasoningText = "thinking"),
|
||||
ChatMessage("m1", MessageRole.TOOL, "opaque", toolName = "thinking_tool"),
|
||||
),
|
||||
),
|
||||
)
|
||||
val delegate =
|
||||
ChatSearchDelegate(
|
||||
scope = backgroundScope,
|
||||
uiState = uiState,
|
||||
dispatcher = StandardTestDispatcher(testScheduler),
|
||||
debounceMs = 150,
|
||||
)
|
||||
|
||||
delegate.setSearchQuery("thinking")
|
||||
advanceTimeBy(150)
|
||||
runCurrent()
|
||||
assertEquals(listOf(SearchTarget.REASONING, SearchTarget.TOOL), delegate.searchState.matchTargets)
|
||||
assertEquals("m0", delegate.searchState.currentMatchId)
|
||||
delegate.navigateSearchMatch(1)
|
||||
assertEquals("m1", delegate.searchState.currentMatchId)
|
||||
assertEquals(SearchTarget.TOOL, delegate.searchState.matchTargets[delegate.searchState.currentIndex])
|
||||
delegate.clearSearch()
|
||||
assertEquals(emptyList<SearchTarget>(), delegate.searchState.matchTargets)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `clearSearch resets everything including matched sets`() =
|
||||
runTest {
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.m57.hermescontrol.ui.chat
|
||||
import com.m57.hermescontrol.ui.chat.fullbleed.fullBleedItemKeys
|
||||
import com.m57.hermescontrol.ui.chat.fullbleed.groupIntoTurns
|
||||
import com.m57.hermescontrol.ui.chat.fullbleed.messageIdToLazyIndex
|
||||
import com.m57.hermescontrol.ui.chat.fullbleed.searchMatchToLazyIndex
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
|
||||
@@ -24,6 +25,21 @@ class FullBleedItemKeysTest {
|
||||
assertEquals(mapOf("u" to 2, "a" to 5), messageIdToLazyIndex(turns, leadingItems = 2))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun searchTargetsResolveHoistedReasoningAndToolRows() {
|
||||
val messages =
|
||||
listOf(
|
||||
ChatMessage("u", MessageRole.USER, "question"),
|
||||
ChatMessage("t", MessageRole.TOOL, "output", toolName = "terminal"),
|
||||
ChatMessage("a", MessageRole.ASSISTANT, "answer", reasoningText = "thinking"),
|
||||
)
|
||||
val turns = groupIntoTurns(messages)
|
||||
|
||||
assertEquals(1, searchMatchToLazyIndex(turns, messages, SearchMatch(2, 0, SearchTarget.REASONING)))
|
||||
assertEquals(2, searchMatchToLazyIndex(turns, messages, SearchMatch(1, 0, SearchTarget.TOOL)))
|
||||
assertEquals(3, searchMatchToLazyIndex(turns, messages, SearchMatch(2, 0)))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun toolAnchorSurvivesPrependThatExtendsItsAgentTurn() {
|
||||
val tool = ChatMessage("live-tool", MessageRole.TOOL, "output")
|
||||
|
||||
Reference in new issue
Block a user