fix: rewrite title generator to only use user messages; bump background model to qwen2.5:3b
This commit is contained in:
@@ -27,7 +27,7 @@ class Config:
|
|||||||
# Lightweight model for background tasks (title generation, tag suggestions,
|
# Lightweight model for background tasks (title generation, tag suggestions,
|
||||||
# project summaries, RSS classification). Using a separate model keeps the
|
# project summaries, RSS classification). Using a separate model keeps the
|
||||||
# main model's KV cache intact between user messages, enabling prefix cache hits.
|
# main model's KV cache intact between user messages, enabling prefix cache hits.
|
||||||
OLLAMA_BACKGROUND_MODEL: str = os.environ.get("OLLAMA_BACKGROUND_MODEL", "qwen2.5:0.5b")
|
OLLAMA_BACKGROUND_MODEL: str = os.environ.get("OLLAMA_BACKGROUND_MODEL", "qwen2.5:3b")
|
||||||
# KV cache context window for generation. Keep this as small as practical —
|
# KV cache context window for generation. Keep this as small as practical —
|
||||||
# a larger context forces more KV cache into CPU RAM, drastically slowing prefill.
|
# a larger context forces more KV cache into CPU RAM, drastically slowing prefill.
|
||||||
# 16384 covers ~30+ message conversations with our system prompt comfortably.
|
# 16384 covers ~30+ message conversations with our system prompt comfortably.
|
||||||
|
|||||||
@@ -118,34 +118,45 @@ _TOOL_LABELS: dict[str, str] = {
|
|||||||
|
|
||||||
|
|
||||||
async def _generate_title(messages: list[dict], user_id: int) -> str:
|
async def _generate_title(messages: list[dict], user_id: int) -> str:
|
||||||
"""Ask the LLM for a concise conversation title."""
|
"""Ask the LLM for a concise conversation title.
|
||||||
# Build conversation text like summarize_conversation_as_note
|
|
||||||
conv_lines = []
|
Only uses user messages to avoid feeding tool-call JSON, system prompt
|
||||||
|
fragments, or other noise into the title generator. Caps input length
|
||||||
|
to keep the task fast and focused.
|
||||||
|
"""
|
||||||
|
user_texts = []
|
||||||
for m in messages:
|
for m in messages:
|
||||||
if m["role"] in ("system", "tool"):
|
if m["role"] == "user":
|
||||||
continue
|
content = (m.get("content") or "").strip()
|
||||||
label = "User" if m["role"] == "user" else "Assistant"
|
if content:
|
||||||
content = m.get("content", "")
|
user_texts.append(content[:300])
|
||||||
if not content:
|
if not user_texts:
|
||||||
continue
|
return ""
|
||||||
conv_lines.append(f"{label}: {content}")
|
# First + last user messages capture intent best
|
||||||
# Keep only last 6 pairs worth of text
|
if len(user_texts) > 2:
|
||||||
conv_lines = conv_lines[-12:]
|
user_texts = [user_texts[0], user_texts[-1]]
|
||||||
|
|
||||||
prompt_messages = [
|
prompt_messages = [
|
||||||
{
|
{
|
||||||
"role": "system",
|
"role": "user",
|
||||||
"content": (
|
"content": (
|
||||||
"Generate a concise 3-8 word title for this conversation. "
|
"Generate a concise 3-8 word title for a conversation that started with:\n\n"
|
||||||
"Reply with ONLY the title, no quotes or punctuation."
|
+ "\n\n".join(user_texts)
|
||||||
|
+ "\n\nReply with ONLY the title. No quotes, no punctuation, no explanation."
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{"role": "user", "content": "\n\n".join(conv_lines)},
|
|
||||||
]
|
]
|
||||||
bg_model = await get_setting(user_id, "background_model", Config.OLLAMA_BACKGROUND_MODEL)
|
bg_model = await get_setting(user_id, "background_model", Config.OLLAMA_BACKGROUND_MODEL)
|
||||||
title = await generate_completion(prompt_messages, bg_model, max_tokens=30)
|
title = await generate_completion(prompt_messages, bg_model, max_tokens=30, num_ctx=1024)
|
||||||
|
# Strip common LLM noise: quotes, thinking tags, role labels
|
||||||
title = title.strip().strip('"\'').strip()
|
title = title.strip().strip('"\'').strip()
|
||||||
return title[:100] if title else ""
|
for prefix in ("Title:", "title:", "Assistant:", "User:"):
|
||||||
|
if title.startswith(prefix):
|
||||||
|
title = title[len(prefix):].strip()
|
||||||
|
# Drop anything after a newline (model sometimes adds explanation)
|
||||||
|
if "\n" in title:
|
||||||
|
title = title.split("\n")[0].strip()
|
||||||
|
return title[:80] if title else ""
|
||||||
|
|
||||||
|
|
||||||
async def _update_message(
|
async def _update_message(
|
||||||
|
|||||||
@@ -124,7 +124,7 @@ async def generate_project_summary(user_id: int, project_id: int) -> None:
|
|||||||
from fabledassistant.services.settings import get_setting
|
from fabledassistant.services.settings import get_setting
|
||||||
messages = [{"role": "user", "content": prompt}]
|
messages = [{"role": "user", "content": prompt}]
|
||||||
bg_model = await get_setting(user_id, "background_model", Config.OLLAMA_BACKGROUND_MODEL)
|
bg_model = await get_setting(user_id, "background_model", Config.OLLAMA_BACKGROUND_MODEL)
|
||||||
summary = await generate_completion(messages, model=bg_model, max_tokens=400)
|
summary = await generate_completion(messages, model=bg_model, max_tokens=400, num_ctx=2048)
|
||||||
if not summary:
|
if not summary:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user