Show Utility Model rename failures

Notify users when automatic chat renaming cannot reach the Utility Model, while keeping the rename task best-effort. Normalize saved generated titles, mark WebUI state dirty after successful saves, and cover the success and failure paths with focused tests.
This commit is contained in:
Alessandro 2026-06-28 18:46:58 +02:00
parent 186ca2fde6
commit 0b38258d6c
3 changed files with 89 additions and 6 deletions

View file

@ -12,10 +12,11 @@
- Keep automatic rename behavior bounded and non-destructive.
- Do not override explicit user chat names without the intended guard conditions.
- Surface Utility Model rename failures with one scoped error notification per chat.
## Work Guidance
- Coordinate rename behavior with chat persistence and WebUI refresh.
- Coordinate rename behavior with chat persistence and WebUI refresh after successful saves.
## Verification

View file

@ -1,5 +1,7 @@
from helpers import persist_chat, tokens
from helpers.extension import Extension
from helpers.notification import NotificationManager, NotificationPriority, NotificationType
from helpers.state_monitor_integration import mark_dirty_all
from agent import LoopData
import asyncio
@ -29,16 +31,35 @@ class RenameChat(Extension):
"fw.rename_chat.msg.md", current_name=current_name, history=history_text
)
# call utility model
new_name = await self.agent.call_utility_model(
system=system, message=message, background=True
)
try:
new_name = await self.agent.call_utility_model(
system=system, message=message, background=True
)
except Exception:
NotificationManager.send_notification(
type=NotificationType.ERROR,
priority=NotificationPriority.NORMAL,
title="Chat Rename Failed",
message="Automatic chat renaming failed because the Utility Model was not reachable.",
detail=(
"Automatic chat renaming uses the Utility Model. Check Settings > Models > "
"Utility Model, provider/API key, and network reachability."
),
display_time=10,
group="chat_rename",
id=f"chat_rename_failed_{self.agent.context.id}",
)
return
# update name
if new_name:
# trim name to max length if needed
new_name = " ".join(str(new_name).split())
if len(new_name) > 40:
new_name = new_name[:40] + "..."
if not new_name:
return
# apply to context and save
self.agent.context.name = new_name
persist_chat.save_tmp_chat(self.agent.context)
except Exception as e:
mark_dirty_all(reason="monologue_start.RenameChat.change_name")
except Exception:
pass # non-critical

View file

@ -0,0 +1,61 @@
from types import SimpleNamespace
import pytest
from extensions.python.monologue_start import _60_rename_chat as rename_chat
from plugins._model_config.helpers import model_config
pytestmark = pytest.mark.asyncio
class _History:
def output_text(self) -> str:
return "User: Please help me plan the launch."
class _Agent:
def __init__(self, *, response: str | None = None, error: Exception | None = None):
self.context = SimpleNamespace(id="ctx-rename", name="")
self.history = _History()
self._response = response
self._error = error
def read_prompt(self, name: str, **kwargs) -> str:
return name
async def call_utility_model(self, **kwargs) -> str:
if self._error:
raise self._error
return self._response or ""
async def test_rename_failure_sends_utility_model_error_notification(monkeypatch):
sent: list[dict] = []
monkeypatch.setattr(model_config, "get_utility_model_config", lambda agent: {"ctx_length": 1000})
monkeypatch.setattr(rename_chat.NotificationManager, "send_notification", lambda **kwargs: sent.append(kwargs))
await rename_chat.RenameChat(agent=_Agent(error=RuntimeError("offline"))).change_name()
assert len(sent) == 1
assert sent[0]["type"] == rename_chat.NotificationType.ERROR
assert sent[0]["title"] == "Chat Rename Failed"
assert "Utility Model was not reachable" in sent[0]["message"]
assert sent[0]["id"] == "chat_rename_failed_ctx-rename"
async def test_successful_rename_saves_clean_name_and_marks_state_dirty(monkeypatch):
saved_names: list[str] = []
dirty_reasons: list[str | None] = []
monkeypatch.setattr(model_config, "get_utility_model_config", lambda agent: {"ctx_length": 1000})
monkeypatch.setattr(rename_chat.persist_chat, "save_tmp_chat", lambda context: saved_names.append(context.name))
monkeypatch.setattr(rename_chat, "mark_dirty_all", lambda *, reason=None: dirty_reasons.append(reason))
agent = _Agent(response="\n\nLaunch Readiness Notes\n")
await rename_chat.RenameChat(agent=agent).change_name()
assert agent.context.name == "Launch Readiness Notes"
assert saved_names == ["Launch Readiness Notes"]
assert dirty_reasons == ["monologue_start.RenameChat.change_name"]