mirror of
https://github.com/danswer-ai/danswer.git
synced 2025-10-11 05:36:03 +02:00
Add global assistants context (#2900)
* add global assistants context * nit * minor cleanup * minor clarity * nit
This commit is contained in:
@@ -328,7 +328,6 @@ def update_all_personas_display_priority(
|
|||||||
|
|
||||||
for persona in personas:
|
for persona in personas:
|
||||||
persona.display_priority = display_priority_map[persona.id]
|
persona.display_priority = display_priority_map[persona.id]
|
||||||
|
|
||||||
db_session.commit()
|
db_session.commit()
|
||||||
|
|
||||||
|
|
||||||
|
@@ -61,6 +61,7 @@ import {
|
|||||||
import { AdvancedOptionsToggle } from "@/components/AdvancedOptionsToggle";
|
import { AdvancedOptionsToggle } from "@/components/AdvancedOptionsToggle";
|
||||||
import { buildImgUrl } from "@/app/chat/files/images/utils";
|
import { buildImgUrl } from "@/app/chat/files/images/utils";
|
||||||
import { LlmList } from "@/components/llm/LLMList";
|
import { LlmList } from "@/components/llm/LLMList";
|
||||||
|
import { useAssistants } from "@/components/context/AssistantsContext";
|
||||||
|
|
||||||
function findSearchTool(tools: ToolSnapshot[]) {
|
function findSearchTool(tools: ToolSnapshot[]) {
|
||||||
return tools.find((tool) => tool.in_code_tool_id === "SearchTool");
|
return tools.find((tool) => tool.in_code_tool_id === "SearchTool");
|
||||||
@@ -105,6 +106,7 @@ export function AssistantEditor({
|
|||||||
shouldAddAssistantToUserPreferences?: boolean;
|
shouldAddAssistantToUserPreferences?: boolean;
|
||||||
admin?: boolean;
|
admin?: boolean;
|
||||||
}) {
|
}) {
|
||||||
|
const { refreshAssistants } = useAssistants();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
const { popup, setPopup } = usePopup();
|
const { popup, setPopup } = usePopup();
|
||||||
@@ -433,6 +435,7 @@ export function AssistantEditor({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
await refreshAssistants();
|
||||||
router.push(
|
router.push(
|
||||||
redirectType === SuccessfulPersonaUpdateRedirectType.ADMIN
|
redirectType === SuccessfulPersonaUpdateRedirectType.ADMIN
|
||||||
? `/admin/assistants?u=${Date.now()}`
|
? `/admin/assistants?u=${Date.now()}`
|
||||||
|
@@ -5,7 +5,7 @@ import { Persona } from "./interfaces";
|
|||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import { CustomCheckbox } from "@/components/CustomCheckbox";
|
import { CustomCheckbox } from "@/components/CustomCheckbox";
|
||||||
import { usePopup } from "@/components/admin/connectors/Popup";
|
import { usePopup } from "@/components/admin/connectors/Popup";
|
||||||
import { useState, useMemo } from "react";
|
import { useState, useMemo, useEffect } from "react";
|
||||||
import { UniqueIdentifier } from "@dnd-kit/core";
|
import { UniqueIdentifier } from "@dnd-kit/core";
|
||||||
import { DraggableTable } from "@/components/table/DraggableTable";
|
import { DraggableTable } from "@/components/table/DraggableTable";
|
||||||
import {
|
import {
|
||||||
@@ -16,6 +16,7 @@ import {
|
|||||||
import { FiEdit2 } from "react-icons/fi";
|
import { FiEdit2 } from "react-icons/fi";
|
||||||
import { TrashIcon } from "@/components/icons/icons";
|
import { TrashIcon } from "@/components/icons/icons";
|
||||||
import { useUser } from "@/components/user/UserProvider";
|
import { useUser } from "@/components/user/UserProvider";
|
||||||
|
import { useAssistants } from "@/components/context/AssistantsContext";
|
||||||
|
|
||||||
function PersonaTypeDisplay({ persona }: { persona: Persona }) {
|
function PersonaTypeDisplay({ persona }: { persona: Persona }) {
|
||||||
if (persona.builtin_persona) {
|
if (persona.builtin_persona) {
|
||||||
@@ -37,43 +38,36 @@ function PersonaTypeDisplay({ persona }: { persona: Persona }) {
|
|||||||
return <Text>Personal {persona.owner && <>({persona.owner.email})</>}</Text>;
|
return <Text>Personal {persona.owner && <>({persona.owner.email})</>}</Text>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function PersonasTable({
|
export function PersonasTable() {
|
||||||
allPersonas,
|
|
||||||
editablePersonas,
|
|
||||||
}: {
|
|
||||||
allPersonas: Persona[];
|
|
||||||
editablePersonas: Persona[];
|
|
||||||
}) {
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { popup, setPopup } = usePopup();
|
const { popup, setPopup } = usePopup();
|
||||||
|
const { refreshUser, isLoadingUser, isAdmin } = useUser();
|
||||||
const { isLoadingUser, isAdmin } = useUser();
|
const {
|
||||||
|
allAssistants: assistants,
|
||||||
|
refreshAssistants,
|
||||||
|
editablePersonas,
|
||||||
|
} = useAssistants();
|
||||||
|
|
||||||
const editablePersonaIds = useMemo(() => {
|
const editablePersonaIds = useMemo(() => {
|
||||||
return new Set(editablePersonas.map((p) => p.id.toString()));
|
return new Set(editablePersonas.map((p) => p.id.toString()));
|
||||||
}, [editablePersonas]);
|
}, [editablePersonas]);
|
||||||
|
|
||||||
const sortedPersonas = useMemo(() => {
|
const [finalPersonas, setFinalPersonas] = useState<Persona[]>([]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
const editable = editablePersonas.sort(personaComparator);
|
const editable = editablePersonas.sort(personaComparator);
|
||||||
const nonEditable = allPersonas
|
const nonEditable = assistants
|
||||||
.filter((p) => !editablePersonaIds.has(p.id.toString()))
|
.filter((p) => !editablePersonaIds.has(p.id.toString()))
|
||||||
.sort(personaComparator);
|
.sort(personaComparator);
|
||||||
return [...editable, ...nonEditable];
|
setFinalPersonas([...editable, ...nonEditable]);
|
||||||
}, [allPersonas, editablePersonas]);
|
}, [editablePersonas, assistants, editablePersonaIds]);
|
||||||
|
|
||||||
const [finalPersonas, setFinalPersonas] = useState<string[]>(
|
|
||||||
sortedPersonas.map((persona) => persona.id.toString())
|
|
||||||
);
|
|
||||||
const finalPersonaValues = finalPersonas
|
|
||||||
.filter((id) => new Set(allPersonas.map((p) => p.id.toString())).has(id))
|
|
||||||
.map((id) => {
|
|
||||||
return sortedPersonas.find(
|
|
||||||
(persona) => persona.id.toString() === id
|
|
||||||
) as Persona;
|
|
||||||
});
|
|
||||||
|
|
||||||
const updatePersonaOrder = async (orderedPersonaIds: UniqueIdentifier[]) => {
|
const updatePersonaOrder = async (orderedPersonaIds: UniqueIdentifier[]) => {
|
||||||
setFinalPersonas(orderedPersonaIds.map((id) => id.toString()));
|
const reorderedAssistants = orderedPersonaIds.map(
|
||||||
|
(id) => assistants.find((assistant) => assistant.id.toString() === id)!
|
||||||
|
);
|
||||||
|
|
||||||
|
setFinalPersonas(reorderedAssistants);
|
||||||
|
|
||||||
const displayPriorityMap = new Map<UniqueIdentifier, number>();
|
const displayPriorityMap = new Map<UniqueIdentifier, number>();
|
||||||
orderedPersonaIds.forEach((personaId, ind) => {
|
orderedPersonaIds.forEach((personaId, ind) => {
|
||||||
@@ -89,13 +83,19 @@ export function PersonasTable({
|
|||||||
display_priority_map: Object.fromEntries(displayPriorityMap),
|
display_priority_map: Object.fromEntries(displayPriorityMap),
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
setPopup({
|
setPopup({
|
||||||
type: "error",
|
type: "error",
|
||||||
message: `Failed to update persona order - ${await response.text()}`,
|
message: `Failed to update persona order - ${await response.text()}`,
|
||||||
});
|
});
|
||||||
|
setFinalPersonas(assistants);
|
||||||
router.refresh();
|
router.refresh();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await refreshAssistants();
|
||||||
|
await refreshUser();
|
||||||
};
|
};
|
||||||
|
|
||||||
if (isLoadingUser) {
|
if (isLoadingUser) {
|
||||||
@@ -115,8 +115,8 @@ export function PersonasTable({
|
|||||||
<DraggableTable
|
<DraggableTable
|
||||||
headers={["Name", "Description", "Type", "Is Visible", "Delete"]}
|
headers={["Name", "Description", "Type", "Is Visible", "Delete"]}
|
||||||
isAdmin={isAdmin}
|
isAdmin={isAdmin}
|
||||||
rows={finalPersonaValues.map((persona) => {
|
rows={finalPersonas.map((persona) => {
|
||||||
const isEditable = editablePersonaIds.has(persona.id.toString());
|
const isEditable = editablePersonas.includes(persona);
|
||||||
return {
|
return {
|
||||||
id: persona.id.toString(),
|
id: persona.id.toString(),
|
||||||
cells: [
|
cells: [
|
||||||
|
@@ -2,33 +2,10 @@ import { PersonasTable } from "./PersonaTable";
|
|||||||
import { FiPlusSquare } from "react-icons/fi";
|
import { FiPlusSquare } from "react-icons/fi";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { Divider, Text, Title } from "@tremor/react";
|
import { Divider, Text, Title } from "@tremor/react";
|
||||||
import { fetchSS } from "@/lib/utilsSS";
|
|
||||||
import { ErrorCallout } from "@/components/ErrorCallout";
|
|
||||||
import { Persona } from "./interfaces";
|
|
||||||
import { AssistantsIcon } from "@/components/icons/icons";
|
import { AssistantsIcon } from "@/components/icons/icons";
|
||||||
import { AdminPageTitle } from "@/components/admin/Title";
|
import { AdminPageTitle } from "@/components/admin/Title";
|
||||||
|
|
||||||
export default async function Page() {
|
export default async function Page() {
|
||||||
const allPersonaResponse = await fetchSS("/admin/persona");
|
|
||||||
const editablePersonaResponse = await fetchSS(
|
|
||||||
"/admin/persona?get_editable=true"
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!allPersonaResponse.ok || !editablePersonaResponse.ok) {
|
|
||||||
return (
|
|
||||||
<ErrorCallout
|
|
||||||
errorTitle="Something went wrong :("
|
|
||||||
errorMsg={`Failed to fetch personas - ${
|
|
||||||
(await allPersonaResponse.text()) ||
|
|
||||||
(await editablePersonaResponse.text())
|
|
||||||
}`}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const allPersonas = (await allPersonaResponse.json()) as Persona[];
|
|
||||||
const editablePersonas = (await editablePersonaResponse.json()) as Persona[];
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mx-auto container">
|
<div className="mx-auto container">
|
||||||
<AdminPageTitle icon={<AssistantsIcon size={32} />} title="Assistants" />
|
<AdminPageTitle icon={<AssistantsIcon size={32} />} title="Assistants" />
|
||||||
@@ -64,10 +41,7 @@ export default async function Page() {
|
|||||||
<Divider />
|
<Divider />
|
||||||
|
|
||||||
<Title>Existing Assistants</Title>
|
<Title>Existing Assistants</Title>
|
||||||
<PersonasTable
|
<PersonasTable />
|
||||||
allPersonas={allPersonas}
|
|
||||||
editablePersonas={editablePersonas}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@@ -22,22 +22,14 @@ export default async function GalleryPage({
|
|||||||
const {
|
const {
|
||||||
user,
|
user,
|
||||||
chatSessions,
|
chatSessions,
|
||||||
assistants,
|
|
||||||
folders,
|
folders,
|
||||||
openedFolders,
|
openedFolders,
|
||||||
shouldShowWelcomeModal,
|
shouldShowWelcomeModal,
|
||||||
toggleSidebar,
|
toggleSidebar,
|
||||||
hasAnyConnectors,
|
|
||||||
hasImageCompatibleModel,
|
|
||||||
} = data;
|
} = data;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<AssistantsProvider
|
|
||||||
initialAssistants={assistants}
|
|
||||||
hasAnyConnectors={hasAnyConnectors}
|
|
||||||
hasImageCompatibleModel={hasImageCompatibleModel}
|
|
||||||
>
|
|
||||||
{shouldShowWelcomeModal && <WelcomeModal user={user} />}
|
{shouldShowWelcomeModal && <WelcomeModal user={user} />}
|
||||||
|
|
||||||
<InstantSSRAutoRefresh />
|
<InstantSSRAutoRefresh />
|
||||||
@@ -48,7 +40,6 @@ export default async function GalleryPage({
|
|||||||
folders={folders}
|
folders={folders}
|
||||||
openedFolders={openedFolders}
|
openedFolders={openedFolders}
|
||||||
/>
|
/>
|
||||||
</AssistantsProvider>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@@ -23,20 +23,13 @@ export default async function GalleryPage({
|
|||||||
user,
|
user,
|
||||||
chatSessions,
|
chatSessions,
|
||||||
folders,
|
folders,
|
||||||
assistants,
|
|
||||||
openedFolders,
|
openedFolders,
|
||||||
shouldShowWelcomeModal,
|
shouldShowWelcomeModal,
|
||||||
toggleSidebar,
|
toggleSidebar,
|
||||||
hasAnyConnectors,
|
|
||||||
hasImageCompatibleModel,
|
|
||||||
} = data;
|
} = data;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AssistantsProvider
|
<>
|
||||||
initialAssistants={assistants}
|
|
||||||
hasAnyConnectors={hasAnyConnectors}
|
|
||||||
hasImageCompatibleModel={hasImageCompatibleModel}
|
|
||||||
>
|
|
||||||
{shouldShowWelcomeModal && <WelcomeModal user={user} />}
|
{shouldShowWelcomeModal && <WelcomeModal user={user} />}
|
||||||
|
|
||||||
<InstantSSRAutoRefresh />
|
<InstantSSRAutoRefresh />
|
||||||
@@ -46,6 +39,6 @@ export default async function GalleryPage({
|
|||||||
folders={folders}
|
folders={folders}
|
||||||
openedFolders={openedFolders}
|
openedFolders={openedFolders}
|
||||||
/>
|
/>
|
||||||
</AssistantsProvider>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@@ -32,21 +32,13 @@ export default async function Page({
|
|||||||
openedFolders,
|
openedFolders,
|
||||||
defaultAssistantId,
|
defaultAssistantId,
|
||||||
shouldShowWelcomeModal,
|
shouldShowWelcomeModal,
|
||||||
assistants,
|
|
||||||
userInputPrompts,
|
userInputPrompts,
|
||||||
hasAnyConnectors,
|
|
||||||
hasImageCompatibleModel,
|
|
||||||
} = data;
|
} = data;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<InstantSSRAutoRefresh />
|
<InstantSSRAutoRefresh />
|
||||||
{shouldShowWelcomeModal && <WelcomeModal user={user} />}
|
{shouldShowWelcomeModal && <WelcomeModal user={user} />}
|
||||||
<AssistantsProvider
|
|
||||||
initialAssistants={assistants}
|
|
||||||
hasAnyConnectors={hasAnyConnectors}
|
|
||||||
hasImageCompatibleModel={hasImageCompatibleModel}
|
|
||||||
>
|
|
||||||
<ChatProvider
|
<ChatProvider
|
||||||
value={{
|
value={{
|
||||||
chatSessions,
|
chatSessions,
|
||||||
@@ -63,7 +55,6 @@ export default async function Page({
|
|||||||
>
|
>
|
||||||
<WrappedChat initiallyToggled={toggleSidebar} />
|
<WrappedChat initiallyToggled={toggleSidebar} />
|
||||||
</ChatProvider>
|
</ChatProvider>
|
||||||
</AssistantsProvider>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@@ -19,6 +19,8 @@ import { HeaderTitle } from "@/components/header/HeaderTitle";
|
|||||||
import { Logo } from "@/components/Logo";
|
import { Logo } from "@/components/Logo";
|
||||||
import { UserProvider } from "@/components/user/UserProvider";
|
import { UserProvider } from "@/components/user/UserProvider";
|
||||||
import { ProviderContextProvider } from "@/components/chat_search/ProviderContext";
|
import { ProviderContextProvider } from "@/components/chat_search/ProviderContext";
|
||||||
|
import { fetchAssistantData } from "@/lib/chat/fetchAssistantdata";
|
||||||
|
import { AppProvider } from "@/components/context/AppProvider";
|
||||||
|
|
||||||
const inter = Inter({
|
const inter = Inter({
|
||||||
subsets: ["latin"],
|
subsets: ["latin"],
|
||||||
@@ -55,6 +57,10 @@ export default async function RootLayout({
|
|||||||
}) {
|
}) {
|
||||||
const combinedSettings = await fetchSettingsSS();
|
const combinedSettings = await fetchSettingsSS();
|
||||||
|
|
||||||
|
const data = await fetchAssistantData();
|
||||||
|
|
||||||
|
const { assistants, hasAnyConnectors, hasImageCompatibleModel } = data;
|
||||||
|
|
||||||
const productGating =
|
const productGating =
|
||||||
combinedSettings?.settings.product_gating ?? GatingType.NONE;
|
combinedSettings?.settings.product_gating ?? GatingType.NONE;
|
||||||
|
|
||||||
@@ -174,13 +180,14 @@ export default async function RootLayout({
|
|||||||
process.env.THEME_IS_DARK?.toLowerCase() === "true" ? "dark" : ""
|
process.env.THEME_IS_DARK?.toLowerCase() === "true" ? "dark" : ""
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<UserProvider>
|
<AppProvider
|
||||||
<ProviderContextProvider>
|
settings={combinedSettings}
|
||||||
<SettingsProvider settings={combinedSettings}>
|
assistants={assistants}
|
||||||
|
hasAnyConnectors={hasAnyConnectors}
|
||||||
|
hasImageCompatibleModel={hasImageCompatibleModel}
|
||||||
|
>
|
||||||
{children}
|
{children}
|
||||||
</SettingsProvider>
|
</AppProvider>
|
||||||
</ProviderContextProvider>
|
|
||||||
</UserProvider>
|
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@@ -201,11 +201,6 @@ export default async function Home({
|
|||||||
{/* ChatPopup is a custom popup that displays a admin-specified message on initial user visit.
|
{/* ChatPopup is a custom popup that displays a admin-specified message on initial user visit.
|
||||||
Only used in the EE version of the app. */}
|
Only used in the EE version of the app. */}
|
||||||
<ChatPopup />
|
<ChatPopup />
|
||||||
<AssistantsProvider
|
|
||||||
initialAssistants={assistants}
|
|
||||||
hasAnyConnectors={hasAnyConnectors}
|
|
||||||
hasImageCompatibleModel={false}
|
|
||||||
>
|
|
||||||
<SearchProvider
|
<SearchProvider
|
||||||
value={{
|
value={{
|
||||||
querySessions,
|
querySessions,
|
||||||
@@ -225,8 +220,6 @@ export default async function Home({
|
|||||||
searchTypeDefault={searchTypeDefault}
|
searchTypeDefault={searchTypeDefault}
|
||||||
/>
|
/>
|
||||||
</SearchProvider>
|
</SearchProvider>
|
||||||
</AssistantsProvider>
|
|
||||||
s
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
39
web/src/components/context/AppProvider.tsx
Normal file
39
web/src/components/context/AppProvider.tsx
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
"use server";
|
||||||
|
import { CombinedSettings } from "@/app/admin/settings/interfaces";
|
||||||
|
import { UserProvider } from "../user/UserProvider";
|
||||||
|
import { ProviderContextProvider } from "../chat_search/ProviderContext";
|
||||||
|
import { SettingsProvider } from "../settings/SettingsProvider";
|
||||||
|
import { AssistantsProvider } from "./AssistantsContext";
|
||||||
|
import { Persona } from "@/app/admin/assistants/interfaces";
|
||||||
|
|
||||||
|
interface AppProviderProps {
|
||||||
|
children: React.ReactNode;
|
||||||
|
settings: CombinedSettings;
|
||||||
|
assistants: Persona[];
|
||||||
|
hasAnyConnectors: boolean;
|
||||||
|
hasImageCompatibleModel: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const AppProvider = ({
|
||||||
|
children,
|
||||||
|
settings,
|
||||||
|
assistants,
|
||||||
|
hasAnyConnectors,
|
||||||
|
hasImageCompatibleModel,
|
||||||
|
}: AppProviderProps) => {
|
||||||
|
return (
|
||||||
|
<UserProvider>
|
||||||
|
<ProviderContextProvider>
|
||||||
|
<SettingsProvider settings={settings}>
|
||||||
|
<AssistantsProvider
|
||||||
|
initialAssistants={assistants}
|
||||||
|
hasAnyConnectors={hasAnyConnectors}
|
||||||
|
hasImageCompatibleModel={hasImageCompatibleModel}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</AssistantsProvider>
|
||||||
|
</SettingsProvider>
|
||||||
|
</ProviderContextProvider>
|
||||||
|
</UserProvider>
|
||||||
|
);
|
||||||
|
};
|
@@ -1,5 +1,11 @@
|
|||||||
"use client";
|
"use client";
|
||||||
import React, { createContext, useState, useContext, useMemo } from "react";
|
import React, {
|
||||||
|
createContext,
|
||||||
|
useState,
|
||||||
|
useContext,
|
||||||
|
useMemo,
|
||||||
|
useEffect,
|
||||||
|
} from "react";
|
||||||
import { Persona } from "@/app/admin/assistants/interfaces";
|
import { Persona } from "@/app/admin/assistants/interfaces";
|
||||||
import {
|
import {
|
||||||
classifyAssistants,
|
classifyAssistants,
|
||||||
@@ -15,6 +21,10 @@ interface AssistantsContextProps {
|
|||||||
finalAssistants: Persona[];
|
finalAssistants: Persona[];
|
||||||
ownedButHiddenAssistants: Persona[];
|
ownedButHiddenAssistants: Persona[];
|
||||||
refreshAssistants: () => Promise<void>;
|
refreshAssistants: () => Promise<void>;
|
||||||
|
|
||||||
|
// Admin only
|
||||||
|
editablePersonas: Persona[];
|
||||||
|
allAssistants: Persona[];
|
||||||
}
|
}
|
||||||
|
|
||||||
const AssistantsContext = createContext<AssistantsContextProps | undefined>(
|
const AssistantsContext = createContext<AssistantsContextProps | undefined>(
|
||||||
@@ -35,7 +45,54 @@ export const AssistantsProvider: React.FC<{
|
|||||||
const [assistants, setAssistants] = useState<Persona[]>(
|
const [assistants, setAssistants] = useState<Persona[]>(
|
||||||
initialAssistants || []
|
initialAssistants || []
|
||||||
);
|
);
|
||||||
const { user } = useUser();
|
const { user, isLoadingUser, isAdmin } = useUser();
|
||||||
|
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[]>([]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const fetchAllAssistants = async () => {
|
||||||
|
if (!isAdmin) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch("/api/admin/persona");
|
||||||
|
if (!response.ok) {
|
||||||
|
console.error("Failed to fetch all personas");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const personas = await response.json();
|
||||||
|
setAllAssistants(personas);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching all personas:", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
fetchAllAssistants();
|
||||||
|
}, [isAdmin]);
|
||||||
|
|
||||||
const refreshAssistants = async () => {
|
const refreshAssistants = async () => {
|
||||||
try {
|
try {
|
||||||
@@ -92,7 +149,7 @@ export const AssistantsProvider: React.FC<{
|
|||||||
finalAssistants,
|
finalAssistants,
|
||||||
ownedButHiddenAssistants,
|
ownedButHiddenAssistants,
|
||||||
};
|
};
|
||||||
}, [user, assistants]);
|
}, [user, assistants, isLoadingUser]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AssistantsContext.Provider
|
<AssistantsContext.Provider
|
||||||
@@ -103,6 +160,8 @@ export const AssistantsProvider: React.FC<{
|
|||||||
finalAssistants,
|
finalAssistants,
|
||||||
ownedButHiddenAssistants,
|
ownedButHiddenAssistants,
|
||||||
refreshAssistants,
|
refreshAssistants,
|
||||||
|
editablePersonas,
|
||||||
|
allAssistants,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
|
69
web/src/lib/chat/fetchAssistantdata.ts
Normal file
69
web/src/lib/chat/fetchAssistantdata.ts
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
import { fetchSS } from "@/lib/utilsSS";
|
||||||
|
import { CCPairBasicInfo } from "@/lib/types";
|
||||||
|
import { Persona } from "@/app/admin/assistants/interfaces";
|
||||||
|
import { fetchLLMProvidersSS } from "@/lib/llm/fetchLLMs";
|
||||||
|
import { personaComparator } from "@/app/admin/assistants/lib";
|
||||||
|
import { fetchAssistantsSS } from "../assistants/fetchAssistantsSS";
|
||||||
|
import { checkLLMSupportsImageInput } from "../llm/utils";
|
||||||
|
|
||||||
|
interface AssistantData {
|
||||||
|
assistants: Persona[];
|
||||||
|
hasAnyConnectors: boolean;
|
||||||
|
hasImageCompatibleModel: boolean;
|
||||||
|
}
|
||||||
|
export async function fetchAssistantData(): Promise<AssistantData> {
|
||||||
|
const [assistants, assistantsFetchError] = await fetchAssistantsSS();
|
||||||
|
const ccPairsResponse = await fetchSS("/manage/indexing-status");
|
||||||
|
|
||||||
|
let ccPairs: CCPairBasicInfo[] = [];
|
||||||
|
if (ccPairsResponse?.ok) {
|
||||||
|
ccPairs = await ccPairsResponse.json();
|
||||||
|
} else {
|
||||||
|
console.log(`Failed to fetch connectors - ${ccPairsResponse?.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const hasAnyConnectors = ccPairs.length > 0;
|
||||||
|
|
||||||
|
// if no connectors are setup, only show personas that are pure
|
||||||
|
// passthrough and don't do any retrieval
|
||||||
|
let filteredAssistants = assistants;
|
||||||
|
if (assistantsFetchError) {
|
||||||
|
console.log(`Failed to fetch assistants - ${assistantsFetchError}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove those marked as hidden by an admin
|
||||||
|
filteredAssistants = filteredAssistants.filter(
|
||||||
|
(assistant) => assistant.is_visible
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!hasAnyConnectors) {
|
||||||
|
filteredAssistants = filteredAssistants.filter(
|
||||||
|
(assistant) => assistant.num_chunks === 0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// sort them in priority order
|
||||||
|
filteredAssistants.sort(personaComparator);
|
||||||
|
|
||||||
|
const llmProviders = await fetchLLMProvidersSS();
|
||||||
|
const hasImageCompatibleModel = llmProviders.some(
|
||||||
|
(provider) =>
|
||||||
|
provider.provider === "openai" ||
|
||||||
|
provider.model_names.some((model) => checkLLMSupportsImageInput(model))
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!hasImageCompatibleModel) {
|
||||||
|
filteredAssistants = filteredAssistants.filter(
|
||||||
|
(assistant) =>
|
||||||
|
!assistant.tools.some(
|
||||||
|
(tool) => tool.in_code_tool_id === "ImageGenerationTool"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
assistants: filteredAssistants,
|
||||||
|
hasAnyConnectors,
|
||||||
|
hasImageCompatibleModel,
|
||||||
|
};
|
||||||
|
}
|
@@ -37,7 +37,6 @@ interface FetchChatDataResult {
|
|||||||
ccPairs: CCPairBasicInfo[];
|
ccPairs: CCPairBasicInfo[];
|
||||||
availableSources: ValidSources[];
|
availableSources: ValidSources[];
|
||||||
documentSets: DocumentSet[];
|
documentSets: DocumentSet[];
|
||||||
assistants: Persona[];
|
|
||||||
tags: Tag[];
|
tags: Tag[];
|
||||||
llmProviders: LLMProviderDescriptor[];
|
llmProviders: LLMProviderDescriptor[];
|
||||||
folders: Folder[];
|
folders: Folder[];
|
||||||
@@ -47,8 +46,6 @@ interface FetchChatDataResult {
|
|||||||
finalDocumentSidebarInitialWidth?: number;
|
finalDocumentSidebarInitialWidth?: number;
|
||||||
shouldShowWelcomeModal: boolean;
|
shouldShowWelcomeModal: boolean;
|
||||||
userInputPrompts: InputPrompt[];
|
userInputPrompts: InputPrompt[];
|
||||||
hasAnyConnectors: boolean;
|
|
||||||
hasImageCompatibleModel: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function fetchChatData(searchParams: {
|
export async function fetchChatData(searchParams: {
|
||||||
@@ -59,7 +56,6 @@ export async function fetchChatData(searchParams: {
|
|||||||
getCurrentUserSS(),
|
getCurrentUserSS(),
|
||||||
fetchSS("/manage/indexing-status"),
|
fetchSS("/manage/indexing-status"),
|
||||||
fetchSS("/manage/document-set"),
|
fetchSS("/manage/document-set"),
|
||||||
fetchAssistantsSS(),
|
|
||||||
fetchSS("/chat/get-user-chat-sessions"),
|
fetchSS("/chat/get-user-chat-sessions"),
|
||||||
fetchSS("/query/valid-tags"),
|
fetchSS("/query/valid-tags"),
|
||||||
fetchLLMProvidersSS(),
|
fetchLLMProvidersSS(),
|
||||||
@@ -76,7 +72,7 @@ export async function fetchChatData(searchParams: {
|
|||||||
| LLMProviderDescriptor[]
|
| LLMProviderDescriptor[]
|
||||||
| [Persona[], string | null]
|
| [Persona[], string | null]
|
||||||
| null
|
| null
|
||||||
)[] = [null, null, null, null, null, null, null, null, null, null];
|
)[] = [null, null, null, null, null, null, null, null, null];
|
||||||
try {
|
try {
|
||||||
results = await Promise.all(tasks);
|
results = await Promise.all(tasks);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -87,17 +83,13 @@ export async function fetchChatData(searchParams: {
|
|||||||
const user = results[1] as User | null;
|
const user = results[1] as User | null;
|
||||||
const ccPairsResponse = results[2] as Response | null;
|
const ccPairsResponse = results[2] as Response | null;
|
||||||
const documentSetsResponse = results[3] as Response | null;
|
const documentSetsResponse = results[3] as Response | null;
|
||||||
const [rawAssistantsList, assistantsFetchError] = results[4] as [
|
|
||||||
Persona[],
|
|
||||||
string | null,
|
|
||||||
];
|
|
||||||
|
|
||||||
const chatSessionsResponse = results[5] as Response | null;
|
const chatSessionsResponse = results[4] as Response | null;
|
||||||
|
|
||||||
const tagsResponse = results[6] as Response | null;
|
const tagsResponse = results[5] as Response | null;
|
||||||
const llmProviders = (results[7] || []) as LLMProviderDescriptor[];
|
const llmProviders = (results[6] || []) as LLMProviderDescriptor[];
|
||||||
const foldersResponse = results[8] as Response | null;
|
const foldersResponse = results[7] as Response | null;
|
||||||
const userInputPromptsResponse = results[9] as Response | null;
|
const userInputPromptsResponse = results[8] as Response | null;
|
||||||
|
|
||||||
const authDisabled = authTypeMetadata?.authType === "disabled";
|
const authDisabled = authTypeMetadata?.authType === "disabled";
|
||||||
if (!authDisabled && !user) {
|
if (!authDisabled && !user) {
|
||||||
@@ -161,17 +153,6 @@ export async function fetchChatData(searchParams: {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let assistants = rawAssistantsList;
|
|
||||||
if (assistantsFetchError) {
|
|
||||||
console.log(`Failed to fetch assistants - ${assistantsFetchError}`);
|
|
||||||
}
|
|
||||||
// remove those marked as hidden by an admin
|
|
||||||
|
|
||||||
assistants = assistants.filter((assistant) => assistant.is_visible);
|
|
||||||
|
|
||||||
// sort them in priority order
|
|
||||||
assistants.sort(personaComparator);
|
|
||||||
|
|
||||||
let tags: Tag[] = [];
|
let tags: Tag[] = [];
|
||||||
if (tagsResponse?.ok) {
|
if (tagsResponse?.ok) {
|
||||||
tags = (await tagsResponse.json()).tags;
|
tags = (await tagsResponse.json()).tags;
|
||||||
@@ -206,24 +187,6 @@ export async function fetchChatData(searchParams: {
|
|||||||
|
|
||||||
// if no connectors are setup, only show personas that are pure
|
// if no connectors are setup, only show personas that are pure
|
||||||
// passthrough and don't do any retrieval
|
// passthrough and don't do any retrieval
|
||||||
if (!hasAnyConnectors) {
|
|
||||||
assistants = assistants.filter((assistant) => assistant.num_chunks === 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
const hasImageCompatibleModel = llmProviders.some(
|
|
||||||
(provider) =>
|
|
||||||
provider.provider === "openai" ||
|
|
||||||
provider.model_names.some((model) => checkLLMSupportsImageInput(model))
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!hasImageCompatibleModel) {
|
|
||||||
assistants = assistants.filter(
|
|
||||||
(assistant) =>
|
|
||||||
!assistant.tools.some(
|
|
||||||
(tool) => tool.in_code_tool_id === "ImageGenerationTool"
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let folders: Folder[] = [];
|
let folders: Folder[] = [];
|
||||||
if (foldersResponse?.ok) {
|
if (foldersResponse?.ok) {
|
||||||
@@ -243,7 +206,6 @@ export async function fetchChatData(searchParams: {
|
|||||||
ccPairs,
|
ccPairs,
|
||||||
availableSources,
|
availableSources,
|
||||||
documentSets,
|
documentSets,
|
||||||
assistants,
|
|
||||||
tags,
|
tags,
|
||||||
llmProviders,
|
llmProviders,
|
||||||
folders,
|
folders,
|
||||||
@@ -253,7 +215,5 @@ export async function fetchChatData(searchParams: {
|
|||||||
toggleSidebar,
|
toggleSidebar,
|
||||||
shouldShowWelcomeModal,
|
shouldShowWelcomeModal,
|
||||||
userInputPrompts,
|
userInputPrompts,
|
||||||
hasAnyConnectors,
|
|
||||||
hasImageCompatibleModel,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user