Overview
This patch release fixes two regressions reported by community users: summary persistence silently failing on fresh PostgreSQL databases, and cached summaries being rejected every turn for reasoning models (issue #98). It also ships an end-to-end verification suite that replays real OpenWebUI request-construction code against the filter, plus diagnostic logging so any future per-position rejection can be pinpointed without code changes.
Fixes
- Summary persistence on fresh databases: The
chat_summarytable failed to initialize on some PostgreSQL deployments because the shared SQLAlchemy metadata retained a stale unique index definition with the same name as the new non-unique index._init_databasenow deduplicatesChatSummarytable index definitions in memory before emittingCREATE TABLE, and clears any legacy colliding indexes (ix_chat_summary_chat_id,ix_chat_summary_branch_tip_id,ix_chat_summary_updated_at) idempotently. This resolvesDuplicateTable: relation "ix_chat_summary_chat_id" already existsand the resulting⚠️ Summary generated but was not persisted. - Outlet summary reuse for idless plain-chat branches: When the outlet request had no stable message refs but carried a
chat_id, the filter previously failed to attach refs to the generated summary. It now reads the active DB branch and aligns the body against it via_compatible_db_branch_for_body_ref_fallback, so plain-chat summaries can be persisted and reused on subsequent turns. - Reasoning-model inlet rejection (issue #98): Reasoning models store assistant content with folded
<details type="reasoning">blocks in the DB, but the request body reconstructed byprocess_messages_with_outputstrips or re-tags the reasoning. Both existing alignment paths (folded content match and unfolded content match) therefore failed, and the inlet re-sent the full uncompressed history every turn. A new position-based fallback (Path 3) accepts the snapshot when the body and DB branch have equal length, roles / tool_calls / tool_call_id match position-by-position, and DB messages that carry anoutputarray are exempted from content comparison. DB messages without anoutputarray still require exact content equality, so edited or tampered bodies are rejected. Tool-call expansion (body count > DB count) is rejected because the position map no longer aligns. - Path 3 mixed-id fix (issue #98 follow-up): The initial Path 3 guard wrongly required every body message to be idless, but
process_messages_with_outputonly strips theoutputkey — notid. Real reasoning-chat request bodies are therefore mixed-id: user / system / no-output assistant messages keep their DB nodeid, while only rebuilt assistant-with-output messages are idless. The all-idless guard rejected every real body before the position check could run. The guard has been removed — reaching Path 3 already requires_current_branch_refs(messages) is Noneupstream (the body as a whole exposes no usable ref sequence), so the check was both wrong and redundant. - Path 3 diagnostic logging: When Path 3 is eligible (equal length, unfoldable output) but a per-position
_body_position_matches_db_messagecheck fails, the filter now logs the first failing index and the specific field that mismatched (role / tool_calls / tool_call_id / content-on-no-output-message). This makes silent rejections observable indebug_modewithout code instrumentation. - Fail-closed boundaries preserved: Path 3 only activates when the existing folded and unfolded paths both fail. Tampered
tool_calls, edited content on DB messages withoutoutput, mismatchedtool_call_id, role drift, and count mismatches all continue to reject the snapshot.
Verification
- A new end-to-end test module inlines
convert_output_to_messages,process_messages_with_output, andreconcile_tool_pairscopied verbatim from the OpenWebUI main branch, then reconstructs the request body for OpenAI-compatible (reasoning_format=None), Ollama (think_tags), llama.cpp (reasoning_content), and tool-call reasoning chats. The body builder mirrors the real OpenWebUI pipeline exactly (no artificial id stripping), producing genuinely mixed-id bodies. Each scenario asserts behavior at the alignment-method level and at the fullinlet()entry point. - The position-based fallback is validated for both acceptance (reasoning content mismatch with DB output present; mixed-id body) and rejection (edited content with no DB output, tampered tool_calls, tool-call count expansion).
- Regression tests
test_openai_reasoning_body_is_mixed_idandtest_mixed_id_body_path3_acceptslock in the real mixed-id shape and Path 3 acceptance. - Full suite: 15 end-to-end tests plus the existing unit-test suite remain green (one environment-only failure due to
sqlalchemynot being installed in CI is unrelated to this change).
Upgrade Notes
No database migration is required. Update or reinstall the filter so OpenWebUI's stored function content includes the v1.7.3 metadata-dedup and reasoning-model inlet fixes. On first launch after upgrade, the database initializer will clean any colliding legacy indexes automatically.
If you tested an earlier build and saw DB active branch fallback skipped: request body is not a compatible idless view, this release resolves it. If Path 3 still rejects, enable debug_mode and look for Path 3 position fallback rejected at index=N: <reason> — please share that line in issue #98.
Version Changes
New Plugins
- Metadata ID Debug v0.2.0
- Minimal inlet/outlet filter that prints all ID-related fields from metadata to the BROWSER console (DevTools) via event_emitter execute events. Use to verify which IDs OpenWebUI actually sends to filters (chat_id, message_id, user_message_id, assistant_message_id, etc.).
- 📖 README
Plugin Updates
- Async Context Compression: v1.7.2 → v1.7.3 | 📖 README
📚 Documentation Portal
🐛 Report Issues
Full Changelog: release-2026.07.09...release-2026.07.13