Chat title generation

This commit is contained in:
2026-02-14 21:27:44 -08:00
parent 7ef2825c16
commit 684d441763
4 changed files with 144 additions and 0 deletions

View File

@@ -18,6 +18,7 @@ import {
listSearches,
runCompletionStream,
runSearchStream,
suggestChatTitle,
type ModelCatalogResponse,
type Provider,
type ChatDetail,
@@ -269,6 +270,7 @@ export default function App() {
const transcriptEndRef = useRef<HTMLDivElement>(null);
const contextMenuRef = useRef<HTMLDivElement>(null);
const selectedItemRef = useRef<SidebarSelection | null>(null);
const pendingTitleGenerationRef = useRef<Set<string>>(new Set());
const searchRunAbortRef = useRef<AbortController | null>(null);
const searchRunCounterRef = useRef(0);
const [contextMenu, setContextMenu] = useState<ContextMenuState | null>(null);
@@ -580,6 +582,10 @@ export default function App() {
const chat = await createChat();
chatId = chat.id;
setDraftKind(null);
setChats((current) => {
const withoutExisting = current.filter((existing) => existing.id !== chat.id);
return [chat, ...withoutExisting];
});
setSelectedItem({ kind: "chat", id: chatId });
setPendingChatState((current) => (current ? { ...current, chatId } : current));
setSelectedChat({
@@ -618,6 +624,31 @@ export default function App() {
throw new Error("No model available for selected provider");
}
const chatSummary = chats.find((chat) => chat.id === chatId);
const hasExistingTitle = Boolean(selectedChat?.id === chatId ? selectedChat.title?.trim() : chatSummary?.title?.trim());
if (!hasExistingTitle && !pendingTitleGenerationRef.current.has(chatId)) {
pendingTitleGenerationRef.current.add(chatId);
void suggestChatTitle({ chatId, content })
.then((updatedChat) => {
setChats((current) =>
current.map((chat) => {
if (chat.id !== updatedChat.id) return chat;
return { ...chat, title: updatedChat.title, updatedAt: updatedChat.updatedAt };
})
);
setSelectedChat((current) => {
if (!current || current.id !== updatedChat.id) return current;
return { ...current, title: updatedChat.title, updatedAt: updatedChat.updatedAt };
});
})
.catch(() => {
// ignore title suggestion errors so chat flow is not interrupted
})
.finally(() => {
pendingTitleGenerationRef.current.delete(chatId);
});
}
let streamErrorMessage: string | null = null;
await runCompletionStream(