From d003613bb1a960efa36723c3b31e4697a8335b34 Mon Sep 17 00:00:00 2001 From: shatfield4 Date: Tue, 4 Jun 2024 16:00:05 -0700 Subject: [PATCH 1/4] make thread name 'Thread' when new thread is created --- server/models/workspaceThread.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/models/workspaceThread.js b/server/models/workspaceThread.js index a2a96f31097..6077bfd5a22 100644 --- a/server/models/workspaceThread.js +++ b/server/models/workspaceThread.js @@ -8,7 +8,7 @@ const WorkspaceThread = { try { const thread = await prisma.workspace_threads.create({ data: { - name: "New thread", + name: "Thread", slug: uuidv4(), user_id: userId ? Number(userId) : null, workspace_id: workspace.id, From 433a823fd6a7f28c41dc762dba25babaf8953b40 Mon Sep 17 00:00:00 2001 From: shatfield4 Date: Wed, 5 Jun 2024 13:53:52 -0700 Subject: [PATCH 2/4] implement auto generated thread titles --- server/endpoints/chat.js | 30 ++++++++++++++++++++++++++++++ server/utils/threadNames/index.js | 30 ++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 server/utils/threadNames/index.js diff --git a/server/endpoints/chat.js b/server/endpoints/chat.js index 7445c213459..9a3c0802ff2 100644 --- a/server/endpoints/chat.js +++ b/server/endpoints/chat.js @@ -15,6 +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"); function chatEndpoints(app) { if (!app) return; @@ -196,6 +198,34 @@ function chatEndpoints(app) { user, thread ); + + // Check if first message in thread + const chatCount = await WorkspaceChats.count({ + workspaceId: workspace.id, + user_id: user?.id || null, + thread_id: thread.id, + }); + + // Generate thread name + 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"); + } + } + } catch (e) { + console.log("Error generating thread title:", e); + } + } + await Telemetry.sendTelemetry("sent_chat", { multiUserMode: multiUserMode(response), LLMSelection: process.env.LLM_PROVIDER || "openai", diff --git a/server/utils/threadNames/index.js b/server/utils/threadNames/index.js new file mode 100644 index 00000000000..80fdf7d2594 --- /dev/null +++ b/server/utils/threadNames/index.js @@ -0,0 +1,30 @@ +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; From 25b4e58933891a69e3173393b4311cd02dd8716a Mon Sep 17 00:00:00 2001 From: shatfield4 Date: Thu, 6 Jun 2024 18:32:00 -0700 Subject: [PATCH 3/4] implement truncated prompt as thread name --- .../ThreadContainer/ThreadItem/index.jsx | 15 ++-------- .../ThreadContainer/index.jsx | 20 +++++++++++++ .../WorkspaceChat/ChatContainer/index.jsx | 13 ++++++++ server/endpoints/chat.js | 21 ++++++------- server/package.json | 1 + server/utils/threadNames/index.js | 30 ------------------- server/yarn.lock | 5 ++++ 7 files changed, 51 insertions(+), 54 deletions(-) delete mode 100644 server/utils/threadNames/index.js diff --git a/frontend/src/components/Sidebar/ActiveWorkspaces/ThreadContainer/ThreadItem/index.jsx b/frontend/src/components/Sidebar/ActiveWorkspaces/ThreadContainer/ThreadItem/index.jsx index 87fd555870e..c4062102e68 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 d1c0ba8c287..f2d99cd8725 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 28d87e0dfa5..6e32d23f8ec 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 9a3c0802ff2..13e620e7a44 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 1b0ba2802e8..9cc27c8b08f 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 80fdf7d2594..00000000000 --- 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 c6cf4c2c38e..a05c62fc40f 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" From 66d64fab345804f5e1fba4d33358f37dcd68f116 Mon Sep 17 00:00:00 2001 From: timothycarambat Date: Fri, 7 Jun 2024 14:05:29 -0700 Subject: [PATCH 4/4] move rename of thread into workspaceThread function --- server/endpoints/chat.js | 27 +++++---------------------- server/models/workspaceThread.js | 19 +++++++++++++++++++ 2 files changed, 24 insertions(+), 22 deletions(-) diff --git a/server/endpoints/chat.js b/server/endpoints/chat.js index 13e620e7a44..915acbf9596 100644 --- a/server/endpoints/chat.js +++ b/server/endpoints/chat.js @@ -199,30 +199,13 @@ function chatEndpoints(app) { thread ); - // Check if first message in thread - const chatCount = await WorkspaceChats.count({ - workspaceId: workspace.id, - user_id: user?.id || null, - thread_id: thread.id, + await WorkspaceThread.autoRenameThread({ + thread, + workspace, + user, + newName: truncate(message, 22), }); - // Set thread title to truncated message - if (chatCount === 1) { - try { - 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); - } - } - await Telemetry.sendTelemetry("sent_chat", { multiUserMode: multiUserMode(response), LLMSelection: process.env.LLM_PROVIDER || "openai", diff --git a/server/models/workspaceThread.js b/server/models/workspaceThread.js index 6077bfd5a22..ffbc8aedb9d 100644 --- a/server/models/workspaceThread.js +++ b/server/models/workspaceThread.js @@ -84,6 +84,25 @@ const WorkspaceThread = { return []; } }, + + // Will fire on first message (included or not) for a thread and rename the thread with the newName prop. + autoRenameThread: async function ({ + workspace = null, + thread = null, + user = null, + newName = null, + }) { + if (!workspace || !thread || !newName) return false; + const { WorkspaceChats } = require("./workspaceChats"); + const chatCount = await WorkspaceChats.count({ + workspaceId: workspace.id, + user_id: user?.id || null, + thread_id: thread.id, + }); + if (chatCount !== 1) return false; + await this.update(thread, { name: newName }); + return true; + }, }; module.exports = { WorkspaceThread };