Merge pull request #2949 from danswer-ai/avoid_image_confusion

avoid image generation tool confusion
This commit is contained in:
hagen-danswer 2024-10-27 14:54:24 -07:00 committed by GitHub
commit dc7b367816
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 70 additions and 90 deletions

View File

@ -30,6 +30,7 @@ from danswer.file_store.file_store import get_default_file_store
from danswer.file_store.models import ChatFileType from danswer.file_store.models import ChatFileType
from danswer.llm.answering.prompts.utils import build_dummy_prompt from danswer.llm.answering.prompts.utils import build_dummy_prompt
from danswer.server.features.persona.models import CreatePersonaRequest from danswer.server.features.persona.models import CreatePersonaRequest
from danswer.server.features.persona.models import ImageGenerationToolStatus
from danswer.server.features.persona.models import PersonaSharedNotificationData from danswer.server.features.persona.models import PersonaSharedNotificationData
from danswer.server.features.persona.models import PersonaSnapshot from danswer.server.features.persona.models import PersonaSnapshot
from danswer.server.features.persona.models import PromptTemplateResponse from danswer.server.features.persona.models import PromptTemplateResponse
@ -227,6 +228,16 @@ def delete_persona(
) )
@basic_router.get("/image-generation-tool")
def get_image_generation_tool(
_: User
| None = Depends(current_user), # User param not used but kept for consistency
db_session: Session = Depends(get_session),
) -> ImageGenerationToolStatus: # Use bool instead of str for boolean values
is_available = is_image_generation_available(db_session=db_session)
return ImageGenerationToolStatus(is_available=is_available)
@basic_router.get("") @basic_router.get("")
def list_personas( def list_personas(
user: User | None = Depends(current_user), user: User | None = Depends(current_user),

View File

@ -124,3 +124,7 @@ class PromptTemplateResponse(BaseModel):
class PersonaSharedNotificationData(BaseModel): class PersonaSharedNotificationData(BaseModel):
persona_id: int persona_id: int
class ImageGenerationToolStatus(BaseModel):
is_available: bool

View File

@ -45,12 +45,7 @@ import { FullLLMProvider } from "../configuration/llm/interfaces";
import CollapsibleSection from "./CollapsibleSection"; import CollapsibleSection from "./CollapsibleSection";
import { SuccessfulPersonaUpdateRedirectType } from "./enums"; import { SuccessfulPersonaUpdateRedirectType } from "./enums";
import { Persona, StarterMessage } from "./interfaces"; import { Persona, StarterMessage } from "./interfaces";
import { import { buildFinalPrompt, createPersona, updatePersona } from "./lib";
buildFinalPrompt,
createPersona,
providersContainImageGeneratingSupport,
updatePersona,
} from "./lib";
import { Popover } from "@/components/popover/Popover"; import { Popover } from "@/components/popover/Popover";
import { import {
CameraIcon, CameraIcon,
@ -106,7 +101,7 @@ export function AssistantEditor({
shouldAddAssistantToUserPreferences?: boolean; shouldAddAssistantToUserPreferences?: boolean;
admin?: boolean; admin?: boolean;
}) { }) {
const { refreshAssistants } = useAssistants(); const { refreshAssistants, isImageGenerationAvailable } = useAssistants();
const router = useRouter(); const router = useRouter();
const { popup, setPopup } = usePopup(); const { popup, setPopup } = usePopup();
@ -138,42 +133,13 @@ export function AssistantEditor({
const [isIconDropdownOpen, setIsIconDropdownOpen] = useState(false); const [isIconDropdownOpen, setIsIconDropdownOpen] = useState(false);
const [finalPrompt, setFinalPrompt] = useState<string | null>("");
const [finalPromptError, setFinalPromptError] = useState<string>("");
const [removePersonaImage, setRemovePersonaImage] = useState(false); const [removePersonaImage, setRemovePersonaImage] = useState(false);
const triggerFinalPromptUpdate = async (
systemPrompt: string,
taskPrompt: string,
retrievalDisabled: boolean
) => {
const response = await buildFinalPrompt(
systemPrompt,
taskPrompt,
retrievalDisabled
);
if (response.ok) {
setFinalPrompt((await response.json()).final_prompt_template);
}
};
const isUpdate = existingPersona !== undefined && existingPersona !== null; const isUpdate = existingPersona !== undefined && existingPersona !== null;
const existingPrompt = existingPersona?.prompts[0] ?? null; const existingPrompt = existingPersona?.prompts[0] ?? null;
useEffect(() => {
if (isUpdate && existingPrompt) {
triggerFinalPromptUpdate(
existingPrompt.system_prompt,
existingPrompt.task_prompt,
existingPersona.num_chunks === 0
);
}
}, [isUpdate, existingPrompt, existingPersona?.num_chunks]);
const defaultProvider = llmProviders.find( const defaultProvider = llmProviders.find(
(llmProvider) => llmProvider.is_default_provider (llmProvider) => llmProvider.is_default_provider
); );
const defaultProviderName = defaultProvider?.provider;
const defaultModelName = defaultProvider?.default_model_name; const defaultModelName = defaultProvider?.default_model_name;
const providerDisplayNameToProviderName = new Map<string, string>(); const providerDisplayNameToProviderName = new Map<string, string>();
llmProviders.forEach((llmProvider) => { llmProviders.forEach((llmProvider) => {
@ -314,14 +280,6 @@ export function AssistantEditor({
} }
)} )}
onSubmit={async (values, formikHelpers) => { onSubmit={async (values, formikHelpers) => {
if (finalPromptError) {
setPopup({
type: "error",
message: "Cannot submit while there are errors in the form",
});
return;
}
if ( if (
values.llm_model_provider_override && values.llm_model_provider_override &&
!values.llm_model_version_override !values.llm_model_version_override
@ -642,13 +600,7 @@ export function AssistantEditor({
placeholder="e.g. 'You are a professional email writing assistant that always uses a polite enthusiastic tone, emphasizes action items, and leaves blanks for the human to fill in when you have unknowns'" placeholder="e.g. 'You are a professional email writing assistant that always uses a polite enthusiastic tone, emphasizes action items, and leaves blanks for the human to fill in when you have unknowns'"
onChange={(e) => { onChange={(e) => {
setFieldValue("system_prompt", e.target.value); setFieldValue("system_prompt", e.target.value);
triggerFinalPromptUpdate(
e.target.value,
values.task_prompt,
searchToolEnabled()
);
}} }}
error={finalPromptError}
/> />
<div> <div>
@ -775,7 +727,8 @@ export function AssistantEditor({
<TooltipTrigger asChild> <TooltipTrigger asChild>
<div <div
className={`w-fit ${ className={`w-fit ${
!currentLLMSupportsImageOutput !currentLLMSupportsImageOutput ||
!isImageGenerationAvailable
? "opacity-70 cursor-not-allowed" ? "opacity-70 cursor-not-allowed"
: "" : ""
}`} }`}
@ -787,11 +740,14 @@ export function AssistantEditor({
onChange={() => { onChange={() => {
toggleToolInValues(imageGenerationTool.id); toggleToolInValues(imageGenerationTool.id);
}} }}
disabled={!currentLLMSupportsImageOutput} disabled={
!currentLLMSupportsImageOutput ||
!isImageGenerationAvailable
}
/> />
</div> </div>
</TooltipTrigger> </TooltipTrigger>
{!currentLLMSupportsImageOutput && ( {!currentLLMSupportsImageOutput ? (
<TooltipContent side="top" align="center"> <TooltipContent side="top" align="center">
<p className="bg-background-900 max-w-[200px] mb-1 text-sm rounded-lg p-1.5 text-white"> <p className="bg-background-900 max-w-[200px] mb-1 text-sm rounded-lg p-1.5 text-white">
To use Image Generation, select GPT-4o or another To use Image Generation, select GPT-4o or another
@ -799,6 +755,15 @@ export function AssistantEditor({
this Assistant. this Assistant.
</p> </p>
</TooltipContent> </TooltipContent>
) : (
!isImageGenerationAvailable && (
<TooltipContent side="top" align="center">
<p className="bg-background-900 max-w-[200px] mb-1 text-sm rounded-lg p-1.5 text-white">
Image Generation requires an OpenAI or Azure
Dalle configuration.
</p>
</TooltipContent>
)
)} )}
</Tooltip> </Tooltip>
</TooltipProvider> </TooltipProvider>
@ -1024,11 +989,6 @@ export function AssistantEditor({
placeholder="e.g. 'Remember to reference all of the points mentioned in my message to you and focus on identifying action items that can move things forward'" placeholder="e.g. 'Remember to reference all of the points mentioned in my message to you and focus on identifying action items that can move things forward'"
onChange={(e) => { onChange={(e) => {
setFieldValue("task_prompt", e.target.value); setFieldValue("task_prompt", e.target.value);
triggerFinalPromptUpdate(
values.system_prompt,
e.target.value,
searchToolEnabled()
);
}} }}
explanationText="Learn about prompting in our docs!" explanationText="Learn about prompting in our docs!"
explanationLink="https://docs.danswer.dev/guides/assistants" explanationLink="https://docs.danswer.dev/guides/assistants"

View File

@ -21,6 +21,7 @@ interface AssistantsContextProps {
finalAssistants: Persona[]; finalAssistants: Persona[];
ownedButHiddenAssistants: Persona[]; ownedButHiddenAssistants: Persona[];
refreshAssistants: () => Promise<void>; refreshAssistants: () => Promise<void>;
isImageGenerationAvailable: boolean;
// Admin only // Admin only
editablePersonas: Persona[]; editablePersonas: Persona[];
@ -47,51 +48,54 @@ export const AssistantsProvider: React.FC<{
); );
const { user, isLoadingUser, isAdmin } = useUser(); const { user, isLoadingUser, isAdmin } = useUser();
const [editablePersonas, setEditablePersonas] = useState<Persona[]>([]); const [editablePersonas, setEditablePersonas] = useState<Persona[]>([]);
useEffect(() => {
const fetchEditablePersonas = async () => {
if (!isAdmin) {
return;
}
try {
const response = await fetch("/api/admin/persona?get_editable=true");
if (!response.ok) {
console.error("Failed to fetch editable personas");
return;
}
const personas = await response.json();
setEditablePersonas(personas);
} catch (error) {
console.error("Error fetching editable personas:", error);
}
};
fetchEditablePersonas();
}, [isAdmin]);
const [allAssistants, setAllAssistants] = useState<Persona[]>([]); const [allAssistants, setAllAssistants] = useState<Persona[]>([]);
const [isImageGenerationAvailable, setIsImageGenerationAvailable] =
useState<boolean>(false);
useEffect(() => { useEffect(() => {
const fetchAllAssistants = async () => { const checkImageGenerationAvailability = async () => {
try {
const response = await fetch("/api/persona/image-generation-tool");
if (response.ok) {
const { is_available } = await response.json();
setIsImageGenerationAvailable(is_available);
}
} catch (error) {
console.error("Error checking image generation availability:", error);
}
};
checkImageGenerationAvailability();
}, []);
useEffect(() => {
const fetchPersonas = async () => {
if (!isAdmin) { if (!isAdmin) {
return; return;
} }
try { try {
const response = await fetch("/api/admin/persona"); const [editableResponse, allResponse] = await Promise.all([
if (!response.ok) { fetch("/api/admin/persona?get_editable=true"),
console.error("Failed to fetch all personas"); fetch("/api/admin/persona"),
return; ]);
if (editableResponse.ok) {
const editablePersonas = await editableResponse.json();
setEditablePersonas(editablePersonas);
}
if (allResponse.ok) {
const allPersonas = await allResponse.json();
setAllAssistants(allPersonas);
} }
const personas = await response.json();
setAllAssistants(personas);
} catch (error) { } catch (error) {
console.error("Error fetching all personas:", error); console.error("Error fetching personas:", error);
} }
}; };
fetchAllAssistants(); fetchPersonas();
}, [isAdmin]); }, [isAdmin]);
const refreshAssistants = async () => { const refreshAssistants = async () => {
@ -162,6 +166,7 @@ export const AssistantsProvider: React.FC<{
refreshAssistants, refreshAssistants,
editablePersonas, editablePersonas,
allAssistants, allAssistants,
isImageGenerationAvailable,
}} }}
> >
{children} {children}