Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Backend-defined Dependencies #1834

Merged
merged 68 commits into from
Jun 11, 2023
Merged
Show file tree
Hide file tree
Changes from 57 commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
387b8bf
start of defining dependencies on backend
joeyballentine Apr 17, 2023
c55516a
ncnn dep
joeyballentine Apr 17, 2023
0252885
Merge remote-tracking branch 'chaiNNer-org/main' into backend-deps
joeyballentine Apr 19, 2023
151f102
nvidia helper
joeyballentine Apr 19, 2023
8f3d883
define rest of deps in backend
joeyballentine Apr 19, 2023
b376fd4
some fixes
joeyballentine Apr 19, 2023
724c126
import testing & dep installing
joeyballentine Apr 20, 2023
6a72aaa
fixed install stuff
joeyballentine Apr 20, 2023
75aa35f
this dependency stuff is a mess
joeyballentine Apr 22, 2023
de1f539
...
joeyballentine Apr 22, 2023
d6df278
Merge remote-tracking branch 'chaiNNer-org/main' into backend-deps
joeyballentine May 3, 2023
2a276cf
Merge remote-tracking branch 'chaiNNer-org/main' into backend-deps
joeyballentine May 25, 2023
2af8399
WIP core dependency workaround
joeyballentine May 25, 2023
5ee2243
Fixes
joeyballentine May 25, 2023
e202351
fix scoping issue
joeyballentine May 25, 2023
75a274a
Fixes
joeyballentine May 25, 2023
810ed10
Merge remote-tracking branch 'chaiNNer-org/main' into backend-deps-wip
joeyballentine May 26, 2023
4ccf360
cleanup
joeyballentine May 26, 2023
805ef5c
fix workflow
joeyballentine May 26, 2023
e44c585
some fixes
joeyballentine May 26, 2023
a535784
lint
joeyballentine May 26, 2023
a6b356d
make it not run the b uild workflow
joeyballentine May 26, 2023
78307e1
update requirements.txt
joeyballentine May 26, 2023
122808f
renaming
joeyballentine May 26, 2023
3af6271
reorganize stuff
joeyballentine May 27, 2023
e9e86b7
Correct order of operations
joeyballentine May 27, 2023
3c20fa1
SEE for backend ready event
joeyballentine May 27, 2023
e3b4384
wip more
joeyballentine May 27, 2023
b9e353d
getting closer to the finish line
joeyballentine May 29, 2023
c7f246a
move a few required deps over
joeyballentine May 29, 2023
08e0d2b
remove a few prints
joeyballentine May 29, 2023
6322a69
Merge branch 'main' into backend-deps-wip
joeyballentine May 29, 2023
3dead66
float progress
joeyballentine May 30, 2023
67d31e2
Install multiple dependencies at once + other pr suggestions
joeyballentine Jun 6, 2023
a03b6d4
Other minor PR suggestions
joeyballentine Jun 6, 2023
9c46d85
fix dumb mistake
joeyballentine Jun 7, 2023
2e5da26
Merge remote-tracking branch 'chaiNNer-org/main' into backend-deps-wip
joeyballentine Jun 7, 2023
b088760
fixes & debugging
joeyballentine Jun 7, 2023
bc8e615
lint
joeyballentine Jun 7, 2023
72ed5ab
attempting to use a separate sse
joeyballentine Jun 8, 2023
bee1117
setup-sse from main process
joeyballentine Jun 9, 2023
08f07a5
use progress slice
joeyballentine Jun 10, 2023
6af430c
use task
joeyballentine Jun 10, 2023
bba3691
sleepy time
RunDevelopment Jun 10, 2023
1c65810
reuse event loop, because we probably should
joeyballentine Jun 10, 2023
ca71c53
put_and_wait
RunDevelopment Jun 10, 2023
fc6be08
replace console.log with comment
joeyballentine Jun 10, 2023
0d0c3c5
backend linting
joeyballentine Jun 10, 2023
ccef953
lint + remove logs
joeyballentine Jun 10, 2023
7d71cb4
remove comment
joeyballentine Jun 10, 2023
13289ea
Merge branch 'main' into backend-deps-wip
joeyballentine Jun 10, 2023
2ef2413
Merge remote-tracking branch 'chaiNNer-org/main' into backend-deps-wip
joeyballentine Jun 11, 2023
c1570eb
retry getting python info
joeyballentine Jun 11, 2023
5512a2e
Don't --upgrade, allow None versions
joeyballentine Jun 11, 2023
4c0911a
clean up requirements.txt
joeyballentine Jun 11, 2023
84d8bda
Revert "clean up requirements.txt"
joeyballentine Jun 11, 2023
46bc112
More declarative deps
RunDevelopment Jun 11, 2023
9d40b89
remove unnecessary global
joeyballentine Jun 11, 2023
739c42d
remove comments
joeyballentine Jun 11, 2023
a2de212
other cleanup
joeyballentine Jun 11, 2023
406d989
rename some things
joeyballentine Jun 11, 2023
8fd7c3c
add error listener
joeyballentine Jun 11, 2023
2d633ba
add back loading status
joeyballentine Jun 11, 2023
4106d8a
Pr suggestions
joeyballentine Jun 11, 2023
406d046
Added `nodes_available` function
RunDevelopment Jun 11, 2023
750542c
Removed my stupid log.debug
RunDevelopment Jun 11, 2023
429700f
Update src/renderer/splash.tsx
RunDevelopment Jun 11, 2023
7984730
Update src/renderer/main.tsx
RunDevelopment Jun 11, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/make.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ on:
jobs:
test-build-release:
# commit the word "build" to the commit message to enable this job
if: contains(${{ github.event.head_commit.message }}, 'build')
if: contains(github.event.head_commit.message, 'build')
runs-on: ${{ matrix.os }}

strategy:
Expand Down
1 change: 0 additions & 1 deletion .github/workflows/test-backend.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@ jobs:
- uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- run: npx ts-node ./scripts/install-required-deps.ts
- run: python ./backend/src/run.py --no-run
env:
TYPE_CHECK_LEVEL: 'error'
43 changes: 40 additions & 3 deletions backend/src/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@
typeValidateSchema,
)

KB = 1024**1
MB = 1024**2
GB = 1024**3


def _process_inputs(base_inputs: Iterable[Union[BaseInput, NestedGroup]]):
inputs: List[BaseInput] = []
Expand Down Expand Up @@ -174,11 +178,34 @@ def toDict(self):
}


@dataclass
class Dependency:
display_name: str
pypi_name: str
version: str
size_estimate: int | float
auto_update: bool = False
RunDevelopment marked this conversation as resolved.
Show resolved Hide resolved
extra_index_url: str | None = None

import_name: str | None = None

def toDict(self):
return {
"displayName": self.display_name,
"pypiName": self.pypi_name,
"version": self.version,
"sizeEstimate": int(self.size_estimate),
"autoUpdate": self.auto_update,
"findLink": self.extra_index_url,
}


@dataclass
class Package:
where: str
name: str
dependencies: List[str] = field(default_factory=list)
description: str
dependencies: List[Dependency] = field(default_factory=list)
categories: List[Category] = field(default_factory=list)

def add_category(
Expand All @@ -200,6 +227,12 @@ def add_category(
self.categories.append(result)
return result

def add_dependency(
self,
dependency: Dependency,
):
self.dependencies.append(dependency)


def _iter_py_files(directory: str):
for root, _, files in os.walk(directory):
Expand Down Expand Up @@ -268,8 +301,12 @@ def _refresh_nodes(self):
self.nodes[node.schema_id] = node, sub


# pylint: disable=global-at-module-level
global registry
joeyballentine marked this conversation as resolved.
Show resolved Hide resolved
registry = PackageRegistry()


def add_package(where: str, name: str, dependencies: List[str]) -> Package:
return registry.add(Package(where, name, dependencies))
def add_package(
where: str, name: str, description: str, dependencies: List[Dependency]
) -> Package:
return registry.add(Package(where, name, description, dependencies))
Empty file.
15 changes: 15 additions & 0 deletions backend/src/dependencies/install_essential_deps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from typing import List

from .store import DependencyInfo, install_dependencies

# These are the dependencies we _absolutely need_ to install before we can do anything else
deps: List[DependencyInfo] = [
{
"package_name": "semver",
"version": "3.0.0",
},
]


# Note: We can't be sure we have semver yet so we can't use it to compare versions
install_dependencies(deps)
54 changes: 54 additions & 0 deletions backend/src/dependencies/install_other_deps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
from typing import List

from system import is_arm_mac, is_windows

from .store import DependencyInfo
from .versioned_dependency_helpers import install_version_checked_dependencies

# I'm leaving this here in case I can use the Dependency class in the future, so I don't lose the extra info

# dependencies=[
# Dependency("OpenCV", "opencv-python", "4.7.0.68", 30 * MB, import_name="cv2"),
# Dependency("NumPy", "numpy", "1.23.2", 15 * MB),
# Dependency("Pillow (PIL)", "Pillow", "9.2.0", 3 * MB, import_name="PIL"),
# Dependency("appdirs", "appdirs", "1.4.4", 13.5 * KB),
# Dependency("FFMPEG", "ffmpeg-python", "0.2.0", 25 * KB, import_name="ffmpeg"),
# Dependency("Requests", "requests", "2.28.2", 452 * KB),
# Dependency("re2", "google-re2", "1.0", 275 * KB, import_name="re2"),
# Dependency("scipy", "scipy", "1.9.3", 42 * MB),
# ],

# if is_arm_mac:
# package.add_dependency(Dependency("Pasteboard", "pasteboard", "0.3.3", 19 * KB))
# elif is_windows:
# package.add_dependency(
# Dependency("Pywin32", "pywin32", "304", 12 * MB, import_name="win32clipboard")
# )

deps: List[DependencyInfo] = [
{
"package_name": "appdirs",
"version": "1.4.4",
},
{
"package_name": "ffmpeg-python",
"version": "0.2.0",
},
{
"package_name": "requests",
"version": "2.28.2",
},
RunDevelopment marked this conversation as resolved.
Show resolved Hide resolved
{
"package_name": "scipy",
"version": "1.9.3",
},
{"package_name": "pynvml", "version": "11.5.0"},
{"package_name": "typing_extensions", "version": "4.6.2"},
]

if is_arm_mac:
deps.append({"package_name": "pasteboard", "version": "0.3.3"})
elif is_windows:
deps.append({"package_name": "pywin32", "version": None})

install_version_checked_dependencies(deps)
43 changes: 43 additions & 0 deletions backend/src/dependencies/install_required_deps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import subprocess
from json import loads as json_parse
from typing import List

from .store import DependencyInfo, installed_packages, python_path
from .versioned_dependency_helpers import install_version_checked_dependencies

# Get the list of installed packages
# We can't rely on using the package's __version__ attribute because not all packages actually have it
try:
pip_list = subprocess.check_output(
[python_path, "-m", "pip", "list", "--format=json"]
)
for p in json_parse(pip_list):
installed_packages[p["name"]] = p["version"]
except Exception as e:
print(f"Failed to get installed packages: {e}")


deps: List[DependencyInfo] = [
{
"package_name": "sanic",
"version": "23.3.0",
},
{
"package_name": "Sanic-Cors",
"version": "2.2.0",
},
{
"package_name": "numpy",
"version": "1.23.2",
},
{
"package_name": "opencv-python",
"version": "4.7.0.68",
},
{
"package_name": "Pillow",
"version": "9.2.0",
},
joeyballentine marked this conversation as resolved.
Show resolved Hide resolved
]

install_version_checked_dependencies(deps)
49 changes: 49 additions & 0 deletions backend/src/dependencies/store.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import subprocess
import sys
from typing import List, TypedDict, Union

python_path = sys.executable

# pylint: disable=global-at-module-level
global installed_packages
installed_packages = {}
joeyballentine marked this conversation as resolved.
Show resolved Hide resolved


class DependencyInfo(TypedDict):
package_name: str
version: Union[str, None]


def pin(package_name: str, version: Union[str, None]) -> str:
if version is None:
return package_name
return f"{package_name}=={version}"


def install_dependencies(dependency_info_array: List[DependencyInfo]):
subprocess.check_call(
[
python_path,
"-m",
"pip",
"install",
*[
pin(dep_info["package_name"], dep_info["version"])
for dep_info in dependency_info_array
],
"--disable-pip-version-check",
"--no-warn-script-location",
]
)
for dep_info in dependency_info_array:
package_name = dep_info["package_name"]
version = dep_info["version"]
installed_packages[package_name] = version


__all__ = [
"DependencyInfo",
"python_path",
"install_dependencies",
"installed_packages",
]
36 changes: 36 additions & 0 deletions backend/src/dependencies/versioned_dependency_helpers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import re
from typing import List

from semver.version import Version

from .store import DependencyInfo, install_dependencies, installed_packages


def coerce_semver(version: str) -> Version:
try:
return Version.parse(version, True)
except Exception:
regex = r"(\d+)\.(\d+)\.(\d+)"
match = re.search(regex, version)
if match:
return Version(
int(match.group(1)),
int(match.group(2)),
int(match.group(3)),
)
return Version(0, 0, 0)


def install_version_checked_dependencies(dependencies: List[DependencyInfo]):
dependencies_to_install = []
for dependency in dependencies:
version = installed_packages.get(dependency["package_name"], None)
if dependency["version"] and version:
installed_version = coerce_semver(version)
dep_version = coerce_semver(dependency["version"])
if installed_version < dep_version:
dependencies_to_install.append(dependency)
elif not version:
dependencies_to_install.append(dependency)
if len(dependencies_to_install) > 0:
install_dependencies(dependencies_to_install)
28 changes: 28 additions & 0 deletions backend/src/events.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@ class IteratorProgressUpdateData(TypedDict):
running: Optional[List[NodeId]]


class BackendStatusData(TypedDict):
message: str
percent: float


class FinishEvent(TypedDict):
event: Literal["finish"]
data: FinishData
Expand All @@ -67,11 +72,23 @@ class IteratorProgressUpdateEvent(TypedDict):
data: IteratorProgressUpdateData


class BackendStatusEvent(TypedDict):
event: Literal["backend-status"]
data: BackendStatusData


class BackendStateEvent(TypedDict):
event: Union[Literal["backend-ready"], Literal["backend-started"]]
data: None


Event = Union[
FinishEvent,
ExecutionErrorEvent,
NodeFinishEvent,
IteratorProgressUpdateEvent,
BackendStatusEvent,
BackendStateEvent,
]


Expand All @@ -84,3 +101,14 @@ async def get(self) -> Event:

async def put(self, event: Event) -> None:
await self.queue.put(event)

async def wait_until_empty(self, timeout: float) -> None:
while timeout > 0:
if self.queue.empty():
return
await asyncio.sleep(0.01)
timeout -= 0.01

async def put_and_wait(self, event: Event, timeout: float = float("inf")) -> None:
await self.queue.put(event)
await self.wait_until_empty(timeout)
51 changes: 51 additions & 0 deletions backend/src/gpu.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import pynvml as nv
from sanic.log import logger

# pylint: disable=global-at-module-level
global nvidia_is_available
joeyballentine marked this conversation as resolved.
Show resolved Hide resolved
nvidia_is_available = False

try:
nv.nvmlInit()
nvidia_is_available = True
nv.nvmlShutdown()
except nv.NVMLError as e:
logger.info("No Nvidia GPU found, or invalid driver installed.")
except Exception as e:
logger.info(f"Unknown error occurred when trying to initialize Nvidia GPU: {e}")


class NvidiaHelper:
def __init__(self):
nv.nvmlInit()

self.__num_gpus = nv.nvmlDeviceGetCount()

self.__gpus = []
for i in range(self.__num_gpus):
handle = nv.nvmlDeviceGetHandleByIndex(i)
self.__gpus.append(
{
"name": nv.nvmlDeviceGetName(handle),
"uuid": nv.nvmlDeviceGetUUID(handle),
"index": i,
"handle": handle,
}
)

def __del__(self):
nv.nvmlShutdown()

def list_gpus(self):
return self.__gpus

def get_current_vram_usage(self, gpu_index=0):
info = nv.nvmlDeviceGetMemoryInfo(self.__gpus[gpu_index]["handle"])

return info.total, info.used, info.free


__all__ = [
"nvidia_is_available",
"NvidiaHelper",
]
RunDevelopment marked this conversation as resolved.
Show resolved Hide resolved
Loading