mirror of
https://github.com/Mintplex-Labs/anything-llm.git
synced 2024-10-04 01:40:12 +02:00
Multiple LLM Support framework + AzureOpenAI Support (#180)
* Remove LangchainJS for chat support chaining Implement runtime LLM selection Implement AzureOpenAI Support for LLM + Emebedding WIP on frontend Update env to reflect the new fields * Remove LangchainJS for chat support chaining Implement runtime LLM selection Implement AzureOpenAI Support for LLM + Emebedding WIP on frontend Update env to reflect the new fields * Replace keys with LLM Selection in settings modal Enforce checks for new ENVs depending on LLM selection
This commit is contained in:
parent
285bddb0fb
commit
1f29cec918
5
.vscode/settings.json
vendored
Normal file
5
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"cSpell.words": [
|
||||||
|
"openai"
|
||||||
|
]
|
||||||
|
}
|
@ -1,8 +1,24 @@
|
|||||||
SERVER_PORT=3001
|
SERVER_PORT=3001
|
||||||
OPEN_AI_KEY=
|
|
||||||
OPEN_MODEL_PREF='gpt-3.5-turbo'
|
|
||||||
CACHE_VECTORS="true"
|
CACHE_VECTORS="true"
|
||||||
|
# JWT_SECRET="my-random-string-for-seeding" # Only needed if AUTH_TOKEN is set. Please generate random string at least 12 chars long.
|
||||||
|
|
||||||
|
###########################################
|
||||||
|
######## LLM API SElECTION ################
|
||||||
|
###########################################
|
||||||
|
LLM_PROVIDER='openai'
|
||||||
|
# OPEN_AI_KEY=
|
||||||
|
OPEN_MODEL_PREF='gpt-3.5-turbo'
|
||||||
|
|
||||||
|
# LLM_PROVIDER='azure'
|
||||||
|
# AZURE_OPENAI_ENDPOINT=
|
||||||
|
# AZURE_OPENAI_KEY=
|
||||||
|
# OPEN_MODEL_PREF='my-gpt35-deployment' # This is the "deployment" on Azure you want to use. Not the base model.
|
||||||
|
# EMBEDDING_MODEL_PREF='embedder-model' # This is the "deployment" on Azure you want to use for embeddings. Not the base model. Valid base model is text-embedding-ada-002
|
||||||
|
|
||||||
|
|
||||||
|
###########################################
|
||||||
|
######## Vector Database Selection ########
|
||||||
|
###########################################
|
||||||
# Enable all below if you are using vector database: Chroma.
|
# Enable all below if you are using vector database: Chroma.
|
||||||
# VECTOR_DB="chroma"
|
# VECTOR_DB="chroma"
|
||||||
# CHROMA_ENDPOINT='http://localhost:8000'
|
# CHROMA_ENDPOINT='http://localhost:8000'
|
||||||
@ -18,7 +34,6 @@ PINECONE_INDEX=
|
|||||||
|
|
||||||
# CLOUD DEPLOYMENT VARIRABLES ONLY
|
# CLOUD DEPLOYMENT VARIRABLES ONLY
|
||||||
# AUTH_TOKEN="hunter2" # This is the password to your application if remote hosting.
|
# AUTH_TOKEN="hunter2" # This is the password to your application if remote hosting.
|
||||||
# JWT_SECRET="my-random-string-for-seeding" # Only needed if AUTH_TOKEN is set. Please generate random string at least 12 chars long.
|
|
||||||
# NO_DEBUG="true"
|
# NO_DEBUG="true"
|
||||||
STORAGE_DIR="./server/storage"
|
STORAGE_DIR="./server/storage"
|
||||||
GOOGLE_APIS_KEY=
|
GOOGLE_APIS_KEY=
|
||||||
|
@ -1,220 +0,0 @@
|
|||||||
import React, { useState } from "react";
|
|
||||||
import { AlertCircle, Loader } from "react-feather";
|
|
||||||
import System from "../../../../models/system";
|
|
||||||
|
|
||||||
const noop = () => false;
|
|
||||||
export default function SystemKeys({ hideModal = noop, user, settings = {} }) {
|
|
||||||
const canDebug = settings.MultiUserMode
|
|
||||||
? settings?.CanDebug && user?.role === "admin"
|
|
||||||
: settings?.CanDebug;
|
|
||||||
function validSettings(settings) {
|
|
||||||
return (
|
|
||||||
settings?.OpenAiKey &&
|
|
||||||
!!settings?.OpenAiModelPref &&
|
|
||||||
!!settings?.VectorDB &&
|
|
||||||
(settings?.VectorDB === "chroma" ? !!settings?.ChromaEndpoint : true) &&
|
|
||||||
(settings?.VectorDB === "pinecone"
|
|
||||||
? !!settings?.PineConeKey &&
|
|
||||||
!!settings?.PineConeEnvironment &&
|
|
||||||
!!settings?.PineConeIndex
|
|
||||||
: true)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="relative w-full max-w-2xl max-h-full">
|
|
||||||
<div className="relative bg-white rounded-lg shadow dark:bg-stone-700">
|
|
||||||
<div className="flex items-start justify-between px-6 py-4">
|
|
||||||
<p className="text-gray-800 dark:text-stone-200 text-base ">
|
|
||||||
These are the credentials and settings for how your AnythingLLM
|
|
||||||
instance will function. Its important these keys are current and
|
|
||||||
correct.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div className="p-6 space-y-6 flex h-full w-full">
|
|
||||||
<div className="w-full flex flex-col gap-y-4">
|
|
||||||
{!validSettings(settings) && (
|
|
||||||
<div className="bg-orange-300 p-4 rounded-lg border border-orange-600 text-orange-700 w-full items-center flex gap-x-2">
|
|
||||||
<AlertCircle className="h-8 w-8" />
|
|
||||||
<p className="text-sm md:text-base ">
|
|
||||||
Ensure all fields are green before attempting to use
|
|
||||||
AnythingLLM or it may not function as expected!
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<ShowKey
|
|
||||||
name="OpenAI API Key"
|
|
||||||
env="OpenAiKey"
|
|
||||||
value={settings?.OpenAiKey ? "*".repeat(20) : ""}
|
|
||||||
valid={settings?.OpenAiKey}
|
|
||||||
allowDebug={canDebug}
|
|
||||||
/>
|
|
||||||
<ShowKey
|
|
||||||
name="OpenAI Model for chats"
|
|
||||||
env="OpenAiModelPref"
|
|
||||||
value={settings?.OpenAiModelPref}
|
|
||||||
valid={!!settings?.OpenAiModelPref}
|
|
||||||
allowDebug={canDebug}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center p-6 space-x-2 border-t border-gray-200 rounded-b dark:border-gray-600">
|
|
||||||
<button
|
|
||||||
onClick={hideModal}
|
|
||||||
type="button"
|
|
||||||
className="text-gray-500 bg-white hover:bg-gray-100 focus:ring-4 focus:outline-none focus:ring-blue-300 rounded-lg border border-gray-200 text-sm font-medium px-5 py-2.5 hover:text-gray-900 focus:z-10 dark:bg-gray-700 dark:text-gray-300 dark:border-gray-500 dark:hover:text-white dark:hover:bg-gray-600 dark:focus:ring-gray-600"
|
|
||||||
>
|
|
||||||
Close
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function ShowKey({ name, env, value, valid, allowDebug = true }) {
|
|
||||||
const [isValid, setIsValid] = useState(valid);
|
|
||||||
const [debug, setDebug] = useState(false);
|
|
||||||
const [saving, setSaving] = useState(false);
|
|
||||||
const handleSubmit = async (e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
setSaving(true);
|
|
||||||
const data = {};
|
|
||||||
const form = new FormData(e.target);
|
|
||||||
for (var [key, value] of form.entries()) data[key] = value;
|
|
||||||
const { error } = await System.updateSystem(data);
|
|
||||||
if (!!error) {
|
|
||||||
alert(error);
|
|
||||||
setSaving(false);
|
|
||||||
setIsValid(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setSaving(false);
|
|
||||||
setDebug(false);
|
|
||||||
setIsValid(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!isValid) {
|
|
||||||
return (
|
|
||||||
<form onSubmit={handleSubmit}>
|
|
||||||
<div>
|
|
||||||
<label
|
|
||||||
htmlFor="error"
|
|
||||||
className="block mb-2 text-sm font-medium text-red-700 dark:text-red-500"
|
|
||||||
>
|
|
||||||
{name}
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
id="error"
|
|
||||||
name={env}
|
|
||||||
disabled={!debug}
|
|
||||||
className="bg-red-50 border border-red-500 text-red-900 placeholder-red-700 text-sm rounded-lg focus:ring-red-500 dark:bg-gray-700 focus:border-red-500 block w-full p-2.5 dark:text-red-500 dark:placeholder-red-500 dark:border-red-500"
|
|
||||||
placeholder={name}
|
|
||||||
defaultValue={value}
|
|
||||||
required={true}
|
|
||||||
autoComplete="off"
|
|
||||||
/>
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<p className="mt-2 text-sm text-red-600 dark:text-red-500">
|
|
||||||
Need setup in .env file.
|
|
||||||
</p>
|
|
||||||
{allowDebug && (
|
|
||||||
<>
|
|
||||||
{debug ? (
|
|
||||||
<div className="flex items-center gap-x-2 mt-2">
|
|
||||||
{saving ? (
|
|
||||||
<>
|
|
||||||
<Loader className="animate-spin h-4 w-4 text-slate-300 dark:text-slate-500" />
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={() => setDebug(false)}
|
|
||||||
className="text-xs text-slate-300 dark:text-slate-500"
|
|
||||||
>
|
|
||||||
Cancel
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
type="submit"
|
|
||||||
className="text-xs text-blue-300 dark:text-blue-500"
|
|
||||||
>
|
|
||||||
Save
|
|
||||||
</button>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={() => setDebug(true)}
|
|
||||||
className="mt-2 text-xs text-slate-300 dark:text-slate-500"
|
|
||||||
>
|
|
||||||
Change
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<form onSubmit={handleSubmit}>
|
|
||||||
<div className="mb-6">
|
|
||||||
<label
|
|
||||||
htmlFor="success"
|
|
||||||
className="block mb-2 text-sm font-medium text-gray-800 dark:text-slate-200"
|
|
||||||
>
|
|
||||||
{name}
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
id="success"
|
|
||||||
name={env}
|
|
||||||
disabled={!debug}
|
|
||||||
className="border border-white text-green-900 dark:text-green-400 placeholder-green-700 dark:placeholder-green-500 text-sm rounded-lg focus:ring-green-500 focus:border-green-500 block w-full p-2.5 dark:bg-gray-700 dark:border-green-500"
|
|
||||||
defaultValue={value}
|
|
||||||
required={true}
|
|
||||||
autoComplete="off"
|
|
||||||
/>
|
|
||||||
{allowDebug && (
|
|
||||||
<div className="flex items-center justify-end">
|
|
||||||
{debug ? (
|
|
||||||
<div className="flex items-center gap-x-2 mt-2">
|
|
||||||
{saving ? (
|
|
||||||
<>
|
|
||||||
<Loader className="animate-spin h-4 w-4 text-slate-300 dark:text-slate-500" />
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<button
|
|
||||||
onClick={() => setDebug(false)}
|
|
||||||
className="text-xs text-slate-300 dark:text-slate-500"
|
|
||||||
>
|
|
||||||
Cancel
|
|
||||||
</button>
|
|
||||||
<button className="text-xs text-blue-300 dark:text-blue-500">
|
|
||||||
Save
|
|
||||||
</button>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<button
|
|
||||||
onClick={() => setDebug(true)}
|
|
||||||
className="mt-2 text-xs text-slate-300 dark:text-slate-500"
|
|
||||||
>
|
|
||||||
Change
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
);
|
|
||||||
}
|
|
281
frontend/src/components/Modals/Settings/LLMSelection/index.jsx
Normal file
281
frontend/src/components/Modals/Settings/LLMSelection/index.jsx
Normal file
@ -0,0 +1,281 @@
|
|||||||
|
import React, { useState } from "react";
|
||||||
|
import System from "../../../../models/system";
|
||||||
|
import OpenAiLogo from "../../../../media/llmprovider/openai.png";
|
||||||
|
import AzureOpenAiLogo from "../../../../media/llmprovider/azure.png";
|
||||||
|
import AnthropicLogo from "../../../../media/llmprovider/anthropic.png";
|
||||||
|
|
||||||
|
const noop = () => false;
|
||||||
|
export default function LLMSelection({
|
||||||
|
hideModal = noop,
|
||||||
|
user,
|
||||||
|
settings = {},
|
||||||
|
}) {
|
||||||
|
const [hasChanges, setHasChanges] = useState(false);
|
||||||
|
const [llmChoice, setLLMChoice] = useState(settings?.LLMProvider || "openai");
|
||||||
|
const [saving, setSaving] = useState(false);
|
||||||
|
const [error, setError] = useState(null);
|
||||||
|
const canDebug = settings.MultiUserMode
|
||||||
|
? settings?.CanDebug && user?.role === "admin"
|
||||||
|
: settings?.CanDebug;
|
||||||
|
|
||||||
|
function updateLLMChoice(selection) {
|
||||||
|
if (!canDebug || selection === llmChoice) return false;
|
||||||
|
setHasChanges(true);
|
||||||
|
setLLMChoice(selection);
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSubmit = async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
setSaving(true);
|
||||||
|
setError(null);
|
||||||
|
const data = {};
|
||||||
|
const form = new FormData(e.target);
|
||||||
|
for (var [key, value] of form.entries()) data[key] = value;
|
||||||
|
const { error } = await System.updateSystem(data);
|
||||||
|
setError(error);
|
||||||
|
setSaving(false);
|
||||||
|
setHasChanges(!!error ? true : false);
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<div className="relative w-full max-w-2xl max-h-full">
|
||||||
|
<div className="relative bg-white rounded-lg shadow dark:bg-stone-700">
|
||||||
|
<div className="flex items-start justify-between px-6 py-4">
|
||||||
|
<p className="text-gray-800 dark:text-stone-200 text-base ">
|
||||||
|
These are the credentials and settings for your preferred LLM chat &
|
||||||
|
embedding provider. Its important these keys are current and correct
|
||||||
|
or else AnythingLLM will not function properly.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{!!error && (
|
||||||
|
<div className="mb-8 bg-red-700 dark:bg-orange-800 bg-opacity-30 border border-red-800 dark:border-orange-600 p-4 rounded-lg w-[90%] flex mx-auto">
|
||||||
|
<p className="text-red-800 dark:text-orange-300 text-sm">{error}</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<form onSubmit={handleSubmit} onChange={() => setHasChanges(true)}>
|
||||||
|
<div className="px-6 space-y-6 flex h-full w-full">
|
||||||
|
<div className="w-full flex flex-col gap-y-4">
|
||||||
|
<p className="block text-sm font-medium text-gray-800 dark:text-slate-200">
|
||||||
|
LLM providers
|
||||||
|
</p>
|
||||||
|
<div className="w-full flex overflow-x-scroll gap-x-4 no-scroll">
|
||||||
|
<input hidden={true} name="LLMProvider" value={llmChoice} />
|
||||||
|
<LLMProviderOption
|
||||||
|
name="OpenAI"
|
||||||
|
value="openai"
|
||||||
|
link="openai.com"
|
||||||
|
description="The standard option for most non-commercial use. Provides both chat and embedding."
|
||||||
|
checked={llmChoice === "openai"}
|
||||||
|
image={OpenAiLogo}
|
||||||
|
onClick={updateLLMChoice}
|
||||||
|
/>
|
||||||
|
<LLMProviderOption
|
||||||
|
name="Azure OpenAi"
|
||||||
|
value="azure"
|
||||||
|
link="azure.microsoft.com"
|
||||||
|
description="The enterprise option of OpenAI hosted on Azure services. Provides both chat and embedding."
|
||||||
|
checked={llmChoice === "azure"}
|
||||||
|
image={AzureOpenAiLogo}
|
||||||
|
onClick={updateLLMChoice}
|
||||||
|
/>
|
||||||
|
<LLMProviderOption
|
||||||
|
name="Anthropic Claude 2"
|
||||||
|
value="anthropic-claude-2"
|
||||||
|
link="anthropic.com"
|
||||||
|
description="[COMING SOON] A friendly AI Assistant hosted by Anthropic. Provides chat services only!"
|
||||||
|
checked={llmChoice === "anthropic-claude-2"}
|
||||||
|
image={AnthropicLogo}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{llmChoice === "openai" && (
|
||||||
|
<>
|
||||||
|
<div>
|
||||||
|
<label className="block mb-2 text-sm font-medium text-gray-800 dark:text-slate-200">
|
||||||
|
API Key
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
name="OpenAiKey"
|
||||||
|
disabled={!canDebug}
|
||||||
|
className="bg-gray-50 border border-gray-500 text-gray-900 placeholder-gray-500 text-sm rounded-lg dark:bg-stone-700 focus:border-stone-500 block w-full p-2.5 dark:text-slate-200 dark:placeholder-stone-500 dark:border-slate-200"
|
||||||
|
placeholder="OpenAI API Key"
|
||||||
|
defaultValue={settings?.OpenAiKey ? "*".repeat(20) : ""}
|
||||||
|
required={true}
|
||||||
|
autoComplete="off"
|
||||||
|
spellCheck={false}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label className="block mb-2 text-sm font-medium text-gray-800 dark:text-slate-200">
|
||||||
|
Chat Model Selection
|
||||||
|
</label>
|
||||||
|
<select
|
||||||
|
disabled={!canDebug}
|
||||||
|
name="OpenAiModelPref"
|
||||||
|
defaultValue={settings?.OpenAiModelPref}
|
||||||
|
required={true}
|
||||||
|
className="bg-gray-50 border border-gray-500 text-gray-900 text-sm rounded-lg block w-full p-2.5 dark:bg-stone-700 dark:border-slate-200 dark:placeholder-stone-500 dark:text-slate-200"
|
||||||
|
>
|
||||||
|
{[
|
||||||
|
"gpt-3.5-turbo",
|
||||||
|
"gpt-3.5-turbo-0613",
|
||||||
|
"gpt-3.5-turbo-16k",
|
||||||
|
"gpt-4",
|
||||||
|
"gpt-4-0613",
|
||||||
|
"gpt-4-32k",
|
||||||
|
"gpt-4-32k-0613",
|
||||||
|
].map((model) => {
|
||||||
|
return (
|
||||||
|
<option key={model} value={model}>
|
||||||
|
{model}
|
||||||
|
</option>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{llmChoice === "azure" && (
|
||||||
|
<>
|
||||||
|
<div>
|
||||||
|
<label className="block mb-2 text-sm font-medium text-gray-800 dark:text-slate-200">
|
||||||
|
Azure Service Endpoint
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="url"
|
||||||
|
name="AzureOpenAiEndpoint"
|
||||||
|
disabled={!canDebug}
|
||||||
|
className="bg-gray-50 border border-gray-500 text-gray-900 placeholder-gray-500 text-sm rounded-lg dark:bg-stone-700 focus:border-stone-500 block w-full p-2.5 dark:text-slate-200 dark:placeholder-stone-500 dark:border-slate-200"
|
||||||
|
placeholder="https://my-azure.openai.azure.com"
|
||||||
|
defaultValue={settings?.AzureOpenAiEndpoint}
|
||||||
|
required={true}
|
||||||
|
autoComplete="off"
|
||||||
|
spellCheck={false}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label className="block mb-2 text-sm font-medium text-gray-800 dark:text-slate-200">
|
||||||
|
API Key
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="password"
|
||||||
|
name="AzureOpenAiKey"
|
||||||
|
disabled={!canDebug}
|
||||||
|
className="bg-gray-50 border border-gray-500 text-gray-900 placeholder-gray-500 text-sm rounded-lg dark:bg-stone-700 focus:border-stone-500 block w-full p-2.5 dark:text-slate-200 dark:placeholder-stone-500 dark:border-slate-200"
|
||||||
|
placeholder="Azure OpenAI API Key"
|
||||||
|
defaultValue={
|
||||||
|
settings?.AzureOpenAiKey ? "*".repeat(20) : ""
|
||||||
|
}
|
||||||
|
required={true}
|
||||||
|
autoComplete="off"
|
||||||
|
spellCheck={false}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label className="block mb-2 text-sm font-medium text-gray-800 dark:text-slate-200">
|
||||||
|
Chat Model Deployment Name
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
name="AzureOpenAiModelPref"
|
||||||
|
disabled={!canDebug}
|
||||||
|
className="bg-gray-50 border border-gray-500 text-gray-900 placeholder-gray-500 text-sm rounded-lg dark:bg-stone-700 focus:border-stone-500 block w-full p-2.5 dark:text-slate-200 dark:placeholder-stone-500 dark:border-slate-200"
|
||||||
|
placeholder="Azure OpenAI chat model deployment name"
|
||||||
|
defaultValue={settings?.AzureOpenAiModelPref}
|
||||||
|
required={true}
|
||||||
|
autoComplete="off"
|
||||||
|
spellCheck={false}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label className="block mb-2 text-sm font-medium text-gray-800 dark:text-slate-200">
|
||||||
|
Embedding Model Deployment Name
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
name="AzureOpenAiEmbeddingModelPref"
|
||||||
|
disabled={!canDebug}
|
||||||
|
className="bg-gray-50 border border-gray-500 text-gray-900 placeholder-gray-500 text-sm rounded-lg dark:bg-stone-700 focus:border-stone-500 block w-full p-2.5 dark:text-slate-200 dark:placeholder-stone-500 dark:border-slate-200"
|
||||||
|
placeholder="Azure OpenAI embedding model deployment name"
|
||||||
|
defaultValue={settings?.AzureOpenAiEmbeddingModelPref}
|
||||||
|
required={true}
|
||||||
|
autoComplete="off"
|
||||||
|
spellCheck={false}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{llmChoice === "anthropic-claude-2" && (
|
||||||
|
<div className="w-full h-40 items-center justify-center flex">
|
||||||
|
<p className="text-gray-800 dark:text-slate-400">
|
||||||
|
This provider is unavailable and cannot be used in
|
||||||
|
AnythingLLM currently.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="w-full p-4">
|
||||||
|
<button
|
||||||
|
hidden={!hasChanges}
|
||||||
|
disabled={saving}
|
||||||
|
type="submit"
|
||||||
|
className="w-full text-gray-500 bg-white hover:bg-gray-100 focus:ring-4 focus:outline-none focus:ring-blue-300 rounded-lg border border-gray-200 text-sm font-medium px-5 py-2.5 hover:text-gray-900 focus:z-10 dark:bg-gray-700 dark:text-gray-300 dark:border-gray-500 dark:hover:text-white dark:hover:bg-gray-600 dark:focus:ring-gray-600"
|
||||||
|
>
|
||||||
|
{saving ? "Saving..." : "Save changes"}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<div className="flex items-center p-6 space-x-2 border-t border-gray-200 rounded-b dark:border-gray-600">
|
||||||
|
<button
|
||||||
|
onClick={hideModal}
|
||||||
|
type="button"
|
||||||
|
className="text-gray-500 bg-white hover:bg-gray-100 focus:ring-4 focus:outline-none focus:ring-blue-300 rounded-lg border border-gray-200 text-sm font-medium px-5 py-2.5 hover:text-gray-900 focus:z-10 dark:bg-gray-700 dark:text-gray-300 dark:border-gray-500 dark:hover:text-white dark:hover:bg-gray-600 dark:focus:ring-gray-600"
|
||||||
|
>
|
||||||
|
Close
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const LLMProviderOption = ({
|
||||||
|
name,
|
||||||
|
link,
|
||||||
|
description,
|
||||||
|
value,
|
||||||
|
image,
|
||||||
|
checked = false,
|
||||||
|
onClick,
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<div onClick={() => onClick(value)}>
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
value={value}
|
||||||
|
className="peer hidden"
|
||||||
|
checked={checked}
|
||||||
|
readOnly={true}
|
||||||
|
formNoValidate={true}
|
||||||
|
/>
|
||||||
|
<label className="transition-all duration-300 inline-flex h-full w-60 cursor-pointer items-center justify-between rounded-lg border border-gray-200 bg-white p-5 text-gray-500 hover:bg-gray-50 hover:text-gray-600 peer-checked:border-blue-600 peer-checked:bg-blue-50 peer-checked:dark:bg-stone-800 peer-checked:text-gray-600 dark:border-slate-200 dark:bg-stone-800 dark:text-slate-400 dark:hover:bg-stone-700 dark:hover:text-slate-300 dark:peer-checked:text-slate-300">
|
||||||
|
<div className="block">
|
||||||
|
<img src={image} alt={name} className="mb-2 h-10 w-10 rounded-full" />
|
||||||
|
<div className="w-full text-lg font-semibold">{name}</div>
|
||||||
|
<div className="flex w-full flex-col gap-y-1 text-sm">
|
||||||
|
<p className="text-xs text-slate-400">{link}</p>
|
||||||
|
{description}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
@ -57,7 +57,7 @@ export default function VectorDBSelection({
|
|||||||
<div className="px-6 space-y-6 flex h-full w-full">
|
<div className="px-6 space-y-6 flex h-full w-full">
|
||||||
<div className="w-full flex flex-col gap-y-4">
|
<div className="w-full flex flex-col gap-y-4">
|
||||||
<p className="block text-sm font-medium text-gray-800 dark:text-slate-200">
|
<p className="block text-sm font-medium text-gray-800 dark:text-slate-200">
|
||||||
Vector database provider
|
Vector database providers
|
||||||
</p>
|
</p>
|
||||||
<div className="w-full flex overflow-x-scroll gap-x-4 no-scroll">
|
<div className="w-full flex overflow-x-scroll gap-x-4 no-scroll">
|
||||||
<input hidden={true} name="VectorDB" value={vectorDB} />
|
<input hidden={true} name="VectorDB" value={vectorDB} />
|
||||||
@ -96,7 +96,7 @@ export default function VectorDBSelection({
|
|||||||
Pinecone DB API Key
|
Pinecone DB API Key
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="password"
|
||||||
name="PineConeKey"
|
name="PineConeKey"
|
||||||
disabled={!canDebug}
|
disabled={!canDebug}
|
||||||
className="bg-gray-50 border border-gray-500 text-gray-900 placeholder-gray-500 text-sm rounded-lg dark:bg-stone-700 focus:border-stone-500 block w-full p-2.5 dark:text-slate-200 dark:placeholder-stone-500 dark:border-slate-200"
|
className="bg-gray-50 border border-gray-500 text-gray-900 placeholder-gray-500 text-sm rounded-lg dark:bg-stone-700 focus:border-stone-500 block w-full p-2.5 dark:text-slate-200 dark:placeholder-stone-500 dark:border-slate-200"
|
||||||
|
@ -1,15 +1,22 @@
|
|||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { Archive, Lock, Key, X, Users, Database } from "react-feather";
|
import {
|
||||||
import SystemKeys from "./Keys";
|
Archive,
|
||||||
|
Lock,
|
||||||
|
X,
|
||||||
|
Users,
|
||||||
|
Database,
|
||||||
|
MessageSquare,
|
||||||
|
} from "react-feather";
|
||||||
import ExportOrImportData from "./ExportImport";
|
import ExportOrImportData from "./ExportImport";
|
||||||
import PasswordProtection from "./PasswordProtection";
|
import PasswordProtection from "./PasswordProtection";
|
||||||
import System from "../../../models/system";
|
import System from "../../../models/system";
|
||||||
import MultiUserMode from "./MultiUserMode";
|
import MultiUserMode from "./MultiUserMode";
|
||||||
import useUser from "../../../hooks/useUser";
|
import useUser from "../../../hooks/useUser";
|
||||||
import VectorDBSelection from "./VectorDbs";
|
import VectorDBSelection from "./VectorDbs";
|
||||||
|
import LLMSelection from "./LLMSelection";
|
||||||
|
|
||||||
const TABS = {
|
const TABS = {
|
||||||
keys: SystemKeys,
|
llm: LLMSelection,
|
||||||
exportimport: ExportOrImportData,
|
exportimport: ExportOrImportData,
|
||||||
password: PasswordProtection,
|
password: PasswordProtection,
|
||||||
multiuser: MultiUserMode,
|
multiuser: MultiUserMode,
|
||||||
@ -20,9 +27,9 @@ const noop = () => false;
|
|||||||
export default function SystemSettingsModal({ hideModal = noop }) {
|
export default function SystemSettingsModal({ hideModal = noop }) {
|
||||||
const { user } = useUser();
|
const { user } = useUser();
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [selectedTab, setSelectedTab] = useState("keys");
|
const [selectedTab, setSelectedTab] = useState("llm");
|
||||||
const [settings, setSettings] = useState(null);
|
const [settings, setSettings] = useState(null);
|
||||||
const Component = TABS[selectedTab || "keys"];
|
const Component = TABS[selectedTab || "llm"];
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
async function fetchKeys() {
|
async function fetchKeys() {
|
||||||
@ -87,10 +94,10 @@ function SettingTabs({ selectedTab, changeTab, settings, user }) {
|
|||||||
return (
|
return (
|
||||||
<ul className="flex overflow-x-scroll no-scroll -mb-px text-sm gap-x-2 font-medium text-center text-gray-500 dark:text-gray-400">
|
<ul className="flex overflow-x-scroll no-scroll -mb-px text-sm gap-x-2 font-medium text-center text-gray-500 dark:text-gray-400">
|
||||||
<SettingTab
|
<SettingTab
|
||||||
active={selectedTab === "keys"}
|
active={selectedTab === "llm"}
|
||||||
displayName="Keys"
|
displayName="LLM Choice"
|
||||||
tabName="keys"
|
tabName="llm"
|
||||||
icon={<Key className="h-4 w-4 flex-shrink-0" />}
|
icon={<MessageSquare className="h-4 w-4 flex-shrink-0" />}
|
||||||
onClick={changeTab}
|
onClick={changeTab}
|
||||||
/>
|
/>
|
||||||
<SettingTab
|
<SettingTab
|
||||||
|
BIN
frontend/src/media/llmprovider/anthropic.png
Normal file
BIN
frontend/src/media/llmprovider/anthropic.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 12 KiB |
BIN
frontend/src/media/llmprovider/azure.png
Normal file
BIN
frontend/src/media/llmprovider/azure.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 34 KiB |
BIN
frontend/src/media/llmprovider/openai.png
Normal file
BIN
frontend/src/media/llmprovider/openai.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 22 KiB |
@ -1,8 +1,23 @@
|
|||||||
SERVER_PORT=3001
|
SERVER_PORT=3001
|
||||||
OPEN_AI_KEY=
|
|
||||||
OPEN_MODEL_PREF='gpt-3.5-turbo'
|
|
||||||
CACHE_VECTORS="true"
|
CACHE_VECTORS="true"
|
||||||
|
JWT_SECRET="my-random-string-for-seeding" # Please generate random string at least 12 chars long.
|
||||||
|
|
||||||
|
###########################################
|
||||||
|
######## LLM API SElECTION ################
|
||||||
|
###########################################
|
||||||
|
LLM_PROVIDER='openai'
|
||||||
|
# OPEN_AI_KEY=
|
||||||
|
OPEN_MODEL_PREF='gpt-3.5-turbo'
|
||||||
|
|
||||||
|
# LLM_PROVIDER='azure'
|
||||||
|
# AZURE_OPENAI_ENDPOINT=
|
||||||
|
# AZURE_OPENAI_KEY=
|
||||||
|
# OPEN_MODEL_PREF='my-gpt35-deployment' # This is the "deployment" on Azure you want to use. Not the base model.
|
||||||
|
# EMBEDDING_MODEL_PREF='embedder-model' # This is the "deployment" on Azure you want to use for embeddings. Not the base model. Valid base model is text-embedding-ada-002
|
||||||
|
|
||||||
|
###########################################
|
||||||
|
######## Vector Database Selection ########
|
||||||
|
###########################################
|
||||||
# Enable all below if you are using vector database: Chroma.
|
# Enable all below if you are using vector database: Chroma.
|
||||||
# VECTOR_DB="chroma"
|
# VECTOR_DB="chroma"
|
||||||
# CHROMA_ENDPOINT='http://localhost:8000'
|
# CHROMA_ENDPOINT='http://localhost:8000'
|
||||||
@ -16,8 +31,6 @@ PINECONE_INDEX=
|
|||||||
# Enable all below if you are using vector database: LanceDB.
|
# Enable all below if you are using vector database: LanceDB.
|
||||||
# VECTOR_DB="lancedb"
|
# VECTOR_DB="lancedb"
|
||||||
|
|
||||||
JWT_SECRET="my-random-string-for-seeding" # Please generate random string at least 12 chars long.
|
|
||||||
|
|
||||||
# CLOUD DEPLOYMENT VARIRABLES ONLY
|
# CLOUD DEPLOYMENT VARIRABLES ONLY
|
||||||
# AUTH_TOKEN="hunter2" # This is the password to your application if remote hosting.
|
# AUTH_TOKEN="hunter2" # This is the password to your application if remote hosting.
|
||||||
# STORAGE_DIR= # absolute filesystem path with no trailing slash
|
# STORAGE_DIR= # absolute filesystem path with no trailing slash
|
||||||
|
@ -38,17 +38,16 @@ function systemEndpoints(app) {
|
|||||||
|
|
||||||
app.get("/setup-complete", async (_, response) => {
|
app.get("/setup-complete", async (_, response) => {
|
||||||
try {
|
try {
|
||||||
|
const llmProvider = process.env.LLM_PROVIDER || "openai";
|
||||||
const vectorDB = process.env.VECTOR_DB || "pinecone";
|
const vectorDB = process.env.VECTOR_DB || "pinecone";
|
||||||
const results = {
|
const results = {
|
||||||
CanDebug: !!!process.env.NO_DEBUG,
|
CanDebug: !!!process.env.NO_DEBUG,
|
||||||
RequiresAuth: !!process.env.AUTH_TOKEN,
|
RequiresAuth: !!process.env.AUTH_TOKEN,
|
||||||
VectorDB: vectorDB,
|
|
||||||
OpenAiKey: !!process.env.OPEN_AI_KEY,
|
|
||||||
OpenAiModelPref: process.env.OPEN_MODEL_PREF || "gpt-3.5-turbo",
|
|
||||||
AuthToken: !!process.env.AUTH_TOKEN,
|
AuthToken: !!process.env.AUTH_TOKEN,
|
||||||
JWTSecret: !!process.env.JWT_SECRET,
|
JWTSecret: !!process.env.JWT_SECRET,
|
||||||
StorageDir: process.env.STORAGE_DIR,
|
StorageDir: process.env.STORAGE_DIR,
|
||||||
MultiUserMode: await SystemSettings.isMultiUserMode(),
|
MultiUserMode: await SystemSettings.isMultiUserMode(),
|
||||||
|
VectorDB: vectorDB,
|
||||||
...(vectorDB === "pinecone"
|
...(vectorDB === "pinecone"
|
||||||
? {
|
? {
|
||||||
PineConeEnvironment: process.env.PINECONE_ENVIRONMENT,
|
PineConeEnvironment: process.env.PINECONE_ENVIRONMENT,
|
||||||
@ -61,6 +60,22 @@ function systemEndpoints(app) {
|
|||||||
ChromaEndpoint: process.env.CHROMA_ENDPOINT,
|
ChromaEndpoint: process.env.CHROMA_ENDPOINT,
|
||||||
}
|
}
|
||||||
: {}),
|
: {}),
|
||||||
|
LLMProvider: llmProvider,
|
||||||
|
...(llmProvider === "openai"
|
||||||
|
? {
|
||||||
|
OpenAiKey: !!process.env.OPEN_AI_KEY,
|
||||||
|
OpenAiModelPref: process.env.OPEN_MODEL_PREF || "gpt-3.5-turbo",
|
||||||
|
}
|
||||||
|
: {}),
|
||||||
|
|
||||||
|
...(llmProvider === "azure"
|
||||||
|
? {
|
||||||
|
AzureOpenAiEndpoint: process.env.AZURE_OPENAI_ENDPOINT,
|
||||||
|
AzureOpenAiKey: !!process.env.AZURE_OPENAI_KEY,
|
||||||
|
AzureOpenAiModelPref: process.env.OPEN_MODEL_PREF,
|
||||||
|
AzureOpenAiEmbeddingModelPref: process.env.EMBEDDING_MODEL_PREF,
|
||||||
|
}
|
||||||
|
: {}),
|
||||||
};
|
};
|
||||||
response.status(200).json({ results });
|
response.status(200).json({ results });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
"lint": "yarn prettier --write ./endpoints ./models ./utils index.js"
|
"lint": "yarn prettier --write ./endpoints ./models ./utils index.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@azure/openai": "^1.0.0-beta.3",
|
||||||
"@googleapis/youtube": "^9.0.0",
|
"@googleapis/youtube": "^9.0.0",
|
||||||
"@pinecone-database/pinecone": "^0.1.6",
|
"@pinecone-database/pinecone": "^0.1.6",
|
||||||
"archiver": "^5.3.1",
|
"archiver": "^5.3.1",
|
||||||
|
99
server/utils/AiProviders/azureOpenAi/index.js
Normal file
99
server/utils/AiProviders/azureOpenAi/index.js
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
class AzureOpenAi {
|
||||||
|
constructor() {
|
||||||
|
const { OpenAIClient, AzureKeyCredential } = require("@azure/openai");
|
||||||
|
const openai = new OpenAIClient(
|
||||||
|
process.env.AZURE_OPENAI_ENDPOINT,
|
||||||
|
new AzureKeyCredential(process.env.AZURE_OPENAI_KEY)
|
||||||
|
);
|
||||||
|
this.openai = openai;
|
||||||
|
}
|
||||||
|
|
||||||
|
isValidChatModel(_modelName = "") {
|
||||||
|
// The Azure user names their "models" as deployments and they can be any name
|
||||||
|
// so we rely on the user to put in the correct deployment as only they would
|
||||||
|
// know it.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
async isSafe(_input = "") {
|
||||||
|
// Not implemented by Azure OpenAI so must be stubbed
|
||||||
|
return { safe: true, reasons: [] };
|
||||||
|
}
|
||||||
|
|
||||||
|
async sendChat(chatHistory = [], prompt, workspace = {}) {
|
||||||
|
const model = process.env.OPEN_MODEL_PREF;
|
||||||
|
if (!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 textResponse = await this.openai
|
||||||
|
.getChatCompletions(
|
||||||
|
model,
|
||||||
|
[
|
||||||
|
{ role: "system", content: "" },
|
||||||
|
...chatHistory,
|
||||||
|
{ role: "user", content: prompt },
|
||||||
|
],
|
||||||
|
{
|
||||||
|
temperature: Number(workspace?.openAiTemp ?? 0.7),
|
||||||
|
n: 1,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.then((res) => {
|
||||||
|
if (!res.hasOwnProperty("choices"))
|
||||||
|
throw new Error("OpenAI chat: No results!");
|
||||||
|
if (res.choices.length === 0)
|
||||||
|
throw new Error("OpenAI 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 getChatCompletion(messages = [], { temperature = 0.7 }) {
|
||||||
|
const model = process.env.OPEN_MODEL_PREF;
|
||||||
|
if (!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 data = await this.openai.getChatCompletions(model, messages, {
|
||||||
|
temperature,
|
||||||
|
});
|
||||||
|
if (!data.hasOwnProperty("choices")) return null;
|
||||||
|
return data.choices[0].message.content;
|
||||||
|
}
|
||||||
|
|
||||||
|
async embedTextInput(textInput) {
|
||||||
|
const result = await this.embedChunks(textInput);
|
||||||
|
return result?.[0] || [];
|
||||||
|
}
|
||||||
|
|
||||||
|
async embedChunks(textChunks = []) {
|
||||||
|
const textEmbeddingModel =
|
||||||
|
process.env.EMBEDDING_MODEL_PREF || "text-embedding-ada-002";
|
||||||
|
if (!textEmbeddingModel)
|
||||||
|
throw new Error(
|
||||||
|
"No EMBEDDING_MODEL_PREF ENV defined. This must the name of a deployment on your Azure account for an embedding model."
|
||||||
|
);
|
||||||
|
|
||||||
|
const { data = [] } = await this.openai.getEmbeddings(
|
||||||
|
textEmbeddingModel,
|
||||||
|
textChunks
|
||||||
|
);
|
||||||
|
return data.length > 0 &&
|
||||||
|
data.every((embd) => embd.hasOwnProperty("embedding"))
|
||||||
|
? data.map((embd) => embd.embedding)
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
AzureOpenAi,
|
||||||
|
};
|
@ -1,7 +1,6 @@
|
|||||||
const { Configuration, OpenAIApi } = require("openai");
|
|
||||||
|
|
||||||
class OpenAi {
|
class OpenAi {
|
||||||
constructor() {
|
constructor() {
|
||||||
|
const { Configuration, OpenAIApi } = require("openai");
|
||||||
const config = new Configuration({
|
const config = new Configuration({
|
||||||
apiKey: process.env.OPEN_AI_KEY,
|
apiKey: process.env.OPEN_AI_KEY,
|
||||||
});
|
});
|
||||||
|
@ -3,7 +3,8 @@ const { OpenAi } = require("../AiProviders/openAi");
|
|||||||
const { WorkspaceChats } = require("../../models/workspaceChats");
|
const { WorkspaceChats } = require("../../models/workspaceChats");
|
||||||
const { resetMemory } = require("./commands/reset");
|
const { resetMemory } = require("./commands/reset");
|
||||||
const moment = require("moment");
|
const moment = require("moment");
|
||||||
const { getVectorDbClass } = require("../helpers");
|
const { getVectorDbClass, getLLMProvider } = require("../helpers");
|
||||||
|
const { AzureOpenAi } = require("../AiProviders/azureOpenAi");
|
||||||
|
|
||||||
function convertToChatHistory(history = []) {
|
function convertToChatHistory(history = []) {
|
||||||
const formattedHistory = [];
|
const formattedHistory = [];
|
||||||
@ -66,7 +67,7 @@ async function chatWithWorkspace(
|
|||||||
user = null
|
user = null
|
||||||
) {
|
) {
|
||||||
const uuid = uuidv4();
|
const uuid = uuidv4();
|
||||||
const openai = new OpenAi();
|
const LLMConnector = getLLMProvider();
|
||||||
const VectorDb = getVectorDbClass();
|
const VectorDb = getVectorDbClass();
|
||||||
const command = grepCommand(message);
|
const command = grepCommand(message);
|
||||||
|
|
||||||
@ -74,7 +75,7 @@ async function chatWithWorkspace(
|
|||||||
return await VALID_COMMANDS[command](workspace, message, uuid, user);
|
return await VALID_COMMANDS[command](workspace, message, uuid, user);
|
||||||
}
|
}
|
||||||
|
|
||||||
const { safe, reasons = [] } = await openai.isSafe(message);
|
const { safe, reasons = [] } = await LLMConnector.isSafe(message);
|
||||||
if (!safe) {
|
if (!safe) {
|
||||||
return {
|
return {
|
||||||
id: uuid,
|
id: uuid,
|
||||||
@ -93,7 +94,11 @@ async function chatWithWorkspace(
|
|||||||
if (!hasVectorizedSpace || embeddingsCount === 0) {
|
if (!hasVectorizedSpace || embeddingsCount === 0) {
|
||||||
const rawHistory = await WorkspaceChats.forWorkspace(workspace.id);
|
const rawHistory = await WorkspaceChats.forWorkspace(workspace.id);
|
||||||
const chatHistory = convertToPromptHistory(rawHistory);
|
const chatHistory = convertToPromptHistory(rawHistory);
|
||||||
const response = await openai.sendChat(chatHistory, message, workspace);
|
const response = await LLMConnector.sendChat(
|
||||||
|
chatHistory,
|
||||||
|
message,
|
||||||
|
workspace
|
||||||
|
);
|
||||||
const data = { text: response, sources: [], type: "chat" };
|
const data = { text: response, sources: [], type: "chat" };
|
||||||
|
|
||||||
await WorkspaceChats.new({
|
await WorkspaceChats.new({
|
||||||
|
@ -1,21 +1,34 @@
|
|||||||
function getVectorDbClass() {
|
function getVectorDbClass() {
|
||||||
const { Pinecone } = require("../vectorDbProviders/pinecone");
|
|
||||||
const { Chroma } = require("../vectorDbProviders/chroma");
|
|
||||||
const { LanceDb } = require("../vectorDbProviders/lance");
|
|
||||||
|
|
||||||
const vectorSelection = process.env.VECTOR_DB || "pinecone";
|
const vectorSelection = process.env.VECTOR_DB || "pinecone";
|
||||||
switch (vectorSelection) {
|
switch (vectorSelection) {
|
||||||
case "pinecone":
|
case "pinecone":
|
||||||
|
const { Pinecone } = require("../vectorDbProviders/pinecone");
|
||||||
return Pinecone;
|
return Pinecone;
|
||||||
case "chroma":
|
case "chroma":
|
||||||
|
const { Chroma } = require("../vectorDbProviders/chroma");
|
||||||
return Chroma;
|
return Chroma;
|
||||||
case "lancedb":
|
case "lancedb":
|
||||||
|
const { LanceDb } = require("../vectorDbProviders/lance");
|
||||||
return LanceDb;
|
return LanceDb;
|
||||||
default:
|
default:
|
||||||
throw new Error("ENV: No VECTOR_DB value found in environment!");
|
throw new Error("ENV: No VECTOR_DB value found in environment!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getLLMProvider() {
|
||||||
|
const vectorSelection = process.env.LLM_PROVIDER || "openai";
|
||||||
|
switch (vectorSelection) {
|
||||||
|
case "openai":
|
||||||
|
const { OpenAi } = require("../AiProviders/openAi");
|
||||||
|
return new OpenAi();
|
||||||
|
case "azure":
|
||||||
|
const { AzureOpenAi } = require("../AiProviders/azureOpenAi");
|
||||||
|
return new AzureOpenAi();
|
||||||
|
default:
|
||||||
|
throw new Error("ENV: No LLM_PROVIDER value found in environment!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function toChunks(arr, size) {
|
function toChunks(arr, size) {
|
||||||
return Array.from({ length: Math.ceil(arr.length / size) }, (_v, i) =>
|
return Array.from({ length: Math.ceil(arr.length / size) }, (_v, i) =>
|
||||||
arr.slice(i * size, i * size + size)
|
arr.slice(i * size, i * size + size)
|
||||||
@ -24,5 +37,6 @@ function toChunks(arr, size) {
|
|||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
getVectorDbClass,
|
getVectorDbClass,
|
||||||
|
getLLMProvider,
|
||||||
toChunks,
|
toChunks,
|
||||||
};
|
};
|
||||||
|
@ -1,4 +1,9 @@
|
|||||||
const KEY_MAPPING = {
|
const KEY_MAPPING = {
|
||||||
|
LLMProvider: {
|
||||||
|
envKey: "LLM_PROVIDER",
|
||||||
|
checks: [isNotEmpty, supportedLLM],
|
||||||
|
},
|
||||||
|
// OpenAI Settings
|
||||||
OpenAiKey: {
|
OpenAiKey: {
|
||||||
envKey: "OPEN_AI_KEY",
|
envKey: "OPEN_AI_KEY",
|
||||||
checks: [isNotEmpty, validOpenAIKey],
|
checks: [isNotEmpty, validOpenAIKey],
|
||||||
@ -7,6 +12,25 @@ const KEY_MAPPING = {
|
|||||||
envKey: "OPEN_MODEL_PREF",
|
envKey: "OPEN_MODEL_PREF",
|
||||||
checks: [isNotEmpty, validOpenAIModel],
|
checks: [isNotEmpty, validOpenAIModel],
|
||||||
},
|
},
|
||||||
|
// Azure OpenAI Settings
|
||||||
|
AzureOpenAiEndpoint: {
|
||||||
|
envKey: "AZURE_OPENAI_ENDPOINT",
|
||||||
|
checks: [isNotEmpty, validAzureURL],
|
||||||
|
},
|
||||||
|
AzureOpenAiKey: {
|
||||||
|
envKey: "AZURE_OPENAI_KEY",
|
||||||
|
checks: [isNotEmpty],
|
||||||
|
},
|
||||||
|
AzureOpenAiModelPref: {
|
||||||
|
envKey: "OPEN_MODEL_PREF",
|
||||||
|
checks: [isNotEmpty],
|
||||||
|
},
|
||||||
|
AzureOpenAiEmbeddingModelPref: {
|
||||||
|
envKey: "EMBEDDING_MODEL_PREF",
|
||||||
|
checks: [isNotEmpty],
|
||||||
|
},
|
||||||
|
|
||||||
|
// Vector Database Selection Settings
|
||||||
VectorDB: {
|
VectorDB: {
|
||||||
envKey: "VECTOR_DB",
|
envKey: "VECTOR_DB",
|
||||||
checks: [isNotEmpty, supportedVectorDB],
|
checks: [isNotEmpty, supportedVectorDB],
|
||||||
@ -27,6 +51,8 @@ const KEY_MAPPING = {
|
|||||||
envKey: "PINECONE_INDEX",
|
envKey: "PINECONE_INDEX",
|
||||||
checks: [],
|
checks: [],
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// System Settings
|
||||||
AuthToken: {
|
AuthToken: {
|
||||||
envKey: "AUTH_TOKEN",
|
envKey: "AUTH_TOKEN",
|
||||||
checks: [],
|
checks: [],
|
||||||
@ -56,6 +82,10 @@ function validOpenAIKey(input = "") {
|
|||||||
return input.startsWith("sk-") ? null : "OpenAI Key must start with sk-";
|
return input.startsWith("sk-") ? null : "OpenAI Key must start with sk-";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function supportedLLM(input = "") {
|
||||||
|
return ["openai", "azure"].includes(input);
|
||||||
|
}
|
||||||
|
|
||||||
function validOpenAIModel(input = "") {
|
function validOpenAIModel(input = "") {
|
||||||
const validModels = [
|
const validModels = [
|
||||||
"gpt-4",
|
"gpt-4",
|
||||||
@ -85,6 +115,17 @@ function validChromaURL(input = "") {
|
|||||||
: null;
|
: null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function validAzureURL(input = "") {
|
||||||
|
try {
|
||||||
|
new URL(input);
|
||||||
|
if (!input.includes("openai.azure.com"))
|
||||||
|
return "URL must include openai.azure.com";
|
||||||
|
return null;
|
||||||
|
} catch {
|
||||||
|
return "Not a valid URL";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// This will force update .env variables which for any which reason were not able to be parsed or
|
// This will force update .env variables which for any which reason were not able to be parsed or
|
||||||
// read from an ENV file as this seems to be a complicating step for many so allowing people to write
|
// read from an ENV file as this seems to be a complicating step for many so allowing people to write
|
||||||
// to the process will at least alleviate that issue. It does not perform comprehensive validity checks or sanity checks
|
// to the process will at least alleviate that issue. It does not perform comprehensive validity checks or sanity checks
|
||||||
|
@ -1,14 +1,9 @@
|
|||||||
const { ChromaClient, OpenAIEmbeddingFunction } = require("chromadb");
|
const { ChromaClient } = require("chromadb");
|
||||||
const { Chroma: ChromaStore } = require("langchain/vectorstores/chroma");
|
|
||||||
const { OpenAI } = require("langchain/llms/openai");
|
|
||||||
const { VectorDBQAChain } = require("langchain/chains");
|
|
||||||
const { OpenAIEmbeddings } = require("langchain/embeddings/openai");
|
|
||||||
const { RecursiveCharacterTextSplitter } = require("langchain/text_splitter");
|
const { RecursiveCharacterTextSplitter } = require("langchain/text_splitter");
|
||||||
const { storeVectorResult, cachedVectorInformation } = require("../../files");
|
const { storeVectorResult, cachedVectorInformation } = require("../../files");
|
||||||
const { v4: uuidv4 } = require("uuid");
|
const { v4: uuidv4 } = require("uuid");
|
||||||
const { toChunks } = require("../../helpers");
|
const { toChunks, getLLMProvider } = require("../../helpers");
|
||||||
const { chatPrompt } = require("../../chats");
|
const { chatPrompt } = require("../../chats");
|
||||||
const { OpenAi } = require("../../AiProviders/openAi");
|
|
||||||
|
|
||||||
const Chroma = {
|
const Chroma = {
|
||||||
name: "Chroma",
|
name: "Chroma",
|
||||||
@ -49,22 +44,6 @@ const Chroma = {
|
|||||||
const namespace = await this.namespace(client, _namespace);
|
const namespace = await this.namespace(client, _namespace);
|
||||||
return namespace?.vectorCount || 0;
|
return namespace?.vectorCount || 0;
|
||||||
},
|
},
|
||||||
embeddingFunc: function () {
|
|
||||||
return new OpenAIEmbeddingFunction({
|
|
||||||
openai_api_key: process.env.OPEN_AI_KEY,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
embedder: function () {
|
|
||||||
return new OpenAIEmbeddings({ openAIApiKey: process.env.OPEN_AI_KEY });
|
|
||||||
},
|
|
||||||
llm: function ({ temperature = 0.7 }) {
|
|
||||||
const model = process.env.OPEN_MODEL_PREF || "gpt-3.5-turbo";
|
|
||||||
return new OpenAI({
|
|
||||||
openAIApiKey: process.env.OPEN_AI_KEY,
|
|
||||||
modelName: model,
|
|
||||||
temperature,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
similarityResponse: async function (client, namespace, queryVector) {
|
similarityResponse: async function (client, namespace, queryVector) {
|
||||||
const collection = await client.getCollection({ name: namespace });
|
const collection = await client.getCollection({ name: namespace });
|
||||||
const result = {
|
const result = {
|
||||||
@ -131,7 +110,6 @@ const Chroma = {
|
|||||||
const collection = await client.getOrCreateCollection({
|
const collection = await client.getOrCreateCollection({
|
||||||
name: namespace,
|
name: namespace,
|
||||||
metadata: { "hnsw:space": "cosine" },
|
metadata: { "hnsw:space": "cosine" },
|
||||||
embeddingFunction: this.embeddingFunc(),
|
|
||||||
});
|
});
|
||||||
const { chunks } = cacheResult;
|
const { chunks } = cacheResult;
|
||||||
const documentVectors = [];
|
const documentVectors = [];
|
||||||
@ -176,10 +154,10 @@ const Chroma = {
|
|||||||
const textChunks = await textSplitter.splitText(pageContent);
|
const textChunks = await textSplitter.splitText(pageContent);
|
||||||
|
|
||||||
console.log("Chunks created from document:", textChunks.length);
|
console.log("Chunks created from document:", textChunks.length);
|
||||||
const openAiConnector = new OpenAi();
|
const LLMConnector = getLLMProvider();
|
||||||
const documentVectors = [];
|
const documentVectors = [];
|
||||||
const vectors = [];
|
const vectors = [];
|
||||||
const vectorValues = await openAiConnector.embedChunks(textChunks);
|
const vectorValues = await LLMConnector.embedChunks(textChunks);
|
||||||
const submission = {
|
const submission = {
|
||||||
ids: [],
|
ids: [],
|
||||||
embeddings: [],
|
embeddings: [],
|
||||||
@ -216,7 +194,6 @@ const Chroma = {
|
|||||||
const collection = await client.getOrCreateCollection({
|
const collection = await client.getOrCreateCollection({
|
||||||
name: namespace,
|
name: namespace,
|
||||||
metadata: { "hnsw:space": "cosine" },
|
metadata: { "hnsw:space": "cosine" },
|
||||||
embeddingFunction: this.embeddingFunc(),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (vectors.length > 0) {
|
if (vectors.length > 0) {
|
||||||
@ -245,7 +222,6 @@ const Chroma = {
|
|||||||
if (!(await this.namespaceExists(client, namespace))) return;
|
if (!(await this.namespaceExists(client, namespace))) return;
|
||||||
const collection = await client.getCollection({
|
const collection = await client.getCollection({
|
||||||
name: namespace,
|
name: namespace,
|
||||||
embeddingFunction: this.embeddingFunc(),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const knownDocuments = await DocumentVectors.where(`docId = '${docId}'`);
|
const knownDocuments = await DocumentVectors.where(`docId = '${docId}'`);
|
||||||
@ -271,22 +247,36 @@ const Chroma = {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const vectorStore = await ChromaStore.fromExistingCollection(
|
const LLMConnector = getLLMProvider();
|
||||||
this.embedder(),
|
const queryVector = await LLMConnector.embedTextInput(input);
|
||||||
{ collectionName: namespace, url: process.env.CHROMA_ENDPOINT }
|
const { contextTexts, sourceDocuments } = await this.similarityResponse(
|
||||||
|
client,
|
||||||
|
namespace,
|
||||||
|
queryVector
|
||||||
);
|
);
|
||||||
const model = this.llm({
|
const prompt = {
|
||||||
|
role: "system",
|
||||||
|
content: `${chatPrompt(workspace)}
|
||||||
|
Context:
|
||||||
|
${contextTexts
|
||||||
|
.map((text, i) => {
|
||||||
|
return `[CONTEXT ${i}]:\n${text}\n[END CONTEXT ${i}]\n\n`;
|
||||||
|
})
|
||||||
|
.join("")}`,
|
||||||
|
};
|
||||||
|
const memory = [prompt, { role: "user", content: input }];
|
||||||
|
const responseText = await LLMConnector.getChatCompletion(memory, {
|
||||||
temperature: workspace?.openAiTemp ?? 0.7,
|
temperature: workspace?.openAiTemp ?? 0.7,
|
||||||
});
|
});
|
||||||
|
|
||||||
const chain = VectorDBQAChain.fromLLM(model, vectorStore, {
|
// When we roll out own response we have separate metadata and texts,
|
||||||
k: 5,
|
// so for source collection we need to combine them.
|
||||||
returnSourceDocuments: true,
|
const sources = sourceDocuments.map((metadata, i) => {
|
||||||
|
return { metadata: { ...metadata, text: contextTexts[i] } };
|
||||||
});
|
});
|
||||||
const response = await chain.call({ query: input });
|
|
||||||
return {
|
return {
|
||||||
response: response.text,
|
response: responseText,
|
||||||
sources: this.curateSources(response.sourceDocuments),
|
sources: this.curateSources(sources),
|
||||||
message: false,
|
message: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
@ -312,8 +302,8 @@ const Chroma = {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const openAiConnector = new OpenAi();
|
const LLMConnector = getLLMProvider();
|
||||||
const queryVector = await openAiConnector.embedTextInput(input);
|
const queryVector = await LLMConnector.embedTextInput(input);
|
||||||
const { contextTexts, sourceDocuments } = await this.similarityResponse(
|
const { contextTexts, sourceDocuments } = await this.similarityResponse(
|
||||||
client,
|
client,
|
||||||
namespace,
|
namespace,
|
||||||
@ -330,7 +320,7 @@ const Chroma = {
|
|||||||
.join("")}`,
|
.join("")}`,
|
||||||
};
|
};
|
||||||
const memory = [prompt, ...chatHistory, { role: "user", content: input }];
|
const memory = [prompt, ...chatHistory, { role: "user", content: input }];
|
||||||
const responseText = await openAiConnector.getChatCompletion(memory, {
|
const responseText = await LLMConnector.getChatCompletion(memory, {
|
||||||
temperature: workspace?.openAiTemp ?? 0.7,
|
temperature: workspace?.openAiTemp ?? 0.7,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
const lancedb = require("vectordb");
|
const lancedb = require("vectordb");
|
||||||
const { toChunks } = require("../../helpers");
|
const { toChunks, getLLMProvider } = require("../../helpers");
|
||||||
const { OpenAIEmbeddings } = require("langchain/embeddings/openai");
|
const { OpenAIEmbeddings } = require("langchain/embeddings/openai");
|
||||||
const { RecursiveCharacterTextSplitter } = require("langchain/text_splitter");
|
const { RecursiveCharacterTextSplitter } = require("langchain/text_splitter");
|
||||||
const { storeVectorResult, cachedVectorInformation } = require("../../files");
|
const { storeVectorResult, cachedVectorInformation } = require("../../files");
|
||||||
const { v4: uuidv4 } = require("uuid");
|
const { v4: uuidv4 } = require("uuid");
|
||||||
const { chatPrompt } = require("../../chats");
|
const { chatPrompt } = require("../../chats");
|
||||||
const { OpenAi } = require("../../AiProviders/openAi");
|
|
||||||
|
|
||||||
const LanceDb = {
|
const LanceDb = {
|
||||||
uri: `${
|
uri: `${
|
||||||
@ -169,11 +168,11 @@ const LanceDb = {
|
|||||||
const textChunks = await textSplitter.splitText(pageContent);
|
const textChunks = await textSplitter.splitText(pageContent);
|
||||||
|
|
||||||
console.log("Chunks created from document:", textChunks.length);
|
console.log("Chunks created from document:", textChunks.length);
|
||||||
const openAiConnector = new OpenAi();
|
const LLMConnector = getLLMProvider();
|
||||||
const documentVectors = [];
|
const documentVectors = [];
|
||||||
const vectors = [];
|
const vectors = [];
|
||||||
const submissions = [];
|
const submissions = [];
|
||||||
const vectorValues = await openAiConnector.embedChunks(textChunks);
|
const vectorValues = await LLMConnector.embedChunks(textChunks);
|
||||||
|
|
||||||
if (!!vectorValues && vectorValues.length > 0) {
|
if (!!vectorValues && vectorValues.length > 0) {
|
||||||
for (const [i, vector] of vectorValues.entries()) {
|
for (const [i, vector] of vectorValues.entries()) {
|
||||||
@ -230,9 +229,8 @@ const LanceDb = {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// LanceDB does not have langchainJS support so we roll our own here.
|
const LLMConnector = getLLMProvider();
|
||||||
const openAiConnector = new OpenAi();
|
const queryVector = await LLMConnector.embedTextInput(input);
|
||||||
const queryVector = await openAiConnector.embedTextInput(input);
|
|
||||||
const { contextTexts, sourceDocuments } = await this.similarityResponse(
|
const { contextTexts, sourceDocuments } = await this.similarityResponse(
|
||||||
client,
|
client,
|
||||||
namespace,
|
namespace,
|
||||||
@ -249,7 +247,7 @@ const LanceDb = {
|
|||||||
.join("")}`,
|
.join("")}`,
|
||||||
};
|
};
|
||||||
const memory = [prompt, { role: "user", content: input }];
|
const memory = [prompt, { role: "user", content: input }];
|
||||||
const responseText = await openAiConnector.getChatCompletion(memory, {
|
const responseText = await LLMConnector.getChatCompletion(memory, {
|
||||||
temperature: workspace?.openAiTemp ?? 0.7,
|
temperature: workspace?.openAiTemp ?? 0.7,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -281,8 +279,8 @@ const LanceDb = {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const openAiConnector = new OpenAi();
|
const LLMConnector = getLLMProvider();
|
||||||
const queryVector = await openAiConnector.embedTextInput(input);
|
const queryVector = await LLMConnector.embedTextInput(input);
|
||||||
const { contextTexts, sourceDocuments } = await this.similarityResponse(
|
const { contextTexts, sourceDocuments } = await this.similarityResponse(
|
||||||
client,
|
client,
|
||||||
namespace,
|
namespace,
|
||||||
@ -299,7 +297,7 @@ const LanceDb = {
|
|||||||
.join("")}`,
|
.join("")}`,
|
||||||
};
|
};
|
||||||
const memory = [prompt, ...chatHistory, { role: "user", content: input }];
|
const memory = [prompt, ...chatHistory, { role: "user", content: input }];
|
||||||
const responseText = await openAiConnector.getChatCompletion(memory, {
|
const responseText = await LLMConnector.getChatCompletion(memory, {
|
||||||
temperature: workspace?.openAiTemp ?? 0.7,
|
temperature: workspace?.openAiTemp ?? 0.7,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,14 +1,9 @@
|
|||||||
const { PineconeClient } = require("@pinecone-database/pinecone");
|
const { PineconeClient } = require("@pinecone-database/pinecone");
|
||||||
const { PineconeStore } = require("langchain/vectorstores/pinecone");
|
|
||||||
const { OpenAI } = require("langchain/llms/openai");
|
|
||||||
const { VectorDBQAChain } = require("langchain/chains");
|
|
||||||
const { OpenAIEmbeddings } = require("langchain/embeddings/openai");
|
|
||||||
const { RecursiveCharacterTextSplitter } = require("langchain/text_splitter");
|
const { RecursiveCharacterTextSplitter } = require("langchain/text_splitter");
|
||||||
const { storeVectorResult, cachedVectorInformation } = require("../../files");
|
const { storeVectorResult, cachedVectorInformation } = require("../../files");
|
||||||
const { v4: uuidv4 } = require("uuid");
|
const { v4: uuidv4 } = require("uuid");
|
||||||
const { toChunks } = require("../../helpers");
|
const { toChunks, getLLMProvider } = require("../../helpers");
|
||||||
const { chatPrompt } = require("../../chats");
|
const { chatPrompt } = require("../../chats");
|
||||||
const { OpenAi } = require("../../AiProviders/openAi");
|
|
||||||
|
|
||||||
const Pinecone = {
|
const Pinecone = {
|
||||||
name: "Pinecone",
|
name: "Pinecone",
|
||||||
@ -29,17 +24,6 @@ const Pinecone = {
|
|||||||
if (!status.ready) throw new Error("Pinecode::Index not ready.");
|
if (!status.ready) throw new Error("Pinecode::Index not ready.");
|
||||||
return { client, pineconeIndex, indexName: process.env.PINECONE_INDEX };
|
return { client, pineconeIndex, indexName: process.env.PINECONE_INDEX };
|
||||||
},
|
},
|
||||||
embedder: function () {
|
|
||||||
return new OpenAIEmbeddings({ openAIApiKey: process.env.OPEN_AI_KEY });
|
|
||||||
},
|
|
||||||
llm: function ({ temperature = 0.7 }) {
|
|
||||||
const model = process.env.OPEN_MODEL_PREF || "gpt-3.5-turbo";
|
|
||||||
return new OpenAI({
|
|
||||||
openAIApiKey: process.env.OPEN_AI_KEY,
|
|
||||||
modelName: model,
|
|
||||||
temperature,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
totalIndicies: async function () {
|
totalIndicies: async function () {
|
||||||
const { pineconeIndex } = await this.connect();
|
const { pineconeIndex } = await this.connect();
|
||||||
const { namespaces } = await pineconeIndex.describeIndexStats1();
|
const { namespaces } = await pineconeIndex.describeIndexStats1();
|
||||||
@ -144,10 +128,10 @@ const Pinecone = {
|
|||||||
const textChunks = await textSplitter.splitText(pageContent);
|
const textChunks = await textSplitter.splitText(pageContent);
|
||||||
|
|
||||||
console.log("Chunks created from document:", textChunks.length);
|
console.log("Chunks created from document:", textChunks.length);
|
||||||
const openAiConnector = new OpenAi();
|
const LLMConnector = getLLMProvider();
|
||||||
const documentVectors = [];
|
const documentVectors = [];
|
||||||
const vectors = [];
|
const vectors = [];
|
||||||
const vectorValues = await openAiConnector.embedChunks(textChunks);
|
const vectorValues = await LLMConnector.embedChunks(textChunks);
|
||||||
|
|
||||||
if (!!vectorValues && vectorValues.length > 0) {
|
if (!!vectorValues && vectorValues.length > 0) {
|
||||||
for (const [i, vector] of vectorValues.entries()) {
|
for (const [i, vector] of vectorValues.entries()) {
|
||||||
@ -246,22 +230,32 @@ const Pinecone = {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const vectorStore = await PineconeStore.fromExistingIndex(this.embedder(), {
|
const LLMConnector = getLLMProvider();
|
||||||
|
const queryVector = await LLMConnector.embedTextInput(input);
|
||||||
|
const { contextTexts, sourceDocuments } = await this.similarityResponse(
|
||||||
pineconeIndex,
|
pineconeIndex,
|
||||||
namespace,
|
namespace,
|
||||||
});
|
queryVector
|
||||||
|
);
|
||||||
|
const prompt = {
|
||||||
|
role: "system",
|
||||||
|
content: `${chatPrompt(workspace)}
|
||||||
|
Context:
|
||||||
|
${contextTexts
|
||||||
|
.map((text, i) => {
|
||||||
|
return `[CONTEXT ${i}]:\n${text}\n[END CONTEXT ${i}]\n\n`;
|
||||||
|
})
|
||||||
|
.join("")}`,
|
||||||
|
};
|
||||||
|
|
||||||
const model = this.llm({
|
const memory = [prompt, { role: "user", content: input }];
|
||||||
|
const responseText = await LLMConnector.getChatCompletion(memory, {
|
||||||
temperature: workspace?.openAiTemp ?? 0.7,
|
temperature: workspace?.openAiTemp ?? 0.7,
|
||||||
});
|
});
|
||||||
const chain = VectorDBQAChain.fromLLM(model, vectorStore, {
|
|
||||||
k: 5,
|
|
||||||
returnSourceDocuments: true,
|
|
||||||
});
|
|
||||||
const response = await chain.call({ query: input });
|
|
||||||
return {
|
return {
|
||||||
response: response.text,
|
response: responseText,
|
||||||
sources: this.curateSources(response.sourceDocuments),
|
sources: this.curateSources(sourceDocuments),
|
||||||
message: false,
|
message: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
@ -284,8 +278,8 @@ const Pinecone = {
|
|||||||
"Invalid namespace - has it been collected and seeded yet?"
|
"Invalid namespace - has it been collected and seeded yet?"
|
||||||
);
|
);
|
||||||
|
|
||||||
const openAiConnector = new OpenAi();
|
const LLMConnector = getLLMProvider();
|
||||||
const queryVector = await openAiConnector.embedTextInput(input);
|
const queryVector = await LLMConnector.embedTextInput(input);
|
||||||
const { contextTexts, sourceDocuments } = await this.similarityResponse(
|
const { contextTexts, sourceDocuments } = await this.similarityResponse(
|
||||||
pineconeIndex,
|
pineconeIndex,
|
||||||
namespace,
|
namespace,
|
||||||
@ -303,7 +297,7 @@ const Pinecone = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const memory = [prompt, ...chatHistory, { role: "user", content: input }];
|
const memory = [prompt, ...chatHistory, { role: "user", content: input }];
|
||||||
const responseText = await openAiConnector.getChatCompletion(memory, {
|
const responseText = await LLMConnector.getChatCompletion(memory, {
|
||||||
temperature: workspace?.openAiTemp ?? 0.7,
|
temperature: workspace?.openAiTemp ?? 0.7,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
106
server/yarn.lock
106
server/yarn.lock
@ -26,6 +26,93 @@
|
|||||||
pad-left "^2.1.0"
|
pad-left "^2.1.0"
|
||||||
tslib "^2.5.0"
|
tslib "^2.5.0"
|
||||||
|
|
||||||
|
"@azure-rest/core-client@^1.1.3":
|
||||||
|
version "1.1.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/@azure-rest/core-client/-/core-client-1.1.4.tgz#628381c3653f6dbae584ca6f2ae5f74a5c015526"
|
||||||
|
integrity sha512-RUIQOA8T0WcbNlddr8hjl2MuC5GVRqmMwPXqBVsgvdKesLy+eg3y/6nf3qe2fvcJMI1gF6VtgU5U4hRaR4w4ag==
|
||||||
|
dependencies:
|
||||||
|
"@azure/abort-controller" "^1.1.0"
|
||||||
|
"@azure/core-auth" "^1.3.0"
|
||||||
|
"@azure/core-rest-pipeline" "^1.5.0"
|
||||||
|
"@azure/core-tracing" "^1.0.1"
|
||||||
|
"@azure/core-util" "^1.0.0"
|
||||||
|
tslib "^2.2.0"
|
||||||
|
|
||||||
|
"@azure/abort-controller@^1.0.0", "@azure/abort-controller@^1.1.0":
|
||||||
|
version "1.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@azure/abort-controller/-/abort-controller-1.1.0.tgz#788ee78457a55af8a1ad342acb182383d2119249"
|
||||||
|
integrity sha512-TrRLIoSQVzfAJX9H1JeFjzAoDGcoK1IYX1UImfceTZpsyYfWr09Ss1aHW1y5TrrR3iq6RZLBwJ3E24uwPhwahw==
|
||||||
|
dependencies:
|
||||||
|
tslib "^2.2.0"
|
||||||
|
|
||||||
|
"@azure/core-auth@^1.3.0", "@azure/core-auth@^1.4.0":
|
||||||
|
version "1.5.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@azure/core-auth/-/core-auth-1.5.0.tgz#a41848c5c31cb3b7c84c409885267d55a2c92e44"
|
||||||
|
integrity sha512-udzoBuYG1VBoHVohDTrvKjyzel34zt77Bhp7dQntVGGD0ehVq48owENbBG8fIgkHRNUBQH5k1r0hpoMu5L8+kw==
|
||||||
|
dependencies:
|
||||||
|
"@azure/abort-controller" "^1.0.0"
|
||||||
|
"@azure/core-util" "^1.1.0"
|
||||||
|
tslib "^2.2.0"
|
||||||
|
|
||||||
|
"@azure/core-lro@^2.5.3":
|
||||||
|
version "2.5.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/@azure/core-lro/-/core-lro-2.5.4.tgz#b21e2bcb8bd9a8a652ff85b61adeea51a8055f90"
|
||||||
|
integrity sha512-3GJiMVH7/10bulzOKGrrLeG/uCBH/9VtxqaMcB9lIqAeamI/xYQSHJL/KcsLDuH+yTjYpro/u6D/MuRe4dN70Q==
|
||||||
|
dependencies:
|
||||||
|
"@azure/abort-controller" "^1.0.0"
|
||||||
|
"@azure/core-util" "^1.2.0"
|
||||||
|
"@azure/logger" "^1.0.0"
|
||||||
|
tslib "^2.2.0"
|
||||||
|
|
||||||
|
"@azure/core-rest-pipeline@^1.10.2", "@azure/core-rest-pipeline@^1.5.0":
|
||||||
|
version "1.12.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@azure/core-rest-pipeline/-/core-rest-pipeline-1.12.0.tgz#a36dd361807494845522824532c076daa27c2786"
|
||||||
|
integrity sha512-+MnSB0vGZjszSzr5AW8z93/9fkDu2RLtWmAN8gskURq7EW2sSwqy8jZa0V26rjuBVkwhdA3Hw8z3VWoeBUOw+A==
|
||||||
|
dependencies:
|
||||||
|
"@azure/abort-controller" "^1.0.0"
|
||||||
|
"@azure/core-auth" "^1.4.0"
|
||||||
|
"@azure/core-tracing" "^1.0.1"
|
||||||
|
"@azure/core-util" "^1.3.0"
|
||||||
|
"@azure/logger" "^1.0.0"
|
||||||
|
form-data "^4.0.0"
|
||||||
|
http-proxy-agent "^5.0.0"
|
||||||
|
https-proxy-agent "^5.0.0"
|
||||||
|
tslib "^2.2.0"
|
||||||
|
|
||||||
|
"@azure/core-tracing@^1.0.1":
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@azure/core-tracing/-/core-tracing-1.0.1.tgz#352a38cbea438c4a83c86b314f48017d70ba9503"
|
||||||
|
integrity sha512-I5CGMoLtX+pI17ZdiFJZgxMJApsK6jjfm85hpgp3oazCdq5Wxgh4wMr7ge/TTWW1B5WBuvIOI1fMU/FrOAMKrw==
|
||||||
|
dependencies:
|
||||||
|
tslib "^2.2.0"
|
||||||
|
|
||||||
|
"@azure/core-util@^1.0.0", "@azure/core-util@^1.1.0", "@azure/core-util@^1.2.0", "@azure/core-util@^1.3.0":
|
||||||
|
version "1.4.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@azure/core-util/-/core-util-1.4.0.tgz#c120a56b3e48a9e4d20619a0b00268ae9de891c7"
|
||||||
|
integrity sha512-eGAyJpm3skVQoLiRqm/xPa+SXi/NPDdSHMxbRAz2lSprd+Zs+qrpQGQQ2VQ3Nttu+nSZR4XoYQC71LbEI7jsig==
|
||||||
|
dependencies:
|
||||||
|
"@azure/abort-controller" "^1.0.0"
|
||||||
|
tslib "^2.2.0"
|
||||||
|
|
||||||
|
"@azure/logger@^1.0.0", "@azure/logger@^1.0.3":
|
||||||
|
version "1.0.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/@azure/logger/-/logger-1.0.4.tgz#28bc6d0e5b3c38ef29296b32d35da4e483593fa1"
|
||||||
|
integrity sha512-ustrPY8MryhloQj7OWGe+HrYx+aoiOxzbXTtgblbV3xwCqpzUK36phH3XNHQKj3EPonyFUuDTfR3qFhTEAuZEg==
|
||||||
|
dependencies:
|
||||||
|
tslib "^2.2.0"
|
||||||
|
|
||||||
|
"@azure/openai@^1.0.0-beta.3":
|
||||||
|
version "1.0.0-beta.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@azure/openai/-/openai-1.0.0-beta.3.tgz#bf4f5ec0a5644b3a9ce4372620856a65e7721e24"
|
||||||
|
integrity sha512-gW4odbuy/X/W34SdvXomj/JzR09MyMHCY5Kd2ZxJkQo3IUGqJXz1rEv6QER7IAGgBFgNawE97K6UuJfMmoT0rw==
|
||||||
|
dependencies:
|
||||||
|
"@azure-rest/core-client" "^1.1.3"
|
||||||
|
"@azure/core-auth" "^1.4.0"
|
||||||
|
"@azure/core-lro" "^2.5.3"
|
||||||
|
"@azure/core-rest-pipeline" "^1.10.2"
|
||||||
|
"@azure/logger" "^1.0.3"
|
||||||
|
tslib "^2.4.0"
|
||||||
|
|
||||||
"@fortaine/fetch-event-source@^3.0.6":
|
"@fortaine/fetch-event-source@^3.0.6":
|
||||||
version "3.0.6"
|
version "3.0.6"
|
||||||
resolved "https://registry.yarnpkg.com/@fortaine/fetch-event-source/-/fetch-event-source-3.0.6.tgz#b8552a2ca2c5202f5699b93a92be0188d422b06e"
|
resolved "https://registry.yarnpkg.com/@fortaine/fetch-event-source/-/fetch-event-source-3.0.6.tgz#b8552a2ca2c5202f5699b93a92be0188d422b06e"
|
||||||
@ -86,6 +173,11 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82"
|
resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82"
|
||||||
integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==
|
integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==
|
||||||
|
|
||||||
|
"@tootallnate/once@2":
|
||||||
|
version "2.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf"
|
||||||
|
integrity sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==
|
||||||
|
|
||||||
"@types/command-line-args@5.2.0":
|
"@types/command-line-args@5.2.0":
|
||||||
version "5.2.0"
|
version "5.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/@types/command-line-args/-/command-line-args-5.2.0.tgz#adbb77980a1cc376bb208e3f4142e907410430f6"
|
resolved "https://registry.yarnpkg.com/@types/command-line-args/-/command-line-args-5.2.0.tgz#adbb77980a1cc376bb208e3f4142e907410430f6"
|
||||||
@ -1128,6 +1220,15 @@ http-proxy-agent@^4.0.1:
|
|||||||
agent-base "6"
|
agent-base "6"
|
||||||
debug "4"
|
debug "4"
|
||||||
|
|
||||||
|
http-proxy-agent@^5.0.0:
|
||||||
|
version "5.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz#5129800203520d434f142bc78ff3c170800f2b43"
|
||||||
|
integrity sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==
|
||||||
|
dependencies:
|
||||||
|
"@tootallnate/once" "2"
|
||||||
|
agent-base "6"
|
||||||
|
debug "4"
|
||||||
|
|
||||||
https-proxy-agent@^5.0.0:
|
https-proxy-agent@^5.0.0:
|
||||||
version "5.0.1"
|
version "5.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6"
|
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6"
|
||||||
@ -2301,6 +2402,11 @@ tr46@~0.0.3:
|
|||||||
resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a"
|
resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a"
|
||||||
integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==
|
integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==
|
||||||
|
|
||||||
|
tslib@^2.2.0, tslib@^2.4.0:
|
||||||
|
version "2.6.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.1.tgz#fd8c9a0ff42590b25703c0acb3de3d3f4ede0410"
|
||||||
|
integrity sha512-t0hLfiEKfMUoqhG+U1oid7Pva4bbDPHYfJNiB7BiIjRkj1pyC++4N3huJfqY6aRH6VTB0rvtzQwjM4K6qpfOig==
|
||||||
|
|
||||||
tslib@^2.5.0:
|
tslib@^2.5.0:
|
||||||
version "2.6.0"
|
version "2.6.0"
|
||||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.0.tgz#b295854684dbda164e181d259a22cd779dcd7bc3"
|
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.0.tgz#b295854684dbda164e181d259a22cd779dcd7bc3"
|
||||||
|
Loading…
Reference in New Issue
Block a user