mirror of
https://github.com/Mintplex-Labs/anything-llm.git
synced 2024-11-19 20:50:09 +01:00
merge with master
This commit is contained in:
commit
c60077a078
@ -5,7 +5,7 @@
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<b>AnythingLLM: The all-in-one AI app you were looking for.<br />
|
<b>AnythingLLM:</b> The all-in-one AI app you were looking for.<br />
|
||||||
Chat with your docs, use AI Agents, hyper-configurable, multi-user, & no fustrating set up required.
|
Chat with your docs, use AI Agents, hyper-configurable, multi-user, & no fustrating set up required.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
@ -22,7 +22,6 @@ class OpenAiWhisper {
|
|||||||
.create({
|
.create({
|
||||||
file: fs.createReadStream(fullFilePath),
|
file: fs.createReadStream(fullFilePath),
|
||||||
model: this.model,
|
model: this.model,
|
||||||
model: "whisper-1",
|
|
||||||
response_format: "text",
|
response_format: "text",
|
||||||
temperature: this.temperature,
|
temperature: this.temperature,
|
||||||
})
|
})
|
||||||
|
@ -66,11 +66,8 @@ async function loadConfluence({ pageUrl, username, accessToken }) {
|
|||||||
const outFolder = slugify(
|
const outFolder = slugify(
|
||||||
`${subdomain}-confluence-${v4().slice(0, 4)}`
|
`${subdomain}-confluence-${v4().slice(0, 4)}`
|
||||||
).toLowerCase();
|
).toLowerCase();
|
||||||
const outFolderPath = path.resolve(
|
const outFolderPath = path.resolve(documentsFolder, outFolder);
|
||||||
__dirname,
|
if (!fs.existsSync(outFolderPath)) fs.mkdirSync(outFolderPath, { recursive: true });
|
||||||
`../../../../server/storage/documents/${outFolder}`
|
|
||||||
);
|
|
||||||
fs.mkdirSync(outFolderPath);
|
|
||||||
|
|
||||||
docs.forEach((doc) => {
|
docs.forEach((doc) => {
|
||||||
const data = {
|
const data = {
|
||||||
|
@ -32,7 +32,7 @@ async function loadGithubRepo(args) {
|
|||||||
`${repo.author}-${repo.project}-${repo.branch}-${v4().slice(0, 4)}`
|
`${repo.author}-${repo.project}-${repo.branch}-${v4().slice(0, 4)}`
|
||||||
).toLowerCase();
|
).toLowerCase();
|
||||||
const outFolderPath = path.resolve(documentsFolder, outFolder);
|
const outFolderPath = path.resolve(documentsFolder, outFolder);
|
||||||
fs.mkdirSync(outFolderPath);
|
if (!fs.existsSync(outFolderPath)) fs.mkdirSync(outFolderPath, { recursive: true });
|
||||||
|
|
||||||
for (const doc of docs) {
|
for (const doc of docs) {
|
||||||
if (!doc.pageContent) continue;
|
if (!doc.pageContent) continue;
|
||||||
|
@ -9,8 +9,7 @@ const { YoutubeLoader } = require("./YoutubeLoader");
|
|||||||
function validYoutubeVideoUrl(link) {
|
function validYoutubeVideoUrl(link) {
|
||||||
const UrlPattern = require("url-pattern");
|
const UrlPattern = require("url-pattern");
|
||||||
const opts = new URL(link);
|
const opts = new URL(link);
|
||||||
const url = `${opts.protocol}//${opts.host}${opts.pathname}${
|
const url = `${opts.protocol}//${opts.host}${opts.pathname}${opts.searchParams.has("v") ? `?v=${opts.searchParams.get("v")}` : ""
|
||||||
opts.searchParams.has("v") ? `?v=${opts.searchParams.get("v")}` : ""
|
|
||||||
}`;
|
}`;
|
||||||
|
|
||||||
const shortPatternMatch = new UrlPattern(
|
const shortPatternMatch = new UrlPattern(
|
||||||
@ -68,7 +67,7 @@ async function loadYouTubeTranscript({ url }) {
|
|||||||
`${metadata.author} YouTube transcripts`
|
`${metadata.author} YouTube transcripts`
|
||||||
).toLowerCase();
|
).toLowerCase();
|
||||||
const outFolderPath = path.resolve(documentsFolder, outFolder);
|
const outFolderPath = path.resolve(documentsFolder, outFolder);
|
||||||
if (!fs.existsSync(outFolderPath)) fs.mkdirSync(outFolderPath);
|
if (!fs.existsSync(outFolderPath)) fs.mkdirSync(outFolderPath, { recursive: true });
|
||||||
|
|
||||||
const data = {
|
const data = {
|
||||||
id: v4(),
|
id: v4(),
|
||||||
|
@ -66,12 +66,21 @@ GID='1000'
|
|||||||
# GROQ_API_KEY=gsk_abcxyz
|
# GROQ_API_KEY=gsk_abcxyz
|
||||||
# GROQ_MODEL_PREF=llama3-8b-8192
|
# GROQ_MODEL_PREF=llama3-8b-8192
|
||||||
|
|
||||||
|
# LLM_PROVIDER='koboldcpp'
|
||||||
|
# KOBOLD_CPP_BASE_PATH='http://127.0.0.1:5000/v1'
|
||||||
|
# KOBOLD_CPP_MODEL_PREF='koboldcpp/codellama-7b-instruct.Q4_K_S'
|
||||||
|
# KOBOLD_CPP_MODEL_TOKEN_LIMIT=4096
|
||||||
|
|
||||||
# LLM_PROVIDER='generic-openai'
|
# LLM_PROVIDER='generic-openai'
|
||||||
# GENERIC_OPEN_AI_BASE_PATH='http://proxy.url.openai.com/v1'
|
# GENERIC_OPEN_AI_BASE_PATH='http://proxy.url.openai.com/v1'
|
||||||
# GENERIC_OPEN_AI_MODEL_PREF='gpt-3.5-turbo'
|
# GENERIC_OPEN_AI_MODEL_PREF='gpt-3.5-turbo'
|
||||||
# GENERIC_OPEN_AI_MODEL_TOKEN_LIMIT=4096
|
# GENERIC_OPEN_AI_MODEL_TOKEN_LIMIT=4096
|
||||||
# GENERIC_OPEN_AI_API_KEY=sk-123abc
|
# GENERIC_OPEN_AI_API_KEY=sk-123abc
|
||||||
|
|
||||||
|
# LLM_PROVIDER='cohere'
|
||||||
|
# COHERE_API_KEY=
|
||||||
|
# COHERE_MODEL_PREF='command-r'
|
||||||
|
|
||||||
###########################################
|
###########################################
|
||||||
######## Embedding API SElECTION ##########
|
######## Embedding API SElECTION ##########
|
||||||
###########################################
|
###########################################
|
||||||
@ -100,6 +109,10 @@ GID='1000'
|
|||||||
# EMBEDDING_MODEL_PREF='nomic-ai/nomic-embed-text-v1.5-GGUF/nomic-embed-text-v1.5.Q4_0.gguf'
|
# EMBEDDING_MODEL_PREF='nomic-ai/nomic-embed-text-v1.5-GGUF/nomic-embed-text-v1.5.Q4_0.gguf'
|
||||||
# EMBEDDING_MODEL_MAX_CHUNK_LENGTH=8192
|
# EMBEDDING_MODEL_MAX_CHUNK_LENGTH=8192
|
||||||
|
|
||||||
|
# EMBEDDING_ENGINE='cohere'
|
||||||
|
# COHERE_API_KEY=
|
||||||
|
# EMBEDDING_MODEL_PREF='embed-english-v3.0'
|
||||||
|
|
||||||
###########################################
|
###########################################
|
||||||
######## Vector Database Selection ########
|
######## Vector Database Selection ########
|
||||||
###########################################
|
###########################################
|
||||||
|
@ -4,7 +4,7 @@ import { staticHljs as hljs } from "./hljs";
|
|||||||
import { v4 } from "uuid";
|
import { v4 } from "uuid";
|
||||||
|
|
||||||
const markdown = markdownIt({
|
const markdown = markdownIt({
|
||||||
html: true,
|
html: false,
|
||||||
typographer: true,
|
typographer: true,
|
||||||
highlight: function (code, lang) {
|
highlight: function (code, lang) {
|
||||||
const uuid = v4();
|
const uuid = v4();
|
||||||
|
@ -0,0 +1,55 @@
|
|||||||
|
export default function CohereEmbeddingOptions({ settings }) {
|
||||||
|
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">
|
||||||
|
API Key
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="password"
|
||||||
|
name="CohereApiKey"
|
||||||
|
className="bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:border-white block w-full p-2.5"
|
||||||
|
placeholder="Cohere API Key"
|
||||||
|
defaultValue={settings?.CohereApiKey ? "*".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">
|
||||||
|
Model Preference
|
||||||
|
</label>
|
||||||
|
<select
|
||||||
|
name="EmbeddingModelPref"
|
||||||
|
required={true}
|
||||||
|
className="bg-zinc-900 border-gray-500 text-white text-sm rounded-lg block w-full p-2.5"
|
||||||
|
>
|
||||||
|
<optgroup label="Available embedding models">
|
||||||
|
{[
|
||||||
|
"embed-english-v3.0",
|
||||||
|
"embed-multilingual-v3.0",
|
||||||
|
"embed-english-light-v3.0",
|
||||||
|
"embed-multilingual-light-v3.0",
|
||||||
|
"embed-english-v2.0",
|
||||||
|
"embed-english-light-v2.0",
|
||||||
|
"embed-multilingual-v2.0",
|
||||||
|
].map((model) => {
|
||||||
|
return (
|
||||||
|
<option
|
||||||
|
key={model}
|
||||||
|
value={model}
|
||||||
|
selected={settings?.EmbeddingModelPref === model}
|
||||||
|
>
|
||||||
|
{model}
|
||||||
|
</option>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</optgroup>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
@ -0,0 +1,49 @@
|
|||||||
|
export default function CohereAiOptions({ settings }) {
|
||||||
|
return (
|
||||||
|
<div className="w-full flex flex-col">
|
||||||
|
<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">
|
||||||
|
Cohere API Key
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="password"
|
||||||
|
name="CohereApiKey"
|
||||||
|
className="bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:border-white block w-full p-2.5"
|
||||||
|
placeholder="Cohere API Key"
|
||||||
|
defaultValue={settings?.CohereApiKey ? "*".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="CohereModelPref"
|
||||||
|
defaultValue={settings?.CohereModelPref || "command-r"}
|
||||||
|
required={true}
|
||||||
|
className="bg-zinc-900 border-gray-500 text-white text-sm rounded-lg block w-full p-2.5"
|
||||||
|
>
|
||||||
|
{[
|
||||||
|
"command-r",
|
||||||
|
"command-r-plus",
|
||||||
|
"command",
|
||||||
|
"command-light",
|
||||||
|
"command-nightly",
|
||||||
|
"command-light-nightly",
|
||||||
|
].map((model) => {
|
||||||
|
return (
|
||||||
|
<option key={model} value={model}>
|
||||||
|
{model}
|
||||||
|
</option>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
112
frontend/src/components/LLMSelection/KoboldCPPOptions/index.jsx
Normal file
112
frontend/src/components/LLMSelection/KoboldCPPOptions/index.jsx
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
import { useState, useEffect } from "react";
|
||||||
|
import System from "@/models/system";
|
||||||
|
|
||||||
|
export default function KoboldCPPOptions({ settings }) {
|
||||||
|
const [basePathValue, setBasePathValue] = useState(
|
||||||
|
settings?.KoboldCPPBasePath
|
||||||
|
);
|
||||||
|
const [basePath, setBasePath] = useState(settings?.KoboldCPPBasePath);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex gap-4 flex-wrap">
|
||||||
|
<div className="flex flex-col w-60">
|
||||||
|
<label className="text-white text-sm font-semibold block mb-4">
|
||||||
|
Base URL
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="url"
|
||||||
|
name="KoboldCPPBasePath"
|
||||||
|
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:5000/v1"
|
||||||
|
defaultValue={settings?.KoboldCPPBasePath}
|
||||||
|
required={true}
|
||||||
|
autoComplete="off"
|
||||||
|
spellCheck={false}
|
||||||
|
onChange={(e) => setBasePathValue(e.target.value)}
|
||||||
|
onBlur={() => setBasePath(basePathValue)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<KoboldCPPModelSelection settings={settings} basePath={basePath} />
|
||||||
|
<div className="flex flex-col w-60">
|
||||||
|
<label className="text-white text-sm font-semibold block mb-4">
|
||||||
|
Token context window
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
name="KoboldCPPTokenLimit"
|
||||||
|
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()}
|
||||||
|
defaultValue={settings?.KoboldCPPTokenLimit}
|
||||||
|
required={true}
|
||||||
|
autoComplete="off"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function KoboldCPPModelSelection({ settings, basePath = null }) {
|
||||||
|
const [customModels, setCustomModels] = useState([]);
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
async function findCustomModels() {
|
||||||
|
if (!basePath || !basePath.includes("/v1")) {
|
||||||
|
setCustomModels([]);
|
||||||
|
setLoading(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setLoading(true);
|
||||||
|
const { models } = await System.customModels("koboldcpp", 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">
|
||||||
|
Chat Model Selection
|
||||||
|
</label>
|
||||||
|
<select
|
||||||
|
name="KoboldCPPModelPref"
|
||||||
|
disabled={true}
|
||||||
|
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")
|
||||||
|
? "-- 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">
|
||||||
|
Chat Model Selection
|
||||||
|
</label>
|
||||||
|
<select
|
||||||
|
name="KoboldCPPModelPref"
|
||||||
|
required={true}
|
||||||
|
className="bg-zinc-900 border-gray-500 text-white text-sm rounded-lg block w-full p-2.5"
|
||||||
|
>
|
||||||
|
{customModels.map((model) => (
|
||||||
|
<option
|
||||||
|
key={model.id}
|
||||||
|
value={model.id}
|
||||||
|
selected={settings?.KoboldCPPModelPref === model.id}
|
||||||
|
>
|
||||||
|
{model.id}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
@ -150,9 +150,13 @@ export default function ActiveWorkspaces() {
|
|||||||
|
|
||||||
<Link
|
<Link
|
||||||
type="button"
|
type="button"
|
||||||
to={paths.workspace.settings.generalAppearance(
|
to={
|
||||||
|
isInWorkspaceSettings
|
||||||
|
? paths.workspace.chat(workspace.slug)
|
||||||
|
: paths.workspace.settings.generalAppearance(
|
||||||
workspace.slug
|
workspace.slug
|
||||||
)}
|
)
|
||||||
|
}
|
||||||
onMouseEnter={() => handleGearMouseEnter(workspace.id)}
|
onMouseEnter={() => handleGearMouseEnter(workspace.id)}
|
||||||
onMouseLeave={() => handleGearMouseLeave(workspace.id)}
|
onMouseLeave={() => handleGearMouseLeave(workspace.id)}
|
||||||
className="rounded-md flex items-center justify-center text-[#A7A8A9] hover:text-white ml-auto"
|
className="rounded-md flex items-center justify-center text-[#A7A8A9] hover:text-white ml-auto"
|
||||||
|
@ -107,7 +107,7 @@ export function Chartable({ props, workspace }) {
|
|||||||
);
|
);
|
||||||
case "line":
|
case "line":
|
||||||
return (
|
return (
|
||||||
<div className="bg-zinc-900 p-8 pb-12 rounded-xl text-white h-[500px]">
|
<div className="bg-zinc-900 p-8 pb-12 rounded-xl text-white h-[500px] w-full">
|
||||||
<h3 className="text-lg font-medium">{title}</h3>
|
<h3 className="text-lg font-medium">{title}</h3>
|
||||||
<LineChart
|
<LineChart
|
||||||
className="h-[400px]"
|
className="h-[400px]"
|
||||||
@ -371,7 +371,7 @@ export function Chartable({ props, workspace }) {
|
|||||||
<div className="py-2 px-4 w-full flex gap-x-5 md:max-w-[800px] flex-col">
|
<div className="py-2 px-4 w-full flex gap-x-5 md:max-w-[800px] flex-col">
|
||||||
<div className="flex gap-x-5">
|
<div className="flex gap-x-5">
|
||||||
<WorkspaceProfileImage workspace={workspace} />
|
<WorkspaceProfileImage workspace={workspace} />
|
||||||
<div className="relative">
|
<div className="relative w-full">
|
||||||
<DownloadGraph onClick={handleDownload} />
|
<DownloadGraph onClick={handleDownload} />
|
||||||
<div ref={ref}>{renderChart()}</div>
|
<div ref={ref}>{renderChart()}</div>
|
||||||
<span
|
<span
|
||||||
@ -390,7 +390,7 @@ export function Chartable({ props, workspace }) {
|
|||||||
return (
|
return (
|
||||||
<div className="flex justify-center items-end w-full">
|
<div className="flex justify-center items-end w-full">
|
||||||
<div className="py-2 px-4 w-full flex gap-x-5 md:max-w-[800px] flex-col">
|
<div className="py-2 px-4 w-full flex gap-x-5 md:max-w-[800px] flex-col">
|
||||||
<div className="relative">
|
<div className="relative w-full">
|
||||||
<DownloadGraph onClick={handleDownload} />
|
<DownloadGraph onClick={handleDownload} />
|
||||||
<div ref={ref}>{renderChart()}</div>
|
<div ref={ref}>{renderChart()}</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -26,6 +26,14 @@ const PROVIDER_DEFAULT_MODELS = {
|
|||||||
"gemma-7b-it",
|
"gemma-7b-it",
|
||||||
],
|
],
|
||||||
native: [],
|
native: [],
|
||||||
|
cohere: [
|
||||||
|
"command-r",
|
||||||
|
"command-r-plus",
|
||||||
|
"command",
|
||||||
|
"command-light",
|
||||||
|
"command-nightly",
|
||||||
|
"command-light-nightly",
|
||||||
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
// For togetherAi, which has a large model list - we subgroup the options
|
// For togetherAi, which has a large model list - we subgroup the options
|
||||||
|
BIN
frontend/src/media/llmprovider/cohere.png
Normal file
BIN
frontend/src/media/llmprovider/cohere.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 139 KiB |
BIN
frontend/src/media/llmprovider/koboldcpp.png
Normal file
BIN
frontend/src/media/llmprovider/koboldcpp.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.9 KiB |
@ -9,6 +9,22 @@ import showToast from "@/utils/toast";
|
|||||||
import CTAButton from "@/components/lib/CTAButton";
|
import CTAButton from "@/components/lib/CTAButton";
|
||||||
|
|
||||||
export default function AdminLogs() {
|
export default function AdminLogs() {
|
||||||
|
const query = useQuery();
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
|
const [logs, setLogs] = useState([]);
|
||||||
|
const [offset, setOffset] = useState(Number(query.get("offset") || 0));
|
||||||
|
const [canNext, setCanNext] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
async function fetchLogs() {
|
||||||
|
const { logs: _logs, hasPages = false } = await System.eventLogs(offset);
|
||||||
|
setLogs(_logs);
|
||||||
|
setCanNext(hasPages);
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
fetchLogs();
|
||||||
|
}, [offset]);
|
||||||
|
|
||||||
const handleResetLogs = async () => {
|
const handleResetLogs = async () => {
|
||||||
if (
|
if (
|
||||||
!window.confirm(
|
!window.confirm(
|
||||||
@ -19,13 +35,22 @@ export default function AdminLogs() {
|
|||||||
const { success, error } = await System.clearEventLogs();
|
const { success, error } = await System.clearEventLogs();
|
||||||
if (success) {
|
if (success) {
|
||||||
showToast("Event logs cleared successfully.", "success");
|
showToast("Event logs cleared successfully.", "success");
|
||||||
setTimeout(() => {
|
setLogs([]);
|
||||||
window.location.reload();
|
setCanNext(false);
|
||||||
}, 1000);
|
setOffset(0);
|
||||||
} else {
|
} else {
|
||||||
showToast(`Failed to clear logs: ${error}`, "error");
|
showToast(`Failed to clear logs: ${error}`, "error");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handlePrevious = () => {
|
||||||
|
setOffset(Math.max(offset - 1, 0));
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleNext = () => {
|
||||||
|
setOffset(offset + 1);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-screen h-screen overflow-hidden bg-sidebar flex">
|
<div className="w-screen h-screen overflow-hidden bg-sidebar flex">
|
||||||
<Sidebar />
|
<Sidebar />
|
||||||
@ -53,37 +78,28 @@ export default function AdminLogs() {
|
|||||||
Clear Event Logs
|
Clear Event Logs
|
||||||
</CTAButton>
|
</CTAButton>
|
||||||
</div>
|
</div>
|
||||||
<LogsContainer />
|
<LogsContainer
|
||||||
|
loading={loading}
|
||||||
|
logs={logs}
|
||||||
|
offset={offset}
|
||||||
|
canNext={canNext}
|
||||||
|
handleNext={handleNext}
|
||||||
|
handlePrevious={handlePrevious}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function LogsContainer() {
|
function LogsContainer({
|
||||||
const query = useQuery();
|
loading,
|
||||||
const [loading, setLoading] = useState(true);
|
logs,
|
||||||
const [logs, setLogs] = useState([]);
|
offset,
|
||||||
const [offset, setOffset] = useState(Number(query.get("offset") || 0));
|
canNext,
|
||||||
const [canNext, setCanNext] = useState(false);
|
handleNext,
|
||||||
|
handlePrevious,
|
||||||
const handlePrevious = () => {
|
}) {
|
||||||
setOffset(Math.max(offset - 1, 0));
|
|
||||||
};
|
|
||||||
const handleNext = () => {
|
|
||||||
setOffset(offset + 1);
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
async function fetchLogs() {
|
|
||||||
const { logs: _logs, hasPages = false } = await System.eventLogs(offset);
|
|
||||||
setLogs(_logs);
|
|
||||||
setCanNext(hasPages);
|
|
||||||
setLoading(false);
|
|
||||||
}
|
|
||||||
fetchLogs();
|
|
||||||
}, [offset]);
|
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
return (
|
||||||
<Skeleton.default
|
<Skeleton.default
|
||||||
|
@ -9,6 +9,7 @@ import AzureOpenAiLogo from "@/media/llmprovider/azure.png";
|
|||||||
import LocalAiLogo from "@/media/llmprovider/localai.png";
|
import LocalAiLogo from "@/media/llmprovider/localai.png";
|
||||||
import OllamaLogo from "@/media/llmprovider/ollama.png";
|
import OllamaLogo from "@/media/llmprovider/ollama.png";
|
||||||
import LMStudioLogo from "@/media/llmprovider/lmstudio.png";
|
import LMStudioLogo from "@/media/llmprovider/lmstudio.png";
|
||||||
|
import CohereLogo from "@/media/llmprovider/cohere.png";
|
||||||
import PreLoader from "@/components/Preloader";
|
import PreLoader from "@/components/Preloader";
|
||||||
import ChangeWarningModal from "@/components/ChangeWarning";
|
import ChangeWarningModal from "@/components/ChangeWarning";
|
||||||
import OpenAiOptions from "@/components/EmbeddingSelection/OpenAiOptions";
|
import OpenAiOptions from "@/components/EmbeddingSelection/OpenAiOptions";
|
||||||
@ -17,6 +18,8 @@ import LocalAiOptions from "@/components/EmbeddingSelection/LocalAiOptions";
|
|||||||
import NativeEmbeddingOptions from "@/components/EmbeddingSelection/NativeEmbeddingOptions";
|
import NativeEmbeddingOptions from "@/components/EmbeddingSelection/NativeEmbeddingOptions";
|
||||||
import OllamaEmbeddingOptions from "@/components/EmbeddingSelection/OllamaOptions";
|
import OllamaEmbeddingOptions from "@/components/EmbeddingSelection/OllamaOptions";
|
||||||
import LMStudioEmbeddingOptions from "@/components/EmbeddingSelection/LMStudioOptions";
|
import LMStudioEmbeddingOptions from "@/components/EmbeddingSelection/LMStudioOptions";
|
||||||
|
import CohereEmbeddingOptions from "@/components/EmbeddingSelection/CohereOptions";
|
||||||
|
|
||||||
import EmbedderItem from "@/components/EmbeddingSelection/EmbedderItem";
|
import EmbedderItem from "@/components/EmbeddingSelection/EmbedderItem";
|
||||||
import { CaretUpDown, MagnifyingGlass, X } from "@phosphor-icons/react";
|
import { CaretUpDown, MagnifyingGlass, X } from "@phosphor-icons/react";
|
||||||
import { useModal } from "@/hooks/useModal";
|
import { useModal } from "@/hooks/useModal";
|
||||||
@ -68,6 +71,13 @@ const EMBEDDERS = [
|
|||||||
description:
|
description:
|
||||||
"Discover, download, and run thousands of cutting edge LLMs in a few clicks.",
|
"Discover, download, and run thousands of cutting edge LLMs in a few clicks.",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "Cohere",
|
||||||
|
value: "cohere",
|
||||||
|
logo: CohereLogo,
|
||||||
|
options: (settings) => <CohereEmbeddingOptions settings={settings} />,
|
||||||
|
description: "Run powerful embedding models from Cohere.",
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export default function GeneralEmbeddingPreference() {
|
export default function GeneralEmbeddingPreference() {
|
||||||
|
@ -17,6 +17,8 @@ import HuggingFaceLogo from "@/media/llmprovider/huggingface.png";
|
|||||||
import PerplexityLogo from "@/media/llmprovider/perplexity.png";
|
import PerplexityLogo from "@/media/llmprovider/perplexity.png";
|
||||||
import OpenRouterLogo from "@/media/llmprovider/openrouter.jpeg";
|
import OpenRouterLogo from "@/media/llmprovider/openrouter.jpeg";
|
||||||
import GroqLogo from "@/media/llmprovider/groq.png";
|
import GroqLogo from "@/media/llmprovider/groq.png";
|
||||||
|
import KoboldCPPLogo from "@/media/llmprovider/koboldcpp.png";
|
||||||
|
import CohereLogo from "@/media/llmprovider/cohere.png";
|
||||||
import PreLoader from "@/components/Preloader";
|
import PreLoader from "@/components/Preloader";
|
||||||
import OpenAiOptions from "@/components/LLMSelection/OpenAiOptions";
|
import OpenAiOptions from "@/components/LLMSelection/OpenAiOptions";
|
||||||
import GenericOpenAiOptions from "@/components/LLMSelection/GenericOpenAiOptions";
|
import GenericOpenAiOptions from "@/components/LLMSelection/GenericOpenAiOptions";
|
||||||
@ -32,10 +34,12 @@ import HuggingFaceOptions from "@/components/LLMSelection/HuggingFaceOptions";
|
|||||||
import PerplexityOptions from "@/components/LLMSelection/PerplexityOptions";
|
import PerplexityOptions from "@/components/LLMSelection/PerplexityOptions";
|
||||||
import OpenRouterOptions from "@/components/LLMSelection/OpenRouterOptions";
|
import OpenRouterOptions from "@/components/LLMSelection/OpenRouterOptions";
|
||||||
import GroqAiOptions from "@/components/LLMSelection/GroqAiOptions";
|
import GroqAiOptions from "@/components/LLMSelection/GroqAiOptions";
|
||||||
|
import CohereAiOptions from "@/components/LLMSelection/CohereAiOptions";
|
||||||
|
|
||||||
import LLMItem from "@/components/LLMSelection/LLMItem";
|
import LLMItem from "@/components/LLMSelection/LLMItem";
|
||||||
import { CaretUpDown, MagnifyingGlass, X } from "@phosphor-icons/react";
|
import { CaretUpDown, MagnifyingGlass, X } from "@phosphor-icons/react";
|
||||||
import CTAButton from "@/components/lib/CTAButton";
|
import CTAButton from "@/components/lib/CTAButton";
|
||||||
|
import KoboldCPPOptions from "@/components/LLMSelection/KoboldCPPOptions";
|
||||||
|
|
||||||
export const AVAILABLE_LLM_PROVIDERS = [
|
export const AVAILABLE_LLM_PROVIDERS = [
|
||||||
{
|
{
|
||||||
@ -150,6 +154,26 @@ export const AVAILABLE_LLM_PROVIDERS = [
|
|||||||
"The fastest LLM inferencing available for real-time AI applications.",
|
"The fastest LLM inferencing available for real-time AI applications.",
|
||||||
requiredConfig: ["GroqApiKey"],
|
requiredConfig: ["GroqApiKey"],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "KoboldCPP",
|
||||||
|
value: "koboldcpp",
|
||||||
|
logo: KoboldCPPLogo,
|
||||||
|
options: (settings) => <KoboldCPPOptions settings={settings} />,
|
||||||
|
description: "Run local LLMs using koboldcpp.",
|
||||||
|
requiredConfig: [
|
||||||
|
"KoboldCPPModelPref",
|
||||||
|
"KoboldCPPBasePath",
|
||||||
|
"KoboldCPPTokenLimit",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Cohere",
|
||||||
|
value: "cohere",
|
||||||
|
logo: CohereLogo,
|
||||||
|
options: (settings) => <CohereAiOptions settings={settings} />,
|
||||||
|
description: "Run Cohere's powerful Command models.",
|
||||||
|
requiredConfig: ["CohereApiKey"],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "Generic OpenAI",
|
name: "Generic OpenAI",
|
||||||
value: "generic-openai",
|
value: "generic-openai",
|
||||||
|
@ -1,140 +0,0 @@
|
|||||||
import useLogo from "@/hooks/useLogo";
|
|
||||||
import System from "@/models/system";
|
|
||||||
import showToast from "@/utils/toast";
|
|
||||||
import { Plus } from "@phosphor-icons/react";
|
|
||||||
import React, { useState, useEffect } from "react";
|
|
||||||
import AnythingLLM from "@/media/logo/anything-llm.png";
|
|
||||||
import paths from "@/utils/paths";
|
|
||||||
import { useNavigate } from "react-router-dom";
|
|
||||||
|
|
||||||
const TITLE = "Custom Logo";
|
|
||||||
const DESCRIPTION =
|
|
||||||
"Upload your custom logo to make your chatbot yours. Optional.";
|
|
||||||
|
|
||||||
export default function CustomLogo({ setHeader, setForwardBtn, setBackBtn }) {
|
|
||||||
const navigate = useNavigate();
|
|
||||||
function handleForward() {
|
|
||||||
navigate(paths.onboarding.userSetup());
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleBack() {
|
|
||||||
navigate(paths.onboarding.llmPreference());
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setHeader({ title: TITLE, description: DESCRIPTION });
|
|
||||||
setForwardBtn({ showing: true, disabled: false, onClick: handleForward });
|
|
||||||
setBackBtn({ showing: true, disabled: false, onClick: handleBack });
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const { logo: _initLogo, setLogo: _setLogo } = useLogo();
|
|
||||||
const [logo, setLogo] = useState("");
|
|
||||||
const [isDefaultLogo, setIsDefaultLogo] = useState(true);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
async function logoInit() {
|
|
||||||
setLogo(_initLogo || "");
|
|
||||||
const _isDefaultLogo = await System.isDefaultLogo();
|
|
||||||
setIsDefaultLogo(_isDefaultLogo);
|
|
||||||
}
|
|
||||||
logoInit();
|
|
||||||
}, [_initLogo]);
|
|
||||||
|
|
||||||
const handleFileUpload = async (event) => {
|
|
||||||
const file = event.target.files[0];
|
|
||||||
if (!file) return false;
|
|
||||||
|
|
||||||
const objectURL = URL.createObjectURL(file);
|
|
||||||
setLogo(objectURL);
|
|
||||||
|
|
||||||
const formData = new FormData();
|
|
||||||
formData.append("logo", file);
|
|
||||||
const { success, error } = await System.uploadLogo(formData);
|
|
||||||
if (!success) {
|
|
||||||
showToast(`Failed to upload logo: ${error}`, "error");
|
|
||||||
setLogo(_initLogo);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const logoURL = await System.fetchLogo();
|
|
||||||
_setLogo(logoURL);
|
|
||||||
setIsDefaultLogo(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleRemoveLogo = async () => {
|
|
||||||
setLogo("");
|
|
||||||
setIsDefaultLogo(true);
|
|
||||||
|
|
||||||
const { success, error } = await System.removeCustomLogo();
|
|
||||||
if (!success) {
|
|
||||||
console.error("Failed to remove logo:", error);
|
|
||||||
showToast(`Failed to remove logo: ${error}`, "error");
|
|
||||||
const logoURL = await System.fetchLogo();
|
|
||||||
setLogo(logoURL);
|
|
||||||
setIsDefaultLogo(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const logoURL = await System.fetchLogo();
|
|
||||||
_setLogo(logoURL);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="flex items-center w-full">
|
|
||||||
<div className="flex gap-x-8 flex-col w-full">
|
|
||||||
{isDefaultLogo ? (
|
|
||||||
<label className="mt-5 hover:opacity-60 w-full flex justify-center transition-all duration-300">
|
|
||||||
<input
|
|
||||||
id="logo-upload"
|
|
||||||
type="file"
|
|
||||||
accept="image/*"
|
|
||||||
className="hidden"
|
|
||||||
onChange={handleFileUpload}
|
|
||||||
/>
|
|
||||||
<div
|
|
||||||
className="max-w-[600px] w-full h-64 max-h-[600px] py-4 bg-zinc-900/50 rounded-2xl border-2 border-dashed border-white border-opacity-60 justify-center items-center inline-flex cursor-pointer"
|
|
||||||
htmlFor="logo-upload"
|
|
||||||
>
|
|
||||||
<div className="flex flex-col items-center justify-center">
|
|
||||||
<div className="rounded-full bg-white/40">
|
|
||||||
<Plus className="w-6 h-6 text-black/80 m-2" />
|
|
||||||
</div>
|
|
||||||
<div className="text-white text-opacity-80 text-sm font-semibold py-1">
|
|
||||||
Add a custom logo
|
|
||||||
</div>
|
|
||||||
<div className="text-white text-opacity-60 text-xs font-medium py-1">
|
|
||||||
Recommended size: 800 x 200
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</label>
|
|
||||||
) : (
|
|
||||||
<div className="w-full flex justify-center">
|
|
||||||
<img
|
|
||||||
src={logo}
|
|
||||||
alt="Uploaded Logo"
|
|
||||||
className="w-48 h-48 object-contain mr-6"
|
|
||||||
hidden={isDefaultLogo}
|
|
||||||
onError={(e) => (e.target.src = AnythingLLM)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{!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>
|
|
||||||
);
|
|
||||||
}
|
|
@ -15,6 +15,8 @@ import HuggingFaceLogo from "@/media/llmprovider/huggingface.png";
|
|||||||
import PerplexityLogo from "@/media/llmprovider/perplexity.png";
|
import PerplexityLogo from "@/media/llmprovider/perplexity.png";
|
||||||
import OpenRouterLogo from "@/media/llmprovider/openrouter.jpeg";
|
import OpenRouterLogo from "@/media/llmprovider/openrouter.jpeg";
|
||||||
import GroqLogo from "@/media/llmprovider/groq.png";
|
import GroqLogo from "@/media/llmprovider/groq.png";
|
||||||
|
import KoboldCPPLogo from "@/media/llmprovider/koboldcpp.png";
|
||||||
|
import CohereLogo from "@/media/llmprovider/cohere.png";
|
||||||
import ZillizLogo from "@/media/vectordbs/zilliz.png";
|
import ZillizLogo from "@/media/vectordbs/zilliz.png";
|
||||||
import AstraDBLogo from "@/media/vectordbs/astraDB.png";
|
import AstraDBLogo from "@/media/vectordbs/astraDB.png";
|
||||||
import ChromaLogo from "@/media/vectordbs/chroma.png";
|
import ChromaLogo from "@/media/vectordbs/chroma.png";
|
||||||
@ -137,6 +139,13 @@ export const LLM_SELECTION_PRIVACY = {
|
|||||||
],
|
],
|
||||||
logo: GroqLogo,
|
logo: GroqLogo,
|
||||||
},
|
},
|
||||||
|
koboldcpp: {
|
||||||
|
name: "KoboldCPP",
|
||||||
|
description: [
|
||||||
|
"Your model and chats are only accessible on the server running KoboldCPP",
|
||||||
|
],
|
||||||
|
logo: KoboldCPPLogo,
|
||||||
|
},
|
||||||
"generic-openai": {
|
"generic-openai": {
|
||||||
name: "Generic OpenAI compatible service",
|
name: "Generic OpenAI compatible service",
|
||||||
description: [
|
description: [
|
||||||
@ -144,6 +153,13 @@ export const LLM_SELECTION_PRIVACY = {
|
|||||||
],
|
],
|
||||||
logo: GenericOpenAiLogo,
|
logo: GenericOpenAiLogo,
|
||||||
},
|
},
|
||||||
|
cohere: {
|
||||||
|
name: "Cohere",
|
||||||
|
description: [
|
||||||
|
"Data is shared according to the terms of service of cohere.com and your localities privacy laws.",
|
||||||
|
],
|
||||||
|
logo: CohereLogo,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export const VECTOR_DB_PRIVACY = {
|
export const VECTOR_DB_PRIVACY = {
|
||||||
@ -252,6 +268,13 @@ export const EMBEDDING_ENGINE_PRIVACY = {
|
|||||||
],
|
],
|
||||||
logo: LMStudioLogo,
|
logo: LMStudioLogo,
|
||||||
},
|
},
|
||||||
|
cohere: {
|
||||||
|
name: "Cohere",
|
||||||
|
description: [
|
||||||
|
"Data is shared according to the terms of service of cohere.com and your localities privacy laws.",
|
||||||
|
],
|
||||||
|
logo: CohereLogo,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function DataHandling({ setHeader, setForwardBtn, setBackBtn }) {
|
export default function DataHandling({ setHeader, setForwardBtn, setBackBtn }) {
|
||||||
|
@ -14,6 +14,8 @@ import HuggingFaceLogo from "@/media/llmprovider/huggingface.png";
|
|||||||
import PerplexityLogo from "@/media/llmprovider/perplexity.png";
|
import PerplexityLogo from "@/media/llmprovider/perplexity.png";
|
||||||
import OpenRouterLogo from "@/media/llmprovider/openrouter.jpeg";
|
import OpenRouterLogo from "@/media/llmprovider/openrouter.jpeg";
|
||||||
import GroqLogo from "@/media/llmprovider/groq.png";
|
import GroqLogo from "@/media/llmprovider/groq.png";
|
||||||
|
import KoboldCPPLogo from "@/media/llmprovider/koboldcpp.png";
|
||||||
|
import CohereLogo from "@/media/llmprovider/cohere.png";
|
||||||
import OpenAiOptions from "@/components/LLMSelection/OpenAiOptions";
|
import OpenAiOptions from "@/components/LLMSelection/OpenAiOptions";
|
||||||
import GenericOpenAiOptions from "@/components/LLMSelection/GenericOpenAiOptions";
|
import GenericOpenAiOptions from "@/components/LLMSelection/GenericOpenAiOptions";
|
||||||
import AzureAiOptions from "@/components/LLMSelection/AzureAiOptions";
|
import AzureAiOptions from "@/components/LLMSelection/AzureAiOptions";
|
||||||
@ -28,11 +30,14 @@ import TogetherAiOptions from "@/components/LLMSelection/TogetherAiOptions";
|
|||||||
import PerplexityOptions from "@/components/LLMSelection/PerplexityOptions";
|
import PerplexityOptions from "@/components/LLMSelection/PerplexityOptions";
|
||||||
import OpenRouterOptions from "@/components/LLMSelection/OpenRouterOptions";
|
import OpenRouterOptions from "@/components/LLMSelection/OpenRouterOptions";
|
||||||
import GroqAiOptions from "@/components/LLMSelection/GroqAiOptions";
|
import GroqAiOptions from "@/components/LLMSelection/GroqAiOptions";
|
||||||
|
import CohereAiOptions from "@/components/LLMSelection/CohereAiOptions";
|
||||||
|
|
||||||
import LLMItem from "@/components/LLMSelection/LLMItem";
|
import LLMItem from "@/components/LLMSelection/LLMItem";
|
||||||
import System from "@/models/system";
|
import System from "@/models/system";
|
||||||
import paths from "@/utils/paths";
|
import paths from "@/utils/paths";
|
||||||
import showToast from "@/utils/toast";
|
import showToast from "@/utils/toast";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
|
import KoboldCPPOptions from "@/components/LLMSelection/KoboldCPPOptions";
|
||||||
|
|
||||||
const TITLE = "LLM Preference";
|
const TITLE = "LLM Preference";
|
||||||
const DESCRIPTION =
|
const DESCRIPTION =
|
||||||
@ -97,6 +102,13 @@ const LLMS = [
|
|||||||
options: (settings) => <LocalAiOptions settings={settings} />,
|
options: (settings) => <LocalAiOptions settings={settings} />,
|
||||||
description: "Run LLMs locally on your own machine.",
|
description: "Run LLMs locally on your own machine.",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "KoboldCPP",
|
||||||
|
value: "koboldcpp",
|
||||||
|
logo: KoboldCPPLogo,
|
||||||
|
options: (settings) => <KoboldCPPOptions settings={settings} />,
|
||||||
|
description: "Run local LLMs using koboldcpp.",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "Together AI",
|
name: "Together AI",
|
||||||
value: "togetherai",
|
value: "togetherai",
|
||||||
@ -134,6 +146,13 @@ const LLMS = [
|
|||||||
description:
|
description:
|
||||||
"The fastest LLM inferencing available for real-time AI applications.",
|
"The fastest LLM inferencing available for real-time AI applications.",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "Cohere",
|
||||||
|
value: "cohere",
|
||||||
|
logo: CohereLogo,
|
||||||
|
options: (settings) => <CohereAiOptions settings={settings} />,
|
||||||
|
description: "Run Cohere's powerful Command models.",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "Generic OpenAI",
|
name: "Generic OpenAI",
|
||||||
value: "generic-openai",
|
value: "generic-openai",
|
||||||
@ -200,7 +219,7 @@ export default function LLMPreference({
|
|||||||
showToast(`Failed to save LLM settings: ${error}`, "error");
|
showToast(`Failed to save LLM settings: ${error}`, "error");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
navigate(paths.onboarding.customLogo());
|
navigate(paths.onboarding.userSetup());
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -29,7 +29,7 @@ export default function UserSetup({ setHeader, setForwardBtn, setBackBtn }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function handleBack() {
|
function handleBack() {
|
||||||
navigate(paths.onboarding.customLogo());
|
navigate(paths.onboarding.llmPreference());
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -3,7 +3,6 @@ import { useState } from "react";
|
|||||||
import { isMobile } from "react-device-detect";
|
import { isMobile } from "react-device-detect";
|
||||||
import Home from "./Home";
|
import Home from "./Home";
|
||||||
import LLMPreference from "./LLMPreference";
|
import LLMPreference from "./LLMPreference";
|
||||||
import CustomLogo from "./CustomLogo";
|
|
||||||
import UserSetup from "./UserSetup";
|
import UserSetup from "./UserSetup";
|
||||||
import DataHandling from "./DataHandling";
|
import DataHandling from "./DataHandling";
|
||||||
import Survey from "./Survey";
|
import Survey from "./Survey";
|
||||||
@ -12,7 +11,6 @@ import CreateWorkspace from "./CreateWorkspace";
|
|||||||
const OnboardingSteps = {
|
const OnboardingSteps = {
|
||||||
home: Home,
|
home: Home,
|
||||||
"llm-preference": LLMPreference,
|
"llm-preference": LLMPreference,
|
||||||
"custom-logo": CustomLogo,
|
|
||||||
"user-setup": UserSetup,
|
"user-setup": UserSetup,
|
||||||
"data-handling": DataHandling,
|
"data-handling": DataHandling,
|
||||||
survey: Survey,
|
survey: Survey,
|
||||||
|
@ -36,7 +36,6 @@ export default function GeneralInfo({ slug }) {
|
|||||||
);
|
);
|
||||||
if (!!updatedWorkspace) {
|
if (!!updatedWorkspace) {
|
||||||
showToast("Workspace updated!", "success", { clear: true });
|
showToast("Workspace updated!", "success", { clear: true });
|
||||||
setTimeout(() => window.location.reload(), 1_500);
|
|
||||||
} else {
|
} else {
|
||||||
showToast(`Error: ${message}`, "error", { clear: true });
|
showToast(`Error: ${message}`, "error", { clear: true });
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@ export default function handleChat(
|
|||||||
error,
|
error,
|
||||||
close,
|
close,
|
||||||
chatId = null,
|
chatId = null,
|
||||||
|
action = null,
|
||||||
} = chatResult;
|
} = chatResult;
|
||||||
|
|
||||||
if (type === "abort" || type === "statusResponse") {
|
if (type === "abort" || type === "statusResponse") {
|
||||||
@ -132,6 +133,12 @@ export default function handleChat(
|
|||||||
setChatHistory([..._chatHistory]);
|
setChatHistory([..._chatHistory]);
|
||||||
setLoadingResponse(false);
|
setLoadingResponse(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Action Handling via special 'action' attribute on response.
|
||||||
|
if (action === "reset_chat") {
|
||||||
|
// Chat was reset, keep reset message and clear everything else.
|
||||||
|
setChatHistory([_chatHistory.pop()]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function chatPrompt(workspace) {
|
export function chatPrompt(workspace) {
|
||||||
|
@ -5,7 +5,7 @@ import "highlight.js/styles/github-dark-dimmed.min.css";
|
|||||||
import { v4 } from "uuid";
|
import { v4 } from "uuid";
|
||||||
|
|
||||||
const markdown = markdownIt({
|
const markdown = markdownIt({
|
||||||
html: true,
|
html: false,
|
||||||
typographer: true,
|
typographer: true,
|
||||||
highlight: function (code, lang) {
|
highlight: function (code, lang) {
|
||||||
const uuid = v4();
|
const uuid = v4();
|
||||||
|
@ -23,9 +23,6 @@ export default {
|
|||||||
vectorDatabase: () => {
|
vectorDatabase: () => {
|
||||||
return "/onboarding/vector-database";
|
return "/onboarding/vector-database";
|
||||||
},
|
},
|
||||||
customLogo: () => {
|
|
||||||
return "/onboarding/custom-logo";
|
|
||||||
},
|
|
||||||
userSetup: () => {
|
userSetup: () => {
|
||||||
return "/onboarding/user-setup";
|
return "/onboarding/user-setup";
|
||||||
},
|
},
|
||||||
|
@ -63,12 +63,21 @@ JWT_SECRET="my-random-string-for-seeding" # Please generate random string at lea
|
|||||||
# GROQ_API_KEY=gsk_abcxyz
|
# GROQ_API_KEY=gsk_abcxyz
|
||||||
# GROQ_MODEL_PREF=llama3-8b-8192
|
# GROQ_MODEL_PREF=llama3-8b-8192
|
||||||
|
|
||||||
|
# LLM_PROVIDER='koboldcpp'
|
||||||
|
# KOBOLD_CPP_BASE_PATH='http://127.0.0.1:5000/v1'
|
||||||
|
# KOBOLD_CPP_MODEL_PREF='koboldcpp/codellama-7b-instruct.Q4_K_S'
|
||||||
|
# KOBOLD_CPP_MODEL_TOKEN_LIMIT=4096
|
||||||
|
|
||||||
# LLM_PROVIDER='generic-openai'
|
# LLM_PROVIDER='generic-openai'
|
||||||
# GENERIC_OPEN_AI_BASE_PATH='http://proxy.url.openai.com/v1'
|
# GENERIC_OPEN_AI_BASE_PATH='http://proxy.url.openai.com/v1'
|
||||||
# GENERIC_OPEN_AI_MODEL_PREF='gpt-3.5-turbo'
|
# GENERIC_OPEN_AI_MODEL_PREF='gpt-3.5-turbo'
|
||||||
# GENERIC_OPEN_AI_MODEL_TOKEN_LIMIT=4096
|
# GENERIC_OPEN_AI_MODEL_TOKEN_LIMIT=4096
|
||||||
# GENERIC_OPEN_AI_API_KEY=sk-123abc
|
# GENERIC_OPEN_AI_API_KEY=sk-123abc
|
||||||
|
|
||||||
|
# LLM_PROVIDER='cohere'
|
||||||
|
# COHERE_API_KEY=
|
||||||
|
# COHERE_MODEL_PREF='command-r'
|
||||||
|
|
||||||
###########################################
|
###########################################
|
||||||
######## Embedding API SElECTION ##########
|
######## Embedding API SElECTION ##########
|
||||||
###########################################
|
###########################################
|
||||||
@ -97,6 +106,10 @@ JWT_SECRET="my-random-string-for-seeding" # Please generate random string at lea
|
|||||||
# EMBEDDING_MODEL_PREF='nomic-ai/nomic-embed-text-v1.5-GGUF/nomic-embed-text-v1.5.Q4_0.gguf'
|
# EMBEDDING_MODEL_PREF='nomic-ai/nomic-embed-text-v1.5-GGUF/nomic-embed-text-v1.5.Q4_0.gguf'
|
||||||
# EMBEDDING_MODEL_MAX_CHUNK_LENGTH=8192
|
# EMBEDDING_MODEL_MAX_CHUNK_LENGTH=8192
|
||||||
|
|
||||||
|
# EMBEDDING_ENGINE='cohere'
|
||||||
|
# COHERE_API_KEY=
|
||||||
|
# EMBEDDING_MODEL_PREF='embed-english-v3.0'
|
||||||
|
|
||||||
###########################################
|
###########################################
|
||||||
######## Vector Database Selection ########
|
######## Vector Database Selection ########
|
||||||
###########################################
|
###########################################
|
||||||
|
@ -918,7 +918,7 @@ function systemEndpoints(app) {
|
|||||||
[validatedRequest, flexUserRoleValid([ROLES.admin])],
|
[validatedRequest, flexUserRoleValid([ROLES.admin])],
|
||||||
async (request, response) => {
|
async (request, response) => {
|
||||||
try {
|
try {
|
||||||
const { offset = 0, limit = 20 } = reqBody(request);
|
const { offset = 0, limit = 10 } = reqBody(request);
|
||||||
const logs = await EventLogs.whereWithData({}, limit, offset * limit, {
|
const logs = await EventLogs.whereWithData({}, limit, offset * limit, {
|
||||||
id: "desc",
|
id: "desc",
|
||||||
});
|
});
|
||||||
|
@ -363,11 +363,20 @@ const SystemSettings = {
|
|||||||
HuggingFaceLLMAccessToken: !!process.env.HUGGING_FACE_LLM_API_KEY,
|
HuggingFaceLLMAccessToken: !!process.env.HUGGING_FACE_LLM_API_KEY,
|
||||||
HuggingFaceLLMTokenLimit: process.env.HUGGING_FACE_LLM_TOKEN_LIMIT,
|
HuggingFaceLLMTokenLimit: process.env.HUGGING_FACE_LLM_TOKEN_LIMIT,
|
||||||
|
|
||||||
|
// KoboldCPP Keys
|
||||||
|
KoboldCPPModelPref: process.env.KOBOLD_CPP_MODEL_PREF,
|
||||||
|
KoboldCPPBasePath: process.env.KOBOLD_CPP_BASE_PATH,
|
||||||
|
KoboldCPPTokenLimit: process.env.KOBOLD_CPP_MODEL_TOKEN_LIMIT,
|
||||||
|
|
||||||
// Generic OpenAI Keys
|
// Generic OpenAI Keys
|
||||||
GenericOpenAiBasePath: process.env.GENERIC_OPEN_AI_BASE_PATH,
|
GenericOpenAiBasePath: process.env.GENERIC_OPEN_AI_BASE_PATH,
|
||||||
GenericOpenAiModelPref: process.env.GENERIC_OPEN_AI_MODEL_PREF,
|
GenericOpenAiModelPref: process.env.GENERIC_OPEN_AI_MODEL_PREF,
|
||||||
GenericOpenAiTokenLimit: process.env.GENERIC_OPEN_AI_MODEL_TOKEN_LIMIT,
|
GenericOpenAiTokenLimit: process.env.GENERIC_OPEN_AI_MODEL_TOKEN_LIMIT,
|
||||||
GenericOpenAiKey: !!process.env.GENERIC_OPEN_AI_API_KEY,
|
GenericOpenAiKey: !!process.env.GENERIC_OPEN_AI_API_KEY,
|
||||||
|
|
||||||
|
// Cohere API Keys
|
||||||
|
CohereApiKey: !!process.env.COHERE_API_KEY,
|
||||||
|
CohereModelPref: process.env.COHERE_MODEL_PREF,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -41,6 +41,7 @@
|
|||||||
"chalk": "^4",
|
"chalk": "^4",
|
||||||
"check-disk-space": "^3.4.0",
|
"check-disk-space": "^3.4.0",
|
||||||
"chromadb": "^1.5.2",
|
"chromadb": "^1.5.2",
|
||||||
|
"cohere-ai": "^7.9.5",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"dotenv": "^16.0.3",
|
"dotenv": "^16.0.3",
|
||||||
"express": "^4.18.2",
|
"express": "^4.18.2",
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
const { v4 } = require("uuid");
|
const { v4 } = require("uuid");
|
||||||
const { chatPrompt } = require("../../chats");
|
|
||||||
const {
|
const {
|
||||||
writeResponseChunk,
|
writeResponseChunk,
|
||||||
clientAbortedHandler,
|
clientAbortedHandler,
|
||||||
@ -33,7 +32,7 @@ class AnthropicLLM {
|
|||||||
}
|
}
|
||||||
|
|
||||||
streamingEnabled() {
|
streamingEnabled() {
|
||||||
return "streamChat" in this && "streamGetChatCompletion" in this;
|
return "streamGetChatCompletion" in this;
|
||||||
}
|
}
|
||||||
|
|
||||||
promptWindowLimit() {
|
promptWindowLimit() {
|
||||||
@ -110,31 +109,6 @@ class AnthropicLLM {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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,
|
|
||||||
chatHistory,
|
|
||||||
},
|
|
||||||
rawHistory
|
|
||||||
);
|
|
||||||
|
|
||||||
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 streamGetChatCompletion(messages = null, { temperature = 0.7 }) {
|
async streamGetChatCompletion(messages = null, { temperature = 0.7 }) {
|
||||||
if (!this.isValidChatCompletionModel(this.model))
|
if (!this.isValidChatCompletionModel(this.model))
|
||||||
throw new Error(
|
throw new Error(
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
const { AzureOpenAiEmbedder } = require("../../EmbeddingEngines/azureOpenAi");
|
const { AzureOpenAiEmbedder } = require("../../EmbeddingEngines/azureOpenAi");
|
||||||
const { chatPrompt } = require("../../chats");
|
|
||||||
const {
|
const {
|
||||||
writeResponseChunk,
|
writeResponseChunk,
|
||||||
clientAbortedHandler,
|
clientAbortedHandler,
|
||||||
@ -45,7 +44,7 @@ class AzureOpenAiLLM {
|
|||||||
}
|
}
|
||||||
|
|
||||||
streamingEnabled() {
|
streamingEnabled() {
|
||||||
return "streamChat" in this && "streamGetChatCompletion" in this;
|
return "streamGetChatCompletion" in this;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sure the user selected a proper value for the token limit
|
// Sure the user selected a proper value for the token limit
|
||||||
@ -82,66 +81,6 @@ class AzureOpenAiLLM {
|
|||||||
return { safe: true, reasons: [] };
|
return { safe: true, reasons: [] };
|
||||||
}
|
}
|
||||||
|
|
||||||
async sendChat(chatHistory = [], prompt, workspace = {}, rawHistory = []) {
|
|
||||||
if (!this.model)
|
|
||||||
throw new Error(
|
|
||||||
"No OPEN_MODEL_PREF ENV defined. This must the name of a deployment on your Azure account for an LLM chat model like GPT-3.5."
|
|
||||||
);
|
|
||||||
|
|
||||||
const messages = await this.compressMessages(
|
|
||||||
{
|
|
||||||
systemPrompt: chatPrompt(workspace),
|
|
||||||
userPrompt: prompt,
|
|
||||||
chatHistory,
|
|
||||||
},
|
|
||||||
rawHistory
|
|
||||||
);
|
|
||||||
const textResponse = await this.openai
|
|
||||||
.getChatCompletions(this.model, messages, {
|
|
||||||
temperature: Number(workspace?.openAiTemp ?? this.defaultTemp),
|
|
||||||
n: 1,
|
|
||||||
})
|
|
||||||
.then((res) => {
|
|
||||||
if (!res.hasOwnProperty("choices"))
|
|
||||||
throw new Error("AzureOpenAI chat: No results!");
|
|
||||||
if (res.choices.length === 0)
|
|
||||||
throw new Error("AzureOpenAI chat: No results length!");
|
|
||||||
return res.choices[0].message.content;
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
console.log(error);
|
|
||||||
throw new Error(
|
|
||||||
`AzureOpenAI::getChatCompletions failed with: ${error.message}`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
return textResponse;
|
|
||||||
}
|
|
||||||
|
|
||||||
async streamChat(chatHistory = [], prompt, workspace = {}, rawHistory = []) {
|
|
||||||
if (!this.model)
|
|
||||||
throw new Error(
|
|
||||||
"No OPEN_MODEL_PREF ENV defined. This must the name of a deployment on your Azure account for an LLM chat model like GPT-3.5."
|
|
||||||
);
|
|
||||||
|
|
||||||
const messages = await this.compressMessages(
|
|
||||||
{
|
|
||||||
systemPrompt: chatPrompt(workspace),
|
|
||||||
userPrompt: prompt,
|
|
||||||
chatHistory,
|
|
||||||
},
|
|
||||||
rawHistory
|
|
||||||
);
|
|
||||||
const stream = await this.openai.streamChatCompletions(
|
|
||||||
this.model,
|
|
||||||
messages,
|
|
||||||
{
|
|
||||||
temperature: Number(workspace?.openAiTemp ?? this.defaultTemp),
|
|
||||||
n: 1,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
return stream;
|
|
||||||
}
|
|
||||||
|
|
||||||
async getChatCompletion(messages = [], { temperature = 0.7 }) {
|
async getChatCompletion(messages = [], { temperature = 0.7 }) {
|
||||||
if (!this.model)
|
if (!this.model)
|
||||||
throw new Error(
|
throw new Error(
|
||||||
|
226
server/utils/AiProviders/cohere/index.js
Normal file
226
server/utils/AiProviders/cohere/index.js
Normal file
@ -0,0 +1,226 @@
|
|||||||
|
const { v4 } = require("uuid");
|
||||||
|
const { writeResponseChunk } = require("../../helpers/chat/responses");
|
||||||
|
const { NativeEmbedder } = require("../../EmbeddingEngines/native");
|
||||||
|
|
||||||
|
class CohereLLM {
|
||||||
|
constructor(embedder = null) {
|
||||||
|
const { CohereClient } = require("cohere-ai");
|
||||||
|
if (!process.env.COHERE_API_KEY)
|
||||||
|
throw new Error("No Cohere API key was set.");
|
||||||
|
|
||||||
|
const cohere = new CohereClient({
|
||||||
|
token: process.env.COHERE_API_KEY,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.cohere = cohere;
|
||||||
|
this.model = process.env.COHERE_MODEL_PREF;
|
||||||
|
this.limits = {
|
||||||
|
history: this.promptWindowLimit() * 0.15,
|
||||||
|
system: this.promptWindowLimit() * 0.15,
|
||||||
|
user: this.promptWindowLimit() * 0.7,
|
||||||
|
};
|
||||||
|
this.embedder = !!embedder ? embedder : new NativeEmbedder();
|
||||||
|
}
|
||||||
|
|
||||||
|
#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("")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#convertChatHistoryCohere(chatHistory = []) {
|
||||||
|
let cohereHistory = [];
|
||||||
|
chatHistory.forEach((message) => {
|
||||||
|
switch (message.role) {
|
||||||
|
case "system":
|
||||||
|
cohereHistory.push({ role: "SYSTEM", message: message.content });
|
||||||
|
break;
|
||||||
|
case "user":
|
||||||
|
cohereHistory.push({ role: "USER", message: message.content });
|
||||||
|
break;
|
||||||
|
case "assistant":
|
||||||
|
cohereHistory.push({ role: "CHATBOT", message: message.content });
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return cohereHistory;
|
||||||
|
}
|
||||||
|
|
||||||
|
streamingEnabled() {
|
||||||
|
return "streamGetChatCompletion" in this;
|
||||||
|
}
|
||||||
|
|
||||||
|
promptWindowLimit() {
|
||||||
|
switch (this.model) {
|
||||||
|
case "command-r":
|
||||||
|
return 128_000;
|
||||||
|
case "command-r-plus":
|
||||||
|
return 128_000;
|
||||||
|
case "command":
|
||||||
|
return 4_096;
|
||||||
|
case "command-light":
|
||||||
|
return 4_096;
|
||||||
|
case "command-nightly":
|
||||||
|
return 8_192;
|
||||||
|
case "command-light-nightly":
|
||||||
|
return 8_192;
|
||||||
|
default:
|
||||||
|
return 4_096;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async isValidChatCompletionModel(model = "") {
|
||||||
|
const validModels = [
|
||||||
|
"command-r",
|
||||||
|
"command-r-plus",
|
||||||
|
"command",
|
||||||
|
"command-light",
|
||||||
|
"command-nightly",
|
||||||
|
"command-light-nightly",
|
||||||
|
];
|
||||||
|
return validModels.includes(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 getChatCompletion(messages = null, { temperature = 0.7 }) {
|
||||||
|
if (!(await this.isValidChatCompletionModel(this.model)))
|
||||||
|
throw new Error(
|
||||||
|
`Cohere chat: ${this.model} is not valid for chat completion!`
|
||||||
|
);
|
||||||
|
|
||||||
|
const message = messages[messages.length - 1].content; // Get the last message
|
||||||
|
const cohereHistory = this.#convertChatHistoryCohere(messages.slice(0, -1)); // Remove the last message and convert to Cohere
|
||||||
|
|
||||||
|
const chat = await this.cohere.chat({
|
||||||
|
model: this.model,
|
||||||
|
message: message,
|
||||||
|
chatHistory: cohereHistory,
|
||||||
|
temperature,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!chat.hasOwnProperty("text")) return null;
|
||||||
|
return chat.text;
|
||||||
|
}
|
||||||
|
|
||||||
|
async streamGetChatCompletion(messages = null, { temperature = 0.7 }) {
|
||||||
|
if (!(await this.isValidChatCompletionModel(this.model)))
|
||||||
|
throw new Error(
|
||||||
|
`Cohere chat: ${this.model} is not valid for chat completion!`
|
||||||
|
);
|
||||||
|
|
||||||
|
const message = messages[messages.length - 1].content; // Get the last message
|
||||||
|
const cohereHistory = this.#convertChatHistoryCohere(messages.slice(0, -1)); // Remove the last message and convert to Cohere
|
||||||
|
|
||||||
|
const stream = await this.cohere.chatStream({
|
||||||
|
model: this.model,
|
||||||
|
message: message,
|
||||||
|
chatHistory: cohereHistory,
|
||||||
|
temperature,
|
||||||
|
});
|
||||||
|
|
||||||
|
return { type: "stream", stream: stream };
|
||||||
|
}
|
||||||
|
|
||||||
|
async handleStream(response, stream, responseProps) {
|
||||||
|
return new Promise(async (resolve) => {
|
||||||
|
let fullText = "";
|
||||||
|
const { uuid = v4(), sources = [] } = responseProps;
|
||||||
|
|
||||||
|
const handleAbort = () => {
|
||||||
|
writeResponseChunk(response, {
|
||||||
|
uuid,
|
||||||
|
sources,
|
||||||
|
type: "abort",
|
||||||
|
textResponse: fullText,
|
||||||
|
close: true,
|
||||||
|
error: false,
|
||||||
|
});
|
||||||
|
response.removeListener("close", handleAbort);
|
||||||
|
resolve(fullText);
|
||||||
|
};
|
||||||
|
response.on("close", handleAbort);
|
||||||
|
|
||||||
|
try {
|
||||||
|
for await (const chat of stream.stream) {
|
||||||
|
if (chat.eventType === "text-generation") {
|
||||||
|
const text = chat.text;
|
||||||
|
fullText += text;
|
||||||
|
|
||||||
|
writeResponseChunk(response, {
|
||||||
|
uuid,
|
||||||
|
sources,
|
||||||
|
type: "textResponseChunk",
|
||||||
|
textResponse: text,
|
||||||
|
close: false,
|
||||||
|
error: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
writeResponseChunk(response, {
|
||||||
|
uuid,
|
||||||
|
sources,
|
||||||
|
type: "textResponseChunk",
|
||||||
|
textResponse: "",
|
||||||
|
close: true,
|
||||||
|
error: false,
|
||||||
|
});
|
||||||
|
response.removeListener("close", handleAbort);
|
||||||
|
resolve(fullText);
|
||||||
|
} catch (error) {
|
||||||
|
writeResponseChunk(response, {
|
||||||
|
uuid,
|
||||||
|
sources,
|
||||||
|
type: "abort",
|
||||||
|
textResponse: null,
|
||||||
|
close: true,
|
||||||
|
error: error.message,
|
||||||
|
});
|
||||||
|
response.removeListener("close", handleAbort);
|
||||||
|
resolve(fullText);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 = {
|
||||||
|
CohereLLM,
|
||||||
|
};
|
@ -1,4 +1,3 @@
|
|||||||
const { chatPrompt } = require("../../chats");
|
|
||||||
const {
|
const {
|
||||||
writeResponseChunk,
|
writeResponseChunk,
|
||||||
clientAbortedHandler,
|
clientAbortedHandler,
|
||||||
@ -48,7 +47,7 @@ class GeminiLLM {
|
|||||||
}
|
}
|
||||||
|
|
||||||
streamingEnabled() {
|
streamingEnabled() {
|
||||||
return "streamChat" in this && "streamGetChatCompletion" in this;
|
return "streamGetChatCompletion" in this;
|
||||||
}
|
}
|
||||||
|
|
||||||
promptWindowLimit() {
|
promptWindowLimit() {
|
||||||
@ -118,32 +117,6 @@ class GeminiLLM {
|
|||||||
return allMessages;
|
return allMessages;
|
||||||
}
|
}
|
||||||
|
|
||||||
async sendChat(chatHistory = [], prompt, workspace = {}, rawHistory = []) {
|
|
||||||
if (!this.isValidChatCompletionModel(this.model))
|
|
||||||
throw new Error(
|
|
||||||
`Gemini chat: ${this.model} is not valid for chat completion!`
|
|
||||||
);
|
|
||||||
|
|
||||||
const compressedHistory = await this.compressMessages(
|
|
||||||
{
|
|
||||||
systemPrompt: chatPrompt(workspace),
|
|
||||||
chatHistory,
|
|
||||||
},
|
|
||||||
rawHistory
|
|
||||||
);
|
|
||||||
|
|
||||||
const chatThread = this.gemini.startChat({
|
|
||||||
history: this.formatMessages(compressedHistory),
|
|
||||||
});
|
|
||||||
const result = await chatThread.sendMessage(prompt);
|
|
||||||
const response = result.response;
|
|
||||||
const responseText = response.text();
|
|
||||||
|
|
||||||
if (!responseText) throw new Error("Gemini: No response could be parsed.");
|
|
||||||
|
|
||||||
return responseText;
|
|
||||||
}
|
|
||||||
|
|
||||||
async getChatCompletion(messages = [], _opts = {}) {
|
async getChatCompletion(messages = [], _opts = {}) {
|
||||||
if (!this.isValidChatCompletionModel(this.model))
|
if (!this.isValidChatCompletionModel(this.model))
|
||||||
throw new Error(
|
throw new Error(
|
||||||
@ -165,30 +138,6 @@ class GeminiLLM {
|
|||||||
return responseText;
|
return responseText;
|
||||||
}
|
}
|
||||||
|
|
||||||
async streamChat(chatHistory = [], prompt, workspace = {}, rawHistory = []) {
|
|
||||||
if (!this.isValidChatCompletionModel(this.model))
|
|
||||||
throw new Error(
|
|
||||||
`Gemini chat: ${this.model} is not valid for chat completion!`
|
|
||||||
);
|
|
||||||
|
|
||||||
const compressedHistory = await this.compressMessages(
|
|
||||||
{
|
|
||||||
systemPrompt: chatPrompt(workspace),
|
|
||||||
chatHistory,
|
|
||||||
},
|
|
||||||
rawHistory
|
|
||||||
);
|
|
||||||
|
|
||||||
const chatThread = this.gemini.startChat({
|
|
||||||
history: this.formatMessages(compressedHistory),
|
|
||||||
});
|
|
||||||
const responseStream = await chatThread.sendMessageStream(prompt);
|
|
||||||
if (!responseStream.stream)
|
|
||||||
throw new Error("Could not stream response stream from Gemini.");
|
|
||||||
|
|
||||||
return responseStream.stream;
|
|
||||||
}
|
|
||||||
|
|
||||||
async streamGetChatCompletion(messages = [], _opts = {}) {
|
async streamGetChatCompletion(messages = [], _opts = {}) {
|
||||||
if (!this.isValidChatCompletionModel(this.model))
|
if (!this.isValidChatCompletionModel(this.model))
|
||||||
throw new Error(
|
throw new Error(
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
const { NativeEmbedder } = require("../../EmbeddingEngines/native");
|
const { NativeEmbedder } = require("../../EmbeddingEngines/native");
|
||||||
const { chatPrompt } = require("../../chats");
|
|
||||||
const {
|
const {
|
||||||
handleDefaultStreamResponseV2,
|
handleDefaultStreamResponseV2,
|
||||||
} = require("../../helpers/chat/responses");
|
} = require("../../helpers/chat/responses");
|
||||||
@ -53,7 +52,7 @@ class GenericOpenAiLLM {
|
|||||||
}
|
}
|
||||||
|
|
||||||
streamingEnabled() {
|
streamingEnabled() {
|
||||||
return "streamChat" in this && "streamGetChatCompletion" in this;
|
return "streamGetChatCompletion" in this;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure the user set a value for the token limit
|
// Ensure the user set a value for the token limit
|
||||||
@ -89,55 +88,6 @@ class GenericOpenAiLLM {
|
|||||||
return { safe: true, reasons: [] };
|
return { safe: true, reasons: [] };
|
||||||
}
|
}
|
||||||
|
|
||||||
async sendChat(chatHistory = [], prompt, workspace = {}, rawHistory = []) {
|
|
||||||
const textResponse = await this.openai.chat.completions
|
|
||||||
.create({
|
|
||||||
model: this.model,
|
|
||||||
temperature: Number(workspace?.openAiTemp ?? this.defaultTemp),
|
|
||||||
n: 1,
|
|
||||||
messages: await this.compressMessages(
|
|
||||||
{
|
|
||||||
systemPrompt: chatPrompt(workspace),
|
|
||||||
userPrompt: prompt,
|
|
||||||
chatHistory,
|
|
||||||
},
|
|
||||||
rawHistory
|
|
||||||
),
|
|
||||||
})
|
|
||||||
.then((result) => {
|
|
||||||
if (!result.hasOwnProperty("choices"))
|
|
||||||
throw new Error("GenericOpenAI chat: No results!");
|
|
||||||
if (result.choices.length === 0)
|
|
||||||
throw new Error("GenericOpenAI chat: No results length!");
|
|
||||||
return result.choices[0].message.content;
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
throw new Error(
|
|
||||||
`GenericOpenAI::createChatCompletion failed with: ${error.message}`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
return textResponse;
|
|
||||||
}
|
|
||||||
|
|
||||||
async streamChat(chatHistory = [], prompt, workspace = {}, rawHistory = []) {
|
|
||||||
const streamRequest = await this.openai.chat.completions.create({
|
|
||||||
model: this.model,
|
|
||||||
stream: true,
|
|
||||||
temperature: Number(workspace?.openAiTemp ?? this.defaultTemp),
|
|
||||||
n: 1,
|
|
||||||
messages: await this.compressMessages(
|
|
||||||
{
|
|
||||||
systemPrompt: chatPrompt(workspace),
|
|
||||||
userPrompt: prompt,
|
|
||||||
chatHistory,
|
|
||||||
},
|
|
||||||
rawHistory
|
|
||||||
),
|
|
||||||
});
|
|
||||||
return streamRequest;
|
|
||||||
}
|
|
||||||
|
|
||||||
async getChatCompletion(messages = null, { temperature = 0.7 }) {
|
async getChatCompletion(messages = null, { temperature = 0.7 }) {
|
||||||
const result = await this.openai.chat.completions
|
const result = await this.openai.chat.completions
|
||||||
.create({
|
.create({
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
const { NativeEmbedder } = require("../../EmbeddingEngines/native");
|
const { NativeEmbedder } = require("../../EmbeddingEngines/native");
|
||||||
const { chatPrompt } = require("../../chats");
|
|
||||||
const {
|
const {
|
||||||
handleDefaultStreamResponseV2,
|
handleDefaultStreamResponseV2,
|
||||||
} = require("../../helpers/chat/responses");
|
} = require("../../helpers/chat/responses");
|
||||||
@ -38,7 +37,7 @@ class GroqLLM {
|
|||||||
}
|
}
|
||||||
|
|
||||||
streamingEnabled() {
|
streamingEnabled() {
|
||||||
return "streamChat" in this && "streamGetChatCompletion" in this;
|
return "streamGetChatCompletion" in this;
|
||||||
}
|
}
|
||||||
|
|
||||||
promptWindowLimit() {
|
promptWindowLimit() {
|
||||||
@ -91,65 +90,6 @@ class GroqLLM {
|
|||||||
return { safe: true, reasons: [] };
|
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.chat.completions
|
|
||||||
.create({
|
|
||||||
model: this.model,
|
|
||||||
temperature: Number(workspace?.openAiTemp ?? this.defaultTemp),
|
|
||||||
n: 1,
|
|
||||||
messages: await this.compressMessages(
|
|
||||||
{
|
|
||||||
systemPrompt: chatPrompt(workspace),
|
|
||||||
userPrompt: prompt,
|
|
||||||
chatHistory,
|
|
||||||
},
|
|
||||||
rawHistory
|
|
||||||
),
|
|
||||||
})
|
|
||||||
.then((result) => {
|
|
||||||
if (!result.hasOwnProperty("choices"))
|
|
||||||
throw new Error("GroqAI chat: No results!");
|
|
||||||
if (result.choices.length === 0)
|
|
||||||
throw new Error("GroqAI chat: No results length!");
|
|
||||||
return result.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.chat.completions.create({
|
|
||||||
model: this.model,
|
|
||||||
stream: true,
|
|
||||||
temperature: Number(workspace?.openAiTemp ?? this.defaultTemp),
|
|
||||||
n: 1,
|
|
||||||
messages: await this.compressMessages(
|
|
||||||
{
|
|
||||||
systemPrompt: chatPrompt(workspace),
|
|
||||||
userPrompt: prompt,
|
|
||||||
chatHistory,
|
|
||||||
},
|
|
||||||
rawHistory
|
|
||||||
),
|
|
||||||
});
|
|
||||||
return streamRequest;
|
|
||||||
}
|
|
||||||
|
|
||||||
async getChatCompletion(messages = null, { temperature = 0.7 }) {
|
async getChatCompletion(messages = null, { temperature = 0.7 }) {
|
||||||
if (!(await this.isValidChatCompletionModel(this.model)))
|
if (!(await this.isValidChatCompletionModel(this.model)))
|
||||||
throw new Error(
|
throw new Error(
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
const { NativeEmbedder } = require("../../EmbeddingEngines/native");
|
const { NativeEmbedder } = require("../../EmbeddingEngines/native");
|
||||||
const { OpenAiEmbedder } = require("../../EmbeddingEngines/openAi");
|
const { OpenAiEmbedder } = require("../../EmbeddingEngines/openAi");
|
||||||
const { chatPrompt } = require("../../chats");
|
|
||||||
const {
|
const {
|
||||||
handleDefaultStreamResponseV2,
|
handleDefaultStreamResponseV2,
|
||||||
} = require("../../helpers/chat/responses");
|
} = require("../../helpers/chat/responses");
|
||||||
@ -48,7 +47,7 @@ class HuggingFaceLLM {
|
|||||||
}
|
}
|
||||||
|
|
||||||
streamingEnabled() {
|
streamingEnabled() {
|
||||||
return "streamChat" in this && "streamGetChatCompletion" in this;
|
return "streamGetChatCompletion" in this;
|
||||||
}
|
}
|
||||||
|
|
||||||
promptWindowLimit() {
|
promptWindowLimit() {
|
||||||
@ -90,55 +89,6 @@ class HuggingFaceLLM {
|
|||||||
return { safe: true, reasons: [] };
|
return { safe: true, reasons: [] };
|
||||||
}
|
}
|
||||||
|
|
||||||
async sendChat(chatHistory = [], prompt, workspace = {}, rawHistory = []) {
|
|
||||||
const textResponse = await this.openai.chat.completions
|
|
||||||
.create({
|
|
||||||
model: this.model,
|
|
||||||
temperature: Number(workspace?.openAiTemp ?? this.defaultTemp),
|
|
||||||
n: 1,
|
|
||||||
messages: await this.compressMessages(
|
|
||||||
{
|
|
||||||
systemPrompt: chatPrompt(workspace),
|
|
||||||
userPrompt: prompt,
|
|
||||||
chatHistory,
|
|
||||||
},
|
|
||||||
rawHistory
|
|
||||||
),
|
|
||||||
})
|
|
||||||
.then((result) => {
|
|
||||||
if (!result.hasOwnProperty("choices"))
|
|
||||||
throw new Error("HuggingFace chat: No results!");
|
|
||||||
if (result.choices.length === 0)
|
|
||||||
throw new Error("HuggingFace chat: No results length!");
|
|
||||||
return result.choices[0].message.content;
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
throw new Error(
|
|
||||||
`HuggingFace::createChatCompletion failed with: ${error.message}`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
return textResponse;
|
|
||||||
}
|
|
||||||
|
|
||||||
async streamChat(chatHistory = [], prompt, workspace = {}, rawHistory = []) {
|
|
||||||
const streamRequest = await this.openai.chat.completions.create({
|
|
||||||
model: this.model,
|
|
||||||
stream: true,
|
|
||||||
temperature: Number(workspace?.openAiTemp ?? this.defaultTemp),
|
|
||||||
n: 1,
|
|
||||||
messages: await this.compressMessages(
|
|
||||||
{
|
|
||||||
systemPrompt: chatPrompt(workspace),
|
|
||||||
userPrompt: prompt,
|
|
||||||
chatHistory,
|
|
||||||
},
|
|
||||||
rawHistory
|
|
||||||
),
|
|
||||||
});
|
|
||||||
return streamRequest;
|
|
||||||
}
|
|
||||||
|
|
||||||
async getChatCompletion(messages = null, { temperature = 0.7 }) {
|
async getChatCompletion(messages = null, { temperature = 0.7 }) {
|
||||||
const result = await this.openai.createChatCompletion({
|
const result = await this.openai.createChatCompletion({
|
||||||
model: this.model,
|
model: this.model,
|
||||||
|
180
server/utils/AiProviders/koboldCPP/index.js
Normal file
180
server/utils/AiProviders/koboldCPP/index.js
Normal file
@ -0,0 +1,180 @@
|
|||||||
|
const { NativeEmbedder } = require("../../EmbeddingEngines/native");
|
||||||
|
const {
|
||||||
|
clientAbortedHandler,
|
||||||
|
writeResponseChunk,
|
||||||
|
} = require("../../helpers/chat/responses");
|
||||||
|
const { v4: uuidv4 } = require("uuid");
|
||||||
|
|
||||||
|
class KoboldCPPLLM {
|
||||||
|
constructor(embedder = null, modelPreference = null) {
|
||||||
|
const { OpenAI: OpenAIApi } = require("openai");
|
||||||
|
if (!process.env.KOBOLD_CPP_BASE_PATH)
|
||||||
|
throw new Error(
|
||||||
|
"KoboldCPP must have a valid base path to use for the api."
|
||||||
|
);
|
||||||
|
|
||||||
|
this.basePath = process.env.KOBOLD_CPP_BASE_PATH;
|
||||||
|
this.openai = new OpenAIApi({
|
||||||
|
baseURL: this.basePath,
|
||||||
|
apiKey: null,
|
||||||
|
});
|
||||||
|
this.model = modelPreference ?? process.env.KOBOLD_CPP_MODEL_PREF ?? null;
|
||||||
|
if (!this.model) throw new Error("KoboldCPP must have a valid model set.");
|
||||||
|
this.limits = {
|
||||||
|
history: this.promptWindowLimit() * 0.15,
|
||||||
|
system: this.promptWindowLimit() * 0.15,
|
||||||
|
user: this.promptWindowLimit() * 0.7,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!embedder)
|
||||||
|
console.warn(
|
||||||
|
"No embedding provider defined for KoboldCPPLLM - falling back to NativeEmbedder for embedding!"
|
||||||
|
);
|
||||||
|
this.embedder = !embedder ? new NativeEmbedder() : embedder;
|
||||||
|
this.defaultTemp = 0.7;
|
||||||
|
this.log(`Inference API: ${this.basePath} Model: ${this.model}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
log(text, ...args) {
|
||||||
|
console.log(`\x1b[36m[${this.constructor.name}]\x1b[0m ${text}`, ...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
#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 "streamGetChatCompletion" in this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure the user set a value for the token limit
|
||||||
|
// and if undefined - assume 4096 window.
|
||||||
|
promptWindowLimit() {
|
||||||
|
const limit = process.env.KOBOLD_CPP_MODEL_TOKEN_LIMIT || 4096;
|
||||||
|
if (!limit || isNaN(Number(limit)))
|
||||||
|
throw new Error("No token context limit was set.");
|
||||||
|
return Number(limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Short circuit since we have no idea if the model is valid or not
|
||||||
|
// in pre-flight for generic endpoints
|
||||||
|
isValidChatCompletionModel(_modelName = "") {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 getChatCompletion(messages = null, { temperature = 0.7 }) {
|
||||||
|
const result = await this.openai.chat.completions
|
||||||
|
.create({
|
||||||
|
model: this.model,
|
||||||
|
messages,
|
||||||
|
temperature,
|
||||||
|
})
|
||||||
|
.catch((e) => {
|
||||||
|
throw new Error(e.response.data.error.message);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!result.hasOwnProperty("choices") || result.choices.length === 0)
|
||||||
|
return null;
|
||||||
|
return result.choices[0].message.content;
|
||||||
|
}
|
||||||
|
|
||||||
|
async streamGetChatCompletion(messages = null, { temperature = 0.7 }) {
|
||||||
|
const streamRequest = await this.openai.chat.completions.create({
|
||||||
|
model: this.model,
|
||||||
|
stream: true,
|
||||||
|
messages,
|
||||||
|
temperature,
|
||||||
|
});
|
||||||
|
return streamRequest;
|
||||||
|
}
|
||||||
|
|
||||||
|
handleStream(response, stream, responseProps) {
|
||||||
|
const { uuid = uuidv4(), sources = [] } = responseProps;
|
||||||
|
|
||||||
|
// Custom handler for KoboldCPP stream responses
|
||||||
|
return new Promise(async (resolve) => {
|
||||||
|
let fullText = "";
|
||||||
|
const handleAbort = () => clientAbortedHandler(resolve, fullText);
|
||||||
|
response.on("close", handleAbort);
|
||||||
|
|
||||||
|
for await (const chunk of stream) {
|
||||||
|
const message = chunk?.choices?.[0];
|
||||||
|
const token = message?.delta?.content;
|
||||||
|
|
||||||
|
if (token) {
|
||||||
|
fullText += token;
|
||||||
|
writeResponseChunk(response, {
|
||||||
|
uuid,
|
||||||
|
sources: [],
|
||||||
|
type: "textResponseChunk",
|
||||||
|
textResponse: token,
|
||||||
|
close: false,
|
||||||
|
error: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// KoboldCPP finishes with "length" or "stop"
|
||||||
|
if (
|
||||||
|
message.finish_reason !== "null" &&
|
||||||
|
(message.finish_reason === "length" ||
|
||||||
|
message.finish_reason === "stop")
|
||||||
|
) {
|
||||||
|
writeResponseChunk(response, {
|
||||||
|
uuid,
|
||||||
|
sources,
|
||||||
|
type: "textResponseChunk",
|
||||||
|
textResponse: "",
|
||||||
|
close: true,
|
||||||
|
error: false,
|
||||||
|
});
|
||||||
|
response.removeListener("close", handleAbort);
|
||||||
|
resolve(fullText);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 = {
|
||||||
|
KoboldCPPLLM,
|
||||||
|
};
|
@ -1,4 +1,3 @@
|
|||||||
const { chatPrompt } = require("../../chats");
|
|
||||||
const {
|
const {
|
||||||
handleDefaultStreamResponseV2,
|
handleDefaultStreamResponseV2,
|
||||||
} = require("../../helpers/chat/responses");
|
} = require("../../helpers/chat/responses");
|
||||||
@ -49,7 +48,7 @@ class LMStudioLLM {
|
|||||||
}
|
}
|
||||||
|
|
||||||
streamingEnabled() {
|
streamingEnabled() {
|
||||||
return "streamChat" in this && "streamGetChatCompletion" in this;
|
return "streamGetChatCompletion" in this;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure the user set a value for the token limit
|
// Ensure the user set a value for the token limit
|
||||||
@ -85,65 +84,6 @@ class LMStudioLLM {
|
|||||||
return { safe: true, reasons: [] };
|
return { safe: true, reasons: [] };
|
||||||
}
|
}
|
||||||
|
|
||||||
async sendChat(chatHistory = [], prompt, workspace = {}, rawHistory = []) {
|
|
||||||
if (!this.model)
|
|
||||||
throw new Error(
|
|
||||||
`LMStudio chat: ${this.model} is not valid or defined for chat completion!`
|
|
||||||
);
|
|
||||||
|
|
||||||
const textResponse = await this.lmstudio.chat.completions
|
|
||||||
.create({
|
|
||||||
model: this.model,
|
|
||||||
temperature: Number(workspace?.openAiTemp ?? this.defaultTemp),
|
|
||||||
n: 1,
|
|
||||||
messages: await this.compressMessages(
|
|
||||||
{
|
|
||||||
systemPrompt: chatPrompt(workspace),
|
|
||||||
userPrompt: prompt,
|
|
||||||
chatHistory,
|
|
||||||
},
|
|
||||||
rawHistory
|
|
||||||
),
|
|
||||||
})
|
|
||||||
.then((result) => {
|
|
||||||
if (!result.hasOwnProperty("choices"))
|
|
||||||
throw new Error("LMStudio chat: No results!");
|
|
||||||
if (result.choices.length === 0)
|
|
||||||
throw new Error("LMStudio chat: No results length!");
|
|
||||||
return result.choices[0].message.content;
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
throw new Error(
|
|
||||||
`LMStudio::createChatCompletion failed with: ${error.message}`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
return textResponse;
|
|
||||||
}
|
|
||||||
|
|
||||||
async streamChat(chatHistory = [], prompt, workspace = {}, rawHistory = []) {
|
|
||||||
if (!this.model)
|
|
||||||
throw new Error(
|
|
||||||
`LMStudio chat: ${this.model} is not valid or defined for chat completion!`
|
|
||||||
);
|
|
||||||
|
|
||||||
const streamRequest = await this.lmstudio.chat.completions.create({
|
|
||||||
model: this.model,
|
|
||||||
temperature: Number(workspace?.openAiTemp ?? this.defaultTemp),
|
|
||||||
n: 1,
|
|
||||||
stream: true,
|
|
||||||
messages: await this.compressMessages(
|
|
||||||
{
|
|
||||||
systemPrompt: chatPrompt(workspace),
|
|
||||||
userPrompt: prompt,
|
|
||||||
chatHistory,
|
|
||||||
},
|
|
||||||
rawHistory
|
|
||||||
),
|
|
||||||
});
|
|
||||||
return streamRequest;
|
|
||||||
}
|
|
||||||
|
|
||||||
async getChatCompletion(messages = null, { temperature = 0.7 }) {
|
async getChatCompletion(messages = null, { temperature = 0.7 }) {
|
||||||
if (!this.model)
|
if (!this.model)
|
||||||
throw new Error(
|
throw new Error(
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
const { chatPrompt } = require("../../chats");
|
|
||||||
const {
|
const {
|
||||||
handleDefaultStreamResponseV2,
|
handleDefaultStreamResponseV2,
|
||||||
} = require("../../helpers/chat/responses");
|
} = require("../../helpers/chat/responses");
|
||||||
@ -41,7 +40,7 @@ class LocalAiLLM {
|
|||||||
}
|
}
|
||||||
|
|
||||||
streamingEnabled() {
|
streamingEnabled() {
|
||||||
return "streamChat" in this && "streamGetChatCompletion" in this;
|
return "streamGetChatCompletion" in this;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure the user set a value for the token limit
|
// Ensure the user set a value for the token limit
|
||||||
@ -75,65 +74,6 @@ class LocalAiLLM {
|
|||||||
return { safe: true, reasons: [] };
|
return { safe: true, reasons: [] };
|
||||||
}
|
}
|
||||||
|
|
||||||
async sendChat(chatHistory = [], prompt, workspace = {}, rawHistory = []) {
|
|
||||||
if (!(await this.isValidChatCompletionModel(this.model)))
|
|
||||||
throw new Error(
|
|
||||||
`LocalAI chat: ${this.model} is not valid for chat completion!`
|
|
||||||
);
|
|
||||||
|
|
||||||
const textResponse = await this.openai.chat.completions
|
|
||||||
.create({
|
|
||||||
model: this.model,
|
|
||||||
temperature: Number(workspace?.openAiTemp ?? this.defaultTemp),
|
|
||||||
n: 1,
|
|
||||||
messages: await this.compressMessages(
|
|
||||||
{
|
|
||||||
systemPrompt: chatPrompt(workspace),
|
|
||||||
userPrompt: prompt,
|
|
||||||
chatHistory,
|
|
||||||
},
|
|
||||||
rawHistory
|
|
||||||
),
|
|
||||||
})
|
|
||||||
.then((result) => {
|
|
||||||
if (!result.hasOwnProperty("choices"))
|
|
||||||
throw new Error("LocalAI chat: No results!");
|
|
||||||
if (result.choices.length === 0)
|
|
||||||
throw new Error("LocalAI chat: No results length!");
|
|
||||||
return result.choices[0].message.content;
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
throw new Error(
|
|
||||||
`LocalAI::createChatCompletion failed with: ${error.message}`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
return textResponse;
|
|
||||||
}
|
|
||||||
|
|
||||||
async streamChat(chatHistory = [], prompt, workspace = {}, rawHistory = []) {
|
|
||||||
if (!(await this.isValidChatCompletionModel(this.model)))
|
|
||||||
throw new Error(
|
|
||||||
`LocalAI chat: ${this.model} is not valid for chat completion!`
|
|
||||||
);
|
|
||||||
|
|
||||||
const streamRequest = await this.openai.chat.completions.create({
|
|
||||||
model: this.model,
|
|
||||||
stream: true,
|
|
||||||
temperature: Number(workspace?.openAiTemp ?? this.defaultTemp),
|
|
||||||
n: 1,
|
|
||||||
messages: await this.compressMessages(
|
|
||||||
{
|
|
||||||
systemPrompt: chatPrompt(workspace),
|
|
||||||
userPrompt: prompt,
|
|
||||||
chatHistory,
|
|
||||||
},
|
|
||||||
rawHistory
|
|
||||||
),
|
|
||||||
});
|
|
||||||
return streamRequest;
|
|
||||||
}
|
|
||||||
|
|
||||||
async getChatCompletion(messages = null, { temperature = 0.7 }) {
|
async getChatCompletion(messages = null, { temperature = 0.7 }) {
|
||||||
if (!(await this.isValidChatCompletionModel(this.model)))
|
if (!(await this.isValidChatCompletionModel(this.model)))
|
||||||
throw new Error(
|
throw new Error(
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
const { chatPrompt } = require("../../chats");
|
|
||||||
const {
|
const {
|
||||||
handleDefaultStreamResponseV2,
|
handleDefaultStreamResponseV2,
|
||||||
} = require("../../helpers/chat/responses");
|
} = require("../../helpers/chat/responses");
|
||||||
@ -42,7 +41,7 @@ class MistralLLM {
|
|||||||
}
|
}
|
||||||
|
|
||||||
streamingEnabled() {
|
streamingEnabled() {
|
||||||
return "streamChat" in this && "streamGetChatCompletion" in this;
|
return "streamGetChatCompletion" in this;
|
||||||
}
|
}
|
||||||
|
|
||||||
promptWindowLimit() {
|
promptWindowLimit() {
|
||||||
@ -70,64 +69,6 @@ class MistralLLM {
|
|||||||
return { safe: true, reasons: [] };
|
return { safe: true, reasons: [] };
|
||||||
}
|
}
|
||||||
|
|
||||||
async sendChat(chatHistory = [], prompt, workspace = {}, rawHistory = []) {
|
|
||||||
if (!(await this.isValidChatCompletionModel(this.model)))
|
|
||||||
throw new Error(
|
|
||||||
`Mistral chat: ${this.model} is not valid for chat completion!`
|
|
||||||
);
|
|
||||||
|
|
||||||
const textResponse = await this.openai.chat.completions
|
|
||||||
.create({
|
|
||||||
model: this.model,
|
|
||||||
temperature: Number(workspace?.openAiTemp ?? this.defaultTemp),
|
|
||||||
messages: await this.compressMessages(
|
|
||||||
{
|
|
||||||
systemPrompt: chatPrompt(workspace),
|
|
||||||
userPrompt: prompt,
|
|
||||||
chatHistory,
|
|
||||||
},
|
|
||||||
rawHistory
|
|
||||||
),
|
|
||||||
})
|
|
||||||
.then((result) => {
|
|
||||||
if (!result.hasOwnProperty("choices"))
|
|
||||||
throw new Error("Mistral chat: No results!");
|
|
||||||
if (result.choices.length === 0)
|
|
||||||
throw new Error("Mistral chat: No results length!");
|
|
||||||
return result.choices[0].message.content;
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
throw new Error(
|
|
||||||
`Mistral::createChatCompletion failed with: ${error.message}`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
return textResponse;
|
|
||||||
}
|
|
||||||
|
|
||||||
async streamChat(chatHistory = [], prompt, workspace = {}, rawHistory = []) {
|
|
||||||
if (!(await this.isValidChatCompletionModel(this.model)))
|
|
||||||
throw new Error(
|
|
||||||
`Mistral chat: ${this.model} is not valid for chat completion!`
|
|
||||||
);
|
|
||||||
|
|
||||||
const streamRequest = await this.openai.chat.completions.create({
|
|
||||||
model: this.model,
|
|
||||||
stream: true,
|
|
||||||
temperature: Number(workspace?.openAiTemp ?? this.defaultTemp),
|
|
||||||
messages: await this.compressMessages(
|
|
||||||
{
|
|
||||||
systemPrompt: chatPrompt(workspace),
|
|
||||||
userPrompt: prompt,
|
|
||||||
chatHistory,
|
|
||||||
},
|
|
||||||
rawHistory
|
|
||||||
),
|
|
||||||
});
|
|
||||||
|
|
||||||
return streamRequest;
|
|
||||||
}
|
|
||||||
|
|
||||||
async getChatCompletion(messages = null, { temperature = 0.7 }) {
|
async getChatCompletion(messages = null, { temperature = 0.7 }) {
|
||||||
if (!(await this.isValidChatCompletionModel(this.model)))
|
if (!(await this.isValidChatCompletionModel(this.model)))
|
||||||
throw new Error(
|
throw new Error(
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
const fs = require("fs");
|
const fs = require("fs");
|
||||||
const path = require("path");
|
const path = require("path");
|
||||||
const { NativeEmbedder } = require("../../EmbeddingEngines/native");
|
const { NativeEmbedder } = require("../../EmbeddingEngines/native");
|
||||||
const { chatPrompt } = require("../../chats");
|
|
||||||
const {
|
const {
|
||||||
writeResponseChunk,
|
writeResponseChunk,
|
||||||
clientAbortedHandler,
|
clientAbortedHandler,
|
||||||
@ -94,7 +93,7 @@ class NativeLLM {
|
|||||||
}
|
}
|
||||||
|
|
||||||
streamingEnabled() {
|
streamingEnabled() {
|
||||||
return "streamChat" in this && "streamGetChatCompletion" in this;
|
return "streamGetChatCompletion" in this;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure the user set a value for the token limit
|
// Ensure the user set a value for the token limit
|
||||||
@ -123,45 +122,6 @@ class NativeLLM {
|
|||||||
return { safe: true, reasons: [] };
|
return { safe: true, reasons: [] };
|
||||||
}
|
}
|
||||||
|
|
||||||
async sendChat(chatHistory = [], prompt, workspace = {}, rawHistory = []) {
|
|
||||||
try {
|
|
||||||
const messages = await this.compressMessages(
|
|
||||||
{
|
|
||||||
systemPrompt: chatPrompt(workspace),
|
|
||||||
userPrompt: prompt,
|
|
||||||
chatHistory,
|
|
||||||
},
|
|
||||||
rawHistory
|
|
||||||
);
|
|
||||||
|
|
||||||
const model = await this.#llamaClient({
|
|
||||||
temperature: Number(workspace?.openAiTemp ?? this.defaultTemp),
|
|
||||||
});
|
|
||||||
const response = await model.call(messages);
|
|
||||||
return response.content;
|
|
||||||
} catch (error) {
|
|
||||||
throw new Error(
|
|
||||||
`NativeLLM::createChatCompletion failed with: ${error.message}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async streamChat(chatHistory = [], prompt, workspace = {}, rawHistory = []) {
|
|
||||||
const model = await this.#llamaClient({
|
|
||||||
temperature: Number(workspace?.openAiTemp ?? this.defaultTemp),
|
|
||||||
});
|
|
||||||
const messages = await this.compressMessages(
|
|
||||||
{
|
|
||||||
systemPrompt: chatPrompt(workspace),
|
|
||||||
userPrompt: prompt,
|
|
||||||
chatHistory,
|
|
||||||
},
|
|
||||||
rawHistory
|
|
||||||
);
|
|
||||||
const responseStream = await model.stream(messages);
|
|
||||||
return responseStream;
|
|
||||||
}
|
|
||||||
|
|
||||||
async getChatCompletion(messages = null, { temperature = 0.7 }) {
|
async getChatCompletion(messages = null, { temperature = 0.7 }) {
|
||||||
const model = await this.#llamaClient({ temperature });
|
const model = await this.#llamaClient({ temperature });
|
||||||
const response = await model.call(messages);
|
const response = await model.call(messages);
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
const { chatPrompt } = require("../../chats");
|
|
||||||
const { StringOutputParser } = require("@langchain/core/output_parsers");
|
const { StringOutputParser } = require("@langchain/core/output_parsers");
|
||||||
const {
|
const {
|
||||||
writeResponseChunk,
|
writeResponseChunk,
|
||||||
@ -74,7 +73,7 @@ class OllamaAILLM {
|
|||||||
}
|
}
|
||||||
|
|
||||||
streamingEnabled() {
|
streamingEnabled() {
|
||||||
return "streamChat" in this && "streamGetChatCompletion" in this;
|
return "streamGetChatCompletion" in this;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure the user set a value for the token limit
|
// Ensure the user set a value for the token limit
|
||||||
@ -108,53 +107,6 @@ class OllamaAILLM {
|
|||||||
return { safe: true, reasons: [] };
|
return { safe: true, reasons: [] };
|
||||||
}
|
}
|
||||||
|
|
||||||
async sendChat(chatHistory = [], prompt, workspace = {}, rawHistory = []) {
|
|
||||||
const messages = await this.compressMessages(
|
|
||||||
{
|
|
||||||
systemPrompt: chatPrompt(workspace),
|
|
||||||
userPrompt: prompt,
|
|
||||||
chatHistory,
|
|
||||||
},
|
|
||||||
rawHistory
|
|
||||||
);
|
|
||||||
|
|
||||||
const model = this.#ollamaClient({
|
|
||||||
temperature: Number(workspace?.openAiTemp ?? this.defaultTemp),
|
|
||||||
});
|
|
||||||
const textResponse = await model
|
|
||||||
.pipe(new StringOutputParser())
|
|
||||||
.invoke(this.#convertToLangchainPrototypes(messages))
|
|
||||||
.catch((e) => {
|
|
||||||
throw new Error(
|
|
||||||
`Ollama::getChatCompletion failed to communicate with Ollama. ${e.message}`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!textResponse || !textResponse.length)
|
|
||||||
throw new Error(`Ollama::sendChat text response was empty.`);
|
|
||||||
|
|
||||||
return textResponse;
|
|
||||||
}
|
|
||||||
|
|
||||||
async streamChat(chatHistory = [], prompt, workspace = {}, rawHistory = []) {
|
|
||||||
const messages = await this.compressMessages(
|
|
||||||
{
|
|
||||||
systemPrompt: chatPrompt(workspace),
|
|
||||||
userPrompt: prompt,
|
|
||||||
chatHistory,
|
|
||||||
},
|
|
||||||
rawHistory
|
|
||||||
);
|
|
||||||
|
|
||||||
const model = this.#ollamaClient({
|
|
||||||
temperature: Number(workspace?.openAiTemp ?? this.defaultTemp),
|
|
||||||
});
|
|
||||||
const stream = await model
|
|
||||||
.pipe(new StringOutputParser())
|
|
||||||
.stream(this.#convertToLangchainPrototypes(messages));
|
|
||||||
return stream;
|
|
||||||
}
|
|
||||||
|
|
||||||
async getChatCompletion(messages = null, { temperature = 0.7 }) {
|
async getChatCompletion(messages = null, { temperature = 0.7 }) {
|
||||||
const model = this.#ollamaClient({ temperature });
|
const model = this.#ollamaClient({ temperature });
|
||||||
const textResponse = await model
|
const textResponse = await model
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
const { OpenAiEmbedder } = require("../../EmbeddingEngines/openAi");
|
const { OpenAiEmbedder } = require("../../EmbeddingEngines/openAi");
|
||||||
const { chatPrompt } = require("../../chats");
|
|
||||||
const {
|
const {
|
||||||
handleDefaultStreamResponseV2,
|
handleDefaultStreamResponseV2,
|
||||||
} = require("../../helpers/chat/responses");
|
} = require("../../helpers/chat/responses");
|
||||||
@ -41,7 +40,7 @@ class OpenAiLLM {
|
|||||||
}
|
}
|
||||||
|
|
||||||
streamingEnabled() {
|
streamingEnabled() {
|
||||||
return "streamChat" in this && "streamGetChatCompletion" in this;
|
return "streamGetChatCompletion" in this;
|
||||||
}
|
}
|
||||||
|
|
||||||
promptWindowLimit() {
|
promptWindowLimit() {
|
||||||
@ -122,65 +121,6 @@ class OpenAiLLM {
|
|||||||
return { safe: false, reasons };
|
return { safe: false, reasons };
|
||||||
}
|
}
|
||||||
|
|
||||||
async sendChat(chatHistory = [], prompt, workspace = {}, rawHistory = []) {
|
|
||||||
if (!(await this.isValidChatCompletionModel(this.model)))
|
|
||||||
throw new Error(
|
|
||||||
`OpenAI chat: ${this.model} is not valid for chat completion!`
|
|
||||||
);
|
|
||||||
|
|
||||||
const textResponse = await this.openai.chat.completions
|
|
||||||
.create({
|
|
||||||
model: this.model,
|
|
||||||
temperature: Number(workspace?.openAiTemp ?? this.defaultTemp),
|
|
||||||
n: 1,
|
|
||||||
messages: await this.compressMessages(
|
|
||||||
{
|
|
||||||
systemPrompt: chatPrompt(workspace),
|
|
||||||
userPrompt: prompt,
|
|
||||||
chatHistory,
|
|
||||||
},
|
|
||||||
rawHistory
|
|
||||||
),
|
|
||||||
})
|
|
||||||
.then((result) => {
|
|
||||||
if (!result.hasOwnProperty("choices"))
|
|
||||||
throw new Error("OpenAI chat: No results!");
|
|
||||||
if (result.choices.length === 0)
|
|
||||||
throw new Error("OpenAI chat: No results length!");
|
|
||||||
return result.choices[0].message.content;
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
throw new Error(
|
|
||||||
`OpenAI::createChatCompletion failed with: ${error.message}`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
return textResponse;
|
|
||||||
}
|
|
||||||
|
|
||||||
async streamChat(chatHistory = [], prompt, workspace = {}, rawHistory = []) {
|
|
||||||
if (!(await this.isValidChatCompletionModel(this.model)))
|
|
||||||
throw new Error(
|
|
||||||
`OpenAI chat: ${this.model} is not valid for chat completion!`
|
|
||||||
);
|
|
||||||
|
|
||||||
const streamRequest = await this.openai.chat.completions({
|
|
||||||
model: this.model,
|
|
||||||
stream: true,
|
|
||||||
temperature: Number(workspace?.openAiTemp ?? this.defaultTemp),
|
|
||||||
n: 1,
|
|
||||||
messages: await this.compressMessages(
|
|
||||||
{
|
|
||||||
systemPrompt: chatPrompt(workspace),
|
|
||||||
userPrompt: prompt,
|
|
||||||
chatHistory,
|
|
||||||
},
|
|
||||||
rawHistory
|
|
||||||
),
|
|
||||||
});
|
|
||||||
return streamRequest;
|
|
||||||
}
|
|
||||||
|
|
||||||
async getChatCompletion(messages = null, { temperature = 0.7 }) {
|
async getChatCompletion(messages = null, { temperature = 0.7 }) {
|
||||||
if (!(await this.isValidChatCompletionModel(this.model)))
|
if (!(await this.isValidChatCompletionModel(this.model)))
|
||||||
throw new Error(
|
throw new Error(
|
||||||
|
@ -1,10 +1,8 @@
|
|||||||
const { NativeEmbedder } = require("../../EmbeddingEngines/native");
|
const { NativeEmbedder } = require("../../EmbeddingEngines/native");
|
||||||
const { chatPrompt } = require("../../chats");
|
|
||||||
const { v4: uuidv4 } = require("uuid");
|
const { v4: uuidv4 } = require("uuid");
|
||||||
const {
|
const {
|
||||||
writeResponseChunk,
|
writeResponseChunk,
|
||||||
clientAbortedHandler,
|
clientAbortedHandler,
|
||||||
handleDefaultStreamResponseV2,
|
|
||||||
} = require("../../helpers/chat/responses");
|
} = require("../../helpers/chat/responses");
|
||||||
const fs = require("fs");
|
const fs = require("fs");
|
||||||
const path = require("path");
|
const path = require("path");
|
||||||
@ -99,7 +97,7 @@ class OpenRouterLLM {
|
|||||||
}
|
}
|
||||||
|
|
||||||
streamingEnabled() {
|
streamingEnabled() {
|
||||||
return "streamChat" in this && "streamGetChatCompletion" in this;
|
return "streamGetChatCompletion" in this;
|
||||||
}
|
}
|
||||||
|
|
||||||
promptWindowLimit() {
|
promptWindowLimit() {
|
||||||
@ -131,65 +129,6 @@ class OpenRouterLLM {
|
|||||||
return { safe: true, reasons: [] };
|
return { safe: true, reasons: [] };
|
||||||
}
|
}
|
||||||
|
|
||||||
async sendChat(chatHistory = [], prompt, workspace = {}, rawHistory = []) {
|
|
||||||
if (!(await this.isValidChatCompletionModel(this.model)))
|
|
||||||
throw new Error(
|
|
||||||
`OpenRouter chat: ${this.model} is not valid for chat completion!`
|
|
||||||
);
|
|
||||||
|
|
||||||
const textResponse = await this.openai.chat.completions
|
|
||||||
.create({
|
|
||||||
model: this.model,
|
|
||||||
temperature: Number(workspace?.openAiTemp ?? this.defaultTemp),
|
|
||||||
n: 1,
|
|
||||||
messages: await this.compressMessages(
|
|
||||||
{
|
|
||||||
systemPrompt: chatPrompt(workspace),
|
|
||||||
userPrompt: prompt,
|
|
||||||
chatHistory,
|
|
||||||
},
|
|
||||||
rawHistory
|
|
||||||
),
|
|
||||||
})
|
|
||||||
.then((result) => {
|
|
||||||
if (!result.hasOwnProperty("choices"))
|
|
||||||
throw new Error("OpenRouter chat: No results!");
|
|
||||||
if (result.choices.length === 0)
|
|
||||||
throw new Error("OpenRouter chat: No results length!");
|
|
||||||
return result.choices[0].message.content;
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
throw new Error(
|
|
||||||
`OpenRouter::createChatCompletion failed with: ${error.message}`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
return textResponse;
|
|
||||||
}
|
|
||||||
|
|
||||||
async streamChat(chatHistory = [], prompt, workspace = {}, rawHistory = []) {
|
|
||||||
if (!(await this.isValidChatCompletionModel(this.model)))
|
|
||||||
throw new Error(
|
|
||||||
`OpenRouter chat: ${this.model} is not valid for chat completion!`
|
|
||||||
);
|
|
||||||
|
|
||||||
const streamRequest = await this.openai.chat.completions.create({
|
|
||||||
model: this.model,
|
|
||||||
stream: true,
|
|
||||||
temperature: Number(workspace?.openAiTemp ?? this.defaultTemp),
|
|
||||||
n: 1,
|
|
||||||
messages: await this.compressMessages(
|
|
||||||
{
|
|
||||||
systemPrompt: chatPrompt(workspace),
|
|
||||||
userPrompt: prompt,
|
|
||||||
chatHistory,
|
|
||||||
},
|
|
||||||
rawHistory
|
|
||||||
),
|
|
||||||
});
|
|
||||||
return streamRequest;
|
|
||||||
}
|
|
||||||
|
|
||||||
async getChatCompletion(messages = null, { temperature = 0.7 }) {
|
async getChatCompletion(messages = null, { temperature = 0.7 }) {
|
||||||
if (!(await this.isValidChatCompletionModel(this.model)))
|
if (!(await this.isValidChatCompletionModel(this.model)))
|
||||||
throw new Error(
|
throw new Error(
|
||||||
@ -304,143 +243,6 @@ class OpenRouterLLM {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// handleStream(response, stream, responseProps) {
|
|
||||||
// const timeoutThresholdMs = 500;
|
|
||||||
// const { uuid = uuidv4(), sources = [] } = responseProps;
|
|
||||||
|
|
||||||
// return new Promise((resolve) => {
|
|
||||||
// let fullText = "";
|
|
||||||
// let chunk = "";
|
|
||||||
// let lastChunkTime = null; // null when first token is still not received.
|
|
||||||
|
|
||||||
// // Establish listener to early-abort a streaming response
|
|
||||||
// // in case things go sideways or the user does not like the response.
|
|
||||||
// // We preserve the generated text but continue as if chat was completed
|
|
||||||
// // to preserve previously generated content.
|
|
||||||
// const handleAbort = () => clientAbortedHandler(resolve, fullText);
|
|
||||||
// response.on("close", handleAbort);
|
|
||||||
|
|
||||||
// // NOTICE: Not all OpenRouter models will return a stop reason
|
|
||||||
// // which keeps the connection open and so the model never finalizes the stream
|
|
||||||
// // like the traditional OpenAI response schema does. So in the case the response stream
|
|
||||||
// // never reaches a formal close state we maintain an interval timer that if we go >=timeoutThresholdMs with
|
|
||||||
// // no new chunks then we kill the stream and assume it to be complete. OpenRouter is quite fast
|
|
||||||
// // so this threshold should permit most responses, but we can adjust `timeoutThresholdMs` if
|
|
||||||
// // we find it is too aggressive.
|
|
||||||
// const timeoutCheck = setInterval(() => {
|
|
||||||
// if (lastChunkTime === null) return;
|
|
||||||
|
|
||||||
// const now = Number(new Date());
|
|
||||||
// const diffMs = now - lastChunkTime;
|
|
||||||
// if (diffMs >= timeoutThresholdMs) {
|
|
||||||
// console.log(
|
|
||||||
// `OpenRouter stream did not self-close and has been stale for >${timeoutThresholdMs}ms. Closing response stream.`
|
|
||||||
// );
|
|
||||||
// writeResponseChunk(response, {
|
|
||||||
// uuid,
|
|
||||||
// sources,
|
|
||||||
// type: "textResponseChunk",
|
|
||||||
// textResponse: "",
|
|
||||||
// close: true,
|
|
||||||
// error: false,
|
|
||||||
// });
|
|
||||||
// clearInterval(timeoutCheck);
|
|
||||||
// response.removeListener("close", handleAbort);
|
|
||||||
// resolve(fullText);
|
|
||||||
// }
|
|
||||||
// }, 500);
|
|
||||||
|
|
||||||
// stream.data.on("data", (data) => {
|
|
||||||
// const lines = data
|
|
||||||
// ?.toString()
|
|
||||||
// ?.split("\n")
|
|
||||||
// .filter((line) => line.trim() !== "");
|
|
||||||
|
|
||||||
// for (const line of lines) {
|
|
||||||
// let validJSON = false;
|
|
||||||
// const message = chunk + line.replace(/^data: /, "");
|
|
||||||
|
|
||||||
// // JSON chunk is incomplete and has not ended yet
|
|
||||||
// // so we need to stitch it together. You would think JSON
|
|
||||||
// // chunks would only come complete - but they don't!
|
|
||||||
// try {
|
|
||||||
// JSON.parse(message);
|
|
||||||
// validJSON = true;
|
|
||||||
// } catch { }
|
|
||||||
|
|
||||||
// if (!validJSON) {
|
|
||||||
// // It can be possible that the chunk decoding is running away
|
|
||||||
// // and the message chunk fails to append due to string length.
|
|
||||||
// // In this case abort the chunk and reset so we can continue.
|
|
||||||
// // ref: https://github.com/Mintplex-Labs/anything-llm/issues/416
|
|
||||||
// try {
|
|
||||||
// chunk += message;
|
|
||||||
// } catch (e) {
|
|
||||||
// console.error(`Chunk appending error`, e);
|
|
||||||
// chunk = "";
|
|
||||||
// }
|
|
||||||
// continue;
|
|
||||||
// } else {
|
|
||||||
// chunk = "";
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if (message == "[DONE]") {
|
|
||||||
// lastChunkTime = Number(new Date());
|
|
||||||
// writeResponseChunk(response, {
|
|
||||||
// uuid,
|
|
||||||
// sources,
|
|
||||||
// type: "textResponseChunk",
|
|
||||||
// textResponse: "",
|
|
||||||
// close: true,
|
|
||||||
// error: false,
|
|
||||||
// });
|
|
||||||
// clearInterval(timeoutCheck);
|
|
||||||
// response.removeListener("close", handleAbort);
|
|
||||||
// resolve(fullText);
|
|
||||||
// } else {
|
|
||||||
// let finishReason = null;
|
|
||||||
// let token = "";
|
|
||||||
// try {
|
|
||||||
// const json = JSON.parse(message);
|
|
||||||
// token = json?.choices?.[0]?.delta?.content;
|
|
||||||
// finishReason = json?.choices?.[0]?.finish_reason || null;
|
|
||||||
// } catch {
|
|
||||||
// continue;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if (token) {
|
|
||||||
// fullText += token;
|
|
||||||
// lastChunkTime = Number(new Date());
|
|
||||||
// writeResponseChunk(response, {
|
|
||||||
// uuid,
|
|
||||||
// sources: [],
|
|
||||||
// type: "textResponseChunk",
|
|
||||||
// textResponse: token,
|
|
||||||
// close: false,
|
|
||||||
// error: false,
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if (finishReason !== null) {
|
|
||||||
// lastChunkTime = Number(new Date());
|
|
||||||
// writeResponseChunk(response, {
|
|
||||||
// uuid,
|
|
||||||
// sources,
|
|
||||||
// type: "textResponseChunk",
|
|
||||||
// textResponse: "",
|
|
||||||
// close: true,
|
|
||||||
// error: false,
|
|
||||||
// });
|
|
||||||
// clearInterval(timeoutCheck);
|
|
||||||
// response.removeListener("close", handleAbort);
|
|
||||||
// resolve(fullText);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Simple wrapper for dynamic embedder & normalize interface for all LLM implementations
|
// Simple wrapper for dynamic embedder & normalize interface for all LLM implementations
|
||||||
async embedTextInput(textInput) {
|
async embedTextInput(textInput) {
|
||||||
return await this.embedder.embedTextInput(textInput);
|
return await this.embedder.embedTextInput(textInput);
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
const { NativeEmbedder } = require("../../EmbeddingEngines/native");
|
const { NativeEmbedder } = require("../../EmbeddingEngines/native");
|
||||||
const { chatPrompt } = require("../../chats");
|
|
||||||
const {
|
const {
|
||||||
handleDefaultStreamResponseV2,
|
handleDefaultStreamResponseV2,
|
||||||
} = require("../../helpers/chat/responses");
|
} = require("../../helpers/chat/responses");
|
||||||
@ -50,7 +49,7 @@ class PerplexityLLM {
|
|||||||
}
|
}
|
||||||
|
|
||||||
streamingEnabled() {
|
streamingEnabled() {
|
||||||
return "streamChat" in this && "streamGetChatCompletion" in this;
|
return "streamGetChatCompletion" in this;
|
||||||
}
|
}
|
||||||
|
|
||||||
promptWindowLimit() {
|
promptWindowLimit() {
|
||||||
@ -81,65 +80,6 @@ class PerplexityLLM {
|
|||||||
return { safe: true, reasons: [] };
|
return { safe: true, reasons: [] };
|
||||||
}
|
}
|
||||||
|
|
||||||
async sendChat(chatHistory = [], prompt, workspace = {}, rawHistory = []) {
|
|
||||||
if (!(await this.isValidChatCompletionModel(this.model)))
|
|
||||||
throw new Error(
|
|
||||||
`Perplexity chat: ${this.model} is not valid for chat completion!`
|
|
||||||
);
|
|
||||||
|
|
||||||
const textResponse = await this.openai.chat.completions
|
|
||||||
.create({
|
|
||||||
model: this.model,
|
|
||||||
temperature: Number(workspace?.openAiTemp ?? this.defaultTemp),
|
|
||||||
n: 1,
|
|
||||||
messages: await this.compressMessages(
|
|
||||||
{
|
|
||||||
systemPrompt: chatPrompt(workspace),
|
|
||||||
userPrompt: prompt,
|
|
||||||
chatHistory,
|
|
||||||
},
|
|
||||||
rawHistory
|
|
||||||
),
|
|
||||||
})
|
|
||||||
.then((result) => {
|
|
||||||
if (!result.hasOwnProperty("choices"))
|
|
||||||
throw new Error("Perplexity chat: No results!");
|
|
||||||
if (result.choices.length === 0)
|
|
||||||
throw new Error("Perplexity chat: No results length!");
|
|
||||||
return result.choices[0].message.content;
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
throw new Error(
|
|
||||||
`Perplexity::createChatCompletion failed with: ${error.message}`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
return textResponse;
|
|
||||||
}
|
|
||||||
|
|
||||||
async streamChat(chatHistory = [], prompt, workspace = {}, rawHistory = []) {
|
|
||||||
if (!(await this.isValidChatCompletionModel(this.model)))
|
|
||||||
throw new Error(
|
|
||||||
`Perplexity chat: ${this.model} is not valid for chat completion!`
|
|
||||||
);
|
|
||||||
|
|
||||||
const streamRequest = await this.openai.chat.completions.create({
|
|
||||||
model: this.model,
|
|
||||||
stream: true,
|
|
||||||
temperature: Number(workspace?.openAiTemp ?? this.defaultTemp),
|
|
||||||
n: 1,
|
|
||||||
messages: await this.compressMessages(
|
|
||||||
{
|
|
||||||
systemPrompt: chatPrompt(workspace),
|
|
||||||
userPrompt: prompt,
|
|
||||||
chatHistory,
|
|
||||||
},
|
|
||||||
rawHistory
|
|
||||||
),
|
|
||||||
});
|
|
||||||
return streamRequest;
|
|
||||||
}
|
|
||||||
|
|
||||||
async getChatCompletion(messages = null, { temperature = 0.7 }) {
|
async getChatCompletion(messages = null, { temperature = 0.7 }) {
|
||||||
if (!(await this.isValidChatCompletionModel(this.model)))
|
if (!(await this.isValidChatCompletionModel(this.model)))
|
||||||
throw new Error(
|
throw new Error(
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
const { chatPrompt } = require("../../chats");
|
|
||||||
const {
|
const {
|
||||||
handleDefaultStreamResponseV2,
|
handleDefaultStreamResponseV2,
|
||||||
} = require("../../helpers/chat/responses");
|
} = require("../../helpers/chat/responses");
|
||||||
@ -49,7 +48,7 @@ class TogetherAiLLM {
|
|||||||
}
|
}
|
||||||
|
|
||||||
streamingEnabled() {
|
streamingEnabled() {
|
||||||
return "streamChat" in this && "streamGetChatCompletion" in this;
|
return "streamGetChatCompletion" in this;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure the user set a value for the token limit
|
// Ensure the user set a value for the token limit
|
||||||
@ -82,65 +81,6 @@ class TogetherAiLLM {
|
|||||||
return { safe: true, reasons: [] };
|
return { safe: true, reasons: [] };
|
||||||
}
|
}
|
||||||
|
|
||||||
async sendChat(chatHistory = [], prompt, workspace = {}, rawHistory = []) {
|
|
||||||
if (!(await this.isValidChatCompletionModel(this.model)))
|
|
||||||
throw new Error(
|
|
||||||
`Together AI chat: ${this.model} is not valid for chat completion!`
|
|
||||||
);
|
|
||||||
|
|
||||||
const textResponse = await this.openai.chat.completions
|
|
||||||
.create({
|
|
||||||
model: this.model,
|
|
||||||
temperature: Number(workspace?.openAiTemp ?? this.defaultTemp),
|
|
||||||
n: 1,
|
|
||||||
messages: await this.compressMessages(
|
|
||||||
{
|
|
||||||
systemPrompt: chatPrompt(workspace),
|
|
||||||
userPrompt: prompt,
|
|
||||||
chatHistory,
|
|
||||||
},
|
|
||||||
rawHistory
|
|
||||||
),
|
|
||||||
})
|
|
||||||
.then((result) => {
|
|
||||||
if (!result.hasOwnProperty("choices"))
|
|
||||||
throw new Error("Together AI chat: No results!");
|
|
||||||
if (result.choices.length === 0)
|
|
||||||
throw new Error("Together AI chat: No results length!");
|
|
||||||
return result.choices[0].message.content;
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
throw new Error(
|
|
||||||
`TogetherAI::createChatCompletion failed with: ${error.message}`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
return textResponse;
|
|
||||||
}
|
|
||||||
|
|
||||||
async streamChat(chatHistory = [], prompt, workspace = {}, rawHistory = []) {
|
|
||||||
if (!(await this.isValidChatCompletionModel(this.model)))
|
|
||||||
throw new Error(
|
|
||||||
`TogetherAI chat: ${this.model} is not valid for chat completion!`
|
|
||||||
);
|
|
||||||
|
|
||||||
const streamRequest = await this.openai.chat.completions.create({
|
|
||||||
model: this.model,
|
|
||||||
stream: true,
|
|
||||||
temperature: Number(workspace?.openAiTemp ?? this.defaultTemp),
|
|
||||||
n: 1,
|
|
||||||
messages: await this.compressMessages(
|
|
||||||
{
|
|
||||||
systemPrompt: chatPrompt(workspace),
|
|
||||||
userPrompt: prompt,
|
|
||||||
chatHistory,
|
|
||||||
},
|
|
||||||
rawHistory
|
|
||||||
),
|
|
||||||
});
|
|
||||||
return streamRequest;
|
|
||||||
}
|
|
||||||
|
|
||||||
async getChatCompletion(messages = null, { temperature = 0.7 }) {
|
async getChatCompletion(messages = null, { temperature = 0.7 }) {
|
||||||
if (!(await this.isValidChatCompletionModel(this.model)))
|
if (!(await this.isValidChatCompletionModel(this.model)))
|
||||||
throw new Error(
|
throw new Error(
|
||||||
|
86
server/utils/EmbeddingEngines/cohere/index.js
Normal file
86
server/utils/EmbeddingEngines/cohere/index.js
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
const { toChunks } = require("../../helpers");
|
||||||
|
|
||||||
|
class CohereEmbedder {
|
||||||
|
constructor() {
|
||||||
|
if (!process.env.COHERE_API_KEY)
|
||||||
|
throw new Error("No Cohere API key was set.");
|
||||||
|
|
||||||
|
const { CohereClient } = require("cohere-ai");
|
||||||
|
const cohere = new CohereClient({
|
||||||
|
token: process.env.COHERE_API_KEY,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.cohere = cohere;
|
||||||
|
this.model = process.env.EMBEDDING_MODEL_PREF || "embed-english-v3.0";
|
||||||
|
this.inputType = "search_document";
|
||||||
|
|
||||||
|
// Limit of how many strings we can process in a single pass to stay with resource or network limits
|
||||||
|
this.maxConcurrentChunks = 96; // Cohere's limit per request is 96
|
||||||
|
this.embeddingMaxChunkLength = 1945; // https://docs.cohere.com/docs/embed-2 - assume a token is roughly 4 letters with some padding
|
||||||
|
}
|
||||||
|
|
||||||
|
async embedTextInput(textInput) {
|
||||||
|
this.inputType = "search_query";
|
||||||
|
const result = await this.embedChunks([textInput]);
|
||||||
|
return result?.[0] || [];
|
||||||
|
}
|
||||||
|
|
||||||
|
async embedChunks(textChunks = []) {
|
||||||
|
const embeddingRequests = [];
|
||||||
|
this.inputType = "search_document";
|
||||||
|
|
||||||
|
for (const chunk of toChunks(textChunks, this.maxConcurrentChunks)) {
|
||||||
|
embeddingRequests.push(
|
||||||
|
new Promise((resolve) => {
|
||||||
|
this.cohere
|
||||||
|
.embed({
|
||||||
|
texts: chunk,
|
||||||
|
model: this.model,
|
||||||
|
inputType: this.inputType,
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
resolve({ data: res.embeddings, error: null });
|
||||||
|
})
|
||||||
|
.catch((e) => {
|
||||||
|
e.type =
|
||||||
|
e?.response?.data?.error?.code ||
|
||||||
|
e?.response?.status ||
|
||||||
|
"failed_to_embed";
|
||||||
|
e.message = e?.response?.data?.error?.message || e.message;
|
||||||
|
resolve({ data: [], error: e });
|
||||||
|
});
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { data = [], error = null } = await Promise.all(
|
||||||
|
embeddingRequests
|
||||||
|
).then((results) => {
|
||||||
|
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 || []).flat(),
|
||||||
|
error: null,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!!error) throw new Error(`Cohere Failed to embed: ${error}`);
|
||||||
|
|
||||||
|
return data.length > 0 ? data : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
CohereEmbedder,
|
||||||
|
};
|
@ -107,14 +107,21 @@ class NativeEmbedder {
|
|||||||
);
|
);
|
||||||
|
|
||||||
let fetchResponse = await this.#fetchWithHost();
|
let fetchResponse = await this.#fetchWithHost();
|
||||||
if (fetchResponse.pipeline !== null) return fetchResponse.pipeline;
|
if (fetchResponse.pipeline !== null) {
|
||||||
|
this.modelDownloaded = true;
|
||||||
|
return fetchResponse.pipeline;
|
||||||
|
}
|
||||||
|
|
||||||
this.log(
|
this.log(
|
||||||
`Failed to download model from primary URL. Using fallback ${fetchResponse.retry}`
|
`Failed to download model from primary URL. Using fallback ${fetchResponse.retry}`
|
||||||
);
|
);
|
||||||
if (!!fetchResponse.retry)
|
if (!!fetchResponse.retry)
|
||||||
fetchResponse = await this.#fetchWithHost(fetchResponse.retry);
|
fetchResponse = await this.#fetchWithHost(fetchResponse.retry);
|
||||||
if (fetchResponse.pipeline !== null) return fetchResponse.pipeline;
|
if (fetchResponse.pipeline !== null) {
|
||||||
|
this.modelDownloaded = true;
|
||||||
|
return fetchResponse.pipeline;
|
||||||
|
}
|
||||||
|
|
||||||
throw fetchResponse.error;
|
throw fetchResponse.error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,6 +23,7 @@ async function resetMemory(
|
|||||||
sources: [],
|
sources: [],
|
||||||
close: true,
|
close: true,
|
||||||
error: false,
|
error: false,
|
||||||
|
action: "reset_chat",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -131,7 +131,11 @@ async function streamChatWithForEmbed(
|
|||||||
|
|
||||||
// If in query mode and no sources are found, do not
|
// If in query mode and no sources are found, do not
|
||||||
// let the LLM try to hallucinate a response or use general knowledge
|
// let the LLM try to hallucinate a response or use general knowledge
|
||||||
if (chatMode === "query" && sources.length === 0) {
|
if (
|
||||||
|
chatMode === "query" &&
|
||||||
|
sources.length === 0 &&
|
||||||
|
pinnedDocIdentifiers.length === 0
|
||||||
|
) {
|
||||||
writeResponseChunk(response, {
|
writeResponseChunk(response, {
|
||||||
id: uuid,
|
id: uuid,
|
||||||
type: "textResponse",
|
type: "textResponse",
|
||||||
|
@ -140,9 +140,13 @@ async function chatWithWorkspace(
|
|||||||
contextTexts = [...contextTexts, ...vectorSearchResults.contextTexts];
|
contextTexts = [...contextTexts, ...vectorSearchResults.contextTexts];
|
||||||
sources = [...sources, ...vectorSearchResults.sources];
|
sources = [...sources, ...vectorSearchResults.sources];
|
||||||
|
|
||||||
// If in query mode and no sources are found, do not
|
// If in query mode and no sources are found from the vector search and no pinned documents, do not
|
||||||
// let the LLM try to hallucinate a response or use general knowledge and exit early
|
// let the LLM try to hallucinate a response or use general knowledge and exit early
|
||||||
if (chatMode === "query" && sources.length === 0) {
|
if (
|
||||||
|
chatMode === "query" &&
|
||||||
|
vectorSearchResults.sources.length === 0 &&
|
||||||
|
pinnedDocIdentifiers.length === 0
|
||||||
|
) {
|
||||||
return {
|
return {
|
||||||
id: uuid,
|
id: uuid,
|
||||||
type: "textResponse",
|
type: "textResponse",
|
||||||
|
@ -160,9 +160,13 @@ async function streamChatWithWorkspace(
|
|||||||
contextTexts = [...contextTexts, ...vectorSearchResults.contextTexts];
|
contextTexts = [...contextTexts, ...vectorSearchResults.contextTexts];
|
||||||
sources = [...sources, ...vectorSearchResults.sources];
|
sources = [...sources, ...vectorSearchResults.sources];
|
||||||
|
|
||||||
// If in query mode and no sources are found, do not
|
// If in query mode and no sources are found from the vector search and no pinned documents, do not
|
||||||
// let the LLM try to hallucinate a response or use general knowledge and exit early
|
// let the LLM try to hallucinate a response or use general knowledge and exit early
|
||||||
if (chatMode === "query" && sources.length === 0) {
|
if (
|
||||||
|
chatMode === "query" &&
|
||||||
|
sources.length === 0 &&
|
||||||
|
pinnedDocIdentifiers.length === 0
|
||||||
|
) {
|
||||||
writeResponseChunk(response, {
|
writeResponseChunk(response, {
|
||||||
id: uuid,
|
id: uuid,
|
||||||
type: "textResponse",
|
type: "textResponse",
|
||||||
|
@ -14,6 +14,7 @@ const SUPPORT_CUSTOM_MODELS = [
|
|||||||
"perplexity",
|
"perplexity",
|
||||||
"openrouter",
|
"openrouter",
|
||||||
"lmstudio",
|
"lmstudio",
|
||||||
|
"koboldcpp",
|
||||||
];
|
];
|
||||||
|
|
||||||
async function getCustomModels(provider = "", apiKey = null, basePath = null) {
|
async function getCustomModels(provider = "", apiKey = null, basePath = null) {
|
||||||
@ -39,6 +40,8 @@ async function getCustomModels(provider = "", apiKey = null, basePath = null) {
|
|||||||
return await getOpenRouterModels();
|
return await getOpenRouterModels();
|
||||||
case "lmstudio":
|
case "lmstudio":
|
||||||
return await getLMStudioModels(basePath);
|
return await getLMStudioModels(basePath);
|
||||||
|
case "koboldcpp":
|
||||||
|
return await getKoboldCPPModels(basePath);
|
||||||
default:
|
default:
|
||||||
return { models: [], error: "Invalid provider for custom models" };
|
return { models: [], error: "Invalid provider for custom models" };
|
||||||
}
|
}
|
||||||
@ -171,6 +174,28 @@ async function getLMStudioModels(basePath = null) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function getKoboldCPPModels(basePath = null) {
|
||||||
|
try {
|
||||||
|
const { OpenAI: OpenAIApi } = require("openai");
|
||||||
|
const openai = new OpenAIApi({
|
||||||
|
baseURL: basePath || process.env.LMSTUDIO_BASE_PATH,
|
||||||
|
apiKey: null,
|
||||||
|
});
|
||||||
|
const models = await openai.models
|
||||||
|
.list()
|
||||||
|
.then((results) => results.data)
|
||||||
|
.catch((e) => {
|
||||||
|
console.error(`KoboldCPP:listModels`, e.message);
|
||||||
|
return [];
|
||||||
|
});
|
||||||
|
|
||||||
|
return { models, error: null };
|
||||||
|
} catch (e) {
|
||||||
|
console.error(`KoboldCPP:getKoboldCPPModels`, e.message);
|
||||||
|
return { models: [], error: "Could not fetch KoboldCPP Models" };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function ollamaAIModels(basePath = null) {
|
async function ollamaAIModels(basePath = null) {
|
||||||
let url;
|
let url;
|
||||||
try {
|
try {
|
||||||
|
@ -77,6 +77,12 @@ function getLLMProvider({ provider = null, model = null } = {}) {
|
|||||||
case "groq":
|
case "groq":
|
||||||
const { GroqLLM } = require("../AiProviders/groq");
|
const { GroqLLM } = require("../AiProviders/groq");
|
||||||
return new GroqLLM(embedder, model);
|
return new GroqLLM(embedder, model);
|
||||||
|
case "koboldcpp":
|
||||||
|
const { KoboldCPPLLM } = require("../AiProviders/koboldCPP");
|
||||||
|
return new KoboldCPPLLM(embedder, model);
|
||||||
|
case "cohere":
|
||||||
|
const { CohereLLM } = require("../AiProviders/cohere");
|
||||||
|
return new CohereLLM(embedder, model);
|
||||||
case "generic-openai":
|
case "generic-openai":
|
||||||
const { GenericOpenAiLLM } = require("../AiProviders/genericOpenAi");
|
const { GenericOpenAiLLM } = require("../AiProviders/genericOpenAi");
|
||||||
return new GenericOpenAiLLM(embedder, model);
|
return new GenericOpenAiLLM(embedder, model);
|
||||||
@ -110,6 +116,9 @@ function getEmbeddingEngineSelection() {
|
|||||||
case "lmstudio":
|
case "lmstudio":
|
||||||
const { LMStudioEmbedder } = require("../EmbeddingEngines/lmstudio");
|
const { LMStudioEmbedder } = require("../EmbeddingEngines/lmstudio");
|
||||||
return new LMStudioEmbedder();
|
return new LMStudioEmbedder();
|
||||||
|
case "cohere":
|
||||||
|
const { CohereEmbedder } = require("../EmbeddingEngines/cohere");
|
||||||
|
return new CohereEmbedder();
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -132,6 +132,20 @@ const KEY_MAPPING = {
|
|||||||
checks: [nonZero],
|
checks: [nonZero],
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// KoboldCPP Settings
|
||||||
|
KoboldCPPBasePath: {
|
||||||
|
envKey: "KOBOLD_CPP_BASE_PATH",
|
||||||
|
checks: [isNotEmpty, isValidURL],
|
||||||
|
},
|
||||||
|
KoboldCPPModelPref: {
|
||||||
|
envKey: "KOBOLD_CPP_MODEL_PREF",
|
||||||
|
checks: [isNotEmpty],
|
||||||
|
},
|
||||||
|
KoboldCPPTokenLimit: {
|
||||||
|
envKey: "KOBOLD_CPP_MODEL_TOKEN_LIMIT",
|
||||||
|
checks: [nonZero],
|
||||||
|
},
|
||||||
|
|
||||||
// Generic OpenAI InferenceSettings
|
// Generic OpenAI InferenceSettings
|
||||||
GenericOpenAiBasePath: {
|
GenericOpenAiBasePath: {
|
||||||
envKey: "GENERIC_OPEN_AI_BASE_PATH",
|
envKey: "GENERIC_OPEN_AI_BASE_PATH",
|
||||||
@ -290,6 +304,16 @@ const KEY_MAPPING = {
|
|||||||
checks: [isNotEmpty],
|
checks: [isNotEmpty],
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Cohere Options
|
||||||
|
CohereApiKey: {
|
||||||
|
envKey: "COHERE_API_KEY",
|
||||||
|
checks: [isNotEmpty],
|
||||||
|
},
|
||||||
|
CohereModelPref: {
|
||||||
|
envKey: "COHERE_MODEL_PREF",
|
||||||
|
checks: [isNotEmpty],
|
||||||
|
},
|
||||||
|
|
||||||
// Whisper (transcription) providers
|
// Whisper (transcription) providers
|
||||||
WhisperProvider: {
|
WhisperProvider: {
|
||||||
envKey: "WHISPER_PROVIDER",
|
envKey: "WHISPER_PROVIDER",
|
||||||
@ -393,6 +417,8 @@ function supportedLLM(input = "") {
|
|||||||
"perplexity",
|
"perplexity",
|
||||||
"openrouter",
|
"openrouter",
|
||||||
"groq",
|
"groq",
|
||||||
|
"koboldcpp",
|
||||||
|
"cohere",
|
||||||
"generic-openai",
|
"generic-openai",
|
||||||
].includes(input);
|
].includes(input);
|
||||||
return validSelection ? null : `${input} is not a valid LLM provider.`;
|
return validSelection ? null : `${input} is not a valid LLM provider.`;
|
||||||
@ -434,6 +460,7 @@ function supportedEmbeddingModel(input = "") {
|
|||||||
"native",
|
"native",
|
||||||
"ollama",
|
"ollama",
|
||||||
"lmstudio",
|
"lmstudio",
|
||||||
|
"cohere",
|
||||||
];
|
];
|
||||||
return supported.includes(input)
|
return supported.includes(input)
|
||||||
? null
|
? null
|
||||||
|
@ -1817,6 +1817,17 @@ cmake-js@^7.2.1:
|
|||||||
which "^2.0.2"
|
which "^2.0.2"
|
||||||
yargs "^17.7.2"
|
yargs "^17.7.2"
|
||||||
|
|
||||||
|
cohere-ai@^7.9.5:
|
||||||
|
version "7.9.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/cohere-ai/-/cohere-ai-7.9.5.tgz#05a592fe19decb8692d1b19d93ac835d7f816b8b"
|
||||||
|
integrity sha512-tr8LUR3Q46agFpfEwaYwzYO4qAuN0/R/8YroG4bc86LadOacBAabctZUq0zfCdLiL7gB4yWJs4QCzfpRH3rQuw==
|
||||||
|
dependencies:
|
||||||
|
form-data "4.0.0"
|
||||||
|
js-base64 "3.7.2"
|
||||||
|
node-fetch "2.7.0"
|
||||||
|
qs "6.11.2"
|
||||||
|
url-join "4.0.1"
|
||||||
|
|
||||||
color-convert@^1.9.3:
|
color-convert@^1.9.3:
|
||||||
version "1.9.3"
|
version "1.9.3"
|
||||||
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
|
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
|
||||||
@ -2846,19 +2857,19 @@ form-data-encoder@1.7.2:
|
|||||||
resolved "https://registry.yarnpkg.com/form-data-encoder/-/form-data-encoder-1.7.2.tgz#1f1ae3dccf58ed4690b86d87e4f57c654fbab040"
|
resolved "https://registry.yarnpkg.com/form-data-encoder/-/form-data-encoder-1.7.2.tgz#1f1ae3dccf58ed4690b86d87e4f57c654fbab040"
|
||||||
integrity sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==
|
integrity sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==
|
||||||
|
|
||||||
form-data@^3.0.0:
|
form-data@4.0.0, form-data@^4.0.0:
|
||||||
version "3.0.1"
|
version "4.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.1.tgz#ebd53791b78356a99af9a300d4282c4d5eb9755f"
|
resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452"
|
||||||
integrity sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==
|
integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==
|
||||||
dependencies:
|
dependencies:
|
||||||
asynckit "^0.4.0"
|
asynckit "^0.4.0"
|
||||||
combined-stream "^1.0.8"
|
combined-stream "^1.0.8"
|
||||||
mime-types "^2.1.12"
|
mime-types "^2.1.12"
|
||||||
|
|
||||||
form-data@^4.0.0:
|
form-data@^3.0.0:
|
||||||
version "4.0.0"
|
version "3.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452"
|
resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.1.tgz#ebd53791b78356a99af9a300d4282c4d5eb9755f"
|
||||||
integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==
|
integrity sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==
|
||||||
dependencies:
|
dependencies:
|
||||||
asynckit "^0.4.0"
|
asynckit "^0.4.0"
|
||||||
combined-stream "^1.0.8"
|
combined-stream "^1.0.8"
|
||||||
@ -3652,6 +3663,11 @@ joi@^17.11.0:
|
|||||||
"@sideway/formula" "^3.0.1"
|
"@sideway/formula" "^3.0.1"
|
||||||
"@sideway/pinpoint" "^2.0.0"
|
"@sideway/pinpoint" "^2.0.0"
|
||||||
|
|
||||||
|
js-base64@3.7.2:
|
||||||
|
version "3.7.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-3.7.2.tgz#816d11d81a8aff241603d19ce5761e13e41d7745"
|
||||||
|
integrity sha512-NnRs6dsyqUXejqk/yv2aiXlAvOs56sLkX6nUdeaNezI5LFFLlsZjOThmwnrcwh5ZZRwZlCMnVAY3CvhIhoVEKQ==
|
||||||
|
|
||||||
js-tiktoken@^1.0.11, js-tiktoken@^1.0.7, js-tiktoken@^1.0.8:
|
js-tiktoken@^1.0.11, js-tiktoken@^1.0.7, js-tiktoken@^1.0.8:
|
||||||
version "1.0.11"
|
version "1.0.11"
|
||||||
resolved "https://registry.yarnpkg.com/js-tiktoken/-/js-tiktoken-1.0.11.tgz#d7d707b849f703841112660d9d55169424a35344"
|
resolved "https://registry.yarnpkg.com/js-tiktoken/-/js-tiktoken-1.0.11.tgz#d7d707b849f703841112660d9d55169424a35344"
|
||||||
@ -4324,7 +4340,7 @@ node-domexception@1.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/node-domexception/-/node-domexception-1.0.0.tgz#6888db46a1f71c0b76b3f7555016b63fe64766e5"
|
resolved "https://registry.yarnpkg.com/node-domexception/-/node-domexception-1.0.0.tgz#6888db46a1f71c0b76b3f7555016b63fe64766e5"
|
||||||
integrity sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==
|
integrity sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==
|
||||||
|
|
||||||
node-fetch@^2.6.1, node-fetch@^2.6.12, node-fetch@^2.6.7, node-fetch@^2.6.9:
|
node-fetch@2.7.0, node-fetch@^2.6.1, node-fetch@^2.6.12, node-fetch@^2.6.7, node-fetch@^2.6.9:
|
||||||
version "2.7.0"
|
version "2.7.0"
|
||||||
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d"
|
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d"
|
||||||
integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==
|
integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==
|
||||||
@ -4947,6 +4963,13 @@ qs@6.11.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
side-channel "^1.0.4"
|
side-channel "^1.0.4"
|
||||||
|
|
||||||
|
qs@6.11.2:
|
||||||
|
version "6.11.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.2.tgz#64bea51f12c1f5da1bc01496f48ffcff7c69d7d9"
|
||||||
|
integrity sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==
|
||||||
|
dependencies:
|
||||||
|
side-channel "^1.0.4"
|
||||||
|
|
||||||
qs@^6.7.0:
|
qs@^6.7.0:
|
||||||
version "6.12.1"
|
version "6.12.1"
|
||||||
resolved "https://registry.yarnpkg.com/qs/-/qs-6.12.1.tgz#39422111ca7cbdb70425541cba20c7d7b216599a"
|
resolved "https://registry.yarnpkg.com/qs/-/qs-6.12.1.tgz#39422111ca7cbdb70425541cba20c7d7b216599a"
|
||||||
@ -5862,7 +5885,7 @@ uri-js@^4.2.2, uri-js@^4.4.1:
|
|||||||
dependencies:
|
dependencies:
|
||||||
punycode "^2.1.0"
|
punycode "^2.1.0"
|
||||||
|
|
||||||
url-join@^4.0.1:
|
url-join@4.0.1, url-join@^4.0.1:
|
||||||
version "4.0.1"
|
version "4.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/url-join/-/url-join-4.0.1.tgz#b642e21a2646808ffa178c4c5fda39844e12cde7"
|
resolved "https://registry.yarnpkg.com/url-join/-/url-join-4.0.1.tgz#b642e21a2646808ffa178c4c5fda39844e12cde7"
|
||||||
integrity sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==
|
integrity sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==
|
||||||
|
Loading…
Reference in New Issue
Block a user