Admin default (#4032)

* clean up

* minor cleanup

* building

* update agnetic message look

* k

* fix alembic history
This commit is contained in:
pablonyx 2025-02-18 18:31:54 -08:00 committed by GitHub
parent 630bdf71a3
commit 9635522de8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 113 additions and 60 deletions

View File

@ -0,0 +1,31 @@
"""nullable preferences
Revision ID: b388730a2899
Revises: 1a03d2c2856b
Create Date: 2025-02-17 18:49:22.643902
"""
from alembic import op
# revision identifiers, used by Alembic.
revision = "b388730a2899"
down_revision = "1a03d2c2856b"
branch_labels = None
depends_on = None
def upgrade() -> None:
op.alter_column("user", "temperature_override_enabled", nullable=True)
op.alter_column("user", "auto_scroll", nullable=True)
def downgrade() -> None:
# Ensure no null values before making columns non-nullable
op.execute(
'UPDATE "user" SET temperature_override_enabled = false WHERE temperature_override_enabled IS NULL'
)
op.execute('UPDATE "user" SET auto_scroll = false WHERE auto_scroll IS NULL')
op.alter_column("user", "temperature_override_enabled", nullable=False)
op.alter_column("user", "auto_scroll", nullable=False)

View File

@ -148,11 +148,12 @@ class User(SQLAlchemyBaseUserTableUUID, Base):
putting here for simpicity putting here for simpicity
""" """
# if specified, controls the assistants that are shown to the user + their order temperature_override_enabled: Mapped[bool | None] = mapped_column(
# if not specified, all assistants are shown Boolean, default=None
temperature_override_enabled: Mapped[bool] = mapped_column(Boolean, default=False) )
auto_scroll: Mapped[bool] = mapped_column(Boolean, default=True) auto_scroll: Mapped[bool | None] = mapped_column(Boolean, default=None)
shortcut_enabled: Mapped[bool] = mapped_column(Boolean, default=False) shortcut_enabled: Mapped[bool] = mapped_column(Boolean, default=False)
chosen_assistants: Mapped[list[int] | None] = mapped_column( chosen_assistants: Mapped[list[int] | None] = mapped_column(
postgresql.JSONB(), nullable=True, default=None postgresql.JSONB(), nullable=True, default=None
) )

View File

@ -45,9 +45,11 @@ class UserPreferences(BaseModel):
hidden_assistants: list[int] = [] hidden_assistants: list[int] = []
visible_assistants: list[int] = [] visible_assistants: list[int] = []
default_model: str | None = None default_model: str | None = None
auto_scroll: bool | None = None
pinned_assistants: list[int] | None = None pinned_assistants: list[int] | None = None
shortcut_enabled: bool | None = None shortcut_enabled: bool | None = None
# These will default to workspace settings on the frontend if not set
auto_scroll: bool | None = None
temperature_override_enabled: bool | None = None temperature_override_enabled: bool | None = None
@ -86,12 +88,12 @@ class UserInfo(BaseModel):
preferences=( preferences=(
UserPreferences( UserPreferences(
shortcut_enabled=user.shortcut_enabled, shortcut_enabled=user.shortcut_enabled,
auto_scroll=user.auto_scroll,
chosen_assistants=user.chosen_assistants, chosen_assistants=user.chosen_assistants,
default_model=user.default_model, default_model=user.default_model,
hidden_assistants=user.hidden_assistants, hidden_assistants=user.hidden_assistants,
pinned_assistants=user.pinned_assistants, pinned_assistants=user.pinned_assistants,
visible_assistants=user.visible_assistants, visible_assistants=user.visible_assistants,
auto_scroll=user.auto_scroll,
temperature_override_enabled=user.temperature_override_enabled, temperature_override_enabled=user.temperature_override_enabled,
) )
), ),

View File

@ -46,7 +46,9 @@ class Settings(BaseModel):
application_status: ApplicationStatus = ApplicationStatus.ACTIVE application_status: ApplicationStatus = ApplicationStatus.ACTIVE
anonymous_user_enabled: bool | None = None anonymous_user_enabled: bool | None = None
pro_search_disabled: bool | None = None pro_search_disabled: bool | None = None
auto_scroll: bool | None = None
temperature_override_enabled: bool = False
auto_scroll: bool = False
class UserSettings(Settings): class UserSettings(Settings):

View File

@ -213,12 +213,23 @@ export function SettingsForm() {
<Title className="mb-4">Workspace Settings</Title> <Title className="mb-4">Workspace Settings</Title>
<Checkbox <Checkbox
label="Auto-scroll" label="Auto-scroll"
sublabel="If set, the chat window will automatically scroll to the bottom as new lines of text are generated by the AI model." sublabel="If set, the chat window will automatically scroll to the bottom as new lines of text are generated by the AI model. This can be overridden by individual user settings."
checked={settings.auto_scroll} checked={settings.auto_scroll}
onChange={(e) => onChange={(e) =>
handleToggleSettingsField("auto_scroll", e.target.checked) handleToggleSettingsField("auto_scroll", e.target.checked)
} }
/> />
<Checkbox
label="Override default temperature"
sublabel="If set, users will be able to override the default temperature for each assistant."
checked={settings.temperature_override_enabled}
onChange={(e) =>
handleToggleSettingsField(
"temperature_override_enabled",
e.target.checked
)
}
/>
<Checkbox <Checkbox
label="Anonymous Users" label="Anonymous Users"
sublabel="If set, users will not be required to sign in to use Onyx." sublabel="If set, users will not be required to sign in to use Onyx."

View File

@ -13,6 +13,7 @@ export interface Settings {
pro_search_disabled: boolean | null; pro_search_disabled: boolean | null;
application_status: ApplicationStatus; application_status: ApplicationStatus;
auto_scroll: boolean; auto_scroll: boolean;
temperature_override_enabled: boolean;
} }
export enum NotificationType { export enum NotificationType {
@ -54,7 +55,6 @@ export interface EnterpriseSettings {
custom_popup_header: string | null; custom_popup_header: string | null;
custom_popup_content: string | null; custom_popup_content: string | null;
enable_consent_screen: boolean | null; enable_consent_screen: boolean | null;
auto_scroll: boolean;
} }
export interface CombinedSettings { export interface CombinedSettings {

View File

@ -1870,9 +1870,7 @@ export function ChatPage({
}); });
const autoScrollEnabled = const autoScrollEnabled =
user?.preferences?.auto_scroll == null (user?.preferences?.auto_scroll && !agenticGenerating) ?? false;
? settings?.enterpriseSettings?.auto_scroll || false
: user?.preferences?.auto_scroll! && !agenticGenerating;
useScrollonStream({ useScrollonStream({
chatState: currentSessionChatState, chatState: currentSessionChatState,

View File

@ -537,7 +537,6 @@ export const AgenticMessage = ({
{includeMessageSwitcher && ( {includeMessageSwitcher && (
<div className="-mx-1 mr-auto"> <div className="-mx-1 mr-auto">
<MessageSwitcher <MessageSwitcher
disableForStreaming={!isComplete}
currentPage={currentMessageInd + 1} currentPage={currentMessageInd + 1}
totalPages={otherMessagesCanSwitchTo.length} totalPages={otherMessagesCanSwitchTo.length}
handlePrevious={() => { handlePrevious={() => {
@ -624,7 +623,6 @@ export const AgenticMessage = ({
{includeMessageSwitcher && ( {includeMessageSwitcher && (
<div className="-mx-1 mr-auto"> <div className="-mx-1 mr-auto">
<MessageSwitcher <MessageSwitcher
disableForStreaming={!isComplete}
currentPage={currentMessageInd + 1} currentPage={currentMessageInd + 1}
totalPages={otherMessagesCanSwitchTo.length} totalPages={otherMessagesCanSwitchTo.length}
handlePrevious={() => { handlePrevious={() => {
@ -703,52 +701,27 @@ function MessageSwitcher({
totalPages, totalPages,
handlePrevious, handlePrevious,
handleNext, handleNext,
disableForStreaming,
}: { }: {
currentPage: number; currentPage: number;
totalPages: number; totalPages: number;
handlePrevious: () => void; handlePrevious: () => void;
handleNext: () => void; handleNext: () => void;
disableForStreaming?: boolean;
}) { }) {
return ( return (
<div className="flex items-center text-sm space-x-0.5"> <div className="flex items-center text-sm space-x-0.5">
<TooltipProvider> <Hoverable
<Tooltip> icon={FiChevronLeft}
<TooltipTrigger asChild> onClick={currentPage === 1 ? undefined : handlePrevious}
<div> />
<Hoverable
icon={FiChevronLeft}
onClick={currentPage === 1 ? undefined : handlePrevious}
/>
</div>
</TooltipTrigger>
<TooltipContent>
{disableForStreaming ? "Disabled" : "Previous"}
</TooltipContent>
</Tooltip>
</TooltipProvider>
<span className="text-text-darker select-none"> <span className="text-text-darker select-none">
{currentPage} / {totalPages} {currentPage} / {totalPages}
{disableForStreaming ? "Complete" : "Generating"}
</span> </span>
<TooltipProvider> <Hoverable
<Tooltip> icon={FiChevronRight}
<TooltipTrigger asChild> onClick={currentPage === totalPages ? undefined : handleNext}
<div> />
<Hoverable
icon={FiChevronRight}
onClick={currentPage === totalPages ? undefined : handleNext}
/>
</div>
</TooltipTrigger>
<TooltipContent>
{disableForStreaming ? "Disabled" : "Next"}
</TooltipContent>
</Tooltip>
</TooltipProvider>
</div> </div>
); );
} }

View File

@ -154,11 +154,9 @@ export function UserSettingsModal({
}); });
} }
}; };
const defaultProvider = llmProviders.find(
(llmProvider) => llmProvider.is_default_provider
);
const settings = useContext(SettingsContext); const settings = useContext(SettingsContext);
const autoScroll = settings?.enterpriseSettings?.auto_scroll; const autoScroll = settings?.settings?.auto_scroll;
const checked = const checked =
user?.preferences?.auto_scroll === null user?.preferences?.auto_scroll === null

View File

@ -55,7 +55,7 @@ export function WhitelabelingForm() {
<div> <div>
<Formik <Formik
initialValues={{ initialValues={{
auto_scroll: enterpriseSettings?.auto_scroll || false, auto_scroll: settings?.settings?.auto_scroll || false,
application_name: enterpriseSettings?.application_name || null, application_name: enterpriseSettings?.application_name || null,
use_custom_logo: enterpriseSettings?.use_custom_logo || false, use_custom_logo: enterpriseSettings?.use_custom_logo || false,
use_custom_logotype: enterpriseSettings?.use_custom_logotype || false, use_custom_logotype: enterpriseSettings?.use_custom_logotype || false,

View File

@ -1,3 +1,4 @@
"use client";
import { CombinedSettings } from "@/app/admin/settings/interfaces"; import { CombinedSettings } from "@/app/admin/settings/interfaces";
import { UserProvider } from "../user/UserProvider"; import { UserProvider } from "../user/UserProvider";
import { ProviderContextProvider } from "../chat/ProviderContext"; import { ProviderContextProvider } from "../chat/ProviderContext";
@ -27,9 +28,9 @@ export const AppProvider = ({
hasImageCompatibleModel, hasImageCompatibleModel,
}: AppProviderProps) => { }: AppProviderProps) => {
return ( return (
<UserProvider user={user}> <SettingsProvider settings={settings}>
<ProviderContextProvider> <UserProvider settings={settings} user={user}>
<SettingsProvider settings={settings}> <ProviderContextProvider>
<AssistantsProvider <AssistantsProvider
initialAssistants={assistants} initialAssistants={assistants}
hasAnyConnectors={hasAnyConnectors} hasAnyConnectors={hasAnyConnectors}
@ -37,8 +38,8 @@ export const AppProvider = ({
> >
{children} {children}
</AssistantsProvider> </AssistantsProvider>
</SettingsProvider> </ProviderContextProvider>
</ProviderContextProvider> </UserProvider>
</UserProvider> </SettingsProvider>
); );
}; };

View File

@ -52,6 +52,7 @@ export async function fetchSettingsSS(): Promise<CombinedSettings | null> {
needs_reindexing: false, needs_reindexing: false,
anonymous_user_enabled: false, anonymous_user_enabled: false,
pro_search_disabled: false, pro_search_disabled: false,
temperature_override_enabled: true,
}; };
} else { } else {
throw new Error( throw new Error(

View File

@ -4,6 +4,8 @@ import React, { createContext, useContext, useState, useEffect } from "react";
import { User, UserRole } from "@/lib/types"; import { User, UserRole } from "@/lib/types";
import { getCurrentUser } from "@/lib/user"; import { getCurrentUser } from "@/lib/user";
import { usePostHog } from "posthog-js/react"; import { usePostHog } from "posthog-js/react";
import { CombinedSettings } from "@/app/admin/settings/interfaces";
import { SettingsContext } from "../settings/SettingsProvider";
interface UserContextType { interface UserContextType {
user: User | null; user: User | null;
@ -26,14 +28,47 @@ const UserContext = createContext<UserContextType | undefined>(undefined);
export function UserProvider({ export function UserProvider({
children, children,
user, user,
settings,
}: { }: {
children: React.ReactNode; children: React.ReactNode;
user: User | null; user: User | null;
settings: CombinedSettings;
}) { }) {
const [upToDateUser, setUpToDateUser] = useState<User | null>(user); const updatedSettings = useContext(SettingsContext);
const posthog = usePostHog(); const posthog = usePostHog();
// For auto_scroll and temperature_override_enabled:
// - If user has a preference set, use that
// - Otherwise, use the workspace setting if available
function mergeUserPreferences(
currentUser: User | null,
currentSettings: CombinedSettings | null
): User | null {
if (!currentUser) return null;
return {
...currentUser,
preferences: {
...currentUser.preferences,
auto_scroll:
currentUser.preferences?.auto_scroll ??
currentSettings?.settings?.auto_scroll ??
false,
temperature_override_enabled:
currentUser.preferences?.temperature_override_enabled ??
currentSettings?.settings?.temperature_override_enabled ??
false,
},
};
}
const [upToDateUser, setUpToDateUser] = useState<User | null>(
mergeUserPreferences(user, settings)
);
useEffect(() => {
setUpToDateUser(mergeUserPreferences(user, updatedSettings));
}, [user, updatedSettings]);
useEffect(() => { useEffect(() => {
if (!posthog) return; if (!posthog) return;