mirror of
https://github.com/cogwheel0/conduit.git
synced 2026-08-26 19:11:45 +00:00
* perf: stop shell rebuilds cascading into rows and cut per-flush markdown cost
Scrolling:
- Give the timeline slivers a delegate with a real shouldRebuild keyed on
rowBuilder/entries identity and centerIndex, so ChatPage setState (drag
start, keyboard insets, composer resize, pin transitions) rebuilds only
the shell, not every mounted row.
- Memoize the transcript window, ChatTimelineRenderModel, and rowBuilder by
identity in ChatPage; the fresh window list per build also defeated the
stable-layout cache's identity fast path.
- Replace full MediaQuery.of dependencies with scoped paddingOf/sizeOf.
- Raise the streaming cacheExtent from 120px to 600px; the small extent
evicted rows that then remounted with a synchronous markdown compile.
Markdown:
- Memoize buildMarkdownDisplayParts by compiled-document identity; it
re-derived one sub-document per block with deep compares on every build.
- Add identical() fast paths to CompiledMarkdownDocument and
PreparedMarkdownText equality; compare rope segments instead of
materializing both sides.
- Cache per-message structure-signature fragments by ChatMessage identity
instead of rebuilding O(messages x versions) strings per emission.
- Gate incremental preparation and the reference-definition strip on one
shared line-anchored predicate; a bare "]:" substring no longer forces
full re-preparation of the whole message every flush.
- Make the streaming split's unsafe-line detection fence-aware and cap
freezing at the first raw HTML block instead of keeping the entire
document mutable; reuse the fence-close helper instead of compiling a
RegExp per fenced block.
- Skip the four LaTeX extraction regex passes when content has no $ or
backslash; memoize the error-heuristic content scans in the assistant
footer; gate profiler map allocations to profiling builds; memoize the
code-block line split and render >50k-char code plain.
* fix: detect reference definitions past raw HTML blocks in streaming split
The single-pass unsafe-line scan returned at the first raw HTML line, so a
reference definition appearing after that block was never seen and blocks
containing its links could be frozen with the link unresolved. Scan the whole
region for definitions and only record the first raw HTML offset as the
freeze cap.
* fix: distrust fence state after a raw HTML block starts in unsafe-line scan
Backtick lines inside raw HTML are content, not fences; an odd count left the
fence tracker 'inside' a fence and skipped a later real reference definition,
freezing earlier blocks with unresolved links. After the first raw HTML start,
check every line for a definition regardless of fence state — at worst more
conservative than the whole-document check this replaced.
* fix: long-response truncation at completion and follow-ups never arriving
Truncation: the streamed buffer is never periodically folded into message
state, so /api/chat/completed was built from a stale prefix of the response
and the server's echo of that payload truncated the full content when merged
back (worst on the HTTP/SSE transport, which reached completion without any
terminal flush). Flush the buffer before building the completed payload, and
guard the three unguarded overwrite paths (completed echo, replay-gap
authoritative recovery, cumulative chat:completion content snapshots) so a
strict prefix of already-streamed content is never adopted.
Follow-ups: the server emits chat:message:follow_ups only after
chat:completion {done:true}, and the per-stream socket subscription is
disposed synchronously by that done event, so the streaming handler for
follow-ups was unreachable. The passive conversation subscription is the
surviving delivery path; apply the pushed payload directly to the target
message there instead of relying on a debounced refetch that races the
server's own persistence of the suggestions.
* fix: address review feedback on follow-ups delivery and splitter
- Fall through to the debounced refetch when a pushed follow-ups payload
targets a message id not present in local state.
- Split the follow-ups envelope parser into a private implementation with a
visibleForTesting wrapper, matching file convention.
- Detect reference-definition labels containing escaped brackets in the
streaming splitter's unsafe-line scan.
- Use package:checks in the new follow-ups parser test.
* fix: deferred structured-output projections dropping response content
The structured-output projector defers full re-projections geometrically
(next re-render only at 2x the last projected length) and permanently
disables its plain-append fast path once the text contains a backtick, so
the visible content can trail the logical content by up to half the
response. Two consequences fixed here:
- A plain content delta arriving after a deferred projection appended onto
the short stale render and flipped structuredOutputIsLatest, which also
made the terminal projector finalize bail — permanently dropping the
deferred middle of the response on screen and in the persisted echo.
appendVisibleAssistantChunk now materializes the full projection (new
StructuredOutputStreamingProjector.syncProjectionToLatest) before
switching the content basis to plain appends.
- handleCompletionDone flushed the buffer before building the completed
payload but did not finalize the projector first, so the payload (and the
outlet-filter echo derived from it) could carry the stale short render.
Also fold the un-flushed streaming buffer into state in _cancelMessageStream
(conversation switch / message deletion mid-stream discarded the entire
un-synced tail), skipped during provider dispose where state is untouchable.
* fix: harden remaining content-adoption paths against divergent server bodies
Local and server renders of the same turn wrap reasoning/tool sections in
semantic <details> blocks with different attributes (locally injected
duration=\"0\" vs the server's real duration), so every raw startsWith/length
guard was dead on reasoning turns. Content comparisons now strip rendered
semantic details and compare answer bodies:
- applyServerContent adopts only when the server's answer body is at least
as long as the local one; a snapshot whose raw length grew (long reasoning
block) while the answer shrank no longer replaces a complete local answer.
- _shouldPreserveLocalAssistantContent (all snapshot adoptions including the
reopened-stream reconcile and its buffer rebase) compares stripped bodies.
- The completed-echo, replay-gap recovery, and cumulative content-snapshot
guards compare stripped bodies, and an echo differing only by details
wrappers is a no-op instead of an adoption.
Also:
- Hermes: a terminal/recovered output that is a strict prefix of the
streamed text no longer replaces it (lagging aggregate or incomplete
recovery would truncate delivered content).
- The local turn echo payload now carries output, files, embeds, usage,
sources, statusHistory, followUps, and error: the sync outbox rebuilds the
chat blob from these rows and the server merge replaces message objects
wholesale, so omitted fields were wiped from the server copy on push.
- A stale settled markdown refresh no longer leaves the preparation flag set
when nothing newer is queued (indefinite loading skeleton).
* fix: address review findings on the unsafe-line scan and echo payload
- Remove the backslash overlap in the reference-definition label pattern;
the overlapping alternation could backtrack exponentially on long
malformed labels, on the UI isolate.
- Track <details> bodies opaquely (open/close depth) in the unsafe-line
scan, mirroring the block scanner: an unmatched backtick line inside a
details body no longer opens a phantom outer fence that hid later
reference definitions.
- Persist codeExecutions in the local turn-echo payload alongside the other
durable server-shape fields.
* fix: live-tail freeze/duplication, follow-up persistence, scroll-down jank
Live tail (regression from the projection-sync fix): syncProjectionToLatest
re-armed the projector's geometric backoff to 2x the full content length
while the plain-append transition disabled the append path, so subsequent
output snapshots all deferred and the visible tail froze for the rest of the
turn. The sync now preserves the backoff threshold. Same-frame handling now
also matches the upstream client contract (Chat.svelte): a frame carrying an
output snapshot supersedes its own choices delta / content field — Conduit
applied the delta first and the snapshot second, duplicating text the
snapshot already contained.
Follow-ups: pushed suggestions were applied to in-memory state only; the
turn echo had been persisted at completion before the event fired, so a
conversation switch reloaded the message without them. The passive handler
now re-persists the message row after applying the payload.
Scroll-down jank: three down-only per-frame costs while returning toward the
bottom — the bottom-anchor recompute re-armed a full layout-maintenance pass
(row-rect snapshot + pin geometry) on every metrics tick once anchored (now
only on anchored-state transitions); pin geometry re-measured three global
rects per frame mid-scroll (now skipped until motion settles once reported);
and UserScrollNotification(idle) was treated as drag end even though Flutter
publishes it at ballistic START, running mode flips and jump-to-latest
arming mid-fling (drag end now comes from ScrollEndNotification, which fires
at actual rest).
* fix: streamed word drops and quote/entity rendering defects
Quotes/entities:
- Answer text no longer escapes double quotes (element-mode escaping; tags
are still neutralized). " escaped into a context the markdown decoder
skips — immediately after a backquote, or inside code via the streaming
fragment path — surfaced literally on screen. Attribute-mode escaping
stays for <details> attribute values.
- The plain streaming accumulator was seeded/refilled from the RENDERED
(already-escaped) body on reopen/reasoning sync; the next full render
escaped it a second time (&quot; decoding once back to a visible
"). Plain-content derivation now strips semantic details AND
unescapes entities.
- Clipboard copy and TTS decode presentation entities back to literal text.
API replay deliberately does not (it cannot distinguish model-typed
entities from presentation escaping, and the direct bridge has trusted
raw replay for fidelity).
Missing words:
- Whitespace-only deltas were discarded on one transport (trim() guard),
gluing words together and losing paragraph breaks.
- Whitespace-only semantic text blocks were dropped from full renders and
the streaming append delta never re-emits the swallowed prefix — the
blank line between a reasoning section and the answer vanished.
- Once a backtick/tilde disabled the projector's append path for the turn,
geometric backoff left the visible tail up to 50% behind until
completion; renders now use a bounded additive step when appends are
unavailable.
- The SSE parser now mutes same-frame deltas only when the output snapshot
parses into renderable blocks, matching the socket path — an output whose
items all parse away no longer mutes the delta while rendering nothing.
* fix: address review feedback on plain-content whitespace and coverage
- The semantic-details strip in plain-content derivation now consumes only
the wrapper's own trailing newline instead of \s* plus trim, preserving
answer whitespace such as a leading indented code block's indentation.
- Regression tests: the additive re-projection schedule for code-bearing
streams, and a non-renderable output snapshot not muting the same-frame
delta.
* fix: match the details parser's exact close token in streaming scans
The streaming details trackers accepted '</details >' as a close while the
details parser recognizes only the literal '</details>'. A close lookalike
inside a streamed details body exited details tracking early, let a body
backtick open a phantom outer fence, and hid a valid reference definition
after the real close — freezing an earlier reference-style link unresolved.
The preparation engine's checkpoint scanner had the same loose pattern and
could split prepared content mid-block. Both now match the parser exactly,
with a regression test verified to fail against the loose pattern.
* fix: reconcile deferred snapshots at terminal finalize and audit lengths
finalizeStructuredOutputProjection bailed whenever a plain chunk was the
last content-affecting operation; if output snapshots after that chunk had
deferred under the re-projection threshold, the deferred tail was dropped
from the final content. The finalize now adopts the terminal render unless
the accumulated visible text is longer (matching upstream's output-replaces-
content contract while preserving delta-only hybrid streams).
The done-signal log now records message/rendered/plain lengths so a
truncation report can be pinpointed from a single log line: message shorter
than rendered points at a lost flush; rendered shorter than plain points at
an unrepaired deferred projection.
* fix: reconcile terminal projection when plain chunks ended the stream
finalizeStructuredOutputProjection bailed whenever a plain chunk was the
last content-affecting operation. The terminal snapshot render is
authoritative upstream (output replaces content wholesale in Chat.svelte);
adopt it unless the accumulated visible text is longer, preserving
delta-only hybrid streams.
The done-signal log now records message/rendered/plain lengths so a
truncation report can be pinpointed from one log line.
* fix: render output[] in poll recovery when persisted content is empty
OWUI 0.11 never persists a flat content string for a normal completion —
the durable body is the output[] item array, so a reasoning turn's raw
content is ''. pollServerForMessage ignored output[] entirely: whenever the
live socket missed the final frames (buffer caps on long reasoning
streams, reconnects), every recovery path polled the server, extracted an
empty string, adopted nothing, saw done=true, and finished the turn with
the partial local text — permanent tail truncation, reasoning models only.
Recovery now renders output[] with the same renderer the snapshot parser
uses when flat content is empty. Regression test verified to fail without
the fix.
* revert: drop the speculative terminal-finalize reconciliation
The longer-wins adoption added in
|
||
|---|---|---|
| .. | ||
| release_notes_validator_test.dart | ||