mirror of
https://github.com/danswer-ai/danswer.git
synced 2025-06-15 02:20:52 +02:00
a (#2815)
This commit is contained in:
parent
da3c5e3711
commit
a1bfa7847a
@ -140,7 +140,33 @@ export function ChatPage({
|
|||||||
const { user, isAdmin, isLoadingUser } = useUser();
|
const { user, isAdmin, isLoadingUser } = useUser();
|
||||||
|
|
||||||
const existingChatIdRaw = searchParams.get("chatId");
|
const existingChatIdRaw = searchParams.get("chatId");
|
||||||
|
const [sendOnLoad, setSendOnLoad] = useState<string | null>(
|
||||||
|
searchParams.get(SEARCH_PARAM_NAMES.SEND_ON_LOAD)
|
||||||
|
);
|
||||||
|
|
||||||
const currentPersonaId = searchParams.get(SEARCH_PARAM_NAMES.PERSONA_ID);
|
const currentPersonaId = searchParams.get(SEARCH_PARAM_NAMES.PERSONA_ID);
|
||||||
|
const modelVersionFromSearchParams = searchParams.get(
|
||||||
|
SEARCH_PARAM_NAMES.STRUCTURED_MODEL
|
||||||
|
);
|
||||||
|
|
||||||
|
// Effect to handle sendOnLoad
|
||||||
|
useEffect(() => {
|
||||||
|
if (sendOnLoad) {
|
||||||
|
const newSearchParams = new URLSearchParams(searchParams.toString());
|
||||||
|
newSearchParams.delete(SEARCH_PARAM_NAMES.SEND_ON_LOAD);
|
||||||
|
|
||||||
|
// Update the URL without the send-on-load parameter
|
||||||
|
router.replace(`?${newSearchParams.toString()}`, { scroll: false });
|
||||||
|
|
||||||
|
// Update our local state to reflect the change
|
||||||
|
setSendOnLoad(null);
|
||||||
|
|
||||||
|
// If there's a message, submit it
|
||||||
|
if (message) {
|
||||||
|
onSubmit({ messageOverride: message });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [sendOnLoad, searchParams, router]);
|
||||||
|
|
||||||
const existingChatSessionId = existingChatIdRaw ? existingChatIdRaw : null;
|
const existingChatSessionId = existingChatIdRaw ? existingChatIdRaw : null;
|
||||||
|
|
||||||
@ -196,7 +222,7 @@ export function ChatPage({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const llmOverrideManager = useLlmOverride(
|
const llmOverrideManager = useLlmOverride(
|
||||||
user?.preferences.default_model ?? null,
|
modelVersionFromSearchParams || (user?.preferences.default_model ?? null),
|
||||||
selectedChatSession,
|
selectedChatSession,
|
||||||
defaultTemperature
|
defaultTemperature
|
||||||
);
|
);
|
||||||
@ -1853,6 +1879,9 @@ export function ChatPage({
|
|||||||
|
|
||||||
{sharedChatSession && (
|
{sharedChatSession && (
|
||||||
<ShareChatSessionModal
|
<ShareChatSessionModal
|
||||||
|
assistantId={liveAssistant?.id}
|
||||||
|
message={message}
|
||||||
|
modelOverride={llmOverrideManager.llmOverride}
|
||||||
chatSessionId={sharedChatSession.id}
|
chatSessionId={sharedChatSession.id}
|
||||||
existingSharedStatus={sharedChatSession.shared_status}
|
existingSharedStatus={sharedChatSession.shared_status}
|
||||||
onClose={() => setSharedChatSession(null)}
|
onClose={() => setSharedChatSession(null)}
|
||||||
@ -1867,6 +1896,9 @@ export function ChatPage({
|
|||||||
)}
|
)}
|
||||||
{sharingModalVisible && chatSessionIdRef.current !== null && (
|
{sharingModalVisible && chatSessionIdRef.current !== null && (
|
||||||
<ShareChatSessionModal
|
<ShareChatSessionModal
|
||||||
|
message={message}
|
||||||
|
assistantId={liveAssistant?.id}
|
||||||
|
modelOverride={llmOverrideManager.llmOverride}
|
||||||
chatSessionId={chatSessionIdRef.current}
|
chatSessionId={chatSessionIdRef.current}
|
||||||
existingSharedStatus={chatSessionSharedStatus}
|
existingSharedStatus={chatSessionSharedStatus}
|
||||||
onClose={() => setSharingModalVisible(false)}
|
onClose={() => setSharingModalVisible(false)}
|
||||||
|
@ -5,6 +5,10 @@ import { Spinner } from "@/components/Spinner";
|
|||||||
import { ChatSessionSharedStatus } from "../interfaces";
|
import { ChatSessionSharedStatus } from "../interfaces";
|
||||||
import { FiCopy } from "react-icons/fi";
|
import { FiCopy } from "react-icons/fi";
|
||||||
import { CopyButton } from "@/components/CopyButton";
|
import { CopyButton } from "@/components/CopyButton";
|
||||||
|
import { SEARCH_PARAM_NAMES } from "../searchParams";
|
||||||
|
import { usePopup } from "@/components/admin/connectors/Popup";
|
||||||
|
import { destructureValue, structureValue } from "@/lib/llm/utils";
|
||||||
|
import { LlmOverride } from "@/lib/hooks";
|
||||||
|
|
||||||
function buildShareLink(chatSessionId: string) {
|
function buildShareLink(chatSessionId: string) {
|
||||||
const baseUrl = `${window.location.protocol}//${window.location.host}`;
|
const baseUrl = `${window.location.protocol}//${window.location.host}`;
|
||||||
@ -26,6 +30,34 @@ async function generateShareLink(chatSessionId: string) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function generateCloneLink(
|
||||||
|
message?: string,
|
||||||
|
assistantId?: number,
|
||||||
|
modelOverride?: LlmOverride
|
||||||
|
) {
|
||||||
|
const baseUrl = `${window.location.protocol}//${window.location.host}`;
|
||||||
|
const model = modelOverride
|
||||||
|
? structureValue(
|
||||||
|
modelOverride.name,
|
||||||
|
modelOverride.provider,
|
||||||
|
modelOverride.modelName
|
||||||
|
)
|
||||||
|
: null;
|
||||||
|
return `${baseUrl}/chat${
|
||||||
|
message
|
||||||
|
? `?${SEARCH_PARAM_NAMES.USER_PROMPT}=${encodeURIComponent(message)}`
|
||||||
|
: ""
|
||||||
|
}${
|
||||||
|
assistantId
|
||||||
|
? `${message ? "&" : "?"}${SEARCH_PARAM_NAMES.PERSONA_ID}=${assistantId}`
|
||||||
|
: ""
|
||||||
|
}${
|
||||||
|
model
|
||||||
|
? `${message || assistantId ? "&" : "?"}${SEARCH_PARAM_NAMES.STRUCTURED_MODEL}=${encodeURIComponent(model)}`
|
||||||
|
: ""
|
||||||
|
}${message ? `&${SEARCH_PARAM_NAMES.SEND_ON_LOAD}=true` : ""}`;
|
||||||
|
}
|
||||||
|
|
||||||
async function deleteShareLink(chatSessionId: string) {
|
async function deleteShareLink(chatSessionId: string) {
|
||||||
const response = await fetch(`/api/chat/chat-session/${chatSessionId}`, {
|
const response = await fetch(`/api/chat/chat-session/${chatSessionId}`, {
|
||||||
method: "PATCH",
|
method: "PATCH",
|
||||||
@ -43,117 +75,162 @@ export function ShareChatSessionModal({
|
|||||||
existingSharedStatus,
|
existingSharedStatus,
|
||||||
onShare,
|
onShare,
|
||||||
onClose,
|
onClose,
|
||||||
|
message,
|
||||||
|
assistantId,
|
||||||
|
modelOverride,
|
||||||
}: {
|
}: {
|
||||||
chatSessionId: string;
|
chatSessionId: string;
|
||||||
existingSharedStatus: ChatSessionSharedStatus;
|
existingSharedStatus: ChatSessionSharedStatus;
|
||||||
onShare?: (shared: boolean) => void;
|
onShare?: (shared: boolean) => void;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
|
message?: string;
|
||||||
|
assistantId?: number;
|
||||||
|
modelOverride?: LlmOverride;
|
||||||
}) {
|
}) {
|
||||||
const [linkGenerating, setLinkGenerating] = useState(false);
|
|
||||||
const [shareLink, setShareLink] = useState<string>(
|
const [shareLink, setShareLink] = useState<string>(
|
||||||
existingSharedStatus === ChatSessionSharedStatus.Public
|
existingSharedStatus === ChatSessionSharedStatus.Public
|
||||||
? buildShareLink(chatSessionId)
|
? buildShareLink(chatSessionId)
|
||||||
: ""
|
: ""
|
||||||
);
|
);
|
||||||
|
const { popup, setPopup } = usePopup();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal onOutsideClick={onClose} width="max-w-3xl">
|
<>
|
||||||
<>
|
{popup}
|
||||||
<div className="flex mb-4">
|
<Modal onOutsideClick={onClose} width="max-w-3xl">
|
||||||
<h2 className="text-2xl text-emphasis font-bold flex my-auto">
|
<>
|
||||||
Share link to Chat
|
<div className="flex mb-4">
|
||||||
</h2>
|
<h2 className="text-2xl text-emphasis font-bold flex my-auto">
|
||||||
</div>
|
Share link to Chat
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
{linkGenerating && <Spinner />}
|
<div className="flex mt-2">
|
||||||
|
{shareLink ? (
|
||||||
|
<div>
|
||||||
|
<Text>
|
||||||
|
This chat session is currently shared. Anyone at your
|
||||||
|
organization can view the message history using the following
|
||||||
|
link:
|
||||||
|
</Text>
|
||||||
|
|
||||||
<div className="flex mt-2">
|
<div className="flex mt-2">
|
||||||
{shareLink ? (
|
<CopyButton content={shareLink} />
|
||||||
<div>
|
<a
|
||||||
<Text>
|
href={shareLink}
|
||||||
This chat session is currently shared. Anyone at your
|
target="_blank"
|
||||||
organization can view the message history using the following
|
className="underline text-link mt-1 ml-1 text-sm my-auto"
|
||||||
link:
|
rel="noreferrer"
|
||||||
</Text>
|
>
|
||||||
|
{shareLink}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="flex mt-2">
|
<Divider />
|
||||||
<CopyButton content={shareLink} />
|
|
||||||
<a
|
|
||||||
href={shareLink}
|
|
||||||
target="_blank"
|
|
||||||
className="underline text-link mt-1 ml-1 text-sm my-auto"
|
|
||||||
rel="noreferrer"
|
|
||||||
>
|
|
||||||
{shareLink}
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Divider />
|
<Text className="mb-4">
|
||||||
|
Click the button below to make the chat private again.
|
||||||
|
</Text>
|
||||||
|
|
||||||
<Text className="mb-4">
|
<Button
|
||||||
Click the button below to make the chat private again.
|
onClick={async () => {
|
||||||
</Text>
|
const success = await deleteShareLink(chatSessionId);
|
||||||
|
if (success) {
|
||||||
<Button
|
setShareLink("");
|
||||||
onClick={async () => {
|
onShare && onShare(false);
|
||||||
setLinkGenerating(true);
|
|
||||||
|
|
||||||
const success = await deleteShareLink(chatSessionId);
|
|
||||||
if (success) {
|
|
||||||
setShareLink("");
|
|
||||||
onShare && onShare(false);
|
|
||||||
} else {
|
|
||||||
alert("Failed to delete share link");
|
|
||||||
}
|
|
||||||
|
|
||||||
setLinkGenerating(false);
|
|
||||||
}}
|
|
||||||
size="xs"
|
|
||||||
color="red"
|
|
||||||
>
|
|
||||||
Delete Share Link
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div>
|
|
||||||
<Callout title="Warning" color="yellow" className="mb-4">
|
|
||||||
Ensure that all content in the chat is safe to share with the
|
|
||||||
whole organization. The content of the retrieved documents will
|
|
||||||
not be visible, but the names of cited documents as well as the
|
|
||||||
AI and human messages will be visible.
|
|
||||||
</Callout>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
icon={FiCopy}
|
|
||||||
onClick={async () => {
|
|
||||||
setLinkGenerating(true);
|
|
||||||
|
|
||||||
// NOTE: for "inescure" non-https setup, the `navigator.clipboard.writeText` may fail
|
|
||||||
// as the browser may not allow the clipboard to be accessed.
|
|
||||||
try {
|
|
||||||
const shareLink = await generateShareLink(chatSessionId);
|
|
||||||
if (!shareLink) {
|
|
||||||
alert("Failed to generate share link");
|
|
||||||
} else {
|
} else {
|
||||||
setShareLink(shareLink);
|
alert("Failed to delete share link");
|
||||||
onShare && onShare(true);
|
|
||||||
navigator.clipboard.writeText(shareLink);
|
|
||||||
}
|
}
|
||||||
} catch (e) {
|
}}
|
||||||
console.error(e);
|
size="xs"
|
||||||
}
|
color="red"
|
||||||
|
>
|
||||||
|
Delete Share Link
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div>
|
||||||
|
<Callout title="Warning" color="yellow" className="mb-4">
|
||||||
|
Ensure that all content in the chat is safe to share with the
|
||||||
|
whole organization. The content of the retrieved documents
|
||||||
|
will not be visible, but the names of cited documents as well
|
||||||
|
as the AI and human messages will be visible.
|
||||||
|
</Callout>
|
||||||
|
<div className="flex w-full justify-between">
|
||||||
|
<Button
|
||||||
|
icon={FiCopy}
|
||||||
|
onClick={async () => {
|
||||||
|
// NOTE: for "insecure" non-https setup, the `navigator.clipboard.writeText` may fail
|
||||||
|
// as the browser may not allow the clipboard to be accessed.
|
||||||
|
try {
|
||||||
|
const shareLink =
|
||||||
|
await generateShareLink(chatSessionId);
|
||||||
|
if (!shareLink) {
|
||||||
|
alert("Failed to generate share link");
|
||||||
|
} else {
|
||||||
|
setShareLink(shareLink);
|
||||||
|
onShare && onShare(true);
|
||||||
|
navigator.clipboard.writeText(shareLink);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
size="xs"
|
||||||
|
color="green"
|
||||||
|
>
|
||||||
|
Generate and Copy Share Link
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
setLinkGenerating(false);
|
<Divider className="my-4" />
|
||||||
}}
|
<div className="mb-4">
|
||||||
size="xs"
|
<Callout title="Clone Chat" color="blue">
|
||||||
color="green"
|
Generate a link to clone this chat session with the current query.
|
||||||
>
|
This allows others to start a new chat with the same initial
|
||||||
Generate and Copy Share Link
|
message and settings.
|
||||||
</Button>
|
</Callout>
|
||||||
</div>
|
</div>
|
||||||
)}
|
<div className="flex w-full justify-between">
|
||||||
</div>
|
<Button
|
||||||
</>
|
icon={FiCopy}
|
||||||
</Modal>
|
onClick={async () => {
|
||||||
|
// NOTE: for "insecure" non-https setup, the `navigator.clipboard.writeText` may fail
|
||||||
|
// as the browser may not allow the clipboard to be accessed.
|
||||||
|
try {
|
||||||
|
const cloneLink = await generateCloneLink(
|
||||||
|
message,
|
||||||
|
assistantId,
|
||||||
|
modelOverride
|
||||||
|
);
|
||||||
|
if (!cloneLink) {
|
||||||
|
setPopup({
|
||||||
|
message: "Failed to generate clone link",
|
||||||
|
type: "error",
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
navigator.clipboard.writeText(cloneLink);
|
||||||
|
setPopup({
|
||||||
|
message: "Link copied to clipboard!",
|
||||||
|
type: "success",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
alert("Failed to generate or copy link.");
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
size="xs"
|
||||||
|
color="blue"
|
||||||
|
>
|
||||||
|
Generate and Copy Clone Link
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
</Modal>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ export const SEARCH_PARAM_NAMES = {
|
|||||||
TEMPERATURE: "temperature",
|
TEMPERATURE: "temperature",
|
||||||
MODEL_VERSION: "model-version",
|
MODEL_VERSION: "model-version",
|
||||||
SYSTEM_PROMPT: "system-prompt",
|
SYSTEM_PROMPT: "system-prompt",
|
||||||
|
STRUCTURED_MODEL: "structured-model",
|
||||||
// user message
|
// user message
|
||||||
USER_PROMPT: "user-prompt",
|
USER_PROMPT: "user-prompt",
|
||||||
SUBMIT_ON_LOAD: "submit-on-load",
|
SUBMIT_ON_LOAD: "submit-on-load",
|
||||||
@ -16,6 +17,7 @@ export const SEARCH_PARAM_NAMES = {
|
|||||||
TITLE: "title",
|
TITLE: "title",
|
||||||
// for seeding chats
|
// for seeding chats
|
||||||
SEEDED: "seeded",
|
SEEDED: "seeded",
|
||||||
|
SEND_ON_LOAD: "send-on-load",
|
||||||
};
|
};
|
||||||
|
|
||||||
export function shouldSubmitOnLoad(searchParams: ReadonlyURLSearchParams) {
|
export function shouldSubmitOnLoad(searchParams: ReadonlyURLSearchParams) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user