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 };