diff --git a/.github/workflows/build-and-push-image-semver.yaml b/.github/workflows/build-and-push-image-semver.yaml
index 9d572a46677..1da1dda9df3 100644
--- a/.github/workflows/build-and-push-image-semver.yaml
+++ b/.github/workflows/build-and-push-image-semver.yaml
@@ -4,13 +4,9 @@ concurrency:
group: build-${{ github.ref }}
cancel-in-progress: true
-# on:
-# release:
-# types: [published]
-
on:
- push:
- branches: ['master']
+ release:
+ types: [published]
jobs:
push_multi_platform_to_registries:
@@ -67,11 +63,8 @@ jobs:
${{ steps.dockerhub.outputs.enabled == 'true' && 'mintplexlabs/anythingllm' || '' }}
ghcr.io/${{ github.repository }}
tags: |
- type=semver,pattern=1.8.2
- type=semver,pattern=1.8
- # tags: |
- # type=semver,pattern={{version}}
- # type=semver,pattern={{major}}.{{minor}}
+ type=semver,pattern={{version}}
+ type=semver,pattern={{major}}.{{minor}}
- name: Build and push multi-platform Docker image
uses: docker/build-push-action@v6
diff --git a/frontend/src/components/WorkspaceChat/ChatContainer/PromptInput/SlashCommands/SlashPresets/index.jsx b/frontend/src/components/WorkspaceChat/ChatContainer/PromptInput/SlashCommands/SlashPresets/index.jsx
index 715a149dbb6..a8cba21fb92 100644
--- a/frontend/src/components/WorkspaceChat/ChatContainer/PromptInput/SlashCommands/SlashPresets/index.jsx
+++ b/frontend/src/components/WorkspaceChat/ChatContainer/PromptInput/SlashCommands/SlashPresets/index.jsx
@@ -10,7 +10,12 @@ import { useSearchParams } from "react-router-dom";
import { useTranslation } from "react-i18next";
export const CMD_REGEX = new RegExp(/[^a-zA-Z0-9_-]/g);
-export default function SlashPresets({ setShowing, sendCommand, promptRef }) {
+export default function SlashPresets({
+ setShowing,
+ sendCommand,
+ promptRef,
+ workspace,
+}) {
const { t } = useTranslation();
const isActiveAgentSession = useIsAgentSessionActive();
const {
@@ -27,6 +32,11 @@ export default function SlashPresets({ setShowing, sendCommand, promptRef }) {
const [selectedPreset, setSelectedPreset] = useState(null);
const [searchParams] = useSearchParams();
+ const fetchPresets = async () => {
+ const presets = await System.getSlashCommandPresets();
+ setPresets(presets);
+ };
+
useEffect(() => {
fetchPresets();
}, []);
@@ -44,12 +54,8 @@ export default function SlashPresets({ setShowing, sendCommand, promptRef }) {
openAddModal();
}, []);
- if (isActiveAgentSession) return null;
-
- const fetchPresets = async () => {
- const presets = await System.getSlashCommandPresets();
- setPresets(presets);
- };
+ // Hide presets if there's an active agent session OR workspace is in agent mode
+ if (isActiveAgentSession || workspace?.chatMode === "agent") return null;
const handleSavePreset = async (preset) => {
const { error } = await System.createSlashCommandPreset(preset);
diff --git a/frontend/src/components/WorkspaceChat/ChatContainer/PromptInput/SlashCommands/endAgentSession.jsx b/frontend/src/components/WorkspaceChat/ChatContainer/PromptInput/SlashCommands/endAgentSession.jsx
index 6a8d70f6eb1..d24974d5885 100644
--- a/frontend/src/components/WorkspaceChat/ChatContainer/PromptInput/SlashCommands/endAgentSession.jsx
+++ b/frontend/src/components/WorkspaceChat/ChatContainer/PromptInput/SlashCommands/endAgentSession.jsx
@@ -1,8 +1,15 @@
import { useIsAgentSessionActive } from "@/utils/chat/agent";
-export default function EndAgentSession({ setShowing, sendCommand }) {
+export default function EndAgentSession({
+ setShowing,
+ sendCommand,
+ workspace,
+}) {
const isActiveAgentSession = useIsAgentSessionActive();
- if (!isActiveAgentSession) return null;
+
+ // Only show exit command if there's an active agent session AND workspace is NOT in agent mode
+ // If workspace is in agent mode, we don't want users to exit the agent
+ if (!isActiveAgentSession || workspace?.chatMode === "agent") return null;
return (
+ {
+ setChatMode("agent");
+ setHasChanges(true);
+ }}
+ className="transition-bg duration-200 px-6 py-1 text-md text-white/60 disabled:text-white bg-transparent disabled:bg-[#687280] rounded-md"
+ >
+ {t("chat.mode.agent.title")}
+
{chatMode === "chat" ? (
@@ -45,13 +56,17 @@ export default function ChatModeSelection({ workspace, setHasChanges }) {
{t("chat.mode.chat.and")}{" "}
{t("chat.mode.chat.desc-end")}
>
- ) : (
+ ) : chatMode === "query" ? (
<>
{t("chat.mode.query.title")}{" "}
{t("chat.mode.query.desc-start")}{" "}
{t("chat.mode.query.only")}{" "}
{t("chat.mode.query.desc-end")}
>
+ ) : (
+ <>
+ {t("chat.mode.agent.title")} {t("chat.mode.agent.desc")}
+ >
)}
diff --git a/frontend/src/utils/chat/agent.js b/frontend/src/utils/chat/agent.js
index ad1193d304c..9d1aace36f5 100644
--- a/frontend/src/utils/chat/agent.js
+++ b/frontend/src/utils/chat/agent.js
@@ -12,6 +12,7 @@ const handledEvents = [
"awaitingFeedback",
"wssFailure",
"rechartVisualize",
+ "chatReset",
];
export function websocketURI() {
@@ -88,6 +89,25 @@ export default function handleSocketResponse(event, setChatHistory) {
});
}
+ // Handle action parameter for WebSocket messages (similar to HTTP chat)
+ if (data.action === "reset_chat") {
+ return setChatHistory((prev) => {
+ return [
+ {
+ uuid: v4(),
+ type: data.type,
+ content: data.content,
+ role: "assistant",
+ sources: [],
+ closed: true,
+ error: null,
+ animate: data?.animate || false,
+ pending: false,
+ },
+ ];
+ });
+ }
+
return setChatHistory((prev) => {
return [
...prev.filter((msg) => !!msg.content),
diff --git a/server/models/workspace.js b/server/models/workspace.js
index c195b176143..9c7aa566dab 100644
--- a/server/models/workspace.js
+++ b/server/models/workspace.js
@@ -93,7 +93,7 @@ const Workspace = {
return n;
},
chatMode: (value) => {
- if (!value || !["chat", "query"].includes(value)) return "chat";
+ if (!value || !["chat", "query", "agent"].includes(value)) return "chat";
return value;
},
chatProvider: (value) => {
diff --git a/server/prisma/schema.prisma b/server/prisma/schema.prisma
index fbadf4a25a8..5cefa477873 100644
--- a/server/prisma/schema.prisma
+++ b/server/prisma/schema.prisma
@@ -135,7 +135,7 @@ model workspaces {
chatProvider String?
chatModel String?
topN Int? @default(4)
- chatMode String? @default("chat")
+ chatMode String? @default("chat") // Supports: "chat", "query", "agent"
pfpFilename String?
agentProvider String?
agentModel String?
diff --git a/server/utils/agents/aibitat/plugins/websocket.js b/server/utils/agents/aibitat/plugins/websocket.js
index 2544918652b..1b9f8fd8ee2 100644
--- a/server/utils/agents/aibitat/plugins/websocket.js
+++ b/server/utils/agents/aibitat/plugins/websocket.js
@@ -98,6 +98,18 @@ const websocket = {
aibitat.onInterrupt(async (node) => {
const feedback = await socket.askForFeedback(socket, node);
if (WEBSOCKET_BAIL_COMMANDS.includes(feedback)) {
+ // Handle reset command specially
+ if (feedback === "/reset") {
+ socket.send(
+ JSON.stringify({
+ type: "chatReset",
+ content:
+ "Chat has been reset and conversation history cleared.",
+ animate: false,
+ action: "reset_chat",
+ })
+ );
+ }
socket.close();
return;
}
diff --git a/server/utils/chats/index.js b/server/utils/chats/index.js
index 658302f7bb4..8509637492d 100644
--- a/server/utils/chats/index.js
+++ b/server/utils/chats/index.js
@@ -9,6 +9,21 @@ const VALID_COMMANDS = {
"/reset": resetMemory,
};
+async function grepBuiltInCommand(message) {
+ const availableCommands = Object.keys(VALID_COMMANDS);
+
+ // Check if the message starts with any built-in command
+ for (let i = 0; i < availableCommands.length; i++) {
+ const cmd = availableCommands[i];
+ const re = new RegExp(`^(${cmd})`, "i");
+ if (re.test(message)) {
+ return cmd;
+ }
+ }
+
+ return message; // Return original message if no built-in command found
+}
+
async function grepCommand(message, user = null) {
const userPresets = await SlashCommandPresets.getUserPresets(user?.id);
const availableCommands = Object.keys(VALID_COMMANDS);
@@ -113,6 +128,7 @@ module.exports = {
recentChatHistory,
chatPrompt,
grepCommand,
+ grepBuiltInCommand,
grepAllSlashCommands,
VALID_COMMANDS,
};
diff --git a/server/utils/chats/stream.js b/server/utils/chats/stream.js
index 0ecf86c8141..4b6beaf8b9f 100644
--- a/server/utils/chats/stream.js
+++ b/server/utils/chats/stream.js
@@ -6,13 +6,14 @@ const { writeResponseChunk } = require("../helpers/chat/responses");
const { grepAgents } = require("./agents");
const {
grepCommand,
+ grepBuiltInCommand,
VALID_COMMANDS,
chatPrompt,
recentChatHistory,
sourceIdentifier,
} = require("./index");
-const VALID_CHAT_MODE = ["chat", "query"];
+const VALID_CHAT_MODE = ["chat", "query", "agent"];
async function streamChatWithWorkspace(
response,
@@ -24,8 +25,13 @@ async function streamChatWithWorkspace(
attachments = []
) {
const uuid = uuidv4();
- const updatedMessage = await grepCommand(message, user);
+ // In agent mode, only process built-in commands, skip SlashPresets
+ const updatedMessage =
+ chatMode === "agent"
+ ? await grepBuiltInCommand(message)
+ : await grepCommand(message, user);
+ // Handle valid built-in commands BEFORE agent processing
if (Object.keys(VALID_COMMANDS).includes(updatedMessage)) {
const data = await VALID_COMMANDS[updatedMessage](
workspace,
@@ -38,11 +44,24 @@ async function streamChatWithWorkspace(
return;
}
+ // If workspace is in agent mode AND no SlashPreset was triggered AND not a built-in command, use agent handler
+ if (chatMode === "agent" && updatedMessage === message) {
+ const isAgentChat = await grepAgents({
+ uuid,
+ response,
+ message: `@agent ${message}`, // Add @agent prefix for agent mode
+ user,
+ workspace,
+ thread,
+ });
+ if (isAgentChat) return;
+ }
+
// If is agent enabled chat we will exit this flow early.
const isAgentChat = await grepAgents({
uuid,
response,
- message: updatedMessage,
+ message: updatedMessage, // Use updatedMessage to support SlashPresets
user,
workspace,
thread,