- Pass Anthropic reasoning effort through env, settings, tests, and UI
- Allow max reasoning effort for OpenAI and Anthropic provider tests
- Open generated scripts directly and download narration without popup tabs
Audit of the outline- and slide-generation paths found four systemic defects.
All are fixed here, with 244 regression tests added across 9 new test files.
Failures were being reported as successes. Every fallback path persisted
fabricated or broken content, marked the stage completed, billed the user and
returned a success message:
- Add OutlineRepairFailedError; the repair loop now raises instead of returning
an outline that failed all 10 attempts.
- Drop the 3-page placeholder outline that replaced a failed generation.
- Track failed slides separately so they are not billed, not marked completed,
and not skipped as "already generated" on a re-run.
- Stop decoding binary uploads as latin-1, which turned PDFs into mojibake
outlines that were reported as successful.
Cancellation, reset and slide locks did nothing:
- Forward _is_slides_generation_cancelled through EnhancedPPTService, which has
no __getattr__, so the stop button was a silent no-op for the whole run.
- Catch CancelledError when awaiting the cancelled lock renewer; it is a
BaseException, so the distributed lock was never released.
- Add clear_project_outline/clear_project_slides; save_project_outline(None)
and save_project_slides("", []) were both no-ops, so stage reset kept the
old data and replayed it as a fresh result.
- Persist slide locks in slide_metadata and honour them in batch regeneration.
HTML validation was inverted. libxml2 only knows HTML 4.0, so strict parsing
rejected <header>/<section>/<svg> while accepting truncated documents:
- Add html_structure.py, an HTML5-aware structural check built on html.parser.
- Re-validate after auto-repair rather than treating "the string changed" as
success, which made the retry budget unreachable.
- Prefer the last complete document when extracting HTML, so repair responses
that echo the original no longer return the unrepaired input.
The streaming think-tag filter lost or leaked content when a marker was split
across chunks; rewrite it to buffer partial markers.
Also fixed:
- Confine client-supplied file paths to the upload roots (arbitrary file read).
- Escape model-generated content before it reaches innerHTML (stored XSS).
- Enforce ownership on the slide cancel endpoints and validate stage status.
- Give the fixed page-count mode a real branch; it behaved as ai_decide and
overwrote the user's setting.
- Stop citation markers such as "[1]" from hijacking outline JSON extraction.
- Bill only slides that were actually persisted, and price the streaming and
non-streaming outline routes identically.
- Cancel orphaned generation tasks when an SSE client disconnects.
- Release stale "running" tasks found in the database, not just in Valkey.
- Replace a 3s unconditional poll (which updated absent DOM nodes) with a
stoppable 15s poll, and add an AbortController to prevent concurrent streams.
- Implement restore_project_version, which raised AttributeError on every call.
- Declare lxml, which was imported directly but only present transitively.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Subtrees using CSS features OOXML cannot express (conic gradients,
multi-layer shadows, clipped badges) are now rasterized as PNG
decoration layers while their text stays native editable runs.
Bumps dom-to-pptx bundle version to 20260707-hybrid-raster-v1 across
editor/template helpers, the slides editor template, and smoke tests.
Adds hybrid raster smoke test and fixture.