ux: rename model fields + enforce serial curator execution
Three coordinated changes per operator request 2026-05-24:
1. Settings UI rename matching the language we actually use:
- Chat Model -> Chat & Voice Model
- Worker Model -> Curator Model
Setting KEYS (default_model / background_model) unchanged on
purpose; renaming them requires a migration touching 50+ call
sites for purely UX-facing benefit.
2. Settings UI help text rewritten:
- Chat & Voice: documents that it handles chat AND small
conversational automations (titles, tags). Recommends
OLLAMA_NUM_PARALLEL=2+ on the Ollama server so background
automations get their own KV-cache slot and don't evict
the chat model's working state.
- Curator: notes the app enforces SERIAL execution regardless
of NUM_PARALLEL — only one curator pass runs at a time. This
matters most for 70b CPU models where a second instance
would waste system RAM.
3. Enforce serial curator execution globally:
- New module-level _CURATOR_RUN_LOCK in services/curator.py.
- run_curator_for_conversation now wraps its body in 'async
with _CURATOR_RUN_LOCK' — every entry point (scheduler sweep,
manual route trigger, future hooks) is serialized through it.
- is_curator_running() helper exposes the lock state.
- routes/journal.py manual trigger checks is_curator_running()
first and returns 409 {busy: true} immediately rather than
blocking the HTTP request for minutes waiting for a 70b CPU
pass to finish. The user can retry once the curator clears.
Why a 409 instead of queue: a curator pass on a 70b CPU model
can take 5+ minutes. Tying up an HTTP worker that long is bad;
making the user wait without feedback is worse. 409 surfaces
the busy state immediately and the user retries when they want.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -199,7 +199,19 @@ async def trigger_curator_run(conv_id: int):
|
||||
if _res.scalar_one_or_none() is None:
|
||||
return jsonify({"error": "Conversation not found"}), 404
|
||||
|
||||
from fabledassistant.services.curator import run_curator_for_conversation
|
||||
from fabledassistant.services.curator import (
|
||||
is_curator_running,
|
||||
run_curator_for_conversation,
|
||||
)
|
||||
# The curator typically runs on a large model (30b-70b on CPU); we
|
||||
# serialize runs globally via a module-level lock. Reject rather
|
||||
# than block when busy — blocking would tie up an HTTP worker for
|
||||
# minutes. The user can retry in a moment.
|
||||
if is_curator_running():
|
||||
return jsonify({
|
||||
"error": "Curator is currently running. Please try again in a moment.",
|
||||
"busy": True,
|
||||
}), 409
|
||||
result = await run_curator_for_conversation(conv_id)
|
||||
|
||||
# Stamp last_curator_run_at on success so the scheduler doesn't
|
||||
|
||||
Reference in New Issue
Block a user