Skip to content

fix: Qwen model JSON preamble, force-recompile overview pages, health false positives#529

Merged
pancacake merged 3 commits into
HKUDS:devfrom
alvinets:fix/local-patches-win-utf8
May 29, 2026
Merged

fix: Qwen model JSON preamble, force-recompile overview pages, health false positives#529
pancacake merged 3 commits into
HKUDS:devfrom
alvinets:fix/local-patches-win-utf8

Conversation

@alvinets

Copy link
Copy Markdown

Description

Fixes three categories of issues in the Book Engine encountered when running with local/Qwen models (Qwen3.6-35B-A3B via vLLM):

1. _llm_writer.py — Local model JSON output handling

  • Qwen outputs a thinking/reasoning preamble before the actual JSON, even with response_format=json_object. The existing parse_json_response could not recover JSON reliably.
  • vLLM rejects reasoning_effort: "minimal" — only "low", "medium", "high" are accepted.
  • max_tokens was too low (900) for local models that consume tokens on thinking text.

2. engine.py — Force-recompile & book finalization gaps

  • compile_page(force=True) excluded overview pages from block reset, making "Force regenerate" a no-op for the book intro page.
  • regenerate_block() did not trigger _maybe_finalize_book(), so fixing the last failing block left the book in compiling/partial state.

3. kb_health.py — False-positive "Recurring issue" warnings

  • scan_log_health() flagged every repeated log entry as a "recurring failure", including repeated successful compile_page → ready entries, making the health banner useless.

4. Frontend UX — Missing feedback on long operations

  • No error handling on compilePage and regenerateBlock — failures silently swallowed.
  • No loading state on "Force regenerate" / "Regenerate page" buttons.
  • Hydration mismatch from browser extension attribute injection.

Related Issues

  • Related to local LLM / Qwen model compatibility
  • Related to Book Engine overview page regeneration

Module(s) Affected

  • Other: book (Book Engine — _llm_writer.py, engine.py, kb_health.py)
  • web (Frontend — PageReader.tsx, page.tsx, layout.tsx)

Checklist

  • I have read and followed the contribution guidelines.
  • My code follows the project's coding standards.
  • I have run pre-commit run --all-files and fixed any issues.
  • I have added relevant tests for my changes.
  • I have updated the documentation (if necessary).
  • My changes do not introduce any new security vulnerabilities.

Detailed Changes

deeptutor/book/blocks/_llm_writer.py

  • Added _strip_thinking_preamble() — finds the last { in the response (actual JSON after thinking preamble). Validates with json.loads(), falls back to first { with heuristic prefix check.
  • Increased max_tokens from 900 → 2600 to accommodate thinking preamble + JSON output.
  • Removed response_format={"type": "json_object"} — local models ignore this parameter.
  • Changed reasoning_effort retry from "minimal""low" (vLLM only accepts low/medium/high).
  • Two-pass JSON extraction: _strip_thinking_preamble first, then fallback to parse_json_response on raw text.

deeptutor/book/engine.py

  • Removed page.content_type != ContentType.OVERVIEW guard from compile_page(force=True) — overview pages now reset blocks on force-recompile.
  • Added await self._maybe_finalize_book(book_id) to regenerate_block() for proper book status propagation.

deeptutor/book/kb_health.py

  • Added ("error" in k.lower() or "fail" in k.lower()) filter to repeated_failures — only actual errors/failures are reported, not successful compilations.

web/app/(workspace)/book/components/PageReader.tsx

  • Added disabled + disabled:cursor-not-allowed disabled:opacity-50 to regenerate buttons during loading.
  • Replaced RefreshCcw with <Loader2 className="animate-spin" /> spinner.
  • Button text changes to "Regenerating…" during loading.

web/app/(workspace)/book/page.tsx

  • Added catch clauses to compilePage and handleRegenerateBlock that call notify() with error message.
  • Both still reload book detail in finally for partial progress reflection.

web/app/layout.tsx

  • Added suppressHydrationWarning to <body> for browser extension attribute injection.

Testing

All changes verified end-to-end with Qwen3.6-35B-A3B (vLLM) on Windows:

  1. JSON extraction works across text/code/quiz/concept_graph blocks — thinking preamble stripped.
  2. Overview page How to read this book force-recompiles (3/3 blocks in ~32s).
  3. regenerate_block() on last failing block correctly transitions book to ready.
  4. scan_log_health returns repeated_failures: [] for book with 36 successful compile_page entries.
  5. Error notifications display via notify(); loading spinner shown during compile.

Files Changed

deeptutor/book/blocks/_llm_writer.py               | 59 +++++++++++---
deeptutor/book/engine.py                           |  3 +-
deeptutor/book/kb_health.py                        |  5 +-
web/app/(workspace)/book/components/PageReader.tsx | 21 +++++--
web/app/(workspace)/book/page.tsx                  | 16 ++++-
web/app/layout.tsx                                 |  2 +-
6 files changed, 90 insertions(+), 16 deletions(-)

Name Card Scanner Dev added 3 commits May 27, 2026 15:40
…artup scripts

- Fix UnicodeEncodeError 'gbk' on Windows by setting encoding='utf-8' on
  stdout/stderr reconfigure in run_server.py
- Guard Deep Research send to prevent 'settings are incomplete' crash
- Add lazy import for notebook_manager in services/__init__.py
- Add start_backend.bat with PYTHONIOENCODING=utf-8 and PYTHONUTF8=1
- Add start_frontend.bat convenience script
- Document all patches in PATCHES.md for re-apply after upstream sync
…h false positives

- _llm_writer.py: strip thinking preamble from Qwen output, remove unsupported
  response_format, increase max_tokens to 2600, fix reasoning_effort 'minimal' -> 'low'
- engine.py: allow force-recompile on overview pages (previously a no-op),
  add _maybe_finalize_book to regenerate_block for book status propagation
- kb_health.py: filter repeated_failures to only count error/fail log entries
- PageReader.tsx: loading spinner + disabled state on regenerate buttons
- page.tsx: error notifications via notify() for compile/regenerate failures
- layout.tsx: suppressHydrationWarning for browser extension attribute injection
@alvinets
alvinets marked this pull request as ready for review May 27, 2026 18:29
@pancacake
pancacake merged commit 56a87d3 into HKUDS:dev May 29, 2026
@pancacake

Copy link
Copy Markdown
Collaborator

Thanks for your contribution!

pancacake added a commit that referenced this pull request Jun 11, 2026
#493 (sqlite): route _initialize() through the _connect() contextmanager
(the one remaining raw sqlite3.connect FD leak); document the auto-commit
contract so the redundant explicit commits are understood.

#529 (book):
- _strip_thinking_preamble: guard `{ not in text` before rindex() so plain
  prose (refusals/errors) no longer raises ValueError -> ERROR block;
  hoist json import to module top.
- llm_json: honor caller max_tokens as max(max_tokens, 2600) instead of
  discarding it; bump default to 2600; fix stale 'minimal' docstring -> 'low'.
- engine.compile_page: rebuild OVERVIEW pages deterministically via
  _materialize_overview_page on force, instead of running the generic LLM
  compiler over hand-built intro/concept-graph/index blocks.
- kb_health.scan_log_health: key repeated-failure detection on the op, not
  the free-text message, removing false positives/negatives + dead block_error.
- services/__init__: drop orphaned notebook_manager lazy-import + __all__ entry.
- PageReader.tsx: restore t() i18n on regenerate buttons; add 'Regenerating…'.
- remove PATCHES.md (developer-local scratch notes).

#484 (zulip): also match the disambiguated @**Name|user_id** mention form;
make the docstring truthful; add coverage.

#500: remove stray PR_DESCRIPTION.md committed to repo root.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
vaskoyudha added a commit to vaskoyudha/deeptutor-for-programmer-fork that referenced this pull request Jul 25, 2026
…rview pages, health false positives

Book Engine fixes for local/Qwen models: robust JSON parsing, force-recompile
now resets overview pages, regenerate_block finalizes the book, and
scan_log_health no longer flags repeated successes as failures.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
vaskoyudha added a commit to vaskoyudha/deeptutor-for-programmer-fork that referenced this pull request Jul 25, 2026
…#529/HKUDS#484

HKUDS#493 (sqlite): route _initialize() through the _connect() contextmanager
(the one remaining raw sqlite3.connect FD leak); document the auto-commit
contract so the redundant explicit commits are understood.

HKUDS#529 (book):
- _strip_thinking_preamble: guard `{ not in text` before rindex() so plain
  prose (refusals/errors) no longer raises ValueError -> ERROR block;
  hoist json import to module top.
- llm_json: honor caller max_tokens as max(max_tokens, 2600) instead of
  discarding it; bump default to 2600; fix stale 'minimal' docstring -> 'low'.
- engine.compile_page: rebuild OVERVIEW pages deterministically via
  _materialize_overview_page on force, instead of running the generic LLM
  compiler over hand-built intro/concept-graph/index blocks.
- kb_health.scan_log_health: key repeated-failure detection on the op, not
  the free-text message, removing false positives/negatives + dead block_error.
- services/__init__: drop orphaned notebook_manager lazy-import + __all__ entry.
- PageReader.tsx: restore t() i18n on regenerate buttons; add 'Regenerating…'.
- remove PATCHES.md (developer-local scratch notes).

HKUDS#484 (zulip): also match the disambiguated @**Name|user_id** mention form;
make the docstring truthful; add coverage.

HKUDS#500: remove stray PR_DESCRIPTION.md committed to repo root.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants