diff --git a/.github/workflows/package_mage.yaml b/.github/workflows/package_mage.yaml index a60f55135..3a754a35a 100644 --- a/.github/workflows/package_mage.yaml +++ b/.github/workflows/package_mage.yaml @@ -27,9 +27,9 @@ on: description: "Memgraph package download link. Leave empty to use the official download link." default: "" type: string - push_to_dockerhub: + cuda: type: boolean - description: "Push the image to DockerHub?" + description: "Build the image for CUDA" default: false push_to_s3: type: boolean @@ -60,7 +60,7 @@ jobs: memgraph_download_link: ${{ github.event.inputs.memgraph_download_link || '' }} shorten_tag: 'true' force_release: 'true' - push_to_dockerhub: ${{ github.event.inputs.push_to_dockerhub || 'false' }} + cuda: ${{ inputs.cuda }} push_to_s3: ${{ github.event.inputs.push_to_s3 || 'false' }} malloc: ${{ github.event.inputs.malloc == 'true' }} run_smoke_tests: ${{ inputs.run_smoke_tests }} @@ -89,7 +89,7 @@ jobs: memgraph_download_link: ${{ github.event.inputs.memgraph_download_link || '' }} shorten_tag: 'true' force_release: 'true' - push_to_dockerhub: ${{ github.event.inputs.push_to_dockerhub || 'false' }} + cuda: ${{ inputs.cuda }} push_to_s3: ${{ github.event.inputs.push_to_s3 || 'false' }} malloc: ${{ matrix.malloc }} run_smoke_tests: ${{ inputs.run_smoke_tests }} diff --git a/.github/workflows/reusable_package_mage.yaml b/.github/workflows/reusable_package_mage.yaml index 5a1140292..033e65ecf 100644 --- a/.github/workflows/reusable_package_mage.yaml +++ b/.github/workflows/reusable_package_mage.yaml @@ -66,6 +66,10 @@ on: type: boolean description: "Print the output URL at the end of the workflow" default: false + cuda: + type: boolean + description: "Build the image for CUDA" + default: false env: OS: "${{ inputs.arch == 'arm64' && 'ubuntu-24.04-aarch64' || 'ubuntu-24.04' }}${{ inputs.malloc && '-malloc' || '' }}" @@ -200,7 +204,11 @@ jobs: - name: Set target dockerfile run: | - DOCKERFILE="Dockerfile.release" + if [[ "${{ inputs.cuda }}" == true ]]; then + DOCKERFILE="Dockerfile.cuda" + else + DOCKERFILE="Dockerfile.release" + fi echo "DOCKERFILE=${DOCKERFILE}" >> $GITHUB_ENV - name: Check if specified version tag is already pushed diff --git a/Dockerfile.cuda b/Dockerfile.cuda new file mode 100644 index 000000000..399a08745 --- /dev/null +++ b/Dockerfile.cuda @@ -0,0 +1,249 @@ +ARG PY_VERSION_DEFAULT=3.12 +ARG MGBUILD_IMAGE + +FROM $MGBUILD_IMAGE as builder + +USER root + +ARG TARGETARCH +ARG PY_VERSION_DEFAULT +ARG BUILD_TYPE +ARG CACHE_PRESENT +ENV BUILD_TYPE=${BUILD_TYPE} +ENV PY_VERSION ${PY_VERSION_DEFAULT} +ARG CUSTOM_MIRROR + + +# This modifies the apt configuration to rety 3 times +RUN echo 'Acquire::Retries "3";' > /etc/apt/apt.conf.d/80-retries + +# If CUSTOM_MIRROR is set, replace the default archive.ubuntu.com +# and security.ubuntu.com URIs in your .sources file +RUN if [ -n "$CUSTOM_MIRROR" ]; then \ + sed -E -i \ + -e '/^URIs:/ s#https?://[^ ]*archive\.ubuntu\.com#'"$CUSTOM_MIRROR"'#g' \ + -e '/^URIs:/ s#https?://security\.ubuntu\.com#'"$CUSTOM_MIRROR"'#g' \ + /etc/apt/sources.list.d/ubuntu.sources; \ + fi + +# Essentials for production/dev +RUN apt-get update && apt-get install -y \ + libcurl4 `memgraph` \ + libpython${PY_VERSION} `memgraph` \ + libssl-dev `memgraph` \ + openssl `memgraph` \ + build-essential `mage-memgraph` \ + cmake `mage-memgraph` \ + curl `mage-memgraph` \ + g++ `mage-memgraph` \ + python3 `mage-memgraph` \ + python3-pip `mage-memgraph` \ + python3-setuptools `mage-memgraph` \ + python3-dev `mage-memgraph` \ + clang `mage-memgraph` \ + git `mage-memgraph` \ + unixodbc-dev `mage-memgraph` \ + libboost-all-dev `mage-memgraph` \ + uuid-dev \ + gdb \ + procps \ + libc6-dbg \ + libxmlsec1-dev xmlsec1 \ + libatomic1 \ + --no-install-recommends \ + && ln -s /usr/bin/$(ls /usr/bin | grep perf) /usr/bin/perf \ + && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* + +COPY memgraph-${TARGETARCH}.deb . + +RUN dpkg -i memgraph-${TARGETARCH}.deb && rm memgraph-${TARGETARCH}.deb + +# move memgraph HOME so that mounting /var/lib/memgraph as a volume doesn't break Python +RUN mkdir -pv /home/memgraph && \ + usermod -d /home/memgraph memgraph && \ + chown -R memgraph:memgraph /home/memgraph + +ENV LD_LIBRARY_PATH /usr/lib/memgraph/query_modules + + +WORKDIR /mage +COPY . /mage + +#hacks so we can do everything under the `memgraph` user +RUN chown -R memgraph: /mage && \ + mkdir -pv /usr/lib/memgraph/query_modules && \ + chown -R memgraph: /usr/lib/memgraph/query_modules +USER memgraph + +# First install requirements +RUN python3 -m pip install --no-cache-dir -r /mage/python/requirements-gpu.txt --break-system-packages && \ + python3 -m pip install --no-cache-dir -r /mage/python/tests/requirements.txt --break-system-packages && \ + python3 -m pip install --no-cache-dir -r /usr/lib/memgraph/auth_module/requirements.txt --break-system-packages && \ + python3 -m pip install --no-cache-dir torch-sparse torch-cluster torch-spline-conv torch-geometric torch-scatter --break-system-packages -f https://data.pyg.org/whl/torch-2.6.0+cu126.html; \ + python3 -m pip install --no-cache-dir dgl==2.5.0 -f https://data.dgl.ai/wheels/torch-2.6/repo.html --break-system-packages; \ + rm -fr /home/memgraph/.cache/pip + +# Build query modules +SHELL ["/bin/bash", "-c"] +RUN source /opt/toolchain-v6/activate && curl https://sh.rustup.rs -sSf | sh -s -- -y \ + && export PATH="$HOME/.cargo/bin:${PATH}" \ + && rustup toolchain install 1.85 \ + && rustup default 1.85 \ + && python3 /mage/setup build -p /usr/lib/memgraph/query_modules/ --cpp-build-flags CMAKE_BUILD_TYPE=${BUILD_TYPE} \ + && chown -R memgraph: /mage + + +# Memgraph listens for Bolt Protocol on this port by default. +EXPOSE 7687 + + + +FROM ubuntu:24.04 as base + +USER root + +ARG TARGETARCH +ARG PY_VERSION_DEFAULT=3.12 +ARG MAGE_COMMIT +ARG BUILD_TYPE +ENV BUILD_TYPE=${BUILD_TYPE} +ENV PY_VERSION ${PY_VERSION_DEFAULT} +ENV MAGE_COMMIT=${MAGE_COMMIT} +ARG CUSTOM_MIRROR + +# This modifies the apt configuration to rety 3 times +RUN echo 'Acquire::Retries "3";' > /etc/apt/apt.conf.d/80-retries + +# If CUSTOM_MIRROR is set, replace the default archive.ubuntu.com +# and security.ubuntu.com URIs in your .sources file +RUN if [ -n "$CUSTOM_MIRROR" ]; then \ + sed -E -i \ + -e '/^URIs:/ s#https?://[^ ]*archive\.ubuntu\.com#'"$CUSTOM_MIRROR"'#g' \ + -e '/^URIs:/ s#https?://security\.ubuntu\.com#'"$CUSTOM_MIRROR"'#g' \ + /etc/apt/sources.list.d/ubuntu.sources; \ + fi + +# Essentials for production/dev +RUN apt-get update && \ + apt-get install -y curl && \ + curl -sSL -O https://packages.microsoft.com/config/ubuntu/24.04/packages-microsoft-prod.deb && \ + dpkg -i packages-microsoft-prod.deb && \ + rm packages-microsoft-prod.deb && \ + apt-get update && \ + ACCEPT_EULA=Y apt-get install -y msodbcsql18 unixodbc-dev && \ + apt-get install -y \ + libcurl4 `memgraph` \ + libpython${PY_VERSION} `memgraph` \ + libssl-dev `memgraph` \ + openssl `memgraph` \ + python3 `mage-memgraph` \ + python3-pip `mage-memgraph` \ + python3-setuptools `mage-memgraph` \ + python3-dev `mage-memgraph` \ + libc6-dbg \ + adduser \ + libgomp1 \ + libaio1t64 \ + libatomic1 \ + --no-install-recommends \ + && ln -s /usr/bin/$(ls /usr/bin | grep perf) /usr/bin/perf \ + && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* && \ + if [ "${TARGETARCH}" = "arm64" ]; then \ + ln -s /usr/lib/aarch64-linux-gnu/libaio.so.1t64 /usr/lib/aarch64-linux-gnu/libaio.so.1; \ + else \ + ln -s /usr/lib/x86_64-linux-gnu/libaio.so.1t64 /usr/lib/x86_64-linux-gnu/libaio.so.1; \ + fi + +# install memgraph +COPY memgraph-${TARGETARCH}.deb . + +# fix `memgraph` UID and GID for compatibility with previous Debian releases +RUN groupadd -g 103 memgraph && \ + useradd -u 101 -g memgraph -m -d /home/memgraph -s /bin/bash memgraph && \ + dpkg -i memgraph-${TARGETARCH}.deb && \ + rm memgraph-${TARGETARCH}.deb + +ENV LD_LIBRARY_PATH /usr/lib/memgraph/query_modules + +# Copy modules +COPY --from=builder /usr/lib/memgraph/query_modules/ /usr/lib/memgraph/query_modules/ +COPY --from=builder /usr/lib/memgraph/auth_module/ /usr/lib/memgraph/auth_module/ + +# Copy Python build +COPY --from=builder /usr/local/lib/python${PY_VERSION}/ /usr/local/lib/python${PY_VERSION}/ +COPY --from=builder --chown=memgraph:memgraph /home/memgraph/.local/ /home/memgraph/.local/ + +# copy script to convert to dev container +COPY --from=builder /mage/make-dev-container.sh /make-dev-container.sh + +FROM base as prod + +USER root + +ARG TARGETARCH +ARG PY_VERSION_DEFAULT=3.12 +ARG MAGE_COMMIT +ARG BUILD_TYPE +ENV BUILD_TYPE=${BUILD_TYPE} +ENV PY_VERSION ${PY_VERSION_DEFAULT} +ENV MAGE_COMMIT=${MAGE_COMMIT} +ARG CUSTOM_MIRROR + +# Copy e2e tests +COPY --from=builder --chown=memgraph:memgraph /mage/e2e/ /mage/e2e/ +COPY --from=builder --chown=memgraph:memgraph /mage/e2e_correctness/ /mage/e2e_correctness/ +COPY --from=builder --chown=memgraph:memgraph /mage/test_e2e_correctness.py /mage/test_e2e_correctness.py +COPY --from=builder --chown=memgraph:memgraph /mage/run_e2e_correctness_tests.sh /mage/run_e2e_correctness_tests.sh + +# Copy requirements +COPY --from=builder --chown=memgraph:memgraph /mage/python/requirements.txt /mage/python/requirements.txt +COPY --from=builder --chown=memgraph:memgraph /mage/python/tests/requirements.txt /mage/python/tests/requirements.txt + +RUN if [ -n "$CUSTOM_MIRROR" ]; then \ + sed -E -i \ + -e "/^URIs:/ s#${CUSTOM_MIRROR}/ubuntu/#https://archive.ubuntu.com/ubuntu/#g" \ + -e "/^URIs:/ s#${CUSTOM_MIRROR}/ubuntu/#https://security.ubuntu.com/ubuntu/#g" \ + /etc/apt/sources.list.d/ubuntu.sources; \ + fi + +USER memgraph +EXPOSE 7687 + +ENTRYPOINT ["/usr/lib/memgraph/memgraph"] +CMD [""] + + +FROM base as debug + +USER root + +ARG TARGETARCH +ARG PY_VERSION_DEFAULT=3.12 +ARG MAGE_COMMIT +ARG BUILD_TYPE +ENV BUILD_TYPE=${BUILD_TYPE} +ENV PY_VERSION ${PY_VERSION_DEFAULT} +ENV MAGE_COMMIT=${MAGE_COMMIT} +ARG CUSTOM_MIRROR + +# Add gdb +RUN apt-get update && apt-get install -y \ + gdb \ + --no-install-recommends \ + && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* + +# Copy entire mage repo +COPY --from=builder --chown=memgraph:memgraph /mage/ /mage/ + +RUN if [ -n "$CUSTOM_MIRROR" ]; then \ + sed -E -i \ + -e "/^URIs:/ s#${CUSTOM_MIRROR}/ubuntu/#https://archive.ubuntu.com/ubuntu/#g" \ + -e "/^URIs:/ s#${CUSTOM_MIRROR}/ubuntu/#https://security.ubuntu.com/ubuntu/#g" \ + /etc/apt/sources.list.d/ubuntu.sources; \ + fi + +USER memgraph +EXPOSE 7687 + +ENTRYPOINT ["/usr/lib/memgraph/memgraph"] +CMD [""] \ No newline at end of file diff --git a/Dockerfile.release b/Dockerfile.release index bf3496cd6..cb42d4423 100644 --- a/Dockerfile.release +++ b/Dockerfile.release @@ -79,22 +79,27 @@ USER memgraph # First install requirements RUN python3 -m pip install --no-cache-dir -r /mage/python/requirements.txt --break-system-packages && \ python3 -m pip install --no-cache-dir -r /mage/python/tests/requirements.txt --break-system-packages && \ - python3 -m pip install --no-cache-dir -r /usr/lib/memgraph/auth_module/requirements.txt --break-system-packages - -# RUN python3 -m pip install --no-cache-dir torch torchvision --index-url https://download.pytorch.org/whl/cu124 --break-system-packages && \ -# python3 -m pip install --no-cache-dir "transformers>=4.43.0" "sentence-transformers>=3,<5" "filelock" "huggingface_hub>=0.23.0" --break-system-packages && \ -# rm -fr /home/memgraph/.cache/pip - -# RUN python3 -m pip install --no-cache-dir torch torchvision --index-url https://download.pytorch.org/whl/cu129 --break-system-packages && \ -# python3 -m pip install --no-cache-dir "transformers" "sentence-transformers" "filelock" "huggingface_hub" --break-system-packages && \ -# rm -fr /home/memgraph/.cache/pip - -# TODO: embedding works witho those versions, but pytorch 2.4 has vulnerabilities... -RUN python3 -m pip install --no-cache-dir torch==2.4.1 torchvision==0.19.1 --index-url https://download.pytorch.org/whl/cu124 --break-system-packages && \ - python3 -m pip install --no-cache-dir "transformers" "sentence-transformers" "filelock" "huggingface_hub" --break-system-packages && \ + python3 -m pip install --no-cache-dir -r /usr/lib/memgraph/auth_module/requirements.txt --break-system-packages && \ + if [ "$TARGETARCH" = "arm64" ]; then \ + if [ "$CACHE_PRESENT" = "true" ]; then \ + echo "Using cached torch packages"; \ + python3 -m pip install --no-index --find-links=/mage/wheels/ torch-sparse torch-cluster torch-spline-conv torch-geometric torch-scatter --break-system-packages; \ + else \ + python3 -m pip install --no-cache-dir torch-sparse torch-cluster torch-spline-conv torch-geometric torch-scatter -f https://data.pyg.org/whl/torch-2.6.0+cpu.html --break-system-packages; \ + fi && \ + curl -o dgl-2.5.0-cp312-cp312-linux_aarch64.whl https://s3.eu-west-1.amazonaws.com/deps.memgraph.io/wheels/arm64/dgl-2.5.0-cp312-cp312-linux_aarch64.whl && \ + python3 -m pip install --no-cache-dir dgl-2.5.0-cp312-cp312-linux_aarch64.whl --break-system-packages; \ + else \ + if [ "$CACHE_PRESENT" = "true" ]; then \ + echo "Using cached torch packages"; \ + python3 -m pip install --no-index --find-links=/mage/wheels/ torch-sparse torch-cluster torch-spline-conv torch-geometric torch-scatter --break-system-packages; \ + else \ + python3 -m pip install --no-cache-dir torch-sparse torch-cluster torch-spline-conv torch-geometric torch-scatter -f https://data.pyg.org/whl/torch-2.6.0+cpu.html --break-system-packages; \ + fi && \ + python3 -m pip install --no-cache-dir dgl==2.5.0 -f https://data.dgl.ai/wheels/torch-2.6/repo.html --break-system-packages; \ + fi && \ rm -fr /home/memgraph/.cache/pip - # Build query modules SHELL ["/bin/bash", "-c"] RUN source /opt/toolchain-v6/activate && curl https://sh.rustup.rs -sSf | sh -s -- -y \ @@ -146,13 +151,10 @@ RUN apt-get update && \ apt-get install -y \ libcurl4 `memgraph` \ libpython${PY_VERSION} `memgraph` \ - libssl-dev `memgraph` \ openssl `memgraph` \ python3 `mage-memgraph` \ python3-pip `mage-memgraph` \ python3-setuptools `mage-memgraph` \ - python3-dev `mage-memgraph` \ - libc6-dbg \ adduser \ libgomp1 \ libaio1t64 \ @@ -240,7 +242,7 @@ ARG CUSTOM_MIRROR # Add gdb RUN apt-get update && apt-get install -y \ - gdb \ + gdb libc6-dbg \ --no-install-recommends \ && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* diff --git a/python/embed_worker/embed_worker.py b/python/embed_worker/embed_worker.py new file mode 100644 index 000000000..61cbcd1ae --- /dev/null +++ b/python/embed_worker/embed_worker.py @@ -0,0 +1,34 @@ +import os + + +def encode_chunk(visible_gpu: int | None, model_id: str, texts: list[str], batch_size: int): + """ + Runs in a spawned process. Returns (count, embeddings_as_list) + """ + # Set device visibility BEFORE importing torch/transformers + if visible_gpu is None: + os.environ["CUDA_VISIBLE_DEVICES"] = "" + else: + os.environ["CUDA_VISIBLE_DEVICES"] = str(visible_gpu) + + os.environ.setdefault("HF_HUB_DISABLE_TELEMETRY", "1") + os.environ.setdefault("TRANSFORMERS_NO_TORCHVISION", "1") + os.environ.setdefault("TOKENIZERS_PARALLELISM", "false") + + import torch + from sentence_transformers import SentenceTransformer + + device = "cuda" if torch.cuda.is_available() else "cpu" + model = SentenceTransformer(model_id, device=device) + + if not texts: + return 0, [] + + embs = model.encode( + texts, + batch_size=min(batch_size, len(texts)), + convert_to_numpy=True, + normalize_embeddings=True, + show_progress_bar=False, + ) + return len(texts), embs.tolist() diff --git a/python/embeddings.py b/python/embeddings.py new file mode 100644 index 000000000..2de8eeac4 --- /dev/null +++ b/python/embeddings.py @@ -0,0 +1,136 @@ +import os +import sys +import multiprocessing as mp +from concurrent.futures import ProcessPoolExecutor, as_completed +import mgp + +sys.path.append(os.path.join(os.path.dirname(__file__), "embed_worker")) + +logger: mgp.Logger = mgp.Logger() + +EXCLUDE_PROPERTIES = {"embedding"} +BATCH_SIZE = 2000 +MODEL_ID = "all-MiniLM-L6-v2" + + +def build_texts(vertices): + out = [] + for v in vertices: + txt = " ".join(lbl.name for lbl in v.labels) + " " + " ".join( + f"{k}: {val}" for k, val in v.properties.items() if k not in EXCLUDE_PROPERTIES + ) + out.append(txt) + return out + + +def split_slices(n_items: int, n_parts: int): + base, rem = divmod(n_items, n_parts) + start = 0 + slices = [] + for i in range(n_parts): + end = start + base + (1 if i < rem else 0) + slices.append((start, end)) + start = end + return slices + + +def get_visible_gpus(): + # Avoid creating a CUDA context in the parent if possible + try: + import subprocess + out = subprocess.check_output( + ["nvidia-smi", "--query-gpu=index", "--format=csv,noheader"], text=True + ) + return [int(x) for x in out.strip().splitlines() if x.strip()] + except Exception: + try: + import torch + return list(range(torch.cuda.device_count())) if torch.cuda.is_available() else [] + except Exception: + return [] + + +@mgp.write_proc +def compute_embeddings(ctx: mgp.ProcCtx) -> mgp.Record(success=bool): + logger.info( + f"compute_embeddings: starting (py_exec={sys.executable}, py_ver={sys.version.split()[0]})" + ) + try: + # Parent imports are okay; workers import only embed_worker + import embed_worker # <-- our pure worker module + except Exception as e: + logger.error(f"Failed to import worker module: {e}") + return mgp.Record(success=False) + + try: + vertices = list(ctx.graph.vertices) + texts = build_texts(vertices) + n = len(texts) + if n == 0: + logger.info("No vertices to process.") + return mgp.Record(success=True) + + gpus = get_visible_gpus() + logger.info(f"Found {len(gpus)} GPU(s): {gpus}") + + # CPU fallback (single process in parent) + if not gpus: + try: + from sentence_transformers import SentenceTransformer + model = SentenceTransformer(MODEL_ID, device="cpu") + embs = model.encode( + texts, + batch_size=min(BATCH_SIZE, n), + convert_to_numpy=True, + normalize_embeddings=True, + show_progress_bar=False, + ) + for v, e in zip(vertices, embs.tolist()): + v.properties["embedding"] = e + logger.info(f"Processed {n} vertices on CPU.") + return mgp.Record(success=True) + except Exception as e: + logger.error(f"CPU path failed: {e}") + return mgp.Record(success=False) + + # Multi-GPU via spawn + slices = split_slices(n, len(gpus)) + tasks = [] + for gpu, (a, b) in zip(gpus, slices): + if a < b: + tasks.append((gpu, MODEL_ID, texts[a:b], BATCH_SIZE, a, b)) + + results = [] + total = 0 + + mp.set_executable("/usr/bin/python3") + ctx_spawn = mp.get_context("spawn") + with ProcessPoolExecutor(max_workers=len(tasks), mp_context=ctx_spawn) as ex: + fut2info = { + ex.submit(embed_worker.encode_chunk, t[0], t[1], t[2], t[3]): (t[0], t[4], t[5]) + for t in tasks + } + for fut in as_completed(fut2info): + gpu, a, b = fut2info[fut] + try: + count, embs = fut.result() + if count != (b - a) or len(embs) != (b - a): + logger.error(f"GPU {gpu} returned mismatched count {count} for slice [{a}:{b})") + continue + results.append((a, b, embs)) + total += count + logger.info(f"GPU {gpu} returned {count} embeddings for slice [{a}:{b}).") + except Exception as e: + logger.error(f"Worker on GPU {gpu} failed: {e}") + + # Write back + for a, b, embs in results: + for i, e in enumerate(embs, start=a): + vertices[i].properties["embedding"] = e + + logger.info(f"Successfully processed {total}/{n} vertices across {len(gpus)} GPU(s).") + return mgp.Record(success=(total == n)) + + except Exception as e: + logger.error(f"Failed to compute embeddings: {e}") + return mgp.Record(success=False) diff --git a/python/requirements-gpu.txt b/python/requirements-gpu.txt new file mode 100644 index 000000000..3ab4178ac --- /dev/null +++ b/python/requirements-gpu.txt @@ -0,0 +1,25 @@ +--extra-index-url https://download.pytorch.org/whl/cu126 +boto3==1.36.23 +defusedxml==0.7.1 +duckdb==1.2.1 +elasticsearch==8.17.0 +filelock==3.19.1 +gekko==1.2.1 +gensim==4.3.3 +gqlalchemy==1.6.0 +huggingface-hub==0.35.1 +igraph==0.11.8 +mysql-connector-python==9.1.0 +networkx==2.8.8 +oracledb==2.5.1 +pandas==2.2.3 +platformdirs==4.3.6 +pyarrow==19.0.1 +psycopg2-binary==2.9.10 +pyodbc==5.2.0 +python-Levenshtein==0.26.1 +sentence-transformers==5.1.1 +torch==2.6.0 +scikit-learn==1.5.2 +scipy==1.13.1 +six==1.17.0 diff --git a/python/requirements.txt b/python/requirements.txt index 273604cd5..38441b490 100644 --- a/python/requirements.txt +++ b/python/requirements.txt @@ -5,9 +5,10 @@ duckdb==1.2.1 elasticsearch==8.17.0 gekko==1.2.1 gensim==4.3.3 -gqlalchemy==1.6.0 +gqlalchemy==1.8.0 igraph==0.11.8 mysql-connector-python==9.1.0 +neo4j==5.28.2 networkx==2.8.8 oracledb==2.5.1 pandas==2.2.3 @@ -19,3 +20,6 @@ python-Levenshtein==0.26.1 scikit-learn==1.5.2 scipy==1.13.1 six==1.17.0 +torch==2.6.0+cpu; platform_machine != "aarch64" +torch==2.6.0; platform_machine == "aarch64" +torchmetrics==0.10.0 # current implementation of node_classification module depends on version <= 0.10.0