From 0e53f34155b64af31ca32cc3cee914c0b9bc904e Mon Sep 17 00:00:00 2001 From: Weves Date: Thu, 3 Jul 2025 17:35:57 -0700 Subject: [PATCH 01/10] claude stuff --- backend/onyx/db/persona.py | 21 ++++- backend/onyx/server/features/persona/api.py | 29 ++++++- .../onyx/server/features/persona/models.py | 84 +++++++++++++++++-- 3 files changed, 123 insertions(+), 11 deletions(-) diff --git a/backend/onyx/db/persona.py b/backend/onyx/db/persona.py index 7bf9a1ab451..741e20e46de 100644 --- a/backend/onyx/db/persona.py +++ b/backend/onyx/db/persona.py @@ -22,6 +22,7 @@ from onyx.configs.constants import NotificationType from onyx.context.search.enums import RecencyBiasSetting from onyx.db.constants import SLACK_BOT_PERSONA_PREFIX +from onyx.db.models import ConnectorCredentialPair from onyx.db.models import DocumentSet from onyx.db.models import Persona from onyx.db.models import Persona__User @@ -332,6 +333,7 @@ def get_personas_for_user( joinedload_all: bool = False, # a bit jank include_prompt: bool = True, + minimal_load: bool = False, ) -> Sequence[Persona]: stmt = select(Persona) stmt = _add_user_filters(stmt, user, get_editable) @@ -343,10 +345,25 @@ def get_personas_for_user( if not include_deleted: stmt = stmt.where(Persona.deleted.is_(False)) - if joinedload_all: + # Always load the owner/user relationship for the owner field + stmt = stmt.options(selectinload(Persona.user)) + + if minimal_load: + # For list views, only load tools (needed for filtering) + stmt = stmt.options( + selectinload(Persona.tools), + ) + elif joinedload_all: stmt = stmt.options( selectinload(Persona.tools), - selectinload(Persona.document_sets), + selectinload(Persona.document_sets) + .selectinload(DocumentSet.connector_credential_pairs) + .selectinload(ConnectorCredentialPair.connector), + selectinload(Persona.document_sets) + .selectinload(DocumentSet.connector_credential_pairs) + .selectinload(ConnectorCredentialPair.credential), + selectinload(Persona.document_sets).selectinload(DocumentSet.users), + selectinload(Persona.document_sets).selectinload(DocumentSet.groups), selectinload(Persona.groups), selectinload(Persona.users), selectinload(Persona.labels), diff --git a/backend/onyx/server/features/persona/api.py b/backend/onyx/server/features/persona/api.py index 4375a936473..141d8d3c81d 100644 --- a/backend/onyx/server/features/persona/api.py +++ b/backend/onyx/server/features/persona/api.py @@ -1,3 +1,4 @@ +import time from uuid import UUID from fastapi import APIRouter @@ -393,20 +394,30 @@ def list_personas( db_session: Session = Depends(get_session), include_deleted: bool = False, persona_ids: list[int] = Query(None), + lightweight: bool = Query( + True, description="Return lightweight response for better performance" + ), ) -> list[PersonaSnapshot]: + start_time = time.time() + + # Use minimal loading for better performance + db_start = time.time() personas = get_personas_for_user( user=user, include_deleted=include_deleted, db_session=db_session, get_editable=False, - joinedload_all=True, + joinedload_all=not lightweight, # Only load all data if not lightweight + minimal_load=lightweight, include_prompt=False, ) + db_time = time.time() - db_start if persona_ids: personas = [p for p in personas if p.id in persona_ids] # Filter out personas with unavailable tools + filter_start = time.time() personas = [ p for p in personas @@ -415,8 +426,22 @@ def list_personas( and not is_image_generation_available(db_session=db_session) ) ] + filter_time = time.time() - filter_start + + serialization_start = time.time() + result = [PersonaSnapshot.from_model(p) for p in personas] + serialization_time = time.time() - serialization_start + + total_time = time.time() - start_time + + logger.info( + f"list_personas performance - Total: {total_time:.3f}s, " + f"DB: {db_time:.3f}s, Filter: {filter_time:.3f}s, " + f"Serialization: {serialization_time:.3f}s, " + f"Count: {len(result)}, Lightweight: {lightweight}" + ) - return [PersonaSnapshot.from_model(p) for p in personas] + return result @basic_router.get("/{persona_id}") diff --git a/backend/onyx/server/features/persona/models.py b/backend/onyx/server/features/persona/models.py index 7549cbc476c..9c1a984b318 100644 --- a/backend/onyx/server/features/persona/models.py +++ b/backend/onyx/server/features/persona/models.py @@ -18,6 +18,68 @@ logger = setup_logger() +class PersonaListSnapshot(BaseModel): + """Lightweight persona model for list views - minimal data for performance""" + + id: int + name: str + description: str + is_public: bool + is_visible: bool + icon_shape: int | None + icon_color: str | None + uploaded_image_id: str | None + display_priority: int | None + is_default_persona: bool + builtin_persona: bool + owner: MinimalUserSnapshot | None + # Empty arrays to maintain compatibility with frontend + tools: list[ToolSnapshot] = Field(default_factory=list) + document_sets: list[DocumentSet] = Field(default_factory=list) + users: list[MinimalUserSnapshot] = Field(default_factory=list) + groups: list[int] = Field(default_factory=list) + user_file_ids: list[int] = Field(default_factory=list) + user_folder_ids: list[int] = Field(default_factory=list) + labels: list["PersonaLabelSnapshot"] = Field(default_factory=list) + starter_messages: list[StarterMessage] | None = None + llm_model_provider_override: str | None = None + llm_model_version_override: str | None = None + num_chunks: float | None = None + + @classmethod + def from_model(cls, persona: Persona) -> "PersonaListSnapshot": + return PersonaListSnapshot( + id=persona.id, + name=persona.name, + description=persona.description, + is_public=persona.is_public, + is_visible=persona.is_visible, + icon_shape=persona.icon_shape, + icon_color=persona.icon_color, + uploaded_image_id=persona.uploaded_image_id, + display_priority=persona.display_priority, + is_default_persona=persona.is_default_persona, + builtin_persona=persona.builtin_persona, + owner=( + MinimalUserSnapshot(id=persona.user.id, email=persona.user.email) + if persona.user + else None + ), + starter_messages=persona.starter_messages, + llm_model_provider_override=persona.llm_model_provider_override, + llm_model_version_override=persona.llm_model_version_override, + num_chunks=persona.num_chunks, + # Return empty arrays for list view - these aren't loaded + tools=[], + document_sets=[], + users=[], + groups=[], + user_file_ids=[], + user_folder_ids=[], + labels=[], + ) + + class PromptSnapshot(BaseModel): id: int name: str @@ -116,6 +178,7 @@ class PersonaSnapshot(BaseModel): @classmethod def from_model(cls, persona: Persona) -> "PersonaSnapshot": + # Safely handle potentially unloaded relationships return PersonaSnapshot( id=persona.id, name=persona.name, @@ -125,14 +188,21 @@ def from_model(cls, persona: Persona) -> "PersonaSnapshot": icon_shape=persona.icon_shape, icon_color=persona.icon_color, uploaded_image_id=persona.uploaded_image_id, - user_file_ids=[file.id for file in persona.user_files], - user_folder_ids=[folder.id for folder in persona.user_folders], + user_file_ids=[file.id for file in getattr(persona, "user_files", [])], + user_folder_ids=[ + folder.id for folder in getattr(persona, "user_folders", []) + ], display_priority=persona.display_priority, is_default_persona=persona.is_default_persona, builtin_persona=persona.builtin_persona, starter_messages=persona.starter_messages, - tools=[ToolSnapshot.from_model(tool) for tool in persona.tools], - labels=[PersonaLabelSnapshot.from_model(label) for label in persona.labels], + tools=[ + ToolSnapshot.from_model(tool) for tool in getattr(persona, "tools", []) + ], + labels=[ + PersonaLabelSnapshot.from_model(label) + for label in getattr(persona, "labels", []) + ], owner=( MinimalUserSnapshot(id=persona.user.id, email=persona.user.email) if persona.user @@ -140,12 +210,12 @@ def from_model(cls, persona: Persona) -> "PersonaSnapshot": ), users=[ MinimalUserSnapshot(id=user.id, email=user.email) - for user in persona.users + for user in getattr(persona, "users", []) ], - groups=[user_group.id for user_group in persona.groups], + groups=[user_group.id for user_group in getattr(persona, "groups", [])], document_sets=[ DocumentSet.from_model(document_set_model) - for document_set_model in persona.document_sets + for document_set_model in getattr(persona, "document_sets", []) ], llm_model_provider_override=persona.llm_model_provider_override, llm_model_version_override=persona.llm_model_version_override, From 3176c67b94241eb2599874870ee95663321a5737 Mon Sep 17 00:00:00 2001 From: Weves Date: Thu, 3 Jul 2025 19:11:49 -0700 Subject: [PATCH 02/10] Send over less Assistant data --- backend/onyx/db/persona.py | 63 ++++++++++-------- backend/onyx/server/features/persona/api.py | 35 ++-------- .../onyx/server/features/persona/models.py | 64 +++++++++--------- web/src/app/admin/assistants/interfaces.ts | 25 +++++++ web/src/app/admin/assistants/lib.ts | 7 +- web/src/app/assistants/mine/AssistantCard.tsx | 7 +- web/src/app/chat/ChatIntro.tsx | 8 ++- web/src/app/chat/ChatPage.tsx | 22 +++---- web/src/app/chat/RegenerateOption.tsx | 4 +- web/src/app/chat/input/ChatInputBar.tsx | 12 ++-- web/src/app/chat/input/LLMPopover.tsx | 7 +- web/src/app/chat/lib.tsx | 21 +++--- web/src/app/chat/message/AgenticMessage.tsx | 6 +- web/src/app/chat/message/Messages.tsx | 6 +- .../chat/sessionSidebar/HistorySidebar.tsx | 20 +++--- .../components/assistants/AssistantIcon.tsx | 4 +- .../components/assistants/StarterMessage.tsx | 4 +- .../components/context/AssistantsContext.tsx | 66 ++++++++++--------- web/src/lib/assistants/checkOwnership.ts | 9 ++- web/src/lib/assistants/fetchAssistantsSS.ts | 6 +- web/src/lib/assistants/utils.ts | 44 +++++-------- web/src/lib/chat/fetchAssistantdata.ts | 13 ++-- web/src/lib/hooks.ts | 10 ++- web/src/lib/llm/utils.ts | 6 +- 24 files changed, 243 insertions(+), 226 deletions(-) diff --git a/backend/onyx/db/persona.py b/backend/onyx/db/persona.py index 741e20e46de..c2e2dc134be 100644 --- a/backend/onyx/db/persona.py +++ b/backend/onyx/db/persona.py @@ -1,5 +1,6 @@ from collections.abc import Sequence from datetime import datetime +from enum import Enum from uuid import UUID from fastapi import HTTPException @@ -11,7 +12,6 @@ from sqlalchemy import update from sqlalchemy.orm import aliased from sqlalchemy.orm import joinedload -from sqlalchemy.orm import selectinload from sqlalchemy.orm import Session from onyx.auth.schemas import UserRole @@ -46,6 +46,12 @@ logger = setup_logger() +class PersonaLoadType(Enum): + NONE = "none" + MINIMAL = "minimal" + FULL = "full" + + def _add_user_filters( stmt: Select, user: User | None, get_editable: bool = True ) -> Select: @@ -324,16 +330,13 @@ def update_persona_public_status( def get_personas_for_user( # if user is `None` assume the user is an admin or auth is disabled + load_type: PersonaLoadType, # defines how much of the persona to pre-load user: User | None, db_session: Session, get_editable: bool = True, include_default: bool = True, include_slack_bot_personas: bool = False, include_deleted: bool = False, - joinedload_all: bool = False, - # a bit jank - include_prompt: bool = True, - minimal_load: bool = False, ) -> Sequence[Persona]: stmt = select(Persona) stmt = _add_user_filters(stmt, user, get_editable) @@ -345,35 +348,37 @@ def get_personas_for_user( if not include_deleted: stmt = stmt.where(Persona.deleted.is_(False)) - # Always load the owner/user relationship for the owner field - stmt = stmt.options(selectinload(Persona.user)) - - if minimal_load: - # For list views, only load tools (needed for filtering) + if load_type == PersonaLoadType.MINIMAL: + # For ChatPage, only load essential relationships stmt = stmt.options( - selectinload(Persona.tools), + # Used for retrieval capability checking + joinedload(Persona.tools), + # Used for filtering + joinedload(Persona.labels), + # only show document sets in the UI that the assistant has access to + joinedload(Persona.document_sets), ) - elif joinedload_all: + elif load_type == PersonaLoadType.FULL: stmt = stmt.options( - selectinload(Persona.tools), - selectinload(Persona.document_sets) - .selectinload(DocumentSet.connector_credential_pairs) - .selectinload(ConnectorCredentialPair.connector), - selectinload(Persona.document_sets) - .selectinload(DocumentSet.connector_credential_pairs) - .selectinload(ConnectorCredentialPair.credential), - selectinload(Persona.document_sets).selectinload(DocumentSet.users), - selectinload(Persona.document_sets).selectinload(DocumentSet.groups), - selectinload(Persona.groups), - selectinload(Persona.users), - selectinload(Persona.labels), - selectinload(Persona.user_files), - selectinload(Persona.user_folders), + joinedload(Persona.user), + joinedload(Persona.tools), + joinedload(Persona.document_sets) + .joinedload(DocumentSet.connector_credential_pairs) + .joinedload(ConnectorCredentialPair.connector), + joinedload(Persona.document_sets) + .joinedload(DocumentSet.connector_credential_pairs) + .joinedload(ConnectorCredentialPair.credential), + joinedload(Persona.document_sets).joinedload(DocumentSet.users), + joinedload(Persona.document_sets).joinedload(DocumentSet.groups), + joinedload(Persona.groups), + joinedload(Persona.users), + joinedload(Persona.labels), + joinedload(Persona.user_files), + joinedload(Persona.user_folders), + joinedload(Persona.prompts), ) - if include_prompt: - stmt = stmt.options(selectinload(Persona.prompts)) - results = db_session.execute(stmt).scalars().all() + results = db_session.execute(stmt).unique().scalars().all() return results diff --git a/backend/onyx/server/features/persona/api.py b/backend/onyx/server/features/persona/api.py index 141d8d3c81d..86601603670 100644 --- a/backend/onyx/server/features/persona/api.py +++ b/backend/onyx/server/features/persona/api.py @@ -1,4 +1,3 @@ -import time from uuid import UUID from fastapi import APIRouter @@ -30,6 +29,7 @@ from onyx.db.persona import get_personas_for_user from onyx.db.persona import mark_persona_as_deleted from onyx.db.persona import mark_persona_as_not_deleted +from onyx.db.persona import PersonaLoadType from onyx.db.persona import update_all_personas_display_priority from onyx.db.persona import update_persona_is_default from onyx.db.persona import update_persona_label @@ -46,6 +46,7 @@ from onyx.server.features.persona.models import FullPersonaSnapshot from onyx.server.features.persona.models import GenerateStarterMessageRequest from onyx.server.features.persona.models import ImageGenerationToolStatus +from onyx.server.features.persona.models import MinimalPersonaSnapshot from onyx.server.features.persona.models import PersonaLabelCreate from onyx.server.features.persona.models import PersonaLabelResponse from onyx.server.features.persona.models import PersonaSharedNotificationData @@ -155,7 +156,7 @@ def list_personas_admin( user=user, get_editable=get_editable, include_deleted=include_deleted, - joinedload_all=True, + load_type=PersonaLoadType.FULL, ) ] @@ -394,30 +395,19 @@ def list_personas( db_session: Session = Depends(get_session), include_deleted: bool = False, persona_ids: list[int] = Query(None), - lightweight: bool = Query( - True, description="Return lightweight response for better performance" - ), -) -> list[PersonaSnapshot]: - start_time = time.time() - - # Use minimal loading for better performance - db_start = time.time() +) -> list[MinimalPersonaSnapshot]: personas = get_personas_for_user( + load_type=PersonaLoadType.MINIMAL, user=user, include_deleted=include_deleted, db_session=db_session, get_editable=False, - joinedload_all=not lightweight, # Only load all data if not lightweight - minimal_load=lightweight, - include_prompt=False, ) - db_time = time.time() - db_start if persona_ids: personas = [p for p in personas if p.id in persona_ids] # Filter out personas with unavailable tools - filter_start = time.time() personas = [ p for p in personas @@ -426,21 +416,8 @@ def list_personas( and not is_image_generation_available(db_session=db_session) ) ] - filter_time = time.time() - filter_start - - serialization_start = time.time() - result = [PersonaSnapshot.from_model(p) for p in personas] - serialization_time = time.time() - serialization_start - - total_time = time.time() - start_time - - logger.info( - f"list_personas performance - Total: {total_time:.3f}s, " - f"DB: {db_time:.3f}s, Filter: {filter_time:.3f}s, " - f"Serialization: {serialization_time:.3f}s, " - f"Count: {len(result)}, Lightweight: {lightweight}" - ) + result = [MinimalPersonaSnapshot.from_model(p) for p in personas] return result diff --git a/backend/onyx/server/features/persona/models.py b/backend/onyx/server/features/persona/models.py index 9c1a984b318..ab6ee4b3892 100644 --- a/backend/onyx/server/features/persona/models.py +++ b/backend/onyx/server/features/persona/models.py @@ -18,65 +18,61 @@ logger = setup_logger() -class PersonaListSnapshot(BaseModel): - """Lightweight persona model for list views - minimal data for performance""" +class MinimalPersonaSnapshot(BaseModel): + """Minimal persona model optimized for ChatPage.tsx - only includes fields actually used""" + # Core fields used by ChatPage id: int name: str description: str - is_public: bool - is_visible: bool + tools: list[ToolSnapshot] + starter_messages: list[StarterMessage] | None + document_sets: list[DocumentSet] + llm_model_version_override: str | None + llm_model_provider_override: str | None + + uploaded_image_id: str | None icon_shape: int | None icon_color: str | None - uploaded_image_id: str | None + + is_public: bool + is_visible: bool display_priority: int | None is_default_persona: bool builtin_persona: bool + + labels: list["PersonaLabelSnapshot"] owner: MinimalUserSnapshot | None - # Empty arrays to maintain compatibility with frontend - tools: list[ToolSnapshot] = Field(default_factory=list) - document_sets: list[DocumentSet] = Field(default_factory=list) - users: list[MinimalUserSnapshot] = Field(default_factory=list) - groups: list[int] = Field(default_factory=list) - user_file_ids: list[int] = Field(default_factory=list) - user_folder_ids: list[int] = Field(default_factory=list) - labels: list["PersonaLabelSnapshot"] = Field(default_factory=list) - starter_messages: list[StarterMessage] | None = None - llm_model_provider_override: str | None = None - llm_model_version_override: str | None = None - num_chunks: float | None = None @classmethod - def from_model(cls, persona: Persona) -> "PersonaListSnapshot": - return PersonaListSnapshot( + def from_model(cls, persona: Persona) -> "MinimalPersonaSnapshot": + return MinimalPersonaSnapshot( + # Core fields actually used by ChatPage id=persona.id, name=persona.name, description=persona.description, - is_public=persona.is_public, - is_visible=persona.is_visible, + tools=[ToolSnapshot.from_model(tool) for tool in persona.tools], + starter_messages=persona.starter_messages, + document_sets=[ + DocumentSet.from_model(document_set) + for document_set in persona.document_sets + ], + llm_model_version_override=persona.llm_model_version_override, + llm_model_provider_override=persona.llm_model_provider_override, + uploaded_image_id=persona.uploaded_image_id, icon_shape=persona.icon_shape, icon_color=persona.icon_color, - uploaded_image_id=persona.uploaded_image_id, + is_public=persona.is_public, + is_visible=persona.is_visible, display_priority=persona.display_priority, is_default_persona=persona.is_default_persona, builtin_persona=persona.builtin_persona, + labels=[PersonaLabelSnapshot.from_model(label) for label in persona.labels], owner=( MinimalUserSnapshot(id=persona.user.id, email=persona.user.email) if persona.user else None ), - starter_messages=persona.starter_messages, - llm_model_provider_override=persona.llm_model_provider_override, - llm_model_version_override=persona.llm_model_version_override, - num_chunks=persona.num_chunks, - # Return empty arrays for list view - these aren't loaded - tools=[], - document_sets=[], - users=[], - groups=[], - user_file_ids=[], - user_folder_ids=[], - labels=[], ) diff --git a/web/src/app/admin/assistants/interfaces.ts b/web/src/app/admin/assistants/interfaces.ts index 77fd63dfa51..5411cecbc55 100644 --- a/web/src/app/admin/assistants/interfaces.ts +++ b/web/src/app/admin/assistants/interfaces.ts @@ -18,6 +18,31 @@ export interface Prompt { datetime_aware: boolean; default_prompt: boolean; } + +export interface MinimalPersonaSnapshot { + id: number; + name: string; + description: string; + tools: ToolSnapshot[]; + starter_messages: StarterMessage[] | null; + document_sets: DocumentSet[]; + llm_model_version_override?: string; + llm_model_provider_override?: string; + + uploaded_image_id?: string; + icon_shape?: number; + icon_color?: string; + + is_public: boolean; + is_visible: boolean; + display_priority: number | null; + is_default_persona: boolean; + builtin_persona: boolean; + + labels: PersonaLabel[]; + owner: MinimalUserSnapshot | null; +} + export interface Persona { id: number; name: string; diff --git a/web/src/app/admin/assistants/lib.ts b/web/src/app/admin/assistants/lib.ts index 2cfce111272..9194c6585ca 100644 --- a/web/src/app/admin/assistants/lib.ts +++ b/web/src/app/admin/assistants/lib.ts @@ -1,5 +1,5 @@ import { LLMProviderView } from "../configuration/llm/interfaces"; -import { Persona, StarterMessage } from "./interfaces"; +import { MinimalPersonaSnapshot, Persona, StarterMessage } from "./interfaces"; interface PersonaUpsertRequest { name: string; @@ -250,7 +250,10 @@ function closerToZeroNegativesFirstComparator(a: number, b: number) { return absA > absB ? 1 : -1; } -export function personaComparator(a: Persona, b: Persona) { +export function personaComparator( + a: MinimalPersonaSnapshot, + b: MinimalPersonaSnapshot +) { if (a.display_priority === null && b.display_priority === null) { return closerToZeroNegativesFirstComparator(a.id, b.id); } diff --git a/web/src/app/assistants/mine/AssistantCard.tsx b/web/src/app/assistants/mine/AssistantCard.tsx index 79456785236..d46715fe300 100644 --- a/web/src/app/assistants/mine/AssistantCard.tsx +++ b/web/src/app/assistants/mine/AssistantCard.tsx @@ -15,7 +15,10 @@ import { PopoverContent, } from "@/components/ui/popover"; import { AssistantIcon } from "@/components/assistants/AssistantIcon"; -import { Persona } from "@/app/admin/assistants/interfaces"; +import { + MinimalPersonaSnapshot, + Persona, +} from "@/app/admin/assistants/interfaces"; import { useUser } from "@/components/user/UserProvider"; import { useAssistants } from "@/components/context/AssistantsContext"; import { checkUserOwnsAssistant } from "@/lib/assistants/utils"; @@ -54,7 +57,7 @@ export const AssistantBadge = ({ }; const AssistantCard: React.FC<{ - persona: Persona; + persona: MinimalPersonaSnapshot; pinned: boolean; closeModal: () => void; }> = ({ persona, pinned, closeModal }) => { diff --git a/web/src/app/chat/ChatIntro.tsx b/web/src/app/chat/ChatIntro.tsx index 7944b29fcd5..2a7e836059f 100644 --- a/web/src/app/chat/ChatIntro.tsx +++ b/web/src/app/chat/ChatIntro.tsx @@ -1,7 +1,11 @@ import { AssistantIcon } from "@/components/assistants/AssistantIcon"; -import { Persona } from "../admin/assistants/interfaces"; +import { MinimalPersonaSnapshot } from "../admin/assistants/interfaces"; -export function ChatIntro({ selectedPersona }: { selectedPersona: Persona }) { +export function ChatIntro({ + selectedPersona, +}: { + selectedPersona: MinimalPersonaSnapshot; +}) { return (
diff --git a/web/src/app/chat/ChatPage.tsx b/web/src/app/chat/ChatPage.tsx index f71f60f18cd..76835df155e 100644 --- a/web/src/app/chat/ChatPage.tsx +++ b/web/src/app/chat/ChatPage.tsx @@ -29,7 +29,7 @@ import { import Prism from "prismjs"; import Cookies from "js-cookie"; import { HistorySidebar } from "./sessionSidebar/HistorySidebar"; -import { Persona } from "../admin/assistants/interfaces"; +import { MinimalPersonaSnapshot } from "../admin/assistants/interfaces"; import { HealthCheckBanner } from "@/components/health/healthcheck"; import { buildChatUrl, @@ -405,7 +405,7 @@ export function ChatPage({ const existingChatSessionAssistantId = selectedChatSession?.persona_id; const [selectedAssistant, setSelectedAssistant] = useState< - Persona | undefined + MinimalPersonaSnapshot | undefined >( // NOTE: look through available assistants here, so that even if the user // has hidden this assistant it still shows the correct assistant when @@ -435,7 +435,7 @@ export function ChatPage({ }; const [alternativeAssistant, setAlternativeAssistant] = - useState(null); + useState(null); const [presentingDocument, setPresentingDocument] = useState(null); @@ -446,7 +446,7 @@ export function ChatPage({ // 3. First pinned assistants (ordered list of pinned assistants) // 4. Available assistants (ordered list of available assistants) // Relevant test: `live_assistant.spec.ts` - const liveAssistant: Persona | undefined = useMemo( + const liveAssistant: MinimalPersonaSnapshot | undefined = useMemo( () => alternativeAssistant || selectedAssistant || @@ -535,7 +535,7 @@ export function ChatPage({ // 2. we "@"ed the `GPT` assistant and sent a message // 3. while the `GPT` assistant message is generating, we "@" the `Paraphrase` assistant const [alternativeGeneratingAssistant, setAlternativeGeneratingAssistant] = - useState(null); + useState(null); // used to track whether or not the initial "submit on load" has been performed // this only applies if `?submit-on-load=true` or `?submit-on-load=1` is in the URL @@ -1327,7 +1327,7 @@ export function ChatPage({ queryOverride?: string; forceSearch?: boolean; isSeededChat?: boolean; - alternativeAssistantOverride?: Persona | null; + alternativeAssistantOverride?: MinimalPersonaSnapshot | null; modelOverride?: LlmDescriptor; regenerationRequest?: RegenerationRequest | null; overrideFileDescriptors?: FileDescriptor[]; @@ -2197,10 +2197,7 @@ export function ChatPage({ useEffect(() => { if (liveAssistant) { const hasSearchTool = liveAssistant.tools.some( - (tool) => - tool.in_code_tool_id === SEARCH_TOOL_ID && - liveAssistant.user_file_ids?.length == 0 && - liveAssistant.user_folder_ids?.length == 0 + (tool) => tool.in_code_tool_id === SEARCH_TOOL_ID ); setRetrievalEnabled(hasSearchTool); if (!hasSearchTool) { @@ -2212,10 +2209,7 @@ export function ChatPage({ const [retrievalEnabled, setRetrievalEnabled] = useState(() => { if (liveAssistant) { return liveAssistant.tools.some( - (tool) => - tool.in_code_tool_id === SEARCH_TOOL_ID && - liveAssistant.user_file_ids?.length == 0 && - liveAssistant.user_folder_ids?.length == 0 + (tool) => tool.in_code_tool_id === SEARCH_TOOL_ID ); } return false; diff --git a/web/src/app/chat/RegenerateOption.tsx b/web/src/app/chat/RegenerateOption.tsx index 8446e15a9cf..36b1972e70e 100644 --- a/web/src/app/chat/RegenerateOption.tsx +++ b/web/src/app/chat/RegenerateOption.tsx @@ -5,7 +5,7 @@ import { useLlmManager, } from "@/lib/hooks"; -import { Persona } from "@/app/admin/assistants/interfaces"; +import { MinimalPersonaSnapshot } from "@/app/admin/assistants/interfaces"; import { parseLlmDescriptor } from "@/lib/llm/utils"; import { useState } from "react"; import { Hoverable } from "@/components/Hoverable"; @@ -19,7 +19,7 @@ export default function RegenerateOption({ overriddenModel, onDropdownVisibleChange, }: { - selectedAssistant: Persona; + selectedAssistant: MinimalPersonaSnapshot; regenerate: (modelOverRide: LlmDescriptor) => Promise; overriddenModel?: string; onDropdownVisibleChange: (isVisible: boolean) => void; diff --git a/web/src/app/chat/input/ChatInputBar.tsx b/web/src/app/chat/input/ChatInputBar.tsx index a2c04905ccf..cb9fae049c5 100644 --- a/web/src/app/chat/input/ChatInputBar.tsx +++ b/web/src/app/chat/input/ChatInputBar.tsx @@ -2,7 +2,7 @@ import React, { useContext, useEffect, useMemo, useRef, useState } from "react"; import { FiPlusCircle, FiPlus, FiX, FiFilter } from "react-icons/fi"; import { FiLoader } from "react-icons/fi"; import { ChatInputOption } from "./ChatInputOption"; -import { Persona } from "@/app/admin/assistants/interfaces"; +import { MinimalPersonaSnapshot } from "@/app/admin/assistants/interfaces"; import LLMPopover from "./LLMPopover"; import { InputPrompt } from "@/app/chat/interfaces"; @@ -182,10 +182,12 @@ interface ChatInputBarProps { onSubmit: () => void; llmManager: LlmManager; chatState: ChatState; - alternativeAssistant: Persona | null; + alternativeAssistant: MinimalPersonaSnapshot | null; // assistants - selectedAssistant: Persona; - setAlternativeAssistant: (alternativeAssistant: Persona | null) => void; + selectedAssistant: MinimalPersonaSnapshot; + setAlternativeAssistant: ( + alternativeAssistant: MinimalPersonaSnapshot | null + ) => void; toggleDocumentSidebar: () => void; setFiles: (files: FileDescriptor[]) => void; handleFileUpload: (files: File[]) => void; @@ -306,7 +308,7 @@ export function ChatInputBar({ }; }, []); - const updatedTaggedAssistant = (assistant: Persona) => { + const updatedTaggedAssistant = (assistant: MinimalPersonaSnapshot) => { setAlternativeAssistant( assistant.id == selectedAssistant.id ? null : assistant ); diff --git a/web/src/app/chat/input/LLMPopover.tsx b/web/src/app/chat/input/LLMPopover.tsx index f5e6df3947a..5704d623707 100644 --- a/web/src/app/chat/input/LLMPopover.tsx +++ b/web/src/app/chat/input/LLMPopover.tsx @@ -8,7 +8,10 @@ import { getDisplayNameForModel, LlmDescriptor } from "@/lib/hooks"; import { modelSupportsImageInput } from "@/lib/llm/utils"; import { LLMProviderDescriptor } from "@/app/admin/configuration/llm/interfaces"; import { getProviderIcon } from "@/app/admin/configuration/llm/utils"; -import { Persona } from "@/app/admin/assistants/interfaces"; +import { + MinimalPersonaSnapshot, + Persona, +} from "@/app/admin/assistants/interfaces"; import { LlmManager } from "@/lib/hooks"; import { @@ -28,7 +31,7 @@ interface LLMPopoverProps { llmProviders: LLMProviderDescriptor[]; llmManager: LlmManager; requiresImageGeneration?: boolean; - currentAssistant?: Persona; + currentAssistant?: MinimalPersonaSnapshot; trigger?: React.ReactElement; onSelect?: (value: string) => void; currentModelName?: string; diff --git a/web/src/app/chat/lib.tsx b/web/src/app/chat/lib.tsx index 6e89f56eef2..a2dc34d8e7d 100644 --- a/web/src/app/chat/lib.tsx +++ b/web/src/app/chat/lib.tsx @@ -28,7 +28,10 @@ import { AgenticMessageResponseIDInfo, UserKnowledgeFilePacket, } from "./interfaces"; -import { Persona } from "../admin/assistants/interfaces"; +import { + MinimalPersonaSnapshot, + Persona, +} from "../admin/assistants/interfaces"; import { ReadonlyURLSearchParams } from "next/navigation"; import { SEARCH_PARAM_NAMES } from "./searchParams"; import { Settings } from "../admin/settings/interfaces"; @@ -631,8 +634,8 @@ export function removeMessage( export function checkAnyAssistantHasSearch( messageHistory: Message[], - availableAssistants: Persona[], - livePersona: Persona + availableAssistants: MinimalPersonaSnapshot[], + livePersona: MinimalPersonaSnapshot ): boolean { const response = messageHistory.some((message) => { @@ -653,19 +656,17 @@ export function checkAnyAssistantHasSearch( return response; } -export function personaIncludesRetrieval(selectedPersona: Persona) { +export function personaIncludesRetrieval( + selectedPersona: MinimalPersonaSnapshot +) { return selectedPersona.tools.some( (tool) => tool.in_code_tool_id && - [SEARCH_TOOL_ID, INTERNET_SEARCH_TOOL_ID].includes( - tool.in_code_tool_id - ) && - selectedPersona.user_file_ids?.length === 0 && - selectedPersona.user_folder_ids?.length === 0 + [SEARCH_TOOL_ID, INTERNET_SEARCH_TOOL_ID].includes(tool.in_code_tool_id) ); } -export function personaIncludesImage(selectedPersona: Persona) { +export function personaIncludesImage(selectedPersona: MinimalPersonaSnapshot) { return selectedPersona.tools.some( (tool) => tool.in_code_tool_id && tool.in_code_tool_id == IIMAGE_GENERATION_TOOL_ID diff --git a/web/src/app/chat/message/AgenticMessage.tsx b/web/src/app/chat/message/AgenticMessage.tsx index 86cc80f4214..4ac364774e9 100644 --- a/web/src/app/chat/message/AgenticMessage.tsx +++ b/web/src/app/chat/message/AgenticMessage.tsx @@ -27,7 +27,7 @@ import rehypePrism from "rehype-prism-plus"; import "prismjs/themes/prism-tomorrow.css"; import "./custom-code-styles.css"; -import { Persona } from "@/app/admin/assistants/interfaces"; +import { MinimalPersonaSnapshot } from "@/app/admin/assistants/interfaces"; import { AssistantIcon } from "@/components/assistants/AssistantIcon"; import { LikeFeedback, DislikeFeedback } from "@/components/icons/icons"; @@ -107,8 +107,8 @@ export const AgenticMessage = ({ onMessageSelection?: (messageId: number) => void; toggleDocumentSelection?: (second: boolean) => void; docs?: OnyxDocument[] | null; - alternativeAssistant?: Persona | null; - currentPersona: Persona; + alternativeAssistant?: MinimalPersonaSnapshot | null; + currentPersona: MinimalPersonaSnapshot; messageId: number | null; content: string | JSX.Element; files?: FileDescriptor[]; diff --git a/web/src/app/chat/message/Messages.tsx b/web/src/app/chat/message/Messages.tsx index d76890362e6..4762ef98ae0 100644 --- a/web/src/app/chat/message/Messages.tsx +++ b/web/src/app/chat/message/Messages.tsx @@ -40,7 +40,7 @@ import { CodeBlock } from "./CodeBlock"; import rehypePrism from "rehype-prism-plus"; import "prismjs/themes/prism-tomorrow.css"; import "./custom-code-styles.css"; -import { Persona } from "@/app/admin/assistants/interfaces"; +import { MinimalPersonaSnapshot } from "@/app/admin/assistants/interfaces"; import { AssistantIcon } from "@/components/assistants/AssistantIcon"; import { LikeFeedback, DislikeFeedback } from "@/components/icons/icons"; import { @@ -254,8 +254,8 @@ export const AIMessage = ({ selectedDocuments?: OnyxDocument[] | null; toggleDocumentSelection?: () => void; docs?: OnyxDocument[] | null; - alternativeAssistant?: Persona | null; - currentPersona: Persona; + alternativeAssistant?: MinimalPersonaSnapshot | null; + currentPersona: MinimalPersonaSnapshot; messageId: number | null; content: string | JSX.Element; files?: FileDescriptor[]; diff --git a/web/src/app/chat/sessionSidebar/HistorySidebar.tsx b/web/src/app/chat/sessionSidebar/HistorySidebar.tsx index 09c8421ff43..0905a68bdb5 100644 --- a/web/src/app/chat/sessionSidebar/HistorySidebar.tsx +++ b/web/src/app/chat/sessionSidebar/HistorySidebar.tsx @@ -27,7 +27,7 @@ import { import { PagesTab } from "./PagesTab"; import { pageType } from "./types"; import LogoWithText from "@/components/header/LogoWithText"; -import { Persona } from "@/app/admin/assistants/interfaces"; +import { MinimalPersonaSnapshot } from "@/app/admin/assistants/interfaces"; import { DragEndEvent } from "@dnd-kit/core"; import { useAssistants } from "@/components/context/AssistantsContext"; import { AssistantIcon } from "@/components/assistants/AssistantIcon"; @@ -56,7 +56,7 @@ import { restrictToVerticalAxis } from "@dnd-kit/modifiers"; import { TruncatedText } from "@/components/ui/truncatedText"; interface HistorySidebarProps { - liveAssistant?: Persona | null; + liveAssistant?: MinimalPersonaSnapshot | null; page: pageType; existingChats?: ChatSession[]; currentChatSession?: ChatSession | null | undefined; @@ -73,7 +73,7 @@ interface HistorySidebarProps { } interface SortableAssistantProps { - assistant: Persona; + assistant: MinimalPersonaSnapshot; active: boolean; onClick: () => void; onPinAction: (e: React.MouseEvent) => void; @@ -213,18 +213,22 @@ export const HistorySidebar = forwardRef( const { active, over } = event; if (active.id !== over?.id) { - setPinnedAssistants((prevAssistants: Persona[]) => { + setPinnedAssistants((prevAssistants: MinimalPersonaSnapshot[]) => { const oldIndex = prevAssistants.findIndex( - (a: Persona) => (a.id === 0 ? "assistant-0" : a.id) === active.id + (a: MinimalPersonaSnapshot) => + (a.id === 0 ? "assistant-0" : a.id) === active.id ); const newIndex = prevAssistants.findIndex( - (a: Persona) => (a.id === 0 ? "assistant-0" : a.id) === over?.id + (a: MinimalPersonaSnapshot) => + (a.id === 0 ? "assistant-0" : a.id) === over?.id ); const newOrder = arrayMove(prevAssistants, oldIndex, newIndex); // Ensure we're sending the correct IDs to the API - const reorderedIds = newOrder.map((a: Persona) => a.id); + const reorderedIds = newOrder.map( + (a: MinimalPersonaSnapshot) => a.id + ); reorderPinnedAssistants(reorderedIds); return newOrder; @@ -351,7 +355,7 @@ export const HistorySidebar = forwardRef( strategy={verticalListSortingStrategy} >
- {pinnedAssistants.map((assistant: Persona) => ( + {pinnedAssistants.map((assistant: MinimalPersonaSnapshot) => ( void; }) { const settings = useContext(SettingsContext); diff --git a/web/src/components/context/AssistantsContext.tsx b/web/src/components/context/AssistantsContext.tsx index 3223788454b..3aa685d420d 100644 --- a/web/src/components/context/AssistantsContext.tsx +++ b/web/src/components/context/AssistantsContext.tsx @@ -8,7 +8,10 @@ import React, { SetStateAction, Dispatch, } from "react"; -import { Persona } from "@/app/admin/assistants/interfaces"; +import { + MinimalPersonaSnapshot, + Persona, +} from "@/app/admin/assistants/interfaces"; import { classifyAssistants, orderAssistantsForUser, @@ -18,18 +21,18 @@ import { import { useUser } from "../user/UserProvider"; interface AssistantsContextProps { - assistants: Persona[]; - visibleAssistants: Persona[]; - hiddenAssistants: Persona[]; - finalAssistants: Persona[]; - ownedButHiddenAssistants: Persona[]; + assistants: MinimalPersonaSnapshot[]; + visibleAssistants: MinimalPersonaSnapshot[]; + hiddenAssistants: MinimalPersonaSnapshot[]; + finalAssistants: MinimalPersonaSnapshot[]; + ownedButHiddenAssistants: MinimalPersonaSnapshot[]; refreshAssistants: () => Promise; isImageGenerationAvailable: boolean; // Admin only - editablePersonas: Persona[]; - allAssistants: Persona[]; - pinnedAssistants: Persona[]; - setPinnedAssistants: Dispatch>; + editablePersonas: MinimalPersonaSnapshot[]; + allAssistants: MinimalPersonaSnapshot[]; + pinnedAssistants: MinimalPersonaSnapshot[]; + setPinnedAssistants: Dispatch>; } const AssistantsContext = createContext( @@ -38,27 +41,29 @@ const AssistantsContext = createContext( export const AssistantsProvider: React.FC<{ children: React.ReactNode; - initialAssistants: Persona[]; - hasAnyConnectors: boolean; - hasImageCompatibleModel: boolean; -}> = ({ - children, - initialAssistants, - hasAnyConnectors, - hasImageCompatibleModel, -}) => { - const [assistants, setAssistants] = useState( + initialAssistants: MinimalPersonaSnapshot[]; +}> = ({ children, initialAssistants }) => { + const [assistants, setAssistants] = useState( initialAssistants || [] ); const { user, isAdmin, isCurator } = useUser(); - const [editablePersonas, setEditablePersonas] = useState([]); - const [allAssistants, setAllAssistants] = useState([]); + const [editablePersonas, setEditablePersonas] = useState< + MinimalPersonaSnapshot[] + >([]); + const [allAssistants, setAllAssistants] = useState( + [] + ); - const [pinnedAssistants, setPinnedAssistants] = useState(() => { + const [pinnedAssistants, setPinnedAssistants] = useState< + MinimalPersonaSnapshot[] + >(() => { if (user?.preferences.pinned_assistants) { return user.preferences.pinned_assistants .map((id) => assistants.find((assistant) => assistant.id === id)) - .filter((assistant): assistant is Persona => assistant !== undefined); + .filter( + (assistant): assistant is MinimalPersonaSnapshot => + assistant !== undefined + ); } else { return assistants.filter((a) => a.is_default_persona); } @@ -69,7 +74,10 @@ export const AssistantsProvider: React.FC<{ if (user?.preferences.pinned_assistants) { return user.preferences.pinned_assistants .map((id) => assistants.find((assistant) => assistant.id === id)) - .filter((assistant): assistant is Persona => assistant !== undefined); + .filter( + (assistant): assistant is MinimalPersonaSnapshot => + assistant !== undefined + ); } else { return assistants.filter((a) => a.is_default_persona); } @@ -135,13 +143,9 @@ export const AssistantsProvider: React.FC<{ }, }); if (!response.ok) throw new Error("Failed to fetch assistants"); - let assistants: Persona[] = await response.json(); + let assistants: MinimalPersonaSnapshot[] = await response.json(); - let filteredAssistants = filterAssistants( - assistants, - hasAnyConnectors, - hasImageCompatibleModel - ); + let filteredAssistants = filterAssistants(assistants); setAssistants(filteredAssistants); diff --git a/web/src/lib/assistants/checkOwnership.ts b/web/src/lib/assistants/checkOwnership.ts index 0102f406349..7e6d723d511 100644 --- a/web/src/lib/assistants/checkOwnership.ts +++ b/web/src/lib/assistants/checkOwnership.ts @@ -1,14 +1,17 @@ -import { Persona } from "@/app/admin/assistants/interfaces"; +import { MinimalPersonaSnapshot } from "@/app/admin/assistants/interfaces"; import { User } from "../types"; import { checkUserIsNoAuthUser } from "../user"; -export function checkUserOwnsAssistant(user: User | null, assistant: Persona) { +export function checkUserOwnsAssistant( + user: User | null, + assistant: MinimalPersonaSnapshot +) { return checkUserIdOwnsAssistant(user?.id, assistant); } export function checkUserIdOwnsAssistant( userId: string | undefined, - assistant: Persona + assistant: MinimalPersonaSnapshot ) { return ( (!userId || diff --git a/web/src/lib/assistants/fetchAssistantsSS.ts b/web/src/lib/assistants/fetchAssistantsSS.ts index abe96eb2cc5..7fc3e6c7217 100644 --- a/web/src/lib/assistants/fetchAssistantsSS.ts +++ b/web/src/lib/assistants/fetchAssistantsSS.ts @@ -1,12 +1,12 @@ -import { Persona } from "@/app/admin/assistants/interfaces"; +import { MinimalPersonaSnapshot } from "@/app/admin/assistants/interfaces"; import { fetchSS } from "../utilsSS"; -export type FetchAssistantsResponse = [Persona[], string | null]; +export type FetchAssistantsResponse = [MinimalPersonaSnapshot[], string | null]; export async function fetchAssistantsSS(): Promise { const response = await fetchSS("/persona"); if (response.ok) { - return [(await response.json()) as Persona[], null]; + return [(await response.json()) as MinimalPersonaSnapshot[], null]; } return [[], (await response.json()).detail || "Unknown Error"]; } diff --git a/web/src/lib/assistants/utils.ts b/web/src/lib/assistants/utils.ts index 5c3ab5adcd1..97fd26b8896 100644 --- a/web/src/lib/assistants/utils.ts +++ b/web/src/lib/assistants/utils.ts @@ -1,15 +1,21 @@ -import { Persona } from "@/app/admin/assistants/interfaces"; +import { + MinimalPersonaSnapshot, + Persona, +} from "@/app/admin/assistants/interfaces"; import { User } from "../types"; import { checkUserIsNoAuthUser } from "../user"; import { personaComparator } from "@/app/admin/assistants/lib"; -export function checkUserOwnsAssistant(user: User | null, assistant: Persona) { +export function checkUserOwnsAssistant( + user: User | null, + assistant: MinimalPersonaSnapshot +) { return checkUserIdOwnsAssistant(user?.id, assistant); } export function checkUserIdOwnsAssistant( userId: string | undefined, - assistant: Persona + assistant: MinimalPersonaSnapshot ) { return ( (!userId || @@ -19,7 +25,10 @@ export function checkUserIdOwnsAssistant( ); } -export function classifyAssistants(user: User | null, assistants: Persona[]) { +export function classifyAssistants( + user: User | null, + assistants: MinimalPersonaSnapshot[] +) { if (!user) { return { visibleAssistants: assistants.filter( @@ -59,7 +68,7 @@ export function classifyAssistants(user: User | null, assistants: Persona[]) { } export function orderAssistantsForUser( - assistants: Persona[], + assistants: MinimalPersonaSnapshot[], user: User | null ) { let orderedAssistants = [...assistants]; @@ -112,7 +121,7 @@ export function orderAssistantsForUser( export function getUserCreatedAssistants( user: User | null, - assistants: Persona[] + assistants: MinimalPersonaSnapshot[] ) { return assistants.filter((assistant) => checkUserOwnsAssistant(user, assistant) @@ -121,29 +130,10 @@ export function getUserCreatedAssistants( // Filter assistants based on connector status, image compatibility and visibility export function filterAssistants( - assistants: Persona[], - hasAnyConnectors: boolean, - hasImageCompatibleModel: boolean -): Persona[] { + assistants: MinimalPersonaSnapshot[] +): MinimalPersonaSnapshot[] { let filteredAssistants = assistants.filter( (assistant) => assistant.is_visible ); - - if (!hasAnyConnectors) { - filteredAssistants = filteredAssistants.filter( - (assistant) => - assistant.num_chunks === 0 || assistant.document_sets.length > 0 - ); - } - - if (!hasImageCompatibleModel) { - filteredAssistants = filteredAssistants.filter( - (assistant) => - !assistant.tools.some( - (tool) => tool.in_code_tool_id === "ImageGenerationTool" - ) - ); - } - return filteredAssistants.sort(personaComparator); } diff --git a/web/src/lib/chat/fetchAssistantdata.ts b/web/src/lib/chat/fetchAssistantdata.ts index 048dc357c70..79cc848cbfd 100644 --- a/web/src/lib/chat/fetchAssistantdata.ts +++ b/web/src/lib/chat/fetchAssistantdata.ts @@ -1,12 +1,15 @@ import { fetchSS } from "@/lib/utilsSS"; -import { Persona } from "@/app/admin/assistants/interfaces"; +import { + MinimalPersonaSnapshot, + Persona, +} from "@/app/admin/assistants/interfaces"; import { fetchLLMProvidersSS } from "@/lib/llm/fetchLLMs"; import { fetchAssistantsSS } from "../assistants/fetchAssistantsSS"; import { modelSupportsImageInput } from "../llm/utils"; import { filterAssistants } from "../assistants/utils"; interface AssistantData { - assistants: Persona[]; + assistants: MinimalPersonaSnapshot[]; hasAnyConnectors: boolean; hasImageCompatibleModel: boolean; } @@ -51,11 +54,7 @@ export async function fetchAssistantData(): Promise { ) ); - let filteredAssistants = filterAssistants( - assistants, - hasAnyConnectors, - hasImageCompatibleModel - ); + let filteredAssistants = filterAssistants(assistants); return { assistants: filteredAssistants, diff --git a/web/src/lib/hooks.ts b/web/src/lib/hooks.ts index 23343fbe856..7f776469fdd 100644 --- a/web/src/lib/hooks.ts +++ b/web/src/lib/hooks.ts @@ -18,7 +18,11 @@ import { ChatSession } from "@/app/chat/interfaces"; import { AllUsersResponse } from "./types"; import { Credential } from "./connectors/credentials"; import { SettingsContext } from "@/components/settings/SettingsProvider"; -import { Persona, PersonaLabel } from "@/app/admin/assistants/interfaces"; +import { + MinimalPersonaSnapshot, + Persona, + PersonaLabel, +} from "@/app/admin/assistants/interfaces"; import { LLMProviderDescriptor } from "@/app/admin/configuration/llm/interfaces"; import { isAnthropic } from "@/app/admin/configuration/llm/utils"; import { getSourceMetadata } from "./sources"; @@ -380,7 +384,7 @@ export interface LlmManager { updateModelOverrideBasedOnChatSession: (chatSession?: ChatSession) => void; imageFilesPresent: boolean; updateImageFilesPresent: (present: boolean) => void; - liveAssistant: Persona | null; + liveAssistant: MinimalPersonaSnapshot | null; maxTemperature: number; } @@ -428,7 +432,7 @@ providing appropriate defaults for new conversations based on the available tool export function useLlmManager( llmProviders: LLMProviderDescriptor[], currentChatSession?: ChatSession, - liveAssistant?: Persona + liveAssistant?: MinimalPersonaSnapshot ): LlmManager { const { user } = useUser(); diff --git a/web/src/lib/llm/utils.ts b/web/src/lib/llm/utils.ts index 47c01b8ee93..ea7249f7ae4 100644 --- a/web/src/lib/llm/utils.ts +++ b/web/src/lib/llm/utils.ts @@ -1,4 +1,4 @@ -import { Persona } from "@/app/admin/assistants/interfaces"; +import { MinimalPersonaSnapshot } from "@/app/admin/assistants/interfaces"; import { LLMProviderDescriptor, ModelConfiguration, @@ -7,7 +7,7 @@ import { LlmDescriptor } from "@/lib/hooks"; export function getFinalLLM( llmProviders: LLMProviderDescriptor[], - persona: Persona | null, + persona: MinimalPersonaSnapshot | null, currentLlm: LlmDescriptor | null ): [string, string] { const defaultProvider = llmProviders.find( @@ -38,7 +38,7 @@ export function getFinalLLM( } export function getLLMProviderOverrideForPersona( - liveAssistant: Persona, + liveAssistant: MinimalPersonaSnapshot, llmProviders: LLMProviderDescriptor[] ): LlmDescriptor | null { const overrideProvider = liveAssistant.llm_model_provider_override; From 5baff11166f8d75ac2d6ef4a684950353175182d Mon Sep 17 00:00:00 2001 From: Weves Date: Thu, 3 Jul 2025 19:47:35 -0700 Subject: [PATCH 03/10] more --- web/src/app/admin/assistants/PersonaTable.tsx | 36 +++++++------- web/src/app/admin/assistants/hooks.ts | 30 ++++++++++++ web/src/app/admin/assistants/interfaces.ts | 22 +-------- web/src/app/admin/assistants/lib.ts | 4 +- web/src/app/admin/assistants/page.tsx | 48 +++++++++++++++++-- .../SlackChannelConfigCreationForm.tsx | 6 +-- .../channels/SlackChannelConfigFormFields.tsx | 46 +++++++++--------- .../admin/bots/[bot-id]/channels/new/page.tsx | 4 +- .../modal/configuration/AssistantsTab.tsx | 6 +-- web/src/components/Loading.tsx | 2 + .../components/assistants/AssistantCards.tsx | 13 +++-- .../components/assistants/AssistantIcon.tsx | 7 ++- web/src/lib/assistants/checkOwnership.ts | 9 ++-- web/src/lib/hooks.ts | 1 - 14 files changed, 149 insertions(+), 85 deletions(-) create mode 100644 web/src/app/admin/assistants/hooks.ts diff --git a/web/src/app/admin/assistants/PersonaTable.tsx b/web/src/app/admin/assistants/PersonaTable.tsx index 8f431b27a1a..a12450d512d 100644 --- a/web/src/app/admin/assistants/PersonaTable.tsx +++ b/web/src/app/admin/assistants/PersonaTable.tsx @@ -40,16 +40,18 @@ function PersonaTypeDisplay({ persona }: { persona: Persona }) { return Personal {persona.owner && <>({persona.owner.email})}; } -export function PersonasTable() { +export function PersonasTable({ + personas, + refreshPersonas, +}: { + personas: Persona[]; + refreshPersonas: () => void; +}) { const router = useRouter(); const { popup, setPopup } = usePopup(); const { refreshUser, isAdmin } = useUser(); - const { - allAssistants: assistants, - refreshAssistants, - editablePersonas, - } = useAssistants(); + const editablePersonas = personas.filter((p) => !p.builtin_persona); const editablePersonaIds = useMemo(() => { return new Set(editablePersonas.map((p) => p.id.toString())); }, [editablePersonas]); @@ -63,18 +65,18 @@ export function PersonasTable() { useEffect(() => { const editable = editablePersonas.sort(personaComparator); - const nonEditable = assistants + const nonEditable = personas .filter((p) => !editablePersonaIds.has(p.id.toString())) .sort(personaComparator); setFinalPersonas([...editable, ...nonEditable]); - }, [editablePersonas, assistants, editablePersonaIds]); + }, [editablePersonas, personas, editablePersonaIds]); const updatePersonaOrder = async (orderedPersonaIds: UniqueIdentifier[]) => { - const reorderedAssistants = orderedPersonaIds.map( - (id) => assistants.find((assistant) => assistant.id.toString() === id)! + const reorderedPersonas = orderedPersonaIds.map( + (id) => personas.find((persona) => persona.id.toString() === id)! ); - setFinalPersonas(reorderedAssistants); + setFinalPersonas(reorderedPersonas); const displayPriorityMap = new Map(); orderedPersonaIds.forEach((personaId, ind) => { @@ -96,12 +98,12 @@ export function PersonasTable() { type: "error", message: `Failed to update persona order - ${await response.text()}`, }); - setFinalPersonas(assistants); - await refreshAssistants(); + setFinalPersonas(personas); + await refreshPersonas(); return; } - await refreshAssistants(); + await refreshPersonas(); await refreshUser(); }; @@ -119,7 +121,7 @@ export function PersonasTable() { if (personaToDelete) { const response = await deletePersona(personaToDelete.id); if (response.ok) { - await refreshAssistants(); + refreshPersonas(); closeDeleteModal(); } else { setPopup({ @@ -147,7 +149,7 @@ export function PersonasTable() { personaToToggleDefault.is_default_persona ); if (response.ok) { - await refreshAssistants(); + refreshPersonas(); closeDefaultModal(); } else { setPopup({ @@ -267,7 +269,7 @@ export function PersonasTable() { persona.is_visible ); if (response.ok) { - await refreshAssistants(); + refreshPersonas(); } else { setPopup({ type: "error", diff --git a/web/src/app/admin/assistants/hooks.ts b/web/src/app/admin/assistants/hooks.ts new file mode 100644 index 00000000000..ee4c824db56 --- /dev/null +++ b/web/src/app/admin/assistants/hooks.ts @@ -0,0 +1,30 @@ +import useSWR from "swr"; +import { errorHandlingFetcher } from "@/lib/fetcher"; +import { buildApiPath } from "@/lib/urlBuilder"; +import { Persona } from "@/app/admin/assistants/interfaces"; + +interface UseAdminPersonasOptions { + includeDeleted?: boolean; + getEditable?: boolean; +} + +export const useAdminPersonas = (options?: UseAdminPersonasOptions) => { + const { includeDeleted = false, getEditable = false } = options || {}; + + const url = buildApiPath("/api/admin/persona", { + include_deleted: includeDeleted.toString(), + get_editable: getEditable.toString(), + }); + + const { data, error, isLoading, mutate } = useSWR( + url, + errorHandlingFetcher + ); + + return { + personas: data, + error, + isLoading, + refresh: mutate, + }; +}; diff --git a/web/src/app/admin/assistants/interfaces.ts b/web/src/app/admin/assistants/interfaces.ts index 5411cecbc55..fe88e3a9449 100644 --- a/web/src/app/admin/assistants/interfaces.ts +++ b/web/src/app/admin/assistants/interfaces.ts @@ -39,33 +39,15 @@ export interface MinimalPersonaSnapshot { is_default_persona: boolean; builtin_persona: boolean; - labels: PersonaLabel[]; + labels?: PersonaLabel[]; owner: MinimalUserSnapshot | null; } -export interface Persona { - id: number; - name: string; - description: string; - is_public: boolean; - is_visible: boolean; - icon_shape?: number; - icon_color?: string; - uploaded_image_id?: string; +export interface Persona extends MinimalPersonaSnapshot { user_file_ids: number[]; user_folder_ids: number[]; - display_priority: number | null; - is_default_persona: boolean; - builtin_persona: boolean; - starter_messages: StarterMessage[] | null; - tools: ToolSnapshot[]; - labels?: PersonaLabel[]; - owner: MinimalUserSnapshot | null; users: MinimalUserSnapshot[]; groups: number[]; - document_sets: DocumentSet[]; - llm_model_provider_override?: string; - llm_model_version_override?: string; num_chunks?: number; } diff --git a/web/src/app/admin/assistants/lib.ts b/web/src/app/admin/assistants/lib.ts index 9194c6585ca..fed7d0f7d14 100644 --- a/web/src/app/admin/assistants/lib.ts +++ b/web/src/app/admin/assistants/lib.ts @@ -251,8 +251,8 @@ function closerToZeroNegativesFirstComparator(a: number, b: number) { } export function personaComparator( - a: MinimalPersonaSnapshot, - b: MinimalPersonaSnapshot + a: MinimalPersonaSnapshot | Persona, + b: MinimalPersonaSnapshot | Persona ) { if (a.display_priority === null && b.display_priority === null) { return closerToZeroNegativesFirstComparator(a.id, b.id); diff --git a/web/src/app/admin/assistants/page.tsx b/web/src/app/admin/assistants/page.tsx index fc667cbf3dd..9923522d08c 100644 --- a/web/src/app/admin/assistants/page.tsx +++ b/web/src/app/admin/assistants/page.tsx @@ -1,3 +1,5 @@ +"use client"; + import { PersonasTable } from "./PersonaTable"; import Text from "@/components/ui/text"; import Title from "@/components/ui/title"; @@ -6,11 +8,20 @@ import { AssistantsIcon } from "@/components/icons/icons"; import { AdminPageTitle } from "@/components/admin/Title"; import { SubLabel } from "@/components/Field"; import CreateButton from "@/components/ui/createButton"; -export default async function Page() { - return ( -
- } title="Assistants" /> +import { useAdminPersonas } from "./hooks"; +import { Persona } from "./interfaces"; +import { ThreeDotsLoader } from "@/components/Loading"; +import { ErrorCallout } from "@/components/ErrorCallout"; +function MainContent({ + personas, + refreshPersonas, +}: { + personas: Persona[]; + refreshPersonas: () => void; +}) { + return ( +
Assistants are a way to build custom search/question-answering experiences for different use cases. @@ -40,8 +51,35 @@ export default async function Page() { hidden will not be displayed. Editable assistants are shown at the top. - +
); } + +export default function Page() { + const { personas, isLoading, error, refresh } = useAdminPersonas(); + + return ( +
+ } title="Assistants" /> + + {isLoading && } + + {error && ( + + )} + + {!isLoading && !error && ( + + )} +
+ ); +} diff --git a/web/src/app/admin/bots/[bot-id]/channels/SlackChannelConfigCreationForm.tsx b/web/src/app/admin/bots/[bot-id]/channels/SlackChannelConfigCreationForm.tsx index d1826e955ed..5b4c2520246 100644 --- a/web/src/app/admin/bots/[bot-id]/channels/SlackChannelConfigCreationForm.tsx +++ b/web/src/app/admin/bots/[bot-id]/channels/SlackChannelConfigCreationForm.tsx @@ -16,7 +16,7 @@ import { } from "../lib"; import CardSection from "@/components/admin/CardSection"; import { useRouter } from "next/navigation"; -import { Persona } from "@/app/admin/assistants/interfaces"; +import { MinimalPersonaSnapshot } from "@/app/admin/assistants/interfaces"; import { StandardAnswerCategoryResponse } from "@/components/standardAnswers/getStandardAnswerCategoriesIfEE"; import { SEARCH_TOOL_ID } from "@/app/chat/tools/constants"; import { SlackChannelConfigFormFields } from "./SlackChannelConfigFormFields"; @@ -30,7 +30,7 @@ export const SlackChannelConfigCreationForm = ({ }: { slack_bot_id: number; documentSets: DocumentSet[]; - personas: Persona[]; + personas: MinimalPersonaSnapshot[]; standardAnswerCategoryResponse: StandardAnswerCategoryResponse; existingSlackChannelConfig?: SlackChannelConfig; }) => { @@ -59,7 +59,7 @@ export const SlackChannelConfigCreationForm = ({ } return acc; }, - [[], []] as [Persona[], Persona[]] + [[], []] as [MinimalPersonaSnapshot[], MinimalPersonaSnapshot[]] ); }, [personas]); diff --git a/web/src/app/admin/bots/[bot-id]/channels/SlackChannelConfigFormFields.tsx b/web/src/app/admin/bots/[bot-id]/channels/SlackChannelConfigFormFields.tsx index 33517637ec3..c0a0b21d1c3 100644 --- a/web/src/app/admin/bots/[bot-id]/channels/SlackChannelConfigFormFields.tsx +++ b/web/src/app/admin/bots/[bot-id]/channels/SlackChannelConfigFormFields.tsx @@ -11,7 +11,7 @@ import { TextFormField, } from "@/components/Field"; import { Button } from "@/components/ui/button"; -import { Persona } from "@/app/admin/assistants/interfaces"; +import { MinimalPersonaSnapshot } from "@/app/admin/assistants/interfaces"; import { DocumentSetSelectable } from "@/components/documentSet/DocumentSetSelectable"; import CollapsibleSection from "@/app/admin/assistants/CollapsibleSection"; import { StandardAnswerCategoryResponse } from "@/components/standardAnswers/getStandardAnswerCategoriesIfEE"; @@ -48,8 +48,8 @@ export interface SlackChannelConfigFormFieldsProps { isUpdate: boolean; isDefault: boolean; documentSets: DocumentSet[]; - searchEnabledAssistants: Persona[]; - nonSearchAssistants: Persona[]; + searchEnabledAssistants: MinimalPersonaSnapshot[]; + nonSearchAssistants: MinimalPersonaSnapshot[]; standardAnswerCategoryResponse: StandardAnswerCategoryResponse; setPopup: (popup: { message: string; @@ -82,8 +82,8 @@ export function SlackChannelConfigFormFields({ ); const [syncEnabledAssistants, availableAssistants] = useMemo(() => { - const sync: Persona[] = []; - const available: Persona[] = []; + const sync: MinimalPersonaSnapshot[] = []; + const available: MinimalPersonaSnapshot[] = []; searchEnabledAssistants.forEach((persona) => { const hasSyncSet = persona.document_sets.some(documentSetContainsSync); @@ -461,23 +461,25 @@ export function SlackChannelConfigFormFields({ Un-selectable assistants:

- {syncEnabledAssistants.map((persona: Persona) => ( - - ))} + {syncEnabledAssistants.map( + (persona: MinimalPersonaSnapshot) => ( + + ) + )}
)} diff --git a/web/src/app/admin/bots/[bot-id]/channels/new/page.tsx b/web/src/app/admin/bots/[bot-id]/channels/new/page.tsx index 7308c98e707..9116eb034ec 100644 --- a/web/src/app/admin/bots/[bot-id]/channels/new/page.tsx +++ b/web/src/app/admin/bots/[bot-id]/channels/new/page.tsx @@ -32,8 +32,8 @@ async function NewChannelConfigPage(props: { standardAnswerCategoryResponse, ] = await Promise.all([ fetchSS("/manage/document-set") as Promise, - fetchAssistantsSS() as Promise<[Persona[], string | null]>, - getStandardAnswerCategoriesIfEE() as Promise, + fetchAssistantsSS(), + getStandardAnswerCategoriesIfEE(), ]); if (!documentSetsResponse.ok) { diff --git a/web/src/app/chat/modal/configuration/AssistantsTab.tsx b/web/src/app/chat/modal/configuration/AssistantsTab.tsx index a29db23cdec..1cdfdbf474c 100644 --- a/web/src/app/chat/modal/configuration/AssistantsTab.tsx +++ b/web/src/app/chat/modal/configuration/AssistantsTab.tsx @@ -13,7 +13,7 @@ import { sortableKeyboardCoordinates, verticalListSortingStrategy, } from "@dnd-kit/sortable"; -import { Persona } from "@/app/admin/assistants/interfaces"; +import { MinimalPersonaSnapshot } from "@/app/admin/assistants/interfaces"; import { LLMProviderDescriptor } from "@/app/admin/configuration/llm/interfaces"; import { getFinalLLM } from "@/lib/llm/utils"; import React, { useEffect, useState } from "react"; @@ -27,9 +27,9 @@ export function AssistantsTab({ llmProviders, onSelect, }: { - selectedAssistant: Persona; + selectedAssistant: MinimalPersonaSnapshot; llmProviders: LLMProviderDescriptor[]; - onSelect: (assistant: Persona) => void; + onSelect: (assistant: MinimalPersonaSnapshot) => void; }) { const { refreshUser } = useUser(); const [_, llmName] = getFinalLLM(llmProviders, null, null); diff --git a/web/src/components/Loading.tsx b/web/src/components/Loading.tsx index 7cdcb5a9887..2de50a8dd58 100644 --- a/web/src/components/Loading.tsx +++ b/web/src/components/Loading.tsx @@ -1,3 +1,5 @@ +"use client"; + import React, { useState, useEffect } from "react"; import "./loading.css"; import { ThreeDots } from "react-loader-spinner"; diff --git a/web/src/components/assistants/AssistantCards.tsx b/web/src/components/assistants/AssistantCards.tsx index fce804bbe89..d1aacedfe1d 100644 --- a/web/src/components/assistants/AssistantCards.tsx +++ b/web/src/components/assistants/AssistantCards.tsx @@ -1,4 +1,7 @@ -import { Persona } from "@/app/admin/assistants/interfaces"; +import { + MinimalPersonaSnapshot, + Persona, +} from "@/app/admin/assistants/interfaces"; import { AssistantIcon } from "@/components/assistants/AssistantIcon"; import { useSortable } from "@dnd-kit/sortable"; import React from "react"; @@ -14,9 +17,9 @@ export const AssistantCard = ({ isSelected, onSelect, }: { - assistant: Persona; + assistant: MinimalPersonaSnapshot; isSelected: boolean; - onSelect: (assistant: Persona) => void; + onSelect: (assistant: MinimalPersonaSnapshot) => void; }) => { const renderBadgeContent = (tool: { name: string }) => { switch (tool.name) { @@ -73,9 +76,9 @@ export const AssistantCard = ({ }; export function DraggableAssistantCard(props: { - assistant: Persona; + assistant: MinimalPersonaSnapshot; isSelected: boolean; - onSelect: (assistant: Persona) => void; + onSelect: (assistant: MinimalPersonaSnapshot) => void; llmName: string; }) { const { diff --git a/web/src/components/assistants/AssistantIcon.tsx b/web/src/components/assistants/AssistantIcon.tsx index c0e68dd213d..c5c5abe887c 100644 --- a/web/src/components/assistants/AssistantIcon.tsx +++ b/web/src/components/assistants/AssistantIcon.tsx @@ -1,6 +1,9 @@ import React from "react"; import crypto from "crypto"; -import { MinimalPersonaSnapshot } from "@/app/admin/assistants/interfaces"; +import { + MinimalPersonaSnapshot, + Persona, +} from "@/app/admin/assistants/interfaces"; import { buildImgUrl } from "@/app/chat/files/images/utils"; import { ArtAsistantIcon, @@ -92,7 +95,7 @@ export function AssistantIcon({ className, disableToolip, }: { - assistant: MinimalPersonaSnapshot; + assistant: MinimalPersonaSnapshot | Persona; size?: IconSize; className?: string; border?: boolean; diff --git a/web/src/lib/assistants/checkOwnership.ts b/web/src/lib/assistants/checkOwnership.ts index 7e6d723d511..115ffc86c96 100644 --- a/web/src/lib/assistants/checkOwnership.ts +++ b/web/src/lib/assistants/checkOwnership.ts @@ -1,17 +1,20 @@ -import { MinimalPersonaSnapshot } from "@/app/admin/assistants/interfaces"; +import { + MinimalPersonaSnapshot, + Persona, +} from "@/app/admin/assistants/interfaces"; import { User } from "../types"; import { checkUserIsNoAuthUser } from "../user"; export function checkUserOwnsAssistant( user: User | null, - assistant: MinimalPersonaSnapshot + assistant: MinimalPersonaSnapshot | Persona ) { return checkUserIdOwnsAssistant(user?.id, assistant); } export function checkUserIdOwnsAssistant( userId: string | undefined, - assistant: MinimalPersonaSnapshot + assistant: MinimalPersonaSnapshot | Persona ) { return ( (!userId || diff --git a/web/src/lib/hooks.ts b/web/src/lib/hooks.ts index 7f776469fdd..b37bb50a689 100644 --- a/web/src/lib/hooks.ts +++ b/web/src/lib/hooks.ts @@ -20,7 +20,6 @@ import { Credential } from "./connectors/credentials"; import { SettingsContext } from "@/components/settings/SettingsProvider"; import { MinimalPersonaSnapshot, - Persona, PersonaLabel, } from "@/app/admin/assistants/interfaces"; import { LLMProviderDescriptor } from "@/app/admin/configuration/llm/interfaces"; From cb0c9b3e2f02ae5202d49edc019d8bf8cd353bd5 Mon Sep 17 00:00:00 2001 From: Weves Date: Fri, 4 Jul 2025 11:30:29 -0700 Subject: [PATCH 04/10] Fix build --- web/src/app/admin/assistants/PersonaTable.tsx | 1 - web/src/app/admin/bots/SlackBotTable.tsx | 1 - .../app/admin/bots/[bot-id]/channels/new/page.tsx | 6 +----- .../configuration/llm/LLMProviderUpdateForm.tsx | 1 - .../admin/connector/[ccPairId]/ReIndexModal.tsx | 2 +- web/src/app/assistants/mine/AssistantCard.tsx | 5 +---- web/src/app/auth/login/EmailPasswordForm.tsx | 1 - web/src/app/chat/ChatPage.tsx | 1 - web/src/app/chat/input/ChatInputBar.tsx | 1 - web/src/app/chat/input/LLMPopover.tsx | 5 +---- web/src/app/chat/lib.tsx | 5 +---- .../chat/my-documents/[id]/UserFolderContent.tsx | 1 - .../chat/my-documents/components/FilePicker.tsx | 1 - .../my-documents/components/SelectedItemsList.tsx | 1 - .../app/chat/shared/[chatId]/SharedChatDisplay.tsx | 2 -- web/src/app/ee/admin/standard-answer/page.tsx | 1 - web/src/components/Field.tsx | 2 +- web/src/components/admin/ClientLayout.tsx | 2 -- web/src/components/assistants/AssistantCards.tsx | 5 +---- web/src/components/context/AppProvider.tsx | 4 ++-- web/src/components/context/AssistantsContext.tsx | 14 +++++++++----- .../credentials/actions/CreateCredential.tsx | 8 ++------ web/src/lib/assistants/utils.ts | 5 +---- web/src/lib/chat/fetchAssistantdata.ts | 5 +---- 24 files changed, 22 insertions(+), 58 deletions(-) diff --git a/web/src/app/admin/assistants/PersonaTable.tsx b/web/src/app/admin/assistants/PersonaTable.tsx index a12450d512d..57504f618f9 100644 --- a/web/src/app/admin/assistants/PersonaTable.tsx +++ b/web/src/app/admin/assistants/PersonaTable.tsx @@ -17,7 +17,6 @@ import { import { FiEdit2 } from "react-icons/fi"; import { TrashIcon } from "@/components/icons/icons"; import { useUser } from "@/components/user/UserProvider"; -import { useAssistants } from "@/components/context/AssistantsContext"; import { ConfirmEntityModal } from "@/components/modals/ConfirmEntityModal"; function PersonaTypeDisplay({ persona }: { persona: Persona }) { diff --git a/web/src/app/admin/bots/SlackBotTable.tsx b/web/src/app/admin/bots/SlackBotTable.tsx index ddab71ce08a..f8fe9674657 100644 --- a/web/src/app/admin/bots/SlackBotTable.tsx +++ b/web/src/app/admin/bots/SlackBotTable.tsx @@ -3,7 +3,6 @@ import { PageSelector } from "@/components/PageSelector"; import { useRouter } from "next/navigation"; import { useEffect, useState } from "react"; -import { FiEdit } from "react-icons/fi"; import { Table, TableBody, diff --git a/web/src/app/admin/bots/[bot-id]/channels/new/page.tsx b/web/src/app/admin/bots/[bot-id]/channels/new/page.tsx index 9116eb034ec..56f4848c7b9 100644 --- a/web/src/app/admin/bots/[bot-id]/channels/new/page.tsx +++ b/web/src/app/admin/bots/[bot-id]/channels/new/page.tsx @@ -5,12 +5,8 @@ import { ErrorCallout } from "@/components/ErrorCallout"; import { DocumentSet, ValidSources } from "@/lib/types"; import { BackButton } from "@/components/BackButton"; import { fetchAssistantsSS } from "@/lib/assistants/fetchAssistantsSS"; -import { - getStandardAnswerCategoriesIfEE, - StandardAnswerCategoryResponse, -} from "@/components/standardAnswers/getStandardAnswerCategoriesIfEE"; +import { getStandardAnswerCategoriesIfEE } from "@/components/standardAnswers/getStandardAnswerCategoriesIfEE"; import { redirect } from "next/navigation"; -import { Persona } from "../../../../assistants/interfaces"; import { SourceIcon } from "@/components/SourceIcon"; async function NewChannelConfigPage(props: { diff --git a/web/src/app/admin/configuration/llm/LLMProviderUpdateForm.tsx b/web/src/app/admin/configuration/llm/LLMProviderUpdateForm.tsx index 94e9c37ccad..4da28d68ae0 100644 --- a/web/src/app/admin/configuration/llm/LLMProviderUpdateForm.tsx +++ b/web/src/app/admin/configuration/llm/LLMProviderUpdateForm.tsx @@ -16,7 +16,6 @@ import { useState } from "react"; import { useSWRConfig } from "swr"; import { LLMProviderView, - ModelConfiguration, ModelConfigurationUpsertRequest, WellKnownLLMProviderDescriptor, } from "./interfaces"; diff --git a/web/src/app/admin/connector/[ccPairId]/ReIndexModal.tsx b/web/src/app/admin/connector/[ccPairId]/ReIndexModal.tsx index c187551fc80..1b4e9b99920 100644 --- a/web/src/app/admin/connector/[ccPairId]/ReIndexModal.tsx +++ b/web/src/app/admin/connector/[ccPairId]/ReIndexModal.tsx @@ -2,7 +2,7 @@ import { Button } from "@/components/ui/button"; import { useState } from "react"; -import { usePopup, PopupSpec } from "@/components/admin/connectors/Popup"; +import { PopupSpec } from "@/components/admin/connectors/Popup"; import { triggerIndexing } from "./lib"; import { Modal } from "@/components/Modal"; import Text from "@/components/ui/text"; diff --git a/web/src/app/assistants/mine/AssistantCard.tsx b/web/src/app/assistants/mine/AssistantCard.tsx index d46715fe300..92817fa70b3 100644 --- a/web/src/app/assistants/mine/AssistantCard.tsx +++ b/web/src/app/assistants/mine/AssistantCard.tsx @@ -15,10 +15,7 @@ import { PopoverContent, } from "@/components/ui/popover"; import { AssistantIcon } from "@/components/assistants/AssistantIcon"; -import { - MinimalPersonaSnapshot, - Persona, -} from "@/app/admin/assistants/interfaces"; +import { MinimalPersonaSnapshot } from "@/app/admin/assistants/interfaces"; import { useUser } from "@/components/user/UserProvider"; import { useAssistants } from "@/components/context/AssistantsContext"; import { checkUserOwnsAssistant } from "@/lib/assistants/utils"; diff --git a/web/src/app/auth/login/EmailPasswordForm.tsx b/web/src/app/auth/login/EmailPasswordForm.tsx index 3a9709ea491..4a773f9a212 100644 --- a/web/src/app/auth/login/EmailPasswordForm.tsx +++ b/web/src/app/auth/login/EmailPasswordForm.tsx @@ -9,7 +9,6 @@ import * as Yup from "yup"; import { requestEmailVerification } from "../lib"; import { useState } from "react"; import { Spinner } from "@/components/Spinner"; -import { NEXT_PUBLIC_FORGOT_PASSWORD_ENABLED } from "@/lib/constants"; import Link from "next/link"; import { useUser } from "@/components/user/UserProvider"; import { useRouter } from "next/navigation"; diff --git a/web/src/app/chat/ChatPage.tsx b/web/src/app/chat/ChatPage.tsx index 76835df155e..12abd4b762e 100644 --- a/web/src/app/chat/ChatPage.tsx +++ b/web/src/app/chat/ChatPage.tsx @@ -49,7 +49,6 @@ import { setMessageAsLatest, updateLlmOverrideForChatSession, updateParentChildren, - uploadFilesForChat, useScrollonStream, } from "./lib"; import { diff --git a/web/src/app/chat/input/ChatInputBar.tsx b/web/src/app/chat/input/ChatInputBar.tsx index cb9fae049c5..f0cdd63a3d5 100644 --- a/web/src/app/chat/input/ChatInputBar.tsx +++ b/web/src/app/chat/input/ChatInputBar.tsx @@ -39,7 +39,6 @@ import { AgenticToggle } from "./AgenticToggle"; import { SettingsContext } from "@/components/settings/SettingsProvider"; import { getProviderIcon } from "@/app/admin/configuration/llm/utils"; import { useDocumentsContext } from "../my-documents/DocumentsContext"; -import { UploadIntent } from "../ChatPage"; const MAX_INPUT_HEIGHT = 200; export const SourceChip2 = ({ diff --git a/web/src/app/chat/input/LLMPopover.tsx b/web/src/app/chat/input/LLMPopover.tsx index 5704d623707..6d64a2fec25 100644 --- a/web/src/app/chat/input/LLMPopover.tsx +++ b/web/src/app/chat/input/LLMPopover.tsx @@ -8,10 +8,7 @@ import { getDisplayNameForModel, LlmDescriptor } from "@/lib/hooks"; import { modelSupportsImageInput } from "@/lib/llm/utils"; import { LLMProviderDescriptor } from "@/app/admin/configuration/llm/interfaces"; import { getProviderIcon } from "@/app/admin/configuration/llm/utils"; -import { - MinimalPersonaSnapshot, - Persona, -} from "@/app/admin/assistants/interfaces"; +import { MinimalPersonaSnapshot } from "@/app/admin/assistants/interfaces"; import { LlmManager } from "@/lib/hooks"; import { diff --git a/web/src/app/chat/lib.tsx b/web/src/app/chat/lib.tsx index a2dc34d8e7d..3f4243d3ec9 100644 --- a/web/src/app/chat/lib.tsx +++ b/web/src/app/chat/lib.tsx @@ -28,10 +28,7 @@ import { AgenticMessageResponseIDInfo, UserKnowledgeFilePacket, } from "./interfaces"; -import { - MinimalPersonaSnapshot, - Persona, -} from "../admin/assistants/interfaces"; +import { MinimalPersonaSnapshot } from "../admin/assistants/interfaces"; import { ReadonlyURLSearchParams } from "next/navigation"; import { SEARCH_PARAM_NAMES } from "./searchParams"; import { Settings } from "../admin/settings/interfaces"; diff --git a/web/src/app/chat/my-documents/[id]/UserFolderContent.tsx b/web/src/app/chat/my-documents/[id]/UserFolderContent.tsx index 9ac07f3eb6f..c8458393682 100644 --- a/web/src/app/chat/my-documents/[id]/UserFolderContent.tsx +++ b/web/src/app/chat/my-documents/[id]/UserFolderContent.tsx @@ -6,7 +6,6 @@ import { ArrowUp, ArrowDown, Trash, - Upload, } from "lucide-react"; import { useDocumentsContext } from "../DocumentsContext"; import { useChatContext } from "@/components/context/ChatContext"; diff --git a/web/src/app/chat/my-documents/components/FilePicker.tsx b/web/src/app/chat/my-documents/components/FilePicker.tsx index e6e3e84f255..3341b3fc5b7 100644 --- a/web/src/app/chat/my-documents/components/FilePicker.tsx +++ b/web/src/app/chat/my-documents/components/FilePicker.tsx @@ -34,7 +34,6 @@ import { TooltipTrigger, TooltipContent, } from "@/components/ui/tooltip"; -import { useRouter } from "next/navigation"; import { usePopup } from "@/components/admin/connectors/Popup"; import { getFormattedDateTime } from "@/lib/dateUtils"; import { FileUploadSection } from "../[id]/components/upload/FileUploadSection"; diff --git a/web/src/app/chat/my-documents/components/SelectedItemsList.tsx b/web/src/app/chat/my-documents/components/SelectedItemsList.tsx index 41844f8cf44..1a3b71ec99a 100644 --- a/web/src/app/chat/my-documents/components/SelectedItemsList.tsx +++ b/web/src/app/chat/my-documents/components/SelectedItemsList.tsx @@ -2,7 +2,6 @@ import React from "react"; import { cn, truncateString } from "@/lib/utils"; import { Button } from "@/components/ui/button"; import { X, FolderIcon, Loader2 } from "lucide-react"; -import { ScrollArea } from "@/components/ui/scroll-area"; import { FolderResponse, FileResponse } from "../DocumentsContext"; import { getFileIconFromFileNameAndLink } from "@/lib/assistantIconUtils"; import { MinimalOnyxDocument } from "@/lib/search/interfaces"; diff --git a/web/src/app/chat/shared/[chatId]/SharedChatDisplay.tsx b/web/src/app/chat/shared/[chatId]/SharedChatDisplay.tsx index 4dccccd3e3f..7fba199d182 100644 --- a/web/src/app/chat/shared/[chatId]/SharedChatDisplay.tsx +++ b/web/src/app/chat/shared/[chatId]/SharedChatDisplay.tsx @@ -15,14 +15,12 @@ import { useContext, useEffect, useState } from "react"; import { SettingsContext } from "@/components/settings/SettingsProvider"; import { OnyxInitializingLoader } from "@/components/OnyxInitializingLoader"; import { Persona } from "@/app/admin/assistants/interfaces"; -import { Button } from "@/components/ui/button"; import { MinimalOnyxDocument } from "@/lib/search/interfaces"; import TextView from "@/components/chat/TextView"; import { DocumentResults } from "../../documentSidebar/DocumentResults"; import { Modal } from "@/components/Modal"; import FunctionalHeader from "@/components/chat/Header"; import FixedLogo from "@/components/logo/FixedLogo"; -import { useRouter } from "next/navigation"; import Link from "next/link"; function BackToOnyxButton({ diff --git a/web/src/app/ee/admin/standard-answer/page.tsx b/web/src/app/ee/admin/standard-answer/page.tsx index 3755bac895a..59486535196 100644 --- a/web/src/app/ee/admin/standard-answer/page.tsx +++ b/web/src/app/ee/admin/standard-answer/page.tsx @@ -25,7 +25,6 @@ import { deleteStandardAnswer } from "./lib"; import { FilterDropdown } from "@/components/search/filtering/FilterDropdown"; import { FiTag } from "react-icons/fi"; import { PageSelector } from "@/components/PageSelector"; -import { CustomCheckbox } from "@/components/CustomCheckbox"; import Text from "@/components/ui/text"; import { TableHeader } from "@/components/ui/table"; import CreateButton from "@/components/ui/createButton"; diff --git a/web/src/components/Field.tsx b/web/src/components/Field.tsx index 7f5af3d317d..7682c014c2c 100644 --- a/web/src/components/Field.tsx +++ b/web/src/components/Field.tsx @@ -35,7 +35,7 @@ import { CheckedState } from "@radix-ui/react-checkbox"; import { transformLinkUri } from "@/lib/utils"; import FileInput from "@/app/admin/connectors/[connector]/pages/ConnectorInput/FileInput"; -import { DatePicker, DatePickerProps } from "./ui/datePicker"; +import { DatePicker } from "./ui/datePicker"; import { Textarea, TextareaProps } from "./ui/textarea"; export function SectionHeader({ diff --git a/web/src/components/admin/ClientLayout.tsx b/web/src/components/admin/ClientLayout.tsx index 2a7b551988e..54b49487cb0 100644 --- a/web/src/components/admin/ClientLayout.tsx +++ b/web/src/components/admin/ClientLayout.tsx @@ -40,8 +40,6 @@ import { } from "@/app/admin/settings/interfaces"; import Link from "next/link"; import { Button } from "../ui/button"; -import useSWR from "swr"; -import { errorHandlingFetcher } from "@/lib/fetcher"; import { useIsKGExposed } from "@/app/admin/kg/utils"; import { useFederatedOAuthStatus } from "@/lib/hooks/useFederatedOAuthStatus"; diff --git a/web/src/components/assistants/AssistantCards.tsx b/web/src/components/assistants/AssistantCards.tsx index d1aacedfe1d..5e9df5a129f 100644 --- a/web/src/components/assistants/AssistantCards.tsx +++ b/web/src/components/assistants/AssistantCards.tsx @@ -1,7 +1,4 @@ -import { - MinimalPersonaSnapshot, - Persona, -} from "@/app/admin/assistants/interfaces"; +import { MinimalPersonaSnapshot } from "@/app/admin/assistants/interfaces"; import { AssistantIcon } from "@/components/assistants/AssistantIcon"; import { useSortable } from "@dnd-kit/sortable"; import React from "react"; diff --git a/web/src/components/context/AppProvider.tsx b/web/src/components/context/AppProvider.tsx index 4b22c548a27..8e693336a9d 100644 --- a/web/src/components/context/AppProvider.tsx +++ b/web/src/components/context/AppProvider.tsx @@ -4,7 +4,7 @@ import { UserProvider } from "../user/UserProvider"; import { ProviderContextProvider } from "../chat/ProviderContext"; import { SettingsProvider } from "../settings/SettingsProvider"; import { AssistantsProvider } from "./AssistantsContext"; -import { Persona } from "@/app/admin/assistants/interfaces"; +import { MinimalPersonaSnapshot } from "@/app/admin/assistants/interfaces"; import { User } from "@/lib/types"; import { ModalProvider } from "./ModalContext"; import { AuthTypeMetadata } from "@/lib/userSS"; @@ -13,7 +13,7 @@ interface AppProviderProps { children: React.ReactNode; user: User | null; settings: CombinedSettings; - assistants: Persona[]; + assistants: MinimalPersonaSnapshot[]; hasAnyConnectors: boolean; hasImageCompatibleModel: boolean; authTypeMetadata: AuthTypeMetadata; diff --git a/web/src/components/context/AssistantsContext.tsx b/web/src/components/context/AssistantsContext.tsx index 3aa685d420d..950f6e7ba75 100644 --- a/web/src/components/context/AssistantsContext.tsx +++ b/web/src/components/context/AssistantsContext.tsx @@ -8,10 +8,7 @@ import React, { SetStateAction, Dispatch, } from "react"; -import { - MinimalPersonaSnapshot, - Persona, -} from "@/app/admin/assistants/interfaces"; +import { MinimalPersonaSnapshot } from "@/app/admin/assistants/interfaces"; import { classifyAssistants, orderAssistantsForUser, @@ -42,7 +39,14 @@ const AssistantsContext = createContext( export const AssistantsProvider: React.FC<{ children: React.ReactNode; initialAssistants: MinimalPersonaSnapshot[]; -}> = ({ children, initialAssistants }) => { + hasAnyConnectors?: boolean; + hasImageCompatibleModel?: boolean; +}> = ({ + children, + initialAssistants, + hasAnyConnectors, + hasImageCompatibleModel, +}) => { const [assistants, setAssistants] = useState( initialAssistants || [] ); diff --git a/web/src/components/credentials/actions/CreateCredential.tsx b/web/src/components/credentials/actions/CreateCredential.tsx index 88331acf202..17bfdec7f26 100644 --- a/web/src/components/credentials/actions/CreateCredential.tsx +++ b/web/src/components/credentials/actions/CreateCredential.tsx @@ -3,17 +3,13 @@ import { Button } from "@/components/ui/button"; import { ValidSources, AccessType } from "@/lib/types"; import { FaAccusoft } from "react-icons/fa"; import { submitCredential } from "@/components/admin/connectors/CredentialForm"; -import { BooleanFormField, TextFormField } from "@/components/Field"; +import { TextFormField } from "@/components/Field"; import { Form, Formik, FormikHelpers } from "formik"; import { PopupSpec } from "@/components/admin/connectors/Popup"; import { getSourceDocLink } from "@/lib/sources"; import GDriveMain from "@/app/admin/connectors/[connector]/pages/gdrive/GoogleDrivePage"; import { Connector } from "@/lib/connectors/connectors"; -import { - Credential, - credentialTemplates, - getDisplayNameForCredentialKey, -} from "@/lib/connectors/credentials"; +import { Credential, credentialTemplates } from "@/lib/connectors/credentials"; import { PlusCircleIcon } from "../../icons/icons"; import { GmailMain } from "@/app/admin/connectors/[connector]/pages/gmail/GmailPage"; import { ActionType, dictionaryType } from "../types"; diff --git a/web/src/lib/assistants/utils.ts b/web/src/lib/assistants/utils.ts index 97fd26b8896..106b8c3c862 100644 --- a/web/src/lib/assistants/utils.ts +++ b/web/src/lib/assistants/utils.ts @@ -1,7 +1,4 @@ -import { - MinimalPersonaSnapshot, - Persona, -} from "@/app/admin/assistants/interfaces"; +import { MinimalPersonaSnapshot } from "@/app/admin/assistants/interfaces"; import { User } from "../types"; import { checkUserIsNoAuthUser } from "../user"; import { personaComparator } from "@/app/admin/assistants/lib"; diff --git a/web/src/lib/chat/fetchAssistantdata.ts b/web/src/lib/chat/fetchAssistantdata.ts index 79cc848cbfd..f17b70de593 100644 --- a/web/src/lib/chat/fetchAssistantdata.ts +++ b/web/src/lib/chat/fetchAssistantdata.ts @@ -1,8 +1,5 @@ import { fetchSS } from "@/lib/utilsSS"; -import { - MinimalPersonaSnapshot, - Persona, -} from "@/app/admin/assistants/interfaces"; +import { MinimalPersonaSnapshot } from "@/app/admin/assistants/interfaces"; import { fetchLLMProvidersSS } from "@/lib/llm/fetchLLMs"; import { fetchAssistantsSS } from "../assistants/fetchAssistantsSS"; import { modelSupportsImageInput } from "../llm/utils"; From 1e50a4af752f0df8016b79c1ecb0c87a5b84009d Mon Sep 17 00:00:00 2001 From: Weves Date: Fri, 4 Jul 2025 15:22:08 -0700 Subject: [PATCH 05/10] Fix mypy --- backend/onyx/server/openai_assistants_api/asssistants_api.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/backend/onyx/server/openai_assistants_api/asssistants_api.py b/backend/onyx/server/openai_assistants_api/asssistants_api.py index 97d182fb057..c1423f694ee 100644 --- a/backend/onyx/server/openai_assistants_api/asssistants_api.py +++ b/backend/onyx/server/openai_assistants_api/asssistants_api.py @@ -17,6 +17,7 @@ from onyx.db.persona import get_persona_by_id from onyx.db.persona import get_personas_for_user from onyx.db.persona import mark_persona_as_deleted +from onyx.db.persona import PersonaLoadType from onyx.db.persona import upsert_persona from onyx.db.prompts import upsert_prompt from onyx.db.tools import get_tool_by_name @@ -244,10 +245,10 @@ def list_assistants( ) -> ListAssistantsResponse: personas = list( get_personas_for_user( + load_type=PersonaLoadType.FULL, user=user, db_session=db_session, get_editable=False, - joinedload_all=True, ) ) From fad732f598b5d7a5f2cc549225e54e08658292fc Mon Sep 17 00:00:00 2001 From: Weves Date: Fri, 4 Jul 2025 15:34:31 -0700 Subject: [PATCH 06/10] fix --- .../onyx/server/features/persona/models.py | 22 ++++++------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/backend/onyx/server/features/persona/models.py b/backend/onyx/server/features/persona/models.py index ab6ee4b3892..a5e1aee8829 100644 --- a/backend/onyx/server/features/persona/models.py +++ b/backend/onyx/server/features/persona/models.py @@ -174,7 +174,6 @@ class PersonaSnapshot(BaseModel): @classmethod def from_model(cls, persona: Persona) -> "PersonaSnapshot": - # Safely handle potentially unloaded relationships return PersonaSnapshot( id=persona.id, name=persona.name, @@ -184,21 +183,14 @@ def from_model(cls, persona: Persona) -> "PersonaSnapshot": icon_shape=persona.icon_shape, icon_color=persona.icon_color, uploaded_image_id=persona.uploaded_image_id, - user_file_ids=[file.id for file in getattr(persona, "user_files", [])], - user_folder_ids=[ - folder.id for folder in getattr(persona, "user_folders", []) - ], + user_file_ids=[file.id for file in persona.user_files], + user_folder_ids=[folder.id for folder in persona.user_folders], display_priority=persona.display_priority, is_default_persona=persona.is_default_persona, builtin_persona=persona.builtin_persona, starter_messages=persona.starter_messages, - tools=[ - ToolSnapshot.from_model(tool) for tool in getattr(persona, "tools", []) - ], - labels=[ - PersonaLabelSnapshot.from_model(label) - for label in getattr(persona, "labels", []) - ], + tools=[ToolSnapshot.from_model(tool) for tool in persona.tools], + labels=[PersonaLabelSnapshot.from_model(label) for label in persona.labels], owner=( MinimalUserSnapshot(id=persona.user.id, email=persona.user.email) if persona.user @@ -206,12 +198,12 @@ def from_model(cls, persona: Persona) -> "PersonaSnapshot": ), users=[ MinimalUserSnapshot(id=user.id, email=user.email) - for user in getattr(persona, "users", []) + for user in persona.users ], - groups=[user_group.id for user_group in getattr(persona, "groups", [])], + groups=[user_group.id for user_group in persona.groups], document_sets=[ DocumentSet.from_model(document_set_model) - for document_set_model in getattr(persona, "document_sets", []) + for document_set_model in persona.document_sets ], llm_model_provider_override=persona.llm_model_provider_override, llm_model_version_override=persona.llm_model_version_override, From 3ee5653618995a070551d425f28ed4c1f7210f0a Mon Sep 17 00:00:00 2001 From: Weves Date: Fri, 4 Jul 2025 16:28:50 -0700 Subject: [PATCH 07/10] small tweak --- backend/onyx/db/persona.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/backend/onyx/db/persona.py b/backend/onyx/db/persona.py index c2e2dc134be..1fddbdb418f 100644 --- a/backend/onyx/db/persona.py +++ b/backend/onyx/db/persona.py @@ -329,8 +329,9 @@ def update_persona_public_status( def get_personas_for_user( + # defines how much of the persona to pre-load + load_type: PersonaLoadType, # if user is `None` assume the user is an admin or auth is disabled - load_type: PersonaLoadType, # defines how much of the persona to pre-load user: User | None, db_session: Session, get_editable: bool = True, From 47ea4a0d91fc1211114f7953708eca51caae10e8 Mon Sep 17 00:00:00 2001 From: Weves Date: Wed, 9 Jul 2025 10:39:05 -0700 Subject: [PATCH 08/10] Address EL cmments --- backend/onyx/db/persona.py | 8 ++++++++ web/src/app/admin/assistants/interfaces.ts | 6 +++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/backend/onyx/db/persona.py b/backend/onyx/db/persona.py index 1fddbdb418f..4171ef47705 100644 --- a/backend/onyx/db/persona.py +++ b/backend/onyx/db/persona.py @@ -358,6 +358,14 @@ def get_personas_for_user( joinedload(Persona.labels), # only show document sets in the UI that the assistant has access to joinedload(Persona.document_sets), + joinedload(DocumentSet.connector_credential_pairs).joinedload( + ConnectorCredentialPair.connector + ), + joinedload(DocumentSet.connector_credential_pairs).joinedload( + ConnectorCredentialPair.credential + ), + # user + joinedload(Persona.user), ) elif load_type == PersonaLoadType.FULL: stmt = stmt.options( diff --git a/web/src/app/admin/assistants/interfaces.ts b/web/src/app/admin/assistants/interfaces.ts index fe88e3a9449..b78047c2ac3 100644 --- a/web/src/app/admin/assistants/interfaces.ts +++ b/web/src/app/admin/assistants/interfaces.ts @@ -23,9 +23,9 @@ export interface MinimalPersonaSnapshot { id: number; name: string; description: string; - tools: ToolSnapshot[]; - starter_messages: StarterMessage[] | null; - document_sets: DocumentSet[]; + tools: readonly ToolSnapshot[]; + starter_messages: readonly StarterMessage[] | null; + document_sets: readonly DocumentSet[]; llm_model_version_override?: string; llm_model_provider_override?: string; From 7cc68454369d66e3dea2f09f1b58b4f89f521372 Mon Sep 17 00:00:00 2001 From: Weves Date: Wed, 9 Jul 2025 11:07:01 -0700 Subject: [PATCH 09/10] fix --- backend/onyx/db/persona.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/backend/onyx/db/persona.py b/backend/onyx/db/persona.py index 4171ef47705..f5beb1d71e8 100644 --- a/backend/onyx/db/persona.py +++ b/backend/onyx/db/persona.py @@ -358,12 +358,12 @@ def get_personas_for_user( joinedload(Persona.labels), # only show document sets in the UI that the assistant has access to joinedload(Persona.document_sets), - joinedload(DocumentSet.connector_credential_pairs).joinedload( - ConnectorCredentialPair.connector - ), - joinedload(DocumentSet.connector_credential_pairs).joinedload( - ConnectorCredentialPair.credential - ), + joinedload(Persona.document_sets) + .joinedload(DocumentSet.connector_credential_pairs) + .joinedload(ConnectorCredentialPair.connector), + joinedload(Persona.document_sets) + .joinedload(DocumentSet.connector_credential_pairs) + .joinedload(ConnectorCredentialPair.credential), # user joinedload(Persona.user), ) From ed6c7566537ecf16d86a5425480dc973f1480922 Mon Sep 17 00:00:00 2001 From: Weves Date: Wed, 9 Jul 2025 11:32:33 -0700 Subject: [PATCH 10/10] Fix build --- web/src/app/admin/assistants/AssistantEditor.tsx | 8 +++----- web/src/app/admin/assistants/interfaces.ts | 6 +++--- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/web/src/app/admin/assistants/AssistantEditor.tsx b/web/src/app/admin/assistants/AssistantEditor.tsx index 558982d58fd..641406c3ab2 100644 --- a/web/src/app/admin/assistants/AssistantEditor.tsx +++ b/web/src/app/admin/assistants/AssistantEditor.tsx @@ -258,7 +258,7 @@ export function AssistantEditor({ existingPersona?.llm_model_version_override ?? null, starter_messages: existingPersona?.starter_messages?.length ? existingPersona.starter_messages - : [{ message: "" }], + : [{ message: "", name: "" }], enabled_tools_map: enabledToolsMap, icon_color: existingPersona?.icon_color ?? defautIconColor, icon_shape: existingPersona?.icon_shape ?? defaultIconShape, @@ -526,10 +526,8 @@ export function AssistantEditor({ // to tell the backend to not fetch any documents const numChunks = searchToolEnabled ? values.num_chunks || 25 : 0; const starterMessages = values.starter_messages - .filter( - (message: { message: string }) => message.message.trim() !== "" - ) - .map((message: { message: string; name?: string }) => ({ + .filter((message: StarterMessage) => message.message.trim() !== "") + .map((message: StarterMessage) => ({ message: message.message, name: message.message, })); diff --git a/web/src/app/admin/assistants/interfaces.ts b/web/src/app/admin/assistants/interfaces.ts index b78047c2ac3..fe88e3a9449 100644 --- a/web/src/app/admin/assistants/interfaces.ts +++ b/web/src/app/admin/assistants/interfaces.ts @@ -23,9 +23,9 @@ export interface MinimalPersonaSnapshot { id: number; name: string; description: string; - tools: readonly ToolSnapshot[]; - starter_messages: readonly StarterMessage[] | null; - document_sets: readonly DocumentSet[]; + tools: ToolSnapshot[]; + starter_messages: StarterMessage[] | null; + document_sets: DocumentSet[]; llm_model_version_override?: string; llm_model_provider_override?: string;