diff --git a/backend/ee/onyx/server/query_history/api.py b/backend/ee/onyx/server/query_history/api.py index 1468f07c028e..b7dfbb6e062b 100644 --- a/backend/ee/onyx/server/query_history/api.py +++ b/backend/ee/onyx/server/query_history/api.py @@ -2,6 +2,7 @@ import csv import io from datetime import datetime from datetime import timezone +from http import HTTPStatus from uuid import UUID from fastapi import APIRouter @@ -21,8 +22,10 @@ from ee.onyx.server.query_history.models import QuestionAnswerPairSnapshot from onyx.auth.users import current_admin_user from onyx.auth.users import get_display_email from onyx.chat.chat_utils import create_chat_chain +from onyx.configs.app_configs import ONYX_QUERY_HISTORY_TYPE from onyx.configs.constants import MessageType from onyx.configs.constants import QAFeedbackType +from onyx.configs.constants import QueryHistoryType from onyx.configs.constants import SessionType from onyx.db.chat import get_chat_session_by_id from onyx.db.chat import get_chat_sessions_by_user @@ -35,6 +38,8 @@ from onyx.server.query_and_chat.models import ChatSessionsResponse router = APIRouter() +ONYX_ANONYMIZED_EMAIL = "anonymous@anonymous.invalid" + def fetch_and_process_chat_session_history( db_session: Session, @@ -107,6 +112,17 @@ def get_user_chat_sessions( _: User | None = Depends(current_admin_user), db_session: Session = Depends(get_session), ) -> ChatSessionsResponse: + # we specifically don't allow this endpoint if "anonymized" since + # this is a direct query on the user id + if ONYX_QUERY_HISTORY_TYPE in [ + QueryHistoryType.DISABLED, + QueryHistoryType.ANONYMIZED, + ]: + raise HTTPException( + status_code=HTTPStatus.FORBIDDEN, + detail="Per user query history has been disabled by the administrator.", + ) + try: chat_sessions = get_chat_sessions_by_user( user_id=user_id, deleted=False, db_session=db_session, limit=0 @@ -141,6 +157,12 @@ def get_chat_session_history( _: User | None = Depends(current_admin_user), db_session: Session = Depends(get_session), ) -> PaginatedReturn[ChatSessionMinimal]: + if ONYX_QUERY_HISTORY_TYPE == QueryHistoryType.DISABLED: + raise HTTPException( + status_code=HTTPStatus.FORBIDDEN, + detail="Query history has been disabled by the administrator.", + ) + page_of_chat_sessions = get_page_of_chat_sessions( page_num=page_num, page_size=page_size, @@ -157,11 +179,16 @@ def get_chat_session_history( feedback_filter=feedback_type, ) + minimal_chat_sessions: list[ChatSessionMinimal] = [] + + for chat_session in page_of_chat_sessions: + minimal_chat_session = ChatSessionMinimal.from_chat_session(chat_session) + if ONYX_QUERY_HISTORY_TYPE == QueryHistoryType.ANONYMIZED: + minimal_chat_session.user_email = ONYX_ANONYMIZED_EMAIL + minimal_chat_sessions.append(minimal_chat_session) + return PaginatedReturn( - items=[ - ChatSessionMinimal.from_chat_session(chat_session) - for chat_session in page_of_chat_sessions - ], + items=minimal_chat_sessions, total_items=total_filtered_chat_sessions_count, ) @@ -172,6 +199,12 @@ def get_chat_session_admin( _: User | None = Depends(current_admin_user), db_session: Session = Depends(get_session), ) -> ChatSessionSnapshot: + if ONYX_QUERY_HISTORY_TYPE == QueryHistoryType.DISABLED: + raise HTTPException( + status_code=HTTPStatus.FORBIDDEN, + detail="Query history has been disabled by the administrator.", + ) + try: chat_session = get_chat_session_by_id( chat_session_id=chat_session_id, @@ -193,6 +226,9 @@ def get_chat_session_admin( f"Could not create snapshot for chat session with id '{chat_session_id}'", ) + if ONYX_QUERY_HISTORY_TYPE == QueryHistoryType.ANONYMIZED: + snapshot.user_email = ONYX_ANONYMIZED_EMAIL + return snapshot @@ -203,6 +239,12 @@ def get_query_history_as_csv( end: datetime | None = None, db_session: Session = Depends(get_session), ) -> StreamingResponse: + if ONYX_QUERY_HISTORY_TYPE == QueryHistoryType.DISABLED: + raise HTTPException( + status_code=HTTPStatus.FORBIDDEN, + detail="Query history has been disabled by the administrator.", + ) + complete_chat_session_history = fetch_and_process_chat_session_history( db_session=db_session, start=start or datetime.fromtimestamp(0, tz=timezone.utc), @@ -213,6 +255,9 @@ def get_query_history_as_csv( question_answer_pairs: list[QuestionAnswerPairSnapshot] = [] for chat_session_snapshot in complete_chat_session_history: + if ONYX_QUERY_HISTORY_TYPE == QueryHistoryType.ANONYMIZED: + chat_session_snapshot.user_email = ONYX_ANONYMIZED_EMAIL + question_answer_pairs.extend( QuestionAnswerPairSnapshot.from_chat_session_snapshot(chat_session_snapshot) ) diff --git a/backend/onyx/configs/app_configs.py b/backend/onyx/configs/app_configs.py index 1a4d11c01b1b..3cd33fe42b72 100644 --- a/backend/onyx/configs/app_configs.py +++ b/backend/onyx/configs/app_configs.py @@ -6,6 +6,7 @@ from typing import cast from onyx.auth.schemas import AuthBackend from onyx.configs.constants import AuthType from onyx.configs.constants import DocumentIndexType +from onyx.configs.constants import QueryHistoryType from onyx.file_processing.enums import HtmlBasedConnectorTransformLinksStrategy ##### @@ -29,6 +30,9 @@ GENERATIVE_MODEL_ACCESS_CHECK_FREQ = int( ) # 1 day DISABLE_GENERATIVE_AI = os.environ.get("DISABLE_GENERATIVE_AI", "").lower() == "true" +ONYX_QUERY_HISTORY_TYPE = QueryHistoryType( + (os.environ.get("ONYX_QUERY_HISTORY_TYPE") or QueryHistoryType.NORMAL.value).lower() +) ##### # Web Configs diff --git a/backend/onyx/configs/constants.py b/backend/onyx/configs/constants.py index 94ca026b1907..c47e4d6e52aa 100644 --- a/backend/onyx/configs/constants.py +++ b/backend/onyx/configs/constants.py @@ -213,6 +213,12 @@ class AuthType(str, Enum): CLOUD = "cloud" +class QueryHistoryType(str, Enum): + DISABLED = "disabled" + ANONYMIZED = "anonymized" + NORMAL = "normal" + + # Special characters for password validation PASSWORD_SPECIAL_CHARS = "!@#$%^&*()_+-=[]{}|;:,.<>?" diff --git a/backend/onyx/server/settings/models.py b/backend/onyx/server/settings/models.py index 58dd0a51fac7..7e1e5ef74925 100644 --- a/backend/onyx/server/settings/models.py +++ b/backend/onyx/server/settings/models.py @@ -4,6 +4,7 @@ from enum import Enum from pydantic import BaseModel from onyx.configs.constants import NotificationType +from onyx.configs.constants import QueryHistoryType from onyx.db.models import Notification as NotificationDBModel from shared_configs.configs import POSTGRES_DEFAULT_SCHEMA @@ -50,6 +51,7 @@ class Settings(BaseModel): temperature_override_enabled: bool | None = False auto_scroll: bool | None = False + query_history_type: QueryHistoryType | None = None class UserSettings(Settings): diff --git a/backend/onyx/server/settings/store.py b/backend/onyx/server/settings/store.py index 3602fd41e902..d35b306f5672 100644 --- a/backend/onyx/server/settings/store.py +++ b/backend/onyx/server/settings/store.py @@ -1,3 +1,4 @@ +from onyx.configs.app_configs import ONYX_QUERY_HISTORY_TYPE from onyx.configs.constants import KV_SETTINGS_KEY from onyx.configs.constants import OnyxRedisLocks from onyx.key_value_store.factory import get_kv_store @@ -45,6 +46,7 @@ def load_settings() -> Settings: anonymous_user_enabled = False settings.anonymous_user_enabled = anonymous_user_enabled + settings.query_history_type = ONYX_QUERY_HISTORY_TYPE return settings diff --git a/web/src/app/admin/settings/interfaces.ts b/web/src/app/admin/settings/interfaces.ts index c56875c1ddc3..c4320d605d60 100644 --- a/web/src/app/admin/settings/interfaces.ts +++ b/web/src/app/admin/settings/interfaces.ts @@ -4,6 +4,12 @@ export enum ApplicationStatus { ACTIVE = "active", } +export enum QueryHistoryType { + DISABLED = "disabled", + ANONYMIZED = "anonymized", + NORMAL = "normal", +} + export interface Settings { anonymous_user_enabled: boolean; maximum_chat_retention_days: number | null; @@ -14,6 +20,7 @@ export interface Settings { application_status: ApplicationStatus; auto_scroll: boolean; temperature_override_enabled: boolean; + query_history_type: QueryHistoryType; } export enum NotificationType { diff --git a/web/src/components/admin/ClientLayout.tsx b/web/src/components/admin/ClientLayout.tsx index 9ccec914af54..5a4a1259f2cc 100644 --- a/web/src/components/admin/ClientLayout.tsx +++ b/web/src/components/admin/ClientLayout.tsx @@ -359,18 +359,25 @@ export function ClientLayout({ ), link: "/admin/performance/usage", }, - { - name: ( -