fix(ui): thinking window no longer scrolls — live 320px view pinned to the stream tail

This commit is contained in:
2026-08-24 18:24:57 -04:00
parent 76c6a01199
commit 04a7f4c05d
11 changed files with 618 additions and 279 deletions
+44 -8
View File
@@ -16,8 +16,9 @@ Implements just enough of the aipi surface:
- user message containing ``pretend to think slowly`` -> 3s warm-up delay
(used by the loading-feedback story).
- user message containing ``think out loud`` -> the answer is preceded by
~800 chars of deterministic ``reasoning_content`` chunks (the
thinking-display story, phase 17).
~2 700 chars of deterministic ``reasoning_content`` chunks (the
thinking-display story, phase 17; lengthened in phase 21 so the
rendered scratchpad overflows the 320px ``.thinking-text`` window)
- user message containing ``think out loud then hesitate`` -> the
``think out loud`` stream, then a 4s pause before the first content
frame (the sources-midstream story, phase 20 — a deterministic
@@ -167,13 +168,18 @@ def compose_answer(body: dict[str, Any]) -> str:
def compose_thinking(body: dict[str, Any]) -> str:
"""Deterministic reasoning scratchpad (thinking-display story, phase 17).
"""Deterministic reasoning scratchpad (thinking-display story, phase 17;
lengthened in phase 21).
A fixed 4-line "Step 1… Step 4" template quoting the first ~60 chars
of the user question: unique per question, byte-stable across runs,
~700–900 chars total (≈ 60–75 frames at the mock's 12-char/0.02s
pacing). The ``Step 2: Check my notes`` line fragment is what the E2E
assertions key off.
A fixed "Step 1… Step 4" template interleaved with a "Scratch" deep-dive
block, quoting the first ~60 chars of the user question: unique per
question, byte-stable across runs, ~2 700 chars total (≈ 230 frames at
the mock's 12-char/0.02s pacing). The length is deliberate (phase 21,
thinking-no-scroll story): rendered in the 320px ``.thinking-text``
window it overflows by ~2x, so the live-tail clip and the no-user-scroll
contract are observable in E2E. The ``Step 2: Check my notes`` line
fragment (phase 17) and the ``nothing is invented`` tail (phase 20's
THINKING_TAIL) are what the E2E assertions key off — both are preserved.
"""
q = _user(body).strip()[:60]
return (
@@ -183,6 +189,36 @@ def compose_thinking(body: dict[str, Any]) -> str:
"Step 2: Check my notes for the closest match. The homelab kubernetes file "
"is the obvious candidate, but I should also consider whether a deployments "
"note covers the same ground better.\n"
"Scratch 1: the kubernetes file is organized by component — control plane, "
"worker nodes, ingress, storage — so I can map each part of the question to "
"a section instead of summarizing the whole file at once, and keep the "
"answer anchored to the structure the notes actually use.\n"
"Scratch 2: I should check whether the deployments note duplicates any of "
"that ground; if it does, I will prefer the homelab file because the "
"question is phrased around the cluster itself, and I will say which file "
"each fact came from so the citation is honest.\n"
"Scratch 3: versions and ports are the facts most likely to be stale in my "
"memory — the etcd backup schedule, the ingress controller port, the "
"registry mirror address — so I will re-read those lines verbatim before "
"writing a single one of them into the answer.\n"
"Scratch 4: if the answer needs a sequence, for example how a node joins the "
"cluster or how the load balancer fronts the control plane, I will keep the "
"order exactly as the notes write it rather than re-deriving it from general "
"kubernetes knowledge that may not match this setup.\n"
"Scratch 5: anything I cannot find in the notes — a host I do not recognize, "
"a version I am not sure about, a schedule I cannot place — gets left out of "
"the answer instead of guessed, because the honesty rule beats a longer "
"answer every single time.\n"
"Scratch 6: one more pass over the question wording to make sure I am "
"answering the cluster setup, not some other homelab topic that shares the "
"same vocabulary, and I will stay on the specific the question asked about.\n"
"Scratch 7: I will also verify that the file describes the current setup — "
"if the notes mention a migration from an older cluster, I should answer "
"from the post-migration section and not mix in the old host names or the "
"old port numbers that no longer apply.\n"
"Scratch 8: final shape check before I commit — short paragraphs, a few "
"bullets at most, the document path cited where the fact came from, and no "
"invented facts anywhere in the draft.\n"
"Step 3: Re-read the relevant sections top to bottom so every specific — "
"hosts, versions, ports, schedules — is exact as written rather than "
"remembered, and note which document each fact comes from.\n"