Skip to content

Add Construction Safety and Playground Damage Detection Features#483

Open
RohanExploit wants to merge 4 commits intomainfrom
feature/safety-and-playground-detection-6191078611621407715
Open

Add Construction Safety and Playground Damage Detection Features#483
RohanExploit wants to merge 4 commits intomainfrom
feature/safety-and-playground-detection-6191078611621407715

Conversation

@RohanExploit
Copy link
Owner

@RohanExploit RohanExploit commented Feb 27, 2026

This PR introduces two new AI-powered detection features: Construction Safety and Playground Damage. These features utilize the Hugging Face Inference API (CLIP model) to provide lightweight, resource-efficient detection without heavy local models.

Key Changes:

  • Backend:

    • backend/hf_api_service.py: Added detect_construction_safety_clip and detect_playground_damage_clip functions.
    • backend/routers/detection.py: Added corresponding API endpoints.
    • backend/tests/test_new_detectors.py: Added unit tests for the new endpoints (mocking HF API).
  • Frontend:

    • frontend/src/ConstructionSafetyDetector.jsx: New camera-based UI for construction safety.
    • frontend/src/PlaygroundDetector.jsx: New camera-based UI for playground damage.
    • frontend/src/views/Home.jsx: Added a "Public Safety" section.
    • frontend/src/App.jsx: Added routes for the new detectors.
    • frontend/src/api/detectors.js: Updated API client.

Verification:

  • Validated backend endpoints using pytest.
  • Verified frontend integration via manual walkthrough and verify_detectors.py script.
  • Confirmed that render.yaml and environment variables are correctly set up for deployment.

PR created automatically by Jules for task 6191078611621407715 started by @RohanExploit


Summary by cubic

Adds construction safety and playground damage detection with CLIP, plus camera UIs and backend endpoints. Optimizes Render deployment with a lightweight build script (skips frontend build, uses --no-cache-dir) and ensures it runs by setting execute permissions.

  • New Features

    • Backend: CLIP detectors detect_construction_safety_clip and detect_playground_damage_clip, with cached endpoints /api/detect-construction-safety and /api/detect-playground-damage.
    • Frontend: New views ConstructionSafetyDetector.jsx and PlaygroundDetector.jsx, routes /construction-safety and /playground-damage, API methods constructionSafety and playgroundDamage, and a “Public Safety” section on Home.
    • Tests: Unit tests for both endpoints with mocked HF API.
  • Dependencies

    • Replaced googletrans with deep-translator; updated backend requirements to resolve httpx/httpcore conflicts.
    • Render build: render.yaml now runs chmod +x ./render-build.sh then executes it; script uses --no-cache-dir and skips frontend build to reduce memory and prevent permission errors.

Written for commit a95464e. Summary will update on new commits.

Summary by CodeRabbit

  • New Features

    • Construction Safety detector (webcam capture) and Playground Damage detector (webcam capture)
    • New "Public Safety" category and routes in the app
    • Camera diagnostics/modal for camera access and checks
  • Tests

    • Added automated tests for the two new detectors
  • Chores

    • Added browser automation verification script
    • Updated backend translation implementation (improved translation handling)

- Added `detect_construction_safety_clip` and `detect_playground_damage_clip` to `backend/hf_api_service.py` using Hugging Face CLIP inference.
- Exposed new endpoints `/api/detect-construction-safety` and `/api/detect-playground-damage` in `backend/routers/detection.py`.
- Created frontend components `ConstructionSafetyDetector.jsx` and `PlaygroundDetector.jsx`.
- Integrated new detectors into `frontend/src/api/detectors.js` and `frontend/src/App.jsx`.
- Added new "Public Safety" category to `frontend/src/views/Home.jsx` with the new detection features.
- Added backend tests in `backend/tests/test_new_detectors.py`.
@google-labs-jules
Copy link
Contributor

👋 Jules, reporting for duty! I'm here to lend a hand with this pull request.

When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down.

I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job!

For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!

New to Jules? Learn more at jules.google/docs.


For security, I will only act on instructions from the user who triggered this task.

Copilot AI review requested due to automatic review settings February 27, 2026 10:00
@netlify
Copy link

netlify bot commented Feb 27, 2026

Deploy Preview for fixmybharat canceled.

Name Link
🔨 Latest commit a95464e
🔍 Latest deploy log https://app.netlify.com/projects/fixmybharat/deploys/69a16e0082519f0008559270

@github-actions
Copy link

🙏 Thank you for your contribution, @RohanExploit!

PR Details:

Quality Checklist:
Please ensure your PR meets the following criteria:

  • Code follows the project's style guidelines
  • Self-review of code completed
  • Code is commented where necessary
  • Documentation updated (if applicable)
  • No new warnings generated
  • Tests added/updated (if applicable)
  • All tests passing locally
  • No breaking changes to existing functionality

Review Process:

  1. Automated checks will run on your code
  2. A maintainer will review your changes
  3. Address any requested changes promptly
  4. Once approved, your PR will be merged! 🎉

Note: The maintainers will monitor code quality and ensure the overall project flow isn't broken.

@coderabbitai
Copy link

coderabbitai bot commented Feb 27, 2026

📝 Walkthrough

Walkthrough

Adds two CLIP-based detectors (construction safety and playground damage) with backend detection functions, cached router endpoints, frontend detector views and API bindings, tests, Playwright verification, and dependency updates for translation.

Changes

Cohort / File(s) Summary
Backend Detector Functions
backend/hf_api_service.py
Added detect_construction_safety_clip and detect_playground_damage_clip, both delegating to _detect_clip_generic with new label/target sets.
Backend Routing & Caching
backend/routers/detection.py
Added cached helpers and POST endpoints: /api/detect-construction-safety and /api/detect-playground-damage, wired into existing cache/validation patterns.
Backend Tests
backend/tests/test_new_detectors.py
Refactored test fixture for app/http client mocking; added tests for the two new endpoints with mocked CLIP responses.
Frontend API & Routing
frontend/src/api/detectors.js, frontend/src/App.jsx
Added constructionSafety and playgroundDamage detector API methods; registered routes /construction-safety and /playground-damage with lazy-loaded components.
Frontend Detector Components
frontend/src/ConstructionSafetyDetector.jsx, frontend/src/PlaygroundDetector.jsx
New webcam-based React components for capture/retake/analyze flows, FormData upload to backend, detection overlays and error handling.
Frontend Navigation / Home
frontend/src/views/Home.jsx
Added "Public Safety" category entries and CameraCheckModal integration to Home and side tools.
Frontend Verification
frontend_verification/verify_detectors.py
New Playwright script to navigate and screenshot Home, construction-safety, and playground-damage pages.
Dependencies / Voice
backend/requirements*.txt, backend/voice_service.py
Replaced googletrans with deep-translator and adapted synchronous translation usage in voice_service.

Sequence Diagram(s)

sequenceDiagram
    participant User as User (Browser)
    participant UI as Detector Component
    participant API as Backend Router
    participant Cache as Cache Layer
    participant HFService as HF API Service
    participant HFClip as HuggingFace CLIP

    User->>UI: Capture image (webcam)
    UI->>API: POST /api/detect-{type} (FormData)
    API->>API: Validate & extract image bytes
    API->>Cache: lookup(image_hash)
    alt cache hit
        Cache-->>API: cached detections
    else cache miss
        API->>HFService: detect_{type}_clip(image_bytes)
        HFService->>HFClip: run CLIP inference
        HFClip-->>HFService: labels & scores
        HFService-->>API: detections
        API->>Cache: store(image_hash, detections)
    end
    API-->>UI: detections JSON
    UI->>User: render overlay (labels & confidence)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested labels

ECWoC26, ECWoC26-L2

Poem

🐰 Hard hats, broken swings — I sniff the scene with care,
I hop and scan each image, eyes alert and aware.
From scaffold to playground, I point what’s out of place,
A rabbit on patrol — keeping kids safe, just in case! 🥕✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 29.41% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main changes: adding two new detection features (Construction Safety and Playground Damage) for the application.
Description check ✅ Passed PR description is comprehensive and covers the required template sections with sufficient detail about changes, scope, and verification.

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/safety-and-playground-detection-6191078611621407715

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
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

3 issues found across 12 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="backend/tests/test_new_detectors.py">

<violation number="1" location="backend/tests/test_new_detectors.py:65">
P2: Fix the indentation on the nested `with TestClient(app)` block; the extra leading space will cause an IndentationError.</violation>
</file>

<file name="frontend/src/views/Home.jsx">

<violation number="1" location="frontend/src/views/Home.jsx:110">
P3: Localize the new item labels with translation keys to keep this section consistent with the rest of the UI.</violation>
</file>

<file name="frontend/src/PlaygroundDetector.jsx">

<violation number="1" location="frontend/src/PlaygroundDetector.jsx:21">
P2: Guard against webcamRef.current being null before calling getScreenshot to avoid a runtime crash when capture is pressed too early.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

mock_client = AsyncMock()
# Patch get_http_client in detection router to return our mock
with patch("backend.routers.detection.get_http_client", return_value=mock_client):
with TestClient(app) as c:
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Feb 27, 2026

Choose a reason for hiding this comment

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

P2: Fix the indentation on the nested with TestClient(app) block; the extra leading space will cause an IndentationError.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At backend/tests/test_new_detectors.py, line 65:

<comment>Fix the indentation on the nested `with TestClient(app)` block; the extra leading space will cause an IndentationError.</comment>

<file context>
@@ -32,21 +32,39 @@
+        mock_client = AsyncMock()
+        # Patch get_http_client in detection router to return our mock
+        with patch("backend.routers.detection.get_http_client", return_value=mock_client):
+             with TestClient(app) as c:
+                c.app.state.http_client = mock_client
+                yield c
</file context>
Suggested change
with TestClient(app) as c:
with TestClient(app) as c:
Fix with Cubic

- Added missing dependencies to `backend/requirements-render.txt` and `backend/requirements.txt`:
    - `pywebpush`: Required for push notifications in `backend/tasks.py`.
    - `SpeechRecognition`, `pydub`, `langdetect`: Required for voice features in `backend/voice_service.py`.
    - `deep-translator`: Added as a replacement for `googletrans`.
- Switched from `googletrans` to `deep-translator` in `backend/voice_service.py` to resolve version conflicts with `httpx` and `httpcore` (which clashed with `python-telegram-bot`).
- Verified frontend build passes locally.
- Verified backend code imports successfully.
Copy link

@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: 5

🧹 Nitpick comments (3)
frontend_verification/verify_detectors.py (1)

17-20: Avoid hardcoded credentials in verification automation.

Move login credentials to environment variables so the script is portable and safer for CI/local runs.

Suggested patch
 from playwright.sync_api import sync_playwright
 import time
+import os
@@
-        if "login" in page.url:
-            page.fill('input[type="email"]', 'test@example.com')
-            page.fill('input[type="password"]', 'password')
-            page.click('button:has-text("Login")')
-            time.sleep(2)
+        if "login" in page.url:
+            email = os.getenv("E2E_EMAIL")
+            password = os.getenv("E2E_PASSWORD")
+            if email and password:
+                page.fill('input[type="email"]', email)
+                page.fill('input[type="password"]', password)
+                page.click('button:has-text("Login")')
+                time.sleep(2)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend_verification/verify_detectors.py` around lines 17 - 20, Replace
hardcoded credentials used in the Playwright steps (the page.fill calls for
'input[type="email"]' and 'input[type="password"]' before
page.click('button:has-text("Login")')) with values read from environment
variables (e.g., via os.environ.get or dotenv) and fail-fast or raise a clear
error if they are missing; update any setup or test harness to document the
required env vars and remove the literal 'test@example.com' and 'password'
strings so CI/local runs can supply credentials securely.
frontend/src/ConstructionSafetyDetector.jsx (2)

94-99: Consider adding defensive checks for detection data.

If the API response contains malformed detection objects (missing label or confidence), the rendering could fail or display "NaN%". A defensive check ensures graceful degradation.

🛡️ Proposed fix
 {detections.map((d, idx) => (
-    <li key={idx} className="flex justify-between text-sm font-medium">
+    <li key={d.label ?? idx} className="flex justify-between text-sm font-medium">
         <span>{d.label}</span>
-        <span className="text-gray-400">{(d.confidence * 100).toFixed(0)}%</span>
+        <span className="text-gray-400">
+          {typeof d.confidence === 'number' ? `${(d.confidence * 100).toFixed(0)}%` : 'N/A'}
+        </span>
     </li>
 ))}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/ConstructionSafetyDetector.jsx` around lines 94 - 99, The
detections rendering in ConstructionSafetyDetector.jsx currently assumes every
detection has valid label and confidence; update the code that maps over
detections (the detections.map callback) to defensively handle malformed objects
by filtering out or skipping items missing a string label, and normalizing
confidence via Number(d.confidence) with a fallback (e.g., 0) before
multiplying/formatting so you never render "NaN%"; ensure you still display a
sensible placeholder or skip the item if label is missing and keep the existing
key usage (idx) intact.

48-55: Consider replacing alert() with inline UI feedback.

Using browser alert() blocks the UI thread and provides a jarring experience. Consider using inline state-based messaging or a toast notification system for a more polished UX.

💡 Example approach using inline state
+  const [message, setMessage] = useState(null);
   
   // In detectSafety:
   if (data.detections && data.detections.length > 0) {
       setDetections(data.detections);
+      setMessage(null);
   } else {
-      alert("No safety violations detected.");
+      setMessage({ type: 'info', text: 'No safety violations detected.' });
   }
   // ...
   } catch (error) {
       console.error("Error:", error);
-      alert("An error occurred during detection.");
+      setMessage({ type: 'error', text: 'An error occurred during detection.' });
   }

Then render message inline in the JSX when present.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/ConstructionSafetyDetector.jsx` around lines 48 - 55, Replace
blocking alert() calls in the ConstructionSafetyDetector component with
state-driven inline UI feedback: add a message state (e.g., message and
setMessage via useState in the ConstructionSafetyDetector component), call
setMessage("No safety violations detected.") instead of alert in the else branch
that checks data.detections, and call setMessage("An error occurred during
detection.") (or include error details) inside the catch block instead of alert;
then render the message in the component JSX (or hook into your
toast/notification component) so feedback is non-blocking and part of the UI
while keeping existing setDetections logic intact.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@backend/tests/test_new_detectors.py`:
- Around line 49-67: The fixture `client` currently only patches
`backend.routers.detection.get_http_client`, but `_get_cached_result` uses the
global `backend.dependencies.SHARED_HTTP_CLIENT` and tests can leak cached
state; update the fixture to also patch
`backend.dependencies.SHARED_HTTP_CLIENT` to return the same `mock_client` (or
set `c.app.state.http_client` from that mock) and ensure any in-memory cache
used by the cached detector endpoints is cleared/reset before yielding (e.g.,
reset the shared cache or call its clear/reset helper) so each test gets a fresh
client and no cross-test cache leakage.

In `@frontend/src/ConstructionSafetyDetector.jsx`:
- Line 85: The onUserMediaError handler currently swallows the actual error;
update the onUserMediaError callback (where setCameraError is called) to include
the original error information — e.g., stringify or append err.message/err.name
— so the state contains the descriptive error and/or also call
console.error(err) for debugging; locate the onUserMediaError prop passed to the
camera/video component and change the inline arrow function to include the err
details when calling setCameraError and/or logging.
- Around line 20-23: The capture callback currently calls
webcamRef.current.getScreenshot() without ensuring webcamRef.current exists;
update the capture function to null-check webcamRef (webcamRef.current) before
calling getScreenshot() and only call setImgSrc(imageSrc) when imageSrc is
non-null, returning early or handling the error case if webcamRef.current is
falsy. Locate the capture useCallback and add a guard like if
(!webcamRef?.current) return (or handle error) and ensure imageSrc is validated
before calling setImgSrc.

In `@frontend/src/PlaygroundDetector.jsx`:
- Around line 109-114: The capture button in PlaygroundDetector.jsx is icon-only
and missing an accessible name; update the button element (the one with
onClick={capture}) to include an accessible label such as aria-label="Capture
photo" or add visually-hidden text inside the button (e.g., a span with
screen-reader-only class) so assistive technologies can announce its purpose;
ensure the label is descriptive and matches the capture handler name/behavior.

In `@frontend/src/views/Home.jsx`:
- Around line 107-111: The Public Safety block in Home.jsx uses hard-coded label
strings and a non-pattern title; update the i18n usage so the title and item
labels use translation keys instead of literals. Replace title: t('Public
Safety') with a namespaced key such as t('publicSafety.title') and change the
item labels for ids 'construction-safety' and 'playground-damage' to use
t('publicSafety.constructionSafety') and t('publicSafety.playgroundDamage')
(keep the id, icon, color and bg fields unchanged) so the block follows the
existing i18n key pattern.

---

Nitpick comments:
In `@frontend_verification/verify_detectors.py`:
- Around line 17-20: Replace hardcoded credentials used in the Playwright steps
(the page.fill calls for 'input[type="email"]' and 'input[type="password"]'
before page.click('button:has-text("Login")')) with values read from environment
variables (e.g., via os.environ.get or dotenv) and fail-fast or raise a clear
error if they are missing; update any setup or test harness to document the
required env vars and remove the literal 'test@example.com' and 'password'
strings so CI/local runs can supply credentials securely.

In `@frontend/src/ConstructionSafetyDetector.jsx`:
- Around line 94-99: The detections rendering in ConstructionSafetyDetector.jsx
currently assumes every detection has valid label and confidence; update the
code that maps over detections (the detections.map callback) to defensively
handle malformed objects by filtering out or skipping items missing a string
label, and normalizing confidence via Number(d.confidence) with a fallback
(e.g., 0) before multiplying/formatting so you never render "NaN%"; ensure you
still display a sensible placeholder or skip the item if label is missing and
keep the existing key usage (idx) intact.
- Around line 48-55: Replace blocking alert() calls in the
ConstructionSafetyDetector component with state-driven inline UI feedback: add a
message state (e.g., message and setMessage via useState in the
ConstructionSafetyDetector component), call setMessage("No safety violations
detected.") instead of alert in the else branch that checks data.detections, and
call setMessage("An error occurred during detection.") (or include error
details) inside the catch block instead of alert; then render the message in the
component JSX (or hook into your toast/notification component) so feedback is
non-blocking and part of the UI while keeping existing setDetections logic
intact.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 169ed20 and c1c591b.

⛔ Files ignored due to path filters (3)
  • frontend_verification/construction_safety.png is excluded by !**/*.png
  • frontend_verification/home_categories.png is excluded by !**/*.png
  • frontend_verification/playground_damage.png is excluded by !**/*.png
📒 Files selected for processing (9)
  • backend/hf_api_service.py
  • backend/routers/detection.py
  • backend/tests/test_new_detectors.py
  • frontend/src/App.jsx
  • frontend/src/ConstructionSafetyDetector.jsx
  • frontend/src/PlaygroundDetector.jsx
  • frontend/src/api/detectors.js
  • frontend/src/views/Home.jsx
  • frontend_verification/verify_detectors.py

Comment on lines +49 to +67
@pytest.fixture
def client():
mock_client = AsyncMock()
# Patch get_http_client in detection router to return our mock
with patch("backend.routers.detection.get_http_client", return_value=mock_client):
with TestClient(app) as c:
c.app.state.http_client = mock_client
yield c
# We need to successfully import backend.main to get 'app'
# We will wrap the import in a patch context to mock side effects

with patch("backend.ai_factory.create_all_ai_services") as mock_create:
mock_action = AsyncMock()
mock_chat = AsyncMock()
mock_summary = AsyncMock()
mock_create.return_value = (mock_action, mock_chat, mock_summary)

from backend.main import app

mock_client = AsyncMock()
# Patch get_http_client in detection router to return our mock
with patch("backend.routers.detection.get_http_client", return_value=mock_client):
with TestClient(app) as c:
c.app.state.http_client = mock_client
yield c
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Fixture does not reliably mock the client path used by cached detector endpoints.

These endpoint tests go through _get_cached_result, which uses backend.dependencies.SHARED_HTTP_CLIENT. Patching only get_http_client can leave calls unmocked, and the global cache can leak state across tests.

Suggested patch
 `@pytest.fixture`
 def client():
@@
-        mock_client = AsyncMock()
-        # Patch get_http_client in detection router to return our mock
-        with patch("backend.routers.detection.get_http_client", return_value=mock_client):
-             with TestClient(app) as c:
-                c.app.state.http_client = mock_client
-                yield c
+        from backend import dependencies as deps
+        from backend.routers import detection as detection_router
+
+        mock_client = AsyncMock()
+        detection_router._cache_store.clear()
+
+        with patch.object(deps, "SHARED_HTTP_CLIENT", mock_client), \
+             patch("backend.routers.detection.get_http_client", return_value=mock_client):
+            with TestClient(app) as c:
+                c.app.state.http_client = mock_client
+                yield c
+
+        detection_router._cache_store.clear()
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@pytest.fixture
def client():
mock_client = AsyncMock()
# Patch get_http_client in detection router to return our mock
with patch("backend.routers.detection.get_http_client", return_value=mock_client):
with TestClient(app) as c:
c.app.state.http_client = mock_client
yield c
# We need to successfully import backend.main to get 'app'
# We will wrap the import in a patch context to mock side effects
with patch("backend.ai_factory.create_all_ai_services") as mock_create:
mock_action = AsyncMock()
mock_chat = AsyncMock()
mock_summary = AsyncMock()
mock_create.return_value = (mock_action, mock_chat, mock_summary)
from backend.main import app
mock_client = AsyncMock()
# Patch get_http_client in detection router to return our mock
with patch("backend.routers.detection.get_http_client", return_value=mock_client):
with TestClient(app) as c:
c.app.state.http_client = mock_client
yield c
`@pytest.fixture`
def client():
# We need to successfully import backend.main to get 'app'
# We will wrap the import in a patch context to mock side effects
with patch("backend.ai_factory.create_all_ai_services") as mock_create:
mock_action = AsyncMock()
mock_chat = AsyncMock()
mock_summary = AsyncMock()
mock_create.return_value = (mock_action, mock_chat, mock_summary)
from backend.main import app
from backend import dependencies as deps
from backend.routers import detection as detection_router
mock_client = AsyncMock()
detection_router._cache_store.clear()
with patch.object(deps, "SHARED_HTTP_CLIENT", mock_client), \
patch("backend.routers.detection.get_http_client", return_value=mock_client):
with TestClient(app) as c:
c.app.state.http_client = mock_client
yield c
detection_router._cache_store.clear()
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/tests/test_new_detectors.py` around lines 49 - 67, The fixture
`client` currently only patches `backend.routers.detection.get_http_client`, but
`_get_cached_result` uses the global `backend.dependencies.SHARED_HTTP_CLIENT`
and tests can leak cached state; update the fixture to also patch
`backend.dependencies.SHARED_HTTP_CLIENT` to return the same `mock_client` (or
set `c.app.state.http_client` from that mock) and ensure any in-memory cache
used by the cached detector endpoints is cleared/reset before yielding (e.g.,
reset the shared cache or call its clear/reset helper) so each test gets a fresh
client and no cross-test cache leakage.

Comment on lines +20 to +23
const capture = useCallback(() => {
const imageSrc = webcamRef.current.getScreenshot();
setImgSrc(imageSrc);
}, [webcamRef]);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Add a null check before accessing webcamRef.current.

If the webcam hasn't mounted yet or is in an error state, webcamRef.current will be null, causing a TypeError when calling getScreenshot().

🛡️ Proposed fix
 const capture = useCallback(() => {
+   if (!webcamRef.current) return;
   const imageSrc = webcamRef.current.getScreenshot();
   setImgSrc(imageSrc);
- }, [webcamRef]);
+ }, []);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const capture = useCallback(() => {
const imageSrc = webcamRef.current.getScreenshot();
setImgSrc(imageSrc);
}, [webcamRef]);
const capture = useCallback(() => {
if (!webcamRef.current) return;
const imageSrc = webcamRef.current.getScreenshot();
setImgSrc(imageSrc);
}, []);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/ConstructionSafetyDetector.jsx` around lines 20 - 23, The
capture callback currently calls webcamRef.current.getScreenshot() without
ensuring webcamRef.current exists; update the capture function to null-check
webcamRef (webcamRef.current) before calling getScreenshot() and only call
setImgSrc(imageSrc) when imageSrc is non-null, returning early or handling the
error case if webcamRef.current is falsy. Locate the capture useCallback and add
a guard like if (!webcamRef?.current) return (or handle error) and ensure
imageSrc is validated before calling setImgSrc.

screenshotFormat="image/jpeg"
videoConstraints={videoConstraints}
className="absolute inset-0 w-full h-full object-cover"
onUserMediaError={(err) => setCameraError("Camera access denied. Please check permissions.")}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Preserve original error information in camera error handling.

The onUserMediaError callback receives the actual error object but discards it, always showing a generic message. Logging or displaying the real error helps with debugging.

🔧 Proposed fix
- onUserMediaError={(err) => setCameraError("Camera access denied. Please check permissions.")}
+ onUserMediaError={(err) => {
+   console.error("Camera error:", err);
+   const message = err?.message || "Camera access denied. Please check permissions.";
+   setCameraError(message);
+ }}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
onUserMediaError={(err) => setCameraError("Camera access denied. Please check permissions.")}
onUserMediaError={(err) => {
console.error("Camera error:", err);
const message = err?.message || "Camera access denied. Please check permissions.";
setCameraError(message);
}}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/ConstructionSafetyDetector.jsx` at line 85, The onUserMediaError
handler currently swallows the actual error; update the onUserMediaError
callback (where setCameraError is called) to include the original error
information — e.g., stringify or append err.message/err.name — so the state
contains the descriptive error and/or also call console.error(err) for
debugging; locate the onUserMediaError prop passed to the camera/video component
and change the inline arrow function to include the err details when calling
setCameraError and/or logging.

Comment on lines +109 to +114
<button
onClick={capture}
className="w-20 h-20 rounded-full bg-white border-4 border-gray-200 shadow-lg flex items-center justify-center active:scale-95 transition-transform"
>
<div className="w-16 h-16 rounded-full bg-green-600"></div>
</button>
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Add an accessible name to the capture button.

The icon-only control lacks an accessible label, which makes it unclear for assistive technologies.

Suggested patch
-           <button
-             onClick={capture}
+           <button
+             type="button"
+             aria-label="Capture playground image"
+             onClick={capture}
              className="w-20 h-20 rounded-full bg-white border-4 border-gray-200 shadow-lg flex items-center justify-center active:scale-95 transition-transform"
            >
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<button
onClick={capture}
className="w-20 h-20 rounded-full bg-white border-4 border-gray-200 shadow-lg flex items-center justify-center active:scale-95 transition-transform"
>
<div className="w-16 h-16 rounded-full bg-green-600"></div>
</button>
<button
type="button"
aria-label="Capture playground image"
onClick={capture}
className="w-20 h-20 rounded-full bg-white border-4 border-gray-200 shadow-lg flex items-center justify-center active:scale-95 transition-transform"
>
<div className="w-16 h-16 rounded-full bg-green-600"></div>
</button>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/PlaygroundDetector.jsx` around lines 109 - 114, The capture
button in PlaygroundDetector.jsx is icon-only and missing an accessible name;
update the button element (the one with onClick={capture}) to include an
accessible label such as aria-label="Capture photo" or add visually-hidden text
inside the button (e.g., a span with screen-reader-only class) so assistive
technologies can announce its purpose; ensure the label is descriptive and
matches the capture handler name/behavior.

Comment on lines +107 to +111
title: t('Public Safety'),
icon: <Construction size={20} className="text-orange-600" />,
items: [
{ id: 'construction-safety', label: "Construction Safety", icon: <HardHat size={24} />, color: 'text-orange-600', bg: 'bg-orange-50' },
{ id: 'playground-damage', label: "Playground Damage", icon: <Trophy size={24} />, color: 'text-green-600', bg: 'bg-green-50' },
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Use translation keys for the new Public Safety title/items.

This segment bypasses the existing i18n key pattern, which can leave these labels untranslated in non-English locales.

Suggested patch
-      title: t('Public Safety'),
+      title: t('home.categories.publicSafety'),
@@
-        { id: 'construction-safety', label: "Construction Safety", icon: <HardHat size={24} />, color: 'text-orange-600', bg: 'bg-orange-50' },
-        { id: 'playground-damage', label: "Playground Damage", icon: <Trophy size={24} />, color: 'text-green-600', bg: 'bg-green-50' },
+        { id: 'construction-safety', label: t('home.issues.constructionSafety'), icon: <HardHat size={24} />, color: 'text-orange-600', bg: 'bg-orange-50' },
+        { id: 'playground-damage', label: t('home.issues.playgroundDamage'), icon: <Trophy size={24} />, color: 'text-green-600', bg: 'bg-green-50' },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
title: t('Public Safety'),
icon: <Construction size={20} className="text-orange-600" />,
items: [
{ id: 'construction-safety', label: "Construction Safety", icon: <HardHat size={24} />, color: 'text-orange-600', bg: 'bg-orange-50' },
{ id: 'playground-damage', label: "Playground Damage", icon: <Trophy size={24} />, color: 'text-green-600', bg: 'bg-green-50' },
title: t('home.categories.publicSafety'),
icon: <Construction size={20} className="text-orange-600" />,
items: [
{ id: 'construction-safety', label: t('home.issues.constructionSafety'), icon: <HardHat size={24} />, color: 'text-orange-600', bg: 'bg-orange-50' },
{ id: 'playground-damage', label: t('home.issues.playgroundDamage'), icon: <Trophy size={24} />, color: 'text-green-600', bg: 'bg-green-50' },
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/views/Home.jsx` around lines 107 - 111, The Public Safety block
in Home.jsx uses hard-coded label strings and a non-pattern title; update the
i18n usage so the title and item labels use translation keys instead of
literals. Replace title: t('Public Safety') with a namespaced key such as
t('publicSafety.title') and change the item labels for ids 'construction-safety'
and 'playground-damage' to use t('publicSafety.constructionSafety') and
t('publicSafety.playgroundDamage') (keep the id, icon, color and bg fields
unchanged) so the block follows the existing i18n key pattern.

Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

1 issue found across 3 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="backend/voice_service.py">

<violation number="1" location="backend/voice_service.py:233">
P2: When `source_language` is `'auto'`, the response now reports `'auto'` instead of the detected language, so clients lose accurate source-language info. Detect the language for the response when auto mode is used.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

- Updated `render-build.sh` to use `--no-cache-dir` to prevent OOM errors and removed redundant frontend build steps for the backend service.
- Updated `render.yaml` to ensure it uses `./render-build.sh` as the build command.
- Re-verified and fixed `backend/requirements-render.txt` to include `deep-translator` (replacing `googletrans`), `pywebpush`, `SpeechRecognition`, `pydub`, and `langdetect` to resolve runtime import errors and dependency conflicts.
@github-actions
Copy link

🔍 Quality Reminder

Thanks for the updates! Please ensure:
- Your changes don't break existing functionality
- All tests still pass
- Code quality standards are maintained

*The maintainers will verify that the overall project flow remains intact.*

Copy link
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 adds two new AI-powered detection features for construction safety and playground damage using the Hugging Face CLIP model. The implementation follows the existing detector pattern in the codebase, leveraging zero-shot image classification to identify safety violations and equipment damage without requiring heavy local models.

Changes:

  • Added backend detection endpoints and CLIP-based detection functions for construction safety and playground damage
  • Implemented frontend detector components with camera capture and real-time detection UI
  • Added comprehensive unit tests with mocked HF API responses
  • Integrated new features into the Home view with a "Public Safety" category
  • Included verification script for manual frontend testing

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 12 comments.

Show a summary per file
File Description
backend/hf_api_service.py Added detect_construction_safety_clip and detect_playground_damage_clip functions using the _detect_clip_generic helper pattern
backend/routers/detection.py Added /api/detect-construction-safety and /api/detect-playground-damage endpoints with caching support
backend/tests/test_new_detectors.py Added unit tests for both new endpoints with HF API mocking
frontend/src/ConstructionSafetyDetector.jsx New detector component for construction safety violations with camera capture and detection UI
frontend/src/PlaygroundDetector.jsx New detector component for playground equipment damage with camera capture and detection UI
frontend/src/views/Home.jsx Added "Public Safety" category section with links to new detectors and Camera Diagnostics Modal
frontend/src/App.jsx Added routes for new detectors and updated validViews list
frontend/src/api/detectors.js Added API client methods for new detection endpoints
frontend_verification/verify_detectors.py Playwright-based verification script for manual frontend testing
frontend_verification/playground_damage.png Screenshot artifact from verification (binary file)

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

Comment on lines 34 to 47
Copy link

Copilot AI Feb 27, 2026

Choose a reason for hiding this comment

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

The comments on lines 35-47 are outdated implementation notes about debugging the test setup. These verbose comments explain the thought process for fixing a previous issue but are no longer relevant since the solution has been implemented. Consider removing or condensing these comments to just explain the final approach: "Mock create_all_ai_services from ai_factory before importing main to avoid initializing AI services during tests."

Copilot uses AI. Check for mistakes.
Comment on lines 107 to 111
Copy link

Copilot AI Feb 27, 2026

Choose a reason for hiding this comment

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

The new "Public Safety" section uses hardcoded English strings instead of internationalization (i18n) keys. All other categories use t() function for translations (e.g., line 79: t('home.categories.roadTraffic')). For consistency, the title should use t('home.categories.publicSafety') and the labels should use t('home.issues.constructionSafety') and t('home.issues.playgroundDamage') with corresponding entries added to the locale files.

Suggested change
title: t('home.categories.publicSafety'),
icon: <Construction size={20} className="text-orange-600" />,
items: [
{ id: 'construction-safety', label: t('home.issues.constructionSafety'), icon: <HardHat size={24} />, color: 'text-orange-600', bg: 'bg-orange-50' },
{ id: 'playground-damage', label: t('home.issues.playgroundDamage'), icon: <Trophy size={24} />, color: 'text-green-600', bg: 'bg-green-50' },

Copilot uses AI. Check for mistakes.
Copy link

@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

🧹 Nitpick comments (1)
backend/voice_service.py (1)

225-227: Redundant ternary expression.

Line 227 is effectively a no-op: source_language if source_language != 'auto' else 'auto' always returns source_language.

Simplify the assignment
-            # Use deep-translator (synchronous, robust)
-            # Handle 'auto' source
-            src_lang = source_language if source_language != 'auto' else 'auto'
+            # Use deep-translator (synchronous, robust)
+            src_lang = source_language
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/voice_service.py` around lines 225 - 227, The assignment to src_lang
in voice_service.py is redundant: replace the ternary expression
"source_language if source_language != 'auto' else 'auto'" with a direct
assignment of source_language (i.e., set src_lang = source_language) to simplify
the code and remove the no-op conditional that references source_language and
src_lang.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@backend/voice_service.py`:
- Around line 231-243: When source_language == 'auto' we currently return 'auto'
and lose the real detected language; before calling the translator (or
immediately after receiving translated), run langdetect.detect(text) (handle
exceptions and fallback to 'unknown' or original source_language) and assign it
to detected_src, use detected_src for source_language and source_language_name
(via SUPPORTED_LANGUAGES.get(detected_src, 'Unknown')), and update logger.info
to log detected_src (replace src_lang) so the returned dict contains the actual
detected language instead of 'auto'.

---

Nitpick comments:
In `@backend/voice_service.py`:
- Around line 225-227: The assignment to src_lang in voice_service.py is
redundant: replace the ternary expression "source_language if source_language !=
'auto' else 'auto'" with a direct assignment of source_language (i.e., set
src_lang = source_language) to simplify the code and remove the no-op
conditional that references source_language and src_lang.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c1c591b and e1295b0.

📒 Files selected for processing (3)
  • backend/requirements-render.txt
  • backend/requirements.txt
  • backend/voice_service.py

Comment on lines 231 to 243
# deep-translator doesn't return source language detection in the same way,
# so we trust the input or what we detected earlier
detected_src = source_language

logger.info(f"Successfully translated text: {translation.src} -> {translation.dest}")
logger.info(f"Successfully translated text: {src_lang} -> {target_language}")

return {
'translated_text': translation.text,
'source_language': translation.src,
'source_language_name': SUPPORTED_LANGUAGES.get(translation.src, 'Unknown'),
'target_language': translation.dest,
'target_language_name': SUPPORTED_LANGUAGES.get(translation.dest, 'Unknown'),
'translated_text': translated,
'source_language': detected_src,
'source_language_name': SUPPORTED_LANGUAGES.get(detected_src, 'Unknown'),
'target_language': target_language,
'target_language_name': SUPPORTED_LANGUAGES.get(target_language, 'Unknown'),
'original_text': text,
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🌐 Web query:

deep-translator GoogleTranslator source auto detected language

💡 Result:

In deep-translator (Python), GoogleTranslator supports automatic source-language detection—just set source="auto".

from deep_translator import GoogleTranslator

text = "bonjour la vie"
translated = GoogleTranslator(source="auto", target="en").translate(text)
print(translated)

This uses Google Translate’s auto-detect for the source language. [1]

If you instead want to detect the language without translating, deep-translator provides single_detection / batch_detection, but they require your own API key (from detectlanguage.com). [1]

from deep_translator import single_detection

lang = single_detection("bonjour la vie", api_key="YOUR_KEY")
print(lang)  # e.g., "fr"

Note: GoogleTranslator’s source default is "auto" (so you can omit it if you want auto-detect). [2]

Sources: [1] deep-translator docs, README (Language Detection + GoogleTranslator source='auto') [2] deep-translator GoogleTranslator reference (default source="auto")


🏁 Script executed:

head -50 backend/voice_service.py | cat -n

Repository: RohanExploit/VishwaGuru

Length of output: 1712


🏁 Script executed:

wc -l backend/voice_service.py

Repository: RohanExploit/VishwaGuru

Length of output: 95


🏁 Script executed:

sed -n '1,20p' backend/voice_service.py | cat -n

Repository: RohanExploit/VishwaGuru

Length of output: 776


🏁 Script executed:

sed -n '220,250p' backend/voice_service.py | cat -n

Repository: RohanExploit/VishwaGuru

Length of output: 1678


Source language detection is lost when source_language='auto'.

When the caller passes source_language='auto', deep-translator's GoogleTranslator performs auto-detection internally but doesn't expose the detected language code. The response will return source_language='auto' and source_language_name='Unknown', losing the information about which language was actually detected.

Since langdetect is already imported, detect the source language before translation when 'auto' is specified:

Use langdetect for source language detection
             # Use deep-translator (synchronous, robust)
-            # Handle 'auto' source
-            src_lang = source_language if source_language != 'auto' else 'auto'
+            src_lang = source_language
+            detected_src = source_language
+            
+            # Detect source language if 'auto' is specified
+            if source_language == 'auto':
+                try:
+                    detected_src = detect(text)
+                except LangDetectException:
+                    detected_src = 'auto'  # Fallback if detection fails
             
             translated = GoogleTranslator(source=src_lang, target=target_language).translate(text)
             
-            # deep-translator doesn't return source language detection in the same way,
-            # so we trust the input or what we detected earlier
-            detected_src = source_language
-            
             logger.info(f"Successfully translated text: {src_lang} -> {target_language}")
             
             return {
                 'translated_text': translated,
                 'source_language': detected_src,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/voice_service.py` around lines 231 - 243, When source_language ==
'auto' we currently return 'auto' and lose the real detected language; before
calling the translator (or immediately after receiving translated), run
langdetect.detect(text) (handle exceptions and fallback to 'unknown' or original
source_language) and assign it to detected_src, use detected_src for
source_language and source_language_name (via
SUPPORTED_LANGUAGES.get(detected_src, 'Unknown')), and update logger.info to log
detected_src (replace src_lang) so the returned dict contains the actual
detected language instead of 'auto'.

- Updated `render.yaml` to explicitly run `chmod +x ./render-build.sh` before executing it.
- This resolves potential "Permission denied" errors during the Render build process.
Copy link
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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants