Fixed tool calling history
This commit is contained in:
1 parent
dd43033888
commit
524ed8ce53
11 files changed
+471
-55
No files matched your search
@@ -6,7 +6,9 @@ import iris.protocol.ChannelInfo
|
||||
import iris.protocol.RuntimeMeta
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertFalse
|
||||
import kotlin.test.assertNull
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
/** Cache DB round-trip tests (JDBC in-memory SQLite; shared by both host targets). */
|
||||
class ChatDbTest {
|
||||
@@ -42,22 +44,80 @@ class ChatDbTest {
|
||||
db.saveLanes(
|
||||
mapOf(
|
||||
"android:default" to
|
||||
listOf(
|
||||
listOf<ChatItem>(
|
||||
msg("m1", ts = 100),
|
||||
ToolItem(id = "tool_1", index = 0, name = "bash", anchorId = "m1"),
|
||||
msg("m2", role = "assistant", text = "hi", ts = 200),
|
||||
ToolItem(id = "tool_1", index = 0, name = "bash"),
|
||||
),
|
||||
"android:default::thr_1" to listOf(msg("m3", ts = 300)),
|
||||
"android:default::thr_1" to listOf<ChatItem>(msg("m3", ts = 300)),
|
||||
),
|
||||
)
|
||||
val loaded = db.loadLanes()
|
||||
assertEquals(setOf("android:default", "android:default::thr_1"), loaded.keys)
|
||||
// Tool cards are ephemeral — not persisted.
|
||||
assertEquals(listOf("m1", "m2"), loaded["android:default"]!!.map { it.id })
|
||||
// Tool cards are persisted and restored at their anchored position
|
||||
// (after the message they follow, before the answer).
|
||||
assertEquals(listOf("m1", "tool_1", "m2"), loaded["android:default"]!!.map { it.id })
|
||||
assertEquals(listOf("m3"), loaded["android:default::thr_1"]!!.map { it.id })
|
||||
// Ordered by ts.
|
||||
assertEquals(100L, loaded["android:default"]!![0].ts)
|
||||
assertEquals(200L, loaded["android:default"]!![1].ts)
|
||||
// Messages ordered by ts.
|
||||
assertEquals(100L, (loaded["android:default"]!![0] as MessageItem).ts)
|
||||
assertEquals(200L, (loaded["android:default"]!![2] as MessageItem).ts)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun toolCardsShareAnchorKeepOrder() {
|
||||
val db = newDb()
|
||||
db.saveLanes(
|
||||
mapOf(
|
||||
"android:default" to
|
||||
listOf<ChatItem>(
|
||||
msg("m1", ts = 100),
|
||||
ToolItem(id = "tool_1", index = 0, name = "search_files", anchorId = "m1"),
|
||||
ToolItem(id = "tool_2", index = 1, name = "terminal", anchorId = "m1"),
|
||||
msg("m2", role = "assistant", ts = 200),
|
||||
),
|
||||
),
|
||||
)
|
||||
assertEquals(listOf("m1", "tool_1", "tool_2", "m2"), db.loadLanes()["android:default"]!!.map { it.id })
|
||||
}
|
||||
|
||||
@Test
|
||||
fun toolCardWithMissingAnchorFallsToEnd() {
|
||||
val db = newDb()
|
||||
db.saveLanes(
|
||||
mapOf(
|
||||
"android:default" to
|
||||
listOf<ChatItem>(
|
||||
msg("m1", ts = 100),
|
||||
ToolItem(id = "tool_1", index = 0, name = "bash", anchorId = "deleted"),
|
||||
msg("m2", role = "assistant", ts = 200),
|
||||
),
|
||||
),
|
||||
)
|
||||
assertEquals(listOf("m1", "m2", "tool_1"), db.loadLanes()["android:default"]!!.map { it.id })
|
||||
}
|
||||
|
||||
@Test
|
||||
fun restoreClosesOpenToolCard() {
|
||||
val db = newDb()
|
||||
db.saveLanes(
|
||||
mapOf(
|
||||
"android:default" to
|
||||
listOf<ChatItem>(
|
||||
msg("m1", ts = 100),
|
||||
ToolItem(id = "tool_1", index = 0, name = "bash", done = false, anchorId = "m1"),
|
||||
ToolItem(id = "tool_2", index = 1, name = "ls", done = true, ok = true, anchorId = "m1"),
|
||||
),
|
||||
),
|
||||
)
|
||||
val lane = db.loadLanes()["android:default"]!!
|
||||
// The process died before tool.end — the open card is closed as
|
||||
// interrupted, not left spinning.
|
||||
val open = lane.first { it.id == "tool_1" } as ToolItem
|
||||
assertTrue(open.done)
|
||||
assertFalse(open.ok)
|
||||
// A completed card is untouched.
|
||||
val done = lane.first { it.id == "tool_2" } as ToolItem
|
||||
assertTrue(done.ok)
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -82,21 +142,22 @@ class ChatDbTest {
|
||||
),
|
||||
)
|
||||
val lane = db.loadLanes()["android:default"]!!
|
||||
val msgItem = { id: String -> lane.first { it.id == id } as MessageItem }
|
||||
// A pending send becomes failed (tap to retry); the gateway never
|
||||
// acknowledged it before the process died.
|
||||
assertEquals(MsgStatus.Failed, lane.first { it.id == "p1" }.status)
|
||||
assertEquals(false, lane.first { it.id == "p1" }.pending)
|
||||
assertEquals(MsgStatus.Failed, msgItem("p1").status)
|
||||
assertEquals(false, msgItem("p1").pending)
|
||||
// A streaming bubble is restored as finalized.
|
||||
assertEquals(false, lane.first { it.id == "s1" }.streaming)
|
||||
assertEquals(false, msgItem("s1").streaming)
|
||||
// Read status is preserved.
|
||||
assertEquals(MsgStatus.Read, lane.first { it.id == "r1" }.status)
|
||||
assertEquals(MsgStatus.Read, msgItem("r1").status)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun systemMessagesAreNotPersisted() {
|
||||
val db = newDb()
|
||||
db.saveLanes(mapOf("android:default" to listOf(msg("sys_1", role = "system", isSystem = true))))
|
||||
assertEquals(emptyMap<String, List<MessageItem>>(), db.loadLanes())
|
||||
db.saveLanes(mapOf("android:default" to listOf<ChatItem>(msg("sys_1", role = "system", isSystem = true))))
|
||||
assertTrue(db.loadLanes().isEmpty())
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -121,8 +182,8 @@ class ChatDbTest {
|
||||
),
|
||||
),
|
||||
)
|
||||
db.saveLanes(mapOf("android:default" to listOf(item)))
|
||||
val loaded = db.loadLanes()["android:default"]!!.first()
|
||||
db.saveLanes(mapOf("android:default" to listOf<ChatItem>(item)))
|
||||
val loaded = db.loadLanes()["android:default"]!!.first() as MessageItem
|
||||
assertEquals("gpt", loaded.runtime?.model)
|
||||
assertEquals("/tmp/a.png", loaded.media.first().localPath)
|
||||
}
|
||||
@@ -150,10 +211,10 @@ class ChatDbTest {
|
||||
assertNull(db.metaGet("last_lane"))
|
||||
db.metaPut("last_lane", "android:chan_1")
|
||||
assertEquals("android:chan_1", db.metaGet("last_lane"))
|
||||
db.saveLanes(mapOf("android:default" to listOf(msg("m1"))))
|
||||
db.saveLanes(mapOf("android:default" to listOf<ChatItem>(msg("m1"))))
|
||||
db.saveChannels(listOf(ChannelInfo(chatId = "android:default", name = "General")))
|
||||
db.clearAll()
|
||||
assertEquals(emptyMap<String, List<MessageItem>>(), db.loadLanes())
|
||||
assertTrue(db.loadLanes().isEmpty())
|
||||
assertEquals(emptyList<ChannelInfo>(), db.loadChannels())
|
||||
assertNull(db.metaGet("last_lane"))
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user