|
|
const { |
|
|
multiUserMode, |
|
|
userFromSession, |
|
|
reqBody, |
|
|
safeJsonParse, |
|
|
} = require("../utils/http"); |
|
|
const { validatedRequest } = require("../utils/middleware/validatedRequest"); |
|
|
const { Telemetry } = require("../models/telemetry"); |
|
|
const { |
|
|
flexUserRoleValid, |
|
|
ROLES, |
|
|
} = require("../utils/middleware/multiUserProtected"); |
|
|
const { EventLogs } = require("../models/eventLogs"); |
|
|
const { WorkspaceThread } = require("../models/workspaceThread"); |
|
|
const { |
|
|
validWorkspaceSlug, |
|
|
validWorkspaceAndThreadSlug, |
|
|
} = require("../utils/middleware/validWorkspace"); |
|
|
const { WorkspaceChats } = require("../models/workspaceChats"); |
|
|
const { convertToChatHistory } = require("../utils/helpers/chat/responses"); |
|
|
const { getModelTag } = require("./utils"); |
|
|
|
|
|
function workspaceThreadEndpoints(app) { |
|
|
if (!app) return; |
|
|
|
|
|
app.post( |
|
|
"/workspace/:slug/thread/new", |
|
|
[validatedRequest, flexUserRoleValid([ROLES.all]), validWorkspaceSlug], |
|
|
async (request, response) => { |
|
|
try { |
|
|
const user = await userFromSession(request, response); |
|
|
const workspace = response.locals.workspace; |
|
|
const { thread, message } = await WorkspaceThread.new( |
|
|
workspace, |
|
|
user?.id |
|
|
); |
|
|
await Telemetry.sendTelemetry( |
|
|
"workspace_thread_created", |
|
|
{ |
|
|
multiUserMode: multiUserMode(response), |
|
|
LLMSelection: process.env.LLM_PROVIDER || "openai", |
|
|
Embedder: process.env.EMBEDDING_ENGINE || "inherit", |
|
|
VectorDbSelection: process.env.VECTOR_DB || "lancedb", |
|
|
TTSSelection: process.env.TTS_PROVIDER || "native", |
|
|
LLMModel: getModelTag(), |
|
|
}, |
|
|
user?.id |
|
|
); |
|
|
|
|
|
await EventLogs.logEvent( |
|
|
"workspace_thread_created", |
|
|
{ |
|
|
workspaceName: workspace?.name || "Unknown Workspace", |
|
|
}, |
|
|
user?.id |
|
|
); |
|
|
response.status(200).json({ thread, message }); |
|
|
} catch (e) { |
|
|
console.error(e.message, e); |
|
|
response.sendStatus(500).end(); |
|
|
} |
|
|
} |
|
|
); |
|
|
|
|
|
app.get( |
|
|
"/workspace/:slug/threads", |
|
|
[validatedRequest, flexUserRoleValid([ROLES.all]), validWorkspaceSlug], |
|
|
async (request, response) => { |
|
|
try { |
|
|
const user = await userFromSession(request, response); |
|
|
const workspace = response.locals.workspace; |
|
|
const threads = await WorkspaceThread.where({ |
|
|
workspace_id: workspace.id, |
|
|
user_id: user?.id || null, |
|
|
}); |
|
|
response.status(200).json({ threads }); |
|
|
} catch (e) { |
|
|
console.error(e.message, e); |
|
|
response.sendStatus(500).end(); |
|
|
} |
|
|
} |
|
|
); |
|
|
|
|
|
app.delete( |
|
|
"/workspace/:slug/thread/:threadSlug", |
|
|
[ |
|
|
validatedRequest, |
|
|
flexUserRoleValid([ROLES.all]), |
|
|
validWorkspaceAndThreadSlug, |
|
|
], |
|
|
async (_, response) => { |
|
|
try { |
|
|
const thread = response.locals.thread; |
|
|
await WorkspaceThread.delete({ id: thread.id }); |
|
|
response.sendStatus(200).end(); |
|
|
} catch (e) { |
|
|
console.error(e.message, e); |
|
|
response.sendStatus(500).end(); |
|
|
} |
|
|
} |
|
|
); |
|
|
|
|
|
app.delete( |
|
|
"/workspace/:slug/thread-bulk-delete", |
|
|
[validatedRequest, flexUserRoleValid([ROLES.all]), validWorkspaceSlug], |
|
|
async (request, response) => { |
|
|
try { |
|
|
const { slugs = [] } = reqBody(request); |
|
|
if (slugs.length === 0) return response.sendStatus(200).end(); |
|
|
|
|
|
const user = await userFromSession(request, response); |
|
|
const workspace = response.locals.workspace; |
|
|
await WorkspaceThread.delete({ |
|
|
slug: { in: slugs }, |
|
|
user_id: user?.id ?? null, |
|
|
workspace_id: workspace.id, |
|
|
}); |
|
|
response.sendStatus(200).end(); |
|
|
} catch (e) { |
|
|
console.error(e.message, e); |
|
|
response.sendStatus(500).end(); |
|
|
} |
|
|
} |
|
|
); |
|
|
|
|
|
app.get( |
|
|
"/workspace/:slug/thread/:threadSlug/chats", |
|
|
[ |
|
|
validatedRequest, |
|
|
flexUserRoleValid([ROLES.all]), |
|
|
validWorkspaceAndThreadSlug, |
|
|
], |
|
|
async (request, response) => { |
|
|
try { |
|
|
const user = await userFromSession(request, response); |
|
|
const workspace = response.locals.workspace; |
|
|
const thread = response.locals.thread; |
|
|
const history = await WorkspaceChats.where( |
|
|
{ |
|
|
workspaceId: workspace.id, |
|
|
user_id: user?.id || null, |
|
|
thread_id: thread.id, |
|
|
api_session_id: null, |
|
|
include: true, |
|
|
}, |
|
|
null, |
|
|
{ id: "asc" } |
|
|
); |
|
|
|
|
|
response.status(200).json({ history: convertToChatHistory(history) }); |
|
|
} catch (e) { |
|
|
console.error(e.message, e); |
|
|
response.sendStatus(500).end(); |
|
|
} |
|
|
} |
|
|
); |
|
|
|
|
|
app.post( |
|
|
"/workspace/:slug/thread/:threadSlug/update", |
|
|
[ |
|
|
validatedRequest, |
|
|
flexUserRoleValid([ROLES.all]), |
|
|
validWorkspaceAndThreadSlug, |
|
|
], |
|
|
async (request, response) => { |
|
|
try { |
|
|
const data = reqBody(request); |
|
|
const currentThread = response.locals.thread; |
|
|
const { thread, message } = await WorkspaceThread.update( |
|
|
currentThread, |
|
|
data |
|
|
); |
|
|
response.status(200).json({ thread, message }); |
|
|
} catch (e) { |
|
|
console.error(e.message, e); |
|
|
response.sendStatus(500).end(); |
|
|
} |
|
|
} |
|
|
); |
|
|
|
|
|
app.delete( |
|
|
"/workspace/:slug/thread/:threadSlug/delete-edited-chats", |
|
|
[ |
|
|
validatedRequest, |
|
|
flexUserRoleValid([ROLES.all]), |
|
|
validWorkspaceAndThreadSlug, |
|
|
], |
|
|
async (request, response) => { |
|
|
try { |
|
|
const { startingId } = reqBody(request); |
|
|
const user = await userFromSession(request, response); |
|
|
const workspace = response.locals.workspace; |
|
|
const thread = response.locals.thread; |
|
|
|
|
|
await WorkspaceChats.delete({ |
|
|
workspaceId: Number(workspace.id), |
|
|
thread_id: Number(thread.id), |
|
|
user_id: user?.id, |
|
|
id: { gte: Number(startingId) }, |
|
|
}); |
|
|
|
|
|
response.sendStatus(200).end(); |
|
|
} catch (e) { |
|
|
console.error(e.message, e); |
|
|
response.sendStatus(500).end(); |
|
|
} |
|
|
} |
|
|
); |
|
|
|
|
|
app.post( |
|
|
"/workspace/:slug/thread/:threadSlug/update-chat", |
|
|
[ |
|
|
validatedRequest, |
|
|
flexUserRoleValid([ROLES.all]), |
|
|
validWorkspaceAndThreadSlug, |
|
|
], |
|
|
async (request, response) => { |
|
|
try { |
|
|
const { chatId, newText = null } = reqBody(request); |
|
|
if (!newText || !String(newText).trim()) |
|
|
throw new Error("Cannot save empty response"); |
|
|
|
|
|
const user = await userFromSession(request, response); |
|
|
const workspace = response.locals.workspace; |
|
|
const thread = response.locals.thread; |
|
|
const existingChat = await WorkspaceChats.get({ |
|
|
workspaceId: workspace.id, |
|
|
thread_id: thread.id, |
|
|
user_id: user?.id, |
|
|
id: Number(chatId), |
|
|
}); |
|
|
if (!existingChat) throw new Error("Invalid chat."); |
|
|
|
|
|
const chatResponse = safeJsonParse(existingChat.response, null); |
|
|
if (!chatResponse) throw new Error("Failed to parse chat response"); |
|
|
|
|
|
await WorkspaceChats._update(existingChat.id, { |
|
|
response: JSON.stringify({ |
|
|
...chatResponse, |
|
|
text: String(newText), |
|
|
}), |
|
|
}); |
|
|
|
|
|
response.sendStatus(200).end(); |
|
|
} catch (e) { |
|
|
console.error(e.message, e); |
|
|
response.sendStatus(500).end(); |
|
|
} |
|
|
} |
|
|
); |
|
|
} |
|
|
|
|
|
module.exports = { workspaceThreadEndpoints }; |
|
|
|