Skip to content

Fix: issues#9

Merged
Jurgee merged 24 commits into
mainfrom
fix/issues
May 18, 2026
Merged

Fix: issues#9
Jurgee merged 24 commits into
mainfrom
fix/issues

Conversation

@Jurgee
Copy link
Copy Markdown
Collaborator

@Jurgee Jurgee commented May 14, 2026

Mentioned issues in this PR: #7 and #8

Summary by CodeRabbit

  • New Features

    • Added Prov‑GigaPath foundation‑embedding endpoint (compressed image input, precision‑controlled outputs).
  • Documentation

    • Updated repo overview and key pages; added Prov‑GigaPath docs and SDK example.
    • Added model contract guidance (get_config) and WSI/heatmap usage; clarified deployment registration in Helm values.
  • Refactor

    • Replaced legacy tile heatmap flow with a concurrent in‑process heatmap pipeline and direct multi‑resolution output.
  • Chores

    • Added output_tile_size/n_channels config options and added pyvips to the CPU image.

Review Change Stack

Jurgee and others added 8 commits May 14, 2026 13:05
@Jurgee Jurgee self-assigned this May 14, 2026
@Jurgee Jurgee requested review from a team, JakubPekar, Copilot and ejdam87 May 14, 2026 18:20
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 14, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Refactors HeatmapBuilder to run concurrent per-tile predictions into a shared MaskBuilder and write a BigTIFF pyramid; extends SemanticSegmentation config and Helm user_config; adds Prov-GigaPath docs, model integration/get_config guidance, README updates, and Helm registration instructions.

Changes

Heatmap Building Refactor

Layer / File(s) Summary
Async per-tile processing architecture
builders/heatmap_builder.py
HeatmapBuilder.root schedules concurrent per-tile async workers, loads model handle/config, computes tile/output sizes and stride, and manages task backpressure.
MaskBuilder initialization & updates
builders/heatmap_builder.py
Initializes a single MaskBuilder up-front, normalizes per-tile predictions to batched tensors, and updates the mask via update_batch(y,x) under the shared pipeline.
Finalization and BigTIFF output
builders/heatmap_builder.py, docker/Dockerfile.cpu
Finalizes the mask (NaNs→0), resizes to source resolution, converts to 8-bit via pyvips, writes a tiled DEFLATE-compressed BigTIFF pyramid, ensures output directories, and guarantees mask_builder.cleanup(); pyvips added to Docker deps.

Models & Helm

Layer / File(s) Summary
SemanticSegmentation config surface
models/semantic_segmentation.py, helm/rayservice/applications/episeg-1.yaml
Adds output_tile_size and n_channels to Config and SemanticSegmentation instance state; reconfigure() assigns them and get_config() returns them; Helm user_config set for episeg-1.

Foundation Models & WSI Documentation

Layer / File(s) Summary
Prov-GigaPath model endpoint specification
docs/available-models.md
Documents /prov-gigapath embedding endpoint with LZ4-compressed input, x-output-dtype header behavior, and an SDK example for embedding calls.
Model integration and WSI guidance
docs/guides/adding-models.md
Adds get_config contract for WSI builders, shows invoking deployed foundation models via Ray Serve app handles from downstream models, and advises when to use HeatmapBuilder vs custom WSI aggregation.
Helm application registration instruction & README
docs/guides/deployment-guide.md, README.md
Adds explicit instruction to register new application YAML filenames in helm/rayservice/values.yaml and updates README top-level directory list and key pages.

Sequence Diagram

sequenceDiagram
  participant Root as HeatmapBuilder.root
  participant Worker as per-tile worker
  participant Model as Ray Model (predict.remote)
  participant Mask as MaskBuilder
  participant Writer as pyvips Image

  Root->>Worker: enumerate tiles & schedule bounded tasks
  Worker->>Worker: fetch tissue tile (thread executor)
  alt tile is empty
    Worker-->>Root: skip
  else
    Worker->>Model: model.predict.remote(tile batch)
    Model-->>Worker: prediction array
    Worker->>Worker: normalize to batched tensor shape
    alt first initialization
      Worker->>Mask: create MaskBuilder(extents,tile,stride,channels)
    end
    Worker->>Mask: update_batch(pred, coords=[[y,x]])
    Worker-->>Root: task complete
  end

  Root->>Root: await all tasks
  Root->>Mask: finalize (NaNs→0, resize)
  Root->>Writer: convert to 8-bit & write BigTIFF pyramid (DEFLATE, tiles)
  Root->>Mask: cleanup()
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related issues

Possibly related PRs

  • RationAI/model-service#4: Overlaps on Helm application splitting and deployment wiring referenced by the new Helm entries and docs.

Suggested reviewers

  • JakubPekar
  • ejdam87
  • matejpekar

Poem

🐰 In tiles we hop, predictions hum,

Threads stitch masks till the final sum,
Docs point the helm to apps anew,
Embeddings whisper paths to view,
A rabbit cheers — the pipeline grew!

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'Fix: issues' is vague and generic, using non-descriptive terms that don't convey meaningful information about the changeset, which includes heatmap builder refactoring, model config updates, documentation additions, and Docker dependency changes. Replace with a specific, descriptive title that captures the main change, such as 'Refactor heatmap builder to use in-process MaskBuilder pipeline with pyvips' or 'Update model inference pipeline to support async config retrieval and tile-based prediction'.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/issues

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request transitions the HeatmapBuilder to use the MaskBuilder from the ratiopath library, enabling support for diverse prediction shapes and multi-resolution BigTIFF generation via pyvips. It also introduces documentation for the Prov-GigaPath model and provides architectural guidance for foundation model integration and whole-slide inference. The review feedback highlights critical performance improvements, recommending that CPU and I/O-bound operations—such as batch updates and TIFF saving—be offloaded to an executor to avoid blocking the asyncio event loop. A memory optimization was also suggested to handle large arrays in-place during the finalization step.

Comment thread builders/heatmap_builder.py Outdated
Comment thread builders/heatmap_builder.py Outdated
Comment thread builders/heatmap_builder.py Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@builders/heatmap_builder.py`:
- Around line 136-156: The code may leak resources because
mask_builder.cleanup() is only called after the work and will be skipped if
finalize(), resize_to_source(), or vips_image.tiffsave(...) raise; wrap the
post-processing block that calls mask_builder.finalize(),
mask_builder.resize_to_source(...), the vips_image manipulation and tiffsave()
in a try/finally where mask_builder.cleanup() is invoked in the finally to
guarantee cleanup; ensure you still return or re-raise the exception after
cleanup as appropriate and keep existing behavior when mask_builder is None.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5ff453ed-90c5-4434-8653-b0e23c0f9c1f

📥 Commits

Reviewing files that changed from the base of the PR and between 55a5bb2 and 2ca73c9.

📒 Files selected for processing (6)
  • README.md
  • builders/heatmap_builder.py
  • docs/available-models.md
  • docs/guides/adding-models.md
  • docs/guides/deployment-guide.md
  • misc/tile_heatmap_builder.py
💤 Files with no reviewable changes (1)
  • misc/tile_heatmap_builder.py

Comment thread builders/heatmap_builder.py Outdated
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR addresses the linked issues by replacing the local heatmap aggregation helper with the ratiopath mask builder and clarifying deployment documentation.

Changes:

  • Replaces TileHeatmapBuilder usage with ratiopath.masks.mask_builders.MaskBuilder.
  • Deletes the old local tile heatmap builder implementation.
  • Updates documentation for Helm application registration, model listings, and WSI/foundation-model usage.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
README.md Refreshes repository structure and key documentation links.
misc/tile_heatmap_builder.py Removes the old local heatmap builder implementation.
docs/guides/deployment-guide.md Adds the missing Helm values.yaml application registration step.
docs/guides/adding-models.md Documents foundation-model reuse and WSI builder guidance.
docs/available-models.md Adds Prov-GigaPath to available model documentation and renumbers Heatmap Builder.
builders/heatmap_builder.py Migrates heatmap aggregation to ratiopath MaskBuilder.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread builders/heatmap_builder.py Outdated
Comment thread builders/heatmap_builder.py Outdated
Co-authored-by: Copilot <copilot@github.com>
@Jurgee Jurgee requested a review from matejpekar May 14, 2026 18:29
Comment thread builders/heatmap_builder.py Outdated
Comment thread docs/guides/adding-models.md
Comment thread builders/heatmap_builder.py Outdated
Comment thread builders/heatmap_builder.py Outdated
Comment thread builders/heatmap_builder.py
Jurgee and others added 2 commits May 15, 2026 14:56
Co-authored-by: Matěj Pekár <matej.pekar120@gmail.com>
Co-authored-by: Copilot <copilot@github.com>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated 2 comments.

Comment thread builders/heatmap_builder.py Outdated
Comment thread docs/guides/adding-models.md Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
builders/heatmap_builder.py (1)

71-147: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Cleanup gap remains for exceptions during tile processing.

The try/finally only wraps the finalization phase. mask_builder is allocated at lines 71-78 inside the with block, but if any worker task raises and task.result() at line 122 or 128 re-raises, control leaves the with block entirely and the try at line 130 is never entered — mask_builder.cleanup() won't run and the memmap files leak.

Widen the try/finally to cover the entire lifetime of mask_builder.

🛡️ Proposed fix to widen the cleanup scope
         loop = asyncio.get_running_loop()
         tasks: set[asyncio.Task[Any]] = set()
-        with (
-            OpenSlide(slide_path) as slide,
-            OpenSlide(tissue_mask_path) as tissue_slide,
-            ThreadPoolExecutor(max_workers=self.num_threads) as executor,
-        ):
-            level = slide.closest_level(model_config["mpp"])
-            ...
-            mask_builder = MaskBuilder(
-                ...
-            )
-            ...
-
-        try:
-            result = np.nan_to_num(mask_builder.finalize(), nan=0.0, copy=False)
-            ...
-        finally:
-            mask_builder.cleanup()
+        mask_builder: MaskBuilder | None = None
+        try:
+            with (
+                OpenSlide(slide_path) as slide,
+                OpenSlide(tissue_mask_path) as tissue_slide,
+                ThreadPoolExecutor(max_workers=self.num_threads) as executor,
+            ):
+                level = slide.closest_level(model_config["mpp"])
+                ...
+                mask_builder = MaskBuilder(
+                    ...
+                )
+                ...  # tile processing
+
+            result = np.nan_to_num(mask_builder.finalize(), nan=0.0, copy=False)
+            ...
+        finally:
+            if mask_builder is not None:
+                mask_builder.cleanup()
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@builders/heatmap_builder.py` around lines 71 - 147, The cleanup for
MaskBuilder is not guaranteed if worker tasks raise because the try/finally only
wraps finalization; expand the try/finally to begin immediately after
constructing mask_builder (the MaskBuilder(...) call) and encompass the tile
processing loop (process_tile, task scheduling/awaiting, and the
finalize/resizing/saving block) so that mask_builder.cleanup() is always called
in the finally; also ensure any pending asyncio Tasks are cancelled/awaited
inside that finally before cleanup to avoid dangling tasks referencing the
memmap.
🧹 Nitpick comments (2)
docs/guides/adding-models.md (2)

191-214: ⚡ Quick win

Clarify return type when calling batched methods through handles.

The example shows:

embedding = await self.foundation_model.predict.remote(image)

When calling a method decorated with @serve.batch through a Ray Serve handle, the return type matches the method's signature. If the foundation model's predict returns list[float] (as shown earlier in line 130), then embedding here would be a list, not a single array. Consider adding a brief note about the expected return type to avoid confusion during implementation.

📝 Suggested clarification

Add a comment or note after line 210:

     embedding = await self.foundation_model.predict.remote(image)
+    # Note: embedding type matches the foundation model's predict return type
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/guides/adding-models.md` around lines 191 - 214, The example should
clarify that calling a batched Serve method via a handle returns whatever the
method's signature declares (e.g., a list), so update the example around
self.foundation_model.predict.remote to note that await
self.foundation_model.predict.remote(image) will yield the same return type as
the foundation model's predict (for instance list[float] if predict is annotated
to return list[float]) and recommend extracting or converting that list into the
expected format before passing to your own predict; reference the foundation
model's predict method, the `@serve.batch` decorator, and the await
self.foundation_model.predict.remote call so readers know where to add the
explanatory note.

216-223: ⚡ Quick win

Consider adding reference to HeatmapBuilder implementation.

The section mentions creating "a custom Application (similar to HeatmapBuilder)" but doesn't link to the HeatmapBuilder code. Consider adding a reference to help developers understand the pattern.

📝 Suggested addition
 2. **Custom WSI Aggregations (Non-Heatmap Outputs):** If your model generates something else across the entire slide (for example, a single slide-level scalar score, diagnostic classification, custom tabular statistics, embedded feature bags), you must **implement your own WSI aggregator service**. You should create a custom Application (similar to `HeatmapBuilder`) that takes paths to WSI files, iterates through the WSI tiles querying your base model for each tile, and correctly aggregates the results into your desired slide-level output format.
+   
+   See [`builders/heatmap_builder.py`](https://github.com/RationAI/model-service/blob/main/builders/heatmap_builder.py) for reference on implementing a WSI aggregation service.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/guides/adding-models.md` around lines 216 - 223, Add a concrete
reference to the HeatmapBuilder implementation so readers can inspect the
pattern; update the Whole-Slide (WSI) section to link or point to the
HeatmapBuilder service (HeatmapBuilder, running under /heatmap-builder) and the
repository or file that contains its implementation (e.g., the HeatmapBuilder
application/module), and suggest looking at its tile-iteration, model-querying,
and aggregation logic as an example for building custom WSI aggregators.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@builders/heatmap_builder.py`:
- Around line 71-147: The cleanup for MaskBuilder is not guaranteed if worker
tasks raise because the try/finally only wraps finalization; expand the
try/finally to begin immediately after constructing mask_builder (the
MaskBuilder(...) call) and encompass the tile processing loop (process_tile,
task scheduling/awaiting, and the finalize/resizing/saving block) so that
mask_builder.cleanup() is always called in the finally; also ensure any pending
asyncio Tasks are cancelled/awaited inside that finally before cleanup to avoid
dangling tasks referencing the memmap.

---

Nitpick comments:
In `@docs/guides/adding-models.md`:
- Around line 191-214: The example should clarify that calling a batched Serve
method via a handle returns whatever the method's signature declares (e.g., a
list), so update the example around self.foundation_model.predict.remote to note
that await self.foundation_model.predict.remote(image) will yield the same
return type as the foundation model's predict (for instance list[float] if
predict is annotated to return list[float]) and recommend extracting or
converting that list into the expected format before passing to your own
predict; reference the foundation model's predict method, the `@serve.batch`
decorator, and the await self.foundation_model.predict.remote call so readers
know where to add the explanatory note.
- Around line 216-223: Add a concrete reference to the HeatmapBuilder
implementation so readers can inspect the pattern; update the Whole-Slide (WSI)
section to link or point to the HeatmapBuilder service (HeatmapBuilder, running
under /heatmap-builder) and the repository or file that contains its
implementation (e.g., the HeatmapBuilder application/module), and suggest
looking at its tile-iteration, model-querying, and aggregation logic as an
example for building custom WSI aggregators.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1223a048-8f36-4c03-bb78-a219f8504305

📥 Commits

Reviewing files that changed from the base of the PR and between e62899b and 4bffeee.

📒 Files selected for processing (4)
  • builders/heatmap_builder.py
  • docs/guides/adding-models.md
  • helm/rayservice/applications/episeg-1.yaml
  • models/semantic_segmentation.py

Jurgee and others added 3 commits May 15, 2026 18:26
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <copilot@github.com>
@Jurgee Jurgee requested a review from matejpekar May 15, 2026 16:37
matejpekar
matejpekar previously approved these changes May 15, 2026
Copy link
Copy Markdown
Member

@matejpekar matejpekar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Jurgee make sure to test it before merging!

Co-authored-by: Copilot <copilot@github.com>
Jurgee and others added 7 commits May 17, 2026 15:32
Co-authored-by: Copilot <copilot@github.com>
Co-authored-by: Copilot <copilot@github.com>
Co-authored-by: Copilot <copilot@github.com>
Co-authored-by: Copilot <copilot@github.com>
Co-authored-by: Copilot <copilot@github.com>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@Jurgee Jurgee requested a review from matejpekar May 17, 2026 16:10
Comment thread builders/heatmap_builder.py Outdated
Co-authored-by: Matěj Pekár <matej.pekar120@gmail.com>
@Jurgee Jurgee requested a review from matejpekar May 17, 2026 17:32
@Jurgee Jurgee merged commit 3d37a6e into main May 18, 2026
4 checks passed
@Jurgee Jurgee deleted the fix/issues branch May 18, 2026 14:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants