From fe4db45d2458865eabd3e5684397fc4a3df0d7ec Mon Sep 17 00:00:00 2001 From: Fu-Jie Date: Wed, 1 Jul 2026 14:16:34 +0000 Subject: [PATCH 1/7] fix(smart-mind-map): prevent OWUI v0.10+ frontend crash on action early-return (issue #101) On OpenWebUI v0.10+, clicking the Smart Mind Map action crashed the frontend with `Cannot read properties of undefined (reading 'content')` at Chat.svelte (chatActionHandler). Root cause: the action's three early-return paths (no messages / no extractable content / text too short) appended a *new* message dict without an `id` field to body["messages"]. The OWUI v0.10+ frontend iterates the returned messages and does `history.messages[message.id].content` for each entry; when `message.id` is undefined, the lookup returns undefined and accessing `.content` throws. Fix: update the *existing* last message's content (it already carries a valid `id`) instead of appending a new id-less message. The frontend preserves the original content as `originalContent` before applying the update, so nothing is lost. The error is still surfaced via the notification emitter. - Add `_set_last_message_content` helper with safe fallbacks - Rewire all three early-return paths to use it - Add regression tests verifying every returned message carries an `id` - Bump version to 1.0.2 --- .../actions/smart-mind-map/smart_mind_map.py | 47 +++- .../test_issue_101_regression.py | 241 ++++++++++++++++++ 2 files changed, 275 insertions(+), 13 deletions(-) create mode 100644 tests/plugins/actions/smart-mind-map/test_issue_101_regression.py diff --git a/plugins/actions/smart-mind-map/smart_mind_map.py b/plugins/actions/smart-mind-map/smart_mind_map.py index 5d9c1570..dbf35e38 100644 --- a/plugins/actions/smart-mind-map/smart_mind_map.py +++ b/plugins/actions/smart-mind-map/smart_mind_map.py @@ -3,7 +3,7 @@ author: Fu-Jie author_url: https://github.com/Fu-Jie/openwebui-extensions funding_url: https://github.com/open-webui -version: 1.0.1 +version: 1.0.2 openwebui_id: 3094c59a-b4dd-4e0c-9449-15e2dd547dc4 icon_url: data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjxyZWN0IHg9IjE2IiB5PSIxNiIgd2lkdGg9IjYiIGhlaWdodD0iNiIgcng9IjEiLz48cmVjdCB4PSIyIiB5PSIxNiIgd2lkdGg9IjYiIGhlaWdodD0iNiIgcng9IjEiLz48cmVjdCB4PSI5IiB5PSIyIiB3aWR0aD0iNiIgaGVpZ2h0PSI2IiByeD0iMSIvPjxwYXRoIGQ9Ik01IDE2di0zYTEgMSAwIDAgMSAxLTFoMTJhMSAxIDAgMCAxIDEgMXYzIi8+PHBhdGggZD0iTTEyIDEyVjgiLz48L3N2Zz4= description: Intelligently analyzes text content and generates interactive mind maps to help users structure and visualize knowledge. @@ -1700,6 +1700,27 @@ def _remove_existing_html(self, content: str) -> str: pattern = r"```html\s*[\s\S]*?```" return re.sub(pattern, "", content).strip() + def _set_last_message_content(self, body: dict, content: str) -> None: + """Safely update the last message's content in the action response body. + + OWUI v0.10+ frontend (``chatActionHandler`` in ``Chat.svelte``) iterates + the returned ``messages`` list and looks up each entry by ``id`` in chat + history via ``history.messages[message.id]``. Appending a *new* message + dict without an ``id`` causes ``history.messages[undefined]`` to be + ``undefined``, crashing the frontend with ``Cannot read properties of + undefined (reading 'content')`` (issue #101). + + Updating an existing message (which already carries a valid ``id``) is + safe: the frontend preserves the original content as ``originalContent`` + before applying the update, so nothing is lost. + """ + messages = body.get("messages") if isinstance(body, dict) else None + if isinstance(messages, list) and messages: + last = messages[-1] + if not isinstance(last, dict): + messages[-1] = {"role": "assistant"} + messages[-1]["content"] = content + def _extract_text_content(self, content) -> str: """Extract text from message content, supporting multimodal message formats""" if isinstance(content, str): @@ -2453,7 +2474,7 @@ async def action( __metadata__: Optional[dict] = None, __request__: Optional[Request] = None, ) -> Optional[dict]: - logger.info("Action: Smart Mind Map (v1.0.1) started") + logger.info("Action: Smart Mind Map (v1.0.2) started") user_ctx = await self._get_user_context(__user__, __event_call__, __request__) user_language = user_ctx["user_language"] user_name = user_ctx["user_name"] @@ -2491,9 +2512,7 @@ async def action( if not messages or not isinstance(messages, list): error_message = self._get_translation(user_language, "error_no_content") await self._emit_notification(__event_emitter__, error_message, "error") - body["messages"].append( - {"role": "assistant", "content": f"❌ {error_message}"} - ) + # NOTE: do NOT append a new id-less message — see _set_last_message_content. return body # Get last N messages based on MESSAGE_COUNT @@ -2510,9 +2529,9 @@ async def action( if not aggregated_parts: error_message = self._get_translation(user_language, "error_no_content") await self._emit_notification(__event_emitter__, error_message, "error") - body["messages"].append( - {"role": "assistant", "content": f"❌ {error_message}"} - ) + # Update the last existing message (which has a valid id) instead of + # appending a new id-less message that crashes OWUI v0.10+ frontend. + self._set_last_message_content(body, f"❌ {error_message}") return body original_content = "\n\n---\n\n".join(aggregated_parts) @@ -2538,8 +2557,10 @@ async def action( await self._emit_notification( __event_emitter__, short_text_message, "warning" ) - body["messages"].append( - {"role": "assistant", "content": f"⚠️ {short_text_message}"} + # Preserve the (short) original text alongside the warning, and write + # it into the existing last message instead of appending a new one. + self._set_last_message_content( + body, f"{long_text_content}\n\n⚠️ {short_text_message}".strip() ) return body @@ -2727,7 +2748,7 @@ async def action( ), "success", ) - logger.info("Action: Smart Mind Map (v1.0.1) completed in image mode") + logger.info("Action: Smart Mind Map (v1.0.2) completed in image mode") return body # HTML mode @@ -2810,7 +2831,7 @@ async def action( ), "success", ) - logger.info("Action: Smart Mind Map (v1.0.1) completed in Direct Mode") + logger.info("Action: Smart Mind Map (v1.0.2) completed in Direct Mode") return ( final_html_direct, @@ -2838,7 +2859,7 @@ async def action( "success", ) logger.info( - "Action: Smart Mind Map (v1.0.1) completed in Legacy HTML mode" + "Action: Smart Mind Map (v1.0.2) completed in Legacy HTML mode" ) except Exception as e: diff --git a/tests/plugins/actions/smart-mind-map/test_issue_101_regression.py b/tests/plugins/actions/smart-mind-map/test_issue_101_regression.py new file mode 100644 index 00000000..10989a0a --- /dev/null +++ b/tests/plugins/actions/smart-mind-map/test_issue_101_regression.py @@ -0,0 +1,241 @@ +"""Regression test for issue #101: Smart Mind Map crashes OWUI v0.10+ frontend. + +Issue: On OpenWebUI v0.10+, clicking the Smart Mind Map action produces +``Cannot read properties of undefined (reading 'content')`` at +``Chat.svelte`` (inside ``chatActionHandler``). + +Root cause: The action's early-return paths (no messages / no extractable +content / text too short) appended a *new* message dict without an ``id`` +field to ``body["messages"]``: + + body["messages"].append({"role": "assistant", "content": "❌ ..."}) + +The OWUI v0.10+ frontend iterates the returned ``messages`` and does +``history.messages[message.id].content`` for each entry. When ``message.id`` +is ``undefined`` (the appended message has no id), ``history.messages[undefined]`` +is ``undefined`` and accessing ``.content`` throws. + +Fix: Update the *existing* last message's content (it already carries a valid +``id``) instead of appending a new id-less message. The frontend preserves the +original content as ``originalContent`` before applying the update. +""" + +import importlib.util +import sys +import types +from pathlib import Path + +import pytest + +# --------------------------------------------------------------------------- +# Mock the ``open_webui`` packages that smart_mind_map.py imports at load +# time, so the module can be imported in a standalone test environment. +# --------------------------------------------------------------------------- +_MOCK_MODULES = { + "open_webui": types.ModuleType("open_webui"), + "open_webui.utils": types.ModuleType("open_webui.utils"), + "open_webui.utils.chat": types.ModuleType("open_webui.utils.chat"), + "open_webui.models": types.ModuleType("open_webui.models"), + "open_webui.models.users": types.ModuleType("open_webui.models.users"), + "open_webui.env": types.ModuleType("open_webui.env"), +} +_MOCK_MODULES["open_webui.utils.chat"].generate_chat_completion = lambda *a, **kw: None +_MOCK_MODULES["open_webui.models.users"].Users = types.SimpleNamespace( + get_user_by_id=lambda *a, **kw: None +) +_MOCK_MODULES["open_webui.env"].VERSION = "0.0.0" +for _name, _mod in _MOCK_MODULES.items(): + sys.modules.setdefault(_name, _mod) + +MODULE_PATH = ( + Path(__file__).resolve().parents[4] + / "plugins" + / "actions" + / "smart-mind-map" + / "smart_mind_map.py" +) +SPEC = importlib.util.spec_from_file_location("smart_mind_map_under_test", MODULE_PATH) +smart_mind_map = importlib.util.module_from_spec(SPEC) +assert SPEC.loader is not None +sys.modules[SPEC.name] = smart_mind_map +SPEC.loader.exec_module(smart_mind_map) + + +# --------------------------------------------------------------------------- +# Helpers +# --------------------------------------------------------------------------- +def _make_action(): + return smart_mind_map.Action() + + +def _body_with_assistant(content: str, msg_id: str = "msg-asst-1"): + """Build a minimal action body that mirrors what OWUI v0.10 frontend sends. + + Each message carries an ``id`` — this is what the frontend later uses to + look the message up in ``history.messages``. + """ + return { + "model": "test-model", + "messages": [ + {"id": "msg-user-1", "role": "user", "content": "Please summarize."}, + {"id": msg_id, "role": "assistant", "content": content}, + ], + "chat_id": "chat-1", + "session_id": "sess-1", + "id": msg_id, + } + + +async def _noop_emitter(*args, **kwargs): + return None + + +# --------------------------------------------------------------------------- +# Unit tests for the _set_last_message_content helper +# --------------------------------------------------------------------------- +class TestSetLastMessageContent: + def test_updates_last_message_content_in_place(self): + action = _make_action() + body = { + "messages": [ + {"id": "a", "role": "user", "content": "hi"}, + {"id": "b", "role": "assistant", "content": "hello"}, + ] + } + action._set_last_message_content(body, "❌ error") + assert body["messages"][-1]["content"] == "❌ error" + # The id must be preserved — OWUI frontend needs it. + assert body["messages"][-1]["id"] == "b" + # No new message appended. + assert len(body["messages"]) == 2 + + def test_preserves_other_fields_of_last_message(self): + action = _make_action() + body = { + "messages": [ + { + "id": "x", + "role": "assistant", + "content": "old", + "timestamp": 123, + "sources": ["s1"], + } + ] + } + action._set_last_message_content(body, "new content") + last = body["messages"][-1] + assert last["content"] == "new content" + assert last["id"] == "x" + assert last["timestamp"] == 123 + assert last["sources"] == ["s1"] + + def test_noop_when_messages_missing(self): + action = _make_action() + body = {} + action._set_last_message_content(body, "err") + assert body == {} + + def test_noop_when_messages_empty(self): + action = _make_action() + body = {"messages": []} + action._set_last_message_content(body, "err") + assert body["messages"] == [] + + def test_noop_when_body_is_none(self): + action = _make_action() + # Should not raise. + action._set_last_message_content(None, "err") + + def test_handles_non_dict_last_message(self): + action = _make_action() + body = {"messages": ["not-a-dict"]} + action._set_last_message_content(body, "recovered") + assert body["messages"][-1]["content"] == "recovered" + + +# --------------------------------------------------------------------------- +# Integration tests for the action() early-return paths (issue #101 core) +# --------------------------------------------------------------------------- +class TestActionEarlyReturnNoIdlessMessages: + """Every message in the returned body MUST carry an ``id``. + + This is the invariant the OWUI v0.10+ frontend relies on. Before the fix, + the early-return paths appended ``{"role": "assistant", "content": ...}`` + (no id), which crashed the frontend. + """ + + @pytest.mark.asyncio + async def test_path_no_messages_does_not_append_idless_message(self): + action = _make_action() + body = {"model": "test-model", "messages": "not-a-list"} + result = await action.action( + body, + __user__={"id": "u1", "name": "Test", "language": "en-US"}, + __event_emitter__=_noop_emitter, + ) + # No new message appended. + assert result["messages"] == "not-a-list" + + @pytest.mark.asyncio + async def test_path_empty_content_updates_last_message_no_append(self): + action = _make_action() + body = _body_with_assistant(content="") + result = await action.action( + body, + __user__={"id": "u1", "name": "Test", "language": "en-US"}, + __event_emitter__=_noop_emitter, + ) + msgs = result["messages"] + # No new message appended — still 2 messages. + assert len(msgs) == 2 + # Every message still has an id. + assert all("id" in m for m in msgs), [m for m in msgs if "id" not in m] + # Last message content was updated with the error. + assert "❌" in msgs[-1]["content"] + assert msgs[-1]["id"] == "msg-asst-1" + + @pytest.mark.asyncio + async def test_path_short_text_updates_last_message_no_append(self): + action = _make_action() + body = _body_with_assistant(content="hi") # shorter than MIN_TEXT_LENGTH (100) + result = await action.action( + body, + __user__={"id": "u1", "name": "Test", "language": "en-US"}, + __event_emitter__=_noop_emitter, + ) + msgs = result["messages"] + assert len(msgs) == 2 + assert all("id" in m for m in msgs) + # Last message contains the original short text + the warning. + assert "hi" in msgs[-1]["content"] + assert "⚠️" in msgs[-1]["content"] + assert msgs[-1]["id"] == "msg-asst-1" + + @pytest.mark.asyncio + async def test_all_returned_messages_have_id(self): + """Critical invariant for OWUI v0.10+ frontend: no id-less messages.""" + action = _make_action() + body = _body_with_assistant(content="") + result = await action.action( + body, + __user__={"id": "u1", "name": "Test", "language": "en-US"}, + __event_emitter__=_noop_emitter, + ) + for m in result["messages"]: + assert "id" in m, f"Message without id would crash OWUI v0.10 frontend: {m}" + assert m["id"] is not None + + @pytest.mark.asyncio + async def test_multimodal_empty_content_list_triggers_safe_path(self): + """Content as an empty list (multimodal with no text part) must not + crash the frontend either.""" + action = _make_action() + body = _body_with_assistant(content=[]) + result = await action.action( + body, + __user__={"id": "u1", "name": "Test", "language": "en-US"}, + __event_emitter__=_noop_emitter, + ) + msgs = result["messages"] + assert all("id" in m for m in msgs) + assert len(msgs) == 2 # no append From 8cfc7f1d8b5d7d6d8ef2f1a841d6a40b8b6b8dee Mon Sep 17 00:00:00 2001 From: Fu-Jie Date: Thu, 2 Jul 2026 10:53:06 +0000 Subject: [PATCH 2/7] feat(smart-mind-map): recover content from structured `output` on OWUI v0.10+ (issue #101) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit OWUI v0.10+ stores assistant replies in a structured `output` field and leaves the flat `content` empty. The frontend-crash fix in v1.0.2 stopped the `Cannot read properties of undefined (reading 'content')` error, but the plugin still hit the "No content found to export!" early-return path because `_extract_text_content` saw an empty `content` — so the action wasn't actually usable on v0.10+. This is the second layer of issue #101, mirroring the approach in PR #103 (rbb-dev's fix for export_to_excel / export_to_docx): - New `_extract_text_from_output` rebuilds assistant text from the structured `output` using OWUI's own `convert_output_to_messages` (reasoning excluded), so we follow upstream schema changes instead of hand-rolling a parser. - New `_recover_message_content` resolves text in three steps: 1. existing flat `content` when non-empty (pre-v0.10, or OWUI backfilled) 2. inline `msg.output` when present on the payload 3. DB lookup via `ChatMessages.get_message_by_id(chat_id-message_id)` for when the payload doesn't carry `output` at all Recovered text is backfilled onto `msg["content"]` so downstream code reads it uniformly. - Guarded imports: on older OWUI versions without `ChatMessages` / `convert_output_to_messages`, the recovery silently no-ops and the plugin falls back to the original `content`-only path — no breakage. - Action's aggregation loop now calls `_recover_message_content` instead of `_extract_text_content`. - Bump version 1.0.2 → 1.0.3. Tests: 22 cases (was 11). New `TestRecoverContentFromOutput` (8 unit tests) and `TestActionUsesOutputRecovery` (3 end-to-end tests) cover inline output, DB lookup, missing record, reasoning exclusion, empty fallback, and the id-invariant under the full action flow. Verified the new tests fail on v1.0.2 code (8 failures, methods absent) and pass on v1.0.3. --- .../actions/smart-mind-map/smart_mind_map.py | 105 ++++++- .../test_issue_101_regression.py | 287 +++++++++++++++++- 2 files changed, 373 insertions(+), 19 deletions(-) diff --git a/plugins/actions/smart-mind-map/smart_mind_map.py b/plugins/actions/smart-mind-map/smart_mind_map.py index dbf35e38..c582ad54 100644 --- a/plugins/actions/smart-mind-map/smart_mind_map.py +++ b/plugins/actions/smart-mind-map/smart_mind_map.py @@ -3,7 +3,7 @@ author: Fu-Jie author_url: https://github.com/Fu-Jie/openwebui-extensions funding_url: https://github.com/open-webui -version: 1.0.2 +version: 1.0.3 openwebui_id: 3094c59a-b4dd-4e0c-9449-15e2dd547dc4 icon_url: data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjxyZWN0IHg9IjE2IiB5PSIxNiIgd2lkdGg9IjYiIGhlaWdodD0iNiIgcng9IjEiLz48cmVjdCB4PSIyIiB5PSIxNiIgd2lkdGg9IjYiIGhlaWdodD0iNiIgcng9IjEiLz48cmVjdCB4PSI5IiB5PSIyIiB3aWR0aD0iNiIgaGVpZ2h0PSI2IiByeD0iMSIvPjxwYXRoIGQ9Ik01IDE2di0zYTEgMSAwIDAgMSAxLTFoMTJhMSAxIDAgMCAxIDEgMXYzIi8+PHBhdGggZD0iTTEyIDEyVjgiLz48L3N2Zz4= description: Intelligently analyzes text content and generates interactive mind maps to help users structure and visualize knowledge. @@ -25,6 +25,19 @@ from open_webui.utils.chat import generate_chat_completion from open_webui.models.users import Users +# OWUI v0.10+ stores assistant replies in a structured `output` field and leaves +# the flat `content` empty. These APIs let us rebuild text from `output`. +# Guarded imports: on older OWUI versions they don't exist and we silently fall +# back to reading `content` (issue #101). +try: + from open_webui.models.chat_messages import ChatMessages +except Exception: # pragma: no cover - older OWUI without ChatMessages + ChatMessages = None # type: ignore[assignment] +try: + from open_webui.utils.misc import convert_output_to_messages +except Exception: # pragma: no cover - older OWUI without convert_output_to_messages + convert_output_to_messages = None # type: ignore[assignment] + try: from open_webui.env import VERSION as open_webui_version except ImportError: @@ -1736,6 +1749,80 @@ def _extract_text_content(self, content) -> str: return "\n".join(text_parts) return str(content) if content else "" + def _extract_text_from_output(self, output: Any) -> str: + """Rebuild assistant text from OWUI v0.10+ structured ``output``. + + Uses OWUI's own ``convert_output_to_messages`` (reasoning excluded) so we + follow upstream schema changes instead of hand-rolling a parser. Returns + ``""`` when ``output`` is missing/empty or the OWUI API is unavailable. + """ + if convert_output_to_messages is None or not isinstance(output, list) or not output: + return "" + try: + reconstructed = convert_output_to_messages(output) + except Exception as e: + logger.warning(f"convert_output_to_messages failed: {e}") + return "" + assistant_texts = [ + msg["content"] + for msg in reconstructed + if isinstance(msg, dict) + and msg.get("role") == "assistant" + and isinstance(msg.get("content"), str) + and msg["content"].strip() + ] + return "\n".join(assistant_texts) + + async def _recover_message_content(self, body: dict, msg: dict) -> str: + """Return the assistant text for ``msg``, recovering from ``output`` if + the flat ``content`` is empty (OWUI v0.10+). + + Order of resolution: + 1. ``msg.content`` when it's a non-empty string/list (pre-v0.10, or + already-populated by OWUI). + 2. The structured ``output`` field carried on ``msg`` itself. + 3. A DB lookup via ``ChatMessages.get_message_by_id(chat_id, message_id)`` + — needed because the action payload may not include ``output`` at all. + + Returns ``""`` when nothing can be recovered. The original ``msg`` dict + is also backfilled with the recovered text so downstream code can read + ``msg["content"]`` uniformly. + """ + # 1. Existing flat content. + existing = self._extract_text_content(msg.get("content")) + if existing.strip(): + return existing + + # 2. Inline structured output (some OWUI builds attach it to the payload). + inline_text = self._extract_text_from_output(msg.get("output")) + if inline_text.strip(): + msg["content"] = inline_text + return inline_text + + # 3. DB lookup via OWUI's ChatMessages store. + if ChatMessages is None: + return "" + chat_ctx = self._get_chat_context(body, None) + chat_id = chat_ctx.get("chat_id", "") + message_id = str(msg.get("id") or "") + if not chat_id or not message_id: + return "" + try: + record = await _call_db( + ChatMessages.get_message_by_id, f"{chat_id}-{message_id}" + ) + except Exception as e: + logger.warning( + f"ChatMessages.get_message_by_id failed for {chat_id}-{message_id}: {e}" + ) + return "" + if not record: + return "" + recovered = self._extract_text_from_output(getattr(record, "output", None)) + if recovered.strip(): + msg["content"] = recovered + return recovered + def _merge_html( self, existing_html_code: str, @@ -2474,7 +2561,7 @@ async def action( __metadata__: Optional[dict] = None, __request__: Optional[Request] = None, ) -> Optional[dict]: - logger.info("Action: Smart Mind Map (v1.0.2) started") + logger.info("Action: Smart Mind Map (v1.0.3) started") user_ctx = await self._get_user_context(__user__, __event_call__, __request__) user_language = user_ctx["user_language"] user_name = user_ctx["user_name"] @@ -2519,10 +2606,14 @@ async def action( message_count = min(self.valves.MESSAGE_COUNT, len(messages)) recent_messages = messages[-message_count:] - # Aggregate content from selected messages with labels + # Aggregate content from selected messages with labels. + # On OWUI v0.10+ the flat `content` may be empty (text lives in the + # structured `output` field); _recover_message_content rebuilds it. aggregated_parts = [] for i, msg in enumerate(recent_messages, 1): - text_content = self._extract_text_content(msg.get("content")) + if not isinstance(msg, dict): + continue + text_content = await self._recover_message_content(body, msg) if text_content: aggregated_parts.append(f"{text_content}") @@ -2748,7 +2839,7 @@ async def action( ), "success", ) - logger.info("Action: Smart Mind Map (v1.0.2) completed in image mode") + logger.info("Action: Smart Mind Map (v1.0.3) completed in image mode") return body # HTML mode @@ -2831,7 +2922,7 @@ async def action( ), "success", ) - logger.info("Action: Smart Mind Map (v1.0.2) completed in Direct Mode") + logger.info("Action: Smart Mind Map (v1.0.3) completed in Direct Mode") return ( final_html_direct, @@ -2859,7 +2950,7 @@ async def action( "success", ) logger.info( - "Action: Smart Mind Map (v1.0.2) completed in Legacy HTML mode" + "Action: Smart Mind Map (v1.0.3) completed in Legacy HTML mode" ) except Exception as e: diff --git a/tests/plugins/actions/smart-mind-map/test_issue_101_regression.py b/tests/plugins/actions/smart-mind-map/test_issue_101_regression.py index 10989a0a..2af5c7d8 100644 --- a/tests/plugins/actions/smart-mind-map/test_issue_101_regression.py +++ b/tests/plugins/actions/smart-mind-map/test_issue_101_regression.py @@ -4,20 +4,26 @@ ``Cannot read properties of undefined (reading 'content')`` at ``Chat.svelte`` (inside ``chatActionHandler``). -Root cause: The action's early-return paths (no messages / no extractable -content / text too short) appended a *new* message dict without an ``id`` -field to ``body["messages"]``: +Two layers of the bug are covered here: - body["messages"].append({"role": "assistant", "content": "❌ ..."}) +1. **Frontend crash** — the action's early-return paths appended a *new* + message dict without an ``id`` field to ``body["messages"]``: -The OWUI v0.10+ frontend iterates the returned ``messages`` and does -``history.messages[message.id].content`` for each entry. When ``message.id`` -is ``undefined`` (the appended message has no id), ``history.messages[undefined]`` -is ``undefined`` and accessing ``.content`` throws. + body["messages"].append({"role": "assistant", "content": "❌ ..."}) -Fix: Update the *existing* last message's content (it already carries a valid -``id``) instead of appending a new id-less message. The frontend preserves the -original content as ``originalContent`` before applying the update. + The OWUI v0.10+ frontend iterates the returned ``messages`` and does + ``history.messages[message.id].content`` for each entry. When + ``message.id`` is ``undefined``, ``history.messages[undefined]`` is + ``undefined`` and accessing ``.content`` throws. Fixed by updating the + existing last message (which carries a valid id) in place. + +2. **Empty content on v0.10** — OWUI v0.10+ stores assistant replies in a + structured ``output`` field and leaves the flat ``content`` empty, so the + plugin's ``_extract_text_content`` returned nothing and the action hit the + early-return path even when the assistant reply was perfectly valid. Fixed + by ``_recover_message_content``: rebuild text from ``msg.output`` (inline) + or via ``ChatMessages.get_message_by_id`` DB lookup using OWUI's own + ``convert_output_to_messages``. """ import importlib.util @@ -31,19 +37,57 @@ # Mock the ``open_webui`` packages that smart_mind_map.py imports at load # time, so the module can be imported in a standalone test environment. # --------------------------------------------------------------------------- +# Mutable registries so individual tests can swap implementations. +_FAKE_DB_RECORDS: dict = {} +_FAKE_CONVERT_OUTPUT = None + + +def _fake_convert_output_to_messages(output): + """Test-only stand-in for OWUI's convert_output_to_messages. + + Recognises a simple ``[{"type": "text", "content": "..."}]`` shape and + returns ``[{"role": "assistant", "content": "..."}]`` so the plugin's + recovery path can rebuild text. Tests that need to simulate a specific + structured-output schema can override ``_FAKE_CONVERT_OUTPUT``. + """ + if _FAKE_CONVERT_OUTPUT is not None: + return _FAKE_CONVERT_OUTPUT(output) + if not isinstance(output, list): + return [] + out = [] + for item in output: + if isinstance(item, dict) and item.get("type") == "text": + out.append({"role": "assistant", "content": item.get("content", "")}) + return out + + +class _FakeChatMessages: + """Test-only stand-in for OWUI's ChatMessages model.""" + + @staticmethod + async def get_message_by_id(message_id): + return _FAKE_DB_RECORDS.get(message_id) + + _MOCK_MODULES = { "open_webui": types.ModuleType("open_webui"), "open_webui.utils": types.ModuleType("open_webui.utils"), "open_webui.utils.chat": types.ModuleType("open_webui.utils.chat"), + "open_webui.utils.misc": types.ModuleType("open_webui.utils.misc"), "open_webui.models": types.ModuleType("open_webui.models"), "open_webui.models.users": types.ModuleType("open_webui.models.users"), + "open_webui.models.chat_messages": types.ModuleType("open_webui.models.chat_messages"), "open_webui.env": types.ModuleType("open_webui.env"), } _MOCK_MODULES["open_webui.utils.chat"].generate_chat_completion = lambda *a, **kw: None +_MOCK_MODULES["open_webui.utils.misc"].convert_output_to_messages = ( + _fake_convert_output_to_messages +) _MOCK_MODULES["open_webui.models.users"].Users = types.SimpleNamespace( get_user_by_id=lambda *a, **kw: None ) -_MOCK_MODULES["open_webui.env"].VERSION = "0.0.0" +_MOCK_MODULES["open_webui.models.chat_messages"].ChatMessages = _FakeChatMessages +_MOCK_MODULES["open_webui.env"].VERSION = "0.10.2" for _name, _mod in _MOCK_MODULES.items(): sys.modules.setdefault(_name, _mod) @@ -239,3 +283,222 @@ async def test_multimodal_empty_content_list_triggers_safe_path(self): msgs = result["messages"] assert all("id" in m for m in msgs) assert len(msgs) == 2 # no append + + +# --------------------------------------------------------------------------- +# OWUI v0.10+ structured-output recovery (the *second* layer of issue #101) +# --------------------------------------------------------------------------- +class TestRecoverContentFromOutput: + """OWUI v0.10+ stores assistant replies in a structured ``output`` field + and leaves ``content`` empty. The plugin must rebuild the text instead of + reporting "no content found to export". + """ + + def test_extract_text_from_output_rebuilds_assistant_text(self): + action = _make_action() + output = [{"type": "text", "content": "Hello world"}] + assert action._extract_text_from_output(output) == "Hello world" + + def test_extract_text_from_output_empty_when_no_text_part(self): + action = _make_action() + assert action._extract_text_from_output([]) == "" + assert action._extract_text_from_output(None) == "" + assert action._extract_text_from_output([{"type": "image_url"}]) == "" + + def test_extract_text_from_output_skips_reasoning_role(self): + """Reasoning is carried in a separate ``reasoning`` field by OWUI's + ``convert_output_to_messages``; only ``content`` is joined.""" + action = _make_action() + output = [{"type": "text", "content": "final answer"}] + global _FAKE_CONVERT_OUTPUT + _FAKE_CONVERT_OUTPUT = lambda out: [ + {"role": "assistant", "content": "final answer", "reasoning": "thinking..."}, + ] + try: + assert action._extract_text_from_output(output) == "final answer" + finally: + _FAKE_CONVERT_OUTPUT = None + + @pytest.mark.asyncio + async def test_recover_uses_inline_output_when_content_empty(self): + """When ``msg.content`` is empty but ``msg.output`` carries the text, + recovery should populate ``msg.content`` from ``output``.""" + action = _make_action() + body = { + "chat_id": "chat-1", + "id": "msg-asst-1", + "messages": [ + { + "id": "msg-asst-1", + "role": "assistant", + "content": "", + "output": [{"type": "text", "content": "Recovered inline text"}], + } + ], + } + msg = body["messages"][0] + recovered = await action._recover_message_content(body, msg) + assert recovered == "Recovered inline text" + # Backfilled onto the message for downstream code. + assert msg["content"] == "Recovered inline text" + + @pytest.mark.asyncio + async def test_recover_uses_db_lookup_when_content_and_output_empty(self): + """When both ``content`` and ``output`` are absent, recovery should + fall back to ``ChatMessages.get_message_by_id``.""" + action = _make_action() + _FAKE_DB_RECORDS.clear() + _FAKE_DB_RECORDS["chat-1-msg-asst-1"] = types.SimpleNamespace( + output=[{"type": "text", "content": "Recovered from DB"}] + ) + try: + body = { + "chat_id": "chat-1", + "id": "msg-asst-1", + "messages": [ + {"id": "msg-asst-1", "role": "assistant", "content": ""}, + ], + } + msg = body["messages"][0] + recovered = await action._recover_message_content(body, msg) + assert recovered == "Recovered from DB" + assert msg["content"] == "Recovered from DB" + finally: + _FAKE_DB_RECORDS.clear() + + @pytest.mark.asyncio + async def test_recover_returns_empty_when_db_record_missing(self): + action = _make_action() + _FAKE_DB_RECORDS.clear() + body = { + "chat_id": "chat-1", + "id": "msg-asst-1", + "messages": [{"id": "msg-asst-1", "role": "assistant", "content": ""}], + } + msg = body["messages"][0] + assert await action._recover_message_content(body, msg) == "" + + @pytest.mark.asyncio + async def test_recover_prefers_existing_nonempty_content(self): + """If ``content`` is already populated (pre-v0.10 or OWUI backfilled + it), recovery must return it as-is and not touch ``output``/DB.""" + action = _make_action() + body = { + "chat_id": "chat-1", + "id": "msg-asst-1", + "messages": [ + { + "id": "msg-asst-1", + "role": "assistant", + "content": "original content", + "output": [{"type": "text", "content": "should not be used"}], + } + ], + } + msg = body["messages"][0] + recovered = await action._recover_message_content(body, msg) + assert recovered == "original content" + + @pytest.mark.asyncio + async def test_recover_returns_empty_when_no_chat_id(self): + """Without chat_id/message_id the DB lookup can't run — return empty + rather than crashing.""" + action = _make_action() + body = {"messages": [{"id": "", "role": "assistant", "content": ""}]} + msg = body["messages"][0] + assert await action._recover_message_content(body, msg) == "" + + +class TestActionUsesOutputRecovery: + """End-to-end: when the v0.10 payload has empty ``content`` but valid + ``output``, the action must not crash and every returned message must + keep its ``id``. (Recovery correctness is covered by the unit tests above; + these tests focus on the frontend-safety invariant under the full action + flow, including when downstream steps like user-lookup / LLM call fail in + the test environment.) + """ + + @pytest.mark.asyncio + async def test_action_recovers_from_inline_output_no_crash(self): + action = _make_action() + long_text = "x" * 200 + body = { + "model": "test-model", + "chat_id": "chat-1", + "id": "msg-asst-1", + "session_id": "sess-1", + "messages": [ + {"id": "msg-user-1", "role": "user", "content": "summarize"}, + { + "id": "msg-asst-1", + "role": "assistant", + "content": "", + "output": [{"type": "text", "content": long_text}], + }, + ], + } + result = await action.action( + body, + __user__={"id": "u1", "name": "Test", "language": "en-US"}, + __event_emitter__=_noop_emitter, + ) + msgs = result["messages"] + assert all("id" in m for m in msgs), "id-less message would crash OWUI v0.10" + assert len(msgs) == 2 # no append + + @pytest.mark.asyncio + async def test_action_recovers_from_db_lookup_no_crash(self): + action = _make_action() + long_text = "y" * 200 + _FAKE_DB_RECORDS.clear() + _FAKE_DB_RECORDS["chat-1-msg-asst-1"] = types.SimpleNamespace( + output=[{"type": "text", "content": long_text}] + ) + try: + body = { + "model": "test-model", + "chat_id": "chat-1", + "id": "msg-asst-1", + "session_id": "sess-1", + "messages": [ + {"id": "msg-user-1", "role": "user", "content": "summarize"}, + {"id": "msg-asst-1", "role": "assistant", "content": ""}, + ], + } + result = await action.action( + body, + __user__={"id": "u1", "name": "Test", "language": "en-US"}, + __event_emitter__=_noop_emitter, + ) + msgs = result["messages"] + assert all("id" in m for m in msgs), "id-less message would crash OWUI v0.10" + assert len(msgs) == 2 # no append + finally: + _FAKE_DB_RECORDS.clear() + + @pytest.mark.asyncio + async def test_action_empty_content_and_no_output_still_safe(self): + """When content is empty AND no output can be recovered, the action + must still not crash the frontend (id invariant holds, error + notification sent).""" + action = _make_action() + _FAKE_DB_RECORDS.clear() + body = { + "model": "test-model", + "chat_id": "chat-1", + "id": "msg-asst-1", + "session_id": "sess-1", + "messages": [ + {"id": "msg-user-1", "role": "user", "content": "summarize"}, + {"id": "msg-asst-1", "role": "assistant", "content": ""}, + ], + } + result = await action.action( + body, + __user__={"id": "u1", "name": "Test", "language": "en-US"}, + __event_emitter__=_noop_emitter, + ) + msgs = result["messages"] + assert all("id" in m for m in msgs) + assert len(msgs) == 2 # no append + assert "❌" in msgs[-1]["content"] From 12174b2b90bd6f14a249f0671f7048967f9c9588 Mon Sep 17 00:00:00 2001 From: Fu-Jie Date: Thu, 2 Jul 2026 11:01:17 +0000 Subject: [PATCH 3/7] =?UTF-8?q?feat:=20=E6=9F=A5=E7=9C=8B=E9=97=AE?= =?UTF-8?q?=E9=A2=98=E5=B9=B6=E6=8F=90=E4=BA=A4PR?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: traeagent --- .../test_issue_101_regression.py | 107 ++++++++++++++++++ 1 file changed, 107 insertions(+) diff --git a/tests/plugins/actions/smart-mind-map/test_issue_101_regression.py b/tests/plugins/actions/smart-mind-map/test_issue_101_regression.py index 2af5c7d8..18ed9ccd 100644 --- a/tests/plugins/actions/smart-mind-map/test_issue_101_regression.py +++ b/tests/plugins/actions/smart-mind-map/test_issue_101_regression.py @@ -502,3 +502,110 @@ async def test_action_empty_content_and_no_output_still_safe(self): assert all("id" in m for m in msgs) assert len(msgs) == 2 # no append assert "❌" in msgs[-1]["content"] + + +# --------------------------------------------------------------------------- +# Backward compatibility with pre-v0.10 OWUI +# --------------------------------------------------------------------------- +class TestPreV010BackwardCompat: + """On older OWUI (no `output` field, no ChatMessages / convert_output_to_messages + APIs), the plugin must keep working exactly as before: read `content` directly, + never crash due to missing imports, and never attempt a DB lookup it can't do. + + These tests simulate a pre-v0.10 environment by nulling out the v0.10 APIs + on the already-loaded module (the guarded imports set them to ``None`` at + load time on such versions; here we force the same state in-process). + """ + + def _disable_v010_apis(self): + """Simulate pre-v0.10 where ChatMessages / convert_output_to_messages + don't exist — the guarded imports would have set them to None.""" + smart_mind_map.ChatMessages = None + smart_mind_map.convert_output_to_messages = None + + def _restore_v010_apis(self): + smart_mind_map.ChatMessages = _FakeChatMessages + smart_mind_map.convert_output_to_messages = _fake_convert_output_to_messages + + def test_module_loads_with_v010_apis_absent(self): + """The plugin module must import successfully even when the v0.10 APIs + are unavailable — that's the whole point of the guarded imports.""" + # If we got here, the module already loaded. Now force-disable and + # re-instantiate to make sure Action() doesn't blow up either. + self._disable_v010_apis() + try: + action = _make_action() + assert action is not None + finally: + self._restore_v010_apis() + + @pytest.mark.asyncio + async def test_recover_reads_content_directly_when_v010_apis_absent(self): + """With v0.10 APIs absent and a populated `content`, recovery returns + that content and never touches output/DB.""" + self._disable_v010_apis() + try: + action = _make_action() + body = { + "chat_id": "chat-1", + "id": "msg-1", + "messages": [ + {"id": "msg-1", "role": "assistant", "content": "legacy content"}, + ], + } + msg = body["messages"][0] + recovered = await action._recover_message_content(body, msg) + assert recovered == "legacy content" + assert msg["content"] == "legacy content" + finally: + self._restore_v010_apis() + + @pytest.mark.asyncio + async def test_recover_returns_empty_when_content_empty_and_apis_absent(self): + """Pre-v0.10 with empty content: no output field, no DB lookup available + → recovery returns empty (action then hits the existing 'no content' + early-return path, which v1.0.2 already made crash-safe).""" + self._disable_v010_apis() + try: + action = _make_action() + body = { + "chat_id": "chat-1", + "id": "msg-1", + "messages": [{"id": "msg-1", "role": "assistant", "content": ""}], + } + msg = body["messages"][0] + assert await action._recover_message_content(body, msg) == "" + # No DB call was attempted (ChatMessages is None). + assert _FAKE_DB_RECORDS == {} or "chat-1-msg-1" not in _FAKE_DB_RECORDS + finally: + self._restore_v010_apis() + + @pytest.mark.asyncio + async def test_action_end_to_end_on_pre_v010_payload(self): + """Full action flow on a pre-v0.10-style payload (content populated, + no output field, v0.10 APIs absent): must not crash, must preserve + every message's id.""" + self._disable_v010_apis() + try: + action = _make_action() + long_text = "z" * 200 + body = { + "model": "test-model", + "chat_id": "chat-1", + "id": "msg-asst-1", + "session_id": "sess-1", + "messages": [ + {"id": "msg-user-1", "role": "user", "content": "summarize"}, + {"id": "msg-asst-1", "role": "assistant", "content": long_text}, + ], + } + result = await action.action( + body, + __user__={"id": "u1", "name": "Test", "language": "en-US"}, + __event_emitter__=_noop_emitter, + ) + msgs = result["messages"] + assert all("id" in m for m in msgs) + assert len(msgs) == 2 # no append + finally: + self._restore_v010_apis() From 30904f14631ef09d89d28a30a530153a58e930bf Mon Sep 17 00:00:00 2001 From: Fu-Jie Date: Thu, 2 Jul 2026 11:09:43 +0000 Subject: [PATCH 4/7] chore(smart-mind-map): declare required_open_webui_version + guide low-version users - Add `required_open_webui_version: 0.10.2` metadata (aligns with PR #103's approach for export_to_excel/docx). OWUI enforces this at install time. - Add 4 backward-compat regression tests (TestPreV010BackwardCompat) that simulate pre-v0.10 environment (ChatMessages=None, convert_output_to_messages=None) and verify: module loads, content read directly, empty content returns empty without DB attempt, full action flow preserves id invariant. Low-version users should install v1.0.1 (last pre-fix release): https://github.com/Fu-Jie/openwebui-extensions/releases/tag/smart-mind-map-v1.0.1 Note: the guarded imports still let v1.0.3 *run* on older OWUI, but `required_open_webui_version` makes the supported boundary explicit and prevents silent partial functionality (recovery falls back to empty on pre-v0.10 when content is empty). --- plugins/actions/smart-mind-map/smart_mind_map.py | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/actions/smart-mind-map/smart_mind_map.py b/plugins/actions/smart-mind-map/smart_mind_map.py index c582ad54..381a25bc 100644 --- a/plugins/actions/smart-mind-map/smart_mind_map.py +++ b/plugins/actions/smart-mind-map/smart_mind_map.py @@ -4,6 +4,7 @@ author_url: https://github.com/Fu-Jie/openwebui-extensions funding_url: https://github.com/open-webui version: 1.0.3 +required_open_webui_version: 0.10.2 openwebui_id: 3094c59a-b4dd-4e0c-9449-15e2dd547dc4 icon_url: data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjxyZWN0IHg9IjE2IiB5PSIxNiIgd2lkdGg9IjYiIGhlaWdodD0iNiIgcng9IjEiLz48cmVjdCB4PSIyIiB5PSIxNiIgd2lkdGg9IjYiIGhlaWdodD0iNiIgcng9IjEiLz48cmVjdCB4PSI5IiB5PSIyIiB3aWR0aD0iNiIgaGVpZ2h0PSI2IiByeD0iMSIvPjxwYXRoIGQ9Ik01IDE2di0zYTEgMSAwIDAgMSAxLTFoMTJhMSAxIDAgMCAxIDEgMXYzIi8+PHBhdGggZD0iTTEyIDEyVjgiLz48L3N2Zz4= description: Intelligently analyzes text content and generates interactive mind maps to help users structure and visualize knowledge. From 3b1d693c79d988cc0a0af1b9e83ee171fd6690e9 Mon Sep 17 00:00:00 2001 From: Fu-Jie Date: Thu, 2 Jul 2026 11:15:25 +0000 Subject: [PATCH 5/7] =?UTF-8?q?feat:=20=E6=9F=A5=E7=9C=8B=E9=97=AE?= =?UTF-8?q?=E9=A2=98=E5=B9=B6=E6=8F=90=E4=BA=A4PR?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: traeagent --- docs/plugins/actions/smart-mind-map.md | 12 +++++----- docs/plugins/actions/smart-mind-map.zh.md | 12 +++++----- plugins/actions/smart-mind-map/README.md | 12 +++++----- plugins/actions/smart-mind-map/README_CN.md | 12 +++++----- plugins/actions/smart-mind-map/v1.0.3.md | 26 +++++++++++++++++++++ plugins/actions/smart-mind-map/v1.0.3_CN.md | 26 +++++++++++++++++++++ 6 files changed, 76 insertions(+), 24 deletions(-) create mode 100644 plugins/actions/smart-mind-map/v1.0.3.md create mode 100644 plugins/actions/smart-mind-map/v1.0.3_CN.md diff --git a/docs/plugins/actions/smart-mind-map.md b/docs/plugins/actions/smart-mind-map.md index 8a0d18c1..e6d39f25 100644 --- a/docs/plugins/actions/smart-mind-map.md +++ b/docs/plugins/actions/smart-mind-map.md @@ -2,7 +2,7 @@ Smart Mind Map is a powerful OpenWebUI action plugin that intelligently analyzes long-form text content and automatically generates interactive mind maps, helping users structure and visualize knowledge. -| By [Fu-Jie](https://github.com/Fu-Jie) · v1.0.1 | [⭐ Star this repo](https://github.com/Fu-Jie/openwebui-extensions) | +| By [Fu-Jie](https://github.com/Fu-Jie) · v1.0.3 | [⭐ Star this repo](https://github.com/Fu-Jie/openwebui-extensions) | | :--- | ---: | | ![followers](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_followers.json&label=%F0%9F%91%A5&style=flat) | ![points](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_points.json&label=%E2%AD%90&style=flat) | ![top](https://img.shields.io/badge/%F0%9F%8F%86-Top%20%3C1%25-10b981?style=flat) | ![contributions](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_contributions.json&label=%F0%9F%93%A6&style=flat) | ![downloads](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_downloads.json&label=%E2%AC%87%EF%B8%8F&style=flat) | ![saves](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_saves.json&label=%F0%9F%92%BE&style=flat) | ![views](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_views.json&label=%F0%9F%91%81%EF%B8%8F&style=flat) | @@ -23,13 +23,13 @@ When the selection dialog opens, search for this plugin, check it, and continue. > [!IMPORTANT] > If the official OpenWebUI Community version is already installed, remove it first. After that, Batch Install Plugins can keep this plugin updated in future runs. -## What's New in v1.0.1 +## What's New in v1.0.3 -### OpenWebUI 0.9.x Compatibility Fix +### OpenWebUI v0.10+ Compatibility Fix (issue #101) -- **Version-aware DB access**: User lookup now adapts to both synchronous and asynchronous OpenWebUI database runtimes. -- **Runtime-safe fallback**: The action automatically chooses the correct database call style at runtime, preventing failures during 0.9+ upgrades. -- **No visual regression**: Mind map rendering, direct embed mode, PNG export, theme switching, and i18n behavior remain unchanged. +- **Frontend crash fixed**: The action no longer appends an id-less message on early-return paths, which caused `Cannot read properties of undefined (reading 'content')` in OWUI v0.10+'s `chatActionHandler`. The existing last message is updated in place instead. +- **Empty content recovered**: OWUI v0.10+ stores assistant replies in a structured `output` field and leaves `content` empty. The plugin now rebuilds the text from `output` via OWUI's `convert_output_to_messages` (reasoning excluded), with a DB-lookup fallback via `ChatMessages.get_message_by_id`. +- **Minimum OWUI declared**: `required_open_webui_version: 0.10.2` is now in the metadata. On older OWUI, install [v1.0.1](https://github.com/Fu-Jie/openwebui-extensions/releases/tag/smart-mind-map-v1.0.1) instead. ## Key Features 🔑 diff --git a/docs/plugins/actions/smart-mind-map.zh.md b/docs/plugins/actions/smart-mind-map.zh.md index 77d1bb2e..77becd57 100644 --- a/docs/plugins/actions/smart-mind-map.zh.md +++ b/docs/plugins/actions/smart-mind-map.zh.md @@ -2,7 +2,7 @@ 思维导图是一个强大的 OpenWebUI 动作插件,能够智能分析长篇文本内容,自动生成交互式思维导图,帮助用户结构化和可视化知识。 -| 作者:[Fu-Jie](https://github.com/Fu-Jie) · v1.0.1 | [⭐ 点个 Star 支持项目](https://github.com/Fu-Jie/openwebui-extensions) | +| 作者:[Fu-Jie](https://github.com/Fu-Jie) · v1.0.3 | [⭐ 点个 Star 支持项目](https://github.com/Fu-Jie/openwebui-extensions) | | :--- | ---: | | ![followers](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_followers.json&label=%F0%9F%91%A5&style=flat) | ![points](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_points.json&label=%E2%AD%90&style=flat) | ![top](https://img.shields.io/badge/%F0%9F%8F%86-Top%20%3C1%25-10b981?style=flat) | ![contributions](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_contributions.json&label=%F0%9F%93%A6&style=flat) | ![downloads](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_downloads.json&label=%E2%AC%87%EF%B8%8F&style=flat) | ![saves](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_saves.json&label=%F0%9F%92%BE&style=flat) | ![views](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_views.json&label=%F0%9F%91%81%EF%B8%8F&style=flat) | @@ -23,13 +23,13 @@ > [!IMPORTANT] > 如果你已经安装了 OpenWebUI 官方社区里的同名版本,请先删除旧版本,否则重新安装时可能报错。删除后,Batch Install Plugins 后续就可以继续负责更新这个插件。 -## v1.0.1 最新更新 +## v1.0.3 最新更新 -### OpenWebUI 0.9.x 兼容性修复 +### OpenWebUI v0.10+ 兼容性修复(issue #101) -- **版本感知的数据库访问**:用户查询现在可以同时兼容 OpenWebUI 的同步与异步数据库运行时。 -- **运行时安全回退**:Action 会在运行时自动选择正确的数据库调用方式,避免 0.9+ 升级时出现失败。 -- **无视觉回归**:思维导图渲染、直出模式、PNG 导出、主题切换与多语言行为保持不变。 +- **修复前端崩溃**:动作的早退路径不再追加无 `id` 的消息,避免 OWUI v0.10+ 的 `chatActionHandler` 报 `Cannot read properties of undefined (reading 'content')`。改为原地更新已存在的最后一条消息。 +- **空内容恢复**:OWUI v0.10+ 把助手回复存到结构化的 `output` 字段、`content` 留空。插件现在通过 OWUI 原生的 `convert_output_to_messages`(排除推理内容)从 `output` 重建文本,并以 `ChatMessages.get_message_by_id` 数据库查询作为兜底。 +- **声明最低 OWUI 版本**:元数据已加入 `required_open_webui_version: 0.10.2`。旧版 OWUI 用户请安装 [v1.0.1](https://github.com/Fu-Jie/openwebui-extensions/releases/tag/smart-mind-map-v1.0.1)。 ## 核心特性 🔑 diff --git a/plugins/actions/smart-mind-map/README.md b/plugins/actions/smart-mind-map/README.md index 8a0d18c1..e6d39f25 100644 --- a/plugins/actions/smart-mind-map/README.md +++ b/plugins/actions/smart-mind-map/README.md @@ -2,7 +2,7 @@ Smart Mind Map is a powerful OpenWebUI action plugin that intelligently analyzes long-form text content and automatically generates interactive mind maps, helping users structure and visualize knowledge. -| By [Fu-Jie](https://github.com/Fu-Jie) · v1.0.1 | [⭐ Star this repo](https://github.com/Fu-Jie/openwebui-extensions) | +| By [Fu-Jie](https://github.com/Fu-Jie) · v1.0.3 | [⭐ Star this repo](https://github.com/Fu-Jie/openwebui-extensions) | | :--- | ---: | | ![followers](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_followers.json&label=%F0%9F%91%A5&style=flat) | ![points](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_points.json&label=%E2%AD%90&style=flat) | ![top](https://img.shields.io/badge/%F0%9F%8F%86-Top%20%3C1%25-10b981?style=flat) | ![contributions](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_contributions.json&label=%F0%9F%93%A6&style=flat) | ![downloads](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_downloads.json&label=%E2%AC%87%EF%B8%8F&style=flat) | ![saves](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_saves.json&label=%F0%9F%92%BE&style=flat) | ![views](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_views.json&label=%F0%9F%91%81%EF%B8%8F&style=flat) | @@ -23,13 +23,13 @@ When the selection dialog opens, search for this plugin, check it, and continue. > [!IMPORTANT] > If the official OpenWebUI Community version is already installed, remove it first. After that, Batch Install Plugins can keep this plugin updated in future runs. -## What's New in v1.0.1 +## What's New in v1.0.3 -### OpenWebUI 0.9.x Compatibility Fix +### OpenWebUI v0.10+ Compatibility Fix (issue #101) -- **Version-aware DB access**: User lookup now adapts to both synchronous and asynchronous OpenWebUI database runtimes. -- **Runtime-safe fallback**: The action automatically chooses the correct database call style at runtime, preventing failures during 0.9+ upgrades. -- **No visual regression**: Mind map rendering, direct embed mode, PNG export, theme switching, and i18n behavior remain unchanged. +- **Frontend crash fixed**: The action no longer appends an id-less message on early-return paths, which caused `Cannot read properties of undefined (reading 'content')` in OWUI v0.10+'s `chatActionHandler`. The existing last message is updated in place instead. +- **Empty content recovered**: OWUI v0.10+ stores assistant replies in a structured `output` field and leaves `content` empty. The plugin now rebuilds the text from `output` via OWUI's `convert_output_to_messages` (reasoning excluded), with a DB-lookup fallback via `ChatMessages.get_message_by_id`. +- **Minimum OWUI declared**: `required_open_webui_version: 0.10.2` is now in the metadata. On older OWUI, install [v1.0.1](https://github.com/Fu-Jie/openwebui-extensions/releases/tag/smart-mind-map-v1.0.1) instead. ## Key Features 🔑 diff --git a/plugins/actions/smart-mind-map/README_CN.md b/plugins/actions/smart-mind-map/README_CN.md index 77d1bb2e..77becd57 100644 --- a/plugins/actions/smart-mind-map/README_CN.md +++ b/plugins/actions/smart-mind-map/README_CN.md @@ -2,7 +2,7 @@ 思维导图是一个强大的 OpenWebUI 动作插件,能够智能分析长篇文本内容,自动生成交互式思维导图,帮助用户结构化和可视化知识。 -| 作者:[Fu-Jie](https://github.com/Fu-Jie) · v1.0.1 | [⭐ 点个 Star 支持项目](https://github.com/Fu-Jie/openwebui-extensions) | +| 作者:[Fu-Jie](https://github.com/Fu-Jie) · v1.0.3 | [⭐ 点个 Star 支持项目](https://github.com/Fu-Jie/openwebui-extensions) | | :--- | ---: | | ![followers](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_followers.json&label=%F0%9F%91%A5&style=flat) | ![points](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_points.json&label=%E2%AD%90&style=flat) | ![top](https://img.shields.io/badge/%F0%9F%8F%86-Top%20%3C1%25-10b981?style=flat) | ![contributions](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_contributions.json&label=%F0%9F%93%A6&style=flat) | ![downloads](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_downloads.json&label=%E2%AC%87%EF%B8%8F&style=flat) | ![saves](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_saves.json&label=%F0%9F%92%BE&style=flat) | ![views](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_views.json&label=%F0%9F%91%81%EF%B8%8F&style=flat) | @@ -23,13 +23,13 @@ > [!IMPORTANT] > 如果你已经安装了 OpenWebUI 官方社区里的同名版本,请先删除旧版本,否则重新安装时可能报错。删除后,Batch Install Plugins 后续就可以继续负责更新这个插件。 -## v1.0.1 最新更新 +## v1.0.3 最新更新 -### OpenWebUI 0.9.x 兼容性修复 +### OpenWebUI v0.10+ 兼容性修复(issue #101) -- **版本感知的数据库访问**:用户查询现在可以同时兼容 OpenWebUI 的同步与异步数据库运行时。 -- **运行时安全回退**:Action 会在运行时自动选择正确的数据库调用方式,避免 0.9+ 升级时出现失败。 -- **无视觉回归**:思维导图渲染、直出模式、PNG 导出、主题切换与多语言行为保持不变。 +- **修复前端崩溃**:动作的早退路径不再追加无 `id` 的消息,避免 OWUI v0.10+ 的 `chatActionHandler` 报 `Cannot read properties of undefined (reading 'content')`。改为原地更新已存在的最后一条消息。 +- **空内容恢复**:OWUI v0.10+ 把助手回复存到结构化的 `output` 字段、`content` 留空。插件现在通过 OWUI 原生的 `convert_output_to_messages`(排除推理内容)从 `output` 重建文本,并以 `ChatMessages.get_message_by_id` 数据库查询作为兜底。 +- **声明最低 OWUI 版本**:元数据已加入 `required_open_webui_version: 0.10.2`。旧版 OWUI 用户请安装 [v1.0.1](https://github.com/Fu-Jie/openwebui-extensions/releases/tag/smart-mind-map-v1.0.1)。 ## 核心特性 🔑 diff --git a/plugins/actions/smart-mind-map/v1.0.3.md b/plugins/actions/smart-mind-map/v1.0.3.md new file mode 100644 index 00000000..18df1858 --- /dev/null +++ b/plugins/actions/smart-mind-map/v1.0.3.md @@ -0,0 +1,26 @@ +# Smart Mind Map v1.0.3 Release Notes + +## Overview + +Smart Mind Map v1.0.3 restores the action on OpenWebUI v0.10+. On v0.10+, clicking the action either crashed the frontend (`Cannot read properties of undefined (reading 'content')`) or reported "No content found to export!" on valid replies. This release fixes both layers and declares a minimum OpenWebUI version. + +## Bug Fixes + +- **Frontend crash on OWUI v0.10+** (issue #101): The action's early-return paths used to append a new assistant message without an `id`, which made the OWUI v0.10+ frontend (`chatActionHandler` in `Chat.svelte`) look up `history.messages[undefined]` and crash. The action now updates the existing last message in place (the frontend preserves the original content as `originalContent`), so no id-less message is ever returned. +- **Empty content on OWUI v0.10+** (issue #101, second layer): OWUI v0.10+ moved assistant replies to a structured `output` field and leaves the flat `content` empty, so the plugin's content extraction returned nothing and the action bailed out on valid replies. The plugin now rebuilds the assistant text from `output` using OWUI's own `convert_output_to_messages` (reasoning excluded), with a three-step resolution: existing `content` → inline `msg.output` → `ChatMessages.get_message_by_id` DB lookup. Recovered text is backfilled onto `msg["content"]` so downstream code reads it uniformly. + +## Compatibility + +- **Minimum OpenWebUI**: `required_open_webui_version: 0.10.2` is now declared in the plugin metadata. OWUI enforces this at install time, aligning with PR #103's approach for `export_to_excel` / `export_to_docx`. +- **Guarded imports**: `ChatMessages` and `convert_output_to_messages` are imported with `try/except`. On older OWUI where they don't exist, recovery silently no-ops and the plugin falls back to the original `content`-only path — no crash. +- **Using OpenWebUI < 0.10.2?** Install [Smart Mind Map v1.0.1](https://github.com/Fu-Jie/openwebui-extensions/releases/tag/smart-mind-map-v1.0.1) instead — it's the last release before the v0.10 compatibility fix and works on pre-v0.10 OWUI. + +## Migration Notes + +- No new Valves are introduced. +- No action configuration changes are required. +- If you previously pinned to v1.0.1 on OWUI < 0.10.2, stay on v1.0.1 — v1.0.3's `required_open_webui_version` will prevent installation on older setups by design. + +## Credits + +The structured-`output` recovery approach follows the technique pioneered by @rbb-dev in [PR #103](https://github.com/Fu-Jie/openwebui-extensions/pull/103) for `export_to_excel` / `export_to_docx` — using OWUI's `ChatMessages` + `convert_output_to_messages` rather than hand-rolled parsing. diff --git a/plugins/actions/smart-mind-map/v1.0.3_CN.md b/plugins/actions/smart-mind-map/v1.0.3_CN.md new file mode 100644 index 00000000..6e219422 --- /dev/null +++ b/plugins/actions/smart-mind-map/v1.0.3_CN.md @@ -0,0 +1,26 @@ +# 智能思维导图 v1.0.3 发布说明 + +## 概览 + +智能思维导图 v1.0.3 修复了在 OpenWebUI v0.10+ 上无法使用的问题。在 v0.10+ 上点击该动作时,要么前端崩溃(`Cannot read properties of undefined (reading 'content')`),要么对正常的助手回复报 "No content found to export!"。本次更新修复了这两个层面的问题,并声明了最低 OpenWebUI 版本要求。 + +## 问题修复 + +- **OWUI v0.10+ 前端崩溃**(issue #101):动作的早退路径此前会追加一条不带 `id` 的 assistant 消息,导致 OWUI v0.10+ 前端(`Chat.svelte` 中的 `chatActionHandler`)按 `history.messages[undefined]` 查找时崩溃。现在改为原地更新已存在的最后一条消息(前端会把原内容备份为 `originalContent`),不会再返回任何无 `id` 的消息。 +- **OWUI v0.10+ 内容为空**(issue #101 第二层):OWUI v0.10+ 把助手回复存到结构化的 `output` 字段,扁平的 `content` 留空,导致插件的内容提取拿不到任何文本,对正常回复也走早退路径。现在插件通过 OWUI 原生的 `convert_output_to_messages`(排除推理内容)从 `output` 重建助手文本,三步回退:已有的 `content` → 内联 `msg.output` → `ChatMessages.get_message_by_id` 数据库查询。恢复的文本会回填到 `msg["content"]`,下游代码统一读取 content。 + +## 兼容性 + +- **最低 OpenWebUI 版本**:插件元数据已声明 `required_open_webui_version: 0.10.2`,OWUI 在安装时强制校验,与 PR #103 对 `export_to_excel` / `export_to_docx` 的做法一致。 +- **受保护的 import**:`ChatMessages` 与 `convert_output_to_messages` 通过 `try/except` 导入。在旧版 OWUI 上它们不存在时,恢复逻辑静默跳过,插件回退到原来只读 `content` 的路径,不会崩溃。 +- **使用低于 0.10.2 的 OpenWebUI?** 请安装 [智能思维导图 v1.0.1](https://github.com/Fu-Jie/openwebui-extensions/releases/tag/smart-mind-map-v1.0.1),这是 v0.10 兼容修复前的最后一个版本,可在旧版 OWUI 上正常使用。 + +## 迁移说明 + +- 不引入新的 Valves 配置。 +- 无需更改动作配置。 +- 如果你此前在 OWUI < 0.10.2 上锁定使用 v1.0.1,请继续使用 v1.0.1 —— v1.0.3 的 `required_open_webui_version` 会按设计阻止在旧环境上安装。 + +## 致谢 + +结构化 `output` 内容恢复的实现方式参考了 @rbb-dev 在 [PR #103](https://github.com/Fu-Jie/openwebui-extensions/pull/103) 中为 `export_to_excel` / `export_to_docx` 提出的方案——使用 OWUI 的 `ChatMessages` + `convert_output_to_messages`,而非手写解析逻辑。 From 6f6f794722b50b24fa4ff17d3ac73f4323cb9864 Mon Sep 17 00:00:00 2001 From: Fu-Jie Date: Thu, 2 Jul 2026 11:16:56 +0000 Subject: [PATCH 6/7] docs(smart-mind-map): sync index version to v1.0.3 Update the Smart Mind Map version badge in docs/plugins/actions/index.md and index.zh.md from 1.0.1 to 1.0.3 so all 7 version locations stay consistent (release-prep skill check now passes). --- docs/plugins/actions/index.md | 2 +- docs/plugins/actions/index.zh.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/plugins/actions/index.md b/docs/plugins/actions/index.md index 2d3f14d0..2ce1c8a7 100644 --- a/docs/plugins/actions/index.md +++ b/docs/plugins/actions/index.md @@ -23,7 +23,7 @@ Actions are interactive plugins that: Intelligently analyzes text content and generates interactive mind maps with beautiful visualizations. - **Version:** 1.0.1 + **Version:** 1.0.3 [:octicons-arrow-right-24: Documentation](smart-mind-map.md) diff --git a/docs/plugins/actions/index.zh.md b/docs/plugins/actions/index.zh.md index 6610487c..928f09c0 100644 --- a/docs/plugins/actions/index.zh.md +++ b/docs/plugins/actions/index.zh.md @@ -23,7 +23,7 @@ Actions 是交互式插件,能够: 智能分析文本并生成交互式、精美的思维导图。 - **版本:** 1.0.1 + **版本:** 1.0.3 [:octicons-arrow-right-24: 查看文档](smart-mind-map.md) From 59968917d9c6ed3abd3b9a10e4f5046bdf54c215 Mon Sep 17 00:00:00 2001 From: Fu-Jie Date: Thu, 2 Jul 2026 11:33:34 +0000 Subject: [PATCH 7/7] docs(smart-mind-map): rename unreleased v1.0.3 -> v1.0.2 across all version locations v1.0.3 was never tagged/released, so collapse both layers of the OWUI v0.10+ fix (frontend crash + empty content recovery) into a single v1.0.2 release. Syncs .py docstring/log strings, README/README_CN, docs, index, and renames v1.0.3(.md|_CN.md) -> v1.0.2(.md|_CN.md). Version consistency check passes; 26 regression tests still green. --- docs/plugins/actions/index.md | 2 +- docs/plugins/actions/index.zh.md | 2 +- docs/plugins/actions/smart-mind-map.md | 4 ++-- docs/plugins/actions/smart-mind-map.zh.md | 4 ++-- plugins/actions/smart-mind-map/README.md | 4 ++-- plugins/actions/smart-mind-map/README_CN.md | 4 ++-- plugins/actions/smart-mind-map/smart_mind_map.py | 10 +++++----- .../actions/smart-mind-map/{v1.0.3.md => v1.0.2.md} | 6 +++--- .../smart-mind-map/{v1.0.3_CN.md => v1.0.2_CN.md} | 6 +++--- 9 files changed, 21 insertions(+), 21 deletions(-) rename plugins/actions/smart-mind-map/{v1.0.3.md => v1.0.2.md} (93%) rename plugins/actions/smart-mind-map/{v1.0.3_CN.md => v1.0.2_CN.md} (93%) diff --git a/docs/plugins/actions/index.md b/docs/plugins/actions/index.md index 2ce1c8a7..d39c9e71 100644 --- a/docs/plugins/actions/index.md +++ b/docs/plugins/actions/index.md @@ -23,7 +23,7 @@ Actions are interactive plugins that: Intelligently analyzes text content and generates interactive mind maps with beautiful visualizations. - **Version:** 1.0.3 + **Version:** 1.0.2 [:octicons-arrow-right-24: Documentation](smart-mind-map.md) diff --git a/docs/plugins/actions/index.zh.md b/docs/plugins/actions/index.zh.md index 928f09c0..c3c97dea 100644 --- a/docs/plugins/actions/index.zh.md +++ b/docs/plugins/actions/index.zh.md @@ -23,7 +23,7 @@ Actions 是交互式插件,能够: 智能分析文本并生成交互式、精美的思维导图。 - **版本:** 1.0.3 + **版本:** 1.0.2 [:octicons-arrow-right-24: 查看文档](smart-mind-map.md) diff --git a/docs/plugins/actions/smart-mind-map.md b/docs/plugins/actions/smart-mind-map.md index e6d39f25..8bd51858 100644 --- a/docs/plugins/actions/smart-mind-map.md +++ b/docs/plugins/actions/smart-mind-map.md @@ -2,7 +2,7 @@ Smart Mind Map is a powerful OpenWebUI action plugin that intelligently analyzes long-form text content and automatically generates interactive mind maps, helping users structure and visualize knowledge. -| By [Fu-Jie](https://github.com/Fu-Jie) · v1.0.3 | [⭐ Star this repo](https://github.com/Fu-Jie/openwebui-extensions) | +| By [Fu-Jie](https://github.com/Fu-Jie) · v1.0.2 | [⭐ Star this repo](https://github.com/Fu-Jie/openwebui-extensions) | | :--- | ---: | | ![followers](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_followers.json&label=%F0%9F%91%A5&style=flat) | ![points](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_points.json&label=%E2%AD%90&style=flat) | ![top](https://img.shields.io/badge/%F0%9F%8F%86-Top%20%3C1%25-10b981?style=flat) | ![contributions](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_contributions.json&label=%F0%9F%93%A6&style=flat) | ![downloads](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_downloads.json&label=%E2%AC%87%EF%B8%8F&style=flat) | ![saves](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_saves.json&label=%F0%9F%92%BE&style=flat) | ![views](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_views.json&label=%F0%9F%91%81%EF%B8%8F&style=flat) | @@ -23,7 +23,7 @@ When the selection dialog opens, search for this plugin, check it, and continue. > [!IMPORTANT] > If the official OpenWebUI Community version is already installed, remove it first. After that, Batch Install Plugins can keep this plugin updated in future runs. -## What's New in v1.0.3 +## What's New in v1.0.2 ### OpenWebUI v0.10+ Compatibility Fix (issue #101) diff --git a/docs/plugins/actions/smart-mind-map.zh.md b/docs/plugins/actions/smart-mind-map.zh.md index 77becd57..57893959 100644 --- a/docs/plugins/actions/smart-mind-map.zh.md +++ b/docs/plugins/actions/smart-mind-map.zh.md @@ -2,7 +2,7 @@ 思维导图是一个强大的 OpenWebUI 动作插件,能够智能分析长篇文本内容,自动生成交互式思维导图,帮助用户结构化和可视化知识。 -| 作者:[Fu-Jie](https://github.com/Fu-Jie) · v1.0.3 | [⭐ 点个 Star 支持项目](https://github.com/Fu-Jie/openwebui-extensions) | +| 作者:[Fu-Jie](https://github.com/Fu-Jie) · v1.0.2 | [⭐ 点个 Star 支持项目](https://github.com/Fu-Jie/openwebui-extensions) | | :--- | ---: | | ![followers](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_followers.json&label=%F0%9F%91%A5&style=flat) | ![points](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_points.json&label=%E2%AD%90&style=flat) | ![top](https://img.shields.io/badge/%F0%9F%8F%86-Top%20%3C1%25-10b981?style=flat) | ![contributions](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_contributions.json&label=%F0%9F%93%A6&style=flat) | ![downloads](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_downloads.json&label=%E2%AC%87%EF%B8%8F&style=flat) | ![saves](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_saves.json&label=%F0%9F%92%BE&style=flat) | ![views](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_views.json&label=%F0%9F%91%81%EF%B8%8F&style=flat) | @@ -23,7 +23,7 @@ > [!IMPORTANT] > 如果你已经安装了 OpenWebUI 官方社区里的同名版本,请先删除旧版本,否则重新安装时可能报错。删除后,Batch Install Plugins 后续就可以继续负责更新这个插件。 -## v1.0.3 最新更新 +## v1.0.2 最新更新 ### OpenWebUI v0.10+ 兼容性修复(issue #101) diff --git a/plugins/actions/smart-mind-map/README.md b/plugins/actions/smart-mind-map/README.md index e6d39f25..8bd51858 100644 --- a/plugins/actions/smart-mind-map/README.md +++ b/plugins/actions/smart-mind-map/README.md @@ -2,7 +2,7 @@ Smart Mind Map is a powerful OpenWebUI action plugin that intelligently analyzes long-form text content and automatically generates interactive mind maps, helping users structure and visualize knowledge. -| By [Fu-Jie](https://github.com/Fu-Jie) · v1.0.3 | [⭐ Star this repo](https://github.com/Fu-Jie/openwebui-extensions) | +| By [Fu-Jie](https://github.com/Fu-Jie) · v1.0.2 | [⭐ Star this repo](https://github.com/Fu-Jie/openwebui-extensions) | | :--- | ---: | | ![followers](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_followers.json&label=%F0%9F%91%A5&style=flat) | ![points](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_points.json&label=%E2%AD%90&style=flat) | ![top](https://img.shields.io/badge/%F0%9F%8F%86-Top%20%3C1%25-10b981?style=flat) | ![contributions](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_contributions.json&label=%F0%9F%93%A6&style=flat) | ![downloads](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_downloads.json&label=%E2%AC%87%EF%B8%8F&style=flat) | ![saves](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_saves.json&label=%F0%9F%92%BE&style=flat) | ![views](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_views.json&label=%F0%9F%91%81%EF%B8%8F&style=flat) | @@ -23,7 +23,7 @@ When the selection dialog opens, search for this plugin, check it, and continue. > [!IMPORTANT] > If the official OpenWebUI Community version is already installed, remove it first. After that, Batch Install Plugins can keep this plugin updated in future runs. -## What's New in v1.0.3 +## What's New in v1.0.2 ### OpenWebUI v0.10+ Compatibility Fix (issue #101) diff --git a/plugins/actions/smart-mind-map/README_CN.md b/plugins/actions/smart-mind-map/README_CN.md index 77becd57..57893959 100644 --- a/plugins/actions/smart-mind-map/README_CN.md +++ b/plugins/actions/smart-mind-map/README_CN.md @@ -2,7 +2,7 @@ 思维导图是一个强大的 OpenWebUI 动作插件,能够智能分析长篇文本内容,自动生成交互式思维导图,帮助用户结构化和可视化知识。 -| 作者:[Fu-Jie](https://github.com/Fu-Jie) · v1.0.3 | [⭐ 点个 Star 支持项目](https://github.com/Fu-Jie/openwebui-extensions) | +| 作者:[Fu-Jie](https://github.com/Fu-Jie) · v1.0.2 | [⭐ 点个 Star 支持项目](https://github.com/Fu-Jie/openwebui-extensions) | | :--- | ---: | | ![followers](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_followers.json&label=%F0%9F%91%A5&style=flat) | ![points](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_points.json&label=%E2%AD%90&style=flat) | ![top](https://img.shields.io/badge/%F0%9F%8F%86-Top%20%3C1%25-10b981?style=flat) | ![contributions](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_contributions.json&label=%F0%9F%93%A6&style=flat) | ![downloads](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_downloads.json&label=%E2%AC%87%EF%B8%8F&style=flat) | ![saves](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_saves.json&label=%F0%9F%92%BE&style=flat) | ![views](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FFu-Jie%2Fdb3d95687075a880af6f1fba76d679c6%2Fraw%2Fbadge_views.json&label=%F0%9F%91%81%EF%B8%8F&style=flat) | @@ -23,7 +23,7 @@ > [!IMPORTANT] > 如果你已经安装了 OpenWebUI 官方社区里的同名版本,请先删除旧版本,否则重新安装时可能报错。删除后,Batch Install Plugins 后续就可以继续负责更新这个插件。 -## v1.0.3 最新更新 +## v1.0.2 最新更新 ### OpenWebUI v0.10+ 兼容性修复(issue #101) diff --git a/plugins/actions/smart-mind-map/smart_mind_map.py b/plugins/actions/smart-mind-map/smart_mind_map.py index 381a25bc..1a75cacc 100644 --- a/plugins/actions/smart-mind-map/smart_mind_map.py +++ b/plugins/actions/smart-mind-map/smart_mind_map.py @@ -3,7 +3,7 @@ author: Fu-Jie author_url: https://github.com/Fu-Jie/openwebui-extensions funding_url: https://github.com/open-webui -version: 1.0.3 +version: 1.0.2 required_open_webui_version: 0.10.2 openwebui_id: 3094c59a-b4dd-4e0c-9449-15e2dd547dc4 icon_url: data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjxyZWN0IHg9IjE2IiB5PSIxNiIgd2lkdGg9IjYiIGhlaWdodD0iNiIgcng9IjEiLz48cmVjdCB4PSIyIiB5PSIxNiIgd2lkdGg9IjYiIGhlaWdodD0iNiIgcng9IjEiLz48cmVjdCB4PSI5IiB5PSIyIiB3aWR0aD0iNiIgaGVpZ2h0PSI2IiByeD0iMSIvPjxwYXRoIGQ9Ik01IDE2di0zYTEgMSAwIDAgMSAxLTFoMTJhMSAxIDAgMCAxIDEgMXYzIi8+PHBhdGggZD0iTTEyIDEyVjgiLz48L3N2Zz4= @@ -2562,7 +2562,7 @@ async def action( __metadata__: Optional[dict] = None, __request__: Optional[Request] = None, ) -> Optional[dict]: - logger.info("Action: Smart Mind Map (v1.0.3) started") + logger.info("Action: Smart Mind Map (v1.0.2) started") user_ctx = await self._get_user_context(__user__, __event_call__, __request__) user_language = user_ctx["user_language"] user_name = user_ctx["user_name"] @@ -2840,7 +2840,7 @@ async def action( ), "success", ) - logger.info("Action: Smart Mind Map (v1.0.3) completed in image mode") + logger.info("Action: Smart Mind Map (v1.0.2) completed in image mode") return body # HTML mode @@ -2923,7 +2923,7 @@ async def action( ), "success", ) - logger.info("Action: Smart Mind Map (v1.0.3) completed in Direct Mode") + logger.info("Action: Smart Mind Map (v1.0.2) completed in Direct Mode") return ( final_html_direct, @@ -2951,7 +2951,7 @@ async def action( "success", ) logger.info( - "Action: Smart Mind Map (v1.0.3) completed in Legacy HTML mode" + "Action: Smart Mind Map (v1.0.2) completed in Legacy HTML mode" ) except Exception as e: diff --git a/plugins/actions/smart-mind-map/v1.0.3.md b/plugins/actions/smart-mind-map/v1.0.2.md similarity index 93% rename from plugins/actions/smart-mind-map/v1.0.3.md rename to plugins/actions/smart-mind-map/v1.0.2.md index 18df1858..909fbdc2 100644 --- a/plugins/actions/smart-mind-map/v1.0.3.md +++ b/plugins/actions/smart-mind-map/v1.0.2.md @@ -1,8 +1,8 @@ -# Smart Mind Map v1.0.3 Release Notes +# Smart Mind Map v1.0.2 Release Notes ## Overview -Smart Mind Map v1.0.3 restores the action on OpenWebUI v0.10+. On v0.10+, clicking the action either crashed the frontend (`Cannot read properties of undefined (reading 'content')`) or reported "No content found to export!" on valid replies. This release fixes both layers and declares a minimum OpenWebUI version. +Smart Mind Map v1.0.2 restores the action on OpenWebUI v0.10+. On v0.10+, clicking the action either crashed the frontend (`Cannot read properties of undefined (reading 'content')`) or reported "No content found to export!" on valid replies. This release fixes both layers and declares a minimum OpenWebUI version. ## Bug Fixes @@ -19,7 +19,7 @@ Smart Mind Map v1.0.3 restores the action on OpenWebUI v0.10+. On v0.10+, clicki - No new Valves are introduced. - No action configuration changes are required. -- If you previously pinned to v1.0.1 on OWUI < 0.10.2, stay on v1.0.1 — v1.0.3's `required_open_webui_version` will prevent installation on older setups by design. +- If you previously pinned to v1.0.1 on OWUI < 0.10.2, stay on v1.0.1 — v1.0.2's `required_open_webui_version` will prevent installation on older setups by design. ## Credits diff --git a/plugins/actions/smart-mind-map/v1.0.3_CN.md b/plugins/actions/smart-mind-map/v1.0.2_CN.md similarity index 93% rename from plugins/actions/smart-mind-map/v1.0.3_CN.md rename to plugins/actions/smart-mind-map/v1.0.2_CN.md index 6e219422..07f7f1bb 100644 --- a/plugins/actions/smart-mind-map/v1.0.3_CN.md +++ b/plugins/actions/smart-mind-map/v1.0.2_CN.md @@ -1,8 +1,8 @@ -# 智能思维导图 v1.0.3 发布说明 +# 智能思维导图 v1.0.2 发布说明 ## 概览 -智能思维导图 v1.0.3 修复了在 OpenWebUI v0.10+ 上无法使用的问题。在 v0.10+ 上点击该动作时,要么前端崩溃(`Cannot read properties of undefined (reading 'content')`),要么对正常的助手回复报 "No content found to export!"。本次更新修复了这两个层面的问题,并声明了最低 OpenWebUI 版本要求。 +智能思维导图 v1.0.2 修复了在 OpenWebUI v0.10+ 上无法使用的问题。在 v0.10+ 上点击该动作时,要么前端崩溃(`Cannot read properties of undefined (reading 'content')`),要么对正常的助手回复报 "No content found to export!"。本次更新修复了这两个层面的问题,并声明了最低 OpenWebUI 版本要求。 ## 问题修复 @@ -19,7 +19,7 @@ - 不引入新的 Valves 配置。 - 无需更改动作配置。 -- 如果你此前在 OWUI < 0.10.2 上锁定使用 v1.0.1,请继续使用 v1.0.1 —— v1.0.3 的 `required_open_webui_version` 会按设计阻止在旧环境上安装。 +- 如果你此前在 OWUI < 0.10.2 上锁定使用 v1.0.1,请继续使用 v1.0.1 —— v1.0.2 的 `required_open_webui_version` 会按设计阻止在旧环境上安装。 ## 致谢