From 30757b5bcf0101c511f865884dc2a9c62c6547d6 Mon Sep 17 00:00:00 2001 From: PeninsulaInd Date: Mon, 23 Feb 2026 11:11:34 -0600 Subject: [PATCH] Generate conversation titles via LLM instead of truncating first line - _maybe_set_title sets a quick truncated fallback immediately - Then fires a background thread to ask the LLM for a 5-8 word summary - Background thread doesn't block the streaming response - Title appears in sidebar on first chunk, then upgrades when LLM responds Co-Authored-By: Claude Opus 4.6 --- cheddahbot/agent.py | 49 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 43 insertions(+), 6 deletions(-) diff --git a/cheddahbot/agent.py b/cheddahbot/agent.py index 13dcb73..0bf801b 100644 --- a/cheddahbot/agent.py +++ b/cheddahbot/agent.py @@ -5,6 +5,7 @@ from __future__ import annotations import base64 import json import logging +import threading import uuid from collections.abc import Generator from pathlib import Path @@ -294,19 +295,55 @@ class Agent: self._memory.auto_flush(conv_id) def _maybe_set_title(self, conv_id: str, user_input: str): - """Set conversation title from first user message if still 'New Chat'.""" + """Set conversation title from first user message if still 'New Chat'. + + Sets a quick truncated fallback immediately, then fires a background + thread to generate a proper 5-8 word LLM summary. + """ try: current_title = self.db.get_conversation_title(conv_id) if current_title and current_title != "New Chat": return - title = user_input.split("\n", 1)[0].strip() - if len(title) > 50: - title = title[:47] + "..." - if title: - self.db.update_conversation_title(conv_id, title) + # Immediate fallback: first line, truncated + fallback = user_input.split("\n", 1)[0].strip() + if len(fallback) > 50: + fallback = fallback[:47] + "..." + if fallback: + self.db.update_conversation_title(conv_id, fallback) + # Fire background LLM call to generate a better title + threading.Thread( + target=self._generate_title, + args=(conv_id, user_input), + daemon=True, + ).start() except Exception as e: log.warning("Failed to set conversation title: %s", e) + def _generate_title(self, conv_id: str, user_input: str): + """Background: ask the LLM for a 5-8 word conversation title.""" + try: + prompt = user_input[:500] # cap input to keep it cheap + messages = [ + { + "role": "system", + "content": ( + "Generate a short 5-8 word title summarizing this conversation opener. " + "Reply with ONLY the title — no quotes, no punctuation at the end, no explanation." + ), + }, + {"role": "user", "content": prompt}, + ] + parts = [] + for chunk in self.llm.chat(messages, tools=None, stream=False): + if chunk.get("type") == "text": + parts.append(chunk["content"]) + title = "".join(parts).strip().strip('"').strip("'") + if title and len(title) <= 60: + self.db.update_conversation_title(conv_id, title) + log.debug("Generated conversation title: %s", title) + except Exception as e: + log.warning("Background title generation failed: %s", e) + def respond_to_prompt(self, prompt: str) -> str: """Non-streaming response for scheduled tasks / internal use.""" result_parts = []