📝 (endpoints.py): Add support for caching components to improve performance and reduce load on the server

📝 (setup.py): Change function create_or_update_starter_projects to be asynchronous to handle await calls
📝 (types.py): Add caching mechanism to function aget_all_components to improve performance and reduce redundant calls
📝 (main.py): Change call to create_or_update_starter_projects to be awaited to handle asynchronous operation
📝 (index.tsx): Add functionality to reload components in the menu bar to update component data dynamically
📝 (index.ts): Modify getAll function to accept a force_refresh parameter to force a refresh of data
📝 (typesStore.ts): Update getTypes function to accept a force_refresh parameter and pass it to the getAll function
📝 (index.ts): Update getTypes function in TypesStoreType to accept a force_refresh parameter
📝 (test_initial_setup.py): Update test_create_or_update_starter_projects to await the asynchronous function create_or_update_starter_projects
This commit is contained in:
ogabrielluiz 2024-06-04 13:35:00 -03:00
commit a65965fcb3
9 changed files with 75 additions and 13 deletions

View file

@ -1,3 +1,4 @@
from asyncio import Lock
from http import HTTPStatus from http import HTTPStatus
from typing import TYPE_CHECKING, Annotated, List, Optional, Union from typing import TYPE_CHECKING, Annotated, List, Optional, Union
from uuid import UUID from uuid import UUID
@ -32,11 +33,18 @@ from langflow.services.cache.utils import save_uploaded_file
from langflow.services.database.models.flow import Flow from langflow.services.database.models.flow import Flow
from langflow.services.database.models.flow.utils import get_all_webhook_components_in_flow, get_flow_by_id from langflow.services.database.models.flow.utils import get_all_webhook_components_in_flow, get_flow_by_id
from langflow.services.database.models.user.model import User from langflow.services.database.models.user.model import User
from langflow.services.deps import get_session, get_session_service, get_settings_service, get_task_service from langflow.services.deps import (
get_cache_service,
get_session,
get_session_service,
get_settings_service,
get_task_service,
)
from langflow.services.session.service import SessionService from langflow.services.session.service import SessionService
from langflow.services.task.service import TaskService from langflow.services.task.service import TaskService
if TYPE_CHECKING: if TYPE_CHECKING:
from langflow.services.cache.base import CacheService
from langflow.services.settings.manager import SettingsService from langflow.services.settings.manager import SettingsService
router = APIRouter(tags=["Base"]) router = APIRouter(tags=["Base"])
@ -45,12 +53,18 @@ router = APIRouter(tags=["Base"])
@router.get("/all", dependencies=[Depends(get_current_active_user)]) @router.get("/all", dependencies=[Depends(get_current_active_user)])
async def get_all( async def get_all(
settings_service=Depends(get_settings_service), settings_service=Depends(get_settings_service),
cache_service: "CacheService" = Depends(dependency=get_cache_service),
force_refresh: bool = False,
): ):
from langflow.interface.types import aget_all_types_dict from langflow.interface.types import aget_all_types_dict
logger.debug("Building langchain types dict") logger.debug("Building langchain types dict")
try: try:
async with Lock() as lock:
all_types_dict = await cache_service.get(key="all_types_dict", lock=lock)
if not all_types_dict or force_refresh:
all_types_dict = await aget_all_types_dict(settings_service.settings.components_path) all_types_dict = await aget_all_types_dict(settings_service.settings.components_path)
await cache_service.set(key="all_types_dict", value=all_types_dict, lock=lock)
return all_types_dict return all_types_dict
except Exception as exc: except Exception as exc:
logger.exception(exc) logger.exception(exc)

View file

@ -14,7 +14,7 @@ from loguru import logger
from sqlmodel import select from sqlmodel import select
from langflow.base.constants import FIELD_FORMAT_ATTRIBUTES, NODE_FORMAT_ATTRIBUTES from langflow.base.constants import FIELD_FORMAT_ATTRIBUTES, NODE_FORMAT_ATTRIBUTES
from langflow.interface.types import get_all_components from langflow.interface.types import aget_all_components
from langflow.services.auth.utils import create_super_user from langflow.services.auth.utils import create_super_user
from langflow.services.database.models.flow.model import Flow, FlowCreate from langflow.services.database.models.flow.model import Flow, FlowCreate
from langflow.services.database.models.folder.model import Folder, FolderCreate from langflow.services.database.models.folder.model import Folder, FolderCreate
@ -364,10 +364,10 @@ def find_existing_flow(session, flow_id, flow_endpoint_name):
return None return None
def create_or_update_starter_projects(): async def create_or_update_starter_projects():
components_paths = get_settings_service().settings.components_path components_paths = get_settings_service().settings.components_path
try: try:
all_types_dict = get_all_components(components_paths, as_dict=True) all_types_dict = await aget_all_components(components_paths, as_dict=True)
except Exception as e: except Exception as e:
logger.exception(f"Error loading components: {e}") logger.exception(f"Error loading components: {e}")
raise e raise e

View file

@ -1,3 +1,7 @@
import json
from cachetools import TTLCache, cached
from langflow.custom.utils import abuild_custom_components, build_custom_components from langflow.custom.utils import abuild_custom_components, build_custom_components
@ -13,6 +17,27 @@ def get_all_types_dict(components_paths):
return custom_components_from_file return custom_components_from_file
# TypeError: unhashable type: 'list'
def key_func(*args, **kwargs):
# components_paths is a list of paths
return json.dumps(args) + json.dumps(kwargs)
@cached(cache=TTLCache(maxsize=1, ttl=15), key=key_func)
async def aget_all_components(components_paths, as_dict=False):
"""Get all components names combining native and custom components."""
all_types_dict = await aget_all_types_dict(components_paths)
components = {} if as_dict else []
for category in all_types_dict.values():
for component in category.values():
component["name"] = component["display_name"]
if as_dict:
components[component["name"]] = component
else:
components.append(component)
return components
def get_all_components(components_paths, as_dict=False): def get_all_components(components_paths, as_dict=False):
"""Get all components names combining native and custom components.""" """Get all components names combining native and custom components."""
all_types_dict = get_all_types_dict(components_paths) all_types_dict = get_all_types_dict(components_paths)

View file

@ -51,7 +51,7 @@ def get_lifespan(fix_migration=False, socketio_server=None, version=None):
setup_llm_caching() setup_llm_caching()
LangfuseInstance.update() LangfuseInstance.update()
initialize_super_user_if_needed() initialize_super_user_if_needed()
create_or_update_starter_projects() await create_or_update_starter_projects()
load_flows_from_directory() load_flows_from_directory()
yield yield
except Exception as exc: except Exception as exc:

View file

@ -16,6 +16,7 @@ import FlowSettingsModal from "../../../../modals/flowSettingsModal";
import useAlertStore from "../../../../stores/alertStore"; import useAlertStore from "../../../../stores/alertStore";
import useFlowStore from "../../../../stores/flowStore"; import useFlowStore from "../../../../stores/flowStore";
import useFlowsManagerStore from "../../../../stores/flowsManagerStore"; import useFlowsManagerStore from "../../../../stores/flowsManagerStore";
import { useTypesStore } from "../../../../stores/typesStore";
import { cn } from "../../../../utils/utils"; import { cn } from "../../../../utils/utils";
import IconComponent from "../../../genericIconComponent"; import IconComponent from "../../../genericIconComponent";
import ShadTooltip from "../../../shadTooltipComponent"; import ShadTooltip from "../../../shadTooltipComponent";
@ -34,6 +35,7 @@ export const MenuBar = ({}: {}): JSX.Element => {
const uploadFlow = useFlowsManagerStore((state) => state.uploadFlow); const uploadFlow = useFlowsManagerStore((state) => state.uploadFlow);
const navigate = useNavigate(); const navigate = useNavigate();
const isBuilding = useFlowStore((state) => state.isBuilding); const isBuilding = useFlowStore((state) => state.isBuilding);
const getTypes = useTypesStore((state) => state.getTypes);
function handleAddFlow(duplicate?: boolean) { function handleAddFlow(duplicate?: boolean) {
try { try {
@ -55,6 +57,12 @@ export const MenuBar = ({}: {}): JSX.Element => {
} }
} }
function handleReloadComponents() {
getTypes(true).then(() => {
setSuccessData({ title: "Components reloaded successfully" });
});
}
function printByBuildStatus() { function printByBuildStatus() {
if (isBuilding) { if (isBuilding) {
return "Building..."; return "Building...";
@ -188,6 +196,18 @@ export const MenuBar = ({}: {}): JSX.Element => {
)} )}
<span className="absolute right-2 top-[0.4em]">Y</span> <span className="absolute right-2 top-[0.4em]">Y</span>
</DropdownMenuItem> </DropdownMenuItem>
<DropdownMenuItem
onClick={() => {
handleReloadComponents();
}}
className="cursor-pointer"
>
<IconComponent
name="RefreshCcw"
className="header-menu-options"
/>
Reload Components
</DropdownMenuItem>
</DropdownMenuContent> </DropdownMenuContent>
</DropdownMenu> </DropdownMenu>
<FlowSettingsModal <FlowSettingsModal

View file

@ -32,10 +32,13 @@ import {
/** /**
* Fetches all objects from the API endpoint. * Fetches all objects from the API endpoint.
* *
* @param {boolean} force_refresh - Whether to force a refresh of the data.
* @returns {Promise<AxiosResponse<APIObjectType>>} A promise that resolves to an AxiosResponse containing all the objects. * @returns {Promise<AxiosResponse<APIObjectType>>} A promise that resolves to an AxiosResponse containing all the objects.
*/ */
export async function getAll(): Promise<AxiosResponse<APIObjectType>> { export async function getAll(
return await api.get(`${BASE_URL_API}all`); force_refresh: boolean = true
): Promise<AxiosResponse<APIObjectType>> {
return await api.get(`${BASE_URL_API}all?force_refresh=${force_refresh}`);
} }
const GITHUB_API_URL = "https://api.github.com"; const GITHUB_API_URL = "https://api.github.com";

View file

@ -21,11 +21,11 @@ export const useTypesStore = create<TypesStoreType>((set, get) => ({
types: {}, types: {},
templates: {}, templates: {},
data: {}, data: {},
getTypes: () => { getTypes: (force_refresh: boolean = false) => {
return new Promise<void>(async (resolve, reject) => { return new Promise<void>(async (resolve, reject) => {
const setLoading = useFlowsManagerStore.getState().setIsLoading; const setLoading = useFlowsManagerStore.getState().setIsLoading;
setLoading(true); setLoading(true);
getAll() getAll(force_refresh)
.then((response) => { .then((response) => {
const data = response?.data; const data = response?.data;
useAlertStore.setState({ loading: false }); useAlertStore.setState({ loading: false });

View file

@ -7,7 +7,7 @@ export type TypesStoreType = {
setTemplates: (newState: {}) => void; setTemplates: (newState: {}) => void;
data: APIDataType; data: APIDataType;
setData: (newState: {}) => void; setData: (newState: {}) => void;
getTypes: () => Promise<void>; getTypes: (force_refresh?: boolean) => Promise<void>;
ComponentFields: Set<string>; ComponentFields: Set<string>;
setComponentFields: (fields: Set<string>) => void; setComponentFields: (fields: Set<string>) => void;
addComponentField: (field: string) => void; addComponentField: (field: string) => void;

View file

@ -46,7 +46,7 @@ def test_get_project_data():
async def test_create_or_update_starter_projects(client): async def test_create_or_update_starter_projects(client):
with session_scope() as session: with session_scope() as session:
# Run the function to create or update projects # Run the function to create or update projects
create_or_update_starter_projects() await create_or_update_starter_projects()
# Get the number of projects returned by load_starter_projects # Get the number of projects returned by load_starter_projects
num_projects = len(load_starter_projects()) num_projects = len(load_starter_projects())