Compare commits

..

1 Commits

Author SHA1 Message Date
pablodanswer
81e1ac9183 remove empty directory 2024-10-28 16:10:34 -07:00
23 changed files with 111 additions and 208 deletions

View File

@@ -21,8 +21,6 @@ celery_app.config_from_object("danswer.background.celery.configs.beat")
@beat_init.connect
def on_beat_init(sender: Any, **kwargs: Any) -> None:
logger.info("beat_init signal received.")
# celery beat shouldn't touch the db at all. But just setting a low minimum here.
SqlEngine.set_app_name(POSTGRES_CELERY_BEAT_APP_NAME)
SqlEngine.init_engine(pool_size=2, max_overflow=0)
app_base.wait_for_redis(sender, **kwargs)

View File

@@ -58,7 +58,7 @@ def on_worker_init(sender: Any, **kwargs: Any) -> None:
logger.info(f"Multiprocessing start method: {multiprocessing.get_start_method()}")
SqlEngine.set_app_name(POSTGRES_CELERY_WORKER_HEAVY_APP_NAME)
SqlEngine.init_engine(pool_size=4, max_overflow=12)
SqlEngine.init_engine(pool_size=8, max_overflow=0)
app_base.wait_for_redis(sender, **kwargs)
app_base.on_secondary_worker_init(sender, **kwargs)

View File

@@ -166,6 +166,19 @@ def on_worker_init(sender: Any, **kwargs: Any) -> None:
r.delete(key)
# @worker_process_init.connect
# def on_worker_process_init(sender: Any, **kwargs: Any) -> None:
# """This only runs inside child processes when the worker is in pool=prefork mode.
# This may be technically unnecessary since we're finding prefork pools to be
# unstable and currently aren't planning on using them."""
# logger.info("worker_process_init signal received.")
# SqlEngine.set_app_name(POSTGRES_CELERY_WORKER_INDEXING_CHILD_APP_NAME)
# SqlEngine.init_engine(pool_size=5, max_overflow=0)
# # https://stackoverflow.com/questions/43944787/sqlalchemy-celery-with-scoped-session-error
# SqlEngine.get_engine().dispose(close=False)
@worker_ready.connect
def on_worker_ready(sender: Any, **kwargs: Any) -> None:
app_base.on_worker_ready(sender, **kwargs)

View File

@@ -34,7 +34,6 @@ from danswer.db.engine import get_session_with_tenant
from danswer.db.enums import ConnectorCredentialPairStatus
from danswer.db.models import ConnectorCredentialPair
from danswer.redis.redis_pool import get_redis_client
from danswer.utils.logger import pruning_ctx
from danswer.utils.logger import setup_logger
logger = setup_logger()
@@ -230,15 +229,10 @@ def connector_pruning_generator_task(
and compares those IDs to locally stored documents and deletes all locally stored IDs missing
from the most recently pulled document ID list"""
pruning_ctx_dict = pruning_ctx.get()
pruning_ctx_dict["cc_pair_id"] = cc_pair_id
pruning_ctx_dict["request_id"] = self.request.id
pruning_ctx.set(pruning_ctx_dict)
r = get_redis_client(tenant_id=tenant_id)
rcp = RedisConnectorPruning(cc_pair_id)
r = get_redis_client(tenant_id=tenant_id)
lock = r.lock(
DanswerRedisLocks.PRUNING_LOCK_PREFIX + f"_{rcp._id}",
timeout=CELERY_PRUNING_LOCK_TIMEOUT,

View File

@@ -11,8 +11,7 @@ from typing import Any
from typing import Literal
from typing import Optional
from danswer.configs.constants import POSTGRES_CELERY_WORKER_INDEXING_CHILD_APP_NAME
from danswer.db.engine import SqlEngine
from danswer.db.engine import get_sqlalchemy_engine
from danswer.utils.logger import setup_logger
logger = setup_logger()
@@ -38,9 +37,7 @@ def _initializer(
if kwargs is None:
kwargs = {}
logger.info("Initializing spawned worker child process.")
SqlEngine.set_app_name(POSTGRES_CELERY_WORKER_INDEXING_CHILD_APP_NAME)
SqlEngine.init_engine(pool_size=4, max_overflow=12, pool_recycle=60)
get_sqlalchemy_engine().dispose(close=False)
return func(*args, **kwargs)

View File

@@ -91,13 +91,12 @@ class ConfluenceConnector(LoadConnector, PollConnector, SlimConnector):
cql_page_query += f" and id='{page_id}'"
self.cql_page_query = cql_page_query
self.cql_time_filter = ""
self.cql_label_filter = ""
self.cql_time_filter = ""
if labels_to_skip:
labels_to_skip = list(set(labels_to_skip))
comma_separated_labels = ",".join(f"'{label}'" for label in labels_to_skip)
self.cql_label_filter = f" and label not in ({comma_separated_labels})"
comma_separated_labels = ",".join(labels_to_skip)
self.cql_label_filter = f"&label not in ({comma_separated_labels})"
def load_credentials(self, credentials: dict[str, Any]) -> dict[str, Any] | None:
# see https://github.com/atlassian-api/atlassian-python-api/blob/master/atlassian/rest_client.py
@@ -126,8 +125,7 @@ class ConfluenceConnector(LoadConnector, PollConnector, SlimConnector):
for comment in comments:
comment_string += "\nComment:\n"
comment_string += extract_text_from_confluence_html(
confluence_client=self.confluence_client,
confluence_object=comment,
confluence_client=self.confluence_client, confluence_object=comment
)
return comment_string

View File

@@ -231,7 +231,6 @@ def _get_chunks_via_visit_api(
return document_chunks
@retry(tries=10, delay=1, backoff=2)
def get_all_vespa_ids_for_document_id(
document_id: str,
index_name: str,

View File

@@ -104,7 +104,6 @@ class TenantRedis(redis.Redis):
"startswith",
"sadd",
"srem",
"scard",
] # Regular methods that need simple prefixing
if item == "scan_iter":

View File

@@ -1,4 +1,3 @@
import contextvars
import logging
import os
from collections.abc import MutableMapping
@@ -17,10 +16,6 @@ from shared_configs.configs import TENANT_ID_PREFIX
logging.addLevelName(logging.INFO + 5, "NOTICE")
pruning_ctx: contextvars.ContextVar[dict[str, Any]] = contextvars.ContextVar(
"pruning_ctx", default=dict()
)
class IndexAttemptSingleton:
"""Used to tell if this process is an indexing job, and if so what is the
@@ -66,22 +61,14 @@ class DanswerLoggingAdapter(logging.LoggerAdapter):
) -> tuple[str, MutableMapping[str, Any]]:
# If this is an indexing job, add the attempt ID to the log message
# This helps filter the logs for this specific indexing
index_attempt_id = IndexAttemptSingleton.get_index_attempt_id()
attempt_id = IndexAttemptSingleton.get_index_attempt_id()
cc_pair_id = IndexAttemptSingleton.get_connector_credential_pair_id()
pruning_ctx_dict = pruning_ctx.get()
if len(pruning_ctx_dict) > 0:
if "request_id" in pruning_ctx_dict:
msg = f"[Prune: {pruning_ctx_dict['request_id']}] {msg}"
if attempt_id is not None:
msg = f"[Attempt: {attempt_id}] {msg}"
if "cc_pair_id" in pruning_ctx_dict:
msg = f"[CC Pair: {pruning_ctx_dict['cc_pair_id']}] {msg}"
else:
if index_attempt_id is not None:
msg = f"[Index Attempt: {index_attempt_id}] {msg}"
if cc_pair_id is not None:
msg = f"[CC Pair: {cc_pair_id}] {msg}"
if cc_pair_id is not None:
msg = f"[CC Pair: {cc_pair_id}] {msg}"
# Add tenant information if it differs from default
# This will always be the case for authenticated API requests

View File

@@ -10,11 +10,6 @@ from ee.danswer.configs.app_configs import API_KEY_HASH_ROUNDS
_API_KEY_HEADER_NAME = "Authorization"
# NOTE for others who are curious: In the context of a header, "X-" often refers
# to non-standard, experimental, or custom headers in HTTP or other protocols. It
# indicates that the header is not part of the official standards defined by
# organizations like the Internet Engineering Task Force (IETF).
_API_KEY_HEADER_ALTERNATIVE_NAME = "X-Danswer-Authorization"
_BEARER_PREFIX = "Bearer "
_API_KEY_PREFIX = "dn_"
_API_KEY_LEN = 192
@@ -48,9 +43,7 @@ def build_displayable_api_key(api_key: str) -> str:
def get_hashed_api_key_from_request(request: Request) -> str | None:
raw_api_key_header = request.headers.get(
_API_KEY_HEADER_ALTERNATIVE_NAME
) or request.headers.get(_API_KEY_HEADER_NAME)
raw_api_key_header = request.headers.get(_API_KEY_HEADER_NAME)
if raw_api_key_header is None:
return None

View File

@@ -2512,7 +2512,7 @@ export function ChatPage({
)}
</div>
</div>
<FixedLogo chat />
<FixedLogo />
</div>
</div>
<DocumentSidebar

View File

@@ -16,7 +16,6 @@ import { Hoverable } from "@/components/Hoverable";
import { Popover } from "@/components/popover/Popover";
import { StarFeedback } from "@/components/icons/icons";
import { IconType } from "react-icons";
import { FiRefreshCw } from "react-icons/fi";
export function RegenerateDropdown({
options,
@@ -69,7 +68,7 @@ export function RegenerateDropdown({
py-1.5
"
>
Regenerate with
Pick a model
</p>
{options.map((option, ind) => {
const isSelected = option.value === selected;
@@ -93,7 +92,7 @@ export function RegenerateDropdown({
content={
<div onClick={() => toggleDropdownVisible(!isOpen)}>
{!alternate ? (
<Hoverable size={16} icon={FiRefreshCw as IconType} />
<Hoverable size={16} icon={StarFeedback as IconType} />
) : (
<Hoverable
size={16}

View File

@@ -299,9 +299,7 @@ export const AIMessage = ({
className={"py-5 ml-4 px-5 relative flex "}
>
<div
className={`mx-auto ${
shared ? "w-full" : "w-[90%]"
} max-w-message-max`}
className={`mx-auto ${shared ? "w-full" : "w-[90%]"} max-w-message-max`}
>
<div className={`desktop:mr-12 ${!shared && "mobile:ml-0 md:ml-8"}`}>
<div className="flex">
@@ -547,21 +545,9 @@ export const AIMessage = ({
className={`
absolute -bottom-5
z-10
invisible ${
(isHovering ||
isRegenerateHovered ||
settings?.isMobile) &&
"!visible"
}
opacity-0 ${
(isHovering ||
isRegenerateHovered ||
settings?.isMobile) &&
"!opacity-100"
}
translate-y-2 ${
(isHovering || settings?.isMobile) && "!translate-y-0"
}
invisible ${(isHovering || isRegenerateHovered || settings?.isMobile) && "!visible"}
opacity-0 ${(isHovering || isRegenerateHovered || settings?.isMobile) && "!opacity-100"}
translate-y-2 ${(isHovering || settings?.isMobile) && "!translate-y-0"}
transition-transform duration-300 ease-in-out
flex md:flex-row gap-x-0.5 bg-background-125/40 -mx-1.5 p-1.5 rounded-lg
`}
@@ -717,7 +703,9 @@ export const HumanMessage = ({
}, [isEditing]);
const handleEditSubmit = () => {
onEdit?.(editedContent);
if (editedContent.trim() !== content.trim()) {
onEdit?.(editedContent);
}
setIsEditing(false);
};
@@ -733,9 +721,7 @@ export const HumanMessage = ({
onMouseLeave={() => setIsHovered(false)}
>
<div
className={`text-user-text mx-auto ${
shared ? "w-full" : "w-[90%]"
} max-w-[790px]`}
className={`text-user-text mx-auto ${shared ? "w-full" : "w-[90%]"} max-w-[790px]`}
>
<div className="xl:ml-8">
<div className="flex flex-col mr-4">

View File

@@ -122,7 +122,7 @@ export function ChatSessionDisplay({
);
}}
>
<BasicSelectable chat padding="extra" fullWidth selected={isSelected}>
<BasicSelectable padding="extra" fullWidth selected={isSelected}>
<>
<div className="flex relative">
{isRenamingChat ? (
@@ -142,11 +142,7 @@ export function ChatSessionDisplay({
{chatName || `Chat ${chatSession.id}`}
<span
className={`absolute right-0 top-0 h-full w-8 bg-gradient-to-r from-transparent
${
isSelected
? "to-background-chat-hover"
: " to-background-chat-selected group-hover:to-background-chat-hover"
} `}
${isSelected ? "to-background-200" : " to-background-100 group-hover:to-background-200"} `}
/>
</p>
)}
@@ -180,9 +176,7 @@ export function ChatSessionDisplay({
This chat will expire{" "}
{daysUntilExpiration < 1
? "today"
: `in ${daysUntilExpiration} day${
daysUntilExpiration !== 1 ? "s" : ""
}`}
: `in ${daysUntilExpiration} day${daysUntilExpiration !== 1 ? "s" : ""}`}
</p>
}
>

View File

@@ -94,7 +94,7 @@ export const HistorySidebar = forwardRef<HTMLDivElement, HistorySidebarProps>(
bg-background-sidebar
w-full
border-r
border-sidebar-border
border-border
flex
flex-col relative
h-screen

View File

@@ -8,7 +8,7 @@ import Link from "next/link";
import { useContext } from "react";
import { FiSidebar } from "react-icons/fi";
export default function FixedLogo({ chat }: { chat?: boolean }) {
export default function FixedLogo() {
const combinedSettings = useContext(SettingsContext);
const settings = combinedSettings?.settings;
const enterpriseSettings = combinedSettings?.enterpriseSettings;
@@ -28,15 +28,13 @@ export default function FixedLogo({ chat }: { chat?: boolean }) {
<div className="w-full">
{enterpriseSettings && enterpriseSettings.application_name ? (
<div>
<HeaderTitle chat={chat}>
{enterpriseSettings.application_name}
</HeaderTitle>
<HeaderTitle>{enterpriseSettings.application_name}</HeaderTitle>
{!NEXT_PUBLIC_DO_NOT_USE_TOGGLE_OFF_DANSWER_POWERED && (
<p className="text-xs text-subtle">Powered by Danswer</p>
)}
</div>
) : (
<HeaderTitle chat={chat}>Danswer</HeaderTitle>
<HeaderTitle>Danswer</HeaderTitle>
)}
</div>
</div>

View File

@@ -36,7 +36,7 @@ const ToggleSwitch = () => {
};
return (
<div className="bg-background-toggle mobile:mt-8 flex rounded-full p-1">
<div className="bg-gray-100 mobile:mt-8 flex rounded-full p-1">
<div
className={`absolute mobile:mt-8 top-1 bottom-1 ${
activeTab === "chat" ? "w-[45%]" : "w-[50%]"
@@ -148,9 +148,7 @@ export default function FunctionalWrapper({
{(!settings ||
(settings.search_page_enabled && settings.chat_page_enabled)) && (
<div
className={`mobile:hidden z-30 flex fixed ${
chatBannerPresent ? (twoLines ? "top-20" : "top-14") : "top-4"
} left-1/2 transform -translate-x-1/2`}
className={`mobile:hidden z-30 flex fixed ${chatBannerPresent ? (twoLines ? "top-20" : "top-14") : "top-4"} left-1/2 transform -translate-x-1/2`}
>
<div
style={{ transition: "width 0.30s ease-out" }}

View File

@@ -81,14 +81,12 @@ export function BasicSelectable({
children,
selected,
hasBorder,
chat,
fullWidth = false,
padding = "normal",
}: {
children: string | JSX.Element;
selected: boolean;
hasBorder?: boolean;
chat?: boolean;
fullWidth?: boolean;
padding?: "none" | "normal" | "extra";
}) {
@@ -102,15 +100,7 @@ export function BasicSelectable({
${padding == "extra" && "p-1.5"}
select-none
${hasBorder ? "border border-border" : ""}
${
selected
? chat
? "bg-background-chat-selected"
: "bg-hover"
: chat
? "bg-background-chat-hover"
: "hover:bg-hover"
}
${selected ? "bg-hover" : "hover:bg-hover"}
${fullWidth ? "w-full" : ""}`}
>
{children}

View File

@@ -2,21 +2,13 @@
import React from "react";
export function HeaderTitle({
children,
chat,
}: {
children: JSX.Element | string;
chat?: boolean;
}) {
export function HeaderTitle({ children }: { children: JSX.Element | string }) {
const isString = typeof children === "string";
const textSize = isString && children.length > 10 ? "text-xl" : "text-2xl";
return (
<h1
className={`${textSize} ${
chat ? "text-text-sidebar-header" : "text-text-header"
} break-words line-clamp-2 ellipsis text-strong leading-none font-bold`}
className={`${textSize} break-words line-clamp-2 ellipsis text-strong leading-none font-bold`}
>
{children}
</h1>

View File

@@ -70,11 +70,7 @@ export default function SearchAnswer({
return (
<div
ref={answerContainerRef}
className={`my-4 ${
searchAnswerExpanded ? "min-h-[16rem]" : "h-[16rem]"
} ${
!searchAnswerExpanded && searchAnswerOverflowing && "overflow-y-hidden"
} p-4 border-2 border-search-answer-border rounded-lg relative`}
className={`my-4 ${searchAnswerExpanded ? "min-h-[16rem]" : "h-[16rem]"} ${!searchAnswerExpanded && searchAnswerOverflowing && "overflow-y-hidden"} p-4 border-2 border-border rounded-lg relative`}
>
<div>
<div className="flex gap-x-2">

View File

@@ -68,8 +68,8 @@ export const AnimatedToggle = ({
{/* Toggle switch */}
<div
className={`
w-10 h-6 flex items-center rounded-full p-1 transition-all duration-300 ease-in-out
${isOn ? "bg-toggled-background" : "bg-untoggled-background"}
w-10 h-6 flex items-center rounded-full p-1 transition-all duration-300 ease-in-out
${isOn ? "bg-background-400" : "bg-background-200"}
`}
>
<div
@@ -178,14 +178,10 @@ export const FullSearchBar = ({
suppressContentEditableWarning={true}
/>
<div
className={`flex flex-nowrap ${
showingSidebar ? " 2xl:justify-between" : "2xl:justify-end"
} justify-between 4xl:justify-end w-full max-w-full items-center space-x-3 py-3 px-4`}
className={`flex flex-nowrap ${showingSidebar ? " 2xl:justify-between" : "2xl:justify-end"} justify-between 4xl:justify-end w-full max-w-full items-center space-x-3 py-3 px-4`}
>
<div
className={`-my-1 flex-grow 4xl:hidden ${
!showingSidebar && "2xl:hidden"
}`}
className={`-my-1 flex-grow 4xl:hidden ${!showingSidebar && "2xl:hidden"}`}
>
{(ccPairs.length > 0 || documentSets.length > 0) && (
<HorizontalSourceSelector
@@ -212,11 +208,7 @@ export const FullSearchBar = ({
>
<SendIcon
size={28}
className={`text-emphasis ${
disabled || !query
? "bg-disabled-submit-background"
: "bg-submit-background"
} text-white p-1 rounded-full`}
className={`text-emphasis ${disabled || !query ? "bg-disabled-submit-background" : "bg-submit-background"} text-white p-1 rounded-full`}
/>
</button>
</div>

View File

@@ -724,9 +724,7 @@ export const SearchSection = ({
{
<div
className={`desktop:px-24 w-full ${
chatBannerPresent && "mt-10"
} pt-10 relative max-w-[2000px] xl:max-w-[1430px] mx-auto`}
className={`desktop:px-24 w-full ${chatBannerPresent && "mt-10"} pt-10 relative max-w-[2000px] xl:max-w-[1430px] mx-auto`}
>
<div className="absolute z-10 mobile:px-4 mobile:max-w-searchbar-max mobile:w-[90%] top-12 desktop:left-4 hidden 2xl:block mobile:left-1/2 mobile:transform mobile:-translate-x-1/2 desktop:w-52 3xl:w-64">
{!settings?.isMobile &&
@@ -851,7 +849,7 @@ export const SearchSection = ({
</div>
</div>
</div>
<FixedLogo chat />
<FixedLogo />
</>
);
};

View File

@@ -1,4 +1,5 @@
import { fetchSS } from "@/lib/utilsSS";
import { CCPairBasicInfo } from "@/lib/types";
import { Persona } from "@/app/admin/assistants/interfaces";
import { fetchLLMProvidersSS } from "@/lib/llm/fetchLLMs";
import { personaComparator } from "@/app/admin/assistants/lib";
@@ -11,77 +12,58 @@ interface AssistantData {
hasImageCompatibleModel: boolean;
}
export async function fetchAssistantData(): Promise<AssistantData> {
// Default state if anything fails
const defaultState: AssistantData = {
assistants: [],
hasAnyConnectors: false,
hasImageCompatibleModel: false,
};
const [assistants, assistantsFetchError] = await fetchAssistantsSS();
const ccPairsResponse = await fetchSS("/manage/indexing-status");
try {
// Fetch core assistants data first
const [assistants, assistantsFetchError] = await fetchAssistantsSS();
if (assistantsFetchError) {
console.error(`Failed to fetch assistants - ${assistantsFetchError}`);
return defaultState;
}
// Parallel fetch of additional data
const [ccPairsResponse, llmProviders] = await Promise.all([
fetchSS("/manage/indexing-status").catch((error) => {
console.error("Failed to fetch connectors:", error);
return null;
}),
fetchLLMProvidersSS().catch((error) => {
console.error("Failed to fetch LLM providers:", error);
return [];
}),
]);
// Process visible assistants
let filteredAssistants = assistants.filter(
(assistant) => assistant.is_visible
);
// Process connector status
const hasAnyConnectors = ccPairsResponse?.ok
? (await ccPairsResponse.json()).length > 0
: false;
// Filter assistants based on connector status
if (!hasAnyConnectors) {
filteredAssistants = filteredAssistants.filter(
(assistant) => assistant.num_chunks === 0
);
}
// Sort assistants
filteredAssistants.sort(personaComparator);
// Check for image-compatible models
const hasImageCompatibleModel = llmProviders.some(
(provider) =>
provider.provider === "openai" ||
provider.model_names.some((model) => checkLLMSupportsImageInput(model))
);
// Filter out image generation tools if no compatible model
if (!hasImageCompatibleModel) {
filteredAssistants = filteredAssistants.filter(
(assistant) =>
!assistant.tools.some(
(tool) => tool.in_code_tool_id === "ImageGenerationTool"
)
);
}
return {
assistants: filteredAssistants,
hasAnyConnectors,
hasImageCompatibleModel,
};
} catch (error) {
console.error("Unexpected error in fetchAssistantData:", error);
return defaultState;
let ccPairs: CCPairBasicInfo[] = [];
if (ccPairsResponse?.ok) {
ccPairs = await ccPairsResponse.json();
} else {
console.log(`Failed to fetch connectors - ${ccPairsResponse?.status}`);
}
const hasAnyConnectors = ccPairs.length > 0;
// if no connectors are setup, only show personas that are pure
// passthrough and don't do any retrieval
let filteredAssistants = assistants;
if (assistantsFetchError) {
console.log(`Failed to fetch assistants - ${assistantsFetchError}`);
}
// remove those marked as hidden by an admin
filteredAssistants = filteredAssistants.filter(
(assistant) => assistant.is_visible
);
if (!hasAnyConnectors) {
filteredAssistants = filteredAssistants.filter(
(assistant) => assistant.num_chunks === 0
);
}
// sort them in priority order
filteredAssistants.sort(personaComparator);
const llmProviders = await fetchLLMProvidersSS();
const hasImageCompatibleModel = llmProviders.some(
(provider) =>
provider.provider === "openai" ||
provider.model_names.some((model) => checkLLMSupportsImageInput(model))
);
if (!hasImageCompatibleModel) {
filteredAssistants = filteredAssistants.filter(
(assistant) =>
!assistant.tools.some(
(tool) => tool.in_code_tool_id === "ImageGenerationTool"
)
);
}
return {
assistants: filteredAssistants,
hasAnyConnectors,
hasImageCompatibleModel,
};
}