mirror of
https://github.com/Mintplex-Labs/anything-llm.git
synced 2024-11-19 12:40:09 +01:00
Merge branch 'master' of github.com:Mintplex-Labs/anything-llm into render
This commit is contained in:
commit
8fc547e78a
@ -78,8 +78,8 @@
|
|||||||
},
|
},
|
||||||
"updateContentCommand": "cd server && yarn && cd ../collector && PUPPETEER_DOWNLOAD_BASE_URL=https://storage.googleapis.com/chrome-for-testing-public yarn && cd ../frontend && yarn && cd .. && yarn setup:envs && yarn prisma:setup && echo \"Please run yarn dev:server, yarn dev:collector, and yarn dev:frontend in separate terminal tabs.\"",
|
"updateContentCommand": "cd server && yarn && cd ../collector && PUPPETEER_DOWNLOAD_BASE_URL=https://storage.googleapis.com/chrome-for-testing-public yarn && cd ../frontend && yarn && cd .. && yarn setup:envs && yarn prisma:setup && echo \"Please run yarn dev:server, yarn dev:collector, and yarn dev:frontend in separate terminal tabs.\"",
|
||||||
// Use 'postCreateCommand' to run commands after the container is created.
|
// Use 'postCreateCommand' to run commands after the container is created.
|
||||||
// This configures VITE for github codespaces
|
// This configures VITE for github codespaces and installs gh cli
|
||||||
"postCreateCommand": "if [ \"${CODESPACES}\" = \"true\" ]; then echo 'VITE_API_BASE=\"https://$CODESPACE_NAME-3001.$GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN/api\"' > ./frontend/.env; fi",
|
"postCreateCommand": "if [ \"${CODESPACES}\" = \"true\" ]; then echo 'VITE_API_BASE=\"https://$CODESPACE_NAME-3001.$GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN/api\"' > ./frontend/.env && (type -p wget >/dev/null || (sudo apt update && sudo apt-get install wget -y)) && sudo mkdir -p -m 755 /etc/apt/keyrings && wget -qO- https://cli.github.com/packages/githubcli-archive-keyring.gpg | sudo tee /etc/apt/keyrings/githubcli-archive-keyring.gpg > /dev/null && sudo chmod go+r /etc/apt/keyrings/githubcli-archive-keyring.gpg && echo \"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main\" | sudo tee /etc/apt/sources.list.d/github-cli.list > /dev/null && sudo apt update && sudo apt install gh -y; fi",
|
||||||
"portsAttributes": {
|
"portsAttributes": {
|
||||||
"3001": {
|
"3001": {
|
||||||
"label": "Backend",
|
"label": "Backend",
|
||||||
@ -208,4 +208,4 @@
|
|||||||
}
|
}
|
||||||
// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
|
// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
|
||||||
// "remoteUser": "root"
|
// "remoteUser": "root"
|
||||||
}
|
}
|
||||||
|
115
.github/workflows/build-and-push-image-semver.yaml
vendored
Normal file
115
.github/workflows/build-and-push-image-semver.yaml
vendored
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
name: Publish AnythingLLM Docker image on Release (amd64 & arm64)
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: build-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
on:
|
||||||
|
release:
|
||||||
|
types: [published]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
push_multi_platform_to_registries:
|
||||||
|
name: Push Docker multi-platform image to multiple registries
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
packages: write
|
||||||
|
contents: read
|
||||||
|
steps:
|
||||||
|
- name: Check out the repo
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Check if DockerHub build needed
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
# Check if the secret for USERNAME is set (don't even check for the password)
|
||||||
|
if [[ -z "${{ secrets.DOCKER_USERNAME }}" ]]; then
|
||||||
|
echo "DockerHub build not needed"
|
||||||
|
echo "enabled=false" >> $GITHUB_OUTPUT
|
||||||
|
else
|
||||||
|
echo "DockerHub build needed"
|
||||||
|
echo "enabled=true" >> $GITHUB_OUTPUT
|
||||||
|
fi
|
||||||
|
id: dockerhub
|
||||||
|
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v3
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
|
- name: Log in to Docker Hub
|
||||||
|
uses: docker/login-action@f4ef78c080cd8ba55a85445d5b36e214a81df20a
|
||||||
|
# Only login to the Docker Hub if the repo is mintplex/anythingllm, to allow for forks to build on GHCR
|
||||||
|
if: steps.dockerhub.outputs.enabled == 'true'
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKER_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||||
|
|
||||||
|
- name: Log in to the Container registry
|
||||||
|
uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Extract metadata (tags, labels) for Docker
|
||||||
|
id: meta
|
||||||
|
uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7
|
||||||
|
with:
|
||||||
|
images: |
|
||||||
|
${{ steps.dockerhub.outputs.enabled == 'true' && 'mintplexlabs/anythingllm' || '' }}
|
||||||
|
ghcr.io/${{ github.repository }}
|
||||||
|
tags: |
|
||||||
|
type=semver,pattern={{version}}
|
||||||
|
type=semver,pattern={{major}}.{{minor}}
|
||||||
|
|
||||||
|
- name: Build and push multi-platform Docker image
|
||||||
|
uses: docker/build-push-action@v6
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
file: ./docker/Dockerfile
|
||||||
|
push: true
|
||||||
|
sbom: true
|
||||||
|
provenance: mode=max
|
||||||
|
platforms: linux/amd64,linux/arm64
|
||||||
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
|
cache-from: type=gha
|
||||||
|
cache-to: type=gha,mode=max
|
||||||
|
|
||||||
|
# For Docker scout there are some intermediary reported CVEs which exists outside
|
||||||
|
# of execution content or are unreachable by an attacker but exist in image.
|
||||||
|
# We create VEX files for these so they don't show in scout summary.
|
||||||
|
- name: Collect known and verified CVE exceptions
|
||||||
|
id: cve-list
|
||||||
|
run: |
|
||||||
|
# Collect CVEs from filenames in vex folder
|
||||||
|
CVE_NAMES=""
|
||||||
|
for file in ./docker/vex/*.vex.json; do
|
||||||
|
[ -e "$file" ] || continue
|
||||||
|
filename=$(basename "$file")
|
||||||
|
stripped_filename=${filename%.vex.json}
|
||||||
|
CVE_NAMES+=" $stripped_filename"
|
||||||
|
done
|
||||||
|
echo "CVE_EXCEPTIONS=$CVE_NAMES" >> $GITHUB_OUTPUT
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
# About VEX attestations https://docs.docker.com/scout/explore/exceptions/
|
||||||
|
# Justifications https://github.com/openvex/spec/blob/main/OPENVEX-SPEC.md#status-justifications
|
||||||
|
- name: Add VEX attestations
|
||||||
|
env:
|
||||||
|
CVE_EXCEPTIONS: ${{ steps.cve-list.outputs.CVE_EXCEPTIONS }}
|
||||||
|
run: |
|
||||||
|
echo $CVE_EXCEPTIONS
|
||||||
|
curl -sSfL https://raw.githubusercontent.com/docker/scout-cli/main/install.sh | sh -s --
|
||||||
|
for cve in $CVE_EXCEPTIONS; do
|
||||||
|
for tag in "${{ join(fromJSON(steps.meta.outputs.json).tags, ' ') }}"; do
|
||||||
|
echo "Attaching VEX exception $cve to $tag"
|
||||||
|
docker scout attestation add \
|
||||||
|
--file "./docker/vex/$cve.vex.json" \
|
||||||
|
--predicate-type https://openvex.dev/ns/v0.2.0 \
|
||||||
|
$tag
|
||||||
|
done
|
||||||
|
done
|
||||||
|
shell: bash
|
2
.github/workflows/dev-build.yaml
vendored
2
.github/workflows/dev-build.yaml
vendored
@ -6,7 +6,7 @@ concurrency:
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: ['558-multi-modal-support'] # put your current branch to create a build. Core team only.
|
branches: ['pipertts-support'] # put your current branch to create a build. Core team only.
|
||||||
paths-ignore:
|
paths-ignore:
|
||||||
- '**.md'
|
- '**.md'
|
||||||
- 'cloud-deployments/*'
|
- 'cloud-deployments/*'
|
||||||
|
2
.vscode/tasks.json
vendored
2
.vscode/tasks.json
vendored
@ -46,7 +46,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"command": "cd ${workspaceFolder}/server/ && yarn dev",
|
"command": "if [ \"${CODESPACES}\" = \"true\" ]; then while ! gh codespace ports -c $CODESPACE_NAME | grep 3001; do sleep 1; done; gh codespace ports visibility 3001:public -c $CODESPACE_NAME; fi & cd ${workspaceFolder}/server/ && yarn dev",
|
||||||
"runOptions": {
|
"runOptions": {
|
||||||
"instanceLimit": 1,
|
"instanceLimit": 1,
|
||||||
"reevaluateOnRerun": true
|
"reevaluateOnRerun": true
|
||||||
|
@ -110,6 +110,7 @@ AnythingLLM divides your documents into objects called `workspaces`. A Workspace
|
|||||||
**TTS (text-to-speech) support:**
|
**TTS (text-to-speech) support:**
|
||||||
|
|
||||||
- Native Browser Built-in (default)
|
- Native Browser Built-in (default)
|
||||||
|
- [PiperTTSLocal - runs in browser](https://github.com/rhasspy/piper)
|
||||||
- [OpenAI TTS](https://platform.openai.com/docs/guides/text-to-speech/voice-options)
|
- [OpenAI TTS](https://platform.openai.com/docs/guides/text-to-speech/voice-options)
|
||||||
- [ElevenLabs](https://elevenlabs.io/)
|
- [ElevenLabs](https://elevenlabs.io/)
|
||||||
|
|
||||||
|
@ -9,8 +9,10 @@ sudo systemctl enable docker
|
|||||||
sudo systemctl start docker
|
sudo systemctl start docker
|
||||||
|
|
||||||
mkdir -p /home/anythingllm
|
mkdir -p /home/anythingllm
|
||||||
touch /home/anythingllm/.env
|
cat <<EOF >/home/anythingllm/.env
|
||||||
|
${env_content}
|
||||||
|
EOF
|
||||||
|
|
||||||
sudo docker pull mintplexlabs/anythingllm
|
sudo docker pull mintplexlabs/anythingllm
|
||||||
sudo docker run -d -p 3001:3001 --cap-add SYS_ADMIN -v /home/anythingllm:/app/server/storage -v /home/anythingllm/.env:/app/server/.env -e STORAGE_DIR="/app/server/storage" mintplexlabs/anythingllm
|
sudo docker run -d -p 3001:3001 --cap-add SYS_ADMIN -v /home/anythingllm:/app/server/storage -v /home/anythingllm/.env:/app/server/.env -e STORAGE_DIR="/app/server/storage" mintplexlabs/anythingllm
|
||||||
echo "Container ID: $(sudo docker ps --latest --quiet)"
|
echo "Container ID: $(sudo docker ps --latest --quiet)"
|
||||||
|
@ -223,10 +223,6 @@ class GitLabRepoLoader {
|
|||||||
const objects = Array.isArray(data)
|
const objects = Array.isArray(data)
|
||||||
? data.filter((item) => item.type === "blob")
|
? data.filter((item) => item.type === "blob")
|
||||||
: []; // only get files, not paths or submodules
|
: []; // only get files, not paths or submodules
|
||||||
if (objects.length === 0) {
|
|
||||||
fetching = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply ignore path rules to found objects. If any rules match it is an invalid file path.
|
// Apply ignore path rules to found objects. If any rules match it is an invalid file path.
|
||||||
console.log(
|
console.log(
|
||||||
|
@ -9,34 +9,36 @@ const { tokenizeString } = require("../../tokenizer");
|
|||||||
const path = require("path");
|
const path = require("path");
|
||||||
const fs = require("fs");
|
const fs = require("fs");
|
||||||
|
|
||||||
async function discoverLinks(startUrl, depth = 1, maxLinks = 20) {
|
async function discoverLinks(startUrl, maxDepth = 1, maxLinks = 20) {
|
||||||
const baseUrl = new URL(startUrl);
|
const baseUrl = new URL(startUrl);
|
||||||
const discoveredLinks = new Set();
|
const discoveredLinks = new Set([startUrl]);
|
||||||
const pendingLinks = [startUrl];
|
let queue = [[startUrl, 0]]; // [url, currentDepth]
|
||||||
let currentLevel = 0;
|
const scrapedUrls = new Set();
|
||||||
depth = depth < 1 ? 1 : depth;
|
|
||||||
maxLinks = maxLinks < 1 ? 1 : maxLinks;
|
|
||||||
|
|
||||||
// Check depth and if there are any links left to scrape
|
for (let currentDepth = 0; currentDepth < maxDepth; currentDepth++) {
|
||||||
while (currentLevel < depth && pendingLinks.length > 0) {
|
const levelSize = queue.length;
|
||||||
const newLinks = await getPageLinks(pendingLinks[0], baseUrl);
|
const nextQueue = [];
|
||||||
pendingLinks.shift();
|
|
||||||
|
|
||||||
for (const link of newLinks) {
|
for (let i = 0; i < levelSize && discoveredLinks.size < maxLinks; i++) {
|
||||||
if (!discoveredLinks.has(link)) {
|
const [currentUrl, urlDepth] = queue[i];
|
||||||
discoveredLinks.add(link);
|
|
||||||
pendingLinks.push(link);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Exit out if we reach maxLinks
|
if (!scrapedUrls.has(currentUrl)) {
|
||||||
if (discoveredLinks.size >= maxLinks) {
|
scrapedUrls.add(currentUrl);
|
||||||
return Array.from(discoveredLinks).slice(0, maxLinks);
|
const newLinks = await getPageLinks(currentUrl, baseUrl);
|
||||||
|
|
||||||
|
for (const link of newLinks) {
|
||||||
|
if (!discoveredLinks.has(link) && discoveredLinks.size < maxLinks) {
|
||||||
|
discoveredLinks.add(link);
|
||||||
|
if (urlDepth + 1 < maxDepth) {
|
||||||
|
nextQueue.push([link, urlDepth + 1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pendingLinks.length === 0) {
|
queue = nextQueue;
|
||||||
currentLevel++;
|
if (queue.length === 0 || discoveredLinks.size >= maxLinks) break;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return Array.from(discoveredLinks);
|
return Array.from(discoveredLinks);
|
||||||
|
@ -47,10 +47,12 @@ class YoutubeTranscript {
|
|||||||
let transcript = "";
|
let transcript = "";
|
||||||
const chunks = transcriptXML.getElementsByTagName("text");
|
const chunks = transcriptXML.getElementsByTagName("text");
|
||||||
for (const chunk of chunks) {
|
for (const chunk of chunks) {
|
||||||
transcript += chunk.textContent;
|
// Add space after each text chunk
|
||||||
|
transcript += chunk.textContent + " ";
|
||||||
}
|
}
|
||||||
|
|
||||||
return transcript;
|
// Trim extra whitespace
|
||||||
|
return transcript.trim().replace(/\s+/g, " ");
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw new YoutubeTranscriptError(e);
|
throw new YoutubeTranscriptError(e);
|
||||||
}
|
}
|
||||||
|
@ -37,6 +37,7 @@ class MimeDetector {
|
|||||||
"lua",
|
"lua",
|
||||||
"pas",
|
"pas",
|
||||||
"r",
|
"r",
|
||||||
|
"go",
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
true
|
true
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@metamask/jazzicon": "^2.0.0",
|
"@metamask/jazzicon": "^2.0.0",
|
||||||
"@microsoft/fetch-event-source": "^2.0.1",
|
"@microsoft/fetch-event-source": "^2.0.1",
|
||||||
|
"@mintplex-labs/piper-tts-web": "^1.0.4",
|
||||||
"@phosphor-icons/react": "^2.1.7",
|
"@phosphor-icons/react": "^2.1.7",
|
||||||
"@tremor/react": "^3.15.1",
|
"@tremor/react": "^3.15.1",
|
||||||
"dompurify": "^3.0.8",
|
"dompurify": "^3.0.8",
|
||||||
@ -24,7 +25,9 @@
|
|||||||
"js-levenshtein": "^1.1.6",
|
"js-levenshtein": "^1.1.6",
|
||||||
"lodash.debounce": "^4.0.8",
|
"lodash.debounce": "^4.0.8",
|
||||||
"markdown-it": "^13.0.1",
|
"markdown-it": "^13.0.1",
|
||||||
|
"markdown-it-katex": "^2.0.3",
|
||||||
"moment": "^2.30.1",
|
"moment": "^2.30.1",
|
||||||
|
"onnxruntime-web": "^1.18.0",
|
||||||
"pluralize": "^8.0.0",
|
"pluralize": "^8.0.0",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-device-detect": "^2.2.2",
|
"react-device-detect": "^2.2.2",
|
||||||
|
220
frontend/src/components/TextToSpeech/PiperTTSOptions/index.jsx
Normal file
220
frontend/src/components/TextToSpeech/PiperTTSOptions/index.jsx
Normal file
@ -0,0 +1,220 @@
|
|||||||
|
import { useState, useEffect, useRef } from "react";
|
||||||
|
import PiperTTSClient from "@/utils/piperTTS";
|
||||||
|
import { titleCase } from "text-case";
|
||||||
|
import { humanFileSize } from "@/utils/numbers";
|
||||||
|
import showToast from "@/utils/toast";
|
||||||
|
import { CircleNotch, PauseCircle, PlayCircle } from "@phosphor-icons/react";
|
||||||
|
|
||||||
|
export default function PiperTTSOptions({ settings }) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<p className="text-sm font-base text-white text-opacity-60 mb-4">
|
||||||
|
All PiperTTS models will run in your browser locally. This can be
|
||||||
|
resource intensive on lower-end devices.
|
||||||
|
</p>
|
||||||
|
<div className="flex gap-x-4 items-center">
|
||||||
|
<PiperTTSModelSelection settings={settings} />
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function voicesByLanguage(voices = []) {
|
||||||
|
const voicesByLanguage = voices.reduce((acc, voice) => {
|
||||||
|
const langName = voice?.language?.name_english ?? "Unlisted";
|
||||||
|
acc[langName] = acc[langName] || [];
|
||||||
|
acc[langName].push(voice);
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
return Object.entries(voicesByLanguage);
|
||||||
|
}
|
||||||
|
|
||||||
|
function voiceDisplayName(voice) {
|
||||||
|
const { is_stored, name, quality, files } = voice;
|
||||||
|
const onnxFileKey = Object.keys(files).find((key) => key.endsWith(".onnx"));
|
||||||
|
const fileSize = files?.[onnxFileKey]?.size_bytes || 0;
|
||||||
|
return `${is_stored ? "✔ " : ""}${titleCase(name)}-${quality === "low" ? "Low" : "HQ"} (${humanFileSize(fileSize)})`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function PiperTTSModelSelection({ settings }) {
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
|
const [voices, setVoices] = useState([]);
|
||||||
|
const [selectedVoice, setSelectedVoice] = useState(
|
||||||
|
settings?.TTSPiperTTSVoiceModel
|
||||||
|
);
|
||||||
|
|
||||||
|
function flushVoices() {
|
||||||
|
PiperTTSClient.flush()
|
||||||
|
.then(() =>
|
||||||
|
showToast("All voices flushed from browser storage", "info", {
|
||||||
|
clear: true,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.catch((e) => console.error(e));
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
PiperTTSClient.voices()
|
||||||
|
.then((voices) => {
|
||||||
|
if (voices?.length !== 0) return setVoices(voices);
|
||||||
|
throw new Error("Could not fetch voices from web worker.");
|
||||||
|
})
|
||||||
|
.catch((e) => {
|
||||||
|
console.error(e);
|
||||||
|
})
|
||||||
|
.finally(() => setLoading(false));
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
if (loading) {
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col w-60">
|
||||||
|
<label className="text-white text-sm font-semibold block mb-3">
|
||||||
|
Voice Model Selection
|
||||||
|
</label>
|
||||||
|
<select
|
||||||
|
name="TTSPiperTTSVoiceModel"
|
||||||
|
value=""
|
||||||
|
disabled={true}
|
||||||
|
className="border-none bg-zinc-900 border-gray-500 text-white text-sm rounded-lg block w-full p-2.5"
|
||||||
|
>
|
||||||
|
<option value="" disabled={true}>
|
||||||
|
-- loading available models --
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col w-fit">
|
||||||
|
<div className="flex flex-col w-60">
|
||||||
|
<label className="text-white text-sm font-semibold block mb-3">
|
||||||
|
Voice Model Selection
|
||||||
|
</label>
|
||||||
|
<div className="flex items-center w-fit gap-x-4 mb-2">
|
||||||
|
<select
|
||||||
|
name="TTSPiperTTSVoiceModel"
|
||||||
|
required={true}
|
||||||
|
onChange={(e) => setSelectedVoice(e.target.value)}
|
||||||
|
value={selectedVoice}
|
||||||
|
className="border-none flex-shrink-0 bg-zinc-900 border-gray-500 text-white text-sm rounded-lg block w-full p-2.5"
|
||||||
|
>
|
||||||
|
{voicesByLanguage(voices).map(([lang, voices]) => {
|
||||||
|
return (
|
||||||
|
<optgroup key={lang} label={lang}>
|
||||||
|
{voices.map((voice) => (
|
||||||
|
<option key={voice.key} value={voice.key}>
|
||||||
|
{voiceDisplayName(voice)}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</optgroup>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</select>
|
||||||
|
<DemoVoiceSample voiceId={selectedVoice} />
|
||||||
|
</div>
|
||||||
|
<p className="text-xs text-white/40">
|
||||||
|
The "✔" indicates this model is already stored locally and does not
|
||||||
|
need to be downloaded when run.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
{!!voices.find((voice) => voice.is_stored) && (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={flushVoices}
|
||||||
|
className="w-fit border-none hover:text-white hover:underline text-white/40 text-sm my-4"
|
||||||
|
>
|
||||||
|
Flush voice cache
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function DemoVoiceSample({ voiceId }) {
|
||||||
|
const playerRef = useRef(null);
|
||||||
|
const [speaking, setSpeaking] = useState(false);
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [audioSrc, setAudioSrc] = useState(null);
|
||||||
|
|
||||||
|
async function speakMessage(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
if (speaking) {
|
||||||
|
playerRef?.current?.pause();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (!audioSrc) {
|
||||||
|
setLoading(true);
|
||||||
|
const client = new PiperTTSClient({ voiceId });
|
||||||
|
const blobUrl = await client.getAudioBlobForText(
|
||||||
|
"Hello, welcome to AnythingLLM!"
|
||||||
|
);
|
||||||
|
setAudioSrc(blobUrl);
|
||||||
|
setLoading(false);
|
||||||
|
client.worker?.terminate();
|
||||||
|
PiperTTSClient._instance = null;
|
||||||
|
} else {
|
||||||
|
playerRef.current.play();
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
setLoading(false);
|
||||||
|
setSpeaking(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
function setupPlayer() {
|
||||||
|
if (!playerRef?.current) return;
|
||||||
|
playerRef.current.addEventListener("play", () => {
|
||||||
|
setSpeaking(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
playerRef.current.addEventListener("pause", () => {
|
||||||
|
playerRef.current.currentTime = 0;
|
||||||
|
setSpeaking(false);
|
||||||
|
setAudioSrc(null);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
setupPlayer();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={speakMessage}
|
||||||
|
disabled={loading}
|
||||||
|
className="border-none text-zinc-300 flex items-center gap-x-1"
|
||||||
|
>
|
||||||
|
{speaking ? (
|
||||||
|
<>
|
||||||
|
<PauseCircle size={20} className="flex-shrink-0" />
|
||||||
|
<p className="text-sm flex-shrink-0">Stop demo</p>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
{loading ? (
|
||||||
|
<>
|
||||||
|
<CircleNotch size={20} className="animate-spin flex-shrink-0" />
|
||||||
|
<p className="text-sm flex-shrink-0">Loading voice</p>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<PlayCircle size={20} className="flex-shrink-0" />
|
||||||
|
<p className="text-sm flex-shrink-0">Play sample</p>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
<audio
|
||||||
|
ref={playerRef}
|
||||||
|
hidden={true}
|
||||||
|
src={audioSrc}
|
||||||
|
autoPlay={true}
|
||||||
|
controls={false}
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
}
|
@ -7,6 +7,7 @@ import { Plus, X } from "@phosphor-icons/react";
|
|||||||
|
|
||||||
export default function AccountModal({ user, hideModal }) {
|
export default function AccountModal({ user, hideModal }) {
|
||||||
const { pfp, setPfp } = usePfp();
|
const { pfp, setPfp } = usePfp();
|
||||||
|
|
||||||
const handleFileUpload = async (event) => {
|
const handleFileUpload = async (event) => {
|
||||||
const file = event.target.files[0];
|
const file = event.target.files[0];
|
||||||
if (!file) return false;
|
if (!file) return false;
|
||||||
@ -133,6 +134,10 @@ export default function AccountModal({ user, hideModal }) {
|
|||||||
required
|
required
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
/>
|
/>
|
||||||
|
<p className="mt-2 text-xs text-white/60">
|
||||||
|
Username must be only contain lowercase letters, numbers,
|
||||||
|
underscores, and hyphens with no spaces
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label
|
<label
|
||||||
@ -143,10 +148,14 @@ export default function AccountModal({ user, hideModal }) {
|
|||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
name="password"
|
name="password"
|
||||||
type="password"
|
type="text"
|
||||||
className="bg-zinc-900 placeholder:text-white/20 border-gray-500 text-white text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5"
|
className="bg-zinc-900 placeholder:text-white/20 border-gray-500 text-white text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5"
|
||||||
placeholder={`${user.username}'s new password`}
|
placeholder={`${user.username}'s new password`}
|
||||||
|
minLength={8}
|
||||||
/>
|
/>
|
||||||
|
<p className="mt-2 text-xs text-white/60">
|
||||||
|
Password must be at least 8 characters long
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<LanguagePreference />
|
<LanguagePreference />
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import NativeTTSMessage from "./native";
|
import NativeTTSMessage from "./native";
|
||||||
import AsyncTTSMessage from "./asyncTts";
|
import AsyncTTSMessage from "./asyncTts";
|
||||||
|
import PiperTTSMessage from "./piperTTS";
|
||||||
import System from "@/models/system";
|
import System from "@/models/system";
|
||||||
|
|
||||||
export default function TTSMessage({ slug, chatId, message }) {
|
export default function TTSMessage({ slug, chatId, message }) {
|
||||||
|
const [settings, setSettings] = useState({});
|
||||||
const [provider, setProvider] = useState("native");
|
const [provider, setProvider] = useState("native");
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
|
|
||||||
@ -11,13 +13,26 @@ export default function TTSMessage({ slug, chatId, message }) {
|
|||||||
async function getSettings() {
|
async function getSettings() {
|
||||||
const _settings = await System.keys();
|
const _settings = await System.keys();
|
||||||
setProvider(_settings?.TextToSpeechProvider ?? "native");
|
setProvider(_settings?.TextToSpeechProvider ?? "native");
|
||||||
|
setSettings(_settings);
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
getSettings();
|
getSettings();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
if (!chatId || loading) return null;
|
if (!chatId || loading) return null;
|
||||||
if (provider !== "native")
|
|
||||||
return <AsyncTTSMessage slug={slug} chatId={chatId} />;
|
switch (provider) {
|
||||||
return <NativeTTSMessage message={message} />;
|
case "openai":
|
||||||
|
case "elevenlabs":
|
||||||
|
return <AsyncTTSMessage slug={slug} chatId={chatId} />;
|
||||||
|
case "piper_local":
|
||||||
|
return (
|
||||||
|
<PiperTTSMessage
|
||||||
|
voiceId={settings?.TTSPiperTTSVoiceModel}
|
||||||
|
message={message}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
default:
|
||||||
|
return <NativeTTSMessage message={message} />;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,91 @@
|
|||||||
|
import { useEffect, useState, useRef } from "react";
|
||||||
|
import { SpeakerHigh, PauseCircle, CircleNotch } from "@phosphor-icons/react";
|
||||||
|
import { Tooltip } from "react-tooltip";
|
||||||
|
import PiperTTSClient from "@/utils/piperTTS";
|
||||||
|
|
||||||
|
export default function PiperTTS({ voiceId = null, message }) {
|
||||||
|
const playerRef = useRef(null);
|
||||||
|
const [speaking, setSpeaking] = useState(false);
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [audioSrc, setAudioSrc] = useState(null);
|
||||||
|
|
||||||
|
async function speakMessage(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
if (speaking) {
|
||||||
|
playerRef?.current?.pause();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (!audioSrc) {
|
||||||
|
setLoading(true);
|
||||||
|
const client = new PiperTTSClient({ voiceId });
|
||||||
|
const blobUrl = await client.getAudioBlobForText(message);
|
||||||
|
setAudioSrc(blobUrl);
|
||||||
|
setLoading(false);
|
||||||
|
} else {
|
||||||
|
playerRef.current.play();
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
setLoading(false);
|
||||||
|
setSpeaking(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
function setupPlayer() {
|
||||||
|
if (!playerRef?.current) return;
|
||||||
|
playerRef.current.addEventListener("play", () => {
|
||||||
|
setSpeaking(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
playerRef.current.addEventListener("pause", () => {
|
||||||
|
playerRef.current.currentTime = 0;
|
||||||
|
setSpeaking(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
setupPlayer();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="mt-3 relative">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={speakMessage}
|
||||||
|
disabled={loading}
|
||||||
|
data-tooltip-id="message-to-speech"
|
||||||
|
data-tooltip-content={
|
||||||
|
speaking ? "Pause TTS speech of message" : "TTS Speak message"
|
||||||
|
}
|
||||||
|
className="border-none text-zinc-300"
|
||||||
|
aria-label={speaking ? "Pause speech" : "Speak message"}
|
||||||
|
>
|
||||||
|
{speaking ? (
|
||||||
|
<PauseCircle size={18} className="mb-1" />
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
{loading ? (
|
||||||
|
<CircleNotch size={18} className="mb-1 animate-spin" />
|
||||||
|
) : (
|
||||||
|
<SpeakerHigh size={18} className="mb-1" />
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
<audio
|
||||||
|
ref={playerRef}
|
||||||
|
hidden={true}
|
||||||
|
src={audioSrc}
|
||||||
|
autoPlay={true}
|
||||||
|
controls={false}
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
<Tooltip
|
||||||
|
id="message-to-speech"
|
||||||
|
place="bottom"
|
||||||
|
delayShow={300}
|
||||||
|
className="tooltip !text-xs"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
@ -1,3 +1,4 @@
|
|||||||
|
import useUser from "@/hooks/useUser";
|
||||||
import { PaperclipHorizontal } from "@phosphor-icons/react";
|
import { PaperclipHorizontal } from "@phosphor-icons/react";
|
||||||
import { Tooltip } from "react-tooltip";
|
import { Tooltip } from "react-tooltip";
|
||||||
|
|
||||||
@ -6,6 +7,9 @@ import { Tooltip } from "react-tooltip";
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export default function AttachItem() {
|
export default function AttachItem() {
|
||||||
|
const { user } = useUser();
|
||||||
|
if (!!user && user.role === "default") return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<button
|
<button
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { useState } from "react";
|
import { useState, useEffect } from "react";
|
||||||
import { X } from "@phosphor-icons/react";
|
import { X } from "@phosphor-icons/react";
|
||||||
import ModalWrapper from "@/components/ModalWrapper";
|
import ModalWrapper from "@/components/ModalWrapper";
|
||||||
import { CMD_REGEX } from ".";
|
import { CMD_REGEX } from ".";
|
||||||
@ -10,9 +10,15 @@ export default function EditPresetModal({
|
|||||||
onDelete,
|
onDelete,
|
||||||
preset,
|
preset,
|
||||||
}) {
|
}) {
|
||||||
const [command, setCommand] = useState(preset?.command?.slice(1) || "");
|
const [command, setCommand] = useState("");
|
||||||
const [deleting, setDeleting] = useState(false);
|
const [deleting, setDeleting] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (preset && isOpen) {
|
||||||
|
setCommand(preset.command?.slice(1) || "");
|
||||||
|
}
|
||||||
|
}, [preset, isOpen]);
|
||||||
|
|
||||||
const handleSubmit = (e) => {
|
const handleSubmit = (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const form = new FormData(e.target);
|
const form = new FormData(e.target);
|
||||||
|
@ -62,13 +62,18 @@ export default function SlashPresets({ setShowing, sendCommand }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fetchPresets();
|
fetchPresets();
|
||||||
closeEditModal();
|
closeEditModalAndResetPreset();
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleDeletePreset = async (presetId) => {
|
const handleDeletePreset = async (presetId) => {
|
||||||
await System.deleteSlashCommandPreset(presetId);
|
await System.deleteSlashCommandPreset(presetId);
|
||||||
fetchPresets();
|
fetchPresets();
|
||||||
|
closeEditModalAndResetPreset();
|
||||||
|
};
|
||||||
|
|
||||||
|
const closeEditModalAndResetPreset = () => {
|
||||||
closeEditModal();
|
closeEditModal();
|
||||||
|
setSelectedPreset(null);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -116,7 +121,7 @@ export default function SlashPresets({ setShowing, sendCommand }) {
|
|||||||
{selectedPreset && (
|
{selectedPreset && (
|
||||||
<EditPresetModal
|
<EditPresetModal
|
||||||
isOpen={isEditModalOpen}
|
isOpen={isEditModalOpen}
|
||||||
onClose={closeEditModal}
|
onClose={closeEditModalAndResetPreset}
|
||||||
onSave={handleUpdatePreset}
|
onSave={handleUpdatePreset}
|
||||||
onDelete={handleDeletePreset}
|
onDelete={handleDeletePreset}
|
||||||
preset={selectedPreset}
|
preset={selectedPreset}
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
import { useEffect } from "react";
|
import { useEffect, useCallback } from "react";
|
||||||
import { Microphone } from "@phosphor-icons/react";
|
import { Microphone } from "@phosphor-icons/react";
|
||||||
import { Tooltip } from "react-tooltip";
|
import { Tooltip } from "react-tooltip";
|
||||||
import _regeneratorRuntime from "regenerator-runtime";
|
import _regeneratorRuntime from "regenerator-runtime";
|
||||||
import SpeechRecognition, {
|
import SpeechRecognition, {
|
||||||
useSpeechRecognition,
|
useSpeechRecognition,
|
||||||
} from "react-speech-recognition";
|
} from "react-speech-recognition";
|
||||||
|
import { PROMPT_INPUT_EVENT } from "../../PromptInput";
|
||||||
|
|
||||||
let timeout;
|
let timeout;
|
||||||
const SILENCE_INTERVAL = 3_200; // wait in seconds of silence before closing.
|
const SILENCE_INTERVAL = 3_200; // wait in seconds of silence before closing.
|
||||||
@ -45,15 +46,49 @@ export default function SpeechToText({ sendCommand }) {
|
|||||||
clearTimeout(timeout);
|
clearTimeout(timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleKeyPress = useCallback(
|
||||||
|
(event) => {
|
||||||
|
if (event.ctrlKey && event.keyCode === 77) {
|
||||||
|
if (listening) {
|
||||||
|
endTTSSession();
|
||||||
|
} else {
|
||||||
|
startSTTSession();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[listening, endTTSSession, startSTTSession]
|
||||||
|
);
|
||||||
|
|
||||||
|
function handlePromptUpdate(e) {
|
||||||
|
if (!e?.detail && timeout) {
|
||||||
|
endTTSSession();
|
||||||
|
clearTimeout(timeout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (transcript?.length > 0) {
|
document.addEventListener("keydown", handleKeyPress);
|
||||||
|
return () => {
|
||||||
|
document.removeEventListener("keydown", handleKeyPress);
|
||||||
|
};
|
||||||
|
}, [handleKeyPress]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!!window)
|
||||||
|
window.addEventListener(PROMPT_INPUT_EVENT, handlePromptUpdate);
|
||||||
|
return () =>
|
||||||
|
window?.removeEventListener(PROMPT_INPUT_EVENT, handlePromptUpdate);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (transcript?.length > 0 && listening) {
|
||||||
sendCommand(transcript, false);
|
sendCommand(transcript, false);
|
||||||
clearTimeout(timeout);
|
clearTimeout(timeout);
|
||||||
timeout = setTimeout(() => {
|
timeout = setTimeout(() => {
|
||||||
endTTSSession();
|
endTTSSession();
|
||||||
}, SILENCE_INTERVAL);
|
}, SILENCE_INTERVAL);
|
||||||
}
|
}
|
||||||
}, [transcript]);
|
}, [transcript, listening]);
|
||||||
|
|
||||||
if (!browserSupportsSpeechRecognition) return null;
|
if (!browserSupportsSpeechRecognition) return null;
|
||||||
return (
|
return (
|
||||||
@ -69,7 +104,9 @@ export default function SpeechToText({ sendCommand }) {
|
|||||||
>
|
>
|
||||||
<Microphone
|
<Microphone
|
||||||
weight="fill"
|
weight="fill"
|
||||||
className="w-6 h-6 pointer-events-none text-white"
|
className={`w-6 h-6 pointer-events-none text-white overflow-hidden rounded-full ${
|
||||||
|
listening ? "animate-pulse-glow" : ""
|
||||||
|
}`}
|
||||||
/>
|
/>
|
||||||
<Tooltip
|
<Tooltip
|
||||||
id="tooltip-text-size-btn"
|
id="tooltip-text-size-btn"
|
||||||
|
@ -112,7 +112,7 @@ export default function PromptInput({
|
|||||||
className="flex flex-col gap-y-1 rounded-t-lg md:w-3/4 w-full mx-auto max-w-xl items-center"
|
className="flex flex-col gap-y-1 rounded-t-lg md:w-3/4 w-full mx-auto max-w-xl items-center"
|
||||||
>
|
>
|
||||||
<div className="flex items-center rounded-lg md:mb-4">
|
<div className="flex items-center rounded-lg md:mb-4">
|
||||||
<div className="w-[635px] bg-main-gradient shadow-2xl border border-white/50 rounded-2xl flex flex-col px-4 overflow-hidden">
|
<div className="w-[95vw] md:w-[635px] bg-main-gradient shadow-2xl border border-white/50 rounded-2xl flex flex-col px-4 overflow-hidden">
|
||||||
<AttachmentManager attachments={attachments} />
|
<AttachmentManager attachments={attachments} />
|
||||||
<div className="flex items-center w-full border-b-2 border-gray-500/50">
|
<div className="flex items-center w-full border-b-2 border-gray-500/50">
|
||||||
<textarea
|
<textarea
|
||||||
|
@ -14,6 +14,9 @@ import handleSocketResponse, {
|
|||||||
AGENT_SESSION_START,
|
AGENT_SESSION_START,
|
||||||
} from "@/utils/chat/agent";
|
} from "@/utils/chat/agent";
|
||||||
import DnDFileUploaderWrapper from "./DnDWrapper";
|
import DnDFileUploaderWrapper from "./DnDWrapper";
|
||||||
|
import SpeechRecognition, {
|
||||||
|
useSpeechRecognition,
|
||||||
|
} from "react-speech-recognition";
|
||||||
|
|
||||||
export default function ChatContainer({ workspace, knownHistory = [] }) {
|
export default function ChatContainer({ workspace, knownHistory = [] }) {
|
||||||
const { threadSlug = null } = useParams();
|
const { threadSlug = null } = useParams();
|
||||||
@ -29,6 +32,10 @@ export default function ChatContainer({ workspace, knownHistory = [] }) {
|
|||||||
setMessage(event.target.value);
|
setMessage(event.target.value);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const { listening, resetTranscript } = useSpeechRecognition({
|
||||||
|
clearTranscriptOnListen: true,
|
||||||
|
});
|
||||||
|
|
||||||
// Emit an update to the state of the prompt input without directly
|
// Emit an update to the state of the prompt input without directly
|
||||||
// passing a prop in so that it does not re-render constantly.
|
// passing a prop in so that it does not re-render constantly.
|
||||||
function setMessageEmit(messageContent = "") {
|
function setMessageEmit(messageContent = "") {
|
||||||
@ -57,11 +64,20 @@ export default function ChatContainer({ workspace, knownHistory = [] }) {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
if (listening) {
|
||||||
|
// Stop the mic if the send button is clicked
|
||||||
|
endTTSSession();
|
||||||
|
}
|
||||||
setChatHistory(prevChatHistory);
|
setChatHistory(prevChatHistory);
|
||||||
setMessageEmit("");
|
setMessageEmit("");
|
||||||
setLoadingResponse(true);
|
setLoadingResponse(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function endTTSSession() {
|
||||||
|
SpeechRecognition.stopListening();
|
||||||
|
resetTranscript();
|
||||||
|
}
|
||||||
|
|
||||||
const regenerateAssistantMessage = (chatId) => {
|
const regenerateAssistantMessage = (chatId) => {
|
||||||
const updatedHistory = chatHistory.slice(0, -1);
|
const updatedHistory = chatHistory.slice(0, -1);
|
||||||
const lastUserMessage = updatedHistory.slice(-1)[0];
|
const lastUserMessage = updatedHistory.slice(-1)[0];
|
||||||
|
@ -794,3 +794,12 @@ does not extend the close button beyond the viewport. */
|
|||||||
top: -3.5rem;
|
top: -3.5rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Math/Katex formatting to prevent duplication of content on screen */
|
||||||
|
.katex-html[aria-hidden="true"] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.katex-mathml {
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
482
frontend/src/locales/de/common.js
Normal file
482
frontend/src/locales/de/common.js
Normal file
@ -0,0 +1,482 @@
|
|||||||
|
const TRANSLATIONS = {
|
||||||
|
common: {
|
||||||
|
"workspaces-name": "Name der Arbeitsbereiche",
|
||||||
|
error: "Fehler",
|
||||||
|
success: "Erfolg",
|
||||||
|
user: "Benutzer",
|
||||||
|
selection: "Modellauswahl",
|
||||||
|
saving: "Speichern...",
|
||||||
|
save: "Änderungen speichern",
|
||||||
|
previous: "Vorherige Seite",
|
||||||
|
next: "Nächste Seite",
|
||||||
|
},
|
||||||
|
|
||||||
|
settings: {
|
||||||
|
title: "Instanzeinstellungen",
|
||||||
|
system: "Allgemeine Einstellungen",
|
||||||
|
invites: "Einladungen",
|
||||||
|
users: "Benutzer",
|
||||||
|
workspaces: "Arbeitsbereiche",
|
||||||
|
"workspace-chats": "Arbeitsbereich-Chats",
|
||||||
|
customization: "Anpassung",
|
||||||
|
"api-keys": "Entwickler-API",
|
||||||
|
llm: "LLM",
|
||||||
|
transcription: "Transkription",
|
||||||
|
embedder: "Einbettung",
|
||||||
|
"text-splitting": "Textsplitting & Chunking",
|
||||||
|
"voice-speech": "Sprache & Sprachausgabe",
|
||||||
|
"vector-database": "Vektordatenbank",
|
||||||
|
embeds: "Chat-Einbettung",
|
||||||
|
"embed-chats": "Chat-Einbettungsverlauf",
|
||||||
|
security: "Sicherheit",
|
||||||
|
"event-logs": "Ereignisprotokolle",
|
||||||
|
privacy: "Datenschutz & Datenverarbeitung",
|
||||||
|
"ai-providers": "KI-Anbieter",
|
||||||
|
"agent-skills": "Agentenfähigkeiten",
|
||||||
|
admin: "Administrator",
|
||||||
|
tools: "Werkzeuge",
|
||||||
|
"experimental-features": "Experimentelle Funktionen",
|
||||||
|
contact: "Support kontaktieren",
|
||||||
|
},
|
||||||
|
|
||||||
|
login: {
|
||||||
|
"multi-user": {
|
||||||
|
welcome: "Willkommen bei",
|
||||||
|
"placeholder-username": "Benutzername",
|
||||||
|
"placeholder-password": "Passwort",
|
||||||
|
login: "Anmelden",
|
||||||
|
validating: "Überprüfung...",
|
||||||
|
"forgot-pass": "Passwort vergessen",
|
||||||
|
reset: "Zurücksetzen",
|
||||||
|
},
|
||||||
|
"sign-in": {
|
||||||
|
start: "Melden Sie sich bei Ihrem",
|
||||||
|
end: "Konto an.",
|
||||||
|
},
|
||||||
|
"password-reset": {
|
||||||
|
title: "Passwort zurücksetzen",
|
||||||
|
description:
|
||||||
|
"Geben Sie die erforderlichen Informationen unten ein, um Ihr Passwort zurückzusetzen.",
|
||||||
|
"recovery-codes": "Wiederherstellungscodes",
|
||||||
|
"recovery-code": "Wiederherstellungscode {{index}}",
|
||||||
|
"back-to-login": "Zurück zur Anmeldung",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
welcomeMessage: {
|
||||||
|
part1:
|
||||||
|
"Willkommen bei AnythingLLM, AnythingLLM ist ein Open-Source-KI-Tool von Mintplex Labs, das alles in einen trainierten Chatbot verwandelt, den Sie abfragen und mit dem Sie chatten können. AnythingLLM ist eine BYOK-Software (Bring-Your-Own-Keys), daher gibt es keine Abonnements, Gebühren oder Kosten für diese Software außerhalb der Dienste, die Sie damit nutzen möchten.",
|
||||||
|
part2:
|
||||||
|
"AnythingLLM ist der einfachste Weg, leistungsstarke KI-Produkte wie OpenAI, GPT-4, LangChain, PineconeDB, ChromaDB und andere Dienste in einem übersichtlichen Paket ohne Aufwand zusammenzufassen, um Ihre Produktivität um das 100-fache zu steigern.",
|
||||||
|
part3:
|
||||||
|
"AnythingLLM kann vollständig lokal auf Ihrem Computer mit geringem Overhead laufen - Sie werden nicht einmal bemerken, dass es da ist! Keine GPU erforderlich. Cloud- und On-Premises-Installation ist ebenfalls verfügbar.\nDas KI-Tool-Ökosystem wird jeden Tag leistungsfähiger. AnythingLLM macht es einfach, es zu nutzen.",
|
||||||
|
githubIssue: "Erstellen Sie ein Problem auf Github",
|
||||||
|
user1: "Wie fange ich an?!",
|
||||||
|
part4:
|
||||||
|
'Es ist einfach. Alle Sammlungen sind in Behältern organisiert, die wir "Arbeitsbereiche" nennen. Arbeitsbereiche sind Behälter für Dateien, Dokumente, Bilder, PDFs und andere Dateien, die in etwas umgewandelt werden, das LLMs verstehen und in Gesprächen verwenden können.\n\nSie können jederzeit Dateien hinzufügen und entfernen.',
|
||||||
|
createWorkspace: "Erstellen Sie Ihren ersten Arbeitsbereich",
|
||||||
|
user2:
|
||||||
|
"Ist das so eine Art KI-Dropbox oder so? Was ist mit dem Chatten? Es ist doch ein Chatbot, oder?",
|
||||||
|
part5:
|
||||||
|
"AnythingLLM ist mehr als eine intelligentere Dropbox.\n\nAnythingLLM bietet zwei Möglichkeiten, mit Ihren Daten zu sprechen:\n\n<i>Abfrage:</i> Ihre Chats geben Daten oder Schlussfolgerungen zurück, die in den Dokumenten Ihres Arbeitsbereichs gefunden wurden, auf die es Zugriff hat. Je mehr Dokumente Sie dem Arbeitsbereich hinzufügen, desto intelligenter wird er! \n\n<i>Konversation:</i> Ihre Dokumente + Ihr laufender Chat-Verlauf tragen gleichzeitig zum LLM-Wissen bei. Großartig für das Anhängen von Echtzeit-Textinformationen oder Korrekturen und Missverständnissen, die das LLM haben könnte. \n\nSie können zwischen beiden Modi wechseln \n<i>mitten im Chatten!</i>",
|
||||||
|
user3: "Wow, das klingt erstaunlich, lass es mich gleich ausprobieren!",
|
||||||
|
part6: "Viel Spaß!",
|
||||||
|
starOnGithub: "Stern auf GitHub",
|
||||||
|
contact: "Kontaktieren Sie Mintplex Labs",
|
||||||
|
},
|
||||||
|
|
||||||
|
"new-workspace": {
|
||||||
|
title: "Neuer Arbeitsbereich",
|
||||||
|
placeholder: "Mein Arbeitsbereich",
|
||||||
|
},
|
||||||
|
|
||||||
|
"workspaces—settings": {
|
||||||
|
general: "Allgemeine Einstellungen",
|
||||||
|
chat: "Chat-Einstellungen",
|
||||||
|
vector: "Vektordatenbank",
|
||||||
|
members: "Mitglieder",
|
||||||
|
agent: "Agentenkonfiguration",
|
||||||
|
},
|
||||||
|
|
||||||
|
general: {
|
||||||
|
vector: {
|
||||||
|
title: "Vektoranzahl",
|
||||||
|
description: "Gesamtanzahl der Vektoren in Ihrer Vektordatenbank.",
|
||||||
|
},
|
||||||
|
names: {
|
||||||
|
description: "Dies ändert nur den Anzeigenamen Ihres Arbeitsbereichs.",
|
||||||
|
},
|
||||||
|
message: {
|
||||||
|
title: "Vorgeschlagene Chat-Nachrichten",
|
||||||
|
description:
|
||||||
|
"Passen Sie die Nachrichten an, die Ihren Arbeitsbereich-Benutzern vorgeschlagen werden.",
|
||||||
|
add: "Neue Nachricht hinzufügen",
|
||||||
|
save: "Nachrichten speichern",
|
||||||
|
heading: "Erkläre mir",
|
||||||
|
body: "die Vorteile von AnythingLLM",
|
||||||
|
},
|
||||||
|
pfp: {
|
||||||
|
title: "Assistent-Profilbild",
|
||||||
|
description:
|
||||||
|
"Passen Sie das Profilbild des Assistenten für diesen Arbeitsbereich an.",
|
||||||
|
image: "Arbeitsbereich-Bild",
|
||||||
|
remove: "Arbeitsbereich-Bild entfernen",
|
||||||
|
},
|
||||||
|
delete: {
|
||||||
|
title: "Arbeitsbereich löschen",
|
||||||
|
description:
|
||||||
|
"Löschen Sie diesen Arbeitsbereich und alle seine Daten. Dies löscht den Arbeitsbereich für alle Benutzer.",
|
||||||
|
delete: "Arbeitsbereich löschen",
|
||||||
|
deleting: "Arbeitsbereich wird gelöscht...",
|
||||||
|
"confirm-start": "Sie sind dabei, Ihren gesamten",
|
||||||
|
"confirm-end":
|
||||||
|
"Arbeitsbereich zu löschen. Dies entfernt alle Vektoreinbettungen in Ihrer Vektordatenbank.\n\nDie ursprünglichen Quelldateien bleiben unberührt. Diese Aktion ist irreversibel.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
chat: {
|
||||||
|
llm: {
|
||||||
|
title: "Arbeitsbereich-LLM-Anbieter",
|
||||||
|
description:
|
||||||
|
"Der spezifische LLM-Anbieter und das Modell, das für diesen Arbeitsbereich verwendet wird. Standardmäßig wird der System-LLM-Anbieter und dessen Einstellungen verwendet.",
|
||||||
|
search: "Durchsuchen Sie alle LLM-Anbieter",
|
||||||
|
},
|
||||||
|
model: {
|
||||||
|
title: "Arbeitsbereich-Chat-Modell",
|
||||||
|
description:
|
||||||
|
"Das spezifische Chat-Modell, das für diesen Arbeitsbereich verwendet wird. Wenn leer, wird die System-LLM-Präferenz verwendet.",
|
||||||
|
wait: "-- warte auf Modelle --",
|
||||||
|
},
|
||||||
|
mode: {
|
||||||
|
title: "Chat-Modus",
|
||||||
|
chat: {
|
||||||
|
title: "Chat",
|
||||||
|
"desc-start": "wird Antworten mit dem allgemeinen Wissen des LLM",
|
||||||
|
and: "und",
|
||||||
|
"desc-end": "gefundenem Dokumentenkontext liefern.",
|
||||||
|
},
|
||||||
|
query: {
|
||||||
|
title: "Abfrage",
|
||||||
|
"desc-start": "wird Antworten",
|
||||||
|
only: "nur",
|
||||||
|
"desc-end": "liefern, wenn Dokumentenkontext gefunden wird.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
history: {
|
||||||
|
title: "Chat-Verlauf",
|
||||||
|
"desc-start":
|
||||||
|
"Die Anzahl der vorherigen Chats, die in das Kurzzeitgedächtnis der Antwort einbezogen werden.",
|
||||||
|
recommend: "Empfohlen 20. ",
|
||||||
|
"desc-end":
|
||||||
|
"Alles über 45 führt wahrscheinlich zu kontinuierlichen Chat-Ausfällen, abhängig von der Nachrichtengröße.",
|
||||||
|
},
|
||||||
|
prompt: {
|
||||||
|
title: "Prompt",
|
||||||
|
description:
|
||||||
|
"Der Prompt, der in diesem Arbeitsbereich verwendet wird. Definieren Sie den Kontext und die Anweisungen für die KI, um eine Antwort zu generieren. Sie sollten einen sorgfältig formulierten Prompt bereitstellen, damit die KI eine relevante und genaue Antwort generieren kann.",
|
||||||
|
},
|
||||||
|
refusal: {
|
||||||
|
title: "Abfragemodus-Ablehnungsantwort",
|
||||||
|
"desc-start": "Wenn im",
|
||||||
|
query: "Abfrage",
|
||||||
|
"desc-end":
|
||||||
|
"modus, möchten Sie vielleicht eine benutzerdefinierte Ablehnungsantwort zurückgeben, wenn kein Kontext gefunden wird.",
|
||||||
|
},
|
||||||
|
temperature: {
|
||||||
|
title: "LLM-Temperatur",
|
||||||
|
"desc-start":
|
||||||
|
'Diese Einstellung steuert, wie "kreativ" Ihre LLM-Antworten sein werden.',
|
||||||
|
"desc-end":
|
||||||
|
"Je höher die Zahl, desto kreativer. Bei einigen Modellen kann dies zu unverständlichen Antworten führen, wenn sie zu hoch eingestellt ist.",
|
||||||
|
hint: "Die meisten LLMs haben verschiedene akzeptable Bereiche gültiger Werte. Konsultieren Sie Ihren LLM-Anbieter für diese Informationen.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
"vector-workspace": {
|
||||||
|
identifier: "Vektordatenbank-Identifikator",
|
||||||
|
snippets: {
|
||||||
|
title: "Maximale Kontext-Snippets",
|
||||||
|
description:
|
||||||
|
"Diese Einstellung steuert die maximale Anzahl von Kontext-Snippets, die pro Chat oder Abfrage an das LLM gesendet werden.",
|
||||||
|
recommend: "Empfohlen: 4",
|
||||||
|
},
|
||||||
|
doc: {
|
||||||
|
title: "Dokumentähnlichkeitsschwelle",
|
||||||
|
description:
|
||||||
|
"Der minimale Ähnlichkeitswert, der erforderlich ist, damit eine Quelle als relevant für den Chat betrachtet wird. Je höher die Zahl, desto ähnlicher muss die Quelle dem Chat sein.",
|
||||||
|
zero: "Keine Einschränkung",
|
||||||
|
low: "Niedrig (Ähnlichkeitswert ≥ .25)",
|
||||||
|
medium: "Mittel (Ähnlichkeitswert ≥ .50)",
|
||||||
|
high: "Hoch (Ähnlichkeitswert ≥ .75)",
|
||||||
|
},
|
||||||
|
reset: {
|
||||||
|
reset: "Vektordatenbank zurücksetzen",
|
||||||
|
resetting: "Vektoren werden gelöscht...",
|
||||||
|
confirm:
|
||||||
|
"Sie sind dabei, die Vektordatenbank dieses Arbeitsbereichs zurückzusetzen. Dies entfernt alle derzeit eingebetteten Vektoreinbettungen.\n\nDie ursprünglichen Quelldateien bleiben unberührt. Diese Aktion ist irreversibel.",
|
||||||
|
error:
|
||||||
|
"Die Arbeitsbereich-Vektordatenbank konnte nicht zurückgesetzt werden!",
|
||||||
|
success: "Die Arbeitsbereich-Vektordatenbank wurde zurückgesetzt!",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
agent: {
|
||||||
|
"performance-warning":
|
||||||
|
"Die Leistung von LLMs, die Werkzeugaufrufe nicht explizit unterstützen, hängt stark von den Fähigkeiten und der Genauigkeit des Modells ab. Einige Fähigkeiten können eingeschränkt oder nicht funktionsfähig sein.",
|
||||||
|
provider: {
|
||||||
|
title: "Arbeitsbereich-Agent LLM-Anbieter",
|
||||||
|
description:
|
||||||
|
"Der spezifische LLM-Anbieter und das Modell, das für den @agent-Agenten dieses Arbeitsbereichs verwendet wird.",
|
||||||
|
},
|
||||||
|
mode: {
|
||||||
|
chat: {
|
||||||
|
title: "Arbeitsbereich-Agent Chat-Modell",
|
||||||
|
description:
|
||||||
|
"Das spezifische Chat-Modell, das für den @agent-Agenten dieses Arbeitsbereichs verwendet wird.",
|
||||||
|
},
|
||||||
|
title: "Arbeitsbereich-Agent-Modell",
|
||||||
|
description:
|
||||||
|
"Das spezifische LLM-Modell, das für den @agent-Agenten dieses Arbeitsbereichs verwendet wird.",
|
||||||
|
wait: "-- warte auf Modelle --",
|
||||||
|
},
|
||||||
|
|
||||||
|
skill: {
|
||||||
|
title: "Standard-Agentenfähigkeiten",
|
||||||
|
description:
|
||||||
|
"Verbessern Sie die natürlichen Fähigkeiten des Standard-Agenten mit diesen vorgefertigten Fähigkeiten. Diese Einrichtung gilt für alle Arbeitsbereiche.",
|
||||||
|
rag: {
|
||||||
|
title: "RAG & Langzeitgedächtnis",
|
||||||
|
description:
|
||||||
|
'Erlauben Sie dem Agenten, Ihre lokalen Dokumente zu nutzen, um eine Abfrage zu beantworten oder bitten Sie den Agenten, Inhalte für den Langzeitabruf zu "merken".',
|
||||||
|
},
|
||||||
|
view: {
|
||||||
|
title: "Dokumente anzeigen & zusammenfassen",
|
||||||
|
description:
|
||||||
|
"Erlauben Sie dem Agenten, den Inhalt der aktuell eingebetteten Arbeitsbereichsdateien aufzulisten und zusammenzufassen.",
|
||||||
|
},
|
||||||
|
scrape: {
|
||||||
|
title: "Websites durchsuchen",
|
||||||
|
description:
|
||||||
|
"Erlauben Sie dem Agenten, Websites zu besuchen und deren Inhalt zu extrahieren.",
|
||||||
|
},
|
||||||
|
generate: {
|
||||||
|
title: "Diagramme generieren",
|
||||||
|
description:
|
||||||
|
"Aktivieren Sie den Standard-Agenten, um verschiedene Arten von Diagrammen aus bereitgestellten oder im Chat gegebenen Daten zu generieren.",
|
||||||
|
},
|
||||||
|
save: {
|
||||||
|
title: "Dateien generieren & im Browser speichern",
|
||||||
|
description:
|
||||||
|
"Aktivieren Sie den Standard-Agenten, um Dateien zu generieren und zu schreiben, die gespeichert und in Ihrem Browser heruntergeladen werden können.",
|
||||||
|
},
|
||||||
|
web: {
|
||||||
|
title: "Live-Websuche und -Browsing",
|
||||||
|
"desc-start":
|
||||||
|
"Ermöglichen Sie Ihrem Agenten, das Web zu durchsuchen, um Ihre Fragen zu beantworten, indem Sie eine Verbindung zu einem Websuche-Anbieter (SERP) herstellen.",
|
||||||
|
"desc-end":
|
||||||
|
"Die Websuche während Agentensitzungen funktioniert erst, wenn dies eingerichtet ist.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
recorded: {
|
||||||
|
title: "Arbeitsbereich-Chats",
|
||||||
|
description:
|
||||||
|
"Dies sind alle aufgezeichneten Chats und Nachrichten, die von Benutzern gesendet wurden, geordnet nach ihrem Erstellungsdatum.",
|
||||||
|
export: "Exportieren",
|
||||||
|
table: {
|
||||||
|
id: "Id",
|
||||||
|
by: "Gesendet von",
|
||||||
|
workspace: "Arbeitsbereich",
|
||||||
|
prompt: "Prompt",
|
||||||
|
response: "Antwort",
|
||||||
|
at: "Gesendet am",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
appearance: {
|
||||||
|
title: "Erscheinungsbild",
|
||||||
|
description: "Passen Sie die Erscheinungseinstellungen Ihrer Plattform an.",
|
||||||
|
logo: {
|
||||||
|
title: "Logo anpassen",
|
||||||
|
description:
|
||||||
|
"Laden Sie Ihr eigenes Logo hoch, um Ihren Chatbot zu personalisieren.",
|
||||||
|
add: "Benutzerdefiniertes Logo hinzufügen",
|
||||||
|
recommended: "Empfohlene Größe: 800 x 200",
|
||||||
|
remove: "Entfernen",
|
||||||
|
replace: "Ersetzen",
|
||||||
|
},
|
||||||
|
message: {
|
||||||
|
title: "Nachrichten anpassen",
|
||||||
|
description:
|
||||||
|
"Passen Sie die automatischen Nachrichten an, die Ihren Benutzern angezeigt werden.",
|
||||||
|
new: "Neu",
|
||||||
|
system: "System",
|
||||||
|
user: "Benutzer",
|
||||||
|
message: "Nachricht",
|
||||||
|
assistant: "AnythingLLM Chat-Assistent",
|
||||||
|
"double-click": "Doppelklicken zum Bearbeiten...",
|
||||||
|
save: "Nachrichten speichern",
|
||||||
|
},
|
||||||
|
icons: {
|
||||||
|
title: "Benutzerdefinierte Fußzeilen-Icons",
|
||||||
|
description:
|
||||||
|
"Passen Sie die Fußzeilen-Icons an, die am unteren Rand der Seitenleiste angezeigt werden.",
|
||||||
|
icon: "Icon",
|
||||||
|
link: "Link",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
api: {
|
||||||
|
title: "API-Schlüssel",
|
||||||
|
description:
|
||||||
|
"API-Schlüssel ermöglichen es dem Besitzer, programmatisch auf diese AnythingLLM-Instanz zuzugreifen und sie zu verwalten.",
|
||||||
|
link: "Lesen Sie die API-Dokumentation",
|
||||||
|
generate: "Neuen API-Schlüssel generieren",
|
||||||
|
table: {
|
||||||
|
key: "API-Schlüssel",
|
||||||
|
by: "Erstellt von",
|
||||||
|
created: "Erstellt",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
llm: {
|
||||||
|
title: "LLM-Präferenz",
|
||||||
|
description:
|
||||||
|
"Dies sind die Anmeldeinformationen und Einstellungen für Ihren bevorzugten LLM-Chat- und Einbettungsanbieter. Es ist wichtig, dass diese Schlüssel aktuell und korrekt sind, sonst wird AnythingLLM nicht richtig funktionieren.",
|
||||||
|
provider: "LLM-Anbieter",
|
||||||
|
},
|
||||||
|
|
||||||
|
transcription: {
|
||||||
|
title: "Transkriptionsmodell-Präferenz",
|
||||||
|
description:
|
||||||
|
"Dies sind die Anmeldeinformationen und Einstellungen für Ihren bevorzugten Transkriptionsmodellanbieter. Es ist wichtig, dass diese Schlüssel aktuell und korrekt sind, sonst werden Mediendateien und Audio nicht transkribiert.",
|
||||||
|
provider: "Transkriptionsanbieter",
|
||||||
|
"warn-start":
|
||||||
|
"Die Verwendung des lokalen Whisper-Modells auf Maschinen mit begrenztem RAM oder CPU kann AnythingLLM bei der Verarbeitung von Mediendateien zum Stillstand bringen.",
|
||||||
|
"warn-recommend":
|
||||||
|
"Wir empfehlen mindestens 2 GB RAM und das Hochladen von Dateien <10 MB.",
|
||||||
|
"warn-end":
|
||||||
|
"Das eingebaute Modell wird bei der ersten Verwendung automatisch heruntergeladen.",
|
||||||
|
},
|
||||||
|
|
||||||
|
embedding: {
|
||||||
|
title: "Einbettungspräferenz",
|
||||||
|
"desc-start":
|
||||||
|
"Bei der Verwendung eines LLM, das keine native Unterstützung für eine Einbettungs-Engine bietet, müssen Sie möglicherweise zusätzlich Anmeldeinformationen für die Texteinbettung angeben.",
|
||||||
|
"desc-end":
|
||||||
|
"Einbettung ist der Prozess, Text in Vektoren umzuwandeln. Diese Anmeldeinformationen sind erforderlich, um Ihre Dateien und Prompts in ein Format umzuwandeln, das AnythingLLM zur Verarbeitung verwenden kann.",
|
||||||
|
provider: {
|
||||||
|
title: "Einbettungsanbieter",
|
||||||
|
description:
|
||||||
|
"Bei Verwendung der nativen Einbettungs-Engine von AnythingLLM ist keine Einrichtung erforderlich.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
text: {
|
||||||
|
title: "Textsplitting & Chunking-Präferenzen",
|
||||||
|
"desc-start":
|
||||||
|
"Manchmal möchten Sie vielleicht die Standardmethode ändern, wie neue Dokumente gesplittet und gechunkt werden, bevor sie in Ihre Vektordatenbank eingefügt werden.",
|
||||||
|
"desc-end":
|
||||||
|
"Sie sollten diese Einstellung nur ändern, wenn Sie verstehen, wie Textsplitting funktioniert und welche Nebenwirkungen es hat.",
|
||||||
|
"warn-start": "Änderungen hier gelten nur für",
|
||||||
|
"warn-center": "neu eingebettete Dokumente",
|
||||||
|
"warn-end": ", nicht für bestehende Dokumente.",
|
||||||
|
size: {
|
||||||
|
title: "Textchunk-Größe",
|
||||||
|
description:
|
||||||
|
"Dies ist die maximale Länge der Zeichen, die in einem einzelnen Vektor vorhanden sein können.",
|
||||||
|
recommend: "Die maximale Länge des Einbettungsmodells beträgt",
|
||||||
|
},
|
||||||
|
|
||||||
|
overlap: {
|
||||||
|
title: "Textchunk-Überlappung",
|
||||||
|
description:
|
||||||
|
"Dies ist die maximale Überlappung von Zeichen, die während des Chunkings zwischen zwei benachbarten Textchunks auftritt.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
vector: {
|
||||||
|
title: "Vektordatenbank",
|
||||||
|
description:
|
||||||
|
"Dies sind die Anmeldeinformationen und Einstellungen für die Funktionsweise Ihrer AnythingLLM-Instanz. Es ist wichtig, dass diese Schlüssel aktuell und korrekt sind.",
|
||||||
|
provider: {
|
||||||
|
title: "Vektordatenbankanbieter",
|
||||||
|
description: "Für LanceDB ist keine Konfiguration erforderlich.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
embeddable: {
|
||||||
|
title: "Einbettbare Chat-Widgets",
|
||||||
|
description:
|
||||||
|
"Einbettbare Chat-Widgets sind öffentlich zugängliche Chat-Schnittstellen, die an einen einzelnen Arbeitsbereich gebunden sind. Diese ermöglichen es Ihnen, Arbeitsbereiche zu erstellen, die Sie dann weltweit veröffentlichen können.",
|
||||||
|
create: "Einbettung erstellen",
|
||||||
|
table: {
|
||||||
|
workspace: "Arbeitsbereich",
|
||||||
|
chats: "Gesendete Chats",
|
||||||
|
Active: "Aktive Domains",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
"embed-chats": {
|
||||||
|
title: "Eingebettete Chats",
|
||||||
|
description:
|
||||||
|
"Dies sind alle aufgezeichneten Chats und Nachrichten von jeder Einbettung, die Sie veröffentlicht haben.",
|
||||||
|
table: {
|
||||||
|
embed: "Einbettung",
|
||||||
|
sender: "Absender",
|
||||||
|
message: "Nachricht",
|
||||||
|
response: "Antwort",
|
||||||
|
at: "Gesendet am",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
multi: {
|
||||||
|
title: "Mehrbenutzer-Modus",
|
||||||
|
description:
|
||||||
|
"Richten Sie Ihre Instanz ein, um Ihr Team zu unterstützen, indem Sie den Mehrbenutzer-Modus aktivieren.",
|
||||||
|
enable: {
|
||||||
|
"is-enable": "Mehrbenutzer-Modus ist aktiviert",
|
||||||
|
enable: "Mehrbenutzer-Modus aktivieren",
|
||||||
|
description:
|
||||||
|
"Standardmäßig sind Sie der einzige Administrator. Als Administrator müssen Sie Konten für alle neuen Benutzer oder Administratoren erstellen. Verlieren Sie Ihr Passwort nicht, da nur ein Administrator-Benutzer Passwörter zurücksetzen kann.",
|
||||||
|
username: "Administrator-Kontoname",
|
||||||
|
password: "Administrator-Kontopasswort",
|
||||||
|
},
|
||||||
|
password: {
|
||||||
|
title: "Passwortschutz",
|
||||||
|
description:
|
||||||
|
"Schützen Sie Ihre AnythingLLM-Instanz mit einem Passwort. Wenn Sie dieses vergessen, gibt es keine Wiederherstellungsmethode, also stellen Sie sicher, dass Sie dieses Passwort speichern.",
|
||||||
|
},
|
||||||
|
instance: {
|
||||||
|
title: "Instanz mit Passwort schützen",
|
||||||
|
description:
|
||||||
|
"Standardmäßig sind Sie der einzige Administrator. Als Administrator müssen Sie Konten für alle neuen Benutzer oder Administratoren erstellen. Verlieren Sie Ihr Passwort nicht, da nur ein Administrator-Benutzer Passwörter zurücksetzen kann.",
|
||||||
|
password: "Instanz-Passwort",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
event: {
|
||||||
|
title: "Ereignisprotokolle",
|
||||||
|
description:
|
||||||
|
"Sehen Sie alle Aktionen und Ereignisse, die auf dieser Instanz zur Überwachung stattfinden.",
|
||||||
|
clear: "Ereignisprotokolle löschen",
|
||||||
|
table: {
|
||||||
|
type: "Ereignistyp",
|
||||||
|
user: "Benutzer",
|
||||||
|
occurred: "Aufgetreten am",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
privacy: {
|
||||||
|
title: "Datenschutz & Datenverarbeitung",
|
||||||
|
description:
|
||||||
|
"Dies ist Ihre Konfiguration dafür, wie verbundene Drittanbieter und AnythingLLM Ihre Daten behandeln.",
|
||||||
|
llm: "LLM-Auswahl",
|
||||||
|
embedding: "Einbettungspräferenz",
|
||||||
|
vector: "Vektordatenbank",
|
||||||
|
anonymous: "Anonyme Telemetrie aktiviert",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TRANSLATIONS;
|
496
frontend/src/locales/pt_BR/common.js
Normal file
496
frontend/src/locales/pt_BR/common.js
Normal file
@ -0,0 +1,496 @@
|
|||||||
|
const TRANSLATIONS = {
|
||||||
|
common: {
|
||||||
|
"workspaces-name": "Nome dos Workspaces",
|
||||||
|
error: "erro",
|
||||||
|
success: "sucesso",
|
||||||
|
user: "Usuário",
|
||||||
|
selection: "Seleção de Modelo",
|
||||||
|
saving: "Salvando...",
|
||||||
|
save: "Salvar alterações",
|
||||||
|
previous: "Página Anterior",
|
||||||
|
next: "Próxima Página",
|
||||||
|
},
|
||||||
|
|
||||||
|
// Setting Sidebar menu items.
|
||||||
|
settings: {
|
||||||
|
title: "Configurações da Instância",
|
||||||
|
system: "Configurações Gerais",
|
||||||
|
invites: "Convites",
|
||||||
|
users: "Usuários",
|
||||||
|
workspaces: "Workspaces",
|
||||||
|
"workspace-chats": "Chats do Workspace",
|
||||||
|
customization: "Customização",
|
||||||
|
"api-keys": "API para Desenvolvedores",
|
||||||
|
llm: "LLM",
|
||||||
|
transcription: "Transcrição",
|
||||||
|
embedder: "Incorporador",
|
||||||
|
"text-splitting": "Divisor de Texto e Fragmentação",
|
||||||
|
"voice-speech": "Voz e Fala",
|
||||||
|
"vector-database": "Banco de Dados Vetorial",
|
||||||
|
embeds: "Incorporar Chat",
|
||||||
|
"embed-chats": "Histórico de Chats Incorporados",
|
||||||
|
security: "Segurança",
|
||||||
|
"event-logs": "Logs de Eventos",
|
||||||
|
privacy: "Privacidade e Dados",
|
||||||
|
"ai-providers": "Provedores de IA",
|
||||||
|
"agent-skills": "Habilidades do Agente",
|
||||||
|
admin: "Admin",
|
||||||
|
tools: "Ferramentas",
|
||||||
|
"experimental-features": "Recursos Experimentais",
|
||||||
|
contact: "Contato com Suporte",
|
||||||
|
},
|
||||||
|
|
||||||
|
// Page Definitions
|
||||||
|
login: {
|
||||||
|
"multi-user": {
|
||||||
|
welcome: "Bem-vindo ao",
|
||||||
|
"placeholder-username": "Nome de Usuário",
|
||||||
|
"placeholder-password": "Senha",
|
||||||
|
login: "Entrar",
|
||||||
|
validating: "Validando...",
|
||||||
|
"forgot-pass": "Esqueceu a senha",
|
||||||
|
reset: "Redefinir",
|
||||||
|
},
|
||||||
|
"sign-in": {
|
||||||
|
start: "Faça login na sua",
|
||||||
|
end: "conta.",
|
||||||
|
},
|
||||||
|
"password-reset": {
|
||||||
|
title: "Redefinição de Senha",
|
||||||
|
description:
|
||||||
|
"Forneça as informações necessárias abaixo para redefinir sua senha.",
|
||||||
|
"recovery-codes": "Códigos de Recuperação",
|
||||||
|
"recovery-code": "Código de Recuperação {{index}}",
|
||||||
|
"back-to-login": "Voltar ao Login",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
welcomeMessage: {
|
||||||
|
part1:
|
||||||
|
"Bem-vindo ao AnythingLLM, AnythingLLM é uma ferramenta de IA de código aberto da Mintplex Labs que transforma qualquer coisa em um chatbot treinado que você pode consultar e conversar. AnythingLLM é um software BYOK (bring-your-own-keys | traga suas próprias chaves), portanto, não há assinatura, taxa ou cobranças para este software fora dos serviços que você deseja usar com ele.",
|
||||||
|
part2:
|
||||||
|
"AnythingLLM é a maneira mais fácil de reunir produtos de IA poderosos como OpenAi, GPT-4, LangChain, PineconeDB, ChromaDB e outros serviços em um pacote organizado sem complicações para aumentar sua produtividade em 100x.",
|
||||||
|
part3:
|
||||||
|
"AnythingLLM pode ser executado totalmente localmente em sua máquina com pouca sobrecarga que você nem perceberá que está lá! Não é necessário GPU. A instalação em nuvem e localmente também está disponível.\nO ecossistema de ferramentas de IA fica mais poderoso a cada dia. AnythingLLM facilita o uso.",
|
||||||
|
githubIssue: "Criar uma issue no Github",
|
||||||
|
user1: "Como eu começo?!",
|
||||||
|
part4:
|
||||||
|
'É simples. Todas as coleções são organizadas em grupos que chamamos de "Workspaces". Workspaces são grupos de arquivos, documentos, imagens, PDFs e outros arquivos que serão transformados em algo que os LLMs podem entender e usar em conversas.\n\nVocê pode adicionar e remover arquivos a qualquer momento.',
|
||||||
|
createWorkspace: "Crie seu primeiro workspace",
|
||||||
|
user2:
|
||||||
|
"Isso é como um Dropbox de IA ou algo assim? E quanto a conversar? Não é um chatbot?",
|
||||||
|
part5:
|
||||||
|
"AnythingLLM é mais do que um Dropbox mais inteligente.\n\nAnythingLLM oferece duas maneiras de conversar com seus dados:\n\n<i>Consulta:</i> Seus chats retornarão dados ou inferências encontradas com os documentos em seu workspace ao qual tem acesso. Adicionar mais documentos ao Workspace o torna mais inteligente!\n\n<i>Conversacional:</i> Seus documentos + seu histórico de chat em andamento contribuem para o conhecimento do LLM ao mesmo tempo. Ótimo para adicionar informações em tempo real baseadas em texto ou correções e mal-entendidos que o LLM possa ter.\n\nVocê pode alternar entre qualquer modo \n<i>no meio da conversa!</i>",
|
||||||
|
user3: "Uau, isso soa incrível, deixe-me experimentar já!",
|
||||||
|
part6: "Divirta-se!",
|
||||||
|
starOnGithub: "Dar estrela no GitHub",
|
||||||
|
contact: "Contato Mintplex Labs",
|
||||||
|
},
|
||||||
|
|
||||||
|
"new-workspace": {
|
||||||
|
title: "Novo Workspace",
|
||||||
|
placeholder: "Meu Workspace",
|
||||||
|
},
|
||||||
|
|
||||||
|
// Workspace Settings menu items
|
||||||
|
"workspaces—settings": {
|
||||||
|
general: "Configurações Gerais",
|
||||||
|
chat: "Configurações de Chat",
|
||||||
|
vector: "Banco de Dados Vetorial",
|
||||||
|
members: "Membros",
|
||||||
|
agent: "Configuração do Agente",
|
||||||
|
},
|
||||||
|
|
||||||
|
// General Appearance
|
||||||
|
general: {
|
||||||
|
vector: {
|
||||||
|
title: "Contagem de Vetores",
|
||||||
|
description: "Número total de vetores no seu banco de dados vetorial.",
|
||||||
|
},
|
||||||
|
names: {
|
||||||
|
description: "Isso mudará apenas o nome de exibição do seu workspace.",
|
||||||
|
},
|
||||||
|
message: {
|
||||||
|
title: "Mensagens de Chat Sugeridas",
|
||||||
|
description:
|
||||||
|
"Personalize as mensagens que serão sugeridas aos usuários do seu workspace.",
|
||||||
|
add: "Adicionar nova mensagem",
|
||||||
|
save: "Salvar Mensagens",
|
||||||
|
heading: "Explique para mim",
|
||||||
|
body: "os benefícios do AnythingLLM",
|
||||||
|
},
|
||||||
|
pfp: {
|
||||||
|
title: "Imagem de Perfil do Assistente",
|
||||||
|
description:
|
||||||
|
"Personalize a imagem de perfil do assistente para este workspace.",
|
||||||
|
image: "Imagem do Workspace",
|
||||||
|
remove: "Remover Imagem do Workspace",
|
||||||
|
},
|
||||||
|
delete: {
|
||||||
|
title: "Excluir Workspace",
|
||||||
|
description:
|
||||||
|
"Excluir este workspace e todos os seus dados. Isso excluirá o workspace para todos os usuários.",
|
||||||
|
delete: "Excluir Workspace",
|
||||||
|
deleting: "Excluindo Workspace...",
|
||||||
|
"confirm-start": "Você está prestes a excluir todo o seu",
|
||||||
|
"confirm-end":
|
||||||
|
"workspace. Isso removerá todas as incorporações vetoriais no seu banco de dados vetorial.\n\nOs arquivos de origem originais permanecerão intactos. Esta ação é irreversível.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
// Chat Settings
|
||||||
|
chat: {
|
||||||
|
llm: {
|
||||||
|
title: "Provedor de LLM do Workspace",
|
||||||
|
description:
|
||||||
|
"O provedor e modelo específico de LLM que será usado para este workspace. Por padrão, usa o provedor e as configurações do sistema LLM.",
|
||||||
|
search: "Pesquisar todos os provedores de LLM",
|
||||||
|
},
|
||||||
|
model: {
|
||||||
|
title: "Modelo de Chat do Workspace",
|
||||||
|
description:
|
||||||
|
"O modelo de chat específico que será usado para este workspace. Se vazio, usará a preferência do LLM do sistema.",
|
||||||
|
wait: "-- aguardando modelos --",
|
||||||
|
},
|
||||||
|
mode: {
|
||||||
|
title: "Modo de Chat",
|
||||||
|
chat: {
|
||||||
|
title: "Chat",
|
||||||
|
"desc-start": "fornecerá respostas com o conhecimento geral do LLM",
|
||||||
|
and: "e",
|
||||||
|
"desc-end": "contexto do documento encontrado.",
|
||||||
|
},
|
||||||
|
query: {
|
||||||
|
title: "Consulta",
|
||||||
|
"desc-start": "fornecerá respostas",
|
||||||
|
only: "somente",
|
||||||
|
"desc-end": "se o contexto do documento for encontrado.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
history: {
|
||||||
|
title: "Histórico de Chat",
|
||||||
|
"desc-start":
|
||||||
|
"O número de chats anteriores que serão incluídos na memória de curto prazo da resposta.",
|
||||||
|
recommend: "Recomendado: 20. ",
|
||||||
|
"desc-end":
|
||||||
|
"Qualquer coisa acima de 45 provavelmente levará a falhas contínuas de chat dependendo do tamanho da mensagem.",
|
||||||
|
},
|
||||||
|
prompt: {
|
||||||
|
title: "Prompt",
|
||||||
|
description:
|
||||||
|
"O prompt que será usado neste workspace. Defina o contexto e as instruções para que a IA gere uma resposta. Você deve fornecer um prompt cuidadosamente elaborado para que a IA possa gerar uma resposta relevante e precisa.",
|
||||||
|
},
|
||||||
|
refusal: {
|
||||||
|
title: "Resposta de Recusa no Modo de Consulta",
|
||||||
|
"desc-start": "Quando estiver no modo",
|
||||||
|
query: "consulta",
|
||||||
|
"desc-end":
|
||||||
|
", você pode querer retornar uma resposta de recusa personalizada quando nenhum contexto for encontrado.",
|
||||||
|
},
|
||||||
|
temperature: {
|
||||||
|
title: "Temperatura do LLM",
|
||||||
|
"desc-start":
|
||||||
|
'Esta configuração controla o quão "criativas" serão as respostas do seu LLM.',
|
||||||
|
"desc-end":
|
||||||
|
"Quanto maior o número, mais criativa será a resposta. Para alguns modelos, isso pode levar a respostas incoerentes quando configurado muito alto.",
|
||||||
|
hint: "A maioria dos LLMs tem vários intervalos aceitáveis de valores válidos. Consulte seu provedor de LLM para essa informação.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
// Vector Database
|
||||||
|
"vector-workspace": {
|
||||||
|
identifier: "Identificador do Banco de Dados Vetorial",
|
||||||
|
snippets: {
|
||||||
|
title: "Máximo de Trechos de Contexto",
|
||||||
|
description:
|
||||||
|
"Esta configuração controla a quantidade máxima de trechos de contexto que será enviada ao LLM por chat ou consulta.",
|
||||||
|
recommend: "Recomendado: 4",
|
||||||
|
},
|
||||||
|
doc: {
|
||||||
|
title: "Limite de Similaridade de Documentos",
|
||||||
|
description:
|
||||||
|
"A pontuação mínima de similaridade necessária para que uma fonte seja considerada relacionada ao chat. Quanto maior o número, mais semelhante a fonte deve ser ao chat.",
|
||||||
|
zero: "Sem restrição",
|
||||||
|
low: "Baixo (pontuação de similaridade ≥ 0,25)",
|
||||||
|
medium: "Médio (pontuação de similaridade ≥ 0,50)",
|
||||||
|
high: "Alto (pontuação de similaridade ≥ 0,75)",
|
||||||
|
},
|
||||||
|
reset: {
|
||||||
|
reset: "Redefinir Banco de Dados Vetorial",
|
||||||
|
resetting: "Limpando vetores...",
|
||||||
|
confirm:
|
||||||
|
"Você está prestes a redefinir o banco de dados vetorial deste workspace. Isso removerá todas as incorporações vetoriais atualmente embutidas.\n\nOs arquivos de origem originais permanecerão intactos. Esta ação é irreversível.",
|
||||||
|
error: "O banco de dados vetorial do workspace não pôde ser redefinido!",
|
||||||
|
success:
|
||||||
|
"O banco de dados vetorial do workspace foi redefinido com sucesso!",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
// Agent Configuration
|
||||||
|
agent: {
|
||||||
|
"performance-warning":
|
||||||
|
"O desempenho dos LLMs que não suportam explicitamente a chamada de ferramentas depende muito das capacidades e da precisão do modelo. Algumas habilidades podem ser limitadas ou não funcionais.",
|
||||||
|
provider: {
|
||||||
|
title: "Provedor de LLM do Agente do Workspace",
|
||||||
|
description:
|
||||||
|
"O provedor e modelo específico de LLM que será usado para o agente @agent deste workspace.",
|
||||||
|
},
|
||||||
|
mode: {
|
||||||
|
chat: {
|
||||||
|
title: "Modelo de Chat do Agente do Workspace",
|
||||||
|
description:
|
||||||
|
"O modelo de chat específico que será usado para o agente @agent deste workspace.",
|
||||||
|
},
|
||||||
|
title: "Modelo do Agente do Workspace",
|
||||||
|
description:
|
||||||
|
"O modelo de LLM específico que será usado para o agente @agent deste workspace.",
|
||||||
|
wait: "-- aguardando modelos --",
|
||||||
|
},
|
||||||
|
|
||||||
|
skill: {
|
||||||
|
title: "Habilidades padrão do agente",
|
||||||
|
description:
|
||||||
|
"Melhore as habilidades naturais do agente padrão com essas habilidades pré-construídas. Esta configuração se aplica a todos os workspaces.",
|
||||||
|
rag: {
|
||||||
|
title: "RAG e memória de longo prazo",
|
||||||
|
description:
|
||||||
|
'Permitir que o agente utilize seus documentos locais para responder a uma consulta ou pedir ao agente para "lembrar" peças de conteúdo para recuperação de memória de longo prazo.',
|
||||||
|
},
|
||||||
|
view: {
|
||||||
|
title: "Visualizar e resumir documentos",
|
||||||
|
description:
|
||||||
|
"Permitir que o agente liste e resuma o conteúdo dos arquivos do workspace atualmente incorporados.",
|
||||||
|
},
|
||||||
|
scrape: {
|
||||||
|
title: "Raspagem de sites",
|
||||||
|
description:
|
||||||
|
"Permitir que o agente visite e raspe o conteúdo de sites.",
|
||||||
|
},
|
||||||
|
generate: {
|
||||||
|
title: "Gerar gráficos",
|
||||||
|
description:
|
||||||
|
"Habilitar o agente padrão para gerar vários tipos de gráficos a partir dos dados fornecidos ou dados no chat.",
|
||||||
|
},
|
||||||
|
save: {
|
||||||
|
title: "Gerar e salvar arquivos no navegador",
|
||||||
|
description:
|
||||||
|
"Habilitar o agente padrão para gerar e gravar arquivos que podem ser salvos e baixados no seu navegador.",
|
||||||
|
},
|
||||||
|
web: {
|
||||||
|
title: "Pesquisa e navegação na web ao vivo",
|
||||||
|
"desc-start":
|
||||||
|
"Permitir que seu agente pesquise na web para responder suas perguntas conectando-se a um provedor de pesquisa na web (SERP).",
|
||||||
|
"desc-end":
|
||||||
|
"A pesquisa na web durante as sessões do agente não funcionará até que isso seja configurado.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
// Workspace Chats
|
||||||
|
recorded: {
|
||||||
|
title: "Chats do Workspace",
|
||||||
|
description:
|
||||||
|
"Estes são todos os chats e mensagens gravados que foram enviados pelos usuários ordenados por data de criação.",
|
||||||
|
export: "Exportar",
|
||||||
|
table: {
|
||||||
|
id: "Id",
|
||||||
|
by: "Enviado Por",
|
||||||
|
workspace: "Workspace",
|
||||||
|
prompt: "Prompt",
|
||||||
|
response: "Resposta",
|
||||||
|
at: "Enviado Em",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
// Appearance
|
||||||
|
appearance: {
|
||||||
|
title: "Aparência",
|
||||||
|
description: "Personalize as configurações de aparência da sua plataforma.",
|
||||||
|
logo: {
|
||||||
|
title: "Personalizar Logo",
|
||||||
|
description:
|
||||||
|
"Envie seu logotipo personalizado para tornar seu chatbot seu.",
|
||||||
|
add: "Adicionar um logotipo personalizado",
|
||||||
|
recommended: "Tamanho recomendado: 800 x 200",
|
||||||
|
remove: "Remover",
|
||||||
|
replace: "Substituir",
|
||||||
|
},
|
||||||
|
message: {
|
||||||
|
title: "Personalizar Mensagens",
|
||||||
|
description:
|
||||||
|
"Personalize as mensagens automáticas exibidas aos seus usuários.",
|
||||||
|
new: "Novo",
|
||||||
|
system: "sistema",
|
||||||
|
user: "usuário",
|
||||||
|
message: "mensagem",
|
||||||
|
assistant: "Assistente de Chat AnythingLLM",
|
||||||
|
"double-click": "Clique duas vezes para editar...",
|
||||||
|
save: "Salvar Mensagens",
|
||||||
|
},
|
||||||
|
icons: {
|
||||||
|
title: "Ícones de Rodapé Personalizados",
|
||||||
|
description:
|
||||||
|
"Personalize os ícones de rodapé exibidos na parte inferior da barra lateral.",
|
||||||
|
icon: "Ícone",
|
||||||
|
link: "Link",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
// API Keys
|
||||||
|
api: {
|
||||||
|
title: "Chaves API",
|
||||||
|
description:
|
||||||
|
"As chaves API permitem que o titular acesse e gerencie programaticamente esta instância do AnythingLLM.",
|
||||||
|
link: "Leia a documentação da API",
|
||||||
|
generate: "Gerar Nova Chave API",
|
||||||
|
table: {
|
||||||
|
key: "Chave API",
|
||||||
|
by: "Criado Por",
|
||||||
|
created: "Criado",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
llm: {
|
||||||
|
title: "Preferência de LLM",
|
||||||
|
description:
|
||||||
|
"Estas são as credenciais e configurações para seu provedor preferido de chat e incorporação de LLM. É importante que essas chaves estejam atualizadas e corretas, caso contrário, o AnythingLLM não funcionará corretamente.",
|
||||||
|
provider: "Provedor de LLM",
|
||||||
|
},
|
||||||
|
|
||||||
|
transcription: {
|
||||||
|
title: "Preferência de Modelo de Transcrição",
|
||||||
|
description:
|
||||||
|
"Estas são as credenciais e configurações para seu provedor preferido de modelo de transcrição. É importante que essas chaves estejam atualizadas e corretas, caso contrário, os arquivos de mídia e áudio não serão transcritos.",
|
||||||
|
provider: "Provedor de Transcrição",
|
||||||
|
"warn-start":
|
||||||
|
"Usar o modelo whisper local em máquinas com RAM ou CPU limitados pode travar o AnythingLLM ao processar arquivos de mídia.",
|
||||||
|
"warn-recommend":
|
||||||
|
"Recomendamos pelo menos 2GB de RAM e upload de arquivos <10Mb.",
|
||||||
|
"warn-end":
|
||||||
|
"O modelo embutido será baixado automaticamente no primeiro uso.",
|
||||||
|
},
|
||||||
|
|
||||||
|
embedding: {
|
||||||
|
title: "Preferência de Incorporação",
|
||||||
|
"desc-start":
|
||||||
|
"Ao usar um LLM que não suporta nativamente um mecanismo de incorporação - pode ser necessário especificar adicionalmente as credenciais para incorporação de texto.",
|
||||||
|
"desc-end":
|
||||||
|
"A incorporação é o processo de transformar texto em vetores. Essas credenciais são necessárias para transformar seus arquivos e prompts em um formato que o AnythingLLM possa usar para processar.",
|
||||||
|
provider: {
|
||||||
|
title: "Provedor de Incorporação",
|
||||||
|
description:
|
||||||
|
"Não é necessária configuração ao usar o mecanismo de incorporação nativo do AnythingLLM.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
text: {
|
||||||
|
title: "Preferências de Divisão e Fragmentação de Texto",
|
||||||
|
"desc-start":
|
||||||
|
"Às vezes, você pode querer alterar a maneira padrão como novos documentos são divididos e fragmentados antes de serem inseridos em seu banco de dados de vetores.",
|
||||||
|
"desc-end":
|
||||||
|
"Você só deve modificar esta configuração se entender como a divisão de texto funciona e seus efeitos colaterais.",
|
||||||
|
"warn-start": "As alterações aqui se aplicarão apenas a",
|
||||||
|
"warn-center": "documentos recém-incorporados",
|
||||||
|
"warn-end": ", não documentos existentes.",
|
||||||
|
size: {
|
||||||
|
title: "Tamanho do Fragmento de Texto",
|
||||||
|
description:
|
||||||
|
"Este é o comprimento máximo de caracteres que pode estar presente em um único vetor.",
|
||||||
|
recommend: "O comprimento máximo do modelo de incorporação é",
|
||||||
|
},
|
||||||
|
|
||||||
|
overlap: {
|
||||||
|
title: "Sobreposição de Fragmento de Texto",
|
||||||
|
description:
|
||||||
|
"Esta é a sobreposição máxima de caracteres que ocorre durante a fragmentação entre dois fragmentos de texto adjacentes.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
// Vector Database
|
||||||
|
vector: {
|
||||||
|
title: "Banco de Dados Vetorial",
|
||||||
|
description:
|
||||||
|
"Estas são as credenciais e configurações de como sua instância do AnythingLLM funcionará. É importante que essas chaves estejam atualizadas e corretas.",
|
||||||
|
provider: {
|
||||||
|
title: "Provedor de Banco de Dados Vetorial",
|
||||||
|
description: "Não há configuração necessária para o LanceDB.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
// Embeddable Chat Widgets
|
||||||
|
embeddable: {
|
||||||
|
title: "Widgets de Chat Incorporáveis",
|
||||||
|
description:
|
||||||
|
"Os widgets de chat incorporáveis são interfaces de chat públicas vinculadas a um único workspace. Eles permitem que você construa workspaces que você pode publicar para o mundo.",
|
||||||
|
create: "Criar incorporação",
|
||||||
|
table: {
|
||||||
|
workspace: "Workspace",
|
||||||
|
chats: "Chats Enviados",
|
||||||
|
Active: "Domínios Ativos",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
"embed-chats": {
|
||||||
|
title: "Incorporar Chats",
|
||||||
|
description:
|
||||||
|
"Estes são todos os chats e mensagens registrados de qualquer incorporação que você publicou.",
|
||||||
|
table: {
|
||||||
|
embed: "Incorporação",
|
||||||
|
sender: "Remetente",
|
||||||
|
message: "Mensagem",
|
||||||
|
response: "Resposta",
|
||||||
|
at: "Enviado Em",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
multi: {
|
||||||
|
title: "Modo Multiusuário",
|
||||||
|
description:
|
||||||
|
"Configure sua instância para suportar sua equipe ativando o Modo Multiusuário.",
|
||||||
|
enable: {
|
||||||
|
"is-enable": "Modo Multiusuário está Ativado",
|
||||||
|
enable: "Ativar Modo Multiusuário",
|
||||||
|
description:
|
||||||
|
"Por padrão, você será o único administrador. Como administrador, você precisará criar contas para todos os novos usuários ou administradores. Não perca sua senha, pois apenas um usuário Administrador pode redefinir senhas.",
|
||||||
|
username: "Nome de usuário da conta de Administrador",
|
||||||
|
password: "Senha da conta de Administrador",
|
||||||
|
},
|
||||||
|
password: {
|
||||||
|
title: "Proteção por Senha",
|
||||||
|
description:
|
||||||
|
"Proteja sua instância do AnythingLLM com uma senha. Se você esquecer esta senha, não há método de recuperação, então certifique-se de salvar esta senha.",
|
||||||
|
},
|
||||||
|
instance: {
|
||||||
|
title: "Proteger Instância com Senha",
|
||||||
|
description:
|
||||||
|
"Por padrão, você será o único administrador. Como administrador, você precisará criar contas para todos os novos usuários ou administradores. Não perca sua senha, pois apenas um usuário Administrador pode redefinir senhas.",
|
||||||
|
password: "Senha da instância",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
// Event Logs
|
||||||
|
event: {
|
||||||
|
title: "Logs de Eventos",
|
||||||
|
description:
|
||||||
|
"Veja todas as ações e eventos acontecendo nesta instância para monitoramento.",
|
||||||
|
clear: "Limpar Logs de Eventos",
|
||||||
|
table: {
|
||||||
|
type: "Tipo de Evento",
|
||||||
|
user: "Usuário",
|
||||||
|
occurred: "Ocorreu Em",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
// Privacy & Data-Handling
|
||||||
|
privacy: {
|
||||||
|
title: "Privacidade e Tratamento de Dados",
|
||||||
|
description:
|
||||||
|
"Esta é a sua configuração de como os provedores de terceiros conectados e o AnythingLLM tratam seus dados.",
|
||||||
|
llm: "Seleção de LLM",
|
||||||
|
embedding: "Preferência de Incorporação",
|
||||||
|
vector: "Banco de Dados Vetorial",
|
||||||
|
anonymous: "Telemetria Anônima Ativada",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TRANSLATIONS;
|
@ -19,30 +19,38 @@ import Korean from "./ko/common.js";
|
|||||||
import Spanish from "./es/common.js";
|
import Spanish from "./es/common.js";
|
||||||
import French from "./fr/common.js";
|
import French from "./fr/common.js";
|
||||||
import Mandarin from "./zh/common.js";
|
import Mandarin from "./zh/common.js";
|
||||||
|
import German from "./de/common.js";
|
||||||
import Russian from "./ru/common.js";
|
import Russian from "./ru/common.js";
|
||||||
import Italian from "./it/common.js";
|
import Italian from "./it/common.js";
|
||||||
|
import Portuguese from "./pt_BR/common.js";
|
||||||
|
|
||||||
export const defaultNS = "common";
|
export const defaultNS = "common";
|
||||||
export const resources = {
|
export const resources = {
|
||||||
en: {
|
en: {
|
||||||
common: English,
|
common: English,
|
||||||
},
|
},
|
||||||
ko: {
|
|
||||||
common: Korean,
|
|
||||||
},
|
|
||||||
zh: {
|
zh: {
|
||||||
common: Mandarin,
|
common: Mandarin,
|
||||||
},
|
},
|
||||||
es: {
|
es: {
|
||||||
common: Spanish,
|
common: Spanish,
|
||||||
},
|
},
|
||||||
|
de: {
|
||||||
|
common: German,
|
||||||
|
},
|
||||||
fr: {
|
fr: {
|
||||||
common: French,
|
common: French,
|
||||||
},
|
},
|
||||||
|
ko: {
|
||||||
|
common: Korean,
|
||||||
|
},
|
||||||
ru: {
|
ru: {
|
||||||
common: Russian,
|
common: Russian,
|
||||||
},
|
},
|
||||||
it: {
|
it: {
|
||||||
common: Italian,
|
common: Italian,
|
||||||
},
|
},
|
||||||
|
pt: {
|
||||||
|
common: Portuguese,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
BIN
frontend/src/media/ttsproviders/piper.png
Normal file
BIN
frontend/src/media/ttsproviders/piper.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
@ -7,6 +7,7 @@ import { RoleHintDisplay } from "..";
|
|||||||
export default function NewUserModal({ closeModal }) {
|
export default function NewUserModal({ closeModal }) {
|
||||||
const [error, setError] = useState(null);
|
const [error, setError] = useState(null);
|
||||||
const [role, setRole] = useState("default");
|
const [role, setRole] = useState("default");
|
||||||
|
|
||||||
const handleCreate = async (e) => {
|
const handleCreate = async (e) => {
|
||||||
setError(null);
|
setError(null);
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@ -54,7 +55,18 @@ export default function NewUserModal({ closeModal }) {
|
|||||||
minLength={2}
|
minLength={2}
|
||||||
required={true}
|
required={true}
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
|
pattern="^[a-z0-9_-]+$"
|
||||||
|
onInvalid={(e) =>
|
||||||
|
e.target.setCustomValidity(
|
||||||
|
"Username must be only contain lowercase letters, numbers, underscores, and hyphens with no spaces"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
onChange={(e) => e.target.setCustomValidity("")}
|
||||||
/>
|
/>
|
||||||
|
<p className="mt-2 text-xs text-white/60">
|
||||||
|
Username must be only contain lowercase letters, numbers,
|
||||||
|
underscores, and hyphens with no spaces
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label
|
<label
|
||||||
@ -70,7 +82,11 @@ export default function NewUserModal({ closeModal }) {
|
|||||||
placeholder="User's initial password"
|
placeholder="User's initial password"
|
||||||
required={true}
|
required={true}
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
|
minLength={8}
|
||||||
/>
|
/>
|
||||||
|
<p className="mt-2 text-xs text-white/60">
|
||||||
|
Password must be at least 8 characters long
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label
|
<label
|
||||||
@ -84,10 +100,10 @@ export default function NewUserModal({ closeModal }) {
|
|||||||
required={true}
|
required={true}
|
||||||
defaultValue={"default"}
|
defaultValue={"default"}
|
||||||
onChange={(e) => setRole(e.target.value)}
|
onChange={(e) => setRole(e.target.value)}
|
||||||
className="rounded-lg bg-zinc-900 px-4 py-2 text-sm text-white border-gray-500 focus:ring-blue-500 focus:border-blue-500"
|
className="rounded-lg bg-zinc-900 px-4 py-2 text-sm text-white border-gray-500 focus:ring-blue-500 focus:border-blue-500 w-full"
|
||||||
>
|
>
|
||||||
<option value="default">Default</option>
|
<option value="default">Default</option>
|
||||||
<option value="manager">Manager </option>
|
<option value="manager">Manager</option>
|
||||||
{user?.role === "admin" && (
|
{user?.role === "admin" && (
|
||||||
<option value="admin">Administrator</option>
|
<option value="admin">Administrator</option>
|
||||||
)}
|
)}
|
||||||
|
@ -52,11 +52,15 @@ export default function EditUserModal({ currentUser, user, closeModal }) {
|
|||||||
type="text"
|
type="text"
|
||||||
className="bg-zinc-900 placeholder:text-white/20 border-gray-500 text-white text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5"
|
className="bg-zinc-900 placeholder:text-white/20 border-gray-500 text-white text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5"
|
||||||
placeholder="User's username"
|
placeholder="User's username"
|
||||||
minLength={2}
|
|
||||||
defaultValue={user.username}
|
defaultValue={user.username}
|
||||||
|
minLength={2}
|
||||||
required={true}
|
required={true}
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
/>
|
/>
|
||||||
|
<p className="mt-2 text-xs text-white/60">
|
||||||
|
Username must be only contain lowercase letters, numbers,
|
||||||
|
underscores, and hyphens with no spaces
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label
|
<label
|
||||||
@ -71,7 +75,11 @@ export default function EditUserModal({ currentUser, user, closeModal }) {
|
|||||||
className="bg-zinc-900 placeholder:text-white/20 border-gray-500 text-white text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5"
|
className="bg-zinc-900 placeholder:text-white/20 border-gray-500 text-white text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5"
|
||||||
placeholder={`${user.username}'s new password`}
|
placeholder={`${user.username}'s new password`}
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
|
minLength={8}
|
||||||
/>
|
/>
|
||||||
|
<p className="mt-2 text-xs text-white/60">
|
||||||
|
Password must be at least 8 characters long
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label
|
<label
|
||||||
@ -85,7 +93,7 @@ export default function EditUserModal({ currentUser, user, closeModal }) {
|
|||||||
required={true}
|
required={true}
|
||||||
defaultValue={user.role}
|
defaultValue={user.role}
|
||||||
onChange={(e) => setRole(e.target.value)}
|
onChange={(e) => setRole(e.target.value)}
|
||||||
className="rounded-lg bg-zinc-900 px-4 py-2 text-sm text-white border-gray-500 focus:ring-blue-500 focus:border-blue-500"
|
className="rounded-lg bg-zinc-900 px-4 py-2 text-sm text-white border-gray-500 focus:ring-blue-500 focus:border-blue-500 w-full"
|
||||||
>
|
>
|
||||||
<option value="default">Default</option>
|
<option value="default">Default</option>
|
||||||
<option value="manager">Manager</option>
|
<option value="manager">Manager</option>
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
import FineTuning from "@/models/experimental/fineTuning";
|
import FineTuning from "@/models/experimental/fineTuning";
|
||||||
import { dollarFormat } from "@/utils/numbers";
|
import { dollarFormat } from "@/utils/numbers";
|
||||||
import showToast from "@/utils/toast";
|
import showToast from "@/utils/toast";
|
||||||
import { CheckCircle } from "@phosphor-icons/react";
|
import { Check } from "@phosphor-icons/react";
|
||||||
import { useState } from "react";
|
import { useState, useEffect } from "react";
|
||||||
import FineTuningSteps from "../index";
|
import FineTuningSteps from "../index";
|
||||||
|
import CTAButton from "@/components/lib/CTAButton";
|
||||||
|
import Workspace from "@/models/workspace";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {{settings: import("../index").OrderSettings}} param0
|
* @param {{settings: import("../index").OrderSettings}} param0
|
||||||
@ -11,6 +13,18 @@ import FineTuningSteps from "../index";
|
|||||||
*/
|
*/
|
||||||
export default function Confirmation({ settings, setSettings, setStep }) {
|
export default function Confirmation({ settings, setSettings, setStep }) {
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [workspaces, setWorkspaces] = useState([]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
Workspace.all()
|
||||||
|
.then((fetchedWorkspaces) => {
|
||||||
|
setWorkspaces(fetchedWorkspaces);
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
showToast("Failed to fetch workspaces", "error");
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
async function handleCheckout() {
|
async function handleCheckout() {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
const data = await FineTuning.createOrder({
|
const data = await FineTuning.createOrder({
|
||||||
@ -40,107 +54,124 @@ export default function Confirmation({ settings, setSettings, setStep }) {
|
|||||||
setStep(FineTuningSteps.confirmation.next());
|
setStep(FineTuningSteps.confirmation.next());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getWorkspaceName = (slug) => {
|
||||||
|
const workspace = workspaces.find((ws) => ws.slug === slug);
|
||||||
|
return workspace ? workspace.name : slug;
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex-[2] flex flex-col gap-y-[18px] mt-10">
|
<div className="flex-[2] flex flex-col gap-y-[18px] mt-10">
|
||||||
<div className="bg-[#303237] text-white rounded-xl flex-1 p-4 flex flex-col justify-between">
|
<div className="bg-[#303237] text-white rounded-xl flex-1 p-6">
|
||||||
<div className="w-full flex flex-col gap-y-4">
|
<div className="w-full flex flex-col gap-y-3 max-w-[700px]">
|
||||||
<h2 className="text-xl text-white font-semibold">Confirm & Submit</h2>
|
<h2 className="text-base text-white font-semibold">
|
||||||
<p>
|
Confirm & Submit
|
||||||
|
</h2>
|
||||||
|
<p className="text-white/80 text-sm">
|
||||||
Below are your fine-tuning order details. If you have any questions
|
Below are your fine-tuning order details. If you have any questions
|
||||||
before or after ordering your fine-tune you can{" "}
|
before or after ordering your fine-tune you can checkout the{" "}
|
||||||
<a
|
<a
|
||||||
href="https://docs.useanything.com/fine-tuning/overview"
|
href="https://docs.useanything.com/fine-tuning/overview"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
className="underline"
|
rel="noreferrer"
|
||||||
|
className="underline text-sky-400"
|
||||||
>
|
>
|
||||||
checkout the fine-tuning FAQ
|
fine-tuning FAQ
|
||||||
</a>{" "}
|
</a>{" "}
|
||||||
or email{" "}
|
or email{" "}
|
||||||
<a className="underline" href="mailto:team@mintplexlabs.com">
|
<a
|
||||||
|
className="underline text-sky-400"
|
||||||
|
href="mailto:team@mintplexlabs.com"
|
||||||
|
>
|
||||||
team@mintplexlabs.com
|
team@mintplexlabs.com
|
||||||
</a>
|
</a>
|
||||||
.
|
.
|
||||||
</p>
|
</p>
|
||||||
<div className="p-2 bg-zinc-800 text-white font-mono flex flex-col gap-y-2 h-full rounded-lg">
|
<div className="p-4 bg-zinc-900 text-white flex flex-col gap-y-2 rounded-lg mt-4">
|
||||||
<div className="flex items-center gap-x-1 text-sm">
|
<div className="flex flex-col gap-y-3 text-sm">
|
||||||
<p className="">Contact e-mail:</p>
|
<div className="flex items-start gap-x-1">
|
||||||
<p className="font-thin">{settings.email}</p>
|
<p className="w-1/3">Contact e-mail:</p>
|
||||||
</div>
|
<p className="text-white/80 w-2/3">{settings.email}</p>
|
||||||
<div className="flex items-center gap-x-1 text-sm">
|
|
||||||
<p className="">Base LLM:</p>
|
|
||||||
<p className="font-thin">{settings.baseModel}</p>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center gap-x-1 text-sm">
|
|
||||||
<p className="">Output model name:</p>
|
|
||||||
<p className="font-thin">"{settings.modelName}"</p>
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-col gap-y-1 text-sm">
|
|
||||||
<div className="flex items-center gap-x-1">
|
|
||||||
<p className="">Training on workspaces:</p>
|
|
||||||
{settings.trainingData.slugs.map((slug, i) => {
|
|
||||||
return (
|
|
||||||
<p key={slug} className="font-thin">
|
|
||||||
"{slug}"
|
|
||||||
{i !== settings.trainingData.slugs.length - 1 ? "," : ""}
|
|
||||||
</p>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
</div>
|
||||||
{settings.trainingData.feedback === true ? (
|
<div className="flex items-start gap-x-1">
|
||||||
<p className="underline">
|
<p className="w-1/3">Base LLM:</p>
|
||||||
training on <b>positive-feedback chats only</b>.
|
<p className="text-white/80 w-2/3">{settings.baseModel}</p>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-start gap-x-1">
|
||||||
|
<p className="w-1/3">Output model name:</p>
|
||||||
|
<p className="text-white/80 w-2/3">"{settings.modelName}"</p>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-start gap-x-1">
|
||||||
|
<p className="w-1/3">Training on workspaces:</p>
|
||||||
|
<div className="text-white/80 w-2/3 flex flex-wrap gap-1">
|
||||||
|
{settings.trainingData.slugs.map((slug, i) => (
|
||||||
|
<span
|
||||||
|
key={slug}
|
||||||
|
className={`rounded-full bg-white/10 px-2 py-0.5 h-[20px] text-xs font-medium text-white shadow-sm`}
|
||||||
|
>
|
||||||
|
{getWorkspaceName(slug)}
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-start gap-x-1">
|
||||||
|
<p className="w-1/3">Training data:</p>
|
||||||
|
<p className="text-white/80 w-2/3">
|
||||||
|
{settings.trainingData.feedback === true
|
||||||
|
? "Training on positive-feedback chats only"
|
||||||
|
: "Training on all chats"}
|
||||||
</p>
|
</p>
|
||||||
) : (
|
</div>
|
||||||
<p className="underline">
|
|
||||||
training on <b>all chats</b>.
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<br />
|
<div className="mt-4">
|
||||||
<div className="flex items-center gap-x-1 text-sm">
|
<ul className="flex flex-col gap-y-1 text-sm">
|
||||||
<CheckCircle className="text-green-300" />
|
<li className="flex items-center gap-x-2">
|
||||||
<p className="font-thin">Agreed to Terms and Conditions</p>
|
<Check className="text-white" size={12} weight="bold" />
|
||||||
</div>
|
<p className="text-white/80">
|
||||||
<div className="flex items-center gap-x-1 text-sm">
|
Agreed to Terms and Conditions
|
||||||
<CheckCircle className="text-green-300" />
|
</p>
|
||||||
<p className="font-thin">Understand privacy & data handling</p>
|
</li>
|
||||||
</div>
|
<li className="flex items-center gap-x-2">
|
||||||
<div className="flex items-center gap-x-1 text-sm">
|
<Check className="text-white" size={12} weight="bold" />
|
||||||
<CheckCircle className="text-green-300" />
|
<p className="text-white/80">
|
||||||
<p className="font-thin">Agreed to Fulfillment terms</p>
|
Understand privacy & data handling
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li className="flex items-center gap-x-2">
|
||||||
|
<Check className="text-white" size={12} weight="bold" />
|
||||||
|
<p className="text-white/80">Agreed to Fulfillment terms</p>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div className="mt-4 border-white/40 pt-2">
|
||||||
<div className="flex items-center gap-x-1 text-lg border-t-[2px] border-white/40 pt-2 mb-0">
|
<div className="flex items-center gap-x-1 text-lg mb-0">
|
||||||
<p className="">Total one-time cost:</p>
|
<p className="">Total one-time cost:</p>
|
||||||
<p className="font-thin">
|
<p className="text-white/80">
|
||||||
{dollarFormat(settings.tuningInfo.pricing.usd)}
|
{dollarFormat(settings.tuningInfo.pricing.usd)}
|
||||||
<sup>*</sup>
|
<sup>*</sup>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<p className="m-0 p-0 text-xs text-white/60 font-mono">
|
<p className="m-0 p-0 text-xs text-white/60">
|
||||||
<sup>*</sup> price does not include any coupons, incentives, or
|
<sup>*</sup> price does not include any coupons, incentives, or
|
||||||
discounts you can apply at checkout.
|
discounts you can apply at checkout.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p>
|
<p className="text-xs text-white/80 mt-4">
|
||||||
Once you proceed to checkout, if you do not complete this purchase
|
Once you proceed to checkout, if you do not complete this purchase
|
||||||
your data will be deleted from our servers within 1 hour of
|
your data will be deleted from our servers within 1 hour of
|
||||||
abandonment of the creation of the checkout in accordance to our
|
abandonment of the creation of the checkout in accordance to our
|
||||||
privacy and data handling policy.
|
privacy and data handling policy.
|
||||||
</p>
|
</p>
|
||||||
|
<CTAButton
|
||||||
|
disabled={loading}
|
||||||
|
onClick={handleCheckout}
|
||||||
|
className="text-dark-text w-full mt-[18px] h-[34px] hover:bg-accent"
|
||||||
|
>
|
||||||
|
{loading ? "Generating order..." : "Start Training →"}
|
||||||
|
</CTAButton>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button
|
|
||||||
disabled={loading}
|
|
||||||
onClick={handleCheckout}
|
|
||||||
type="button"
|
|
||||||
className="mt-8 w-full py-2 text-center text-black bg-white hover:bg-white/80 border-none rounded-lg"
|
|
||||||
>
|
|
||||||
{loading ? <>Generating order...</> : <>Start Training →</>}
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -1,8 +1,14 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import FineTuning from "@/models/experimental/fineTuning";
|
import FineTuning from "@/models/experimental/fineTuning";
|
||||||
import Workspace from "@/models/workspace";
|
import Workspace from "@/models/workspace";
|
||||||
import { CheckCircle, Warning, X } from "@phosphor-icons/react";
|
import {
|
||||||
|
CheckCircle,
|
||||||
|
Warning,
|
||||||
|
X,
|
||||||
|
MagnifyingGlass,
|
||||||
|
} from "@phosphor-icons/react";
|
||||||
import FineTuningSteps from "..";
|
import FineTuningSteps from "..";
|
||||||
|
import CTAButton from "@/components/lib/CTAButton";
|
||||||
|
|
||||||
export default function DataUpload({ setSettings, setStep }) {
|
export default function DataUpload({ setSettings, setStep }) {
|
||||||
const [workspaces, setWorkspaces] = useState([]);
|
const [workspaces, setWorkspaces] = useState([]);
|
||||||
@ -41,34 +47,29 @@ export default function DataUpload({ setSettings, setStep }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex-[2] flex flex-col gap-y-[18px] mt-10">
|
<div className="flex-[2] flex flex-col gap-y-[18px] mt-10">
|
||||||
<div className="bg-[#303237] text-white rounded-xl flex-1 p-4">
|
<div className="bg-[#303237] text-white rounded-xl flex-1 p-6">
|
||||||
<form
|
<div className="w-full flex flex-col gap-y-3 max-w-[700px]">
|
||||||
onSubmit={handleSubmit}
|
<h2 className="text-base text-white font-semibold">
|
||||||
className="flex flex-col justify-between h-full"
|
Select your training dataset
|
||||||
>
|
</h2>
|
||||||
<div className="w-full flex flex-col gap-y-4">
|
<p className="text-white/80 text-sm">
|
||||||
<h2 className="text-xl text-white font-semibold">
|
This is the data your model will be trained and tuned on. This is a
|
||||||
Select your training dataset.
|
critical step and you should always train on the exact information
|
||||||
</h2>
|
you want the model to inherit. By default, AnythingLLM will use all
|
||||||
<p>
|
chats, but you can filter chats by workspace and even limit training
|
||||||
This is the data your model will be trained and tuned on. This is
|
to chats which users have left a positive feedback indication on
|
||||||
a critical step and you should always train on the exact
|
(thumbs up).
|
||||||
information you want the model to inherit. By default, AnythingLLM
|
</p>
|
||||||
will use all chats, but you can filter chats by workspace and even
|
|
||||||
limit training to chats which users have left a positive feedback
|
|
||||||
indication on (thumbs up).
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div className="flex flex-col pr-10">
|
<form onSubmit={handleSubmit} className="flex flex-col gap-y-6 mt-4">
|
||||||
<div className="flex flex-col gap-y-1 mb-4">
|
<div className="flex flex-col">
|
||||||
<label className="text-white text-sm font-bold">
|
<label className="text-white text-sm font-semibold block mb-3">
|
||||||
Only use positive responses
|
Only use positive responses
|
||||||
</label>
|
</label>
|
||||||
<p className="text-xs font-normal text-white/80">
|
<p className="text-xs font-normal text-white/80 mb-2">
|
||||||
Enabling this toggle will filter your dataset to only use
|
Enabling this toggle will filter your dataset to only use
|
||||||
"positive" responses that were marked during chatting.
|
"positive" responses that were marked during chatting.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
|
||||||
<label className="relative inline-flex cursor-pointer items-center w-fit">
|
<label className="relative inline-flex cursor-pointer items-center w-fit">
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
@ -84,34 +85,33 @@ export default function DataUpload({ setSettings, setStep }) {
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-col pr-10">
|
<div className="flex flex-col">
|
||||||
<div className="flex flex-col gap-y-1 mb-4">
|
<label className="text-white text-sm font-semibold block mb-3">
|
||||||
<label className="text-white text-sm font-bold">
|
Selected Workspaces
|
||||||
Selected Workspaces
|
</label>
|
||||||
</label>
|
<p className="text-xs font-normal text-white/80 mb-2">
|
||||||
<p className="text-xs font-normal text-white/80">
|
Your training data will be limited to these workspaces.
|
||||||
You training data will be limited to these workspaces.
|
</p>
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<WorkspaceSelector
|
<WorkspaceSelector
|
||||||
workspaces={workspaces}
|
workspaces={workspaces}
|
||||||
selectedWorkspaces={dataFilters.workspaces}
|
selectedWorkspaces={dataFilters.workspaces}
|
||||||
setDataFilters={setDataFilters}
|
setDataFilters={setDataFilters}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<DatasetSummary
|
<DatasetSummary
|
||||||
workspaces={dataFilters.workspaces}
|
workspaces={dataFilters.workspaces}
|
||||||
feedback={dataFilters.feedback}
|
feedback={dataFilters.feedback}
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
|
|
||||||
<button
|
<CTAButton
|
||||||
type="submit"
|
type="submit"
|
||||||
className="mt-20 w-full py-2 text-center text-white hover:bg-primary-button border-none rounded-lg"
|
className="text-dark-text w-full mt-[18px] h-[34px] hover:bg-accent"
|
||||||
>
|
>
|
||||||
Proceed to Confirmation →
|
Proceed to Confirmation →
|
||||||
</button>
|
</CTAButton>
|
||||||
</form>
|
</form>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@ -155,60 +155,59 @@ function WorkspaceSelector({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col items-center">
|
<div className="flex flex-col gap-y-2">
|
||||||
<div className="w-full h-fit">
|
<div className="min-w-[150px] max-w-[300px] h-[32px] p-[10px] rounded-lg flex items-center bg-dark-highlight mt-1">
|
||||||
<div className="w-full relative z-1">
|
<MagnifyingGlass size={16} className="text-white" />
|
||||||
<div className="p-1 flex border border-white/40 bg-zinc-800 rounded">
|
<input
|
||||||
<div className="flex flex-auto flex-wrap">
|
value={query}
|
||||||
{selectedWorkspaces.map((workspace) => {
|
onChange={(e) => setQuery(e.target.value)}
|
||||||
return (
|
onFocus={() => setShowSuggestions(true)}
|
||||||
<div
|
onBlur={() =>
|
||||||
key={workspace.slug}
|
setTimeout(() => {
|
||||||
className="flex gap-x-1 justify-center items-center m-1 font-medium py-1 px-2 bg-zinc-800 rounded-full text-white/40 bg-white/10 border border-white/40 "
|
setShowSuggestions(false);
|
||||||
>
|
}, 500)
|
||||||
<div className="text-xs font-normal text-white leading-none max-w-full flex-initial">
|
}
|
||||||
{workspace.name}
|
placeholder="Enter a workspace name"
|
||||||
</div>
|
className="bg-transparent p-1 px-2 appearance-none outline-none h-full w-full text-white text-xs placeholder:`text-white/50`"
|
||||||
<div className="flex flex-auto flex-row-reverse">
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col items-center -ml-2">
|
||||||
|
<div className="w-full h-fit">
|
||||||
|
<div className="w-full relative z-1">
|
||||||
|
<div className="p-2 flex rounded-lg">
|
||||||
|
<div className="flex flex-wrap gap-2 w-full">
|
||||||
|
{selectedWorkspaces.map((workspace) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={workspace.slug}
|
||||||
|
className="flex items-center justify-between rounded-full h-[20px] bg-white/10 px-2 py-1 text-xs font-medium text-white shadow-sm"
|
||||||
|
>
|
||||||
|
<span className="truncate mr-1">{workspace.name}</span>
|
||||||
<button
|
<button
|
||||||
onClick={() => handleRemoveWorkspace(workspace)}
|
onClick={() => handleRemoveWorkspace(workspace)}
|
||||||
type="button"
|
type="button"
|
||||||
className="hover:text-red-500"
|
className="hover:text-red-500 flex-shrink-0"
|
||||||
>
|
>
|
||||||
<X size={14} weight="bold" />
|
<X size={10} weight="bold" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
);
|
||||||
);
|
})}
|
||||||
})}
|
|
||||||
<div className="flex-1">
|
|
||||||
<input
|
|
||||||
value={query}
|
|
||||||
onChange={(e) => setQuery(e.target.value)}
|
|
||||||
onFocus={() => setShowSuggestions(true)}
|
|
||||||
onBlur={() =>
|
|
||||||
setTimeout(() => {
|
|
||||||
setShowSuggestions(false);
|
|
||||||
}, 500)
|
|
||||||
}
|
|
||||||
placeholder="Enter a workspace name"
|
|
||||||
className="w-[200px] bg-transparent p-1 px-2 appearance-none outline-none h-full w-full text-white"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
{showSuggestions && (
|
||||||
{showSuggestions && (
|
<div className="w-full flex relative">
|
||||||
<div className="w-full flex relative">
|
<div className="w-full absolute top-0 z-20">
|
||||||
<div className="w-full absolute top-0 z-20">
|
<WorkspaceSuggestions
|
||||||
<WorkspaceSuggestions
|
availableWorkspaces={availableWorkspaces}
|
||||||
availableWorkspaces={availableWorkspaces}
|
addWorkspace={handleAddWorkspace}
|
||||||
addWorkspace={handleAddWorkspace}
|
query={query}
|
||||||
query={query}
|
/>
|
||||||
/>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
)}
|
||||||
)}
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@ -221,7 +220,7 @@ function WorkspaceSuggestions({
|
|||||||
}) {
|
}) {
|
||||||
if (availableWorkspaces.length === 0) {
|
if (availableWorkspaces.length === 0) {
|
||||||
return (
|
return (
|
||||||
<div className="w-full mt-[2px] bg-zinc-800 border border-white/40 top-[45px] h-40 rounded-lg p-2 text-sm">
|
<div className="w-full mt-[2px] bg-zinc-900 top-[45px] h-40 rounded-lg p-2 text-sm">
|
||||||
<p className="text-center text-white/40">
|
<p className="text-center text-white/40">
|
||||||
no workspaces available to select.
|
no workspaces available to select.
|
||||||
</p>
|
</p>
|
||||||
@ -239,7 +238,7 @@ function WorkspaceSuggestions({
|
|||||||
: availableWorkspaces;
|
: availableWorkspaces;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full mt-[2px] bg-zinc-800 border border-white/40 top-[45px] h-40 rounded-lg p-2 text-sm flex flex-col gap-y-1 justify-start overflow-y-scroll">
|
<div className="w-full mt-[2px] bg-zinc-900 top-[45px] h-40 rounded-lg p-2 text-sm flex flex-col gap-y-1 justify-start overflow-y-scroll">
|
||||||
{filteredWorkspace.map((workspace) => {
|
{filteredWorkspace.map((workspace) => {
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
@ -271,19 +270,19 @@ function DatasetSummary({ workspaces = [], feedback = null }) {
|
|||||||
}, [workspaces, feedback]);
|
}, [workspaces, feedback]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="bg-zinc-800 text-white/80 p-2 rounded-lg font-mono">
|
<div className="bg-zinc-900 text-white/80 p-4 rounded-lg text-sm">
|
||||||
<p>Training dataset size: {stats.count ?? "Unknown"}</p>
|
<p>Training dataset size: {stats.count ?? "Unknown"}</p>
|
||||||
{stats.count < stats.recommendedMin ? (
|
{stats.count < stats.recommendedMin ? (
|
||||||
<div className="flex items-center gap-x-1 text-red-500 text-sm p-1 rounded-lg bg-red-500/20 w-fit my-2">
|
<div className="flex items-center gap-x-1 text-red-500 text-sm p-2 rounded-lg bg-red-500/20 w-fit my-2">
|
||||||
<Warning />
|
<Warning size={14} />
|
||||||
<p>
|
<p>
|
||||||
Your dataset is below the recommended minimum of{" "}
|
Your dataset is below the recommended minimum of{" "}
|
||||||
{stats.recommendedMin}! You may see no impact from a fine-tune.
|
{stats.recommendedMin}! You may see no impact from a fine-tune.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="flex items-center gap-x-1 text-green-500 text-sm p-1 rounded-lg bg-green-500/20 w-fit my-2">
|
<div className="flex items-center gap-x-1 text-green-500 text-sm p-2 rounded-lg bg-green-500/20 w-fit my-2">
|
||||||
<CheckCircle />
|
<CheckCircle size={14} />
|
||||||
<p>
|
<p>
|
||||||
Your dataset is large enough that you should see good results from a
|
Your dataset is large enough that you should see good results from a
|
||||||
fine-tune.
|
fine-tune.
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import CTAButton from "@/components/lib/CTAButton";
|
||||||
import FineTuningSteps from "..";
|
import FineTuningSteps from "..";
|
||||||
|
|
||||||
export default function Fulfillment({ setSettings, setStep }) {
|
export default function Fulfillment({ setSettings, setStep }) {
|
||||||
@ -10,34 +11,29 @@ export default function Fulfillment({ setSettings, setStep }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex-[2] flex flex-col gap-y-[18px] mt-10">
|
<div className="flex-[2] flex flex-col gap-y-[18px] mt-10">
|
||||||
<div className="bg-[#303237] text-white rounded-xl flex-1 p-4">
|
<div className="bg-[#303237] text-white rounded-xl flex-1 p-6">
|
||||||
<div className="w-full flex flex-col gap-y-4">
|
<div className="w-full flex flex-col gap-y-3 max-w-[700px]">
|
||||||
<h2 className="text-xl text-white font-semibold">
|
<h2 className="text-base text-white font-semibold">
|
||||||
Fulfillment Policy
|
Fulfillment Policy
|
||||||
</h2>
|
</h2>
|
||||||
<p>
|
<p className="text-white/80 text-sm">
|
||||||
Fulfillment of a fine-tune model is straight-forward. We do not host
|
Fulfillment of a fine-tune model is straight-forward. We do not host
|
||||||
your model. We provide you a download link to run the model in a
|
your model. We provide you a download link to run the model in a
|
||||||
standard format where ever you run local LLMs
|
standard format where ever you run local LLMs
|
||||||
</p>
|
</p>
|
||||||
<div className="flex flex-col gap-y-2 text-white/75 text-sm border p-2 border-white rounded-lg font-mono h-[60vh] overflow-y-scroll">
|
<div className="flex flex-col gap-y-2 text-white/80 text-xs font-semibold rounded-lg p-4 h-[60vh] overflow-y-auto bg-dark-text mt-2">
|
||||||
<h1 class="text-white/80 text-lg font-semibold">
|
<div className="text-xs text-white">
|
||||||
Fulfillment Terms
|
<h1>Fulfillment Terms</h1>
|
||||||
</h1>
|
<p>Last updated: July 15, 2024</p>
|
||||||
<p>
|
</div>
|
||||||
<strong>Last updated: July 15, 2024</strong>
|
<p className="text-white/80">
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
These fulfillment terms outline the agreement between Mintplex
|
These fulfillment terms outline the agreement between Mintplex
|
||||||
Labs Inc. (“Company,” “we,” “us,” or “our”) and the customer
|
Labs Inc. ("Company," "we," "us," or "our") and the customer
|
||||||
regarding the creation and delivery of fine-tuned models.
|
regarding the creation and delivery of fine-tuned models.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2 class="text-white/80 text-base font-semibold">
|
<h2 className="text-white mt-4">Delivery of Model</h2>
|
||||||
Delivery of Model
|
<p className="text-white/80">
|
||||||
</h2>
|
|
||||||
<p>
|
|
||||||
Upon completion of a fine-tuning job, we will deliver a download
|
Upon completion of a fine-tuning job, we will deliver a download
|
||||||
link to a .gguf model file suitable for LLM text inferencing. The
|
link to a .gguf model file suitable for LLM text inferencing. The
|
||||||
customer acknowledges that this exchange is strictly transactional
|
customer acknowledges that this exchange is strictly transactional
|
||||||
@ -45,17 +41,15 @@ export default function Fulfillment({ setSettings, setStep }) {
|
|||||||
is considered concluded and will be ineligible for a refund.
|
is considered concluded and will be ineligible for a refund.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2 class="text-white/80 text-base font-semibold">Support</h2>
|
<h2 className="text-white mt-4">Support</h2>
|
||||||
<p>
|
<p className="text-white/80">
|
||||||
Please note that the delivery of the model does not include any
|
Please note that the delivery of the model does not include any
|
||||||
dedicated support. Customers are encouraged to refer to available
|
dedicated support. Customers are encouraged to refer to available
|
||||||
documentation and resources for guidance on using the model.
|
documentation and resources for guidance on using the model.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2 class="text-white/80 text-base font-semibold">
|
<h2 className="text-white mt-4">Requesting Download Links</h2>
|
||||||
Requesting Download Links
|
<p className="text-white/80">
|
||||||
</h2>
|
|
||||||
<p>
|
|
||||||
Customers may request refreshed download links from
|
Customers may request refreshed download links from
|
||||||
my.mintplexlabs.com as long as the model is retained in our cloud
|
my.mintplexlabs.com as long as the model is retained in our cloud
|
||||||
storage. We will retain a model in our storage for a maximum of 3
|
storage. We will retain a model in our storage for a maximum of 3
|
||||||
@ -63,10 +57,8 @@ export default function Fulfillment({ setSettings, setStep }) {
|
|||||||
links are valid for 24 hours.
|
links are valid for 24 hours.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2 class="text-white/80 text-base font-semibold">
|
<h2 className="text-white mt-4">Cancellation and Refunds</h2>
|
||||||
Cancellation and Refunds
|
<p className="text-white/80">
|
||||||
</h2>
|
|
||||||
<p>
|
|
||||||
Mintplex Labs Inc. reserves the right to cancel any fine-tuning
|
Mintplex Labs Inc. reserves the right to cancel any fine-tuning
|
||||||
job at our discretion. In the event of a cancellation, a refund
|
job at our discretion. In the event of a cancellation, a refund
|
||||||
may be issued. Additionally, we reserve the right to deny a
|
may be issued. Additionally, we reserve the right to deny a
|
||||||
@ -74,22 +66,22 @@ export default function Fulfillment({ setSettings, setStep }) {
|
|||||||
cause or notice to the Customer.
|
cause or notice to the Customer.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2 class="text-white/80 text-base font-semibold">No Guarantees</h2>
|
<h2 className="text-white mt-4">No Guarantees</h2>
|
||||||
<p>
|
<p className="text-white/80">
|
||||||
Mintplex Labs Inc. makes <strong>NO GUARANTEES</strong> regarding
|
Mintplex Labs Inc. makes <strong>NO GUARANTEES</strong> regarding
|
||||||
the resulting model's output, functionality, speed, or
|
the resulting model's output, functionality, speed, or
|
||||||
compatibility with your tools, infrastructure and devices. Refund
|
compatibility with your tools, infrastructure and devices. Refund
|
||||||
requests of this nature are not eligible for refunds.
|
requests of this nature are not eligible for refunds.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p className="text-white/80">
|
||||||
Models are delivered and accepted in "As-Is" condition. All
|
Models are delivered and accepted in "As-Is" condition. All
|
||||||
delivered model and output files are deemed final and
|
delivered model and output files are deemed final and
|
||||||
non-refundable for any reason after training is complete and a
|
non-refundable for any reason after training is complete and a
|
||||||
model has been generated.
|
model has been generated.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2 class="text-white/80 text-base font-semibold">Payment Terms</h2>
|
<h2 className="text-white mt-4">Payment Terms</h2>
|
||||||
<p>
|
<p className="text-white/80">
|
||||||
All payments are required prior to the commencement of the
|
All payments are required prior to the commencement of the
|
||||||
fine-tuning process. Customers are responsible for ensuring that
|
fine-tuning process. Customers are responsible for ensuring that
|
||||||
valid payment information is provided. Checkout sessions not
|
valid payment information is provided. Checkout sessions not
|
||||||
@ -97,32 +89,36 @@ export default function Fulfillment({ setSettings, setStep }) {
|
|||||||
abandoned and will be deleted from our system.
|
abandoned and will be deleted from our system.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2 class="text-white/80 text-base font-semibold">
|
<h2 className="text-white mt-4">
|
||||||
Denial of Service for Payment Reasons
|
Denial of Service for Payment Reasons
|
||||||
</h2>
|
</h2>
|
||||||
<p>
|
<p className="text-white/80">
|
||||||
Mintplex Labs Inc. reserves the right to deny service to any
|
Mintplex Labs Inc. reserves the right to deny service to any
|
||||||
customer with an outstanding balance or invalid payment
|
customer with an outstanding balance or invalid payment
|
||||||
information. If any discrepancies arise regarding payment or
|
information. If any discrepancies arise regarding payment or
|
||||||
usage, we may suspend services until the matter is resolved.
|
usage, we may suspend services until the matter is resolved.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2 class="text-white/80 text-base font-semibold">Contact</h2>
|
<h2 className="text-white mt-4">Contact</h2>
|
||||||
<p>
|
<p className="text-white/80">
|
||||||
For any questions related to payment or fulfillment of services,
|
For any questions related to payment or fulfillment of services,
|
||||||
please contact us at{" "}
|
please contact us at{" "}
|
||||||
<a href="mailto:team@mintplexlabs.com">team@mintplexlabs.com</a>.
|
<a
|
||||||
|
href="mailto:team@mintplexlabs.com"
|
||||||
|
className="text-blue-400 hover:underline"
|
||||||
|
>
|
||||||
|
team@mintplexlabs.com
|
||||||
|
</a>
|
||||||
|
.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
<CTAButton
|
||||||
|
className="text-dark-text w-full mt-[18px] h-[34px] hover:bg-accent"
|
||||||
|
onClick={handleAccept}
|
||||||
|
>
|
||||||
|
Agree and continue →
|
||||||
|
</CTAButton>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button
|
|
||||||
onClick={handleAccept}
|
|
||||||
type="button"
|
|
||||||
className="mt-8 w-full py-2 text-center text-white hover:bg-primary-button border-none rounded-lg"
|
|
||||||
>
|
|
||||||
Agree and continue →
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { CheckCircle, XCircle } from "@phosphor-icons/react";
|
import { Check, X } from "@phosphor-icons/react";
|
||||||
import FineTuningSteps from "..";
|
import FineTuningSteps from "..";
|
||||||
|
import CTAButton from "@/components/lib/CTAButton";
|
||||||
|
|
||||||
export default function Introduction({ setSettings, setStep }) {
|
export default function Introduction({ setSettings, setStep }) {
|
||||||
const handleAccept = () => {
|
const handleAccept = () => {
|
||||||
@ -11,12 +12,12 @@ export default function Introduction({ setSettings, setStep }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex-[2] flex flex-col gap-y-[18px] mt-10">
|
<div className="flex-[2] flex flex-col gap-y-[18px] mt-10">
|
||||||
<div className="bg-[#303237] text-white rounded-xl flex-1 p-4">
|
<div className="bg-[#303237] text-white rounded-xl flex-1 p-6">
|
||||||
<div className="w-full flex flex-col gap-y-4">
|
<div className="w-full flex flex-col gap-y-2 max-w-[700px]">
|
||||||
<h2 className="text-xl text-white font-semibold">
|
<h2 className="text-base text-white font-semibold">
|
||||||
What is a "Fine-Tuned" model?
|
What is a "Fine-Tuned" model?
|
||||||
</h2>
|
</h2>
|
||||||
<div className="flex flex-col gap-y-2 text-white/80">
|
<div className="flex flex-col gap-y-[25px] text-white/80 text-sm">
|
||||||
<p>
|
<p>
|
||||||
Fine-tuned models are basically "customized"
|
Fine-tuned models are basically "customized"
|
||||||
Language-Learning-Models (LLMs). These can be based on popular
|
Language-Learning-Models (LLMs). These can be based on popular
|
||||||
@ -36,8 +37,8 @@ export default function Introduction({ setSettings, setStep }) {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-col gap-y-2 text-white/80">
|
<div className="flex flex-col gap-y-2 text-white/80 text-sm mt-4">
|
||||||
<h3 className="text-lg text-white font-semibold">
|
<h3 className="text-base text-white font-semibold">
|
||||||
When should I get a fine-tuned model?
|
When should I get a fine-tuned model?
|
||||||
</h3>
|
</h3>
|
||||||
<p>
|
<p>
|
||||||
@ -45,65 +46,65 @@ export default function Introduction({ setSettings, setStep }) {
|
|||||||
following
|
following
|
||||||
</p>
|
</p>
|
||||||
<ul className="flex flex-col gap-y-1">
|
<ul className="flex flex-col gap-y-1">
|
||||||
<li className="flex items-center gap-x-1">
|
<li className="flex items-center gap-x-2">
|
||||||
<CheckCircle className="text-green-300" /> Setting the style,
|
<Check className="text-white" size={12} weight="bold" /> Setting
|
||||||
tone, format, or other qualitative aspects without prompting
|
the style, tone, format, or other qualitative aspects without
|
||||||
|
prompting
|
||||||
</li>
|
</li>
|
||||||
<li className="flex items-center gap-x-1">
|
<li className="flex items-center gap-x-2">
|
||||||
<CheckCircle className="text-green-300" /> Improving reliability
|
<Check className="text-white" size={12} weight="bold" />{" "}
|
||||||
at producing a desired output
|
Improving reliability at producing a desired output
|
||||||
</li>
|
</li>
|
||||||
<li className="flex items-center gap-x-1">
|
<li className="flex items-center gap-x-2">
|
||||||
<CheckCircle className="text-green-300" /> Correcting failures
|
<Check className="text-white" size={12} weight="bold" />{" "}
|
||||||
to follow complex prompts, citations, or lack of background
|
Correcting failures to follow complex prompts, citations, or
|
||||||
knowledge
|
lack of background knowledge
|
||||||
</li>
|
</li>
|
||||||
<li className="flex items-center gap-x-1">
|
<li className="flex items-center gap-x-2">
|
||||||
<CheckCircle className="text-green-300" /> You want to run this
|
<Check className="text-white" size={12} weight="bold" /> You
|
||||||
model privately or offline
|
want to run this model privately or offline
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-col gap-y-2 text-white/80">
|
<div className="flex flex-col gap-y-2 text-white/80 text-sm mt-4">
|
||||||
<h3 className="text-lg text-white font-semibold">
|
<h3 className="text-base text-white font-semibold">
|
||||||
What are fine-tunes bad for?
|
What are fine-tunes bad for?
|
||||||
</h3>
|
</h3>
|
||||||
<p>
|
<p>
|
||||||
Fine-tuned models powerful, but they are not the "silver bullet"
|
Fine-tuned models are powerful, but they are not the "silver
|
||||||
to any issues you have with RAG currently. Some notable
|
bullet" to any issues you have with RAG currently. Some notable
|
||||||
limitations are
|
limitations are
|
||||||
</p>
|
</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li className="flex items-center gap-x-1">
|
<li className="flex items-center gap-x-1">
|
||||||
<XCircle className="text-red-300" /> You need perfect recall of
|
<X className="text-white" size={12} weight="bold" /> You need
|
||||||
some piece of literature or reference document
|
perfect recall of some piece of literature or reference document
|
||||||
</li>
|
</li>
|
||||||
<li className="flex items-center gap-x-1">
|
<li className="flex items-center gap-x-1">
|
||||||
<XCircle className="text-red-300" /> You want your model to have
|
<X className="text-white" size={12} weight="bold" /> You want
|
||||||
perfect memory or recollection
|
your model to have perfect memory or recollection
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-col gap-y-2 text-white/80">
|
<div className="flex flex-col gap-y-2 text-white/80 text-sm">
|
||||||
<p>
|
<p>
|
||||||
In summary, if you are getting good results with RAG currently,
|
In summary, if you are getting good results with RAG currently,
|
||||||
creating a fine-tune can squeeze <b>even more performance</b> out
|
creating a fine-tune can squeeze <b>even more performance</b> out
|
||||||
of a model. Fine-Tunes are are for improving response quality and
|
of a model. Fine-Tunes are for improving response quality and
|
||||||
general responses, but they are <b>not for knowledge recall</b> -
|
general responses, but they are <b>not for knowledge recall</b> -
|
||||||
that is what RAG is for! Together, it is a powerful combination.
|
that is what RAG is for! Together, it is a powerful combination.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
<CTAButton
|
||||||
|
className="text-dark-text w-full mt-[18px] h-[34px] hover:bg-accent"
|
||||||
|
onClick={handleAccept}
|
||||||
|
text="Create fine-tune model →"
|
||||||
|
>
|
||||||
|
Create a fine-tune model →
|
||||||
|
</CTAButton>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button
|
|
||||||
onClick={handleAccept}
|
|
||||||
type="button"
|
|
||||||
className="mt-8 w-full py-2 text-center text-white hover:bg-primary-button border-none rounded-lg"
|
|
||||||
>
|
|
||||||
Start a fine-tune →
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -2,9 +2,11 @@ import FineTuning from "@/models/experimental/fineTuning";
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import FineTuningSteps from "..";
|
import FineTuningSteps from "..";
|
||||||
import { CircleNotch } from "@phosphor-icons/react";
|
import { CircleNotch } from "@phosphor-icons/react";
|
||||||
|
import CTAButton from "@/components/lib/CTAButton";
|
||||||
|
|
||||||
export default function OrderDetails({ setSettings, setStep }) {
|
export default function OrderDetails({ setSettings, setStep }) {
|
||||||
const [info, setInfo] = useState({});
|
const [info, setInfo] = useState({});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
FineTuning.info()
|
FineTuning.info()
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
@ -32,33 +34,30 @@ export default function OrderDetails({ setSettings, setStep }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex-[2] flex flex-col gap-y-[18px] mt-10">
|
<div className="flex-[2] flex flex-col gap-y-[18px] mt-10">
|
||||||
<div className="bg-[#303237] text-white rounded-xl flex-1 p-4">
|
<div className="bg-[#303237] text-white rounded-xl flex-1 p-6">
|
||||||
<form onSubmit={handleSubmit}>
|
<div className="w-full flex flex-col gap-y-3 max-w-[700px]">
|
||||||
<div className="w-full flex flex-col gap-y-4">
|
<h2 className="text-base text-white font-semibold">
|
||||||
<h2 className="text-xl text-white font-semibold">
|
Time to create your fine tune!
|
||||||
Time to create your fine tune!
|
</h2>
|
||||||
</h2>
|
<p className="text-white/80 text-sm">
|
||||||
<p>
|
Creating a model is quite simple. Currently we have a limited base
|
||||||
Creating a model is quite simple. Currently we have a limited base
|
model selection, however in the future we plan to expand support to
|
||||||
model selection, however in the future we plan to expand support
|
many more foundational models.
|
||||||
to many more foundational models.
|
</p>
|
||||||
</p>
|
<form onSubmit={handleSubmit} className="flex flex-col gap-y-6 mt-4">
|
||||||
|
<div className="flex flex-col">
|
||||||
<div className="flex flex-col pr-10">
|
<label className="text-white text-sm font-semibold block mb-3">
|
||||||
<div className="flex flex-col gap-y-1 mb-4">
|
Account e-mail
|
||||||
<label className="text-white text-sm font-bold">
|
</label>
|
||||||
Account e-mail
|
<p className="text-xs font-normal text-white/80 mb-2">
|
||||||
</label>
|
This e-mail is where you will receive all order information and
|
||||||
<p className="text-xs font-normal text-white/80">
|
updates. This e-mail <b>must be accurate</b> or else we won't be
|
||||||
This e-mail is where you will receive all order information
|
able to contact you with your fine-tuned model!
|
||||||
and updates. This e-mail <b>must be accurate</b> or else we
|
</p>
|
||||||
won't be able to contact you with your fine-tuned model!
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<input
|
<input
|
||||||
type="email"
|
type="email"
|
||||||
name="email"
|
name="email"
|
||||||
className="border-none bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:outline-primary-button active:outline-primary-button outline-none block w-full p-2.5"
|
className="bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:outline-primary-button active:outline-primary-button outline-none block w-full max-w-[200px] p-2.5"
|
||||||
placeholder="jdoe@example.com"
|
placeholder="jdoe@example.com"
|
||||||
required={true}
|
required={true}
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
@ -66,33 +65,29 @@ export default function OrderDetails({ setSettings, setStep }) {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-col pr-10">
|
<div className="flex flex-col">
|
||||||
<div className="flex flex-col gap-y-1 mb-4">
|
<label className="text-white text-sm font-semibold block mb-3">
|
||||||
<label className="text-white text-sm font-bold">
|
Preferred Base Model
|
||||||
Preferred Base Model
|
</label>
|
||||||
</label>
|
<p className="text-xs font-normal text-white/80 mb-2">
|
||||||
<p className="text-xs font-normal text-white/80">
|
This is the foundational model your fine-tune will be based on.
|
||||||
This is the foundational model your fine-tune will be based
|
We recommend Llama 3 8B.
|
||||||
on. We recommend Llama 3 8B.
|
</p>
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
{info.hasOwnProperty("availableBaseModels") ? (
|
{info.hasOwnProperty("availableBaseModels") ? (
|
||||||
<select
|
<select
|
||||||
name="baseModel"
|
name="baseModel"
|
||||||
required={true}
|
required={true}
|
||||||
className="border-none bg-zinc-900 border-gray-500 text-white text-sm rounded-lg block w-fit p-2.5"
|
className="bg-zinc-900 border-gray-500 text-white text-sm rounded-lg block w-full max-w-[200px] p-2.5"
|
||||||
>
|
>
|
||||||
<option disabled="true" selected="true" value="">
|
<option disabled value="">
|
||||||
-- select a base model --
|
-- select a base model --
|
||||||
</option>
|
</option>
|
||||||
<optgroup label="Available base models">
|
<optgroup label="Available base models">
|
||||||
{(info?.availableBaseModels || []).map((model) => {
|
{(info?.availableBaseModels || []).map((model) => (
|
||||||
return (
|
<option key={model} value={model}>
|
||||||
<option key={model} value={model}>
|
{model}
|
||||||
{model}
|
</option>
|
||||||
</option>
|
))}
|
||||||
);
|
|
||||||
})}
|
|
||||||
</optgroup>
|
</optgroup>
|
||||||
</select>
|
</select>
|
||||||
) : (
|
) : (
|
||||||
@ -103,35 +98,34 @@ export default function OrderDetails({ setSettings, setStep }) {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-col pr-10">
|
<div className="flex flex-col">
|
||||||
<div className="flex flex-col gap-y-1 mb-4">
|
<label className="text-white text-sm font-semibold block mb-3">
|
||||||
<label className="text-white text-sm font-bold">
|
Model name
|
||||||
Model name
|
</label>
|
||||||
</label>
|
<p className="text-xs font-normal text-white/80 mb-2">
|
||||||
<p className="text-xs font-normal text-white/80">
|
What would you like to call your model? This has no impact on
|
||||||
What would you like to call your model? This has no impact on
|
its output or training and is only used for how we communicate
|
||||||
its output or training and is only used for how we communicate
|
with you about the model.
|
||||||
with you about the model.
|
</p>
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
name="modelName"
|
name="modelName"
|
||||||
className="border-none bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:outline-primary-button active:outline-primary-button outline-none block w-full p-2.5"
|
className="bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:outline-primary-button active:outline-primary-button outline-none block w-full max-w-[200px] p-2.5"
|
||||||
placeholder="My really cool model!"
|
placeholder="My really cool model!"
|
||||||
required={true}
|
required={true}
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
spellCheck={false}
|
spellCheck={false}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
<button
|
<CTAButton
|
||||||
type="submit"
|
type="submit"
|
||||||
className="mt-8 w-full py-2 text-center text-white hover:bg-primary-button border-none rounded-lg"
|
className="text-dark-text w-full mt-[18px] h-[34px] hover:bg-accent"
|
||||||
>
|
>
|
||||||
Proceed to data selection →
|
Proceed to data selection →
|
||||||
</button>
|
</CTAButton>
|
||||||
</form>
|
</form>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -1,13 +1,15 @@
|
|||||||
|
import CTAButton from "@/components/lib/CTAButton";
|
||||||
|
import paths from "@/utils/paths";
|
||||||
|
|
||||||
export default function OrderPlaced({ settings }) {
|
export default function OrderPlaced({ settings }) {
|
||||||
return (
|
return (
|
||||||
<div className="flex-[2] flex flex-col gap-y-[18px] mt-10">
|
<div className="flex-[2] flex flex-col gap-y-[18px] mt-10">
|
||||||
<div className="bg-[#303237] text-white rounded-xl flex-1 p-4">
|
<div className="bg-[#303237] text-white rounded-xl flex-1 p-6">
|
||||||
<div className="w-full flex flex-col gap-y-4">
|
<div className="w-full flex flex-col gap-y-2 max-w-[700px]">
|
||||||
<h2 className="text-xl text-white font-semibold">
|
<h2 className="text-base text-white font-semibold">
|
||||||
Your order is placed!
|
Your order is placed!
|
||||||
</h2>
|
</h2>
|
||||||
|
<div className="flex flex-col gap-y-[25px] text-white/80 text-xs">
|
||||||
<div className="">
|
|
||||||
<p>
|
<p>
|
||||||
Your fine-tune will begin once payment is complete. If the payment
|
Your fine-tune will begin once payment is complete. If the payment
|
||||||
window did not automatically open - your checkout link is below.
|
window did not automatically open - your checkout link is below.
|
||||||
@ -15,53 +17,65 @@ export default function OrderPlaced({ settings }) {
|
|||||||
<a
|
<a
|
||||||
href={settings.checkoutUrl}
|
href={settings.checkoutUrl}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
className="text-xs font-mono text-white/60 underline"
|
rel="noreferrer"
|
||||||
|
className="text-sky-400 hover:underline hover:cursor-pointer"
|
||||||
>
|
>
|
||||||
{new URL(settings.checkoutUrl).origin}
|
{new URL(settings.checkoutUrl).origin}
|
||||||
</a>
|
</a>
|
||||||
<p className="text-xs font-mono text-white/80">
|
<p className="text-xs text-white/80">
|
||||||
Your fine-tune does not begin until this payment is completed.
|
Your fine-tune does not begin until this payment is completed.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="">
|
<div className="flex flex-col gap-y-2">
|
||||||
<p className="font-mono text-white/80 text-sm">
|
<p className="text-white/80 font-medium">
|
||||||
Reference: {settings.jobId}
|
Reference: <span className="font-normal">{settings.jobId}</span>
|
||||||
</p>
|
</p>
|
||||||
<p className="text-xs font-mono text-white/80">
|
<p className="text-xs text-white/80">
|
||||||
This reference id is how we will communicate with you about your
|
This reference id is how we will communicate with you about your
|
||||||
fine-tune training status. <b>Save this reference id.</b>
|
fine-tune training status. <b>Save this reference id.</b>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex flex-col gap-y-2">
|
||||||
|
<p className="text-white/80 font-medium">
|
||||||
|
Contact: <span className="font-normal">{settings.email}</span>
|
||||||
|
</p>
|
||||||
|
<p className="text-xs text-white/80">
|
||||||
|
Check the email above for order confirmation, status updates,
|
||||||
|
and more. Mintplex Labs will only contact you about your order
|
||||||
|
via email.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex flex-col items-left gap-x-4 text-xs">
|
||||||
|
<a
|
||||||
|
href="https://docs.useanything.com/fine-tuning/overview"
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
className="text-sky-400 hover:underline hover:cursor-pointer"
|
||||||
|
>
|
||||||
|
Documentation
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
href="mailto:team@mintplexlabs.com"
|
||||||
|
className="text-sky-400 hover:underline hover:cursor-pointer"
|
||||||
|
>
|
||||||
|
Contact support
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p className="text-xs text-white/80">
|
||||||
|
You can close this window or navigate away once you see the
|
||||||
|
confirmation email in your inbox.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="">
|
<CTAButton
|
||||||
<p className="font-mono text-white/80 text-sm">
|
className="text-dark-text w-full mt-[18px] h-[34px] hover:bg-accent"
|
||||||
Contact: {settings.email}
|
onClick={() => (window.location.href = paths.home())}
|
||||||
</p>
|
>
|
||||||
<p className="text-xs font-mono text-white/80">
|
Finish
|
||||||
Check the email above for order confirmation, status updates, and
|
</CTAButton>
|
||||||
more. Mintplex Labs will only contact you about your order via
|
|
||||||
email.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="font-mono text-white/80 text-sm flex items-center gap-x-2">
|
|
||||||
<a
|
|
||||||
href="https://docs.useanything.com/fine-tuning/overview"
|
|
||||||
target="_blank"
|
|
||||||
className="underline"
|
|
||||||
>
|
|
||||||
Documentation
|
|
||||||
</a>
|
|
||||||
<a href="mailto:team@mintplexlabs.com" className="underline">
|
|
||||||
Contact support
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<p className="text-xs font-mono text-white/80">
|
|
||||||
You can close this window or navigate away once you see the
|
|
||||||
confirmation email in your inbox.
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import CTAButton from "@/components/lib/CTAButton";
|
||||||
import FineTuningSteps from "..";
|
import FineTuningSteps from "..";
|
||||||
|
|
||||||
export default function PrivacyHandling({ setSettings, setStep }) {
|
export default function PrivacyHandling({ setSettings, setStep }) {
|
||||||
@ -10,12 +11,12 @@ export default function PrivacyHandling({ setSettings, setStep }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex-[2] flex flex-col gap-y-[18px] mt-10">
|
<div className="flex-[2] flex flex-col gap-y-[18px] mt-10">
|
||||||
<div className="bg-[#303237] text-white rounded-xl flex-1 p-4">
|
<div className="bg-[#303237] text-white rounded-xl flex-1 p-6">
|
||||||
<div className="w-full flex flex-col gap-y-4">
|
<div className="w-full flex flex-col gap-y-3 max-w-[700px]">
|
||||||
<h2 className="text-xl text-white font-semibold">
|
<h2 className="text-base text-white font-semibold">
|
||||||
Data Handling Policy & Privacy
|
Data Handling Policy & Privacy
|
||||||
</h2>
|
</h2>
|
||||||
<p>
|
<p className="text-white/80 text-sm">
|
||||||
Please accept the terms and conditions to continue with creation and
|
Please accept the terms and conditions to continue with creation and
|
||||||
ordering of a fine-tune model. We take the handling of your data
|
ordering of a fine-tune model. We take the handling of your data
|
||||||
very seriously and will only use your uploaded data for training the
|
very seriously and will only use your uploaded data for training the
|
||||||
@ -23,18 +24,14 @@ export default function PrivacyHandling({ setSettings, setStep }) {
|
|||||||
completed, or canceled your information is automatically and
|
completed, or canceled your information is automatically and
|
||||||
permanently deleted.
|
permanently deleted.
|
||||||
</p>
|
</p>
|
||||||
<div className="flex flex-col gap-y-2 text-white/75 text-sm border p-2 border-white rounded-lg font-mono h-[60vh] overflow-y-scroll">
|
<div className="flex flex-col gap-y-2 text-white/75 text-xs font-semibold rounded-lg p-4 h-[60vh] overflow-y-auto bg-dark-text mt-2">
|
||||||
<h1 class="text-white/80 text-lg font-semibold">Privacy Policy</h1>
|
<div className="text-xs">
|
||||||
|
<h1 className="text-white/80">Privacy Policy</h1>
|
||||||
<p>
|
<p>Mintplex Labs Inc.</p>
|
||||||
<strong>Mintplex Labs Inc.</strong>
|
<p>Effective Date: July 15, 2024</p>
|
||||||
</p>
|
</div>
|
||||||
<p>Effective Date: July 15, 2024</p>
|
<h2 className="text-white mt-4">1. Introduction</h2>
|
||||||
|
<p className="text-white/80">
|
||||||
<h2 class="text-white/80 text-base font-semibold">
|
|
||||||
1. Introduction
|
|
||||||
</h2>
|
|
||||||
<p>
|
|
||||||
Welcome to Mintplex Labs Inc. ("we", "our", "us"). We are
|
Welcome to Mintplex Labs Inc. ("we", "our", "us"). We are
|
||||||
committed to protecting your privacy and ensuring the security of
|
committed to protecting your privacy and ensuring the security of
|
||||||
your personal information. This Privacy Policy describes how we
|
your personal information. This Privacy Policy describes how we
|
||||||
@ -42,45 +39,41 @@ export default function PrivacyHandling({ setSettings, setStep }) {
|
|||||||
services.
|
services.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2 class="text-white/80 text-base font-semibold">
|
<h2 className="text-white mt-4">2. Information We Collect</h2>
|
||||||
2. Information We Collect
|
<p className="text-white/80">
|
||||||
</h2>
|
|
||||||
<p>
|
|
||||||
When you place an order with us for tuning and large language
|
When you place an order with us for tuning and large language
|
||||||
model (LLM) fulfillment, we collect certain personal information
|
model (LLM) fulfillment, we collect certain personal information
|
||||||
from you, including but not limited to:
|
from you, including but not limited to:
|
||||||
</p>
|
</p>
|
||||||
<ul>
|
<ul className="list-disc pl-5 text-white/80">
|
||||||
<li>Email address</li>
|
<li>Email address</li>
|
||||||
<li>Payment information</li>
|
<li>Payment information</li>
|
||||||
<li>Uploaded training data</li>
|
<li>Uploaded training data</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<h2 class="text-white/80 text-base font-semibold">
|
<h2 className="text-white mt-4">3. Use of Information</h2>
|
||||||
3. Use of Information
|
<p className="text-white/80">
|
||||||
</h2>
|
We use the information we collect for the following purposes:
|
||||||
<p>We use the information we collect for the following purposes:</p>
|
</p>
|
||||||
<ul>
|
<ul className="list-disc pl-5 text-white/80">
|
||||||
<li>To process and fulfill your order</li>
|
<li>To process and fulfill your order</li>
|
||||||
<li>To communicate with you regarding your order</li>
|
<li>To communicate with you regarding your order</li>
|
||||||
<li>To improve our services</li>
|
<li>To improve our services</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<h2 class="text-white/80 text-base font-semibold">
|
<h2 className="text-white mt-4">4. Data Retention and Deletion</h2>
|
||||||
4. Data Retention and Deletion
|
<p className="text-white/80">
|
||||||
</h2>
|
|
||||||
<p>
|
|
||||||
Uploaded training data is only retained for the duration of the
|
Uploaded training data is only retained for the duration of the
|
||||||
model training. Upon training completion, failure, or order
|
model training. Upon training completion, failure, or order
|
||||||
cancellation, the user data is permanently deleted from our
|
cancellation, the user data is permanently deleted from our
|
||||||
storage.
|
storage.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p className="text-white/80">
|
||||||
If you partially complete the order flow and do not finalize your
|
If you partially complete the order flow and do not finalize your
|
||||||
order, any details and information associated with your order will
|
order, any details and information associated with your order will
|
||||||
be deleted 1 hour from abandonment.
|
be deleted 1 hour from abandonment.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p className="text-white/80">
|
||||||
After you confirm receipt of your resulting model files, you can
|
After you confirm receipt of your resulting model files, you can
|
||||||
request us to delete your model from our storage at any time.
|
request us to delete your model from our storage at any time.
|
||||||
Additionally, we may proactively reach out to you to confirm that
|
Additionally, we may proactively reach out to you to confirm that
|
||||||
@ -90,10 +83,8 @@ export default function PrivacyHandling({ setSettings, setStep }) {
|
|||||||
storage.
|
storage.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2 class="text-white/80 text-base font-semibold">
|
<h2 className="text-white mt-4">5. Data Storage and Security</h2>
|
||||||
5. Data Storage and Security
|
<p className="text-white/80">
|
||||||
</h2>
|
|
||||||
<p>
|
|
||||||
Our cloud storage provider is AWS. We have implement standard
|
Our cloud storage provider is AWS. We have implement standard
|
||||||
encryption and protection policies to ensure the security of your
|
encryption and protection policies to ensure the security of your
|
||||||
data. The storage solution has no public access, and all requests
|
data. The storage solution has no public access, and all requests
|
||||||
@ -104,43 +95,41 @@ export default function PrivacyHandling({ setSettings, setStep }) {
|
|||||||
e-mail you used during checkout.
|
e-mail you used during checkout.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2 class="text-white/80 text-base font-semibold">
|
<h2 className="text-white mt-4">6. Payment Processing</h2>
|
||||||
6. Payment Processing
|
<p className="text-white/80">
|
||||||
</h2>
|
|
||||||
<p>
|
|
||||||
We use Stripe as our payment processor. Your email may be shared
|
We use Stripe as our payment processor. Your email may be shared
|
||||||
with Stripe for customer service and payment management purposes.
|
with Stripe for customer service and payment management purposes.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2 class="text-white/80 text-base font-semibold">
|
<h2 className="text-white mt-4">7. Data Sharing</h2>
|
||||||
7. Data Sharing
|
<p className="text-white/80">
|
||||||
</h2>
|
|
||||||
<p>
|
|
||||||
We do not sell or share your personal information with third
|
We do not sell or share your personal information with third
|
||||||
parties except as necessary to provide our services, comply with
|
parties except as necessary to provide our services, comply with
|
||||||
legal obligations, or protect our rights.
|
legal obligations, or protect our rights.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2 class="text-white/80 text-base font-semibold">
|
<h2 className="text-white mt-4">8. Your Rights</h2>
|
||||||
8. Your Rights
|
<p className="text-white/80">
|
||||||
</h2>
|
|
||||||
<p>
|
|
||||||
You have the right to access, correct, or delete your personal
|
You have the right to access, correct, or delete your personal
|
||||||
information. If you wish to exercise these rights, please contact
|
information. If you wish to exercise these rights, please contact
|
||||||
us at{" "}
|
us at{" "}
|
||||||
<a href="mailto:team@mintplexlabs.com">team@mintplexlabs.com</a>.
|
<a
|
||||||
|
href="mailto:team@mintplexlabs.com"
|
||||||
|
className="text-blue-400 hover:underline"
|
||||||
|
>
|
||||||
|
team@mintplexlabs.com
|
||||||
|
</a>
|
||||||
|
.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2 class="text-white/80 text-base font-semibold">
|
<h2 className="text-white mt-4">9. California Privacy Rights</h2>
|
||||||
9. California Privacy Rights
|
<p className="text-white/80">
|
||||||
</h2>
|
|
||||||
<p>
|
|
||||||
Under the California Consumer Privacy Act as amended by the
|
Under the California Consumer Privacy Act as amended by the
|
||||||
California Privacy Rights Act (the “CCPA”), California residents
|
California Privacy Rights Act (the "CCPA"), California residents
|
||||||
have additional rights beyond what is set out in this privacy
|
have additional rights beyond what is set out in this privacy
|
||||||
notice:
|
notice:
|
||||||
</p>
|
</p>
|
||||||
<ul>
|
<ul className="list-disc pl-5 text-white/80">
|
||||||
<li>
|
<li>
|
||||||
<strong>Right to Know:</strong> You have the right to request
|
<strong>Right to Know:</strong> You have the right to request
|
||||||
information about the categories and specific pieces of personal
|
information about the categories and specific pieces of personal
|
||||||
@ -170,63 +159,70 @@ export default function PrivacyHandling({ setSettings, setStep }) {
|
|||||||
your CCPA rights.
|
your CCPA rights.
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>
|
<p className="text-white/80">
|
||||||
<strong>Submitting a Request:</strong>
|
<strong>Submitting a Request:</strong>
|
||||||
<br />
|
<br />
|
||||||
You may submit a request to know, delete, or correct your personal
|
You may submit a request to know, delete, or correct your personal
|
||||||
information by contacting us at{" "}
|
information by contacting us at{" "}
|
||||||
<a href="mailto:team@mintplexlabs.com">team@mintplexlabs.com</a>.
|
<a
|
||||||
We will confirm your identity before processing your request and
|
href="mailto:team@mintplexlabs.com"
|
||||||
|
className="text-blue-400 hover:underline"
|
||||||
|
>
|
||||||
|
team@mintplexlabs.com
|
||||||
|
</a>
|
||||||
|
. We will confirm your identity before processing your request and
|
||||||
respond within 45 days. If more time is needed, we will inform you
|
respond within 45 days. If more time is needed, we will inform you
|
||||||
of the reason and extension period in writing. You may make a
|
of the reason and extension period in writing. You may make a
|
||||||
request for your information twice every 12 months. If you are
|
request for your information twice every 12 months. If you are
|
||||||
making an erasure request, please include details of the
|
making an erasure request, please include details of the
|
||||||
information you would like erased.
|
information you would like erased.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p className="text-white/80">
|
||||||
Please note that if you request that we remove your information,
|
Please note that if you request that we remove your information,
|
||||||
we may retain some of the information for specific reasons, such
|
we may retain some of the information for specific reasons, such
|
||||||
as to resolve disputes, troubleshoot problems, and as required by
|
as to resolve disputes, troubleshoot problems, and as required by
|
||||||
law. Some information may not be completely removed from our
|
law. Some information may not be completely removed from our
|
||||||
databases due to technical constraints and regular backups.
|
databases due to technical constraints and regular backups.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p className="text-white/80">
|
||||||
We will not discriminate against you for exercising any of your
|
We will not discriminate against you for exercising any of your
|
||||||
CCPA rights.
|
CCPA rights.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2 class="text-white/80 text-base font-semibold">
|
<h2 className="text-white mt-4">10. Contact Us</h2>
|
||||||
10. Contact Us
|
<p className="text-white/80">
|
||||||
</h2>
|
|
||||||
<p>
|
|
||||||
If you have any questions or concerns about this Privacy Policy,
|
If you have any questions or concerns about this Privacy Policy,
|
||||||
please contact us at{" "}
|
please contact us at{" "}
|
||||||
<a href="mailto:team@mintplexlabs.com">team@mintplexlabs.com</a>.
|
<a
|
||||||
|
href="mailto:team@mintplexlabs.com"
|
||||||
|
className="text-blue-400 hover:underline"
|
||||||
|
>
|
||||||
|
team@mintplexlabs.com
|
||||||
|
</a>
|
||||||
|
.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2 class="text-white/80 text-base font-semibold">
|
<h2 className="text-white mt-4">
|
||||||
11. Changes to This Privacy Policy
|
11. Changes to This Privacy Policy
|
||||||
</h2>
|
</h2>
|
||||||
<p>
|
<p className="text-white/80">
|
||||||
We may update this Privacy Policy from time to time. We will
|
We may update this Privacy Policy from time to time. We will
|
||||||
notify you of any changes by posting the new Privacy Policy on our
|
notify you of any changes by posting the new Privacy Policy on our
|
||||||
website. You are advised to review this Privacy Policy
|
website. You are advised to review this Privacy Policy
|
||||||
periodically for any changes.
|
periodically for any changes.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p className="text-white/80">
|
||||||
By using our services, you agree to the terms of this Privacy
|
By using our services, you agree to the terms of this Privacy
|
||||||
Policy.
|
Policy.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
<CTAButton
|
||||||
|
className="text-dark-text w-full mt-[18px] h-[34px] hover:bg-accent"
|
||||||
|
onClick={handleAccept}
|
||||||
|
>
|
||||||
|
Agree and continue →
|
||||||
|
</CTAButton>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button
|
|
||||||
onClick={handleAccept}
|
|
||||||
type="button"
|
|
||||||
className="mt-8 w-full py-2 text-center text-white hover:bg-primary-button border-none rounded-lg"
|
|
||||||
>
|
|
||||||
Agree and continue →
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import CTAButton from "@/components/lib/CTAButton";
|
||||||
import FineTuningSteps from "..";
|
import FineTuningSteps from "..";
|
||||||
|
|
||||||
export default function TermsAndConditions({ setSettings, setStep }) {
|
export default function TermsAndConditions({ setSettings, setStep }) {
|
||||||
@ -10,24 +11,21 @@ export default function TermsAndConditions({ setSettings, setStep }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex-[2] flex flex-col gap-y-[18px] mt-10">
|
<div className="flex-[2] flex flex-col gap-y-[18px] mt-10">
|
||||||
<div className="bg-[#303237] text-white rounded-xl flex-1 p-4">
|
<div className="bg-[#303237] text-white rounded-xl flex-1 p-6">
|
||||||
<div className="w-full flex flex-col gap-y-4">
|
<div className="w-full flex flex-col gap-y-3 max-w-[700px]">
|
||||||
<h2 className="text-xl text-white font-semibold">
|
<h2 className="text-base text-white font-semibold">
|
||||||
Terms and Conditions
|
Terms and Conditions
|
||||||
</h2>
|
</h2>
|
||||||
<p>
|
<p className="text-white/80 text-sm">
|
||||||
Please accept the terms and conditions to continue with creation and
|
Please accept the terms and conditions to continue with creation and
|
||||||
ordering of a fine-tune model.
|
ordering of a fine-tune model.
|
||||||
</p>
|
</p>
|
||||||
<div className="flex flex-col gap-y-2 text-white/75 text-sm border p-2 border-white rounded-lg font-mono h-[60vh] overflow-y-scroll">
|
<div className="flex flex-col gap-y-2 text-white/80 text-xs font-semibold rounded-lg p-4 h-[60vh] overflow-y-auto bg-dark-text mt-2">
|
||||||
<h1 className="text-white/80 text-lg font-semibold">
|
<div className="text-xs text-white">
|
||||||
Mintplex Labs Inc. Fine-Tuning Terms of Service
|
<h1>Mintplex Labs Inc. Fine-Tuning Terms of Service</h1>
|
||||||
</h1>
|
<p>Last Updated: July 15, 2024</p>
|
||||||
<p>
|
</div>
|
||||||
<strong>Last Updated:</strong> July 15, 2024
|
<p className="text-white/80">
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
This Agreement is between Mintplex Labs Inc. ("Company") and the
|
This Agreement is between Mintplex Labs Inc. ("Company") and the
|
||||||
customer ("Customer") accessing or using the services provided by
|
customer ("Customer") accessing or using the services provided by
|
||||||
the Company. By signing up, accessing, or using the services,
|
the Company. By signing up, accessing, or using the services,
|
||||||
@ -35,20 +33,16 @@ export default function TermsAndConditions({ setSettings, setStep }) {
|
|||||||
be bound by the terms and conditions outlined below.
|
be bound by the terms and conditions outlined below.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2 className="text-white/80 text-base font-semibold">
|
<h2 className="text-white mt-4">1. Services Provided</h2>
|
||||||
1. Services Provided
|
<p className="text-white/80">
|
||||||
</h2>
|
|
||||||
<p>
|
|
||||||
Mintplex Labs Inc. provides model fine-tuning services for
|
Mintplex Labs Inc. provides model fine-tuning services for
|
||||||
customers. The deliverable for these services is a download link
|
customers. The deliverable for these services is a download link
|
||||||
to the output ".GGUF" file that can be used by the Customer for
|
to the output ".GGUF" file that can be used by the Customer for
|
||||||
Large-Language text inferencing.
|
Large-Language text inferencing.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2 className="text-white/80 text-base font-semibold">
|
<h2 className="text-white mt-4">2. Payment Terms</h2>
|
||||||
2. Payment Terms
|
<ul className="list-disc pl-5 text-white/80">
|
||||||
</h2>
|
|
||||||
<ul>
|
|
||||||
<li>
|
<li>
|
||||||
<strong>One-Time Payment:</strong> A one-time payment is
|
<strong>One-Time Payment:</strong> A one-time payment is
|
||||||
required before the execution of the training.
|
required before the execution of the training.
|
||||||
@ -64,10 +58,8 @@ export default function TermsAndConditions({ setSettings, setStep }) {
|
|||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<h2 className="text-white/80 text-base font-semibold">
|
<h2 className="text-white mt-4">3. Order Form</h2>
|
||||||
3. Order Form
|
<ul className="list-disc pl-5 text-white/80">
|
||||||
</h2>
|
|
||||||
<ul>
|
|
||||||
<li>
|
<li>
|
||||||
<strong>Service:</strong> Model fine-tuning
|
<strong>Service:</strong> Model fine-tuning
|
||||||
</li>
|
</li>
|
||||||
@ -79,88 +71,81 @@ export default function TermsAndConditions({ setSettings, setStep }) {
|
|||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<h2 className="text-white/80 text-base font-semibold">
|
<h2 className="text-white mt-4">4. Customer Responsibilities</h2>
|
||||||
4. Customer Responsibilities
|
<p className="text-white/80">
|
||||||
</h2>
|
|
||||||
<p>
|
|
||||||
The Customer must provide all necessary data and information
|
The Customer must provide all necessary data and information
|
||||||
required for model fine-tuning.
|
required for model fine-tuning.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p className="text-white/80">
|
||||||
The Customer must ensure timely payment as per the terms mentioned
|
The Customer must ensure timely payment as per the terms mentioned
|
||||||
above.
|
above.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p className="text-white/80">
|
||||||
The Customer understands the data collected for tuning will be
|
The Customer understands the data collected for tuning will be
|
||||||
stored to a private cloud storage location temporarily while
|
stored to a private cloud storage location temporarily while
|
||||||
training is in progress.
|
training is in progress.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p className="text-white/80">
|
||||||
The Customer understands the data collected for tuning will be
|
The Customer understands the data collected for tuning will be
|
||||||
fully deleted once the order is completed or canceled by the
|
fully deleted once the order is completed or canceled by the
|
||||||
Company.
|
Company.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p className="text-white/80">
|
||||||
The Customer understands and has reviewed the Privacy Policy for
|
The Customer understands and has reviewed the Privacy Policy for
|
||||||
Fine-Tuning by the Company.
|
Fine-Tuning by the Company.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2 className="text-white/80 text-base font-semibold">
|
<h2 className="text-white mt-4">5. Refund Policy</h2>
|
||||||
5. Refund Policy
|
<p className="text-white/80">
|
||||||
</h2>
|
|
||||||
<p>
|
|
||||||
Refunds will be processed in the event of training failure or if
|
Refunds will be processed in the event of training failure or if
|
||||||
the complete model file is not delivered to the Customer. Refunds
|
the complete model file is not delivered to the Customer. Refunds
|
||||||
will be issued to the original payment method within 30 days of
|
will be issued to the original payment method within 30 days of
|
||||||
the refund request.
|
the refund request.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2 className="text-white/80 text-base font-semibold">
|
<h2 className="text-white mt-4">6. Governing Law</h2>
|
||||||
6. Governing Law
|
<p className="text-white/80">
|
||||||
</h2>
|
|
||||||
<p>
|
|
||||||
This Agreement shall be governed by and construed in accordance
|
This Agreement shall be governed by and construed in accordance
|
||||||
with the laws of the State of California.
|
with the laws of the State of California.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2 className="text-white/80 text-base font-semibold">
|
<h2 className="text-white mt-4">7. Dispute Resolution</h2>
|
||||||
7. Dispute Resolution
|
<p className="text-white/80">
|
||||||
</h2>
|
|
||||||
<p>
|
|
||||||
Any disputes arising out of or in connection with this Agreement
|
Any disputes arising out of or in connection with this Agreement
|
||||||
shall be resolved in the state or federal courts located in
|
shall be resolved in the state or federal courts located in
|
||||||
California.
|
California.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2 className="text-white/80 text-base font-semibold">
|
<h2 className="text-white mt-4">8. Notices</h2>
|
||||||
8. Notices
|
<p className="text-white/80">
|
||||||
</h2>
|
|
||||||
<p>
|
|
||||||
All notices under this Agreement shall be in writing and shall be
|
All notices under this Agreement shall be in writing and shall be
|
||||||
deemed given when delivered personally, sent by confirmed email,
|
deemed given when delivered personally, sent by confirmed email,
|
||||||
or sent by certified or registered mail, return receipt requested,
|
or sent by certified or registered mail, return receipt requested,
|
||||||
and addressed to the respective parties as follows:
|
and addressed to the respective parties as follows:
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p className="text-white/80">
|
||||||
For Company:{" "}
|
For Company:{" "}
|
||||||
<a href="mailto:team@mintplexlabs.com">team@mintplexlabs.com</a>
|
<a
|
||||||
|
href="mailto:team@mintplexlabs.com"
|
||||||
|
className="text-blue-400 hover:underline"
|
||||||
|
>
|
||||||
|
team@mintplexlabs.com
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
<p className="text-white/80">
|
||||||
|
For Customer: The main email address on Customer's account
|
||||||
</p>
|
</p>
|
||||||
<p>For Customer: The main email address on Customer's account</p>
|
|
||||||
|
|
||||||
<h2 className="text-white/80 text-base font-semibold">
|
<h2 className="text-white mt-4">9. Amendments</h2>
|
||||||
9. Amendments
|
<p className="text-white/80">
|
||||||
</h2>
|
|
||||||
<p>
|
|
||||||
The Company reserves the right to amend these terms at any time by
|
The Company reserves the right to amend these terms at any time by
|
||||||
providing notice to the Customer. The Customer's continued use of
|
providing notice to the Customer. The Customer's continued use of
|
||||||
the services after such amendments will constitute acceptance of
|
the services after such amendments will constitute acceptance of
|
||||||
the amended terms.
|
the amended terms.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2 className="text-white/80 text-base font-semibold">
|
<h2 className="text-white mt-4">10. Indemnity</h2>
|
||||||
10. Indemnity
|
<p className="text-white/80">
|
||||||
</h2>
|
|
||||||
<p>
|
|
||||||
The Customer agrees to indemnify, defend, and hold harmless
|
The Customer agrees to indemnify, defend, and hold harmless
|
||||||
Mintplex Labs Inc., its affiliates, and their respective officers,
|
Mintplex Labs Inc., its affiliates, and their respective officers,
|
||||||
directors, employees, agents, and representatives from and against
|
directors, employees, agents, and representatives from and against
|
||||||
@ -173,15 +158,13 @@ export default function TermsAndConditions({ setSettings, setStep }) {
|
|||||||
person or entity.
|
person or entity.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
<CTAButton
|
||||||
|
className="text-dark-text w-full mt-[18px] h-[34px] hover:bg-accent"
|
||||||
|
onClick={handleAccept}
|
||||||
|
>
|
||||||
|
Agree and continue →
|
||||||
|
</CTAButton>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button
|
|
||||||
onClick={handleAccept}
|
|
||||||
type="button"
|
|
||||||
className="mt-8 w-full py-2 text-center text-white hover:bg-primary-button border-none rounded-lg"
|
|
||||||
>
|
|
||||||
Agree and continue →
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -26,7 +26,7 @@ import OrderPlaced from "./OrderPlaced";
|
|||||||
|
|
||||||
const FineTuningSteps = {
|
const FineTuningSteps = {
|
||||||
intro: {
|
intro: {
|
||||||
name: "Introduction to Fine-Tuning",
|
name: "1. Introduction to Fine-Tuning",
|
||||||
next: () => "privacy",
|
next: () => "privacy",
|
||||||
component: ({ settings, setSettings, setStep }) => (
|
component: ({ settings, setSettings, setStep }) => (
|
||||||
<Introduction
|
<Introduction
|
||||||
@ -37,7 +37,7 @@ const FineTuningSteps = {
|
|||||||
),
|
),
|
||||||
},
|
},
|
||||||
privacy: {
|
privacy: {
|
||||||
name: "How your data is handled",
|
name: "2. How your data is handled",
|
||||||
next: () => "tos",
|
next: () => "tos",
|
||||||
component: ({ settings, setSettings, setStep }) => (
|
component: ({ settings, setSettings, setStep }) => (
|
||||||
<PrivacyPolicy
|
<PrivacyPolicy
|
||||||
@ -48,7 +48,7 @@ const FineTuningSteps = {
|
|||||||
),
|
),
|
||||||
},
|
},
|
||||||
tos: {
|
tos: {
|
||||||
name: "Terms of service",
|
name: "3. Terms of service",
|
||||||
next: () => "fulfillment",
|
next: () => "fulfillment",
|
||||||
component: ({ settings, setSettings, setStep }) => (
|
component: ({ settings, setSettings, setStep }) => (
|
||||||
<TermsAndConditions
|
<TermsAndConditions
|
||||||
@ -59,7 +59,7 @@ const FineTuningSteps = {
|
|||||||
),
|
),
|
||||||
},
|
},
|
||||||
fulfillment: {
|
fulfillment: {
|
||||||
name: "Fulfillment terms",
|
name: "4. Fulfillment terms",
|
||||||
next: () => "order-details",
|
next: () => "order-details",
|
||||||
component: ({ settings, setSettings, setStep }) => (
|
component: ({ settings, setSettings, setStep }) => (
|
||||||
<Fulfillment
|
<Fulfillment
|
||||||
@ -70,7 +70,7 @@ const FineTuningSteps = {
|
|||||||
),
|
),
|
||||||
},
|
},
|
||||||
"order-details": {
|
"order-details": {
|
||||||
name: "Model details & information",
|
name: "5. Model details & information",
|
||||||
next: () => "data-selection",
|
next: () => "data-selection",
|
||||||
component: ({ settings, setSettings, setStep }) => (
|
component: ({ settings, setSettings, setStep }) => (
|
||||||
<OrderDetails
|
<OrderDetails
|
||||||
@ -81,7 +81,7 @@ const FineTuningSteps = {
|
|||||||
),
|
),
|
||||||
},
|
},
|
||||||
"data-selection": {
|
"data-selection": {
|
||||||
name: "Data selection",
|
name: "6. Data selection",
|
||||||
next: () => "confirmation",
|
next: () => "confirmation",
|
||||||
component: ({ settings, setSettings, setStep }) => (
|
component: ({ settings, setSettings, setStep }) => (
|
||||||
<DataUpload
|
<DataUpload
|
||||||
@ -92,7 +92,7 @@ const FineTuningSteps = {
|
|||||||
),
|
),
|
||||||
},
|
},
|
||||||
confirmation: {
|
confirmation: {
|
||||||
name: "Review and Submit",
|
name: "7. Review and Submit",
|
||||||
next: () => "done",
|
next: () => "done",
|
||||||
component: ({ settings, setSettings, setStep }) => (
|
component: ({ settings, setSettings, setStep }) => (
|
||||||
<Confirmation
|
<Confirmation
|
||||||
@ -103,7 +103,7 @@ const FineTuningSteps = {
|
|||||||
),
|
),
|
||||||
},
|
},
|
||||||
done: {
|
done: {
|
||||||
name: "Order placed",
|
name: "8. Order placed",
|
||||||
next: () => "done",
|
next: () => "done",
|
||||||
component: ({ settings }) => <OrderPlaced settings={settings} />,
|
component: ({ settings }) => <OrderPlaced settings={settings} />,
|
||||||
},
|
},
|
||||||
@ -133,7 +133,7 @@ export function FineTuningCreationLayout({ setStep, children }) {
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
id="fine-tune-create-order-container"
|
id="fine-tune-create-order-container"
|
||||||
className="w-screen h-screen overflow-y-auto bg-sidebar flex"
|
className="w-screen h-screen overflow-hidden bg-sidebar flex md:mt-0 mt-6"
|
||||||
>
|
>
|
||||||
<Sidebar />
|
<Sidebar />
|
||||||
<div
|
<div
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import FineTuningSteps, { FineTuningCreationLayout } from "./Steps";
|
import FineTuningSteps, { FineTuningCreationLayout } from "./Steps";
|
||||||
import { CheckCircle, Circle, Sparkle } from "@phosphor-icons/react";
|
import { Sparkle } from "@phosphor-icons/react";
|
||||||
import { isMobile } from "react-device-detect";
|
import { isMobile } from "react-device-detect";
|
||||||
|
|
||||||
function SideBarSelection({ setStep, currentStep }) {
|
function SideBarSelection({ setStep, currentStep }) {
|
||||||
const currentIndex = Object.keys(FineTuningSteps).indexOf(currentStep);
|
const currentIndex = Object.keys(FineTuningSteps).indexOf(currentStep);
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`bg-white/5 text-white rounded-xl ${
|
className={`bg-white/5 text-white rounded-xl py-1 px-4 ${
|
||||||
isMobile ? "w-full" : "min-w-[360px] w-fit"
|
isMobile ? "w-full" : "min-w-[360px] w-fit"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
@ -21,28 +21,32 @@ function SideBarSelection({ setStep, currentStep }) {
|
|||||||
<div
|
<div
|
||||||
key={stepKey}
|
key={stepKey}
|
||||||
className={[
|
className={[
|
||||||
"py-3 px-4 flex items-center justify-between transition-all duration-300",
|
"py-3 flex items-center justify-between transition-all duration-300",
|
||||||
isSelected ? "rounded-t-xl" : "",
|
isSelected ? "rounded-t-xl" : "",
|
||||||
isLast ? "" : "border-b border-white/10",
|
isLast ? "" : "border-b border-white/10",
|
||||||
].join(" ")}
|
].join(" ")}
|
||||||
>
|
>
|
||||||
{isDone ? (
|
{isDone || isSelected ? (
|
||||||
<button
|
<button
|
||||||
onClick={() => setStep(stepKey)}
|
onClick={() => setStep(stepKey)}
|
||||||
className="border-none hover:underline text-white/40 text-sm font-light"
|
className="border-none hover:underline text-sm font-medium"
|
||||||
>
|
>
|
||||||
{props.name}
|
{props.name}
|
||||||
</button>
|
</button>
|
||||||
) : (
|
) : (
|
||||||
<div className="text-sm font-light">{props.name}</div>
|
<div className="text-sm text-white/40 font-medium">
|
||||||
|
{props.name}
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="flex items-center gap-x-2">
|
<div className="flex items-center gap-x-2">
|
||||||
{isDone ? (
|
{isDone ? (
|
||||||
<CheckCircle className={`text-green-300`} />
|
<div className="w-[14px] h-[14px] rounded-full border border-[#32D583] flex items-center justify-center">
|
||||||
|
<div className="w-[5.6px] h-[5.6px] rounded-full bg-[#6CE9A6]"></div>
|
||||||
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<Circle
|
<div
|
||||||
className={`text-white-800 ${
|
className={`w-[14px] h-[14px] rounded-full border border-white ${
|
||||||
isSelected ? "animate-pulse" : "opacity-10"
|
isSelected ? "animate-pulse" : "opacity-50"
|
||||||
}`}
|
}`}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@ -63,15 +67,19 @@ export default function FineTuningFlow() {
|
|||||||
return (
|
return (
|
||||||
<FineTuningCreationLayout setStep={setStep}>
|
<FineTuningCreationLayout setStep={setStep}>
|
||||||
{(settings, setSettings, setStep) => (
|
{(settings, setSettings, setStep) => (
|
||||||
<div className="flex-1 flex gap-x-6 p-4 mt-10">
|
<div className="flex-1 flex h-full">
|
||||||
<div className="flex flex-col gap-y-[18px]">
|
<div className="flex flex-col gap-y-[18px] p-4 mt-10 w-[360px] flex-shrink-0">
|
||||||
<div className="text-white flex items-center gap-x-2">
|
<div className="text-white flex items-center gap-x-2">
|
||||||
<Sparkle size={24} />
|
<Sparkle size={24} />
|
||||||
<p className="text-lg font-medium">Custom Fine-Tuned Model</p>
|
<p className="text-lg font-medium">Custom Fine-Tuned Model</p>
|
||||||
</div>
|
</div>
|
||||||
<SideBarSelection setStep={setStep} currentStep={step} />
|
<SideBarSelection setStep={setStep} currentStep={step} />
|
||||||
</div>
|
</div>
|
||||||
{StepPage.component({ settings, setSettings, setStep })}
|
<div className="flex-1 overflow-y-auto p-4 mt-10 pb-[74px] h-screen">
|
||||||
|
<div className=" ml-8">
|
||||||
|
{StepPage.component({ settings, setSettings, setStep })}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</FineTuningCreationLayout>
|
</FineTuningCreationLayout>
|
||||||
|
@ -7,9 +7,11 @@ import CTAButton from "@/components/lib/CTAButton";
|
|||||||
import OpenAiLogo from "@/media/llmprovider/openai.png";
|
import OpenAiLogo from "@/media/llmprovider/openai.png";
|
||||||
import AnythingLLMIcon from "@/media/logo/anything-llm-icon.png";
|
import AnythingLLMIcon from "@/media/logo/anything-llm-icon.png";
|
||||||
import ElevenLabsIcon from "@/media/ttsproviders/elevenlabs.png";
|
import ElevenLabsIcon from "@/media/ttsproviders/elevenlabs.png";
|
||||||
|
import PiperTTSIcon from "@/media/ttsproviders/piper.png";
|
||||||
import BrowserNative from "@/components/TextToSpeech/BrowserNative";
|
import BrowserNative from "@/components/TextToSpeech/BrowserNative";
|
||||||
import OpenAiTTSOptions from "@/components/TextToSpeech/OpenAiOptions";
|
import OpenAiTTSOptions from "@/components/TextToSpeech/OpenAiOptions";
|
||||||
import ElevenLabsTTSOptions from "@/components/TextToSpeech/ElevenLabsOptions";
|
import ElevenLabsTTSOptions from "@/components/TextToSpeech/ElevenLabsOptions";
|
||||||
|
import PiperTTSOptions from "@/components/TextToSpeech/PiperTTSOptions";
|
||||||
|
|
||||||
const PROVIDERS = [
|
const PROVIDERS = [
|
||||||
{
|
{
|
||||||
@ -33,6 +35,13 @@ const PROVIDERS = [
|
|||||||
options: (settings) => <ElevenLabsTTSOptions settings={settings} />,
|
options: (settings) => <ElevenLabsTTSOptions settings={settings} />,
|
||||||
description: "Use ElevenLabs's text to speech voices and technology.",
|
description: "Use ElevenLabs's text to speech voices and technology.",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "PiperTTS",
|
||||||
|
value: "piper_local",
|
||||||
|
logo: PiperTTSIcon,
|
||||||
|
options: (settings) => <PiperTTSOptions settings={settings} />,
|
||||||
|
description: "Run TTS models locally in your browser privately.",
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export default function TextToSpeechProvider({ settings }) {
|
export default function TextToSpeechProvider({ settings }) {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { encode as HTMLEncode } from "he";
|
import { encode as HTMLEncode } from "he";
|
||||||
import markdownIt from "markdown-it";
|
import markdownIt from "markdown-it";
|
||||||
|
import markdownItKatex from "markdown-it-katex";
|
||||||
import hljs from "highlight.js";
|
import hljs from "highlight.js";
|
||||||
import "highlight.js/styles/github-dark-dimmed.min.css";
|
import "highlight.js/styles/github-dark-dimmed.min.css";
|
||||||
import { v4 } from "uuid";
|
import { v4 } from "uuid";
|
||||||
@ -43,7 +44,7 @@ const markdown = markdownIt({
|
|||||||
"</pre></div>"
|
"</pre></div>"
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
});
|
}).use(markdownItKatex);
|
||||||
|
|
||||||
export default function renderMarkdown(text = "") {
|
export default function renderMarkdown(text = "") {
|
||||||
return markdown.render(text);
|
return markdown.render(text);
|
||||||
|
138
frontend/src/utils/piperTTS/index.js
Normal file
138
frontend/src/utils/piperTTS/index.js
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
import showToast from "../toast";
|
||||||
|
|
||||||
|
export default class PiperTTSClient {
|
||||||
|
static _instance;
|
||||||
|
voiceId = "en_US-hfc_female-medium";
|
||||||
|
worker = null;
|
||||||
|
|
||||||
|
constructor({ voiceId } = { voiceId: null }) {
|
||||||
|
if (PiperTTSClient._instance) {
|
||||||
|
this.voiceId = voiceId !== null ? voiceId : this.voiceId;
|
||||||
|
return PiperTTSClient._instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.voiceId = voiceId !== null ? voiceId : this.voiceId;
|
||||||
|
PiperTTSClient._instance = this;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
#getWorker() {
|
||||||
|
if (!this.worker)
|
||||||
|
this.worker = new Worker(new URL("./worker.js", import.meta.url), {
|
||||||
|
type: "module",
|
||||||
|
});
|
||||||
|
return this.worker;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all available voices for a client
|
||||||
|
* @returns {Promise<import("@mintplex-labs/piper-tts-web/dist/types").Voice[]}>}
|
||||||
|
*/
|
||||||
|
static async voices() {
|
||||||
|
const tmpWorker = new Worker(new URL("./worker.js", import.meta.url), {
|
||||||
|
type: "module",
|
||||||
|
});
|
||||||
|
tmpWorker.postMessage({ type: "voices" });
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
let timeout = null;
|
||||||
|
const handleMessage = (event) => {
|
||||||
|
if (event.data.type !== "voices") {
|
||||||
|
console.log("PiperTTSWorker debug event:", event.data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
resolve(event.data.voices);
|
||||||
|
tmpWorker.removeEventListener("message", handleMessage);
|
||||||
|
timeout && clearTimeout(timeout);
|
||||||
|
tmpWorker.terminate();
|
||||||
|
};
|
||||||
|
|
||||||
|
timeout = setTimeout(() => {
|
||||||
|
reject("TTS Worker timed out.");
|
||||||
|
}, 30_000);
|
||||||
|
tmpWorker.addEventListener("message", handleMessage);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static async flush() {
|
||||||
|
const tmpWorker = new Worker(new URL("./worker.js", import.meta.url), {
|
||||||
|
type: "module",
|
||||||
|
});
|
||||||
|
tmpWorker.postMessage({ type: "flush" });
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
let timeout = null;
|
||||||
|
const handleMessage = (event) => {
|
||||||
|
if (event.data.type !== "flush") {
|
||||||
|
console.log("PiperTTSWorker debug event:", event.data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
resolve(event.data.flushed);
|
||||||
|
tmpWorker.removeEventListener("message", handleMessage);
|
||||||
|
timeout && clearTimeout(timeout);
|
||||||
|
tmpWorker.terminate();
|
||||||
|
};
|
||||||
|
|
||||||
|
timeout = setTimeout(() => {
|
||||||
|
reject("TTS Worker timed out.");
|
||||||
|
}, 30_000);
|
||||||
|
tmpWorker.addEventListener("message", handleMessage);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs prediction via webworker so we can get an audio blob back.
|
||||||
|
* @returns {Promise<{blobURL: string|null, error: string|null}>} objectURL blob: type.
|
||||||
|
*/
|
||||||
|
async waitForBlobResponse() {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
let timeout = null;
|
||||||
|
const handleMessage = (event) => {
|
||||||
|
if (event.data.type === "error") {
|
||||||
|
this.worker.removeEventListener("message", handleMessage);
|
||||||
|
timeout && clearTimeout(timeout);
|
||||||
|
return resolve({ blobURL: null, error: event.data.message });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.data.type !== "result") {
|
||||||
|
console.log("PiperTTSWorker debug event:", event.data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
resolve({
|
||||||
|
blobURL: URL.createObjectURL(event.data.audio),
|
||||||
|
error: null,
|
||||||
|
});
|
||||||
|
this.worker.removeEventListener("message", handleMessage);
|
||||||
|
timeout && clearTimeout(timeout);
|
||||||
|
};
|
||||||
|
|
||||||
|
timeout = setTimeout(() => {
|
||||||
|
resolve({ blobURL: null, error: "PiperTTSWorker Worker timed out." });
|
||||||
|
}, 30_000);
|
||||||
|
this.worker.addEventListener("message", handleMessage);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async getAudioBlobForText(textToSpeak, voiceId = null) {
|
||||||
|
const primaryWorker = this.#getWorker();
|
||||||
|
primaryWorker.postMessage({
|
||||||
|
type: "init",
|
||||||
|
text: String(textToSpeak),
|
||||||
|
voiceId: voiceId ?? this.voiceId,
|
||||||
|
// Don't reference WASM because in the docker image
|
||||||
|
// the user will be connected to internet (mostly)
|
||||||
|
// and it bloats the app size on the frontend or app significantly
|
||||||
|
// and running the docker image fully offline is not an intended use-case unlike the app.
|
||||||
|
});
|
||||||
|
|
||||||
|
const { blobURL, error } = await this.waitForBlobResponse();
|
||||||
|
if (!!error) {
|
||||||
|
showToast(
|
||||||
|
`Could not generate voice prediction. Error: ${error}`,
|
||||||
|
"error",
|
||||||
|
{ clear: true }
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return blobURL;
|
||||||
|
}
|
||||||
|
}
|
94
frontend/src/utils/piperTTS/worker.js
Normal file
94
frontend/src/utils/piperTTS/worker.js
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
import * as TTS from "@mintplex-labs/piper-tts-web";
|
||||||
|
|
||||||
|
/** @type {import("@mintplexlabs/piper-web-tts").TtsSession | null} */
|
||||||
|
let PIPER_SESSION = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef PredictionRequest
|
||||||
|
* @property {('init')} type
|
||||||
|
* @property {string} text - the text to inference on
|
||||||
|
* @property {import('@mintplexlabs/piper-web-tts').VoiceId} voiceId - the voiceID key to use.
|
||||||
|
* @property {string|null} baseUrl - the base URL to fetch WASMs from.
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* @typedef PredictionRequestResponse
|
||||||
|
* @property {('result')} type
|
||||||
|
* @property {Blob} audio - the text to inference on
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef VoicesRequest
|
||||||
|
* @property {('voices')} type
|
||||||
|
* @property {string|null} baseUrl - the base URL to fetch WASMs from.
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* @typedef VoicesRequestResponse
|
||||||
|
* @property {('voices')} type
|
||||||
|
* @property {[import("@mintplex-labs/piper-tts-web/dist/types")['Voice']]} voices - available voices in array
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef FlushRequest
|
||||||
|
* @property {('flush')} type
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* @typedef FlushRequestResponse
|
||||||
|
* @property {('flush')} type
|
||||||
|
* @property {true} flushed
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Web worker for generating client-side PiperTTS predictions
|
||||||
|
* @param {MessageEvent<PredictionRequest | VoicesRequest | FlushRequest>} event - The event object containing the prediction request
|
||||||
|
* @returns {Promise<PredictionRequestResponse|VoicesRequestResponse|FlushRequestResponse>}
|
||||||
|
*/
|
||||||
|
async function main(event) {
|
||||||
|
if (event.data.type === "voices") {
|
||||||
|
const stored = await TTS.stored();
|
||||||
|
const voices = await TTS.voices();
|
||||||
|
voices.forEach((voice) => (voice.is_stored = stored.includes(voice.key)));
|
||||||
|
|
||||||
|
self.postMessage({ type: "voices", voices });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.data.type === "flush") {
|
||||||
|
await TTS.flush();
|
||||||
|
self.postMessage({ type: "flush", flushed: true });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.data?.type !== "init") return;
|
||||||
|
if (!PIPER_SESSION) {
|
||||||
|
PIPER_SESSION = new TTS.TtsSession({
|
||||||
|
voiceId: event.data.voiceId,
|
||||||
|
progress: (e) => self.postMessage(JSON.stringify(e)),
|
||||||
|
logger: (msg) => self.postMessage(msg),
|
||||||
|
...(!!event.data.baseUrl
|
||||||
|
? {
|
||||||
|
wasmPaths: {
|
||||||
|
onnxWasm: `${event.data.baseUrl}/piper/ort/`,
|
||||||
|
piperData: `${event.data.baseUrl}/piper/piper_phonemize.data`,
|
||||||
|
piperWasm: `${event.data.baseUrl}/piper/piper_phonemize.wasm`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
: {}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.data.voiceId && PIPER_SESSION.voiceId !== event.data.voiceId)
|
||||||
|
PIPER_SESSION.voiceId = event.data.voiceId;
|
||||||
|
|
||||||
|
PIPER_SESSION.predict(event.data.text)
|
||||||
|
.then((res) => {
|
||||||
|
if (res instanceof Blob) {
|
||||||
|
self.postMessage({ type: "result", audio: res });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
self.postMessage({ type: "error", message: error.message, error }); // Will be an error.
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
self.addEventListener("message", main);
|
@ -86,7 +86,8 @@ export default {
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
animation: {
|
animation: {
|
||||||
sweep: "sweep 0.5s ease-in-out"
|
sweep: "sweep 0.5s ease-in-out",
|
||||||
|
"pulse-glow": "pulse-glow 1.5s infinite"
|
||||||
},
|
},
|
||||||
keyframes: {
|
keyframes: {
|
||||||
sweep: {
|
sweep: {
|
||||||
@ -100,6 +101,26 @@ export default {
|
|||||||
fadeOut: {
|
fadeOut: {
|
||||||
"0%": { opacity: 1 },
|
"0%": { opacity: 1 },
|
||||||
"100%": { opacity: 0 }
|
"100%": { opacity: 0 }
|
||||||
|
},
|
||||||
|
"pulse-glow": {
|
||||||
|
"0%": {
|
||||||
|
opacity: 1,
|
||||||
|
transform: "scale(1)",
|
||||||
|
boxShadow: "0 0 0 rgba(255, 255, 255, 0.0)",
|
||||||
|
backgroundColor: "rgba(255, 255, 255, 0.0)"
|
||||||
|
},
|
||||||
|
"50%": {
|
||||||
|
opacity: 1,
|
||||||
|
transform: "scale(1.1)",
|
||||||
|
boxShadow: "0 0 15px rgba(255, 255, 255, 0.2)",
|
||||||
|
backgroundColor: "rgba(255, 255, 255, 0.1)"
|
||||||
|
},
|
||||||
|
"100%": {
|
||||||
|
opacity: 1,
|
||||||
|
transform: "scale(1)",
|
||||||
|
boxShadow: "0 0 0 rgba(255, 255, 255, 0.0)",
|
||||||
|
backgroundColor: "rgba(255, 255, 255, 0.0)"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,14 @@ dns.setDefaultResultOrder("verbatim")
|
|||||||
|
|
||||||
// https://vitejs.dev/config/
|
// https://vitejs.dev/config/
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
|
assetsInclude: [
|
||||||
|
'./public/piper/ort-wasm-simd-threaded.wasm',
|
||||||
|
'./public/piper/piper_phonemize.wasm',
|
||||||
|
'./public/piper/piper_phonemize.data',
|
||||||
|
],
|
||||||
|
worker: {
|
||||||
|
format: 'es'
|
||||||
|
},
|
||||||
server: {
|
server: {
|
||||||
port: 3000,
|
port: 3000,
|
||||||
host: "localhost"
|
host: "localhost"
|
||||||
@ -60,7 +68,7 @@ export default defineConfig({
|
|||||||
},
|
},
|
||||||
external: [
|
external: [
|
||||||
// Reduces transformation time by 50% and we don't even use this variant, so we can ignore.
|
// Reduces transformation time by 50% and we don't even use this variant, so we can ignore.
|
||||||
/@phosphor-icons\/react\/dist\/ssr/
|
/@phosphor-icons\/react\/dist\/ssr/,
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
commonjsOptions: {
|
commonjsOptions: {
|
||||||
@ -68,6 +76,7 @@ export default defineConfig({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
optimizeDeps: {
|
optimizeDeps: {
|
||||||
|
include: ["@mintplex-labs/piper-tts-web"],
|
||||||
esbuildOptions: {
|
esbuildOptions: {
|
||||||
define: {
|
define: {
|
||||||
global: "globalThis"
|
global: "globalThis"
|
||||||
|
@ -496,6 +496,11 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@microsoft/fetch-event-source/-/fetch-event-source-2.0.1.tgz#9ceecc94b49fbaa15666e38ae8587f64acce007d"
|
resolved "https://registry.yarnpkg.com/@microsoft/fetch-event-source/-/fetch-event-source-2.0.1.tgz#9ceecc94b49fbaa15666e38ae8587f64acce007d"
|
||||||
integrity sha512-W6CLUJ2eBMw3Rec70qrsEW0jOm/3twwJv21mrmj2yORiaVmVYGS4sSS5yUwvQc1ZlDLYGPnClVWmUUMagKNsfA==
|
integrity sha512-W6CLUJ2eBMw3Rec70qrsEW0jOm/3twwJv21mrmj2yORiaVmVYGS4sSS5yUwvQc1ZlDLYGPnClVWmUUMagKNsfA==
|
||||||
|
|
||||||
|
"@mintplex-labs/piper-tts-web@^1.0.4":
|
||||||
|
version "1.0.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/@mintplex-labs/piper-tts-web/-/piper-tts-web-1.0.4.tgz#016b196fa86dc8b616691dd381f3ca1939196444"
|
||||||
|
integrity sha512-Y24X+CJaGXoY5HFPSstHvJI6408OAtw3Pmq2OIYwpRpcwLLbgadWg8l1ODHNkgpB0Ps5fS9PAAQB60fHA3Bdag==
|
||||||
|
|
||||||
"@nodelib/fs.scandir@2.1.5":
|
"@nodelib/fs.scandir@2.1.5":
|
||||||
version "2.1.5"
|
version "2.1.5"
|
||||||
resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5"
|
resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5"
|
||||||
@ -532,6 +537,59 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@pkgr/core/-/core-0.1.1.tgz#1ec17e2edbec25c8306d424ecfbf13c7de1aaa31"
|
resolved "https://registry.yarnpkg.com/@pkgr/core/-/core-0.1.1.tgz#1ec17e2edbec25c8306d424ecfbf13c7de1aaa31"
|
||||||
integrity sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==
|
integrity sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==
|
||||||
|
|
||||||
|
"@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2":
|
||||||
|
version "1.1.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf"
|
||||||
|
integrity sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==
|
||||||
|
|
||||||
|
"@protobufjs/base64@^1.1.2":
|
||||||
|
version "1.1.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@protobufjs/base64/-/base64-1.1.2.tgz#4c85730e59b9a1f1f349047dbf24296034bb2735"
|
||||||
|
integrity sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==
|
||||||
|
|
||||||
|
"@protobufjs/codegen@^2.0.4":
|
||||||
|
version "2.0.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/@protobufjs/codegen/-/codegen-2.0.4.tgz#7ef37f0d010fb028ad1ad59722e506d9262815cb"
|
||||||
|
integrity sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==
|
||||||
|
|
||||||
|
"@protobufjs/eventemitter@^1.1.0":
|
||||||
|
version "1.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz#355cbc98bafad5978f9ed095f397621f1d066b70"
|
||||||
|
integrity sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==
|
||||||
|
|
||||||
|
"@protobufjs/fetch@^1.1.0":
|
||||||
|
version "1.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@protobufjs/fetch/-/fetch-1.1.0.tgz#ba99fb598614af65700c1619ff06d454b0d84c45"
|
||||||
|
integrity sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==
|
||||||
|
dependencies:
|
||||||
|
"@protobufjs/aspromise" "^1.1.1"
|
||||||
|
"@protobufjs/inquire" "^1.1.0"
|
||||||
|
|
||||||
|
"@protobufjs/float@^1.0.2":
|
||||||
|
version "1.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@protobufjs/float/-/float-1.0.2.tgz#5e9e1abdcb73fc0a7cb8b291df78c8cbd97b87d1"
|
||||||
|
integrity sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==
|
||||||
|
|
||||||
|
"@protobufjs/inquire@^1.1.0":
|
||||||
|
version "1.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@protobufjs/inquire/-/inquire-1.1.0.tgz#ff200e3e7cf2429e2dcafc1140828e8cc638f089"
|
||||||
|
integrity sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==
|
||||||
|
|
||||||
|
"@protobufjs/path@^1.1.2":
|
||||||
|
version "1.1.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@protobufjs/path/-/path-1.1.2.tgz#6cc2b20c5c9ad6ad0dccfd21ca7673d8d7fbf68d"
|
||||||
|
integrity sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==
|
||||||
|
|
||||||
|
"@protobufjs/pool@^1.1.0":
|
||||||
|
version "1.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@protobufjs/pool/-/pool-1.1.0.tgz#09fd15f2d6d3abfa9b65bc366506d6ad7846ff54"
|
||||||
|
integrity sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==
|
||||||
|
|
||||||
|
"@protobufjs/utf8@^1.1.0":
|
||||||
|
version "1.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570"
|
||||||
|
integrity sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==
|
||||||
|
|
||||||
"@remix-run/router@1.18.0":
|
"@remix-run/router@1.18.0":
|
||||||
version "1.18.0"
|
version "1.18.0"
|
||||||
resolved "https://registry.yarnpkg.com/@remix-run/router/-/router-1.18.0.tgz#20b033d1f542a100c1d57cfd18ecf442d1784732"
|
resolved "https://registry.yarnpkg.com/@remix-run/router/-/router-1.18.0.tgz#20b033d1f542a100c1d57cfd18ecf442d1784732"
|
||||||
@ -652,6 +710,13 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.11.tgz#56588b17ae8f50c53983a524fc3cc47437969d64"
|
resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.11.tgz#56588b17ae8f50c53983a524fc3cc47437969d64"
|
||||||
integrity sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==
|
integrity sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==
|
||||||
|
|
||||||
|
"@types/node@>=13.7.0":
|
||||||
|
version "22.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/node/-/node-22.1.0.tgz#6d6adc648b5e03f0e83c78dc788c2b037d0ad94b"
|
||||||
|
integrity sha512-AOmuRF0R2/5j1knA3c6G3HOk523Ga+l+ZXltX8SF1+5oqcXijjfTd8fY3XRZqSihEu9XhtQnKYLmkFaoxgsJHw==
|
||||||
|
dependencies:
|
||||||
|
undici-types "~6.13.0"
|
||||||
|
|
||||||
"@types/prop-types@*":
|
"@types/prop-types@*":
|
||||||
version "15.7.12"
|
version "15.7.12"
|
||||||
resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.12.tgz#12bb1e2be27293c1406acb6af1c3f3a1481d98c6"
|
resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.12.tgz#12bb1e2be27293c1406acb6af1c3f3a1481d98c6"
|
||||||
@ -1729,6 +1794,11 @@ flat-cache@^3.0.4:
|
|||||||
keyv "^4.5.3"
|
keyv "^4.5.3"
|
||||||
rimraf "^3.0.2"
|
rimraf "^3.0.2"
|
||||||
|
|
||||||
|
flatbuffers@^1.12.0:
|
||||||
|
version "1.12.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/flatbuffers/-/flatbuffers-1.12.0.tgz#72e87d1726cb1b216e839ef02658aa87dcef68aa"
|
||||||
|
integrity sha512-c7CZADjRcl6j0PlvFy0ZqXQ67qSEZfrVPynmnL+2zPc+NtMvrF8Y0QceMo7QqnSPc7+uWjUIAbvCQ5WIKlMVdQ==
|
||||||
|
|
||||||
flatted@^3.2.9:
|
flatted@^3.2.9:
|
||||||
version "3.3.1"
|
version "3.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.1.tgz#21db470729a6734d4997002f439cb308987f567a"
|
resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.1.tgz#21db470729a6734d4997002f439cb308987f567a"
|
||||||
@ -1898,6 +1968,11 @@ graphemer@^1.4.0:
|
|||||||
resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6"
|
resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6"
|
||||||
integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==
|
integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==
|
||||||
|
|
||||||
|
guid-typescript@^1.0.9:
|
||||||
|
version "1.0.9"
|
||||||
|
resolved "https://registry.yarnpkg.com/guid-typescript/-/guid-typescript-1.0.9.tgz#e35f77003535b0297ea08548f5ace6adb1480ddc"
|
||||||
|
integrity sha512-Y8T4vYhEfwJOTbouREvG+3XDsjr8E3kIr7uf+JZ0BYloFsttiHU0WfvANVsR7TxNUJa/WpCnw/Ino/p+DeBhBQ==
|
||||||
|
|
||||||
has-bigints@^1.0.1, has-bigints@^1.0.2:
|
has-bigints@^1.0.1, has-bigints@^1.0.2:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa"
|
resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa"
|
||||||
@ -2347,6 +2422,13 @@ json5@^2.2.3:
|
|||||||
object.assign "^4.1.4"
|
object.assign "^4.1.4"
|
||||||
object.values "^1.1.6"
|
object.values "^1.1.6"
|
||||||
|
|
||||||
|
katex@^0.6.0:
|
||||||
|
version "0.6.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/katex/-/katex-0.6.0.tgz#12418e09121c05c92041b6b3b9fb6bab213cb6f3"
|
||||||
|
integrity sha512-rS4mY3SvHYg5LtQV6RBcK0if7ur6plyEukAOV+jGGPqFImuzu8fHL6M752iBmRGoUyF0bhZbAPoezehn7xYksA==
|
||||||
|
dependencies:
|
||||||
|
match-at "^0.1.0"
|
||||||
|
|
||||||
keyv@^4.5.3:
|
keyv@^4.5.3:
|
||||||
version "4.5.4"
|
version "4.5.4"
|
||||||
resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93"
|
resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93"
|
||||||
@ -2406,6 +2488,11 @@ lodash@^4.17.21:
|
|||||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
|
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
|
||||||
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
|
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
|
||||||
|
|
||||||
|
long@^5.0.0, long@^5.2.3:
|
||||||
|
version "5.2.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/long/-/long-5.2.3.tgz#a3ba97f3877cf1d778eccbcb048525ebb77499e1"
|
||||||
|
integrity sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==
|
||||||
|
|
||||||
loose-envify@^1.1.0, loose-envify@^1.4.0:
|
loose-envify@^1.1.0, loose-envify@^1.4.0:
|
||||||
version "1.4.0"
|
version "1.4.0"
|
||||||
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
|
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
|
||||||
@ -2425,6 +2512,13 @@ lru-cache@^5.1.1:
|
|||||||
dependencies:
|
dependencies:
|
||||||
yallist "^3.0.2"
|
yallist "^3.0.2"
|
||||||
|
|
||||||
|
markdown-it-katex@^2.0.3:
|
||||||
|
version "2.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/markdown-it-katex/-/markdown-it-katex-2.0.3.tgz#d7b86a1aea0b9d6496fab4e7919a18fdef589c39"
|
||||||
|
integrity sha512-nUkkMtRWeg7OpdflamflE/Ho/pWl64Lk9wNBKOmaj33XkQdumhXAIYhI0WO03GeiycPCsxbmX536V5NEXpC3Ng==
|
||||||
|
dependencies:
|
||||||
|
katex "^0.6.0"
|
||||||
|
|
||||||
markdown-it@^13.0.1:
|
markdown-it@^13.0.1:
|
||||||
version "13.0.2"
|
version "13.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-13.0.2.tgz#1bc22e23379a6952e5d56217fbed881e0c94d536"
|
resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-13.0.2.tgz#1bc22e23379a6952e5d56217fbed881e0c94d536"
|
||||||
@ -2436,6 +2530,11 @@ markdown-it@^13.0.1:
|
|||||||
mdurl "^1.0.1"
|
mdurl "^1.0.1"
|
||||||
uc.micro "^1.0.5"
|
uc.micro "^1.0.5"
|
||||||
|
|
||||||
|
match-at@^0.1.0:
|
||||||
|
version "0.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/match-at/-/match-at-0.1.1.tgz#25d040d291777704d5e6556bbb79230ec2de0540"
|
||||||
|
integrity sha512-h4Yd392z9mST+dzc+yjuybOGFNOZjmXIPKWjxBd1Bb23r4SmDOsk2NYCU2BMUBGbSpZqwVsZYNq26QS3xfaT3Q==
|
||||||
|
|
||||||
mdurl@^1.0.1:
|
mdurl@^1.0.1:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e"
|
resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e"
|
||||||
@ -2592,6 +2691,23 @@ once@^1.3.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
wrappy "1"
|
wrappy "1"
|
||||||
|
|
||||||
|
onnxruntime-common@1.18.0:
|
||||||
|
version "1.18.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/onnxruntime-common/-/onnxruntime-common-1.18.0.tgz#b904dc6ff134e7f21a3eab702fac17538f59e116"
|
||||||
|
integrity sha512-lufrSzX6QdKrktAELG5x5VkBpapbCeS3dQwrXbN0eD9rHvU0yAWl7Ztju9FvgAKWvwd/teEKJNj3OwM6eTZh3Q==
|
||||||
|
|
||||||
|
onnxruntime-web@^1.18.0:
|
||||||
|
version "1.18.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/onnxruntime-web/-/onnxruntime-web-1.18.0.tgz#cd46268d9472f89697da0a3282f13129f0acbfa0"
|
||||||
|
integrity sha512-o1UKj4ABIj1gmG7ae0RKJ3/GT+3yoF0RRpfDfeoe0huzRW4FDRLfbkDETmdFAvnJEXuYDE0YT+hhkia0352StQ==
|
||||||
|
dependencies:
|
||||||
|
flatbuffers "^1.12.0"
|
||||||
|
guid-typescript "^1.0.9"
|
||||||
|
long "^5.2.3"
|
||||||
|
onnxruntime-common "1.18.0"
|
||||||
|
platform "^1.3.6"
|
||||||
|
protobufjs "^7.2.4"
|
||||||
|
|
||||||
open@^8.4.0:
|
open@^8.4.0:
|
||||||
version "8.4.2"
|
version "8.4.2"
|
||||||
resolved "https://registry.yarnpkg.com/open/-/open-8.4.2.tgz#5b5ffe2a8f793dcd2aad73e550cb87b59cb084f9"
|
resolved "https://registry.yarnpkg.com/open/-/open-8.4.2.tgz#5b5ffe2a8f793dcd2aad73e550cb87b59cb084f9"
|
||||||
@ -2694,6 +2810,11 @@ pirates@^4.0.1:
|
|||||||
resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.6.tgz#3018ae32ecfcff6c29ba2267cbf21166ac1f36b9"
|
resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.6.tgz#3018ae32ecfcff6c29ba2267cbf21166ac1f36b9"
|
||||||
integrity sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==
|
integrity sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==
|
||||||
|
|
||||||
|
platform@^1.3.6:
|
||||||
|
version "1.3.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/platform/-/platform-1.3.6.tgz#48b4ce983164b209c2d45a107adb31f473a6e7a7"
|
||||||
|
integrity sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==
|
||||||
|
|
||||||
pluralize@^8.0.0:
|
pluralize@^8.0.0:
|
||||||
version "8.0.0"
|
version "8.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-8.0.0.tgz#1a6fa16a38d12a1901e0320fa017051c539ce3b1"
|
resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-8.0.0.tgz#1a6fa16a38d12a1901e0320fa017051c539ce3b1"
|
||||||
@ -2783,6 +2904,24 @@ prop-types@^15.6.2, prop-types@^15.8.1:
|
|||||||
object-assign "^4.1.1"
|
object-assign "^4.1.1"
|
||||||
react-is "^16.13.1"
|
react-is "^16.13.1"
|
||||||
|
|
||||||
|
protobufjs@^7.2.4:
|
||||||
|
version "7.3.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-7.3.2.tgz#60f3b7624968868f6f739430cfbc8c9370e26df4"
|
||||||
|
integrity sha512-RXyHaACeqXeqAKGLDl68rQKbmObRsTIn4TYVUUug1KfS47YWCo5MacGITEryugIgZqORCvJWEk4l449POg5Txg==
|
||||||
|
dependencies:
|
||||||
|
"@protobufjs/aspromise" "^1.1.2"
|
||||||
|
"@protobufjs/base64" "^1.1.2"
|
||||||
|
"@protobufjs/codegen" "^2.0.4"
|
||||||
|
"@protobufjs/eventemitter" "^1.1.0"
|
||||||
|
"@protobufjs/fetch" "^1.1.0"
|
||||||
|
"@protobufjs/float" "^1.0.2"
|
||||||
|
"@protobufjs/inquire" "^1.1.0"
|
||||||
|
"@protobufjs/path" "^1.1.2"
|
||||||
|
"@protobufjs/pool" "^1.1.0"
|
||||||
|
"@protobufjs/utf8" "^1.1.0"
|
||||||
|
"@types/node" ">=13.7.0"
|
||||||
|
long "^5.0.0"
|
||||||
|
|
||||||
punycode@^2.1.0:
|
punycode@^2.1.0:
|
||||||
version "2.3.1"
|
version "2.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5"
|
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5"
|
||||||
@ -3593,6 +3732,11 @@ unbox-primitive@^1.0.2:
|
|||||||
has-symbols "^1.0.3"
|
has-symbols "^1.0.3"
|
||||||
which-boxed-primitive "^1.0.2"
|
which-boxed-primitive "^1.0.2"
|
||||||
|
|
||||||
|
undici-types@~6.13.0:
|
||||||
|
version "6.13.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.13.0.tgz#e3e79220ab8c81ed1496b5812471afd7cf075ea5"
|
||||||
|
integrity sha512-xtFJHudx8S2DSoujjMd1WeWvn7KKWFRESZTMeL1RptAYERu29D6jphMjjY+vn96jvN3kVPDNxU/E13VTaXj6jg==
|
||||||
|
|
||||||
update-browserslist-db@^1.1.0:
|
update-browserslist-db@^1.1.0:
|
||||||
version "1.1.0"
|
version "1.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz#7ca61c0d8650766090728046e416a8cde682859e"
|
resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz#7ca61c0d8650766090728046e416a8cde682859e"
|
||||||
|
230
server/endpoints/api/embed/index.js
Normal file
230
server/endpoints/api/embed/index.js
Normal file
@ -0,0 +1,230 @@
|
|||||||
|
const { EmbedConfig } = require("../../../models/embedConfig");
|
||||||
|
const { EmbedChats } = require("../../../models/embedChats");
|
||||||
|
const { validApiKey } = require("../../../utils/middleware/validApiKey");
|
||||||
|
|
||||||
|
function apiEmbedEndpoints(app) {
|
||||||
|
if (!app) return;
|
||||||
|
|
||||||
|
app.get("/v1/embed", [validApiKey], async (request, response) => {
|
||||||
|
/*
|
||||||
|
#swagger.tags = ['Embed']
|
||||||
|
#swagger.description = 'List all active embeds'
|
||||||
|
#swagger.responses[200] = {
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: {
|
||||||
|
type: 'object',
|
||||||
|
example: {
|
||||||
|
embeds: [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"uuid": "embed-uuid-1",
|
||||||
|
"enabled": true,
|
||||||
|
"chat_mode": "query",
|
||||||
|
"createdAt": "2023-04-01T12:00:00Z",
|
||||||
|
"workspace": {
|
||||||
|
"id": 1,
|
||||||
|
"name": "Workspace 1"
|
||||||
|
},
|
||||||
|
"chat_count": 10
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"uuid": "embed-uuid-2",
|
||||||
|
"enabled": false,
|
||||||
|
"chat_mode": "chat",
|
||||||
|
"createdAt": "2023-04-02T14:30:00Z",
|
||||||
|
"workspace": {
|
||||||
|
"id": 1,
|
||||||
|
"name": "Workspace 1"
|
||||||
|
},
|
||||||
|
"chat_count": 10
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#swagger.responses[403] = {
|
||||||
|
schema: {
|
||||||
|
"$ref": "#/definitions/InvalidAPIKey"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
try {
|
||||||
|
const embeds = await EmbedConfig.whereWithWorkspace();
|
||||||
|
const filteredEmbeds = embeds.map((embed) => ({
|
||||||
|
id: embed.id,
|
||||||
|
uuid: embed.uuid,
|
||||||
|
enabled: embed.enabled,
|
||||||
|
chat_mode: embed.chat_mode,
|
||||||
|
createdAt: embed.createdAt,
|
||||||
|
workspace: {
|
||||||
|
id: embed.workspace.id,
|
||||||
|
name: embed.workspace.name,
|
||||||
|
},
|
||||||
|
chat_count: embed._count.embed_chats,
|
||||||
|
}));
|
||||||
|
response.status(200).json({ embeds: filteredEmbeds });
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e.message, e);
|
||||||
|
response.sendStatus(500).end();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get(
|
||||||
|
"/v1/embed/:embedUuid/chats",
|
||||||
|
[validApiKey],
|
||||||
|
async (request, response) => {
|
||||||
|
/*
|
||||||
|
#swagger.tags = ['Embed']
|
||||||
|
#swagger.description = 'Get all chats for a specific embed'
|
||||||
|
#swagger.parameters['embedUuid'] = {
|
||||||
|
in: 'path',
|
||||||
|
description: 'UUID of the embed',
|
||||||
|
required: true,
|
||||||
|
type: 'string'
|
||||||
|
}
|
||||||
|
#swagger.responses[200] = {
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: {
|
||||||
|
type: 'object',
|
||||||
|
example: {
|
||||||
|
chats: [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"session_id": "session-uuid-1",
|
||||||
|
"prompt": "Hello",
|
||||||
|
"response": "Hi there!",
|
||||||
|
"createdAt": "2023-04-01T12:00:00Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"session_id": "session-uuid-2",
|
||||||
|
"prompt": "How are you?",
|
||||||
|
"response": "I'm doing well, thank you!",
|
||||||
|
"createdAt": "2023-04-02T14:30:00Z"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#swagger.responses[403] = {
|
||||||
|
schema: {
|
||||||
|
"$ref": "#/definitions/InvalidAPIKey"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#swagger.responses[404] = {
|
||||||
|
description: "Embed not found",
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
try {
|
||||||
|
const { embedUuid } = request.params;
|
||||||
|
const embed = await EmbedConfig.get({ uuid: String(embedUuid) });
|
||||||
|
if (!embed) {
|
||||||
|
return response.status(404).json({ error: "Embed not found" });
|
||||||
|
}
|
||||||
|
|
||||||
|
const chats = await EmbedChats.where({ embed_id: embed.id });
|
||||||
|
const formattedChats = chats.map((chat) => ({
|
||||||
|
id: chat.id,
|
||||||
|
session_id: chat.session_id,
|
||||||
|
prompt: chat.prompt,
|
||||||
|
response: chat.response,
|
||||||
|
createdAt: chat.createdAt,
|
||||||
|
}));
|
||||||
|
|
||||||
|
response.status(200).json({ chats: formattedChats });
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e.message, e);
|
||||||
|
response.sendStatus(500).end();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
app.get(
|
||||||
|
"/v1/embed/:embedUuid/chats/:sessionUuid",
|
||||||
|
[validApiKey],
|
||||||
|
async (request, response) => {
|
||||||
|
/*
|
||||||
|
#swagger.tags = ['Embed']
|
||||||
|
#swagger.description = 'Get chats for a specific embed and session'
|
||||||
|
#swagger.parameters['embedUuid'] = {
|
||||||
|
in: 'path',
|
||||||
|
description: 'UUID of the embed',
|
||||||
|
required: true,
|
||||||
|
type: 'string'
|
||||||
|
}
|
||||||
|
#swagger.parameters['sessionUuid'] = {
|
||||||
|
in: 'path',
|
||||||
|
description: 'UUID of the session',
|
||||||
|
required: true,
|
||||||
|
type: 'string'
|
||||||
|
}
|
||||||
|
#swagger.responses[200] = {
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: {
|
||||||
|
type: 'object',
|
||||||
|
example: {
|
||||||
|
chats: [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"prompt": "Hello",
|
||||||
|
"response": "Hi there!",
|
||||||
|
"createdAt": "2023-04-01T12:00:00Z"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#swagger.responses[403] = {
|
||||||
|
schema: {
|
||||||
|
"$ref": "#/definitions/InvalidAPIKey"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#swagger.responses[404] = {
|
||||||
|
description: "Embed or session not found",
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
try {
|
||||||
|
const { embedUuid, sessionUuid } = request.params;
|
||||||
|
const embed = await EmbedConfig.get({ uuid: String(embedUuid) });
|
||||||
|
if (!embed) {
|
||||||
|
return response.status(404).json({ error: "Embed not found" });
|
||||||
|
}
|
||||||
|
|
||||||
|
const chats = await EmbedChats.where({
|
||||||
|
embed_id: embed.id,
|
||||||
|
session_id: String(sessionUuid),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!chats || chats.length === 0) {
|
||||||
|
return response
|
||||||
|
.status(404)
|
||||||
|
.json({ error: "No chats found for this session" });
|
||||||
|
}
|
||||||
|
|
||||||
|
const formattedChats = chats.map((chat) => ({
|
||||||
|
id: chat.id,
|
||||||
|
prompt: chat.prompt,
|
||||||
|
response: chat.response,
|
||||||
|
createdAt: chat.createdAt,
|
||||||
|
}));
|
||||||
|
|
||||||
|
response.status(200).json({ chats: formattedChats });
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e.message, e);
|
||||||
|
response.sendStatus(500).end();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { apiEmbedEndpoints };
|
@ -7,6 +7,7 @@ const { apiWorkspaceEndpoints } = require("./workspace");
|
|||||||
const { apiWorkspaceThreadEndpoints } = require("./workspaceThread");
|
const { apiWorkspaceThreadEndpoints } = require("./workspaceThread");
|
||||||
const { apiUserManagementEndpoints } = require("./userManagement");
|
const { apiUserManagementEndpoints } = require("./userManagement");
|
||||||
const { apiOpenAICompatibleEndpoints } = require("./openai");
|
const { apiOpenAICompatibleEndpoints } = require("./openai");
|
||||||
|
const { apiEmbedEndpoints } = require("./embed");
|
||||||
|
|
||||||
// All endpoints must be documented and pass through the validApiKey Middleware.
|
// All endpoints must be documented and pass through the validApiKey Middleware.
|
||||||
// How to JSDoc an endpoint
|
// How to JSDoc an endpoint
|
||||||
@ -22,6 +23,7 @@ function developerEndpoints(app, router) {
|
|||||||
apiWorkspaceThreadEndpoints(router);
|
apiWorkspaceThreadEndpoints(router);
|
||||||
apiUserManagementEndpoints(router);
|
apiUserManagementEndpoints(router);
|
||||||
apiOpenAICompatibleEndpoints(router);
|
apiOpenAICompatibleEndpoints(router);
|
||||||
|
apiEmbedEndpoints(router);
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = { developerEndpoints };
|
module.exports = { developerEndpoints };
|
||||||
|
@ -154,6 +154,7 @@ function apiOpenAICompatibleEndpoints(app) {
|
|||||||
workspace.chatProvider ?? process.env.LLM_PROVIDER ?? "openai",
|
workspace.chatProvider ?? process.env.LLM_PROVIDER ?? "openai",
|
||||||
Embedder: process.env.EMBEDDING_ENGINE || "inherit",
|
Embedder: process.env.EMBEDDING_ENGINE || "inherit",
|
||||||
VectorDbSelection: process.env.VECTOR_DB || "lancedb",
|
VectorDbSelection: process.env.VECTOR_DB || "lancedb",
|
||||||
|
TTSSelection: process.env.TTS_PROVIDER || "native",
|
||||||
});
|
});
|
||||||
await EventLogs.logEvent("api_sent_chat", {
|
await EventLogs.logEvent("api_sent_chat", {
|
||||||
workspaceName: workspace?.name,
|
workspaceName: workspace?.name,
|
||||||
@ -180,6 +181,7 @@ function apiOpenAICompatibleEndpoints(app) {
|
|||||||
LLMSelection: process.env.LLM_PROVIDER || "openai",
|
LLMSelection: process.env.LLM_PROVIDER || "openai",
|
||||||
Embedder: process.env.EMBEDDING_ENGINE || "inherit",
|
Embedder: process.env.EMBEDDING_ENGINE || "inherit",
|
||||||
VectorDbSelection: process.env.VECTOR_DB || "lancedb",
|
VectorDbSelection: process.env.VECTOR_DB || "lancedb",
|
||||||
|
TTSSelection: process.env.TTS_PROVIDER || "native",
|
||||||
});
|
});
|
||||||
await EventLogs.logEvent("api_sent_chat", {
|
await EventLogs.logEvent("api_sent_chat", {
|
||||||
workspaceName: workspace?.name,
|
workspaceName: workspace?.name,
|
||||||
|
@ -73,6 +73,7 @@ function apiWorkspaceEndpoints(app) {
|
|||||||
LLMSelection: process.env.LLM_PROVIDER || "openai",
|
LLMSelection: process.env.LLM_PROVIDER || "openai",
|
||||||
Embedder: process.env.EMBEDDING_ENGINE || "inherit",
|
Embedder: process.env.EMBEDDING_ENGINE || "inherit",
|
||||||
VectorDbSelection: process.env.VECTOR_DB || "lancedb",
|
VectorDbSelection: process.env.VECTOR_DB || "lancedb",
|
||||||
|
TTSSelection: process.env.TTS_PROVIDER || "native",
|
||||||
});
|
});
|
||||||
await EventLogs.logEvent("api_workspace_created", {
|
await EventLogs.logEvent("api_workspace_created", {
|
||||||
workspaceName: workspace?.name || "Unknown Workspace",
|
workspaceName: workspace?.name || "Unknown Workspace",
|
||||||
@ -622,6 +623,7 @@ function apiWorkspaceEndpoints(app) {
|
|||||||
LLMSelection: process.env.LLM_PROVIDER || "openai",
|
LLMSelection: process.env.LLM_PROVIDER || "openai",
|
||||||
Embedder: process.env.EMBEDDING_ENGINE || "inherit",
|
Embedder: process.env.EMBEDDING_ENGINE || "inherit",
|
||||||
VectorDbSelection: process.env.VECTOR_DB || "lancedb",
|
VectorDbSelection: process.env.VECTOR_DB || "lancedb",
|
||||||
|
TTSSelection: process.env.TTS_PROVIDER || "native",
|
||||||
});
|
});
|
||||||
await EventLogs.logEvent("api_sent_chat", {
|
await EventLogs.logEvent("api_sent_chat", {
|
||||||
workspaceName: workspace?.name,
|
workspaceName: workspace?.name,
|
||||||
@ -745,6 +747,7 @@ function apiWorkspaceEndpoints(app) {
|
|||||||
LLMSelection: process.env.LLM_PROVIDER || "openai",
|
LLMSelection: process.env.LLM_PROVIDER || "openai",
|
||||||
Embedder: process.env.EMBEDDING_ENGINE || "inherit",
|
Embedder: process.env.EMBEDDING_ENGINE || "inherit",
|
||||||
VectorDbSelection: process.env.VECTOR_DB || "lancedb",
|
VectorDbSelection: process.env.VECTOR_DB || "lancedb",
|
||||||
|
TTSSelection: process.env.TTS_PROVIDER || "native",
|
||||||
});
|
});
|
||||||
await EventLogs.logEvent("api_sent_chat", {
|
await EventLogs.logEvent("api_sent_chat", {
|
||||||
workspaceName: workspace?.name,
|
workspaceName: workspace?.name,
|
||||||
|
@ -90,6 +90,7 @@ function apiWorkspaceThreadEndpoints(app) {
|
|||||||
LLMSelection: process.env.LLM_PROVIDER || "openai",
|
LLMSelection: process.env.LLM_PROVIDER || "openai",
|
||||||
Embedder: process.env.EMBEDDING_ENGINE || "inherit",
|
Embedder: process.env.EMBEDDING_ENGINE || "inherit",
|
||||||
VectorDbSelection: process.env.VECTOR_DB || "lancedb",
|
VectorDbSelection: process.env.VECTOR_DB || "lancedb",
|
||||||
|
TTSSelection: process.env.TTS_PROVIDER || "native",
|
||||||
});
|
});
|
||||||
await EventLogs.logEvent("api_workspace_thread_created", {
|
await EventLogs.logEvent("api_workspace_thread_created", {
|
||||||
workspaceName: workspace?.name || "Unknown Workspace",
|
workspaceName: workspace?.name || "Unknown Workspace",
|
||||||
@ -416,6 +417,7 @@ function apiWorkspaceThreadEndpoints(app) {
|
|||||||
LLMSelection: process.env.LLM_PROVIDER || "openai",
|
LLMSelection: process.env.LLM_PROVIDER || "openai",
|
||||||
Embedder: process.env.EMBEDDING_ENGINE || "inherit",
|
Embedder: process.env.EMBEDDING_ENGINE || "inherit",
|
||||||
VectorDbSelection: process.env.VECTOR_DB || "lancedb",
|
VectorDbSelection: process.env.VECTOR_DB || "lancedb",
|
||||||
|
TTSSelection: process.env.TTS_PROVIDER || "native",
|
||||||
});
|
});
|
||||||
await EventLogs.logEvent("api_sent_chat", {
|
await EventLogs.logEvent("api_sent_chat", {
|
||||||
workspaceName: workspace?.name,
|
workspaceName: workspace?.name,
|
||||||
@ -567,6 +569,7 @@ function apiWorkspaceThreadEndpoints(app) {
|
|||||||
LLMSelection: process.env.LLM_PROVIDER || "openai",
|
LLMSelection: process.env.LLM_PROVIDER || "openai",
|
||||||
Embedder: process.env.EMBEDDING_ENGINE || "inherit",
|
Embedder: process.env.EMBEDDING_ENGINE || "inherit",
|
||||||
VectorDbSelection: process.env.VECTOR_DB || "lancedb",
|
VectorDbSelection: process.env.VECTOR_DB || "lancedb",
|
||||||
|
TTSSelection: process.env.TTS_PROVIDER || "native",
|
||||||
});
|
});
|
||||||
await EventLogs.logEvent("api_sent_chat", {
|
await EventLogs.logEvent("api_sent_chat", {
|
||||||
workspaceName: workspace?.name,
|
workspaceName: workspace?.name,
|
||||||
|
@ -98,6 +98,7 @@ function chatEndpoints(app) {
|
|||||||
Embedder: process.env.EMBEDDING_ENGINE || "inherit",
|
Embedder: process.env.EMBEDDING_ENGINE || "inherit",
|
||||||
VectorDbSelection: process.env.VECTOR_DB || "lancedb",
|
VectorDbSelection: process.env.VECTOR_DB || "lancedb",
|
||||||
multiModal: Array.isArray(attachments) && attachments?.length !== 0,
|
multiModal: Array.isArray(attachments) && attachments?.length !== 0,
|
||||||
|
TTSSelection: process.env.TTS_PROVIDER || "native",
|
||||||
});
|
});
|
||||||
|
|
||||||
await EventLogs.logEvent(
|
await EventLogs.logEvent(
|
||||||
@ -226,6 +227,7 @@ function chatEndpoints(app) {
|
|||||||
Embedder: process.env.EMBEDDING_ENGINE || "inherit",
|
Embedder: process.env.EMBEDDING_ENGINE || "inherit",
|
||||||
VectorDbSelection: process.env.VECTOR_DB || "lancedb",
|
VectorDbSelection: process.env.VECTOR_DB || "lancedb",
|
||||||
multiModal: Array.isArray(attachments) && attachments?.length !== 0,
|
multiModal: Array.isArray(attachments) && attachments?.length !== 0,
|
||||||
|
TTSSelection: process.env.TTS_PROVIDER || "native",
|
||||||
});
|
});
|
||||||
|
|
||||||
await EventLogs.logEvent(
|
await EventLogs.logEvent(
|
||||||
|
@ -40,6 +40,7 @@ function workspaceThreadEndpoints(app) {
|
|||||||
LLMSelection: process.env.LLM_PROVIDER || "openai",
|
LLMSelection: process.env.LLM_PROVIDER || "openai",
|
||||||
Embedder: process.env.EMBEDDING_ENGINE || "inherit",
|
Embedder: process.env.EMBEDDING_ENGINE || "inherit",
|
||||||
VectorDbSelection: process.env.VECTOR_DB || "lancedb",
|
VectorDbSelection: process.env.VECTOR_DB || "lancedb",
|
||||||
|
TTSSelection: process.env.TTS_PROVIDER || "native",
|
||||||
},
|
},
|
||||||
user?.id
|
user?.id
|
||||||
);
|
);
|
||||||
|
@ -55,6 +55,7 @@ function workspaceEndpoints(app) {
|
|||||||
LLMSelection: process.env.LLM_PROVIDER || "openai",
|
LLMSelection: process.env.LLM_PROVIDER || "openai",
|
||||||
Embedder: process.env.EMBEDDING_ENGINE || "inherit",
|
Embedder: process.env.EMBEDDING_ENGINE || "inherit",
|
||||||
VectorDbSelection: process.env.VECTOR_DB || "lancedb",
|
VectorDbSelection: process.env.VECTOR_DB || "lancedb",
|
||||||
|
TTSSelection: process.env.TTS_PROVIDER || "native",
|
||||||
},
|
},
|
||||||
user?.id
|
user?.id
|
||||||
);
|
);
|
||||||
|
@ -142,6 +142,7 @@ const Document = {
|
|||||||
LLMSelection: process.env.LLM_PROVIDER || "openai",
|
LLMSelection: process.env.LLM_PROVIDER || "openai",
|
||||||
Embedder: process.env.EMBEDDING_ENGINE || "inherit",
|
Embedder: process.env.EMBEDDING_ENGINE || "inherit",
|
||||||
VectorDbSelection: process.env.VECTOR_DB || "lancedb",
|
VectorDbSelection: process.env.VECTOR_DB || "lancedb",
|
||||||
|
TTSSelection: process.env.TTS_PROVIDER || "native",
|
||||||
});
|
});
|
||||||
await EventLogs.logEvent(
|
await EventLogs.logEvent(
|
||||||
"workspace_documents_added",
|
"workspace_documents_added",
|
||||||
@ -185,6 +186,7 @@ const Document = {
|
|||||||
LLMSelection: process.env.LLM_PROVIDER || "openai",
|
LLMSelection: process.env.LLM_PROVIDER || "openai",
|
||||||
Embedder: process.env.EMBEDDING_ENGINE || "inherit",
|
Embedder: process.env.EMBEDDING_ENGINE || "inherit",
|
||||||
VectorDbSelection: process.env.VECTOR_DB || "lancedb",
|
VectorDbSelection: process.env.VECTOR_DB || "lancedb",
|
||||||
|
TTSSelection: process.env.TTS_PROVIDER || "native",
|
||||||
});
|
});
|
||||||
await EventLogs.logEvent(
|
await EventLogs.logEvent(
|
||||||
"workspace_documents_removed",
|
"workspace_documents_removed",
|
||||||
|
@ -214,6 +214,9 @@ const SystemSettings = {
|
|||||||
// Eleven Labs TTS
|
// Eleven Labs TTS
|
||||||
TTSElevenLabsKey: !!process.env.TTS_ELEVEN_LABS_KEY,
|
TTSElevenLabsKey: !!process.env.TTS_ELEVEN_LABS_KEY,
|
||||||
TTSElevenLabsVoiceModel: process.env.TTS_ELEVEN_LABS_VOICE_MODEL,
|
TTSElevenLabsVoiceModel: process.env.TTS_ELEVEN_LABS_VOICE_MODEL,
|
||||||
|
// Piper TTS
|
||||||
|
TTSPiperTTSVoiceModel:
|
||||||
|
process.env.TTS_PIPER_VOICE_MODEL ?? "en_US-hfc_female-medium",
|
||||||
|
|
||||||
// --------------------------------------------------------
|
// --------------------------------------------------------
|
||||||
// Agent Settings & Configs
|
// Agent Settings & Configs
|
||||||
|
@ -2,6 +2,7 @@ const prisma = require("../utils/prisma");
|
|||||||
const { EventLogs } = require("./eventLogs");
|
const { EventLogs } = require("./eventLogs");
|
||||||
|
|
||||||
const User = {
|
const User = {
|
||||||
|
usernameRegex: new RegExp(/^[a-z0-9_-]+$/),
|
||||||
writable: [
|
writable: [
|
||||||
// Used for generic updates so we can validate keys in request body
|
// Used for generic updates so we can validate keys in request body
|
||||||
"username",
|
"username",
|
||||||
@ -32,7 +33,6 @@ const User = {
|
|||||||
return String(role);
|
return String(role);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
// validations for the above writable fields.
|
// validations for the above writable fields.
|
||||||
castColumnValue: function (key, value) {
|
castColumnValue: function (key, value) {
|
||||||
switch (key) {
|
switch (key) {
|
||||||
@ -55,6 +55,12 @@ const User = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// Do not allow new users to bypass validation
|
||||||
|
if (!this.usernameRegex.test(username))
|
||||||
|
throw new Error(
|
||||||
|
"Username must be only contain lowercase letters, numbers, underscores, and hyphens with no spaces"
|
||||||
|
);
|
||||||
|
|
||||||
const bcrypt = require("bcrypt");
|
const bcrypt = require("bcrypt");
|
||||||
const hashedPassword = bcrypt.hashSync(password, 10);
|
const hashedPassword = bcrypt.hashSync(password, 10);
|
||||||
const user = await prisma.users.create({
|
const user = await prisma.users.create({
|
||||||
@ -70,7 +76,6 @@ const User = {
|
|||||||
return { user: null, error: error.message };
|
return { user: null, error: error.message };
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// Log the changes to a user object, but omit sensitive fields
|
// Log the changes to a user object, but omit sensitive fields
|
||||||
// that are not meant to be logged.
|
// that are not meant to be logged.
|
||||||
loggedChanges: function (updates, prev = {}) {
|
loggedChanges: function (updates, prev = {}) {
|
||||||
@ -93,7 +98,6 @@ const User = {
|
|||||||
where: { id: parseInt(userId) },
|
where: { id: parseInt(userId) },
|
||||||
});
|
});
|
||||||
if (!currentUser) return { success: false, error: "User not found" };
|
if (!currentUser) return { success: false, error: "User not found" };
|
||||||
|
|
||||||
// Removes non-writable fields for generic updates
|
// Removes non-writable fields for generic updates
|
||||||
// and force-casts to the proper type;
|
// and force-casts to the proper type;
|
||||||
Object.entries(updates).forEach(([key, value]) => {
|
Object.entries(updates).forEach(([key, value]) => {
|
||||||
@ -123,6 +127,17 @@ const User = {
|
|||||||
updates.password = bcrypt.hashSync(updates.password, 10);
|
updates.password = bcrypt.hashSync(updates.password, 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
updates.hasOwnProperty("username") &&
|
||||||
|
currentUser.username !== updates.username &&
|
||||||
|
!this.usernameRegex.test(updates.username)
|
||||||
|
)
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error:
|
||||||
|
"Username must be only contain lowercase letters, numbers, underscores, and hyphens with no spaces",
|
||||||
|
};
|
||||||
|
|
||||||
const user = await prisma.users.update({
|
const user = await prisma.users.update({
|
||||||
where: { id: parseInt(userId) },
|
where: { id: parseInt(userId) },
|
||||||
data: updates,
|
data: updates,
|
||||||
@ -170,7 +185,6 @@ const User = {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// Returns user object with all fields
|
// Returns user object with all fields
|
||||||
_get: async function (clause = {}) {
|
_get: async function (clause = {}) {
|
||||||
try {
|
try {
|
||||||
|
@ -38,6 +38,7 @@ const endpointsFiles = [
|
|||||||
"../endpoints/api/workspaceThread/index.js",
|
"../endpoints/api/workspaceThread/index.js",
|
||||||
"../endpoints/api/userManagement/index.js",
|
"../endpoints/api/userManagement/index.js",
|
||||||
"../endpoints/api/openai/index.js",
|
"../endpoints/api/openai/index.js",
|
||||||
|
"../endpoints/api/embed/index.js",
|
||||||
];
|
];
|
||||||
|
|
||||||
swaggerAutogen(outputFile, endpointsFiles, doc).then(({ data }) => {
|
swaggerAutogen(outputFile, endpointsFiles, doc).then(({ data }) => {
|
||||||
|
@ -3140,6 +3140,214 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"/v1/embed": {
|
||||||
|
"get": {
|
||||||
|
"tags": [
|
||||||
|
"Embed"
|
||||||
|
],
|
||||||
|
"description": "List all active embeds",
|
||||||
|
"parameters": [],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"example": {
|
||||||
|
"embeds": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"uuid": "embed-uuid-1",
|
||||||
|
"enabled": true,
|
||||||
|
"chat_mode": "query",
|
||||||
|
"createdAt": "2023-04-01T12:00:00Z",
|
||||||
|
"workspace": {
|
||||||
|
"id": 1,
|
||||||
|
"name": "Workspace 1"
|
||||||
|
},
|
||||||
|
"chat_count": 10
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"uuid": "embed-uuid-2",
|
||||||
|
"enabled": false,
|
||||||
|
"chat_mode": "chat",
|
||||||
|
"createdAt": "2023-04-02T14:30:00Z",
|
||||||
|
"workspace": {
|
||||||
|
"id": 1,
|
||||||
|
"name": "Workspace 1"
|
||||||
|
},
|
||||||
|
"chat_count": 10
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"403": {
|
||||||
|
"description": "Forbidden",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/InvalidAPIKey"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"application/xml": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/InvalidAPIKey"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal Server Error"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/v1/embed/{embedUuid}/chats": {
|
||||||
|
"get": {
|
||||||
|
"tags": [
|
||||||
|
"Embed"
|
||||||
|
],
|
||||||
|
"description": "Get all chats for a specific embed",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "embedUuid",
|
||||||
|
"in": "path",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"description": "UUID of the embed"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"example": {
|
||||||
|
"chats": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"session_id": "session-uuid-1",
|
||||||
|
"prompt": "Hello",
|
||||||
|
"response": "Hi there!",
|
||||||
|
"createdAt": "2023-04-01T12:00:00Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"session_id": "session-uuid-2",
|
||||||
|
"prompt": "How are you?",
|
||||||
|
"response": "I'm doing well, thank you!",
|
||||||
|
"createdAt": "2023-04-02T14:30:00Z"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"403": {
|
||||||
|
"description": "Forbidden",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/InvalidAPIKey"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"application/xml": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/InvalidAPIKey"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"404": {
|
||||||
|
"description": "Embed not found"
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal Server Error"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/v1/embed/{embedUuid}/chats/{sessionUuid}": {
|
||||||
|
"get": {
|
||||||
|
"tags": [
|
||||||
|
"Embed"
|
||||||
|
],
|
||||||
|
"description": "Get chats for a specific embed and session",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "embedUuid",
|
||||||
|
"in": "path",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"description": "UUID of the embed"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "sessionUuid",
|
||||||
|
"in": "path",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"description": "UUID of the session"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"example": {
|
||||||
|
"chats": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"prompt": "Hello",
|
||||||
|
"response": "Hi there!",
|
||||||
|
"createdAt": "2023-04-01T12:00:00Z"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"403": {
|
||||||
|
"description": "Forbidden",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/InvalidAPIKey"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"application/xml": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/InvalidAPIKey"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"404": {
|
||||||
|
"description": "Embed or session not found"
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal Server Error"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"components": {
|
"components": {
|
||||||
|
@ -17,12 +17,14 @@ class GeminiLLM {
|
|||||||
this.gemini = genAI.getGenerativeModel(
|
this.gemini = genAI.getGenerativeModel(
|
||||||
{ model: this.model },
|
{ model: this.model },
|
||||||
{
|
{
|
||||||
// Gemini-1.5-pro and Gemini-1.5-flash are only available on the v1beta API.
|
// Gemini-1.5-pro-* and Gemini-1.5-flash are only available on the v1beta API.
|
||||||
apiVersion:
|
apiVersion: [
|
||||||
this.model === "gemini-1.5-pro-latest" ||
|
"gemini-1.5-pro-latest",
|
||||||
this.model === "gemini-1.5-flash-latest"
|
"gemini-1.5-flash-latest",
|
||||||
? "v1beta"
|
"gemini-1.5-pro-exp-0801",
|
||||||
: "v1",
|
].includes(this.model)
|
||||||
|
? "v1beta"
|
||||||
|
: "v1",
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
this.limits = {
|
this.limits = {
|
||||||
|
@ -14,17 +14,6 @@ async function grepCommand(message, user = null) {
|
|||||||
const userPresets = await SlashCommandPresets.getUserPresets(user?.id);
|
const userPresets = await SlashCommandPresets.getUserPresets(user?.id);
|
||||||
const availableCommands = Object.keys(VALID_COMMANDS);
|
const availableCommands = Object.keys(VALID_COMMANDS);
|
||||||
|
|
||||||
// Check if the message starts with any preset command
|
|
||||||
const foundPreset = userPresets.find((p) => message.startsWith(p.command));
|
|
||||||
if (!!foundPreset) {
|
|
||||||
// Replace the preset command with the corresponding prompt
|
|
||||||
const updatedMessage = message.replace(
|
|
||||||
foundPreset.command,
|
|
||||||
foundPreset.prompt
|
|
||||||
);
|
|
||||||
return updatedMessage;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the message starts with any built-in command
|
// Check if the message starts with any built-in command
|
||||||
for (let i = 0; i < availableCommands.length; i++) {
|
for (let i = 0; i < availableCommands.length; i++) {
|
||||||
const cmd = availableCommands[i];
|
const cmd = availableCommands[i];
|
||||||
@ -34,7 +23,15 @@ async function grepCommand(message, user = null) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return message;
|
// Replace all preset commands with their corresponding prompts
|
||||||
|
// Allows multiple commands in one message
|
||||||
|
let updatedMessage = message;
|
||||||
|
for (const preset of userPresets) {
|
||||||
|
const regex = new RegExp(preset.command, "g");
|
||||||
|
updatedMessage = updatedMessage.replace(regex, preset.prompt);
|
||||||
|
}
|
||||||
|
|
||||||
|
return updatedMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function chatWithWorkspace(
|
async function chatWithWorkspace(
|
||||||
|
@ -477,6 +477,12 @@ const KEY_MAPPING = {
|
|||||||
envKey: "TTS_ELEVEN_LABS_VOICE_MODEL",
|
envKey: "TTS_ELEVEN_LABS_VOICE_MODEL",
|
||||||
checks: [],
|
checks: [],
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// PiperTTS Local
|
||||||
|
TTSPiperTTSVoiceModel: {
|
||||||
|
envKey: "TTS_PIPER_VOICE_MODEL",
|
||||||
|
checks: [],
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
function isNotEmpty(input = "") {
|
function isNotEmpty(input = "") {
|
||||||
@ -536,7 +542,12 @@ function validOllamaLLMBasePath(input = "") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function supportedTTSProvider(input = "") {
|
function supportedTTSProvider(input = "") {
|
||||||
const validSelection = ["native", "openai", "elevenlabs"].includes(input);
|
const validSelection = [
|
||||||
|
"native",
|
||||||
|
"openai",
|
||||||
|
"elevenlabs",
|
||||||
|
"piper_local",
|
||||||
|
].includes(input);
|
||||||
return validSelection ? null : `${input} is not a valid TTS provider.`;
|
return validSelection ? null : `${input} is not a valid TTS provider.`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user