Compare commits

...

20 Commits

Author SHA1 Message Date
shatfield4
ea24d35aae Merge branch 'master' into 809-style-implement-new-side-bar-menus 2024-03-06 16:47:33 -08:00
shatfield4
b3a463415b add check for roles to SettingsButton component and hide button from footer when in mobile 2024-03-06 16:39:40 -08:00
Francisco Bischoff
9ce3d1150d
Update Ubuntu base image and improve Dockerfile (#609)
* Update Ubuntu base image and improve Dockerfile

* Add unzip to Docker image dependencies

Needed for the arm64 build

* reset tabs

* formalized lint rules for hadolint. however the Docker formatting is being handled by MS Docker extension which doesn't indent code as expected. WIP.

* found a workaround to keep formatting

---------

Co-authored-by: timothycarambat <rambat1010@gmail.com>
2024-03-06 16:34:45 -08:00
timothycarambat
da9533e552 small changes to UI to conform to designs
Fix conditional hook render
2024-03-06 16:00:00 -08:00
Sean Hatfield
5907eb9939
Make EditWorkspaceUsersModal scrollable (#853)
make EditWorkspaceUsersModal scrollable
2024-03-06 14:58:35 -08:00
Sean Hatfield
e0d5d8039a
[FEAT] Claude 3 support and implement new version of Anthropic SDK (#863)
* implement new version of anthropic sdk and support new models

* remove handleAnthropicStream and move to handleStream inside anthropic provider

* update useGetProvidersModels for new anthropic models
2024-03-06 14:57:47 -08:00
Sean Hatfield
0634013788
[FEAT] Groq LLM support (#865)
* Groq LLM support complete

* update useGetProvidersModels for groq models

* Add definiations
update comments and error log reports
add example envs

---------

Co-authored-by: timothycarambat <rambat1010@gmail.com>
2024-03-06 14:48:38 -08:00
Gabriel Koo
4731ec8be8
[FIX] : missing import for parseAuthHeader in server/utils/vectorDbProviders/chroma/index.js (#869)
fix: import parseAuthHeader in chroma/index.js
2024-03-06 09:14:36 -08:00
Sean Hatfield
fde905aac1
[FEAT] JSON export append all metadata fields to workspace chats (#845)
have JSON export append all metadata fields
2024-02-29 17:04:59 -08:00
Timothy Carambat
147426704c
adjust max upload window height (#844) 2024-02-29 10:09:49 -08:00
Timothy Carambat
ec90060d36
Re-map some file mimes to support text (#842)
re-map some file mimes to support text
2024-02-29 10:05:03 -08:00
timothycarambat
60fc5f715a Merge branch 'master' of github.com:Mintplex-Labs/anything-llm 2024-02-28 12:05:57 -08:00
timothycarambat
e775cc2b05 fix Ollama embedder typo 2024-02-28 12:05:44 -08:00
Timothy Carambat
9e085baf5e
Add chat-export to API (#831) 2024-02-27 12:52:57 -08:00
Sean Hatfield
2974a969b5
[FEAT]: Pin to workspace UI update (#828)
* pin to workspace UI update

* tweak css

* change tooltip to white to match designs
2024-02-27 11:55:26 -08:00
Timothy Carambat
bbe3c0a5d6
Bulk remove files from file picker (#830)
Do bulk deletion on backend
2024-02-27 11:53:42 -08:00
Sean Hatfield
e87cba3468
[CHORE] Normalize styles of all input and select elements (#804)
* normalize styles of all input and select elements

* missed placeholder text input

* missed input fields on onboarding flow
2024-02-27 11:47:01 -08:00
Timothy Carambat
b64cb199f9
788 ollama embedder (#814)
* Add Ollama embedder model support calls

* update docs
2024-02-26 16:12:20 -08:00
Timothy Carambat
b20e3ce52c
Only show critical toasts during onboarding (#810) 2024-02-26 13:54:22 -08:00
Sean Hatfield
4d74f23c82
[CHORE]: Improve UX of custom logo screens (#806)
improve UX of custom logo screens
2024-02-26 13:50:28 -08:00
90 changed files with 1195 additions and 329 deletions

8
.hadolint.yaml Normal file
View File

@ -0,0 +1,8 @@
failure-threshold: warning
ignored:
- DL3008
- DL3013
format: tty
trustedRegistries:
- docker.io
- gcr.io

View File

@ -4,16 +4,20 @@
"Astra",
"Dockerized",
"Embeddable",
"GROQ",
"hljs",
"inferencing",
"Langchain",
"Milvus",
"Mintplex",
"Ollama",
"openai",
"openrouter",
"Qdrant",
"vectordbs",
"Weaviate",
"Zilliz"
],
"eslint.experimental.useFlatConfig": true
}
"eslint.experimental.useFlatConfig": true,
"docker.languageserver.formatter.ignoreMultilineInstructions": true
}

View File

@ -82,6 +82,7 @@ Some cool features of AnythingLLM
- [Azure OpenAI](https://azure.microsoft.com/en-us/products/ai-services/openai-service)
- [LM Studio (all)](https://lmstudio.ai)
- [LocalAi (all)](https://localai.io/)
- [Ollama (all)](https://ollama.ai/)
**Supported Vector Databases:**

View File

@ -1,28 +1,16 @@
const fs = require("fs");
const path = require("path");
const { getType } = require("mime");
const { MimeDetector } = require("./mime");
function isTextType(filepath) {
if (!fs.existsSync(filepath)) return false;
// These are types of mime primary classes that for sure
// cannot also for forced into a text type.
const nonTextTypes = ["multipart", "image", "model", "audio", "video"];
// These are full-mimes we for sure cannot parse or interpret as text
// documents
const BAD_MIMES = [
"application/octet-stream",
"application/zip",
"application/pkcs8",
"application/vnd.microsoft.portable-executable",
"application/x-msdownload",
];
try {
const mime = getType(filepath);
if (BAD_MIMES.includes(mime)) return false;
if (!fs.existsSync(filepath)) return false;
const mimeLib = new MimeDetector();
const mime = mimeLib.getType(filepath);
if (mimeLib.badMimes.includes(mime)) return false;
const type = mime.split("/")[0];
if (nonTextTypes.includes(type)) return false;
if (mimeLib.nonTextTypes.includes(type)) return false;
return true;
} catch {
return false;

View File

@ -0,0 +1,37 @@
const MimeLib = require("mime");
class MimeDetector {
nonTextTypes = ["multipart", "image", "model", "audio", "video"];
badMimes = [
"application/octet-stream",
"application/zip",
"application/pkcs8",
"application/vnd.microsoft.portable-executable",
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", // XLSX are binaries and need to be handled explicitly.
"application/x-msdownload",
];
constructor() {
this.lib = MimeLib;
this.setOverrides();
}
setOverrides() {
// the .ts extension maps to video/mp2t because of https://en.wikipedia.org/wiki/MPEG_transport_stream
// which has had this extension far before TS was invented. So need to force re-map this MIME map.
this.lib.define(
{
"text/plain": ["ts", "py", "opts", "lock", "jsonl"],
},
true
);
}
getType(filepath) {
return this.lib.getType(filepath);
}
}
module.exports = {
MimeDetector,
};

View File

@ -61,6 +61,10 @@ GID='1000'
# HUGGING_FACE_LLM_API_KEY=hf_xxxxxx
# HUGGING_FACE_LLM_TOKEN_LIMIT=8000
# LLM_PROVIDER='groq'
# GROQ_API_KEY=gsk_abcxyz
# GROQ_MODEL_PREF=llama2-70b-4096
###########################################
######## Embedding API SElECTION ##########
###########################################
@ -79,6 +83,11 @@ GID='1000'
# EMBEDDING_MODEL_PREF='text-embedding-ada-002'
# EMBEDDING_MODEL_MAX_CHUNK_LENGTH=1000 # The max chunk size in chars a string to embed can be
# EMBEDDING_ENGINE='ollama'
# EMBEDDING_BASE_PATH='http://127.0.0.1:11434'
# EMBEDDING_MODEL_PREF='nomic-embed-text:latest'
# EMBEDDING_MODEL_MAX_CHUNK_LENGTH=8192
###########################################
######## Vector Database Selection ########
###########################################

View File

@ -1,12 +1,17 @@
# Setup base image
FROM ubuntu:jammy-20230522 AS base
FROM ubuntu:jammy-20230916 AS base
# Build arguments
ARG ARG_UID=1000
ARG ARG_GID=1000
FROM base AS build-arm64
RUN echo "Preparing build of AnythingLLM image for arm64 architecture"
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
# Install system dependencies
# hadolint ignore=DL3008,DL3013
RUN DEBIAN_FRONTEND=noninteractive apt-get update && \
DEBIAN_FRONTEND=noninteractive apt-get install -yq --no-install-recommends \
unzip curl gnupg libgfortran5 libgbm1 tzdata netcat \
@ -25,8 +30,8 @@ RUN DEBIAN_FRONTEND=noninteractive apt-get update && \
&& rm yarn_1.22.19_all.deb
# Create a group and user with specific UID and GID
RUN groupadd -g $ARG_GID anythingllm && \
useradd -u $ARG_UID -m -d /app -s /bin/bash -g anythingllm anythingllm && \
RUN groupadd -g "$ARG_GID" anythingllm && \
useradd -l -u "$ARG_UID" -m -d /app -s /bin/bash -g anythingllm anythingllm && \
mkdir -p /app/frontend/ /app/server/ /app/collector/ && chown -R anythingllm:anythingllm /app
# Copy docker helper scripts
@ -61,6 +66,10 @@ RUN echo "Done running arm64 specific installtion steps"
FROM base AS build-amd64
RUN echo "Preparing build of AnythingLLM image for non-ARM architecture"
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
# Install system dependencies
# hadolint ignore=DL3008,DL3013
RUN DEBIAN_FRONTEND=noninteractive apt-get update && \
DEBIAN_FRONTEND=noninteractive apt-get install -yq --no-install-recommends \
curl gnupg libgfortran5 libgbm1 tzdata netcat \
@ -79,8 +88,8 @@ RUN DEBIAN_FRONTEND=noninteractive apt-get update && \
&& rm yarn_1.22.19_all.deb
# Create a group and user with specific UID and GID
RUN groupadd -g $ARG_GID anythingllm && \
useradd -u $ARG_UID -m -d /app -s /bin/bash -g anythingllm anythingllm && \
RUN groupadd -g "$ARG_GID" anythingllm && \
useradd -l -u "$ARG_UID" -m -d /app -s /bin/bash -g anythingllm anythingllm && \
mkdir -p /app/frontend/ /app/server/ /app/collector/ && chown -R anythingllm:anythingllm /app
# Copy docker helper scripts
@ -95,6 +104,8 @@ RUN chmod +x /usr/local/bin/docker-entrypoint.sh && \
#############################################
# COMMON BUILD FLOW FOR ALL ARCHS
#############################################
# hadolint ignore=DL3006
FROM build-${TARGETARCH} AS build
RUN echo "Running common build flow of AnythingLLM image for all architectures"
@ -102,43 +113,54 @@ USER anythingllm
WORKDIR /app
# Install frontend dependencies
FROM build as frontend-deps
FROM build AS frontend-deps
COPY ./frontend/package.json ./frontend/yarn.lock ./frontend/
RUN cd ./frontend/ && yarn install --network-timeout 100000 && yarn cache clean
WORKDIR /app/frontend
RUN yarn install --network-timeout 100000 && yarn cache clean
WORKDIR /app
# Install server dependencies
FROM build as server-deps
FROM build AS server-deps
COPY ./server/package.json ./server/yarn.lock ./server/
RUN cd ./server/ && yarn install --production --network-timeout 100000 && yarn cache clean
WORKDIR /app/server
RUN yarn install --production --network-timeout 100000 && yarn cache clean
WORKDIR /app
# Compile Llama.cpp bindings for node-llama-cpp for this operating system.
USER root
RUN cd ./server && npx --no node-llama-cpp download
WORKDIR /app/server
RUN npx --no node-llama-cpp download
WORKDIR /app
USER anythingllm
# Build the frontend
FROM frontend-deps as build-stage
FROM frontend-deps AS build-stage
COPY ./frontend/ ./frontend/
RUN cd ./frontend/ && yarn build && yarn cache clean
WORKDIR /app/frontend
RUN yarn build && yarn cache clean
WORKDIR /app
# Setup the server
FROM server-deps as production-stage
FROM server-deps AS production-stage
COPY --chown=anythingllm:anythingllm ./server/ ./server/
# Copy built static frontend files to the server public directory
COPY --from=build-stage /app/frontend/dist ./server/public
COPY --chown=anythingllm:anythingllm --from=build-stage /app/frontend/dist ./server/public
# Copy the collector
COPY --chown=anythingllm:anythingllm ./collector/ ./collector/
# Install collector dependencies
WORKDIR /app/collector
ENV PUPPETEER_DOWNLOAD_BASE_URL=https://storage.googleapis.com/chrome-for-testing-public
RUN cd /app/collector && yarn install --production --network-timeout 100000 && yarn cache clean
RUN yarn install --production --network-timeout 100000 && yarn cache clean
# Migrate and Run Prisma against known schema
RUN cd ./server && npx prisma generate --schema=./prisma/schema.prisma
RUN cd ./server && npx prisma migrate deploy --schema=./prisma/schema.prisma
WORKDIR /app/server
RUN npx prisma generate --schema=./prisma/schema.prisma && \
npx prisma migrate deploy --schema=./prisma/schema.prisma
WORKDIR /app
# Setup the environment
ENV NODE_ENV=production
@ -152,4 +174,4 @@ HEALTHCHECK --interval=1m --timeout=10s --start-period=1m \
CMD /bin/bash /usr/local/bin/docker-healthcheck.sh || exit 1
# Run the server
ENTRYPOINT ["/bin/bash", "/usr/local/bin/docker-entrypoint.sh"]
ENTRYPOINT ["/bin/bash", "/usr/local/bin/docker-entrypoint.sh"]

View File

@ -1,9 +1,10 @@
#!/bin/bash
{ cd /app/server/ &&\
npx prisma generate --schema=./prisma/schema.prisma &&\
npx prisma migrate deploy --schema=./prisma/schema.prisma &&\
node /app/server/index.js
{
cd /app/server/ &&
npx prisma generate --schema=./prisma/schema.prisma &&
npx prisma migrate deploy --schema=./prisma/schema.prisma &&
node /app/server/index.js
} &
{ node /app/collector/index.js; } &
wait -n
exit $?
exit $?

View File

@ -4,10 +4,10 @@
response=$(curl --write-out '%{http_code}' --silent --output /dev/null http://localhost:3001/api/ping)
# If the HTTP response code is 200 (OK), the server is up
if [ $response -eq 200 ]; then
echo "Server is up"
exit 0
if [ "$response" -eq 200 ]; then
echo "Server is up"
exit 0
else
echo "Server is down"
exit 1
echo "Server is down"
exit 1
fi

View File

@ -9,7 +9,7 @@ export default function AzureAiOptions({ settings }) {
<input
type="url"
name="AzureOpenAiEndpoint"
className="bg-zinc-900 text-white placeholder-white placeholder-opacity-60 text-sm rounded-lg focus:border-white block w-full p-2.5"
className="bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:border-white block w-full p-2.5"
placeholder="https://my-azure.openai.azure.com"
defaultValue={settings?.AzureOpenAiEndpoint}
required={true}
@ -25,7 +25,7 @@ export default function AzureAiOptions({ settings }) {
<input
type="password"
name="AzureOpenAiKey"
className="bg-zinc-900 text-white placeholder-white placeholder-opacity-60 text-sm rounded-lg focus:border-white block w-full p-2.5"
className="bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:border-white block w-full p-2.5"
placeholder="Azure OpenAI API Key"
defaultValue={settings?.AzureOpenAiKey ? "*".repeat(20) : ""}
required={true}
@ -41,7 +41,7 @@ export default function AzureAiOptions({ settings }) {
<input
type="text"
name="AzureOpenAiEmbeddingModelPref"
className="bg-zinc-900 text-white placeholder-white placeholder-opacity-60 text-sm rounded-lg focus:border-white block w-full p-2.5"
className="bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:border-white block w-full p-2.5"
placeholder="Azure OpenAI embedding model deployment name"
defaultValue={settings?.AzureOpenAiEmbeddingModelPref}
required={true}

View File

@ -19,7 +19,7 @@ export default function LocalAiOptions({ settings }) {
<input
type="url"
name="EmbeddingBasePath"
className="bg-zinc-900 text-white placeholder-white placeholder-opacity-60 text-sm rounded-lg focus:border-white block w-full p-2.5"
className="bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:border-white block w-full p-2.5"
placeholder="http://localhost:8080/v1"
defaultValue={settings?.EmbeddingBasePath}
onChange={(e) => setBasePathValue(e.target.value)}
@ -41,7 +41,7 @@ export default function LocalAiOptions({ settings }) {
<input
type="number"
name="EmbeddingModelMaxChunkLength"
className="bg-zinc-900 text-white placeholder-white placeholder-opacity-60 text-sm rounded-lg focus:border-white block w-full p-2.5"
className="bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:border-white block w-full p-2.5"
placeholder="1000"
min={1}
onScroll={(e) => e.target.blur()}
@ -62,7 +62,7 @@ export default function LocalAiOptions({ settings }) {
<input
type="password"
name="LocalAiApiKey"
className="bg-zinc-900 text-white placeholder-white placeholder-opacity-60 text-sm rounded-lg focus:border-white block w-full p-2.5"
className="bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:border-white block w-full p-2.5"
placeholder="sk-mysecretkey"
defaultValue={settings?.LocalAiApiKey ? "*".repeat(20) : ""}
autoComplete="off"
@ -108,7 +108,7 @@ function LocalAIModelSelection({ settings, apiKey = null, basePath = null }) {
<select
name="EmbeddingModelPref"
disabled={true}
className="bg-zinc-900 border border-gray-500 text-white text-sm rounded-lg block w-full p-2.5"
className="bg-zinc-900 border-gray-500 text-white text-sm rounded-lg block w-full p-2.5"
>
<option disabled={true} selected={true}>
{basePath?.includes("/v1")
@ -128,7 +128,7 @@ function LocalAIModelSelection({ settings, apiKey = null, basePath = null }) {
<select
name="EmbeddingModelPref"
required={true}
className="bg-zinc-900 border border-gray-500 text-white text-sm rounded-lg block w-full p-2.5"
className="bg-zinc-900 border-gray-500 text-white text-sm rounded-lg block w-full p-2.5"
>
{customModels.length > 0 && (
<optgroup label="Your loaded models">

View File

@ -0,0 +1,120 @@
import React, { useEffect, useState } from "react";
import System from "@/models/system";
export default function OllamaEmbeddingOptions({ settings }) {
const [basePathValue, setBasePathValue] = useState(
settings?.EmbeddingBasePath
);
const [basePath, setBasePath] = useState(settings?.EmbeddingBasePath);
return (
<div className="w-full flex flex-col gap-y-4">
<div className="w-full flex items-center gap-4">
<div className="flex flex-col w-60">
<label className="text-white text-sm font-semibold block mb-4">
Ollama Base URL
</label>
<input
type="url"
name="EmbeddingBasePath"
className="bg-zinc-900 text-white placeholder-white/20 text-sm rounded-lg focus:border-white block w-full p-2.5"
placeholder="http://127.0.0.1:11434"
defaultValue={settings?.EmbeddingBasePath}
onChange={(e) => setBasePathValue(e.target.value)}
onBlur={() => setBasePath(basePathValue)}
required={true}
autoComplete="off"
spellCheck={false}
/>
</div>
<OllamaLLMModelSelection settings={settings} basePath={basePath} />
<div className="flex flex-col w-60">
<label className="text-white text-sm font-semibold block mb-4">
Max embedding chunk length
</label>
<input
type="number"
name="EmbeddingModelMaxChunkLength"
className="bg-zinc-900 text-white placeholder-white/20 text-sm rounded-lg focus:border-white block w-full p-2.5"
placeholder="8192"
min={1}
onScroll={(e) => e.target.blur()}
defaultValue={settings?.EmbeddingModelMaxChunkLength}
required={false}
autoComplete="off"
/>
</div>
</div>
</div>
);
}
function OllamaLLMModelSelection({ settings, basePath = null }) {
const [customModels, setCustomModels] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
async function findCustomModels() {
if (!basePath) {
setCustomModels([]);
setLoading(false);
return;
}
setLoading(true);
const { models } = await System.customModels("ollama", null, basePath);
setCustomModels(models || []);
setLoading(false);
}
findCustomModels();
}, [basePath]);
if (loading || customModels.length == 0) {
return (
<div className="flex flex-col w-60">
<label className="text-white text-sm font-semibold block mb-4">
Embedding Model Selection
</label>
<select
name="EmbeddingModelPref"
disabled={true}
className="bg-zinc-900 border border-gray-500 text-white text-sm rounded-lg block w-full p-2.5"
>
<option disabled={true} selected={true}>
{!!basePath
? "-- loading available models --"
: "-- waiting for URL --"}
</option>
</select>
</div>
);
}
return (
<div className="flex flex-col w-60">
<label className="text-white text-sm font-semibold block mb-4">
Embedding Model Selection
</label>
<select
name="EmbeddingModelPref"
required={true}
className="bg-zinc-900 border border-gray-500 text-white text-sm rounded-lg block w-full p-2.5"
>
{customModels.length > 0 && (
<optgroup label="Your loaded models">
{customModels.map((model) => {
return (
<option
key={model.id}
value={model.id}
selected={settings.EmbeddingModelPref === model.id}
>
{model.id}
</option>
);
})}
</optgroup>
)}
</select>
</div>
);
}

View File

@ -9,7 +9,7 @@ export default function OpenAiOptions({ settings }) {
<input
type="password"
name="OpenAiKey"
className="bg-zinc-900 text-white placeholder-white placeholder-opacity-60 text-sm rounded-lg focus:border-white block w-full p-2.5"
className="bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:border-white block w-full p-2.5"
placeholder="OpenAI API Key"
defaultValue={settings?.OpenAiKey ? "*".repeat(20) : ""}
required={true}
@ -24,7 +24,7 @@ export default function OpenAiOptions({ settings }) {
<select
name="EmbeddingModelPref"
required={true}
className="bg-zinc-900 border border-gray-500 text-white text-sm rounded-lg block w-full p-2.5"
className="bg-zinc-900 border-gray-500 text-white text-sm rounded-lg block w-full p-2.5"
>
<optgroup label="Available embedding models">
{[

View File

@ -13,6 +13,7 @@ import {
} from "@phosphor-icons/react";
import React, { useEffect, useState } from "react";
import SettingsButton from "../SettingsButton";
import { isMobile } from "react-device-detect";
export const MAX_ICONS = 3;
export const ICON_COMPONENTS = {
@ -44,7 +45,7 @@ export default function Footer() {
if (!Array.isArray(footerData) || footerData.length === 0) {
return (
<div className="flex justify-center mt-2">
<div className="flex justify-center mb-2">
<div className="flex space-x-4">
<a
href={paths.github()}
@ -73,14 +74,14 @@ export default function Footer() {
className="h-5 w-5 stroke-slate-200 group-hover:stroke-slate-200"
/>
</a>
<SettingsButton />
{!isMobile && <SettingsButton />}
</div>
</div>
);
}
return (
<div className="flex justify-center mt-2">
<div className="flex justify-center mb-2">
<div className="flex space-x-4">
{footerData.map((item, index) => (
<a
@ -96,7 +97,7 @@ export default function Footer() {
})}
</a>
))}
<SettingsButton />
{!isMobile && <SettingsButton />}
</div>
</div>
);

View File

@ -29,7 +29,7 @@ export default function AnthropicAiOptions({ settings, showAlert = false }) {
<input
type="password"
name="AnthropicApiKey"
className="bg-zinc-900 text-white placeholder-white placeholder-opacity-60 text-sm rounded-lg focus:border-white block w-full p-2.5"
className="bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:border-white block w-full p-2.5"
placeholder="Anthropic Claude-2 API Key"
defaultValue={settings?.AnthropicApiKey ? "*".repeat(20) : ""}
required={true}
@ -46,9 +46,15 @@ export default function AnthropicAiOptions({ settings, showAlert = false }) {
name="AnthropicModelPref"
defaultValue={settings?.AnthropicModelPref || "claude-2"}
required={true}
className="bg-zinc-900 border border-gray-500 text-white text-sm rounded-lg block w-full p-2.5"
className="bg-zinc-900 border-gray-500 text-white text-sm rounded-lg block w-full p-2.5"
>
{["claude-2", "claude-instant-1"].map((model) => {
{[
"claude-instant-1.2",
"claude-2.0",
"claude-2.1",
"claude-3-opus-20240229",
"claude-3-sonnet-20240229",
].map((model) => {
return (
<option key={model} value={model}>
{model}

View File

@ -9,7 +9,7 @@ export default function AzureAiOptions({ settings }) {
<input
type="url"
name="AzureOpenAiEndpoint"
className="bg-zinc-900 text-white placeholder-white placeholder-opacity-60 text-sm rounded-lg focus:border-white block w-full p-2.5"
className="bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:border-white block w-full p-2.5"
placeholder="https://my-azure.openai.azure.com"
defaultValue={settings?.AzureOpenAiEndpoint}
required={true}
@ -25,7 +25,7 @@ export default function AzureAiOptions({ settings }) {
<input
type="password"
name="AzureOpenAiKey"
className="bg-zinc-900 text-white placeholder-white placeholder-opacity-60 text-sm rounded-lg focus:border-white block w-full p-2.5"
className="bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:border-white block w-full p-2.5"
placeholder="Azure OpenAI API Key"
defaultValue={settings?.AzureOpenAiKey ? "*".repeat(20) : ""}
required={true}
@ -41,7 +41,7 @@ export default function AzureAiOptions({ settings }) {
<input
type="text"
name="AzureOpenAiModelPref"
className="bg-zinc-900 text-white placeholder-white placeholder-opacity-60 text-sm rounded-lg focus:border-white block w-full p-2.5"
className="bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:border-white block w-full p-2.5"
placeholder="Azure OpenAI chat model deployment name"
defaultValue={settings?.AzureOpenAiModelPref}
required={true}
@ -59,7 +59,7 @@ export default function AzureAiOptions({ settings }) {
<select
name="AzureOpenAiTokenLimit"
defaultValue={settings?.AzureOpenAiTokenLimit || 4096}
className="bg-zinc-900 text-white placeholder-white placeholder-opacity-60 text-sm rounded-lg focus:border-white block w-full p-2.5"
className="bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:border-white block w-full p-2.5"
required={true}
>
<option value={4096}>4,096 (gpt-3.5-turbo)</option>
@ -77,7 +77,7 @@ export default function AzureAiOptions({ settings }) {
<input
type="text"
name="AzureOpenAiEmbeddingModelPref"
className="bg-zinc-900 text-white placeholder-white placeholder-opacity-60 text-sm rounded-lg focus:border-white block w-full p-2.5"
className="bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:border-white block w-full p-2.5"
placeholder="Azure OpenAI embedding model deployment name"
defaultValue={settings?.AzureOpenAiEmbeddingModelPref}
required={true}

View File

@ -9,7 +9,7 @@ export default function GeminiLLMOptions({ settings }) {
<input
type="password"
name="GeminiLLMApiKey"
className="bg-zinc-900 text-white placeholder-white placeholder-opacity-60 text-sm rounded-lg focus:border-white block w-full p-2.5"
className="bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:border-white block w-full p-2.5"
placeholder="Google Gemini API Key"
defaultValue={settings?.GeminiLLMApiKey ? "*".repeat(20) : ""}
required={true}
@ -26,7 +26,7 @@ export default function GeminiLLMOptions({ settings }) {
name="GeminiLLMModelPref"
defaultValue={settings?.GeminiLLMModelPref || "gemini-pro"}
required={true}
className="bg-zinc-900 border border-gray-500 text-white text-sm rounded-lg block w-full p-2.5"
className="bg-zinc-900 border-gray-500 text-white text-sm rounded-lg block w-full p-2.5"
>
{["gemini-pro"].map((model) => {
return (

View File

@ -0,0 +1,41 @@
export default function GroqAiOptions({ settings }) {
return (
<div className="flex gap-x-4">
<div className="flex flex-col w-60">
<label className="text-white text-sm font-semibold block mb-4">
Groq API Key
</label>
<input
type="password"
name="GroqApiKey"
className="bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:border-white block w-full p-2.5"
placeholder="Groq API Key"
defaultValue={settings?.GroqApiKey ? "*".repeat(20) : ""}
required={true}
autoComplete="off"
spellCheck={false}
/>
</div>
<div className="flex flex-col w-60">
<label className="text-white text-sm font-semibold block mb-4">
Chat Model Selection
</label>
<select
name="GroqModelPref"
defaultValue={settings?.GroqModelPref || "llama2-70b-4096"}
required={true}
className="bg-zinc-900 border-gray-500 text-white text-sm rounded-lg block w-full p-2.5"
>
{["llama2-70b-4096", "mixtral-8x7b-32768"].map((model) => {
return (
<option key={model} value={model}>
{model}
</option>
);
})}
</select>
</div>
</div>
);
}

View File

@ -9,7 +9,7 @@ export default function HuggingFaceOptions({ settings }) {
<input
type="url"
name="HuggingFaceLLMEndpoint"
className="bg-zinc-900 text-white placeholder-white placeholder-opacity-60 text-sm rounded-lg focus:border-white block w-full p-2.5"
className="bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:border-white block w-full p-2.5"
placeholder="https://example.endpoints.huggingface.cloud"
defaultValue={settings?.HuggingFaceLLMEndpoint}
required={true}
@ -24,7 +24,7 @@ export default function HuggingFaceOptions({ settings }) {
<input
type="password"
name="HuggingFaceLLMAccessToken"
className="bg-zinc-900 text-white placeholder-white placeholder-opacity-60 text-sm rounded-lg focus:border-white block w-full p-2.5"
className="bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:border-white block w-full p-2.5"
placeholder="HuggingFace Access Token"
defaultValue={
settings?.HuggingFaceLLMAccessToken ? "*".repeat(20) : ""
@ -41,7 +41,7 @@ export default function HuggingFaceOptions({ settings }) {
<input
type="number"
name="HuggingFaceLLMTokenLimit"
className="bg-zinc-900 text-white placeholder-white placeholder-opacity-60 text-sm rounded-lg focus:border-white block w-full p-2.5"
className="bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:border-white block w-full p-2.5"
placeholder="4096"
min={1}
onScroll={(e) => e.target.blur()}

View File

@ -29,7 +29,7 @@ export default function LMStudioOptions({ settings, showAlert = false }) {
<input
type="url"
name="LMStudioBasePath"
className="bg-zinc-900 text-white placeholder-white placeholder-opacity-60 text-sm rounded-lg focus:border-white block w-full p-2.5"
className="bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:border-white block w-full p-2.5"
placeholder="http://localhost:1234/v1"
defaultValue={settings?.LMStudioBasePath}
required={true}
@ -44,7 +44,7 @@ export default function LMStudioOptions({ settings, showAlert = false }) {
<input
type="number"
name="LMStudioTokenLimit"
className="bg-zinc-900 text-white placeholder-white placeholder-opacity-60 text-sm rounded-lg focus:border-white block w-full p-2.5"
className="bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:border-white block w-full p-2.5"
placeholder="4096"
min={1}
onScroll={(e) => e.target.blur()}

View File

@ -36,7 +36,7 @@ export default function LocalAiOptions({ settings, showAlert = false }) {
<input
type="url"
name="LocalAiBasePath"
className="bg-zinc-900 text-white placeholder-white placeholder-opacity-60 text-sm rounded-lg focus:border-white block w-full p-2.5"
className="bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:border-white block w-full p-2.5"
placeholder="http://localhost:1234/v1"
defaultValue={settings?.LocalAiBasePath}
required={true}
@ -58,7 +58,7 @@ export default function LocalAiOptions({ settings, showAlert = false }) {
<input
type="number"
name="LocalAiTokenLimit"
className="bg-zinc-900 text-white placeholder-white placeholder-opacity-60 text-sm rounded-lg focus:border-white block w-full p-2.5"
className="bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:border-white block w-full p-2.5"
placeholder="4096"
min={1}
onScroll={(e) => e.target.blur()}
@ -80,7 +80,7 @@ export default function LocalAiOptions({ settings, showAlert = false }) {
<input
type="password"
name="LocalAiApiKey"
className="bg-zinc-900 text-white placeholder-white placeholder-opacity-60 text-sm rounded-lg focus:border-white block w-full p-2.5"
className="bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:border-white block w-full p-2.5"
placeholder="sk-mysecretkey"
defaultValue={settings?.LocalAiApiKey ? "*".repeat(20) : ""}
autoComplete="off"
@ -126,7 +126,7 @@ function LocalAIModelSelection({ settings, basePath = null, apiKey = null }) {
<select
name="LocalAiModelPref"
disabled={true}
className="bg-zinc-900 border border-gray-500 text-white text-sm rounded-lg block w-full p-2.5"
className="bg-zinc-900 border-gray-500 text-white text-sm rounded-lg block w-full p-2.5"
>
<option disabled={true} selected={true}>
{basePath?.includes("/v1")
@ -146,7 +146,7 @@ function LocalAIModelSelection({ settings, basePath = null, apiKey = null }) {
<select
name="LocalAiModelPref"
required={true}
className="bg-zinc-900 border border-gray-500 text-white text-sm rounded-lg block w-full p-2.5"
className="bg-zinc-900 border-gray-500 text-white text-sm rounded-lg block w-full p-2.5"
>
{customModels.length > 0 && (
<optgroup label="Your loaded models">

View File

@ -14,7 +14,7 @@ export default function MistralOptions({ settings }) {
<input
type="password"
name="MistralApiKey"
className="bg-zinc-900 text-white placeholder-white placeholder-opacity-60 text-sm rounded-lg focus:border-white block w-full p-2.5"
className="bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:border-white block w-full p-2.5"
placeholder="Mistral API Key"
defaultValue={settings?.MistralApiKey ? "*".repeat(20) : ""}
required={true}
@ -60,7 +60,7 @@ function MistralModelSelection({ apiKey, settings }) {
<select
name="MistralModelPref"
disabled={true}
className="bg-zinc-900 border border-gray-500 text-white text-sm rounded-lg block w-full p-2.5"
className="bg-zinc-900 border-gray-500 text-white text-sm rounded-lg block w-full p-2.5"
>
<option disabled={true} selected={true}>
{!!apiKey
@ -80,7 +80,7 @@ function MistralModelSelection({ apiKey, settings }) {
<select
name="MistralModelPref"
required={true}
className="bg-zinc-900 border border-gray-500 text-white text-sm rounded-lg block w-full p-2.5"
className="bg-zinc-900 border-gray-500 text-white text-sm rounded-lg block w-full p-2.5"
>
{customModels.length > 0 && (
<optgroup label="Available Mistral Models">

View File

@ -43,7 +43,7 @@ function NativeModelSelection({ settings }) {
<select
name="NativeLLMModelPref"
disabled={true}
className="bg-zinc-900 border border-gray-500 text-white text-sm rounded-lg block w-full p-2.5"
className="bg-zinc-900 border-gray-500 text-white text-sm rounded-lg block w-full p-2.5"
>
<option disabled={true} selected={true}>
-- waiting for models --
@ -62,7 +62,7 @@ function NativeModelSelection({ settings }) {
<select
name="NativeLLMModelPref"
required={true}
className="bg-zinc-900 border border-gray-500 text-white text-sm rounded-lg block w-full p-2.5"
className="bg-zinc-900 border-gray-500 text-white text-sm rounded-lg block w-full p-2.5"
>
{customModels.length > 0 && (
<optgroup label="Your loaded models">
@ -88,7 +88,7 @@ function NativeModelSelection({ settings }) {
<input
type="number"
name="NativeLLMTokenLimit"
className="bg-zinc-900 text-white placeholder-white placeholder-opacity-60 text-sm rounded-lg focus:border-white block w-full p-2.5"
className="bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:border-white block w-full p-2.5"
placeholder="4096"
min={1}
onScroll={(e) => e.target.blur()}

View File

@ -17,7 +17,7 @@ export default function OllamaLLMOptions({ settings }) {
<input
type="url"
name="OllamaLLMBasePath"
className="bg-zinc-900 text-white placeholder-white placeholder-opacity-60 text-sm rounded-lg focus:border-white block w-full p-2.5"
className="bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:border-white block w-full p-2.5"
placeholder="http://127.0.0.1:11434"
defaultValue={settings?.OllamaLLMBasePath}
required={true}
@ -35,7 +35,7 @@ export default function OllamaLLMOptions({ settings }) {
<input
type="number"
name="OllamaLLMTokenLimit"
className="bg-zinc-900 text-white placeholder-white placeholder-opacity-60 text-sm rounded-lg focus:border-white block w-full p-2.5"
className="bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:border-white block w-full p-2.5"
placeholder="4096"
min={1}
onScroll={(e) => e.target.blur()}
@ -77,7 +77,7 @@ function OllamaLLMModelSelection({ settings, basePath = null }) {
<select
name="OllamaLLMModelPref"
disabled={true}
className="bg-zinc-900 border border-gray-500 text-white text-sm rounded-lg block w-full p-2.5"
className="bg-zinc-900 border-gray-500 text-white text-sm rounded-lg block w-full p-2.5"
>
<option disabled={true} selected={true}>
{!!basePath
@ -97,7 +97,7 @@ function OllamaLLMModelSelection({ settings, basePath = null }) {
<select
name="OllamaLLMModelPref"
required={true}
className="bg-zinc-900 border border-gray-500 text-white text-sm rounded-lg block w-full p-2.5"
className="bg-zinc-900 border-gray-500 text-white text-sm rounded-lg block w-full p-2.5"
>
{customModels.length > 0 && (
<optgroup label="Your loaded models">

View File

@ -14,7 +14,7 @@ export default function OpenAiOptions({ settings }) {
<input
type="password"
name="OpenAiKey"
className="bg-zinc-900 text-white placeholder-white placeholder-opacity-60 text-sm rounded-lg focus:border-white block w-full p-2.5"
className="bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:border-white block w-full p-2.5"
placeholder="OpenAI API Key"
defaultValue={settings?.OpenAiKey ? "*".repeat(20) : ""}
required={true}
@ -60,7 +60,7 @@ function OpenAIModelSelection({ apiKey, settings }) {
<select
name="OpenAiModelPref"
disabled={true}
className="bg-zinc-900 border border-gray-500 text-white text-sm rounded-lg block w-full p-2.5"
className="bg-zinc-900 border-gray-500 text-white text-sm rounded-lg block w-full p-2.5"
>
<option disabled={true} selected={true}>
-- loading available models --
@ -78,7 +78,7 @@ function OpenAIModelSelection({ apiKey, settings }) {
<select
name="OpenAiModelPref"
required={true}
className="bg-zinc-900 border border-gray-500 text-white text-sm rounded-lg block w-full p-2.5"
className="bg-zinc-900 border-gray-500 text-white text-sm rounded-lg block w-full p-2.5"
>
<optgroup label="General LLM models">
{[

View File

@ -11,7 +11,7 @@ export default function OpenRouterOptions({ settings }) {
<input
type="password"
name="OpenRouterApiKey"
className="bg-zinc-900 text-white placeholder-white placeholder-opacity-60 text-sm rounded-lg focus:border-white block w-full p-2.5"
className="bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:border-white block w-full p-2.5"
placeholder="OpenRouter API Key"
defaultValue={settings?.OpenRouterApiKey ? "*".repeat(20) : ""}
required={true}
@ -56,7 +56,7 @@ function OpenRouterModelSelection({ settings }) {
<select
name="OpenRouterModelPref"
disabled={true}
className="bg-zinc-900 border border-gray-500 text-white text-sm rounded-lg block w-full p-2.5"
className="bg-zinc-900 border-gray-500 text-white text-sm rounded-lg block w-full p-2.5"
>
<option disabled={true} selected={true}>
-- loading available models --
@ -74,7 +74,7 @@ function OpenRouterModelSelection({ settings }) {
<select
name="OpenRouterModelPref"
required={true}
className="bg-zinc-900 border border-gray-500 text-white text-sm rounded-lg block w-full p-2.5"
className="bg-zinc-900 border-gray-500 text-white text-sm rounded-lg block w-full p-2.5"
>
{Object.keys(groupedModels)
.sort()

View File

@ -11,7 +11,7 @@ export default function PerplexityOptions({ settings }) {
<input
type="password"
name="PerplexityApiKey"
className="bg-zinc-900 text-white placeholder-white placeholder-opacity-60 text-sm rounded-lg focus:border-white block w-full p-2.5"
className="bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:border-white block w-full p-2.5"
placeholder="Perplexity API Key"
defaultValue={settings?.PerplexityApiKey ? "*".repeat(20) : ""}
required={true}
@ -47,7 +47,7 @@ function PerplexityModelSelection({ settings }) {
<select
name="PerplexityModelPref"
disabled={true}
className="bg-zinc-900 border border-gray-500 text-white text-sm rounded-lg block w-full p-2.5"
className="bg-zinc-900 border-gray-500 text-white text-sm rounded-lg block w-full p-2.5"
>
<option disabled={true} selected={true}>
-- loading available models --
@ -65,7 +65,7 @@ function PerplexityModelSelection({ settings }) {
<select
name="PerplexityModelPref"
required={true}
className="bg-zinc-900 border border-gray-500 text-white text-sm rounded-lg block w-full p-2.5"
className="bg-zinc-900 border-gray-500 text-white text-sm rounded-lg block w-full p-2.5"
>
{customModels.length > 0 && (
<optgroup label="Available Perplexity Models">

View File

@ -11,7 +11,7 @@ export default function TogetherAiOptions({ settings }) {
<input
type="password"
name="TogetherAiApiKey"
className="bg-zinc-900 text-white placeholder-white placeholder-opacity-60 text-sm rounded-lg focus:border-white block w-full p-2.5"
className="bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:border-white block w-full p-2.5"
placeholder="Together AI API Key"
defaultValue={settings?.TogetherAiApiKey ? "*".repeat(20) : ""}
required={true}
@ -56,7 +56,7 @@ function TogetherAiModelSelection({ settings }) {
<select
name="TogetherAiModelPref"
disabled={true}
className="bg-zinc-900 border border-gray-500 text-white text-sm rounded-lg block w-full p-2.5"
className="bg-zinc-900 border-gray-500 text-white text-sm rounded-lg block w-full p-2.5"
>
<option disabled={true} selected={true}>
-- loading available models --
@ -74,7 +74,7 @@ function TogetherAiModelSelection({ settings }) {
<select
name="TogetherAiModelPref"
required={true}
className="bg-zinc-900 border border-gray-500 text-white text-sm rounded-lg block w-full p-2.5"
className="bg-zinc-900 border-gray-500 text-white text-sm rounded-lg block w-full p-2.5"
>
{Object.keys(groupedModels)
.sort()

View File

@ -3,6 +3,7 @@ import PreLoader from "@/components/Preloader";
import { memo, useEffect, useState } from "react";
import FolderRow from "./FolderRow";
import pluralize from "pluralize";
import System from "@/models/system";
function Directory({
files,
@ -19,6 +20,40 @@ function Directory({
}) {
const [amountSelected, setAmountSelected] = useState(0);
const deleteFiles = async (event) => {
event.stopPropagation();
if (
!window.confirm(
"Are you sure you want to delete these files?\nThis will remove the files from the system and remove them from any existing workspaces automatically.\nThis action is not reversible."
)
) {
return false;
}
try {
const toRemove = [];
for (const itemId of Object.keys(selectedItems)) {
for (const folder of files.items) {
const foundItem = folder.items.find((file) => file.id === itemId);
if (foundItem) {
toRemove.push(`${folder.name}/${foundItem.name}`);
break;
}
}
}
setLoading(true);
setLoadingMessage(`Removing ${toRemove.length} documents. Please wait.`);
await System.deleteDocuments(toRemove);
await fetchKeys(true);
setSelectedItems({});
} catch (error) {
console.error("Failed to delete the document:", error);
} finally {
setLoading(false);
setSelectedItems({});
}
};
const toggleSelection = (item) => {
setSelectedItems((prevSelectedItems) => {
const newSelectedItems = { ...prevSelectedItems };
@ -119,18 +154,24 @@ function Directory({
</div>
{amountSelected !== 0 && (
<div className="absolute bottom-0 left-0 w-full flex justify-center items-center h-9 bg-white rounded-b-2xl">
<div className="flex gap-x-5">
<div
<div className="absolute bottom-0 left-0 w-full flex justify-between items-center h-9 bg-white rounded-b-2xl">
<div className="flex gap-x-5 w-[80%] justify-center">
<button
onMouseEnter={() => setHighlightWorkspace(true)}
onMouseLeave={() => setHighlightWorkspace(false)}
onClick={moveToWorkspace}
className="text-sm font-semibold h-7 px-2.5 rounded-lg transition-all duration-300 hover:text-white hover:bg-neutral-800/80 cursor-pointer flex items-center"
className="border-none text-sm font-semibold h-7 px-2.5 rounded-lg hover:text-white hover:bg-neutral-800/80 flex items-center"
>
Move {amountSelected} {pluralize("file", amountSelected)} to
workspace
</div>
</button>
</div>
<button
onClick={deleteFiles}
className="border-none text-red-500/50 text-sm font-semibold h-7 px-2.5 rounded-lg hover:text-red-500/80 flex items-center"
>
Delete
</button>
</div>
)}
</div>

View File

@ -35,7 +35,7 @@ export default function UploadFile({ workspace, fetchKeys, setLoading }) {
const handleUploadSuccess = () => {
fetchKeys(true);
showToast("File uploaded successfully", "success");
showToast("File uploaded successfully", "success", { clear: true });
};
const handleUploadError = (message) => {
@ -105,7 +105,7 @@ export default function UploadFile({ workspace, fetchKeys, setLoading }) {
</div>
</div>
) : (
<div className="grid grid-cols-2 gap-2 overflow-auto max-h-[400px] p-1 overflow-y-auto">
<div className="grid grid-cols-2 gap-2 overflow-auto max-h-[180px] p-1 overflow-y-scroll no-scroll">
{files.map((file) => (
<FileUploadProgress
key={file.uid}
@ -128,7 +128,7 @@ export default function UploadFile({ workspace, fetchKeys, setLoading }) {
disabled={fetchingUrl}
name="link"
type="url"
className="disabled:bg-zinc-600 disabled:text-slate-300 bg-zinc-900 text-white text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-3/4 p-2.5"
className="disabled:bg-zinc-600 disabled:text-slate-300 bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-3/4 p-2.5"
placeholder={"https://example.com"}
autoComplete="off"
/>

View File

@ -4,12 +4,7 @@ import {
getFileExtension,
middleTruncate,
} from "@/utils/directories";
import {
ArrowUUpLeft,
File,
PushPin,
PushPinSlash,
} from "@phosphor-icons/react";
import { ArrowUUpLeft, File, PushPin } from "@phosphor-icons/react";
import Workspace from "@/models/workspace";
import debounce from "lodash.debounce";
import { Tooltip } from "react-tooltip";
@ -144,28 +139,27 @@ const PinItemToWorkspace = memo(({ workspace, docPath, item }) => {
if (!item) return <div />;
const PinIcon = pinned ? PushPinSlash : PushPin;
return (
<div
onMouseEnter={() => setHover(true)}
onMouseLeave={() => setHover(false)}
className="flex gap-x-2 items-center hover:bg-main-gradient p-[2px] rounded ml-2"
>
<PinIcon
<PushPin
data-tooltip-id={`pin-${item.id}`}
data-tooltip-content={
pinned ? "Unpin document from workspace" : "Pin document to workspace"
pinned ? "Un-Pin from workspace" : "Pin to workspace"
}
size={16}
onClick={updatePinStatus}
weight={hover ? "fill" : "regular"}
className={`outline-none text-base font-bold w-4 h-4 ml-2 flex-shrink-0 cursor-pointer ${
pinned ? "hover:text-red-300" : ""
}`}
weight={hover || pinned ? "fill" : "regular"}
className="outline-none text-base font-bold flex-shrink-0 cursor-pointer"
/>
<Tooltip
id={`pin-${item.id}`}
place="bottom"
delayShow={300}
className="tooltip !text-xs"
className="tooltip invert !text-xs"
/>
</div>
);
@ -184,7 +178,7 @@ const RemoveItemFromWorkspace = ({ item, onClick }) => {
id={`remove-${item.id}`}
place="bottom"
delayShow={300}
className="tooltip !text-xs"
className="tooltip invert !text-xs"
/>
</div>
);

View File

@ -52,7 +52,7 @@ export default function NewWorkspaceModal({ hideModal = noop }) {
name="name"
type="text"
id="name"
className="bg-zinc-900 w-full text-white placeholder-white placeholder-opacity-60 text-sm rounded-lg focus:border-white block w-full p-2.5"
className="bg-zinc-900 w-full text-white placeholder:text-white/20 text-sm rounded-lg focus:border-white block w-full p-2.5"
placeholder="My Workspace"
required={true}
autoComplete="off"

View File

@ -8,6 +8,8 @@ export default function SettingsButton() {
const isInSettings = !!useMatch("/settings/*");
const { user } = useUser();
if (user && user?.role === "default") return null;
if (isInSettings)
return (
<Link

View File

@ -112,9 +112,7 @@ export default function SettingsSidebar() {
<SidebarOptions user={user} />
</div>
</div>
<div>
<Footer />
</div>
<Footer />
</div>
</div>
</div>
@ -151,9 +149,7 @@ export default function SettingsSidebar() {
<SidebarOptions user={user} />
</div>
</div>
<div className="mb-2">
<Footer />
</div>
<Footer />
</div>
</div>
</div>
@ -185,22 +181,22 @@ const Option = ({
return (
<>
<div className="flex gap-x-2 items-center justify-between text-white">
<div className="flex gap-x-2 items-center justify-between">
<Link
to={href}
className={`
transition-all duration-[200ms]
flex flex-grow w-[75%] gap-x-2 py-[6px] px-[12px] rounded-[4px] text-white justify-start items-center
hover:bg-workspace-item-selected-gradient hover:text-white hover:font-bold
flex flex-grow w-[75%] gap-x-2 py-[6px] px-[12px] rounded-[4px] justify-start items-center
hover:bg-workspace-item-selected-gradient hover:text-white hover:font-medium
${
isActive
? "bg-menu-item-selected-gradient font-bold border-outline"
: "hover:bg-menu-item-selected-gradient text-white/30"
? "bg-menu-item-selected-gradient font-medium border-outline text-white"
: "hover:bg-menu-item-selected-gradient text-zinc-200"
}
`}
>
{React.cloneElement(icon, { weight: isActive ? "fill" : "regular" })}
<p className="text-sm leading-loose text-opacity-60 whitespace-nowrap overflow-hidden ">
<p className="text-sm leading-loose whitespace-nowrap overflow-hidden ">
{btnText}
</p>
</Link>

View File

@ -92,8 +92,12 @@ export default function ActiveWorkspaces() {
className={`
transition-all duration-[200ms]
flex flex-grow w-[75%] gap-x-2 py-[6px] px-[12px] rounded-[4px] text-white justify-start items-center
hover:bg-workspace-item-selected-gradient hover:font-bold border-2 border-outline
${isActive && "bg-workspace-item-selected-gradient font-bold"}`}
hover:bg-workspace-item-selected-gradient border-outline
${
isActive
? "bg-workspace-item-selected-gradient font-medium border-none"
: "border-[1px]"
}`}
>
<div className="flex flex-row justify-between w-full">
<div className="flex items-center space-x-2">
@ -103,8 +107,8 @@ export default function ActiveWorkspaces() {
size={24}
/>
<p
className={`text-white text-[14px] leading-loose whitespace-nowrap overflow-hidden ${
isActive ? "" : "text-opacity-80"
className={`text-[14px] leading-loose whitespace-nowrap overflow-hidden ${
isActive ? "text-white " : "text-zinc-200"
}`}
>
{isActive || isHovered

View File

@ -38,10 +38,10 @@ export default function Sidebar() {
<div
ref={sidebarRef}
style={{ height: "calc(100% - 76px)" }}
className="transition-all duration-500 relative m-[16px] rounded-[16px] bg-sidebar border-2 border-outline min-w-[250px] p-[10px]"
className="transition-all pt-[11px] px-[10px] duration-500 relative m-[16px] rounded-[16px] bg-sidebar border-2 border-outline min-w-[250px]"
>
<div className="flex flex-col h-full overflow-x-hidden">
<div className="flex-grow flex flex-col min-w-[235px]">
<div className="flex-grow flex flex-col w-[235px]">
<div className="flex flex-col gap-y-2 pb-8 overflow-y-scroll no-scroll">
<div className="flex gap-x-2 items-center justify-between">
{(!user || user?.role !== "default") && (
@ -144,11 +144,9 @@ export function SidebarMobileHeader() {
style={{ objectFit: "contain" }}
/>
</div>
{(!user || user?.role !== "default") && (
<div className="flex gap-x-2 items-center text-slate-500 shink-0">
<SettingsButton />
</div>
)}
<div className="flex gap-x-2 items-center text-slate-500 shink-0">
<SettingsButton />
</div>
</div>
{/* Primary Body */}

View File

@ -125,7 +125,7 @@ export default function AccountModal({ user, hideModal }) {
<input
name="username"
type="text"
className="bg-zinc-900 border border-gray-500 text-white text-sm rounded-lg 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"
minLength={2}
defaultValue={user.username}
@ -143,7 +143,7 @@ export default function AccountModal({ user, hideModal }) {
<input
name="password"
type="password"
className="bg-zinc-900 border border-gray-500 text-white text-sm rounded-lg 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`}
/>
</div>

View File

@ -61,7 +61,7 @@ export default function UserButton() {
type="button"
className="uppercase transition-all duration-300 w-[35px] h-[35px] text-base font-semibold rounded-full flex items-center bg-sidebar-button hover:bg-menu-item-selected-gradient justify-center text-white p-2 hover:border-slate-100 hover:border-opacity-50 border-transparent border"
>
{mode === "multi" ? userDisplay() : <Person size={14} />}
{mode === "multi" ? <UserDisplay /> : <Person size={14} />}
</button>
{showMenu && (
@ -109,7 +109,7 @@ export default function UserButton() {
);
}
function userDisplay() {
function UserDisplay() {
const { pfp } = usePfp();
const user = userFromStorage();

View File

@ -9,7 +9,7 @@ export default function AstraDBOptions({ settings }) {
<input
type="url"
name="AstraDBEndpoint"
className="bg-zinc-900 text-white placeholder-white placeholder-opacity-60 text-sm rounded-lg focus:border-white block w-full p-2.5"
className="bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:border-white block w-full p-2.5"
placeholder="Astra DB API endpoint"
defaultValue={settings?.AstraDBEndpoint}
required={true}
@ -25,7 +25,7 @@ export default function AstraDBOptions({ settings }) {
<input
type="password"
name="AstraDBApplicationToken"
className="bg-zinc-900 text-white placeholder-white placeholder-opacity-60 text-sm rounded-lg focus:border-white block w-full p-2.5"
className="bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:border-white block w-full p-2.5"
placeholder="AstraCS:..."
defaultValue={
settings?.AstraDBApplicationToken ? "*".repeat(20) : ""

View File

@ -9,7 +9,7 @@ export default function ChromaDBOptions({ settings }) {
<input
type="url"
name="ChromaEndpoint"
className="bg-zinc-900 text-white placeholder-white placeholder-opacity-60 text-sm rounded-lg focus:border-white block w-full p-2.5"
className="bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:border-white block w-full p-2.5"
placeholder="http://localhost:8000"
defaultValue={settings?.ChromaEndpoint}
required={true}
@ -27,7 +27,7 @@ export default function ChromaDBOptions({ settings }) {
autoComplete="off"
type="text"
defaultValue={settings?.ChromaApiHeader}
className="bg-zinc-900 text-white placeholder-white placeholder-opacity-60 text-sm rounded-lg focus:border-white block w-full p-2.5"
className="bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:border-white block w-full p-2.5"
placeholder="X-Api-Key"
/>
</div>
@ -41,7 +41,7 @@ export default function ChromaDBOptions({ settings }) {
autoComplete="off"
type="password"
defaultValue={settings?.ChromaApiKey ? "*".repeat(20) : ""}
className="bg-zinc-900 text-white placeholder-white placeholder-opacity-60 text-sm rounded-lg focus:border-white block w-full p-2.5"
className="bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:border-white block w-full p-2.5"
placeholder="sk-myApiKeyToAccessMyChromaInstance"
/>
</div>

View File

@ -9,7 +9,7 @@ export default function MilvusDBOptions({ settings }) {
<input
type="text"
name="MilvusAddress"
className="bg-zinc-900 text-white placeholder-white placeholder-opacity-60 text-sm rounded-lg focus:border-white block w-full p-2.5"
className="bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:border-white block w-full p-2.5"
placeholder="http://localhost:19530"
defaultValue={settings?.MilvusAddress}
required={true}
@ -25,7 +25,7 @@ export default function MilvusDBOptions({ settings }) {
<input
type="text"
name="MilvusUsername"
className="bg-zinc-900 text-white placeholder-white placeholder-opacity-60 text-sm rounded-lg focus:border-white block w-full p-2.5"
className="bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:border-white block w-full p-2.5"
placeholder="username"
defaultValue={settings?.MilvusUsername}
autoComplete="off"
@ -39,7 +39,7 @@ export default function MilvusDBOptions({ settings }) {
<input
type="password"
name="MilvusPassword"
className="bg-zinc-900 text-white placeholder-white placeholder-opacity-60 text-sm rounded-lg focus:border-white block w-full p-2.5"
className="bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:border-white block w-full p-2.5"
placeholder="password"
defaultValue={settings?.MilvusPassword ? "*".repeat(20) : ""}
autoComplete="off"

View File

@ -9,7 +9,7 @@ export default function PineconeDBOptions({ settings }) {
<input
type="password"
name="PineConeKey"
className="bg-zinc-900 text-white placeholder-white placeholder-opacity-60 text-sm rounded-lg focus:border-white block w-full p-2.5"
className="bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:border-white block w-full p-2.5"
placeholder="Pinecone API Key"
defaultValue={settings?.PineConeKey ? "*".repeat(20) : ""}
required={true}
@ -24,7 +24,7 @@ export default function PineconeDBOptions({ settings }) {
<input
type="text"
name="PineConeIndex"
className="bg-zinc-900 text-white placeholder-white placeholder-opacity-60 text-sm rounded-lg focus:border-white block w-full p-2.5"
className="bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:border-white block w-full p-2.5"
placeholder="my-index"
defaultValue={settings?.PineConeIndex}
required={true}

View File

@ -9,7 +9,7 @@ export default function QDrantDBOptions({ settings }) {
<input
type="url"
name="QdrantEndpoint"
className="bg-zinc-900 text-white placeholder-white placeholder-opacity-60 text-sm rounded-lg focus:border-white block w-full p-2.5"
className="bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:border-white block w-full p-2.5"
placeholder="http://localhost:6633"
defaultValue={settings?.QdrantEndpoint}
required={true}
@ -25,7 +25,7 @@ export default function QDrantDBOptions({ settings }) {
<input
type="password"
name="QdrantApiKey"
className="bg-zinc-900 text-white placeholder-white placeholder-opacity-60 text-sm rounded-lg focus:border-white block w-full p-2.5"
className="bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:border-white block w-full p-2.5"
placeholder="wOeqxsYP4....1244sba"
defaultValue={settings?.QdrantApiKey}
autoComplete="off"

View File

@ -9,7 +9,7 @@ export default function WeaviateDBOptions({ settings }) {
<input
type="url"
name="WeaviateEndpoint"
className="bg-zinc-900 text-white placeholder-white placeholder-opacity-60 text-sm rounded-lg focus:border-white block w-full p-2.5"
className="bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:border-white block w-full p-2.5"
placeholder="http://localhost:8080"
defaultValue={settings?.WeaviateEndpoint}
required={true}
@ -25,7 +25,7 @@ export default function WeaviateDBOptions({ settings }) {
<input
type="password"
name="WeaviateApiKey"
className="bg-zinc-900 text-white placeholder-white placeholder-opacity-60 text-sm rounded-lg focus:border-white block w-full p-2.5"
className="bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:border-white block w-full p-2.5"
placeholder="sk-123Abcweaviate"
defaultValue={settings?.WeaviateApiKey}
autoComplete="off"

View File

@ -9,7 +9,7 @@ export default function ZillizCloudOptions({ settings }) {
<input
type="text"
name="ZillizEndpoint"
className="bg-zinc-900 text-white placeholder-white placeholder-opacity-60 text-sm rounded-lg focus:border-white block w-full p-2.5"
className="bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:border-white block w-full p-2.5"
placeholder="https://sample.api.gcp-us-west1.zillizcloud.com"
defaultValue={settings?.ZillizEndpoint}
required={true}
@ -25,7 +25,7 @@ export default function ZillizCloudOptions({ settings }) {
<input
type="password"
name="ZillizApiToken"
className="bg-zinc-900 text-white placeholder-white placeholder-opacity-60 text-sm rounded-lg focus:border-white block w-full p-2.5"
className="bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:border-white block w-full p-2.5"
placeholder="Zilliz cluster API Token"
defaultValue={settings?.ZillizApiToken ? "*".repeat(20) : ""}
autoComplete="off"

View File

@ -13,12 +13,19 @@ const PROVIDER_DEFAULT_MODELS = {
"gpt-4-32k",
],
gemini: ["gemini-pro"],
anthropic: ["claude-2", "claude-instant-1"],
anthropic: [
"claude-instant-1.2",
"claude-2.0",
"claude-2.1",
"claude-3-opus-20240229",
"claude-3-sonnet-20240229",
],
azure: [],
lmstudio: [],
localai: [],
ollama: [],
togetherai: [],
groq: ["llama2-70b-4096", "mixtral-8x7b-32768"],
native: [],
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -152,6 +152,18 @@ const System = {
return false;
});
},
deleteDocuments: async (names = []) => {
return await fetch(`${API_BASE}/system/remove-documents`, {
method: "DELETE",
headers: baseHeaders(),
body: JSON.stringify({ names }),
})
.then((res) => res.ok)
.catch((e) => {
console.error(e);
return false;
});
},
deleteFolder: async (name) => {
return await fetch(`${API_BASE}/system/remove-folder`, {
method: "DELETE",

View File

@ -49,7 +49,7 @@ export default function NewUserModal({ closeModal }) {
<input
name="username"
type="text"
className="bg-zinc-900 border 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"
minLength={2}
required={true}
@ -66,7 +66,7 @@ export default function NewUserModal({ closeModal }) {
<input
name="password"
type="text"
className="bg-zinc-900 border 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 initial password"
required={true}
autoComplete="off"
@ -84,7 +84,7 @@ export default function NewUserModal({ closeModal }) {
required={true}
defaultValue={"default"}
onChange={(e) => setRole(e.target.value)}
className="rounded-lg bg-zinc-900 px-4 py-2 text-sm text-white border 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"
>
<option value="default">Default</option>
<option value="manager">Manager </option>

View File

@ -50,7 +50,7 @@ export default function EditUserModal({ currentUser, user, closeModal }) {
<input
name="username"
type="text"
className="bg-zinc-900 border 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"
minLength={2}
defaultValue={user.username}
@ -68,7 +68,7 @@ export default function EditUserModal({ currentUser, user, closeModal }) {
<input
name="password"
type="text"
className="bg-zinc-900 border 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`}
autoComplete="off"
/>
@ -85,7 +85,7 @@ export default function EditUserModal({ currentUser, user, closeModal }) {
required={true}
defaultValue={user.role}
onChange={(e) => setRole(e.target.value)}
className="rounded-lg bg-zinc-900 px-4 py-2 text-sm text-white border 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"
>
<option value="default">Default</option>
<option value="manager">Manager</option>

View File

@ -42,7 +42,7 @@ export default function NewWorkspaceModal({ closeModal }) {
<input
name="name"
type="text"
className="bg-zinc-900 border 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="My workspace"
minLength={4}
required={true}

View File

@ -52,7 +52,7 @@ export default function EditWorkspaceUsersModal({
</div>
<form onSubmit={handleUpdate}>
<div className="p-6 space-y-6 flex h-full w-full">
<div className="w-full flex flex-col gap-y-4">
<div className="w-full flex flex-col gap-y-4 max-h-[350px] overflow-y-scroll">
{users
.filter((user) => user.role !== "admin")
.map((user) => {

View File

@ -107,12 +107,14 @@ export default function CustomLogo() {
</div>
</div>
</label>
<button
onClick={handleRemoveLogo}
className="text-white text-base font-medium hover:text-opacity-60"
>
Delete
</button>
{!isDefaultLogo && (
<button
onClick={handleRemoveLogo}
className="text-white text-base font-medium hover:text-opacity-60"
>
Delete
</button>
)}
</div>
</div>
</div>

View File

@ -73,7 +73,7 @@ export default function NewIconForm({ handleSubmit, showing }) {
name="url"
required={true}
placeholder="https://example.com"
className="bg-sidebar text-white placeholder-white/60 rounded-md p-2"
className="bg-sidebar text-white placeholder:text-white/20 rounded-md p-2"
/>
</div>
{selectedIcon !== "" && (

View File

@ -64,7 +64,7 @@ export default function SupportEmail() {
<input
name="supportEmail"
type="email"
className="bg-zinc-900 mt-4 text-white text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 max-w-[275px]"
className="bg-zinc-900 mt-4 text-white placeholder:text-white/20 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 max-w-[275px]"
placeholder="support@mycompany.com"
required={true}
autoComplete="off"

View File

@ -132,7 +132,7 @@ export default function GithubConnectorSetup() {
<input
type="url"
name="repo"
className="bg-zinc-900 text-white placeholder-white placeholder-opacity-60 text-sm rounded-lg focus:border-white block w-full p-2.5"
className="bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:border-white block w-full p-2.5"
placeholder="https://github.com/Mintplex-Labs/anything-llm"
required={true}
autoComplete="off"
@ -156,7 +156,7 @@ export default function GithubConnectorSetup() {
<input
type="text"
name="accessToken"
className="bg-zinc-900 text-white placeholder-white placeholder-opacity-60 text-sm rounded-lg focus:border-white block w-full p-2.5"
className="bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:border-white block w-full p-2.5"
placeholder="github_pat_1234_abcdefg"
required={false}
autoComplete="off"
@ -189,7 +189,7 @@ export default function GithubConnectorSetup() {
classNames={{
tag: "bg-blue-300/10 text-zinc-800 m-1",
input:
"flex bg-zinc-900 text-white placeholder-white placeholder-opacity-60 text-sm rounded-lg focus:border-white p-2.5",
"flex bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:border-white p-2.5",
}}
/>
</div>
@ -257,7 +257,7 @@ function GitHubBranchSelection({ repo, accessToken }) {
<select
name="branch"
required={true}
className="bg-zinc-900 border border-gray-500 text-white text-sm rounded-lg block w-full p-2.5"
className="bg-zinc-900 border-gray-500 text-white text-sm rounded-lg block w-full p-2.5"
>
<option disabled={true} selected={true}>
-- loading available models --
@ -278,7 +278,7 @@ function GitHubBranchSelection({ repo, accessToken }) {
<select
name="branch"
required={true}
className="bg-zinc-900 border border-gray-500 text-white text-sm rounded-lg block w-full p-2.5"
className="bg-zinc-900 border-gray-500 text-white text-sm rounded-lg block w-full p-2.5"
>
{allBranches.map((branch) => {
return (

View File

@ -79,7 +79,7 @@ export default function YouTubeTranscriptConnectorSetup() {
<input
type="url"
name="url"
className="bg-zinc-900 text-white placeholder-white placeholder-opacity-60 text-sm rounded-lg focus:border-white block w-full p-2.5"
className="bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:border-white block w-full p-2.5"
placeholder="https://youtube.com/watch?v=abc123"
required={true}
autoComplete="off"

View File

@ -44,13 +44,13 @@ export default function CodeSnippetModal({ embed, closeModal }) {
}
function createScriptTagSnippet(embed, scriptHost, serverHost) {
return `<!--
return `<!--
Paste this script at the bottom of your HTML before the </body> tag.
See more style and config options on our docs
https://github.com/Mintplex-Labs/anything-llm/tree/master/embed/README.md
-->
<script
data-embed-id="${embed.uuid}"
<script
data-embed-id="${embed.uuid}"
data-base-api-url="${serverHost}/api/embed"
src="${scriptHost}/embed/anythingllm-chat-widget.min.js">
</script>
@ -98,7 +98,7 @@ const ScriptTag = ({ embed }) => {
<button
disabled={copied}
onClick={handleClick}
className="disabled:border disabled:border-green-300 border border-transparent relative w-full font-mono flex bg-zinc-900 text-white placeholder-white placeholder-opacity-60 text-sm rounded-lg focus:border-white p-2.5"
className="disabled:border disabled:border-green-300 border border-transparent relative w-full font-mono flex bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:border-white p-2.5"
>
<div
className="flex w-full text-left flex-col gap-y-1 pr-6 pl-4 whitespace-pre-line"

View File

@ -144,7 +144,7 @@ export const WorkspaceSelection = ({ defaultValue = null }) => {
name="workspace_id"
required={true}
defaultValue={defaultValue}
className="min-w-[15rem] rounded-lg bg-zinc-900 px-4 py-2 text-sm text-white border border-gray-500 focus:ring-blue-500 focus:border-blue-500"
className="min-w-[15rem] rounded-lg bg-zinc-900 px-4 py-2 text-sm text-white focus:ring-blue-500 focus:border-blue-500"
>
{workspaces.map((workspace) => {
return (
@ -274,7 +274,7 @@ export const PermittedDomains = ({ defaultValue = [] }) => {
classNames={{
tag: "bg-blue-300/10 text-zinc-800 m-1",
input:
"flex bg-zinc-900 text-white placeholder-white placeholder-opacity-60 text-sm rounded-lg focus:border-white p-2.5",
"flex bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:border-white p-2.5",
}}
/>
</div>
@ -293,7 +293,7 @@ export const NumberInput = ({ name, title, hint, defaultValue = 0 }) => {
<input
type="number"
name={name}
className="bg-zinc-900 text-white placeholder-white placeholder-opacity-60 text-sm rounded-lg focus:border-white block w-[15rem] p-2.5"
className="bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:border-white block w-[15rem] p-2.5"
min={0}
defaultValue={defaultValue}
onScroll={(e) => e.target.blur()}

View File

@ -7,12 +7,14 @@ import AnythingLLMIcon from "@/media/logo/anything-llm-icon.png";
import OpenAiLogo from "@/media/llmprovider/openai.png";
import AzureOpenAiLogo from "@/media/llmprovider/azure.png";
import LocalAiLogo from "@/media/llmprovider/localai.png";
import OllamaLogo from "@/media/llmprovider/ollama.png";
import PreLoader from "@/components/Preloader";
import ChangeWarningModal from "@/components/ChangeWarning";
import OpenAiOptions from "@/components/EmbeddingSelection/OpenAiOptions";
import AzureAiOptions from "@/components/EmbeddingSelection/AzureAiOptions";
import LocalAiOptions from "@/components/EmbeddingSelection/LocalAiOptions";
import NativeEmbeddingOptions from "@/components/EmbeddingSelection/NativeEmbeddingOptions";
import OllamaEmbeddingOptions from "@/components/EmbeddingSelection/OllamaOptions";
import EmbedderItem from "@/components/EmbeddingSelection/EmbedderItem";
import { MagnifyingGlass } from "@phosphor-icons/react";
import { useModal } from "@/hooks/useModal";
@ -108,6 +110,13 @@ export default function GeneralEmbeddingPreference() {
options: <LocalAiOptions settings={settings} />,
description: "Run embedding models locally on your own machine.",
},
{
name: "Ollama",
value: "ollama",
logo: OllamaLogo,
options: <OllamaEmbeddingOptions settings={settings} />,
description: "Run embedding models locally on your own machine.",
},
];
useEffect(() => {

View File

@ -16,6 +16,7 @@ import MistralLogo from "@/media/llmprovider/mistral.jpeg";
import HuggingFaceLogo from "@/media/llmprovider/huggingface.png";
import PerplexityLogo from "@/media/llmprovider/perplexity.png";
import OpenRouterLogo from "@/media/llmprovider/openrouter.jpeg";
import GroqLogo from "@/media/llmprovider/groq.png";
import PreLoader from "@/components/Preloader";
import OpenAiOptions from "@/components/LLMSelection/OpenAiOptions";
import AzureAiOptions from "@/components/LLMSelection/AzureAiOptions";
@ -28,11 +29,12 @@ import OllamaLLMOptions from "@/components/LLMSelection/OllamaLLMOptions";
import TogetherAiOptions from "@/components/LLMSelection/TogetherAiOptions";
import MistralOptions from "@/components/LLMSelection/MistralOptions";
import HuggingFaceOptions from "@/components/LLMSelection/HuggingFaceOptions";
import PerplexityOptions from "@/components/LLMSelection/PerplexityOptions";
import OpenRouterOptions from "@/components/LLMSelection/OpenRouterOptions";
import GroqAiOptions from "@/components/LLMSelection/GroqAiOptions";
import LLMItem from "@/components/LLMSelection/LLMItem";
import { MagnifyingGlass } from "@phosphor-icons/react";
import PerplexityOptions from "@/components/LLMSelection/PerplexityOptions";
import OpenRouterOptions from "@/components/LLMSelection/OpenRouterOptions";
export default function GeneralLLMPreference() {
const [saving, setSaving] = useState(false);
@ -173,6 +175,14 @@ export default function GeneralLLMPreference() {
options: <OpenRouterOptions settings={settings} />,
description: "A unified interface for LLMs.",
},
{
name: "Groq",
value: "groq",
logo: GroqLogo,
options: <GroqAiOptions settings={settings} />,
description:
"The fastest LLM inferencing available for real-time AI applications.",
},
{
name: "Native",
value: "native",

View File

@ -141,7 +141,7 @@ function MultiUserMode() {
<input
name="username"
type="text"
className="bg-zinc-900 text-white text-sm rounded-lg focus:border-blue-500 block w-full p-2.5 placeholder-white placeholder-opacity-60 focus:ring-blue-500"
className="bg-zinc-900 text-white text-sm rounded-lg focus:border-blue-500 block w-full p-2.5 placeholder:text-white/20 focus:ring-blue-500"
placeholder="Your admin username"
minLength={2}
required={true}
@ -160,7 +160,7 @@ function MultiUserMode() {
<input
name="password"
type="text"
className="bg-zinc-900 text-white text-sm rounded-lg focus:border-blue-500 block w-full p-2.5 placeholder-white placeholder-opacity-60 focus:ring-blue-500"
className="bg-zinc-900 text-white text-sm rounded-lg focus:border-blue-500 block w-full p-2.5 placeholder:text-white/20 focus:ring-blue-500"
placeholder="Your admin password"
minLength={8}
required={true}
@ -303,7 +303,7 @@ function PasswordProtection() {
<input
name="password"
type="text"
className="bg-zinc-900 text-white text-sm rounded-lg focus:border-blue-500 block w-full p-2.5 placeholder-white placeholder-opacity-60 focus:ring-blue-500"
className="bg-zinc-900 text-white text-sm rounded-lg focus:border-blue-500 block w-full p-2.5 placeholder:text-white/20 focus:ring-blue-500"
placeholder="Your Instance Password"
minLength={8}
required={true}

View File

@ -76,7 +76,7 @@ export default function CreateWorkspace({
<input
name="name"
type="text"
className="bg-zinc-900 text-white text-sm rounded-lg block w-full p-2.5"
className="bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg block w-full p-2.5"
placeholder="My Workspace"
minLength={4}
required={true}

View File

@ -58,8 +58,6 @@ export default function CustomLogo({ setHeader, setForwardBtn, setBackBtn }) {
const logoURL = await System.fetchLogo();
_setLogo(logoURL);
showToast("Image uploaded successfully.", "success", { clear: true });
setIsDefaultLogo(false);
};
@ -79,8 +77,6 @@ export default function CustomLogo({ setHeader, setForwardBtn, setBackBtn }) {
const logoURL = await System.fetchLogo();
_setLogo(logoURL);
showToast("Image successfully removed.", "success", { clear: true });
};
return (
@ -123,13 +119,21 @@ export default function CustomLogo({ setHeader, setForwardBtn, setBackBtn }) {
/>
</div>
)}
<button
onClick={handleRemoveLogo}
className="text-white text-base font-medium hover:text-opacity-60 mt-8"
>
Remove logo
</button>
{!isDefaultLogo ? (
<button
onClick={handleRemoveLogo}
className="text-white text-base font-medium hover:text-opacity-60 mt-8"
>
Remove logo
</button>
) : (
<button
onClick={handleForward}
className="text-white text-base font-medium hover:text-opacity-60 mt-8"
>
Skip
</button>
)}
</div>
</div>
);

View File

@ -13,6 +13,7 @@ import MistralLogo from "@/media/llmprovider/mistral.jpeg";
import HuggingFaceLogo from "@/media/llmprovider/huggingface.png";
import PerplexityLogo from "@/media/llmprovider/perplexity.png";
import OpenRouterLogo from "@/media/llmprovider/openrouter.jpeg";
import GroqLogo from "@/media/llmprovider/groq.png";
import ZillizLogo from "@/media/vectordbs/zilliz.png";
import AstraDBLogo from "@/media/vectordbs/astraDB.png";
import ChromaLogo from "@/media/vectordbs/chroma.png";
@ -127,6 +128,14 @@ const LLM_SELECTION_PRIVACY = {
],
logo: OpenRouterLogo,
},
groq: {
name: "Groq",
description: [
"Your chats will not be used for training",
"Your prompts and document text used in response creation are visible to Groq",
],
logo: GroqLogo,
},
};
const VECTOR_DB_PRIVACY = {
@ -221,6 +230,13 @@ const EMBEDDING_ENGINE_PRIVACY = {
],
logo: LocalAiLogo,
},
ollama: {
name: "Ollama",
description: [
"Your document text is embedded privately on the server running Ollama",
],
logo: OllamaLogo,
},
};
export default function DataHandling({ setHeader, setForwardBtn, setBackBtn }) {

View File

@ -4,10 +4,12 @@ import AnythingLLMIcon from "@/media/logo/anything-llm-icon.png";
import OpenAiLogo from "@/media/llmprovider/openai.png";
import AzureOpenAiLogo from "@/media/llmprovider/azure.png";
import LocalAiLogo from "@/media/llmprovider/localai.png";
import OllamaLogo from "@/media/llmprovider/ollama.png";
import NativeEmbeddingOptions from "@/components/EmbeddingSelection/NativeEmbeddingOptions";
import OpenAiOptions from "@/components/EmbeddingSelection/OpenAiOptions";
import AzureAiOptions from "@/components/EmbeddingSelection/AzureAiOptions";
import LocalAiOptions from "@/components/EmbeddingSelection/LocalAiOptions";
import OllamaEmbeddingOptions from "@/components/EmbeddingSelection/OllamaOptions";
import EmbedderItem from "@/components/EmbeddingSelection/EmbedderItem";
import System from "@/models/system";
import paths from "@/utils/paths";
@ -70,6 +72,13 @@ export default function EmbeddingPreference({
options: <LocalAiOptions settings={settings} />,
description: "Run embedding models locally on your own machine.",
},
{
name: "Ollama",
value: "ollama",
logo: OllamaLogo,
options: <OllamaEmbeddingOptions settings={settings} />,
description: "Run embedding models locally on your own machine.",
},
];
function handleForward() {
@ -95,9 +104,6 @@ export default function EmbeddingPreference({
showToast(`Failed to save embedding settings: ${error}`, "error");
return;
}
showToast("Embedder settings saved successfully.", "success", {
clear: true,
});
navigate(paths.onboarding.vectorDatabase());
};

View File

@ -13,6 +13,7 @@ import MistralLogo from "@/media/llmprovider/mistral.jpeg";
import HuggingFaceLogo from "@/media/llmprovider/huggingface.png";
import PerplexityLogo from "@/media/llmprovider/perplexity.png";
import OpenRouterLogo from "@/media/llmprovider/openrouter.jpeg";
import GroqLogo from "@/media/llmprovider/groq.png";
import OpenAiOptions from "@/components/LLMSelection/OpenAiOptions";
import AzureAiOptions from "@/components/LLMSelection/AzureAiOptions";
import AnthropicAiOptions from "@/components/LLMSelection/AnthropicAiOptions";
@ -25,12 +26,13 @@ import MistralOptions from "@/components/LLMSelection/MistralOptions";
import HuggingFaceOptions from "@/components/LLMSelection/HuggingFaceOptions";
import TogetherAiOptions from "@/components/LLMSelection/TogetherAiOptions";
import PerplexityOptions from "@/components/LLMSelection/PerplexityOptions";
import OpenRouterOptions from "@/components/LLMSelection/OpenRouterOptions";
import GroqAiOptions from "@/components/LLMSelection/GroqAiOptions";
import LLMItem from "@/components/LLMSelection/LLMItem";
import System from "@/models/system";
import paths from "@/utils/paths";
import showToast from "@/utils/toast";
import { useNavigate } from "react-router-dom";
import OpenRouterOptions from "@/components/LLMSelection/OpenRouterOptions";
const TITLE = "LLM Preference";
const DESCRIPTION =
@ -147,6 +149,14 @@ export default function LLMPreference({
options: <OpenRouterOptions settings={settings} />,
description: "A unified interface for LLMs.",
},
{
name: "Groq",
value: "groq",
logo: GroqLogo,
options: <GroqAiOptions settings={settings} />,
description:
"The fastest LLM inferencing available for real-time AI applications.",
},
{
name: "Native",
value: "native",
@ -180,7 +190,6 @@ export default function LLMPreference({
showToast(`Failed to save LLM settings: ${error}`, "error");
return;
}
showToast("LLM settings saved successfully.", "success", { clear: true });
navigate(paths.onboarding.embeddingPreference());
};

View File

@ -102,7 +102,7 @@ export default function Survey({ setHeader, setForwardBtn, setBackBtn }) {
type="email"
placeholder="you@gmail.com"
required={true}
className="mt-2 bg-zinc-900 text-white text-sm font-medium font-['Plus Jakarta Sans'] leading-tight w-full h-11 p-2.5 bg-zinc-900 rounded-lg"
className="mt-2 bg-zinc-900 text-white placeholder:text-white/20 text-sm font-medium font-['Plus Jakarta Sans'] leading-tight w-full h-11 p-2.5 bg-zinc-900 rounded-lg"
/>
</div>
@ -269,7 +269,7 @@ export default function Survey({ setHeader, setForwardBtn, setBackBtn }) {
<textarea
name="comment"
rows={5}
className="mt-2 bg-zinc-900 text-white text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5"
className="mt-2 bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5"
placeholder="If you have any questions or comments right now, you can leave them here and we will get back to you. You can also email team@mintplexlabs.com"
wrap="soft"
autoComplete="off"

View File

@ -124,8 +124,6 @@ const JustMe = ({
return;
}
showToast("Password set successfully!", "success", { clear: true });
// Auto-request token with password that was just set so they
// are not redirected to login after completion.
const { token } = await System.requestToken({
@ -245,9 +243,7 @@ const MyTeam = ({ setMultiUserLoginValid, myTeamSubmitRef, navigate }) => {
return;
}
showToast("Multi-user login enabled.", "success", { clear: true });
navigate(paths.onboarding.dataHandling());
// Auto-request token with credentials that was just set so they
// are not redirected to login after completion.
const { user, token } = await System.requestToken(data);

View File

@ -133,9 +133,6 @@ export default function VectorDatabaseConnection({
showToast(`Failed to save Vector Database settings: ${error}`, "error");
return;
}
showToast("Vector Database settings saved successfully.", "success", {
clear: true,
});
navigate(paths.onboarding.customLogo());
};

View File

@ -21,7 +21,7 @@ export default function ChatHistorySettings({ workspace, setHasChanges }) {
step={1}
onWheel={(e) => e.target.blur()}
defaultValue={workspace?.openAiHistory ?? 20}
className="bg-zinc-900 text-white text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5"
className="bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5"
placeholder="20"
required={true}
autoComplete="off"

View File

@ -18,7 +18,7 @@ export default function ChatPromptSettings({ workspace, setHasChanges }) {
name="openAiPrompt"
rows={5}
defaultValue={chatPrompt(workspace)}
className="bg-zinc-900 text-white text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 mt-2"
className="bg-zinc-900 placeholder:text-white/20 text-white text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 mt-2"
placeholder="Given the following conversation, relevant context, and a follow up question, reply with an answer to the current question the user is asking. Return only your response to the question given the above information following the users instructions as needed."
required={true}
wrap="soft"

View File

@ -36,7 +36,7 @@ export default function ChatTemperatureSettings({
step={0.1}
onWheel={(e) => e.target.blur()}
defaultValue={workspace?.openAiTemp ?? defaults.temp}
className="bg-zinc-900 text-white text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5"
className="bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5"
placeholder="0.7"
required={true}
autoComplete="off"

View File

@ -143,7 +143,7 @@ export default function SuggestedChatMessages({ slug }) {
</label>
<input
placeholder="Message heading"
className=" bg-zinc-900 text-white text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block p-2.5 w-full"
className=" bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block p-2.5 w-full"
value={newMessage.heading}
name="heading"
onChange={onEditChange}
@ -155,7 +155,7 @@ export default function SuggestedChatMessages({ slug }) {
</label>
<input
placeholder="Message"
className="bg-zinc-900 text-white text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block p-2.5 w-full"
className="bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block p-2.5 w-full"
value={newMessage.message}
name="message"
onChange={onEditChange}

View File

@ -15,7 +15,7 @@ export default function WorkspaceName({ workspace, setHasChanges }) {
minLength={2}
maxLength={80}
defaultValue={workspace?.name}
className="bg-zinc-900 text-white text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5"
className="bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5"
placeholder="My Workspace"
required={true}
autoComplete="off"

View File

@ -20,7 +20,7 @@ export default function MaxContextSnippets({ workspace, setHasChanges }) {
step={1}
onWheel={(e) => e.target.blur()}
defaultValue={workspace?.topN ?? 4}
className="bg-zinc-900 text-white text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 mt-2"
className="bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 mt-2"
placeholder="4"
required={true}
autoComplete="off"

View File

@ -58,6 +58,10 @@ JWT_SECRET="my-random-string-for-seeding" # Please generate random string at lea
# HUGGING_FACE_LLM_API_KEY=hf_xxxxxx
# HUGGING_FACE_LLM_TOKEN_LIMIT=8000
# LLM_PROVIDER='groq'
# GROQ_API_KEY=gsk_abcxyz
# GROQ_MODEL_PREF=llama2-70b-4096
###########################################
######## Embedding API SElECTION ##########
###########################################
@ -76,6 +80,11 @@ JWT_SECRET="my-random-string-for-seeding" # Please generate random string at lea
# EMBEDDING_MODEL_PREF='text-embedding-ada-002'
# EMBEDDING_MODEL_MAX_CHUNK_LENGTH=1000 # The max chunk size in chars a string to embed can be
# EMBEDDING_ENGINE='ollama'
# EMBEDDING_BASE_PATH='http://127.0.0.1:11434'
# EMBEDDING_MODEL_PREF='nomic-embed-text:latest'
# EMBEDDING_MODEL_MAX_CHUNK_LENGTH=8192
###########################################
######## Vector Database Selection ########
###########################################

View File

@ -1,5 +1,10 @@
const { EventLogs } = require("../../../models/eventLogs");
const { SystemSettings } = require("../../../models/systemSettings");
const { getVectorDbClass } = require("../../../utils/helpers");
const {
prepareWorkspaceChatsForExport,
exportChatsAsType,
} = require("../../../utils/helpers/chat/convertTo");
const { dumpENV, updateENV } = require("../../../utils/helpers/updateENV");
const { reqBody } = require("../../../utils/http");
const { validApiKey } = require("../../../utils/middleware/validApiKey");
@ -147,6 +152,60 @@ function apiSystemEndpoints(app) {
}
}
);
app.get(
"/v1/system/export-chats",
[validApiKey],
async (request, response) => {
/*
#swagger.tags = ['System Settings']
#swagger.description = 'Export all of the chats from the system in a known format. Output depends on the type sent. Will be send with the correct header for the output.'
#swagger.parameters['type'] = {
in: 'query',
description: "Export format jsonl, json, csv, jsonAlpaca",
required: false,
type: 'string'
}
#swagger.responses[200] = {
content: {
"application/json": {
schema: {
type: 'object',
example: [
{
"role": "user",
"content": "What is AnythinglLM?"
},
{
"role": "assistant",
"content": "AnythingLLM is a knowledge graph and vector database management system built using NodeJS express server. It provides an interface for handling all interactions, including vectorDB management and LLM (Language Model) interactions."
},
]
}
}
}
}
#swagger.responses[403] = {
schema: {
"$ref": "#/definitions/InvalidAPIKey"
}
}
*/
try {
const { type = "jsonl" } = request.query;
const chats = await prepareWorkspaceChatsForExport(type);
const { contentType, data } = await exportChatsAsType(chats, type);
await EventLogs.logEvent("exported_chats", {
type,
});
response.setHeader("Content-Type", contentType);
response.status(200).send(data);
} catch (e) {
console.log(e.message, e);
response.sendStatus(500).end();
}
}
);
}
module.exports = { apiSystemEndpoints };

View File

@ -260,6 +260,21 @@ function systemEndpoints(app) {
}
);
app.delete(
"/system/remove-documents",
[validatedRequest, flexUserRoleValid([ROLES.admin, ROLES.manager])],
async (request, response) => {
try {
const { names } = reqBody(request);
for await (const name of names) await purgeDocument(name);
response.sendStatus(200).end();
} catch (e) {
console.log(e.message, e);
response.sendStatus(500).end();
}
}
);
app.delete(
"/system/remove-folder",
[validatedRequest, flexUserRoleValid([ROLES.admin, ROLES.manager])],

View File

@ -219,12 +219,25 @@ const SystemSettings = {
AzureOpenAiEmbeddingModelPref: process.env.EMBEDDING_MODEL_PREF,
}
: {}),
...(llmProvider === "groq"
? {
GroqApiKey: !!process.env.GROQ_API_KEY,
GroqModelPref: process.env.GROQ_MODEL_PREF,
// For embedding credentials when groq is selected.
OpenAiKey: !!process.env.OPEN_AI_KEY,
AzureOpenAiEndpoint: process.env.AZURE_OPENAI_ENDPOINT,
AzureOpenAiKey: !!process.env.AZURE_OPENAI_KEY,
AzureOpenAiEmbeddingModelPref: process.env.EMBEDDING_MODEL_PREF,
}
: {}),
...(llmProvider === "native"
? {
NativeLLMModelPref: process.env.NATIVE_LLM_MODEL_PREF,
NativeLLMTokenLimit: process.env.NATIVE_LLM_MODEL_TOKEN_LIMIT,
// For embedding credentials when ollama is selected.
// For embedding credentials when native is selected.
OpenAiKey: !!process.env.OPEN_AI_KEY,
AzureOpenAiEndpoint: process.env.AZURE_OPENAI_ENDPOINT,
AzureOpenAiKey: !!process.env.AZURE_OPENAI_KEY,

View File

@ -20,7 +20,7 @@
"seed": "node prisma/seed.js"
},
"dependencies": {
"@anthropic-ai/sdk": "^0.8.1",
"@anthropic-ai/sdk": "^0.16.1",
"@azure/openai": "1.0.0-beta.10",
"@datastax/astra-db-ts": "^0.1.3",
"@google/generative-ai": "^0.1.3",

View File

@ -2232,6 +2232,72 @@
}
}
}
},
"/v1/system/export-chats": {
"get": {
"tags": [
"System Settings"
],
"description": "Export all of the chats from the system in a known format. Output depends on the type sent. Will be send with the correct header for the output.",
"parameters": [
{
"name": "Authorization",
"in": "header",
"schema": {
"type": "string"
}
},
{
"name": "type",
"in": "query",
"description": "Export format jsonl, json, csv, jsonAlpaca",
"required": false,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "OK",
"content": {
"application/json": {
"schema": {
"type": "object",
"example": [
{
"role": "user",
"content": "What is AnythinglLM?"
},
{
"role": "assistant",
"content": "AnythingLLM is a knowledge graph and vector database management system built using NodeJS express server. It provides an interface for handling all interactions, including vectorDB management and LLM (Language Model) interactions."
}
]
}
}
}
},
"403": {
"description": "Forbidden",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidAPIKey"
}
},
"application/xml": {
"schema": {
"$ref": "#/components/schemas/InvalidAPIKey"
}
}
}
},
"500": {
"description": "Internal Server Error"
}
}
}
}
},
"components": {

View File

@ -1,6 +1,6 @@
const { v4 } = require("uuid");
const { chatPrompt } = require("../../chats");
const { writeResponseChunk } = require("../../helpers/chat/responses");
class AnthropicLLM {
constructor(embedder = null, modelPreference = null) {
if (!process.env.ANTHROPIC_API_KEY)
@ -13,7 +13,7 @@ class AnthropicLLM {
});
this.anthropic = anthropic;
this.model =
modelPreference || process.env.ANTHROPIC_MODEL_PREF || "claude-2";
modelPreference || process.env.ANTHROPIC_MODEL_PREF || "claude-2.0";
this.limits = {
history: this.promptWindowLimit() * 0.15,
system: this.promptWindowLimit() * 0.15,
@ -35,17 +35,29 @@ class AnthropicLLM {
promptWindowLimit() {
switch (this.model) {
case "claude-instant-1":
return 72_000;
case "claude-2":
case "claude-instant-1.2":
return 100_000;
case "claude-2.0":
return 100_000;
case "claude-2.1":
return 200_000;
case "claude-3-opus-20240229":
return 200_000;
case "claude-3-sonnet-20240229":
return 200_000;
default:
return 72_000; // assume a claude-instant-1 model
return 100_000; // assume a claude-instant-1.2 model
}
}
isValidChatCompletionModel(modelName = "") {
const validModels = ["claude-2", "claude-instant-1"];
const validModels = [
"claude-instant-1.2",
"claude-2.0",
"claude-2.1",
"claude-3-opus-20240229",
"claude-3-sonnet-20240229",
];
return validModels.includes(modelName);
}
@ -62,36 +74,43 @@ class AnthropicLLM {
chatHistory = [],
userPrompt = "",
}) {
return `\n\nHuman: Please read question supplied within the <question> tags. Using all information generate an answer to the question and output it within <${
this.answerKey
}> tags. Previous conversations can be used within the <history> tags and can be used to influence the output. Content between the <system> tag is additional information and instruction that will impact how answers are formatted or responded to. Additional contextual information retrieved to help answer the users specific query is available to use for answering and can be found between <context> tags. When no <context> tags may are present use the knowledge available and in the conversation to answer. When one or more <context> tags are available you will use those to help answer the question or augment pre-existing knowledge. You should never say "Based on the provided context" or other phrasing that is not related to the user question.
<system>${systemPrompt}</system>
${contextTexts
.map((text, i) => {
return `<context>${text}</context>\n`;
})
.join("")}
<history>${chatHistory.map((history) => {
switch (history.role) {
case "assistant":
return `\n\nAssistant: ${history.content}`;
case "user":
return `\n\nHuman: ${history.content}`;
default:
return "\n";
}
})}</history>
<question>${userPrompt}</question>
\n\nAssistant:`;
const prompt = {
role: "system",
content: `${systemPrompt}${this.#appendContext(contextTexts)}`,
};
return [prompt, ...chatHistory, { role: "user", content: userPrompt }];
}
async sendChat(chatHistory = [], prompt, workspace = {}, rawHistory = []) {
async getChatCompletion(messages = null, { temperature = 0.7 }) {
if (!this.isValidChatCompletionModel(this.model))
throw new Error(
`Anthropic chat: ${this.model} is not valid for chat completion!`
);
const compressedPrompt = await this.compressMessages(
try {
const response = await this.anthropic.messages.create({
model: this.model,
max_tokens: 4096,
system: messages[0].content, // Strip out the system message
messages: messages.slice(1), // Pop off the system message
temperature: Number(temperature ?? this.defaultTemp),
});
return response.content[0].text;
} catch (error) {
console.log(error);
return error;
}
}
async streamChat(chatHistory = [], prompt, workspace = {}, rawHistory = []) {
if (!this.isValidChatCompletionModel(this.model))
throw new Error(
`Anthropic chat: ${this.model} is not valid for chat completion!`
);
const messages = await this.compressMessages(
{
systemPrompt: chatPrompt(workspace),
userPrompt: prompt,
@ -99,58 +118,85 @@ class AnthropicLLM {
},
rawHistory
);
const { content, error } = await this.anthropic.completions
.create({
model: this.model,
max_tokens_to_sample: 300,
prompt: compressedPrompt,
})
.then((res) => {
const { completion } = res;
const re = new RegExp(
"(?:<" + this.answerKey + ">)([\\s\\S]*)(?:</" + this.answerKey + ">)"
);
const response = completion.match(re)?.[1]?.trim();
if (!response)
throw new Error("Anthropic: No response could be parsed.");
return { content: response, error: null };
})
.catch((e) => {
return { content: null, error: e.message };
});
if (error) throw new Error(error);
return content;
const streamRequest = await this.anthropic.messages.stream({
model: this.model,
max_tokens: 4096,
system: messages[0].content, // Strip out the system message
messages: messages.slice(1), // Pop off the system message
temperature: Number(workspace?.openAiTemp ?? this.defaultTemp),
});
return streamRequest;
}
async getChatCompletion(prompt = "", _opts = {}) {
async streamGetChatCompletion(messages = null, { temperature = 0.7 }) {
if (!this.isValidChatCompletionModel(this.model))
throw new Error(
`Anthropic chat: ${this.model} is not valid for chat completion!`
`OpenAI chat: ${this.model} is not valid for chat completion!`
);
const { content, error } = await this.anthropic.completions
.create({
model: this.model,
max_tokens_to_sample: 300,
prompt,
})
.then((res) => {
const { completion } = res;
const re = new RegExp(
"(?:<" + this.answerKey + ">)([\\s\\S]*)(?:</" + this.answerKey + ">)"
);
const response = completion.match(re)?.[1]?.trim();
if (!response)
throw new Error("Anthropic: No response could be parsed.");
return { content: response, error: null };
})
.catch((e) => {
return { content: null, error: e.message };
});
const streamRequest = await this.anthropic.messages.stream({
model: this.model,
max_tokens: 4096,
system: messages[0].content, // Strip out the system message
messages: messages.slice(1), // Pop off the system message
temperature: Number(temperature ?? this.defaultTemp),
});
return streamRequest;
}
if (error) throw new Error(error);
return content;
handleStream(response, stream, responseProps) {
return new Promise((resolve) => {
let fullText = "";
const { uuid = v4(), sources = [] } = responseProps;
stream.on("streamEvent", (message) => {
const data = message;
if (
data.type === "content_block_delta" &&
data.delta.type === "text_delta"
) {
const text = data.delta.text;
fullText += text;
writeResponseChunk(response, {
uuid,
sources,
type: "textResponseChunk",
textResponse: text,
close: false,
error: false,
});
}
if (
message.type === "message_stop" ||
(data.stop_reason && data.stop_reason === "end_turn")
) {
writeResponseChunk(response, {
uuid,
sources,
type: "textResponseChunk",
textResponse: "",
close: true,
error: false,
});
resolve(fullText);
}
});
});
}
#appendContext(contextTexts = []) {
if (!contextTexts || !contextTexts.length) return "";
return (
"\nContext:\n" +
contextTexts
.map((text, i) => {
return `[CONTEXT ${i}]:\n${text}\n[END CONTEXT ${i}]\n\n`;
})
.join("")
);
}
async compressMessages(promptArgs = {}, rawHistory = []) {

View File

@ -0,0 +1,207 @@
const { NativeEmbedder } = require("../../EmbeddingEngines/native");
const { chatPrompt } = require("../../chats");
const { handleDefaultStreamResponse } = require("../../helpers/chat/responses");
class GroqLLM {
constructor(embedder = null, modelPreference = null) {
const { Configuration, OpenAIApi } = require("openai");
if (!process.env.GROQ_API_KEY) throw new Error("No Groq API key was set.");
const config = new Configuration({
basePath: "https://api.groq.com/openai/v1",
apiKey: process.env.GROQ_API_KEY,
});
this.openai = new OpenAIApi(config);
this.model =
modelPreference || process.env.GROQ_MODEL_PREF || "llama2-70b-4096";
this.limits = {
history: this.promptWindowLimit() * 0.15,
system: this.promptWindowLimit() * 0.15,
user: this.promptWindowLimit() * 0.7,
};
this.embedder = !embedder ? new NativeEmbedder() : embedder;
this.defaultTemp = 0.7;
}
#appendContext(contextTexts = []) {
if (!contextTexts || !contextTexts.length) return "";
return (
"\nContext:\n" +
contextTexts
.map((text, i) => {
return `[CONTEXT ${i}]:\n${text}\n[END CONTEXT ${i}]\n\n`;
})
.join("")
);
}
streamingEnabled() {
return "streamChat" in this && "streamGetChatCompletion" in this;
}
promptWindowLimit() {
switch (this.model) {
case "llama2-70b-4096":
return 4096;
case "mixtral-8x7b-32768":
return 32_768;
default:
return 4096;
}
}
async isValidChatCompletionModel(modelName = "") {
const validModels = ["llama2-70b-4096", "mixtral-8x7b-32768"];
const isPreset = validModels.some((model) => modelName === model);
if (isPreset) return true;
const model = await this.openai
.retrieveModel(modelName)
.then((res) => res.data)
.catch(() => null);
return !!model;
}
constructPrompt({
systemPrompt = "",
contextTexts = [],
chatHistory = [],
userPrompt = "",
}) {
const prompt = {
role: "system",
content: `${systemPrompt}${this.#appendContext(contextTexts)}`,
};
return [prompt, ...chatHistory, { role: "user", content: userPrompt }];
}
async isSafe(_input = "") {
// Not implemented so must be stubbed
return { safe: true, reasons: [] };
}
async sendChat(chatHistory = [], prompt, workspace = {}, rawHistory = []) {
if (!(await this.isValidChatCompletionModel(this.model)))
throw new Error(
`Groq chat: ${this.model} is not valid for chat completion!`
);
const textResponse = await this.openai
.createChatCompletion({
model: this.model,
temperature: Number(workspace?.openAiTemp ?? this.defaultTemp),
n: 1,
messages: await this.compressMessages(
{
systemPrompt: chatPrompt(workspace),
userPrompt: prompt,
chatHistory,
},
rawHistory
),
})
.then((json) => {
const res = json.data;
if (!res.hasOwnProperty("choices"))
throw new Error("GroqAI chat: No results!");
if (res.choices.length === 0)
throw new Error("GroqAI chat: No results length!");
return res.choices[0].message.content;
})
.catch((error) => {
throw new Error(
`GroqAI::createChatCompletion failed with: ${error.message}`
);
});
return textResponse;
}
async streamChat(chatHistory = [], prompt, workspace = {}, rawHistory = []) {
if (!(await this.isValidChatCompletionModel(this.model)))
throw new Error(
`GroqAI:streamChat: ${this.model} is not valid for chat completion!`
);
const streamRequest = await this.openai.createChatCompletion(
{
model: this.model,
stream: true,
temperature: Number(workspace?.openAiTemp ?? this.defaultTemp),
n: 1,
messages: await this.compressMessages(
{
systemPrompt: chatPrompt(workspace),
userPrompt: prompt,
chatHistory,
},
rawHistory
),
},
{ responseType: "stream" }
);
return streamRequest;
}
async getChatCompletion(messages = null, { temperature = 0.7 }) {
if (!(await this.isValidChatCompletionModel(this.model)))
throw new Error(
`GroqAI:chatCompletion: ${this.model} is not valid for chat completion!`
);
const { data } = await this.openai
.createChatCompletion({
model: this.model,
messages,
temperature,
})
.catch((e) => {
throw new Error(e.response.data.error.message);
});
if (!data.hasOwnProperty("choices")) return null;
return data.choices[0].message.content;
}
async streamGetChatCompletion(messages = null, { temperature = 0.7 }) {
if (!(await this.isValidChatCompletionModel(this.model)))
throw new Error(
`GroqAI:streamChatCompletion: ${this.model} is not valid for chat completion!`
);
const streamRequest = await this.openai.createChatCompletion(
{
model: this.model,
stream: true,
messages,
temperature,
},
{ responseType: "stream" }
);
return streamRequest;
}
handleStream(response, stream, responseProps) {
return handleDefaultStreamResponse(response, stream, responseProps);
}
// Simple wrapper for dynamic embedder & normalize interface for all LLM implementations
async embedTextInput(textInput) {
return await this.embedder.embedTextInput(textInput);
}
async embedChunks(textChunks = []) {
return await this.embedder.embedChunks(textChunks);
}
async compressMessages(promptArgs = {}, rawHistory = []) {
const { messageArrayCompressor } = require("../../helpers/chat");
const messageArray = this.constructPrompt(promptArgs);
return await messageArrayCompressor(this, messageArray, rawHistory);
}
}
module.exports = {
GroqLLM,
};

View File

@ -0,0 +1,90 @@
const { maximumChunkLength } = require("../../helpers");
class OllamaEmbedder {
constructor() {
if (!process.env.EMBEDDING_BASE_PATH)
throw new Error("No embedding base path was set.");
if (!process.env.EMBEDDING_MODEL_PREF)
throw new Error("No embedding model was set.");
this.basePath = `${process.env.EMBEDDING_BASE_PATH}/api/embeddings`;
this.model = process.env.EMBEDDING_MODEL_PREF;
// Limit of how many strings we can process in a single pass to stay with resource or network limits
this.maxConcurrentChunks = 1;
this.embeddingMaxChunkLength = maximumChunkLength();
}
log(text, ...args) {
console.log(`\x1b[36m[${this.constructor.name}]\x1b[0m ${text}`, ...args);
}
async embedTextInput(textInput) {
const result = await this.embedChunks([textInput]);
return result?.[0] || [];
}
async embedChunks(textChunks = []) {
const embeddingRequests = [];
this.log(
`Embedding ${textChunks.length} chunks of text with ${this.model}.`
);
for (const chunk of textChunks) {
embeddingRequests.push(
new Promise((resolve) => {
fetch(this.basePath, {
method: "POST",
body: JSON.stringify({
model: this.model,
prompt: chunk,
}),
})
.then((res) => res.json())
.then(({ embedding }) => {
resolve({ data: embedding, error: null });
return;
})
.catch((error) => {
resolve({ data: [], error: error.message });
return;
});
})
);
}
const { data = [], error = null } = await Promise.all(
embeddingRequests
).then((results) => {
// If any errors were returned from Ollama abort the entire sequence because the embeddings
// will be incomplete.
const errors = results
.filter((res) => !!res.error)
.map((res) => res.error)
.flat();
if (errors.length > 0) {
let uniqueErrors = new Set();
errors.map((error) =>
uniqueErrors.add(`[${error.type}]: ${error.message}`)
);
return {
data: [],
error: Array.from(uniqueErrors).join(", "),
};
}
return {
data: results.map((res) => res?.data || []),
error: null,
};
});
if (!!error) throw new Error(`Ollama Failed to embed: ${error}`);
return data.length > 0 ? data : null;
}
}
module.exports = {
OllamaEmbedder,
};

View File

@ -21,12 +21,8 @@ async function convertToCSV(preparedData) {
return rows.join("\n");
}
async function convertToJSON(workspaceChatsMap) {
const allMessages = [].concat.apply(
[],
Object.values(workspaceChatsMap).map((workspace) => workspace.messages)
);
return JSON.stringify(allMessages, null, 4);
async function convertToJSON(preparedData) {
return JSON.stringify(preparedData, null, 4);
}
// ref: https://raw.githubusercontent.com/gururise/AlpacaDataCleaned/main/alpaca_data.json
@ -48,7 +44,7 @@ async function prepareWorkspaceChatsForExport(format = "jsonl") {
id: "asc",
});
if (format === "csv") {
if (format === "csv" || format === "json") {
const preparedData = chats.map((chat) => {
const responseJson = JSON.parse(chat.response);
return {

View File

@ -73,6 +73,9 @@ function getLLMProvider(modelPreference = null) {
case "huggingface":
const { HuggingFaceLLM } = require("../AiProviders/huggingface");
return new HuggingFaceLLM(embedder, modelPreference);
case "groq":
const { GroqLLM } = require("../AiProviders/groq");
return new GroqLLM(embedder, modelPreference);
default:
throw new Error("ENV: No LLM_PROVIDER value found in environment!");
}
@ -92,6 +95,9 @@ function getEmbeddingEngineSelection() {
case "localai":
const { LocalAiEmbedder } = require("../EmbeddingEngines/localAi");
return new LocalAiEmbedder();
case "ollama":
const { OllamaEmbedder } = require("../EmbeddingEngines/ollama");
return new OllamaEmbedder();
case "native":
const { NativeEmbedder } = require("../EmbeddingEngines/native");
console.log("\x1b[34m[INFO]\x1b[0m Using Native Embedder");

View File

@ -135,7 +135,7 @@ const KEY_MAPPING = {
},
EmbeddingBasePath: {
envKey: "EMBEDDING_BASE_PATH",
checks: [isNotEmpty, validLLMExternalBasePath, validDockerizedUrl],
checks: [isNotEmpty, validDockerizedUrl],
},
EmbeddingModelPref: {
envKey: "EMBEDDING_MODEL_PREF",
@ -259,6 +259,16 @@ const KEY_MAPPING = {
checks: [isNotEmpty],
},
// Groq Options
GroqApiKey: {
envKey: "GROQ_API_KEY",
checks: [isNotEmpty],
},
GroqModelPref: {
envKey: "GROQ_MODEL_PREF",
checks: [isNotEmpty],
},
// System Settings
AuthToken: {
envKey: "AUTH_TOKEN",
@ -336,6 +346,7 @@ function supportedLLM(input = "") {
"huggingface",
"perplexity",
"openrouter",
"groq",
].includes(input);
return validSelection ? null : `${input} is not a valid LLM provider.`;
}
@ -348,14 +359,20 @@ function validGeminiModel(input = "") {
}
function validAnthropicModel(input = "") {
const validModels = ["claude-2", "claude-instant-1"];
const validModels = [
"claude-instant-1.2",
"claude-2.0",
"claude-2.1",
"claude-3-opus-20240229",
"claude-3-sonnet-20240229",
];
return validModels.includes(input)
? null
: `Invalid Model type. Must be one of ${validModels.join(", ")}.`;
}
function supportedEmbeddingModel(input = "") {
const supported = ["openai", "azure", "localai", "native"];
const supported = ["openai", "azure", "localai", "native", "ollama"];
return supported.includes(input)
? null
: `Invalid Embedding model type. Must be one of ${supported.join(", ")}.`;

View File

@ -7,6 +7,7 @@ const {
getLLMProvider,
getEmbeddingEngineSelection,
} = require("../../helpers");
const { parseAuthHeader } = require("../../http");
const Chroma = {
name: "Chroma",

View File

@ -7,10 +7,10 @@
resolved "https://registry.yarnpkg.com/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz#bd9154aec9983f77b3a034ecaa015c2e4201f6cf"
integrity sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==
"@anthropic-ai/sdk@^0.8.1":
version "0.8.1"
resolved "https://registry.yarnpkg.com/@anthropic-ai/sdk/-/sdk-0.8.1.tgz#7c7c6cb262abe3e6d0bb8bd1179b4589edd7a6ad"
integrity sha512-59etePenCizVx1O8Qhi1T1ruE04ISfNzCnyhZNcsss1QljsLmYS83jttarMNEvGYcsUF7rwxw2lzcC3Zbxao7g==
"@anthropic-ai/sdk@^0.16.1":
version "0.16.1"
resolved "https://registry.yarnpkg.com/@anthropic-ai/sdk/-/sdk-0.16.1.tgz#7472c42389d9a5323c20afa53995e1c3b922b95d"
integrity sha512-vHgvfWEyFy5ktqam56Nrhv8MVa7EJthsRYNi+1OrFFfyrj9tR2/aji1QbVbQjYU/pPhPFaYrdCEC/MLPFrmKwA==
dependencies:
"@types/node" "^18.11.18"
"@types/node-fetch" "^2.6.4"