mirror of
https://github.com/Mintplex-Labs/anything-llm.git
synced 2024-06-28 13:44:30 +02:00
- added meta respons page
- added checkbox,toggleSwitch,textarea generic components
This commit is contained in:
parent
872db95dfe
commit
b7ee0b27f4
|
@ -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 (
|
||||||
|
<div className={`relative w-full max-h-full ${borderStyle}`}>
|
||||||
|
<div className="relative rounded-lg">
|
||||||
|
<div className="space-y-6 flex h-full w-full">
|
||||||
|
<div className="w-full flex flex-col gap-y-4">
|
||||||
|
<div className="flex gap-4">
|
||||||
|
{Icon && (
|
||||||
|
<Icon className="w-16 h-16 text-white text-opacity-60" />
|
||||||
|
)}
|
||||||
|
<div>
|
||||||
|
<div className="flex flex-row gap-4">
|
||||||
|
{inline && (
|
||||||
|
<div>
|
||||||
|
<CheckBox
|
||||||
|
initialChecked={initialChecked}
|
||||||
|
onToggle={onToggle}
|
||||||
|
name={name}
|
||||||
|
disabled={disabled}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<label className="block input-label mb-4 first-letter:capitalize">
|
||||||
|
{label}
|
||||||
|
</label>
|
||||||
|
{badge && (
|
||||||
|
<Badge
|
||||||
|
showDot
|
||||||
|
animated={badgeAnimated}
|
||||||
|
label={badgeLabel}
|
||||||
|
bg={badgeBg}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
{!inline && (
|
||||||
|
<CheckBox
|
||||||
|
initialChecked={initialChecked}
|
||||||
|
onToggle={onToggle}
|
||||||
|
name={name}
|
||||||
|
disabled={disabled}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center justify-between space-x-14">
|
||||||
|
<p className="text-white text-opacity-60 text-xs font-medium py-1.5">
|
||||||
|
{description}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
import TextArea from "../../Inputs/TextArea";
|
||||||
|
|
||||||
|
export default function TextAreaBlock({
|
||||||
|
label,
|
||||||
|
description,
|
||||||
|
defaultValue,
|
||||||
|
required,
|
||||||
|
placeholder,
|
||||||
|
onChange,
|
||||||
|
name,
|
||||||
|
disabled,
|
||||||
|
initialRows,
|
||||||
|
className,
|
||||||
|
autoComplete,
|
||||||
|
wrap,
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className="flex flex-col">
|
||||||
|
{label && (
|
||||||
|
<label htmlFor="name" className="block input-label">
|
||||||
|
{label}
|
||||||
|
</label>
|
||||||
|
)}
|
||||||
|
{description && (
|
||||||
|
<p className="text-white text-opacity-60 text-xs font-medium py-1.5">
|
||||||
|
{description}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<TextArea
|
||||||
|
defaultValue={defaultValue}
|
||||||
|
required={required}
|
||||||
|
placeholder={placeholder}
|
||||||
|
onChange={onChange}
|
||||||
|
name={name}
|
||||||
|
disabled={disabled}
|
||||||
|
initialRows={initialRows}
|
||||||
|
className={className}
|
||||||
|
autoComplete={autoComplete}
|
||||||
|
wrap={wrap}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
|
@ -1,31 +1,73 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import ToggleButton from "../../Buttons/ToggleButton";
|
import Badge from "@/components/Generic/Badges/Badge";
|
||||||
import Badge from "../../Badges/Badge";
|
import ToggleButton from "@/components/Generic/Inputs/ToggleSwitch";
|
||||||
|
|
||||||
// ToggleBlock: A component that includes a ToggleButton with additional context
|
|
||||||
export default function ToggleBlock({
|
export default function ToggleBlock({
|
||||||
|
content, // toggle content goes here
|
||||||
initialChecked,
|
initialChecked,
|
||||||
label,
|
label,
|
||||||
onToggle,
|
onToggle,
|
||||||
description,
|
description,
|
||||||
name,
|
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 (
|
return (
|
||||||
<div className="relative w-full max-h-full">
|
<div className={`relative w-full max-h-full ${borderStyle}`}>
|
||||||
<div className="relative rounded-lg">
|
<div className="relative rounded-lg">
|
||||||
<div className="flex items-start justify-between px-6 py-4"></div>
|
|
||||||
<div className="space-y-6 flex h-full w-full">
|
<div className="space-y-6 flex h-full w-full">
|
||||||
<div className="w-full flex flex-col gap-y-4">
|
<div className="w-full flex flex-col gap-y-4">
|
||||||
<div>
|
<div className="flex gap-4">
|
||||||
<div className="flex flex-row gap-3" >
|
{Icon && (
|
||||||
<label className="block input-label mb-4">{label}</label>
|
<Icon className="w-16 h-16 text-white text-opacity-60" />
|
||||||
<Badge showDot animated />
|
)}
|
||||||
|
<div>
|
||||||
|
<div className="flex flex-row gap-4">
|
||||||
|
{inline && (
|
||||||
|
<div>
|
||||||
|
<ToggleButton
|
||||||
|
initialChecked={initialChecked}
|
||||||
|
onToggle={onToggle}
|
||||||
|
name={name}
|
||||||
|
disabled={disabled}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<label className="block input-label mb-4 first-letter:capitalize">
|
||||||
|
{label}
|
||||||
|
</label>
|
||||||
|
{badge && (
|
||||||
|
<Badge
|
||||||
|
showDot
|
||||||
|
animated={badgeAnimated}
|
||||||
|
label={badgeLabel}
|
||||||
|
bg={badgeBg}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
{!inline && (
|
||||||
|
<ToggleButton
|
||||||
|
initialChecked={initialChecked}
|
||||||
|
onToggle={onToggle}
|
||||||
|
name={name}
|
||||||
|
disabled={disabled}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<ToggleButton
|
|
||||||
initialChecked={initialChecked}
|
|
||||||
onToggle={onToggle}
|
|
||||||
name={name}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -34,6 +76,7 @@ export default function ToggleBlock({
|
||||||
{description}
|
{description}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
{content}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
29
frontend/src/components/Generic/Inputs/CheckBox/index.jsx
Normal file
29
frontend/src/components/Generic/Inputs/CheckBox/index.jsx
Normal file
|
@ -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 (
|
||||||
|
<label className="inline-flex cursor-pointer items-center">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
name={name}
|
||||||
|
onChange={handleChange}
|
||||||
|
checked={isChecked}
|
||||||
|
disabled={disabled}
|
||||||
|
className="cursor-pointer rounded text-sky-400 focus:ring-blue-800 focus:border-blue-800"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
);
|
||||||
|
}
|
54
frontend/src/components/Generic/Inputs/TextArea/index.jsx
Normal file
54
frontend/src/components/Generic/Inputs/TextArea/index.jsx
Normal file
|
@ -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 (
|
||||||
|
<textarea
|
||||||
|
name={name}
|
||||||
|
rows={rows}
|
||||||
|
defaultValue={defaultValue}
|
||||||
|
className={`resize-none bg-zinc-900 placeholder:text-white/20 text-white text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 mt-2 ${className}`}
|
||||||
|
placeholder={placeholder}
|
||||||
|
required={required}
|
||||||
|
wrap={wrap}
|
||||||
|
autoComplete={autoComplete}
|
||||||
|
onChange={onChange || handleChange}
|
||||||
|
disabled={disabled}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
|
@ -1,7 +1,6 @@
|
||||||
import React, { useState, useEffect } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
|
|
||||||
// ToggleButton: A reusable and semi-controlled toggle button
|
export default function ToggleButton({ initialChecked, onToggle, name, disabled }) {
|
||||||
export default function ToggleButton({ initialChecked, onToggle, name }) {
|
|
||||||
const [isChecked, setIsChecked] = useState(initialChecked);
|
const [isChecked, setIsChecked] = useState(initialChecked);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -23,8 +22,9 @@ export default function ToggleButton({ initialChecked, onToggle, name }) {
|
||||||
onChange={handleToggle}
|
onChange={handleToggle}
|
||||||
checked={isChecked}
|
checked={isChecked}
|
||||||
className="peer sr-only pointer-events-none"
|
className="peer sr-only pointer-events-none"
|
||||||
|
disabled={disabled}
|
||||||
/>
|
/>
|
||||||
<div className="pointer-events-none peer h-6 w-11 rounded-full bg-stone-400 after:absolute after:left-[2px] after:top-[2px] after:h-5 after:w-5 after:rounded-full after:shadow-xl after:border after:border-gray-600 after:bg-white after:box-shadow-md after:transition-all after:content-[''] peer-checked:bg-lime-300 peer-checked:after:translate-x-full peer-checked:after:border-white peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-blue-800"></div>
|
<div className="pointer-events-none peer h-6 w-11 rounded-full bg-stone-400 after:absolute after:left-[2px] after:top-[2px] after:h-5 after:w-5 after:rounded-full after:shadow-xl after:border after:border-gray-600 after:bg-white after:box-shadow-md after:transition-all after:content-[''] peer-checked:bg-sky-400 peer-checked:after:translate-x-full peer-checked:after:border-white peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-blue-800"></div>
|
||||||
</label>
|
</label>
|
||||||
);
|
);
|
||||||
}
|
}
|
|
@ -18,19 +18,7 @@ const MetaResponse = {
|
||||||
.catch(() => false);
|
.catch(() => false);
|
||||||
return result;
|
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 = {}) {
|
update: async function (slug, data = {}) {
|
||||||
const { workspace, message } = await fetch(
|
const { workspace, message } = await fetch(
|
||||||
`${API_BASE}/workspace/${slug}/update`,
|
`${API_BASE}/workspace/${slug}/update`,
|
||||||
|
@ -47,134 +35,9 @@ const MetaResponse = {
|
||||||
|
|
||||||
return { workspace, message };
|
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) {
|
delete: async function (slug) {
|
||||||
const result = await fetch(`${API_BASE}/workspace/${slug}`, {
|
const result = await fetch(`${API_BASE}/workspace/${slug}`, {
|
||||||
method: "DELETE",
|
method: "DELETE",
|
||||||
|
@ -195,72 +58,31 @@ const MetaResponse = {
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
return { response, data };
|
return { response, data };
|
||||||
},
|
},
|
||||||
uploadLink: async function (slug, link) {
|
getMetaResponseSettings: async function (slug) {
|
||||||
const response = await fetch(`${API_BASE}/workspace/${slug}/upload-link`, {
|
const settings = await fetch(
|
||||||
method: "POST",
|
`${API_BASE}/workspace/${slug}/metaResponse/settings`,
|
||||||
body: JSON.stringify({ link }),
|
{
|
||||||
headers: baseHeaders(),
|
method: "GET",
|
||||||
});
|
headers: baseHeaders(),
|
||||||
|
}
|
||||||
const data = await response.json();
|
)
|
||||||
return { response, data };
|
.then((res) => res.json())
|
||||||
|
.catch(() => ({}));
|
||||||
|
return settings;
|
||||||
},
|
},
|
||||||
|
updateMetaResponseSettings: async function (slug, data) {
|
||||||
getSuggestedMessages: async function (slug) {
|
const settings = await fetch(
|
||||||
return await fetch(`${API_BASE}/workspace/${slug}/suggested-messages`, {
|
`${API_BASE}/workspace/${slug}/metaResponse/settings`,
|
||||||
method: "GET",
|
{
|
||||||
cache: "no-cache",
|
method: "PATCH",
|
||||||
headers: baseHeaders(),
|
body: JSON.stringify(data),
|
||||||
})
|
headers: baseHeaders(),
|
||||||
.then((res) => {
|
}
|
||||||
if (!res.ok) throw new Error("Could not fetch suggested messages.");
|
)
|
||||||
return res.json();
|
.then((res) => res.json())
|
||||||
})
|
.catch(() => ({}));
|
||||||
.then((res) => res.suggestedMessages)
|
return settings;
|
||||||
.catch((e) => {
|
|
||||||
console.error(e);
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
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) {
|
uploadPfp: async function (formData, slug) {
|
||||||
return await fetch(`${API_BASE}/workspace/${slug}/upload-pfp`, {
|
return await fetch(`${API_BASE}/workspace/${slug}/upload-pfp`, {
|
||||||
|
|
|
@ -1,9 +1,6 @@
|
||||||
import GenericBadge from "@/components/Generic/Badges/Badge";
|
|
||||||
import ToggleBlock from "@/components/Generic/Blocks/ToggleBlock";
|
import ToggleBlock from "@/components/Generic/Blocks/ToggleBlock";
|
||||||
|
|
||||||
export default function ChatEnableMetaResponse({ workspace, setHasChanges }) {
|
export default function ChatEnableMetaResponse({ workspace, setHasChanges }) {
|
||||||
// Toggle metaResponse value
|
|
||||||
|
|
||||||
const toggleMetaResponse = () => {
|
const toggleMetaResponse = () => {
|
||||||
setHasChanges(true);
|
setHasChanges(true);
|
||||||
};
|
};
|
||||||
|
@ -19,6 +16,7 @@ export default function ChatEnableMetaResponse({ workspace, setHasChanges }) {
|
||||||
onToggle={toggleMetaResponse}
|
onToggle={toggleMetaResponse}
|
||||||
name="metaResponse"
|
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."
|
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
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -10,7 +10,7 @@ import ChatTemperatureSettings from "./ChatTemperatureSettings";
|
||||||
import ChatModeSelection from "./ChatModeSelection";
|
import ChatModeSelection from "./ChatModeSelection";
|
||||||
import ChatEnableMetaResponse from "./ChatEnableMetaResponse";
|
import ChatEnableMetaResponse from "./ChatEnableMetaResponse";
|
||||||
|
|
||||||
export default function ChatSettings({ workspace }) {
|
export default function ChatSettings({ workspace, setWorkspace }) {
|
||||||
const [settings, setSettings] = useState({});
|
const [settings, setSettings] = useState({});
|
||||||
const [hasChanges, setHasChanges] = useState(false);
|
const [hasChanges, setHasChanges] = useState(false);
|
||||||
const [saving, setSaving] = useState(false);
|
const [saving, setSaving] = useState(false);
|
||||||
|
@ -43,6 +43,7 @@ export default function ChatSettings({ workspace }) {
|
||||||
);
|
);
|
||||||
if (!!updatedWorkspace) {
|
if (!!updatedWorkspace) {
|
||||||
showToast("Workspace updated!", "success", { clear: true });
|
showToast("Workspace updated!", "success", { clear: true });
|
||||||
|
setWorkspace(updatedWorkspace);
|
||||||
} else {
|
} else {
|
||||||
showToast(`Error: ${message}`, "error", { clear: true });
|
showToast(`Error: ${message}`, "error", { clear: true });
|
||||||
}
|
}
|
||||||
|
@ -75,6 +76,7 @@ export default function ChatSettings({ workspace }) {
|
||||||
/>
|
/>
|
||||||
<ChatEnableMetaResponse
|
<ChatEnableMetaResponse
|
||||||
workspace={workspace}
|
workspace={workspace}
|
||||||
|
|
||||||
setHasChanges={setHasChanges}
|
setHasChanges={setHasChanges}
|
||||||
/>
|
/>
|
||||||
{hasChanges && (
|
{hasChanges && (
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
import ToggleBlock from "@/components/Generic/Blocks/ToggleBlock";
|
||||||
|
|
||||||
|
export default function EnableFeatures({
|
||||||
|
feature,
|
||||||
|
isEnabled,
|
||||||
|
description,
|
||||||
|
onToggle,
|
||||||
|
Icon,
|
||||||
|
content,
|
||||||
|
disabled,
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<div className="relative w-full max-h-full ">
|
||||||
|
<ToggleBlock
|
||||||
|
initialChecked={isEnabled}
|
||||||
|
label={isEnabled ? `${feature} is Enabled` : `Enable ${feature}`}
|
||||||
|
onToggle={onToggle}
|
||||||
|
name={feature}
|
||||||
|
description={description}
|
||||||
|
border
|
||||||
|
Icon={Icon}
|
||||||
|
content={content}
|
||||||
|
disabled={disabled}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
import ToggleBlock from "@/components/Generic/Blocks/ToggleBlock";
|
||||||
|
import { List } from "@phosphor-icons/react";
|
||||||
|
|
||||||
|
export default function EnableInputs({ inputs, onToggle }) {
|
||||||
|
return (
|
||||||
|
<div className="relative w-full max-h-full ">
|
||||||
|
<ToggleBlock
|
||||||
|
initialChecked={inputs?.isEnabled}
|
||||||
|
label={inputs?.isEnabled ? "Inputs is Enabled" : "Enable Inputs"}
|
||||||
|
onToggle={onToggle}
|
||||||
|
name="inputs"
|
||||||
|
description="Traditionally, interaction with AnythingLLM occurs through a text area. Meta Inputs enhance this by offering alternative interaction methods, including option buttons, multi-select checkboxes, sliders, drop-down menus, and date/time selectors. To utilize these components, you'll need to guide the LLM on incorporating them into its responses with a specific schema"
|
||||||
|
badge
|
||||||
|
border
|
||||||
|
Icon={List}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
|
@ -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 (
|
||||||
|
<div className="relative w-full max-h-full ">
|
||||||
|
<ToggleBlock
|
||||||
|
initialChecked={config.systemPrompt.isEnabled}
|
||||||
|
label={
|
||||||
|
config.systemPrompt.isEnabled
|
||||||
|
? "System Prompt is Enabled - (optional)"
|
||||||
|
: "Enable System Prompt - (optional)"
|
||||||
|
}
|
||||||
|
onToggle={toggleSystemPrompt}
|
||||||
|
name="systemPrompt"
|
||||||
|
description="Specify the context and instructions for the AI in this workspace. A well-defined prompt ensures the AI delivers relevant and precise responses."
|
||||||
|
inline
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
|
@ -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 (
|
||||||
|
<div className="flex flex-col gap-4 mt-4">
|
||||||
|
<EnableSystemPrompt workspace={workspace} config={config} />
|
||||||
|
<TextAreaBlock workspace={workspace} />
|
||||||
|
<CheckBoxBlock workspace={workspace} label="override workspace prompt" inline name="systemPrompt"/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
86
frontend/src/pages/WorkspaceSettings/MetaResponse/index.jsx
Normal file
86
frontend/src/pages/WorkspaceSettings/MetaResponse/index.jsx
Normal file
|
@ -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 (
|
||||||
|
<div className="relative w-full gap-4 max-h-full grid grid-cols-1 lg:grid-cols-2 2xl:grid-cols-3">
|
||||||
|
{Object.keys(settings).map((feature) => {
|
||||||
|
const featureSettings = settings[feature];
|
||||||
|
const IconComponent = mapIcons[feature];
|
||||||
|
const FeatureComponent = mapFeatures[feature];
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={feature}
|
||||||
|
className={
|
||||||
|
featureSettings.isEnabled ? "lg:col-span-2 2xl:col-span-3" : ""
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<EnableFeatures
|
||||||
|
feature={feature}
|
||||||
|
isEnabled={featureSettings.isEnabled}
|
||||||
|
description={featureSettings.description}
|
||||||
|
onToggle={(enabled) =>
|
||||||
|
workspace.metaResponse &&
|
||||||
|
handleToggleEnableFeatures(feature, enabled)
|
||||||
|
}
|
||||||
|
disabled={!workspace.metaResponse}
|
||||||
|
Icon={IconComponent}
|
||||||
|
content={
|
||||||
|
featureSettings.isEnabled &&
|
||||||
|
FeatureComponent && (
|
||||||
|
<FeatureComponent
|
||||||
|
workspace={workspace}
|
||||||
|
config={featureSettings.config}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
|
@ -1,27 +1,28 @@
|
||||||
import React, { useEffect, useState } from "react";
|
import PasswordModal, { usePasswordModal } from "@/components/Modals/Password";
|
||||||
import { useParams } from "react-router-dom";
|
import { FullScreenLoader } from "@/components/Preloader";
|
||||||
import Sidebar from "@/components/Sidebar";
|
import Sidebar from "@/components/Sidebar";
|
||||||
import Workspace from "@/models/workspace";
|
import Workspace from "@/models/workspace";
|
||||||
import PasswordModal, { usePasswordModal } from "@/components/Modals/Password";
|
import paths from "@/utils/paths";
|
||||||
import { isMobile } from "react-device-detect";
|
|
||||||
import { FullScreenLoader } from "@/components/Preloader";
|
|
||||||
import {
|
import {
|
||||||
ArrowUUpLeft,
|
ArrowUUpLeft,
|
||||||
ChatText,
|
ChatText,
|
||||||
|
Cube,
|
||||||
Database,
|
Database,
|
||||||
Wrench,
|
Wrench,
|
||||||
} from "@phosphor-icons/react";
|
} from "@phosphor-icons/react";
|
||||||
import paths from "@/utils/paths";
|
import React, { useEffect, useState } from "react";
|
||||||
import { Link } from "react-router-dom";
|
import { isMobile } from "react-device-detect";
|
||||||
import { NavLink } from "react-router-dom";
|
import { Link, NavLink, useParams } from "react-router-dom";
|
||||||
import GeneralAppearance from "./GeneralAppearance";
|
|
||||||
import ChatSettings from "./ChatSettings";
|
import ChatSettings from "./ChatSettings";
|
||||||
|
import GeneralAppearance from "./GeneralAppearance";
|
||||||
import VectorDatabase from "./VectorDatabase";
|
import VectorDatabase from "./VectorDatabase";
|
||||||
|
import MetaResponse from "./MetaResponse";
|
||||||
|
|
||||||
const TABS = {
|
const TABS = {
|
||||||
"general-appearance": GeneralAppearance,
|
"general-appearance": GeneralAppearance,
|
||||||
"chat-settings": ChatSettings,
|
"chat-settings": ChatSettings,
|
||||||
"vector-database": VectorDatabase,
|
"vector-database": VectorDatabase,
|
||||||
|
"meta-response": MetaResponse,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function WorkspaceSettings() {
|
export default function WorkspaceSettings() {
|
||||||
|
@ -91,9 +92,20 @@ function ShowWorkspaceChat() {
|
||||||
icon={<Database className="h-6 w-6" />}
|
icon={<Database className="h-6 w-6" />}
|
||||||
to={paths.workspace.settings.vectorDatabase(slug)}
|
to={paths.workspace.settings.vectorDatabase(slug)}
|
||||||
/>
|
/>
|
||||||
|
{workspace.metaResponse && (
|
||||||
|
<TabItem
|
||||||
|
title="Meta Response"
|
||||||
|
icon={<Cube className="h-6 w-6" />}
|
||||||
|
to={paths.workspace.settings.metaResponse(slug)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="px-16 py-6">
|
<div className="px-16 py-6">
|
||||||
<TabContent slug={slug} workspace={workspace} />
|
<TabContent
|
||||||
|
slug={slug}
|
||||||
|
workspace={workspace}
|
||||||
|
setWorkspace={setWorkspace}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -65,6 +65,9 @@ export default {
|
||||||
vectorDatabase: (slug) => {
|
vectorDatabase: (slug) => {
|
||||||
return `/workspace/${slug}/settings/vector-database`;
|
return `/workspace/${slug}/settings/vector-database`;
|
||||||
},
|
},
|
||||||
|
metaResponse: (slug) => {
|
||||||
|
return `/workspace/${slug}/settings/meta-response`;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
thread: (wsSlug, threadSlug) => {
|
thread: (wsSlug, threadSlug) => {
|
||||||
return `/workspace/${wsSlug}/t/${threadSlug}`;
|
return `/workspace/${wsSlug}/t/${threadSlug}`;
|
||||||
|
|
|
@ -96,7 +96,7 @@ function workspaceMetaResponse(app) {
|
||||||
try {
|
try {
|
||||||
const workspace = response.locals.workspace;
|
const workspace = response.locals.workspace;
|
||||||
const data = request.body;
|
const data = request.body;
|
||||||
const result = await WorkspaceMetaResponse.update(workspace.id, JSON.stringify(data));
|
const result = await WorkspaceMetaResponse.update(workspace.id, data);
|
||||||
response.status(200).json(JSON.parse(result.metaResponseSettings));
|
response.status(200).json(JSON.parse(result.metaResponseSettings));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(e.message, e);
|
console.log(e.message, e);
|
||||||
|
@ -268,6 +268,7 @@ const metaResponseDefaultSettings = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
permissions: ["user"],
|
permissions: ["user"],
|
||||||
|
description: "Traditionally, interaction with AnythingLLM occurs through a text area. Meta Inputs enhance this by offering alternative interaction methods, including option buttons, multi-select checkboxes, sliders, drop-down menus, and date/time selectors. To utilize these components, you'll need to guide the LLM on incorporating them into its responses with a specific schema",
|
||||||
},
|
},
|
||||||
sentiments: {
|
sentiments: {
|
||||||
isEnabled: false,
|
isEnabled: false,
|
||||||
|
@ -279,6 +280,7 @@ const metaResponseDefaultSettings = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
permissions: ["user"],
|
permissions: ["user"],
|
||||||
|
description:"Activate to enable the AI to analyze and adapt its responses based on the emotional tone of the conversation, enhancing interaction personalization"
|
||||||
},
|
},
|
||||||
avatars: {
|
avatars: {
|
||||||
isEnabled: false,
|
isEnabled: false,
|
||||||
|
@ -288,6 +290,7 @@ const metaResponseDefaultSettings = {
|
||||||
avatarName: "user",
|
avatarName: "user",
|
||||||
},
|
},
|
||||||
permissions: ["user"],
|
permissions: ["user"],
|
||||||
|
description:"Enable avatars to reflect user sentiments, allowing the AI to visually empathize and convey understanding through changes in its profile image based on the meta object's sentiment data."
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -633,26 +633,107 @@ const metaResponseDefaultSettings = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
permissions: ["user"],
|
permissions: ["user"],
|
||||||
|
description: "Traditionally, interaction with AnythingLLM occurs through a text area. Meta Inputs enhance this by offering alternative interaction methods, including option buttons, multi-select checkboxes, sliders, drop-down menus, and date/time selectors. To utilize these components, you'll need to guide the LLM on incorporating them into its responses with a specific schema",
|
||||||
},
|
},
|
||||||
sentiments: {
|
sentiments: {
|
||||||
isEnabled: false,
|
isEnabled: false,
|
||||||
config: {
|
config: {
|
||||||
sentimentAnalysis: {
|
systemPrompt: {
|
||||||
isEnabled: false,
|
isEnabled: false,
|
||||||
|
content: "",
|
||||||
|
openAiPrompt: "",
|
||||||
|
overrideSystemPrompt: false,
|
||||||
|
suggestionsList: [
|
||||||
|
{
|
||||||
|
title: "",
|
||||||
|
content: "",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
canEdit: ["admin", "manager"],
|
||||||
|
},
|
||||||
|
promptSchema: {
|
||||||
|
content: "",
|
||||||
|
suggestionsList: [
|
||||||
|
{
|
||||||
|
title: "",
|
||||||
|
content: "",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
overrideWorkspacePrompt: false,
|
||||||
|
canEdit: ["admin", "manager"],
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
dropDownMenu: {
|
||||||
|
isEnabled: false,
|
||||||
|
options: [],
|
||||||
|
|
||||||
scoreThreshold: 0.5,
|
},
|
||||||
|
optionsList: {
|
||||||
|
isEnabled: false,
|
||||||
|
options: [],
|
||||||
|
},
|
||||||
|
optionsButtons: {
|
||||||
|
isEnabled: false,
|
||||||
|
options: [],
|
||||||
|
},
|
||||||
|
multiSelectCheckboxes: {
|
||||||
|
isEnabled: false,
|
||||||
|
options: [],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
permissions: ["user"],
|
permissions: ["user"],
|
||||||
|
description: "Activate to enable the AI to analyze and adapt its responses based on the emotional tone of the conversation, enhancing interaction personalization",
|
||||||
},
|
},
|
||||||
avatars: {
|
avatars: {
|
||||||
isEnabled: false,
|
isEnabled: false,
|
||||||
config: {
|
config: {
|
||||||
avatarType: "circle",
|
systemPrompt: {
|
||||||
avatarSize: "medium",
|
isEnabled: false,
|
||||||
avatarName: "user",
|
content: "",
|
||||||
|
openAiPrompt: "",
|
||||||
|
overrideSystemPrompt: false,
|
||||||
|
suggestionsList: [
|
||||||
|
{
|
||||||
|
title: "",
|
||||||
|
content: "",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
canEdit: ["admin", "manager"],
|
||||||
|
},
|
||||||
|
promptSchema: {
|
||||||
|
content: "",
|
||||||
|
suggestionsList: [
|
||||||
|
{
|
||||||
|
title: "",
|
||||||
|
content: "",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
overrideWorkspacePrompt: false,
|
||||||
|
canEdit: ["admin", "manager"],
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
dropDownMenu: {
|
||||||
|
isEnabled: false,
|
||||||
|
options: [],
|
||||||
|
|
||||||
|
},
|
||||||
|
optionsList: {
|
||||||
|
isEnabled: false,
|
||||||
|
options: [],
|
||||||
|
},
|
||||||
|
optionsButtons: {
|
||||||
|
isEnabled: false,
|
||||||
|
options: [],
|
||||||
|
},
|
||||||
|
multiSelectCheckboxes: {
|
||||||
|
isEnabled: false,
|
||||||
|
options: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
permissions: ["user"],
|
permissions: ["user"],
|
||||||
|
description: "Enable avatars to reflect user sentiments, allowing the AI to visually empathize and convey understanding through changes in its profile image based on the meta object's sentiment data.",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user