Skip to content

Commit

Permalink
Add delete all chats option
Browse files Browse the repository at this point in the history
  • Loading branch information
pablonyx committed Dec 16, 2024
1 parent 0e40102 commit 01293f6
Show file tree
Hide file tree
Showing 22 changed files with 157 additions and 72 deletions.
2 changes: 1 addition & 1 deletion backend/danswer/auth/noauth_user.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ def fetch_no_auth_user(
is_active=True,
is_superuser=False,
is_verified=True,
role=UserRole.ADMIN if anonymous_user_enabled else UserRole.BASIC,
role=UserRole.BASIC if anonymous_user_enabled else UserRole.ADMIN,
preferences=load_no_auth_user_preferences(store),
is_anonymous_user=anonymous_user_enabled,
)
3 changes: 3 additions & 0 deletions backend/danswer/server/manage/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ class UserInfo(BaseModel):
current_token_expiry_length: int | None = None
is_cloud_superuser: bool = False
organization_name: str | None = None
is_anonymous_user: bool | None = None

@classmethod
def from_model(
Expand All @@ -71,6 +72,7 @@ def from_model(
expiry_length: int | None = None,
is_cloud_superuser: bool = False,
organization_name: str | None = None,
is_anonymous_user: bool | None = None,
) -> "UserInfo":
return cls(
id=str(user.id),
Expand All @@ -97,6 +99,7 @@ def from_model(
current_token_created_at=current_token_created_at,
current_token_expiry_length=expiry_length,
is_cloud_superuser=is_cloud_superuser,
is_anonymous_user=is_anonymous_user,
)


Expand Down
4 changes: 2 additions & 2 deletions backend/danswer/server/query_and_chat/token_limit.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from sqlalchemy import select
from sqlalchemy.orm import Session

from danswer.auth.users import current_user
from danswer.auth.users import current_second_level_limited_user
from danswer.db.engine import get_session_context_manager
from danswer.db.engine import get_session_with_tenant
from danswer.db.models import ChatMessage
Expand All @@ -31,7 +31,7 @@


def check_token_rate_limits(
user: User | None = Depends(current_user),
user: User | None = Depends(current_second_level_limited_user),
) -> None:
# short circuit if no rate limits are set up
# NOTE: result of `any_rate_limit_exists` is cached, so this call is fast 99% of the time
Expand Down
73 changes: 73 additions & 0 deletions backend/tests/integration/common_utils/managers/settings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
from typing import Any
from typing import Dict
from typing import Optional

import requests

from tests.integration.common_utils.constants import API_SERVER_URL
from tests.integration.common_utils.constants import GENERAL_HEADERS
from tests.integration.common_utils.test_models import DATestSettings
from tests.integration.common_utils.test_models import DATestUser


class SettingsManager:
@staticmethod
def get_settings(
user_performing_action: DATestUser | None = None,
) -> Dict[str, Any]:
headers = (
user_performing_action.headers
if user_performing_action
else GENERAL_HEADERS
)
headers.pop("Content-Type", None)

response = requests.get(
f"{API_SERVER_URL}/api/manage/admin/settings",
headers=headers,
)

if not response.ok:
return (
{},
f"Failed to get settings - {response.json().get('detail', 'Unknown error')}",
)

return response.json(), ""

@staticmethod
def update_settings(
settings: DATestSettings,
user_performing_action: DATestUser | None = None,
) -> Dict[str, Any]:
headers = (
user_performing_action.headers
if user_performing_action
else GENERAL_HEADERS
)
headers.pop("Content-Type", None)

payload = settings.model_dump()
response = requests.patch(
f"{API_SERVER_URL}/api/manage/admin/settings",
json=payload,
headers=headers,
)

if not response.ok:
return (
{},
f"Failed to update settings - {response.json().get('detail', 'Unknown error')}",
)

return response.json(), ""

@staticmethod
def get_setting(
key: str,
user_performing_action: DATestUser | None = None,
) -> Optional[Any]:
settings, error = SettingsManager.get_settings(user_performing_action)
if error:
return None
return settings.get(key)
16 changes: 16 additions & 0 deletions backend/tests/integration/common_utils/test_models.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from enum import Enum
from typing import Any
from uuid import UUID

Expand Down Expand Up @@ -150,3 +151,18 @@ class StreamedResponse(BaseModel):
relevance_summaries: list[dict[str, Any]] | None = None
tool_result: Any | None = None
user: str | None = None


class DATestGatingType(str, Enum):
FULL = "full"
PARTIAL = "partial"
NONE = "none"


class DATestSettings(BaseModel):
"""General settings"""

maximum_chat_retention_days: int | None = None
gpu_enabled: bool | None = None
product_gating: DATestGatingType = DATestGatingType.NONE
anonymous_user_enabled: bool | None = None
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from tests.integration.common_utils.managers.settings import SettingsManager
from tests.integration.common_utils.managers.user import UserManager
from tests.integration.common_utils.test_models import DATestSettings
from tests.integration.common_utils.test_models import DATestUser


def test_limited(reset: None) -> None:
"""Verify that with a limited role key, limited endpoints are accessible and
others are not."""

# Creating an admin user (first user created is automatically an admin)
admin_user: DATestUser = UserManager.create(name="admin_user")
SettingsManager.update_settings(DATestSettings(anonymous_user_enabled=True))
print(admin_user.headers)
13 changes: 9 additions & 4 deletions web/src/app/auth/login/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,20 +42,21 @@ const Page = async (props: {

// simply take the user to the home page if Auth is disabled
if (authTypeMetadata?.authType === "disabled") {
return redirect("/");
return redirect("/chat");
}

// if user is already logged in, take them to the main app page
const secondsTillExpiration = getSecondsUntilExpiration(currentUser);
if (
currentUser &&
currentUser.is_active &&
!currentUser.is_anonymous_user &&
(secondsTillExpiration === null || secondsTillExpiration > 0)
) {
if (authTypeMetadata?.requiresVerification && !currentUser.is_verified) {
return redirect("/auth/waiting-on-verification");
}
return redirect("/");
return redirect("/chat");
}

// get where to send the user to authenticate
Expand Down Expand Up @@ -105,7 +106,9 @@ const Page = async (props: {
<Text className="mt-4 mx-auto">
Don&apos;t have an account?{" "}
<Link
href={`/auth/signup${searchParams?.next ? `?next=${searchParams.next}` : ""}`}
href={`/auth/signup${
searchParams?.next ? `?next=${searchParams.next}` : ""
}`}
className="text-link font-medium"
>
Create an account
Expand All @@ -127,7 +130,9 @@ const Page = async (props: {
<Text className="mt-4 mx-auto">
Don&apos;t have an account?{" "}
<Link
href={`/auth/signup${searchParams?.next ? `?next=${searchParams.next}` : ""}`}
href={`/auth/signup${
searchParams?.next ? `?next=${searchParams.next}` : ""
}`}
className="text-link font-medium"
>
Create an account
Expand Down
6 changes: 3 additions & 3 deletions web/src/app/auth/signup/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,21 +39,21 @@ const Page = async (props: {

// simply take the user to the home page if Auth is disabled
if (authTypeMetadata?.authType === "disabled") {
return redirect("/");
return redirect("/chat");
}

// if user is already logged in, take them to the main app page
if (currentUser && currentUser.is_active) {
if (!authTypeMetadata?.requiresVerification || currentUser.is_verified) {
return redirect("/");
return redirect("/chat");
}
return redirect("/auth/waiting-on-verification");
}
const cloud = authTypeMetadata?.authType === "cloud";

// only enable this page if basic login is enabled
if (authTypeMetadata?.authType !== "basic" && !cloud) {
return redirect("/");
return redirect("/chat");
}

let authUrl: string | null = null;
Expand Down
2 changes: 1 addition & 1 deletion web/src/app/auth/verify-email/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export default async function Page() {
}

if (!authTypeMetadata?.requiresVerification || currentUser?.is_verified) {
return redirect("/");
return redirect("/chat");
}

return <Verify user={currentUser} />;
Expand Down
4 changes: 2 additions & 2 deletions web/src/app/auth/waiting-on-verification/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,13 @@ export default async function Page() {

if (!currentUser) {
if (authTypeMetadata?.authType === "disabled") {
return redirect("/");
return redirect("/chat");
}
return redirect("/auth/login");
}

if (!authTypeMetadata?.requiresVerification || currentUser.is_verified) {
return redirect("/");
return redirect("/chat");
}

return (
Expand Down
13 changes: 1 addition & 12 deletions web/src/app/chat/ChatPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -184,8 +184,6 @@ export function ChatPage({
const [userSettingsToggled, setUserSettingsToggled] = useState(false);

const { assistants: availableAssistants, finalAssistants } = useAssistants();
console.log(availableAssistants);
console.log(finalAssistants);

const [showApiKeyModal, setShowApiKeyModal] = useState(
!shouldShowWelcomeModal
Expand Down Expand Up @@ -1652,6 +1650,7 @@ export function ChatPage({
setShowDocSidebar: setShowHistorySidebar,
setToggled: removeToggle,
mobile: settings?.isMobile,
isAnonymousUser: user?.is_anonymous_user,
});

const autoScrollEnabled =
Expand Down Expand Up @@ -2735,40 +2734,30 @@ export function ChatPage({
removeDocs={() => {
clearSelectedDocuments();
}}
removeFilters={() => {
filterManager.setSelectedSources([]);
filterManager.setSelectedTags([]);
filterManager.setSelectedDocumentSets([]);
setDocumentSidebarToggled(false);
}}
showConfigureAPIKey={() =>
setShowApiKeyModal(true)
}
chatState={currentSessionChatState}
stopGenerating={stopGenerating}
openModelSettings={() => setSettingsToggled(true)}
inputPrompts={userInputPrompts}
showDocs={() => setDocumentSelection(true)}
selectedDocuments={selectedDocuments}
// assistant stuff
selectedAssistant={liveAssistant}
setSelectedAssistant={onAssistantChange}
setAlternativeAssistant={setAlternativeAssistant}
alternativeAssistant={alternativeAssistant}
// end assistant stuff
message={message}
setMessage={setMessage}
onSubmit={onSubmit}
filterManager={filterManager}
llmOverrideManager={llmOverrideManager}
files={currentMessageFiles}
setFiles={setCurrentMessageFiles}
toggleFilters={
retrievalEnabled ? toggleFilters : undefined
}
handleFileUpload={handleImageUpload}
textAreaRef={textAreaRef}
chatSessionId={chatSessionIdRef.current!}
/>
{enterpriseSettings &&
enterpriseSettings.custom_lower_disclaimer_content && (
Expand Down
20 changes: 1 addition & 19 deletions web/src/app/chat/input/ChatInputBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@ import { FiPlusCircle, FiPlus, FiInfo, FiX, FiSearch } from "react-icons/fi";
import { ChatInputOption } from "./ChatInputOption";
import { Persona } from "@/app/admin/assistants/interfaces";
import { InputPrompt } from "@/app/admin/prompt-library/interfaces";
import { FilterManager, LlmOverrideManager } from "@/lib/hooks";
import { SelectedFilterDisplay } from "./SelectedFilterDisplay";
import { FilterManager } from "@/lib/hooks";
import { useChatContext } from "@/components/context/ChatContext";
import { getFinalLLM } from "@/lib/llm/utils";
import { ChatFileType, FileDescriptor } from "../interfaces";
Expand All @@ -31,22 +30,13 @@ import { SettingsContext } from "@/components/settings/SettingsProvider";
import { ChatState } from "../types";
import UnconfiguredProviderText from "@/components/chat_search/UnconfiguredProviderText";
import { useAssistants } from "@/components/context/AssistantsContext";
import AnimatedToggle from "@/components/search/SearchBar";
import { Popup } from "@/components/admin/connectors/Popup";
import { AssistantsTab } from "../modal/configuration/AssistantsTab";
import { IconType } from "react-icons";
import { LlmTab } from "../modal/configuration/LlmTab";
import { XIcon } from "lucide-react";
import { FilterPills } from "./FilterPills";
import { Tag } from "@/lib/types";
import FiltersDisplay from "./FilterDisplay";

const MAX_INPUT_HEIGHT = 200;

interface ChatInputBarProps {
removeFilters: () => void;
removeDocs: () => void;
openModelSettings: () => void;
showDocs: () => void;
showConfigureAPIKey: () => void;
selectedDocuments: DanswerDocument[];
Expand All @@ -55,27 +45,22 @@ interface ChatInputBarProps {
stopGenerating: () => void;
onSubmit: () => void;
filterManager: FilterManager;
llmOverrideManager: LlmOverrideManager;
chatState: ChatState;
alternativeAssistant: Persona | null;
inputPrompts: InputPrompt[];
// assistants
selectedAssistant: Persona;
setSelectedAssistant: (assistant: Persona) => void;
setAlternativeAssistant: (alternativeAssistant: Persona | null) => void;

files: FileDescriptor[];
setFiles: (files: FileDescriptor[]) => void;
handleFileUpload: (files: File[]) => void;
textAreaRef: React.RefObject<HTMLTextAreaElement>;
chatSessionId?: string;
toggleFilters?: () => void;
}

export function ChatInputBar({
removeFilters,
removeDocs,
openModelSettings,
showDocs,
showConfigureAPIKey,
selectedDocuments,
Expand All @@ -84,20 +69,17 @@ export function ChatInputBar({
stopGenerating,
onSubmit,
filterManager,
llmOverrideManager,
chatState,

// assistants
selectedAssistant,
setSelectedAssistant,
setAlternativeAssistant,

files,
setFiles,
handleFileUpload,
textAreaRef,
alternativeAssistant,
chatSessionId,
inputPrompts,
toggleFilters,
}: ChatInputBarProps) {
Expand Down
Loading

0 comments on commit 01293f6

Please sign in to comment.