From 8a42db1d1456714f6a1793dcdaa3d55d1686ef63 Mon Sep 17 00:00:00 2001 From: Joey Ballentine Date: Sat, 15 Jul 2023 12:58:03 -0400 Subject: [PATCH 1/9] Remove pip list from frontend --- backend/src/server.py | 50 +++++++++++- src/common/dependencies.ts | 1 + src/renderer/contexts/DependencyContext.tsx | 87 +++++++-------------- 3 files changed, 79 insertions(+), 59 deletions(-) diff --git a/backend/src/server.py b/backend/src/server.py index 56b21dadb..f873e513b 100644 --- a/backend/src/server.py +++ b/backend/src/server.py @@ -56,6 +56,7 @@ def __init__(self): # This is necessary because we don't know Sanic's event loop yet. self.queue: EventQueue = None # type: ignore self.setup_queue: EventQueue = None # type: ignore + self.pip_queue: EventQueue = None # type: ignore self.pool = ThreadPoolExecutor(max_workers=4) @staticmethod @@ -406,7 +407,17 @@ async def get_dependencies(_request: Request): await nodes_available() all_dependencies = [] for package in api.registry.packages.values(): - pkg_dependencies = [x.toDict() for x in package.dependencies] + pkg_dependencies = [] + for pkg_dep in package.dependencies: + installed_version = installed_packages.get(pkg_dep.pypi_name, None) + pkg_dep_item = { + **pkg_dep.toDict(), + } + if installed_version is None: + pkg_dep_item["installed"] = False + else: + pkg_dep_item["installed"] = installed_version + pkg_dependencies.append(pkg_dep_item) if package.name == "chaiNNer_standard": continue else: @@ -553,6 +564,43 @@ async def update_progress( logger.info("Done.") +@app.get("/pip-sse") +async def pip_sse(request: Request): + ctx = AppContext.get(request.app) + headers = {"Cache-Control": "no-cache"} + response = await request.respond(headers=headers, content_type="text/event-stream") + while True: + message = await ctx.pip_queue.get() + if response is not None: + await response.send(f"event: {message['event']}\n") + await response.send(f"data: {stringify(message['data'])}\n\n") + + +@app.route("/dependencies/install", methods=["POST"]) +async def install_dependencies_req(request: Request): + await nodes_available() + ctx = AppContext.get(request.app) + + try: + full_data = dict(request.json) # type: ignore + dep_info: List[DependencyInfo] = [ + { + "package_name": dep["pypi_name"], + "display_name": dep["display_name"], + "version": dep["version"], + "from_file": dep["from_file"], + } + for dep in full_data["dependencies"] + ] + # await install_dependencies(dep_info, ctx.pip_queue, logger) + return json(successResponse("Successfully installed dependencies!"), status=200) + except Exception as exception: + logger.error(exception, exc_info=True) + return json( + errorResponse("Error installing dependencies!", exception), status=500 + ) + + exit_code = 0 diff --git a/src/common/dependencies.ts b/src/common/dependencies.ts index 311630bf7..a54155c7b 100644 --- a/src/common/dependencies.ts +++ b/src/common/dependencies.ts @@ -10,6 +10,7 @@ export interface PyPiPackage { */ sizeEstimate: number; autoUpdate: boolean; + installed: Version | false; } export interface Package { name: string; diff --git a/src/renderer/contexts/DependencyContext.tsx b/src/renderer/contexts/DependencyContext.tsx index 13f27aa1e..8efae802a 100644 --- a/src/renderer/contexts/DependencyContext.tsx +++ b/src/renderer/contexts/DependencyContext.tsx @@ -37,8 +37,6 @@ import { Version } from '../../common/common-types'; import { Package, PyPiPackage } from '../../common/dependencies'; import { Integration, externalIntegrations } from '../../common/externalIntegrations'; import { log } from '../../common/log'; -import { OnStdio, PipList, runPipInstall, runPipList, runPipUninstall } from '../../common/pip'; -import { ipcRenderer } from '../../common/safeIpc'; import { noop } from '../../common/util'; import { versionGt } from '../../common/version'; import { useAsyncEffect } from '../hooks/useAsyncEffect'; @@ -77,7 +75,7 @@ const formatSizeEstimate = (packages: readonly PyPiPackage[]): string => formatBytes(packages.reduce((a, p) => a + p.sizeEstimate, 0)); const FeaturePackage = memo( - ({ pkg, installedVersion }: { pkg: PyPiPackage; installedVersion?: Version }) => { + ({ pkg, installedVersion }: { pkg: PyPiPackage; installedVersion: Version | false }) => { let color = 'red.500'; let tagText = 'Missing'; let versionString: string = pkg.version; @@ -116,7 +114,6 @@ const FeaturePackage = memo( const Feature = memo( ({ dep, - pipList, isRunningShell, progress, onInstall, @@ -124,17 +121,15 @@ const Feature = memo( onUpdate, }: { dep: Package; - pipList: PipList; isRunningShell: boolean; progress?: number; onInstall: () => void; onUninstall: () => void; onUpdate: () => void; }) => { - const missingPackages = dep.dependencies.filter((p) => !pipList[p.pypiName]); - const outdatedPackages = dep.dependencies.filter((p) => { - const installedVersion = pipList[p.pypiName]; - return installedVersion && versionGt(p.version, installedVersion); + const missingPackages = dep.dependencies.filter((d) => !d.installed); + const outdatedPackages = dep.dependencies.filter((d) => { + return d.installed && versionGt(d.version, d.installed); }); return ( @@ -256,11 +251,11 @@ const Feature = memo( key={dep.name} w="full" > - {dep.dependencies.map((p) => ( + {dep.dependencies.map((d) => ( ))} @@ -280,39 +275,20 @@ export const DependencyProvider = memo(({ children }: React.PropsWithChildren(); - const refreshInstalledPackages = useCallback(() => setPipList(undefined), [setPipList]); - const [isConsoleOpen, setIsConsoleOpen] = useState(false); const [usePipDirectly, setUsePipDirectly] = useState(false); - useAsyncEffect(() => { - if (pipList) return; - return { - supplier: () => runPipList(pythonInfo), - successEffect: setPipList, - }; - }, [pythonInfo, pipList, setPipList]); - - const [hasNvidia, setHasNvidia] = useState(false); - useAsyncEffect( - () => ({ - supplier: async () => !!(await ipcRenderer.invoke('get-nvidia-gpu-name')), - successEffect: setHasNvidia, - }), - [] - ); - - const [availableDeps, setAvailableDeps] = useState([]); + const [depList, setDepList] = useState([]); + const [refreshDepListTrigger, setRefreshDepListTrigger] = useState(false); useAsyncEffect( () => ({ supplier: async () => { const res = await backend.dependencies(); return res.filter((d) => d.dependencies.length > 0); }, - successEffect: setAvailableDeps, + successEffect: setDepList, }), - [backend] + [backend, refreshDepListTrigger] ); const [installingPackage, setInstallingPackage] = useState(null); @@ -343,7 +319,7 @@ export const DependencyProvider = memo(({ children }: React.PropsWithChildren { setIsRunningShell(false); - refreshInstalledPackages(); + setRefreshDepListTrigger(!refreshDepListTrigger); setInstallingPackage(null); setUninstallingPackage(null); setProgress(0); @@ -353,16 +329,16 @@ export const DependencyProvider = memo(({ children }: React.PropsWithChildren { setInstallingPackage(dep); - changePackages(() => - runPipInstall(pythonInfo, [dep], usePipDirectly ? undefined : setProgress, onStdio) - ); + // changePackages(() => + // runPipInstall(pythonInfo, [dep], usePipDirectly ? undefined : setProgress, onStdio) + // ); }; const uninstallPackage = (dep: Package) => { setUninstallingPackage(dep); - changePackages(() => - runPipUninstall(pythonInfo, [dep], usePipDirectly ? undefined : setProgress, onStdio) - ); + // changePackages(() => + // runPipUninstall(pythonInfo, [dep], usePipDirectly ? undefined : setProgress, onStdio) + // ); }; useEffect(() => { @@ -373,22 +349,18 @@ export const DependencyProvider = memo(({ children }: React.PropsWithChildren { - return availableDeps.filter(({ dependencies }) => - dependencies.some(({ pypiName, version }) => { - if (!pipList) { - return false; - } - const installedVersion = pipList[pypiName]; - if (!installedVersion) { + return depList.filter(({ dependencies }) => + dependencies.some(({ version, installed }) => { + if (!installed) { return true; } - return versionGt(version, installedVersion); + return versionGt(version, installed); }) ).length; - }, [pipList, availableDeps]); + }, [depList]); const value = useMemoObject({ openDependencyManager: onOpen, @@ -444,14 +416,14 @@ export const DependencyProvider = memo(({ children }: React.PropsWithChildren - + {/* {hasNvidia ? 'CUDA supported' : 'CUDA not supported'} - + */} - {!pipList ? ( + {!depList ? ( ) : ( - {availableDeps.map((dep) => { + {depList.map((dep) => { const install = () => installPackage(dep); const uninstall = () => { showAlert({ @@ -541,7 +513,6 @@ export const DependencyProvider = memo(({ children }: React.PropsWithChildren Date: Thu, 20 Jul 2023 18:07:31 -0400 Subject: [PATCH 2/9] sdjknmskcvnfdjvndfv --- backend/src/server.py | 38 ------------------- src/renderer/contexts/DependencyContext.tsx | 41 ++++++++++++--------- 2 files changed, 24 insertions(+), 55 deletions(-) diff --git a/backend/src/server.py b/backend/src/server.py index f873e513b..7b94facba 100644 --- a/backend/src/server.py +++ b/backend/src/server.py @@ -56,7 +56,6 @@ def __init__(self): # This is necessary because we don't know Sanic's event loop yet. self.queue: EventQueue = None # type: ignore self.setup_queue: EventQueue = None # type: ignore - self.pip_queue: EventQueue = None # type: ignore self.pool = ThreadPoolExecutor(max_workers=4) @staticmethod @@ -564,43 +563,6 @@ async def update_progress( logger.info("Done.") -@app.get("/pip-sse") -async def pip_sse(request: Request): - ctx = AppContext.get(request.app) - headers = {"Cache-Control": "no-cache"} - response = await request.respond(headers=headers, content_type="text/event-stream") - while True: - message = await ctx.pip_queue.get() - if response is not None: - await response.send(f"event: {message['event']}\n") - await response.send(f"data: {stringify(message['data'])}\n\n") - - -@app.route("/dependencies/install", methods=["POST"]) -async def install_dependencies_req(request: Request): - await nodes_available() - ctx = AppContext.get(request.app) - - try: - full_data = dict(request.json) # type: ignore - dep_info: List[DependencyInfo] = [ - { - "package_name": dep["pypi_name"], - "display_name": dep["display_name"], - "version": dep["version"], - "from_file": dep["from_file"], - } - for dep in full_data["dependencies"] - ] - # await install_dependencies(dep_info, ctx.pip_queue, logger) - return json(successResponse("Successfully installed dependencies!"), status=200) - except Exception as exception: - logger.error(exception, exc_info=True) - return json( - errorResponse("Error installing dependencies!", exception), status=500 - ) - - exit_code = 0 diff --git a/src/renderer/contexts/DependencyContext.tsx b/src/renderer/contexts/DependencyContext.tsx index 8efae802a..34b308337 100644 --- a/src/renderer/contexts/DependencyContext.tsx +++ b/src/renderer/contexts/DependencyContext.tsx @@ -37,6 +37,7 @@ import { Version } from '../../common/common-types'; import { Package, PyPiPackage } from '../../common/dependencies'; import { Integration, externalIntegrations } from '../../common/externalIntegrations'; import { log } from '../../common/log'; +import { OnStdio, runPipInstall, runPipUninstall } from '../../common/pip'; import { noop } from '../../common/util'; import { versionGt } from '../../common/version'; import { useAsyncEffect } from '../hooks/useAsyncEffect'; @@ -278,7 +279,7 @@ export const DependencyProvider = memo(({ children }: React.PropsWithChildren([]); + const [depList, setDepList] = useState(undefined); const [refreshDepListTrigger, setRefreshDepListTrigger] = useState(false); useAsyncEffect( () => ({ @@ -319,26 +320,30 @@ export const DependencyProvider = memo(({ children }: React.PropsWithChildren { setIsRunningShell(false); - setRefreshDepListTrigger(!refreshDepListTrigger); setInstallingPackage(null); setUninstallingPackage(null); setProgress(0); - restart().catch(log.error); + restart() + .catch(log.error) + .then(() => { + setRefreshDepListTrigger(!refreshDepListTrigger); + }) + .catch(log.error); }); }; const installPackage = (dep: Package) => { setInstallingPackage(dep); - // changePackages(() => - // runPipInstall(pythonInfo, [dep], usePipDirectly ? undefined : setProgress, onStdio) - // ); + changePackages(() => + runPipInstall(pythonInfo, [dep], usePipDirectly ? undefined : setProgress, onStdio) + ); }; const uninstallPackage = (dep: Package) => { setUninstallingPackage(dep); - // changePackages(() => - // runPipUninstall(pythonInfo, [dep], usePipDirectly ? undefined : setProgress, onStdio) - // ); + changePackages(() => + runPipUninstall(pythonInfo, [dep], usePipDirectly ? undefined : setProgress, onStdio) + ); }; useEffect(() => { @@ -352,14 +357,16 @@ export const DependencyProvider = memo(({ children }: React.PropsWithChildren { - return depList.filter(({ dependencies }) => - dependencies.some(({ version, installed }) => { - if (!installed) { - return true; - } - return versionGt(version, installed); - }) - ).length; + return ( + depList?.filter(({ dependencies }) => + dependencies.some(({ version, installed }) => { + if (!installed) { + return true; + } + return versionGt(version, installed); + }) + ).length ?? 0 + ); }, [depList]); const value = useMemoObject({ From 809fb32e72a35c1bcd9c3be19cda974e4c0cc45a Mon Sep 17 00:00:00 2001 From: Joey Ballentine Date: Thu, 20 Jul 2023 18:20:14 -0400 Subject: [PATCH 3/9] use react query (still jank) --- src/renderer/contexts/DependencyContext.tsx | 45 +++++++++++---------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/src/renderer/contexts/DependencyContext.tsx b/src/renderer/contexts/DependencyContext.tsx index 81929721c..0cb70e74e 100644 --- a/src/renderer/contexts/DependencyContext.tsx +++ b/src/renderer/contexts/DependencyContext.tsx @@ -32,6 +32,7 @@ import { } from '@chakra-ui/react'; import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { BsQuestionCircle, BsTerminalFill } from 'react-icons/bs'; +import { useQuery } from 'react-query'; import { createContext, useContext } from 'use-context-selector'; import { Version } from '../../common/common-types'; import { Package, PyPiPackage } from '../../common/dependencies'; @@ -279,27 +280,27 @@ export const DependencyProvider = memo(({ children }: React.PropsWithChildren(undefined); - const [refreshDepListTrigger, setRefreshDepListTrigger] = useState(false); - useAsyncEffect( - () => ({ - supplier: async () => (await backend.listNvidiaGpus()).length > 0, - successEffect: setHasNvidia, - }), - [backend] - ); - - const [availableDeps, setAvailableDeps] = useState([]); - useAsyncEffect( - () => ({ - supplier: async () => { - const res = await backend.dependencies(); - return res.filter((d) => d.dependencies.length > 0); - }, - successEffect: setDepList, - }), - [backend, refreshDepListTrigger] - ); + const { + data: depList, + isLoading, + isFetching, + refetch, + } = useQuery({ + queryKey: 'dependencies', + queryFn: async () => { + try { + const response = await backend.dependencies(); + return response.filter((d) => d.dependencies.length > 0); + } catch (error) { + log.error(error); + throw error; + } + }, + cacheTime: 0, + retry: 25, + refetchOnWindowFocus: false, + refetchInterval: false, + }); const [installingPackage, setInstallingPackage] = useState(null); const [uninstallingPackage, setUninstallingPackage] = useState(null); @@ -335,7 +336,7 @@ export const DependencyProvider = memo(({ children }: React.PropsWithChildren { - setRefreshDepListTrigger(!refreshDepListTrigger); + refetch().catch(log.error); }) .catch(log.error); }); From d023ae5b960dcbcedfd61694618c8628aa1980e4 Mon Sep 17 00:00:00 2001 From: Joey Ballentine Date: Thu, 20 Jul 2023 20:27:45 -0400 Subject: [PATCH 4/9] reorder some things --- src/renderer/contexts/DependencyContext.tsx | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/renderer/contexts/DependencyContext.tsx b/src/renderer/contexts/DependencyContext.tsx index 0cb70e74e..ebc3c38c9 100644 --- a/src/renderer/contexts/DependencyContext.tsx +++ b/src/renderer/contexts/DependencyContext.tsx @@ -329,14 +329,18 @@ export const DependencyProvider = memo(({ children }: React.PropsWithChildren { - setIsRunningShell(false); - setInstallingPackage(null); - setUninstallingPackage(null); - setProgress(0); restart() .catch(log.error) .then(() => { - refetch().catch(log.error); + refetch() + .catch(log.error) + .then(() => { + setIsRunningShell(false); + setInstallingPackage(null); + setUninstallingPackage(null); + setProgress(0); + }) + .catch(log.error); }) .catch(log.error); }); From b0398b2d6550699c06cdb26968d7ed3007e5d97a Mon Sep 17 00:00:00 2001 From: Joey Ballentine Date: Thu, 20 Jul 2023 20:30:32 -0400 Subject: [PATCH 5/9] put back nvidia thingy --- src/renderer/contexts/DependencyContext.tsx | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/renderer/contexts/DependencyContext.tsx b/src/renderer/contexts/DependencyContext.tsx index ebc3c38c9..aec6c0ce9 100644 --- a/src/renderer/contexts/DependencyContext.tsx +++ b/src/renderer/contexts/DependencyContext.tsx @@ -280,6 +280,15 @@ export const DependencyProvider = memo(({ children }: React.PropsWithChildren ({ + supplier: async () => (await backend.listNvidiaGpus()).length > 0, + successEffect: setHasNvidia, + }), + [backend] + ); + const { data: depList, isLoading, @@ -437,14 +446,14 @@ export const DependencyProvider = memo(({ children }: React.PropsWithChildren - {/* + {hasNvidia ? 'CUDA supported' : 'CUDA not supported'} - */} + Date: Thu, 20 Jul 2023 20:39:43 -0400 Subject: [PATCH 6/9] Update src/renderer/contexts/DependencyContext.tsx --- src/renderer/contexts/DependencyContext.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/renderer/contexts/DependencyContext.tsx b/src/renderer/contexts/DependencyContext.tsx index aec6c0ce9..8ac3cd701 100644 --- a/src/renderer/contexts/DependencyContext.tsx +++ b/src/renderer/contexts/DependencyContext.tsx @@ -291,7 +291,6 @@ export const DependencyProvider = memo(({ children }: React.PropsWithChildren Date: Thu, 20 Jul 2023 20:39:48 -0400 Subject: [PATCH 7/9] Update src/renderer/contexts/DependencyContext.tsx --- src/renderer/contexts/DependencyContext.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/renderer/contexts/DependencyContext.tsx b/src/renderer/contexts/DependencyContext.tsx index 8ac3cd701..3131d7b4b 100644 --- a/src/renderer/contexts/DependencyContext.tsx +++ b/src/renderer/contexts/DependencyContext.tsx @@ -291,7 +291,6 @@ export const DependencyProvider = memo(({ children }: React.PropsWithChildren Date: Thu, 20 Jul 2023 22:57:10 -0400 Subject: [PATCH 8/9] lint --- src/renderer/contexts/DependencyContext.tsx | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/renderer/contexts/DependencyContext.tsx b/src/renderer/contexts/DependencyContext.tsx index 3131d7b4b..f2488a68f 100644 --- a/src/renderer/contexts/DependencyContext.tsx +++ b/src/renderer/contexts/DependencyContext.tsx @@ -289,10 +289,7 @@ export const DependencyProvider = memo(({ children }: React.PropsWithChildren { try { From 70fd828116ba6e98e6b71ec36060b3d9cb451ce9 Mon Sep 17 00:00:00 2001 From: Joey Ballentine Date: Sun, 23 Jul 2023 22:25:49 -0400 Subject: [PATCH 9/9] use null instead of false --- backend/src/server.py | 2 +- src/common/dependencies.ts | 2 +- src/renderer/contexts/DependencyContext.tsx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/src/server.py b/backend/src/server.py index 2df51025b..bbe550d39 100644 --- a/backend/src/server.py +++ b/backend/src/server.py @@ -439,7 +439,7 @@ async def get_dependencies(_request: Request): **pkg_dep.toDict(), } if installed_version is None: - pkg_dep_item["installed"] = False + pkg_dep_item["installed"] = None else: pkg_dep_item["installed"] = installed_version pkg_dependencies.append(pkg_dep_item) diff --git a/src/common/dependencies.ts b/src/common/dependencies.ts index a54155c7b..39818312c 100644 --- a/src/common/dependencies.ts +++ b/src/common/dependencies.ts @@ -10,7 +10,7 @@ export interface PyPiPackage { */ sizeEstimate: number; autoUpdate: boolean; - installed: Version | false; + installed: Version | null; } export interface Package { name: string; diff --git a/src/renderer/contexts/DependencyContext.tsx b/src/renderer/contexts/DependencyContext.tsx index f2488a68f..a8e0b3448 100644 --- a/src/renderer/contexts/DependencyContext.tsx +++ b/src/renderer/contexts/DependencyContext.tsx @@ -77,7 +77,7 @@ const formatSizeEstimate = (packages: readonly PyPiPackage[]): string => formatBytes(packages.reduce((a, p) => a + p.sizeEstimate, 0)); const FeaturePackage = memo( - ({ pkg, installedVersion }: { pkg: PyPiPackage; installedVersion: Version | false }) => { + ({ pkg, installedVersion }: { pkg: PyPiPackage; installedVersion: Version | null }) => { let color = 'red.500'; let tagText = 'Missing'; let versionString: string = pkg.version;