diff --git a/frontend/src/components/Generic/Blocks/CheckBoxBlock/index.jsx b/frontend/src/components/Generic/Blocks/CheckBoxBlock/index.jsx
new file mode 100644
index 00000000..de87cb93
--- /dev/null
+++ b/frontend/src/components/Generic/Blocks/CheckBoxBlock/index.jsx
@@ -0,0 +1,82 @@
+import React from "react";
+import Badge from "@/components/Generic/Badges/Badge";
+import CheckBox from "../../Inputs/CheckBox";
+
+export default function CheckBoxBlock({
+ initialChecked,
+ label,
+ onToggle,
+ description,
+ name,
+ badge = false,
+ badgeLabel,
+ badgeAnimated,
+ badgeBg,
+ border,
+ Icon,
+ contentLocation,
+ disabled,
+ inline = false,
+}) {
+ const borderStyle = border ? "border border-gray-600 rounded-2xl p-4" : "";
+ const contentPosition = {
+ middle: "middle",
+ top: "top",
+ bottom: "bottom",
+ }[contentLocation];
+
+ return (
+
+
-
-
-
-
-
+
+ {Icon && (
+
+ )}
+
+
+ {inline && (
+
+
+
+ )}
+
+ {badge && (
+
+ )}
+
+ {!inline && (
+
+ )}
-
@@ -34,6 +76,7 @@ export default function ToggleBlock({
{description}
+ {content}
);
diff --git a/frontend/src/components/Generic/Inputs/CheckBox/index.jsx b/frontend/src/components/Generic/Inputs/CheckBox/index.jsx
new file mode 100644
index 00000000..a951068f
--- /dev/null
+++ b/frontend/src/components/Generic/Inputs/CheckBox/index.jsx
@@ -0,0 +1,29 @@
+import React, { useState, useEffect } from "react";
+
+export default function CheckBox({ initialChecked, onToggle, name, disabled }) {
+ const [isChecked, setIsChecked] = useState(initialChecked);
+
+ useEffect(() => {
+ setIsChecked(initialChecked);
+ }, [initialChecked]);
+
+ const handleChange = (e) => {
+ setIsChecked(e.target.checked);
+ if (onToggle) {
+ onToggle(e.target.checked);
+ }
+ };
+
+ return (
+
+ );
+}
diff --git a/frontend/src/components/Generic/Inputs/TextArea/index.jsx b/frontend/src/components/Generic/Inputs/TextArea/index.jsx
new file mode 100644
index 00000000..1309b499
--- /dev/null
+++ b/frontend/src/components/Generic/Inputs/TextArea/index.jsx
@@ -0,0 +1,54 @@
+import React, { useState } from "react";
+
+export default function TextArea({
+ defaultValue,
+ required = true,
+ placeholder = "Place your text here...",
+ onChange,
+ name = "text-area",
+ disabled = false,
+ initialRows = 5,
+ className = "",
+ autoComplete = "off",
+ wrap = "soft",
+}) {
+ const [rows, setRows] = useState(initialRows);
+
+ const handleChange = (e) => {
+ if (onChange) {
+ onChange(e);
+ }
+
+ // Dynamically adjust rows
+ const textareaLineHeight = 24;
+ const previousRows = e.target.rows;
+ e.target.rows = initialRows;
+
+ const currentRows = ~~(e.target.scrollHeight / textareaLineHeight);
+
+ if (currentRows === previousRows) {
+ e.target.rows = currentRows;
+ }
+
+ if (e.target.scrollHeight > e.target.clientHeight) {
+ e.target.rows = currentRows;
+ }
+
+ setRows(currentRows);
+ };
+
+ return (
+
+ );
+}
diff --git a/frontend/src/components/Generic/Buttons/ToggleButton/index.jsx b/frontend/src/components/Generic/Inputs/ToggleSwitch/index.jsx
similarity index 76%
rename from frontend/src/components/Generic/Buttons/ToggleButton/index.jsx
rename to frontend/src/components/Generic/Inputs/ToggleSwitch/index.jsx
index 90a4f1e9..b347c32e 100644
--- a/frontend/src/components/Generic/Buttons/ToggleButton/index.jsx
+++ b/frontend/src/components/Generic/Inputs/ToggleSwitch/index.jsx
@@ -1,7 +1,6 @@
import React, { useState, useEffect } from "react";
-// ToggleButton: A reusable and semi-controlled toggle button
-export default function ToggleButton({ initialChecked, onToggle, name }) {
+export default function ToggleButton({ initialChecked, onToggle, name, disabled }) {
const [isChecked, setIsChecked] = useState(initialChecked);
useEffect(() => {
@@ -23,8 +22,9 @@ export default function ToggleButton({ initialChecked, onToggle, name }) {
onChange={handleToggle}
checked={isChecked}
className="peer sr-only pointer-events-none"
+ disabled={disabled}
/>
-
+
);
}
diff --git a/frontend/src/models/metaResponse.js b/frontend/src/models/metaResponse.js
index 8319287a..bacd63ea 100644
--- a/frontend/src/models/metaResponse.js
+++ b/frontend/src/models/metaResponse.js
@@ -18,19 +18,7 @@ const MetaResponse = {
.catch(() => false);
return result;
},
- new: async function (data = {}) {
- const { workspace, message } = await fetch(`${API_BASE}/workspace/new`, {
- method: "POST",
- body: JSON.stringify(data),
- headers: baseHeaders(),
- })
- .then((res) => res.json())
- .catch((e) => {
- return { workspace: null, message: e.message };
- });
- return { workspace, message };
- },
update: async function (slug, data = {}) {
const { workspace, message } = await fetch(
`${API_BASE}/workspace/${slug}/update`,
@@ -47,134 +35,9 @@ const MetaResponse = {
return { workspace, message };
},
- modifyEmbeddings: async function (slug, changes = {}) {
- const { workspace, message } = await fetch(
- `${API_BASE}/workspace/${slug}/update-embeddings`,
- {
- method: "POST",
- body: JSON.stringify(changes), // contains 'adds' and 'removes' keys that are arrays of filepaths
- headers: baseHeaders(),
- }
- )
- .then((res) => res.json())
- .catch((e) => {
- return { workspace: null, message: e.message };
- });
- return { workspace, message };
- },
- chatHistory: async function (slug) {
- const history = await fetch(`${API_BASE}/workspace/${slug}/chats`, {
- method: "GET",
- headers: baseHeaders(),
- })
- .then((res) => res.json())
- .then((res) => res.history || [])
- .catch(() => []);
- return history;
- },
- updateChatFeedback: async function (chatId, slug, feedback) {
- const result = await fetch(
- `${API_BASE}/workspace/${slug}/chat-feedback/${chatId}`,
- {
- method: "POST",
- headers: baseHeaders(),
- body: JSON.stringify({ feedback }),
- }
- )
- .then((res) => res.ok)
- .catch(() => false);
- return result;
- },
- streamChat: async function ({ slug }, message, handleChat) {
- const ctrl = new AbortController();
- // Listen for the ABORT_STREAM_EVENT key to be emitted by the client
- // to early abort the streaming response. On abort we send a special `stopGeneration`
- // event to be handled which resets the UI for us to be able to send another message.
- // The backend response abort handling is done in each LLM's handleStreamResponse.
- window.addEventListener(ABORT_STREAM_EVENT, () => {
- ctrl.abort();
- handleChat({ id: v4(), type: "stopGeneration" });
- });
- await fetchEventSource(`${API_BASE}/workspace/${slug}/stream-chat`, {
- method: "POST",
- body: JSON.stringify({ message }),
- headers: baseHeaders(),
- signal: ctrl.signal,
- openWhenHidden: true,
- async onopen(response) {
- if (response.ok) {
- return; // everything's good
- } else if (
- response.status >= 400 &&
- response.status < 500 &&
- response.status !== 429
- ) {
- handleChat({
- id: v4(),
- type: "abort",
- textResponse: null,
- sources: [],
- close: true,
- error: `An error occurred while streaming response. Code ${response.status}`,
- });
- ctrl.abort();
- throw new Error("Invalid Status code response.");
- } else {
- handleChat({
- id: v4(),
- type: "abort",
- textResponse: null,
- sources: [],
- close: true,
- error: `An error occurred while streaming response. Unknown Error.`,
- });
- ctrl.abort();
- throw new Error("Unknown error");
- }
- },
- async onmessage(msg) {
- try {
- const chatResult = JSON.parse(msg.data);
- handleChat(chatResult);
- } catch { }
- },
- onerror(err) {
- handleChat({
- id: v4(),
- type: "abort",
- textResponse: null,
- sources: [],
- close: true,
- error: `An error occurred while streaming response. ${err.message}`,
- });
- ctrl.abort();
- throw new Error();
- },
- });
- },
- all: async function () {
- const workspaces = await fetch(`${API_BASE}/workspaces`, {
- method: "GET",
- headers: baseHeaders(),
- })
- .then((res) => res.json())
- .then((res) => res.workspaces || [])
- .catch(() => []);
-
- return workspaces;
- },
- bySlug: async function (slug = "") {
- const workspace = await fetch(`${API_BASE}/workspace/${slug}`, {
- headers: baseHeaders(),
- })
- .then((res) => res.json())
- .then((res) => res.workspace)
- .catch(() => null);
- return workspace;
- },
delete: async function (slug) {
const result = await fetch(`${API_BASE}/workspace/${slug}`, {
method: "DELETE",
@@ -195,72 +58,31 @@ const MetaResponse = {
const data = await response.json();
return { response, data };
},
- uploadLink: async function (slug, link) {
- const response = await fetch(`${API_BASE}/workspace/${slug}/upload-link`, {
- method: "POST",
- body: JSON.stringify({ link }),
- headers: baseHeaders(),
- });
-
- const data = await response.json();
- return { response, data };
+ getMetaResponseSettings: async function (slug) {
+ const settings = await fetch(
+ `${API_BASE}/workspace/${slug}/metaResponse/settings`,
+ {
+ method: "GET",
+ headers: baseHeaders(),
+ }
+ )
+ .then((res) => res.json())
+ .catch(() => ({}));
+ return settings;
},
-
- getSuggestedMessages: async function (slug) {
- return await fetch(`${API_BASE}/workspace/${slug}/suggested-messages`, {
- method: "GET",
- cache: "no-cache",
- headers: baseHeaders(),
- })
- .then((res) => {
- if (!res.ok) throw new Error("Could not fetch suggested messages.");
- return res.json();
- })
- .then((res) => res.suggestedMessages)
- .catch((e) => {
- console.error(e);
- return null;
- });
+ updateMetaResponseSettings: async function (slug, data) {
+ const settings = await fetch(
+ `${API_BASE}/workspace/${slug}/metaResponse/settings`,
+ {
+ method: "PATCH",
+ body: JSON.stringify(data),
+ headers: baseHeaders(),
+ }
+ )
+ .then((res) => res.json())
+ .catch(() => ({}));
+ return settings;
},
- setSuggestedMessages: async function (slug, messages) {
- return fetch(`${API_BASE}/workspace/${slug}/suggested-messages`, {
- method: "POST",
- headers: baseHeaders(),
- body: JSON.stringify({ messages }),
- })
- .then((res) => {
- if (!res.ok) {
- throw new Error(
- res.statusText || "Error setting suggested messages."
- );
- }
- return { success: true, ...res.json() };
- })
- .catch((e) => {
- console.error(e);
- return { success: false, error: e.message };
- });
- },
- setPinForDocument: async function (slug, docPath, pinStatus) {
- return fetch(`${API_BASE}/workspace/${slug}/update-pin`, {
- method: "POST",
- headers: baseHeaders(),
- body: JSON.stringify({ docPath, pinStatus }),
- })
- .then((res) => {
- if (!res.ok) {
- throw new Error(
- res.statusText || "Error setting pin status for document."
- );
- }
- return true;
- })
- .catch((e) => {
- console.error(e);
- return false;
- });
- },
- threads: WorkspaceThread,
uploadPfp: async function (formData, slug) {
return await fetch(`${API_BASE}/workspace/${slug}/upload-pfp`, {
diff --git a/frontend/src/pages/WorkspaceSettings/ChatSettings/ChatEnableMetaResponse/index.jsx b/frontend/src/pages/WorkspaceSettings/ChatSettings/ChatEnableMetaResponse/index.jsx
index 3ed41f04..32cc0900 100644
--- a/frontend/src/pages/WorkspaceSettings/ChatSettings/ChatEnableMetaResponse/index.jsx
+++ b/frontend/src/pages/WorkspaceSettings/ChatSettings/ChatEnableMetaResponse/index.jsx
@@ -1,9 +1,6 @@
-import GenericBadge from "@/components/Generic/Badges/Badge";
import ToggleBlock from "@/components/Generic/Blocks/ToggleBlock";
export default function ChatEnableMetaResponse({ workspace, setHasChanges }) {
- // Toggle metaResponse value
-
const toggleMetaResponse = () => {
setHasChanges(true);
};
@@ -19,6 +16,7 @@ export default function ChatEnableMetaResponse({ workspace, setHasChanges }) {
onToggle={toggleMetaResponse}
name="metaResponse"
description="Turn on this feature to dynamically adjust the chat interface based on conversation context, using options like dropdowns, sliders, and suggestions for a tailored user experience."
+ badge
/>
);
diff --git a/frontend/src/pages/WorkspaceSettings/ChatSettings/index.jsx b/frontend/src/pages/WorkspaceSettings/ChatSettings/index.jsx
index 06f0163f..804034c6 100644
--- a/frontend/src/pages/WorkspaceSettings/ChatSettings/index.jsx
+++ b/frontend/src/pages/WorkspaceSettings/ChatSettings/index.jsx
@@ -10,7 +10,7 @@ import ChatTemperatureSettings from "./ChatTemperatureSettings";
import ChatModeSelection from "./ChatModeSelection";
import ChatEnableMetaResponse from "./ChatEnableMetaResponse";
-export default function ChatSettings({ workspace }) {
+export default function ChatSettings({ workspace, setWorkspace }) {
const [settings, setSettings] = useState({});
const [hasChanges, setHasChanges] = useState(false);
const [saving, setSaving] = useState(false);
@@ -43,6 +43,7 @@ export default function ChatSettings({ workspace }) {
);
if (!!updatedWorkspace) {
showToast("Workspace updated!", "success", { clear: true });
+ setWorkspace(updatedWorkspace);
} else {
showToast(`Error: ${message}`, "error", { clear: true });
}
@@ -75,6 +76,7 @@ export default function ChatSettings({ workspace }) {
/>
{hasChanges && (
diff --git a/frontend/src/pages/WorkspaceSettings/MetaResponse/EnableFeatures/index.jsx b/frontend/src/pages/WorkspaceSettings/MetaResponse/EnableFeatures/index.jsx
new file mode 100644
index 00000000..782aa2cb
--- /dev/null
+++ b/frontend/src/pages/WorkspaceSettings/MetaResponse/EnableFeatures/index.jsx
@@ -0,0 +1,27 @@
+import ToggleBlock from "@/components/Generic/Blocks/ToggleBlock";
+
+export default function EnableFeatures({
+ feature,
+ isEnabled,
+ description,
+ onToggle,
+ Icon,
+ content,
+ disabled,
+}) {
+ return (
+
+
+
+ );
+}
diff --git a/frontend/src/pages/WorkspaceSettings/MetaResponse/EnableInputs/index.jsx b/frontend/src/pages/WorkspaceSettings/MetaResponse/EnableInputs/index.jsx
new file mode 100644
index 00000000..c73f22ce
--- /dev/null
+++ b/frontend/src/pages/WorkspaceSettings/MetaResponse/EnableInputs/index.jsx
@@ -0,0 +1,19 @@
+import ToggleBlock from "@/components/Generic/Blocks/ToggleBlock";
+import { List } from "@phosphor-icons/react";
+
+export default function EnableInputs({ inputs, onToggle }) {
+ return (
+
+
+
+ );
+}
diff --git a/frontend/src/pages/WorkspaceSettings/MetaResponse/InputsFeature/EnableSystemPrompt/index.jsx b/frontend/src/pages/WorkspaceSettings/MetaResponse/InputsFeature/EnableSystemPrompt/index.jsx
new file mode 100644
index 00000000..afb65806
--- /dev/null
+++ b/frontend/src/pages/WorkspaceSettings/MetaResponse/InputsFeature/EnableSystemPrompt/index.jsx
@@ -0,0 +1,26 @@
+import ToggleBlock from "@/components/Generic/Blocks/ToggleBlock";
+import { useState } from "react";
+
+export default function EnableSystemPrompt({ workspace, config }) {
+ const [isEnabled, setIsEnabled] = useState(config.systemPrompt.isEnabled);
+ const toggleSystemPrompt = () => {
+ setIsEnabled(!isEnabled);
+ };
+
+ return (
+
+
+
+ );
+}
diff --git a/frontend/src/pages/WorkspaceSettings/MetaResponse/InputsFeature/index.jsx b/frontend/src/pages/WorkspaceSettings/MetaResponse/InputsFeature/index.jsx
new file mode 100644
index 00000000..cf035d38
--- /dev/null
+++ b/frontend/src/pages/WorkspaceSettings/MetaResponse/InputsFeature/index.jsx
@@ -0,0 +1,16 @@
+import TextAreaBlock from "@/components/Generic/Blocks/TextAreaBlock";
+import EnableSystemPrompt from "./EnableSystemPrompt";
+import { C } from "../../../../../dist/assets/index-cf7f0eac";
+import CheckBoxBlock from "@/components/Generic/Blocks/CheckBoxBlock";
+
+export default function InputsFeature({ workspace, config }) {
+ console.log("workspace: ", workspace);
+ console.log("config: ", config);
+ return (
+
+
+
+
+
+ );
+}
diff --git a/frontend/src/pages/WorkspaceSettings/MetaResponse/index.jsx b/frontend/src/pages/WorkspaceSettings/MetaResponse/index.jsx
new file mode 100644
index 00000000..ec35ea9a
--- /dev/null
+++ b/frontend/src/pages/WorkspaceSettings/MetaResponse/index.jsx
@@ -0,0 +1,86 @@
+import MetaResponse from "@/models/metaResponse";
+import { ChatText, Heart, UserCircle } from "@phosphor-icons/react";
+import { useEffect, useState } from "react";
+import EnableFeatures from "./EnableFeatures";
+import InputsFeature from "./InputsFeature";
+
+export default function MetaResponseSettings({ workspace }) {
+ const [settings, setSettings] = useState({});
+ useEffect(() => {
+ const fetchMetaResponseSettings = async () => {
+ const settings = await MetaResponse.getMetaResponseSettings(
+ workspace.slug
+ );
+ console.log(settings);
+ setSettings(settings);
+ };
+ fetchMetaResponseSettings();
+ }, []);
+
+ const handleToggleEnableFeatures = async (feature, enabled) => {
+ const updatedFeatureSettings = {
+ ...settings[feature],
+ isEnabled: enabled,
+ };
+
+ const updatedSettings = await MetaResponse.updateMetaResponseSettings(
+ workspace.slug,
+ {
+ ...settings,
+ [feature]: updatedFeatureSettings,
+ }
+ );
+
+ console.log("updatedSettings: ", updatedSettings);
+ setSettings(updatedSettings);
+ };
+
+ const mapIcons = {
+ inputs: ChatText,
+ sentiments: Heart,
+ avatars: UserCircle,
+ };
+
+ const mapFeatures = {
+ inputs: InputsFeature,
+ };
+
+ return (
+
+ {Object.keys(settings).map((feature) => {
+ const featureSettings = settings[feature];
+ const IconComponent = mapIcons[feature];
+ const FeatureComponent = mapFeatures[feature];
+ return (
+
+
+ workspace.metaResponse &&
+ handleToggleEnableFeatures(feature, enabled)
+ }
+ disabled={!workspace.metaResponse}
+ Icon={IconComponent}
+ content={
+ featureSettings.isEnabled &&
+ FeatureComponent && (
+
+ )
+ }
+ />
+
+ );
+ })}
+
+ );
+}
diff --git a/frontend/src/pages/WorkspaceSettings/index.jsx b/frontend/src/pages/WorkspaceSettings/index.jsx
index 952860ad..7413ab1d 100644
--- a/frontend/src/pages/WorkspaceSettings/index.jsx
+++ b/frontend/src/pages/WorkspaceSettings/index.jsx
@@ -1,27 +1,28 @@
-import React, { useEffect, useState } from "react";
-import { useParams } from "react-router-dom";
+import PasswordModal, { usePasswordModal } from "@/components/Modals/Password";
+import { FullScreenLoader } from "@/components/Preloader";
import Sidebar from "@/components/Sidebar";
import Workspace from "@/models/workspace";
-import PasswordModal, { usePasswordModal } from "@/components/Modals/Password";
-import { isMobile } from "react-device-detect";
-import { FullScreenLoader } from "@/components/Preloader";
+import paths from "@/utils/paths";
import {
ArrowUUpLeft,
ChatText,
+ Cube,
Database,
Wrench,
} from "@phosphor-icons/react";
-import paths from "@/utils/paths";
-import { Link } from "react-router-dom";
-import { NavLink } from "react-router-dom";
-import GeneralAppearance from "./GeneralAppearance";
+import React, { useEffect, useState } from "react";
+import { isMobile } from "react-device-detect";
+import { Link, NavLink, useParams } from "react-router-dom";
import ChatSettings from "./ChatSettings";
+import GeneralAppearance from "./GeneralAppearance";
import VectorDatabase from "./VectorDatabase";
+import MetaResponse from "./MetaResponse";
const TABS = {
"general-appearance": GeneralAppearance,
"chat-settings": ChatSettings,
"vector-database": VectorDatabase,
+ "meta-response": MetaResponse,
};
export default function WorkspaceSettings() {
@@ -91,9 +92,20 @@ function ShowWorkspaceChat() {
icon={
}
to={paths.workspace.settings.vectorDatabase(slug)}
/>
+ {workspace.metaResponse && (
+
}
+ to={paths.workspace.settings.metaResponse(slug)}
+ />
+ )}