mirror of
https://github.com/danswer-ai/danswer.git
synced 2025-06-13 01:20:51 +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 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 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;
|
||||
|
||||
@ -196,7 +222,7 @@ export function ChatPage({
|
||||
};
|
||||
|
||||
const llmOverrideManager = useLlmOverride(
|
||||
user?.preferences.default_model ?? null,
|
||||
modelVersionFromSearchParams || (user?.preferences.default_model ?? null),
|
||||
selectedChatSession,
|
||||
defaultTemperature
|
||||
);
|
||||
@ -1853,6 +1879,9 @@ export function ChatPage({
|
||||
|
||||
{sharedChatSession && (
|
||||
<ShareChatSessionModal
|
||||
assistantId={liveAssistant?.id}
|
||||
message={message}
|
||||
modelOverride={llmOverrideManager.llmOverride}
|
||||
chatSessionId={sharedChatSession.id}
|
||||
existingSharedStatus={sharedChatSession.shared_status}
|
||||
onClose={() => setSharedChatSession(null)}
|
||||
@ -1867,6 +1896,9 @@ export function ChatPage({
|
||||
)}
|
||||
{sharingModalVisible && chatSessionIdRef.current !== null && (
|
||||
<ShareChatSessionModal
|
||||
message={message}
|
||||
assistantId={liveAssistant?.id}
|
||||
modelOverride={llmOverrideManager.llmOverride}
|
||||
chatSessionId={chatSessionIdRef.current}
|
||||
existingSharedStatus={chatSessionSharedStatus}
|
||||
onClose={() => setSharingModalVisible(false)}
|
||||
|
@ -5,6 +5,10 @@ import { Spinner } from "@/components/Spinner";
|
||||
import { ChatSessionSharedStatus } from "../interfaces";
|
||||
import { FiCopy } from "react-icons/fi";
|
||||
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) {
|
||||
const baseUrl = `${window.location.protocol}//${window.location.host}`;
|
||||
@ -26,6 +30,34 @@ async function generateShareLink(chatSessionId: string) {
|
||||
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) {
|
||||
const response = await fetch(`/api/chat/chat-session/${chatSessionId}`, {
|
||||
method: "PATCH",
|
||||
@ -43,117 +75,162 @@ export function ShareChatSessionModal({
|
||||
existingSharedStatus,
|
||||
onShare,
|
||||
onClose,
|
||||
message,
|
||||
assistantId,
|
||||
modelOverride,
|
||||
}: {
|
||||
chatSessionId: string;
|
||||
existingSharedStatus: ChatSessionSharedStatus;
|
||||
onShare?: (shared: boolean) => void;
|
||||
onClose: () => void;
|
||||
message?: string;
|
||||
assistantId?: number;
|
||||
modelOverride?: LlmOverride;
|
||||
}) {
|
||||
const [linkGenerating, setLinkGenerating] = useState(false);
|
||||
const [shareLink, setShareLink] = useState<string>(
|
||||
existingSharedStatus === ChatSessionSharedStatus.Public
|
||||
? buildShareLink(chatSessionId)
|
||||
: ""
|
||||
);
|
||||
const { popup, setPopup } = usePopup();
|
||||
|
||||
return (
|
||||
<Modal onOutsideClick={onClose} width="max-w-3xl">
|
||||
<>
|
||||
<div className="flex mb-4">
|
||||
<h2 className="text-2xl text-emphasis font-bold flex my-auto">
|
||||
Share link to Chat
|
||||
</h2>
|
||||
</div>
|
||||
<>
|
||||
{popup}
|
||||
<Modal onOutsideClick={onClose} width="max-w-3xl">
|
||||
<>
|
||||
<div className="flex mb-4">
|
||||
<h2 className="text-2xl text-emphasis font-bold flex my-auto">
|
||||
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">
|
||||
{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">
|
||||
<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>
|
||||
|
||||
<div className="flex mt-2">
|
||||
<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 />
|
||||
|
||||
<Divider />
|
||||
<Text className="mb-4">
|
||||
Click the button below to make the chat private again.
|
||||
</Text>
|
||||
|
||||
<Text className="mb-4">
|
||||
Click the button below to make the chat private again.
|
||||
</Text>
|
||||
|
||||
<Button
|
||||
onClick={async () => {
|
||||
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");
|
||||
<Button
|
||||
onClick={async () => {
|
||||
const success = await deleteShareLink(chatSessionId);
|
||||
if (success) {
|
||||
setShareLink("");
|
||||
onShare && onShare(false);
|
||||
} else {
|
||||
setShareLink(shareLink);
|
||||
onShare && onShare(true);
|
||||
navigator.clipboard.writeText(shareLink);
|
||||
alert("Failed to delete share link");
|
||||
}
|
||||
} 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);
|
||||
}}
|
||||
size="xs"
|
||||
color="green"
|
||||
>
|
||||
Generate and Copy Share Link
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
</Modal>
|
||||
<Divider className="my-4" />
|
||||
<div className="mb-4">
|
||||
<Callout title="Clone Chat" color="blue">
|
||||
Generate a link to clone this chat session with the current query.
|
||||
This allows others to start a new chat with the same initial
|
||||
message and settings.
|
||||
</Callout>
|
||||
</div>
|
||||
<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 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",
|
||||
MODEL_VERSION: "model-version",
|
||||
SYSTEM_PROMPT: "system-prompt",
|
||||
STRUCTURED_MODEL: "structured-model",
|
||||
// user message
|
||||
USER_PROMPT: "user-prompt",
|
||||
SUBMIT_ON_LOAD: "submit-on-load",
|
||||
@ -16,6 +17,7 @@ export const SEARCH_PARAM_NAMES = {
|
||||
TITLE: "title",
|
||||
// for seeding chats
|
||||
SEEDED: "seeded",
|
||||
SEND_ON_LOAD: "send-on-load",
|
||||
};
|
||||
|
||||
export function shouldSubmitOnLoad(searchParams: ReadonlyURLSearchParams) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user