IMPORTANT
Every PR MUST have exactly one of these labels, the merge is blocked otherwise:
major/minor/patchpublishes a new release on merge with the corresponding semver bump.skip-releasemerges without publishing a release (use for docs, CI changes, refactors, etc.).
📝 Description
Add a GPU profile to the ICA sidecar: TurboOCR device framebuffer OCR and CUDA ONNX Runtime NER through libhoop-ica v0.7.0. The CPU ica targets do not change.
New config keys under ica and ica.options select the backend. All carry cap:"ica_gpu", so the control plane does not serve them to an older sidecar.
A versioned GPU runtime asset (TurboOCR device library, libhoop-gpuframe.so, CUDA ORT, TensorRT engines) is built on a GPU runner and consumed by two new Docker targets, ica-gpu and ica-gpu-assets. The release publishes them only when ICA_GPU_ASSETS_URL and ICA_GPU_ASSETS_SHA256 are set.
📣 User-facing impact
None. Opt-in. No GPU artifact is published by this PR.
🔗 Related Issue
Fixes #
🚀 Type of Change
- 🐛 Bug fix (non-breaking change which fixes an issue)
- ✨ New feature (non-breaking change which adds functionality)
- 💥 Breaking change (fix or feature that would cause existing functionality to not work as expected)
- 📚 Documentation update
- 🎨 Style/UI update
- ♻️ Code refactor
- ⚡ Performance improvement
- ✅ Test update
- 🔧 Build configuration change
- 🧹 Chore
📋 Changes Made
sidecar/daemon/ica.go:ocr_turbo_*paths andocr_backend,ocr_turbo_device,ocr_turbo_framebuffer_bytes,ocr_turbo_full_screen,ocr_turbo_regional_cache,pii_matches_only,ner_accelerator,ner_gpu_device,ner_entitiesoptions; range and enum validation;cap:"ica_gpu"on every new key.sidecar/ica/ica.go: turboocr defaults inresolveRuntimeConfig;HostCheckdiscovers a GPU bundle by its manifest (HOOP_ICA_GPU_ASSETSoverrides the root); notes report the resolved GPU settings.Dockerfile.sidecar:ica-gpu-native,ica-gpu,ica-gpu-assets-package,ica-gpu-assetsstages. The native input is a checksum-verified URL; no default..github/workflows/ica-gpu-runtime.yml: manual workflow on a self-hostedica-gpurunner; builds, tests and optionally publishes the runtime asset..github/workflows/release.yml: GPU image and bundle, gated on both repository variables..github/workflows/pullrequest.yml: run the Python tool tests; validate the GPU example configs in the ORT job.deploy/ica-sidecar/:gpu-assets.py(pack/verify/unpack),build-gpu-runtime.py(GPU builder),install-gpu.sh(native installer;install.shhands off onflavour=ica-gpu), two example configs,GPU.md,GPU-RUNTIME.md, tests.- Tests:
sidecar/ica/gpu_test.go,sidecar/daemon/ica_gpu_test.go,sidecar/cmd/ica_gpu_test.go.
🧪 Testing
Test Configuration:
- Browser(s): n/a
- OS: macOS arm64 (no GPU)
Tests performed:
-
Unit tests pass
-
Integration tests pass
-
Manual testing completed
-
go build,go vet,go testonsidecar/ica,sidecar/daemon,sidecar/cmd: pass. -
make test-sidecar-compatagainst 1.279.0: pass. Failed before thecap:tags withunknown field "ocr_turbo_library". -
python3 -m unittest discover -s deploy/ica-sidecar: 12/12 pass. -
hoop-inspect -validateonexamples/config.gpu.single.yamlwith staged dummy assets:config OK, notes showocr turboocr, 1 workers, 1 tiles. Withocr_workers: 4added the engine refuses the config. -
Read-checked the CMake contract between
build-gpu-runtime.py, TurboOCRa43c6764and libhoop-icanative/gpuframe: options, targets, test tag, output paths and smokePASS:match.
Not run: a real CUDA build or inference. The ica-gpu runner does not exist yet. The ORT-tagged binary does not link on this machine (no libtokenizers), so the ner_accelerator: cuda branch is covered only by the engine's own validation.
📸 Screenshots (if applicable)
n/a
✅ Checklist
- My code follows the style guidelines of this project
- I have performed a self-review of my own code
- I have commented my code, particularly in hard-to-understand areas
- My changes generate no new warnings
- New and existing unit tests pass locally with my changes
- I have checked my code and corrected any misspellings
📄 Additional Notes
resolveRuntimeConfigdefaults the four TurboOCR paths forocr_backend: turboocrbut notner_ort_library; the examples set it. A config that omits it fails at Serve.gpu-assets.pyandcheckGPUAssetsinsidecar/ica/ica.goboth hold the required-file list. A later change can move verify/unpack intohoop-inspect.- The runtime workflow needs
GH_TOKENwith read access tomatheusfrancisco/TurboOCR. - Review focus: the
cap:tags,Dockerfile.sidecarGPU stages, andinstall-gpu.sh.
Activity
9Code Review by Qodo
🐞 Bugs (7) 📘 Rule violations (1) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)
1. GPU installs expose the wrong binary name 📘 Rule violation § Compliance ⭐ New
Description
install-gpu.sh installs the staged executable as /usr/local/bin/hoop-ica and configures the service to run that name instead of preserving hoop-inspect. Every native GPU installation and subsequent service restart therefore uses the alternate executable name and violates the component naming contract.
Code
deploy/ica-sidecar/install-gpu.sh[R44-45]+install -m 0755 "$here/hoop-ica" "$rel/bin/hoop-ica" +install -m 0644 "$here/release.txt" "$rel/release.txt"
Relevance
●●● StrongThe installer directly violates the explicit hoop-inspect naming rule; installer safety findings are consistently accepted.
ⓘ Recommendations generated based on similar findings in past PRs
Evidence
The changed installer copies the staged executable into the bundle as hoop-ica and installs it under that name, while the checklist requires the produced executable to retain the hoop-inspect name.
Rule 3062565: Preservehoop-inspectnaming for binary, config directory, and environment variabledeploy/ica-sidecar/install-gpu.sh[44-45]
Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution Issue description The GPU installer renames the sidecar executable to `hoop-ica`, but the component contract requires the executable to remain named `hoop-inspect`. Fix Focus Areas - deploy/ica-sidecar/install-gpu.sh[44-45] - deploy/ica-sidecar/install-gpu.sh[118-133] Recommended Fix Install the staged binary as `/usr/local/bin/hoop-inspect`, update the systemd `ExecStart` and related installer messages to use that name, and keep `/etc/hoop-inspect` as the configuration directory.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
2. GPU config edits skip preview builds 🐞 Bug ☼ Reliability ⭐ New
Description
The new GPU preview steps inherit the ica-assets job's steps.changed.outputs.build gate, but that gate's file list omits sidecar/daemon, where the new GPU configuration fields are defined. A pull request that changes only those fields skips the GPU image and bundle builds, so those artifacts are not checked against the configuration change before release.
Code
.github/workflows/pullrequest-release.yml[R810-811]+ - name: Build and push (ica-gpu) + if: ${{ steps.changed.outputs.build == 'true' && vars.ICA_GPU_ASSETS_URL != '' && vars.ICA_GPU_ASSETS_SHA256 != '' }}
Evidence
The change detector checks Dockerfile.sidecar, deploy/ica-sidecar, sidecar/ica, sidecar/cmd and the workflow, but not sidecar/daemon. The new GPU build step requires that detector's build == 'true' output, while the new GPU option fields reside in sidecar/daemon/ica.go.
.github/workflows/pullrequest-release.yml[759-771].github/workflows/pullrequest-release.yml[810-811]sidecar/daemon/ica.go[128-137]
Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution ## Issue description GPU image and bundle preview steps are skipped for pull requests that change only the daemon's GPU configuration fields because their shared change gate excludes `sidecar/daemon`. ## Fix Focus Areas - .github/workflows/pullrequest-release.yml[759-771] - .github/workflows/pullrequest-release.yml[810-811] ## Recommended Fix Add `sidecar/daemon` to the file paths checked by the `Decide whether the bundle changed` step, so GPU configuration changes trigger the existing preview builds.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
3. A GPU build failure blocks the CPU ICA release bundle 🐞 Bug ☼ Reliability ⭐ New
Description
The new Build and push (ica-gpu), Export the ica GPU assets bundle and `Stage the ica GPU assets bundle steps run inside the same job as the CPU bundle, before its Stage the ica assets bundle` and upload-artifact steps. If anything in the GPU path fails while the two variables are set, the job stops before dist-artifacts-ica-assets is uploaded, so the unchanged CPU bundle never reaches publish-release. Causes include an unreachable or rotated runtime URL, a digest mismatch, or gpu-assets.py unpack --engine rejecting the runtime because the libhoop-ica pin in go.mod changed (even a patch bump). The CPU -ica image has already been pushed by then, and the PR preview job in pullrequest-release.yml is coupled the same way.
Code
.github/workflows/release.yml[R1051-1054]+ - name: Build and push (ica-gpu) + if: ${{ vars.ICA_GPU_ASSETS_URL != '' && vars.ICA_GPU_ASSETS_SHA256 != '' }} + uses: docker/build-push-action@v7 + with:
Evidence
The GPU steps come before the CPU staging and upload steps in the same job, and none of them sets continue-on-error. The Dockerfile's native stage fails the build whenever the runtime manifest's libhoop_ica differs from the engine version go list resolves from go.mod, so a routine engine bump fails the whole release job until someone builds a new GPU runtime on the self-hosted GPU host. In pullrequest-release.yml the GPU steps (lines 804-850) also sit before the job's upload-artifact step (line 851).
.github/workflows/release.yml[1051-1101]Dockerfile.sidecar[448-454]deploy/ica-sidecar/gpu-assets.py[90-91].github/workflows/pullrequest-release.yml[804-856]
Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution ## Issue description The ica-gpu image and bundle steps share a job with the CPU ica-assets bundle and run before its staging and upload. Any GPU failure stops the CPU bundle from being uploaded and blocks the release. Possible failures: the runtime URL is down, the digest does not match, or the engine pin moved past the runtime's `libhoop_ica`. ## Fix Focus Areas - .github/workflows/release.yml[1040-1101] - .github/workflows/pullrequest-release.yml[804-856] ## Recommended Fix Move the GPU image and bundle into a separate job that `needs` the same prerequisites. Have that job upload its own artifact, for example `dist-artifacts-ica-gpu-assets`, and make publish-release consume it only when the variables are set. Alternatively, upload the CPU `stage/` artifact before the GPU steps run. In either case, also check before the build that the runtime's engine pin matches `sidecar/ica/go.mod`, so a mismatch produces a clear message.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
View medium (5)
4. GPU runtime archive stores large libraries several times 🐞 Bug ➹ Performance
Description
collect() adds every *.so* file matching GPU_LIBRARY from the SDK directories to the queue under its own file name, and copy_file follows symlinks, so libcudnn.so, libcudnn.so.9 and libcudnn.so.9.x.y each become a separate full copy. The same happens to the cuBLAS, TensorRT and other CUDA libraries, many of which are hundreds of MB to over 1 GB, so the runtime tarball and every installed release grow by several GB.
Code
deploy/ica-sidecar/build-gpu-runtime.py[R135-138]+ for directory in search_dirs: + for path in sorted(directory.glob("*.so*")): + if GPU_LIBRARY.match(path.name) and not HOST_LIBRARY.match(path.name): + queue.append((path.name, path))
Relevance
●●● StrongThe sweep dereferences library aliases, causing avoidable multi-gigabyte archive duplication.
ⓘ Recommendations generated based on similar findings in past PRs
Evidence
The sweep queues every glob match by file name. copy_file resolves symlinks and writes a separate regular file for each destination name (lines 115-127). gpu-assets.py pack forbids symlinks, so the unversioned link, the SONAME link and the fully versioned file are all stored as full copies. install-gpu.sh then copies the whole gpu tree into every release directory.
deploy/ica-sidecar/build-gpu-runtime.py[130-154]deploy/ica-sidecar/build-gpu-runtime.py[114-127]deploy/ica-sidecar/gpu-assets.py[55-61]
Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution ## Issue description The GPU-library sweep in collect() copies every symlink alias of each CUDA/cuDNN/TensorRT library as a separate full file, which multiplies the archive size. ## Fix Focus Areas - deploy/ica-sidecar/build-gpu-runtime.py[130-154] ## Recommended Fix In the sweep, skip any path that is a symlink and is not the library's SONAME. Read the SONAME with `patchelf --print-soname` or `objdump -p`, and queue each real file once under its SONAME, which is the name the loader and dlopen use. Drop the unversioned development names (`libfoo.so`) and the fully versioned file names, and track resolved paths so each real file is copied once.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
5. GPU asset override stays out of sample ⊘ Outdated
Description
HostCheck reads HOOP_ICA_GPU_ASSETS directly, but the repository sample omits the key. Operators configuring the GPU asset root therefore have no sample entry to discover or set this override.
Code
+ gpuRoot := os.Getenv("HOOP_ICA_GPU_ASSETS") + if gpuRoot == "" { + gpuRoot = defaultGPUAssets + }
Relevance
●●● StrongThe repository recently accepted mirroring newly introduced environment variables in .env.sample.
ⓘ Recommendations generated based on similar findings in past PRs
Evidence
The changed ICA host-check code introduces a direct read of HOOP_ICA_GPU_ASSETS, while the root environment sample contains no matching key.
Rule 1800927: New env vars must be mirrored in .env.samplesidecar/ica/ica.go[47-54].env.sample[1-80]
Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution Issue description `HostCheck` introduces the `HOOP_ICA_GPU_ASSETS` environment variable, but the root `.env.sample` does not list it. Fix Focus Areas - sidecar/ica/ica.go[47-50] - .env.sample[1-179] Recommended Fix Add `HOOP_ICA_GPU_ASSETS=` to `.env.sample`, with a concise comment explaining that it overrides the default GPU asset root for ICA GPU deployments.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
6. A failed GPU install blocks every retry 🐞 Bug ☼ Reliability
Description
install-gpu.sh creates and fills $rel=/opt/libhoop-ica/releases/${version}-gpu before the YAML preflight, the native dependency check, the smoke test and -validate run, and never removes it when one of those fails. A rerun of the same bundle stops at the [[ ! -e $rel ]] guard because the version comes from the bundle's release.txt, so an operator who fixes a typo in the config or a GPU permission has to find and delete the partial directory by hand.
Code
deploy/ica-sidecar/install-gpu.sh[R39-42]+rel=/opt/libhoop-ica/releases/${version}-gpu +[[ ! -e $rel ]] || die "$rel already exists; use a new release version (existing assets are never overwritten)" +install -d -m 0755 "$rel/bin" "$rel/models" +cp -a "$here/gpu" "$rel/gpu"
Relevance
●●● StrongRecent installer precedent accepts staged validation and cleanup to prevent failed upgrades blocking retries.
ⓘ Recommendations generated based on similar findings in past PRs
Evidence
Line 40 refuses an existing $rel. Lines 41-48 create it and copy assets into it, and every check after that (52-76 preflight, 94-106 ldd/smoke/validate) exits through die or a non-zero status under set -e. No trap or cleanup ever removes $rel. The version is read from the bundle at line 23, so the operator cannot choose a new one.
deploy/ica-sidecar/install-gpu.sh[23-48]deploy/ica-sidecar/install-gpu.sh[94-106]
Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution ## Issue description install-gpu.sh copies assets into the final versioned release directory before running its checks. A failed check leaves that directory behind, and the 'already exists' guard then blocks every retry of the same bundle. ## Fix Focus Areas - deploy/ica-sidecar/install-gpu.sh[39-106] ## Recommended Fix Stage into a temporary sibling such as `/opt/libhoop-ica/releases/.${version}-gpu.staging.$$`, and register `trap 'rm -rf "$staging"' EXIT` to remove it. Run the preflight, ldd, smoke and validate checks against the staged paths. Only after every check passes, `mv` the staging directory to `$rel`, clear the trap, and then switch `current`. Alternatively, put an `rm -rf "$rel"` in an ERR/EXIT trap that is disarmed once validation succeeds.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
7. A failed install can switch live assets 🐞 Bug ☼ Reliability
Description
install-gpu.sh repoints /opt/libhoop-ica/current before installing the executable and service configuration. If either subsequent installation fails, the script exits with the live asset path already pointing to the new GPU release while the executable or configuration remains from the previous installation.
Code
deploy/ica-sidecar/install-gpu.sh[R116-119]+ln -sfn "releases/${version}-gpu" /opt/libhoop-ica/current +# Unlink before installing to allow replacement of a running ELF binary. +install -m 0755 "$rel/bin/hoop-ica" /usr/local/bin/hoop-ica +install -o root -g hoop -m 0640 "$rel/config.yaml" /etc/hoop-inspect/config.yaml
Relevance
●●● StrongRecent installer precedent explicitly requires switching live paths only after all checks succeed.
ⓘ Recommendations generated based on similar findings in past PRs
Evidence
Lines 116–119 switch the shared current path before two fallible install calls; set -e exits on either failure without restoring the previous target. The generated service and GPU configuration consume paths below current.
deploy/ica-sidecar/install-gpu.sh[5-5]deploy/ica-sidecar/install-gpu.sh[113-119]deploy/ica-sidecar/install-gpu.sh[123-137]
Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution ## Issue description The installer changes the live asset symlink before all remaining installation steps have succeeded. ## Fix Focus Areas - deploy/ica-sidecar/install-gpu.sh[113-141] ## Recommended Fix Stage the executable, configuration, and unit changes first. Switch `current` only after those writes succeed, and retain or restore the previous symlink if any later activation step fails.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
8. GPU builds reject valid custom libraries 🐞 Bug ≡ Correctness
Description
build() calls ort_version() before configuring the SDK and custom runtime library directories, and that probe loads ONNX Runtime in the current Python process. When the library depends on shared objects available only through configured runtime_library_dirs, the build fails at version detection before the later subprocess environment can use those directories.
Code
deploy/ica-sidecar/build-gpu-runtime.py[223]+ "ort": ort_version(ortlib / "libonnxruntime.so"),
Relevance
●● ModerateDocumented custom dependency directories are configured after ctypes loading; fixing this requires process-loading changes.
ⓘ Recommendations generated based on similar findings in past PRs
Evidence
ort_version() uses ctypes.CDLL in the builder process. The configured SDK and runtime_library_dirs paths are assembled into a subprocess-only environment after the call, although custom dependency directories are a documented builder input.
deploy/ica-sidecar/build-gpu-runtime.py[86-94]deploy/ica-sidecar/build-gpu-runtime.py[217-234]deploy/ica-sidecar/GPU-RUNTIME.md[26-32]
Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution ## Issue description The ONNX Runtime version probe runs before configured dependency directories are available to its loader. ## Fix Focus Areas - deploy/ica-sidecar/build-gpu-runtime.py[86-94] - deploy/ica-sidecar/build-gpu-runtime.py[217-234] ## Recommended Fix Construct the library search environment before probing ORT, and perform the version probe in a subprocess started with that environment. Add a test using an ORT library with a dependency in a configured custom directory.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
9. A half-configured release still pushes the CPU image 🐞 Bug ☼ Reliability
Description
The step that checks ICA_GPU_ASSETS_URL/ICA_GPU_ASSETS_SHA256 runs only after `Build and push (ica) has already pushed hoophq/hoopsidecar:<tag>-ica`. When only one variable is set the job fails after that push, so the image is published but the matching assets artifact is never uploaded.
Code
.github/workflows/release.yml[R1042-1050]+ - name: Check ICA GPU release inputs + env: + GPU_URL: ${{ vars.ICA_GPU_ASSETS_URL }} + GPU_SHA: ${{ vars.ICA_GPU_ASSETS_SHA256 }} + run: | + if { [ -n "$GPU_URL" ] && [ -z "$GPU_SHA" ]; } || { [ -z "$GPU_URL" ] && [ -n "$GPU_SHA" ]; }; then + echo "Set both ICA_GPU_ASSETS_URL and ICA_GPU_ASSETS_SHA256, or neither" >&2 + exit 1 + fi
Evidence
In this job the -ica image push (around line 1002) comes before the new check at 1042. The upload-artifact that publish-release consumes comes after it, so a failed check leaves a pushed image with no artifact.
.github/workflows/release.yml[1002-1050].github/workflows/release.yml[1093-1101]
Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution ## Issue description The ICA GPU variable consistency check runs after the CPU `-ica` image push, so a misconfigured variable fails the release only after that image is already published. ## Fix Focus Areas - .github/workflows/release.yml[1042-1050] ## Recommended Fix Move the 'Check ICA GPU release inputs' step to the start of the job, before 'Build and push (ica)', or into an earlier gating job, so a variable mismatch fails before anything is pushed.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
Context sources
103 rulesbuild-ticket✅ Cross-repo context — repo relationships
repo: hoophq/infra (sha: 88d4b625) — View relationshiprepo: hoophq/documentation (sha: 7a3cdb60) — View relationshiprepo: hoophq/alcatraz (sha: a1d14656) — View relationshiprepo: hoophq/internal-runtime-control (sha: f9e2bc6f) — View relationshipAuto: 🧠 Deep: Large, bug-dense GPU feature spans runtime, installers, workflows, and release paths.Tip of the day
💡 Did you know, you can turn on the rule miner and Qodo learns your standards from review historyPrevious reviews
Review updated until commit 7f59fd8 🧠 Deep
Results up to commit 8eeb693 🧠 Deep
🐞 Bugs (5) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)
1. A failed install can switch live assets 🐞 Bug ☼ Reliability
Description
install-gpu.sh repoints /opt/libhoop-ica/current before installing the executable and service configuration. If either subsequent installation fails, the script exits with the live asset path already pointing to the new GPU release while the executable or configuration remains from the previous installation.
Code
deploy/ica-sidecar/install-gpu.sh[R116-119]+ln -sfn "releases/${version}-gpu" /opt/libhoop-ica/current +# Unlink before installing to allow replacement of a running ELF binary. +install -m 0755 "$rel/bin/hoop-ica" /usr/local/bin/hoop-ica +install -o root -g hoop -m 0640 "$rel/config.yaml" /etc/hoop-inspect/config.yaml
Relevance
●●● StrongRecent installer precedent explicitly requires switching live paths only after all checks succeed.
ⓘ Recommendations generated based on similar findings in past PRs
Evidence
Lines 116–119 switch the shared current path before two fallible install calls; set -e exits on either failure without restoring the previous target. The generated service and GPU configuration consume paths below current.
deploy/ica-sidecar/install-gpu.sh[5-5]deploy/ica-sidecar/install-gpu.sh[113-119]deploy/ica-sidecar/install-gpu.sh[123-137]
Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution ## Issue description The installer changes the live asset symlink before all remaining installation steps have succeeded. ## Fix Focus Areas - deploy/ica-sidecar/install-gpu.sh[113-141] ## Recommended Fix Stage the executable, configuration, and unit changes first. Switch `current` only after those writes succeed, and retain or restore the previous symlink if any later activation step fails.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
2. A failed GPU install blocks every retry 🐞 Bug ☼ Reliability
Description
install-gpu.sh creates and fills $rel=/opt/libhoop-ica/releases/${version}-gpu before the YAML preflight, the native dependency check, the smoke test and -validate run, and never removes it when one of those fails. A rerun of the same bundle stops at the [[ ! -e $rel ]] guard because the version comes from the bundle's release.txt, so an operator who fixes a typo in the config or a GPU permission has to find and delete the partial directory by hand.
Code
deploy/ica-sidecar/install-gpu.sh[R39-42]+rel=/opt/libhoop-ica/releases/${version}-gpu +[[ ! -e $rel ]] || die "$rel already exists; use a new release version (existing assets are never overwritten)" +install -d -m 0755 "$rel/bin" "$rel/models" +cp -a "$here/gpu" "$rel/gpu"
Relevance
●●● StrongRecent installer precedent accepts staged validation and cleanup to prevent failed upgrades blocking retries.
ⓘ Recommendations generated based on similar findings in past PRs
Evidence
Line 40 refuses an existing $rel. Lines 41-48 create it and copy assets into it, and every check after that (52-76 preflight, 94-106 ldd/smoke/validate) exits through die or a non-zero status under set -e. No trap or cleanup ever removes $rel. The version is read from the bundle at line 23, so the operator cannot choose a new one.
deploy/ica-sidecar/install-gpu.sh[23-48]deploy/ica-sidecar/install-gpu.sh[94-106]
Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution ## Issue description install-gpu.sh copies assets into the final versioned release directory before running its checks. A failed check leaves that directory behind, and the 'already exists' guard then blocks every retry of the same bundle. ## Fix Focus Areas - deploy/ica-sidecar/install-gpu.sh[39-106] ## Recommended Fix Stage into a temporary sibling such as `/opt/libhoop-ica/releases/.${version}-gpu.staging.$$`, and register `trap 'rm -rf "$staging"' EXIT` to remove it. Run the preflight, ldd, smoke and validate checks against the staged paths. Only after every check passes, `mv` the staging directory to `$rel`, clear the trap, and then switch `current`. Alternatively, put an `rm -rf "$rel"` in an ERR/EXIT trap that is disarmed once validation succeeds.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
3. GPU runtime archive stores large libraries several times 🐞 Bug ➹ Performance
Description
collect() adds every *.so* file matching GPU_LIBRARY from the SDK directories to the queue under its own file name, and copy_file follows symlinks, so libcudnn.so, libcudnn.so.9 and libcudnn.so.9.x.y each become a separate full copy. The same happens to the cuBLAS, TensorRT and other CUDA libraries, many of which are hundreds of MB to over 1 GB, so the runtime tarball and every installed release grow by several GB.
Code
deploy/ica-sidecar/build-gpu-runtime.py[R135-138]+ for directory in search_dirs: + for path in sorted(directory.glob("*.so*")): + if GPU_LIBRARY.match(path.name) and not HOST_LIBRARY.match(path.name): + queue.append((path.name, path))
Relevance
●●● StrongThe sweep dereferences library aliases, causing avoidable multi-gigabyte archive duplication.
ⓘ Recommendations generated based on similar findings in past PRs
Evidence
The sweep queues every glob match by file name. copy_file resolves symlinks and writes a separate regular file for each destination name (lines 115-127). gpu-assets.py pack forbids symlinks, so the unversioned link, the SONAME link and the fully versioned file are all stored as full copies. install-gpu.sh then copies the whole gpu tree into every release directory.
deploy/ica-sidecar/build-gpu-runtime.py[130-154]deploy/ica-sidecar/build-gpu-runtime.py[114-127]deploy/ica-sidecar/gpu-assets.py[55-61]
Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution ## Issue description The GPU-library sweep in collect() copies every symlink alias of each CUDA/cuDNN/TensorRT library as a separate full file, which multiplies the archive size. ## Fix Focus Areas - deploy/ica-sidecar/build-gpu-runtime.py[130-154] ## Recommended Fix In the sweep, skip any path that is a symlink and is not the library's SONAME. Read the SONAME with `patchelf --print-soname` or `objdump -p`, and queue each real file once under its SONAME, which is the name the loader and dlopen use. Drop the unversioned development names (`libfoo.so`) and the fully versioned file names, and track resolved paths so each real file is copied once.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
View medium (2)
4. GPU asset override stays out of sample ⊘ Outdated
Description
HostCheck reads HOOP_ICA_GPU_ASSETS directly, but the repository sample omits the key. Operators configuring the GPU asset root therefore have no sample entry to discover or set this override.
Code
+ gpuRoot := os.Getenv("HOOP_ICA_GPU_ASSETS") + if gpuRoot == "" { + gpuRoot = defaultGPUAssets + }
Relevance
●●● StrongThe repository recently accepted mirroring newly introduced environment variables in .env.sample.
ⓘ Recommendations generated based on similar findings in past PRs
Evidence
The changed ICA host-check code introduces a direct read of HOOP_ICA_GPU_ASSETS, while the root environment sample contains no matching key.
Rule 1800927: New env vars must be mirrored in .env.samplesidecar/ica/ica.go[47-54].env.sample[1-80]
Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution Issue description `HostCheck` introduces the `HOOP_ICA_GPU_ASSETS` environment variable, but the root `.env.sample` does not list it. Fix Focus Areas - sidecar/ica/ica.go[47-50] - .env.sample[1-179] Recommended Fix Add `HOOP_ICA_GPU_ASSETS=` to `.env.sample`, with a concise comment explaining that it overrides the default GPU asset root for ICA GPU deployments.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
5. GPU builds reject valid custom libraries 🐞 Bug ≡ Correctness
Description
build() calls ort_version() before configuring the SDK and custom runtime library directories, and that probe loads ONNX Runtime in the current Python process. When the library depends on shared objects available only through configured runtime_library_dirs, the build fails at version detection before the later subprocess environment can use those directories.
Code
deploy/ica-sidecar/build-gpu-runtime.py[223]+ "ort": ort_version(ortlib / "libonnxruntime.so"),
Relevance
●● ModerateDocumented custom dependency directories are configured after ctypes loading; fixing this requires process-loading changes.
ⓘ Recommendations generated based on similar findings in past PRs
Evidence
ort_version() uses ctypes.CDLL in the builder process. The configured SDK and runtime_library_dirs paths are assembled into a subprocess-only environment after the call, although custom dependency directories are a documented builder input.
deploy/ica-sidecar/build-gpu-runtime.py[86-94]deploy/ica-sidecar/build-gpu-runtime.py[217-234]deploy/ica-sidecar/GPU-RUNTIME.md[26-32]
Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution ## Issue description The ONNX Runtime version probe runs before configured dependency directories are available to its loader. ## Fix Focus Areas - deploy/ica-sidecar/build-gpu-runtime.py[86-94] - deploy/ica-sidecar/build-gpu-runtime.py[217-234] ## Recommended Fix Construct the library search environment before probing ORT, and perform the version probe in a subprocess started with that environment. Add a test using an ORT library with a dependency in a configured custom directory.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
6. A half-configured release still pushes the CPU image 🐞 Bug ☼ Reliability
Description
The step that checks ICA_GPU_ASSETS_URL/ICA_GPU_ASSETS_SHA256 runs only after `Build and push (ica) has already pushed hoophq/hoopsidecar:<tag>-ica`. When only one variable is set the job fails after that push, so the image is published but the matching assets artifact is never uploaded.
Code
.github/workflows/release.yml[R1042-1050]+ - name: Check ICA GPU release inputs + env: + GPU_URL: ${{ vars.ICA_GPU_ASSETS_URL }} + GPU_SHA: ${{ vars.ICA_GPU_ASSETS_SHA256 }} + run: | + if { [ -n "$GPU_URL" ] && [ -z "$GPU_SHA" ]; } || { [ -z "$GPU_URL" ] && [ -n "$GPU_SHA" ]; }; then + echo "Set both ICA_GPU_ASSETS_URL and ICA_GPU_ASSETS_SHA256, or neither" >&2 + exit 1 + fi
Evidence
In this job the -ica image push (around line 1002) comes before the new check at 1042. The upload-artifact that publish-release consumes comes after it, so a failed check leaves a pushed image with no artifact.
.github/workflows/release.yml[1002-1050].github/workflows/release.yml[1093-1101]
Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution ## Issue description The ICA GPU variable consistency check runs after the CPU `-ica` image push, so a misconfigured variable fails the release only after that image is already published. ## Fix Focus Areas - .github/workflows/release.yml[1042-1050] ## Recommended Fix Move the 'Check ICA GPU release inputs' step to the start of the job, before 'Build and push (ica)', or into an earlier gating job, so a variable mismatch fails before anything is pushed.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
Code review by qodo was updated up to the latest commit https://github.com/hoophq/hoop/commit/7f59fd8c96d8870a5bb9f361ef64fba1858697cf
✅ Build Completed with Success, Version=2083.0.0-g7f59fd8
PR Summary by Qodo
Add an opt-in ICA GPU sidecar profile and verified runtime assets
✨ Enhancement📝 Documentation🧪 Tests⚙️ Configuration changes🕐 40+ MinutesAI Description
Diagram
High-Level Assessment
1. Build the runtime during each release
2. Ship only a GPU container
Files changed (22)
+2092 / -32Enhancement (5)
+354 / -32Dockerfile.sidecar
Add checksum-verified ICA GPU image and bundle targets+55/-0install-gpu.sh
Install and preflight the native ICA GPU bundle+156/-0install.sh
Route GPU bundles to their dedicated installer+5/-0ica.go
Expose and validate GPU ICA configuration fields+63/-22ica.go
Resolve TurboOCR and CUDA NER settings in the ICA engine+75/-10Tests (6)
+551 / -0pullrequest.yml
Run GPU tooling and example-config tests in PR CI+13/-0test_build_gpu_runtime.py
Test the runtime producer with synthetic inputs+170/-0test_gpu_assets.py
Test GPU archive integrity and extraction rules+105/-0ica_gpu_test.go
Validate both GPU YAML examples offline+84/-0ica_gpu_test.go
Test GPU option validation and forwarding+44/-0gpu_test.go
Test GPU defaults, conflicts, and host discovery+135/-0Documentation (5)
+431 / -0GPU-RUNTIME.md
Document GPU runtime production and publication+127/-0GPU.md
Document ICA GPU configuration and delivery+167/-0config.gpu.cidr.yaml
Add a transparent-mode GPU configuration example+65/-0config.gpu.single.yaml
Add a fixed-upstream GPU configuration example+63/-0README.md
Link ICA GPU delivery guidance from the sidecar README+9/-0Other (6)
+756 / -0ica-gpu-runtime.yml
Add a manual GPU runtime build and publication workflow+128/-0pullrequest-release.yml
Add opt-in GPU artifacts to PR release previews+52/-0release.yml
Gate GPU release outputs on a URL and checksum+53/-0build-gpu-runtime.py
Build and smoke-test a pinned native GPU runtime+342/-0gpu-assets.py
Define the GPU runtime archive contract+158/-0gpu-builder.example.json
Provide a GPU builder profile template+23/-0