-
Notifications
You must be signed in to change notification settings - Fork 0
Risk engine & zone frame fix #11
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,12 +1,12 @@ | ||
| { | ||
| "env": { | ||
| "ANTHROPIC_API_KEY": "aero_live_XUjsoGcPnyw0JJ-DtO1ZfHGdoORLaO4LxR808wU2s7k", | ||
| "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'" | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -14,7 +14,7 @@ public class RiskConfig | |
| [nameof(AiEventType.Dwell)] = 20, // max contribution; scaled by dwell duration | ||
| [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. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win Update the concealment evidence description. A 🤖 Prompt for AI Agents |
||
| [nameof(AiEventType.ExitWithoutCheckout)] = 20, | ||
| [nameof(AiEventType.ShelfPickupNoCheckout)] = 25, | ||
| [nameof(AiEventType.BlindSpotMovement)] = 15, | ||
|
|
@@ -36,7 +36,7 @@ public class RiskConfig | |
| public int MediumBand { get; set; } = 70; // >=70 medium alert | ||
| public int HighBand { get; set; } = 90; // >=90 high alert | ||
|
|
||
| public const string RuleVersion = "v4-starter-1.1"; | ||
| public const string RuleVersion = "v4-starter-1.2"; | ||
|
|
||
| public bool IsLowStaffHour(int hour) | ||
| { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -23,6 +23,11 @@ | |
| BLIND_SPOT = "BlindSpot" | ||
| SHELF_LIKE = {SHELF, HIGH_VALUE} | ||
|
|
||
| # Bag/concealment cues must be seen on at least this many detections before they can | ||
| # fire BagOpen / Concealment / count toward HighValueActivity. Rejects one-off false | ||
| # positives (e.g. a normal shopper adjusting a jacket for a few frames). | ||
| SUSTAINED_CUE_DETECTIONS = 10 | ||
|
|
||
|
|
||
| def extract_events(fps: float, frames: list[list[Detection]], zones: list[Zone]) -> list[dict]: | ||
| now = datetime.now(timezone.utc).isoformat() | ||
|
|
@@ -38,9 +43,11 @@ def extract_events(fps: float, frames: list[list[Detection]], zones: list[Zone]) | |
| open_bag_zone: str | None = None | ||
| open_bag_conf = 0.0 | ||
| open_bag_last_idx = -1 | ||
| open_bag_dets = 0 | ||
| bag_fallback_zone: str | None = None | ||
| bag_fallback_conf = 0.0 | ||
| bag_last_idx = -1 | ||
| bag_dets = 0 | ||
|
|
||
| # Product-in-hand handling episodes + first occurrence (for concealment ordering). | ||
| handling_episodes = 0 | ||
|
|
@@ -49,6 +56,12 @@ def extract_events(fps: float, frames: list[list[Detection]], zones: list[Zone]) | |
| product_first_idx = -1 | ||
| product_in_shelf = False | ||
|
|
||
| # Direct concealment cue (item being hidden inside jacket/clothing). | ||
| conceal_dets = 0 | ||
| conceal_zone: str | None = None | ||
| conceal_last_idx = -1 | ||
| conceal_in_hv = False | ||
|
|
||
| # High-value zone activity categories. | ||
| hv_has_person = hv_has_product = hv_has_bag = False | ||
| hv_zone_id: str | None = None | ||
|
|
@@ -81,13 +94,15 @@ def extract_events(fps: float, frames: list[list[Detection]], zones: list[Zone]) | |
| hv_zone_id = hv_zone_id or hv_zone.id | ||
|
|
||
| elif d.cue == "open_bag": | ||
| 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 | ||
|
Comment on lines
+97
to
108
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win Use sustained cues and actual last-seen indices for ordering.
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 |
||
|
|
@@ -104,6 +119,14 @@ def extract_events(fps: float, frames: list[list[Detection]], zones: list[Zone]) | |
| if hv_zone is not None: | ||
| hv_has_product = True | ||
|
|
||
| elif d.cue == "concealment": | ||
| conceal_dets += 1 | ||
| conceal_last_idx = idx | ||
| if shelf_zones: | ||
| conceal_zone = conceal_zone or shelf_zones[0].id | ||
| if hv_zone is not None: | ||
| conceal_in_hv = True | ||
|
|
||
| if handling_now and not prev_handling: | ||
| handling_episodes += 1 | ||
| prev_handling = handling_now | ||
|
|
@@ -176,6 +199,17 @@ def extract_events(fps: float, frames: list[list[Detection]], zones: list[Zone]) | |
| blind_spot_zone = blind_spot_zone or blindspot_zone_id | ||
|
|
||
| # --- Emit events (all evidence is observable-signal language, never conclusions) --- | ||
| # Gate bag/concealment cues on sustained detection counts so a handful of | ||
| # one-off detections (jacket adjust, misfire) cannot drive BagOpen/Concealment. | ||
| open_bag_sustained = open_bag_dets >= SUSTAINED_CUE_DETECTIONS | ||
| bag_sustained = bag_dets >= SUSTAINED_CUE_DETECTIONS | ||
| if not open_bag_sustained: | ||
| open_bag_zone = None | ||
| if not bag_sustained: | ||
| bag_fallback_zone = None | ||
| if not (open_bag_sustained or bag_sustained): | ||
| hv_has_bag = False | ||
|
|
||
| if high_value_seen_zone is not None: | ||
| events.append(_ev("HighValueZoneEntry", high_value_seen_zone, 1.0, 0.9, now)) | ||
|
|
||
|
|
@@ -187,10 +221,18 @@ def extract_events(fps: float, frames: list[list[Detection]], zones: list[Zone]) | |
| elif max_reentries > 0: | ||
| events.append(_ev("RepeatedHandling", reentry_zone, float(max_reentries), 0.7, now)) | ||
|
|
||
| # Concealment: item handled at a shelf, then a bag/open-bag cue appears afterwards. | ||
| # Concealment requires evidence of an ITEM being hidden — a product in hand alone, | ||
| # or an open bag alone, is never concealment. Two valid paths: | ||
| # A) product handled at a shelf, THEN a bag/open-bag cue appears afterwards | ||
| # (item picked up -> moved into a bag), or | ||
| # B) a sustained direct concealment cue ("hiding item inside jacket" style) — | ||
| # the detection itself encodes item-being-hidden. | ||
| 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)) | ||
|
Comment on lines
230
to
236
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 1. Concealment bypasses sustained gate 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
|
||
|
|
||
| # Exit without checkout, and its stronger "carried a product out" variant. | ||
|
|
@@ -216,6 +258,10 @@ def extract_events(fps: float, frames: list[list[Detection]], zones: list[Zone]) | |
| if max_group >= 2: | ||
| events.append(_ev("GroupDistraction", group_zone, float(max_group), 0.7, now)) | ||
|
|
||
| # 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 | ||
|
|
||
|
Comment on lines
+261
to
+264
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win Require sustained concealment within the high-value zone.
🤖 Prompt for AI Agents |
||
| hv_activity = int(hv_has_person) + int(hv_has_product) + int(hv_has_bag) | ||
| if hv_activity >= 2: | ||
| events.append(_ev("HighValueActivity", hv_zone_id, float(hv_activity), 0.8, now)) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,6 @@ | ||
| { | ||
| "schema_version": "1.0", | ||
| "description": "Ground-truth labels for ONEVO Phase 1A camera evaluation. Source: Shoplifting Dataset (2022) - CV Laboratory MNNIT Allahabad. label: 'shoplifting' (positive) or 'normal' (negative).", | ||
| "description": "Ground-truth labels for ONEVO Phase 1A camera evaluation. Custom local test videos. label: 'shoplifting' (positive) or 'normal' (negative).", | ||
| "alert_threshold": 70, | ||
| "default_zone": { | ||
| "name": "Whole Frame (High Value)", | ||
|
|
@@ -26,24 +26,29 @@ | |
| }, | ||
| "clips": [ | ||
| { | ||
| "id": "normal_001", | ||
| "file": "clips/normal_001.mp4", | ||
| "label": "normal" | ||
| "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" | ||
|
Comment on lines
+29
to
51
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🗄️ 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:
💡 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:
🏁 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
🤖 Prompt for AI Agents |
||
| } | ||
| ] | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🔒 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
Source: Linters/SAST tools