diff --git a/frontend/src/views/SettingsView.vue b/frontend/src/views/SettingsView.vue index b743e94..2b8b429 100644 --- a/frontend/src/views/SettingsView.vue +++ b/frontend/src/views/SettingsView.vue @@ -1540,19 +1540,23 @@ function formatUserDate(iso: string): string { -

Model used for new conversations.

+

+ Used for journal chat (no tools — just conversation) and lightweight one-shot tasks like note-title generation and tag suggestions. + Pick a small fast model (e.g. qwen3:8b, llama3.2:3b) — speed matters more than depth here. + Ideally runs on GPU. +

- +

- Model used for background tasks: title generation, tag suggestions, and project summaries. - Using a small dedicated model (e.g. qwen2.5:0.5b) keeps the chat model's KV cache warm between messages, significantly reducing response time. + Used for heavy async work: the journal curator (capture / propose updates), daily prep generation, end-of-day closeout, task body consolidation, project summaries, and profile observation processing. + Pick a smart model — latency doesn't matter, quality does. Often runs on CPU with system RAM (e.g. qwen3:32b, qwen3:30b-a3b). - ⚠ Setting this to the same model as Chat Model will wipe the KV cache after every background task, increasing response latency. + ⚠ Using the same model for both means worker tasks compete for the chat model's resources. Pick different models so they can be loaded simultaneously (OLLAMA_MAX_LOADED_MODELS = 2+).

diff --git a/src/fabledassistant/services/chat.py b/src/fabledassistant/services/chat.py index 2adabad..e6df2bc 100644 --- a/src/fabledassistant/services/chat.py +++ b/src/fabledassistant/services/chat.py @@ -243,8 +243,15 @@ async def save_response_as_note(user_id: int, message_id: int) -> dict: }, {"role": "user", "content": msg.content[:2000]}, ] - bg_model = await get_setting(user_id, "background_model", Config.OLLAMA_BACKGROUND_MODEL) - title = await generate_completion(prompt_messages, bg_model) + # 3-8 word title generation is the kind of trivial small-input + # / small-output task the chat model (default_model) handles in + # ~1s. Routing here so the worker (background_model) isn't + # interrupted from heavier curator / prep / closeout passes. + chat_model = ( + await get_setting(user_id, "default_model", "") + or Config.OLLAMA_MODEL + ) + title = await generate_completion(prompt_messages, chat_model) title = title.strip().strip('"\'').strip()[:100] except Exception: logger.warning("Failed to generate note title, using fallback", exc_info=True) diff --git a/src/fabledassistant/services/journal_prep.py b/src/fabledassistant/services/journal_prep.py index c661dcf..c427bc9 100644 --- a/src/fabledassistant/services/journal_prep.py +++ b/src/fabledassistant/services/journal_prep.py @@ -446,7 +446,16 @@ async def _generate_prep_prose( """Ask the LLM for a direct conversational journal opener built from the sections.""" from fabledassistant.services.llm import generate_completion - model = (await get_setting(user_id, "default_model", "")) or Config.OLLAMA_MODEL + # Daily prep is a deliberate, multi-section generation — runs once a day, + # latency-tolerant, benefits from a smarter model. Route to the worker + # (background_model) rather than the chat model. The chat model in the + # conversation+curator architecture is small/fast/no-tools; prep needs + # the heavier reasoning the worker provides. + model = ( + await get_setting(user_id, "background_model", "") + or Config.OLLAMA_BACKGROUND_MODEL + or Config.OLLAMA_MODEL + ) if not model: logger.warning("No LLM model configured for daily prep — using fallback text") return _fallback_prep_text(day_date) diff --git a/src/fabledassistant/services/tag_suggestions.py b/src/fabledassistant/services/tag_suggestions.py index b39a622..90fce85 100644 --- a/src/fabledassistant/services/tag_suggestions.py +++ b/src/fabledassistant/services/tag_suggestions.py @@ -22,7 +22,14 @@ async def suggest_tags(user_id: int, title: str, body: str, current_tags: list[s return [] existing_tags = await get_all_tags(user_id) - model = await get_setting(user_id, "background_model", Config.OLLAMA_BACKGROUND_MODEL) + # Tag suggestion is a tiny single-shot task (3-5 tags from a note's + # title + body) — route to the chat model (small/fast) rather than + # tying up the worker on a trivial call. The chat model handles this + # in well under a second. + model = ( + await get_setting(user_id, "default_model", "") + or Config.OLLAMA_MODEL + ) existing_list = ", ".join(f"#{t}" for t in existing_tags) if existing_tags else "(none yet)" diff --git a/src/fabledassistant/services/user_profile.py b/src/fabledassistant/services/user_profile.py index bdb4fdb..8b776b8 100644 --- a/src/fabledassistant/services/user_profile.py +++ b/src/fabledassistant/services/user_profile.py @@ -144,7 +144,15 @@ async def _consolidate_observations(user_id: int) -> str: user_prompt += f"Existing summary:\n{existing_summary}\n\n" user_prompt += f"New observations:\n{obs_text}" - model = await get_setting(user_id, "default_model", Config.OLLAMA_MODEL) + # Profile observation consolidation reasons over multiple documents to + # produce a coherent summary — closer to curator-shaped work than chat. + # Route to worker (background_model). Falls back to OLLAMA_MODEL only if + # neither setting nor BACKGROUND default is available. + model = ( + await get_setting(user_id, "background_model", "") + or Config.OLLAMA_BACKGROUND_MODEL + or Config.OLLAMA_MODEL + ) try: new_summary = (await generate_completion( [