Added chat renaming and auto naming by the active LLM

This commit is contained in:
Eric Lammertsma 2026-02-03 20:47:18 -05:00
parent b176599c64
commit d761ca1992
12 changed files with 287 additions and 36 deletions

View file

@ -32,6 +32,7 @@ from app.services.chat_session_state_service import (
clear_ai_responding,
set_ai_responding,
)
from app.prompts import TITLE_GENERATION_PROMPT_TEMPLATE
from app.services.connector_service import ConnectorService
from app.services.new_streaming_service import VercelStreamingService
from app.utils.content_utils import bootstrap_history_from_db
@ -1208,6 +1209,80 @@ async def stream_new_chat(
if completion_event:
yield completion_event
# Generate LLM title for new chats after first response
# Check if this is the first assistant response by counting existing assistant messages
from app.db import NewChatMessage, NewChatThread
from sqlalchemy import func
assistant_count_result = await session.execute(
select(func.count(NewChatMessage.id)).filter(
NewChatMessage.thread_id == chat_id,
NewChatMessage.role == "assistant",
)
)
assistant_message_count = assistant_count_result.scalar() or 0
# Only generate title on the first response (no prior assistant messages)
if assistant_message_count == 0:
print(f"[stream_new_chat] First response - generating title for thread {chat_id}")
print(f"[stream_new_chat] Query length: {len(user_query)}, Response length: {len(accumulated_text)}")
generated_title = None
try:
# Generate title using the same LLM
title_chain = TITLE_GENERATION_PROMPT_TEMPLATE | llm
# Truncate inputs to avoid context length issues
truncated_query = user_query[:500]
truncated_response = accumulated_text[:1000]
print(f"[stream_new_chat] Calling LLM for title generation...")
title_result = await title_chain.ainvoke({
"user_query": truncated_query,
"assistant_response": truncated_response,
})
print(f"[stream_new_chat] LLM title result type: {type(title_result)}")
print(f"[stream_new_chat] LLM title result: {title_result}")
# Extract and clean the title
if title_result and hasattr(title_result, "content"):
raw_title = title_result.content.strip()
print(f"[stream_new_chat] Raw title content: '{raw_title}' (len={len(raw_title)})")
# Validate the title (1-6 words, reasonable length)
if raw_title and len(raw_title) <= 100:
# Remove any quotes or extra formatting
generated_title = raw_title.strip('"\'')
print(f"[stream_new_chat] After stripping quotes: '{generated_title}'")
else:
print(f"[stream_new_chat] Title validation failed: empty={not raw_title}, len={len(raw_title)}")
generated_title = None
else:
print(f"[stream_new_chat] No content attribute on result")
except Exception as title_error:
print(f"[stream_new_chat] Title generation failed: {title_error}")
import traceback
traceback.print_exc()
generated_title = None
# Only update if LLM succeeded (keep truncated prompt title as fallback)
if generated_title:
print(f"[stream_new_chat] Using LLM-generated title: '{generated_title}'")
# Fetch thread and update title
thread_result = await session.execute(
select(NewChatThread).filter(NewChatThread.id == chat_id)
)
thread = thread_result.scalars().first()
if thread:
thread.title = generated_title
await session.commit()
# Notify frontend of the title update
yield streaming_service.format_thread_title_update(
chat_id, generated_title
)
else:
print(f"[stream_new_chat] LLM title generation failed, keeping truncated prompt title")
# Finish the step and message
yield streaming_service.format_finish_step()
yield streaming_service.format_finish()