"""Generate a short session name from the conversation via LLM."""
from navi.llm.base import LLMBackend, Message
_SYSTEM = (
"You are a session title generator. "
"Given the user's messages (and, possibly, the assistant's replies) from a conversation, "
"produce ONE short title (3–6 words, no punctuation at the end). "
"The title must reflect the actual topic. "
"Respond in the same language as the user's messages. "
"If there's still no clear topic (e.g. only greetings, very short or ambiguous text), "
"reply with exactly: NO_TITLE"
)
# Assistant replies can be huge; a tail slice is enough context for a title.
_ASSISTANT_MSG_CHARS = 600
_ASSISTANT_MSG_COUNT = 2
async def generate_session_name(
user_messages: list[str],
backend: LLMBackend,
model: str,
assistant_messages: list[str] | None = None,
) -> str | None:
"""Return a short title or None if content isn't substantial enough."""
if not user_messages:
return None
combined = "\n".join(f"- {m}" for m in user_messages[:10])
# Naming runs right after the first exchange, when user messages are often
# greetings or one-liners with no topic. The agent's final response is
# where the actual subject shows up — include a trimmed tail of it.
if assistant_messages:
combined += "\n\nAssistant replies:\n" + "\n---\n".join(
m[-_ASSISTANT_MSG_CHARS:]
for m in assistant_messages[-_ASSISTANT_MSG_COUNT:]
if m
)
messages = [
Message(role="system", content=_SYSTEM),
Message(role="user", content=f"User messages:\n{combined}"),
]
resp = await backend.complete(messages, tools=[], model=model, temperature=0.4)
text = (resp.content or "").strip()
if not text or text.upper() == "NO_TITLE" or len(text) > 80:
return None
# Strip surrounding quotes if the model added them
return text.strip('"\'')