mirror of
https://github.com/Mintplex-Labs/anything-llm
synced 2026-04-25 17:15:37 +02:00
* Powerpoint File Creation (#5278) * wip * download card * UI for downloading * move to fs system with endpoint to pull files * refactor UI * final-pass * remove save-file-browser skill and refactor * remove fileDownload event * reset * reset file * reset timeout * persist toggle * Txt creation (#5279) * wip * download card * UI for downloading * move to fs system with endpoint to pull files * refactor UI * final-pass * remove save-file-browser skill and refactor * remove fileDownload event * reset * reset file * reset timeout * wip * persist toggle * add arbitrary text creation file * Add PDF document generation with markdown formatting (#5283) add support for branding in bottom right corner refactor core utils and frontend rendering * Xlsx document creation (#5284) add Excel doc & sheet creation * Basic docx creation (#5285) * Basic docx creation * add test theme support + styling and title pages * simplify skill selection * handle TG attachments * send documents over tg * lazy import * pin deps * fix lock * i18n for file creation (#5286) i18n for file-creation connect #5280 * theme overhaul * Add PPTX subagent for better results * forgot files * Add PPTX subagent for better results (#5287) * Add PPTX subagent for better results * forgot files * make sub-agent use proper tool calling if it can and better UI hints
874 lines
26 KiB
JavaScript
874 lines
26 KiB
JavaScript
import { API_BASE, AUTH_TIMESTAMP, fullApiUrl } from "@/utils/constants";
|
|
import { baseHeaders, safeJsonParse } from "@/utils/request";
|
|
import DataConnector from "./dataConnector";
|
|
import LiveDocumentSync from "./experimental/liveSync";
|
|
import AgentPlugins from "./experimental/agentPlugins";
|
|
import SystemPromptVariable from "./systemPromptVariable";
|
|
|
|
const System = {
|
|
cacheKeys: {
|
|
footerIcons: "anythingllm_footer_links",
|
|
supportEmail: "anythingllm_support_email",
|
|
customAppName: "anythingllm_custom_app_name",
|
|
canViewChatHistory: "anythingllm_can_view_chat_history",
|
|
deploymentVersion: "anythingllm_deployment_version",
|
|
},
|
|
ping: async function () {
|
|
return await fetch(`${API_BASE}/ping`)
|
|
.then((res) => res.json())
|
|
.then((res) => res?.online || false)
|
|
.catch(() => false);
|
|
},
|
|
totalIndexes: async function (slug = null) {
|
|
const url = new URL(`${fullApiUrl()}/system/system-vectors`);
|
|
if (!!slug) url.searchParams.append("slug", encodeURIComponent(slug));
|
|
return await fetch(url.toString(), {
|
|
headers: baseHeaders(),
|
|
})
|
|
.then((res) => {
|
|
if (!res.ok) throw new Error("Could not find indexes.");
|
|
return res.json();
|
|
})
|
|
.then((res) => res.vectorCount)
|
|
.catch(() => 0);
|
|
},
|
|
|
|
/**
|
|
* Checks if the onboarding is complete.
|
|
* @returns {Promise<boolean>}
|
|
*/
|
|
isOnboardingComplete: async function () {
|
|
return await fetch(`${API_BASE}/onboarding`)
|
|
.then((res) => {
|
|
if (!res.ok) throw new Error("Could not find onboarding information.");
|
|
return res.json();
|
|
})
|
|
.then((res) => res.onboardingComplete)
|
|
.catch(() => false);
|
|
},
|
|
/**
|
|
* Marks the onboarding as complete.
|
|
* @returns {Promise<boolean>}
|
|
*/
|
|
markOnboardingComplete: async function () {
|
|
return await fetch(`${API_BASE}/onboarding`, {
|
|
method: "POST",
|
|
headers: baseHeaders(),
|
|
})
|
|
.then((res) => res.ok)
|
|
.catch(() => false);
|
|
},
|
|
keys: async function () {
|
|
return await fetch(`${API_BASE}/setup-complete`)
|
|
.then((res) => {
|
|
if (!res.ok) throw new Error("Could not find setup information.");
|
|
return res.json();
|
|
})
|
|
.then((res) => res.results)
|
|
.catch(() => null);
|
|
},
|
|
localFiles: async function () {
|
|
return await fetch(`${API_BASE}/system/local-files`, {
|
|
headers: baseHeaders(),
|
|
})
|
|
.then((res) => {
|
|
if (!res.ok) throw new Error("Could not find setup information.");
|
|
return res.json();
|
|
})
|
|
.then((res) => res.localFiles)
|
|
.catch(() => null);
|
|
},
|
|
needsAuthCheck: function () {
|
|
const lastAuthCheck = window.localStorage.getItem(AUTH_TIMESTAMP);
|
|
if (!lastAuthCheck) return true;
|
|
const expiresAtMs = Number(lastAuthCheck) + 60 * 5 * 1000; // expires in 5 minutes in ms
|
|
return Number(new Date()) > expiresAtMs;
|
|
},
|
|
|
|
checkAuth: async function (currentToken = null) {
|
|
const valid = await fetch(`${API_BASE}/system/check-token`, {
|
|
headers: baseHeaders(currentToken),
|
|
})
|
|
.then((res) => res.ok)
|
|
.catch(() => false);
|
|
|
|
window.localStorage.setItem(AUTH_TIMESTAMP, Number(new Date()));
|
|
return valid;
|
|
},
|
|
requestToken: async function (body) {
|
|
return await fetch(`${API_BASE}/request-token`, {
|
|
method: "POST",
|
|
body: JSON.stringify({ ...body }),
|
|
})
|
|
.then((res) => {
|
|
if (!res.ok) throw new Error("Could not validate login.");
|
|
return res.json();
|
|
})
|
|
.then((res) => res)
|
|
.catch((e) => {
|
|
return { valid: false, message: e.message };
|
|
});
|
|
},
|
|
/**
|
|
* Refreshes the user object from the session.
|
|
* @returns {Promise<{success: boolean, user: Object | null, message: string | null}>}
|
|
*/
|
|
refreshUser: () => {
|
|
return fetch(`${API_BASE}/system/refresh-user`, {
|
|
headers: baseHeaders(),
|
|
})
|
|
.then((res) => {
|
|
if (!res.ok) throw new Error("Could not refresh user.");
|
|
return res.json();
|
|
})
|
|
.catch((e) => {
|
|
return { success: false, user: null, message: e.message };
|
|
});
|
|
},
|
|
recoverAccount: async function (username, recoveryCodes) {
|
|
return await fetch(`${API_BASE}/system/recover-account`, {
|
|
method: "POST",
|
|
headers: baseHeaders(),
|
|
body: JSON.stringify({ username, recoveryCodes }),
|
|
})
|
|
.then(async (res) => {
|
|
const data = await res.json();
|
|
if (!res.ok) {
|
|
throw new Error(data.message || "Error recovering account.");
|
|
}
|
|
return data;
|
|
})
|
|
.catch((e) => {
|
|
console.error(e);
|
|
return { success: false, error: e.message };
|
|
});
|
|
},
|
|
resetPassword: async function (token, newPassword, confirmPassword) {
|
|
return await fetch(`${API_BASE}/system/reset-password`, {
|
|
method: "POST",
|
|
headers: baseHeaders(),
|
|
body: JSON.stringify({ token, newPassword, confirmPassword }),
|
|
})
|
|
.then(async (res) => {
|
|
const data = await res.json();
|
|
if (!res.ok) {
|
|
throw new Error(data.message || "Error resetting password.");
|
|
}
|
|
return data;
|
|
})
|
|
.catch((e) => {
|
|
console.error(e);
|
|
return { success: false, error: e.message };
|
|
});
|
|
},
|
|
|
|
checkDocumentProcessorOnline: async () => {
|
|
return await fetch(`${API_BASE}/system/document-processing-status`, {
|
|
headers: baseHeaders(),
|
|
})
|
|
.then((res) => res.ok)
|
|
.catch(() => false);
|
|
},
|
|
acceptedDocumentTypes: async () => {
|
|
return await fetch(`${API_BASE}/system/accepted-document-types`, {
|
|
headers: baseHeaders(),
|
|
})
|
|
.then((res) => res.json())
|
|
.then((res) => res?.types)
|
|
.catch(() => null);
|
|
},
|
|
updateSystem: async (data) => {
|
|
return await fetch(`${API_BASE}/system/update-env`, {
|
|
method: "POST",
|
|
headers: baseHeaders(),
|
|
body: JSON.stringify(data),
|
|
})
|
|
.then((res) => res.json())
|
|
.catch((e) => {
|
|
console.error(e);
|
|
return { newValues: null, error: e.message };
|
|
});
|
|
},
|
|
updateSystemPassword: async (data) => {
|
|
return await fetch(`${API_BASE}/system/update-password`, {
|
|
method: "POST",
|
|
headers: baseHeaders(),
|
|
body: JSON.stringify(data),
|
|
})
|
|
.then((res) => res.json())
|
|
.catch((e) => {
|
|
console.error(e);
|
|
return { success: false, error: e.message };
|
|
});
|
|
},
|
|
setupMultiUser: async (data) => {
|
|
return await fetch(`${API_BASE}/system/enable-multi-user`, {
|
|
method: "POST",
|
|
headers: baseHeaders(),
|
|
body: JSON.stringify(data),
|
|
})
|
|
.then((res) => res.json())
|
|
.catch((e) => {
|
|
console.error(e);
|
|
return { success: false, error: e.message };
|
|
});
|
|
},
|
|
isMultiUserMode: async () => {
|
|
return await fetch(`${API_BASE}/system/multi-user-mode`, {
|
|
method: "GET",
|
|
headers: baseHeaders(),
|
|
})
|
|
.then((res) => res.json())
|
|
.then((res) => res?.multiUserMode)
|
|
.catch((e) => {
|
|
console.error(e);
|
|
return false;
|
|
});
|
|
},
|
|
deleteDocument: async (name) => {
|
|
return await fetch(`${API_BASE}/system/remove-document`, {
|
|
method: "DELETE",
|
|
headers: baseHeaders(),
|
|
body: JSON.stringify({ name }),
|
|
})
|
|
.then((res) => res.ok)
|
|
.catch((e) => {
|
|
console.error(e);
|
|
return false;
|
|
});
|
|
},
|
|
deleteDocuments: async (names = []) => {
|
|
return await fetch(`${API_BASE}/system/remove-documents`, {
|
|
method: "DELETE",
|
|
headers: baseHeaders(),
|
|
body: JSON.stringify({ names }),
|
|
})
|
|
.then((res) => res.ok)
|
|
.catch((e) => {
|
|
console.error(e);
|
|
return false;
|
|
});
|
|
},
|
|
deleteFolder: async (name) => {
|
|
return await fetch(`${API_BASE}/system/remove-folder`, {
|
|
method: "DELETE",
|
|
headers: baseHeaders(),
|
|
body: JSON.stringify({ name }),
|
|
})
|
|
.then((res) => res.ok)
|
|
.catch((e) => {
|
|
console.error(e);
|
|
return false;
|
|
});
|
|
},
|
|
uploadPfp: async function (formData) {
|
|
return await fetch(`${API_BASE}/system/upload-pfp`, {
|
|
method: "POST",
|
|
body: formData,
|
|
headers: baseHeaders(),
|
|
})
|
|
.then((res) => {
|
|
if (!res.ok) throw new Error("Error uploading pfp.");
|
|
return { success: true, error: null };
|
|
})
|
|
.catch((e) => {
|
|
console.log(e);
|
|
return { success: false, error: e.message };
|
|
});
|
|
},
|
|
uploadLogo: async function (formData) {
|
|
return await fetch(`${API_BASE}/system/upload-logo`, {
|
|
method: "POST",
|
|
body: formData,
|
|
headers: baseHeaders(),
|
|
})
|
|
.then((res) => {
|
|
if (!res.ok) throw new Error("Error uploading logo.");
|
|
return { success: true, error: null };
|
|
})
|
|
.catch((e) => {
|
|
console.log(e);
|
|
return { success: false, error: e.message };
|
|
});
|
|
},
|
|
fetchCustomFooterIcons: async function () {
|
|
const cache = window.localStorage.getItem(this.cacheKeys.footerIcons);
|
|
const { data, lastFetched } = cache
|
|
? safeJsonParse(cache, { data: [], lastFetched: 0 })
|
|
: { data: [], lastFetched: 0 };
|
|
|
|
if (!!data && Date.now() - lastFetched < 3_600_000)
|
|
return { footerData: data, error: null };
|
|
|
|
const { footerData, error } = await fetch(
|
|
`${API_BASE}/system/footer-data`,
|
|
{
|
|
method: "GET",
|
|
cache: "no-cache",
|
|
headers: baseHeaders(),
|
|
}
|
|
)
|
|
.then((res) => res.json())
|
|
.catch((e) => {
|
|
console.log(e);
|
|
return { footerData: [], error: e.message };
|
|
});
|
|
|
|
if (!footerData || !!error) return { footerData: [], error: null };
|
|
|
|
const newData = safeJsonParse(footerData, []);
|
|
window.localStorage.setItem(
|
|
this.cacheKeys.footerIcons,
|
|
JSON.stringify({ data: newData, lastFetched: Date.now() })
|
|
);
|
|
return { footerData: newData, error: null };
|
|
},
|
|
fetchSupportEmail: async function () {
|
|
const cache = window.localStorage.getItem(this.cacheKeys.supportEmail);
|
|
const { email, lastFetched } = cache
|
|
? safeJsonParse(cache, { email: "", lastFetched: 0 })
|
|
: { email: "", lastFetched: 0 };
|
|
|
|
if (!!email && Date.now() - lastFetched < 3_600_000)
|
|
return { email: email, error: null };
|
|
|
|
const { supportEmail, error } = await fetch(
|
|
`${API_BASE}/system/support-email`,
|
|
{
|
|
method: "GET",
|
|
cache: "no-cache",
|
|
headers: baseHeaders(),
|
|
}
|
|
)
|
|
.then((res) => res.json())
|
|
.catch((e) => {
|
|
console.log(e);
|
|
return { email: "", error: e.message };
|
|
});
|
|
|
|
if (!supportEmail || !!error) return { email: "", error: null };
|
|
window.localStorage.setItem(
|
|
this.cacheKeys.supportEmail,
|
|
JSON.stringify({ email: supportEmail, lastFetched: Date.now() })
|
|
);
|
|
return { email: supportEmail, error: null };
|
|
},
|
|
|
|
fetchCustomAppName: async function () {
|
|
const cache = window.localStorage.getItem(this.cacheKeys.customAppName);
|
|
const { appName, lastFetched } = cache
|
|
? safeJsonParse(cache, { appName: "", lastFetched: 0 })
|
|
: { appName: "", lastFetched: 0 };
|
|
|
|
if (!!appName && Date.now() - lastFetched < 3_600_000)
|
|
return { appName: appName, error: null };
|
|
|
|
const { customAppName, error } = await fetch(
|
|
`${API_BASE}/system/custom-app-name`,
|
|
{
|
|
method: "GET",
|
|
cache: "no-cache",
|
|
headers: baseHeaders(),
|
|
}
|
|
)
|
|
.then((res) => res.json())
|
|
.catch((e) => {
|
|
console.log(e);
|
|
return { customAppName: "", error: e.message };
|
|
});
|
|
|
|
if (!customAppName || !!error) {
|
|
window.localStorage.removeItem(this.cacheKeys.customAppName);
|
|
return { appName: "", error: null };
|
|
}
|
|
|
|
window.localStorage.setItem(
|
|
this.cacheKeys.customAppName,
|
|
JSON.stringify({ appName: customAppName, lastFetched: Date.now() })
|
|
);
|
|
return { appName: customAppName, error: null };
|
|
},
|
|
/**
|
|
* Fetches the default system prompt from the server.
|
|
* @returns {Promise<{defaultSystemPrompt: string, saneDefaultSystemPrompt: string}>}
|
|
*/
|
|
fetchDefaultSystemPrompt: async function () {
|
|
return await fetch(`${API_BASE}/system/default-system-prompt`, {
|
|
method: "GET",
|
|
headers: baseHeaders(),
|
|
})
|
|
.then((res) => res.json())
|
|
.then((res) => ({
|
|
defaultSystemPrompt: res.defaultSystemPrompt,
|
|
saneDefaultSystemPrompt: res.saneDefaultSystemPrompt,
|
|
}))
|
|
.catch((e) => {
|
|
console.error(e);
|
|
return { defaultSystemPrompt: "", saneDefaultSystemPrompt: "" };
|
|
});
|
|
},
|
|
updateDefaultSystemPrompt: async function (defaultSystemPrompt) {
|
|
try {
|
|
const res = await fetch(`${API_BASE}/system/default-system-prompt`, {
|
|
method: "POST",
|
|
headers: baseHeaders(),
|
|
body: JSON.stringify({ defaultSystemPrompt }),
|
|
});
|
|
const data = await res.json();
|
|
return data;
|
|
} catch (e) {
|
|
console.error(e);
|
|
return { success: false, message: e.message };
|
|
}
|
|
},
|
|
fetchLogo: async function () {
|
|
const url = new URL(`${fullApiUrl()}/system/logo`);
|
|
url.searchParams.append(
|
|
"theme",
|
|
localStorage.getItem("theme") || "default"
|
|
);
|
|
|
|
return await fetch(url, {
|
|
method: "GET",
|
|
cache: "no-cache",
|
|
})
|
|
.then(async (res) => {
|
|
if (res.ok && res.status !== 204) {
|
|
const isCustomLogo = res.headers.get("X-Is-Custom-Logo") === "true";
|
|
const blob = await res.blob();
|
|
const logoURL = URL.createObjectURL(blob);
|
|
return { isCustomLogo, logoURL };
|
|
}
|
|
throw new Error("Failed to fetch logo!");
|
|
})
|
|
.catch((e) => {
|
|
console.log(e);
|
|
return { isCustomLogo: false, logoURL: null };
|
|
});
|
|
},
|
|
fetchPfp: async function (id) {
|
|
return await fetch(`${API_BASE}/system/pfp/${id}`, {
|
|
method: "GET",
|
|
cache: "no-cache",
|
|
headers: baseHeaders(),
|
|
})
|
|
.then((res) => {
|
|
if (res.ok && res.status !== 204) return res.blob();
|
|
throw new Error("Failed to fetch pfp.");
|
|
})
|
|
.then((blob) => (blob ? URL.createObjectURL(blob) : null))
|
|
.catch(() => {
|
|
return null;
|
|
});
|
|
},
|
|
removePfp: async function () {
|
|
return await fetch(`${API_BASE}/system/remove-pfp`, {
|
|
method: "DELETE",
|
|
headers: baseHeaders(),
|
|
})
|
|
.then((res) => {
|
|
if (res.ok) return { success: true, error: null };
|
|
throw new Error("Failed to remove pfp.");
|
|
})
|
|
.catch((e) => {
|
|
console.log(e);
|
|
return { success: false, error: e.message };
|
|
});
|
|
},
|
|
|
|
isDefaultLogo: async function () {
|
|
return await fetch(`${API_BASE}/system/is-default-logo`, {
|
|
method: "GET",
|
|
cache: "no-cache",
|
|
})
|
|
.then((res) => {
|
|
if (!res.ok) throw new Error("Failed to get is default logo!");
|
|
return res.json();
|
|
})
|
|
.then((res) => res?.isDefaultLogo)
|
|
.catch((e) => {
|
|
console.log(e);
|
|
return null;
|
|
});
|
|
},
|
|
removeCustomLogo: async function () {
|
|
return await fetch(`${API_BASE}/system/remove-logo`, {
|
|
headers: baseHeaders(),
|
|
})
|
|
.then((res) => {
|
|
if (res.ok) return { success: true, error: null };
|
|
throw new Error("Error removing logo!");
|
|
})
|
|
.catch((e) => {
|
|
console.log(e);
|
|
return { success: false, error: e.message };
|
|
});
|
|
},
|
|
getApiKeys: async function () {
|
|
return fetch(`${API_BASE}/system/api-keys`, {
|
|
method: "GET",
|
|
headers: baseHeaders(),
|
|
})
|
|
.then((res) => {
|
|
if (!res.ok) {
|
|
throw new Error(res.statusText || "Error fetching api key.");
|
|
}
|
|
return res.json();
|
|
})
|
|
.catch((e) => {
|
|
console.error(e);
|
|
return { apiKey: null, error: e.message };
|
|
});
|
|
},
|
|
generateApiKey: async function () {
|
|
return fetch(`${API_BASE}/system/generate-api-key`, {
|
|
method: "POST",
|
|
headers: baseHeaders(),
|
|
})
|
|
.then((res) => {
|
|
if (!res.ok) {
|
|
throw new Error(res.statusText || "Error generating api key.");
|
|
}
|
|
return res.json();
|
|
})
|
|
.catch((e) => {
|
|
console.error(e);
|
|
return { apiKey: null, error: e.message };
|
|
});
|
|
},
|
|
deleteApiKey: async function (apiKeyId = "") {
|
|
return fetch(`${API_BASE}/system/api-key/${apiKeyId}`, {
|
|
method: "DELETE",
|
|
headers: baseHeaders(),
|
|
})
|
|
.then((res) => res.ok)
|
|
.catch((e) => {
|
|
console.error(e);
|
|
return false;
|
|
});
|
|
},
|
|
customModels: async function (
|
|
provider,
|
|
apiKey = null,
|
|
basePath = null,
|
|
timeout = null
|
|
) {
|
|
const controller = new AbortController();
|
|
if (!!timeout) {
|
|
setTimeout(() => {
|
|
controller.abort("Request timed out.");
|
|
}, timeout);
|
|
}
|
|
|
|
return fetch(`${API_BASE}/system/custom-models`, {
|
|
method: "POST",
|
|
headers: baseHeaders(),
|
|
signal: controller.signal,
|
|
body: JSON.stringify({
|
|
provider,
|
|
apiKey,
|
|
basePath,
|
|
}),
|
|
})
|
|
.then((res) => {
|
|
if (!res.ok) {
|
|
throw new Error(res.statusText || "Error finding custom models.");
|
|
}
|
|
return res.json();
|
|
})
|
|
.catch((e) => {
|
|
console.error(e);
|
|
return { models: [], error: e.message };
|
|
});
|
|
},
|
|
chats: async (offset = 0) => {
|
|
return await fetch(`${API_BASE}/system/workspace-chats`, {
|
|
method: "POST",
|
|
headers: baseHeaders(),
|
|
body: JSON.stringify({ offset }),
|
|
})
|
|
.then((res) => res.json())
|
|
.catch((e) => {
|
|
console.error(e);
|
|
return [];
|
|
});
|
|
},
|
|
eventLogs: async (offset = 0) => {
|
|
return await fetch(`${API_BASE}/system/event-logs`, {
|
|
method: "POST",
|
|
headers: baseHeaders(),
|
|
body: JSON.stringify({ offset }),
|
|
})
|
|
.then((res) => res.json())
|
|
.catch((e) => {
|
|
console.error(e);
|
|
return [];
|
|
});
|
|
},
|
|
clearEventLogs: async () => {
|
|
return await fetch(`${API_BASE}/system/event-logs`, {
|
|
method: "DELETE",
|
|
headers: baseHeaders(),
|
|
})
|
|
.then((res) => res.json())
|
|
.catch((e) => {
|
|
console.error(e);
|
|
return { success: false, error: e.message };
|
|
});
|
|
},
|
|
deleteChat: async (chatId) => {
|
|
return await fetch(`${API_BASE}/system/workspace-chats/${chatId}`, {
|
|
method: "DELETE",
|
|
headers: baseHeaders(),
|
|
})
|
|
.then((res) => res.json())
|
|
.catch((e) => {
|
|
console.error(e);
|
|
return { success: false, error: e.message };
|
|
});
|
|
},
|
|
exportChats: async (type = "csv", chatType = "workspace") => {
|
|
const url = new URL(`${fullApiUrl()}/system/export-chats`);
|
|
url.searchParams.append("type", encodeURIComponent(type));
|
|
url.searchParams.append("chatType", encodeURIComponent(chatType));
|
|
return await fetch(url, {
|
|
method: "GET",
|
|
headers: baseHeaders(),
|
|
})
|
|
.then((res) => {
|
|
if (res.ok) return res.text();
|
|
throw new Error(res.statusText);
|
|
})
|
|
.catch((e) => {
|
|
console.error(e);
|
|
return null;
|
|
});
|
|
},
|
|
updateUser: async (data) => {
|
|
return await fetch(`${API_BASE}/system/user`, {
|
|
method: "POST",
|
|
headers: baseHeaders(),
|
|
body: JSON.stringify(data),
|
|
})
|
|
.then((res) => res.json())
|
|
.catch((e) => {
|
|
console.error(e);
|
|
return { success: false, error: e.message };
|
|
});
|
|
},
|
|
dataConnectors: DataConnector,
|
|
|
|
getSlashCommandPresets: async function () {
|
|
return await fetch(`${API_BASE}/system/slash-command-presets`, {
|
|
method: "GET",
|
|
headers: baseHeaders(),
|
|
})
|
|
.then((res) => {
|
|
if (!res.ok) throw new Error("Could not fetch slash command presets.");
|
|
return res.json();
|
|
})
|
|
.then((res) => res.presets)
|
|
.catch((e) => {
|
|
console.error(e);
|
|
return [];
|
|
});
|
|
},
|
|
|
|
createSlashCommandPreset: async function (presetData) {
|
|
return await fetch(`${API_BASE}/system/slash-command-presets`, {
|
|
method: "POST",
|
|
headers: baseHeaders(),
|
|
body: JSON.stringify(presetData),
|
|
})
|
|
.then(async (res) => {
|
|
const data = await res.json();
|
|
if (!res.ok)
|
|
throw new Error(
|
|
data.message || "Error creating slash command preset."
|
|
);
|
|
return data;
|
|
})
|
|
.then((res) => ({ preset: res.preset, error: null }))
|
|
.catch((e) => {
|
|
console.error(e);
|
|
return { preset: null, error: e.message };
|
|
});
|
|
},
|
|
|
|
updateSlashCommandPreset: async function (presetId, presetData) {
|
|
return await fetch(`${API_BASE}/system/slash-command-presets/${presetId}`, {
|
|
method: "POST",
|
|
headers: baseHeaders(),
|
|
body: JSON.stringify(presetData),
|
|
})
|
|
.then(async (res) => {
|
|
const data = await res.json();
|
|
if (!res.ok)
|
|
throw new Error(
|
|
data.message || "Could not update slash command preset."
|
|
);
|
|
return data;
|
|
})
|
|
.then((res) => ({ preset: res.preset, error: null }))
|
|
.catch((e) => {
|
|
console.error(e);
|
|
return { preset: null, error: e.message };
|
|
});
|
|
},
|
|
|
|
deleteSlashCommandPreset: async function (presetId) {
|
|
return await fetch(`${API_BASE}/system/slash-command-presets/${presetId}`, {
|
|
method: "DELETE",
|
|
headers: baseHeaders(),
|
|
})
|
|
.then((res) => {
|
|
if (!res.ok) throw new Error("Could not delete slash command preset.");
|
|
return true;
|
|
})
|
|
.catch((e) => {
|
|
console.error(e);
|
|
return false;
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Fetches the can view chat history state from local storage or the system settings.
|
|
* Notice: This is an instance setting that cannot be changed via the UI and it is cached
|
|
* in local storage for 24 hours.
|
|
* @returns {Promise<{viewable: boolean, error: string | null}>}
|
|
*/
|
|
fetchCanViewChatHistory: async function () {
|
|
const cache = window.localStorage.getItem(
|
|
this.cacheKeys.canViewChatHistory
|
|
);
|
|
const { viewable, lastFetched } = cache
|
|
? safeJsonParse(cache, { viewable: false, lastFetched: 0 })
|
|
: { viewable: false, lastFetched: 0 };
|
|
|
|
// Since this is an instance setting that cannot be changed via the UI,
|
|
// we can cache it in local storage for a day and if the admin changes it,
|
|
// they should instruct the users to clear local storage.
|
|
if (typeof viewable === "boolean" && Date.now() - lastFetched < 8.64e7)
|
|
return { viewable, error: null };
|
|
|
|
const res = await System.keys();
|
|
const isViewable = res?.DisableViewChatHistory === false;
|
|
|
|
window.localStorage.setItem(
|
|
this.cacheKeys.canViewChatHistory,
|
|
JSON.stringify({ viewable: isViewable, lastFetched: Date.now() })
|
|
);
|
|
return { viewable: isViewable, error: null };
|
|
},
|
|
|
|
/**
|
|
* Validates a temporary auth token and logs in the user if the token is valid.
|
|
* @param {string} publicToken - the token to validate against
|
|
* @returns {Promise<{valid: boolean, user: import("@prisma/client").users | null, token: string | null, message: string | null}>}
|
|
*/
|
|
simpleSSOLogin: async function (publicToken) {
|
|
return fetch(`${API_BASE}/request-token/sso/simple?token=${publicToken}`, {
|
|
method: "GET",
|
|
})
|
|
.then(async (res) => {
|
|
if (!res.ok) {
|
|
const text = await res.text();
|
|
if (!text.startsWith("{")) throw new Error(text);
|
|
return JSON.parse(text);
|
|
}
|
|
return await res.json();
|
|
})
|
|
.catch((e) => {
|
|
console.error(e);
|
|
return { valid: false, user: null, token: null, message: e.message };
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Fetches the app version from the server.
|
|
* @returns {Promise<string | null>} The app version.
|
|
*/
|
|
fetchAppVersion: async function () {
|
|
const cache = window.localStorage.getItem(this.cacheKeys.deploymentVersion);
|
|
const { version, lastFetched } = cache
|
|
? safeJsonParse(cache, { version: null, lastFetched: 0 })
|
|
: { version: null, lastFetched: 0 };
|
|
|
|
if (!!version && Date.now() - lastFetched < 3_600_000) return version;
|
|
const newVersion = await fetch(`${API_BASE}/utils/metrics`, {
|
|
method: "GET",
|
|
cache: "no-cache",
|
|
})
|
|
.then((res) => {
|
|
if (!res.ok) throw new Error("Could not fetch app version.");
|
|
return res.json();
|
|
})
|
|
.then((res) => res?.appVersion)
|
|
.catch(() => null);
|
|
|
|
if (!newVersion) return null;
|
|
window.localStorage.setItem(
|
|
this.cacheKeys.deploymentVersion,
|
|
JSON.stringify({ version: newVersion, lastFetched: Date.now() })
|
|
);
|
|
return newVersion;
|
|
},
|
|
|
|
/**
|
|
* Validates a SQL connection string.
|
|
* @param {'postgresql'|'mysql'|'sql-server'} engine - the database engine identifier
|
|
* @param {string} connectionString - the connection string to validate
|
|
* @returns {Promise<{success: boolean, error: string | null}>}
|
|
*/
|
|
validateSQLConnection: async function (engine, connectionString) {
|
|
return fetch(`${API_BASE}/system/validate-sql-connection`, {
|
|
method: "POST",
|
|
headers: baseHeaders(),
|
|
body: JSON.stringify({ engine, connectionString }),
|
|
})
|
|
.then((res) => res.json())
|
|
.catch((e) => {
|
|
console.error("Failed to validate SQL connection:", e);
|
|
return { success: false, error: e.message };
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Checks if the filesystem-agent skill is available.
|
|
* The filesystem-agent skill is only available when running in a Docker container.
|
|
* @returns {Promise<boolean>}
|
|
*/
|
|
isFileSystemAgentAvailable: async function () {
|
|
return fetch(`${API_BASE}/agent-skills/filesystem-agent/is-available`, {
|
|
method: "GET",
|
|
headers: baseHeaders(),
|
|
})
|
|
.then((res) => res.json())
|
|
.then((res) => res?.available ?? false)
|
|
.catch(() => false);
|
|
},
|
|
|
|
/**
|
|
* Checks if the create-files-agent skill is available.
|
|
* The create-files-agent skill is only available when running in a Docker container.
|
|
* @returns {Promise<boolean>}
|
|
*/
|
|
isCreateFilesAgentAvailable: async function () {
|
|
return fetch(`${API_BASE}/agent-skills/create-files-agent/is-available`, {
|
|
method: "GET",
|
|
headers: baseHeaders(),
|
|
})
|
|
.then((res) => res.json())
|
|
.then((res) => res?.available ?? false)
|
|
.catch(() => false);
|
|
},
|
|
|
|
experimentalFeatures: {
|
|
liveSync: LiveDocumentSync,
|
|
agentPlugins: AgentPlugins,
|
|
},
|
|
promptVariables: SystemPromptVariable,
|
|
};
|
|
|
|
export default System;
|