mirror of
https://github.com/danswer-ai/danswer.git
synced 2025-09-26 20:08:38 +02:00
update persona defaults (#3042)
* evaluate None to default * fix usage report pagination * update persona defaults * update user preferences * k * validate * update typing * nit * formating nits * fallback to all assistants * update ux + spacing * udpate refresh logic * minor update to refresh * nit * touchup * update starter message * update default live assistant logic --------- Co-authored-by: Yuhong Sun <yuhongsun96@gmail.com>
This commit is contained in:
@@ -246,9 +246,6 @@ export function AssistantEditor({
|
||||
name: Yup.string().required(
|
||||
"Each starter message must have a name"
|
||||
),
|
||||
description: Yup.string().required(
|
||||
"Each starter message must have a description"
|
||||
),
|
||||
message: Yup.string().required(
|
||||
"Each starter message must have a message"
|
||||
),
|
||||
@@ -1053,36 +1050,6 @@ export function AssistantEditor({
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="mt-3">
|
||||
<Label small>Description</Label>
|
||||
<SubLabel>
|
||||
A description which tells the user
|
||||
what they might want to use this
|
||||
Starter Message for. For example
|
||||
"to a client about a new
|
||||
feature"
|
||||
</SubLabel>
|
||||
<Field
|
||||
name={`starter_messages.${index}.description`}
|
||||
className={`
|
||||
border
|
||||
border-border
|
||||
bg-background
|
||||
rounded
|
||||
w-full
|
||||
py-2
|
||||
px-3
|
||||
mr-4
|
||||
`}
|
||||
autoComplete="off"
|
||||
/>
|
||||
<ErrorMessage
|
||||
name={`starter_messages[${index}].description`}
|
||||
component="div"
|
||||
className="text-error text-sm mt-1"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="mt-3">
|
||||
<Label small>Message</Label>
|
||||
<SubLabel>
|
||||
|
@@ -3,7 +3,6 @@ import { DocumentSet, MinimalUserSnapshot } from "@/lib/types";
|
||||
|
||||
export interface StarterMessage {
|
||||
name: string;
|
||||
description: string | null;
|
||||
message: string;
|
||||
}
|
||||
|
||||
|
@@ -191,19 +191,15 @@ const DocumentSetTable = ({
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
{documentSet.is_up_to_date ? (
|
||||
<Badge size="md" variant="success" icon={FiCheckCircle}>
|
||||
<Badge variant="success" icon={FiCheckCircle}>
|
||||
Up to Date
|
||||
</Badge>
|
||||
) : documentSet.cc_pair_descriptors.length > 0 ? (
|
||||
<Badge size="md" variant="in_progress" icon={FiClock}>
|
||||
<Badge variant="in_progress" icon={FiClock}>
|
||||
Syncing
|
||||
</Badge>
|
||||
) : (
|
||||
<Badge
|
||||
size="md"
|
||||
variant="destructive"
|
||||
icon={FiAlertTriangle}
|
||||
>
|
||||
<Badge variant="destructive" icon={FiAlertTriangle}>
|
||||
Deleting
|
||||
</Badge>
|
||||
)}
|
||||
@@ -211,7 +207,6 @@ const DocumentSetTable = ({
|
||||
<TableCell>
|
||||
{documentSet.is_public ? (
|
||||
<Badge
|
||||
size="md"
|
||||
variant={isEditable ? "success" : "default"}
|
||||
icon={FiUnlock}
|
||||
>
|
||||
@@ -219,7 +214,6 @@ const DocumentSetTable = ({
|
||||
</Badge>
|
||||
) : (
|
||||
<Badge
|
||||
size="md"
|
||||
variant={isEditable ? "in_progress" : "outline"}
|
||||
icon={FiLock}
|
||||
>
|
||||
|
@@ -200,8 +200,8 @@ function ConnectorRow({
|
||||
router.push(`/admin/connector/${ccPairsIndexingStatus.cc_pair_id}`);
|
||||
}}
|
||||
>
|
||||
<TableCell className="!w-[300px]">
|
||||
<p className="w-[200px] xl:w-[400px] inline-block ellipsis truncate">
|
||||
<TableCell className="">
|
||||
<p className="lg:w-[200px] xl:w-[400px] inline-block ellipsis truncate">
|
||||
{ccPairsIndexingStatus.name}
|
||||
</p>
|
||||
</TableCell>
|
||||
|
@@ -1,46 +1,42 @@
|
||||
import { Persona } from "../admin/assistants/interfaces";
|
||||
import { AssistantIcon } from "@/components/assistants/AssistantIcon";
|
||||
import { useState } from "react";
|
||||
import { DisplayAssistantCard } from "@/components/assistants/AssistantCards";
|
||||
import { DisplayAssistantCard } from "@/components/assistants/AssistantDescriptionCard";
|
||||
|
||||
export function ChatIntro({ selectedPersona }: { selectedPersona: Persona }) {
|
||||
const [hoveredAssistant, setHoveredAssistant] = useState(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="mobile:w-[90%] mobile:px-4 w-message-xs 2xl:w-message-sm 3xl:w-message">
|
||||
<div className="relative flex w-fit mx-auto justify-center">
|
||||
<div className="absolute z-10 -left-20 top-1/2 -translate-y-1/2">
|
||||
<div className="relative">
|
||||
<div
|
||||
onMouseEnter={() => setHoveredAssistant(true)}
|
||||
onMouseLeave={() => setHoveredAssistant(false)}
|
||||
className="p-4 scale-[.8] cursor-pointer border-dashed rounded-full flex border border-border border-2 border-dashed"
|
||||
style={{
|
||||
borderStyle: "dashed",
|
||||
borderWidth: "1.5px",
|
||||
borderSpacing: "4px",
|
||||
}}
|
||||
>
|
||||
<AssistantIcon
|
||||
disableToolip
|
||||
size={"large"}
|
||||
assistant={selectedPersona}
|
||||
/>
|
||||
</div>
|
||||
<div className="absolute right-full mr-2 w-[300px] top-0">
|
||||
{hoveredAssistant && (
|
||||
<DisplayAssistantCard selectedPersona={selectedPersona} />
|
||||
)}
|
||||
</div>
|
||||
<div className="flex flex-col items-center gap-6">
|
||||
<div className="relative flex w-fit mx-auto justify-center">
|
||||
<div className="absolute z-10 -left-20 top-1/2 -translate-y-1/2">
|
||||
<div className="relative">
|
||||
<div
|
||||
onMouseEnter={() => setHoveredAssistant(true)}
|
||||
onMouseLeave={() => setHoveredAssistant(false)}
|
||||
className="p-4 scale-[.7] cursor-pointer border-dashed rounded-full flex border border-gray-300 border-2 border-dashed"
|
||||
>
|
||||
<AssistantIcon
|
||||
disableToolip
|
||||
size="large"
|
||||
assistant={selectedPersona}
|
||||
/>
|
||||
</div>
|
||||
<div className="absolute right-full mr-1 w-[300px] top-0">
|
||||
{hoveredAssistant && (
|
||||
<DisplayAssistantCard selectedPersona={selectedPersona} />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="text-3xl line-clamp-2 text-text-800 font-base font-semibold text-strong">
|
||||
{selectedPersona?.name || "How can I help you today?"}
|
||||
</div>
|
||||
<div className="text-2xl text-black font-semibold text-center">
|
||||
{selectedPersona.name}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
<p className="text-base text-black font-normal text-center">
|
||||
{selectedPersona.description}
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@@ -66,7 +66,7 @@ import { ShareChatSessionModal } from "./modal/ShareChatSessionModal";
|
||||
import { FiArrowDown } from "react-icons/fi";
|
||||
import { ChatIntro } from "./ChatIntro";
|
||||
import { AIMessage, HumanMessage } from "./message/Messages";
|
||||
import { StarterMessage } from "./StarterMessage";
|
||||
import { StarterMessages } from "../../components/assistants/StarterMessage";
|
||||
import {
|
||||
AnswerPiecePacket,
|
||||
DanswerDocument,
|
||||
@@ -104,6 +104,14 @@ import BlurBackground from "./shared_chat_search/BlurBackground";
|
||||
import { NoAssistantModal } from "@/components/modals/NoAssistantModal";
|
||||
import { useAssistants } from "@/components/context/AssistantsContext";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardHeader,
|
||||
} from "@/components/ui/card";
|
||||
import { AssistantIcon } from "@/components/assistants/AssistantIcon";
|
||||
import AssistantBanner from "../../components/assistants/AssistantBanner";
|
||||
|
||||
const TEMP_USER_MESSAGE_ID = -1;
|
||||
const TEMP_ASSISTANT_MESSAGE_ID = -2;
|
||||
@@ -140,7 +148,7 @@ export function ChatPage({
|
||||
!shouldShowWelcomeModal
|
||||
);
|
||||
|
||||
const { user, isAdmin, isLoadingUser } = useUser();
|
||||
const { user, isAdmin, isLoadingUser, refreshUser } = useUser();
|
||||
|
||||
const existingChatIdRaw = searchParams.get("chatId");
|
||||
const [sendOnLoad, setSendOnLoad] = useState<string | null>(
|
||||
@@ -233,9 +241,17 @@ export function ChatPage({
|
||||
const [alternativeAssistant, setAlternativeAssistant] =
|
||||
useState<Persona | null>(null);
|
||||
|
||||
const {
|
||||
visibleAssistants: assistants,
|
||||
recentAssistants,
|
||||
assistants: allAssistants,
|
||||
refreshRecentAssistants,
|
||||
} = useAssistants();
|
||||
|
||||
const liveAssistant =
|
||||
alternativeAssistant ||
|
||||
selectedAssistant ||
|
||||
recentAssistants[0] ||
|
||||
finalAssistants[0] ||
|
||||
availableAssistants[0];
|
||||
|
||||
@@ -737,7 +753,7 @@ export function ChatPage({
|
||||
setMaxTokens(maxTokens);
|
||||
}
|
||||
}
|
||||
|
||||
refreshRecentAssistants(liveAssistant?.id);
|
||||
fetchMaxTokens();
|
||||
}, [liveAssistant]);
|
||||
|
||||
@@ -2005,48 +2021,35 @@ export function ChatPage({
|
||||
!isFetchingChatMessages &&
|
||||
currentSessionChatState == "input" &&
|
||||
!loadingError && (
|
||||
<div className="h-full flex flex-col justify-center items-center">
|
||||
<div className="h-full mt-12 flex flex-col justify-center items-center">
|
||||
<ChatIntro selectedPersona={liveAssistant} />
|
||||
|
||||
<div
|
||||
key={-4}
|
||||
className={`
|
||||
mx-auto
|
||||
px-4
|
||||
w-full
|
||||
max-w-[750px]
|
||||
flex
|
||||
flex-wrap
|
||||
justify-center
|
||||
mt-2
|
||||
h-40
|
||||
items-start
|
||||
mb-6`}
|
||||
>
|
||||
{currentPersona?.starter_messages &&
|
||||
currentPersona.starter_messages.length >
|
||||
0 && (
|
||||
<>
|
||||
<Separator className="mx-2" />
|
||||
<StarterMessages
|
||||
currentPersona={currentPersona}
|
||||
onSubmit={(messageOverride) =>
|
||||
onSubmit({
|
||||
messageOverride,
|
||||
})
|
||||
}
|
||||
/>
|
||||
|
||||
{currentPersona.starter_messages
|
||||
.slice(0, 4)
|
||||
.map((starterMessage, i) => (
|
||||
<div key={i} className="w-1/2">
|
||||
<StarterMessage
|
||||
starterMessage={starterMessage}
|
||||
onClick={() =>
|
||||
onSubmit({
|
||||
messageOverride:
|
||||
starterMessage.message,
|
||||
})
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
{!isFetchingChatMessages &&
|
||||
currentSessionChatState == "input" &&
|
||||
!loadingError &&
|
||||
allAssistants.length > 1 && (
|
||||
<div className="mx-auto px-4 w-full max-w-[750px] flex flex-col items-center">
|
||||
<Separator className="mx-2 w-full my-12" />
|
||||
<div className="text-sm text-black font-medium mb-4">
|
||||
Recent Assistants
|
||||
</div>
|
||||
<AssistantBanner
|
||||
recentAssistants={recentAssistants}
|
||||
liveAssistant={liveAssistant}
|
||||
allAssistants={allAssistants}
|
||||
onAssistantChange={onAssistantChange}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
@@ -1,29 +0,0 @@
|
||||
import { StarterMessage as StarterMessageType } from "../admin/assistants/interfaces";
|
||||
|
||||
export function StarterMessage({
|
||||
starterMessage,
|
||||
onClick,
|
||||
}: {
|
||||
starterMessage: StarterMessageType;
|
||||
onClick: () => void;
|
||||
}) {
|
||||
return (
|
||||
<div
|
||||
className="mb-4 mx-2 group relative overflow-hidden rounded-xl border border-border bg-gradient-to-br from-white to-background p-4 shadow-sm transition-all duration-300 hover:shadow-md hover:scale-[1.005] cursor-pointer"
|
||||
onClick={onClick}
|
||||
>
|
||||
<div className="absolute inset-0 bg-gradient-to-r from-blue-100 to-purple-100 opacity-0 group-hover:opacity-20 transition-opacity duration-300" />
|
||||
<h3
|
||||
className="text-base flex items-center font-medium text-text-800 group-hover:text-text-900 transition-colors duration-300
|
||||
line-clamp-2 gap-x-2 overflow-hidden"
|
||||
>
|
||||
{starterMessage.name}
|
||||
</h3>
|
||||
<div className={`overflow-hidden transition-all duration-300 max-h-20}`}>
|
||||
<p className="text-sm text-text-600 mt-2">
|
||||
{starterMessage.description}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
118
web/src/components/assistants/AssistantBanner.tsx
Normal file
118
web/src/components/assistants/AssistantBanner.tsx
Normal file
@@ -0,0 +1,118 @@
|
||||
import { Persona } from "../../app/admin/assistants/interfaces";
|
||||
|
||||
import { AssistantIcon } from "@/components/assistants/AssistantIcon";
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipTrigger,
|
||||
TooltipContent,
|
||||
TooltipProvider,
|
||||
} from "@/components/ui/tooltip";
|
||||
|
||||
export default function AssistantBanner({
|
||||
recentAssistants,
|
||||
liveAssistant,
|
||||
allAssistants,
|
||||
onAssistantChange,
|
||||
}: {
|
||||
recentAssistants: Persona[];
|
||||
liveAssistant: Persona | undefined;
|
||||
allAssistants: Persona[];
|
||||
onAssistantChange: (assistant: Persona) => void;
|
||||
}) {
|
||||
return (
|
||||
<div className="flex mx-auto mt-2 gap-4 ">
|
||||
{recentAssistants
|
||||
// First filter out the current assistant
|
||||
.filter((assistant) => assistant.id !== liveAssistant?.id)
|
||||
// Combine with visible assistants to get up to 4 total
|
||||
.concat(
|
||||
allAssistants.filter(
|
||||
(assistant) =>
|
||||
// Exclude current assistant
|
||||
assistant.id !== liveAssistant?.id &&
|
||||
// Exclude assistants already in recentAssistants
|
||||
!recentAssistants.some((recent) => recent.id === assistant.id)
|
||||
)
|
||||
)
|
||||
// Take first 4
|
||||
.slice(0, 4)
|
||||
.map((assistant) => (
|
||||
<TooltipProvider key={assistant.id}>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<div
|
||||
className="flex w-36 mx-3 py-1.5 scale-[1.] rounded-full border border-background-150 justify-center items-center gap-x-2 py-1 px-3 hover:bg-background-125 transition-colors cursor-pointer"
|
||||
onClick={() => onAssistantChange(assistant)}
|
||||
>
|
||||
<AssistantIcon
|
||||
disableToolip
|
||||
size="xs"
|
||||
assistant={assistant}
|
||||
/>
|
||||
<span className="font-semibold text-text-800 text-xs truncate max-w-[120px]">
|
||||
{assistant.name}
|
||||
</span>
|
||||
</div>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent backgroundColor="bg-background">
|
||||
<AssistantCard assistant={assistant} />
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function AssistantCard({ assistant }: { assistant: Persona }) {
|
||||
return (
|
||||
<div className="p-6 backdrop-blur-sm rounded-lg max-w-md w-full mx-auto">
|
||||
<div className="flex items-center mb-4">
|
||||
<div className="mb-auto mt-2">
|
||||
<AssistantIcon disableToolip size="small" assistant={assistant} />
|
||||
</div>
|
||||
<div className="ml-3">
|
||||
<h2 className="text-lg font-semibold text-gray-800">
|
||||
{assistant.name}
|
||||
</h2>
|
||||
<p className="text-sm text-gray-600">{assistant.description}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{assistant.tools.length > 0 ||
|
||||
assistant.llm_relevance_filter ||
|
||||
assistant.llm_filter_extraction ? (
|
||||
<div className="space-y-4">
|
||||
<h3 className="text-base font-medium text-gray-800">Capabilities</h3>
|
||||
<ul className="space-y-2">
|
||||
{assistant.tools.map((tool, index) => (
|
||||
<li
|
||||
key={index}
|
||||
className="flex items-center text-sm text-gray-700"
|
||||
>
|
||||
<span className="mr-2 text-gray-500">•</span>
|
||||
{tool.display_name}
|
||||
</li>
|
||||
))}
|
||||
{assistant.llm_relevance_filter && (
|
||||
<li className="flex items-center text-sm text-gray-700">
|
||||
<span className="mr-2 text-gray-500">•</span>
|
||||
Advanced Relevance Filtering
|
||||
</li>
|
||||
)}
|
||||
{assistant.llm_filter_extraction && (
|
||||
<li className="flex items-center text-sm text-gray-700">
|
||||
<span className="mr-2 text-gray-500">•</span>
|
||||
Smart Information Extraction
|
||||
</li>
|
||||
)}
|
||||
</ul>
|
||||
</div>
|
||||
) : (
|
||||
<p className="text-sm text-gray-600 italic">
|
||||
No specific capabilities listed for this assistant.
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
@@ -1,5 +1,3 @@
|
||||
import { CSS } from "@dnd-kit/utilities";
|
||||
|
||||
import { Persona } from "@/app/admin/assistants/interfaces";
|
||||
import { AssistantTools } from "@/app/assistants/ToolsDisplay";
|
||||
import { Bubble } from "@/components/Bubble";
|
||||
@@ -115,63 +113,3 @@ export function DraggableAssistantCard(props: {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function DisplayAssistantCard({
|
||||
selectedPersona,
|
||||
}: {
|
||||
selectedPersona: Persona;
|
||||
}) {
|
||||
return (
|
||||
<div className="p-4 bg-white/90 backdrop-blur-sm rounded-lg shadow-md border border-border/50 max-w-md w-full mx-auto transition-all duration-300 ease-in-out hover:shadow-lg">
|
||||
<div className="flex items-center mb-3">
|
||||
<AssistantIcon
|
||||
disableToolip
|
||||
size="medium"
|
||||
assistant={selectedPersona}
|
||||
/>
|
||||
<h2 className="ml-3 text-xl font-semibold text-text-900">
|
||||
{selectedPersona.name}
|
||||
</h2>
|
||||
</div>
|
||||
<p className="text-sm text-text-600 mb-3 leading-relaxed">
|
||||
{selectedPersona.description}
|
||||
</p>
|
||||
{selectedPersona.tools.length > 0 ||
|
||||
selectedPersona.llm_relevance_filter ||
|
||||
selectedPersona.llm_filter_extraction ? (
|
||||
<div className="space-y-2">
|
||||
<h3 className="text-base font-medium text-text-900">Capabilities:</h3>
|
||||
<ul className="space-y-.5">
|
||||
{/* display all tools */}
|
||||
{selectedPersona.tools.map((tool, index) => (
|
||||
<li
|
||||
key={index}
|
||||
className="flex items-center text-sm text-text-700"
|
||||
>
|
||||
<span className="mr-2 text-text-500 opacity-70">•</span>{" "}
|
||||
{tool.display_name}
|
||||
</li>
|
||||
))}
|
||||
{/* Built in capabilities */}
|
||||
{selectedPersona.llm_relevance_filter && (
|
||||
<li className="flex items-center text-sm text-text-700">
|
||||
<span className="mr-2 text-text-500 opacity-70">•</span>{" "}
|
||||
Advanced Relevance Filtering
|
||||
</li>
|
||||
)}
|
||||
{selectedPersona.llm_filter_extraction && (
|
||||
<li className="flex items-center text-sm text-text-700">
|
||||
<span className="mr-2 text-text-500 opacity-70">•</span> Smart
|
||||
Information Extraction
|
||||
</li>
|
||||
)}
|
||||
</ul>
|
||||
</div>
|
||||
) : (
|
||||
<p className="text-sm text-text-600 italic">
|
||||
No specific capabilities listed for this assistant.
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
63
web/src/components/assistants/AssistantDescriptionCard.tsx
Normal file
63
web/src/components/assistants/AssistantDescriptionCard.tsx
Normal file
@@ -0,0 +1,63 @@
|
||||
import { Persona } from "@/app/admin/assistants/interfaces";
|
||||
import { AssistantIcon } from "@/components/assistants/AssistantIcon";
|
||||
import React from "react";
|
||||
|
||||
export function DisplayAssistantCard({
|
||||
selectedPersona,
|
||||
}: {
|
||||
selectedPersona: Persona;
|
||||
}) {
|
||||
return (
|
||||
<div className="p-4 bg-white/90 backdrop-blur-sm rounded-lg shadow-md border border-border/50 max-w-md w-full mx-auto transition-all duration-300 ease-in-out hover:shadow-lg">
|
||||
<div className="flex items-center mb-3">
|
||||
<AssistantIcon
|
||||
disableToolip
|
||||
size="medium"
|
||||
assistant={selectedPersona}
|
||||
/>
|
||||
<h2 className="ml-3 text-xl font-semibold text-text-900">
|
||||
{selectedPersona.name}
|
||||
</h2>
|
||||
</div>
|
||||
<p className="text-sm text-text-600 mb-3 leading-relaxed">
|
||||
{selectedPersona.description}
|
||||
</p>
|
||||
{selectedPersona.tools.length > 0 ||
|
||||
selectedPersona.llm_relevance_filter ||
|
||||
selectedPersona.llm_filter_extraction ? (
|
||||
<div className="space-y-2">
|
||||
<h3 className="text-base font-medium text-text-900">Capabilities:</h3>
|
||||
<ul className="space-y-.5">
|
||||
{/* display all tools */}
|
||||
{selectedPersona.tools.map((tool, index) => (
|
||||
<li
|
||||
key={index}
|
||||
className="flex items-center text-sm text-text-700"
|
||||
>
|
||||
<span className="mr-2 text-text-500 opacity-70">•</span>{" "}
|
||||
{tool.display_name}
|
||||
</li>
|
||||
))}
|
||||
{/* Built in capabilities */}
|
||||
{selectedPersona.llm_relevance_filter && (
|
||||
<li className="flex items-center text-sm text-text-700">
|
||||
<span className="mr-2 text-text-500 opacity-70">•</span>{" "}
|
||||
Advanced Relevance Filtering
|
||||
</li>
|
||||
)}
|
||||
{selectedPersona.llm_filter_extraction && (
|
||||
<li className="flex items-center text-sm text-text-700">
|
||||
<span className="mr-2 text-text-500 opacity-70">•</span> Smart
|
||||
Information Extraction
|
||||
</li>
|
||||
)}
|
||||
</ul>
|
||||
</div>
|
||||
) : (
|
||||
<p className="text-sm text-text-600 italic">
|
||||
No specific capabilities listed for this assistant.
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
@@ -25,7 +25,7 @@ export function AssistantIcon({
|
||||
disableToolip,
|
||||
}: {
|
||||
assistant: Persona;
|
||||
size?: "small" | "medium" | "large" | "header";
|
||||
size?: "xs" | "small" | "medium" | "large" | "header";
|
||||
border?: boolean;
|
||||
disableToolip?: boolean;
|
||||
}) {
|
||||
@@ -52,7 +52,9 @@ export function AssistantIcon({
|
||||
? "w-14 h-14"
|
||||
: size === "medium"
|
||||
? "w-8 h-8"
|
||||
: "w-6 h-6"
|
||||
: size === "xs"
|
||||
? "w-4 h-4"
|
||||
: "w-6 h-6"
|
||||
}`}
|
||||
src={buildImgUrl(assistant.uploaded_image_id)}
|
||||
loading="lazy"
|
||||
@@ -68,7 +70,9 @@ export function AssistantIcon({
|
||||
? "w-14 h-14"
|
||||
: size === "medium"
|
||||
? "w-8 h-8"
|
||||
: "w-6 h-6"
|
||||
: size === "xs"
|
||||
? "w-4 h-4"
|
||||
: "w-6 h-6"
|
||||
} `}
|
||||
>
|
||||
{createSVG(
|
||||
@@ -80,7 +84,9 @@ export function AssistantIcon({
|
||||
? 56
|
||||
: size === "medium"
|
||||
? 32
|
||||
: 24
|
||||
: size === "xs"
|
||||
? 16
|
||||
: 24
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
@@ -90,6 +96,7 @@ export function AssistantIcon({
|
||||
${size === "large" ? "w-10 h-10" : ""}
|
||||
${size === "header" ? "w-14 h-14" : ""}
|
||||
${size === "medium" ? "w-8 h-8" : ""}
|
||||
${size === "xs" ? "w-4 h-4" : ""}
|
||||
${!size || size === "small" ? "w-6 h-6" : ""} `}
|
||||
style={{ backgroundColor: color }}
|
||||
/>
|
||||
|
51
web/src/components/assistants/StarterMessage.tsx
Normal file
51
web/src/components/assistants/StarterMessage.tsx
Normal file
@@ -0,0 +1,51 @@
|
||||
import { useContext } from "react";
|
||||
import { Persona } from "../../app/admin/assistants/interfaces";
|
||||
import { SettingsContext } from "../settings/SettingsProvider";
|
||||
|
||||
export function StarterMessages({
|
||||
currentPersona,
|
||||
onSubmit,
|
||||
}: {
|
||||
currentPersona: Persona;
|
||||
onSubmit: (messageOverride: string) => void;
|
||||
}) {
|
||||
const settings = useContext(SettingsContext);
|
||||
const isMobile = settings?.isMobile;
|
||||
return (
|
||||
<div
|
||||
key={-4}
|
||||
className={`
|
||||
mx-auto
|
||||
w-full
|
||||
${
|
||||
isMobile
|
||||
? "gap-x-2 w-2/3 justify-between"
|
||||
: "justify-center max-w-[750px] items-start"
|
||||
}
|
||||
flex
|
||||
mt-6
|
||||
`}
|
||||
>
|
||||
{currentPersona?.starter_messages &&
|
||||
currentPersona.starter_messages.length > 0 && (
|
||||
<>
|
||||
{currentPersona.starter_messages
|
||||
.slice(0, isMobile ? 2 : 4)
|
||||
.map((starterMessage, i) => (
|
||||
<div key={i} className={`${isMobile ? "w-1/2" : "w-1/4"}`}>
|
||||
<button
|
||||
onClick={() => onSubmit(starterMessage.message)}
|
||||
className={`relative flex ${
|
||||
!isMobile && "w-40"
|
||||
} flex-col gap-2 rounded-2xl shadow-sm border border-border px-3 py-2 text-start align-to text-wrap text-[15px] shadow-xs transition enabled:hover:bg-background-100 disabled:cursor-not-allowed line-clamp-3`}
|
||||
style={{ height: `5.2rem` }}
|
||||
>
|
||||
{starterMessage.name}
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
@@ -22,7 +22,8 @@ interface AssistantsContextProps {
|
||||
ownedButHiddenAssistants: Persona[];
|
||||
refreshAssistants: () => Promise<void>;
|
||||
isImageGenerationAvailable: boolean;
|
||||
|
||||
recentAssistants: Persona[];
|
||||
refreshRecentAssistants: (currentAssistant: number) => Promise<void>;
|
||||
// Admin only
|
||||
editablePersonas: Persona[];
|
||||
allAssistants: Persona[];
|
||||
@@ -46,10 +47,21 @@ export const AssistantsProvider: React.FC<{
|
||||
const [assistants, setAssistants] = useState<Persona[]>(
|
||||
initialAssistants || []
|
||||
);
|
||||
const { user, isLoadingUser, isAdmin } = useUser();
|
||||
const { user, isLoadingUser, refreshUser, isAdmin } = useUser();
|
||||
const [editablePersonas, setEditablePersonas] = useState<Persona[]>([]);
|
||||
const [allAssistants, setAllAssistants] = useState<Persona[]>([]);
|
||||
|
||||
const [recentAssistants, setRecentAssistants] = useState<Persona[]>(
|
||||
user?.preferences.recent_assistants
|
||||
?.filter((assistantId) =>
|
||||
assistants.find((assistant) => assistant.id === assistantId)
|
||||
)
|
||||
.map(
|
||||
(assistantId) =>
|
||||
assistants.find((assistant) => assistant.id === assistantId)!
|
||||
) || []
|
||||
);
|
||||
|
||||
const [isImageGenerationAvailable, setIsImageGenerationAvailable] =
|
||||
useState<boolean>(false);
|
||||
|
||||
@@ -98,6 +110,28 @@ export const AssistantsProvider: React.FC<{
|
||||
fetchPersonas();
|
||||
}, [isAdmin]);
|
||||
|
||||
const refreshRecentAssistants = async (currentAssistant: number) => {
|
||||
const response = await fetch("/api/user/recent-assistants", {
|
||||
method: "PATCH",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
current_assistant: currentAssistant,
|
||||
}),
|
||||
});
|
||||
if (!response.ok) {
|
||||
return;
|
||||
}
|
||||
setRecentAssistants((recentAssistants) => [
|
||||
assistants.find((assistant) => assistant.id === currentAssistant)!,
|
||||
|
||||
...recentAssistants.filter(
|
||||
(assistant) => assistant.id !== currentAssistant
|
||||
),
|
||||
]);
|
||||
};
|
||||
|
||||
const refreshAssistants = async () => {
|
||||
try {
|
||||
const response = await fetch("/api/persona", {
|
||||
@@ -125,6 +159,12 @@ export const AssistantsProvider: React.FC<{
|
||||
} catch (error) {
|
||||
console.error("Error refreshing assistants:", error);
|
||||
}
|
||||
setRecentAssistants(
|
||||
assistants.filter(
|
||||
(assistant) =>
|
||||
user?.preferences.recent_assistants?.includes(assistant.id) || false
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
const {
|
||||
@@ -167,6 +207,8 @@ export const AssistantsProvider: React.FC<{
|
||||
editablePersonas,
|
||||
allAssistants,
|
||||
isImageGenerationAvailable,
|
||||
recentAssistants,
|
||||
refreshRecentAssistants,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
|
@@ -203,7 +203,7 @@ export default function CreateCredential({
|
||||
for information on setting up this connector.
|
||||
</p>
|
||||
)}
|
||||
<CardSection className="!border-0 mt-4 flex flex-col gap-y-6">
|
||||
<CardSection className="w-full !border-0 mt-4 flex flex-col gap-y-6">
|
||||
<TextFormField
|
||||
name="name"
|
||||
placeholder="(Optional) credential name.."
|
||||
|
@@ -471,9 +471,9 @@ export function HorizontalSourceSelector({
|
||||
icon={<FiBook size={16} />}
|
||||
defaultDisplay="Sets"
|
||||
resetValues={resetDocuments}
|
||||
width="w-fit max-w-24 ellipsis truncate"
|
||||
width="w-fit max-w-24 etext-llipsis truncate"
|
||||
dropdownWidth="max-w-36 w-fit"
|
||||
optionClassName="truncate break-all ellipsis"
|
||||
optionClassName="truncate w-full break-all"
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -506,7 +506,7 @@ export function HorizontalSourceSelector({
|
||||
resetValues={resetTags}
|
||||
width="w-fit max-w-24 ellipsis truncate"
|
||||
dropdownWidth="max-w-80 w-fit"
|
||||
optionClassName="truncate break-all ellipsis"
|
||||
optionClassName="truncate w-full break-all ellipsis"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
@@ -14,13 +14,16 @@ const TooltipContent = React.forwardRef<
|
||||
React.ElementRef<typeof TooltipPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content> & {
|
||||
maxWidth?: string;
|
||||
backgroundColor?: string;
|
||||
}
|
||||
>(({ className, sideOffset = 4, maxWidth, ...props }, ref) => (
|
||||
>(({ className, sideOffset = 4, maxWidth, backgroundColor, ...props }, ref) => (
|
||||
<TooltipPrimitive.Content
|
||||
ref={ref}
|
||||
sideOffset={sideOffset}
|
||||
className={cn(
|
||||
"z-50 overflow-hidden rounded-md border border-neutral-200 text-white bg-background-900 px-2 py-1.5 text-sm shadow-md animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 dark:border-neutral-800 dark:bg-neutral-950 dark:text-neutral-50 max-w-sm",
|
||||
`z-50 overflow-hidden rounded-md border border-neutral-200 text-white ${
|
||||
backgroundColor || "bg-background-900"
|
||||
} px-2 py-1.5 text-sm shadow-md animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 dark:border-neutral-800 dark:bg-neutral-950 dark:text-neutral-50 max-w-sm`,
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
@@ -8,6 +8,7 @@ interface UserPreferences {
|
||||
visible_assistants: number[];
|
||||
hidden_assistants: number[];
|
||||
default_model: string | null;
|
||||
recent_assistants: number[];
|
||||
}
|
||||
|
||||
export enum UserStatus {
|
||||
|
Reference in New Issue
Block a user