Compare commits

..

1 Commits

Author SHA1 Message Date
pablonyx
0b9353f199 add minor fixes 2025-03-03 17:18:05 -08:00
14 changed files with 79 additions and 134 deletions

View File

@@ -4,8 +4,7 @@ from ee.onyx.server.reporting.usage_export_generation import create_new_usage_re
from onyx.background.celery.apps.primary import celery_app
from onyx.background.task_utils import build_celery_task_wrapper
from onyx.configs.app_configs import JOB_TIMEOUT
from onyx.db.chat import delete_chat_session
from onyx.db.chat import get_chat_sessions_older_than
from onyx.db.chat import delete_chat_sessions_older_than
from onyx.db.engine import get_session_with_current_tenant
from onyx.server.settings.store import load_settings
from onyx.utils.logger import setup_logger
@@ -19,26 +18,7 @@ logger = setup_logger()
@celery_app.task(soft_time_limit=JOB_TIMEOUT)
def perform_ttl_management_task(retention_limit_days: int, *, tenant_id: str) -> None:
with get_session_with_current_tenant() as db_session:
old_chat_sessions = get_chat_sessions_older_than(
retention_limit_days, db_session
)
for user_id, session_id in old_chat_sessions:
# one session per delete so that we don't blow up if a deletion fails.
with get_session_with_current_tenant() as db_session:
try:
delete_chat_session(
user_id,
session_id,
db_session,
include_deleted=True,
hard_delete=True,
)
except Exception:
logger.exception(
"delete_chat_session exceptioned. "
f"user_id={user_id} session_id={session_id}"
)
delete_chat_sessions_older_than(retention_limit_days, db_session)
#####

View File

@@ -104,14 +104,14 @@ async def provision_tenant(tenant_id: str, email: str) -> None:
status_code=409, detail="User already belongs to an organization"
)
logger.debug(f"Provisioning tenant {tenant_id} for user {email}")
logger.info(f"Provisioning tenant: {tenant_id}")
token = None
try:
if not create_schema_if_not_exists(tenant_id):
logger.debug(f"Created schema for tenant {tenant_id}")
logger.info(f"Created schema for tenant {tenant_id}")
else:
logger.debug(f"Schema already exists for tenant {tenant_id}")
logger.info(f"Schema already exists for tenant {tenant_id}")
token = CURRENT_TENANT_ID_CONTEXTVAR.set(tenant_id)

View File

@@ -523,7 +523,6 @@ class UserManager(UUIDIDMixin, BaseUserManager[User, uuid.UUID]):
token = CURRENT_TENANT_ID_CONTEXTVAR.set(tenant_id)
try:
user_count = await get_user_count()
logger.debug(f"Current tenant user count: {user_count}")
with get_session_with_tenant(tenant_id=tenant_id) as db_session:
if user_count == 1:
@@ -545,7 +544,7 @@ class UserManager(UUIDIDMixin, BaseUserManager[User, uuid.UUID]):
finally:
CURRENT_TENANT_ID_CONTEXTVAR.reset(token)
logger.debug(f"User {user.id} has registered.")
logger.notice(f"User {user.id} has registered.")
optional_telemetry(
record_type=RecordType.SIGN_UP,
data={"action": "create"},

View File

@@ -3,7 +3,6 @@ from datetime import datetime
from datetime import timedelta
from typing import Any
from typing import cast
from typing import Tuple
from uuid import UUID
from fastapi import HTTPException
@@ -12,7 +11,6 @@ from sqlalchemy import desc
from sqlalchemy import func
from sqlalchemy import nullsfirst
from sqlalchemy import or_
from sqlalchemy import Row
from sqlalchemy import select
from sqlalchemy import update
from sqlalchemy.exc import MultipleResultsFound
@@ -377,33 +375,24 @@ def delete_chat_session(
db_session.commit()
def get_chat_sessions_older_than(
days_old: int, db_session: Session
) -> list[tuple[UUID | None, UUID]]:
"""
Retrieves chat sessions older than a specified number of days.
Args:
days_old: The number of days to consider as "old".
db_session: The database session.
Returns:
A list of tuples, where each tuple contains the user_id (can be None) and the chat_session_id of an old chat session.
"""
def delete_chat_sessions_older_than(days_old: int, db_session: Session) -> None:
cutoff_time = datetime.utcnow() - timedelta(days=days_old)
old_sessions: Sequence[Row[Tuple[UUID | None, UUID]]] = db_session.execute(
old_sessions = db_session.execute(
select(ChatSession.user_id, ChatSession.id).where(
ChatSession.time_created < cutoff_time
)
).fetchall()
# convert old_sessions to a conventional list of tuples
returned_sessions: list[tuple[UUID | None, UUID]] = [
(user_id, session_id) for user_id, session_id in old_sessions
]
return returned_sessions
for user_id, session_id in old_sessions:
try:
delete_chat_session(
user_id, session_id, db_session, include_deleted=True, hard_delete=True
)
except Exception:
logger.exception(
"delete_chat_session exceptioned. "
f"user_id={user_id} session_id={session_id}"
)
def get_chat_message(

View File

@@ -1979,6 +1979,8 @@ export function ChatPage({
const innerSidebarElementRef = useRef<HTMLDivElement>(null);
const [settingsToggled, setSettingsToggled] = useState(false);
const [showDeleteAllModal, setShowDeleteAllModal] = useState(false);
const currentPersona = alternativeAssistant || liveAssistant;
const HORIZON_DISTANCE = 800;
@@ -2140,6 +2142,32 @@ export function ChatPage({
<ChatPopup />
{showDeleteAllModal && (
<ConfirmEntityModal
entityType="All Chats"
entityName="all your chat sessions"
onClose={() => setShowDeleteAllModal(false)}
additionalDetails="This action cannot be undone. All your chat sessions will be deleted."
onSubmit={async () => {
const response = await deleteAllChatSessions("Chat");
if (response.ok) {
setShowDeleteAllModal(false);
setPopup({
message: "All your chat sessions have been deleted.",
type: "success",
});
refreshChatSessions();
router.push("/chat");
} else {
setPopup({
message: "Failed to delete all chat sessions.",
type: "error",
});
}
}}
/>
)}
{currentFeedback && (
<FeedbackModal
feedbackType={currentFeedback[0]}
@@ -2297,6 +2325,7 @@ export function ChatPage({
folders={folders}
removeToggle={removeToggle}
showShareModal={showShareModal}
showDeleteAllModal={() => setShowDeleteAllModal(true)}
/>
</div>

View File

@@ -329,7 +329,7 @@ export async function deleteChatSession(chatSessionId: string) {
return response;
}
export async function deleteAllChatSessions() {
export async function deleteAllChatSessions(sessionType: "Chat" | "Search") {
const response = await fetch(`/api/chat/delete-all-chat-sessions`, {
method: "DELETE",
headers: {

View File

@@ -24,9 +24,6 @@ import { Monitor, Moon, Sun } from "lucide-react";
import { useTheme } from "next-themes";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { FiTrash2 } from "react-icons/fi";
import { deleteAllChatSessions } from "../lib";
import { useChatContext } from "@/components/context/ChatContext";
type SettingsSection = "settings" | "password";
@@ -50,8 +47,6 @@ export function UserSettingsModal({
updateUserShortcuts,
updateUserTemperatureOverrideEnabled,
} = useUser();
const { refreshChatSessions } = useChatContext();
const router = useRouter();
const containerRef = useRef<HTMLDivElement>(null);
const messageRef = useRef<HTMLDivElement>(null);
const { theme, setTheme } = useTheme();
@@ -62,8 +57,6 @@ export function UserSettingsModal({
const [isLoading, setIsLoading] = useState(false);
const [activeSection, setActiveSection] =
useState<SettingsSection>("settings");
const [isDeleteAllLoading, setIsDeleteAllLoading] = useState(false);
const [showDeleteConfirmation, setShowDeleteConfirmation] = useState(false);
useEffect(() => {
const container = containerRef.current;
@@ -139,6 +132,7 @@ export function UserSettingsModal({
);
});
const router = useRouter();
const handleChangedefaultModel = async (defaultModel: string | null) => {
try {
const response = await setUserDefaultModel(defaultModel);
@@ -211,31 +205,6 @@ export function UserSettingsModal({
};
const showPasswordSection = user?.password_configured;
const handleDeleteAllChats = async () => {
setIsDeleteAllLoading(true);
try {
const response = await deleteAllChatSessions();
if (response.ok) {
setPopup({
message: "All your chat sessions have been deleted.",
type: "success",
});
refreshChatSessions();
router.push("/chat");
} else {
throw new Error("Failed to delete all chat sessions");
}
} catch (error) {
setPopup({
message: "Failed to delete all chat sessions",
type: "error",
});
} finally {
setIsDeleteAllLoading(false);
setShowDeleteConfirmation(false);
}
};
return (
<Modal
onOutsideClick={onClose}
@@ -381,51 +350,6 @@ export function UserSettingsModal({
}}
/>
</div>
<div className="pt-4 border-t border-border">
{!showDeleteConfirmation ? (
<div className="space-y-3">
<p className="text-sm text-neutral-600 ">
This will permanently delete all your chat sessions and
cannot be undone.
</p>
<Button
variant="destructive"
className="w-full flex items-center justify-center"
onClick={() => setShowDeleteConfirmation(true)}
>
<FiTrash2 className="mr-2" size={14} />
Delete All Chats
</Button>
</div>
) : (
<div className="space-y-3">
<p className="text-sm text-neutral-600 ">
Are you sure you want to delete all your chat sessions?
</p>
<div className="flex gap-2">
<Button
type="button"
variant="destructive"
className="flex-1 flex items-center justify-center"
onClick={handleDeleteAllChats}
disabled={isDeleteAllLoading}
>
{isDeleteAllLoading
? "Deleting..."
: "Yes, Delete All"}
</Button>
<Button
variant="outline"
className="flex-1"
onClick={() => setShowDeleteConfirmation(false)}
disabled={isDeleteAllLoading}
>
Cancel
</Button>
</div>
</div>
)}
</div>
</div>
)}
{activeSection === "password" && (

View File

@@ -64,6 +64,7 @@ interface HistorySidebarProps {
showShareModal?: (chatSession: ChatSession) => void;
showDeleteModal?: (chatSession: ChatSession) => void;
explicitlyUntoggle: () => void;
showDeleteAllModal?: () => void;
setShowAssistantsModal: (show: boolean) => void;
toggleChatSessionSearchModal?: () => void;
}
@@ -182,6 +183,7 @@ export const HistorySidebar = forwardRef<HTMLDivElement, HistorySidebarProps>(
showShareModal,
toggleChatSessionSearchModal,
showDeleteModal,
showDeleteAllModal,
},
ref: ForwardedRef<HTMLDivElement>
) => {
@@ -401,6 +403,7 @@ export const HistorySidebar = forwardRef<HTMLDivElement, HistorySidebarProps>(
existingChats={existingChats}
currentChatId={currentChatId}
folders={folders}
showDeleteAllModal={showDeleteAllModal}
/>
</div>
</div>

View File

@@ -10,6 +10,7 @@ import { Folder } from "../folders/interfaces";
import { usePopup } from "@/components/admin/connectors/Popup";
import { useRouter } from "next/navigation";
import { FiPlus, FiTrash2, FiCheck, FiX } from "react-icons/fi";
import { NEXT_PUBLIC_DELETE_ALL_CHATS_ENABLED } from "@/lib/constants";
import { FolderDropdown } from "../folders/FolderDropdown";
import { ChatSessionDisplay } from "./ChatSessionDisplay";
import { useState, useCallback, useRef, useContext, useEffect } from "react";
@@ -106,6 +107,7 @@ export function PagesTab({
closeSidebar,
showShareModal,
showDeleteModal,
showDeleteAllModal,
toggleChatSessionSearchModal,
}: {
existingChats?: ChatSession[];
@@ -115,6 +117,7 @@ export function PagesTab({
closeSidebar?: () => void;
showShareModal?: (chatSession: ChatSession) => void;
showDeleteModal?: (chatSession: ChatSession) => void;
showDeleteAllModal?: () => void;
}) {
const { setPopup, popup } = usePopup();
const router = useRouter();
@@ -435,7 +438,11 @@ export function PagesTab({
</DndContext>
)}
<div className="pl-4 pr-3">
<div
className={`pl-4 pr-3 ${
NEXT_PUBLIC_DELETE_ALL_CHATS_ENABLED && "pb-20"
}`}
>
{!isHistoryEmpty && (
<>
{Object.entries(groupedChatSesssions)
@@ -472,6 +479,17 @@ export function PagesTab({
</p>
)}
</div>
{showDeleteAllModal && NEXT_PUBLIC_DELETE_ALL_CHATS_ENABLED && (
<div className="absolute w-full border-t border-t-border bg-background-100 bottom-0 left-0 p-4">
<button
className="px-4 w-full py-2 px-4 text-text-600 hover:text-text-800 bg-background-125 border border-border-strong/50 shadow-sm rounded-md transition-colors duration-200 flex items-center justify-center text-sm"
onClick={showDeleteAllModal}
>
<FiTrash2 className="mr-2" size={14} />
Clear All History
</button>
</div>
)}
</div>
);
}

View File

@@ -206,7 +206,7 @@ export function SharedChatDisplay({
{chatSession.description || `Unnamed Chat`}
</h1>
<p className=" text-text-darker">
{humanReadableFormat(chatSession.time_updated)}
{humanReadableFormat(chatSession.time_created)}
</p>
<div
className={`

View File

@@ -181,7 +181,7 @@ const SignedUpUserTable = ({
: "All Roles"}
</SelectValue>
</SelectTrigger>
<SelectContent className="bg-background-50">
<SelectContent className="bg-background">
{Object.entries(USER_ROLE_LABELS)
.filter(([role]) => role !== UserRole.EXT_PERM_USER)
.map(([role, label]) => (

View File

@@ -25,8 +25,8 @@ export function mockedRefreshToken(): CustomRefreshTokenResponse {
*/
const mockExp = Date.now() + 3600000; // 1 hour from now in milliseconds
const data: CustomRefreshTokenResponse = {
access_token: "asdf Mock access token",
refresh_token: "asdf Mock refresh token",
access_token: "Mock access token",
refresh_token: "Mock refresh token",
session: { exp: mockExp },
userinfo: {
sub: "Mock email",

View File

@@ -85,7 +85,7 @@ export const LLMSelector: React.FC<LLMSelectorProps> = ({
<span>{userSettings ? "System Default" : "User Default"}</span>
{userSettings && (
<span className=" my-auto font-normal ml-1">
({defaultModelDisplayName}) asdf
({defaultModelDisplayName})
</span>
)}
</SelectItem>

View File

@@ -82,6 +82,9 @@ export const NEXT_PUBLIC_FORGOT_PASSWORD_ENABLED =
export const NEXT_PUBLIC_TEST_ENV =
process.env.NEXT_PUBLIC_TEST_ENV?.toLowerCase() === "true";
export const NEXT_PUBLIC_DELETE_ALL_CHATS_ENABLED =
process.env.NEXT_PUBLIC_DELETE_ALL_CHATS_ENABLED?.toLowerCase() === "true";
export const NEXT_PUBLIC_ENABLE_CHROME_EXTENSION =
process.env.NEXT_PUBLIC_ENABLE_CHROME_EXTENSION?.toLowerCase() === "true";