Risk engine & zone frame fix - #11
Conversation
📝 WalkthroughWalkthroughThe PR adds concealment detection and sustained evidence gating, updates risk scoring and local evaluations, improves single-source snapshot fallback, and renders setup snapshots directly on the zone editor canvas with refresh and error handling. ChangesConcealment detection and evaluation
Snapshot handling and zone editor rendering
Claude credential configuration
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant YOLOEDetector
participant extract_events
participant RiskEngine
participant EvaluationScript
YOLOEDetector->>extract_events: Detect concealment and bag cues
extract_events->>RiskEngine: Emit scored concealment events
RiskEngine->>EvaluationScript: Return event score
EvaluationScript->>EvaluationScript: Classify each clip against alert threshold
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 2📝 Generate docstrings 💡
🛠️ Fix failing CI checks 💡
🧪 Generate unit tests (beta)
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. Comment |
PR Summary by QodoStrengthen concealment signal and fix setup snapshot/zone framing
AI Description
Diagram
High-Level Assessment
Files changed (11)
|
Code Review by Qodo
1. Concealment bypasses sustained gate
|
| latest_bag_idx = max(open_bag_last_idx, bag_last_idx) | ||
| concealment_zone = open_bag_zone or bag_fallback_zone or handling_zone | ||
| if product_in_shelf and product_first_idx >= 0 and latest_bag_idx > product_first_idx: | ||
| conceal_sustained = conceal_dets >= SUSTAINED_CUE_DETECTIONS | ||
| handled_then_bag = product_in_shelf and product_first_idx >= 0 and latest_bag_idx > product_first_idx | ||
| if handled_then_bag or conceal_sustained: | ||
| concealment_zone = (conceal_zone if conceal_sustained else None) \ | ||
| or open_bag_zone or bag_fallback_zone or handling_zone | ||
| events.append(_ev("Concealment", concealment_zone, 1.0, 0.7, now)) |
There was a problem hiding this comment.
1. Concealment bypasses sustained gate 🐞 Bug ≡ Correctness
In extract_events(), BagOpen/HighValueActivity are gated by sustained bag/open_bag counts, but the “handled_then_bag” concealment path still uses ungated open_bag_last_idx/bag_last_idx. This can emit a Concealment event from a single transient bag/open_bag detection after product_first_idx, contradicting the new false-positive suppression intent.
Agent Prompt
### Issue description
`extract_events()` introduced sustained gating for bag/open-bag cues, but `handled_then_bag` still keys off `open_bag_last_idx` / `bag_last_idx` even when the cue was not sustained. This allows one-off bag detections to trigger `Concealment`.
### Issue Context
The code explicitly claims sustained detections should be required to drive BagOpen/Concealment. The current implementation only nulls `open_bag_zone` / `bag_fallback_zone`, not the indexes used by concealment ordering.
### Fix Focus Areas
- cloud-ai/app/events.py[201-236]
### Suggested fix
- Compute a **gated** latest bag index, e.g.:
- `latest_bag_idx = max(open_bag_last_idx if open_bag_sustained else -1, bag_last_idx if bag_sustained else -1)`
- and use that for `handled_then_bag`.
- Alternatively, if `not open_bag_sustained`, also reset `open_bag_last_idx = -1` (same for `bag_last_idx`).
- Keep the zone selection consistent with the gated path (i.e., only allow bag-based concealment if the corresponding sustained flag is true).
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
| JACKET_PROMPTS = { | ||
| **DEFAULT_YOLOE_PROMPTS, | ||
| # Clothing-concealment cues (proxy onto open_bag so Concealment/BagOpen logic fires). | ||
| "person hiding item inside jacket": "open_bag", | ||
| "person putting object under clothing": "open_bag", | ||
| "hand inside jacket": "open_bag", | ||
| # Extra holding phrasings for small items / clothing items. |
There was a problem hiding this comment.
5. Jacket eval prompt inconsistency 🐞 Bug ⚙ Maintainability
The evaluation script run_jacket_test.py overrides DEFAULT_YOLOE_PROMPTS by mapping the new jacket concealment phrases to open_bag, even though production DEFAULT_YOLOE_PROMPTS now maps them to the dedicated concealment cue. This makes results_jacket_prompts.json evaluate a different cue mapping than the production pipeline, potentially invalidating comparisons.
Agent Prompt
### Issue description
The jacket eval script’s prompt map conflicts with the canonical production prompt map by remapping jacket concealment phrases away from `concealment`.
### Issue Context
This doesn’t change production behavior, but it can mislead anyone using the eval artifacts to validate the new concealment cue.
### Fix Focus Areas
- cloud-ai/app/detector.py[29-46]
- cloud-ai/eval/run_jacket_test.py[21-30]
### Suggested fix
- Either:
- Map the jacket phrases to `concealment` in `JACKET_PROMPTS` (preferred for evaluating the new cue), and regenerate `results_jacket_prompts.json`, or
- Keep the override but rename the script/artifact and document clearly that it is a **legacy proxy-to-open_bag** comparison, not representative of production mappings.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
There was a problem hiding this comment.
Actionable comments posted: 10
🤖 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 @.claude/settings.json:
- Around line 3-11: Remove the hard-coded credential from the settings
environment block and the apiKeyHelper command, then rotate or revoke the
exposed key and purge both values from repository history. Update credential
loading to use the existing secret manager or a local environment variable
without storing plaintext secrets in .claude/settings.json.
In `@backend/Services/RiskEngine.cs`:
- Line 17: Update the evidence description for the Concealment entry in the
RiskEngine mapping to describe both supported paths: direct clothing/jacket
concealment and bag or open-bag concealment following shelf handling. Keep the
description operator-facing and auditable while preserving the existing score
and mapping.
In `@cloud-ai/app/events.py`:
- Around line 261-264: Update the high-value-zone product inference in the
event-processing logic around conceal_sustained and conceal_in_hv so both
conditions are calculated from the same high-value-zone frame sequence. Track or
derive sustained concealment within the high-value zone rather than combining
clip-wide concealment counts with a single zone detection, then set
hv_has_product only when that zone-local threshold is met.
- Around line 97-108: Update the detection state around the open-bag and bag
handling branches, including the related handled_then_bag logic, to track each
cue’s latest eligible shelf-detection index separately from its
highest-confidence zone. Only record or use open_bag_last_idx and bag_last_idx
when the corresponding cue meets its sustained threshold; otherwise reset or
ignore the index so an unsustained post-handling detection cannot emit
Concealment.
In `@cloud-ai/eval/ground_truth.json`:
- Around line 29-51: Add the five video assets referenced by the ground-truth
entries shoplifting_1, shoplifting_2, normal_3, shoplifting_4, and
shoplifting_testvideo under the expected clips directory, or update the
evaluation setup to reliably provide or skip them while preserving those
referenced paths.
In `@cloud-ai/eval/results_jacket_prompts.json`:
- Around line 68-76: Regenerate the stale sustained-gating result for the
normal_3 case in the events data, ensuring its open_bag-derived BagOpen event
and resulting score reflect the checked-in extractor requirement of ten
detections; do not retain the impossible BagOpen=1.0 result with only four
detections.
In `@cloud-ai/eval/run_jacket_test.py`:
- Around line 21-30: Update JACKET_PROMPTS in
cloud-ai/eval/run_jacket_test.py:21-30 so the three jacket/clothing phrases use
the canonical concealment cue and revise the stale proxy comment. Regenerate
cloud-ai/eval/results_jacket_prompts.json:2-14 from the corrected prompt map;
both sites require updates.
In `@connector/app/admin.py`:
- Around line 422-425: Update the fallback in the request-handling logic around
the single-frame branch to determine single-source status from configured or
active source metadata, not len(state.last_frames). Prefer validating the
requested camera ID against a known camera-ID mapping before reusing a frame,
and otherwise avoid returning another source’s image when a multi-source
connector temporarily has one frame.
In `@dashboard/src/app/pages/setup/setup.component.ts`:
- Around line 507-510: Update the camera-loading callback around getCamera and
selectedCamera so connectorAdminHost is no longer assigned from cam.onvifHost.
Resolve and assign the connector admin address using the existing connector
registration or configuration source, ensuring loadSnapshot targets the
connector process rather than the camera IP.
- Around line 507-512: Update the camera-loading flow around getCamera,
listZones, and loadSnapshot so callbacks capture the current selection/request
generation and ignore results from older camera selections or snapshot requests.
Increment the generation when selecting or refreshing a camera, and validate it
before assigning selectedCamera, connectorAdminHost, zones, or applying image
callbacks, ensuring only the latest camera and snapshot state can update the
editor.
🪄 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 Plus
Run ID: bc07e0fa-2c6d-4995-a7fa-9ac86bb278f6
📒 Files selected for processing (11)
.claude/settings.jsonbackend/Services/RiskEngine.cscloud-ai/app/detector.pycloud-ai/app/events.pycloud-ai/eval/ground_truth.jsoncloud-ai/eval/results.jsoncloud-ai/eval/results_jacket_prompts.jsoncloud-ai/eval/run_eval.pycloud-ai/eval/run_jacket_test.pyconnector/app/admin.pydashboard/src/app/pages/setup/setup.component.ts
| "ANTHROPIC_API_KEY": "aero_live_qiFbk-Et6G9qRZaElOvjjHgOWIz_O_i6Fts6W9cioFU", | ||
| "ANTHROPIC_BASE_URL": "https://capi.aerolink.lat/", | ||
| "CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC": "1" | ||
| }, | ||
| "permissions": { | ||
| "allow": [], | ||
| "deny": [] | ||
| }, | ||
| "apiKeyHelper": "echo 'aero_live_XUjsoGcPnyw0JJ-DtO1ZfHGdoORLaO4LxR808wU2s7k'" | ||
| } | ||
| "apiKeyHelper": "echo 'aero_live_qiFbk-Et6G9qRZaElOvjjHgOWIz_O_i6Fts6W9cioFU'" |
There was a problem hiding this comment.
🔒 Security & Privacy | 🔴 Critical | ⚡ Quick win
Remove and rotate the hard-coded API credential.
This live-looking key is committed twice, including a helper that prints it directly. Revoke/rotate it immediately, remove both plaintext values from repository history, and load the credential from a secret manager or local environment variable instead.
🧰 Tools
🪛 Betterleaks (1.7.0)
[high] 3-3: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.
(generic-api-key)
🤖 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 @.claude/settings.json around lines 3 - 11, Remove the hard-coded credential
from the settings environment block and the apiKeyHelper command, then rotate or
revoke the exposed key and purge both values from repository history. Update
credential loading to use the existing secret manager or a local environment
variable without storing plaintext secrets in .claude/settings.json.
Source: Linters/SAST tools
| [nameof(AiEventType.RepeatedHandling)] = 15, | ||
| [nameof(AiEventType.BagOpen)] = 20, | ||
| [nameof(AiEventType.Concealment)] = 20, | ||
| [nameof(AiEventType.Concealment)] = 40, // directly observed act of hiding an item — strongest single camera signal |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Update the concealment evidence description.
A Concealment event may now originate from sustained direct clothing/jacket concealment, but the operator-facing evidence still claims a bag/open-bag followed shelf handling. Describe both supported paths so alerts remain auditable.
🤖 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 `@backend/Services/RiskEngine.cs` at line 17, Update the evidence description
for the Concealment entry in the RiskEngine mapping to describe both supported
paths: direct clothing/jacket concealment and bag or open-bag concealment
following shelf handling. Keep the description operator-facing and auditable
while preserving the existing score and mapping.
| open_bag_dets += 1 | ||
| for z in shelf_zones: | ||
| if d.conf >= open_bag_conf: | ||
| open_bag_zone, open_bag_conf, open_bag_last_idx = z.id, d.conf, idx | ||
| if hv_zone is not None: | ||
| hv_has_bag = True | ||
|
|
||
| elif d.cue == "bag": | ||
| bag_dets += 1 | ||
| for z in shelf_zones: | ||
| if d.conf >= bag_fallback_conf: | ||
| bag_fallback_zone, bag_fallback_conf, bag_last_idx = z.id, d.conf, idx |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Use sustained cues and actual last-seen indices for ordering.
open_bag_last_idx only updates when confidence increases, so it is not necessarily the latest bag observation. More critically, it remains valid when the cue is below the sustained threshold, allowing one post-handling bag detection to emit Concealment through handled_then_bag.
Track the last eligible shelf detection independently from the highest-confidence zone, and reset/ignore each index unless its cue is sustained.
Also applies to: 202-235
🤖 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 `@cloud-ai/app/events.py` around lines 97 - 108, Update the detection state
around the open-bag and bag handling branches, including the related
handled_then_bag logic, to track each cue’s latest eligible shelf-detection
index separately from its highest-confidence zone. Only record or use
open_bag_last_idx and bag_last_idx when the corresponding cue meets its
sustained threshold; otherwise reset or ignore the index so an unsustained
post-handling detection cannot emit Concealment.
| # A sustained concealment cue in a high-value zone implies an item is involved. | ||
| if conceal_sustained and conceal_in_hv: | ||
| hv_has_product = True | ||
|
|
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Require sustained concealment within the high-value zone.
conceal_sustained counts detections across the whole clip while conceal_in_hv only requires one high-value-zone detection. Ten detections elsewhere plus one incidental high-value detection will incorrectly credit high-value product activity. Count sustained concealment per high-value zone/frame sequence instead.
🤖 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 `@cloud-ai/app/events.py` around lines 261 - 264, Update the high-value-zone
product inference in the event-processing logic around conceal_sustained and
conceal_in_hv so both conditions are calculated from the same high-value-zone
frame sequence. Track or derive sustained concealment within the high-value zone
rather than combining clip-wide concealment counts with a single zone detection,
then set hv_has_product only when that zone-local threshold is met.
| "id": "shoplifting_1", | ||
| "file": "clips/1.mp4", | ||
| "label": "shoplifting" | ||
| }, | ||
| { | ||
| "id": "shoplifting_2", | ||
| "file": "clips/2.mp4", | ||
| "label": "shoplifting" | ||
| }, | ||
| { | ||
| "id": "normal_002", | ||
| "file": "clips/normal_002.mp4", | ||
| "id": "normal_3", | ||
| "file": "clips/3.mp4", | ||
| "label": "normal" | ||
| }, | ||
| { | ||
| "id": "shoplifting_001", | ||
| "file": "clips/shoplifting_001.mp4", | ||
| "id": "shoplifting_4", | ||
| "file": "clips/4.mp4", | ||
| "label": "shoplifting" | ||
| }, | ||
| { | ||
| "id": "shoplifting_002", | ||
| "file": "clips/shoplifting_002.mp4", | ||
| "id": "shoplifting_testvideo", | ||
| "file": "clips/testvideo.mp4", | ||
| "label": "shoplifting" |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
python - <<'PY'
import json
from pathlib import Path
manifest = Path("cloud-ai/eval/ground_truth.json")
for clip in json.loads(manifest.read_text(encoding="utf-8"))["clips"]:
path = manifest.parent / clip["file"]
print(f"{clip['id']}: {path}")
assert path.is_file(), f"Missing evaluation clip: {path}"
PYRepository: baasith6/camera-phase-1
Length of output: 342
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== candidate eval assets =="
if [ -d cloud-ai/eval ]; then
git ls-files cloud-ai/eval | sed -n '1,200p'
else
echo "cloud-ai/eval not tracked"
fi
echo
echo "== manifest contents =="
cat -n cloud-ai/eval/ground_truth.json | sed -n '1,120p'
echo
echo "== files named like expected clips in repo =="
git ls-files | rg '(^|/)clips/|1\.mp4$|2\.mp4$|3\.mp4$|4\.mp4$|testvideo\.mp4$' || trueRepository: baasith6/camera-phase-1
Length of output: 1896
🌐 Web query:
GitHub baasith6 camera-phase-1 cloud-ai/eval clips ground_truth.json
💡 Result:
The specific path "camera-phase-1/cloud-ai/eval/clips/ground_truth.json" does not appear to be a standard or publicly documented file path associated with the user "baasith6" on GitHub [1]. The GitHub user "baasith6" (Abdul Baasith) is a developer whose public repositories focus on full-stack development, including projects like "RepoGraph-Protocol," agricultural AI vision backends ("agri-ai-vision-back"), and payment system integrations [1][2][3]. A search of his public repositories does not reveal a project named "camera-phase-1" or the specific file structure requested. It is possible that this path refers to: 1. A private repository belonging to the user. 2. A locally organized project structure that has not been pushed to a public GitHub repository. 3. A misremembered file path or project name. While there are many repositories on GitHub related to "camera," "ground truth," and "evaluation" (often involving computer vision, SLAM, or AI training pipelines), they are not linked to the user "baasith6" [4][5][6][7][8]. If this is a file you expect to exist in a public repository, you may want to verify the repository name or check if the repository is private.
Citations:
- 1: https://github.com/baasith6
- 2: https://github.com/baasith6/RepoGraph-Protocol/blob/main/docs/quickstart.md
- 3: https://github.com/baasith6/Bpoint-Testing-Angular-.net/issues
- 4: https://github.com/anuragkj/ACM_Teach_OpenCV
- 5: Biswajit1999/Master-Thesis-2024@6a87446
- 6: https://github.com/rishikeshgithu/Camera-Based-attendance-system
- 7: https://github.com/ruvnet/RuView/blob/main/docs/adr/ADR-079-camera-ground-truth-training.md
- 8: https://github.com/vader-droid33/slam_eval/blob/main/README.md
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== eval scripts mentioning clips =="
rg -n "ground_truth|clips/|VideoCapture|cv2|ground_truth\\.json|results\\.json" cloud-ai/eval -
echo
echo "== gitignored files under cloud-ai/eval or matching clip names =="
if [ -f .gitignore ]; then
cat -n .gitignore | rg 'cloud-ai|eval|\\.mp4$|\\.MP4$|clips' || true
fi
echo
echo "== tracked all files under cloud-ai =="
git ls-files cloud-ai | sed -n '1,200p'Repository: baasith6/camera-phase-1
Length of output: 1157
Add the evaluation clip assets referenced by ground_truth.json.
cloud-ai/eval/ground_truth.json references cloud-ai/eval/clips/{1,2,3,4}.mp4 and cloud-ai/eval/clips/testvideo.mp4, but none of these files are present. The evaluation scripts depend on these paths, so add the clips to the repo or skip/bundle them through the evaluation setup.
🤖 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 `@cloud-ai/eval/ground_truth.json` around lines 29 - 51, Add the five video
assets referenced by the ground-truth entries shoplifting_1, shoplifting_2,
normal_3, shoplifting_4, and shoplifting_testvideo under the expected clips
directory, or update the evaluation setup to reliably provide or skip them while
preserving those referenced paths.
| "open_bag": 4 | ||
| }, | ||
| "events": [ | ||
| "HighValueZoneEntry=1.0", | ||
| "Dwell=32.0", | ||
| "RepeatedHandling=1.0", | ||
| "GroupDistraction=4.0", | ||
| "HighValueActivity=2.0", | ||
| "BagOpen=1.0" |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win
Regenerate this stale sustained-gating result.
normal_3 has only four open_bag detections, but still reports BagOpen=1.0. The current extractor requires ten detections and clears the bag zone otherwise, so this event—and the resulting score of 70—cannot be produced by the checked-in logic.
🤖 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 `@cloud-ai/eval/results_jacket_prompts.json` around lines 68 - 76, Regenerate
the stale sustained-gating result for the normal_3 case in the events data,
ensuring its open_bag-derived BagOpen event and resulting score reflect the
checked-in extractor requirement of ten detections; do not retain the impossible
BagOpen=1.0 result with only four detections.
| JACKET_PROMPTS = { | ||
| **DEFAULT_YOLOE_PROMPTS, | ||
| # Clothing-concealment cues (proxy onto open_bag so Concealment/BagOpen logic fires). | ||
| "person hiding item inside jacket": "open_bag", | ||
| "person putting object under clothing": "open_bag", | ||
| "hand inside jacket": "open_bag", | ||
| # Extra holding phrasings for small items / clothing items. | ||
| "person holding a bottle": "product_in_hand", | ||
| "person holding clothes": "product_in_hand", | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Keep jacket prompts on the canonical concealment cue. The jacket-specific map overrides the new detector behavior back to open_bag, so direct concealment is evaluated through the obsolete bag proxy instead of the new direct-evidence path.
cloud-ai/eval/run_jacket_test.py#L21-L30: map the three jacket/clothing phrases toconcealmentand update the stale proxy wording.cloud-ai/eval/results_jacket_prompts.json#L2-L14: regenerate the artifact from the corrected prompt map.
📍 Affects 2 files
cloud-ai/eval/run_jacket_test.py#L21-L30(this comment)cloud-ai/eval/results_jacket_prompts.json#L2-L14
🤖 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 `@cloud-ai/eval/run_jacket_test.py` around lines 21 - 30, Update JACKET_PROMPTS
in cloud-ai/eval/run_jacket_test.py:21-30 so the three jacket/clothing phrases
use the canonical concealment cue and revise the stale proxy comment. Regenerate
cloud-ai/eval/results_jacket_prompts.json:2-14 from the corrected prompt map;
both sites require updates.
| if not frame and len(state.last_frames) == 1: | ||
| # Single-source connector: tolerate a camera-id mismatch between the | ||
| # dashboard GUID and the locally configured source. | ||
| frame = next(iter(state.last_frames.values())) |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
Do not infer a single-source connector from last_frames.
A multi-source connector can temporarily contain only camera A’s first frame. A request for camera B then receives A’s image, allowing zones for B to be drawn and saved against the wrong scene. Restrict this fallback using configured/active source count (and preferably a known camera-ID mapping), not currently available frames.
🤖 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 `@connector/app/admin.py` around lines 422 - 425, Update the fallback in the
request-handling logic around the single-frame branch to determine single-source
status from configured or active source metadata, not len(state.last_frames).
Prefer validating the requested camera ID against a known camera-ID mapping
before reusing a frame, and otherwise avoid returning another source’s image
when a multi-source connector temporarily has one frame.
| this.api.getCamera(id).subscribe(cam => { | ||
| this.selectedCamera = cam; | ||
| if (cam.onvifHost) this.connectorAdminHost = cam.onvifHost; | ||
| this.loadSnapshot(); |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift
Do not use the ONVIF camera IP as the connector-admin host.
cam.onvifHost is the camera address, but /snapshot is served by the connector process on port 8099. This redirects snapshot requests to http://<camera-ip>:8099, where the connector endpoint is normally unavailable. Resolve the connector’s admin address from connector registration/configuration instead.
🤖 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 `@dashboard/src/app/pages/setup/setup.component.ts` around lines 507 - 510,
Update the camera-loading callback around getCamera and selectedCamera so
connectorAdminHost is no longer assigned from cam.onvifHost. Resolve and assign
the connector admin address using the existing connector registration or
configuration source, ensuring loadSnapshot targets the connector process rather
than the camera IP.
| this.api.getCamera(id).subscribe(cam => { | ||
| this.selectedCamera = cam; | ||
| if (cam.onvifHost) this.connectorAdminHost = cam.onvifHost; | ||
| this.loadSnapshot(); | ||
| }); | ||
| this.api.listZones(id).subscribe((z) => { this.zones = z; setTimeout(() => this.redraw()); }); |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
Ignore stale camera and snapshot callbacks.
Selecting A then B, or refreshing twice, permits an older getCamera, listZones, or image callback to overwrite B’s state. The editor can consequently persist B’s zones using A’s frame. Track a selection/request generation and apply each callback only when it still matches the current camera and latest snapshot request.
Also applies to: 524-533
🤖 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 `@dashboard/src/app/pages/setup/setup.component.ts` around lines 507 - 512,
Update the camera-loading flow around getCamera, listZones, and loadSnapshot so
callbacks capture the current selection/request generation and ignore results
from older camera selections or snapshot requests. Increment the generation when
selecting or refreshing a camera, and validate it before assigning
selectedCamera, connectorAdminHost, zones, or applying image callbacks, ensuring
only the latest camera and snapshot state can update the editor.
Summary by CodeRabbit
New Features
Improvements