mirror of
https://github.com/danswer-ai/danswer.git
synced 2025-06-02 11:09:20 +02:00
Custom banner
This commit is contained in:
parent
91cf45165f
commit
3466f6d3a4
@ -304,9 +304,7 @@ def _prepare_index_attempt(db_session: Session, index_attempt_id: int) -> IndexA
|
|||||||
return attempt
|
return attempt
|
||||||
|
|
||||||
|
|
||||||
def run_indexing_entrypoint(
|
def run_indexing_entrypoint(index_attempt_id: int, is_ee: bool = False) -> None:
|
||||||
index_attempt_id: int, is_ee: bool = False
|
|
||||||
) -> None:
|
|
||||||
"""Entrypoint for indexing run when using dask distributed.
|
"""Entrypoint for indexing run when using dask distributed.
|
||||||
Wraps the actual logic in a `try` block so that we can catch any exceptions
|
Wraps the actual logic in a `try` block so that we can catch any exceptions
|
||||||
and mark the attempt as failed."""
|
and mark the attempt as failed."""
|
||||||
|
@ -35,10 +35,10 @@ from danswer.db.models import IndexModelStatus
|
|||||||
from danswer.db.swap_index import check_index_swap
|
from danswer.db.swap_index import check_index_swap
|
||||||
from danswer.search.search_nlp_models import warm_up_encoders
|
from danswer.search.search_nlp_models import warm_up_encoders
|
||||||
from danswer.utils.logger import setup_logger
|
from danswer.utils.logger import setup_logger
|
||||||
|
from danswer.utils.variable_functionality import global_version
|
||||||
from shared_configs.configs import INDEXING_MODEL_SERVER_HOST
|
from shared_configs.configs import INDEXING_MODEL_SERVER_HOST
|
||||||
from shared_configs.configs import LOG_LEVEL
|
from shared_configs.configs import LOG_LEVEL
|
||||||
from shared_configs.configs import MODEL_SERVER_PORT
|
from shared_configs.configs import MODEL_SERVER_PORT
|
||||||
from danswer.utils.variable_functionality import global_version
|
|
||||||
|
|
||||||
logger = setup_logger()
|
logger = setup_logger()
|
||||||
|
|
||||||
|
@ -9,5 +9,10 @@ class EnterpriseSettings(BaseModel):
|
|||||||
application_name: str | None = None
|
application_name: str | None = None
|
||||||
use_custom_logo: bool = False
|
use_custom_logo: bool = False
|
||||||
|
|
||||||
|
# custom Chat components
|
||||||
|
custom_header_content: str | None = None
|
||||||
|
custom_popup_header: str | None = None
|
||||||
|
custom_popup_content: str | None = None
|
||||||
|
|
||||||
def check_validity(self) -> None:
|
def check_validity(self) -> None:
|
||||||
return
|
return
|
||||||
|
@ -41,13 +41,14 @@ import { usePopup } from "@/components/admin/connectors/Popup";
|
|||||||
import { ResizableSection } from "@/components/resizable/ResizableSection";
|
import { ResizableSection } from "@/components/resizable/ResizableSection";
|
||||||
import { DanswerInitializingLoader } from "@/components/DanswerInitializingLoader";
|
import { DanswerInitializingLoader } from "@/components/DanswerInitializingLoader";
|
||||||
import { ChatIntro } from "./ChatIntro";
|
import { ChatIntro } from "./ChatIntro";
|
||||||
import { HEADER_PADDING } from "@/lib/constants";
|
|
||||||
import { computeAvailableFilters } from "@/lib/filters";
|
import { computeAvailableFilters } from "@/lib/filters";
|
||||||
import { useDocumentSelection } from "./useDocumentSelection";
|
import { useDocumentSelection } from "./useDocumentSelection";
|
||||||
import { StarterMessage } from "./StarterMessage";
|
import { StarterMessage } from "./StarterMessage";
|
||||||
import { ShareChatSessionModal } from "./modal/ShareChatSessionModal";
|
import { ShareChatSessionModal } from "./modal/ShareChatSessionModal";
|
||||||
import { SEARCH_PARAM_NAMES, shouldSubmitOnLoad } from "./searchParams";
|
import { SEARCH_PARAM_NAMES, shouldSubmitOnLoad } from "./searchParams";
|
||||||
import { Persona } from "../admin/assistants/interfaces";
|
import { Persona } from "../admin/assistants/interfaces";
|
||||||
|
import { ChatBanner } from "./ChatBanner";
|
||||||
|
import { HEADER_PADDING } from "@/lib/constants";
|
||||||
|
|
||||||
const MAX_INPUT_HEIGHT = 200;
|
const MAX_INPUT_HEIGHT = 200;
|
||||||
|
|
||||||
@ -594,6 +595,10 @@ export const Chat = ({
|
|||||||
className={`w-full h-full ${HEADER_PADDING} flex flex-col overflow-y-auto overflow-x-hidden relative`}
|
className={`w-full h-full ${HEADER_PADDING} flex flex-col overflow-y-auto overflow-x-hidden relative`}
|
||||||
ref={scrollableDivRef}
|
ref={scrollableDivRef}
|
||||||
>
|
>
|
||||||
|
{/* ChatBanner is a custom banner that displays a admin-specified message at
|
||||||
|
the top of the chat page. Only used in the EE version of the app. */}
|
||||||
|
<ChatBanner />
|
||||||
|
|
||||||
{livePersona && (
|
{livePersona && (
|
||||||
<div className="sticky top-0 left-80 z-10 w-full bg-background/90 flex">
|
<div className="sticky top-0 left-80 z-10 w-full bg-background/90 flex">
|
||||||
<div className="ml-2 p-1 rounded mt-2 w-fit">
|
<div className="ml-2 p-1 rounded mt-2 w-fit">
|
||||||
|
48
web/src/app/chat/ChatBanner.tsx
Normal file
48
web/src/app/chat/ChatBanner.tsx
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import ReactMarkdown from "react-markdown";
|
||||||
|
import { SettingsContext } from "@/components/settings/SettingsProviderClientSideHelper";
|
||||||
|
import { useContext } from "react";
|
||||||
|
import remarkGfm from "remark-gfm";
|
||||||
|
|
||||||
|
export function ChatBanner() {
|
||||||
|
const settings = useContext(SettingsContext);
|
||||||
|
if (!settings?.enterpriseSettings?.custom_header_content) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={`
|
||||||
|
z-[39]
|
||||||
|
w-full
|
||||||
|
h-[30px]
|
||||||
|
bg-background-custom-header
|
||||||
|
border-border
|
||||||
|
border-b
|
||||||
|
flex`}
|
||||||
|
>
|
||||||
|
<div className="mx-auto text-emphasis text-sm flex flex-col">
|
||||||
|
<div className="my-auto">
|
||||||
|
<ReactMarkdown
|
||||||
|
className="prose max-w-full"
|
||||||
|
components={{
|
||||||
|
a: ({ node, ...props }) => (
|
||||||
|
<a
|
||||||
|
{...props}
|
||||||
|
className="text-sm text-link hover:text-link-hover"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
p: ({ node, ...props }) => <p {...props} className="text-sm" />,
|
||||||
|
}}
|
||||||
|
remarkPlugins={[remarkGfm]}
|
||||||
|
>
|
||||||
|
{settings.enterpriseSettings.custom_header_content}
|
||||||
|
</ReactMarkdown>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
@ -72,6 +72,7 @@ import { useChatContext } from "@/components/context/ChatContext";
|
|||||||
import { UserDropdown } from "@/components/UserDropdown";
|
import { UserDropdown } from "@/components/UserDropdown";
|
||||||
import { v4 as uuidv4 } from "uuid";
|
import { v4 as uuidv4 } from "uuid";
|
||||||
import { orderAssistantsForUser } from "@/lib/assistants/orderAssistants";
|
import { orderAssistantsForUser } from "@/lib/assistants/orderAssistants";
|
||||||
|
import { ChatPopup } from "./ChatPopup";
|
||||||
|
|
||||||
const MAX_INPUT_HEIGHT = 200;
|
const MAX_INPUT_HEIGHT = 200;
|
||||||
const TEMP_USER_MESSAGE_ID = -1;
|
const TEMP_USER_MESSAGE_ID = -1;
|
||||||
@ -872,6 +873,10 @@ export function ChatPage({
|
|||||||
<HealthCheckBanner />
|
<HealthCheckBanner />
|
||||||
<InstantSSRAutoRefresh />
|
<InstantSSRAutoRefresh />
|
||||||
|
|
||||||
|
{/* ChatPopup is a custom popup that displays a admin-specified message on initial user visit.
|
||||||
|
Only used in the EE version of the app. */}
|
||||||
|
<ChatPopup />
|
||||||
|
|
||||||
<div className="flex relative bg-background text-default overflow-x-hidden">
|
<div className="flex relative bg-background text-default overflow-x-hidden">
|
||||||
<ChatSidebar
|
<ChatSidebar
|
||||||
existingChats={chatSessions}
|
existingChats={chatSessions}
|
||||||
|
73
web/src/app/chat/ChatPopup.tsx
Normal file
73
web/src/app/chat/ChatPopup.tsx
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { Modal } from "@/components/Modal";
|
||||||
|
import { SettingsContext } from "@/components/settings/SettingsProviderClientSideHelper";
|
||||||
|
import { Button } from "@tremor/react";
|
||||||
|
import { useContext, useEffect, useState } from "react";
|
||||||
|
import ReactMarkdown from "react-markdown";
|
||||||
|
import remarkGfm from "remark-gfm";
|
||||||
|
|
||||||
|
const ALL_USERS_INITIAL_POPUP_FLOW_COMPLETED =
|
||||||
|
"allUsersInitialPopupFlowCompleted";
|
||||||
|
|
||||||
|
export function ChatPopup() {
|
||||||
|
const [completedFlow, setCompletedFlow] = useState(true);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setCompletedFlow(
|
||||||
|
localStorage.getItem(ALL_USERS_INITIAL_POPUP_FLOW_COMPLETED) === "true"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
const settings = useContext(SettingsContext);
|
||||||
|
if (!settings?.enterpriseSettings?.custom_popup_content || completedFlow) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
let popupTitle = settings.enterpriseSettings.custom_popup_header;
|
||||||
|
if (!popupTitle) {
|
||||||
|
popupTitle = `Welcome to ${
|
||||||
|
settings.enterpriseSettings.application_name || "Danswer"
|
||||||
|
}!`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal width="w-3/6 xl:w-[700px]" title={popupTitle}>
|
||||||
|
<>
|
||||||
|
<ReactMarkdown
|
||||||
|
className="prose max-w-full"
|
||||||
|
components={{
|
||||||
|
a: ({ node, ...props }) => (
|
||||||
|
<a
|
||||||
|
{...props}
|
||||||
|
className="text-link hover:text-link-hover"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
p: ({ node, ...props }) => <p {...props} className="text-sm" />,
|
||||||
|
}}
|
||||||
|
remarkPlugins={[remarkGfm]}
|
||||||
|
>
|
||||||
|
{settings.enterpriseSettings.custom_popup_content}
|
||||||
|
</ReactMarkdown>
|
||||||
|
|
||||||
|
<div className="flex w-full">
|
||||||
|
<Button
|
||||||
|
className="mx-auto mt-4"
|
||||||
|
size="xs"
|
||||||
|
onClick={() => {
|
||||||
|
localStorage.setItem(
|
||||||
|
ALL_USERS_INITIAL_POPUP_FLOW_COMPLETED,
|
||||||
|
"true"
|
||||||
|
);
|
||||||
|
setCompletedFlow(true);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Get started!
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
@ -7,8 +7,8 @@ import { SelectedDocumentDisplay } from "./SelectedDocumentDisplay";
|
|||||||
import { removeDuplicateDocs } from "@/lib/documentUtils";
|
import { removeDuplicateDocs } from "@/lib/documentUtils";
|
||||||
import { BasicSelectable } from "@/components/BasicClickable";
|
import { BasicSelectable } from "@/components/BasicClickable";
|
||||||
import { Message, RetrievalType } from "../interfaces";
|
import { Message, RetrievalType } from "../interfaces";
|
||||||
import { HEADER_PADDING } from "@/lib/constants";
|
|
||||||
import { HoverPopup } from "@/components/HoverPopup";
|
import { HoverPopup } from "@/components/HoverPopup";
|
||||||
|
import { HEADER_PADDING } from "@/lib/constants";
|
||||||
|
|
||||||
function SectionHeader({
|
function SectionHeader({
|
||||||
name,
|
name,
|
||||||
|
@ -11,7 +11,7 @@ import {
|
|||||||
SubLabel,
|
SubLabel,
|
||||||
TextFormField,
|
TextFormField,
|
||||||
} from "@/components/admin/connectors/Field";
|
} from "@/components/admin/connectors/Field";
|
||||||
import { Button } from "@tremor/react";
|
import { Button, Divider } from "@tremor/react";
|
||||||
import { ImageUpload } from "./ImageUpload";
|
import { ImageUpload } from "./ImageUpload";
|
||||||
|
|
||||||
export function WhitelabelingForm() {
|
export function WhitelabelingForm() {
|
||||||
@ -42,7 +42,6 @@ export function WhitelabelingForm() {
|
|||||||
alert(`Failed to update settings. ${errorMsg}`);
|
alert(`Failed to update settings. ${errorMsg}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
console.log(enterpriseSettings);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
@ -50,10 +49,17 @@ export function WhitelabelingForm() {
|
|||||||
initialValues={{
|
initialValues={{
|
||||||
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,
|
||||||
|
custom_header_content:
|
||||||
|
enterpriseSettings?.custom_header_content || "",
|
||||||
|
custom_popup_header: enterpriseSettings?.custom_popup_header || "",
|
||||||
|
custom_popup_content: enterpriseSettings?.custom_popup_content || "",
|
||||||
}}
|
}}
|
||||||
validationSchema={Yup.object().shape({
|
validationSchema={Yup.object().shape({
|
||||||
application_name: Yup.string(),
|
application_name: Yup.string().nullable(),
|
||||||
use_custom_logo: Yup.boolean().required(),
|
use_custom_logo: Yup.boolean().required(),
|
||||||
|
custom_header_content: Yup.string().nullable(),
|
||||||
|
custom_popup_header: Yup.string().nullable(),
|
||||||
|
custom_popup_content: Yup.string().nullable(),
|
||||||
})}
|
})}
|
||||||
onSubmit={async (values, formikHelpers) => {
|
onSubmit={async (values, formikHelpers) => {
|
||||||
formikHelpers.setSubmitting(true);
|
formikHelpers.setSubmitting(true);
|
||||||
@ -138,6 +144,44 @@ export function WhitelabelingForm() {
|
|||||||
setSelectedFile={setSelectedFile}
|
setSelectedFile={setSelectedFile}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<Divider />
|
||||||
|
|
||||||
|
<div className="mt-4">
|
||||||
|
<TextFormField
|
||||||
|
label="Custom Chat Header Content"
|
||||||
|
name="custom_header_content"
|
||||||
|
subtext={`Custom Markdown content that will be displayed as a banner at the top of the Chat page.`}
|
||||||
|
placeholder="Your header content..."
|
||||||
|
disabled={isSubmitting}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Divider />
|
||||||
|
|
||||||
|
<div className="mt-4">
|
||||||
|
<TextFormField
|
||||||
|
label="Custom Popup Header"
|
||||||
|
name="custom_popup_header"
|
||||||
|
subtext={`The title for the popup that will be displayed for each user on their initial visit
|
||||||
|
to the application. If left blank AND Custom Popup Content is specified, will use "Welcome to ${
|
||||||
|
values.application_name || "Danswer"
|
||||||
|
}!".`}
|
||||||
|
placeholder="Initial Popup Header"
|
||||||
|
disabled={isSubmitting}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-4">
|
||||||
|
<TextFormField
|
||||||
|
label="Custom Popup Content"
|
||||||
|
name="custom_popup_content"
|
||||||
|
subtext={`Custom Markdown content that will be displayed as a popup on initial visit to the application.`}
|
||||||
|
placeholder="Your popup content..."
|
||||||
|
isTextArea
|
||||||
|
disabled={isSubmitting}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<Button type="submit" className="mt-4">
|
<Button type="submit" className="mt-4">
|
||||||
Update
|
Update
|
||||||
</Button>
|
</Button>
|
||||||
|
@ -23,6 +23,7 @@ import { personaComparator } from "../admin/assistants/lib";
|
|||||||
import { FullEmbeddingModelResponse } from "../admin/models/embedding/embeddingModels";
|
import { FullEmbeddingModelResponse } from "../admin/models/embedding/embeddingModels";
|
||||||
import { NoSourcesModal } from "@/components/initialSetup/search/NoSourcesModal";
|
import { NoSourcesModal } from "@/components/initialSetup/search/NoSourcesModal";
|
||||||
import { NoCompleteSourcesModal } from "@/components/initialSetup/search/NoCompleteSourceModal";
|
import { NoCompleteSourcesModal } from "@/components/initialSetup/search/NoCompleteSourceModal";
|
||||||
|
import { ChatPopup } from "../chat/ChatPopup";
|
||||||
|
|
||||||
export default async function Home() {
|
export default async function Home() {
|
||||||
// Disable caching so we always get the up to date connector / document set / persona info
|
// Disable caching so we always get the up to date connector / document set / persona info
|
||||||
@ -163,6 +164,10 @@ export default async function Home() {
|
|||||||
<NoCompleteSourcesModal ccPairs={ccPairs} />
|
<NoCompleteSourcesModal ccPairs={ccPairs} />
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* ChatPopup is a custom popup that displays a admin-specified message on initial user visit.
|
||||||
|
Only used in the EE version of the app. */}
|
||||||
|
<ChatPopup />
|
||||||
|
|
||||||
<InstantSSRAutoRefresh />
|
<InstantSSRAutoRefresh />
|
||||||
|
|
||||||
<div className="px-24 pt-10 flex flex-col items-center min-h-screen">
|
<div className="px-24 pt-10 flex flex-col items-center min-h-screen">
|
||||||
|
@ -15,7 +15,7 @@ export const GOOGLE_DRIVE_AUTH_IS_ADMIN_COOKIE_NAME =
|
|||||||
|
|
||||||
export const SEARCH_TYPE_COOKIE_NAME = "search_type";
|
export const SEARCH_TYPE_COOKIE_NAME = "search_type";
|
||||||
|
|
||||||
export const HEADER_PADDING = "pt-[64px]";
|
export const HEADER_PADDING = `pt-[64px]`;
|
||||||
|
|
||||||
export const LOGOUT_DISABLED =
|
export const LOGOUT_DISABLED =
|
||||||
process.env.NEXT_PUBLIC_DISABLE_LOGOUT?.toLowerCase() === "true";
|
process.env.NEXT_PUBLIC_DISABLE_LOGOUT?.toLowerCase() === "true";
|
||||||
|
@ -42,9 +42,11 @@ module.exports = {
|
|||||||
"background-emphasis": "#f6f7f8",
|
"background-emphasis": "#f6f7f8",
|
||||||
"background-strong": "#eaecef",
|
"background-strong": "#eaecef",
|
||||||
"background-search": "#ffffff",
|
"background-search": "#ffffff",
|
||||||
|
"background-custom-header": "#f3f4f6",
|
||||||
|
|
||||||
// text or icons
|
// text or icons
|
||||||
link: "#3b82f6", // blue-500
|
link: "#3b82f6", // blue-500
|
||||||
|
"link-hover": "#1d4ed8", // blue-700
|
||||||
subtle: "#6b7280", // gray-500
|
subtle: "#6b7280", // gray-500
|
||||||
default: "#4b5563", // gray-600
|
default: "#4b5563", // gray-600
|
||||||
emphasis: "#374151", // gray-700
|
emphasis: "#374151", // gray-700
|
||||||
|
Loading…
x
Reference in New Issue
Block a user