diff --git a/frontend/src/components/Sidebar/ActiveWorkspaces/ThreadContainer/ThreadItem/index.jsx b/frontend/src/components/Sidebar/ActiveWorkspaces/ThreadContainer/ThreadItem/index.jsx index 87fd5558..c4062102 100644 --- a/frontend/src/components/Sidebar/ActiveWorkspaces/ThreadContainer/ThreadItem/index.jsx +++ b/frontend/src/components/Sidebar/ActiveWorkspaces/ThreadContainer/ThreadItem/index.jsx @@ -27,7 +27,6 @@ export default function ThreadItem({ const { slug } = useParams(); const optionsContainer = useRef(null); const [showOptions, setShowOptions] = useState(false); - const [name, setName] = useState(thread.name); const linkTo = !thread.slug ? paths.workspace.chat(slug) : paths.workspace.thread(slug, thread.slug); @@ -97,7 +96,7 @@ export default function ThreadItem({ isActive ? "font-medium text-white" : "text-slate-400" }`} > - {truncate(name, 25)} + {truncate(thread.name, 25)}

)} @@ -133,7 +132,6 @@ export default function ThreadItem({ workspace={workspace} thread={thread} onRemove={onRemove} - onRename={setName} close={() => setShowOptions(false)} /> )} @@ -144,14 +142,7 @@ export default function ThreadItem({ ); } -function OptionsMenu({ - containerRef, - workspace, - thread, - onRename, - onRemove, - close, -}) { +function OptionsMenu({ containerRef, workspace, thread, onRemove, close }) { const menuRef = useRef(null); // Ref menu options @@ -208,7 +199,7 @@ function OptionsMenu({ return; } - onRename(name); + thread.name = name; close(); }; diff --git a/frontend/src/components/Sidebar/ActiveWorkspaces/ThreadContainer/index.jsx b/frontend/src/components/Sidebar/ActiveWorkspaces/ThreadContainer/index.jsx index d1c0ba8c..f2d99cd8 100644 --- a/frontend/src/components/Sidebar/ActiveWorkspaces/ThreadContainer/index.jsx +++ b/frontend/src/components/Sidebar/ActiveWorkspaces/ThreadContainer/index.jsx @@ -12,6 +12,26 @@ export default function ThreadContainer({ workspace }) { const [loading, setLoading] = useState(true); const [ctrlPressed, setCtrlPressed] = useState(false); + useEffect(() => { + const chatHandler = (event) => { + const { threadSlug, newName } = event.detail; + setThreads((prevThreads) => + prevThreads.map((thread) => { + if (thread.slug === threadSlug) { + return { ...thread, name: newName }; + } + return thread; + }) + ); + }; + + window.addEventListener("renameThread", chatHandler); + + return () => { + window.removeEventListener("renameThread", chatHandler); + }; + }, []); + useEffect(() => { async function fetchThreads() { if (!workspace.slug) return; diff --git a/frontend/src/components/WorkspaceChat/ChatContainer/index.jsx b/frontend/src/components/WorkspaceChat/ChatContainer/index.jsx index 28d87e0d..6e32d23f 100644 --- a/frontend/src/components/WorkspaceChat/ChatContainer/index.jsx +++ b/frontend/src/components/WorkspaceChat/ChatContainer/index.jsx @@ -12,6 +12,7 @@ import handleSocketResponse, { AGENT_SESSION_END, AGENT_SESSION_START, } from "@/utils/chat/agent"; +import truncate from "truncate"; export default function ChatContainer({ workspace, knownHistory = [] }) { const { threadSlug = null } = useParams(); @@ -39,6 +40,18 @@ export default function ChatContainer({ workspace, knownHistory = [] }) { event.preventDefault(); if (!message || message === "") return false; + // If first message and it is a thread + // and message is not blank/whitespace, + // then send event to rename the thread + if (threadSlug && chatHistory.length === 0 && message.trim().length > 0) { + const truncatedName = truncate(message, 22); + window.dispatchEvent( + new CustomEvent("renameThread", { + detail: { threadSlug, newName: truncatedName }, + }) + ); + } + const prevChatHistory = [ ...chatHistory, { content: message, role: "user" }, diff --git a/server/endpoints/chat.js b/server/endpoints/chat.js index 9a3c0802..13e620e7 100644 --- a/server/endpoints/chat.js +++ b/server/endpoints/chat.js @@ -15,8 +15,8 @@ const { validWorkspaceSlug, } = require("../utils/middleware/validWorkspace"); const { writeResponseChunk } = require("../utils/helpers/chat/responses"); -const generateThreadTitle = require("../utils/threadNames"); const { WorkspaceThread } = require("../models/workspaceThread"); +const truncate = require("truncate"); function chatEndpoints(app) { if (!app) return; @@ -206,20 +206,17 @@ function chatEndpoints(app) { thread_id: thread.id, }); - // Generate thread name + // Set thread title to truncated message if (chatCount === 1) { try { - const generatedTitle = await generateThreadTitle(message); - if (generatedTitle) { - const { thread: updatedThread } = await WorkspaceThread.update( - thread, - { - name: generatedTitle, - } - ); - if (!updatedThread) { - console.log("Failed to update thread name"); + const { thread: updatedThread } = await WorkspaceThread.update( + thread, + { + name: truncate(message, 22), } + ); + if (!updatedThread) { + console.log("Failed to update thread name"); } } catch (e) { console.log("Error generating thread title:", e); diff --git a/server/package.json b/server/package.json index 1b0ba280..9cc27c8b 100644 --- a/server/package.json +++ b/server/package.json @@ -75,6 +75,7 @@ "sqlite3": "^5.1.6", "swagger-autogen": "^2.23.5", "swagger-ui-express": "^5.0.0", + "truncate": "^3.0.0", "url-pattern": "^1.0.3", "uuid": "^9.0.0", "uuid-apikey": "^1.5.3", diff --git a/server/utils/threadNames/index.js b/server/utils/threadNames/index.js deleted file mode 100644 index 80fdf7d2..00000000 --- a/server/utils/threadNames/index.js +++ /dev/null @@ -1,30 +0,0 @@ -const { getLLMProvider } = require("../helpers"); - -async function generateThreadTitle(prompt) { - const systemPrompt = - "Listen to any instructions below and do not give any description or explanation when replying. Do not return anything else other than what is asked."; - const getTitlePrompt = `Take the message below and generate a short and concise title for a thread for it (max 22 characters or less). Do not return anything else. - Message:${prompt}\n\nTitle:`; - - const LLMConnector = getLLMProvider(); - const messages = await LLMConnector.compressMessages( - { - systemPrompt: systemPrompt, - userPrompt: getTitlePrompt, - }, - [] - ); - - const title = await LLMConnector.getChatCompletion(messages, { - temperature: LLMConnector.defaultTemp, - }); - - // truncate title to 22 characters - const maxLength = 22; - const truncatedTitle = - title.length > maxLength ? title.slice(0, maxLength - 3) + "..." : title; - - return truncatedTitle; -} - -module.exports = generateThreadTitle; diff --git a/server/yarn.lock b/server/yarn.lock index c6cf4c2c..a05c62fc 100644 --- a/server/yarn.lock +++ b/server/yarn.lock @@ -6211,6 +6211,11 @@ triple-beam@^1.3.0: resolved "https://registry.yarnpkg.com/triple-beam/-/triple-beam-1.4.1.tgz#6fde70271dc6e5d73ca0c3b24e2d92afb7441984" integrity sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg== +truncate@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/truncate/-/truncate-3.0.0.tgz#7dbe19e2f72c614e36b79bab00fbfbeb1cbaf078" + integrity sha512-C+0Xojw7wZPl6MDq5UjMTuxZvBPK04mtdFet7k+GSZPINcvLZFCXg+15kWIL4wAqDB7CksIsKiRLbQ1wa7rKdw== + tslib@^2.2.0, tslib@^2.4.0, tslib@^2.5.3, tslib@^2.6.2: version "2.6.2" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae"