diff --git a/README.md b/README.md index 7b98c252..ab64822a 100644 --- a/README.md +++ b/README.md @@ -87,7 +87,7 @@ AnythingLLM divides your documents into objects called `workspaces`. A Workspace - [LM Studio (all models)](https://lmstudio.ai) - [LocalAI (all models)](https://localai.io/) - [Together AI (chat models)](https://www.together.ai/) -- [Fireworks AI (chat models)](https://fireworks.ai/) +- [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/) @@ -107,6 +107,8 @@ AnythingLLM divides your documents into objects called `workspaces`. A Workspace - [Microsoft Foundry Local](https://github.com/microsoft/Foundry-Local) - [CometAPI (chat models)](https://api.cometapi.com/) - [Docker Model Runner](https://docs.docker.com/ai/model-runner/) +- [PrivateModeAI (chat models)](https://privatemode.ai/) +- [SambaNova Cloud (chat models)](https://cloud.sambanova.ai/) **Embedder models:** @@ -165,8 +167,8 @@ Mintplex Labs & the community maintain a number of deployment methods, scripts, |----------------------------------------|----|-----|---------------|------------| | [![Deploy on Docker][docker-btn]][docker-deploy] | [![Deploy on AWS][aws-btn]][aws-deploy] | [![Deploy on GCP][gcp-btn]][gcp-deploy] | [![Deploy on DigitalOcean][do-btn]][do-deploy] | [![Deploy on Render.com][render-btn]][render-deploy] | -| Railway | RepoCloud | Elestio | Northflank | -| --- | --- | --- | --- | +| Railway | RepoCloud | Elestio | Northflank | +| --------------------------------------------------- | --------------------------------------------------------- | --------------------------------------------------- | ------------------------------------------------------------ | | [![Deploy on Railway][railway-btn]][railway-deploy] | [![Deploy on RepoCloud][repocloud-btn]][repocloud-deploy] | [![Deploy on Elestio][elestio-btn]][elestio-deploy] | [![Deploy on Northflank][northflank-btn]][northflank-deploy] | [or set up a production AnythingLLM instance without Docker →](./BARE_METAL.md) @@ -224,7 +226,7 @@ You can verify these claims by finding all locations `Telemetry.sendTelemetry` i We take privacy very seriously, and we hope you understand that we want to learn how our tool is used, without using annoying popup surveys, so we can build something worth using. The anonymous data is _never_ shared with third parties, ever. -[View all telemetry events in source code](https://github.com/search?q=repo%3AMintplex-Labs%2Fanything-llm%20.sendTelemetry\(&type=code) +[View all telemetry events in source code](https://github.com/search?q=repo%3AMintplex-Labs%2Fanything-llm%20.sendTelemetry(&type=code) diff --git a/docker/.env.example b/docker/.env.example index e20fa82e..a4952a63 100644 --- a/docker/.env.example +++ b/docker/.env.example @@ -171,6 +171,10 @@ GID='1000' # PRIVATEMODE_LLM_BASE_PATH='http://127.0.0.1:8080' # PRIVATEMODE_LLM_MODEL_PREF='gemma-3-27b' +# LLM_PROVIDER='sambanova' +# SAMBANOVA_LLM_API_KEY='xxx-xxx-xxx' +# SAMBANOVA_LLM_MODEL_PREF='gpt-oss-120b' + ########################################### ######## Embedding API SElECTION ########## ########################################### diff --git a/frontend/src/components/LLMSelection/SambaNovaOptions/index.jsx b/frontend/src/components/LLMSelection/SambaNovaOptions/index.jsx new file mode 100644 index 00000000..76bced02 --- /dev/null +++ b/frontend/src/components/LLMSelection/SambaNovaOptions/index.jsx @@ -0,0 +1,103 @@ +import System from "@/models/system"; +import { useState, useEffect } from "react"; + +export default function SambaNovaOptions({ settings }) { + const [inputValue, setInputValue] = useState(settings?.SambaNovaLLMApiKey); + const [apiKey, setApiKey] = useState(settings?.SambaNovaLLMApiKey); + + return ( +
+
+ + setInputValue(e.target.value)} + onBlur={() => setApiKey(inputValue)} + /> +
+ {!settings?.credentialsOnly && ( + + )} +
+ ); +} + +function SambaNovaModelSelection({ settings, apiKey }) { + const [groupedModels, setGroupedModels] = useState({}); + const [loading, setLoading] = useState(true); + + useEffect(() => { + async function findCustomModels() { + setLoading(true); + const { models } = await System.customModels("sambanova"); + if (models?.length > 0) { + const modelsByOrganization = models.reduce((acc, model) => { + acc[model.organization] = acc[model.organization] || []; + acc[model.organization].push(model); + return acc; + }, {}); + setGroupedModels(modelsByOrganization); + } + + setLoading(false); + } + findCustomModels(); + }, []); + + if (loading || Object.keys(groupedModels).length === 0) { + return ( +
+ + +
+ ); + } + + return ( +
+ + +
+ ); +} diff --git a/frontend/src/components/ProviderPrivacy/constants.js b/frontend/src/components/ProviderPrivacy/constants.js index 1d99fff7..d1bedb25 100644 --- a/frontend/src/components/ProviderPrivacy/constants.js +++ b/frontend/src/components/ProviderPrivacy/constants.js @@ -43,6 +43,7 @@ import FoundryLogo from "@/media/llmprovider/foundry-local.png"; import GiteeAILogo from "@/media/llmprovider/giteeai.png"; import DockerModelRunnerLogo from "@/media/llmprovider/docker-model-runner.png"; import PrivateModeLogo from "@/media/llmprovider/privatemode.png"; +import SambaNovaLogo from "@/media/llmprovider/sambanova.png"; const LLM_PROVIDER_PRIVACY_MAP = { openai: { @@ -238,6 +239,11 @@ const LLM_PROVIDER_PRIVACY_MAP = { policyUrl: "https://docs.privatemode.ai/getting-started/faq#q2", logo: PrivateModeLogo, }, + sambanova: { + name: "SambaNova", + policyUrl: "https://sambanova.ai/privacy-policy", + logo: SambaNovaLogo, + }, }; const VECTOR_DB_PROVIDER_PRIVACY_MAP = { diff --git a/frontend/src/hooks/useGetProvidersModels.js b/frontend/src/hooks/useGetProvidersModels.js index 3b8963fb..b13a1c29 100644 --- a/frontend/src/hooks/useGetProvidersModels.js +++ b/frontend/src/hooks/useGetProvidersModels.js @@ -53,6 +53,7 @@ const groupedProviders = [ "openrouter", "ppio", "docker-model-runner", + "sambanova", ]; export default function useGetProviderModels(provider = null) { const [defaultModels, setDefaultModels] = useState([]); diff --git a/frontend/src/media/llmprovider/sambanova.png b/frontend/src/media/llmprovider/sambanova.png new file mode 100644 index 00000000..46c0fcf6 Binary files /dev/null and b/frontend/src/media/llmprovider/sambanova.png differ diff --git a/frontend/src/pages/GeneralSettings/LLMPreference/index.jsx b/frontend/src/pages/GeneralSettings/LLMPreference/index.jsx index dd4c58ce..21039026 100644 --- a/frontend/src/pages/GeneralSettings/LLMPreference/index.jsx +++ b/frontend/src/pages/GeneralSettings/LLMPreference/index.jsx @@ -39,6 +39,7 @@ import FoundryLogo from "@/media/llmprovider/foundry-local.png"; import GiteeAILogo from "@/media/llmprovider/giteeai.png"; import DockerModelRunnerLogo from "@/media/llmprovider/docker-model-runner.png"; import PrivateModeLogo from "@/media/llmprovider/privatemode.png"; +import SambaNovaLogo from "@/media/llmprovider/sambanova.png"; import PreLoader from "@/components/Preloader"; import OpenAiOptions from "@/components/LLMSelection/OpenAiOptions"; @@ -75,6 +76,7 @@ import FoundryOptions from "@/components/LLMSelection/FoundryOptions"; import GiteeAIOptions from "@/components/LLMSelection/GiteeAIOptions/index.jsx"; import DockerModelRunnerOptions from "@/components/LLMSelection/DockerModelRunnerOptions"; import PrivateModeOptions from "@/components/LLMSelection/PrivateModeOptions"; +import SambaNovaOptions from "@/components/LLMSelection/SambaNovaOptions"; import LLMItem from "@/components/LLMSelection/LLMItem"; import { CaretUpDown, MagnifyingGlass, X } from "@phosphor-icons/react"; @@ -177,6 +179,14 @@ export const AVAILABLE_LLM_PROVIDERS = [ "DockerModelRunnerModelTokenLimit", ], }, + { + name: "SambaNova", + value: "sambanova", + logo: SambaNovaLogo, + options: (settings) => , + description: "Run open source models from SambaNova.", + requiredConfig: ["SambaNovaLLMApiKey"], + }, { name: "Local AI", value: "localai", @@ -193,6 +203,7 @@ export const AVAILABLE_LLM_PROVIDERS = [ description: "Run open source models from Together AI.", requiredConfig: ["TogetherAiApiKey"], }, + { name: "Fireworks AI", value: "fireworksai", diff --git a/frontend/src/pages/OnboardingFlow/Steps/LLMPreference/index.jsx b/frontend/src/pages/OnboardingFlow/Steps/LLMPreference/index.jsx index a9b58e4e..b85a7197 100644 --- a/frontend/src/pages/OnboardingFlow/Steps/LLMPreference/index.jsx +++ b/frontend/src/pages/OnboardingFlow/Steps/LLMPreference/index.jsx @@ -33,6 +33,7 @@ import CometApiLogo from "@/media/llmprovider/cometapi.png"; import GiteeAILogo from "@/media/llmprovider/giteeai.png"; import DockerModelRunnerLogo from "@/media/llmprovider/docker-model-runner.png"; import PrivateModeLogo from "@/media/llmprovider/privatemode.png"; +import SambaNovaLogo from "@/media/llmprovider/sambanova.png"; import OpenAiOptions from "@/components/LLMSelection/OpenAiOptions"; import GenericOpenAiOptions from "@/components/LLMSelection/GenericOpenAiOptions"; @@ -67,6 +68,7 @@ import CometApiLLMOptions from "@/components/LLMSelection/CometApiLLMOptions"; import GiteeAiOptions from "@/components/LLMSelection/GiteeAIOptions"; import DockerModelRunnerOptions from "@/components/LLMSelection/DockerModelRunnerOptions"; import PrivateModeOptions from "@/components/LLMSelection/PrivateModeOptions"; +import SambaNovaOptions from "@/components/LLMSelection/SambaNovaOptions"; import LLMItem from "@/components/LLMSelection/LLMItem"; import System from "@/models/system"; @@ -157,6 +159,13 @@ const LLMS = [ options: (settings) => , description: "Run LLMs locally on your own machine.", }, + { + name: "SambaNova", + value: "sambanova", + logo: SambaNovaLogo, + options: (settings) => , + description: "Run open source models from SambaNova.", + }, { name: "Novita AI", value: "novita", diff --git a/frontend/src/pages/WorkspaceSettings/AgentConfig/AgentLLMSelection/index.jsx b/frontend/src/pages/WorkspaceSettings/AgentConfig/AgentLLMSelection/index.jsx index 0ac04a3c..2804275a 100644 --- a/frontend/src/pages/WorkspaceSettings/AgentConfig/AgentLLMSelection/index.jsx +++ b/frontend/src/pages/WorkspaceSettings/AgentConfig/AgentLLMSelection/index.jsx @@ -39,6 +39,7 @@ const ENABLED_PROVIDERS = [ "cohere", "docker-model-runner", "privatemode", + "sambanova", // TODO: More agent support. // "huggingface" // Can be done but already has issues with no-chat templated. Needs to be tested. ]; diff --git a/locales/README.fa-IR.md b/locales/README.fa-IR.md index 70fbf885..da039f69 100644 --- a/locales/README.fa-IR.md +++ b/locales/README.fa-IR.md @@ -55,6 +55,7 @@ AnythingLLM یک اپلیکیشن کامل است که در آن می‌توانید از LLM‌های تجاری آماده یا LLM‌های متن‌باز محبوب و راه‌حل‌های vectorDB برای ساخت یک ChatGPT خصوصی بدون محدودیت استفاده کنید که می‌توانید آن را به صورت محلی اجرا کنید یا از راه دور میزبانی کنید و با هر سندی که به آن ارائه می‌دهید، هوشمندانه گفتگو کنید. AnythingLLM اسناد شما را به اشیایی به نام `workspaces` تقسیم می‌کند. یک Workspace مانند یک رشته عمل می‌کند، اما با اضافه شدن کانتینرسازی اسناد شما. Workspaceها می‌توانند اسناد را به اشتراک بگذارند، اما با یکدیگر ارتباط برقرار نمی‌کنند تا بتوانید زمینه هر workspace را تمیز نگه دارید. +
@@ -90,7 +91,7 @@ AnythingLLM اسناد شما را به اشیایی به نام `workspaces` ت - [LM Studio (all models)](https://lmstudio.ai) - [LocalAi (all models)](https://localai.io/) - [Together AI (chat models)](https://www.together.ai/) -- [Fireworks AI (chat models)](https://fireworks.ai/) +- [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/) @@ -106,6 +107,8 @@ AnythingLLM اسناد شما را به اشیایی به نام `workspaces` ت - [Novita AI (chat models)](https://novita.ai/model-api/product/llm-api?utm_source=github_anything-llm&utm_medium=github_readme&utm_campaign=link) - [PPIO](https://ppinfra.com?utm_source=github_anything-llm) - [Docker Model Runner](https://docs.docker.com/ai/model-runner/) +- [PrivateModeAI (chat models)](https://privatemode.ai/) +- [SambaNova Cloud (chat models)](https://cloud.sambanova.ai/)
@@ -166,14 +169,15 @@ AnythingLLM اسناد شما را به اشیایی به نام `workspaces` ت
Mintplex Labs و جامعه کاربران، روش‌ها، اسکریپت‌ها و قالب‌های متعددی را برای اجرای AnythingLLM به صورت محلی نگهداری می‌کنند. برای مطالعه نحوه استقرار در محیط مورد نظر خود یا استقرار خودکار، به جدول زیر مراجعه کنید. +
-| Docker | AWS | GCP | Digital Ocean | Render.com | -|----------------------------------------|----|-----|---------------|------------| +| Docker | AWS | GCP | Digital Ocean | Render.com | +| ------------------------------------------------ | --------------------------------------- | --------------------------------------- | ---------------------------------------------- | ---------------------------------------------------- | | [![Deploy on Docker][docker-btn]][docker-deploy] | [![Deploy on AWS][aws-btn]][aws-deploy] | [![Deploy on GCP][gcp-btn]][gcp-deploy] | [![Deploy on DigitalOcean][do-btn]][do-deploy] | [![Deploy on Render.com][render-btn]][render-deploy] | -| Railway | RepoCloud | Elestio | -| --- | --- | --- | +| Railway | RepoCloud | Elestio | +| --------------------------------------------------- | --------------------------------------------------------- | --------------------------------------------------- | | [![Deploy on Railway][railway-btn]][railway-deploy] | [![Deploy on RepoCloud][repocloud-btn]][repocloud-deploy] | [![Deploy on Elestio][elestio-btn]][elestio-deploy] |
@@ -224,7 +228,8 @@ AnythingLLM توسط Mintplex Labs Inc دارای ویژگی تله‌متری می‌توانید این ادعاها را با پیدا کردن تمام مکان‌هایی که `Telemetry.sendTelemetry` فراخوانی می‌شود، تأیید کنید. ارائه‌دهنده تله‌متری [PostHog](https://posthog.com/) است. -[مشاهده همه رویدادهای تله‌متری در کد منبع](https://github.com/search?q=repo%3AMintplex-Labs%2Fanything-llm%20.sendTelemetry\(&type=code) +[مشاهده همه رویدادهای تله‌متری در کد منبع](https://github.com/search?q=repo%3AMintplex-Labs%2Fanything-llm%20.sendTelemetry(&type=code) +
@@ -259,10 +264,12 @@ AnythingLLM توسط Mintplex Labs Inc دارای ویژگی تله‌متری
--- +
Copyright © 2025 [Mintplex Labs][profile-link].
This project is [MIT](../LICENSE) licensed. +
diff --git a/locales/README.ja-JP.md b/locales/README.ja-JP.md index cd1798bd..dbd8c5d3 100644 --- a/locales/README.ja-JP.md +++ b/locales/README.ja-JP.md @@ -95,6 +95,8 @@ AnythingLLMは、ドキュメントを`ワークスペース`と呼ばれるオ - [PPIO](https://ppinfra.com?utm_source=github_anything-llm) - [CometAPI (チャットモデル)](https://api.cometapi.com/) - [Docker Model Runner](https://docs.docker.com/ai/model-runner/) +- [PrivateModeAI (chat models)](https://privatemode.ai/) +- [SambaNova Cloud (chat models)](https://cloud.sambanova.ai/) **埋め込みモデル:** @@ -150,8 +152,8 @@ Mintplex Labsおよびコミュニティは、AnythingLLMをローカルで実 |----------------------------------------|----|-----|---------------|------------| | [![Docker上でデプロイ][docker-btn]][docker-deploy] | [![AWS上でデプロイ][aws-btn]][aws-deploy] | [![GCP上でデプロイ][gcp-btn]][gcp-deploy] | [![DigitalOcean上でデプロイ][do-btn]][do-deploy] | [![Render.com上でデプロイ][render-btn]][render-deploy] | -| Railway | -| --------------------------------------------------- | +| Railway | +| ----------------------------------------------------- | | [![Railway上でデプロイ][railway-btn]][railway-deploy] | [Dockerを使用せずに本番環境のAnythingLLMインスタンスを設定する →](../BARE_METAL.md) @@ -201,7 +203,7 @@ Mintplex Labs Inc.によって開発されたAnythingLLMには、匿名の使用 これらの主張を検証するには、`Telemetry.sendTelemetry`が呼び出されるすべての場所を見つけてください。また、これらのイベントは出力ログに書き込まれるため、送信された具体的なデータも確認できます。IPアドレスやその他の識別情報は収集されません。テレメトリープロバイダーは[PostHog](https://posthog.com/)です。 -[ソースコード内のすべてのテレメトリーイベントを表示](https://github.com/search?q=repo%3AMintplex-Labs%2Fanything-llm%20.sendTelemetry\(&type=code) +[ソースコード内のすべてのテレメトリーイベントを表示](https://github.com/search?q=repo%3AMintplex-Labs%2Fanything-llm%20.sendTelemetry(&type=code) diff --git a/locales/README.tr-TR.md b/locales/README.tr-TR.md index 37f30018..e6c4cb3d 100644 --- a/locales/README.tr-TR.md +++ b/locales/README.tr-TR.md @@ -32,7 +32,6 @@ Belgelerinizle sohbet edin, yapay zeka ajanlarını kullanın, son derece özell English · 简体中文 · 日本語 · Turkish

-

👉 Masaüstü için AnythingLLM (Mac, Windows ve Linux)! Şimdi İndir

@@ -87,7 +86,7 @@ AnythingLLM, belgelerinizi **"çalışma alanları" (workspaces)** adı verilen - [LM Studio (all models)](https://lmstudio.ai) - [LocalAi (all models)](https://localai.io/) - [Together AI (chat models)](https://www.together.ai/) -- [Fireworks AI (chat models)](https://fireworks.ai/) +- [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/) @@ -103,6 +102,8 @@ AnythingLLM, belgelerinizi **"çalışma alanları" (workspaces)** adı verilen - [Novita AI (chat models)](https://novita.ai/model-api/product/llm-api?utm_source=github_anything-llm&utm_medium=github_readme&utm_campaign=link) - [PPIO](https://ppinfra.com?utm_source=github_anything-llm) - [Docker Model Runner](https://docs.docker.com/ai/model-runner/) +- [PrivateModeAI (chat models)](https://privatemode.ai/) +- [SambaNova Cloud (chat models)](https://cloud.sambanova.ai/) **Embedder modelleri:** @@ -161,8 +162,8 @@ Mintplex Labs ve topluluk, AnythingLLM'i yerel olarak çalıştırmak için çe |----------------------------------------|----|-----|---------------|------------| | [![Deploy on Docker][docker-btn]][docker-deploy] | [![Deploy on AWS][aws-btn]][aws-deploy] | [![Deploy on GCP][gcp-btn]][gcp-deploy] | [![Deploy on DigitalOcean][do-btn]][do-deploy] | [![Deploy on Render.com][render-btn]][render-deploy] | -| Railway | RepoCloud | Elestio | -| --- | --- | --- | +| Railway | RepoCloud | Elestio | +| --------------------------------------------------- | --------------------------------------------------------- | --------------------------------------------------- | | [![Deploy on Railway][railway-btn]][railway-deploy] | [![Deploy on RepoCloud][repocloud-btn]][repocloud-deploy] | [![Deploy on Elestio][elestio-btn]][elestio-deploy] | [veya Docker kullanmadan üretim ortamında AnythingLLM kurun →](../BARE_METAL.md) @@ -214,11 +215,10 @@ Yalnızca ürün ve yol haritası kararlarını almamıza yardımcı olacak kull Bu verileri doğrulamak için kod içinde **`Telemetry.sendTelemetry` çağrılarını** inceleyebilirsiniz. Ayrıca, bu olaylar günlük kaydına yazıldığı için hangi verilerin gönderildiğini görebilirsiniz (eğer etkinleştirilmişse). **IP adresi veya diğer tanımlayıcı bilgiler toplanmaz.** Telemetri sağlayıcısı, açık kaynaklı bir telemetri toplama hizmeti olan [PostHog](https://posthog.com/)‘dur. -[Kaynak kodda tüm telemetri olaylarını görüntüle](https://github.com/search?q=repo%3AMintplex-Labs%2Fanything-llm%20.sendTelemetry\(&type=code) +[Kaynak kodda tüm telemetri olaylarını görüntüle](https://github.com/search?q=repo%3AMintplex-Labs%2Fanything-llm%20.sendTelemetry(&type=code) - ## 👋 Katkıda Bulunma - Bir **issue** oluşturun. diff --git a/locales/README.zh-CN.md b/locales/README.zh-CN.md index 4c978a9a..68add19a 100644 --- a/locales/README.zh-CN.md +++ b/locales/README.zh-CN.md @@ -54,6 +54,7 @@ AnythingLLM是一个全栈应用程序,您可以使用现成的商业大语言 AnythingLLM将您的文档划分为称为`workspaces` (工作区)的对象。工作区的功能类似于线程,同时增加了文档的容器化。工作区可以共享文档,但工作区之间的内容不会互相干扰或污染,因此您可以保持每个工作区的上下文清晰。 ## AnythingLLM的一些酷炫特性 + - 🆕 [**完全兼容 MCP**](https://docs.anythingllm.com/mcp-compatibility/overview) - 🆕 [**无代码AI代理构建器**](https://docs.anythingllm.com/agent-flows/overview) - 🖼️ **多用户实例支持和权限管理(支持封闭源和开源LLM!)** @@ -103,6 +104,8 @@ AnythingLLM将您的文档划分为称为`workspaces` (工作区)的对象。工 - [PPIO (聊天模型)](https://ppinfra.com?utm_source=github_anything-llm) - [CometAPI (聊天模型)](https://api.cometapi.com/) - [Docker Model Runner](https://docs.docker.com/ai/model-runner/) +- [PrivateModeAI (chat models)](https://privatemode.ai/) +- [SambaNova Cloud (chat models)](https://cloud.sambanova.ai/) **支持的嵌入模型:** @@ -161,8 +164,8 @@ Mintplex Labs和社区维护了许多部署方法、脚本和模板,您可以 |----------------------------------------|----|-----|---------------|------------| | [![在 Docker 上部署][docker-btn]][docker-deploy] | [![在 AWS 上部署][aws-btn]][aws-deploy] | [![在 GCP 上部署][gcp-btn]][gcp-deploy] | [![在DigitalOcean上部署][do-btn]][do-deploy] | [![在 Render.com 上部署][render-btn]][render-deploy] | -| Railway | RepoCloud | Elestio | -| --- | --- | --- | +| Railway | RepoCloud | Elestio | +| --------------------------------------------------- | --------------------------------------------------------- | --------------------------------------------------- | | [![在 Railway 上部署][railway-btn]][railway-deploy] | [![在 RepoCloud 上部署][repocloud-btn]][repocloud-deploy] | [![在 Elestio 上部署][elestio-btn]][elestio-deploy] | [其他方案:不使用Docker配置AnythingLLM实例 →](../BARE_METAL.md) @@ -194,9 +197,6 @@ _以下是一些与 AnythingLLM 兼容的应用程序,但并非由 Mintplex La
有关 AnythingLLM 的远程信息收集与隐私保护更多信息 - - - ### 为什么收集信息? 我们使用这些信息来帮助我们理解 AnythingLLM 的使用情况,帮助我们确定新功能和错误修复的优先级,并帮助我们提高 AnythingLLM 的性能和稳定性。 @@ -219,7 +219,7 @@ _以下是一些与 AnythingLLM 兼容的应用程序,但并非由 Mintplex La 我们非常重视隐私,且不用烦人的弹窗问卷来获取反馈,希望你能理解为什么我们想要知道该工具的使用情况,这样我们才能打造真正值得使用的产品。所有匿名数据 _绝不会_ 与任何第三方共享。 -[在源代码中查看所有信息收集活动](https://github.com/search?q=repo%3AMintplex-Labs%2Fanything-llm%20.sendTelemetry\(&type=code) +[在源代码中查看所有信息收集活动](https://github.com/search?q=repo%3AMintplex-Labs%2Fanything-llm%20.sendTelemetry(&type=code)
diff --git a/server/.env.example b/server/.env.example index 6523d100..a1957878 100644 --- a/server/.env.example +++ b/server/.env.example @@ -170,6 +170,10 @@ SIG_SALT='salt' # Please generate random string at least 32 chars long. # PRIVATEMODE_LLM_BASE_PATH='http://127.0.0.1:8080' # PRIVATEMODE_LLM_MODEL_PREF='gemma-3-27b' +# LLM_PROVIDER='sambanova' +# SAMBANOVA_LLM_API_KEY='xxx-xxx-xxx' +# SAMBANOVA_LLM_MODEL_PREF='gpt-oss-120b' + ########################################### ######## Embedding API SElECTION ########## ########################################### diff --git a/server/endpoints/utils.js b/server/endpoints/utils.js index 61a61d6a..9580b043 100644 --- a/server/endpoints/utils.js +++ b/server/endpoints/utils.js @@ -165,6 +165,9 @@ function getModelTag() { case "privatemode": model = process.env.PRIVATEMODE_LLM_MODEL_PREF; break; + case "sambanova": + model = process.env.SAMBANOVA_LLM_MODEL_PREF; + break; default: model = "--"; break; diff --git a/server/models/systemSettings.js b/server/models/systemSettings.js index 9b6e02ae..debb4b60 100644 --- a/server/models/systemSettings.js +++ b/server/models/systemSettings.js @@ -681,6 +681,10 @@ const SystemSettings = { // Privatemode Keys PrivateModeBasePath: process.env.PRIVATEMODE_LLM_BASE_PATH, PrivateModeModelPref: process.env.PRIVATEMODE_LLM_MODEL_PREF, + + // SambaNova Keys + SambaNovaLLMApiKey: !!process.env.SAMBANOVA_LLM_API_KEY, + SambaNovaLLMModelPref: process.env.SAMBANOVA_LLM_MODEL_PREF, }; }, diff --git a/server/utils/AiProviders/modelMap/index.js b/server/utils/AiProviders/modelMap/index.js index 5e191379..30ac871e 100644 --- a/server/utils/AiProviders/modelMap/index.js +++ b/server/utils/AiProviders/modelMap/index.js @@ -20,6 +20,7 @@ class ContextWindowFinder { deepseek: "deepseek", moonshot: "moonshot", zai: "vercel_ai_gateway", // Vercel has correct context windows for Z.AI models + sambanova: "sambanova", }; static expiryMs = 1000 * 60 * 60 * 24 * 3; // 3 days static remoteUrl = diff --git a/server/utils/AiProviders/sambanova/index.js b/server/utils/AiProviders/sambanova/index.js new file mode 100644 index 00000000..49ae1614 --- /dev/null +++ b/server/utils/AiProviders/sambanova/index.js @@ -0,0 +1,268 @@ +const { NativeEmbedder } = require("../../EmbeddingEngines/native"); +const { + LLMPerformanceMonitor, +} = require("../../helpers/chat/LLMPerformanceMonitor"); +const { v4: uuidv4 } = require("uuid"); +const { writeResponseChunk } = require("../../helpers/chat/responses"); +const { MODEL_MAP } = require("../modelMap"); + +class SambaNovaLLM { + constructor(embedder = null, modelPreference = null) { + if (!process.env.SAMBANOVA_LLM_API_KEY) + throw new Error("No SambaNova API key was set."); + this.className = "SambaNovaLLM"; + const { OpenAI: OpenAIApi } = require("openai"); + + this.openai = new OpenAIApi({ + baseURL: "https://api.sambanova.ai/v1", + apiKey: process.env.SAMBANOVA_LLM_API_KEY, + }); + this.model = modelPreference || process.env.SAMBANOVA_LLM_MODEL_PREF; + 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; + this.log( + `Initialized ${this.model} with context window ${this.promptWindowLimit()}` + ); + } + + log(text, ...args) { + console.log(`\x1b[36m[${this.className}]\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; + } + + static promptWindowLimit(modelName) { + return MODEL_MAP.get("sambanova", modelName) ?? 131072; + } + + promptWindowLimit() { + return MODEL_MAP.get("sambanova", this.model) ?? 131072; + } + + async isValidChatCompletionModel(modelName = "") { + return !!modelName; // name just needs to exist + } + + /** + * Generates appropriate content array for a message + attachments. + * @param {{userPrompt:string, attachments: import("../../helpers").Attachment[]}} + * @returns {string|object[]} + */ + #generateContent({ userPrompt, attachments = [] }) { + if (!attachments.length) return userPrompt; + + const content = [{ type: "text", text: userPrompt }]; + for (let attachment of attachments) { + content.push({ + type: "image_url", + image_url: { + url: attachment.contentString, + }, + }); + } + return content.flat(); + } + + /** + * Construct the user prompt for this model. + * @param {{attachments: import("../../helpers").Attachment[]}} param0 + * @returns + */ + constructPrompt({ + systemPrompt = "", + contextTexts = [], + chatHistory = [], + userPrompt = "", + attachments = [], + }) { + const prompt = { + role: "system", + content: `${systemPrompt}${this.#appendContext(contextTexts)}`, + }; + return [ + prompt, + ...chatHistory, + { + role: "user", + content: this.#generateContent({ userPrompt, attachments }), + }, + ]; + } + + async getChatCompletion(messages = null, { temperature = 0.7 }) { + const result = await LLMPerformanceMonitor.measureAsyncFunction( + this.openai.chat.completions + .create({ + model: this.model, + messages, + temperature, + }) + .catch((e) => { + throw new Error(e.message); + }) + ); + + if ( + !result.output.hasOwnProperty("choices") || + result.output.choices.length === 0 + ) + return null; + + return { + textResponse: result.output.choices[0].message.content, + metrics: { + prompt_tokens: result.output.usage?.prompt_tokens || 0, + completion_tokens: result.output.usage?.completion_tokens || 0, + total_tokens: result.output.usage?.total_tokens || 0, + outputTps: result.output.usage?.total_tokens_per_sec || 0, + duration: result.duration, + model: this.model, + timestamp: new Date(), + }, + }; + } + + async streamGetChatCompletion(messages = null, { temperature = 0.7 }) { + const measuredStreamRequest = await LLMPerformanceMonitor.measureStream({ + func: this.openai.chat.completions.create({ + model: this.model, + stream: true, + messages, + temperature, + stream_options: { + include_usage: true, + }, + }), + messages, + runPromptTokenCalculation: false, + modelTag: this.model, + }); + + return measuredStreamRequest; + } + + handleStream(response, stream, responseProps) { + const { uuid = uuidv4(), sources = [] } = responseProps; + let hasUsageMetrics = false; + let usage = { + prompt_tokens: 0, + total_tokens: 0, + outputTps: 0, + completion_tokens: 0, + }; + + return new Promise(async (resolve) => { + let fullText = ""; + const handleAbort = () => { + stream?.endMeasurement(usage); + clientAbortedHandler(resolve, fullText); + }; + response.on("close", handleAbort); + + try { + for await (const chunk of stream) { + const message = chunk?.choices?.[0]; + const token = message?.delta?.content; + + if ( + chunk.hasOwnProperty("usage") && // exists + !!chunk.usage && + Object.values(chunk.usage).length > 0 + ) { + if (chunk.usage.hasOwnProperty("prompt_tokens")) + usage.prompt_tokens = Number(chunk.usage.prompt_tokens); + if (chunk.usage.hasOwnProperty("completion_tokens")) + usage.completion_tokens = Number(chunk.usage.completion_tokens); + if (chunk.usage.hasOwnProperty("total_tokens")) + usage.total_tokens = Number(chunk.usage.total_tokens); + if (chunk.usage.hasOwnProperty("total_tokens_per_sec")) + usage.outputTps = Number(chunk.usage.total_tokens_per_sec); + hasUsageMetrics = true; + } + + if (token) { + fullText += token; + if (!hasUsageMetrics) usage.completion_tokens++; + writeResponseChunk(response, { + uuid, + sources: [], + type: "textResponseChunk", + textResponse: token, + close: false, + error: false, + }); + } + + if ( + message?.hasOwnProperty("finish_reason") && + message.finish_reason !== "" && + message.finish_reason !== null + ) { + writeResponseChunk(response, { + uuid, + sources, + type: "textResponseChunk", + textResponse: "", + close: true, + error: false, + }); + } + } + + response.removeListener("close", handleAbort); + stream?.endMeasurement(usage); + resolve(fullText); + } catch (e) { + this.log(`\x1b[43m\x1b[34m[STREAMING ERROR]\x1b[0m ${e.message}`); + writeResponseChunk(response, { + uuid, + type: "abort", + textResponse: null, + sources: [], + close: true, + error: e.message, + }); + stream?.endMeasurement(usage); + 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 = { + SambaNovaLLM, +}; diff --git a/server/utils/agents/aibitat/index.js b/server/utils/agents/aibitat/index.js index 86e1a214..fbcdb866 100644 --- a/server/utils/agents/aibitat/index.js +++ b/server/utils/agents/aibitat/index.js @@ -996,6 +996,8 @@ ${this.getHistory({ to: route.to }) return new Providers.DockerModelRunnerProvider({ model: config.model }); case "privatemode": return new Providers.PrivatemodeProvider({ model: config.model }); + case "sambanova": + return new Providers.SambaNovaProvider({ model: config.model }); default: throw new Error( `Unknown provider: ${config.provider}. Please use a valid provider.` diff --git a/server/utils/agents/aibitat/providers/ai-provider.js b/server/utils/agents/aibitat/providers/ai-provider.js index d1da6bae..7b10e729 100644 --- a/server/utils/agents/aibitat/providers/ai-provider.js +++ b/server/utils/agents/aibitat/providers/ai-provider.js @@ -257,6 +257,14 @@ class Provider { apiKey: null, ...config, }); + case "sambanova": + return new ChatOpenAI({ + configuration: { + baseURL: "https://api.sambanova.ai/v1", + }, + apiKey: process.env.SAMBANOVA_LLM_API_KEY ?? null, + ...config, + }); // OSS Model Runners // case "anythingllm_ollama": // return new ChatOllama({ diff --git a/server/utils/agents/aibitat/providers/index.js b/server/utils/agents/aibitat/providers/index.js index 1862dc66..6c6fbfb0 100644 --- a/server/utils/agents/aibitat/providers/index.js +++ b/server/utils/agents/aibitat/providers/index.js @@ -31,6 +31,7 @@ const GiteeAIProvider = require("./giteeai.js"); const CohereProvider = require("./cohere.js"); const DockerModelRunnerProvider = require("./dockerModelRunner.js"); const PrivatemodeProvider = require("./privatemode.js"); +const SambaNovaProvider = require("./sambanova.js"); module.exports = { OpenAIProvider, @@ -66,4 +67,5 @@ module.exports = { CohereProvider, DockerModelRunnerProvider, PrivatemodeProvider, + SambaNovaProvider, }; diff --git a/server/utils/agents/aibitat/providers/sambanova.js b/server/utils/agents/aibitat/providers/sambanova.js new file mode 100644 index 00000000..75b307d1 --- /dev/null +++ b/server/utils/agents/aibitat/providers/sambanova.js @@ -0,0 +1,85 @@ +const OpenAI = require("openai"); +const Provider = require("./ai-provider.js"); +const InheritMultiple = require("./helpers/classes.js"); +const UnTooled = require("./helpers/untooled.js"); + +class SambaNovaProvider extends InheritMultiple([Provider, UnTooled]) { + model; + + constructor(config = {}) { + const { model = "Meta-Llama-3.1-8B-Instruct" } = config; + super(); + const client = new OpenAI({ + baseURL: "https://api.sambanova.ai/v1", + apiKey: process.env.SAMBANOVA_LLM_API_KEY, + maxRetries: 3, + }); + + this._client = client; + this.model = model; + this.verbose = true; + } + + /** + * Create a completion based on the received messages. + * + * @param messages A list of messages to send to the API. + * @param functions + * @returns The completion. + */ + get client() { + return this._client; + } + + get supportsAgentStreaming() { + return true; + } + + async #handleFunctionCallChat({ messages = [] }) { + return await this.client.chat.completions + .create({ + model: this.model, + messages, + }) + .then((result) => { + if (!result.hasOwnProperty("choices")) + throw new Error("SambaNova chat: No results!"); + if (result.choices.length === 0) + throw new Error("SambaNova chat: No results length!"); + return result.choices[0].message.content; + }); + } + + async #handleFunctionCallStream({ messages = [] }) { + return await this.client.chat.completions.create({ + model: this.model, + stream: true, + messages, + }); + } + + async stream(messages, functions = [], eventHandler = null) { + return await UnTooled.prototype.stream.call( + this, + messages, + functions, + this.#handleFunctionCallStream.bind(this), + eventHandler + ); + } + + async complete(messages, functions = []) { + return await UnTooled.prototype.complete.call( + this, + messages, + functions, + this.#handleFunctionCallChat.bind(this) + ); + } + + getCost(_usage) { + return 0; + } +} + +module.exports = SambaNovaProvider; diff --git a/server/utils/agents/index.js b/server/utils/agents/index.js index 582d5495..8712b2cc 100644 --- a/server/utils/agents/index.js +++ b/server/utils/agents/index.js @@ -229,6 +229,10 @@ class AgentHandler { "Privatemode base path must be provided to use agents." ); break; + case "sambanova": + if (!process.env.SAMBANOVA_LLM_API_KEY) + throw new Error("SambaNova API key must be provided to use agents."); + break; default: throw new Error( "No workspace agent provider set. Please set your agent provider in the workspace's settings" @@ -313,6 +317,8 @@ class AgentHandler { return process.env.DOCKER_MODEL_RUNNER_LLM_MODEL_PREF ?? null; case "privatemode": return process.env.PRIVATEMODE_LLM_MODEL_PREF ?? null; + case "sambanova": + return process.env.SAMBANOVA_LLM_MODEL_PREF ?? null; default: return null; } diff --git a/server/utils/helpers/customModels.js b/server/utils/helpers/customModels.js index 974099af..17fe056e 100644 --- a/server/utils/helpers/customModels.js +++ b/server/utils/helpers/customModels.js @@ -46,6 +46,7 @@ const SUPPORT_CUSTOM_MODELS = [ "giteeai", "docker-model-runner", "privatemode", + "sambanova", // Embedding Engines "native-embedder", "cohere-embedder", @@ -123,6 +124,8 @@ async function getCustomModels(provider = "", apiKey = null, basePath = null) { return await getDockerModelRunnerModels(basePath); case "privatemode": return await getPrivatemodeModels(basePath, "generate"); + case "sambanova": + return await getSambaNovaModels(apiKey); default: return { models: [], error: "Invalid provider for custom models" }; } @@ -932,6 +935,53 @@ async function getPrivatemodeModels(basePath = null, task = "any") { } } +/** + * Get SambaNova models + * @param {string} _apiKey - The API key to use + * @returns {Promise<{models: Array<{id: string, organization: string, name: string}>, error: string | null}>} + */ +async function getSambaNovaModels(_apiKey = null) { + try { + const apiKey = + _apiKey === true + ? process.env.SAMBANOVA_LLM_API_KEY + : _apiKey || process.env.SAMBANOVA_LLM_API_KEY || null; + const { OpenAI: OpenAIApi } = require("openai"); + const openai = new OpenAIApi({ + baseURL: "https://api.sambanova.ai/v1", + apiKey, + }); + const models = await openai.models + .list() + .then((results) => results.data) + .then((models) => + models.filter((model) => !model.id.toLowerCase().startsWith("whisper")) + ) + .then((models) => + models.map((model) => { + const organization = + model.hasOwnProperty("owned_by") && + model.owned_by !== "no-reply@sambanova.ai" + ? model.owned_by + : "SambaNova"; + return { + id: model.id, + organization, + name: model.id, + }; + }) + ) + .catch((e) => { + console.error(`SambaNova:listModels`, e.message); + return []; + }); + return { models, error: null }; + } catch (e) { + console.error(`SambaNova:getSambaNovaModels`, e.message); + return { models: [], error: "Could not fetch SambaNova Models" }; + } +} + module.exports = { getCustomModels, SUPPORT_CUSTOM_MODELS, diff --git a/server/utils/helpers/index.js b/server/utils/helpers/index.js index 38f82306..fba0feea 100644 --- a/server/utils/helpers/index.js +++ b/server/utils/helpers/index.js @@ -237,6 +237,9 @@ function getLLMProvider({ provider = null, model = null } = {}) { case "privatemode": const { PrivatemodeLLM } = require("../AiProviders/privatemode"); return new PrivatemodeLLM(embedder, model); + case "sambanova": + const { SambaNovaLLM } = require("../AiProviders/sambanova"); + return new SambaNovaLLM(embedder, model); default: throw new Error( `ENV: No valid LLM_PROVIDER value found in environment! Using ${process.env.LLM_PROVIDER}` @@ -410,6 +413,9 @@ function getLLMProviderClass({ provider = null } = {}) { case "privatemode": const { PrivateModeLLM } = require("../AiProviders/privatemode"); return PrivateModeLLM; + case "sambanova": + const { SambaNovaLLM } = require("../AiProviders/sambanova"); + return SambaNovaLLM; default: return null; } @@ -490,6 +496,8 @@ function getBaseLLMProviderModel({ provider = null } = {}) { return process.env.DOCKER_MODEL_RUNNER_LLM_MODEL_PREF; case "privatemode": return process.env.PRIVATEMODE_LLM_MODEL_PREF; + case "sambanova": + return process.env.SAMBANOVA_LLM_MODEL_PREF; default: return null; } diff --git a/server/utils/helpers/updateENV.js b/server/utils/helpers/updateENV.js index fdac6018..c6ed6c52 100644 --- a/server/utils/helpers/updateENV.js +++ b/server/utils/helpers/updateENV.js @@ -809,6 +809,16 @@ const KEY_MAPPING = { envKey: "PRIVATEMODE_LLM_MODEL_PREF", checks: [isNotEmpty], }, + + // SambaNova Options + SambaNovaLLMApiKey: { + envKey: "SAMBANOVA_LLM_API_KEY", + checks: [isNotEmpty], + }, + SambaNovaLLMModelPref: { + envKey: "SAMBANOVA_LLM_MODEL_PREF", + checks: [isNotEmpty], + }, }; function isNotEmpty(input = "") { @@ -924,6 +934,7 @@ function supportedLLM(input = "") { "giteeai", "docker-model-runner", "privatemode", + "sambanova", ].includes(input); return validSelection ? null : `${input} is not a valid LLM provider.`; }