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 <noreply@anthropic.com>
cora-start
PeninsulaInd 2026-02-23 11:11:34 -06:00
parent d771dd5c80
commit 30757b5bcf
1 changed files with 43 additions and 6 deletions

View File

@ -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 = []