session title should be set by the AI on first chat #4
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
when a user starts a session, that session should get a title from the first chat of the user, decided by the AI - so a separate one-shot AI integration is done to summarize the first chat of the user into a session title.
Session title discovery must be a separate one-shot communication with the AI model that does not leave a session behind, as it is only meant to get a quick summary to show in the title. The one-shot communication happens outside the current session, so is not added there, too. this is the same one-shot mechanism as in #1
session title should be set by the AI on first chat and sessions should only store in database with first chatto session title should be set by the AI on first chatImplemented in
d456458.The deferred-persistence half of this issue was already done in
51c1b2a: a new session lives only in the UI until its first chat turn is stored, so empty sessions never reach the database and are gone on restart. What this commit adds is the AI title on top of that, reusing the one-shot mechanism built for #1 exactly as this issue asks.Trigger.
start_generationalready had to distinguish a draft from a stored session in order to persist it; that same branch now records whether this is the opening turn, and once the reply is queued it callsrequest_first_title. Titling therefore happens exactly once per session, on the turn that brings it into existence.The one-shot is unchanged from #1. Same
CheckpointTarget::OneShotagainst the shared transient KV cache, samereasoning_mode = Directand 48-token cap, samesession_titlesanitizer (first non-empty line, quotes and markdown stripped, 60 characters max). No session row, no per-session checkpoint, nothing added to the conversation being titled.Quiet and loud paths are now separated.
request_titlereturns aResultinstead of writing to the error banner, with two callers:retitle_sessionfor the #1 menu action, which reports failures, andrequest_first_titlefor this automatic path, which swallows them. This mattered more than it first looked — without the split, a user who never asked for a title would have been shown "A title is already being generated." because a background job could not get a slot.Empty turns are filtered out of the summary context.
start_chat_turninserts the assistant row empty, so at trigger time the stored conversation is the user's message plus a blank placeholder. Skipping empty content is correct in general, and it is what makes this pass summarize the opening message on its own, matching "summarize the first chat of the user".A manual rename beats a queued title.
TitleRequestnow carries the title the session had when the request was queued; if it differs when the result arrives, the generated title is dropped. Without this, renaming a session while its title was in flight would have had the edit silently overwritten a minute later.Three consequences worth recording for whoever touches this next:
The one-shot evicts the session's in-memory KV state. Running outside the session means the executor resets, so the next turn in that session reloads its checkpoint from disk. This follows directly from the requirement that the one-shot happen outside the session, on a single-threaded runtime. It is a disk read rather than a re-prefill, since the checkpoint is saved after every turn. If it becomes noticeable on large contexts, the fix is to defer titling until the runtime is idle instead of queueing it immediately.
Titles arrive after the reply, not during it. The request queues behind the generation on the single worker. This is deliberate: the alternative delays the user's answer in order to compute a title. The sidebar shows the "Session N" placeholder until the title lands.
Concurrent first chats can skip a title. There is one titling slot, so starting a second new session while the first one's title is still generating skips the second silently. The window is a few seconds. Queueing them is easy if it ever matters; a queue seemed like the wrong thing to build for that window.
Gates:
cargo fmt --check,cargo clippy --all-targets --all-features -D warnings,make bundle,cargo test --all-features(45 passing), all re-run on top ofaf0de86.No new tests. The additions are
Appstate transitions and database-backed lookups, andAppcan only be built throughApp::load, which opens the real database under Application Support — testing them would need a dependency-injection seam larger than the change. The sanitizer they feed is already covered byone_shot_titles_are_reduced_to_a_single_sidebar_line.Not verified on screen: the app was not launched, so the title replacing the placeholder in the sidebar has not been watched happening.