Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .jules/bolt.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,7 @@
## 2026-02-11 - Multi-Metric Aggregate Queries
**Learning:** Executing multiple separate `count()` queries to gather system statistics results in multiple database round-trips and redundant table scans.
**Action:** Use a single SQLAlchemy query with `func.count()` and `func.sum(case(...))` to calculate all metrics in one go. This reduces network overhead and allows the database to perform calculations in a single pass.

## 2026-02-12 - Regex Caching and I/O Throttling in Priority Engine
**Learning:** Repeatedly calling `re.search` on string patterns and unthrottled `os.path.getmtime` calls in hot paths (like every `analyze` request) introduces measurable latency. Pre-compiling regexes and implementing a time-based (e.g., 5s) throttle for configuration reloads significantly improves throughput.
**Action:** Use `re.compile()` for static patterns and implement time-based throttling for any file-based configuration hot-reloading to avoid filesystem bottlenecks. Ensure the consumer (e.g., `PriorityEngine`) always triggers the reload check before checking cache freshness.
8 changes: 6 additions & 2 deletions backend/adaptive_weights.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ class AdaptiveWeights:
_instance = None
_weights = None
_last_loaded = 0
_last_check_time = 0

def __new__(cls):
if cls._instance is None:
Expand Down Expand Up @@ -40,8 +41,11 @@ def _load_weights(self):
self._weights = {}

def _check_reload(self):
# Optimization: Checking mtime is fast (stat call).
self._load_weights()
# Optimization: 5-second throttle to prevent excessive I/O in hot paths.
now = time.time()
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Mar 9, 2026

Choose a reason for hiding this comment

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

P2: Use a monotonic clock for throttle intervals; wall-clock adjustments can break reload timing.

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

<comment>Use a monotonic clock for throttle intervals; wall-clock adjustments can break reload timing.</comment>

<file context>
@@ -40,8 +41,11 @@ def _load_weights(self):
-        # Optimization: Checking mtime is fast (stat call).
-        self._load_weights()
+        # Optimization: 5-second throttle to prevent excessive I/O in hot paths.
+        now = time.time()
+        if now - self._last_check_time > 5:
+            self._last_check_time = now
</file context>
Suggested change
now = time.time()
now = time.monotonic()
Fix with Cubic

Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

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

For interval-based throttling, time.monotonic() is safer than time.time() because it won’t be affected by system clock adjustments (NTP, manual changes). Using monotonic time avoids unexpected skipped reloads or burst reloads if the wall clock jumps.

Suggested change
now = time.time()
now = time.monotonic()

Copilot uses AI. Check for mistakes.
if now - self._last_check_time > 5:
self._last_check_time = now
self._load_weights()

def _save_weights(self):
try:
Expand Down
31 changes: 26 additions & 5 deletions backend/priority_engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,26 @@ class PriorityEngine:
def __init__(self):
# We no longer hardcode values here.
# They are fetched dynamically from AdaptiveWeights on each analysis.
pass
self._regex_cache = []
self._last_loaded_time = 0

def _get_compiled_patterns(self):
"""
Retrieves pre-compiled regex patterns for urgency calculations.
Invalidates cache if adaptive weights have been updated.
"""
# Ensure latest weights are at least checked for reload (subject to throttling)
adaptive_weights._check_reload()

current_load_time = adaptive_weights._last_loaded
if not self._regex_cache or current_load_time > self._last_loaded_time:
urgency_patterns = adaptive_weights.get_urgency_patterns()
self._regex_cache = [
(re.compile(pattern, re.IGNORECASE), weight)
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

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

re.IGNORECASE is likely redundant here because analyze() lowercases the input text before calling _calculate_urgency(). Dropping the flag avoids extra regex work and makes matching behavior depend only on the normalized text.

Suggested change
(re.compile(pattern, re.IGNORECASE), weight)
(re.compile(pattern), weight)

Copilot uses AI. Check for mistakes.
for pattern, weight in urgency_patterns
]
self._last_loaded_time = current_load_time
Comment on lines +22 to +34
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

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

PriorityEngine is reaching into adaptive_weights internals (_check_reload() and _last_loaded). This tight coupling makes future refactors of AdaptiveWeights risky and bypasses its public API. Consider adding a small public method (e.g., get_urgency_patterns_compiled() or get_weights_version()/last_loaded()) so cache invalidation doesn’t depend on private members.

Suggested change
Invalidates cache if adaptive weights have been updated.
"""
# Ensure latest weights are at least checked for reload (subject to throttling)
adaptive_weights._check_reload()
current_load_time = adaptive_weights._last_loaded
if not self._regex_cache or current_load_time > self._last_loaded_time:
urgency_patterns = adaptive_weights.get_urgency_patterns()
self._regex_cache = [
(re.compile(pattern, re.IGNORECASE), weight)
for pattern, weight in urgency_patterns
]
self._last_loaded_time = current_load_time
This implementation only relies on the public AdaptiveWeights API
and does not access its private internals, to avoid tight coupling.
"""
urgency_patterns = adaptive_weights.get_urgency_patterns()
self._regex_cache = [
(re.compile(pattern, re.IGNORECASE), weight)
for pattern, weight in urgency_patterns
]

Copilot uses AI. Check for mistakes.
return self._regex_cache

def analyze(self, text: str, image_labels: Optional[List[str]] = None) -> Dict[str, Any]:
"""
Expand Down Expand Up @@ -116,13 +135,15 @@ def _calculate_urgency(self, text: str, severity_score: int):
urgency = severity_score
reasons = []

urgency_patterns = adaptive_weights.get_urgency_patterns()
# Optimization: Use pre-compiled regex patterns to improve performance
compiled_patterns = self._get_compiled_patterns()

# Apply regex modifiers
for pattern, weight in urgency_patterns:
if re.search(pattern, text):
for pattern_obj, weight in compiled_patterns:
match = pattern_obj.search(text)
if match:
urgency += weight
reasons.append(f"Urgency increased by context matching pattern: '{pattern}'")
reasons.append(f"Urgency increased by context matching pattern: '{pattern_obj.pattern}'")

# Cap at 100
urgency = min(100, urgency)
Expand Down
3 changes: 3 additions & 0 deletions backend/requirements-render.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,12 @@ a2wsgi
python-jose[cryptography]
passlib[bcrypt]
bcrypt<4.0.0
python-dotenv
SpeechRecognition
pydub
googletrans==4.0.2
langdetect
async_lru
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

🏁 Script executed:

#!/bin/bash
# Verify whether async_lru is actually imported or referenced anywhere in the backend.
rg -nP --type=py -C2 '^\s*(from\s+async_lru\s+import|import\s+async_lru\b|@alru_cache\b|alru_cache\s*\()' backend

# Show the deployment-related comments that currently claim async_lru was removed/replaced.
rg -n -C2 'async[-_ ]lru|manual cache|avoid .*Render|replace async_lru' backend

Repository: RohanExploit/VishwaGuru

Length of output: 1494


Remove async_lru from the Render manifest—it's not imported anywhere in the backend.

backend/routers/detection.py line 49 and backend/gemini_summary.py line 20 both document that the backend switched to manual caches specifically to avoid the async_lru dependency on Render. The rg search confirms async_lru is never imported or used in any backend file, making this an unused deployment-time dependency that contradicts the stated workaround.

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

In `@backend/requirements-render.txt` at line 23, Remove the unused deployment
dependency "async_lru" from requirements-render.txt: delete the "async_lru"
entry from that manifest file, verify there are no remaining imports/usages of
async_lru in the backend codebase (the review already notes
backend/routers/detection.py and backend/gemini_summary.py switched to manual
caches), and run the test/deploy validation to ensure no regression from
removing the package.

indic-nlp-library
numpy
scikit-learn
19 changes: 13 additions & 6 deletions backend_output.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
INFO: Will watch for changes in these directories: ['E:\\projects\\VishwaGuru\\VishwaGuru']
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO: Started reloader process [42100] using StatReload
2026-03-09 02:00:56,311 - backend.adaptive_weights - INFO - Adaptive weights loaded/reloaded.
WARNING: StatReload detected changes in 'backend\main.py'. Reloading...

2026-03-09 15:06:36,764 - backend.adaptive_weights - INFO - Adaptive weights loaded/reloaded.
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Mar 9, 2026

Choose a reason for hiding this comment

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

P3: This change commits nondeterministic runtime log output (timestamps/PIDs), which creates noisy diffs and makes meaningful changes harder to review. Prefer removing this artifact from source control or normalizing dynamic fields in the tracked output.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At backend_output.txt, line 1:

<comment>This change commits nondeterministic runtime log output (timestamps/PIDs), which creates noisy diffs and makes meaningful changes harder to review. Prefer removing this artifact from source control or normalizing dynamic fields in the tracked output.</comment>

<file context>
@@ -1,13 +1,13 @@
-2026-03-09 14:34:15,761 - backend.adaptive_weights - INFO - Adaptive weights loaded/reloaded.
-2026-03-09 14:34:15,793 - backend.rag_service - INFO - Loaded 5 civic policies for RAG.
+2026-03-09 15:06:36,764 - backend.adaptive_weights - INFO - Adaptive weights loaded/reloaded.
+2026-03-09 15:06:36,834 - backend.rag_service - INFO - Loaded 5 civic policies for RAG.
 /home/jules/.pyenv/versions/3.12.12/lib/python3.12/site-packages/pydub/utils.py:170: RuntimeWarning: Couldn't find ffmpeg or avconv - defaulting to ffmpeg, but may not work
</file context>
Fix with Cubic

2026-03-09 15:06:36,834 - backend.rag_service - INFO - Loaded 5 civic policies for RAG.
/home/jules/.pyenv/versions/3.12.12/lib/python3.12/site-packages/pydub/utils.py:170: RuntimeWarning: Couldn't find ffmpeg or avconv - defaulting to ffmpeg, but may not work
warn("Couldn't find ffmpeg or avconv - defaulting to ffmpeg, but may not work", RuntimeWarning)
2026-03-09 15:06:37,177 - backend.main - WARNING - FRONTEND_URL not set. Defaulting to http://localhost:5173 for development.
INFO: Started server process [9775]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on http://0.0.0.0:10000 (Press CTRL+C to quit)
INFO: Shutting down
INFO: Waiting for application shutdown.
INFO: Application shutdown complete.
INFO: Finished server process [9775]
29 changes: 29 additions & 0 deletions benchmark_priority.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import time
import sys
import os
import re

# Add project root to sys.path
sys.path.append(os.getcwd())
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Mar 9, 2026

Choose a reason for hiding this comment

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

P2: Using os.getcwd() for import path setup is brittle; resolve the path from the script location so imports work regardless of launch directory.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At benchmark_priority.py, line 7:

<comment>Using `os.getcwd()` for import path setup is brittle; resolve the path from the script location so imports work regardless of launch directory.</comment>

<file context>
@@ -0,0 +1,29 @@
+import re
+
+# Add project root to sys.path
+sys.path.append(os.getcwd())
+
+from backend.priority_engine import priority_engine
</file context>
Fix with Cubic


from backend.priority_engine import priority_engine
from backend.adaptive_weights import adaptive_weights
Comment on lines +4 to +10
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

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

re and adaptive_weights are imported but unused in this benchmark script. Removing unused imports keeps the benchmark minimal and avoids confusion about what’s being measured.

Suggested change
import re
# Add project root to sys.path
sys.path.append(os.getcwd())
from backend.priority_engine import priority_engine
from backend.adaptive_weights import adaptive_weights
# Add project root to sys.path
sys.path.append(os.getcwd())
from backend.priority_engine import priority_engine

Copilot uses AI. Check for mistakes.

def benchmark(iterations=1000):
text = "Immediate help needed! There is a fire near the hospital and a child is trapped."

# Warm up
priority_engine.analyze(text)

start = time.time()
for _ in range(iterations):
priority_engine.analyze(text)
end = time.time()

print(f"Iterations: {iterations}")
print(f"Total time: {end - start:.4f} seconds")
print(f"Average time per analysis: {(end - start) / iterations * 1000:.4f} ms")

if __name__ == "__main__":
print("Running benchmark...")
benchmark()
Loading