Resend mismatched completed client text (#8697)

This commit is contained in:
2026-07-18 08:06:13 -07:00
committed by GitHub
parent a919e8edf0
commit e6438302e2
2 changed files with 50 additions and 1 deletions
@@ -128,7 +128,14 @@ object HumanPlayerClientConnectionState {
// attempt is already at least as long: equal lengths do not imply equal prefixes.
StreamingTextDiff(newText = text, startingByteCount = 0)
case status => StreamingTextDiff(newText = "", startingByteCount = status.knownByteCount)
case status =>
val serverByteCount = text.getBytes(StandardCharsets.UTF_8).length
if status.knownByteCount == serverByteCount then
StreamingTextDiff(newText = "", startingByteCount = status.knownByteCount)
else
// A legacy client can persist an empty or stale value as complete. Replace it from byte zero when the
// authoritative completed payload has a different length.
StreamingTextDiff(newText = text, startingByteCount = 0)
}
.getOrElse(StreamingTextDiff(newText = text, startingByteCount = 0))
}
@@ -1,6 +1,7 @@
package net.eagle0.eagle.service
import java.io.PrintWriter
import java.nio.charset.StandardCharsets
import net.eagle0.eagle.api.command.AvailableCommands
import net.eagle0.eagle.api.eagle.{GameUpdate, ShardokServerStatus, UpdateStreamResponse}
@@ -488,6 +489,47 @@ class HumanPlayerClientConnectionStateTest
}
}
it should "replace completed client text when its known byte count differs from the server text" in {
val requestId = "chronicle"
val text = "Tomlins completed backstory"
val client = new HumanPlayerClientConnectionState(
eagleGameId = eagleGameId,
factionId = fid,
clientId = "test-client",
grpcObserver = mockObserver,
unfilteredKnownHistoryCount = largerCount,
knownShardokResultCounts = Vector.empty,
streamingTextStatusMap = Map(
requestId -> StreamingTextStatus(
llmIdentifier = requestId,
knownByteCount = 0,
knownComplete = true
)
),
logWriter = mockLogWriter
)
var sentResponse = Option.empty[UpdateStreamResponse]
mockObserver.onNext
.expects(*)
.onCall { (response: UpdateStreamResponse) =>
sentResponse = Some(response)
}: Unit
val updatedClient = client.update(CompleteClientText(requestId, text, 1))
updatedClient.streamingTextStatusMap(requestId).knownByteCount shouldBe text.getBytes(StandardCharsets.UTF_8).length
val gameUpdate = sentResponse
.flatMap(_.responseDetails.gameUpdate)
.getOrElse(fail("expected a game update"))
gameUpdate.gameUpdateDetails match {
case GameUpdate.GameUpdateDetails.StreamingTextResponse(streamingText) =>
streamingText.newText.shouldBe(text)
streamingText.startingByteCount.shouldBe(0)
streamingText.completed.shouldBe(true)
case _ => fail("expected replacement streaming text")
}
}
it should "replace an old attempt even when the retry is already longer" in {
val requestId = "chronicle"
val oldAttempt = "gemini-old"