Honest summarize progress: backend percent pins at 99 the moment tokens start (thinking models stream immediately) then holds there for minutes — so streaming now shows 'Summarizing… working · m:ss' with an indeterminate bar; '0% waiting for model' stays for the pre-token wait; 100 only on success. Transcribe keeps its real percentage.
This commit is contained in:
parent
8586bc2476
commit
1eb6d55f4d
3 changed files with 34 additions and 30 deletions
|
|
@ -1667,13 +1667,15 @@ class DesktopState(private val appDir: File = defaultAppDir()) {
|
||||||
else -> LiveProgress("transcribing…", null)
|
else -> LiveProgress("transcribing…", null)
|
||||||
}
|
}
|
||||||
s != null && s.status == "running" ->
|
s != null && s.status == "running" ->
|
||||||
if (s.progress != null)
|
if (s.progress == null)
|
||||||
LiveProgress("summarizing… ${s.progress}%", s.progress / 100f)
|
LiveProgress("summarizing… 0% waiting for model" +
|
||||||
// No tokens yet: the LLM is cold or the server is busy
|
waitSuffix(s.startedAt, nowMs), null)
|
||||||
// queueing. Still show a number (0%) — a summarize must
|
// Backend summarize progress saturates at 99 the moment
|
||||||
// always display a percentage (user request) — plus the
|
// the first tokens arrive (thinking models stream
|
||||||
// reason and wait time so 0% doesn't look frozen.
|
// reasoning immediately), so the number means
|
||||||
else LiveProgress("summarizing… 0% waiting for model" +
|
// "streaming", never "how far". Elapsed time is the
|
||||||
|
// honest signal; the bar stays indeterminate.
|
||||||
|
else LiveProgress("summarizing… working" +
|
||||||
waitSuffix(s.startedAt, nowMs), null)
|
waitSuffix(s.startedAt, nowMs), null)
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -777,8 +777,11 @@ fun DetailScreen(state: DesktopState) {
|
||||||
val sJob = detail.jobs.firstOrNull { it.jobType == "summarize" }
|
val sJob = detail.jobs.firstOrNull { it.jobType == "summarize" }
|
||||||
sJob?.takeIf { it.status == "running" || it.status == "queued" }?.let {
|
sJob?.takeIf { it.status == "running" || it.status == "queued" }?.let {
|
||||||
val label = when {
|
val label = when {
|
||||||
|
// Streaming summarize shows elapsed time, not the backend
|
||||||
|
// number: it pins at 99% the moment tokens start.
|
||||||
it.status == "running" && it.progress != null ->
|
it.status == "running" && it.progress != null ->
|
||||||
"Summarizing… ${it.progress}%"
|
"Summarizing… working" +
|
||||||
|
DesktopState.waitSuffix(it.startedAt, System.currentTimeMillis())
|
||||||
it.status == "running" -> "Summarizing… 0% waiting for model" +
|
it.status == "running" -> "Summarizing… 0% waiting for model" +
|
||||||
DesktopState.waitSuffix(it.startedAt, System.currentTimeMillis())
|
DesktopState.waitSuffix(it.startedAt, System.currentTimeMillis())
|
||||||
else -> "Summary queued" +
|
else -> "Summary queued" +
|
||||||
|
|
@ -788,18 +791,11 @@ fun DetailScreen(state: DesktopState) {
|
||||||
color = MaterialTheme.colorScheme.primary)
|
color = MaterialTheme.colorScheme.primary)
|
||||||
if (it.status == "running") {
|
if (it.status == "running") {
|
||||||
Spacer(Modifier.height(4.dp))
|
Spacer(Modifier.height(4.dp))
|
||||||
if (it.progress != null) {
|
|
||||||
LinearProgressIndicator(
|
|
||||||
progress = { it.progress / 100f },
|
|
||||||
modifier = Modifier.fillMaxWidth().height(4.dp),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
LinearProgressIndicator(
|
LinearProgressIndicator(
|
||||||
modifier = Modifier.fillMaxWidth().height(4.dp),
|
modifier = Modifier.fillMaxWidth().height(4.dp),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
detail.error?.let {
|
detail.error?.let {
|
||||||
Text(it, color = MaterialTheme.colorScheme.error)
|
Text(it, color = MaterialTheme.colorScheme.error)
|
||||||
if (tJob?.status == "failed") {
|
if (tJob?.status == "failed") {
|
||||||
|
|
@ -963,19 +959,18 @@ fun DetailScreen(state: DesktopState) {
|
||||||
val sStatus = sJob?.status
|
val sStatus = sJob?.status
|
||||||
val sProg = sJob?.progress
|
val sProg = sJob?.progress
|
||||||
val sAttempt = sJob?.attempt ?: 0
|
val sAttempt = sJob?.attempt ?: 0
|
||||||
if (sStatus == "running" && sProg != null && sProg > 0) {
|
// Bar stays indeterminate while running: backend summarize
|
||||||
LinearProgressIndicator(
|
// progress pins at 99 as soon as tokens start, so a
|
||||||
progress = { sProg.coerceIn(0, 99) / 100f },
|
// determinate bar would freeze near-full for minutes.
|
||||||
modifier = Modifier.fillMaxWidth())
|
|
||||||
} else {
|
|
||||||
LinearProgressIndicator(modifier = Modifier.fillMaxWidth())
|
LinearProgressIndicator(modifier = Modifier.fillMaxWidth())
|
||||||
}
|
|
||||||
Text(buildString {
|
Text(buildString {
|
||||||
append(when {
|
append(when {
|
||||||
sStatus == "queued" -> "Queued…" +
|
sStatus == "queued" -> "Queued…" +
|
||||||
DesktopState.waitSuffix(sJob?.createdAt,
|
DesktopState.waitSuffix(sJob?.createdAt,
|
||||||
System.currentTimeMillis())
|
System.currentTimeMillis())
|
||||||
sProg != null && sProg > 0 -> "Summarizing… $sProg%"
|
sProg != null && sProg > 0 -> "Summarizing… working" +
|
||||||
|
DesktopState.waitSuffix(sJob?.startedAt,
|
||||||
|
System.currentTimeMillis())
|
||||||
else -> "Summarizing… 0% waiting for model" +
|
else -> "Summarizing… 0% waiting for model" +
|
||||||
DesktopState.waitSuffix(sJob?.startedAt,
|
DesktopState.waitSuffix(sJob?.startedAt,
|
||||||
System.currentTimeMillis())
|
System.currentTimeMillis())
|
||||||
|
|
|
||||||
|
|
@ -49,13 +49,20 @@ class JobProgressTest {
|
||||||
assertNull(p?.fraction)
|
assertNull(p?.fraction)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test fun `summarizing shows backend percentage`() {
|
@Test fun `streaming summarize shows working plus elapsed, not fake percent`() {
|
||||||
|
val t0 = System.currentTimeMillis() / 1000 * 1000
|
||||||
|
val iso = java.time.LocalDateTime.ofInstant(
|
||||||
|
java.time.Instant.ofEpochMilli(t0 - 42_000), java.time.ZoneOffset.UTC)
|
||||||
|
.withNano(0).toString()
|
||||||
val p = DesktopState.jobProgress(
|
val p = DesktopState.jobProgress(
|
||||||
listOf(job("transcribe", "succeeded"),
|
listOf(job("transcribe", "succeeded"),
|
||||||
job("summarize", "running", progress = 37)),
|
job("summarize", "running", progress = 99, startedIso = iso)),
|
||||||
|
nowMs = t0,
|
||||||
)
|
)
|
||||||
assertEquals("summarizing… 37%", p?.label)
|
// Backend progress pins at 99 as soon as tokens flow — the label
|
||||||
assertEquals(0.37f, p!!.fraction!!, 0.0001f)
|
// must not present it as a completion percentage.
|
||||||
|
assertEquals("summarizing… working · 42s", p?.label)
|
||||||
|
assertNull(p?.fraction)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test fun `waiting label carries elapsed time`() {
|
@Test fun `waiting label carries elapsed time`() {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue