From 1f8cbee8aa306c5f169d11b49dbf1fe28010ffd4 Mon Sep 17 00:00:00 2001 From: Yuneng Jiang Date: Wed, 2 Sep 2026 10:22:01 -0700 Subject: [PATCH] feat(ui): add MongoDB Atlas to the vector store provider dropdown The create form now offers MongoDB Atlas with its connection string, database, collection, embedding model, vector field, text field and candidate count. The connection string renders as a password input because it carries the database user's password, and the embedding model is picked from the proxy's own models, matching how Milvus and Valkey do it. The vector store id doubles as the Atlas Vector Search index name, so the placeholder says so. --- .../public/assets/logos/mongodb.svg | 6 ++ .../_components/VectorStoreForm.test.tsx | 51 ++++++++++++++ .../_components/VectorStoreForm.tsx | 20 +++++- .../vector_store_providers.test.tsx | 41 +++++++++++ .../src/components/vector_store_providers.tsx | 69 +++++++++++++++++++ 5 files changed, 185 insertions(+), 2 deletions(-) create mode 100644 ui/litellm-dashboard/public/assets/logos/mongodb.svg 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",