AI progress: honest milestone bar from closed JSON contract units
Replace the char-count estimate (EST_OUTPUT_CHARS) with ContractProgress:
points are earned only when real output units complete — first token (5),
each closed contract key / array item (5-90), root closed (95), stored (100).
Reasoning/thinking streams no longer fake progress: the first thinking
delta fires the first-token milestone ('the model is alive') and holds.
Surface the job's tone through ProcessingJobOut/JobInfo so the bar can
label itself 'Summarizing dry wit…'. Test rewritten to pin the new
honest-thinking semantics.
This commit is contained in:
parent
6ec811393a
commit
61f2124b1c
6 changed files with 197 additions and 42 deletions
|
|
@ -93,7 +93,8 @@ async def _collect_reply(resp: httpx.Response, ticker) -> str:
|
|||
|
||||
Streaming answers are NDJSON (one JSON object per line, ``done`` on the
|
||||
last); a server honoring stream=false returns one JSON body — both work.
|
||||
The running character count feeds [ticker] for UI progress."""
|
||||
The accumulated content *prefix* feeds [ticker], which derives progress
|
||||
from completed JSON contract units only (see ContractProgress)."""
|
||||
import json as _json
|
||||
|
||||
ctype = resp.headers.get("content-type", "")
|
||||
|
|
@ -103,11 +104,10 @@ async def _collect_reply(resp: httpx.Response, ticker) -> str:
|
|||
content = _json.loads(body)["message"]["content"]
|
||||
except (ValueError, KeyError, TypeError) as e:
|
||||
raise ProviderTransientError("Ollama sent an unreadable reply.") from e
|
||||
ticker(len(content or ""))
|
||||
ticker(content or "")
|
||||
return content or ""
|
||||
|
||||
parts: list[str] = []
|
||||
total = 0
|
||||
async for line in resp.aiter_lines():
|
||||
line = line.strip()
|
||||
if not line:
|
||||
|
|
@ -117,14 +117,15 @@ async def _collect_reply(resp: httpx.Response, ticker) -> str:
|
|||
except ValueError:
|
||||
continue
|
||||
piece = (obj.get("message") or {}).get("content") or ""
|
||||
# Like the openai_compat adapter: a thinking channel is real
|
||||
# work and must move the progress ticker even when the server
|
||||
# ignored think:false.
|
||||
# Like the openai_compat adapter: a thinking channel means the
|
||||
# model started (the ticker's first-token milestone) but is not
|
||||
# contract output, so it moves nothing beyond that.
|
||||
thinking = (obj.get("message") or {}).get("thinking") or ""
|
||||
if piece or thinking:
|
||||
if piece:
|
||||
parts.append(piece)
|
||||
total += len(piece) + len(thinking)
|
||||
ticker(total)
|
||||
ticker("".join(parts))
|
||||
elif thinking:
|
||||
ticker("")
|
||||
if obj.get("done"):
|
||||
break
|
||||
content = "".join(parts)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue