From 163e558b9a1291e6c578aa0e4c916598f9fe30c7 Mon Sep 17 00:00:00 2001 From: Brendan Kellam Date: Thu, 31 Jul 2025 16:38:10 -0700 Subject: [PATCH] fix(ask_sb): Fix '413 content too large' error (#416) --- CHANGELOG.md | 3 +++ .../chat/[id]/components/chatThreadPanel.tsx | 25 ++++++++----------- packages/web/src/features/chat/types.ts | 2 +- .../features/chat/useCreateNewChatThread.ts | 20 +++++++++------ 4 files changed, 26 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e28c07a7..846904b1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Fixed +- [ask sb] Fixed "413 content too large" error when starting a new chat with many repos selected. [#416](https://github.com/sourcebot-dev/sourcebot/pull/416) + ## [4.6.1] - 2025-07-29 ### Added diff --git a/packages/web/src/app/[domain]/chat/[id]/components/chatThreadPanel.tsx b/packages/web/src/app/[domain]/chat/[id]/components/chatThreadPanel.tsx index 92f3ab6e..f70d3837 100644 --- a/packages/web/src/app/[domain]/chat/[id]/components/chatThreadPanel.tsx +++ b/packages/web/src/app/[domain]/chat/[id]/components/chatThreadPanel.tsx @@ -2,12 +2,12 @@ import { ResizablePanel } from '@/components/ui/resizable'; import { ChatThread } from '@/features/chat/components/chatThread'; -import { LanguageModelInfo, SBChatMessage, SearchScope, SET_CHAT_STATE_QUERY_PARAM, SetChatStatePayload } from '@/features/chat/types'; +import { LanguageModelInfo, SBChatMessage, SearchScope, SET_CHAT_STATE_SESSION_STORAGE_KEY, SetChatStatePayload } from '@/features/chat/types'; import { RepositoryQuery, SearchContextQuery } from '@/lib/types'; import { CreateUIMessage } from 'ai'; -import { useRouter, useSearchParams } from 'next/navigation'; import { useEffect, useState } from 'react'; import { useChatId } from '../../useChatId'; +import { useSessionStorage } from 'usehooks-ts'; interface ChatThreadPanelProps { languageModels: LanguageModelInfo[]; @@ -29,9 +29,8 @@ export const ChatThreadPanel = ({ // @note: we are guaranteed to have a chatId because this component will only be // mounted when on a /chat/[id] route. const chatId = useChatId()!; - const router = useRouter(); - const searchParams = useSearchParams(); const [inputMessage, setInputMessage] = useState | undefined>(undefined); + const [chatState, setChatState] = useSessionStorage(SET_CHAT_STATE_SESSION_STORAGE_KEY, null); // Use the last user's last message to determine what repos and contexts we should select by default. const lastUserMessage = messages.findLast((message) => message.role === "user"); @@ -39,24 +38,20 @@ export const ChatThreadPanel = ({ const [selectedSearchScopes, setSelectedSearchScopes] = useState(defaultSelectedSearchScopes); useEffect(() => { - const setChatState = searchParams.get(SET_CHAT_STATE_QUERY_PARAM); - if (!setChatState) { + if (!chatState) { return; } try { - const { inputMessage, selectedSearchScopes } = JSON.parse(setChatState) as SetChatStatePayload; - setInputMessage(inputMessage); - setSelectedSearchScopes(selectedSearchScopes); + setInputMessage(chatState.inputMessage); + setSelectedSearchScopes(chatState.selectedSearchScopes); } catch { - console.error('Invalid message in URL'); + console.error('Invalid chat state in session storage'); + } finally { + setChatState(null); } - // Remove the message from the URL - const newSearchParams = new URLSearchParams(searchParams.toString()); - newSearchParams.delete(SET_CHAT_STATE_QUERY_PARAM); - router.replace(`?${newSearchParams.toString()}`, { scroll: false }); - }, [searchParams, router]); + }, [chatState, setChatState]); return ( ; diff --git a/packages/web/src/features/chat/useCreateNewChatThread.ts b/packages/web/src/features/chat/useCreateNewChatThread.ts index f0de7e1a..22489fbb 100644 --- a/packages/web/src/features/chat/useCreateNewChatThread.ts +++ b/packages/web/src/features/chat/useCreateNewChatThread.ts @@ -10,7 +10,8 @@ import { useRouter } from "next/navigation"; import { createChat } from "./actions"; import { isServiceError } from "@/lib/utils"; import { createPathWithQueryParams } from "@/lib/utils"; -import { SearchScope, SET_CHAT_STATE_QUERY_PARAM, SetChatStatePayload } from "./types"; +import { SearchScope, SET_CHAT_STATE_SESSION_STORAGE_KEY, SetChatStatePayload } from "./types"; +import { useSessionStorage } from "usehooks-ts"; export const useCreateNewChatThread = () => { const domain = useDomain(); @@ -18,6 +19,9 @@ export const useCreateNewChatThread = () => { const { toast } = useToast(); const router = useRouter(); + const [, setChatState] = useSessionStorage(SET_CHAT_STATE_SESSION_STORAGE_KEY, null); + + const createNewChatThread = useCallback(async (children: Descendant[], selectedSearchScopes: SearchScope[]) => { const text = slateContentToString(children); const mentions = getAllMentionElements(children); @@ -34,16 +38,16 @@ export const useCreateNewChatThread = () => { return; } - const url = createPathWithQueryParams(`/${domain}/chat/${response.id}`, - [SET_CHAT_STATE_QUERY_PARAM, JSON.stringify({ - inputMessage, - selectedSearchScopes, - } satisfies SetChatStatePayload)], - ); + setChatState({ + inputMessage, + selectedSearchScopes, + }); + + const url = createPathWithQueryParams(`/${domain}/chat/${response.id}`); router.push(url); router.refresh(); - }, [domain, router, toast]); + }, [domain, router, toast, setChatState]); return { createNewChatThread,