mirror of
https://github.com/Mintplex-Labs/anything-llm
synced 2026-04-25 17:15:37 +02:00
* New chat history layout with chat bubbles (#4985) * new chat history layout, remove message alignment setting * remove orphaned chat alignment hook and MessageDirection * remove workspace profile picture setting and fetch * clean up unnecessary changes * add light mode colors to chat ui and main page backgrounds * update chat message and action icon colors for light mode * update thinking and agent ui, layout, sizing * update user message uploaded images ui * update thought, agent containers to use new colors * add truncatable content with gradient to user chat messages * fix citations margin * implement new edit message UI with save and submit actions * add translations for TruncatableContent subcomponent * remove unused props * fix text colors for default mode chats, agent, thoughts container * Normalize translations for new chat history layout (#5022) * normalize translations * update translations with DMR * lint * fix mismatched home container colors * fix: add password character validation to onboarding single-user setup (#5037) * fix single user mode password bug * share const --------- Co-authored-by: Timothy Carambat <rambat1010@gmail.com> * Native Tool calling (#5071) * checkpoint * test MCP and flows * add native tool call detection back to LMStudio * add native tool call loops for Ollama * Add ablity detection to DMR (regex parse) * bedrock and generic openai with ENV flag * deepseek native tool calling * localAI native function * groq support * linting, add litellm and OR native tool calling via flag * fix: resolve Gemini agent 400 error on tool call responses (#5054) * add gtc__ prefix to tool call names in Gemini agent message formatting * resolve Gemini agent 400 error on tool call responses * add comments explaining geminis thought signatures --------- Co-authored-by: Timothy Carambat <rambat1010@gmail.com> * fix: prevent CMD/CTRL+Arrow scroll from overriding textarea cursor movement (#5053) prevent CMD/CTRL+Arrow scroll from overriding textarea cursor movement Co-authored-by: Timothy Carambat <rambat1010@gmail.com> * linting, assistant speaker spacing and order, copy/edit order --------- Co-authored-by: Marcello Fitton <106866560+angelplusultra@users.noreply.github.com> Co-authored-by: Timothy Carambat <rambat1010@gmail.com> * Implement new citations UI (#5038) * new chat history layout, remove message alignment setting * remove orphaned chat alignment hook and MessageDirection * remove workspace profile picture setting and fetch * clean up unnecessary changes * add light mode colors to chat ui and main page backgrounds * update chat message and action icon colors for light mode * update thinking and agent ui, layout, sizing * update user message uploaded images ui * update thought, agent containers to use new colors * add truncatable content with gradient to user chat messages * fix citations margin * implement new edit message UI with save and submit actions * add translations for TruncatableContent subcomponent * remove unused props * fix text colors for default mode chats, agent, thoughts container * Normalize translations for new chat history layout (#5022) * normalize translations * update translations with DMR * lint * fix mismatched home container colors * implement new citations ui with sources sidebar * bottom sheet for mobile citations * convert mobile citations bottom sheet to new modal design * add score, border separators for mobile citations modal * push down sources sidebar in password/multiuser mode * fix animation gap, simplify sources sidebar by splitting state to persist data on animation * add english translations * fix spacing from citations sidebar when user has auth * Normalize translations for new citation UI (#5087) * normalize translations * update translations using DMR * fix pluralize to use i18n native solution change reset to immediate clear fix spacing for TTS when showing or not to not have space * proper pluralize * hide metrics on mobile, fix last message padding on mobile --------- Co-authored-by: Timothy Carambat <rambat1010@gmail.com> * New prompt input ui/tools menu (#5070) * wip new prompt input ui/tools menu * fix colors for prompt input * redesign workspace llm selector, extract text size + model picker to components * refactor ToolsMenu component * fix colors/refactor WorkspaceModelPicker * fix spacing in ws model picker, change order of tools menu tabs * fix slash commands showing /reset instead of /exit during active agent session * refactor ToolsMenu to be much simpler * cleanup, fix behavior of setupup provider in WorkspaceModelPicker * simplify AgentSkillsTab toggle logic * add english translations for new components * remove legacy slash command/agent popups, add ToolsMenu keyboard nav * fix spacing of workspace model picker text * fix SourcesSidebar and TextSizeMenu positioning after merge * fix keyboard nav in ToolsMenu when clicking on tools button to open * typo * only auto pop up tools menu when prompt input is empty with / * fix z index for tools menu on citation * fix behavior of / in prompt input * move global window agent session state to module level variable * fix prompt input not clearing on /reset * missing translations * revert translating slash command * fix STT auto-submit not working on home page * Normalize translations for new prompt input/tools menu UI (#5130) * normalize translations * update translations using DMR script * normalize translations * update translations using DMR script * remove slash_exit * fix skills.js import after merge * fix tooltip z-index rendering behind citations * patch translation prune script to not remove special cases * updates to tools input * factory translations * use safeJsonParse in clearPromptInputDraft * normalize translations * disable agent skill toggles during active agent sessions + show tooltip on disabled * normalize translations * handle enter key behavior when tools menu is open * fix unfocusable modal for slash command edit/new * fix sending prompt when editing/creating slash commands * hide/show agent skills in tools menu based on role * container borders for dark/light mode compliance to designs --------- Co-authored-by: Timothy Carambat <rambat1010@gmail.com> * update how tooltip works for agent menu * update prompt input to show agent button with CTA in agent panel for user clarify update agent session start prompt button in input * translations * translations + move regex for slash commands to constants * fix open sidebar ux * fix tools menu to always open to slash commands, dismiss auto pop up * fix sidebar open/close button overlapping with ws model picker --------- Co-authored-by: Sean Hatfield <seanhatfield5@gmail.com> Co-authored-by: Marcello Fitton <106866560+angelplusultra@users.noreply.github.com>
259 lines
7.5 KiB
JavaScript
259 lines
7.5 KiB
JavaScript
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, // Do not include API session chats.
|
|
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, role = "assistant" } = reqBody(request);
|
|
if (!newText || !String(newText).trim())
|
|
throw new Error("Cannot save empty edit");
|
|
|
|
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.");
|
|
|
|
if (role === "user") {
|
|
await WorkspaceChats._update(existingChat.id, {
|
|
prompt: String(newText),
|
|
});
|
|
} else {
|
|
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 };
|