diff --git a/backend/danswer/db/search_settings.py b/backend/danswer/db/search_settings.py index 0cb50295337..01f458493f7 100644 --- a/backend/danswer/db/search_settings.py +++ b/backend/danswer/db/search_settings.py @@ -1,3 +1,5 @@ +from sqlalchemy import and_ +from sqlalchemy import delete from sqlalchemy import select from sqlalchemy.orm import Session @@ -13,6 +15,7 @@ from danswer.db.engine import get_sqlalchemy_engine from danswer.db.llm import fetch_embedding_provider from danswer.db.models import CloudEmbeddingProvider +from danswer.db.models import IndexAttempt from danswer.db.models import IndexModelStatus from danswer.db.models import SearchSettings from danswer.indexing.models import IndexingSetting @@ -89,6 +92,30 @@ def get_current_db_embedding_provider( return current_embedding_provider +def delete_search_settings(db_session: Session, search_settings_id: int) -> None: + current_settings = get_current_search_settings(db_session) + + if current_settings.id == search_settings_id: + raise ValueError("Cannot delete currently active search settings") + + # First, delete associated index attempts + index_attempts_query = delete(IndexAttempt).where( + IndexAttempt.search_settings_id == search_settings_id + ) + db_session.execute(index_attempts_query) + + # Then, delete the search settings + search_settings_query = delete(SearchSettings).where( + and_( + SearchSettings.id == search_settings_id, + SearchSettings.status != IndexModelStatus.PRESENT, + ) + ) + + db_session.execute(search_settings_query) + db_session.commit() + + def get_current_search_settings(db_session: Session) -> SearchSettings: query = ( select(SearchSettings) diff --git a/backend/danswer/indexing/models.py b/backend/danswer/indexing/models.py index c468b9fb181..93dc0f7315d 100644 --- a/backend/danswer/indexing/models.py +++ b/backend/danswer/indexing/models.py @@ -95,6 +95,7 @@ def from_index_chunk( class EmbeddingModelDetail(BaseModel): + id: int | None = None model_name: str normalize: bool query_prefix: str | None @@ -112,6 +113,7 @@ def from_db_model( search_settings: "SearchSettings", ) -> "EmbeddingModelDetail": return cls( + id=search_settings.id, model_name=search_settings.model_name, normalize=search_settings.normalize, query_prefix=search_settings.query_prefix, diff --git a/backend/danswer/server/manage/embedding/api.py b/backend/danswer/server/manage/embedding/api.py index 2cee962ee6b..eac872810ef 100644 --- a/backend/danswer/server/manage/embedding/api.py +++ b/backend/danswer/server/manage/embedding/api.py @@ -42,10 +42,10 @@ def test_embedding_configuration( api_key=test_llm_request.api_key, api_url=test_llm_request.api_url, provider_type=test_llm_request.provider_type, + model_name=test_llm_request.model_name, normalize=False, query_prefix=None, passage_prefix=None, - model_name=None, ) test_model.encode(["Testing Embedding"], text_type=EmbedTextType.QUERY) diff --git a/backend/danswer/server/manage/embedding/models.py b/backend/danswer/server/manage/embedding/models.py index 50518e6ec01..b4ca7862b55 100644 --- a/backend/danswer/server/manage/embedding/models.py +++ b/backend/danswer/server/manage/embedding/models.py @@ -8,10 +8,15 @@ from danswer.db.models import CloudEmbeddingProvider as CloudEmbeddingProviderModel +class SearchSettingsDeleteRequest(BaseModel): + search_settings_id: int + + class TestEmbeddingRequest(BaseModel): provider_type: EmbeddingProvider api_key: str | None = None api_url: str | None = None + model_name: str | None = None class CloudEmbeddingProvider(BaseModel): diff --git a/backend/danswer/server/manage/search_settings.py b/backend/danswer/server/manage/search_settings.py index 831528b8157..c8433467f6c 100644 --- a/backend/danswer/server/manage/search_settings.py +++ b/backend/danswer/server/manage/search_settings.py @@ -14,6 +14,7 @@ from danswer.db.models import IndexModelStatus from danswer.db.models import User from danswer.db.search_settings import create_search_settings +from danswer.db.search_settings import delete_search_settings from danswer.db.search_settings import get_current_search_settings from danswer.db.search_settings import get_embedding_provider_from_provider_type from danswer.db.search_settings import get_secondary_search_settings @@ -23,6 +24,7 @@ from danswer.natural_language_processing.search_nlp_models import clean_model_name from danswer.search.models import SavedSearchSettings from danswer.search.models import SearchSettingsCreationRequest +from danswer.server.manage.embedding.models import SearchSettingsDeleteRequest from danswer.server.manage.models import FullModelVersionResponse from danswer.server.models import IdReturn from danswer.utils.logger import setup_logger @@ -97,6 +99,7 @@ def set_new_search_settings( primary_index_name=search_settings.index_name, secondary_index_name=new_search_settings.index_name, ) + document_index.ensure_indices_exist( index_embedding_dim=search_settings.model_dim, secondary_index_embedding_dim=new_search_settings.model_dim, @@ -132,6 +135,21 @@ def cancel_new_embedding( ) +@router.delete("/delete-search-settings") +def delete_search_settings_endpoint( + deletion_request: SearchSettingsDeleteRequest, + _: User | None = Depends(current_admin_user), + db_session: Session = Depends(get_session), +) -> None: + try: + delete_search_settings( + db_session=db_session, + search_settings_id=deletion_request.search_settings_id, + ) + except ValueError as e: + raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e)) + + @router.get("/get-current-search-settings") def get_current_search_settings_endpoint( _: User | None = Depends(current_user), diff --git a/backend/model_server/encoders.py b/backend/model_server/encoders.py index ad9d8582bec..8249ce1fda7 100644 --- a/backend/model_server/encoders.py +++ b/backend/model_server/encoders.py @@ -237,15 +237,18 @@ def get_local_reranking_model( def embed_with_litellm_proxy( - texts: list[str], api_url: str, model: str + texts: list[str], api_url: str, model_name: str, api_key: str | None ) -> list[Embedding]: + headers = {} if not api_key else {"Authorization": f"Bearer {api_key}"} + with httpx.Client() as client: response = client.post( api_url, json={ - "model": model, + "model": model_name, "input": texts, }, + headers=headers, ) response.raise_for_status() result = response.json() @@ -280,7 +283,12 @@ def embed_text( logger.error("API URL not provided for LiteLLM proxy") raise ValueError("API URL is required for LiteLLM proxy embedding.") try: - return embed_with_litellm_proxy(texts, api_url, model_name or "") + return embed_with_litellm_proxy( + texts=texts, + api_url=api_url, + model_name=model_name or "", + api_key=api_key, + ) except Exception as e: logger.exception(f"Error during LiteLLM proxy embedding: {str(e)}") raise diff --git a/backend/shared_configs/configs.py b/backend/shared_configs/configs.py index 2357d96d952..f5698d6a6ff 100644 --- a/backend/shared_configs/configs.py +++ b/backend/shared_configs/configs.py @@ -58,6 +58,7 @@ # Fields which should only be set on new search setting PRESERVED_SEARCH_FIELDS = [ + "id", "provider_type", "api_key", "model_name", diff --git a/web/src/app/admin/connectors/[connector]/AddConnectorPage.tsx b/web/src/app/admin/connectors/[connector]/AddConnectorPage.tsx index fa088caf886..3c919e64e31 100644 --- a/web/src/app/admin/connectors/[connector]/AddConnectorPage.tsx +++ b/web/src/app/admin/connectors/[connector]/AddConnectorPage.tsx @@ -91,7 +91,7 @@ export default function AddConnector({ >({ name: "", groups: [], - is_public: false, + is_public: true, ...configuration.values.reduce( (acc, field) => { if (field.type === "list") { diff --git a/web/src/app/admin/embeddings/modals/ChangeCredentialsModal.tsx b/web/src/app/admin/embeddings/modals/ChangeCredentialsModal.tsx index d17e15d0f78..9e848bbe2c0 100644 --- a/web/src/app/admin/embeddings/modals/ChangeCredentialsModal.tsx +++ b/web/src/app/admin/embeddings/modals/ChangeCredentialsModal.tsx @@ -153,34 +153,22 @@ export function ChangeCredentialsModal({ title={`Modify your ${provider.provider_type} ${isProxy ? "URL" : "key"}`} onOutsideClick={onCancel} > -
Uploaded file: {fileName}
} - > - ) : ( - <> + <> + {isProxy && ( +{model.description}
{model?.provider_type?.toLowerCase() != diff --git a/web/src/app/admin/embeddings/pages/utils.ts b/web/src/app/admin/embeddings/pages/utils.ts new file mode 100644 index 00000000000..86af49522b4 --- /dev/null +++ b/web/src/app/admin/embeddings/pages/utils.ts @@ -0,0 +1,10 @@ +export const deleteSearchSettings = async (search_settings_id: number) => { + const response = await fetch(`/api/search-settings/delete-search-settings`, { + method: "DELETE", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ search_settings_id }), + }); + return response; +}; diff --git a/web/src/components/embedding/interfaces.tsx b/web/src/components/embedding/interfaces.tsx index 0fafaa840ca..daa56128c3d 100644 --- a/web/src/components/embedding/interfaces.tsx +++ b/web/src/components/embedding/interfaces.tsx @@ -39,6 +39,7 @@ export interface CloudEmbeddingProvider { // Embedding Models export interface EmbeddingModelDescriptor { + id?: number; model_name: string; model_dim: number; normalize: boolean;