Co-authored-by: M57 <hy4ri@users.noreply.github.com>
This commit is contained in:
1 parent
85087c70c7
commit
4be1e965f2
16 files changed
+438
-11
No files matched your search
@@ -161,4 +161,36 @@ class ChatMessageMapperTest {
|
||||
|
||||
assertEquals("r", roundTripped.reasoningText)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun roundTripPreservesDisplayKind() {
|
||||
val ui =
|
||||
ChatMessage(
|
||||
id = "msg-4",
|
||||
role = MessageRole.USER,
|
||||
content = "[System: ...changed to gpt-5...]",
|
||||
displayKind = "model_switch",
|
||||
timestamp = 4000L,
|
||||
)
|
||||
|
||||
val roundTripped = ui.toEntity("s").toUiModel()
|
||||
|
||||
assertEquals("model_switch", roundTripped.displayKind)
|
||||
assertEquals(MessageRole.USER, roundTripped.role)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun roundTripWithoutDisplayKindStaysNull() {
|
||||
val ui =
|
||||
ChatMessage(
|
||||
id = "msg-5",
|
||||
role = MessageRole.USER,
|
||||
content = "plain user text",
|
||||
timestamp = 5000L,
|
||||
)
|
||||
|
||||
val roundTripped = ui.toEntity("s").toUiModel()
|
||||
|
||||
assertNull(roundTripped.displayKind)
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import com.m57.hermescontrol.data.remote.OkHttpProvider
|
||||
import kotlinx.serialization.decodeFromString
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.JsonElement
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
import kotlinx.serialization.json.JsonPrimitive
|
||||
import kotlinx.serialization.json.booleanOrNull
|
||||
import org.junit.Assert.assertEquals
|
||||
@@ -420,6 +421,49 @@ class ModelSerializationTest {
|
||||
assertNull(response.disk?.free_mb)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSessionMessageDeserialization_displayKindMarker() {
|
||||
val jsonStr =
|
||||
"""
|
||||
{
|
||||
"id": 42,
|
||||
"role": "user",
|
||||
"content": "[System: The active model for this chat has changed to gpt-5 via provider openai. From this point forward, use this runtime metadata when answering questions about what model/provider is active.]",
|
||||
"timestamp": 1755260000000,
|
||||
"display_kind": "model_switch",
|
||||
"display_metadata": {
|
||||
"delegation_id": "dlg-1",
|
||||
"task_count": 3
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
val message = json.decodeFromString<SessionMessage>(jsonStr)
|
||||
assertEquals(42, message.id)
|
||||
assertEquals("user", message.role)
|
||||
assertEquals("model_switch", message.display_kind)
|
||||
val metadata = message.display_metadata
|
||||
assertNotNull(metadata)
|
||||
val delegationId =
|
||||
(metadata as? JsonObject)?.get("delegation_id") as? JsonPrimitive
|
||||
assertEquals("dlg-1", delegationId?.content)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSessionMessageDeserialization_missingDisplayKind() {
|
||||
val jsonStr =
|
||||
"""
|
||||
{
|
||||
"id": 7,
|
||||
"role": "assistant",
|
||||
"content": "hi"
|
||||
}
|
||||
""".trimIndent()
|
||||
val message = json.decodeFromString<SessionMessage>(jsonStr)
|
||||
assertEquals(7, message.id)
|
||||
assertNull(message.display_kind)
|
||||
assertNull(message.display_metadata)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testStatusResponseDeserialization_typeMismatchInGatewayPlatforms() {
|
||||
val jsonStr =
|
||||
|
||||
@@ -81,6 +81,45 @@ class FullBleedTurnsTest {
|
||||
assertEquals(listOf(entries(AgentEntry.SystemEvent(s))), result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `display_kind marker is a SystemEvent entry not a user turn`() {
|
||||
val marker =
|
||||
msg("m1", MessageRole.USER, content = "[System: The active model has changed to gpt-5]")
|
||||
.copy(displayKind = "model_switch")
|
||||
val result = groupIntoTurns(listOf(marker))
|
||||
assertEquals(listOf<ChatTurn>(entries(AgentEntry.SystemEvent(marker))), result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `marker between assistant and user does not close the agent turn`() {
|
||||
val a = msg("a1", MessageRole.ASSISTANT)
|
||||
val marker =
|
||||
msg("m1", MessageRole.USER, content = "marker")
|
||||
.copy(displayKind = "personality_switch")
|
||||
val u = msg("u1", MessageRole.USER)
|
||||
val result = groupIntoTurns(listOf(a, marker, u))
|
||||
assertEquals(
|
||||
listOf(
|
||||
entries(AgentEntry.Prose(a), AgentEntry.SystemEvent(marker)),
|
||||
ChatTurn.User(u),
|
||||
),
|
||||
result,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `marker only closes nothing and trailing marker lands in agent turn`() {
|
||||
val u = msg("u1", MessageRole.USER)
|
||||
val marker =
|
||||
msg("m1", MessageRole.USER, content = "marker")
|
||||
.copy(displayKind = "auto_continue")
|
||||
val result = groupIntoTurns(listOf(u, marker))
|
||||
assertEquals(
|
||||
listOf(ChatTurn.User(u), entries(AgentEntry.SystemEvent(marker))),
|
||||
result,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `user message closes the current agent turn`() {
|
||||
val a1 = msg("a1", MessageRole.ASSISTANT)
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
package com.m57.hermescontrol.ui.chat.fullbleed
|
||||
|
||||
import com.m57.hermescontrol.R
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertNull
|
||||
import org.junit.Test
|
||||
|
||||
class TimelineMarkerTest {
|
||||
@Test
|
||||
fun knownKindsMapToLabels() {
|
||||
assertEquals(R.string.timeline_marker_model_switch, timelineMarkerLabelRes("model_switch"))
|
||||
assertEquals(
|
||||
R.string.timeline_marker_personality_switch,
|
||||
timelineMarkerLabelRes("personality_switch"),
|
||||
)
|
||||
assertEquals(R.string.timeline_marker_auto_continue, timelineMarkerLabelRes("auto_continue"))
|
||||
assertEquals(
|
||||
R.string.timeline_marker_delegation_complete,
|
||||
timelineMarkerLabelRes("async_delegation_complete"),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun unknownAndNullKindsHaveNoLabel() {
|
||||
assertNull(timelineMarkerLabelRes("hidden"))
|
||||
assertNull(timelineMarkerLabelRes("some_future_kind"))
|
||||
assertNull(timelineMarkerLabelRes(null))
|
||||
assertNull(timelineMarkerLabelRes(""))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun modelParsedFromMarkerContent() {
|
||||
assertEquals(
|
||||
"gpt-5",
|
||||
markerModelFromContent(
|
||||
"[System: The active model for this chat has changed to gpt-5 via provider openai. ...]",
|
||||
),
|
||||
)
|
||||
assertEquals(
|
||||
"deepseek-v4",
|
||||
markerModelFromContent(
|
||||
"The active model for this chat has changed to deepseek-v4. From this point forward...",
|
||||
),
|
||||
)
|
||||
assertEquals(
|
||||
"gpt-4.5",
|
||||
markerModelFromContent(
|
||||
"The active model for this chat has changed to gpt-4.5 via provider openai.",
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun modelParseHandlesMissingOrOddContent() {
|
||||
assertNull(markerModelFromContent(""))
|
||||
assertNull(markerModelFromContent("[System: personality switched]"))
|
||||
assertNull(markerModelFromContent("changed to"))
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user