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 ( +

{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,