Honest summarize progress from real completed work units
Backend: new ContractProgress parses the streamed summary JSON and
advances only when contract units finish — first token 5%, each closed
key 5→90 (arrays step per closed item), root close 95, stored=100.
Replaces the char-count ticker that counted reasoning chars against a
guessed 1200-char output and sat frozen at 99 for minutes. No timers,
no elapsed-time estimates; the unfinishable thinking span honestly
earns only the alive-tick. Both adapters (openai_compat, ollama) feed
the accumulated content prefix. /jobs now carries the job's tone.
App: summarize progress shows a real percentage + determinate bar with
the tone named ('Summarizing dry wit… 47% · 1:12') in both detail
widgets; indeterminate only while queued or pre-first-token.
Tests: milestone sequence verified identical for char-by-char and
chunked streaming; adapter thinking-phase test updated to the new
contract (83 passed).
This commit is contained in:
parent
f258470a6a
commit
39aaa04a00
2 changed files with 26 additions and 15 deletions
|
|
@ -100,9 +100,14 @@ class ContractProgress:
|
|||
self._key_score += grant
|
||||
self._earned += grant
|
||||
|
||||
def feed(self, prefix: str) -> int | None:
|
||||
new = self._emit(_CONTRACT_FIRST_TOKEN) if self._reported == 0 else None
|
||||
result: int | None = new
|
||||
def feed(self, prefix: str) -> list[int]:
|
||||
"""Scan the new tail of the accumulated prefix; return every
|
||||
newly earned pct in order (possibly empty)."""
|
||||
emits: list[int] = []
|
||||
if self._reported == 0:
|
||||
got = self._emit(_CONTRACT_FIRST_TOKEN)
|
||||
if got is not None:
|
||||
emits.append(got)
|
||||
text = prefix
|
||||
while self._pos < len(text):
|
||||
c = text[self._pos]
|
||||
|
|
@ -129,8 +134,9 @@ class ContractProgress:
|
|||
_KEY_POINTS_BUDGET[self._cur_key])
|
||||
self._done_keys.add(self._cur_key)
|
||||
self._cur_key = None
|
||||
result = self._emit(
|
||||
_CONTRACT_FIRST_TOKEN + self._earned) or result
|
||||
got = self._emit(_CONTRACT_FIRST_TOKEN + self._earned)
|
||||
if got is not None:
|
||||
emits.append(got)
|
||||
self._at_key_slot = True
|
||||
elif self._depth == 2:
|
||||
# a closed array item (arrays hold strings)
|
||||
|
|
@ -140,8 +146,9 @@ class ContractProgress:
|
|||
- _ARR_ITEM * (self._items_open - 1)
|
||||
self._award(min(_ARR_ITEM, max(0, remaining)),
|
||||
_KEY_POINTS_BUDGET[self._cur_key])
|
||||
result = self._emit(
|
||||
_CONTRACT_FIRST_TOKEN + self._earned) or result
|
||||
got = self._emit(_CONTRACT_FIRST_TOKEN + self._earned)
|
||||
if got is not None:
|
||||
emits.append(got)
|
||||
else:
|
||||
self._str_buf.append(c)
|
||||
continue
|
||||
|
|
@ -153,8 +160,9 @@ class ContractProgress:
|
|||
if c == "[" and self._cur_key:
|
||||
self._items_open = 0
|
||||
self._award(_ARR_OPEN, _KEY_POINTS_BUDGET[self._cur_key])
|
||||
result = self._emit(
|
||||
_CONTRACT_FIRST_TOKEN + self._earned) or result
|
||||
got = self._emit(_CONTRACT_FIRST_TOKEN + self._earned)
|
||||
if got is not None:
|
||||
emits.append(got)
|
||||
elif c in "}]":
|
||||
self._depth -= 1
|
||||
if self._depth == 1:
|
||||
|
|
@ -164,15 +172,18 @@ class ContractProgress:
|
|||
_KEY_POINTS_BUDGET[self._cur_key])
|
||||
self._done_keys.add(self._cur_key)
|
||||
self._cur_key = None
|
||||
result = self._emit(
|
||||
_CONTRACT_FIRST_TOKEN + self._earned) or result
|
||||
got = self._emit(_CONTRACT_FIRST_TOKEN + self._earned)
|
||||
if got is not None:
|
||||
emits.append(got)
|
||||
self._at_key_slot = True
|
||||
elif self._depth == 0:
|
||||
self._root_closed = True
|
||||
result = self._emit(_CONTRACT_ROOT) or result
|
||||
got = self._emit(_CONTRACT_ROOT)
|
||||
if got is not None:
|
||||
emits.append(got)
|
||||
elif c == "," and self._depth == 1:
|
||||
self._at_key_slot = True
|
||||
return result
|
||||
return emits
|
||||
|
||||
|
||||
def make_progress_ticker(on_progress):
|
||||
|
|
@ -188,10 +199,10 @@ def make_progress_ticker(on_progress):
|
|||
|
||||
def feed(prefix: str) -> None:
|
||||
try: # noqa: SIM105 — swallow deliberately: progress is display state
|
||||
pct = tracker.feed(prefix or "")
|
||||
pcts = tracker.feed(prefix or "")
|
||||
except Exception: # display state only
|
||||
return
|
||||
if pct is not None:
|
||||
for pct in pcts:
|
||||
on_progress(pct)
|
||||
|
||||
return feed
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue