Skip to content

fix(learning): read lesson JSON from reasoning-model replies - #2375

Merged
pancacake merged 1 commit into
HKUDS:devfrom
Mahdi-Hasan:fix/2367-lesson-json-reasoning
Nov 5, 2026
Merged

pancacake merged 1 commit into
HKUDS:devfrom
Mahdi-Hasan:fix/2367-lesson-json-reasoning

Conversation

@Mahdi-Hasan

@Mahdi-Hasan Mahdi-Hasan commented Nov 2, 2026 •

Copy link
Copy Markdown
Contributor

Description

Prepare Lesson and semantic assessment parsed the model reply with json.loads after stripping only a leading fence. With a reasoning model at high effort, this broke in two ways:

  • A <think> block or a prose preface made parsing fail.
  • Hidden reasoning used up the 1800/2200-token budget, so the JSON came back truncated or empty.

Changes:

  • _json_response parses a complete reply first. If that fails, it:
    • removes closed <think>/<thinking> blocks
    • treats a reply that still opens with reasoning as unanswered
    • returns the first JSON object found by JSONDecoder.raw_decode scanning, so fences and prose anywhere are handled
  • Truncated JSON still fails. It is never repaired into a partial object, and a nested fragment of it is never returned.
  • When a reply is unusable, the request is retried once at RETRY_REASONING_EFFORT. This is the project rule in services/llm/structured_retry.py, applied here to lesson preparation and semantic assessment. Provider errors are not retried.
  • Error types and messages are unchanged.

I did not reuse parse_json_response or extract_json_object:

  • parse_json_response runs json_repair, which turns a truncated payload into a partial object.
  • extract_json_object doesn't strip <thinking>, so it can return a JSON draft from inside the reasoning.

Here, both outcomes would hide the failure.

Related Issues

Module(s) Affected

  • Other: learning
  • tests

Checklist

  • This PR targets dev (not main — see the base-branch selector above).
  • 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.
    • All hooks pass except two that fail only on Windows:
    • deeptutor-repo-hygiene calls python3, which Windows lacks. python scripts/check_repo_hygiene.py passes.
    • mypy checks against win32 and flags fcntl/os.killpg. With --platform linux, it reports 3 errors, all in files this PR does not touch (capability_registry.py, capabilities/mastery/tools.py).
  • I have added relevant tests for my changes.
  • I have updated the documentation (if necessary). Not needed: no user docs cover this parsing path, and error types and messages are unchanged.
  • My changes do not introduce any new security vulnerabilities.

Additional Notes

  • 21 new tests in deeptutor/learning/tests/test_lesson_json_response.py:
    • think/thinking blocks, prose prefix, mid-text fence, nested braces in strings
    • truncated and unclosed-think input
    • the retry path
  • 12 of them fail on dev. The invalid-input cases pass on both, which confirms the error type is the same.
  • Focused learning and mastery suites: 1570 passed. The 1 failure (test_old_submission_retry_cannot_overwrite_newer_answer) also fails on dev on Windows.
  • Not verified against a live OpenRouter or DeepSeek endpoint.
  • At max effort, the first call is usually wasted and the retry runs at low effort. If you'd rather raise max_tokens as well, I can add that.

🤖 Generated with Claude Code

Prepare Lesson parsed the model reply with json.loads after stripping
only a leading code fence. Reasoning models add <think> blocks or a
prose preface, and at high effort can spend the 1800-token budget on
hidden reasoning, so every attempt failed as an invalid source-selection
response. Assessment requests share the same parser.

Strip closed <think>/<thinking> blocks, treat a reply that still opens
with reasoning as unanswered, and scan for the first JSON object with
JSONDecoder.raw_decode. Truncated JSON still fails instead of being
repaired into a partial object. When the reply is unusable, retry once
at RETRY_REASONING_EFFORT, the project rule in structured_retry, for
both lesson preparation and semantic assessment.

Fixes HKUDS#2367

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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