θΏ™ζ˜―indexlocζδΎ›ηš„ζœεŠ‘οΌŒδΈθ¦θΎ“ε…₯任何密码
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ AnythingLLM divides your documents into objects called `workspaces`. A Workspace
- [Fireworks AI (chat models)](https://fireworks.ai/)
- [Perplexity (chat models)](https://www.perplexity.ai/)
- [OpenRouter (chat models)](https://openrouter.ai/)
- [DeepSeek (chat models)](https://deepseek.com/)
- [Mistral](https://mistral.ai/)
- [Groq](https://groq.com/)
- [Cohere](https://cohere.com/)
Expand Down
100 changes: 100 additions & 0 deletions frontend/src/components/LLMSelection/DeepSeekOptions/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import { useState, useEffect } from "react";
import System from "@/models/system";

export default function DeepSeekOptions({ settings }) {
const [inputValue, setInputValue] = useState(settings?.DeepSeekApiKey);
const [deepSeekApiKey, setDeepSeekApiKey] = useState(
settings?.DeepSeekApiKey
);

return (
<div className="flex gap-[36px] mt-1.5">
<div className="flex flex-col w-60">
<label className="text-white text-sm font-semibold block mb-3">
API Key
</label>
<input
type="password"
name="DeepSeekApiKey"
className="bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:outline-primary-button active:outline-primary-button outline-none block w-full p-2.5"
placeholder="DeepSeek API Key"
defaultValue={settings?.DeepSeekApiKey ? "*".repeat(20) : ""}
required={true}
autoComplete="off"
spellCheck={false}
onChange={(e) => setInputValue(e.target.value)}
onBlur={() => setDeepSeekApiKey(inputValue)}
/>
</div>
{!settings?.credentialsOnly && (
<DeepSeekModelSelection settings={settings} apiKey={deepSeekApiKey} />
)}
</div>
);
}

function DeepSeekModelSelection({ apiKey, settings }) {
const [models, setModels] = useState([]);
const [loading, setLoading] = useState(true);

useEffect(() => {
async function findCustomModels() {
if (!apiKey) {
setModels([]);
setLoading(true);
return;
}

setLoading(true);
const { models } = await System.customModels(
"deepseek",
typeof apiKey === "boolean" ? null : apiKey
);
setModels(models || []);
setLoading(false);
}
findCustomModels();
}, [apiKey]);

if (loading) {
return (
<div className="flex flex-col w-60">
<label className="text-white text-sm font-semibold block mb-3">
Chat Model Selection
</label>
<select
name="DeepSeekModelPref"
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}>
-- loading available models --
</option>
</select>
</div>
);
}

return (
<div className="flex flex-col w-60">
<label className="text-white text-sm font-semibold block mb-3">
Chat Model Selection
</label>
<select
name="DeepSeekModelPref"
required={true}
className="bg-zinc-900 border-gray-500 text-white text-sm rounded-lg block w-full p-2.5"
>
{models.map((model) => (
<option
key={model.id}
value={model.id}
selected={settings?.DeepSeekModelPref === model.id}
>
{model.name}
</option>
))}
</select>
</div>
);
}
Binary file added frontend/src/media/llmprovider/deepseek.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 10 additions & 0 deletions frontend/src/pages/GeneralSettings/LLMPreference/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import TextGenWebUILogo from "@/media/llmprovider/text-generation-webui.png";
import CohereLogo from "@/media/llmprovider/cohere.png";
import LiteLLMLogo from "@/media/llmprovider/litellm.png";
import AWSBedrockLogo from "@/media/llmprovider/bedrock.png";
import DeepSeekLogo from "@/media/llmprovider/deepseek.png";

import PreLoader from "@/components/Preloader";
import OpenAiOptions from "@/components/LLMSelection/OpenAiOptions";
Expand All @@ -48,6 +49,7 @@ import KoboldCPPOptions from "@/components/LLMSelection/KoboldCPPOptions";
import TextGenWebUIOptions from "@/components/LLMSelection/TextGenWebUIOptions";
import LiteLLMOptions from "@/components/LLMSelection/LiteLLMOptions";
import AWSBedrockLLMOptions from "@/components/LLMSelection/AwsBedrockLLMOptions";
import DeepSeekOptions from "@/components/LLMSelection/DeepSeekOptions";

import LLMItem from "@/components/LLMSelection/LLMItem";
import { CaretUpDown, MagnifyingGlass, X } from "@phosphor-icons/react";
Expand Down Expand Up @@ -211,6 +213,14 @@ export const AVAILABLE_LLM_PROVIDERS = [
description: "Run LiteLLM's OpenAI compatible proxy for various LLMs.",
requiredConfig: ["LiteLLMBasePath"],
},
{
name: "DeepSeek",
value: "deepseek",
logo: DeepSeekLogo,
options: (settings) => <DeepSeekOptions settings={settings} />,
description: "Run DeepSeek's powerful LLMs.",
requiredConfig: ["DeepSeekApiKey"],
},
{
name: "Generic OpenAI",
value: "generic-openai",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import KoboldCPPLogo from "@/media/llmprovider/koboldcpp.png";
import TextGenWebUILogo from "@/media/llmprovider/text-generation-webui.png";
import LiteLLMLogo from "@/media/llmprovider/litellm.png";
import AWSBedrockLogo from "@/media/llmprovider/bedrock.png";
import DeepSeekLogo from "@/media/llmprovider/deepseek.png";

import CohereLogo from "@/media/llmprovider/cohere.png";
import ZillizLogo from "@/media/vectordbs/zilliz.png";
Expand Down Expand Up @@ -196,6 +197,11 @@ export const LLM_SELECTION_PRIVACY = {
],
logo: AWSBedrockLogo,
},
deepseek: {
name: "DeepSeek",
description: ["Your model and chat contents are visible to DeepSeek"],
logo: DeepSeekLogo,
},
};

export const VECTOR_DB_PRIVACY = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import KoboldCPPLogo from "@/media/llmprovider/koboldcpp.png";
import TextGenWebUILogo from "@/media/llmprovider/text-generation-webui.png";
import LiteLLMLogo from "@/media/llmprovider/litellm.png";
import AWSBedrockLogo from "@/media/llmprovider/bedrock.png";
import DeepSeekLogo from "@/media/llmprovider/deepseek.png";

import CohereLogo from "@/media/llmprovider/cohere.png";
import OpenAiOptions from "@/components/LLMSelection/OpenAiOptions";
Expand All @@ -43,6 +44,7 @@ import KoboldCPPOptions from "@/components/LLMSelection/KoboldCPPOptions";
import TextGenWebUIOptions from "@/components/LLMSelection/TextGenWebUIOptions";
import LiteLLMOptions from "@/components/LLMSelection/LiteLLMOptions";
import AWSBedrockLLMOptions from "@/components/LLMSelection/AwsBedrockLLMOptions";
import DeepSeekOptions from "@/components/LLMSelection/DeepSeekOptions";

import LLMItem from "@/components/LLMSelection/LLMItem";
import System from "@/models/system";
Expand Down Expand Up @@ -186,6 +188,13 @@ const LLMS = [
options: (settings) => <LiteLLMOptions settings={settings} />,
description: "Run LiteLLM's OpenAI compatible proxy for various LLMs.",
},
{
name: "DeepSeek",
value: "deepseek",
logo: DeepSeekLogo,
options: (settings) => <DeepSeekOptions settings={settings} />,
description: "Run DeepSeek's powerful LLMs.",
},
{
name: "Generic OpenAI",
value: "generic-openai",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ const ENABLED_PROVIDERS = [
"generic-openai",
"bedrock",
"fireworksai",
"deepseek",
// TODO: More agent support.
// "cohere", // Has tool calling and will need to build explicit support
// "huggingface" // Can be done but already has issues with no-chat templated. Needs to be tested.
Expand Down
4 changes: 4 additions & 0 deletions server/models/systemSettings.js
Original file line number Diff line number Diff line change
Expand Up @@ -506,6 +506,10 @@ const SystemSettings = {

// VoyageAi API Keys
VoyageAiApiKey: !!process.env.VOYAGEAI_API_KEY,

// DeepSeek API Keys
DeepSeekApiKey: !!process.env.DEEPSEEK_API_KEY,
DeepSeekModelPref: process.env.DEEPSEEK_MODEL_PREF,
};
},

Expand Down
127 changes: 127 additions & 0 deletions server/utils/AiProviders/deepseek/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
const { NativeEmbedder } = require("../../EmbeddingEngines/native");
const {
handleDefaultStreamResponseV2,
} = require("../../helpers/chat/responses");
const { MODEL_MAP } = require("../modelMap");

class DeepSeekLLM {
constructor(embedder = null, modelPreference = null) {
if (!process.env.DEEPSEEK_API_KEY)
throw new Error("No DeepSeek API key was set.");
const { OpenAI: OpenAIApi } = require("openai");

this.openai = new OpenAIApi({
apiKey: process.env.DEEPSEEK_API_KEY,
baseURL: "https://api.deepseek.com/v1",
});
this.model =
modelPreference || process.env.DEEPSEEK_MODEL_PREF || "deepseek-chat";
this.limits = {
history: this.promptWindowLimit() * 0.15,
system: this.promptWindowLimit() * 0.15,
user: this.promptWindowLimit() * 0.7,
};

this.embedder = embedder ?? new NativeEmbedder();
this.defaultTemp = 0.7;
}

#appendContext(contextTexts = []) {
if (!contextTexts || !contextTexts.length) return "";
return (
"\nContext:\n" +
contextTexts
.map((text, i) => {
return `[CONTEXT ${i}]:\n${text}\n[END CONTEXT ${i}]\n\n`;
})
.join("")
);
}

streamingEnabled() {
return "streamGetChatCompletion" in this;
}

static promptWindowLimit(modelName) {
return MODEL_MAP.deepseek[modelName] ?? 8192;
}

promptWindowLimit() {
return MODEL_MAP.deepseek[this.model] ?? 8192;
}

async isValidChatCompletionModel(modelName = "") {
const models = await this.openai.models.list().catch(() => ({ data: [] }));
return models.data.some((model) => model.id === modelName);
}

constructPrompt({
systemPrompt = "",
contextTexts = [],
chatHistory = [],
userPrompt = "",
}) {
const prompt = {
role: "system",
content: `${systemPrompt}${this.#appendContext(contextTexts)}`,
};
return [prompt, ...chatHistory, { role: "user", content: userPrompt }];
}

async getChatCompletion(messages = null, { temperature = 0.7 }) {
if (!(await this.isValidChatCompletionModel(this.model)))
throw new Error(
`DeepSeek chat: ${this.model} is not valid for chat completion!`
);

const result = await this.openai.chat.completions
.create({
model: this.model,
messages,
temperature,
})
.catch((e) => {
throw new Error(e.message);
});

if (!result.hasOwnProperty("choices") || result.choices.length === 0)
return null;
return result.choices[0].message.content;
}

async streamGetChatCompletion(messages = null, { temperature = 0.7 }) {
if (!(await this.isValidChatCompletionModel(this.model)))
throw new Error(
`DeepSeek chat: ${this.model} is not valid for chat completion!`
);

const streamRequest = await this.openai.chat.completions.create({
model: this.model,
stream: true,
messages,
temperature,
});
return streamRequest;
}

handleStream(response, stream, responseProps) {
return handleDefaultStreamResponseV2(response, stream, responseProps);
}

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 = {
DeepSeekLLM,
};
4 changes: 4 additions & 0 deletions server/utils/AiProviders/modelMap.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ const MODEL_MAP = {
"gpt-4": 8_192,
"gpt-4-32k": 32_000,
},
deepseek: {
"deepseek-chat": 128_000,
"deepseek-coder": 128_000,
},
};

module.exports = { MODEL_MAP };
2 changes: 2 additions & 0 deletions server/utils/agents/aibitat/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -783,6 +783,8 @@ ${this.getHistory({ to: route.to })
return new Providers.AWSBedrockProvider({});
case "fireworksai":
return new Providers.FireworksAIProvider({ model: config.model });
case "deepseek":
return new Providers.DeepSeekProvider({ model: config.model });

default:
throw new Error(
Expand Down
8 changes: 8 additions & 0 deletions server/utils/agents/aibitat/providers/ai-provider.js
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,14 @@ class Provider {
apiKey: process.env.TEXT_GEN_WEB_UI_API_KEY ?? "not-used",
...config,
});
case "deepseek":
return new ChatOpenAI({
configuration: {
baseURL: "https://api.deepseek.com/v1",
},
apiKey: process.env.DEEPSEEK_API_KEY ?? null,
...config,
});
default:
throw new Error(`Unsupported provider ${provider} for this task.`);
}
Expand Down
Loading