Skip to content

feat: integrate local ONNX inference for offline plant disease detection#67

Open
vedant-kawale-27 wants to merge 2 commits into
jpdevhub:mainfrom
vedant-kawale-27:main
Open

feat: integrate local ONNX inference for offline plant disease detection#67
vedant-kawale-27 wants to merge 2 commits into
jpdevhub:mainfrom
vedant-kawale-27:main

Conversation

@vedant-kawale-27

@vedant-kawale-27 vedant-kawale-27 commented Jun 19, 2026

Copy link
Copy Markdown

Summary

Integrates the fine-tuned PyTorch ResNet18 model into the Next.js frontend using onnxruntime-web, enabling instant, offline crop disease scanning directly on the farmer's device.

Related Issue

Closes #51

Changes

  • Backend Model Export: Added export_to_onnx.py to export the ResNet18 plant disease classification model weights to a standalone, single-file plant_disease_resnet18.onnx asset.
  • WebAssembly Runtimes Setup: Installed onnxruntime-web and configured a postinstall script in package.json to copy the WebAssembly binaries (.wasm) to public/wasm/ for offline CDN-independent load.
  • Webpack & Next.js Bundle Resolution: Configured Webpack aliases and package transpilation in next.config.mjs to resolve Next.js SSR build errors (import.meta compilation constraints) when packaging WASM.
  • Service Worker / PWA Caching: Configured custom Workbox runtime caching in next.config.mjs to fetch and cache .onnx and .wasm files locally on first visit (agronavis-ml-assets).
  • Client Inference Pipeline: Created onnxInference.ts to resize input canvas images to 224x224, apply ImageNet normalization, convert arrays to planar CHW Float32 tensors, execute local WebAssembly inference, and format outputs using diseaseData.ts.
  • UI Integration & IndexedDB History: Modified CropScanTab.tsx to check network state, fall back to local ONNX scanning when offline, display a premium badge (⚡ On-Device vs ☁️ Server), and persist local scan entries inside IndexedDB using offlineStorage.ts.

Testing

  • Tested locally: Verified production bundle compilation passes cleanly via npm run build. Ran npm run test --prefix frontend (all 10 Jest tests successfully passed). Checked local browser fallback scan behavior under offline devtool network emulation.

Checklist

  • Code follows the project's TypeScript / Python style conventions
  • No secrets or .env values are committed
  • CI passes

Summary by CodeRabbit

  • New Features
    • Enabled offline crop disease scanning with on-device analysis—results available without internet connectivity
    • Results now indicate their source (server-based or on-device diagnosis)
    • Added disease information including symptoms and treatment recommendations for all supported crops
    • Offline scan records are automatically saved for future reference

Copilot AI review requested due to automatic review settings June 19, 2026 18:48
@vercel

vercel Bot commented Jun 19, 2026

Copy link
Copy Markdown

@vedant-kawale-27 is attempting to deploy a commit to the karan3431's projects Team on Vercel.

A member of the Team first needs to authorize it.

@github-actions

Copy link
Copy Markdown

🎉 Thanks for your contribution, @vedant-kawale-27!

Please make sure CI passes and the checklist in the PR template is complete. A maintainer will review this soon.

— The AgroNavis team

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@coderabbitai

coderabbitai Bot commented Jun 19, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@vedant-kawale-27, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 37 minutes and 48 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits.

🚦 How do rate limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan refill rate.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, the refill rate gradually slows as usage increases. The highest same-day bursts are limited more strictly.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: cb24d4e5-06a3-4bb6-b056-35fc52e0e8a2

📥 Commits

Reviewing files that changed from the base of the PR and between 6906cd2 and e6a1555.

📒 Files selected for processing (5)
  • backend/export_to_onnx.py
  • frontend/next.config.mjs
  • frontend/package.json
  • frontend/src/utils/diseaseData.ts
  • frontend/src/utils/onnxInference.ts
📝 Walkthrough

Walkthrough

A backend script exports a fine-tuned ResNet18 PyTorch model to ONNX (with optional 8-bit quantization). The frontend gains onnxruntime-web, build configuration, WASM asset setup, browser-side preprocessing/inference utilities, disease metadata constants, an IndexedDB single-record write helper, and a two-path scan flow in CropScanTab that falls back to on-device inference when offline.

Changes

On-Device ONNX Inference Pipeline

Layer / File(s) Summary
Backend ResNet18-to-ONNX export script
backend/export_to_onnx.py
Adds export_model() to build a ResNet18 with resized fc, load fine-tuned or ImageNet weights, export to frontend/public/model/plant_disease_resnet18.onnx, and optionally replace with an 8-bit quantized version via onnxruntime.
Frontend build config and onnxruntime-web dependency
frontend/package.json, frontend/next.config.mjs
Adds onnxruntime-web ^1.26.0, a postinstall WASM copy step into public/wasm, a webpack alias to ort.all.min.js, transpile entry, and a CacheFirst PWA rule for .onnx/.wasm assets.
Disease metadata and class name utilities
frontend/src/utils/diseaseData.ts
Introduces CLASS_NAMES, DISEASE_SYMPTOMS, DISEASE_TREATMENTS, and formatting helpers formatClassName, extractCropType, getSymptoms, getTreatments used by the inference pipeline and UI.
Browser-side ONNX inference pipeline
frontend/src/utils/onnxInference.ts
Defines LocalDiagnosisResult, lazy session init via getInferenceSession, image-to-planar-tensor preprocessing pipeline, and runLocalONNXInference which runs the session, applies argmax and softmax, and maps results to disease metadata.
Single-record offline scan persistence
frontend/src/lib/offlineStorage.ts
Adds addOfflineCropScan to write one OfflineCropScan record into the crop-scans IndexedDB object store.
CropScanTab online/offline scan integration
frontend/src/components/CropScanTab.tsx
Adds scanSource state, rewrites handleScan to attempt server inference when online and fall back to runLocalONNXInference otherwise, persists offline records via addOfflineCropScan, and adds an "⚡ On-Device" / "☁️ Server" badge to the result header.

Sequence Diagram(s)

sequenceDiagram
    participant Farmer
    participant CropScanTab
    participant diagnoseImage as diagnoseImage (Server)
    participant runLocalONNXInference as runLocalONNXInference (WASM)
    participant addOfflineCropScan as IndexedDB

    Farmer->>CropScanTab: Upload image & tap Scan
    CropScanTab->>CropScanTab: Check navigator.onLine
    alt Online
        CropScanTab->>diagnoseImage: POST image
        diagnoseImage-->>CropScanTab: DiagnosisResult (scanSource=server)
    else Offline or server error
        CropScanTab->>runLocalONNXInference: file
        runLocalONNXInference-->>CropScanTab: LocalDiagnosisResult (scanSource=local)
        CropScanTab->>addOfflineCropScan: OfflineCropScan record
        addOfflineCropScan-->>CropScanTab: saved to IndexedDB
    end
    CropScanTab-->>Farmer: Display result + source badge
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • jpdevhub/Agronavis-AI-Farm-Assistant#66: Overlaps directly on frontend/src/lib/offlineStorage.ts and the offline crop-scan IndexedDB persistence pattern used by the new addOfflineCropScan helper and CropScanTab fallback flow.

Suggested labels

Medium, SSoC26

🐇 Hoppity hop, no server in sight,
The ONNX model runs by browser light!
ResNet leaps through WASM's gate,
Detecting disease at offline rate.
With badges bright — ⚡ or ☁️ — we cheer,
On-device crops are diagnosed here! 🌱

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 41.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately summarizes the main change: integrating local ONNX inference for offline plant disease detection, which is the primary objective of this changeset.
Linked Issues check ✅ Passed All requirements from issue #51 are met: PyTorch ResNet18 model converted to ONNX (backend/export_to_onnx.py), onnxruntime-web integrated for browser execution (frontend dependencies and config), offline disease scanning enabled with local inference fallback (CropScanTab.tsx and onnxInference.ts), and offline storage implemented (offlineStorage.ts).
Out of Scope Changes check ✅ Passed All code changes directly support offline ONNX inference: ONNX model export, WASM runtime setup, inference pipeline, UI integration with offline fallback, and offline data persistence. No extraneous modifications detected.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🧹 Nitpick comments (1)
frontend/src/utils/onnxInference.ts (1)

18-46: ⚡ Quick win

Prevent duplicate ONNX session creation during concurrent first calls.

session is cached only after create(...) resolves. Two parallel calls before resolution can initialize multiple sessions.

Proposed patch
 let session: any = null;
+let sessionPromise: Promise<any> | null = null;
@@
 async function getInferenceSession(): Promise<any> {
   if (session) {
     return session;
   }
+  if (sessionPromise) {
+    return sessionPromise;
+  }
@@
-  try {
-    const ort = await import('onnxruntime-web');
+  try {
+    sessionPromise = (async () => {
+      const ort = await import('onnxruntime-web');
@@
-    session = await ort.InferenceSession.create('/model/plant_disease_resnet18.onnx', {
-      executionProviders: ['wasm']
-    });
-    console.log('[LocalInference] ONNX Inference Session loaded successfully.');
-    return session;
+      session = await ort.InferenceSession.create('/model/plant_disease_resnet18.onnx', {
+        executionProviders: ['wasm']
+      });
+      console.log('[LocalInference] ONNX Inference Session loaded successfully.');
+      return session;
+    })();
+    return await sessionPromise;
   } catch (error) {
+    sessionPromise = null;
     console.error('[LocalInference] Failed to load ONNX session:', error);
     throw new Error('Could not initialize the local disease scanner model.');
   }
 }
🤖 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 `@frontend/src/utils/onnxInference.ts` around lines 18 - 46, The race condition
occurs because the `session` variable is only assigned after the async create
call completes, allowing concurrent calls to getInferenceSession to all see
session as null and each initiate their own session creation. Instead of caching
only the resolved session, you need to cache the promise itself. Create a new
variable to hold the session creation promise (separate from the resolved
session variable), store the promise from ort.InferenceSession.create()
immediately before awaiting it, and return the cached promise on subsequent
calls so they wait for the same in-flight creation rather than starting new
ones.
🤖 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 `@backend/export_to_onnx.py`:
- Around line 39-55: Replace the else block that provides fallback behavior when
fine-tuned weights are missing at MODEL_PATH with a hard-fail guard that raises
an exception or exits the program. Instead of attempting to load torchvision
pretrained weights or exporting with random weights, immediately raise an
informative error when the fine-tuned checkpoint is not found. This ensures the
export process terminates early rather than silently proceeding with weights
that may produce incorrect predictions in production.

In `@frontend/next.config.mjs`:
- Around line 23-37: The Workbox configuration for caching ONNX and WASM files
uses a CacheFirst strategy with hardcoded model paths and a 30-day expiration,
which prevents model updates from being served to users. To fix this, either
modify the model asset filenames to include version numbers or hashes (such as
changing the path pattern to include versioning like v1, v2, or a hash suffix)
in the urlPattern matching logic, or add a cacheVersion property within the
options object of the Workbox configuration that can be incremented whenever
models are updated to force cache invalidation. The cacheVersion mechanism in
Workbox automatically appends a version identifier to the cache name, ensuring
old cached entries are abandoned when the version changes.

In `@frontend/package.json`:
- Line 11: The postinstall script in package.json currently only warns when the
onnxruntime-web WASM files are missing, allowing the build to succeed without
the required WASM files. Since the application hardcodes the WASM path at
runtime, this causes failures that are difficult to debug. Replace the warning
console.warn with a fatal error that exits the process with a non-zero code
using process.exit(1) when the source directory is not found, ensuring the build
fails early and prevents incomplete deployments.

In `@frontend/src/utils/diseaseData.ts`:
- Around line 236-242: The extractCropType function incorrectly parses
classnames with diseased_ prefixes and multi-token crop names. The function
currently returns "Diseased" for diseased_cucumber and "Bell" for
bell_pepper_bacterial_spot instead of the actual crop types. Refactor the
function to properly handle both healthy_ and diseased_ prefixes by extracting
the crop name portion that follows these prefixes, and for multi-token crop
names (like bell_pepper), split by underscore and capitalize each word properly
to return the correct crop type that gets displayed in CropScanTab.

In `@frontend/src/utils/onnxInference.ts`:
- Around line 138-172: Replace the hardcoded input and output names in the feeds
object construction and results retrieval with dynamic resolution using
activeSession.inputNames and activeSession.outputNames properties to decouple
from exporter configuration. Additionally, add validation after retrieving the
outputTensor to ensure the logits array length (output.length) matches
CLASS_NAMES.length, throwing a descriptive error if they do not match to prevent
silent data integrity failures.

---

Nitpick comments:
In `@frontend/src/utils/onnxInference.ts`:
- Around line 18-46: The race condition occurs because the `session` variable is
only assigned after the async create call completes, allowing concurrent calls
to getInferenceSession to all see session as null and each initiate their own
session creation. Instead of caching only the resolved session, you need to
cache the promise itself. Create a new variable to hold the session creation
promise (separate from the resolved session variable), store the promise from
ort.InferenceSession.create() immediately before awaiting it, and return the
cached promise on subsequent calls so they wait for the same in-flight creation
rather than starting new ones.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 6a19da11-4b9b-43b8-a9fe-58eeff05d91d

📥 Commits

Reviewing files that changed from the base of the PR and between e220859 and 6906cd2.

⛔ Files ignored due to path filters (5)
  • frontend/package-lock.json is excluded by !**/package-lock.json
  • frontend/public/wasm/ort-wasm-simd-threaded.asyncify.wasm is excluded by !**/*.wasm
  • frontend/public/wasm/ort-wasm-simd-threaded.jsep.wasm is excluded by !**/*.wasm
  • frontend/public/wasm/ort-wasm-simd-threaded.jspi.wasm is excluded by !**/*.wasm
  • frontend/public/wasm/ort-wasm-simd-threaded.wasm is excluded by !**/*.wasm
📒 Files selected for processing (7)
  • backend/export_to_onnx.py
  • frontend/next.config.mjs
  • frontend/package.json
  • frontend/src/components/CropScanTab.tsx
  • frontend/src/lib/offlineStorage.ts
  • frontend/src/utils/diseaseData.ts
  • frontend/src/utils/onnxInference.ts

Comment thread backend/export_to_onnx.py
Comment thread frontend/next.config.mjs
Comment thread frontend/package.json Outdated
Comment thread frontend/src/utils/diseaseData.ts
Comment thread frontend/src/utils/onnxInference.ts Outdated
@vedant-kawale-27

Copy link
Copy Markdown
Author

hee @jpdevhub check it
and if every thing looks good merge it

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.

On-Device Disease Detection (WebAssembly/ONNX)

2 participants