Skip to content

Commit ca6005a

Browse files
committed
fix(android): preserve history context usage (#92837) (thanks @Tosko4)
1 parent 2fb12fe commit ca6005a

2 files changed

Lines changed: 76 additions & 9 deletions

File tree

apps/android/app/src/main/java/ai/openclaw/app/chat/ChatController.kt

Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -699,15 +699,25 @@ class ChatController(
699699

700700
private fun updateSessionFromHistory(history: ChatHistory) {
701701
val info = history.sessionInfo ?: return
702-
upsertSessionEntry(info)
702+
upsertSessionEntry(info, preserveExistingContextUsageWithoutTotal = true)
703703
}
704704

705-
private fun upsertSessionEntry(entry: ChatSessionEntry) {
705+
private fun upsertSessionEntry(
706+
entry: ChatSessionEntry,
707+
preserveExistingContextUsageWithoutTotal: Boolean = false,
708+
) {
706709
val current = _sessions.value
707710
val index = current.indexOfFirst { it.key == entry.key }
708711
_sessions.value =
709712
if (index >= 0) {
710-
current.toMutableList().also { it[index] = mergeChatSessionEntry(it[index], entry) }
713+
current.toMutableList().also {
714+
it[index] =
715+
mergeChatSessionEntry(
716+
existing = it[index],
717+
next = entry,
718+
preserveExistingContextUsageWithoutTotal = preserveExistingContextUsageWithoutTotal,
719+
)
720+
}
711721
} else {
712722
listOf(entry) + current
713723
}
@@ -947,12 +957,34 @@ private fun JsonElement?.asBooleanOrNull(): Boolean? =
947957
internal fun mergeChatSessionEntry(
948958
existing: ChatSessionEntry,
949959
next: ChatSessionEntry,
950-
): ChatSessionEntry =
951-
existing.copy(
960+
preserveExistingContextUsageWithoutTotal: Boolean = false,
961+
): ChatSessionEntry {
962+
val preserveExistingContextUsage = preserveExistingContextUsageWithoutTotal && next.totalTokens == null
963+
return existing.copy(
952964
updatedAtMs = next.updatedAtMs ?: existing.updatedAtMs,
953965
displayName = next.displayName ?: existing.displayName,
954-
totalTokens = if (next.hasContextUsageMetadata) next.totalTokens else null,
955-
totalTokensFresh = if (next.hasContextUsageMetadata) next.totalTokensFresh else null,
956-
contextTokens = if (next.hasContextUsageMetadata) next.contextTokens else null,
957-
hasContextUsageMetadata = next.hasContextUsageMetadata,
966+
totalTokens =
967+
when {
968+
preserveExistingContextUsage -> existing.totalTokens
969+
next.hasContextUsageMetadata -> next.totalTokens
970+
else -> null
971+
},
972+
totalTokensFresh =
973+
when {
974+
preserveExistingContextUsage -> existing.totalTokensFresh
975+
next.hasContextUsageMetadata -> next.totalTokensFresh
976+
else -> null
977+
},
978+
contextTokens =
979+
when {
980+
preserveExistingContextUsage -> next.contextTokens ?: existing.contextTokens
981+
next.hasContextUsageMetadata -> next.contextTokens
982+
else -> null
983+
},
984+
hasContextUsageMetadata =
985+
when {
986+
preserveExistingContextUsage -> existing.hasContextUsageMetadata || next.contextTokens != null
987+
else -> next.hasContextUsageMetadata
988+
},
958989
)
990+
}

apps/android/app/src/test/java/ai/openclaw/app/chat/ChatControllerSessionPolicyTest.kt

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,41 @@ class ChatControllerSessionPolicyTest {
9090
assertFalse(merged.hasContextUsageMetadata)
9191
}
9292

93+
@Test
94+
fun sessionMergePreservesUsageWhenHistorySnapshotOmitsTotalTokens() {
95+
val existing =
96+
ChatSessionEntry(
97+
key = "agent:main:phone",
98+
updatedAtMs = 1L,
99+
displayName = "Phone",
100+
totalTokens = 41_000L,
101+
totalTokensFresh = true,
102+
contextTokens = 100_000L,
103+
)
104+
val next =
105+
ChatSessionEntry(
106+
key = "agent:main:phone",
107+
updatedAtMs = 2L,
108+
displayName = "Phone renamed",
109+
totalTokensFresh = false,
110+
contextTokens = 120_000L,
111+
)
112+
113+
val merged =
114+
mergeChatSessionEntry(
115+
existing = existing,
116+
next = next,
117+
preserveExistingContextUsageWithoutTotal = true,
118+
)
119+
120+
assertEquals(2L, merged.updatedAtMs)
121+
assertEquals("Phone renamed", merged.displayName)
122+
assertEquals(41_000L, merged.totalTokens)
123+
assertEquals(true, merged.totalTokensFresh)
124+
assertEquals(120_000L, merged.contextTokens)
125+
assertTrue(merged.hasContextUsageMetadata)
126+
}
127+
93128
@Test
94129
fun sessionMergeAppliesExplicitStaleUsageMetadata() {
95130
val existing =

0 commit comments

Comments
 (0)