diff --git a/ui/litellm-dashboard/public/assets/logos/mongodb.svg b/ui/litellm-dashboard/public/assets/logos/mongodb.svg new file mode 100644 index 00000000000..fb0d3cbdfab --- /dev/null +++ b/ui/litellm-dashboard/public/assets/logos/mongodb.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/ui/litellm-dashboard/src/app/(dashboard)/vector-stores/_components/VectorStoreForm.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/vector-stores/_components/VectorStoreForm.test.tsx index 71e2a7224ae..94aa6cc99a0 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/vector-stores/_components/VectorStoreForm.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/vector-stores/_components/VectorStoreForm.test.tsx @@ -110,6 +110,57 @@ describe("buildVectorStoreLitellmParams", () => { }); }); + it("renames embedding_model to litellm_embedding_model for mongodb", () => { + const params = buildVectorStoreLitellmParams("mongodb", { + mongodb_connection_string: "mongodb+srv://user:pass@cluster0.mongodb.net", + mongodb_database: "sample_mflix", + mongodb_collection: "embedded_movies", + mongodb_embedding_field: "plot_embedding", + mongodb_text_field: "plot", + mongodb_num_candidates: "200", + embedding_model: "text-embedding-ada-002", + }); + + expect(params).toEqual({ + mongodb_connection_string: "mongodb+srv://user:pass@cluster0.mongodb.net", + mongodb_database: "sample_mflix", + mongodb_collection: "embedded_movies", + mongodb_embedding_field: "plot_embedding", + mongodb_text_field: "plot", + mongodb_num_candidates: "200", + litellm_embedding_model: "text-embedding-ada-002", + }); + }); + + it("sends only mongodb fields when an earlier provider left values in the form", () => { + const params = buildVectorStoreLitellmParams("mongodb", { + mongodb_connection_string: "mongodb+srv://user:pass@cluster0.mongodb.net", + mongodb_database: "sample_mflix", + mongodb_collection: "embedded_movies", + embedding_model: "text-embedding-ada-002", + valkey_host: "left-over-from-valkey.example.com", + valkey_port: "6379", + aws_region_name: "us-west-2", + }); + + expect(params).not.toHaveProperty("valkey_host"); + expect(params).not.toHaveProperty("valkey_port"); + expect(params).not.toHaveProperty("aws_region_name"); + expect(params.mongodb_connection_string).toBe("mongodb+srv://user:pass@cluster0.mongodb.net"); + }); + + it("omits a blank mongodb_num_candidates so litellm picks its own candidate count", () => { + const params = buildVectorStoreLitellmParams("mongodb", { + mongodb_connection_string: "mongodb+srv://user:pass@cluster0.mongodb.net", + mongodb_database: "sample_mflix", + mongodb_collection: "embedded_movies", + embedding_model: "text-embedding-ada-002", + }); + + expect(params.mongodb_num_candidates).toBeUndefined(); + expect(JSON.parse(JSON.stringify(params))).not.toHaveProperty("mongodb_num_candidates"); + }); + it("keeps embedding_model as-is for providers outside the rename set", () => { const params = buildVectorStoreLitellmParams("s3_vectors", { vector_bucket_name: "my-vector-bucket", diff --git a/ui/litellm-dashboard/src/app/(dashboard)/vector-stores/_components/VectorStoreForm.tsx b/ui/litellm-dashboard/src/app/(dashboard)/vector-stores/_components/VectorStoreForm.tsx index 9d78b727768..e25dbe30005 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/vector-stores/_components/VectorStoreForm.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/vector-stores/_components/VectorStoreForm.tsx @@ -34,7 +34,7 @@ import { Textarea } from "@/components/ui/textarea"; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"; import { useZodForm } from "@/lib/forms/useZodForm"; -const EMBEDDING_MODEL_RENAME_PROVIDERS = new Set(["milvus", "valkey"]); +const EMBEDDING_MODEL_RENAME_PROVIDERS = new Set(["milvus", "valkey", "mongodb"]); export const buildVectorStoreLitellmParams = ( provider: string, @@ -70,6 +70,12 @@ const PROVIDER_FIELD_NAMES = [ "vector_bucket_name", "index_name", "aws_region_name", + "mongodb_connection_string", + "mongodb_database", + "mongodb_collection", + "mongodb_embedding_field", + "mongodb_text_field", + "mongodb_num_candidates", "valkey_host", "valkey_port", "valkey_password", @@ -101,6 +107,12 @@ const vectorStoreShape = { vector_bucket_name: optionalText, index_name: optionalText, aws_region_name: optionalText, + mongodb_connection_string: optionalText, + mongodb_database: optionalText, + mongodb_collection: optionalText, + mongodb_embedding_field: optionalText, + mongodb_text_field: optionalText, + mongodb_num_candidates: optionalText, valkey_host: optionalText, valkey_port: optionalText, valkey_password: optionalText, @@ -130,6 +142,8 @@ const EMPTY_VALUES: VectorStoreFormValues = { custom_llm_provider: "bedrock", vector_store_id: "", vertex_location: "global", + mongodb_embedding_field: "embedding", + mongodb_text_field: "text", valkey_port: "6379", valkey_ssl: "false", valkey_text_field: "text", @@ -262,7 +276,9 @@ const VectorStoreForm: React.FC = ({ : 'my-datastore_1234567890 (data store ID from Vertex AI / "Agent Search" console)' : selectedProvider === "valkey" ? "my-search-index (FT index name in Valkey)" - : "Enter vector store ID from your provider"; + : selectedProvider === "mongodb" + ? "my-vector-index (Atlas Vector Search index name)" + : "Enter vector store ID from your provider"; return ( !open && handleCancel()}> diff --git a/ui/litellm-dashboard/src/components/vector_store_providers.test.tsx b/ui/litellm-dashboard/src/components/vector_store_providers.test.tsx index eaf2a52853f..8e3a3aa3402 100644 --- a/ui/litellm-dashboard/src/components/vector_store_providers.test.tsx +++ b/ui/litellm-dashboard/src/components/vector_store_providers.test.tsx @@ -28,6 +28,47 @@ describe("getVectorStoreProviderLogoAndName", () => { }); }); + it("registers mongodb in the provider, logo, and field maps", () => { + expect(getVectorStoreProviderLogoAndName("mongodb")).toEqual({ + logo: expect.stringContaining("mongodb"), + displayName: VectorStoreProviders.MongoDB, + }); + expect(vectorStoreProviderMap.MongoDB).toBe("mongodb"); + expect(getProviderSpecificFields("mongodb").map((field) => field.name)).toEqual([ + "mongodb_connection_string", + "mongodb_database", + "mongodb_collection", + "embedding_model", + "mongodb_embedding_field", + "mongodb_text_field", + "mongodb_num_candidates", + ]); + }); + + it("hides the mongodb connection string, which carries the database password", () => { + const connectionString = getProviderSpecificFields("mongodb").find( + (field) => field.name === "mongodb_connection_string", + ); + + expect(connectionString).toMatchObject({ type: "password", required: true }); + }); + + it("picks the mongodb embedding model from the proxy's models rather than a fixed list", () => { + const embeddingField = getProviderSpecificFields("mongodb").find((field) => field.name === "embedding_model"); + + expect(embeddingField).toMatchObject({ type: "select", required: true }); + expect(embeddingField).not.toHaveProperty("options"); + }); + + it("defaults the mongodb field names so a standard collection needs no extra input", () => { + const fields = getProviderSpecificFields("mongodb"); + const byName = (name: string) => fields.find((field) => field.name === name); + + expect(byName("mongodb_embedding_field")).toMatchObject({ required: false, initialValue: "embedding" }); + expect(byName("mongodb_text_field")).toMatchObject({ required: false, initialValue: "text" }); + expect(byName("mongodb_num_candidates")).toMatchObject({ required: false }); + }); + it("registers valkey in the provider, logo, and field maps", () => { expect(vectorStoreProviderMap.Valkey).toBe("valkey"); expect(vectorStoreProviderLogoMap[VectorStoreProviders.Valkey]).toContain("valkey"); diff --git a/ui/litellm-dashboard/src/components/vector_store_providers.tsx b/ui/litellm-dashboard/src/components/vector_store_providers.tsx index 35cd5c383f7..a75f10771a8 100644 --- a/ui/litellm-dashboard/src/components/vector_store_providers.tsx +++ b/ui/litellm-dashboard/src/components/vector_store_providers.tsx @@ -1,5 +1,6 @@ import { getProviderLogoAndName, Providers, providerLogoMap } from "@/components/provider_info_helpers"; import milvusLogo from "../../public/assets/logos/milvus.svg"; +import mongodbLogo from "../../public/assets/logos/mongodb.svg"; import postgresqlLogo from "../../public/assets/logos/postgresql.svg"; import s3VectorLogo from "../../public/assets/logos/s3_vector.png"; import valkeyLogo from "../../public/assets/logos/valkey.svg"; @@ -13,6 +14,7 @@ export enum VectorStoreProviders { OpenAI = "OpenAI", Azure = "Azure OpenAI", Milvus = "Milvus", + MongoDB = "MongoDB Atlas", Valkey = "Valkey", } @@ -24,6 +26,7 @@ export const vectorStoreProviderMap: Record = { OpenAI: "openai", Azure: "azure", Milvus: "milvus", + MongoDB: "mongodb", S3Vectors: "s3_vectors", Valkey: "valkey", }; @@ -36,6 +39,7 @@ export const vectorStoreProviderLogoMap: Record = { [VectorStoreProviders.OpenAI]: providerLogoMap[Providers.OpenAI] ?? "", [VectorStoreProviders.Azure]: providerLogoMap[Providers.Azure] ?? "", [VectorStoreProviders.Milvus]: milvusLogo.src, + [VectorStoreProviders.MongoDB]: mongodbLogo.src, [VectorStoreProviders.S3Vectors]: s3VectorLogo.src, [VectorStoreProviders.Valkey]: valkeyLogo.src, }; @@ -169,6 +173,71 @@ export const vectorStoreProviderFields: Record type: "select", }, ], + mongodb: [ + { + name: "mongodb_connection_string", + label: "Connection String", + tooltip: + "The full MongoDB connection string for your Atlas cluster, including the database user and password. Copy it from Atlas under Connect, Drivers (e.g. mongodb+srv://user:password@cluster.mongodb.net)", + placeholder: "mongodb+srv://user:password@cluster.mongodb.net", + required: true, + type: "password", + }, + { + name: "mongodb_database", + label: "Database", + tooltip: "The Atlas database holding the collection you want to search", + placeholder: "sample_mflix", + required: true, + type: "text", + }, + { + name: "mongodb_collection", + label: "Collection", + tooltip: "The collection your Atlas Vector Search index was built on", + placeholder: "embedded_movies", + required: true, + type: "text", + }, + { + name: "embedding_model", + label: "Embedding Model", + tooltip: + "The embedding model on this proxy that created the vectors already stored in your collection. LiteLLM embeds every search query with it, so it must be the same model. A different model of the same size will not error, it will just return wrong results. Add it under Models first if it is not listed", + placeholder: "text-embedding-3-small", + required: true, + type: "select", + }, + { + name: "mongodb_embedding_field", + label: "Vector Field Name", + tooltip: + "The field in each document that holds its embedding. It must match the path your Atlas Vector Search index was created on (default: embedding)", + placeholder: "embedding", + required: false, + type: "text", + initialValue: "embedding", + }, + { + name: "mongodb_text_field", + label: "Text Field", + tooltip: + "The field in each document that holds its readable text. LiteLLM returns this text in search results, and it accepts a dotted path such as metadata.body (default: text)", + placeholder: "text", + required: false, + type: "text", + initialValue: "text", + }, + { + name: "mongodb_num_candidates", + label: "Candidates Considered", + tooltip: + "How many nearest neighbours Atlas examines before returning the top results. Higher is more accurate and slower. Leave blank to let LiteLLM scale it with the requested result count", + placeholder: "100", + required: false, + type: "text", + }, + ], valkey: [ { name: "valkey_host",