barathvasan-dev commited on
Commit
a4acadb
·
1 Parent(s): be7f905

Update: Integrate Mistral-7B-Instruct-v0.2 model for NLP-to-SQL engine and add database functions

Browse files
Files changed (5) hide show
  1. app.py +322 -102
  2. app_old.py +373 -0
  3. database.py +644 -535
  4. packages.txt +6 -0
  5. requirements.txt +21 -6
app.py CHANGED
@@ -8,6 +8,7 @@ import pandas as pd
8
  import gradio as gr
9
 
10
  from detector import detect_plate
 
11
  from database import (
12
  save_detection,
13
  run_query,
@@ -18,15 +19,28 @@ from database import (
18
  get_suspicious_vehicles
19
  )
20
 
21
- asyncio.set_event_loop_policy(asyncio.DefaultEventLoopPolicy())
 
 
22
 
 
 
 
23
 
24
- # ================= DETECTION ================= #
 
 
25
 
26
  def detect_and_save(image):
27
 
28
  if image is None:
29
- return "No image uploaded", {}
 
 
 
 
 
 
30
 
31
  now = datetime.now()
32
 
@@ -38,6 +52,7 @@ def detect_and_save(image):
38
  plate, state, vehicle_type, vehicle_conf, success = detect_plate(image)
39
 
40
  if success and plate:
 
41
  save_detection(
42
  plate,
43
  state,
@@ -47,7 +62,20 @@ def detect_and_save(image):
47
  time
48
  )
49
 
50
- result_text = f"{date} {time} | {vehicle_type} | {plate}"
 
 
 
 
 
 
 
 
 
 
 
 
 
51
 
52
  result_json = {
53
  "date": date,
@@ -63,73 +91,208 @@ def detect_and_save(image):
63
 
64
  except Exception as e:
65
 
66
- return f"Error: {str(e)}", {
67
- "error": str(e)
68
- }
 
 
 
69
 
70
 
71
- # ================= QUERY ================= #
 
 
72
 
73
  def query_database(user_query):
74
 
75
  if not user_query.strip():
76
- return "", pd.DataFrame(), {
77
- "error": "Please enter a query"
78
- }
79
 
80
- response = run_query(user_query)
 
 
 
 
 
 
81
 
82
- sql = response.get("sql", "")
83
 
84
- results = response.get("result", [])
85
 
86
- # Convert JSON list -> DataFrame for proper table display
87
- if results:
88
- df = pd.DataFrame(results)
89
- else:
90
- df = pd.DataFrame()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
91
 
92
- return sql, df, response
93
 
94
 
95
- # ================= ANALYTICS ================= #
 
 
96
 
97
  def refresh_analytics():
98
 
99
- state_data = get_vehicles_by_state()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
100
 
101
- hourly_data = get_hourly_traffic()
 
 
 
 
 
102
 
103
- top_data = get_top_plates()
104
 
105
- suspicious_data = get_suspicious_vehicles()
 
 
106
 
107
- return (
108
- state_data,
109
- hourly_data,
110
- top_data,
111
- suspicious_data
112
- )
113
 
114
 
115
- # ================= UI ================= #
 
 
116
 
117
- with gr.Blocks(title="Vehicle Intelligence System") as demo:
 
 
 
118
 
119
- gr.Markdown("# 🚗 Vehicle Intelligence System")
 
 
120
 
121
- gr.Markdown(
122
- "AI-powered vehicle detection and intelligence system"
123
- )
124
 
125
- status, msg = health_check()
 
126
 
127
- gr.Markdown(f"### {msg}")
 
 
 
128
 
129
- # ================= TAB 1 ================= #
 
 
130
 
131
  with gr.Tab("🎥 Detection"):
132
 
 
 
 
 
 
 
 
 
133
  with gr.Row():
134
 
135
  with gr.Column():
@@ -148,7 +311,8 @@ with gr.Blocks(title="Vehicle Intelligence System") as demo:
148
  with gr.Column():
149
 
150
  output_text = gr.Textbox(
151
- label="Detection Result"
 
152
  )
153
 
154
  output_json = gr.JSON(
@@ -161,42 +325,56 @@ with gr.Blocks(title="Vehicle Intelligence System") as demo:
161
  outputs=[
162
  output_text,
163
  output_json
164
- ]
165
- )
166
-
167
- # ================= TAB 2 ================= #
168
-
169
- with gr.Tab("🔍 Database Query"):
170
-
171
- gr.Markdown(
172
- "Ask questions about vehicles using natural language"
173
  )
174
 
175
- with gr.Row():
 
 
176
 
177
- ex1 = gr.Button("How many cars today?")
178
- ex2 = gr.Button("Show TN vehicles")
179
- ex3 = gr.Button("Top repeated plates")
180
- ex4 = gr.Button("Hourly traffic")
181
 
182
- with gr.Row():
 
183
 
184
- ex5 = gr.Button("Count by vehicle type")
185
- ex6 = gr.Button("Latest detections")
186
- ex7 = gr.Button("Suspicious vehicles")
187
- ex8 = gr.Button("State distribution")
 
 
 
188
 
189
  query_input = gr.Textbox(
190
- label="Ask a question",
191
- placeholder="Show all TN vehicles",
192
  lines=2
193
  )
194
 
195
  search_btn = gr.Button(
196
- "🔍 Search",
197
  variant="primary"
198
  )
199
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
200
  with gr.Row():
201
 
202
  sql_output = gr.Code(
@@ -204,9 +382,13 @@ with gr.Blocks(title="Vehicle Intelligence System") as demo:
204
  language="sql"
205
  )
206
 
207
- results_output = gr.Dataframe(
208
- label="Results"
209
- )
 
 
 
 
210
 
211
  json_output = gr.JSON(
212
  label="Full Response"
@@ -219,52 +401,52 @@ with gr.Blocks(title="Vehicle Intelligence System") as demo:
219
  sql_output,
220
  results_output,
221
  json_output
222
- ]
 
223
  )
224
 
225
- ex1.click(
226
- lambda: "How many cars today?",
227
- outputs=query_input
228
- )
229
 
230
- ex2.click(
231
- lambda: "Show TN vehicles",
232
- outputs=query_input
233
- )
234
 
235
- ex3.click(
236
- lambda: "Top repeated plates",
237
- outputs=query_input
238
- )
239
 
240
- ex4.click(
241
- lambda: "Hourly traffic",
242
- outputs=query_input
243
  )
244
 
245
- ex5.click(
246
- lambda: "Count by vehicle type",
247
- outputs=query_input
248
  )
249
 
250
- ex6.click(
251
- lambda: "Latest detections",
252
- outputs=query_input
253
- )
254
 
255
- ex7.click(
256
- lambda: "Suspicious vehicles",
257
- outputs=query_input
 
258
  )
259
 
260
- ex8.click(
261
- lambda: "State distribution",
262
- outputs=query_input
 
 
263
  )
264
 
265
- # ================= TAB 3 ================= #
 
 
 
 
266
 
267
- with gr.Tab("📊 Analytics"):
 
 
268
 
269
  refresh_btn = gr.Button(
270
  "🔄 Refresh Analytics",
@@ -274,21 +456,25 @@ with gr.Blocks(title="Vehicle Intelligence System") as demo:
274
  with gr.Row():
275
 
276
  state_table = gr.Dataframe(
277
- label="Vehicles By State"
 
278
  )
279
 
280
  hourly_table = gr.Dataframe(
281
- label="Traffic By Hour"
 
282
  )
283
 
284
  with gr.Row():
285
 
286
  top_table = gr.Dataframe(
287
- label="Top Plates"
 
288
  )
289
 
290
  suspicious_table = gr.Dataframe(
291
- label="Suspicious Vehicles"
 
292
  )
293
 
294
  refresh_btn.click(
@@ -301,8 +487,42 @@ with gr.Blocks(title="Vehicle Intelligence System") as demo:
301
  ]
302
  )
303
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
304
 
305
- # ================= LAUNCH ================= #
 
 
306
 
307
  if __name__ == "__main__":
308
 
 
8
  import gradio as gr
9
 
10
  from detector import detect_plate
11
+
12
  from database import (
13
  save_detection,
14
  run_query,
 
19
  get_suspicious_vehicles
20
  )
21
 
22
+ # =========================================================
23
+ # ASYNC FIX
24
+ # =========================================================
25
 
26
+ asyncio.set_event_loop_policy(
27
+ asyncio.DefaultEventLoopPolicy()
28
+ )
29
 
30
+ # =========================================================
31
+ # DETECTION
32
+ # =========================================================
33
 
34
  def detect_and_save(image):
35
 
36
  if image is None:
37
+
38
+ return (
39
+ "❌ No image uploaded",
40
+ {
41
+ "error": "No image uploaded"
42
+ }
43
+ )
44
 
45
  now = datetime.now()
46
 
 
52
  plate, state, vehicle_type, vehicle_conf, success = detect_plate(image)
53
 
54
  if success and plate:
55
+
56
  save_detection(
57
  plate,
58
  state,
 
62
  time
63
  )
64
 
65
+ result_text = f"""
66
+ ✅ Detection Success
67
+
68
+ 📅 Date: {date}
69
+ ⏰ Time: {time}
70
+
71
+ 🚗 Vehicle Type: {vehicle_type}
72
+ 🔢 Plate Number: {plate}
73
+ 🌍 State: {state}
74
+
75
+ 🎯 Confidence: {round(vehicle_conf, 3)}
76
+
77
+ 💾 Saved to Database: {success}
78
+ """
79
 
80
  result_json = {
81
  "date": date,
 
91
 
92
  except Exception as e:
93
 
94
+ return (
95
+ f"❌ Error: {str(e)}",
96
+ {
97
+ "error": str(e)
98
+ }
99
+ )
100
 
101
 
102
+ # =========================================================
103
+ # QUERY DATABASE
104
+ # =========================================================
105
 
106
  def query_database(user_query):
107
 
108
  if not user_query.strip():
 
 
 
109
 
110
+ return (
111
+ "",
112
+ pd.DataFrame(),
113
+ {
114
+ "error": "Please enter a query"
115
+ }
116
+ )
117
 
118
+ try:
119
 
120
+ response = run_query(user_query)
121
 
122
+ sql = response.get("sql", "")
123
+
124
+ results = response.get("result", [])
125
+
126
+ if results and len(results) > 0:
127
+
128
+ df = pd.DataFrame(results)
129
+
130
+ else:
131
+
132
+ df = pd.DataFrame({
133
+ "message": [
134
+ "No matching records found"
135
+ ]
136
+ })
137
+
138
+ return (
139
+ sql,
140
+ df,
141
+ response
142
+ )
143
+
144
+ except Exception as e:
145
+
146
+ return (
147
+ "",
148
+ pd.DataFrame(),
149
+ {
150
+ "error": str(e)
151
+ }
152
+ )
153
+
154
+
155
+ # =========================================================
156
+ # CHATBOT
157
+ # =========================================================
158
+
159
+ def chatbot_query(message, history):
160
+
161
+ try:
162
+
163
+ response = run_query(message)
164
+
165
+ sql = response.get("sql", "")
166
+
167
+ results = response.get("result", [])
168
+
169
+ count = response.get("count", 0)
170
+
171
+ if len(results) > 5:
172
+ preview = results[:5]
173
+ else:
174
+ preview = results
175
+
176
+ bot_reply = f"""
177
+ 🔍 SQL Generated:
178
+
179
+ {sql}
180
+
181
+ 📊 Results Found: {count}
182
+
183
+ 📁 Preview:
184
+
185
+ {preview}
186
+ """
187
+
188
+ history.append(
189
+ (message, bot_reply)
190
+ )
191
+
192
+ return history, ""
193
+
194
+ except Exception as e:
195
+
196
+ history.append(
197
+ (
198
+ message,
199
+ f"❌ Error: {str(e)}"
200
+ )
201
+ )
202
 
203
+ return history, ""
204
 
205
 
206
+ # =========================================================
207
+ # ANALYTICS
208
+ # =========================================================
209
 
210
  def refresh_analytics():
211
 
212
+ try:
213
+
214
+ state_data = pd.DataFrame(
215
+ get_vehicles_by_state()
216
+ )
217
+
218
+ hourly_data = pd.DataFrame(
219
+ get_hourly_traffic()
220
+ )
221
+
222
+ top_data = pd.DataFrame(
223
+ get_top_plates()
224
+ )
225
+
226
+ suspicious_data = pd.DataFrame(
227
+ get_suspicious_vehicles()
228
+ )
229
+
230
+ return (
231
+ state_data,
232
+ hourly_data,
233
+ top_data,
234
+ suspicious_data
235
+ )
236
+
237
+ except Exception as e:
238
+
239
+ err_df = pd.DataFrame({
240
+ "error": [str(e)]
241
+ })
242
 
243
+ return (
244
+ err_df,
245
+ err_df,
246
+ err_df,
247
+ err_df
248
+ )
249
 
 
250
 
251
+ # =========================================================
252
+ # HEALTH CHECK
253
+ # =========================================================
254
 
255
+ status, msg = health_check()
 
 
 
 
 
256
 
257
 
258
+ # =========================================================
259
+ # UI
260
+ # =========================================================
261
 
262
+ with gr.Blocks(
263
+ title="Vehicle Intelligence System",
264
+ theme=gr.themes.Soft()
265
+ ) as demo:
266
 
267
+ # =====================================================
268
+ # HEADER
269
+ # =====================================================
270
 
271
+ gr.Markdown("""
272
+ # 🚗 Vehicle Intelligence System
 
273
 
274
+ AI-powered Vehicle Detection + NLP-to-SQL Intelligence Platform
275
+ """)
276
 
277
+ if status:
278
+ gr.Success(msg)
279
+ else:
280
+ gr.Warning(msg)
281
 
282
+ # =====================================================
283
+ # TAB 1 - DETECTION
284
+ # =====================================================
285
 
286
  with gr.Tab("🎥 Detection"):
287
 
288
+ gr.Markdown("""
289
+ Upload a vehicle image for:
290
+
291
+ - License Plate Detection
292
+ - Vehicle Type Classification
293
+ - Database Logging
294
+ """)
295
+
296
  with gr.Row():
297
 
298
  with gr.Column():
 
311
  with gr.Column():
312
 
313
  output_text = gr.Textbox(
314
+ label="Detection Result",
315
+ lines=12
316
  )
317
 
318
  output_json = gr.JSON(
 
325
  outputs=[
326
  output_text,
327
  output_json
328
+ ],
329
+ show_progress=True
 
 
 
 
 
 
 
330
  )
331
 
332
+ # =====================================================
333
+ # TAB 2 - NLP QUERY
334
+ # =====================================================
335
 
336
+ with gr.Tab("🔍 NLP Database Query"):
 
 
 
337
 
338
+ gr.Markdown("""
339
+ Ask questions using natural language.
340
 
341
+ Examples:
342
+ - Show TN vehicles
343
+ - Track TN63MB3157
344
+ - Show traffic in Adyar
345
+ - Top repeated plates
346
+ - Hourly traffic
347
+ """)
348
 
349
  query_input = gr.Textbox(
350
+ label="Ask a Question",
351
+ placeholder="Example: Show all TN vehicles",
352
  lines=2
353
  )
354
 
355
  search_btn = gr.Button(
356
+ "🔍 Search Database",
357
  variant="primary"
358
  )
359
 
360
+ gr.Examples(
361
+ examples=[
362
+ ["Show TN vehicles"],
363
+ ["Track TN63MB3157"],
364
+ ["Show all vehicles from Adyar"],
365
+ ["Top repeated plates"],
366
+ ["Hourly traffic"],
367
+ ["Show suspicious vehicles"],
368
+ ["Show vehicle type distribution"],
369
+ ["Show latest detections"],
370
+ ["Count vehicles in Guindy"],
371
+ ["Show KA state vehicles"],
372
+ ["Show buses"],
373
+ ["Show traffic on 2026-05-01"]
374
+ ],
375
+ inputs=query_input
376
+ )
377
+
378
  with gr.Row():
379
 
380
  sql_output = gr.Code(
 
382
  language="sql"
383
  )
384
 
385
+ results_output = gr.Dataframe(
386
+ headers=None,
387
+ datatype="str",
388
+ interactive=False,
389
+ wrap=True,
390
+ label="Results"
391
+ )
392
 
393
  json_output = gr.JSON(
394
  label="Full Response"
 
401
  sql_output,
402
  results_output,
403
  json_output
404
+ ],
405
+ show_progress=True
406
  )
407
 
408
+ # =====================================================
409
+ # TAB 3 - CHATBOT
410
+ # =====================================================
 
411
 
412
+ with gr.Tab("🤖 AI Assistant"):
 
 
 
413
 
414
+ gr.Markdown("""
415
+ Chat with the Vehicle Intelligence Database
416
+ """)
 
417
 
418
+ chatbot = gr.Chatbot(
419
+ height=500
 
420
  )
421
 
422
+ msg_box = gr.Textbox(
423
+ placeholder="Ask something..."
 
424
  )
425
 
426
+ clear_btn = gr.Button("🗑 Clear Chat")
 
 
 
427
 
428
+ msg_box.submit(
429
+ chatbot_query,
430
+ [msg_box, chatbot],
431
+ [chatbot, msg_box]
432
  )
433
 
434
+ clear_btn.click(
435
+ lambda: None,
436
+ None,
437
+ chatbot,
438
+ queue=False
439
  )
440
 
441
+ # =====================================================
442
+ # TAB 4 - ANALYTICS
443
+ # =====================================================
444
+
445
+ with gr.Tab("📊 Analytics Dashboard"):
446
 
447
+ gr.Markdown("""
448
+ Real-time traffic analytics from vehicle intelligence database
449
+ """)
450
 
451
  refresh_btn = gr.Button(
452
  "🔄 Refresh Analytics",
 
456
  with gr.Row():
457
 
458
  state_table = gr.Dataframe(
459
+ label="🚘 Vehicles By State",
460
+ interactive=False
461
  )
462
 
463
  hourly_table = gr.Dataframe(
464
+ label="🕒 Traffic By Hour",
465
+ interactive=False
466
  )
467
 
468
  with gr.Row():
469
 
470
  top_table = gr.Dataframe(
471
+ label="🏆 Top Repeated Plates",
472
+ interactive=False
473
  )
474
 
475
  suspicious_table = gr.Dataframe(
476
+ label="Suspicious Vehicles",
477
+ interactive=False
478
  )
479
 
480
  refresh_btn.click(
 
487
  ]
488
  )
489
 
490
+ demo.load(
491
+ fn=refresh_analytics,
492
+ outputs=[
493
+ state_table,
494
+ hourly_table,
495
+ top_table,
496
+ suspicious_table
497
+ ]
498
+ )
499
+
500
+ # =====================================================
501
+ # FOOTER
502
+ # =====================================================
503
+
504
+ gr.Markdown("""
505
+ ---
506
+ ### 🚀 Features
507
+
508
+ ✅ AI Vehicle Detection
509
+ ✅ License Plate Recognition
510
+ ✅ NLP-to-SQL Query Engine
511
+ ✅ Supabase PostgreSQL Integration
512
+ ✅ Analytics Dashboard
513
+ ✅ Real-time Vehicle Tracking
514
+ ✅ Hugging Face AI Integration
515
+ """)
516
+
517
+ # =========================================================
518
+ # ENABLE QUEUE
519
+ # =========================================================
520
+
521
+ demo.queue()
522
 
523
+ # =========================================================
524
+ # LAUNCH
525
+ # =========================================================
526
 
527
  if __name__ == "__main__":
528
 
app_old.py ADDED
@@ -0,0 +1,373 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ os.environ['OMP_NUM_THREADS'] = '1'
3
+
4
+ import re
5
+ from datetime import datetime
6
+ import asyncio
7
+
8
+ import cv2
9
+ import gradio as gr
10
+ import numpy as np
11
+ import torch
12
+ from PIL import Image
13
+
14
+ from paddleocr import PaddleOCR
15
+ from ultralytics import YOLO
16
+ from transformers import AutoImageProcessor, AutoModelForImageClassification
17
+ from sqlalchemy import create_engine, text
18
+ from dotenv import load_dotenv
19
+ from huggingface_hub import InferenceClient
20
+
21
+ # Fix async warning
22
+ asyncio.set_event_loop_policy(asyncio.DefaultEventLoopPolicy())
23
+
24
+ # Load environment variables
25
+ load_dotenv()
26
+
27
+ DATABASE_URL = os.getenv("DATABASE_URL")
28
+ engine = create_engine(DATABASE_URL)
29
+
30
+ HF_TOKEN = os.getenv("HF_TOKEN")
31
+ client = InferenceClient(
32
+ model="defog/sqlcoder-7b-2",
33
+ token=HF_TOKEN
34
+ )
35
+
36
+ # ---------------- LOAD MODELS ---------------- #
37
+
38
+ # YOLO (plate detection)
39
+ try:
40
+ yolo_model = YOLO("license-plate-finetune-v1s.pt")
41
+ except Exception as e:
42
+ print(f"Warning: Failed to load YOLO model: {e}")
43
+ yolo_model = None
44
+
45
+ # OCR
46
+ ocr = PaddleOCR(use_angle_cls=True, lang="en", show_log=False)
47
+
48
+ # Vehicle classification
49
+ try:
50
+ processor = AutoImageProcessor.from_pretrained("dima806/vehicle_10_types_image_detection")
51
+ vehicle_model = AutoModelForImageClassification.from_pretrained("dima806/vehicle_10_types_image_detection")
52
+ device = "cuda" if torch.cuda.is_available() else "cpu"
53
+ vehicle_model.to(device)
54
+ vehicle_model.eval()
55
+ except Exception as e:
56
+ print(f"Warning: Failed to load vehicle classification model: {e}")
57
+ processor = None
58
+ vehicle_model = None
59
+ device = "cpu"
60
+
61
+ # Regex
62
+ plate_regex = re.compile(r"[A-Z]{2}\d{1,2}[A-Z]{1,3}\d{3,4}")
63
+
64
+ # Database initialization
65
+ def init_db():
66
+ with engine.connect() as conn:
67
+ conn.execute(text("""
68
+ CREATE TABLE IF NOT EXISTS vehicle_logs (
69
+ id SERIAL PRIMARY KEY,
70
+ timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
71
+ plate TEXT,
72
+ vehicle_type TEXT,
73
+ vehicle_conf FLOAT,
74
+ date TEXT,
75
+ time TEXT
76
+ )
77
+ """))
78
+ conn.commit()
79
+
80
+ init_db()
81
+
82
+ # ---------------- PREPROCESS ---------------- #
83
+
84
+ def preprocess_plate(crop):
85
+ gray = cv2.cvtColor(crop, cv2.COLOR_RGB2GRAY)
86
+ resized = cv2.resize(gray, (320, 96))
87
+
88
+ clahe = cv2.createCLAHE(2.0, (8, 8))
89
+ enhanced = clahe.apply(resized)
90
+
91
+ filtered = cv2.bilateralFilter(enhanced, 7, 50, 50)
92
+
93
+ kernel = np.array([[0,-1,0],[-1,5,-1],[0,-1,0]])
94
+ sharp = cv2.filter2D(filtered, -1, kernel)
95
+
96
+ return cv2.cvtColor(sharp, cv2.COLOR_GRAY2BGR)
97
+
98
+ # ---------------- AUGMENT ---------------- #
99
+
100
+ def build_crops(crop):
101
+ return [
102
+ crop,
103
+ cv2.resize(crop, None, fx=1.2, fy=1.2),
104
+ cv2.GaussianBlur(crop, (3,3), 0)
105
+ ]
106
+
107
+ # ---------------- OCR ---------------- #
108
+
109
+ def run_ocr(image):
110
+ return ocr.ocr(image, cls=True)
111
+
112
+ def parse_ocr(ocr_out):
113
+ texts, confs = [], []
114
+
115
+ if not ocr_out:
116
+ return texts, confs
117
+
118
+ items = ocr_out[0] if isinstance(ocr_out[0], list) else ocr_out
119
+
120
+ for item in items:
121
+ try:
122
+ text, conf = item[1]
123
+ texts.append(text)
124
+ confs.append(float(conf))
125
+ except:
126
+ continue
127
+
128
+ return texts, confs
129
+
130
+ # ---------------- CLEAN ---------------- #
131
+
132
+ def clean_text(text):
133
+ return re.sub(r"[^A-Z0-9]", "", text.upper())
134
+
135
+ def fix_common(text):
136
+ return (
137
+ text.replace("O", "0")
138
+ .replace("I", "1")
139
+ .replace("B", "8")
140
+ .replace("Z", "2")
141
+ .replace("S", "5")
142
+ )
143
+
144
+ # ---------------- VEHICLE CLASSIFICATION ---------------- #
145
+
146
+ def classify_vehicle(image_np):
147
+ try:
148
+ image_pil = Image.fromarray(image_np)
149
+
150
+ inputs = processor(images=image_pil, return_tensors="pt")
151
+ inputs = {k: v.to(device) for k, v in inputs.items()}
152
+
153
+ with torch.no_grad():
154
+ outputs = vehicle_model(**inputs)
155
+
156
+ logits = outputs.logits
157
+ probs = torch.nn.functional.softmax(logits, dim=-1)
158
+
159
+ pred = probs.argmax(-1).item()
160
+ confidence = float(probs.max().item())
161
+
162
+ label = vehicle_model.config.id2label[pred]
163
+
164
+ return label, confidence
165
+
166
+ except Exception as e:
167
+ print("Vehicle classification error:", e)
168
+ return "unknown", 0.0
169
+
170
+ # ---------------- MAIN PIPELINE ---------------- #
171
+
172
+ def detect(image):
173
+ now = datetime.now()
174
+ date = now.strftime("%Y-%m-%d")
175
+ time = now.strftime("%H:%M:%S")
176
+
177
+ # 🔹 Vehicle classification
178
+ vehicle_type, vehicle_conf = classify_vehicle(image)
179
+
180
+ # 🔹 Plate detection
181
+ results = yolo_model(image)
182
+ boxes = results[0].boxes
183
+
184
+ if boxes is None or len(boxes) == 0:
185
+ return f"{date} {time} | {vehicle_type} |", {
186
+ "date": date,
187
+ "time": time,
188
+ "vehicle_type": vehicle_type,
189
+ "vehicle_confidence": round(vehicle_conf, 3),
190
+ "plate": ""
191
+ }
192
+
193
+ h, w = image.shape[:2]
194
+ xyxy = boxes.xyxy.cpu().numpy()
195
+ confs = boxes.conf.cpu().numpy()
196
+
197
+ collected = []
198
+
199
+ for i, (x1, y1, x2, y2) in enumerate(xyxy):
200
+
201
+ if confs[i] < 0.5:
202
+ continue
203
+
204
+ pad = int(0.12 * max(x2 - x1, y2 - y1))
205
+
206
+ l = max(int(x1 - pad), 0)
207
+ t = max(int(y1 - pad), 0)
208
+ r = min(int(x2 + pad), w - 1)
209
+ b = min(int(y2 + pad), h - 1)
210
+
211
+ crop = image[t:b, l:r]
212
+
213
+ for variant in build_crops(crop):
214
+ pre = preprocess_plate(variant)
215
+
216
+ ocr_out = run_ocr(pre)
217
+ texts, confs_ocr = parse_ocr(ocr_out)
218
+
219
+ for txt, cf in zip(texts, confs_ocr):
220
+
221
+ if cf < 0.3:
222
+ continue
223
+
224
+ norm = fix_common(clean_text(txt))
225
+
226
+ if len(norm) < 4:
227
+ continue
228
+
229
+ collected.append(norm)
230
+
231
+ if not collected:
232
+ plate = ""
233
+ else:
234
+ combined = "".join(collected)
235
+ match = plate_regex.search(combined)
236
+ plate = match.group(0) if match else combined
237
+
238
+ # SAVE TO DATABASE
239
+ try:
240
+ with engine.connect() as conn:
241
+ conn.execute(text("""
242
+ INSERT INTO vehicle_logs
243
+ (plate, vehicle_type, vehicle_conf, date, time)
244
+ VALUES (:plate, :vehicle_type, :vehicle_conf, :date, :time)
245
+ """), {
246
+ "plate": plate,
247
+ "vehicle_type": vehicle_type,
248
+ "vehicle_conf": float(vehicle_conf),
249
+ "date": date,
250
+ "time": time
251
+ })
252
+ conn.commit()
253
+ except Exception as e:
254
+ print("Database insert error:", e)
255
+
256
+ # 🔹 Final Output
257
+ return f"{date} {time} | {vehicle_type} | {plate}", {
258
+ "date": date,
259
+ "time": time,
260
+ "vehicle_type": vehicle_type,
261
+ "vehicle_confidence": round(vehicle_conf, 3),
262
+ "plate": plate
263
+ }
264
+
265
+ # ---------------- LLM SQL LAYER (SQLCoder) ---------------- #
266
+
267
+ def ask_llm(user_query):
268
+ schema = """
269
+ Table: vehicle_logs
270
+
271
+ Columns:
272
+ - id (SERIAL PRIMARY KEY)
273
+ - timestamp (TIMESTAMP)
274
+ - plate (TEXT) - License plate number
275
+ - vehicle_type (TEXT) - Type of vehicle
276
+ - vehicle_conf (FLOAT) - Detection confidence
277
+ - date (TEXT) - Date in YYYY-MM-DD format
278
+ - time (TEXT) - Time in HH:MM:SS format
279
+ """
280
+
281
+ prompt = f"""### Task
282
+ Generate PostgreSQL SQL query for the following question.
283
+
284
+ ### Rules
285
+ - Only SELECT queries allowed
286
+ - Use vehicle_logs table
287
+ - No markdown, no explanation
288
+ - Output SQL only
289
+ - Use appropriate WHERE clauses for filtering
290
+ - Use COUNT(*), SUM(), AVG() for aggregations if needed
291
+ - Order by timestamp DESC for chronological queries
292
+
293
+ ### Schema
294
+ {schema}
295
+
296
+ ### User Question
297
+ {user_query}
298
+
299
+ ### SQL Query
300
+ """
301
+
302
+ try:
303
+ response = client.text_generation(
304
+ prompt,
305
+ max_new_tokens=150,
306
+ temperature=0.1
307
+ )
308
+
309
+ sql_query = response.strip()
310
+
311
+ # Clean up markdown formatting if present
312
+ sql_query = sql_query.replace("```sql", "")
313
+ sql_query = sql_query.replace("```", "")
314
+ sql_query = sql_query.strip()
315
+
316
+ return sql_query
317
+
318
+ except Exception as e:
319
+ print(f"LLM error: {e}")
320
+ return f"SELECT * FROM vehicle_logs LIMIT 10; -- Error: {e}"
321
+
322
+ def run_query(user_query):
323
+ sql_query = ask_llm(user_query)
324
+
325
+ try:
326
+ with engine.connect() as conn:
327
+ result = conn.execute(text(sql_query))
328
+ rows = [dict(row._mapping) for row in result]
329
+
330
+ return {
331
+ "query": user_query,
332
+ "sql": sql_query,
333
+ "result": rows
334
+ }
335
+
336
+ except Exception as e:
337
+ return {
338
+ "error": str(e),
339
+ "sql": sql_query
340
+ }
341
+
342
+ # ---------------- UI ---------------- #
343
+
344
+ with gr.Blocks() as demo:
345
+ gr.Markdown("# 🚗 Vehicle Intelligence System")
346
+
347
+ with gr.Tab("Detection"):
348
+ img = gr.Image(type="numpy")
349
+ out1 = gr.Textbox(label="Result")
350
+ out2 = gr.JSON(label="Structured Output")
351
+ btn = gr.Button("Detect")
352
+
353
+ btn.click(
354
+ fn=detect,
355
+ inputs=img,
356
+ outputs=[out1, out2]
357
+ )
358
+
359
+ with gr.Tab("Ask Database"):
360
+ query_input = gr.Textbox(
361
+ label="Ask Anything",
362
+ placeholder="How many cars today?"
363
+ )
364
+ query_output = gr.JSON()
365
+ ask_btn = gr.Button("Ask")
366
+
367
+ ask_btn.click(
368
+ fn=run_query,
369
+ inputs=query_input,
370
+ outputs=query_output
371
+ )
372
+
373
+ demo.launch(server_name="0.0.0.0", server_port=7860)
database.py CHANGED
@@ -1,211 +1,266 @@
1
- import os
 
 
 
 
 
 
2
  import traceback
3
- from datetime import datetime
4
 
5
- from dotenv import load_dotenv
6
  from huggingface_hub import InferenceClient
 
7
  from sqlalchemy import create_engine, text
8
 
9
- load_dotenv()
 
 
10
 
11
- # ================= DATABASE ================= #
12
 
 
13
  DATABASE_URL = os.getenv("DATABASE_URL")
14
- engine = None
15
- db_available = False
16
 
17
- if DATABASE_URL:
18
- try:
19
- engine = create_engine(
20
- DATABASE_URL,
21
- pool_pre_ping=True
22
- )
23
- db_available = True
24
- print("✅ Database engine initialized")
25
- except Exception as e:
26
- print(f"⚠️ Database initialization error: {e}")
27
- db_available = False
28
- else:
29
- print("⚠️ DATABASE_URL not set. Database features disabled.")
30
 
31
- # ================= HUGGINGFACE ================= #
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
 
33
- HF_TOKEN = os.getenv("HF_TOKEN")
34
- client = None
 
35
 
36
- if HF_TOKEN:
37
- try:
38
- client = InferenceClient(
39
- model="defog/sqlcoder-7b-2",
40
- token=HF_TOKEN
41
- )
42
- print("✅ HuggingFace client initialized")
43
- except Exception as e:
44
- print(f"⚠️ HuggingFace client error: {e}")
45
- else:
46
- print("⚠️ HF_TOKEN not set. NLP-to-SQL features disabled.")
47
 
48
- # ================= INIT DB ================= #
 
49
 
50
- def init_db():
51
-
52
- if not db_available or engine is None:
53
- print("⚠️ Skipping database initialization - DATABASE_URL not configured")
54
- return
55
 
56
- try:
57
- with engine.begin() as conn:
58
- # Create table if it doesn't exist
59
- conn.execute(text("""
60
- CREATE TABLE IF NOT EXISTS vehicle_logs (
61
- id BIGSERIAL PRIMARY KEY,
62
- timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
63
- plate TEXT,
64
- state TEXT,
65
- vehicle_type TEXT,
66
- vehicle_conf FLOAT,
67
- date DATE,
68
- hour INTEGER,
69
- day TEXT
70
- )
71
- """))
72
-
73
- print("✅ Database Initialized")
74
-
75
- except Exception as e:
76
- print(f"⚠️ Database initialization error: {e}")
77
- traceback.print_exc()
78
 
 
 
79
 
80
- init_db()
 
81
 
82
- # ================= SAVE ================= #
 
83
 
84
- def save_detection(
85
- plate,
86
- state,
87
- vehicle_type,
88
- vehicle_conf,
89
- date_str,
90
- time_str
91
- ):
92
-
93
- if not db_available or engine is None:
94
- print("⚠️ Database not available for saving detection")
95
- return
96
 
97
- try:
98
- dt = datetime.strptime(
99
- f"{date_str} {time_str}",
100
- "%Y-%m-%d %H:%M:%S"
101
- )
102
 
103
- hour = dt.hour
104
- day = dt.strftime("%A")
105
 
106
- with engine.begin() as conn:
107
- conn.execute(text("""
108
- INSERT INTO vehicle_logs
109
- (
110
- plate,
111
- state,
112
- vehicle_type,
113
- vehicle_conf,
114
- date,
115
- hour,
116
- day
117
- )
118
- VALUES
119
- (
120
- :plate,
121
- :state,
122
- :vehicle_type,
123
- :vehicle_conf,
124
- :date,
125
- :hour,
126
- :day
127
- )
128
- """), {
129
- "plate": plate,
130
- "state": state,
131
- "vehicle_type": vehicle_type,
132
- "vehicle_conf": float(vehicle_conf),
133
- "date": date_str,
134
- "hour": hour,
135
- "day": day
136
- })
137
-
138
- except Exception as e:
139
- print(f"⚠️ Error saving detection: {e}")
140
- traceback.print_exc()
141
 
 
 
 
142
 
143
- # ================= SQL SAFETY ================= #
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
144
 
145
- BLOCKED = [
146
- "DROP",
147
- "DELETE",
148
- "UPDATE",
149
- "INSERT",
150
- "ALTER",
151
- "TRUNCATE"
 
 
 
152
  ]
153
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
154
  def validate_sql(sql):
155
 
 
 
 
 
 
 
 
 
 
 
 
 
156
  upper = sql.upper()
157
 
158
- for word in BLOCKED:
159
 
160
  if word in upper:
161
  return False
162
 
163
- return upper.startswith("SELECT")
 
164
 
 
 
165
 
166
- # ================= NLP TO SQL ================= #
167
 
168
- # ================= HELPER FUNCTIONS ================= #
169
 
170
- def clean_sql(sql_str):
171
- """Clean and normalize SQL output"""
172
-
173
- sql = sql_str.strip()
174
- sql = sql.replace("```sql", "").replace("```", "")
175
- sql = sql.strip()
176
-
177
- if not sql.endswith(";"):
178
- sql += ";"
179
-
180
- return sql
181
-
182
-
183
- # ================= NLP TO SQL ================= #
184
 
185
  def ask_llm(user_query):
186
- """
187
- Advanced NLP-to-SQL Generator
188
- Hybrid Rule-Based + LLM Approach for Vehicle Intelligence
189
- """
190
-
191
- import re
192
-
193
- if client is None:
194
- return "SELECT * FROM vehicle_logs LIMIT 10;"
195
 
196
  q = user_query.lower().strip()
197
 
198
- # =========================================================
199
- # RULE-BASED FAST PATHS (VERY IMPORTANT)
200
- # =========================================================
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
201
 
202
- # ----- PLATE TRACKING (Generic) -----
203
- plate_match = re.search(r'([A-Z]{2}\d{1,2}[A-Z]{1,3}\d{3,4})', user_query.upper())
 
204
 
205
  if plate_match:
 
206
  plate = plate_match.group(1)
207
 
208
- if any(k in q for k in ["location", "route", "travel", "movement", "where", "pass", "track", "history"]):
 
 
 
209
  return clean_sql(f"""
210
  SELECT
211
  timestamp,
@@ -213,456 +268,466 @@ def ask_llm(user_query):
213
  state,
214
  vehicle_type,
215
  location,
216
- camera_id
 
 
 
217
  FROM vehicle_logs
218
  WHERE plate = '{plate}'
219
  ORDER BY timestamp DESC
220
- LIMIT 100;
221
  """)
222
 
223
- if any(k in q for k in ["count", "how many", "detections"]):
 
 
 
224
  return clean_sql(f"""
225
  SELECT
226
  plate,
227
- COUNT(*) as detection_count,
228
  COUNT(DISTINCT location) as unique_locations,
229
- COUNT(DISTINCT date) as days_detected
230
  FROM vehicle_logs
231
  WHERE plate = '{plate}'
232
- GROUP BY plate;
233
  """)
234
 
 
 
235
  return clean_sql(f"""
236
  SELECT *
237
  FROM vehicle_logs
238
  WHERE plate = '{plate}'
239
  ORDER BY timestamp DESC
240
- LIMIT 50;
241
  """)
242
 
243
- # ----- STATE SEARCH (Generic) -----
244
- states_map = {
245
- "tn": "TN", "tamil": "TN", "tamil nadu": "TN",
246
- "ka": "KA", "karnataka": "KA",
247
- "kl": "KL", "kerala": "KL",
248
- "ap": "AP", "andhra": "AP",
249
- "ts": "TS", "telangana": "TS",
250
- "mh": "MH", "maharashtra": "MH",
251
- "dl": "DL", "delhi": "DL",
252
- "gj": "GJ", "gujarat": "GJ",
253
- "rj": "RJ", "rajasthan": "RJ",
254
- "up": "UP", "uttar": "UP",
255
- "wb": "WB", "bengal": "WB",
256
- "hr": "HR", "haryana": "HR",
257
- "pb": "PB", "punjab": "PB"
258
- }
259
 
260
- for key, state_code in states_map.items():
261
  if key in q:
262
- if "count" in q:
 
 
263
  return clean_sql(f"""
264
  SELECT
265
  state,
266
- COUNT(*) as total_vehicles,
267
- COUNT(DISTINCT plate) as unique_plates,
268
- COUNT(DISTINCT location) as locations_active
269
  FROM vehicle_logs
270
- WHERE state = '{state_code}'
271
- GROUP BY state;
272
- """)
273
-
274
- if "distribution" in q or "breakdown" in q:
275
- return clean_sql(f"""
276
- SELECT
277
- vehicle_type,
278
- COUNT(*) as count
279
- FROM vehicle_logs
280
- WHERE state = '{state_code}'
281
- GROUP BY vehicle_type
282
- ORDER BY count DESC;
283
  """)
284
 
285
  return clean_sql(f"""
286
  SELECT *
287
  FROM vehicle_logs
288
- WHERE state = '{state_code}'
289
  ORDER BY timestamp DESC
290
- LIMIT 50;
291
  """)
292
 
293
- # ----- LOCATION SEARCH (Generic) -----
294
- locations = [
295
- "adyar", "guindy", "velachery", "besant", "thiruvanmiyur",
296
- "tnagar", "mylapore", "annanagar", "koyambedu", "nungambakkam",
297
- "kotturpuram", "porur", "indiranagar", "whitefield", "koramangala",
298
- "bangalore", "hyderabad", "trivandrum", "kochi", "pune", "mumbai"
299
- ]
300
 
301
- for loc in locations:
302
  if loc in q:
303
- if "count" in q:
 
 
 
 
304
  return clean_sql(f"""
305
  SELECT
306
  location,
307
- COUNT(*) as detection_count,
308
  COUNT(DISTINCT plate) as unique_vehicles
309
  FROM vehicle_logs
310
  WHERE LOWER(location) LIKE '%{loc}%'
311
  GROUP BY location
312
- ORDER BY detection_count DESC;
313
  """)
314
 
 
 
315
  return clean_sql(f"""
316
  SELECT
317
  timestamp,
318
  plate,
319
  state,
320
  vehicle_type,
321
- location
 
322
  FROM vehicle_logs
323
  WHERE LOWER(location) LIKE '%{loc}%'
324
  ORDER BY timestamp DESC
325
- LIMIT 100;
326
  """)
327
 
328
- # ----- VEHICLE TYPE SEARCH (Generic) -----
329
- vehicle_types = {
330
- "suv": "SUV", "sedan": "Sedan", "hatchback": "Hatchback",
331
- "truck": "Truck", "bus": "Bus", "bike": "Bike",
332
- "motorcycle": "Bike", "auto": "Auto", "taxi": "Taxi",
333
- "car": "Car", "van": "Van", "tempo": "Tempo"
334
- }
 
 
335
 
336
- for vtype_key, vtype_val in vehicle_types.items():
337
- if vtype_key in q:
338
- if "count" in q:
339
  return clean_sql(f"""
340
  SELECT
341
  vehicle_type,
342
- COUNT(*) as count,
343
- ROUND(AVG(vehicle_conf), 2) as avg_confidence
344
  FROM vehicle_logs
345
- WHERE LOWER(vehicle_type) LIKE '%{vtype_val.lower()}%'
346
- GROUP BY vehicle_type;
347
  """)
348
 
349
  return clean_sql(f"""
350
  SELECT *
351
  FROM vehicle_logs
352
- WHERE LOWER(vehicle_type) LIKE '%{vtype_val.lower()}%'
353
  ORDER BY timestamp DESC
354
- LIMIT 50;
355
  """)
356
 
357
- # ----- DATE SEARCH -----
358
- date_match = re.search(r'(\d{4}-\d{2}-\d{2})', q)
 
359
 
360
  if date_match:
361
- date_value = date_match.group(1)
362
 
363
- if "count" in q:
364
- return clean_sql(f"""
365
- SELECT
366
- date,
367
- COUNT(*) as total_detections,
368
- COUNT(DISTINCT plate) as unique_vehicles,
369
- COUNT(DISTINCT location) as unique_locations
370
- FROM vehicle_logs
371
- WHERE date = '{date_value}'
372
- GROUP BY date;
373
- """)
374
 
375
  return clean_sql(f"""
376
  SELECT *
377
  FROM vehicle_logs
378
- WHERE date = '{date_value}'
379
  ORDER BY timestamp DESC
380
- LIMIT 100;
381
  """)
382
 
383
- # ----- TIME-BASED QUERIES -----
384
- if "morning" in q:
385
- return clean_sql("""
386
- SELECT *
387
- FROM vehicle_logs
388
- WHERE hour BETWEEN 6 AND 11
389
- ORDER BY timestamp DESC
390
- LIMIT 100;
391
- """)
392
 
393
- if "afternoon" in q:
394
- return clean_sql("""
395
- SELECT *
396
- FROM vehicle_logs
397
- WHERE hour BETWEEN 12 AND 17
398
- ORDER BY timestamp DESC
399
- LIMIT 100;
400
- """)
401
-
402
- if "evening" in q or "night" in q:
403
- return clean_sql("""
404
- SELECT *
405
- FROM vehicle_logs
406
- WHERE hour BETWEEN 18 AND 23 OR hour BETWEEN 0 AND 5
407
- ORDER BY timestamp DESC
408
- LIMIT 100;
409
- """)
410
 
411
- if "busiest hour" in q or "peak hour" in q:
412
  return clean_sql("""
413
  SELECT
414
  hour,
415
- COUNT(*) as traffic_volume
416
  FROM vehicle_logs
417
  GROUP BY hour
418
- ORDER BY traffic_volume DESC
419
- LIMIT 1;
420
  """)
421
 
422
- if "hourly traffic" in q or "traffic by hour" in q:
423
- return clean_sql("""
424
- SELECT
425
- hour,
426
- COUNT(*) as traffic_count,
427
- COUNT(DISTINCT plate) as unique_vehicles
428
- FROM vehicle_logs
429
- GROUP BY hour
430
- ORDER BY hour;
431
- """)
432
 
433
- # ----- ANALYTICS QUERIES -----
434
- if "top plates" in q or "most detected" in q or "repeated plates" in q:
435
  return clean_sql("""
436
  SELECT
437
  plate,
438
- COUNT(*) as detections,
439
- COUNT(DISTINCT location) as locations,
440
- COUNT(DISTINCT date) as days
441
  FROM vehicle_logs
442
  GROUP BY plate
443
  ORDER BY detections DESC
444
- LIMIT 20;
445
  """)
446
 
447
- if "suspicious" in q or "high frequency" in q or "unusual" in q:
 
448
  return clean_sql("""
449
  SELECT
450
- plate,
451
- COUNT(*) as detection_count,
452
- COUNT(DISTINCT location) as unique_locations,
453
- ROUND(AVG(vehicle_conf), 2) as avg_confidence
454
  FROM vehicle_logs
455
- GROUP BY plate
456
- HAVING COUNT(*) > 10
457
- ORDER BY detection_count DESC
458
- LIMIT 50;
459
  """)
460
 
461
- if "vehicle type" in q and ("count" in q or "distribution" in q or "breakdown" in q):
 
462
  return clean_sql("""
463
  SELECT
464
  vehicle_type,
465
- COUNT(*) as count,
466
- ROUND(100.0 * COUNT(*) / (SELECT COUNT(*) FROM vehicle_logs), 2) as percentage,
467
- ROUND(AVG(vehicle_conf), 2) as avg_confidence
468
  FROM vehicle_logs
469
  GROUP BY vehicle_type
470
- ORDER BY count DESC;
471
  """)
472
 
473
- if "state distribution" in q or ("count" in q and "state" in q):
474
- return clean_sql("""
475
- SELECT
476
- state,
477
- COUNT(*) as count,
478
- COUNT(DISTINCT plate) as unique_plates,
479
- ROUND(100.0 * COUNT(*) / (SELECT COUNT(*) FROM vehicle_logs), 2) as percentage
480
- FROM vehicle_logs
481
- GROUP BY state
482
- ORDER BY count DESC;
483
- """)
484
 
485
- if "latest" in q or "recent" in q or "last detection" in q:
486
  return clean_sql("""
487
  SELECT *
488
  FROM vehicle_logs
489
  ORDER BY timestamp DESC
490
- LIMIT 50;
491
  """)
492
 
493
- if "total vehicle" in q or "total count" in q:
494
- return clean_sql("""
495
- SELECT
496
- COUNT(*) as total_detections,
497
- COUNT(DISTINCT plate) as unique_vehicles,
498
- COUNT(DISTINCT state) as states_active,
499
- COUNT(DISTINCT location) as locations_active
500
- FROM vehicle_logs;
501
- """)
502
 
503
- if "camera" in q or "detection point" in q:
504
- return clean_sql("""
505
- SELECT
506
- camera_id,
507
- location,
508
- COUNT(*) as detections,
509
- COUNT(DISTINCT plate) as unique_vehicles
510
- FROM vehicle_logs
511
- WHERE camera_id IS NOT NULL
512
- GROUP BY camera_id, location
513
- ORDER BY detections DESC
514
- LIMIT 20;
515
- """)
516
 
517
- # ----- ADVANCED COMBINATION QUERIES -----
518
- if "passed through" in q or "traveled through" in q:
519
  return clean_sql("""
520
- SELECT
521
- plate,
522
- location,
523
- COUNT(*) as times_detected,
524
- MIN(timestamp) as first_detection,
525
- MAX(timestamp) as last_detection
526
  FROM vehicle_logs
527
- WHERE location IS NOT NULL
528
- GROUP BY plate, location
529
- ORDER BY plate, MIN(timestamp) DESC
530
- LIMIT 100;
531
  """)
532
 
533
- # =========================================================
534
- # FALLBACK: ADVANCED LLM
535
- # =========================================================
536
-
537
- prompt = f"""
538
- You are an expert PostgreSQL SQL generator for vehicle intelligence.
539
-
540
- DATABASE SCHEMA:
541
- vehicle_logs(
542
- timestamp,
543
- plate,
544
- state,
545
- vehicle_type,
546
- vehicle_conf,
547
- camera_id,
548
- location,
549
- date,
550
- hour,
551
- day
552
- )
 
 
 
 
 
 
 
 
 
553
 
554
- VALID COLUMNS:
555
- timestamp - Detection timestamp
556
- plate - License plate number
557
- state - State code (TN, KA, KL, AP, TS, MH, DL, GJ, RJ, UP, WB, HR, PB)
558
- vehicle_type - Car, SUV, Truck, Bus, Bike, Auto, Taxi, Van, etc.
559
- vehicle_conf - Detection confidence (0.0-1.0)
560
- camera_id - Camera identifier
561
- location - Detection location/area name
562
- date - Detection date (YYYY-MM-DD)
563
- hour - Hour of day (0-23)
564
- day - Day of week (Monday-Sunday)
565
-
566
- STRICT RULES:
567
- 1. ONLY SELECT queries
568
- 2. NEVER JOIN tables
569
- 3. NEVER use subqueries (except COUNT aggregates)
570
- 4. ONLY vehicle_logs table
571
- 5. ALWAYS use LIMIT 50 or LIMIT 100
572
- 6. NEVER use DELETE, UPDATE, DROP, ALTER, CREATE, TRUNCATE
573
- 7. NEVER invent columns or tables
574
- 8. Return SQL ONLY (no explanation)
575
- 9. No markdown formatting
576
- 10. Always end with semicolon
577
-
578
- EXAMPLES:
579
-
580
- Q: Show TN vehicles
581
- A: SELECT * FROM vehicle_logs WHERE state='TN' ORDER BY timestamp DESC LIMIT 50;
582
-
583
- Q: Show all vehicles from Adyar
584
- A: SELECT * FROM vehicle_logs WHERE LOWER(location) LIKE '%adyar%' ORDER BY timestamp DESC LIMIT 50;
585
-
586
- Q: Show suspicious vehicles
587
- A: SELECT plate, COUNT(*) as count FROM vehicle_logs GROUP BY plate HAVING COUNT(*) > 10 ORDER BY count DESC LIMIT 50;
588
-
589
- Q: Show vehicle type distribution
590
- A: SELECT vehicle_type, COUNT(*) as count FROM vehicle_logs GROUP BY vehicle_type ORDER BY count DESC;
591
-
592
- Q: Show hourly traffic
593
- A: SELECT hour, COUNT(*) as count FROM vehicle_logs GROUP BY hour ORDER BY hour;
594
-
595
- Q: Show latest detections
596
- A: SELECT * FROM vehicle_logs ORDER BY timestamp DESC LIMIT 50;
597
-
598
- Q: Track vehicle TN63AB1234
599
- A: SELECT * FROM vehicle_logs WHERE plate='TN63AB1234' ORDER BY timestamp DESC LIMIT 100;
600
-
601
- USER QUESTION:
602
- {user_query}
603
 
604
- SQL:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
605
  """
606
 
 
 
 
 
607
  try:
608
- response = client.text_generation(
609
- prompt,
610
- max_new_tokens=150,
611
- temperature=0.05,
612
- repetition_penalty=1.2
 
 
 
 
 
 
 
 
 
613
  )
614
 
615
- sql_query = clean_sql(response.strip())
616
 
617
- # =====================================================
618
- # EXTRA SAFETY VALIDATION
619
- # =====================================================
620
 
621
- sql_upper = sql_query.upper()
 
 
622
 
623
- blocked = ["DROP", "DELETE", "UPDATE", "INSERT", "ALTER", "CREATE", "TRUNCATE", "JOIN", "UNION"]
624
 
625
- for b in blocked:
626
- if b in sql_upper:
627
- return f"SELECT * FROM vehicle_logs LIMIT 10;" # Fallback
 
 
 
628
 
629
- if "VEHICLE_LOGS" not in sql_upper:
630
- return f"SELECT * FROM vehicle_logs LIMIT 10;" # Fallback
631
 
632
- if not sql_upper.startswith("SELECT"):
633
- return f"SELECT * FROM vehicle_logs LIMIT 10;" # Fallback
634
 
635
- return sql_query
 
 
636
 
637
  except Exception as e:
638
- print(f"⚠️ LLM Error: {e}")
639
- return f"SELECT * FROM vehicle_logs LIMIT 10;" # Fallback
640
 
 
 
641
 
642
- # ================= QUERY ================= #
 
 
 
 
 
643
 
644
- def run_query(user_query):
 
 
645
 
646
- if not db_available or engine is None:
647
- return {
648
- "query": user_query,
649
- "error": "Database not configured. Set DATABASE_URL in Hugging Face Spaces secrets.",
650
- "sql": None,
651
- "result": []
652
- }
653
 
654
  try:
655
 
656
  sql = ask_llm(user_query)
657
 
658
- if not validate_sql(sql):
 
 
659
 
660
- return {
661
- "query": user_query,
662
- "error": "Unsafe SQL blocked",
663
- "sql": sql,
664
- "result": []
665
- }
666
 
667
  with engine.connect() as conn:
668
 
@@ -676,8 +741,8 @@ def run_query(user_query):
676
  return {
677
  "query": user_query,
678
  "sql": sql,
679
- "result": rows,
680
- "count": len(rows)
681
  }
682
 
683
  except Exception as e:
@@ -691,104 +756,148 @@ def run_query(user_query):
691
  "result": []
692
  }
693
 
 
 
 
694
 
695
- # ================= ANALYTICS ================= #
696
-
697
- def get_vehicles_by_state():
698
-
699
- if not db_available or engine is None:
700
- return []
701
-
702
  try:
 
 
 
 
 
 
 
 
 
 
 
703
  with engine.connect() as conn:
704
- result = conn.execute(text("""
705
- SELECT state, COUNT(*) AS count
706
- FROM vehicle_logs
707
- GROUP BY state
708
- ORDER BY count DESC
709
- """))
710
- return [dict(r._mapping) for r in result]
711
  except Exception as e:
712
- print(f"⚠️ Error in get_vehicles_by_state: {e}")
713
- return []
 
714
 
715
 
716
- def get_hourly_traffic():
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
717
 
718
- if not db_available or engine is None:
719
- return []
720
 
 
 
 
721
  try:
 
 
 
 
 
 
 
 
722
  with engine.connect() as conn:
723
- result = conn.execute(text("""
724
- SELECT hour, COUNT(*) AS count
725
- FROM vehicle_logs
726
- GROUP BY hour
727
- ORDER BY hour
728
- """))
729
- return [dict(r._mapping) for r in result]
730
  except Exception as e:
731
- print(f"⚠️ Error in get_hourly_traffic: {e}")
732
  return []
733
 
734
 
735
- def get_top_plates():
736
-
737
- if not db_available or engine is None:
738
- return []
739
-
740
  try:
 
 
 
 
 
 
 
 
741
  with engine.connect() as conn:
742
- result = conn.execute(text("""
743
- SELECT plate, COUNT(*) AS count
744
- FROM vehicle_logs
745
- WHERE plate != ''
746
- GROUP BY plate
747
- ORDER BY count DESC
748
- LIMIT 10
749
- """))
750
- return [dict(r._mapping) for r in result]
751
  except Exception as e:
752
- print(f"⚠️ Error in get_top_plates: {e}")
753
  return []
754
 
755
 
756
- def get_suspicious_vehicles():
757
-
758
- if not db_available or engine is None:
759
- return []
760
-
761
  try:
 
 
 
 
 
 
 
 
 
762
  with engine.connect() as conn:
763
- result = conn.execute(text("""
764
- SELECT plate,
765
- COUNT(*) AS detections
766
- FROM vehicle_logs
767
- GROUP BY plate
768
- HAVING COUNT(*) > 5
769
- ORDER BY detections DESC
770
- LIMIT 20
771
- """))
772
- return [dict(r._mapping) for r in result]
773
  except Exception as e:
774
- print(f"⚠️ Error in get_suspicious_vehicles: {e}")
775
  return []
776
 
777
 
778
- # ================= HEALTH ================= #
779
-
780
- def health_check():
781
-
782
- if not db_available or engine is None:
783
- return False, "⚠️ Database not configured. Set DATABASE_URL in Hugging Face Spaces secrets."
784
-
785
  try:
786
-
 
 
 
 
 
 
 
 
 
 
 
787
  with engine.connect() as conn:
788
- conn.execute(text("SELECT 1"))
789
-
790
- return True, "✅ Database Connected"
791
-
 
792
  except Exception as e:
793
-
794
- return False, str(e)
 
1
+ # =========================================================
2
+ # ULTRA ADVANCED HYBRID NLP TO SQL ENGINE
3
+ # RULE BASED + LLM BASED + SQL SAFETY
4
+ # MISTRAL / SQLCODER READY
5
+ # =========================================================
6
+
7
+ import re
8
  import traceback
9
+ import os
10
 
 
11
  from huggingface_hub import InferenceClient
12
+ from dotenv import load_dotenv
13
  from sqlalchemy import create_engine, text
14
 
15
+ # =========================================================
16
+ # ENVIRONMENT SETUP
17
+ # =========================================================
18
 
19
+ load_dotenv()
20
 
21
+ HF_TOKEN = os.getenv("HF_TOKEN")
22
  DATABASE_URL = os.getenv("DATABASE_URL")
 
 
23
 
24
+ # Initialize Mistral client
25
+ client = InferenceClient(
26
+ model="mistralai/Mistral-7B-Instruct-v0.2",
27
+ token=HF_TOKEN
28
+ )
 
 
 
 
 
 
 
 
29
 
30
+ # Initialize database engine
31
+ try:
32
+ engine = create_engine(DATABASE_URL)
33
+ print("✅ Database connection initialized")
34
+ except Exception as e:
35
+ print(f"⚠️ Database connection warning: {e}")
36
+ engine = None
37
+
38
+ # =========================================================
39
+ # CONFIG
40
+ # =========================================================
41
+
42
+ USE_LLM = True
43
+
44
+ # =========================================================
45
+ # DATABASE KNOWLEDGE
46
+ # =========================================================
47
+
48
+ SCHEMA = {
49
+ "table": "vehicle_logs",
50
+ "columns": [
51
+ "timestamp",
52
+ "plate",
53
+ "state",
54
+ "vehicle_type",
55
+ "vehicle_conf",
56
+ "camera_id",
57
+ "location",
58
+ "date",
59
+ "hour",
60
+ "day"
61
+ ]
62
+ }
63
 
64
+ VALID_STATES = {
65
+ "tn": "TN",
66
+ "tamil nadu": "TN",
67
 
68
+ "ka": "KA",
69
+ "karnataka": "KA",
 
 
 
 
 
 
 
 
 
70
 
71
+ "kl": "KL",
72
+ "kerala": "KL",
73
 
74
+ "ap": "AP",
75
+ "andhra": "AP",
 
 
 
76
 
77
+ "ts": "TS",
78
+ "telangana": "TS",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
79
 
80
+ "mh": "MH",
81
+ "maharashtra": "MH",
82
 
83
+ "dl": "DL",
84
+ "delhi": "DL",
85
 
86
+ "gj": "GJ",
87
+ "gujarat": "GJ",
88
 
89
+ "rj": "RJ",
90
+ "rajasthan": "RJ",
 
 
 
 
 
 
 
 
 
 
91
 
92
+ "up": "UP",
93
+ "uttar pradesh": "UP",
 
 
 
94
 
95
+ "wb": "WB",
96
+ "west bengal": "WB",
97
 
98
+ "hr": "HR",
99
+ "haryana": "HR",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
100
 
101
+ "pb": "PB",
102
+ "punjab": "PB"
103
+ }
104
 
105
+ KNOWN_LOCATIONS = [
106
+ "adyar",
107
+ "guindy",
108
+ "velachery",
109
+ "besantnagar",
110
+ "besant nagar",
111
+ "thiruvanmiyur",
112
+ "tnagar",
113
+ "t nagar",
114
+ "mylapore",
115
+ "annanagar",
116
+ "anna nagar",
117
+ "koyambedu",
118
+ "nungambakkam",
119
+ "kotturpuram"
120
+ ]
121
 
122
+ VEHICLE_TYPES = [
123
+ "suv",
124
+ "bus",
125
+ "truck",
126
+ "bike",
127
+ "auto",
128
+ "taxi",
129
+ "car",
130
+ "jeep",
131
+ "sedan"
132
  ]
133
 
134
+ # =========================================================
135
+ # SQL CLEANER
136
+ # =========================================================
137
+
138
+ def clean_sql(sql):
139
+
140
+ sql = sql.replace("```sql", "")
141
+ sql = sql.replace("```", "")
142
+ sql = sql.strip()
143
+
144
+ if not sql.endswith(";"):
145
+ sql += ";"
146
+
147
+ return sql
148
+
149
+
150
+ # =========================================================
151
+ # SQL VALIDATOR
152
+ # =========================================================
153
+
154
  def validate_sql(sql):
155
 
156
+ blocked = [
157
+ "DROP",
158
+ "DELETE",
159
+ "UPDATE",
160
+ "INSERT",
161
+ "ALTER",
162
+ "CREATE",
163
+ "TRUNCATE",
164
+ "JOIN",
165
+ "UNION"
166
+ ]
167
+
168
  upper = sql.upper()
169
 
170
+ for word in blocked:
171
 
172
  if word in upper:
173
  return False
174
 
175
+ if not upper.startswith("SELECT"):
176
+ return False
177
 
178
+ if "VEHICLE_LOGS" not in upper:
179
+ return False
180
 
181
+ return True
182
 
 
183
 
184
+ # =========================================================
185
+ # MAIN NLP TO SQL ENGINE
186
+ # =========================================================
 
 
 
 
 
 
 
 
 
 
 
187
 
188
  def ask_llm(user_query):
 
 
 
 
 
 
 
 
 
189
 
190
  q = user_query.lower().strip()
191
 
192
+ # =====================================================
193
+ # ENTITY EXTRACTION
194
+ # =====================================================
195
+
196
+ plate_match = re.search(
197
+ r'([A-Z]{2}\d{1,2}[A-Z]{1,3}\d{3,4})',
198
+ user_query.upper()
199
+ )
200
+
201
+ date_match = re.search(
202
+ r'(\d{4}-\d{2}-\d{2})',
203
+ q
204
+ )
205
+
206
+ # =====================================================
207
+ # INTENT DETECTION
208
+ # =====================================================
209
+
210
+ intents = {
211
+
212
+ "tracking":
213
+ any(k in q for k in [
214
+ "track",
215
+ "history",
216
+ "movement",
217
+ "travel",
218
+ "route",
219
+ "visited",
220
+ "where"
221
+ ]),
222
+
223
+ "count":
224
+ any(k in q for k in [
225
+ "count",
226
+ "how many",
227
+ "total"
228
+ ]),
229
+
230
+ "analytics":
231
+ any(k in q for k in [
232
+ "top",
233
+ "most",
234
+ "distribution",
235
+ "analysis",
236
+ "statistics",
237
+ "peak"
238
+ ]),
239
+
240
+ "latest":
241
+ any(k in q for k in [
242
+ "latest",
243
+ "recent",
244
+ "last"
245
+ ])
246
+ }
247
+
248
+ # =====================================================
249
+ # RULE BASED ENGINE
250
+ # =====================================================
251
 
252
+ # =====================================================
253
+ # PLATE TRACKING
254
+ # =====================================================
255
 
256
  if plate_match:
257
+
258
  plate = plate_match.group(1)
259
 
260
+ # TRACKING
261
+
262
+ if intents["tracking"]:
263
+
264
  return clean_sql(f"""
265
  SELECT
266
  timestamp,
 
268
  state,
269
  vehicle_type,
270
  location,
271
+ camera_id,
272
+ date,
273
+ hour,
274
+ day
275
  FROM vehicle_logs
276
  WHERE plate = '{plate}'
277
  ORDER BY timestamp DESC
278
+ LIMIT 100
279
  """)
280
 
281
+ # COUNT
282
+
283
+ if intents["count"]:
284
+
285
  return clean_sql(f"""
286
  SELECT
287
  plate,
288
+ COUNT(*) as detections,
289
  COUNT(DISTINCT location) as unique_locations,
290
+ COUNT(DISTINCT date) as active_days
291
  FROM vehicle_logs
292
  WHERE plate = '{plate}'
293
+ GROUP BY plate
294
  """)
295
 
296
+ # DEFAULT
297
+
298
  return clean_sql(f"""
299
  SELECT *
300
  FROM vehicle_logs
301
  WHERE plate = '{plate}'
302
  ORDER BY timestamp DESC
303
+ LIMIT 50
304
  """)
305
 
306
+ # =====================================================
307
+ # STATE QUERIES
308
+ # =====================================================
309
+
310
+ for key, state in VALID_STATES.items():
 
 
 
 
 
 
 
 
 
 
 
311
 
 
312
  if key in q:
313
+
314
+ if intents["count"]:
315
+
316
  return clean_sql(f"""
317
  SELECT
318
  state,
319
+ COUNT(*) as total_detections,
320
+ COUNT(DISTINCT plate) as unique_vehicles
 
321
  FROM vehicle_logs
322
+ WHERE state = '{state}'
323
+ GROUP BY state
 
 
 
 
 
 
 
 
 
 
 
324
  """)
325
 
326
  return clean_sql(f"""
327
  SELECT *
328
  FROM vehicle_logs
329
+ WHERE state = '{state}'
330
  ORDER BY timestamp DESC
331
+ LIMIT 100
332
  """)
333
 
334
+ # =====================================================
335
+ # LOCATION QUERIES
336
+ # =====================================================
337
+
338
+ for loc in KNOWN_LOCATIONS:
 
 
339
 
 
340
  if loc in q:
341
+
342
+ # COUNT
343
+
344
+ if intents["count"]:
345
+
346
  return clean_sql(f"""
347
  SELECT
348
  location,
349
+ COUNT(*) as detections,
350
  COUNT(DISTINCT plate) as unique_vehicles
351
  FROM vehicle_logs
352
  WHERE LOWER(location) LIKE '%{loc}%'
353
  GROUP BY location
354
+ ORDER BY detections DESC
355
  """)
356
 
357
+ # DEFAULT
358
+
359
  return clean_sql(f"""
360
  SELECT
361
  timestamp,
362
  plate,
363
  state,
364
  vehicle_type,
365
+ location,
366
+ camera_id
367
  FROM vehicle_logs
368
  WHERE LOWER(location) LIKE '%{loc}%'
369
  ORDER BY timestamp DESC
370
+ LIMIT 100
371
  """)
372
 
373
+ # =====================================================
374
+ # VEHICLE TYPE
375
+ # =====================================================
376
+
377
+ for vtype in VEHICLE_TYPES:
378
+
379
+ if vtype in q:
380
+
381
+ if intents["count"]:
382
 
 
 
 
383
  return clean_sql(f"""
384
  SELECT
385
  vehicle_type,
386
+ COUNT(*) as count
 
387
  FROM vehicle_logs
388
+ WHERE LOWER(vehicle_type) LIKE '%{vtype}%'
389
+ GROUP BY vehicle_type
390
  """)
391
 
392
  return clean_sql(f"""
393
  SELECT *
394
  FROM vehicle_logs
395
+ WHERE LOWER(vehicle_type) LIKE '%{vtype}%'
396
  ORDER BY timestamp DESC
397
+ LIMIT 50
398
  """)
399
 
400
+ # =====================================================
401
+ # DATE QUERY
402
+ # =====================================================
403
 
404
  if date_match:
 
405
 
406
+ d = date_match.group(1)
 
 
 
 
 
 
 
 
 
 
407
 
408
  return clean_sql(f"""
409
  SELECT *
410
  FROM vehicle_logs
411
+ WHERE date = '{d}'
412
  ORDER BY timestamp DESC
413
+ LIMIT 100
414
  """)
415
 
416
+ # =====================================================
417
+ # ANALYTICS
418
+ # =====================================================
 
 
 
 
 
 
419
 
420
+ if "hourly traffic" in q or "traffic by hour" in q:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
421
 
 
422
  return clean_sql("""
423
  SELECT
424
  hour,
425
+ COUNT(*) as traffic
426
  FROM vehicle_logs
427
  GROUP BY hour
428
+ ORDER BY hour
 
429
  """)
430
 
431
+ if "top vehicles" in q or "most detected" in q:
 
 
 
 
 
 
 
 
 
432
 
 
 
433
  return clean_sql("""
434
  SELECT
435
  plate,
436
+ COUNT(*) as detections
 
 
437
  FROM vehicle_logs
438
  GROUP BY plate
439
  ORDER BY detections DESC
440
+ LIMIT 20
441
  """)
442
 
443
+ if "state distribution" in q:
444
+
445
  return clean_sql("""
446
  SELECT
447
+ state,
448
+ COUNT(*) as count
 
 
449
  FROM vehicle_logs
450
+ GROUP BY state
451
+ ORDER BY count DESC
 
 
452
  """)
453
 
454
+ if "vehicle type distribution" in q:
455
+
456
  return clean_sql("""
457
  SELECT
458
  vehicle_type,
459
+ COUNT(*) as count
 
 
460
  FROM vehicle_logs
461
  GROUP BY vehicle_type
462
+ ORDER BY count DESC
463
  """)
464
 
465
+ if "latest" in q or "recent" in q:
 
 
 
 
 
 
 
 
 
 
466
 
 
467
  return clean_sql("""
468
  SELECT *
469
  FROM vehicle_logs
470
  ORDER BY timestamp DESC
471
+ LIMIT 50
472
  """)
473
 
474
+ # =====================================================
475
+ # LLM FALLBACK
476
+ # =====================================================
 
 
 
 
 
 
477
 
478
+ if not USE_LLM:
 
 
 
 
 
 
 
 
 
 
 
 
479
 
 
 
480
  return clean_sql("""
481
+ SELECT *
 
 
 
 
 
482
  FROM vehicle_logs
483
+ ORDER BY timestamp DESC
484
+ LIMIT 10
 
 
485
  """)
486
 
487
+ # =====================================================
488
+ # SYSTEM PROMPT
489
+ # =====================================================
490
+
491
+ system_prompt = f"""
492
+ You are an elite PostgreSQL SQL generator.
493
+
494
+ Your job:
495
+ Convert natural language into VALID PostgreSQL SQL.
496
+
497
+ ==================================================
498
+ DATABASE
499
+ ==================================================
500
+
501
+ TABLE:
502
+ vehicle_logs
503
+
504
+ AVAILABLE COLUMNS:
505
+
506
+ timestamp
507
+ plate
508
+ state
509
+ vehicle_type
510
+ vehicle_conf
511
+ camera_id
512
+ location
513
+ date
514
+ hour
515
+ day
516
 
517
+ ==================================================
518
+ COLUMN MEANINGS
519
+ ==================================================
520
+
521
+ timestamp:
522
+ vehicle detection timestamp
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
523
 
524
+ plate:
525
+ vehicle number plate
526
+
527
+ state:
528
+ vehicle state code
529
+
530
+ vehicle_type:
531
+ type of vehicle
532
+
533
+ vehicle_conf:
534
+ AI detection confidence
535
+
536
+ camera_id:
537
+ CCTV camera ID
538
+
539
+ location:
540
+ detected location
541
+
542
+ date:
543
+ YYYY-MM-DD
544
+
545
+ hour:
546
+ 0-23
547
+
548
+ day:
549
+ Monday-Sunday
550
+
551
+ ==================================================
552
+ KNOWN STATES
553
+ ==================================================
554
+
555
+ TN
556
+ KA
557
+ KL
558
+ AP
559
+ TS
560
+ MH
561
+ DL
562
+ GJ
563
+ RJ
564
+ UP
565
+ WB
566
+ HR
567
+ PB
568
+
569
+ ==================================================
570
+ KNOWN LOCATIONS
571
+ ==================================================
572
+
573
+ {KNOWN_LOCATIONS}
574
+
575
+ ==================================================
576
+ STRICT RULES
577
+ ==================================================
578
+
579
+ 1. ONLY use vehicle_logs
580
+ 2. NEVER use JOIN
581
+ 3. NEVER invent tables
582
+ 4. NEVER invent columns
583
+ 5. ONLY SELECT queries
584
+ 6. NEVER use UPDATE
585
+ 7. NEVER use DELETE
586
+ 8. NEVER use DROP
587
+ 9. NEVER use ALTER
588
+ 10. PostgreSQL syntax only
589
+ 11. Always use LIMIT 50 or LIMIT 100
590
+ 12. Return SQL ONLY
591
+ 13. No markdown
592
+ 14. No explanation
593
+
594
+ ==================================================
595
+ QUERY UNDERSTANDING
596
+ ==================================================
597
+
598
+ track vehicle
599
+ → WHERE plate=''
600
+
601
+ show TN vehicles
602
+ → WHERE state='TN'
603
+
604
+ show vehicles from adyar
605
+ → WHERE LOWER(location) LIKE '%adyar%'
606
+
607
+ top vehicles
608
+ → GROUP BY plate
609
+
610
+ hourly traffic
611
+ → GROUP BY hour
612
+
613
+ vehicle type distribution
614
+ → GROUP BY vehicle_type
615
+
616
+ latest detections
617
+ → ORDER BY timestamp DESC
618
+
619
+ ==================================================
620
+ GOOD EXAMPLES
621
+ ==================================================
622
+
623
+ SELECT *
624
+ FROM vehicle_logs
625
+ WHERE state='TN'
626
+ ORDER BY timestamp DESC
627
+ LIMIT 50;
628
+
629
+ SELECT *
630
+ FROM vehicle_logs
631
+ WHERE LOWER(location) LIKE '%adyar%'
632
+ ORDER BY timestamp DESC
633
+ LIMIT 50;
634
+
635
+ SELECT
636
+ plate,
637
+ COUNT(*) as detections
638
+ FROM vehicle_logs
639
+ GROUP BY plate
640
+ ORDER BY detections DESC
641
+ LIMIT 20;
642
+
643
+ SELECT *
644
+ FROM vehicle_logs
645
+ WHERE plate='TN63MB3157'
646
+ ORDER BY timestamp DESC
647
+ LIMIT 100;
648
+ """
649
+
650
+ user_prompt = f"""
651
+ Generate PostgreSQL SQL query for:
652
+
653
+ {user_query}
654
  """
655
 
656
+ # =====================================================
657
+ # MISTRAL / SQLCODER CALL
658
+ # =====================================================
659
+
660
  try:
661
+
662
+ response = client.chat_completion(
663
+ messages=[
664
+ {
665
+ "role": "system",
666
+ "content": system_prompt
667
+ },
668
+ {
669
+ "role": "user",
670
+ "content": user_prompt
671
+ }
672
+ ],
673
+ max_tokens=250,
674
+ temperature=0.05
675
  )
676
 
677
+ sql = response.choices[0].message.content.strip()
678
 
679
+ sql = clean_sql(sql)
 
 
680
 
681
+ # =================================================
682
+ # SAFETY
683
+ # =================================================
684
 
685
+ if not validate_sql(sql):
686
 
687
+ return clean_sql("""
688
+ SELECT *
689
+ FROM vehicle_logs
690
+ ORDER BY timestamp DESC
691
+ LIMIT 10
692
+ """)
693
 
694
+ # AUTO LIMIT
 
695
 
696
+ if "LIMIT" not in sql.upper():
 
697
 
698
+ sql = sql.replace(";", " LIMIT 50;")
699
+
700
+ return sql
701
 
702
  except Exception as e:
 
 
703
 
704
+ print("LLM ERROR:", e)
705
+ traceback.print_exc()
706
 
707
+ return clean_sql("""
708
+ SELECT *
709
+ FROM vehicle_logs
710
+ ORDER BY timestamp DESC
711
+ LIMIT 10
712
+ """)
713
 
714
+ # =========================================================
715
+ # QUERY EXECUTION
716
+ # =========================================================
717
 
718
+ def run_query(user_query):
 
 
 
 
 
 
719
 
720
  try:
721
 
722
  sql = ask_llm(user_query)
723
 
724
+ print("\n==============================")
725
+ print("USER QUERY:")
726
+ print(user_query)
727
 
728
+ print("\nGENERATED SQL:")
729
+ print(sql)
730
+ print("==============================")
 
 
 
731
 
732
  with engine.connect() as conn:
733
 
 
741
  return {
742
  "query": user_query,
743
  "sql": sql,
744
+ "count": len(rows),
745
+ "result": rows
746
  }
747
 
748
  except Exception as e:
 
756
  "result": []
757
  }
758
 
759
+ # =========================================================
760
+ # DATABASE OPERATIONS
761
+ # =========================================================
762
 
763
+ def save_detection(plate, state, vehicle_type, vehicle_conf, date, time):
764
+ """Save a vehicle detection to the database"""
765
+
 
 
 
 
766
  try:
767
+
768
+ if engine is None:
769
+ print("⚠️ Engine not initialized")
770
+ return False
771
+
772
+ query = f"""
773
+ INSERT INTO vehicle_logs
774
+ (plate, state, vehicle_type, vehicle_conf, date, time, timestamp)
775
+ VALUES ('{plate}', '{state}', '{vehicle_type}', {vehicle_conf}, '{date}', '{time}', NOW())
776
+ """
777
+
778
  with engine.connect() as conn:
779
+ conn.execute(text(query))
780
+ conn.commit()
781
+
782
+ print(f"✅ Saved: {plate} from {state}")
783
+ return True
784
+
 
785
  except Exception as e:
786
+ print(f"Save Error: {e}")
787
+ traceback.print_exc()
788
+ return False
789
 
790
 
791
+ def health_check():
792
+ """Check database health"""
793
+
794
+ try:
795
+
796
+ if engine is None:
797
+ return False, "❌ Database not configured"
798
+
799
+ with engine.connect() as conn:
800
+ result = conn.execute(text("SELECT COUNT(*) FROM vehicle_logs"))
801
+ count = result.scalar()
802
+
803
+ return True, f"✅ Database OK - {count} records"
804
+
805
+ except Exception as e:
806
+ return False, f"❌ Database Error: {str(e)}"
807
 
 
 
808
 
809
+ def get_vehicles_by_state():
810
+ """Get vehicle count by state"""
811
+
812
  try:
813
+
814
+ sql = """
815
+ SELECT state, COUNT(*) as count
816
+ FROM vehicle_logs
817
+ GROUP BY state
818
+ ORDER BY count DESC
819
+ """
820
+
821
  with engine.connect() as conn:
822
+ result = conn.execute(text(sql))
823
+ rows = [dict(r._mapping) for r in result]
824
+
825
+ return rows
826
+
 
 
827
  except Exception as e:
828
+ print(f"State Query Error: {e}")
829
  return []
830
 
831
 
832
+ def get_hourly_traffic():
833
+ """Get traffic by hour"""
834
+
 
 
835
  try:
836
+
837
+ sql = """
838
+ SELECT hour, COUNT(*) as traffic
839
+ FROM vehicle_logs
840
+ GROUP BY hour
841
+ ORDER BY hour
842
+ """
843
+
844
  with engine.connect() as conn:
845
+ result = conn.execute(text(sql))
846
+ rows = [dict(r._mapping) for r in result]
847
+
848
+ return rows
849
+
 
 
 
 
850
  except Exception as e:
851
+ print(f"Hourly Traffic Error: {e}")
852
  return []
853
 
854
 
855
+ def get_top_plates():
856
+ """Get top detected plates"""
857
+
 
 
858
  try:
859
+
860
+ sql = """
861
+ SELECT plate, COUNT(*) as detections
862
+ FROM vehicle_logs
863
+ GROUP BY plate
864
+ ORDER BY detections DESC
865
+ LIMIT 20
866
+ """
867
+
868
  with engine.connect() as conn:
869
+ result = conn.execute(text(sql))
870
+ rows = [dict(r._mapping) for r in result]
871
+
872
+ return rows
873
+
 
 
 
 
 
874
  except Exception as e:
875
+ print(f"Top Plates Error: {e}")
876
  return []
877
 
878
 
879
+ def get_suspicious_vehicles():
880
+ """Get vehicles detected multiple times (potentially suspicious)"""
881
+
 
 
 
 
882
  try:
883
+
884
+ sql = """
885
+ SELECT plate, state, COUNT(*) as detections,
886
+ COUNT(DISTINCT location) as locations,
887
+ COUNT(DISTINCT date) as days
888
+ FROM vehicle_logs
889
+ GROUP BY plate, state
890
+ HAVING COUNT(*) > 5
891
+ ORDER BY detections DESC
892
+ LIMIT 20
893
+ """
894
+
895
  with engine.connect() as conn:
896
+ result = conn.execute(text(sql))
897
+ rows = [dict(r._mapping) for r in result]
898
+
899
+ return rows
900
+
901
  except Exception as e:
902
+ print(f"Suspicious Vehicles Error: {e}")
903
+ return []
packages.txt ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ libglib2.0-0
2
+ libsm6
3
+ libxext6
4
+ libxrender-dev
5
+ ffmpeg
6
+ libgomp1
requirements.txt CHANGED
@@ -1,17 +1,32 @@
1
  ultralytics==8.4.46
 
2
  opencv-python-headless==4.8.1.78
 
3
  numpy==1.26.4
4
 
5
  paddleocr==2.7.0.3
6
  paddlepaddle==2.6.2
7
 
8
  transformers==4.40.0
 
9
  torch==2.1.2
 
 
10
  Pillow==10.1.0
11
 
12
- sqlalchemy
13
- psycopg2-binary
14
- python-dotenv
15
- pandas
16
- gradio
17
- huggingface_hub
 
 
 
 
 
 
 
 
 
 
 
1
  ultralytics==8.4.46
2
+
3
  opencv-python-headless==4.8.1.78
4
+
5
  numpy==1.26.4
6
 
7
  paddleocr==2.7.0.3
8
  paddlepaddle==2.6.2
9
 
10
  transformers==4.40.0
11
+
12
  torch==2.1.2
13
+ torchvision==0.16.2
14
+
15
  Pillow==10.1.0
16
 
17
+ sqlalchemy==2.0.30
18
+ psycopg2-binary==2.9.9
19
+
20
+ python-dotenv==1.0.1
21
+
22
+ pandas==2.2.2
23
+
24
+ gradio==4.44.1
25
+
26
+ huggingface_hub==0.23.0
27
+
28
+ accelerate==0.30.1
29
+
30
+ sentencepiece==0.2.0
31
+
32
+ scipy==1.13.1